Imported Upstream version 1.21.0 upstream/1.21.0
authorDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 3 Mar 2020 00:39:18 +0000 (09:39 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 3 Mar 2020 00:39:18 +0000 (09:39 +0900)
755 files changed:
.gitignore
.gitmodules
.pylintrc
.vscode/launch.json [deleted file]
BUILD
BUILD.gn
CMakeLists.txt
CONTRIBUTING.md
Makefile
WORKSPACE
bazel/BUILD
bazel/cc_grpc_library.bzl
bazel/generate_cc.bzl
bazel/grpc_build_system.bzl
bazel/grpc_deps.bzl
bazel/grpc_python_deps.bzl [new file with mode: 0644]
bazel/protobuf.bzl [new file with mode: 0644]
bazel/python_rules.bzl [new file with mode: 0644]
build.yaml
config.m4
config.w32
doc/PROTOCOL-HTTP2.md
doc/core/grpc-polling-engines.md
doc/environment_variables.md
doc/g_stands_for.md
doc/python/sphinx/grpc.rst
doc/statuscodes.md
etc/roots.pem
examples/BUILD
examples/cpp/route_guide/route_guide_server.cc
examples/csharp/HelloworldXamarin/iOS/AppDelegate.cs
examples/objective-c/auth_sample/MakeRPCViewController.m
examples/objective-c/helloworld/main.m
examples/objective-c/helloworld_macos/main.m
examples/objective-c/route_guide/ViewControllers.m
examples/python/errors/client.py
examples/python/errors/server.py
examples/python/errors/test/_error_handling_example_test.py
examples/python/multiprocessing/BUILD
examples/python/wait_for_ready/BUILD.bazel [new file with mode: 0644]
examples/python/wait_for_ready/README.md [new file with mode: 0644]
examples/python/wait_for_ready/test/_wait_for_ready_example_test.py [new file with mode: 0644]
examples/python/wait_for_ready/wait_for_ready_example.py [new file with mode: 0644]
examples/ruby/without_protobuf/echo_client.rb
gRPC-C++.podspec
gRPC-Core.podspec
gRPC-ProtoRPC.podspec
gRPC-RxLibrary.podspec
gRPC.podspec
grpc.gemspec
grpc.gyp
include/grpc/grpc_security.h
include/grpc/impl/codegen/grpc_types.h
include/grpc/impl/codegen/port_platform.h
include/grpc/impl/codegen/slice.h
include/grpc/impl/codegen/status.h
include/grpc/slice.h
include/grpcpp/channel.h
include/grpcpp/channel_impl.h [new file with mode: 0644]
include/grpcpp/create_channel.h
include/grpcpp/create_channel_impl.h [new file with mode: 0644]
include/grpcpp/ext/proto_server_reflection_plugin.h
include/grpcpp/ext/proto_server_reflection_plugin_impl.h [new file with mode: 0644]
include/grpcpp/ext/server_load_reporting.h
include/grpcpp/ext/server_load_reporting_impl.h [new file with mode: 0644]
include/grpcpp/generic/generic_stub.h
include/grpcpp/generic/generic_stub_impl.h [new file with mode: 0644]
include/grpcpp/health_check_service_interface.h
include/grpcpp/health_check_service_interface_impl.h [new file with mode: 0644]
include/grpcpp/impl/codegen/async_generic_service.h
include/grpcpp/impl/codegen/async_stream.h
include/grpcpp/impl/codegen/async_unary_call.h
include/grpcpp/impl/codegen/call.h
include/grpcpp/impl/codegen/call_op_set.h
include/grpcpp/impl/codegen/channel_interface.h
include/grpcpp/impl/codegen/client_callback.h
include/grpcpp/impl/codegen/client_context.h
include/grpcpp/impl/codegen/client_interceptor.h
include/grpcpp/impl/codegen/client_unary_call.h
include/grpcpp/impl/codegen/completion_queue.h
include/grpcpp/impl/codegen/completion_queue_impl.h [new file with mode: 0644]
include/grpcpp/impl/codegen/intercepted_channel.h
include/grpcpp/impl/codegen/message_allocator.h [new file with mode: 0644]
include/grpcpp/impl/codegen/method_handler_impl.h
include/grpcpp/impl/codegen/rpc_service_method.h
include/grpcpp/impl/codegen/server_callback.h
include/grpcpp/impl/codegen/server_context.h
include/grpcpp/impl/codegen/server_interface.h
include/grpcpp/impl/codegen/service_type.h
include/grpcpp/impl/codegen/status_code_enum.h
include/grpcpp/impl/codegen/sync.h [new file with mode: 0644]
include/grpcpp/impl/codegen/sync_stream.h
include/grpcpp/impl/server_builder_plugin.h
include/grpcpp/impl/server_initializer.h
include/grpcpp/impl/server_initializer_impl.h [new file with mode: 0644]
include/grpcpp/resource_quota.h
include/grpcpp/resource_quota_impl.h [new file with mode: 0644]
include/grpcpp/security/auth_metadata_processor.h
include/grpcpp/security/auth_metadata_processor_impl.h [new file with mode: 0644]
include/grpcpp/security/credentials.h
include/grpcpp/security/credentials_impl.h [new file with mode: 0644]
include/grpcpp/security/server_credentials.h
include/grpcpp/security/server_credentials_impl.h [new file with mode: 0644]
include/grpcpp/server.h
include/grpcpp/server_builder.h
include/grpcpp/server_builder_impl.h [new file with mode: 0644]
include/grpcpp/server_impl.h [new file with mode: 0644]
include/grpcpp/support/channel_arguments.h
include/grpcpp/support/channel_arguments_impl.h [new file with mode: 0644]
include/grpcpp/support/error_details.h
include/grpcpp/support/error_details_impl.h [new file with mode: 0644]
include/grpcpp/support/message_allocator.h [new file with mode: 0644]
package.xml
src/android/test/interop/app/src/main/cpp/grpc-interop.cc
src/compiler/cpp_generator.cc
src/compiler/cpp_generator.h
src/compiler/cpp_plugin.cc
src/compiler/cpp_plugin.h [new file with mode: 0644]
src/compiler/csharp_generator.cc
src/compiler/objective_c_generator.cc
src/compiler/protobuf_plugin.h
src/compiler/python_generator.cc
src/compiler/python_generator.h
src/compiler/python_generator_helpers.h
src/compiler/schema_interface.h
src/core/ext/filters/client_channel/backup_poller.cc
src/core/ext/filters/client_channel/backup_poller.h
src/core/ext/filters/client_channel/channel_connectivity.cc
src/core/ext/filters/client_channel/client_channel.cc
src/core/ext/filters/client_channel/client_channel_channelz.cc
src/core/ext/filters/client_channel/client_channel_plugin.cc
src/core/ext/filters/client_channel/health/health_check_client.cc
src/core/ext/filters/client_channel/health/health_check_client.h
src/core/ext/filters/client_channel/http_connect_handshaker.cc
src/core/ext/filters/client_channel/http_connect_handshaker.h
src/core/ext/filters/client_channel/lb_policy.cc
src/core/ext/filters/client_channel/lb_policy.h
src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc
src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h
src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc
src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h
src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
src/core/ext/filters/client_channel/lb_policy/subchannel_list.h
src/core/ext/filters/client_channel/lb_policy/xds/xds.cc
src/core/ext/filters/client_channel/lb_policy_factory.h
src/core/ext/filters/client_channel/lb_policy_registry.cc
src/core/ext/filters/client_channel/lb_policy_registry.h
src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc
src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h
src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc [new file with mode: 0644]
src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc
src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc
src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h
src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc
src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc [new file with mode: 0644]
src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc
src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc
src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc [new file with mode: 0644]
src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h [new file with mode: 0644]
src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc
src/core/ext/filters/client_channel/resolver_result_parsing.cc
src/core/ext/filters/client_channel/resolver_result_parsing.h
src/core/ext/filters/client_channel/resolving_lb_policy.cc
src/core/ext/filters/client_channel/resolving_lb_policy.h
src/core/ext/filters/client_channel/service_config.cc
src/core/ext/filters/client_channel/service_config.h
src/core/ext/filters/client_channel/subchannel.cc
src/core/ext/filters/client_channel/subchannel.h
src/core/ext/filters/deadline/deadline_filter.cc
src/core/ext/filters/deadline/deadline_filter.h
src/core/ext/filters/http/client/http_client_filter.cc
src/core/ext/filters/http/client/http_client_filter.h
src/core/ext/filters/http/client_authority_filter.cc
src/core/ext/filters/http/message_compress/message_compress_filter.cc
src/core/ext/filters/http/server/http_server_filter.cc
src/core/ext/filters/message_size/message_size_filter.cc
src/core/ext/filters/message_size/message_size_filter.h
src/core/ext/transport/chttp2/alpn/alpn.h
src/core/ext/transport/chttp2/transport/chttp2_plugin.cc
src/core/ext/transport/chttp2/transport/chttp2_transport.cc
src/core/ext/transport/chttp2/transport/flow_control.h
src/core/ext/transport/chttp2/transport/frame_settings.cc
src/core/ext/transport/chttp2/transport/hpack_encoder.cc
src/core/ext/transport/chttp2/transport/hpack_parser.cc
src/core/ext/transport/chttp2/transport/hpack_table.cc
src/core/ext/transport/chttp2/transport/incoming_metadata.cc
src/core/ext/transport/chttp2/transport/incoming_metadata.h
src/core/ext/transport/chttp2/transport/internal.h
src/core/ext/transport/chttp2/transport/parsing.cc
src/core/ext/transport/chttp2/transport/stream_lists.cc
src/core/ext/transport/chttp2/transport/writing.cc
src/core/ext/transport/cronet/transport/cronet_transport.cc
src/core/ext/transport/inproc/inproc_transport.cc
src/core/ext/upb-generated/envoy/api/v2/endpoint/load_report.upb.c [new file with mode: 0644]
src/core/ext/upb-generated/envoy/api/v2/endpoint/load_report.upb.h [new file with mode: 0644]
src/core/ext/upb-generated/envoy/service/load_stats/v2/lrs.upb.c [new file with mode: 0644]
src/core/ext/upb-generated/envoy/service/load_stats/v2/lrs.upb.h [new file with mode: 0644]
src/core/lib/channel/channel_args.cc
src/core/lib/channel/channel_args.h
src/core/lib/channel/channel_stack.h
src/core/lib/channel/channelz_registry.cc
src/core/lib/channel/connected_channel.cc
src/core/lib/channel/context.h
src/core/lib/channel/handshaker.cc
src/core/lib/channel/handshaker.h
src/core/lib/compression/compression_args.cc [new file with mode: 0644]
src/core/lib/compression/compression_args.h [new file with mode: 0644]
src/core/lib/debug/trace.cc
src/core/lib/debug/trace.h
src/core/lib/gpr/arena.cc [deleted file]
src/core/lib/gpr/arena.h
src/core/lib/gpr/env.h
src/core/lib/gpr/env_linux.cc
src/core/lib/gpr/env_posix.cc
src/core/lib/gpr/env_windows.cc
src/core/lib/gpr/log.cc
src/core/lib/gpr/string.cc
src/core/lib/gpr/string.h
src/core/lib/gpr/time_posix.cc
src/core/lib/gprpp/arena.cc [new file with mode: 0644]
src/core/lib/gprpp/arena.h [new file with mode: 0644]
src/core/lib/gprpp/fork.cc
src/core/lib/gprpp/global_config.h [new file with mode: 0644]
src/core/lib/gprpp/global_config_custom.h [new file with mode: 0644]
src/core/lib/gprpp/global_config_env.cc [new file with mode: 0644]
src/core/lib/gprpp/global_config_env.h [new file with mode: 0644]
src/core/lib/gprpp/global_config_generic.h [new file with mode: 0644]
src/core/lib/gprpp/map.h [new file with mode: 0644]
src/core/lib/gprpp/optional.h
src/core/lib/gprpp/orphanable.h
src/core/lib/gprpp/pair.h [moved from src/core/lib/gprpp/mutex_lock.h with 56% similarity]
src/core/lib/gprpp/ref_counted.h
src/core/lib/gprpp/sync.h [new file with mode: 0644]
src/core/lib/http/parser.cc
src/core/lib/iomgr/call_combiner.cc
src/core/lib/iomgr/call_combiner.h
src/core/lib/iomgr/cfstream_handle.h
src/core/lib/iomgr/combiner.h
src/core/lib/iomgr/endpoint_pair_windows.cc
src/core/lib/iomgr/error.h
src/core/lib/iomgr/ev_epoll1_linux.cc
src/core/lib/iomgr/ev_epollex_linux.cc
src/core/lib/iomgr/ev_poll_posix.cc
src/core/lib/iomgr/ev_posix.cc
src/core/lib/iomgr/ev_posix.h
src/core/lib/iomgr/executor.cc
src/core/lib/iomgr/fork_posix.cc
src/core/lib/iomgr/internal_errqueue.cc
src/core/lib/iomgr/iomgr.cc
src/core/lib/iomgr/iomgr_windows.cc
src/core/lib/iomgr/lockfree_event.cc
src/core/lib/iomgr/port.h
src/core/lib/iomgr/resource_quota.cc
src/core/lib/iomgr/socket_utils_common_posix.cc
src/core/lib/iomgr/socket_windows.cc
src/core/lib/iomgr/socket_windows.h
src/core/lib/iomgr/tcp_client_custom.cc
src/core/lib/iomgr/tcp_client_posix.cc
src/core/lib/iomgr/tcp_client_windows.cc
src/core/lib/iomgr/tcp_custom.cc
src/core/lib/iomgr/tcp_posix.cc
src/core/lib/iomgr/tcp_server_custom.cc
src/core/lib/iomgr/tcp_server_posix.cc
src/core/lib/iomgr/tcp_server_windows.cc
src/core/lib/iomgr/tcp_windows.cc
src/core/lib/iomgr/timer_generic.cc
src/core/lib/iomgr/timer_manager.cc
src/core/lib/profiling/basic_timers.cc
src/core/lib/security/context/security_context.cc
src/core/lib/security/context/security_context.h
src/core/lib/security/credentials/jwt/jwt_credentials.cc
src/core/lib/security/credentials/jwt/jwt_verifier.cc
src/core/lib/security/credentials/oauth2/oauth2_credentials.cc
src/core/lib/security/credentials/plugin/plugin_credentials.cc
src/core/lib/security/security_connector/load_system_roots_linux.cc
src/core/lib/security/security_connector/security_connector.cc
src/core/lib/security/security_connector/ssl/ssl_security_connector.cc
src/core/lib/security/security_connector/ssl_utils.cc
src/core/lib/security/security_connector/ssl_utils.h
src/core/lib/security/transport/client_auth_filter.cc
src/core/lib/security/transport/secure_endpoint.cc
src/core/lib/security/transport/server_auth_filter.cc
src/core/lib/slice/slice.cc
src/core/lib/slice/slice_buffer.cc
src/core/lib/slice/slice_intern.cc
src/core/lib/slice/slice_internal.h
src/core/lib/surface/api_trace.h
src/core/lib/surface/call.cc
src/core/lib/surface/call.h
src/core/lib/surface/call_details.cc
src/core/lib/surface/completion_queue.cc
src/core/lib/surface/init.cc
src/core/lib/surface/lame_client.cc
src/core/lib/surface/server.cc
src/core/lib/surface/version.cc
src/core/lib/transport/bdp_estimator.cc
src/core/lib/transport/bdp_estimator.h
src/core/lib/transport/connectivity_state.cc
src/core/lib/transport/connectivity_state.h
src/core/lib/transport/error_utils.cc
src/core/lib/transport/metadata.cc
src/core/lib/transport/metadata.h
src/core/lib/transport/static_metadata.cc
src/core/lib/transport/static_metadata.h
src/core/lib/transport/status_metadata.cc
src/core/lib/transport/transport.cc
src/core/lib/transport/transport.h
src/core/lib/transport/transport_impl.h
src/core/tsi/fake_transport_security.cc
src/core/tsi/ssl/session_cache/ssl_session_cache.cc
src/core/tsi/ssl_transport_security.cc
src/cpp/client/channel_cc.cc
src/cpp/client/client_context.cc
src/cpp/client/create_channel.cc
src/cpp/client/create_channel_internal.cc
src/cpp/client/create_channel_internal.h
src/cpp/client/create_channel_posix.cc
src/cpp/client/credentials_cc.cc
src/cpp/client/cronet_credentials.cc
src/cpp/client/generic_stub.cc
src/cpp/client/insecure_credentials.cc
src/cpp/client/secure_credentials.cc
src/cpp/client/secure_credentials.h
src/cpp/common/channel_arguments.cc
src/cpp/common/completion_queue_cc.cc
src/cpp/common/resource_quota_cc.cc
src/cpp/common/secure_channel_arguments.cc
src/cpp/common/version_cc.cc
src/cpp/ext/proto_server_reflection_plugin.cc
src/cpp/server/channelz/channelz_service.cc
src/cpp/server/dynamic_thread_pool.cc
src/cpp/server/dynamic_thread_pool.h
src/cpp/server/health/default_health_check_service.cc
src/cpp/server/health/default_health_check_service.h
src/cpp/server/health/health_check_service.cc
src/cpp/server/insecure_server_credentials.cc
src/cpp/server/load_reporter/load_reporter.cc
src/cpp/server/load_reporter/load_reporter.h
src/cpp/server/load_reporter/load_reporter_async_service_impl.cc
src/cpp/server/load_reporter/load_reporter_async_service_impl.h
src/cpp/server/load_reporter/load_reporting_service_server_builder_option.cc
src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.h
src/cpp/server/load_reporter/util.cc
src/cpp/server/secure_server_credentials.cc
src/cpp/server/secure_server_credentials.h
src/cpp/server/server_builder.cc
src/cpp/server/server_cc.cc
src/cpp/server/server_context.cc
src/cpp/server/server_credentials.cc
src/cpp/thread_manager/thread_manager.cc
src/cpp/thread_manager/thread_manager.h
src/cpp/util/error_details.cc
src/csharp/Grpc.Core.Api/AsyncAuthInterceptor.cs [moved from src/csharp/Grpc.Core/AsyncAuthInterceptor.cs with 100% similarity]
src/csharp/Grpc.Core.Api/AsyncClientStreamingCall.cs [moved from src/csharp/Grpc.Core/AsyncClientStreamingCall.cs with 100% similarity]
src/csharp/Grpc.Core.Api/AsyncDuplexStreamingCall.cs [moved from src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs with 100% similarity]
src/csharp/Grpc.Core.Api/AsyncServerStreamingCall.cs [moved from src/csharp/Grpc.Core/AsyncServerStreamingCall.cs with 100% similarity]
src/csharp/Grpc.Core.Api/AsyncUnaryCall.cs [moved from src/csharp/Grpc.Core/AsyncUnaryCall.cs with 100% similarity]
src/csharp/Grpc.Core.Api/BindServiceMethodAttribute.cs [new file with mode: 0644]
src/csharp/Grpc.Core.Api/CallCredentials.cs [new file with mode: 0644]
src/csharp/Grpc.Core.Api/CallCredentialsConfiguratorBase.cs [new file with mode: 0644]
src/csharp/Grpc.Core.Api/CallFlags.cs [moved from src/csharp/Grpc.Core/Internal/CallFlags.cs with 100% similarity]
src/csharp/Grpc.Core.Api/CallInvoker.cs [moved from src/csharp/Grpc.Core/CallInvoker.cs with 98% similarity]
src/csharp/Grpc.Core.Api/CallOptions.cs [moved from src/csharp/Grpc.Core/CallOptions.cs with 84% similarity]
src/csharp/Grpc.Core.Api/DeserializationContext.cs
src/csharp/Grpc.Core.Api/Grpc.Core.Api.csproj
src/csharp/Grpc.Core.Api/IClientStreamWriter.cs [moved from src/csharp/Grpc.Core/IClientStreamWriter.cs with 100% similarity]
src/csharp/Grpc.Core.Api/Interceptors/ClientInterceptorContext.cs [moved from src/csharp/Grpc.Core/Interceptors/ClientInterceptorContext.cs with 100% similarity]
src/csharp/Grpc.Core.Api/Interceptors/Interceptor.cs [moved from src/csharp/Grpc.Core/Interceptors/Interceptor.cs with 100% similarity]
src/csharp/Grpc.Core.Api/StatusCode.cs
src/csharp/Grpc.Core.Api/VersionInfo.cs
src/csharp/Grpc.Core.Tests/FakeCredentials.cs
src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs
src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs
src/csharp/Grpc.Core.Tests/Internal/CompletionQueueSafeHandleTest.cs
src/csharp/Grpc.Core.Tests/Internal/DefaultDeserializationContextTest.cs [new file with mode: 0644]
src/csharp/Grpc.Core.Tests/Internal/FakeBufferReaderManager.cs [new file with mode: 0644]
src/csharp/Grpc.Core.Tests/Internal/FakeBufferReaderManagerTest.cs [new file with mode: 0644]
src/csharp/Grpc.Core.Tests/Internal/ReusableSliceBufferTest.cs [new file with mode: 0644]
src/csharp/Grpc.Core.Tests/Internal/SliceTest.cs [new file with mode: 0644]
src/csharp/Grpc.Core/CallCredentials.cs [deleted file]
src/csharp/Grpc.Core/ChannelCredentials.cs
src/csharp/Grpc.Core/ForwardedTypes.cs
src/csharp/Grpc.Core/Grpc.Core.csproj
src/csharp/Grpc.Core/Internal/AsyncCall.cs
src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs
src/csharp/Grpc.Core/Internal/CallOptionsExtensions.cs [new file with mode: 0644]
src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
src/csharp/Grpc.Core/Internal/ChannelCredentialsSafeHandle.cs
src/csharp/Grpc.Core/Internal/DefaultCallCredentialsConfigurator.cs [new file with mode: 0644]
src/csharp/Grpc.Core/Internal/DefaultDeserializationContext.cs
src/csharp/Grpc.Core/Internal/INativeCall.cs
src/csharp/Grpc.Core/Internal/NativeCallbackDispatcher.cs
src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs
src/csharp/Grpc.Core/Internal/ReusableSliceBuffer.cs [new file with mode: 0644]
src/csharp/Grpc.Core/Internal/Slice.cs [new file with mode: 0644]
src/csharp/Grpc.Core/VerifyPeerContext.cs [new file with mode: 0644]
src/csharp/Grpc.Examples/MathGrpc.cs
src/csharp/Grpc.HealthCheck/HealthGrpc.cs
src/csharp/Grpc.IntegrationTesting/BenchmarkServiceGrpc.cs
src/csharp/Grpc.IntegrationTesting/EmptyServiceGrpc.cs
src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs
src/csharp/Grpc.IntegrationTesting/ReportQpsScenarioServiceGrpc.cs
src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs
src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
src/csharp/Grpc.IntegrationTesting/WorkerServiceGrpc.cs
src/csharp/Grpc.Reflection/ReflectionGrpc.cs
src/csharp/Grpc.Tools/build/_protobuf/Google.Protobuf.Tools.targets
src/csharp/README.md
src/csharp/build/dependencies.props
src/csharp/build_unitypackage.bat
src/csharp/ext/grpc_csharp_ext.c
src/csharp/tests.json
src/csharp/unitypackage/unitypackage_skeleton/Plugins/Grpc.Core/runtimes/grpc_csharp_ext_dummy_stubs.c
src/objective-c/!ProtoCompiler-gRPCPlugin.podspec
src/objective-c/GRPCClient/GRPCCall.h
src/objective-c/GRPCClient/GRPCCall.m
src/objective-c/GRPCClient/GRPCCallOptions.h
src/objective-c/GRPCClient/GRPCCallOptions.m
src/objective-c/GRPCClient/private/GRPCSecureChannelFactory.m
src/objective-c/GRPCClient/private/version.h
src/objective-c/ProtoRPC/ProtoRPC.h
src/objective-c/ProtoRPC/ProtoRPC.m
src/objective-c/README-CFSTREAM.md
src/objective-c/manual_tests/GrpcIosTest.xcodeproj/xcshareddata/xcschemes/GrpcIosTest.xcscheme [new file with mode: 0644]
src/objective-c/manual_tests/GrpcIosTestUITests/GrpcIosTestUITests.m
src/objective-c/manual_tests/Main.storyboard
src/objective-c/manual_tests/ViewController.m
src/objective-c/tests/APIv2Tests/APIv2Tests.m
src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.mm
src/objective-c/tests/GRPCClientTests.m
src/objective-c/tests/InteropTests.m
src/objective-c/tests/MacTests/Info.plist [new file with mode: 0644]
src/objective-c/tests/MacTests/StressTests.h [new file with mode: 0644]
src/objective-c/tests/MacTests/StressTests.m [new file with mode: 0644]
src/objective-c/tests/MacTests/StressTestsCleartext.m [new file with mode: 0644]
src/objective-c/tests/MacTests/StressTestsSSL.m [new file with mode: 0644]
src/objective-c/tests/Podfile
src/objective-c/tests/RemoteTestClient/RemoteTest.podspec
src/objective-c/tests/RxLibraryUnitTests.m
src/objective-c/tests/Tests.xcodeproj/project.pbxproj
src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme
src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/MacTests.xcscheme [new file with mode: 0644]
src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme [new file with mode: 0644]
src/objective-c/tests/UnitTests/UnitTests.m
src/objective-c/tests/run_tests.sh
src/objective-c/tests/version.h
src/php/composer.json
src/php/ext/grpc/version.h
src/proto/grpc/channelz/BUILD
src/proto/grpc/health/v1/BUILD
src/proto/grpc/reflection/v1alpha/BUILD
src/proto/grpc/testing/BUILD
src/proto/grpc/testing/echo_messages.proto
src/proto/grpc/testing/proto2/BUILD.bazel
src/python/grpcio/README.rst
src/python/grpcio/grpc/BUILD.bazel
src/python/grpcio/grpc/__init__.py
src/python/grpcio/grpc/_channel.py
src/python/grpcio/grpc/_compression.py [new file with mode: 0644]
src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pxd.pxi
src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi
src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
src/python/grpcio/grpc/_grpcio_metadata.py
src/python/grpcio/grpc/_interceptor.py
src/python/grpcio/grpc/_server.py
src/python/grpcio/grpc_core_dependencies.py
src/python/grpcio/grpc_version.py
src/python/grpcio_channelz/README.rst
src/python/grpcio_channelz/grpc_channelz/v1/BUILD.bazel
src/python/grpcio_channelz/grpc_version.py
src/python/grpcio_channelz/setup.py
src/python/grpcio_health_checking/README.rst
src/python/grpcio_health_checking/grpc_health/v1/BUILD.bazel
src/python/grpcio_health_checking/grpc_version.py
src/python/grpcio_health_checking/setup.py
src/python/grpcio_reflection/README.rst
src/python/grpcio_reflection/grpc_reflection/v1alpha/BUILD.bazel
src/python/grpcio_reflection/grpc_version.py
src/python/grpcio_reflection/setup.py
src/python/grpcio_status/README.rst
src/python/grpcio_status/grpc_version.py
src/python/grpcio_status/setup.py
src/python/grpcio_testing/README.rst
src/python/grpcio_testing/grpc_testing/_server/_servicer_context.py
src/python/grpcio_testing/grpc_version.py
src/python/grpcio_testing/setup.py
src/python/grpcio_tests/commands.py
src/python/grpcio_tests/grpc_version.py
src/python/grpcio_tests/tests/fork/_fork_interop_test.py
src/python/grpcio_tests/tests/health_check/_health_servicer_test.py
src/python/grpcio_tests/tests/interop/BUILD.bazel
src/python/grpcio_tests/tests/interop/_insecure_intraop_test.py
src/python/grpcio_tests/tests/interop/_secure_intraop_test.py
src/python/grpcio_tests/tests/interop/methods.py
src/python/grpcio_tests/tests/interop/server.py
src/python/grpcio_tests/tests/interop/service.py [new file with mode: 0644]
src/python/grpcio_tests/tests/reflection/BUILD.bazel
src/python/grpcio_tests/tests/stress/client.py
src/python/grpcio_tests/tests/unit/BUILD.bazel
src/python/grpcio_tests/tests/unit/_api_test.py
src/python/grpcio_tests/tests/unit/_compression_test.py
src/python/grpcio_tests/tests/unit/_tcp_proxy.py [new file with mode: 0644]
src/ruby/ext/grpc/rb_grpc.c
src/ruby/lib/grpc/errors.rb
src/ruby/lib/grpc/generic/bidi_call.rb
src/ruby/lib/grpc/generic/rpc_server.rb
src/ruby/lib/grpc/version.rb
src/ruby/spec/errors_spec.rb [new file with mode: 0644]
src/ruby/tools/version.rb
templates/Makefile.template
templates/gRPC-C++.podspec.template
templates/gRPC-Core.podspec.template
templates/gRPC-ProtoRPC.podspec.template
templates/gRPC.podspec.template
templates/grpc.gemspec.template
templates/src/csharp/Grpc.Core/Internal/native_methods.include
templates/tools/dockerfile/bazel.include
templates/tools/dockerfile/debian_jessie_header.include
templates/tools/dockerfile/interoptest/grpc_interop_java_oracle8/Dockerfile.include
templates/tools/dockerfile/interoptest/grpc_interop_java_oracle8/java_deps.include
templates/tools/dockerfile/java_build_interop.sh.include
test/core/bad_client/bad_client.cc
test/core/bad_connection/close_fd_test.cc
test/core/bad_ssl/bad_ssl_test.cc
test/core/channel/channel_args_test.cc
test/core/client_channel/BUILD
test/core/client_channel/resolvers/dns_resolver_test.cc
test/core/client_channel/service_config_test.cc [new file with mode: 0644]
test/core/compression/compression_test.cc
test/core/end2end/bad_server_response_test.cc
test/core/end2end/fixtures/h2_compress.cc
test/core/end2end/fixtures/h2_full+trace.cc
test/core/end2end/fixtures/h2_sockpair+trace.cc
test/core/end2end/fixtures/h2_spiffe.cc
test/core/end2end/fixtures/h2_ssl.cc
test/core/end2end/fixtures/h2_ssl_cred_reload.cc [new file with mode: 0644]
test/core/end2end/fixtures/h2_ssl_proxy.cc
test/core/end2end/gen_build_yaml.py
test/core/end2end/generate_tests.bzl
test/core/end2end/h2_ssl_cert_test.cc
test/core/end2end/h2_ssl_session_reuse_test.cc
test/core/end2end/tests/compressed_payload.cc
test/core/end2end/tests/keepalive_timeout.cc
test/core/end2end/tests/retry_throttled.cc
test/core/end2end/tests/simple_request.cc
test/core/end2end/tests/stream_compression_compressed_payload.cc
test/core/end2end/tests/stream_compression_payload.cc
test/core/end2end/tests/stream_compression_ping_pong_streaming.cc
test/core/end2end/tests/workaround_cronet_compression.cc
test/core/gpr/arena_test.cc
test/core/gpr/env_test.cc
test/core/gpr/log_test.cc
test/core/gpr/string_test.cc
test/core/gprpp/BUILD
test/core/gprpp/global_config_env_test.cc [new file with mode: 0644]
test/core/gprpp/global_config_test.cc [new file with mode: 0644]
test/core/gprpp/map_test.cc [new file with mode: 0644]
test/core/handshake/BUILD
test/core/http/httpscli_test.cc
test/core/iomgr/resolve_address_posix_test.cc
test/core/iomgr/resolve_address_test.cc
test/core/iomgr/socket_utils_test.cc
test/core/network_benchmarks/BUILD
test/core/security/credentials_test.cc
test/core/security/security_connector_test.cc
test/core/slice/slice_test.cc
test/core/transport/connectivity_state_test.cc
test/core/transport/stream_owned_slice_test.cc
test/core/util/BUILD
test/core/util/fuzzer_corpus_test.cc
test/core/util/reconnect_server.cc
test/core/util/test_config.cc
test/core/util/test_lb_policies.cc
test/core/util/test_tcp_server.cc
test/core/util/test_tcp_server.h
test/cpp/client/client_channel_stress_test.cc
test/cpp/codegen/BUILD
test/cpp/codegen/compiler_test_golden
test/cpp/codegen/golden_file_test.cc
test/cpp/end2end/BUILD
test/cpp/end2end/async_end2end_test.cc
test/cpp/end2end/cfstream_test.cc
test/cpp/end2end/channelz_service_test.cc
test/cpp/end2end/client_callback_end2end_test.cc
test/cpp/end2end/client_crash_test.cc
test/cpp/end2end/client_crash_test_server.cc
test/cpp/end2end/client_interceptors_end2end_test.cc
test/cpp/end2end/client_lb_end2end_test.cc
test/cpp/end2end/end2end_test.cc
test/cpp/end2end/filter_end2end_test.cc
test/cpp/end2end/generic_end2end_test.cc
test/cpp/end2end/grpclb_end2end_test.cc
test/cpp/end2end/health_service_end2end_test.cc
test/cpp/end2end/hybrid_end2end_test.cc
test/cpp/end2end/message_allocator_end2end_test.cc [new file with mode: 0644]
test/cpp/end2end/mock_test.cc
test/cpp/end2end/nonblocking_test.cc
test/cpp/end2end/proto_server_reflection_test.cc
test/cpp/end2end/raw_end2end_test.cc
test/cpp/end2end/server_builder_plugin_test.cc
test/cpp/end2end/server_crash_test_client.cc
test/cpp/end2end/server_early_return_test.cc
test/cpp/end2end/server_interceptors_end2end_test.cc
test/cpp/end2end/server_load_reporting_end2end_test.cc
test/cpp/end2end/service_config_end2end_test.cc [new file with mode: 0644]
test/cpp/end2end/shutdown_test.cc
test/cpp/end2end/streaming_throughput_test.cc
test/cpp/end2end/test_service_impl.cc
test/cpp/end2end/thread_stress_test.cc
test/cpp/end2end/time_change_test.cc
test/cpp/end2end/xds_end2end_test.cc
test/cpp/interop/interop_test.cc
test/cpp/microbenchmarks/bm_alarm.cc
test/cpp/microbenchmarks/bm_arena.cc
test/cpp/microbenchmarks/bm_byte_buffer.cc
test/cpp/microbenchmarks/bm_call_create.cc
test/cpp/microbenchmarks/bm_channel.cc
test/cpp/microbenchmarks/bm_chttp2_hpack.cc
test/cpp/microbenchmarks/bm_chttp2_transport.cc
test/cpp/microbenchmarks/bm_closure.cc
test/cpp/microbenchmarks/bm_cq.cc
test/cpp/microbenchmarks/bm_cq_multiple_threads.cc
test/cpp/microbenchmarks/bm_error.cc
test/cpp/microbenchmarks/bm_fullstack_streaming_ping_pong.cc
test/cpp/microbenchmarks/bm_fullstack_streaming_pump.cc
test/cpp/microbenchmarks/bm_fullstack_trickle.cc
test/cpp/microbenchmarks/bm_fullstack_unary_ping_pong.cc
test/cpp/microbenchmarks/bm_metadata.cc
test/cpp/microbenchmarks/bm_pollset.cc
test/cpp/microbenchmarks/bm_timer.cc
test/cpp/microbenchmarks/fullstack_fixtures.h
test/cpp/microbenchmarks/helpers.cc
test/cpp/microbenchmarks/helpers.h
test/cpp/naming/BUILD
test/cpp/naming/address_sorting_test.cc
test/cpp/naming/cancel_ares_query_test.cc
test/cpp/naming/dns_test_util.cc [new file with mode: 0644]
test/cpp/naming/dns_test_util.h [new file with mode: 0644]
test/cpp/naming/gen_build_yaml.py
test/cpp/naming/generate_resolver_component_tests.bzl
test/cpp/naming/resolver_component_test.cc
test/cpp/naming/resolver_component_tests_runner.py
test/cpp/naming/resolver_component_tests_runner_invoker.cc
test/cpp/naming/resolver_test_record_groups.yaml
test/cpp/performance/writes_per_rpc_test.cc
test/cpp/qps/client_callback.cc
test/cpp/qps/driver.cc
test/cpp/qps/json_run_localhost.cc
test/cpp/server/server_request_call_test.cc
test/cpp/util/cli_call_test.cc
test/cpp/util/create_test_channel.cc
test/cpp/util/create_test_channel.h
test/cpp/util/grpc_tool.cc
test/cpp/util/grpc_tool_test.cc
test/cpp/util/metrics_server.h
test/cpp/util/test_credentials_provider.cc
third_party/cares/cares.BUILD
third_party/toolchains/BUILD
third_party/toolchains/bazel_0.23.2_rbe_windows/BUILD [new file with mode: 0644]
third_party/toolchains/bazel_0.23.2_rbe_windows/cc_toolchain_config.bzl [new file with mode: 0644]
third_party/toolchains/bazel_0.23.2_rbe_windows/dummy_toolchain.bzl [new file with mode: 0644]
tools/codegen/core/gen_static_metadata.py
tools/codegen/core/gen_upb_api.sh
tools/distrib/bazel_style.cfg [new file with mode: 0644]
tools/distrib/format_bazel.sh [new file with mode: 0755]
tools/distrib/python/docgen.py
tools/distrib/python/grpcio_tools/README.rst
tools/distrib/python/grpcio_tools/grpc_version.py
tools/distrib/python/grpcio_tools/setup.py
tools/distrib/sanitize.sh
tools/dockerfile/grpc_clang_format/Dockerfile
tools/dockerfile/grpc_clang_tidy/Dockerfile
tools/dockerfile/interoptest/grpc_interop_cxx/Dockerfile
tools/dockerfile/interoptest/grpc_interop_java/Dockerfile
tools/dockerfile/interoptest/grpc_interop_java/build_interop.sh
tools/dockerfile/interoptest/grpc_interop_java_oracle8/Dockerfile
tools/dockerfile/interoptest/grpc_interop_java_oracle8/build_interop.sh
tools/dockerfile/interoptest/grpc_interop_node/Dockerfile
tools/dockerfile/interoptest/grpc_interop_nodepurejs/Dockerfile
tools/dockerfile/interoptest/grpc_interop_php/Dockerfile
tools/dockerfile/interoptest/grpc_interop_php7/Dockerfile
tools/dockerfile/interoptest/grpc_interop_ruby/Dockerfile
tools/dockerfile/test/bazel/Dockerfile
tools/dockerfile/test/cxx_jessie_x64/Dockerfile
tools/dockerfile/test/fuzzer/Dockerfile
tools/dockerfile/test/node_jessie_x64/Dockerfile
tools/dockerfile/test/php7_jessie_x64/Dockerfile
tools/dockerfile/test/php_jessie_x64/Dockerfile
tools/dockerfile/test/python_jessie_x64/Dockerfile
tools/dockerfile/test/ruby_jessie_x64/Dockerfile
tools/dockerfile/test/sanity/Dockerfile
tools/doxygen/Doxyfile.c++
tools/doxygen/Doxyfile.c++.internal
tools/doxygen/Doxyfile.core.internal
tools/gce/create_linux_kokoro_performance_worker_from_image.sh
tools/gce/linux_kokoro_performance_worker_init.sh
tools/internal_ci/linux/grpc_basictests_csharp.cfg [new file with mode: 0644]
tools/internal_ci/linux/grpc_basictests_node.cfg [new file with mode: 0644]
tools/internal_ci/linux/grpc_basictests_php.cfg [new file with mode: 0644]
tools/internal_ci/linux/grpc_basictests_python.cfg [new file with mode: 0644]
tools/internal_ci/linux/grpc_basictests_ruby.cfg [new file with mode: 0644]
tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh
tools/internal_ci/linux/grpc_bazel_rbe_incompatible_changes.sh [new file with mode: 0644]
tools/internal_ci/linux/grpc_publish_packages.cfg
tools/internal_ci/linux/grpc_publish_packages.sh
tools/internal_ci/linux/pull_request/grpc_basictests_csharp.cfg [moved from tools/internal_ci/linux/pull_request/grpc_basictests_c_cpp_dbg.cfg with 85% similarity]
tools/internal_ci/linux/pull_request/grpc_basictests_node.cfg [moved from tools/internal_ci/linux/pull_request/grpc_basictests_c_cpp_opt.cfg with 85% similarity]
tools/internal_ci/linux/pull_request/grpc_basictests_php.cfg [new file with mode: 0644]
tools/internal_ci/linux/pull_request/grpc_basictests_python.cfg [new file with mode: 0644]
tools/internal_ci/linux/pull_request/grpc_basictests_ruby.cfg [new file with mode: 0644]
tools/internal_ci/macos/grpc_basictests_c_cpp.cfg [new file with mode: 0644]
tools/internal_ci/macos/grpc_basictests_csharp.cfg [new file with mode: 0644]
tools/internal_ci/macos/grpc_basictests_node.cfg [new file with mode: 0644]
tools/internal_ci/macos/grpc_basictests_php.cfg [new file with mode: 0644]
tools/internal_ci/macos/grpc_basictests_python.cfg [new file with mode: 0644]
tools/internal_ci/macos/grpc_basictests_ruby.cfg [new file with mode: 0644]
tools/internal_ci/macos/grpc_run_bazel_tests.sh
tools/internal_ci/macos/pull_request/grpc_basictests_c_cpp.cfg [new file with mode: 0644]
tools/internal_ci/macos/pull_request/grpc_basictests_csharp.cfg [new file with mode: 0644]
tools/internal_ci/macos/pull_request/grpc_basictests_node.cfg [new file with mode: 0644]
tools/internal_ci/macos/pull_request/grpc_basictests_php.cfg [new file with mode: 0644]
tools/internal_ci/macos/pull_request/grpc_basictests_python.cfg [new file with mode: 0644]
tools/internal_ci/macos/pull_request/grpc_basictests_ruby.cfg [new file with mode: 0644]
tools/internal_ci/windows/bazel_rbe.bat [new file with mode: 0644]
tools/internal_ci/windows/grpc_basictests_c.cfg [new file with mode: 0644]
tools/internal_ci/windows/grpc_basictests_csharp.cfg [new file with mode: 0644]
tools/internal_ci/windows/grpc_basictests_python.cfg [new file with mode: 0644]
tools/internal_ci/windows/pull_request/grpc_basictests_c.cfg [new file with mode: 0644]
tools/internal_ci/windows/pull_request/grpc_basictests_csharp.cfg [new file with mode: 0644]
tools/internal_ci/windows/pull_request/grpc_basictests_python.cfg [new file with mode: 0644]
tools/internal_ci/windows/pull_request/grpc_bazel_rbe_dbg.cfg [new file with mode: 0644]
tools/interop_matrix/client_matrix.py
tools/interop_matrix/testcases/csharp__master
tools/interop_matrix/testcases/csharp__v1.18.0 [new file with mode: 0644]
tools/interop_matrix/testcases/csharpcoreclr__master
tools/interop_matrix/testcases/csharpcoreclr__v1.18.0 [new file with mode: 0755]
tools/release/release_notes.py [new file with mode: 0644]
tools/remote_build/README.md
tools/remote_build/kokoro.bazelrc
tools/remote_build/rbe_common.bazelrc
tools/remote_build/windows.bazelrc
tools/run_tests/generated/sources_and_headers.json
tools/run_tests/generated/tests.json
tools/run_tests/python_utils/upload_rbe_results.py
tools/run_tests/run_microbenchmark.py
tools/run_tests/run_tests.py
tools/run_tests/run_tests_matrix.py
tools/run_tests/sanity/check_bazel_workspace.py
tools/run_tests/sanity/core_banned_functions.py

index cde82bc..7b4c5d3 100644 (file)
@@ -115,6 +115,7 @@ bazel-genfiles
 bazel-grpc
 bazel-out
 bazel-testlogs
+bazel_format_virtual_environment/
 
 # Debug output
 gdb.txt
@@ -134,3 +135,11 @@ bm_*.json
 
 # cmake build files
 /cmake/build
+
+# Visual Studio Code artifacts
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+
index 06f1394..f4690a2 100644 (file)
@@ -50,7 +50,7 @@
        url = https://github.com/googleapis/googleapis.git
 [submodule "third_party/protoc-gen-validate"]
        path = third_party/protoc-gen-validate
-       url = https://github.com/lyft/protoc-gen-validate.git
+       url = https://github.com/envoyproxy/protoc-gen-validate.git
 [submodule "third_party/upb"]
        path = third_party/upb
        url = https://github.com/google/upb.git
index ba74dec..fcc8e73 100644 (file)
--- a/.pylintrc
+++ b/.pylintrc
@@ -6,6 +6,8 @@ ignore=
        src/python/grpcio/grpc/framework/foundation,
        src/python/grpcio/grpc/framework/interfaces,
 
+extension-pkg-whitelist=grpc._cython.cygrpc
+
 [VARIABLES]
 
 # TODO(https://github.com/PyCQA/pylint/issues/1345): How does the inspection
@@ -17,7 +19,7 @@ dummy-variables-rgx=^ignored_|^unused_
 # NOTE(nathaniel): Not particularly attached to this value; it just seems to
 # be what works for us at the moment (excepting the dead-code-walking Beta
 # API).
-max-args=6
+max-args=7
 
 [MISCELLANEOUS]
 
diff --git a/.vscode/launch.json b/.vscode/launch.json
deleted file mode 100644 (file)
index 700c61c..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-{
-    "version": "0.2.0",
-    "configurations": [
-        {
-            "type": "node",
-            "request": "launch",
-            "name": "Mocha Tests",
-            "cwd": "${workspaceRoot}",
-            "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/mocha",
-            "windows": {
-                "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/mocha.cmd"
-            },
-            "runtimeArgs": [
-                "-u",
-                "tdd",
-                "--timeout",
-                "999999",
-                "--colors",
-                "${workspaceRoot}/src/node/test"
-            ],
-            "internalConsoleOptions": "openOnSessionStart"
-        },
-        {
-            "type": "node",
-            "request": "attach",
-            "name": "Attach to Process",
-            "port": 5858
-        }
-    ]
-}
diff --git a/BUILD b/BUILD
index 9157f5a..bf12841 100644 (file)
--- a/BUILD
+++ b/BUILD
@@ -74,11 +74,11 @@ config_setting(
 )
 
 # This should be updated along with build.yaml
-g_stands_for = "godric"
+g_stands_for = "gandalf"
 
 core_version = "7.0.0"
 
-version = "1.20.1"
+version = "1.21.0"
 
 GPR_PUBLIC_HDRS = [
     "include/grpc/support/alloc.h",
@@ -192,8 +192,8 @@ GRPCXX_PUBLIC_HDRS = [
     "include/grpc++/impl/service_type.h",
     "include/grpc++/impl/sync_cxx11.h",
     "include/grpc++/impl/sync_no_cxx11.h",
-    "include/grpc++/resource_quota.h",
     "include/grpc++/security/auth_context.h",
+    "include/grpc++/resource_quota.h",
     "include/grpc++/security/auth_metadata_processor.h",
     "include/grpc++/security/credentials.h",
     "include/grpc++/security/server_credentials.h",
@@ -216,16 +216,20 @@ GRPCXX_PUBLIC_HDRS = [
     "include/grpcpp/alarm.h",
     "include/grpcpp/alarm_impl.h",
     "include/grpcpp/channel.h",
+    "include/grpcpp/channel_impl.h",
     "include/grpcpp/client_context.h",
     "include/grpcpp/completion_queue.h",
     "include/grpcpp/create_channel.h",
+    "include/grpcpp/create_channel_impl.h",
     "include/grpcpp/create_channel_posix.h",
     "include/grpcpp/create_channel_posix_impl.h",
     "include/grpcpp/ext/health_check_service_server_builder_option.h",
     "include/grpcpp/generic/async_generic_service.h",
     "include/grpcpp/generic/generic_stub.h",
+    "include/grpcpp/generic/generic_stub_impl.h",
     "include/grpcpp/grpcpp.h",
     "include/grpcpp/health_check_service_interface.h",
+    "include/grpcpp/health_check_service_interface_impl.h",
     "include/grpcpp/impl/call.h",
     "include/grpcpp/impl/channel_argument_option.h",
     "include/grpcpp/impl/client_unary_call.h",
@@ -239,16 +243,23 @@ GRPCXX_PUBLIC_HDRS = [
     "include/grpcpp/impl/server_builder_option_impl.h",
     "include/grpcpp/impl/server_builder_plugin.h",
     "include/grpcpp/impl/server_initializer.h",
+    "include/grpcpp/impl/server_initializer_impl.h",
     "include/grpcpp/impl/service_type.h",
     "include/grpcpp/impl/sync_cxx11.h",
     "include/grpcpp/impl/sync_no_cxx11.h",
     "include/grpcpp/resource_quota.h",
+    "include/grpcpp/resource_quota_impl.h",
     "include/grpcpp/security/auth_context.h",
     "include/grpcpp/security/auth_metadata_processor.h",
+    "include/grpcpp/security/auth_metadata_processor_impl.h",
     "include/grpcpp/security/credentials.h",
+    "include/grpcpp/security/credentials_impl.h",
     "include/grpcpp/security/server_credentials.h",
+    "include/grpcpp/security/server_credentials_impl.h",
     "include/grpcpp/server.h",
+    "include/grpcpp/server_impl.h",
     "include/grpcpp/server_builder.h",
+    "include/grpcpp/server_builder_impl.h",
     "include/grpcpp/server_context.h",
     "include/grpcpp/server_posix.h",
     "include/grpcpp/server_posix_impl.h",
@@ -256,10 +267,12 @@ GRPCXX_PUBLIC_HDRS = [
     "include/grpcpp/support/async_unary_call.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_interceptor.h",
     "include/grpcpp/support/config.h",
     "include/grpcpp/support/interceptor.h",
+    "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",
@@ -310,7 +323,6 @@ grpc_cc_library(
     public_hdrs = GRPC_PUBLIC_HDRS + GRPC_SECURE_PUBLIC_HDRS,
     standalone = True,
     deps = [
-        "grpc_cfstream",
         "grpc_common",
         "grpc_lb_policy_grpclb_secure",
         "grpc_lb_policy_xds_secure",
@@ -367,7 +379,6 @@ grpc_cc_library(
         "grpc++_codegen_base",
         "grpc++_codegen_base_src",
         "grpc++_codegen_proto",
-        "grpc_cfstream",
     ],
 )
 
@@ -398,6 +409,7 @@ grpc_cc_library(
     hdrs = [
         "include/grpc++/support/error_details.h",
         "include/grpcpp/support/error_details.h",
+        "include/grpcpp/support/error_details_impl.h",
     ],
     language = "c++",
     standalone = True,
@@ -422,6 +434,7 @@ grpc_cc_library(
         "src/compiler/config.h",
         "src/compiler/cpp_generator.h",
         "src/compiler/cpp_generator_helpers.h",
+        "src/compiler/cpp_plugin.h",
         "src/compiler/csharp_generator.h",
         "src/compiler/csharp_generator_helpers.h",
         "src/compiler/generator_helpers.h",
@@ -519,10 +532,20 @@ grpc_cc_library(
 )
 
 grpc_cc_library(
+    name = "grpc++_internal_hdrs_only",
+    hdrs = [
+        "include/grpcpp/impl/codegen/sync.h",
+    ],
+    language = "c++",
+    deps = [
+        "gpr_codegen",
+    ],
+)
+
+grpc_cc_library(
     name = "gpr_base",
     srcs = [
         "src/core/lib/gpr/alloc.cc",
-        "src/core/lib/gpr/arena.cc",
         "src/core/lib/gpr/atm.cc",
         "src/core/lib/gpr/cpu_iphone.cc",
         "src/core/lib/gpr/cpu_linux.cc",
@@ -555,7 +578,9 @@ grpc_cc_library(
         "src/core/lib/gpr/tmpfile_posix.cc",
         "src/core/lib/gpr/tmpfile_windows.cc",
         "src/core/lib/gpr/wrap_memcpy.cc",
+        "src/core/lib/gprpp/arena.cc",
         "src/core/lib/gprpp/fork.cc",
+        "src/core/lib/gprpp/global_config_env.cc",
         "src/core/lib/gprpp/thd_posix.cc",
         "src/core/lib/gprpp/thd_windows.cc",
         "src/core/lib/profiling/basic_timers.cc",
@@ -579,10 +604,18 @@ grpc_cc_library(
         "src/core/lib/gpr/tmpfile.h",
         "src/core/lib/gpr/useful.h",
         "src/core/lib/gprpp/abstract.h",
+        "src/core/lib/gprpp/arena.h",
+        "src/core/lib/gprpp/atomic.h",
         "src/core/lib/gprpp/fork.h",
+        "src/core/lib/gprpp/global_config_custom.h",
+        "src/core/lib/gprpp/global_config_env.h",
+        "src/core/lib/gprpp/global_config_generic.h",
+        "src/core/lib/gprpp/global_config.h",
         "src/core/lib/gprpp/manual_constructor.h",
+        "src/core/lib/gprpp/map.h",
         "src/core/lib/gprpp/memory.h",
-        "src/core/lib/gprpp/mutex_lock.h",
+        "src/core/lib/gprpp/pair.h",
+        "src/core/lib/gprpp/sync.h",
         "src/core/lib/gprpp/thd.h",
         "src/core/lib/profiling/timers.h",
     ],
@@ -716,6 +749,7 @@ grpc_cc_library(
         "src/core/lib/channel/handshaker_registry.cc",
         "src/core/lib/channel/status_util.cc",
         "src/core/lib/compression/compression.cc",
+        "src/core/lib/compression/compression_args.cc",
         "src/core/lib/compression/compression_internal.cc",
         "src/core/lib/compression/message_compress.cc",
         "src/core/lib/compression/stream_compression.cc",
@@ -728,12 +762,15 @@ grpc_cc_library(
         "src/core/lib/http/parser.cc",
         "src/core/lib/iomgr/buffer_list.cc",
         "src/core/lib/iomgr/call_combiner.cc",
+        "src/core/lib/iomgr/cfstream_handle.cc",
         "src/core/lib/iomgr/combiner.cc",
         "src/core/lib/iomgr/endpoint.cc",
+        "src/core/lib/iomgr/endpoint_cfstream.cc",
         "src/core/lib/iomgr/endpoint_pair_posix.cc",
         "src/core/lib/iomgr/endpoint_pair_uv.cc",
         "src/core/lib/iomgr/endpoint_pair_windows.cc",
         "src/core/lib/iomgr/error.cc",
+        "src/core/lib/iomgr/error_cfstream.cc",
         "src/core/lib/iomgr/ev_epoll1_linux.cc",
         "src/core/lib/iomgr/ev_epollex_linux.cc",
         "src/core/lib/iomgr/ev_poll_posix.cc",
@@ -754,6 +791,7 @@ grpc_cc_library(
         "src/core/lib/iomgr/iomgr_custom.cc",
         "src/core/lib/iomgr/iomgr_internal.cc",
         "src/core/lib/iomgr/iomgr_posix.cc",
+        "src/core/lib/iomgr/iomgr_posix_cfstream.cc",
         "src/core/lib/iomgr/iomgr_windows.cc",
         "src/core/lib/iomgr/is_epollexclusive_available.cc",
         "src/core/lib/iomgr/load_file.cc",
@@ -780,6 +818,7 @@ grpc_cc_library(
         "src/core/lib/iomgr/socket_utils_windows.cc",
         "src/core/lib/iomgr/socket_windows.cc",
         "src/core/lib/iomgr/tcp_client.cc",
+        "src/core/lib/iomgr/tcp_client_cfstream.cc",
         "src/core/lib/iomgr/tcp_client_custom.cc",
         "src/core/lib/iomgr/tcp_client_posix.cc",
         "src/core/lib/iomgr/tcp_client_windows.cc",
@@ -866,6 +905,7 @@ grpc_cc_library(
         "src/core/lib/channel/handshaker_registry.h",
         "src/core/lib/channel/status_util.h",
         "src/core/lib/compression/algorithm_metadata.h",
+        "src/core/lib/compression/compression_args.h",
         "src/core/lib/compression/compression_internal.h",
         "src/core/lib/compression/message_compress.h",
         "src/core/lib/compression/stream_compression.h",
@@ -879,12 +919,15 @@ grpc_cc_library(
         "src/core/lib/iomgr/block_annotate.h",
         "src/core/lib/iomgr/buffer_list.h",
         "src/core/lib/iomgr/call_combiner.h",
+        "src/core/lib/iomgr/cfstream_handle.h",
         "src/core/lib/iomgr/closure.h",
         "src/core/lib/iomgr/combiner.h",
         "src/core/lib/iomgr/dynamic_annotations.h",
         "src/core/lib/iomgr/endpoint.h",
+        "src/core/lib/iomgr/endpoint_cfstream.h",
         "src/core/lib/iomgr/endpoint_pair.h",
         "src/core/lib/iomgr/error.h",
+        "src/core/lib/iomgr/error_cfstream.h",
         "src/core/lib/iomgr/error_internal.h",
         "src/core/lib/iomgr/ev_epoll1_linux.h",
         "src/core/lib/iomgr/ev_epollex_linux.h",
@@ -1040,27 +1083,6 @@ grpc_cc_library(
 )
 
 grpc_cc_library(
-    name = "grpc_cfstream",
-    srcs = [
-        "src/core/lib/iomgr/cfstream_handle.cc",
-        "src/core/lib/iomgr/endpoint_cfstream.cc",
-        "src/core/lib/iomgr/error_cfstream.cc",
-        "src/core/lib/iomgr/iomgr_posix_cfstream.cc",
-        "src/core/lib/iomgr/tcp_client_cfstream.cc",
-    ],
-    hdrs = [
-        "src/core/lib/iomgr/cfstream_handle.h",
-        "src/core/lib/iomgr/endpoint_cfstream.h",
-        "src/core/lib/iomgr/error_cfstream.h",
-    ],
-    use_cfstream = True,
-    deps = [
-        ":gpr_base",
-        ":grpc_base",
-    ],
-)
-
-grpc_cc_library(
     name = "grpc_client_channel",
     srcs = [
         "src/core/ext/filters/client_channel/backup_poller.cc",
@@ -1479,6 +1501,7 @@ grpc_cc_library(
     language = "c++",
     public_hdrs = [
         "include/grpcpp/ext/server_load_reporting.h",
+        "include/grpcpp/ext/server_load_reporting_impl.h",
     ],
     deps = [
         "lb_server_load_reporting_filter",
@@ -1539,6 +1562,20 @@ grpc_cc_library(
 )
 
 grpc_cc_library(
+    name = "grpc_resolver_dns_selection",
+    srcs = [
+        "src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc",
+    ],
+    hdrs = [
+        "src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+    ],
+)
+
+grpc_cc_library(
     name = "grpc_resolver_dns_native",
     srcs = [
         "src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc",
@@ -1547,6 +1584,7 @@ grpc_cc_library(
     deps = [
         "grpc_base",
         "grpc_client_channel",
+        "grpc_resolver_dns_selection",
     ],
 )
 
@@ -1555,10 +1593,12 @@ grpc_cc_library(
     srcs = [
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc",
+        "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc",
+        "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc",
     ],
@@ -1574,6 +1614,7 @@ grpc_cc_library(
     deps = [
         "grpc_base",
         "grpc_client_channel",
+        "grpc_resolver_dns_selection",
     ],
 )
 
@@ -2120,6 +2161,7 @@ grpc_cc_library(
         "include/grpcpp/impl/codegen/client_interceptor.h",
         "include/grpcpp/impl/codegen/client_unary_call.h",
         "include/grpcpp/impl/codegen/completion_queue.h",
+        "include/grpcpp/impl/codegen/completion_queue_impl.h",
         "include/grpcpp/impl/codegen/completion_queue_tag.h",
         "include/grpcpp/impl/codegen/config.h",
         "include/grpcpp/impl/codegen/core_codegen_interface.h",
@@ -2128,6 +2170,7 @@ grpc_cc_library(
         "include/grpcpp/impl/codegen/intercepted_channel.h",
         "include/grpcpp/impl/codegen/interceptor.h",
         "include/grpcpp/impl/codegen/interceptor_common.h",
+        "include/grpcpp/impl/codegen/message_allocator.h",
         "include/grpcpp/impl/codegen/metadata_map.h",
         "include/grpcpp/impl/codegen/method_handler_impl.h",
         "include/grpcpp/impl/codegen/rpc_method.h",
@@ -2148,6 +2191,7 @@ grpc_cc_library(
         "include/grpcpp/impl/codegen/time.h",
     ],
     deps = [
+        "grpc++_internal_hdrs_only",
         "grpc_codegen",
     ],
 )
@@ -2203,6 +2247,7 @@ grpc_cc_library(
     public_hdrs = [
         "include/grpc++/ext/proto_server_reflection_plugin.h",
         "include/grpcpp/ext/proto_server_reflection_plugin.h",
+        "include/grpcpp/ext/proto_server_reflection_plugin_impl.h",
     ],
     deps = [
         ":grpc++",
@@ -2319,6 +2364,28 @@ grpc_cc_library(
 
 #TODO: Get this into build.yaml once we start using it.
 grpc_cc_library(
+    name = "envoy_lrs_upb",
+    srcs = [
+        "src/core/ext/upb-generated/envoy/api/v2/endpoint/load_report.upb.c",
+        "src/core/ext/upb-generated/envoy/service/load_stats/v2/lrs.upb.c",
+    ],
+    hdrs = [
+        "src/core/ext/upb-generated/envoy/api/v2/endpoint/load_report.upb.h",
+        "src/core/ext/upb-generated/envoy/service/load_stats/v2/lrs.upb.h",
+    ],
+    language = "c++",
+    external_deps = [
+        "upb_lib",
+    ],
+    deps = [
+        ":envoy_core_upb",
+        ":google_api_upb",
+        ":proto_gen_validate_upb",
+    ],
+    tags = ["no_windows"],
+)
+
+grpc_cc_library(
     name = "envoy_ads_upb",
     srcs = [
         "src/core/ext/upb-generated/envoy/api/v2/auth/cert.upb.c",
@@ -2350,6 +2417,7 @@ grpc_cc_library(
         ":google_api_upb",
         ":proto_gen_validate_upb",
     ],
+    tags = ["no_windows"],
 )
 
 grpc_cc_library(
@@ -2457,3 +2525,11 @@ grpc_cc_library(
 )
 
 grpc_generate_one_off_targets()
+
+filegroup(
+    name = "root_certificates",
+    srcs = [
+        "etc/roots.pem",
+    ],
+    visibility = ["//visibility:public"],
+)
index e566a01..d3c1186 100644 (file)
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -131,7 +131,6 @@ config("grpc_config") {
         "include/grpc/support/time.h",
         "src/core/lib/gpr/alloc.cc",
         "src/core/lib/gpr/alloc.h",
-        "src/core/lib/gpr/arena.cc",
         "src/core/lib/gpr/arena.h",
         "src/core/lib/gpr/atm.cc",
         "src/core/lib/gpr/cpu_iphone.cc",
@@ -180,12 +179,21 @@ config("grpc_config") {
         "src/core/lib/gpr/useful.h",
         "src/core/lib/gpr/wrap_memcpy.cc",
         "src/core/lib/gprpp/abstract.h",
+        "src/core/lib/gprpp/arena.cc",
+        "src/core/lib/gprpp/arena.h",
         "src/core/lib/gprpp/atomic.h",
         "src/core/lib/gprpp/fork.cc",
         "src/core/lib/gprpp/fork.h",
+        "src/core/lib/gprpp/global_config.h",
+        "src/core/lib/gprpp/global_config_custom.h",
+        "src/core/lib/gprpp/global_config_env.cc",
+        "src/core/lib/gprpp/global_config_env.h",
+        "src/core/lib/gprpp/global_config_generic.h",
         "src/core/lib/gprpp/manual_constructor.h",
+        "src/core/lib/gprpp/map.h",
         "src/core/lib/gprpp/memory.h",
-        "src/core/lib/gprpp/mutex_lock.h",
+        "src/core/lib/gprpp/pair.h",
+        "src/core/lib/gprpp/sync.h",
         "src/core/lib/gprpp/thd.h",
         "src/core/lib/gprpp/thd_posix.cc",
         "src/core/lib/gprpp/thd_windows.cc",
@@ -307,13 +315,17 @@ config("grpc_config") {
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h",
+        "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc",
+        "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc",
+        "src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc",
+        "src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h",
         "src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc",
         "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc",
         "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h",
@@ -443,6 +455,8 @@ config("grpc_config") {
         "src/core/lib/channel/status_util.h",
         "src/core/lib/compression/algorithm_metadata.h",
         "src/core/lib/compression/compression.cc",
+        "src/core/lib/compression/compression_args.cc",
+        "src/core/lib/compression/compression_args.h",
         "src/core/lib/compression/compression_internal.cc",
         "src/core/lib/compression/compression_internal.h",
         "src/core/lib/compression/message_compress.cc",
@@ -477,18 +491,24 @@ config("grpc_config") {
         "src/core/lib/iomgr/buffer_list.h",
         "src/core/lib/iomgr/call_combiner.cc",
         "src/core/lib/iomgr/call_combiner.h",
+        "src/core/lib/iomgr/cfstream_handle.cc",
+        "src/core/lib/iomgr/cfstream_handle.h",
         "src/core/lib/iomgr/closure.h",
         "src/core/lib/iomgr/combiner.cc",
         "src/core/lib/iomgr/combiner.h",
         "src/core/lib/iomgr/dynamic_annotations.h",
         "src/core/lib/iomgr/endpoint.cc",
         "src/core/lib/iomgr/endpoint.h",
+        "src/core/lib/iomgr/endpoint_cfstream.cc",
+        "src/core/lib/iomgr/endpoint_cfstream.h",
         "src/core/lib/iomgr/endpoint_pair.h",
         "src/core/lib/iomgr/endpoint_pair_posix.cc",
         "src/core/lib/iomgr/endpoint_pair_uv.cc",
         "src/core/lib/iomgr/endpoint_pair_windows.cc",
         "src/core/lib/iomgr/error.cc",
         "src/core/lib/iomgr/error.h",
+        "src/core/lib/iomgr/error_cfstream.cc",
+        "src/core/lib/iomgr/error_cfstream.h",
         "src/core/lib/iomgr/error_internal.h",
         "src/core/lib/iomgr/ev_epoll1_linux.cc",
         "src/core/lib/iomgr/ev_epoll1_linux.h",
@@ -524,6 +544,7 @@ config("grpc_config") {
         "src/core/lib/iomgr/iomgr_internal.h",
         "src/core/lib/iomgr/iomgr_posix.cc",
         "src/core/lib/iomgr/iomgr_posix.h",
+        "src/core/lib/iomgr/iomgr_posix_cfstream.cc",
         "src/core/lib/iomgr/iomgr_uv.cc",
         "src/core/lib/iomgr/iomgr_windows.cc",
         "src/core/lib/iomgr/is_epollexclusive_available.cc",
@@ -579,6 +600,7 @@ config("grpc_config") {
         "src/core/lib/iomgr/sys_epoll_wrapper.h",
         "src/core/lib/iomgr/tcp_client.cc",
         "src/core/lib/iomgr/tcp_client.h",
+        "src/core/lib/iomgr/tcp_client_cfstream.cc",
         "src/core/lib/iomgr/tcp_client_custom.cc",
         "src/core/lib/iomgr/tcp_client_posix.cc",
         "src/core/lib/iomgr/tcp_client_posix.h",
@@ -1001,16 +1023,20 @@ config("grpc_config") {
         "include/grpcpp/alarm.h",
         "include/grpcpp/alarm_impl.h",
         "include/grpcpp/channel.h",
+        "include/grpcpp/channel_impl.h",
         "include/grpcpp/client_context.h",
         "include/grpcpp/completion_queue.h",
         "include/grpcpp/create_channel.h",
+        "include/grpcpp/create_channel_impl.h",
         "include/grpcpp/create_channel_posix.h",
         "include/grpcpp/create_channel_posix_impl.h",
         "include/grpcpp/ext/health_check_service_server_builder_option.h",
         "include/grpcpp/generic/async_generic_service.h",
         "include/grpcpp/generic/generic_stub.h",
+        "include/grpcpp/generic/generic_stub_impl.h",
         "include/grpcpp/grpcpp.h",
         "include/grpcpp/health_check_service_interface.h",
+        "include/grpcpp/health_check_service_interface_impl.h",
         "include/grpcpp/impl/call.h",
         "include/grpcpp/impl/channel_argument_option.h",
         "include/grpcpp/impl/client_unary_call.h",
@@ -1029,6 +1055,7 @@ config("grpc_config") {
         "include/grpcpp/impl/codegen/client_interceptor.h",
         "include/grpcpp/impl/codegen/client_unary_call.h",
         "include/grpcpp/impl/codegen/completion_queue.h",
+        "include/grpcpp/impl/codegen/completion_queue_impl.h",
         "include/grpcpp/impl/codegen/completion_queue_tag.h",
         "include/grpcpp/impl/codegen/config.h",
         "include/grpcpp/impl/codegen/config_protobuf.h",
@@ -1040,6 +1067,7 @@ config("grpc_config") {
         "include/grpcpp/impl/codegen/intercepted_channel.h",
         "include/grpcpp/impl/codegen/interceptor.h",
         "include/grpcpp/impl/codegen/interceptor_common.h",
+        "include/grpcpp/impl/codegen/message_allocator.h",
         "include/grpcpp/impl/codegen/metadata_map.h",
         "include/grpcpp/impl/codegen/method_handler_impl.h",
         "include/grpcpp/impl/codegen/proto_buffer_reader.h",
@@ -1059,6 +1087,7 @@ config("grpc_config") {
         "include/grpcpp/impl/codegen/status_code_enum.h",
         "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/time.h",
         "include/grpcpp/impl/grpc_library.h",
@@ -1070,25 +1099,34 @@ config("grpc_config") {
         "include/grpcpp/impl/server_builder_option_impl.h",
         "include/grpcpp/impl/server_builder_plugin.h",
         "include/grpcpp/impl/server_initializer.h",
+        "include/grpcpp/impl/server_initializer_impl.h",
         "include/grpcpp/impl/service_type.h",
         "include/grpcpp/resource_quota.h",
+        "include/grpcpp/resource_quota_impl.h",
         "include/grpcpp/security/auth_context.h",
         "include/grpcpp/security/auth_metadata_processor.h",
+        "include/grpcpp/security/auth_metadata_processor_impl.h",
         "include/grpcpp/security/credentials.h",
+        "include/grpcpp/security/credentials_impl.h",
         "include/grpcpp/security/server_credentials.h",
+        "include/grpcpp/security/server_credentials_impl.h",
         "include/grpcpp/server.h",
         "include/grpcpp/server_builder.h",
+        "include/grpcpp/server_builder_impl.h",
         "include/grpcpp/server_context.h",
+        "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_unary_call.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_interceptor.h",
         "include/grpcpp/support/config.h",
         "include/grpcpp/support/interceptor.h",
+        "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",
@@ -1116,6 +1154,7 @@ config("grpc_config") {
         "src/core/lib/channel/handshaker_registry.h",
         "src/core/lib/channel/status_util.h",
         "src/core/lib/compression/algorithm_metadata.h",
+        "src/core/lib/compression/compression_args.h",
         "src/core/lib/compression/compression_internal.h",
         "src/core/lib/compression/message_compress.h",
         "src/core/lib/compression/stream_compression.h",
@@ -1141,17 +1180,24 @@ config("grpc_config") {
         "src/core/lib/gpr/tmpfile.h",
         "src/core/lib/gpr/useful.h",
         "src/core/lib/gprpp/abstract.h",
+        "src/core/lib/gprpp/arena.h",
         "src/core/lib/gprpp/atomic.h",
         "src/core/lib/gprpp/debug_location.h",
         "src/core/lib/gprpp/fork.h",
+        "src/core/lib/gprpp/global_config.h",
+        "src/core/lib/gprpp/global_config_custom.h",
+        "src/core/lib/gprpp/global_config_env.h",
+        "src/core/lib/gprpp/global_config_generic.h",
         "src/core/lib/gprpp/inlined_vector.h",
         "src/core/lib/gprpp/manual_constructor.h",
+        "src/core/lib/gprpp/map.h",
         "src/core/lib/gprpp/memory.h",
-        "src/core/lib/gprpp/mutex_lock.h",
         "src/core/lib/gprpp/optional.h",
         "src/core/lib/gprpp/orphanable.h",
+        "src/core/lib/gprpp/pair.h",
         "src/core/lib/gprpp/ref_counted.h",
         "src/core/lib/gprpp/ref_counted_ptr.h",
+        "src/core/lib/gprpp/sync.h",
         "src/core/lib/gprpp/thd.h",
         "src/core/lib/http/format_request.h",
         "src/core/lib/http/httpcli.h",
@@ -1159,12 +1205,15 @@ config("grpc_config") {
         "src/core/lib/iomgr/block_annotate.h",
         "src/core/lib/iomgr/buffer_list.h",
         "src/core/lib/iomgr/call_combiner.h",
+        "src/core/lib/iomgr/cfstream_handle.h",
         "src/core/lib/iomgr/closure.h",
         "src/core/lib/iomgr/combiner.h",
         "src/core/lib/iomgr/dynamic_annotations.h",
         "src/core/lib/iomgr/endpoint.h",
+        "src/core/lib/iomgr/endpoint_cfstream.h",
         "src/core/lib/iomgr/endpoint_pair.h",
         "src/core/lib/iomgr/error.h",
+        "src/core/lib/iomgr/error_cfstream.h",
         "src/core/lib/iomgr/error_internal.h",
         "src/core/lib/iomgr/ev_epoll1_linux.h",
         "src/core/lib/iomgr/ev_epollex_linux.h",
index e820737..dd3b5e3 100644 (file)
@@ -24,7 +24,7 @@
 cmake_minimum_required(VERSION 2.8)
 
 set(PACKAGE_NAME      "grpc")
-set(PACKAGE_VERSION   "1.20.1")
+set(PACKAGE_VERSION   "1.21.0")
 set(PACKAGE_STRING    "${PACKAGE_NAME} ${PACKAGE_VERSION}")
 set(PACKAGE_TARNAME   "${PACKAGE_NAME}-${PACKAGE_VERSION}")
 set(PACKAGE_BUGREPORT "https://github.com/grpc/grpc/issues/")
@@ -484,6 +484,7 @@ add_dependencies(buildtests_c h2_sockpair+trace_test)
 add_dependencies(buildtests_c h2_sockpair_1byte_test)
 add_dependencies(buildtests_c h2_spiffe_test)
 add_dependencies(buildtests_c h2_ssl_test)
+add_dependencies(buildtests_c h2_ssl_cred_reload_test)
 add_dependencies(buildtests_c h2_ssl_proxy_test)
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_c h2_uds_test)
@@ -628,9 +629,12 @@ add_dependencies(buildtests_cxx error_details_test)
 add_dependencies(buildtests_cxx exception_test)
 add_dependencies(buildtests_cxx filter_end2end_test)
 add_dependencies(buildtests_cxx generic_end2end_test)
+add_dependencies(buildtests_cxx global_config_env_test)
+add_dependencies(buildtests_cxx global_config_test)
 add_dependencies(buildtests_cxx golden_file_test)
 add_dependencies(buildtests_cxx grpc_alts_credentials_options_test)
 add_dependencies(buildtests_cxx grpc_cli)
+add_dependencies(buildtests_cxx grpc_core_map_test)
 add_dependencies(buildtests_cxx grpc_linux_system_roots_test)
 add_dependencies(buildtests_cxx grpc_tool_test)
 add_dependencies(buildtests_cxx grpclb_api_test)
@@ -659,6 +663,7 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_cxx json_run_localhost)
 endif()
 add_dependencies(buildtests_cxx memory_test)
+add_dependencies(buildtests_cxx message_allocator_end2end_test)
 add_dependencies(buildtests_cxx metrics_client)
 add_dependencies(buildtests_cxx mock_test)
 add_dependencies(buildtests_cxx nonblocking_test)
@@ -698,6 +703,8 @@ add_dependencies(buildtests_cxx server_crash_test_client)
 add_dependencies(buildtests_cxx server_early_return_test)
 add_dependencies(buildtests_cxx server_interceptors_end2end_test)
 add_dependencies(buildtests_cxx server_request_call_test)
+add_dependencies(buildtests_cxx service_config_end2end_test)
+add_dependencies(buildtests_cxx service_config_test)
 add_dependencies(buildtests_cxx shutdown_test)
 add_dependencies(buildtests_cxx slice_hash_table_test)
 add_dependencies(buildtests_cxx slice_weak_hash_table_test)
@@ -835,7 +842,6 @@ endif (gRPC_BUILD_TESTS)
 
 add_library(gpr
   src/core/lib/gpr/alloc.cc
-  src/core/lib/gpr/arena.cc
   src/core/lib/gpr/atm.cc
   src/core/lib/gpr/cpu_iphone.cc
   src/core/lib/gpr/cpu_linux.cc
@@ -868,7 +874,9 @@ add_library(gpr
   src/core/lib/gpr/tmpfile_posix.cc
   src/core/lib/gpr/tmpfile_windows.cc
   src/core/lib/gpr/wrap_memcpy.cc
+  src/core/lib/gprpp/arena.cc
   src/core/lib/gprpp/fork.cc
+  src/core/lib/gprpp/global_config_env.cc
   src/core/lib/gprpp/thd_posix.cc
   src/core/lib/gprpp/thd_windows.cc
   src/core/lib/profiling/basic_timers.cc
@@ -980,6 +988,7 @@ add_library(grpc
   src/core/lib/channel/handshaker_registry.cc
   src/core/lib/channel/status_util.cc
   src/core/lib/compression/compression.cc
+  src/core/lib/compression/compression_args.cc
   src/core/lib/compression/compression_internal.cc
   src/core/lib/compression/message_compress.cc
   src/core/lib/compression/stream_compression.cc
@@ -992,12 +1001,15 @@ add_library(grpc
   src/core/lib/http/parser.cc
   src/core/lib/iomgr/buffer_list.cc
   src/core/lib/iomgr/call_combiner.cc
+  src/core/lib/iomgr/cfstream_handle.cc
   src/core/lib/iomgr/combiner.cc
   src/core/lib/iomgr/endpoint.cc
+  src/core/lib/iomgr/endpoint_cfstream.cc
   src/core/lib/iomgr/endpoint_pair_posix.cc
   src/core/lib/iomgr/endpoint_pair_uv.cc
   src/core/lib/iomgr/endpoint_pair_windows.cc
   src/core/lib/iomgr/error.cc
+  src/core/lib/iomgr/error_cfstream.cc
   src/core/lib/iomgr/ev_epoll1_linux.cc
   src/core/lib/iomgr/ev_epollex_linux.cc
   src/core/lib/iomgr/ev_poll_posix.cc
@@ -1018,6 +1030,7 @@ add_library(grpc
   src/core/lib/iomgr/iomgr_custom.cc
   src/core/lib/iomgr/iomgr_internal.cc
   src/core/lib/iomgr/iomgr_posix.cc
+  src/core/lib/iomgr/iomgr_posix_cfstream.cc
   src/core/lib/iomgr/iomgr_uv.cc
   src/core/lib/iomgr/iomgr_windows.cc
   src/core/lib/iomgr/is_epollexclusive_available.cc
@@ -1046,6 +1059,7 @@ add_library(grpc
   src/core/lib/iomgr/socket_utils_windows.cc
   src/core/lib/iomgr/socket_windows.cc
   src/core/lib/iomgr/tcp_client.cc
+  src/core/lib/iomgr/tcp_client_cfstream.cc
   src/core/lib/iomgr/tcp_client_custom.cc
   src/core/lib/iomgr/tcp_client_posix.cc
   src/core/lib/iomgr/tcp_client_windows.cc
@@ -1279,12 +1293,15 @@ add_library(grpc
   src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc
+  src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc
+  src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc
+  src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc
   src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc
   src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc
   src/core/ext/filters/census/grpc_context.cc
@@ -1406,6 +1423,7 @@ add_library(grpc_cronet
   src/core/lib/channel/handshaker_registry.cc
   src/core/lib/channel/status_util.cc
   src/core/lib/compression/compression.cc
+  src/core/lib/compression/compression_args.cc
   src/core/lib/compression/compression_internal.cc
   src/core/lib/compression/message_compress.cc
   src/core/lib/compression/stream_compression.cc
@@ -1418,12 +1436,15 @@ add_library(grpc_cronet
   src/core/lib/http/parser.cc
   src/core/lib/iomgr/buffer_list.cc
   src/core/lib/iomgr/call_combiner.cc
+  src/core/lib/iomgr/cfstream_handle.cc
   src/core/lib/iomgr/combiner.cc
   src/core/lib/iomgr/endpoint.cc
+  src/core/lib/iomgr/endpoint_cfstream.cc
   src/core/lib/iomgr/endpoint_pair_posix.cc
   src/core/lib/iomgr/endpoint_pair_uv.cc
   src/core/lib/iomgr/endpoint_pair_windows.cc
   src/core/lib/iomgr/error.cc
+  src/core/lib/iomgr/error_cfstream.cc
   src/core/lib/iomgr/ev_epoll1_linux.cc
   src/core/lib/iomgr/ev_epollex_linux.cc
   src/core/lib/iomgr/ev_poll_posix.cc
@@ -1444,6 +1465,7 @@ add_library(grpc_cronet
   src/core/lib/iomgr/iomgr_custom.cc
   src/core/lib/iomgr/iomgr_internal.cc
   src/core/lib/iomgr/iomgr_posix.cc
+  src/core/lib/iomgr/iomgr_posix_cfstream.cc
   src/core/lib/iomgr/iomgr_uv.cc
   src/core/lib/iomgr/iomgr_windows.cc
   src/core/lib/iomgr/is_epollexclusive_available.cc
@@ -1472,6 +1494,7 @@ add_library(grpc_cronet
   src/core/lib/iomgr/socket_utils_windows.cc
   src/core/lib/iomgr/socket_windows.cc
   src/core/lib/iomgr/tcp_client.cc
+  src/core/lib/iomgr/tcp_client_cfstream.cc
   src/core/lib/iomgr/tcp_client_custom.cc
   src/core/lib/iomgr/tcp_client_posix.cc
   src/core/lib/iomgr/tcp_client_windows.cc
@@ -1817,6 +1840,7 @@ add_library(grpc_test_util
   src/core/lib/channel/handshaker_registry.cc
   src/core/lib/channel/status_util.cc
   src/core/lib/compression/compression.cc
+  src/core/lib/compression/compression_args.cc
   src/core/lib/compression/compression_internal.cc
   src/core/lib/compression/message_compress.cc
   src/core/lib/compression/stream_compression.cc
@@ -1829,12 +1853,15 @@ add_library(grpc_test_util
   src/core/lib/http/parser.cc
   src/core/lib/iomgr/buffer_list.cc
   src/core/lib/iomgr/call_combiner.cc
+  src/core/lib/iomgr/cfstream_handle.cc
   src/core/lib/iomgr/combiner.cc
   src/core/lib/iomgr/endpoint.cc
+  src/core/lib/iomgr/endpoint_cfstream.cc
   src/core/lib/iomgr/endpoint_pair_posix.cc
   src/core/lib/iomgr/endpoint_pair_uv.cc
   src/core/lib/iomgr/endpoint_pair_windows.cc
   src/core/lib/iomgr/error.cc
+  src/core/lib/iomgr/error_cfstream.cc
   src/core/lib/iomgr/ev_epoll1_linux.cc
   src/core/lib/iomgr/ev_epollex_linux.cc
   src/core/lib/iomgr/ev_poll_posix.cc
@@ -1855,6 +1882,7 @@ add_library(grpc_test_util
   src/core/lib/iomgr/iomgr_custom.cc
   src/core/lib/iomgr/iomgr_internal.cc
   src/core/lib/iomgr/iomgr_posix.cc
+  src/core/lib/iomgr/iomgr_posix_cfstream.cc
   src/core/lib/iomgr/iomgr_uv.cc
   src/core/lib/iomgr/iomgr_windows.cc
   src/core/lib/iomgr/is_epollexclusive_available.cc
@@ -1883,6 +1911,7 @@ add_library(grpc_test_util
   src/core/lib/iomgr/socket_utils_windows.cc
   src/core/lib/iomgr/socket_windows.cc
   src/core/lib/iomgr/tcp_client.cc
+  src/core/lib/iomgr/tcp_client_cfstream.cc
   src/core/lib/iomgr/tcp_client_custom.cc
   src/core/lib/iomgr/tcp_client_posix.cc
   src/core/lib/iomgr/tcp_client_windows.cc
@@ -2141,6 +2170,7 @@ add_library(grpc_test_util_unsecure
   src/core/lib/channel/handshaker_registry.cc
   src/core/lib/channel/status_util.cc
   src/core/lib/compression/compression.cc
+  src/core/lib/compression/compression_args.cc
   src/core/lib/compression/compression_internal.cc
   src/core/lib/compression/message_compress.cc
   src/core/lib/compression/stream_compression.cc
@@ -2153,12 +2183,15 @@ add_library(grpc_test_util_unsecure
   src/core/lib/http/parser.cc
   src/core/lib/iomgr/buffer_list.cc
   src/core/lib/iomgr/call_combiner.cc
+  src/core/lib/iomgr/cfstream_handle.cc
   src/core/lib/iomgr/combiner.cc
   src/core/lib/iomgr/endpoint.cc
+  src/core/lib/iomgr/endpoint_cfstream.cc
   src/core/lib/iomgr/endpoint_pair_posix.cc
   src/core/lib/iomgr/endpoint_pair_uv.cc
   src/core/lib/iomgr/endpoint_pair_windows.cc
   src/core/lib/iomgr/error.cc
+  src/core/lib/iomgr/error_cfstream.cc
   src/core/lib/iomgr/ev_epoll1_linux.cc
   src/core/lib/iomgr/ev_epollex_linux.cc
   src/core/lib/iomgr/ev_poll_posix.cc
@@ -2179,6 +2212,7 @@ add_library(grpc_test_util_unsecure
   src/core/lib/iomgr/iomgr_custom.cc
   src/core/lib/iomgr/iomgr_internal.cc
   src/core/lib/iomgr/iomgr_posix.cc
+  src/core/lib/iomgr/iomgr_posix_cfstream.cc
   src/core/lib/iomgr/iomgr_uv.cc
   src/core/lib/iomgr/iomgr_windows.cc
   src/core/lib/iomgr/is_epollexclusive_available.cc
@@ -2207,6 +2241,7 @@ add_library(grpc_test_util_unsecure
   src/core/lib/iomgr/socket_utils_windows.cc
   src/core/lib/iomgr/socket_windows.cc
   src/core/lib/iomgr/tcp_client.cc
+  src/core/lib/iomgr/tcp_client_cfstream.cc
   src/core/lib/iomgr/tcp_client_custom.cc
   src/core/lib/iomgr/tcp_client_posix.cc
   src/core/lib/iomgr/tcp_client_windows.cc
@@ -2441,6 +2476,7 @@ add_library(grpc_unsecure
   src/core/lib/channel/handshaker_registry.cc
   src/core/lib/channel/status_util.cc
   src/core/lib/compression/compression.cc
+  src/core/lib/compression/compression_args.cc
   src/core/lib/compression/compression_internal.cc
   src/core/lib/compression/message_compress.cc
   src/core/lib/compression/stream_compression.cc
@@ -2453,12 +2489,15 @@ add_library(grpc_unsecure
   src/core/lib/http/parser.cc
   src/core/lib/iomgr/buffer_list.cc
   src/core/lib/iomgr/call_combiner.cc
+  src/core/lib/iomgr/cfstream_handle.cc
   src/core/lib/iomgr/combiner.cc
   src/core/lib/iomgr/endpoint.cc
+  src/core/lib/iomgr/endpoint_cfstream.cc
   src/core/lib/iomgr/endpoint_pair_posix.cc
   src/core/lib/iomgr/endpoint_pair_uv.cc
   src/core/lib/iomgr/endpoint_pair_windows.cc
   src/core/lib/iomgr/error.cc
+  src/core/lib/iomgr/error_cfstream.cc
   src/core/lib/iomgr/ev_epoll1_linux.cc
   src/core/lib/iomgr/ev_epollex_linux.cc
   src/core/lib/iomgr/ev_poll_posix.cc
@@ -2479,6 +2518,7 @@ add_library(grpc_unsecure
   src/core/lib/iomgr/iomgr_custom.cc
   src/core/lib/iomgr/iomgr_internal.cc
   src/core/lib/iomgr/iomgr_posix.cc
+  src/core/lib/iomgr/iomgr_posix_cfstream.cc
   src/core/lib/iomgr/iomgr_uv.cc
   src/core/lib/iomgr/iomgr_windows.cc
   src/core/lib/iomgr/is_epollexclusive_available.cc
@@ -2507,6 +2547,7 @@ add_library(grpc_unsecure
   src/core/lib/iomgr/socket_utils_windows.cc
   src/core/lib/iomgr/socket_windows.cc
   src/core/lib/iomgr/tcp_client.cc
+  src/core/lib/iomgr/tcp_client_cfstream.cc
   src/core/lib/iomgr/tcp_client_custom.cc
   src/core/lib/iomgr/tcp_client_posix.cc
   src/core/lib/iomgr/tcp_client_windows.cc
@@ -2648,12 +2689,15 @@ add_library(grpc_unsecure
   src/core/ext/transport/inproc/inproc_transport.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc
+  src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc
+  src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc
+  src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc
   src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc
   src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc
   src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc
@@ -2863,6 +2907,49 @@ target_link_libraries(test_tcp_server
 
 
 endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_library(dns_test_util
+  test/cpp/naming/dns_test_util.cc
+)
+
+if(WIN32 AND MSVC)
+  set_target_properties(dns_test_util PROPERTIES COMPILE_PDB_NAME "dns_test_util"
+    COMPILE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
+  )
+  if (gRPC_INSTALL)
+    install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dns_test_util.pdb
+      DESTINATION ${gRPC_INSTALL_LIBDIR} OPTIONAL
+    )
+  endif()
+endif()
+
+
+target_include_directories(dns_test_util
+  PUBLIC $<INSTALL_INTERFACE:${gRPC_INSTALL_INCLUDEDIR}> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
+  PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+target_link_libraries(dns_test_util
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+
+endif (gRPC_BUILD_TESTS)
 
 add_library(grpc++
   src/cpp/client/insecure_credentials.cc
@@ -2996,16 +3083,20 @@ foreach(_hdr
   include/grpcpp/alarm.h
   include/grpcpp/alarm_impl.h
   include/grpcpp/channel.h
+  include/grpcpp/channel_impl.h
   include/grpcpp/client_context.h
   include/grpcpp/completion_queue.h
   include/grpcpp/create_channel.h
+  include/grpcpp/create_channel_impl.h
   include/grpcpp/create_channel_posix.h
   include/grpcpp/create_channel_posix_impl.h
   include/grpcpp/ext/health_check_service_server_builder_option.h
   include/grpcpp/generic/async_generic_service.h
   include/grpcpp/generic/generic_stub.h
+  include/grpcpp/generic/generic_stub_impl.h
   include/grpcpp/grpcpp.h
   include/grpcpp/health_check_service_interface.h
+  include/grpcpp/health_check_service_interface_impl.h
   include/grpcpp/impl/call.h
   include/grpcpp/impl/channel_argument_option.h
   include/grpcpp/impl/client_unary_call.h
@@ -3019,25 +3110,34 @@ foreach(_hdr
   include/grpcpp/impl/server_builder_option_impl.h
   include/grpcpp/impl/server_builder_plugin.h
   include/grpcpp/impl/server_initializer.h
+  include/grpcpp/impl/server_initializer_impl.h
   include/grpcpp/impl/service_type.h
   include/grpcpp/resource_quota.h
+  include/grpcpp/resource_quota_impl.h
   include/grpcpp/security/auth_context.h
   include/grpcpp/security/auth_metadata_processor.h
+  include/grpcpp/security/auth_metadata_processor_impl.h
   include/grpcpp/security/credentials.h
+  include/grpcpp/security/credentials_impl.h
   include/grpcpp/security/server_credentials.h
+  include/grpcpp/security/server_credentials_impl.h
   include/grpcpp/server.h
   include/grpcpp/server_builder.h
+  include/grpcpp/server_builder_impl.h
   include/grpcpp/server_context.h
+  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_unary_call.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_interceptor.h
   include/grpcpp/support/config.h
   include/grpcpp/support/interceptor.h
+  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
@@ -3145,6 +3245,7 @@ foreach(_hdr
   include/grpcpp/impl/codegen/client_interceptor.h
   include/grpcpp/impl/codegen/client_unary_call.h
   include/grpcpp/impl/codegen/completion_queue.h
+  include/grpcpp/impl/codegen/completion_queue_impl.h
   include/grpcpp/impl/codegen/completion_queue_tag.h
   include/grpcpp/impl/codegen/config.h
   include/grpcpp/impl/codegen/core_codegen_interface.h
@@ -3153,6 +3254,7 @@ foreach(_hdr
   include/grpcpp/impl/codegen/intercepted_channel.h
   include/grpcpp/impl/codegen/interceptor.h
   include/grpcpp/impl/codegen/interceptor_common.h
+  include/grpcpp/impl/codegen/message_allocator.h
   include/grpcpp/impl/codegen/metadata_map.h
   include/grpcpp/impl/codegen/method_handler_impl.h
   include/grpcpp/impl/codegen/rpc_method.h
@@ -3171,6 +3273,7 @@ foreach(_hdr
   include/grpcpp/impl/codegen/stub_options.h
   include/grpcpp/impl/codegen/sync_stream.h
   include/grpcpp/impl/codegen/time.h
+  include/grpcpp/impl/codegen/sync.h
   include/grpc++/impl/codegen/proto_utils.h
   include/grpcpp/impl/codegen/proto_buffer_reader.h
   include/grpcpp/impl/codegen/proto_buffer_writer.h
@@ -3331,6 +3434,7 @@ add_library(grpc++_cronet
   src/core/lib/channel/handshaker_registry.cc
   src/core/lib/channel/status_util.cc
   src/core/lib/compression/compression.cc
+  src/core/lib/compression/compression_args.cc
   src/core/lib/compression/compression_internal.cc
   src/core/lib/compression/message_compress.cc
   src/core/lib/compression/stream_compression.cc
@@ -3343,12 +3447,15 @@ add_library(grpc++_cronet
   src/core/lib/http/parser.cc
   src/core/lib/iomgr/buffer_list.cc
   src/core/lib/iomgr/call_combiner.cc
+  src/core/lib/iomgr/cfstream_handle.cc
   src/core/lib/iomgr/combiner.cc
   src/core/lib/iomgr/endpoint.cc
+  src/core/lib/iomgr/endpoint_cfstream.cc
   src/core/lib/iomgr/endpoint_pair_posix.cc
   src/core/lib/iomgr/endpoint_pair_uv.cc
   src/core/lib/iomgr/endpoint_pair_windows.cc
   src/core/lib/iomgr/error.cc
+  src/core/lib/iomgr/error_cfstream.cc
   src/core/lib/iomgr/ev_epoll1_linux.cc
   src/core/lib/iomgr/ev_epollex_linux.cc
   src/core/lib/iomgr/ev_poll_posix.cc
@@ -3369,6 +3476,7 @@ add_library(grpc++_cronet
   src/core/lib/iomgr/iomgr_custom.cc
   src/core/lib/iomgr/iomgr_internal.cc
   src/core/lib/iomgr/iomgr_posix.cc
+  src/core/lib/iomgr/iomgr_posix_cfstream.cc
   src/core/lib/iomgr/iomgr_uv.cc
   src/core/lib/iomgr/iomgr_windows.cc
   src/core/lib/iomgr/is_epollexclusive_available.cc
@@ -3397,6 +3505,7 @@ add_library(grpc++_cronet
   src/core/lib/iomgr/socket_utils_windows.cc
   src/core/lib/iomgr/socket_windows.cc
   src/core/lib/iomgr/tcp_client.cc
+  src/core/lib/iomgr/tcp_client_cfstream.cc
   src/core/lib/iomgr/tcp_client_custom.cc
   src/core/lib/iomgr/tcp_client_posix.cc
   src/core/lib/iomgr/tcp_client_windows.cc
@@ -3590,16 +3699,20 @@ foreach(_hdr
   include/grpcpp/alarm.h
   include/grpcpp/alarm_impl.h
   include/grpcpp/channel.h
+  include/grpcpp/channel_impl.h
   include/grpcpp/client_context.h
   include/grpcpp/completion_queue.h
   include/grpcpp/create_channel.h
+  include/grpcpp/create_channel_impl.h
   include/grpcpp/create_channel_posix.h
   include/grpcpp/create_channel_posix_impl.h
   include/grpcpp/ext/health_check_service_server_builder_option.h
   include/grpcpp/generic/async_generic_service.h
   include/grpcpp/generic/generic_stub.h
+  include/grpcpp/generic/generic_stub_impl.h
   include/grpcpp/grpcpp.h
   include/grpcpp/health_check_service_interface.h
+  include/grpcpp/health_check_service_interface_impl.h
   include/grpcpp/impl/call.h
   include/grpcpp/impl/channel_argument_option.h
   include/grpcpp/impl/client_unary_call.h
@@ -3613,25 +3726,34 @@ foreach(_hdr
   include/grpcpp/impl/server_builder_option_impl.h
   include/grpcpp/impl/server_builder_plugin.h
   include/grpcpp/impl/server_initializer.h
+  include/grpcpp/impl/server_initializer_impl.h
   include/grpcpp/impl/service_type.h
   include/grpcpp/resource_quota.h
+  include/grpcpp/resource_quota_impl.h
   include/grpcpp/security/auth_context.h
   include/grpcpp/security/auth_metadata_processor.h
+  include/grpcpp/security/auth_metadata_processor_impl.h
   include/grpcpp/security/credentials.h
+  include/grpcpp/security/credentials_impl.h
   include/grpcpp/security/server_credentials.h
+  include/grpcpp/security/server_credentials_impl.h
   include/grpcpp/server.h
   include/grpcpp/server_builder.h
+  include/grpcpp/server_builder_impl.h
   include/grpcpp/server_context.h
+  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_unary_call.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_interceptor.h
   include/grpcpp/support/config.h
   include/grpcpp/support/interceptor.h
+  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
@@ -3739,6 +3861,7 @@ foreach(_hdr
   include/grpcpp/impl/codegen/client_interceptor.h
   include/grpcpp/impl/codegen/client_unary_call.h
   include/grpcpp/impl/codegen/completion_queue.h
+  include/grpcpp/impl/codegen/completion_queue_impl.h
   include/grpcpp/impl/codegen/completion_queue_tag.h
   include/grpcpp/impl/codegen/config.h
   include/grpcpp/impl/codegen/core_codegen_interface.h
@@ -3747,6 +3870,7 @@ foreach(_hdr
   include/grpcpp/impl/codegen/intercepted_channel.h
   include/grpcpp/impl/codegen/interceptor.h
   include/grpcpp/impl/codegen/interceptor_common.h
+  include/grpcpp/impl/codegen/message_allocator.h
   include/grpcpp/impl/codegen/metadata_map.h
   include/grpcpp/impl/codegen/method_handler_impl.h
   include/grpcpp/impl/codegen/rpc_method.h
@@ -3765,6 +3889,7 @@ foreach(_hdr
   include/grpcpp/impl/codegen/stub_options.h
   include/grpcpp/impl/codegen/sync_stream.h
   include/grpcpp/impl/codegen/time.h
+  include/grpcpp/impl/codegen/sync.h
   include/grpc/census.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
@@ -3831,6 +3956,7 @@ target_link_libraries(grpc++_error_details
 foreach(_hdr
   include/grpc++/support/error_details.h
   include/grpcpp/support/error_details.h
+  include/grpcpp/support/error_details_impl.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
   get_filename_component(_path ${_path} PATH)
@@ -3964,6 +4090,7 @@ target_link_libraries(grpc++_reflection
 foreach(_hdr
   include/grpc++/ext/proto_server_reflection_plugin.h
   include/grpcpp/ext/proto_server_reflection_plugin.h
+  include/grpcpp/ext/proto_server_reflection_plugin_impl.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
   get_filename_component(_path ${_path} PATH)
@@ -4169,6 +4296,7 @@ foreach(_hdr
   include/grpcpp/impl/codegen/client_interceptor.h
   include/grpcpp/impl/codegen/client_unary_call.h
   include/grpcpp/impl/codegen/completion_queue.h
+  include/grpcpp/impl/codegen/completion_queue_impl.h
   include/grpcpp/impl/codegen/completion_queue_tag.h
   include/grpcpp/impl/codegen/config.h
   include/grpcpp/impl/codegen/core_codegen_interface.h
@@ -4177,6 +4305,7 @@ foreach(_hdr
   include/grpcpp/impl/codegen/intercepted_channel.h
   include/grpcpp/impl/codegen/interceptor.h
   include/grpcpp/impl/codegen/interceptor_common.h
+  include/grpcpp/impl/codegen/message_allocator.h
   include/grpcpp/impl/codegen/metadata_map.h
   include/grpcpp/impl/codegen/method_handler_impl.h
   include/grpcpp/impl/codegen/rpc_method.h
@@ -4217,6 +4346,7 @@ foreach(_hdr
   include/grpc/impl/codegen/sync_generic.h
   include/grpc/impl/codegen/sync_posix.h
   include/grpc/impl/codegen/sync_windows.h
+  include/grpcpp/impl/codegen/sync.h
   include/grpc++/impl/codegen/proto_utils.h
   include/grpcpp/impl/codegen/proto_buffer_reader.h
   include/grpcpp/impl/codegen/proto_buffer_writer.h
@@ -4365,6 +4495,7 @@ foreach(_hdr
   include/grpcpp/impl/codegen/client_interceptor.h
   include/grpcpp/impl/codegen/client_unary_call.h
   include/grpcpp/impl/codegen/completion_queue.h
+  include/grpcpp/impl/codegen/completion_queue_impl.h
   include/grpcpp/impl/codegen/completion_queue_tag.h
   include/grpcpp/impl/codegen/config.h
   include/grpcpp/impl/codegen/core_codegen_interface.h
@@ -4373,6 +4504,7 @@ foreach(_hdr
   include/grpcpp/impl/codegen/intercepted_channel.h
   include/grpcpp/impl/codegen/interceptor.h
   include/grpcpp/impl/codegen/interceptor_common.h
+  include/grpcpp/impl/codegen/message_allocator.h
   include/grpcpp/impl/codegen/metadata_map.h
   include/grpcpp/impl/codegen/method_handler_impl.h
   include/grpcpp/impl/codegen/rpc_method.h
@@ -4413,6 +4545,7 @@ foreach(_hdr
   include/grpc/impl/codegen/sync_generic.h
   include/grpc/impl/codegen/sync_posix.h
   include/grpc/impl/codegen/sync_windows.h
+  include/grpcpp/impl/codegen/sync.h
   include/grpc++/impl/codegen/proto_utils.h
   include/grpcpp/impl/codegen/proto_buffer_reader.h
   include/grpcpp/impl/codegen/proto_buffer_writer.h
@@ -4556,16 +4689,20 @@ foreach(_hdr
   include/grpcpp/alarm.h
   include/grpcpp/alarm_impl.h
   include/grpcpp/channel.h
+  include/grpcpp/channel_impl.h
   include/grpcpp/client_context.h
   include/grpcpp/completion_queue.h
   include/grpcpp/create_channel.h
+  include/grpcpp/create_channel_impl.h
   include/grpcpp/create_channel_posix.h
   include/grpcpp/create_channel_posix_impl.h
   include/grpcpp/ext/health_check_service_server_builder_option.h
   include/grpcpp/generic/async_generic_service.h
   include/grpcpp/generic/generic_stub.h
+  include/grpcpp/generic/generic_stub_impl.h
   include/grpcpp/grpcpp.h
   include/grpcpp/health_check_service_interface.h
+  include/grpcpp/health_check_service_interface_impl.h
   include/grpcpp/impl/call.h
   include/grpcpp/impl/channel_argument_option.h
   include/grpcpp/impl/client_unary_call.h
@@ -4579,25 +4716,34 @@ foreach(_hdr
   include/grpcpp/impl/server_builder_option_impl.h
   include/grpcpp/impl/server_builder_plugin.h
   include/grpcpp/impl/server_initializer.h
+  include/grpcpp/impl/server_initializer_impl.h
   include/grpcpp/impl/service_type.h
   include/grpcpp/resource_quota.h
+  include/grpcpp/resource_quota_impl.h
   include/grpcpp/security/auth_context.h
   include/grpcpp/security/auth_metadata_processor.h
+  include/grpcpp/security/auth_metadata_processor_impl.h
   include/grpcpp/security/credentials.h
+  include/grpcpp/security/credentials_impl.h
   include/grpcpp/security/server_credentials.h
+  include/grpcpp/security/server_credentials_impl.h
   include/grpcpp/server.h
   include/grpcpp/server_builder.h
+  include/grpcpp/server_builder_impl.h
   include/grpcpp/server_context.h
+  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_unary_call.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_interceptor.h
   include/grpcpp/support/config.h
   include/grpcpp/support/interceptor.h
+  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
@@ -4705,6 +4851,7 @@ foreach(_hdr
   include/grpcpp/impl/codegen/client_interceptor.h
   include/grpcpp/impl/codegen/client_unary_call.h
   include/grpcpp/impl/codegen/completion_queue.h
+  include/grpcpp/impl/codegen/completion_queue_impl.h
   include/grpcpp/impl/codegen/completion_queue_tag.h
   include/grpcpp/impl/codegen/config.h
   include/grpcpp/impl/codegen/core_codegen_interface.h
@@ -4713,6 +4860,7 @@ foreach(_hdr
   include/grpcpp/impl/codegen/intercepted_channel.h
   include/grpcpp/impl/codegen/interceptor.h
   include/grpcpp/impl/codegen/interceptor_common.h
+  include/grpcpp/impl/codegen/message_allocator.h
   include/grpcpp/impl/codegen/metadata_map.h
   include/grpcpp/impl/codegen/method_handler_impl.h
   include/grpcpp/impl/codegen/rpc_method.h
@@ -4731,6 +4879,7 @@ foreach(_hdr
   include/grpcpp/impl/codegen/stub_options.h
   include/grpcpp/impl/codegen/sync_stream.h
   include/grpcpp/impl/codegen/time.h
+  include/grpcpp/impl/codegen/sync.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
   get_filename_component(_path ${_path} PATH)
@@ -12641,6 +12790,7 @@ target_include_directories(client_crash_test_server
 target_link_libraries(client_crash_test_server
   ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc++_test_config
   grpc++_test_util
   grpc_test_util
   grpc++
@@ -13345,6 +13495,80 @@ target_link_libraries(generic_end2end_test
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+add_executable(global_config_env_test
+  test/core/gprpp/global_config_env_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(global_config_env_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
+  PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(global_config_env_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  gpr
+  grpc_test_util_unsecure
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_executable(global_config_test
+  test/core/gprpp/global_config_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(global_config_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
+  PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(global_config_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  gpr
+  grpc_test_util_unsecure
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
 add_executable(golden_file_test
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/compiler_test.pb.cc
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/compiler_test.grpc.pb.cc
@@ -13380,6 +13604,7 @@ target_include_directories(golden_file_test
 target_link_libraries(golden_file_test
   ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc++_test_config
   grpc++
   grpc
   gpr
@@ -13466,6 +13691,45 @@ target_link_libraries(grpc_cli
 
 
 endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_executable(grpc_core_map_test
+  test/core/gprpp/map_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(grpc_core_map_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
+  PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(grpc_core_map_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc_test_util
+  grpc++
+  grpc
+  gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+
+endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_CODEGEN)
 
 add_executable(grpc_cpp_plugin
@@ -14425,6 +14689,46 @@ target_link_libraries(memory_test
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+add_executable(message_allocator_end2end_test
+  test/cpp/end2end/message_allocator_end2end_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(message_allocator_end2end_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
+  PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(message_allocator_end2end_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc++_test_util
+  grpc_test_util
+  grpc++
+  grpc
+  gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
 add_executable(metrics_client
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/metrics.pb.cc
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/metrics.grpc.pb.cc
@@ -15545,6 +15849,7 @@ target_include_directories(server_crash_test_client
 target_link_libraries(server_crash_test_client
   ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc++_test_config
   grpc++_test_util
   grpc_test_util
   grpc++
@@ -15692,6 +15997,85 @@ target_link_libraries(server_request_call_test
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+add_executable(service_config_end2end_test
+  test/cpp/end2end/service_config_end2end_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(service_config_end2end_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
+  PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(service_config_end2end_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc++_test_util
+  grpc_test_util
+  grpc++
+  grpc
+  gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_executable(service_config_test
+  test/core/client_channel/service_config_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(service_config_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
+  PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(service_config_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc_test_util
+  grpc++
+  grpc
+  gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
 add_executable(shutdown_test
   test/cpp/end2end/shutdown_test.cc
   third_party/googletest/googletest/src/gtest-all.cc
@@ -17567,6 +17951,41 @@ target_link_libraries(h2_ssl_test
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+add_executable(h2_ssl_cred_reload_test
+  test/core/end2end/fixtures/h2_ssl_cred_reload.cc
+)
+
+
+target_include_directories(h2_ssl_cred_reload_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
+  PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR}
+)
+
+target_link_libraries(h2_ssl_cred_reload_test
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  end2end_tests
+  grpc_test_util
+  grpc
+  gpr
+)
+
+  # avoid dependency on libstdc++
+  if (_gRPC_CORE_NOSTDCXX_FLAGS)
+    set_target_properties(h2_ssl_cred_reload_test PROPERTIES LINKER_LANGUAGE C)
+    target_compile_options(h2_ssl_cred_reload_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${_gRPC_CORE_NOSTDCXX_FLAGS}>)
+  endif()
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
 add_executable(h2_ssl_proxy_test
   test/core/end2end/fixtures/h2_ssl_proxy.cc
 )
@@ -18163,6 +18582,7 @@ target_include_directories(resolver_component_test_unsecure
 target_link_libraries(resolver_component_test_unsecure
   ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
+  dns_test_util
   grpc++_test_util_unsecure
   grpc_test_util_unsecure
   grpc++_unsecure
@@ -18204,6 +18624,7 @@ target_include_directories(resolver_component_test
 target_link_libraries(resolver_component_test
   ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
+  dns_test_util
   grpc++_test_util
   grpc_test_util
   grpc++
@@ -18413,6 +18834,7 @@ target_include_directories(cancel_ares_query_test
 target_link_libraries(cancel_ares_query_test
   ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
+  dns_test_util
   grpc++_test_util
   grpc_test_util
   grpc++
index 1d14d5e..862a901 100644 (file)
@@ -110,5 +110,16 @@ How to get your contributions merged smoothly and quickly.
 - Exceptions to the rules can be made if there's a compelling reason for doing
   so.
 
-
+## Obtaining Commit Access
+We grant Commit Access to contributors based on the following criteria:
+* Sustained contribution to the gRPC project.
+* Deep understanding of the areas contributed to, and good consideration of various reliability, usability and performance tradeoffs. 
+* Contributions demonstrate that obtaining Commit Access will significantly reduce friction for the contributors or others. 
+
+In addition to submitting PRs, a Contributor with Commit Access can:
+* Review PRs and merge once other checks and criteria pass.
+* Triage bugs and PRs and assign appropriate labels and reviewers. 
+
+### Obtaining Commit Access without Code Contributions 
+The [gRPC organization](https://github.com/grpc) is comprised of multiple repositories and commit access is usually restricted to one or more of these repositories. Some repositories such as the [grpc.github.io](https://github.com/grpc/grpc.github.io/) do not have code, but the same principle of sustained, high quality contributions, with a good understanding of the fundamentals, apply. 
 
index 53e70c9..67495f2 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -460,8 +460,8 @@ Q = @
 endif
 
 CORE_VERSION = 7.0.0
-CPP_VERSION = 1.20.1
-CSHARP_VERSION = 1.20.1
+CPP_VERSION = 1.21.0
+CSHARP_VERSION = 1.21.0
 
 CPPFLAGS_NO_ARCH += $(addprefix -I, $(INCLUDES)) $(addprefix -D, $(DEFINES))
 CPPFLAGS += $(CPPFLAGS_NO_ARCH) $(ARCH_FLAGS)
@@ -478,11 +478,11 @@ LDFLAGS += $(EXTRA_LDFLAGS)
 DEFINES += $(EXTRA_DEFINES)
 LDLIBS += $(EXTRA_LDLIBS)
 
-HOST_CPPFLAGS = $(CPPFLAGS)
-HOST_CFLAGS = $(CFLAGS)
-HOST_CXXFLAGS = $(CXXFLAGS)
-HOST_LDFLAGS = $(LDFLAGS)
-HOST_LDLIBS = $(LDLIBS)
+HOST_CPPFLAGS += $(CPPFLAGS)
+HOST_CFLAGS += $(CFLAGS)
+HOST_CXXFLAGS += $(CXXFLAGS)
+HOST_LDFLAGS += $(LDFLAGS)
+HOST_LDLIBS += $(LDLIBS)
 
 # These are automatically computed variables.
 # There shouldn't be any need to change anything from now on.
@@ -1206,9 +1206,12 @@ error_details_test: $(BINDIR)/$(CONFIG)/error_details_test
 exception_test: $(BINDIR)/$(CONFIG)/exception_test
 filter_end2end_test: $(BINDIR)/$(CONFIG)/filter_end2end_test
 generic_end2end_test: $(BINDIR)/$(CONFIG)/generic_end2end_test
+global_config_env_test: $(BINDIR)/$(CONFIG)/global_config_env_test
+global_config_test: $(BINDIR)/$(CONFIG)/global_config_test
 golden_file_test: $(BINDIR)/$(CONFIG)/golden_file_test
 grpc_alts_credentials_options_test: $(BINDIR)/$(CONFIG)/grpc_alts_credentials_options_test
 grpc_cli: $(BINDIR)/$(CONFIG)/grpc_cli
+grpc_core_map_test: $(BINDIR)/$(CONFIG)/grpc_core_map_test
 grpc_cpp_plugin: $(BINDIR)/$(CONFIG)/grpc_cpp_plugin
 grpc_csharp_plugin: $(BINDIR)/$(CONFIG)/grpc_csharp_plugin
 grpc_linux_system_roots_test: $(BINDIR)/$(CONFIG)/grpc_linux_system_roots_test
@@ -1232,6 +1235,7 @@ interop_server: $(BINDIR)/$(CONFIG)/interop_server
 interop_test: $(BINDIR)/$(CONFIG)/interop_test
 json_run_localhost: $(BINDIR)/$(CONFIG)/json_run_localhost
 memory_test: $(BINDIR)/$(CONFIG)/memory_test
+message_allocator_end2end_test: $(BINDIR)/$(CONFIG)/message_allocator_end2end_test
 metrics_client: $(BINDIR)/$(CONFIG)/metrics_client
 mock_test: $(BINDIR)/$(CONFIG)/mock_test
 nonblocking_test: $(BINDIR)/$(CONFIG)/nonblocking_test
@@ -1261,6 +1265,8 @@ server_crash_test_client: $(BINDIR)/$(CONFIG)/server_crash_test_client
 server_early_return_test: $(BINDIR)/$(CONFIG)/server_early_return_test
 server_interceptors_end2end_test: $(BINDIR)/$(CONFIG)/server_interceptors_end2end_test
 server_request_call_test: $(BINDIR)/$(CONFIG)/server_request_call_test
+service_config_end2end_test: $(BINDIR)/$(CONFIG)/service_config_end2end_test
+service_config_test: $(BINDIR)/$(CONFIG)/service_config_test
 shutdown_test: $(BINDIR)/$(CONFIG)/shutdown_test
 slice_hash_table_test: $(BINDIR)/$(CONFIG)/slice_hash_table_test
 slice_weak_hash_table_test: $(BINDIR)/$(CONFIG)/slice_weak_hash_table_test
@@ -1314,6 +1320,7 @@ h2_sockpair+trace_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_test
 h2_sockpair_1byte_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_test
 h2_spiffe_test: $(BINDIR)/$(CONFIG)/h2_spiffe_test
 h2_ssl_test: $(BINDIR)/$(CONFIG)/h2_ssl_test
+h2_ssl_cred_reload_test: $(BINDIR)/$(CONFIG)/h2_ssl_cred_reload_test
 h2_ssl_proxy_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_test
 h2_uds_test: $(BINDIR)/$(CONFIG)/h2_uds_test
 inproc_test: $(BINDIR)/$(CONFIG)/inproc_test
@@ -1405,9 +1412,9 @@ pc_cxx: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++.pc
 pc_cxx_unsecure: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++_unsecure.pc
 
 ifeq ($(EMBED_OPENSSL),true)
-privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++_core_stats.a $(LIBDIR)/$(CONFIG)/libgrpc++_proto_reflection_desc_db.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libhttp2_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libqps.a $(LIBDIR)/$(CONFIG)/libboringssl_test_util.a $(LIBDIR)/$(CONFIG)/libbenchmark.a
+privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libdns_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_core_stats.a $(LIBDIR)/$(CONFIG)/libgrpc++_proto_reflection_desc_db.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libhttp2_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libqps.a $(LIBDIR)/$(CONFIG)/libboringssl_test_util.a $(LIBDIR)/$(CONFIG)/libbenchmark.a
 else
-privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++_core_stats.a $(LIBDIR)/$(CONFIG)/libgrpc++_proto_reflection_desc_db.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libhttp2_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libqps.a $(LIBDIR)/$(CONFIG)/libbenchmark.a
+privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libdns_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_core_stats.a $(LIBDIR)/$(CONFIG)/libgrpc++_proto_reflection_desc_db.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libhttp2_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libqps.a $(LIBDIR)/$(CONFIG)/libbenchmark.a
 endif
 
 
@@ -1577,6 +1584,7 @@ buildtests_c: privatelibs_c \
   $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_test \
   $(BINDIR)/$(CONFIG)/h2_spiffe_test \
   $(BINDIR)/$(CONFIG)/h2_ssl_test \
+  $(BINDIR)/$(CONFIG)/h2_ssl_cred_reload_test \
   $(BINDIR)/$(CONFIG)/h2_ssl_proxy_test \
   $(BINDIR)/$(CONFIG)/h2_uds_test \
   $(BINDIR)/$(CONFIG)/inproc_test \
@@ -1677,9 +1685,12 @@ buildtests_cxx: privatelibs_cxx \
   $(BINDIR)/$(CONFIG)/exception_test \
   $(BINDIR)/$(CONFIG)/filter_end2end_test \
   $(BINDIR)/$(CONFIG)/generic_end2end_test \
+  $(BINDIR)/$(CONFIG)/global_config_env_test \
+  $(BINDIR)/$(CONFIG)/global_config_test \
   $(BINDIR)/$(CONFIG)/golden_file_test \
   $(BINDIR)/$(CONFIG)/grpc_alts_credentials_options_test \
   $(BINDIR)/$(CONFIG)/grpc_cli \
+  $(BINDIR)/$(CONFIG)/grpc_core_map_test \
   $(BINDIR)/$(CONFIG)/grpc_linux_system_roots_test \
   $(BINDIR)/$(CONFIG)/grpc_tool_test \
   $(BINDIR)/$(CONFIG)/grpclb_api_test \
@@ -1696,6 +1707,7 @@ buildtests_cxx: privatelibs_cxx \
   $(BINDIR)/$(CONFIG)/interop_test \
   $(BINDIR)/$(CONFIG)/json_run_localhost \
   $(BINDIR)/$(CONFIG)/memory_test \
+  $(BINDIR)/$(CONFIG)/message_allocator_end2end_test \
   $(BINDIR)/$(CONFIG)/metrics_client \
   $(BINDIR)/$(CONFIG)/mock_test \
   $(BINDIR)/$(CONFIG)/nonblocking_test \
@@ -1725,6 +1737,8 @@ buildtests_cxx: privatelibs_cxx \
   $(BINDIR)/$(CONFIG)/server_early_return_test \
   $(BINDIR)/$(CONFIG)/server_interceptors_end2end_test \
   $(BINDIR)/$(CONFIG)/server_request_call_test \
+  $(BINDIR)/$(CONFIG)/service_config_end2end_test \
+  $(BINDIR)/$(CONFIG)/service_config_test \
   $(BINDIR)/$(CONFIG)/shutdown_test \
   $(BINDIR)/$(CONFIG)/slice_hash_table_test \
   $(BINDIR)/$(CONFIG)/slice_weak_hash_table_test \
@@ -1818,9 +1832,12 @@ buildtests_cxx: privatelibs_cxx \
   $(BINDIR)/$(CONFIG)/exception_test \
   $(BINDIR)/$(CONFIG)/filter_end2end_test \
   $(BINDIR)/$(CONFIG)/generic_end2end_test \
+  $(BINDIR)/$(CONFIG)/global_config_env_test \
+  $(BINDIR)/$(CONFIG)/global_config_test \
   $(BINDIR)/$(CONFIG)/golden_file_test \
   $(BINDIR)/$(CONFIG)/grpc_alts_credentials_options_test \
   $(BINDIR)/$(CONFIG)/grpc_cli \
+  $(BINDIR)/$(CONFIG)/grpc_core_map_test \
   $(BINDIR)/$(CONFIG)/grpc_linux_system_roots_test \
   $(BINDIR)/$(CONFIG)/grpc_tool_test \
   $(BINDIR)/$(CONFIG)/grpclb_api_test \
@@ -1837,6 +1854,7 @@ buildtests_cxx: privatelibs_cxx \
   $(BINDIR)/$(CONFIG)/interop_test \
   $(BINDIR)/$(CONFIG)/json_run_localhost \
   $(BINDIR)/$(CONFIG)/memory_test \
+  $(BINDIR)/$(CONFIG)/message_allocator_end2end_test \
   $(BINDIR)/$(CONFIG)/metrics_client \
   $(BINDIR)/$(CONFIG)/mock_test \
   $(BINDIR)/$(CONFIG)/nonblocking_test \
@@ -1866,6 +1884,8 @@ buildtests_cxx: privatelibs_cxx \
   $(BINDIR)/$(CONFIG)/server_early_return_test \
   $(BINDIR)/$(CONFIG)/server_interceptors_end2end_test \
   $(BINDIR)/$(CONFIG)/server_request_call_test \
+  $(BINDIR)/$(CONFIG)/service_config_end2end_test \
+  $(BINDIR)/$(CONFIG)/service_config_test \
   $(BINDIR)/$(CONFIG)/shutdown_test \
   $(BINDIR)/$(CONFIG)/slice_hash_table_test \
   $(BINDIR)/$(CONFIG)/slice_weak_hash_table_test \
@@ -2307,10 +2327,16 @@ test_cxx: buildtests_cxx
        $(Q) $(BINDIR)/$(CONFIG)/filter_end2end_test || ( echo test filter_end2end_test failed ; exit 1 )
        $(E) "[RUN]     Testing generic_end2end_test"
        $(Q) $(BINDIR)/$(CONFIG)/generic_end2end_test || ( echo test generic_end2end_test failed ; exit 1 )
+       $(E) "[RUN]     Testing global_config_env_test"
+       $(Q) $(BINDIR)/$(CONFIG)/global_config_env_test || ( echo test global_config_env_test failed ; exit 1 )
+       $(E) "[RUN]     Testing global_config_test"
+       $(Q) $(BINDIR)/$(CONFIG)/global_config_test || ( echo test global_config_test failed ; exit 1 )
        $(E) "[RUN]     Testing golden_file_test"
        $(Q) $(BINDIR)/$(CONFIG)/golden_file_test || ( echo test golden_file_test failed ; exit 1 )
        $(E) "[RUN]     Testing grpc_alts_credentials_options_test"
        $(Q) $(BINDIR)/$(CONFIG)/grpc_alts_credentials_options_test || ( echo test grpc_alts_credentials_options_test failed ; exit 1 )
+       $(E) "[RUN]     Testing grpc_core_map_test"
+       $(Q) $(BINDIR)/$(CONFIG)/grpc_core_map_test || ( echo test grpc_core_map_test failed ; exit 1 )
        $(E) "[RUN]     Testing grpc_linux_system_roots_test"
        $(Q) $(BINDIR)/$(CONFIG)/grpc_linux_system_roots_test || ( echo test grpc_linux_system_roots_test failed ; exit 1 )
        $(E) "[RUN]     Testing grpc_tool_test"
@@ -2333,6 +2359,8 @@ test_cxx: buildtests_cxx
        $(Q) $(BINDIR)/$(CONFIG)/interop_test || ( echo test interop_test failed ; exit 1 )
        $(E) "[RUN]     Testing memory_test"
        $(Q) $(BINDIR)/$(CONFIG)/memory_test || ( echo test memory_test failed ; exit 1 )
+       $(E) "[RUN]     Testing message_allocator_end2end_test"
+       $(Q) $(BINDIR)/$(CONFIG)/message_allocator_end2end_test || ( echo test message_allocator_end2end_test failed ; exit 1 )
        $(E) "[RUN]     Testing mock_test"
        $(Q) $(BINDIR)/$(CONFIG)/mock_test || ( echo test mock_test failed ; exit 1 )
        $(E) "[RUN]     Testing nonblocking_test"
@@ -2377,6 +2405,10 @@ test_cxx: buildtests_cxx
        $(Q) $(BINDIR)/$(CONFIG)/server_interceptors_end2end_test || ( echo test server_interceptors_end2end_test failed ; exit 1 )
        $(E) "[RUN]     Testing server_request_call_test"
        $(Q) $(BINDIR)/$(CONFIG)/server_request_call_test || ( echo test server_request_call_test failed ; exit 1 )
+       $(E) "[RUN]     Testing service_config_end2end_test"
+       $(Q) $(BINDIR)/$(CONFIG)/service_config_end2end_test || ( echo test service_config_end2end_test failed ; exit 1 )
+       $(E) "[RUN]     Testing service_config_test"
+       $(Q) $(BINDIR)/$(CONFIG)/service_config_test || ( echo test service_config_test failed ; exit 1 )
        $(E) "[RUN]     Testing shutdown_test"
        $(Q) $(BINDIR)/$(CONFIG)/shutdown_test || ( echo test shutdown_test failed ; exit 1 )
        $(E) "[RUN]     Testing slice_hash_table_test"
@@ -3303,7 +3335,6 @@ endif
 
 LIBGPR_SRC = \
     src/core/lib/gpr/alloc.cc \
-    src/core/lib/gpr/arena.cc \
     src/core/lib/gpr/atm.cc \
     src/core/lib/gpr/cpu_iphone.cc \
     src/core/lib/gpr/cpu_linux.cc \
@@ -3336,7 +3367,9 @@ LIBGPR_SRC = \
     src/core/lib/gpr/tmpfile_posix.cc \
     src/core/lib/gpr/tmpfile_windows.cc \
     src/core/lib/gpr/wrap_memcpy.cc \
+    src/core/lib/gprpp/arena.cc \
     src/core/lib/gprpp/fork.cc \
+    src/core/lib/gprpp/global_config_env.cc \
     src/core/lib/gprpp/thd_posix.cc \
     src/core/lib/gprpp/thd_windows.cc \
     src/core/lib/profiling/basic_timers.cc \
@@ -3427,6 +3460,7 @@ LIBGRPC_SRC = \
     src/core/lib/channel/handshaker_registry.cc \
     src/core/lib/channel/status_util.cc \
     src/core/lib/compression/compression.cc \
+    src/core/lib/compression/compression_args.cc \
     src/core/lib/compression/compression_internal.cc \
     src/core/lib/compression/message_compress.cc \
     src/core/lib/compression/stream_compression.cc \
@@ -3439,12 +3473,15 @@ LIBGRPC_SRC = \
     src/core/lib/http/parser.cc \
     src/core/lib/iomgr/buffer_list.cc \
     src/core/lib/iomgr/call_combiner.cc \
+    src/core/lib/iomgr/cfstream_handle.cc \
     src/core/lib/iomgr/combiner.cc \
     src/core/lib/iomgr/endpoint.cc \
+    src/core/lib/iomgr/endpoint_cfstream.cc \
     src/core/lib/iomgr/endpoint_pair_posix.cc \
     src/core/lib/iomgr/endpoint_pair_uv.cc \
     src/core/lib/iomgr/endpoint_pair_windows.cc \
     src/core/lib/iomgr/error.cc \
+    src/core/lib/iomgr/error_cfstream.cc \
     src/core/lib/iomgr/ev_epoll1_linux.cc \
     src/core/lib/iomgr/ev_epollex_linux.cc \
     src/core/lib/iomgr/ev_poll_posix.cc \
@@ -3465,6 +3502,7 @@ LIBGRPC_SRC = \
     src/core/lib/iomgr/iomgr_custom.cc \
     src/core/lib/iomgr/iomgr_internal.cc \
     src/core/lib/iomgr/iomgr_posix.cc \
+    src/core/lib/iomgr/iomgr_posix_cfstream.cc \
     src/core/lib/iomgr/iomgr_uv.cc \
     src/core/lib/iomgr/iomgr_windows.cc \
     src/core/lib/iomgr/is_epollexclusive_available.cc \
@@ -3493,6 +3531,7 @@ LIBGRPC_SRC = \
     src/core/lib/iomgr/socket_utils_windows.cc \
     src/core/lib/iomgr/socket_windows.cc \
     src/core/lib/iomgr/tcp_client.cc \
+    src/core/lib/iomgr/tcp_client_cfstream.cc \
     src/core/lib/iomgr/tcp_client_custom.cc \
     src/core/lib/iomgr/tcp_client_posix.cc \
     src/core/lib/iomgr/tcp_client_windows.cc \
@@ -3726,12 +3765,15 @@ LIBGRPC_SRC = \
     src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc \
+    src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc \
+    src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc \
+    src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc \
     src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc \
     src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc \
     src/core/ext/filters/census/grpc_context.cc \
@@ -3847,6 +3889,7 @@ LIBGRPC_CRONET_SRC = \
     src/core/lib/channel/handshaker_registry.cc \
     src/core/lib/channel/status_util.cc \
     src/core/lib/compression/compression.cc \
+    src/core/lib/compression/compression_args.cc \
     src/core/lib/compression/compression_internal.cc \
     src/core/lib/compression/message_compress.cc \
     src/core/lib/compression/stream_compression.cc \
@@ -3859,12 +3902,15 @@ LIBGRPC_CRONET_SRC = \
     src/core/lib/http/parser.cc \
     src/core/lib/iomgr/buffer_list.cc \
     src/core/lib/iomgr/call_combiner.cc \
+    src/core/lib/iomgr/cfstream_handle.cc \
     src/core/lib/iomgr/combiner.cc \
     src/core/lib/iomgr/endpoint.cc \
+    src/core/lib/iomgr/endpoint_cfstream.cc \
     src/core/lib/iomgr/endpoint_pair_posix.cc \
     src/core/lib/iomgr/endpoint_pair_uv.cc \
     src/core/lib/iomgr/endpoint_pair_windows.cc \
     src/core/lib/iomgr/error.cc \
+    src/core/lib/iomgr/error_cfstream.cc \
     src/core/lib/iomgr/ev_epoll1_linux.cc \
     src/core/lib/iomgr/ev_epollex_linux.cc \
     src/core/lib/iomgr/ev_poll_posix.cc \
@@ -3885,6 +3931,7 @@ LIBGRPC_CRONET_SRC = \
     src/core/lib/iomgr/iomgr_custom.cc \
     src/core/lib/iomgr/iomgr_internal.cc \
     src/core/lib/iomgr/iomgr_posix.cc \
+    src/core/lib/iomgr/iomgr_posix_cfstream.cc \
     src/core/lib/iomgr/iomgr_uv.cc \
     src/core/lib/iomgr/iomgr_windows.cc \
     src/core/lib/iomgr/is_epollexclusive_available.cc \
@@ -3913,6 +3960,7 @@ LIBGRPC_CRONET_SRC = \
     src/core/lib/iomgr/socket_utils_windows.cc \
     src/core/lib/iomgr/socket_windows.cc \
     src/core/lib/iomgr/tcp_client.cc \
+    src/core/lib/iomgr/tcp_client_cfstream.cc \
     src/core/lib/iomgr/tcp_client_custom.cc \
     src/core/lib/iomgr/tcp_client_posix.cc \
     src/core/lib/iomgr/tcp_client_windows.cc \
@@ -4251,6 +4299,7 @@ LIBGRPC_TEST_UTIL_SRC = \
     src/core/lib/channel/handshaker_registry.cc \
     src/core/lib/channel/status_util.cc \
     src/core/lib/compression/compression.cc \
+    src/core/lib/compression/compression_args.cc \
     src/core/lib/compression/compression_internal.cc \
     src/core/lib/compression/message_compress.cc \
     src/core/lib/compression/stream_compression.cc \
@@ -4263,12 +4312,15 @@ LIBGRPC_TEST_UTIL_SRC = \
     src/core/lib/http/parser.cc \
     src/core/lib/iomgr/buffer_list.cc \
     src/core/lib/iomgr/call_combiner.cc \
+    src/core/lib/iomgr/cfstream_handle.cc \
     src/core/lib/iomgr/combiner.cc \
     src/core/lib/iomgr/endpoint.cc \
+    src/core/lib/iomgr/endpoint_cfstream.cc \
     src/core/lib/iomgr/endpoint_pair_posix.cc \
     src/core/lib/iomgr/endpoint_pair_uv.cc \
     src/core/lib/iomgr/endpoint_pair_windows.cc \
     src/core/lib/iomgr/error.cc \
+    src/core/lib/iomgr/error_cfstream.cc \
     src/core/lib/iomgr/ev_epoll1_linux.cc \
     src/core/lib/iomgr/ev_epollex_linux.cc \
     src/core/lib/iomgr/ev_poll_posix.cc \
@@ -4289,6 +4341,7 @@ LIBGRPC_TEST_UTIL_SRC = \
     src/core/lib/iomgr/iomgr_custom.cc \
     src/core/lib/iomgr/iomgr_internal.cc \
     src/core/lib/iomgr/iomgr_posix.cc \
+    src/core/lib/iomgr/iomgr_posix_cfstream.cc \
     src/core/lib/iomgr/iomgr_uv.cc \
     src/core/lib/iomgr/iomgr_windows.cc \
     src/core/lib/iomgr/is_epollexclusive_available.cc \
@@ -4317,6 +4370,7 @@ LIBGRPC_TEST_UTIL_SRC = \
     src/core/lib/iomgr/socket_utils_windows.cc \
     src/core/lib/iomgr/socket_windows.cc \
     src/core/lib/iomgr/tcp_client.cc \
+    src/core/lib/iomgr/tcp_client_cfstream.cc \
     src/core/lib/iomgr/tcp_client_custom.cc \
     src/core/lib/iomgr/tcp_client_posix.cc \
     src/core/lib/iomgr/tcp_client_windows.cc \
@@ -4562,6 +4616,7 @@ LIBGRPC_TEST_UTIL_UNSECURE_SRC = \
     src/core/lib/channel/handshaker_registry.cc \
     src/core/lib/channel/status_util.cc \
     src/core/lib/compression/compression.cc \
+    src/core/lib/compression/compression_args.cc \
     src/core/lib/compression/compression_internal.cc \
     src/core/lib/compression/message_compress.cc \
     src/core/lib/compression/stream_compression.cc \
@@ -4574,12 +4629,15 @@ LIBGRPC_TEST_UTIL_UNSECURE_SRC = \
     src/core/lib/http/parser.cc \
     src/core/lib/iomgr/buffer_list.cc \
     src/core/lib/iomgr/call_combiner.cc \
+    src/core/lib/iomgr/cfstream_handle.cc \
     src/core/lib/iomgr/combiner.cc \
     src/core/lib/iomgr/endpoint.cc \
+    src/core/lib/iomgr/endpoint_cfstream.cc \
     src/core/lib/iomgr/endpoint_pair_posix.cc \
     src/core/lib/iomgr/endpoint_pair_uv.cc \
     src/core/lib/iomgr/endpoint_pair_windows.cc \
     src/core/lib/iomgr/error.cc \
+    src/core/lib/iomgr/error_cfstream.cc \
     src/core/lib/iomgr/ev_epoll1_linux.cc \
     src/core/lib/iomgr/ev_epollex_linux.cc \
     src/core/lib/iomgr/ev_poll_posix.cc \
@@ -4600,6 +4658,7 @@ LIBGRPC_TEST_UTIL_UNSECURE_SRC = \
     src/core/lib/iomgr/iomgr_custom.cc \
     src/core/lib/iomgr/iomgr_internal.cc \
     src/core/lib/iomgr/iomgr_posix.cc \
+    src/core/lib/iomgr/iomgr_posix_cfstream.cc \
     src/core/lib/iomgr/iomgr_uv.cc \
     src/core/lib/iomgr/iomgr_windows.cc \
     src/core/lib/iomgr/is_epollexclusive_available.cc \
@@ -4628,6 +4687,7 @@ LIBGRPC_TEST_UTIL_UNSECURE_SRC = \
     src/core/lib/iomgr/socket_utils_windows.cc \
     src/core/lib/iomgr/socket_windows.cc \
     src/core/lib/iomgr/tcp_client.cc \
+    src/core/lib/iomgr/tcp_client_cfstream.cc \
     src/core/lib/iomgr/tcp_client_custom.cc \
     src/core/lib/iomgr/tcp_client_posix.cc \
     src/core/lib/iomgr/tcp_client_windows.cc \
@@ -4836,6 +4896,7 @@ LIBGRPC_UNSECURE_SRC = \
     src/core/lib/channel/handshaker_registry.cc \
     src/core/lib/channel/status_util.cc \
     src/core/lib/compression/compression.cc \
+    src/core/lib/compression/compression_args.cc \
     src/core/lib/compression/compression_internal.cc \
     src/core/lib/compression/message_compress.cc \
     src/core/lib/compression/stream_compression.cc \
@@ -4848,12 +4909,15 @@ LIBGRPC_UNSECURE_SRC = \
     src/core/lib/http/parser.cc \
     src/core/lib/iomgr/buffer_list.cc \
     src/core/lib/iomgr/call_combiner.cc \
+    src/core/lib/iomgr/cfstream_handle.cc \
     src/core/lib/iomgr/combiner.cc \
     src/core/lib/iomgr/endpoint.cc \
+    src/core/lib/iomgr/endpoint_cfstream.cc \
     src/core/lib/iomgr/endpoint_pair_posix.cc \
     src/core/lib/iomgr/endpoint_pair_uv.cc \
     src/core/lib/iomgr/endpoint_pair_windows.cc \
     src/core/lib/iomgr/error.cc \
+    src/core/lib/iomgr/error_cfstream.cc \
     src/core/lib/iomgr/ev_epoll1_linux.cc \
     src/core/lib/iomgr/ev_epollex_linux.cc \
     src/core/lib/iomgr/ev_poll_posix.cc \
@@ -4874,6 +4938,7 @@ LIBGRPC_UNSECURE_SRC = \
     src/core/lib/iomgr/iomgr_custom.cc \
     src/core/lib/iomgr/iomgr_internal.cc \
     src/core/lib/iomgr/iomgr_posix.cc \
+    src/core/lib/iomgr/iomgr_posix_cfstream.cc \
     src/core/lib/iomgr/iomgr_uv.cc \
     src/core/lib/iomgr/iomgr_windows.cc \
     src/core/lib/iomgr/is_epollexclusive_available.cc \
@@ -4902,6 +4967,7 @@ LIBGRPC_UNSECURE_SRC = \
     src/core/lib/iomgr/socket_utils_windows.cc \
     src/core/lib/iomgr/socket_windows.cc \
     src/core/lib/iomgr/tcp_client.cc \
+    src/core/lib/iomgr/tcp_client_cfstream.cc \
     src/core/lib/iomgr/tcp_client_custom.cc \
     src/core/lib/iomgr/tcp_client_posix.cc \
     src/core/lib/iomgr/tcp_client_windows.cc \
@@ -5043,12 +5109,15 @@ LIBGRPC_UNSECURE_SRC = \
     src/core/ext/transport/inproc/inproc_transport.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc \
+    src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc \
+    src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc \
+    src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc \
     src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc \
     src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc \
     src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc \
@@ -5226,6 +5295,55 @@ endif
 endif
 
 
+LIBDNS_TEST_UTIL_SRC = \
+    test/cpp/naming/dns_test_util.cc \
+
+PUBLIC_HEADERS_CXX += \
+
+LIBDNS_TEST_UTIL_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBDNS_TEST_UTIL_SRC))))
+
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure libraries if you don't have OpenSSL.
+
+$(LIBDIR)/$(CONFIG)/libdns_test_util.a: openssl_dep_error
+
+
+else
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build a C++ library if you don't have protobuf - a bit overreached, but still okay.
+
+$(LIBDIR)/$(CONFIG)/libdns_test_util.a: protobuf_dep_error
+
+
+else
+
+$(LIBDIR)/$(CONFIG)/libdns_test_util.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(CARES_DEP) $(ADDRESS_SORTING_DEP) $(PROTOBUF_DEP) $(LIBDNS_TEST_UTIL_OBJS) 
+       $(E) "[AR]      Creating $@"
+       $(Q) mkdir -p `dirname $@`
+       $(Q) rm -f $(LIBDIR)/$(CONFIG)/libdns_test_util.a
+       $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libdns_test_util.a $(LIBDNS_TEST_UTIL_OBJS) 
+ifeq ($(SYSTEM),Darwin)
+       $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libdns_test_util.a
+endif
+
+
+
+
+endif
+
+endif
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(LIBDNS_TEST_UTIL_OBJS:.o=.dep)
+endif
+endif
+
+
 LIBGRPC++_SRC = \
     src/cpp/client/insecure_credentials.cc \
     src/cpp/client/secure_credentials.cc \
@@ -5323,16 +5441,20 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/alarm.h \
     include/grpcpp/alarm_impl.h \
     include/grpcpp/channel.h \
+    include/grpcpp/channel_impl.h \
     include/grpcpp/client_context.h \
     include/grpcpp/completion_queue.h \
     include/grpcpp/create_channel.h \
+    include/grpcpp/create_channel_impl.h \
     include/grpcpp/create_channel_posix.h \
     include/grpcpp/create_channel_posix_impl.h \
     include/grpcpp/ext/health_check_service_server_builder_option.h \
     include/grpcpp/generic/async_generic_service.h \
     include/grpcpp/generic/generic_stub.h \
+    include/grpcpp/generic/generic_stub_impl.h \
     include/grpcpp/grpcpp.h \
     include/grpcpp/health_check_service_interface.h \
+    include/grpcpp/health_check_service_interface_impl.h \
     include/grpcpp/impl/call.h \
     include/grpcpp/impl/channel_argument_option.h \
     include/grpcpp/impl/client_unary_call.h \
@@ -5346,25 +5468,34 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/impl/server_builder_option_impl.h \
     include/grpcpp/impl/server_builder_plugin.h \
     include/grpcpp/impl/server_initializer.h \
+    include/grpcpp/impl/server_initializer_impl.h \
     include/grpcpp/impl/service_type.h \
     include/grpcpp/resource_quota.h \
+    include/grpcpp/resource_quota_impl.h \
     include/grpcpp/security/auth_context.h \
     include/grpcpp/security/auth_metadata_processor.h \
+    include/grpcpp/security/auth_metadata_processor_impl.h \
     include/grpcpp/security/credentials.h \
+    include/grpcpp/security/credentials_impl.h \
     include/grpcpp/security/server_credentials.h \
+    include/grpcpp/security/server_credentials_impl.h \
     include/grpcpp/server.h \
     include/grpcpp/server_builder.h \
+    include/grpcpp/server_builder_impl.h \
     include/grpcpp/server_context.h \
+    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_unary_call.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_interceptor.h \
     include/grpcpp/support/config.h \
     include/grpcpp/support/interceptor.h \
+    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 \
@@ -5472,6 +5603,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/impl/codegen/client_interceptor.h \
     include/grpcpp/impl/codegen/client_unary_call.h \
     include/grpcpp/impl/codegen/completion_queue.h \
+    include/grpcpp/impl/codegen/completion_queue_impl.h \
     include/grpcpp/impl/codegen/completion_queue_tag.h \
     include/grpcpp/impl/codegen/config.h \
     include/grpcpp/impl/codegen/core_codegen_interface.h \
@@ -5480,6 +5612,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/impl/codegen/intercepted_channel.h \
     include/grpcpp/impl/codegen/interceptor.h \
     include/grpcpp/impl/codegen/interceptor_common.h \
+    include/grpcpp/impl/codegen/message_allocator.h \
     include/grpcpp/impl/codegen/metadata_map.h \
     include/grpcpp/impl/codegen/method_handler_impl.h \
     include/grpcpp/impl/codegen/rpc_method.h \
@@ -5498,6 +5631,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/impl/codegen/stub_options.h \
     include/grpcpp/impl/codegen/sync_stream.h \
     include/grpcpp/impl/codegen/time.h \
+    include/grpcpp/impl/codegen/sync.h \
     include/grpc++/impl/codegen/proto_utils.h \
     include/grpcpp/impl/codegen/proto_buffer_reader.h \
     include/grpcpp/impl/codegen/proto_buffer_writer.h \
@@ -5702,6 +5836,7 @@ LIBGRPC++_CRONET_SRC = \
     src/core/lib/channel/handshaker_registry.cc \
     src/core/lib/channel/status_util.cc \
     src/core/lib/compression/compression.cc \
+    src/core/lib/compression/compression_args.cc \
     src/core/lib/compression/compression_internal.cc \
     src/core/lib/compression/message_compress.cc \
     src/core/lib/compression/stream_compression.cc \
@@ -5714,12 +5849,15 @@ LIBGRPC++_CRONET_SRC = \
     src/core/lib/http/parser.cc \
     src/core/lib/iomgr/buffer_list.cc \
     src/core/lib/iomgr/call_combiner.cc \
+    src/core/lib/iomgr/cfstream_handle.cc \
     src/core/lib/iomgr/combiner.cc \
     src/core/lib/iomgr/endpoint.cc \
+    src/core/lib/iomgr/endpoint_cfstream.cc \
     src/core/lib/iomgr/endpoint_pair_posix.cc \
     src/core/lib/iomgr/endpoint_pair_uv.cc \
     src/core/lib/iomgr/endpoint_pair_windows.cc \
     src/core/lib/iomgr/error.cc \
+    src/core/lib/iomgr/error_cfstream.cc \
     src/core/lib/iomgr/ev_epoll1_linux.cc \
     src/core/lib/iomgr/ev_epollex_linux.cc \
     src/core/lib/iomgr/ev_poll_posix.cc \
@@ -5740,6 +5878,7 @@ LIBGRPC++_CRONET_SRC = \
     src/core/lib/iomgr/iomgr_custom.cc \
     src/core/lib/iomgr/iomgr_internal.cc \
     src/core/lib/iomgr/iomgr_posix.cc \
+    src/core/lib/iomgr/iomgr_posix_cfstream.cc \
     src/core/lib/iomgr/iomgr_uv.cc \
     src/core/lib/iomgr/iomgr_windows.cc \
     src/core/lib/iomgr/is_epollexclusive_available.cc \
@@ -5768,6 +5907,7 @@ LIBGRPC++_CRONET_SRC = \
     src/core/lib/iomgr/socket_utils_windows.cc \
     src/core/lib/iomgr/socket_windows.cc \
     src/core/lib/iomgr/tcp_client.cc \
+    src/core/lib/iomgr/tcp_client_cfstream.cc \
     src/core/lib/iomgr/tcp_client_custom.cc \
     src/core/lib/iomgr/tcp_client_posix.cc \
     src/core/lib/iomgr/tcp_client_windows.cc \
@@ -5925,16 +6065,20 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/alarm.h \
     include/grpcpp/alarm_impl.h \
     include/grpcpp/channel.h \
+    include/grpcpp/channel_impl.h \
     include/grpcpp/client_context.h \
     include/grpcpp/completion_queue.h \
     include/grpcpp/create_channel.h \
+    include/grpcpp/create_channel_impl.h \
     include/grpcpp/create_channel_posix.h \
     include/grpcpp/create_channel_posix_impl.h \
     include/grpcpp/ext/health_check_service_server_builder_option.h \
     include/grpcpp/generic/async_generic_service.h \
     include/grpcpp/generic/generic_stub.h \
+    include/grpcpp/generic/generic_stub_impl.h \
     include/grpcpp/grpcpp.h \
     include/grpcpp/health_check_service_interface.h \
+    include/grpcpp/health_check_service_interface_impl.h \
     include/grpcpp/impl/call.h \
     include/grpcpp/impl/channel_argument_option.h \
     include/grpcpp/impl/client_unary_call.h \
@@ -5948,25 +6092,34 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/impl/server_builder_option_impl.h \
     include/grpcpp/impl/server_builder_plugin.h \
     include/grpcpp/impl/server_initializer.h \
+    include/grpcpp/impl/server_initializer_impl.h \
     include/grpcpp/impl/service_type.h \
     include/grpcpp/resource_quota.h \
+    include/grpcpp/resource_quota_impl.h \
     include/grpcpp/security/auth_context.h \
     include/grpcpp/security/auth_metadata_processor.h \
+    include/grpcpp/security/auth_metadata_processor_impl.h \
     include/grpcpp/security/credentials.h \
+    include/grpcpp/security/credentials_impl.h \
     include/grpcpp/security/server_credentials.h \
+    include/grpcpp/security/server_credentials_impl.h \
     include/grpcpp/server.h \
     include/grpcpp/server_builder.h \
+    include/grpcpp/server_builder_impl.h \
     include/grpcpp/server_context.h \
+    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_unary_call.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_interceptor.h \
     include/grpcpp/support/config.h \
     include/grpcpp/support/interceptor.h \
+    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 \
@@ -6074,6 +6227,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/impl/codegen/client_interceptor.h \
     include/grpcpp/impl/codegen/client_unary_call.h \
     include/grpcpp/impl/codegen/completion_queue.h \
+    include/grpcpp/impl/codegen/completion_queue_impl.h \
     include/grpcpp/impl/codegen/completion_queue_tag.h \
     include/grpcpp/impl/codegen/config.h \
     include/grpcpp/impl/codegen/core_codegen_interface.h \
@@ -6082,6 +6236,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/impl/codegen/intercepted_channel.h \
     include/grpcpp/impl/codegen/interceptor.h \
     include/grpcpp/impl/codegen/interceptor_common.h \
+    include/grpcpp/impl/codegen/message_allocator.h \
     include/grpcpp/impl/codegen/metadata_map.h \
     include/grpcpp/impl/codegen/method_handler_impl.h \
     include/grpcpp/impl/codegen/rpc_method.h \
@@ -6100,6 +6255,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/impl/codegen/stub_options.h \
     include/grpcpp/impl/codegen/sync_stream.h \
     include/grpcpp/impl/codegen/time.h \
+    include/grpcpp/impl/codegen/sync.h \
     include/grpc/census.h \
 
 LIBGRPC++_CRONET_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_CRONET_SRC))))
@@ -6172,6 +6328,7 @@ LIBGRPC++_ERROR_DETAILS_SRC = \
 PUBLIC_HEADERS_CXX += \
     include/grpc++/support/error_details.h \
     include/grpcpp/support/error_details.h \
+    include/grpcpp/support/error_details_impl.h \
 
 LIBGRPC++_ERROR_DETAILS_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_ERROR_DETAILS_SRC))))
 
@@ -6298,6 +6455,7 @@ LIBGRPC++_REFLECTION_SRC = \
 PUBLIC_HEADERS_CXX += \
     include/grpc++/ext/proto_server_reflection_plugin.h \
     include/grpcpp/ext/proto_server_reflection_plugin.h \
+    include/grpcpp/ext/proto_server_reflection_plugin_impl.h \
 
 LIBGRPC++_REFLECTION_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_REFLECTION_SRC))))
 
@@ -6476,6 +6634,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/impl/codegen/client_interceptor.h \
     include/grpcpp/impl/codegen/client_unary_call.h \
     include/grpcpp/impl/codegen/completion_queue.h \
+    include/grpcpp/impl/codegen/completion_queue_impl.h \
     include/grpcpp/impl/codegen/completion_queue_tag.h \
     include/grpcpp/impl/codegen/config.h \
     include/grpcpp/impl/codegen/core_codegen_interface.h \
@@ -6484,6 +6643,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/impl/codegen/intercepted_channel.h \
     include/grpcpp/impl/codegen/interceptor.h \
     include/grpcpp/impl/codegen/interceptor_common.h \
+    include/grpcpp/impl/codegen/message_allocator.h \
     include/grpcpp/impl/codegen/metadata_map.h \
     include/grpcpp/impl/codegen/method_handler_impl.h \
     include/grpcpp/impl/codegen/rpc_method.h \
@@ -6524,6 +6684,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpc/impl/codegen/sync_generic.h \
     include/grpc/impl/codegen/sync_posix.h \
     include/grpc/impl/codegen/sync_windows.h \
+    include/grpcpp/impl/codegen/sync.h \
     include/grpc++/impl/codegen/proto_utils.h \
     include/grpcpp/impl/codegen/proto_buffer_reader.h \
     include/grpcpp/impl/codegen/proto_buffer_writer.h \
@@ -6643,6 +6804,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/impl/codegen/client_interceptor.h \
     include/grpcpp/impl/codegen/client_unary_call.h \
     include/grpcpp/impl/codegen/completion_queue.h \
+    include/grpcpp/impl/codegen/completion_queue_impl.h \
     include/grpcpp/impl/codegen/completion_queue_tag.h \
     include/grpcpp/impl/codegen/config.h \
     include/grpcpp/impl/codegen/core_codegen_interface.h \
@@ -6651,6 +6813,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/impl/codegen/intercepted_channel.h \
     include/grpcpp/impl/codegen/interceptor.h \
     include/grpcpp/impl/codegen/interceptor_common.h \
+    include/grpcpp/impl/codegen/message_allocator.h \
     include/grpcpp/impl/codegen/metadata_map.h \
     include/grpcpp/impl/codegen/method_handler_impl.h \
     include/grpcpp/impl/codegen/rpc_method.h \
@@ -6691,6 +6854,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpc/impl/codegen/sync_generic.h \
     include/grpc/impl/codegen/sync_posix.h \
     include/grpc/impl/codegen/sync_windows.h \
+    include/grpcpp/impl/codegen/sync.h \
     include/grpc++/impl/codegen/proto_utils.h \
     include/grpcpp/impl/codegen/proto_buffer_reader.h \
     include/grpcpp/impl/codegen/proto_buffer_writer.h \
@@ -6840,16 +7004,20 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/alarm.h \
     include/grpcpp/alarm_impl.h \
     include/grpcpp/channel.h \
+    include/grpcpp/channel_impl.h \
     include/grpcpp/client_context.h \
     include/grpcpp/completion_queue.h \
     include/grpcpp/create_channel.h \
+    include/grpcpp/create_channel_impl.h \
     include/grpcpp/create_channel_posix.h \
     include/grpcpp/create_channel_posix_impl.h \
     include/grpcpp/ext/health_check_service_server_builder_option.h \
     include/grpcpp/generic/async_generic_service.h \
     include/grpcpp/generic/generic_stub.h \
+    include/grpcpp/generic/generic_stub_impl.h \
     include/grpcpp/grpcpp.h \
     include/grpcpp/health_check_service_interface.h \
+    include/grpcpp/health_check_service_interface_impl.h \
     include/grpcpp/impl/call.h \
     include/grpcpp/impl/channel_argument_option.h \
     include/grpcpp/impl/client_unary_call.h \
@@ -6863,25 +7031,34 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/impl/server_builder_option_impl.h \
     include/grpcpp/impl/server_builder_plugin.h \
     include/grpcpp/impl/server_initializer.h \
+    include/grpcpp/impl/server_initializer_impl.h \
     include/grpcpp/impl/service_type.h \
     include/grpcpp/resource_quota.h \
+    include/grpcpp/resource_quota_impl.h \
     include/grpcpp/security/auth_context.h \
     include/grpcpp/security/auth_metadata_processor.h \
+    include/grpcpp/security/auth_metadata_processor_impl.h \
     include/grpcpp/security/credentials.h \
+    include/grpcpp/security/credentials_impl.h \
     include/grpcpp/security/server_credentials.h \
+    include/grpcpp/security/server_credentials_impl.h \
     include/grpcpp/server.h \
     include/grpcpp/server_builder.h \
+    include/grpcpp/server_builder_impl.h \
     include/grpcpp/server_context.h \
+    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_unary_call.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_interceptor.h \
     include/grpcpp/support/config.h \
     include/grpcpp/support/interceptor.h \
+    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 \
@@ -6989,6 +7166,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/impl/codegen/client_interceptor.h \
     include/grpcpp/impl/codegen/client_unary_call.h \
     include/grpcpp/impl/codegen/completion_queue.h \
+    include/grpcpp/impl/codegen/completion_queue_impl.h \
     include/grpcpp/impl/codegen/completion_queue_tag.h \
     include/grpcpp/impl/codegen/config.h \
     include/grpcpp/impl/codegen/core_codegen_interface.h \
@@ -6997,6 +7175,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/impl/codegen/intercepted_channel.h \
     include/grpcpp/impl/codegen/interceptor.h \
     include/grpcpp/impl/codegen/interceptor_common.h \
+    include/grpcpp/impl/codegen/message_allocator.h \
     include/grpcpp/impl/codegen/metadata_map.h \
     include/grpcpp/impl/codegen/method_handler_impl.h \
     include/grpcpp/impl/codegen/rpc_method.h \
@@ -7015,6 +7194,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpcpp/impl/codegen/stub_options.h \
     include/grpcpp/impl/codegen/sync_stream.h \
     include/grpcpp/impl/codegen/time.h \
+    include/grpcpp/impl/codegen/sync.h \
 
 LIBGRPC++_UNSECURE_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_UNSECURE_SRC))))
 
@@ -15575,16 +15755,16 @@ $(BINDIR)/$(CONFIG)/client_crash_test_server: protobuf_dep_error
 
 else
 
-$(BINDIR)/$(CONFIG)/client_crash_test_server: $(PROTOBUF_DEP) $(CLIENT_CRASH_TEST_SERVER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(BINDIR)/$(CONFIG)/client_crash_test_server: $(PROTOBUF_DEP) $(CLIENT_CRASH_TEST_SERVER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
        $(E) "[LD]      Linking $@"
        $(Q) mkdir -p `dirname $@`
-       $(Q) $(LDXX) $(LDFLAGS) $(CLIENT_CRASH_TEST_SERVER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/client_crash_test_server
+       $(Q) $(LDXX) $(LDFLAGS) $(CLIENT_CRASH_TEST_SERVER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/client_crash_test_server
 
 endif
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/cpp/end2end/client_crash_test_server.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(OBJDIR)/$(CONFIG)/test/cpp/end2end/client_crash_test_server.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
 deps_client_crash_test_server: $(CLIENT_CRASH_TEST_SERVER_OBJS:.o=.dep)
 
@@ -16298,6 +16478,92 @@ endif
 endif
 
 
+GLOBAL_CONFIG_ENV_TEST_SRC = \
+    test/core/gprpp/global_config_env_test.cc \
+
+GLOBAL_CONFIG_ENV_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GLOBAL_CONFIG_ENV_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/global_config_env_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/global_config_env_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/global_config_env_test: $(PROTOBUF_DEP) $(GLOBAL_CONFIG_ENV_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a
+       $(E) "[LD]      Linking $@"
+       $(Q) mkdir -p `dirname $@`
+       $(Q) $(LDXX) $(LDFLAGS) $(GLOBAL_CONFIG_ENV_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/global_config_env_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/gprpp/global_config_env_test.o:  $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a
+
+deps_global_config_env_test: $(GLOBAL_CONFIG_ENV_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(GLOBAL_CONFIG_ENV_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+GLOBAL_CONFIG_TEST_SRC = \
+    test/core/gprpp/global_config_test.cc \
+
+GLOBAL_CONFIG_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GLOBAL_CONFIG_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/global_config_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/global_config_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/global_config_test: $(PROTOBUF_DEP) $(GLOBAL_CONFIG_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a
+       $(E) "[LD]      Linking $@"
+       $(Q) mkdir -p `dirname $@`
+       $(Q) $(LDXX) $(LDFLAGS) $(GLOBAL_CONFIG_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/global_config_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/gprpp/global_config_test.o:  $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a
+
+deps_global_config_test: $(GLOBAL_CONFIG_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(GLOBAL_CONFIG_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 GOLDEN_FILE_TEST_SRC = \
     $(GENDIR)/src/proto/grpc/testing/compiler_test.pb.cc $(GENDIR)/src/proto/grpc/testing/compiler_test.grpc.pb.cc \
     test/cpp/codegen/golden_file_test.cc \
@@ -16322,18 +16588,18 @@ $(BINDIR)/$(CONFIG)/golden_file_test: protobuf_dep_error
 
 else
 
-$(BINDIR)/$(CONFIG)/golden_file_test: $(PROTOBUF_DEP) $(GOLDEN_FILE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(BINDIR)/$(CONFIG)/golden_file_test: $(PROTOBUF_DEP) $(GOLDEN_FILE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
        $(E) "[LD]      Linking $@"
        $(Q) mkdir -p `dirname $@`
-       $(Q) $(LDXX) $(LDFLAGS) $(GOLDEN_FILE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/golden_file_test
+       $(Q) $(LDXX) $(LDFLAGS) $(GOLDEN_FILE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/golden_file_test
 
 endif
 
 endif
 
-$(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/compiler_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/compiler_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
-$(OBJDIR)/$(CONFIG)/test/cpp/codegen/golden_file_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(OBJDIR)/$(CONFIG)/test/cpp/codegen/golden_file_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
 deps_golden_file_test: $(GOLDEN_FILE_TEST_OBJS:.o=.dep)
 
@@ -16431,6 +16697,49 @@ endif
 endif
 
 
+GRPC_CORE_MAP_TEST_SRC = \
+    test/core/gprpp/map_test.cc \
+
+GRPC_CORE_MAP_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_CORE_MAP_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/grpc_core_map_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/grpc_core_map_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/grpc_core_map_test: $(PROTOBUF_DEP) $(GRPC_CORE_MAP_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+       $(E) "[LD]      Linking $@"
+       $(Q) mkdir -p `dirname $@`
+       $(Q) $(LDXX) $(LDFLAGS) $(GRPC_CORE_MAP_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/grpc_core_map_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/gprpp/map_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_grpc_core_map_test: $(GRPC_CORE_MAP_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(GRPC_CORE_MAP_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 GRPC_CPP_PLUGIN_SRC = \
     src/compiler/cpp_plugin.cc \
 
@@ -17315,6 +17624,49 @@ endif
 endif
 
 
+MESSAGE_ALLOCATOR_END2END_TEST_SRC = \
+    test/cpp/end2end/message_allocator_end2end_test.cc \
+
+MESSAGE_ALLOCATOR_END2END_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(MESSAGE_ALLOCATOR_END2END_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/message_allocator_end2end_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/message_allocator_end2end_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/message_allocator_end2end_test: $(PROTOBUF_DEP) $(MESSAGE_ALLOCATOR_END2END_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+       $(E) "[LD]      Linking $@"
+       $(Q) mkdir -p `dirname $@`
+       $(Q) $(LDXX) $(LDFLAGS) $(MESSAGE_ALLOCATOR_END2END_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/message_allocator_end2end_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/cpp/end2end/message_allocator_end2end_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_message_allocator_end2end_test: $(MESSAGE_ALLOCATOR_END2END_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(MESSAGE_ALLOCATOR_END2END_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 METRICS_CLIENT_SRC = \
     $(GENDIR)/src/proto/grpc/testing/metrics.pb.cc $(GENDIR)/src/proto/grpc/testing/metrics.grpc.pb.cc \
     test/cpp/interop/metrics_client.cc \
@@ -18452,16 +18804,16 @@ $(BINDIR)/$(CONFIG)/server_crash_test_client: protobuf_dep_error
 
 else
 
-$(BINDIR)/$(CONFIG)/server_crash_test_client: $(PROTOBUF_DEP) $(SERVER_CRASH_TEST_CLIENT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(BINDIR)/$(CONFIG)/server_crash_test_client: $(PROTOBUF_DEP) $(SERVER_CRASH_TEST_CLIENT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
        $(E) "[LD]      Linking $@"
        $(Q) mkdir -p `dirname $@`
-       $(Q) $(LDXX) $(LDFLAGS) $(SERVER_CRASH_TEST_CLIENT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/server_crash_test_client
+       $(Q) $(LDXX) $(LDFLAGS) $(SERVER_CRASH_TEST_CLIENT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/server_crash_test_client
 
 endif
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/cpp/end2end/server_crash_test_client.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(OBJDIR)/$(CONFIG)/test/cpp/end2end/server_crash_test_client.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
 deps_server_crash_test_client: $(SERVER_CRASH_TEST_CLIENT_OBJS:.o=.dep)
 
@@ -18611,6 +18963,92 @@ endif
 $(OBJDIR)/$(CONFIG)/test/cpp/server/server_request_call_test.o: $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc
 
 
+SERVICE_CONFIG_END2END_TEST_SRC = \
+    test/cpp/end2end/service_config_end2end_test.cc \
+
+SERVICE_CONFIG_END2END_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SERVICE_CONFIG_END2END_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/service_config_end2end_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/service_config_end2end_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/service_config_end2end_test: $(PROTOBUF_DEP) $(SERVICE_CONFIG_END2END_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+       $(E) "[LD]      Linking $@"
+       $(Q) mkdir -p `dirname $@`
+       $(Q) $(LDXX) $(LDFLAGS) $(SERVICE_CONFIG_END2END_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/service_config_end2end_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/cpp/end2end/service_config_end2end_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_service_config_end2end_test: $(SERVICE_CONFIG_END2END_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(SERVICE_CONFIG_END2END_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+SERVICE_CONFIG_TEST_SRC = \
+    test/core/client_channel/service_config_test.cc \
+
+SERVICE_CONFIG_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SERVICE_CONFIG_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/service_config_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/service_config_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/service_config_test: $(PROTOBUF_DEP) $(SERVICE_CONFIG_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+       $(E) "[LD]      Linking $@"
+       $(Q) mkdir -p `dirname $@`
+       $(Q) $(LDXX) $(LDFLAGS) $(SERVICE_CONFIG_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/service_config_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/client_channel/service_config_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_service_config_test: $(SERVICE_CONFIG_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(SERVICE_CONFIG_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 SHUTDOWN_TEST_SRC = \
     test/cpp/end2end/shutdown_test.cc \
 
@@ -20539,6 +20977,38 @@ endif
 endif
 
 
+H2_SSL_CRED_RELOAD_TEST_SRC = \
+    test/core/end2end/fixtures/h2_ssl_cred_reload.cc \
+
+H2_SSL_CRED_RELOAD_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(H2_SSL_CRED_RELOAD_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/h2_ssl_cred_reload_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/h2_ssl_cred_reload_test: $(H2_SSL_CRED_RELOAD_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+       $(E) "[LD]      Linking $@"
+       $(Q) mkdir -p `dirname $@`
+       $(Q) $(LD) $(LDFLAGS) $(H2_SSL_CRED_RELOAD_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_cred_reload_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/end2end/fixtures/h2_ssl_cred_reload.o:  $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_h2_ssl_cred_reload_test: $(H2_SSL_CRED_RELOAD_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(H2_SSL_CRED_RELOAD_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 H2_SSL_PROXY_TEST_SRC = \
     test/core/end2end/fixtures/h2_ssl_proxy.cc \
 
@@ -20918,16 +21388,16 @@ $(BINDIR)/$(CONFIG)/resolver_component_test_unsecure: protobuf_dep_error
 
 else
 
-$(BINDIR)/$(CONFIG)/resolver_component_test_unsecure: $(PROTOBUF_DEP) $(RESOLVER_COMPONENT_TEST_UNSECURE_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
+$(BINDIR)/$(CONFIG)/resolver_component_test_unsecure: $(PROTOBUF_DEP) $(RESOLVER_COMPONENT_TEST_UNSECURE_OBJS) $(LIBDIR)/$(CONFIG)/libdns_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
        $(E) "[LD]      Linking $@"
        $(Q) mkdir -p `dirname $@`
-       $(Q) $(LDXX) $(LDFLAGS) $(RESOLVER_COMPONENT_TEST_UNSECURE_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/resolver_component_test_unsecure
+       $(Q) $(LDXX) $(LDFLAGS) $(RESOLVER_COMPONENT_TEST_UNSECURE_OBJS) $(LIBDIR)/$(CONFIG)/libdns_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/resolver_component_test_unsecure
 
 endif
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/cpp/naming/resolver_component_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
+$(OBJDIR)/$(CONFIG)/test/cpp/naming/resolver_component_test.o:  $(LIBDIR)/$(CONFIG)/libdns_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
 
 deps_resolver_component_test_unsecure: $(RESOLVER_COMPONENT_TEST_UNSECURE_OBJS:.o=.dep)
 
@@ -20961,16 +21431,16 @@ $(BINDIR)/$(CONFIG)/resolver_component_test: protobuf_dep_error
 
 else
 
-$(BINDIR)/$(CONFIG)/resolver_component_test: $(PROTOBUF_DEP) $(RESOLVER_COMPONENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
+$(BINDIR)/$(CONFIG)/resolver_component_test: $(PROTOBUF_DEP) $(RESOLVER_COMPONENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libdns_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
        $(E) "[LD]      Linking $@"
        $(Q) mkdir -p `dirname $@`
-       $(Q) $(LDXX) $(LDFLAGS) $(RESOLVER_COMPONENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/resolver_component_test
+       $(Q) $(LDXX) $(LDFLAGS) $(RESOLVER_COMPONENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libdns_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/resolver_component_test
 
 endif
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/cpp/naming/resolver_component_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
+$(OBJDIR)/$(CONFIG)/test/cpp/naming/resolver_component_test.o:  $(LIBDIR)/$(CONFIG)/libdns_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
 
 deps_resolver_component_test: $(RESOLVER_COMPONENT_TEST_OBJS:.o=.dep)
 
@@ -21176,16 +21646,16 @@ $(BINDIR)/$(CONFIG)/cancel_ares_query_test: protobuf_dep_error
 
 else
 
-$(BINDIR)/$(CONFIG)/cancel_ares_query_test: $(PROTOBUF_DEP) $(CANCEL_ARES_QUERY_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
+$(BINDIR)/$(CONFIG)/cancel_ares_query_test: $(PROTOBUF_DEP) $(CANCEL_ARES_QUERY_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libdns_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
        $(E) "[LD]      Linking $@"
        $(Q) mkdir -p `dirname $@`
-       $(Q) $(LDXX) $(LDFLAGS) $(CANCEL_ARES_QUERY_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/cancel_ares_query_test
+       $(Q) $(LDXX) $(LDFLAGS) $(CANCEL_ARES_QUERY_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libdns_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/cancel_ares_query_test
 
 endif
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/cpp/naming/cancel_ares_query_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
+$(OBJDIR)/$(CONFIG)/test/cpp/naming/cancel_ares_query_test.o:  $(LIBDIR)/$(CONFIG)/libdns_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
 
 deps_cancel_ares_query_test: $(CANCEL_ARES_QUERY_TEST_OBJS:.o=.dep)
 
@@ -21815,6 +22285,7 @@ test/cpp/interop/interop_server.cc: $(OPENSSL_DEP)
 test/cpp/interop/interop_server_bootstrap.cc: $(OPENSSL_DEP)
 test/cpp/interop/server_helper.cc: $(OPENSSL_DEP)
 test/cpp/microbenchmarks/helpers.cc: $(OPENSSL_DEP)
+test/cpp/naming/dns_test_util.cc: $(OPENSSL_DEP)
 test/cpp/qps/benchmark_config.cc: $(OPENSSL_DEP)
 test/cpp/qps/client_async.cc: $(OPENSSL_DEP)
 test/cpp/qps/client_callback.cc: $(OPENSSL_DEP)
index 81371bf..2db3c5d 100644 (file)
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -9,28 +9,15 @@ grpc_deps()
 grpc_test_only_deps()
 
 register_execution_platforms(
-    "//third_party/toolchains:rbe_ubuntu1604",
-    "//third_party/toolchains:rbe_ubuntu1604_large",
+    "//third_party/toolchains:local",
+    "//third_party/toolchains:local_large",
+    "//third_party/toolchains:rbe_windows",
 )
 
 register_toolchains(
-    "//third_party/toolchains:cc-toolchain-clang-x86_64-default",
+    "//third_party/toolchains/bazel_0.23.2_rbe_windows:cc-toolchain-x64_windows",
 )
 
-http_archive(
-    name = "cython",
-    build_file = "//third_party:cython.BUILD",
-    sha256 = "d68138a2381afbdd0876c3cb2a22389043fa01c4badede1228ee073032b07a27",
-    strip_prefix = "cython-c2b80d87658a8525ce091cbe146cb7eaa29fed5c",
-    urls = [
-        "https://github.com/cython/cython/archive/c2b80d87658a8525ce091cbe146cb7eaa29fed5c.tar.gz",
-    ],
-)
-
-load("//third_party/py:python_configure.bzl", "python_configure")
-
-python_configure(name = "local_config_python")
-
 git_repository(
     name = "io_bazel_rules_python",
     commit = "8b5d0683a7d878b28fffe464779c8a53659fc645",
@@ -39,24 +26,42 @@ git_repository(
 
 load("@io_bazel_rules_python//python:pip.bzl", "pip_repositories", "pip_import")
 
-pip_repositories()
-
 pip_import(
     name = "grpc_python_dependencies",
     requirements = "//:requirements.bazel.txt",
 )
 
-load("@grpc_python_dependencies//:requirements.bzl", "pip_install")
+http_archive(
+    name = "cython",
+    build_file = "//third_party:cython.BUILD",
+    sha256 = "d68138a2381afbdd0876c3cb2a22389043fa01c4badede1228ee073032b07a27",
+    strip_prefix = "cython-c2b80d87658a8525ce091cbe146cb7eaa29fed5c",
+    urls = [
+        "https://github.com/cython/cython/archive/c2b80d87658a8525ce091cbe146cb7eaa29fed5c.tar.gz",
+    ],
+)
 
-pip_install()
+load("//bazel:grpc_python_deps.bzl", "grpc_python_deps")
 
-# NOTE(https://github.com/pubref/rules_protobuf/pull/196): Switch to upstream repo after this gets merged.
-git_repository(
-    name = "org_pubref_rules_protobuf",
-    remote = "https://github.com/ghostwriternr/rules_protobuf",
-    tag = "v0.8.2.1-alpha",
-)
+grpc_python_deps()
 
-load("@org_pubref_rules_protobuf//python:rules.bzl", "py_proto_repositories")
+load("@bazel_toolchains//rules:rbe_repo.bzl", "rbe_autoconfig")
 
-py_proto_repositories()
+# Create toolchain configuration for remote execution.
+rbe_autoconfig(
+    name = "rbe_default",
+)
+
+load("@bazel_toolchains//rules:environments.bzl", "clang_env")
+load("@bazel_skylib//lib:dicts.bzl", "dicts")
+
+# Create msan toolchain configuration for remote execution.
+rbe_autoconfig(
+    name = "rbe_msan",
+    env = dicts.add(
+        clang_env(),
+        {
+            "BAZEL_LINKOPTS": "-lc++:-lc++abi:-lm",
+        },
+    ),
+)
index 3240289..c3c82c9 100644 (file)
@@ -17,15 +17,3 @@ licenses(["notice"])  # Apache v2
 package(default_visibility = ["//:__subpackages__"])
 
 load(":cc_grpc_library.bzl", "cc_grpc_library")
-
-proto_library(
-    name = "well_known_protos_list",
-    srcs = ["@com_google_protobuf//:well_known_protos"],
-)
-
-cc_grpc_library(
-    name = "well_known_protos",
-    srcs = "well_known_protos_list",
-    proto_only = True,
-    deps = [],
-)
index 6bfcd65..572af75 100644 (file)
 """Generates and compiles C++ grpc stubs from proto_library rules."""
 
 load("//bazel:generate_cc.bzl", "generate_cc")
+load("//bazel:protobuf.bzl", "well_known_proto_libs")
 
-def cc_grpc_library(name, srcs, deps, proto_only, well_known_protos, generate_mocks = False, use_external = False, **kwargs):
-  """Generates C++ grpc classes from a .proto file.
+def cc_grpc_library(
+        name,
+        srcs,
+        deps,
+        proto_only = False,
+        well_known_protos = False,
+        generate_mocks = False,
+        use_external = False,
+        grpc_only = False,
+        **kwargs):
+    """Generates C++ grpc classes for services defined in a proto file.
 
-  Assumes the generated classes will be used in cc_api_version = 2.
+    If grpc_only is True, this rule is compatible with proto_library and
+    cc_proto_library native rules such that it expects proto_library target
+    as srcs argument and generates only grpc library classes, expecting
+    protobuf messages classes library (cc_proto_library target) to be passed in
+    deps argument. By default grpc_only is False which makes this rule to behave
+    in a backwards-compatible mode (trying to generate both proto and grpc
+    classes).
 
-  Arguments:
-      name: name of rule.
-      srcs: a single proto_library, which wraps the .proto files with services.
-      deps: a list of C++ proto_library (or cc_proto_library) which provides
-        the compiled code of any message that the services depend on.
-      well_known_protos: Should this library additionally depend on well known
-        protos
-      use_external: When True the grpc deps are prefixed with //external. This
-        allows grpc to be used as a dependency in other bazel projects.
-      generate_mocks: When True, Google Mock code for client stub is generated.
-      **kwargs: rest of arguments, e.g., compatible_with and visibility.
-  """
-  if len(srcs) > 1:
-    fail("Only one srcs value supported", "srcs")
+    Assumes the generated classes will be used in cc_api_version = 2.
 
-  proto_target = "_" + name + "_only"
-  codegen_target = "_" + name + "_codegen"
-  codegen_grpc_target = "_" + name + "_grpc_codegen"
-  proto_deps = ["_" + dep + "_only" for dep in deps if dep.find(':') == -1]
-  proto_deps += [dep.split(':')[0] + ':' + "_" + dep.split(':')[1] + "_only" for dep in deps if dep.find(':') != -1]
+    Args:
+        name (str): Name of rule.
+        srcs (list): A single .proto file which contains services definitions,
+          or if grpc_only parameter is True, a single proto_library which
+          contains services descriptors.
+        deps (list): A list of C++ proto_library (or cc_proto_library) which
+          provides the compiled code of any message that the services depend on.
+        proto_only (bool): If True, create only C++ proto classes library,
+          avoid creating C++ grpc classes library (expect it in deps).
+          Deprecated, use native cc_proto_library instead. False by default.
+        well_known_protos (bool): Should this library additionally depend on
+          well known protos. Deprecated, the well known protos should be
+          specified as explicit dependencies of the proto_library target
+          (passed in srcs parameter) instead. False by default.
+        generate_mocks (bool): when True, Google Mock code for client stub is
+          generated. False by default.
+        use_external (bool): Not used.
+        grpc_only (bool): if True, generate only grpc library, expecting
+          protobuf messages library (cc_proto_library target) to be passed as
+          deps. False by default (will become True by default eventually).
+        **kwargs: rest of arguments, e.g., compatible_with and visibility
+    """
+    if len(srcs) > 1:
+        fail("Only one srcs value supported", "srcs")
+    if grpc_only and proto_only:
+        fail("A mutualy exclusive configuration is specified: grpc_only = True and proto_only = True")
 
-  native.proto_library(
-      name = proto_target,
-      srcs = srcs,
-      deps = proto_deps,
-      **kwargs
-  )
+    extra_deps = []
+    proto_targets = []
 
-  generate_cc(
-      name = codegen_target,
-      srcs = [proto_target],
-      well_known_protos = well_known_protos,
-      **kwargs
-  )
+    if not grpc_only:
+        proto_target = "_" + name + "_only"
+        cc_proto_target = name if proto_only else "_" + name + "_cc_proto"
 
-  if not proto_only:
-    plugin = "@com_github_grpc_grpc//:grpc_cpp_plugin"
-    generate_cc(
-        name = codegen_grpc_target,
-        srcs = [proto_target],
-        plugin = plugin,
-        well_known_protos = well_known_protos,
-        generate_mocks = generate_mocks,
-        **kwargs
-    )
-    grpc_deps  = ["@com_github_grpc_grpc//:grpc++_codegen_proto",
-                  "//external:protobuf"]
-    native.cc_library(
-        name = name,
-        srcs = [":" + codegen_grpc_target, ":" + codegen_target],
-        hdrs = [":" + codegen_grpc_target, ":" + codegen_target],
-        deps = deps + grpc_deps,
-        **kwargs
-    )
-  else:
-    native.cc_library(
-        name = name,
-        srcs = [":" + codegen_target],
-        hdrs = [":" + codegen_target],
-        deps = deps + ["//external:protobuf"],
-        **kwargs
-    )
+        proto_deps = ["_" + dep + "_only" for dep in deps if dep.find(":") == -1]
+        proto_deps += [dep.split(":")[0] + ":" + "_" + dep.split(":")[1] + "_only" for dep in deps if dep.find(":") != -1]
+        if well_known_protos:
+            proto_deps += well_known_proto_libs()
+
+        native.proto_library(
+            name = proto_target,
+            srcs = srcs,
+            deps = proto_deps,
+            **kwargs
+        )
+
+        native.cc_proto_library(
+            name = cc_proto_target,
+            deps = [":" + proto_target],
+            **kwargs
+        )
+        extra_deps.append(":" + cc_proto_target)
+        proto_targets.append(proto_target)
+    else:
+        if not srcs:
+            fail("srcs cannot be empty", "srcs")
+        proto_targets += srcs
+
+    if not proto_only:
+        codegen_grpc_target = "_" + name + "_grpc_codegen"
+        generate_cc(
+            name = codegen_grpc_target,
+            srcs = proto_targets,
+            plugin = "@com_github_grpc_grpc//:grpc_cpp_plugin",
+            well_known_protos = well_known_protos,
+            generate_mocks = generate_mocks,
+            **kwargs
+        )
+
+        native.cc_library(
+            name = name,
+            srcs = [":" + codegen_grpc_target],
+            hdrs = [":" + codegen_grpc_target],
+            deps = deps +
+                   extra_deps +
+                   ["@com_github_grpc_grpc//:grpc++_codegen_proto"],
+            **kwargs
+        )
index 8f30c84..29a888f 100644 (file)
@@ -4,81 +4,142 @@ This is an internal rule used by cc_grpc_library, and shouldn't be used
 directly.
 """
 
+load(
+    "//bazel:protobuf.bzl",
+    "get_include_protoc_args",
+    "get_plugin_args",
+    "get_proto_root",
+    "proto_path_to_generated_filename",
+)
+
+_GRPC_PROTO_HEADER_FMT = "{}.grpc.pb.h"
+_GRPC_PROTO_SRC_FMT = "{}.grpc.pb.cc"
+_GRPC_PROTO_MOCK_HEADER_FMT = "{}_mock.grpc.pb.h"
+_PROTO_HEADER_FMT = "{}.pb.h"
+_PROTO_SRC_FMT = "{}.pb.cc"
+
+def _strip_package_from_path(label_package, file):
+    prefix_len = 0
+    if not file.is_source and file.path.startswith(file.root.path):
+        prefix_len = len(file.root.path) + 1
+
+    path = file.path
+    if len(label_package) == 0:
+        return path
+    if not path.startswith(label_package + "/", prefix_len):
+        fail("'{}' does not lie within '{}'.".format(path, label_package))
+    return path[prefix_len + len(label_package + "/"):]
+
+def _get_srcs_file_path(file):
+    if not file.is_source and file.path.startswith(file.root.path):
+        return file.path[len(file.root.path) + 1:]
+    return file.path
+
+def _join_directories(directories):
+    massaged_directories = [directory for directory in directories if len(directory) != 0]
+    return "/".join(massaged_directories)
+
 def generate_cc_impl(ctx):
-  """Implementation of the generate_cc rule."""
-  protos = [f for src in ctx.attr.srcs for f in src.proto.direct_sources]
-  includes = [f for src in ctx.attr.srcs for f in src.proto.transitive_imports]
-  outs = []
-  # label_len is length of the path from WORKSPACE root to the location of this build file
-  label_len = 0
-  # proto_root is the directory relative to which generated include paths should be
-  proto_root = ""
-  if ctx.label.package:
-    # The +1 is for the trailing slash.
-    label_len += len(ctx.label.package) + 1
-  if ctx.label.workspace_root:
-    label_len += len(ctx.label.workspace_root) + 1
-    proto_root = "/" + ctx.label.workspace_root
-
-  if ctx.executable.plugin:
-    outs += [proto.path[label_len:-len(".proto")] + ".grpc.pb.h" for proto in protos]
-    outs += [proto.path[label_len:-len(".proto")] + ".grpc.pb.cc" for proto in protos]
-    if ctx.attr.generate_mocks:
-      outs += [proto.path[label_len:-len(".proto")] + "_mock.grpc.pb.h" for proto in protos]
-  else:
-    outs += [proto.path[label_len:-len(".proto")] + ".pb.h" for proto in protos]
-    outs += [proto.path[label_len:-len(".proto")] + ".pb.cc" for proto in protos]
-  out_files = [ctx.actions.declare_file(out) for out in outs]
-  dir_out = str(ctx.genfiles_dir.path + proto_root)
-
-  arguments = []
-  if ctx.executable.plugin:
-    arguments += ["--plugin=protoc-gen-PLUGIN=" + ctx.executable.plugin.path]
-    flags = list(ctx.attr.flags)
-    if ctx.attr.generate_mocks:
-      flags.append("generate_mock_code=true")
-    arguments += ["--PLUGIN_out=" + ",".join(flags) + ":" + dir_out]
-    tools = [ctx.executable.plugin]
-  else:
-    arguments += ["--cpp_out=" + ",".join(ctx.attr.flags) + ":" + dir_out]
-    tools = []
-
-  # Import protos relative to their workspace root so that protoc prints the
-  # right include paths.
-  for include in includes:
-    directory = include.path
-    if directory.startswith("external"):
-      external_sep = directory.find("/")
-      repository_sep = directory.find("/", external_sep + 1)
-      arguments += ["--proto_path=" + directory[:repository_sep]]
+    """Implementation of the generate_cc rule."""
+    protos = [f for src in ctx.attr.srcs for f in src.proto.check_deps_sources]
+    includes = [
+        f
+        for src in ctx.attr.srcs
+        for f in src.proto.transitive_imports
+    ]
+    outs = []
+    proto_root = get_proto_root(
+        ctx.label.workspace_root,
+    )
+
+    label_package = _join_directories([ctx.label.workspace_root, ctx.label.package])
+    if ctx.executable.plugin:
+        outs += [
+            proto_path_to_generated_filename(
+                _strip_package_from_path(label_package, proto),
+                _GRPC_PROTO_HEADER_FMT,
+            )
+            for proto in protos
+        ]
+        outs += [
+            proto_path_to_generated_filename(
+                _strip_package_from_path(label_package, proto),
+                _GRPC_PROTO_SRC_FMT,
+            )
+            for proto in protos
+        ]
+        if ctx.attr.generate_mocks:
+            outs += [
+                proto_path_to_generated_filename(
+                    _strip_package_from_path(label_package, proto),
+                    _GRPC_PROTO_MOCK_HEADER_FMT,
+                )
+                for proto in protos
+            ]
     else:
-      arguments += ["--proto_path=."]
-  # Include the output directory so that protoc puts the generated code in the
-  # right directory.
-  arguments += ["--proto_path={0}{1}".format(dir_out, proto_root)]
-  arguments += [proto.path for proto in protos]
-
-  # create a list of well known proto files if the argument is non-None
-  well_known_proto_files = []
-  if ctx.attr.well_known_protos:
-    f = ctx.attr.well_known_protos.files.to_list()[0].dirname
-    if f != "external/com_google_protobuf/src/google/protobuf":
-      print("Error: Only @com_google_protobuf//:well_known_protos is supported")
+        outs += [
+            proto_path_to_generated_filename(
+                _strip_package_from_path(label_package, proto),
+                _PROTO_HEADER_FMT,
+            )
+            for proto in protos
+        ]
+        outs += [
+            proto_path_to_generated_filename(
+                _strip_package_from_path(label_package, proto),
+                _PROTO_SRC_FMT,
+            )
+            for proto in protos
+        ]
+    out_files = [ctx.actions.declare_file(out) for out in outs]
+    dir_out = str(ctx.genfiles_dir.path + proto_root)
+
+    arguments = []
+    if ctx.executable.plugin:
+        arguments += get_plugin_args(
+            ctx.executable.plugin,
+            ctx.attr.flags,
+            dir_out,
+            ctx.attr.generate_mocks,
+        )
+        tools = [ctx.executable.plugin]
     else:
-      # f points to "external/com_google_protobuf/src/google/protobuf"
-      # add -I argument to protoc so it knows where to look for the proto files.
-      arguments += ["-I{0}".format(f + "/../..")]
-      well_known_proto_files = [f for f in ctx.attr.well_known_protos.files]
+        arguments += ["--cpp_out=" + ",".join(ctx.attr.flags) + ":" + dir_out]
+        tools = []
+
+    arguments += get_include_protoc_args(includes)
+
+    # Include the output directory so that protoc puts the generated code in the
+    # right directory.
+    arguments += ["--proto_path={0}{1}".format(dir_out, proto_root)]
+    arguments += [_get_srcs_file_path(proto) for proto in protos]
+
+    # create a list of well known proto files if the argument is non-None
+    well_known_proto_files = []
+    if ctx.attr.well_known_protos:
+        f = ctx.attr.well_known_protos.files.to_list()[0].dirname
+        if f != "external/com_google_protobuf/src/google/protobuf":
+            print(
+                "Error: Only @com_google_protobuf//:well_known_protos is supported",
+            )
+        else:
+            # f points to "external/com_google_protobuf/src/google/protobuf"
+            # add -I argument to protoc so it knows where to look for the proto files.
+            arguments += ["-I{0}".format(f + "/../..")]
+            well_known_proto_files = [
+                f
+                for f in ctx.attr.well_known_protos.files
+            ]
 
-  ctx.actions.run(
-      inputs = protos + includes + well_known_proto_files,
-      tools = tools,
-      outputs = out_files,
-      executable = ctx.executable._protoc,
-      arguments = arguments,
-  )
+    ctx.actions.run(
+        inputs = protos + includes + well_known_proto_files,
+        tools = tools,
+        outputs = out_files,
+        executable = ctx.executable._protoc,
+        arguments = arguments,
+    )
 
-  return struct(files=depset(out_files))
+    return struct(files = depset(out_files))
 
 _generate_cc = rule(
     attrs = {
@@ -96,10 +157,8 @@ _generate_cc = rule(
             mandatory = False,
             allow_empty = True,
         ),
-        "well_known_protos" : attr.label(
-            mandatory = False,
-        ),
-        "generate_mocks" : attr.bool(
+        "well_known_protos": attr.label(mandatory = False),
+        "generate_mocks": attr.bool(
             default = False,
             mandatory = False,
         ),
@@ -115,7 +174,10 @@ _generate_cc = rule(
 )
 
 def generate_cc(well_known_protos, **kwargs):
-  if well_known_protos:
-    _generate_cc(well_known_protos="@com_google_protobuf//:well_known_protos", **kwargs)
-  else:
-    _generate_cc(**kwargs)
+    if well_known_protos:
+        _generate_cc(
+            well_known_protos = "@com_google_protobuf//:well_known_protos",
+            **kwargs
+        )
+    else:
+        _generate_cc(**kwargs)
index bc4d1f1..5f8477d 100644 (file)
@@ -112,10 +112,7 @@ def grpc_cc_library(
         visibility = visibility,
         testonly = testonly,
         linkopts = linkopts,
-        includes = [
-            "include",
-            "src/core/ext/upb-generated",
-        ],
+        includes = ["include"] + if_not_windows(["src/core/ext/upb-generated"]),
         alwayslink = alwayslink,
         data = data,
         tags = tags,
index 891783b..a4e6509 100644 (file)
@@ -110,6 +110,8 @@ def grpc_deps():
         http_archive(
             name = "boringssl",
             # on the chromium-stable-with-bazel branch
+            # NOTE: This URL generates a tarball containing dynamic date
+            # information, so the sha256 is not consistent.
             url = "https://boringssl.googlesource.com/boringssl/+archive/afc30d43eef92979b05776ec0963c9cede5fb80f.tar.gz",
         )
 
@@ -117,6 +119,7 @@ def grpc_deps():
         http_archive(
             name = "com_github_madler_zlib",
             build_file = "@com_github_grpc_grpc//third_party:zlib.BUILD",
+            sha256 = "6d4d6640ca3121620995ee255945161821218752b551a1a180f4215f7d124d45",
             strip_prefix = "zlib-cacf7f1d4e3d44d871b605da3b647f07d718623f",
             url = "https://github.com/madler/zlib/archive/cacf7f1d4e3d44d871b605da3b647f07d718623f.tar.gz",
         )
@@ -124,6 +127,7 @@ def grpc_deps():
     if "com_google_protobuf" not in native.existing_rules():
         http_archive(
             name = "com_google_protobuf",
+            sha256 = "cf9e2fb1d2cd30ec9d51ff1749045208bd641f290f64b85046485934b0e03783",
             strip_prefix = "protobuf-582743bf40c5d3639a70f98f183914a2c0cd0680",
             url = "https://github.com/google/protobuf/archive/582743bf40c5d3639a70f98f183914a2c0cd0680.tar.gz",
         )
@@ -132,6 +136,7 @@ def grpc_deps():
         http_archive(
             name = "com_github_nanopb_nanopb",
             build_file = "@com_github_grpc_grpc//third_party:nanopb.BUILD",
+            sha256 = "8bbbb1e78d4ddb0a1919276924ab10d11b631df48b657d960e0c795a25515735",
             strip_prefix = "nanopb-f8ac463766281625ad710900479130c7fcb4d63b",
             url = "https://github.com/nanopb/nanopb/archive/f8ac463766281625ad710900479130c7fcb4d63b.tar.gz",
         )
@@ -140,6 +145,7 @@ def grpc_deps():
         http_archive(
             name = "com_github_google_googletest",
             build_file = "@com_github_grpc_grpc//third_party:gtest.BUILD",
+            sha256 = "175a22300b3450e27e5f2e6f95cc9abca74617cbc21a1e0ed19bdfbd22ea0305",
             strip_prefix = "googletest-ec44c6c1675c25b9827aacd08c02433cccde7780",
             url = "https://github.com/google/googletest/archive/ec44c6c1675c25b9827aacd08c02433cccde7780.tar.gz",
         )
@@ -147,6 +153,7 @@ def grpc_deps():
     if "com_github_gflags_gflags" not in native.existing_rules():
         http_archive(
             name = "com_github_gflags_gflags",
+            sha256 = "63ae70ea3e05780f7547d03503a53de3a7d2d83ad1caaa443a31cb20aea28654",
             strip_prefix = "gflags-28f50e0fed19872e0fd50dd23ce2ee8cd759338e",
             url = "https://github.com/gflags/gflags/archive/28f50e0fed19872e0fd50dd23ce2ee8cd759338e.tar.gz",
         )
@@ -154,6 +161,7 @@ def grpc_deps():
     if "com_github_google_benchmark" not in native.existing_rules():
         http_archive(
             name = "com_github_google_benchmark",
+            sha256 = "c7682e9007ddfd94072647abab3e89ffd9084089460ae47d67060974467b58bf",
             strip_prefix = "benchmark-e776aa0275e293707b6a0901e0e8d8a8a3679508",
             url = "https://github.com/google/benchmark/archive/e776aa0275e293707b6a0901e0e8d8a8a3679508.tar.gz",
         )
@@ -162,6 +170,7 @@ def grpc_deps():
         http_archive(
             name = "com_github_cares_cares",
             build_file = "@com_github_grpc_grpc//third_party:cares/cares.BUILD",
+            sha256 = "e8c2751ddc70fed9dc6f999acd92e232d5846f009ee1674f8aee81f19b2b915a",
             strip_prefix = "c-ares-e982924acee7f7313b4baa4ee5ec000c5e373c30",
             url = "https://github.com/c-ares/c-ares/archive/e982924acee7f7313b4baa4ee5ec000c5e373c30.tar.gz",
         )
@@ -169,19 +178,20 @@ def grpc_deps():
     if "com_google_absl" not in native.existing_rules():
         http_archive(
             name = "com_google_absl",
+            sha256 = "5fe2a3a8f8378e81d4d3db6541f48030e04233ccd2d6c7e9d981ed577b314ae8",
             strip_prefix = "abseil-cpp-308ce31528a7edfa39f5f6d36142278a0ae1bf45",
             url = "https://github.com/abseil/abseil-cpp/archive/308ce31528a7edfa39f5f6d36142278a0ae1bf45.tar.gz",
         )
 
-    if "com_github_bazelbuild_bazeltoolchains" not in native.existing_rules():
+    if "bazel_toolchains" not in native.existing_rules():
         http_archive(
-            name = "com_github_bazelbuild_bazeltoolchains",
-            strip_prefix = "bazel-toolchains-37419a124bdb9af2fec5b99a973d359b6b899b61",
+            name = "bazel_toolchains",
+            sha256 = "67335b3563d9b67dc2550b8f27cc689b64fadac491e69ce78763d9ba894cc5cc",
+            strip_prefix = "bazel-toolchains-cddc376d428ada2927ad359211c3e356bd9c9fbb",
             urls = [
-                "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/37419a124bdb9af2fec5b99a973d359b6b899b61.tar.gz",
-                "https://github.com/bazelbuild/bazel-toolchains/archive/37419a124bdb9af2fec5b99a973d359b6b899b61.tar.gz",
+                "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/cddc376d428ada2927ad359211c3e356bd9c9fbb.tar.gz",
+                "https://github.com/bazelbuild/bazel-toolchains/archive/cddc376d428ada2927ad359211c3e356bd9c9fbb.tar.gz",
             ],
-            sha256 = "ee854b5de299138c1f4a2edb5573d22b21d975acfc7aa938f36d30b49ef97498",
         )
 
     if "bazel_skylib" not in native.existing_rules():
@@ -195,6 +205,7 @@ def grpc_deps():
     if "io_opencensus_cpp" not in native.existing_rules():
         http_archive(
             name = "io_opencensus_cpp",
+            sha256 = "3436ca23dc1b3345186defd0f46d64244079ba3d3234a0c5d6ef5e8d5d06c8b5",
             strip_prefix = "opencensus-cpp-9b1e354e89bf3d92aedc00af45b418ce870f3d77",
             url = "https://github.com/census-instrumentation/opencensus-cpp/archive/9b1e354e89bf3d92aedc00af45b418ce870f3d77.tar.gz",
         )
@@ -202,6 +213,7 @@ def grpc_deps():
     if "upb" not in native.existing_rules():
         http_archive(
             name = "upb",
+            sha256 = "0e749a8973968397f849a3b42e28ee9c85dc418c2477954c2a6a4495f323241d",
             strip_prefix = "upb-ed9faae0993704b033c594b072d65e1bf19207fa",
             url = "https://github.com/google/upb/archive/ed9faae0993704b033c594b072d65e1bf19207fa.tar.gz",
         )
@@ -223,6 +235,7 @@ def grpc_test_only_deps():
     if "com_github_twisted_twisted" not in native.existing_rules():
         http_archive(
             name = "com_github_twisted_twisted",
+            sha256 = "ca17699d0d62eafc5c28daf2c7d0a18e62ae77b4137300b6c7d7868b39b06139",
             strip_prefix = "twisted-twisted-17.5.0",
             url = "https://github.com/twisted/twisted/archive/twisted-17.5.0.zip",
             build_file = "@com_github_grpc_grpc//third_party:twisted.BUILD",
@@ -231,6 +244,7 @@ def grpc_test_only_deps():
     if "com_github_yaml_pyyaml" not in native.existing_rules():
         http_archive(
             name = "com_github_yaml_pyyaml",
+            sha256 = "6b4314b1b2051ddb9d4fcd1634e1fa9c1bb4012954273c9ff3ef689f6ec6c93e",
             strip_prefix = "pyyaml-3.12",
             url = "https://github.com/yaml/pyyaml/archive/3.12.zip",
             build_file = "@com_github_grpc_grpc//third_party:yaml.BUILD",
@@ -239,6 +253,7 @@ def grpc_test_only_deps():
     if "com_github_twisted_incremental" not in native.existing_rules():
         http_archive(
             name = "com_github_twisted_incremental",
+            sha256 = "f0ca93359ee70243ff7fbf2d904a6291810bd88cb80ed4aca6fa77f318a41a36",
             strip_prefix = "incremental-incremental-17.5.0",
             url = "https://github.com/twisted/incremental/archive/incremental-17.5.0.zip",
             build_file = "@com_github_grpc_grpc//third_party:incremental.BUILD",
@@ -247,6 +262,7 @@ def grpc_test_only_deps():
     if "com_github_zopefoundation_zope_interface" not in native.existing_rules():
         http_archive(
             name = "com_github_zopefoundation_zope_interface",
+            sha256 = "e9579fc6149294339897be3aa9ecd8a29217c0b013fe6f44fcdae00e3204198a",
             strip_prefix = "zope.interface-4.4.3",
             url = "https://github.com/zopefoundation/zope.interface/archive/4.4.3.zip",
             build_file = "@com_github_grpc_grpc//third_party:zope_interface.BUILD",
@@ -255,6 +271,7 @@ def grpc_test_only_deps():
     if "com_github_twisted_constantly" not in native.existing_rules():
         http_archive(
             name = "com_github_twisted_constantly",
+            sha256 = "2702cd322161a579d2c0dbf94af4e57712eedc7bd7bbbdc554a230544f7d346c",
             strip_prefix = "constantly-15.1.0",
             url = "https://github.com/twisted/constantly/archive/15.1.0.zip",
             build_file = "@com_github_grpc_grpc//third_party:constantly.BUILD",
diff --git a/bazel/grpc_python_deps.bzl b/bazel/grpc_python_deps.bzl
new file mode 100644 (file)
index 0000000..91438f3
--- /dev/null
@@ -0,0 +1,8 @@
+load("//third_party/py:python_configure.bzl", "python_configure")
+load("@io_bazel_rules_python//python:pip.bzl", "pip_repositories")
+load("@grpc_python_dependencies//:requirements.bzl", "pip_install")
+
+def grpc_python_deps():
+    python_configure(name = "local_config_python")
+    pip_repositories()
+    pip_install()
diff --git a/bazel/protobuf.bzl b/bazel/protobuf.bzl
new file mode 100644 (file)
index 0000000..f2df7bd
--- /dev/null
@@ -0,0 +1,104 @@
+"""Utility functions for generating protobuf code."""
+
+_PROTO_EXTENSION = ".proto"
+
+def well_known_proto_libs():
+    return [
+        "@com_google_protobuf//:any_proto",
+        "@com_google_protobuf//:api_proto",
+        "@com_google_protobuf//:compiler_plugin_proto",
+        "@com_google_protobuf//:descriptor_proto",
+        "@com_google_protobuf//:duration_proto",
+        "@com_google_protobuf//:empty_proto",
+        "@com_google_protobuf//:field_mask_proto",
+        "@com_google_protobuf//:source_context_proto",
+        "@com_google_protobuf//:struct_proto",
+        "@com_google_protobuf//:timestamp_proto",
+        "@com_google_protobuf//:type_proto",
+        "@com_google_protobuf//:wrappers_proto",
+    ]
+
+def get_proto_root(workspace_root):
+    """Gets the root protobuf directory.
+
+    Args:
+      workspace_root: context.label.workspace_root
+
+    Returns:
+      The directory relative to which generated include paths should be.
+    """
+    if workspace_root:
+        return "/{}".format(workspace_root)
+    else:
+        return ""
+
+def _strip_proto_extension(proto_filename):
+    if not proto_filename.endswith(_PROTO_EXTENSION):
+        fail('"{}" does not end with "{}"'.format(
+            proto_filename,
+            _PROTO_EXTENSION,
+        ))
+    return proto_filename[:-len(_PROTO_EXTENSION)]
+
+def proto_path_to_generated_filename(proto_path, fmt_str):
+    """Calculates the name of a generated file for a protobuf path.
+
+    For example, "examples/protos/helloworld.proto" might map to
+      "helloworld.pb.h".
+
+    Args:
+      proto_path: The path to the .proto file.
+      fmt_str: A format string used to calculate the generated filename. For
+        example, "{}.pb.h" might be used to calculate a C++ header filename.
+
+    Returns:
+      The generated filename.
+    """
+    return fmt_str.format(_strip_proto_extension(proto_path))
+
+def _get_include_directory(include):
+    directory = include.path
+    prefix_len = 0
+    if not include.is_source and directory.startswith(include.root.path):
+        prefix_len = len(include.root.path) + 1
+
+    if directory.startswith("external", prefix_len):
+        external_separator = directory.find("/", prefix_len)
+        repository_separator = directory.find("/", external_separator + 1)
+        return directory[:repository_separator]
+    else:
+        return include.root.path if include.root.path else "."
+
+def get_include_protoc_args(includes):
+    """Returns protoc args that imports protos relative to their import root.
+
+    Args:
+      includes: A list of included proto files.
+
+    Returns:
+      A list of arguments to be passed to protoc. For example, ["--proto_path=."].
+    """
+    return [
+        "--proto_path={}".format(_get_include_directory(include))
+        for include in includes
+    ]
+
+def get_plugin_args(plugin, flags, dir_out, generate_mocks):
+    """Returns arguments configuring protoc to use a plugin for a language.
+
+    Args:
+      plugin: An executable file to run as the protoc plugin.
+      flags: The plugin flags to be passed to protoc.
+      dir_out: The output directory for the plugin.
+      generate_mocks: A bool indicating whether to generate mocks.
+
+    Returns:
+      A list of protoc arguments configuring the plugin.
+    """
+    augmented_flags = list(flags)
+    if generate_mocks:
+        augmented_flags.append("generate_mock_code=true")
+    return [
+        "--plugin=protoc-gen-PLUGIN=" + plugin.path,
+        "--PLUGIN_out=" + ",".join(augmented_flags) + ":" + dir_out,
+    ]
diff --git a/bazel/python_rules.bzl b/bazel/python_rules.bzl
new file mode 100644 (file)
index 0000000..2f3b38a
--- /dev/null
@@ -0,0 +1,186 @@
+"""Generates and compiles Python gRPC stubs from proto_library rules."""
+
+load("@grpc_python_dependencies//:requirements.bzl", "requirement")
+load(
+    "//bazel:protobuf.bzl",
+    "get_include_protoc_args",
+    "get_plugin_args",
+    "get_proto_root",
+    "proto_path_to_generated_filename",
+)
+
+_GENERATED_PROTO_FORMAT = "{}_pb2.py"
+_GENERATED_GRPC_PROTO_FORMAT = "{}_pb2_grpc.py"
+
+def _get_staged_proto_file(context, source_file):
+    if source_file.dirname == context.label.package:
+        return source_file
+    else:
+        copied_proto = context.actions.declare_file(source_file.basename)
+        context.actions.run_shell(
+            inputs = [source_file],
+            outputs = [copied_proto],
+            command = "cp {} {}".format(source_file.path, copied_proto.path),
+            mnemonic = "CopySourceProto",
+        )
+        return copied_proto
+
+def _generate_py_impl(context):
+    protos = []
+    for src in context.attr.deps:
+        for file in src.proto.direct_sources:
+            protos.append(_get_staged_proto_file(context, file))
+    includes = [
+        file
+        for src in context.attr.deps
+        for file in src.proto.transitive_imports
+    ]
+    proto_root = get_proto_root(context.label.workspace_root)
+    format_str = (_GENERATED_GRPC_PROTO_FORMAT if context.executable.plugin else _GENERATED_PROTO_FORMAT)
+    out_files = [
+        context.actions.declare_file(
+            proto_path_to_generated_filename(
+                proto.basename,
+                format_str,
+            ),
+        )
+        for proto in protos
+    ]
+
+    arguments = []
+    tools = [context.executable._protoc]
+    if context.executable.plugin:
+        arguments += get_plugin_args(
+            context.executable.plugin,
+            context.attr.flags,
+            context.genfiles_dir.path,
+            False,
+        )
+        tools += [context.executable.plugin]
+    else:
+        arguments += [
+            "--python_out={}:{}".format(
+                ",".join(context.attr.flags),
+                context.genfiles_dir.path,
+            ),
+        ]
+
+    arguments += get_include_protoc_args(includes)
+    arguments += [
+        "--proto_path={}".format(context.genfiles_dir.path)
+        for proto in protos
+    ]
+    for proto in protos:
+        massaged_path = proto.path
+        if massaged_path.startswith(context.genfiles_dir.path):
+            massaged_path = proto.path[len(context.genfiles_dir.path) + 1:]
+        arguments.append(massaged_path)
+
+    well_known_proto_files = []
+    if context.attr.well_known_protos:
+        well_known_proto_directory = context.attr.well_known_protos.files.to_list(
+        )[0].dirname
+
+        arguments += ["-I{}".format(well_known_proto_directory + "/../..")]
+        well_known_proto_files = context.attr.well_known_protos.files.to_list()
+
+    context.actions.run(
+        inputs = protos + includes + well_known_proto_files,
+        tools = tools,
+        outputs = out_files,
+        executable = context.executable._protoc,
+        arguments = arguments,
+        mnemonic = "ProtocInvocation",
+    )
+    return struct(files = depset(out_files))
+
+__generate_py = rule(
+    attrs = {
+        "deps": attr.label_list(
+            mandatory = True,
+            allow_empty = False,
+            providers = ["proto"],
+        ),
+        "plugin": attr.label(
+            executable = True,
+            providers = ["files_to_run"],
+            cfg = "host",
+        ),
+        "flags": attr.string_list(
+            mandatory = False,
+            allow_empty = True,
+        ),
+        "well_known_protos": attr.label(mandatory = False),
+        "_protoc": attr.label(
+            default = Label("//external:protocol_compiler"),
+            executable = True,
+            cfg = "host",
+        ),
+    },
+    output_to_genfiles = True,
+    implementation = _generate_py_impl,
+)
+
+def _generate_py(well_known_protos, **kwargs):
+    if well_known_protos:
+        __generate_py(
+            well_known_protos = "@com_google_protobuf//:well_known_protos",
+            **kwargs
+        )
+    else:
+        __generate_py(**kwargs)
+
+def py_proto_library(
+        name,
+        deps,
+        well_known_protos = True,
+        proto_only = False,
+        **kwargs):
+    """Generate python code for a protobuf.
+
+    Args:
+      name: The name of the target.
+      deps: A list of dependencies. Must contain a single element.
+      well_known_protos: A bool indicating whether or not to include well-known
+        protos.
+      proto_only: A bool indicating whether to generate vanilla protobuf code
+        or to also generate gRPC code.
+    """
+    if len(deps) > 1:
+        fail("The supported length of 'deps' is 1.")
+
+    codegen_target = "_{}_codegen".format(name)
+    codegen_grpc_target = "_{}_grpc_codegen".format(name)
+
+    _generate_py(
+        name = codegen_target,
+        deps = deps,
+        well_known_protos = well_known_protos,
+        **kwargs
+    )
+
+    if not proto_only:
+        _generate_py(
+            name = codegen_grpc_target,
+            deps = deps,
+            plugin = "//:grpc_python_plugin",
+            well_known_protos = well_known_protos,
+            **kwargs
+        )
+
+        native.py_library(
+            name = name,
+            srcs = [
+                ":{}".format(codegen_grpc_target),
+                ":{}".format(codegen_target),
+            ],
+            deps = [requirement("protobuf")],
+            **kwargs
+        )
+    else:
+        native.py_library(
+            name = name,
+            srcs = [":{}".format(codegen_target), ":{}".format(codegen_target)],
+            deps = [requirement("protobuf")],
+            **kwargs
+        )
index f647e42..39f55d4 100644 (file)
@@ -13,8 +13,8 @@ settings:
   '#09': Per-language overrides are possible with (eg) ruby_version tag here
   '#10': See the expand_version.py for all the quirks here
   core_version: 7.0.0
-  g_stands_for: godric
-  version: 1.20.1
+  g_stands_for: gandalf
+  version: 1.21.0
 filegroups:
 - name: alts_proto
   headers:
@@ -114,7 +114,6 @@ filegroups:
 - name: gpr_base
   src:
   - src/core/lib/gpr/alloc.cc
-  - src/core/lib/gpr/arena.cc
   - src/core/lib/gpr/atm.cc
   - src/core/lib/gpr/cpu_iphone.cc
   - src/core/lib/gpr/cpu_linux.cc
@@ -147,7 +146,9 @@ filegroups:
   - src/core/lib/gpr/tmpfile_posix.cc
   - src/core/lib/gpr/tmpfile_windows.cc
   - src/core/lib/gpr/wrap_memcpy.cc
+  - src/core/lib/gprpp/arena.cc
   - src/core/lib/gprpp/fork.cc
+  - src/core/lib/gprpp/global_config_env.cc
   - src/core/lib/gprpp/thd_posix.cc
   - src/core/lib/gprpp/thd_windows.cc
   - src/core/lib/profiling/basic_timers.cc
@@ -191,11 +192,18 @@ filegroups:
   - src/core/lib/gpr/tmpfile.h
   - src/core/lib/gpr/useful.h
   - src/core/lib/gprpp/abstract.h
+  - src/core/lib/gprpp/arena.h
   - src/core/lib/gprpp/atomic.h
   - src/core/lib/gprpp/fork.h
+  - src/core/lib/gprpp/global_config.h
+  - src/core/lib/gprpp/global_config_custom.h
+  - src/core/lib/gprpp/global_config_env.h
+  - src/core/lib/gprpp/global_config_generic.h
   - src/core/lib/gprpp/manual_constructor.h
+  - src/core/lib/gprpp/map.h
   - src/core/lib/gprpp/memory.h
-  - src/core/lib/gprpp/mutex_lock.h
+  - src/core/lib/gprpp/pair.h
+  - src/core/lib/gprpp/sync.h
   - src/core/lib/gprpp/thd.h
   - src/core/lib/profiling/timers.h
   uses:
@@ -243,6 +251,7 @@ filegroups:
   - src/core/lib/channel/handshaker_registry.cc
   - src/core/lib/channel/status_util.cc
   - src/core/lib/compression/compression.cc
+  - src/core/lib/compression/compression_args.cc
   - src/core/lib/compression/compression_internal.cc
   - src/core/lib/compression/message_compress.cc
   - src/core/lib/compression/stream_compression.cc
@@ -255,12 +264,15 @@ filegroups:
   - src/core/lib/http/parser.cc
   - src/core/lib/iomgr/buffer_list.cc
   - src/core/lib/iomgr/call_combiner.cc
+  - src/core/lib/iomgr/cfstream_handle.cc
   - src/core/lib/iomgr/combiner.cc
   - src/core/lib/iomgr/endpoint.cc
+  - src/core/lib/iomgr/endpoint_cfstream.cc
   - src/core/lib/iomgr/endpoint_pair_posix.cc
   - src/core/lib/iomgr/endpoint_pair_uv.cc
   - src/core/lib/iomgr/endpoint_pair_windows.cc
   - src/core/lib/iomgr/error.cc
+  - src/core/lib/iomgr/error_cfstream.cc
   - src/core/lib/iomgr/ev_epoll1_linux.cc
   - src/core/lib/iomgr/ev_epollex_linux.cc
   - src/core/lib/iomgr/ev_poll_posix.cc
@@ -281,6 +293,7 @@ filegroups:
   - src/core/lib/iomgr/iomgr_custom.cc
   - src/core/lib/iomgr/iomgr_internal.cc
   - src/core/lib/iomgr/iomgr_posix.cc
+  - src/core/lib/iomgr/iomgr_posix_cfstream.cc
   - src/core/lib/iomgr/iomgr_uv.cc
   - src/core/lib/iomgr/iomgr_windows.cc
   - src/core/lib/iomgr/is_epollexclusive_available.cc
@@ -309,6 +322,7 @@ filegroups:
   - src/core/lib/iomgr/socket_utils_windows.cc
   - src/core/lib/iomgr/socket_windows.cc
   - src/core/lib/iomgr/tcp_client.cc
+  - src/core/lib/iomgr/tcp_client_cfstream.cc
   - src/core/lib/iomgr/tcp_client_custom.cc
   - src/core/lib/iomgr/tcp_client_posix.cc
   - src/core/lib/iomgr/tcp_client_windows.cc
@@ -416,6 +430,7 @@ filegroups:
   - src/core/lib/channel/handshaker_registry.h
   - src/core/lib/channel/status_util.h
   - src/core/lib/compression/algorithm_metadata.h
+  - src/core/lib/compression/compression_args.h
   - src/core/lib/compression/compression_internal.h
   - src/core/lib/compression/message_compress.h
   - src/core/lib/compression/stream_compression.h
@@ -435,12 +450,15 @@ filegroups:
   - src/core/lib/iomgr/block_annotate.h
   - src/core/lib/iomgr/buffer_list.h
   - src/core/lib/iomgr/call_combiner.h
+  - src/core/lib/iomgr/cfstream_handle.h
   - src/core/lib/iomgr/closure.h
   - src/core/lib/iomgr/combiner.h
   - src/core/lib/iomgr/dynamic_annotations.h
   - src/core/lib/iomgr/endpoint.h
+  - src/core/lib/iomgr/endpoint_cfstream.h
   - src/core/lib/iomgr/endpoint_pair.h
   - src/core/lib/iomgr/error.h
+  - src/core/lib/iomgr/error_cfstream.h
   - src/core/lib/iomgr/error_internal.h
   - src/core/lib/iomgr/ev_epoll1_linux.h
   - src/core/lib/iomgr/ev_epollex_linux.h
@@ -541,20 +559,6 @@ filegroups:
   uses:
   - grpc_codegen
   - grpc_trace_headers
-- name: grpc_cfstream
-  headers:
-  - src/core/lib/iomgr/cfstream_handle.h
-  - src/core/lib/iomgr/endpoint_cfstream.h
-  - src/core/lib/iomgr/error_cfstream.h
-  src:
-  - src/core/lib/iomgr/cfstream_handle.cc
-  - src/core/lib/iomgr/endpoint_cfstream.cc
-  - src/core/lib/iomgr/error_cfstream.cc
-  - src/core/lib/iomgr/iomgr_posix_cfstream.cc
-  - src/core/lib/iomgr/tcp_client_cfstream.cc
-  uses:
-  - grpc_base_headers
-  - gpr_base_headers
 - name: grpc_client_authority_filter
   headers:
   - src/core/ext/filters/http/client_authority_filter.h
@@ -779,16 +783,19 @@ filegroups:
   src:
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc
+  - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc
+  - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc
   plugin: grpc_resolver_dns_ares
   uses:
   - grpc_base
   - grpc_client_channel
+  - grpc_resolver_dns_selection
 - name: grpc_resolver_dns_native
   src:
   - src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc
@@ -796,6 +803,14 @@ filegroups:
   uses:
   - grpc_base
   - grpc_client_channel
+  - grpc_resolver_dns_selection
+- name: grpc_resolver_dns_selection
+  headers:
+  - src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h
+  src:
+  - src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc
+  uses:
+  - grpc_base
 - name: grpc_resolver_fake
   headers:
   - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h
@@ -1246,6 +1261,7 @@ filegroups:
   - include/grpcpp/impl/codegen/client_interceptor.h
   - include/grpcpp/impl/codegen/client_unary_call.h
   - include/grpcpp/impl/codegen/completion_queue.h
+  - include/grpcpp/impl/codegen/completion_queue_impl.h
   - include/grpcpp/impl/codegen/completion_queue_tag.h
   - include/grpcpp/impl/codegen/config.h
   - include/grpcpp/impl/codegen/core_codegen_interface.h
@@ -1254,6 +1270,7 @@ filegroups:
   - include/grpcpp/impl/codegen/intercepted_channel.h
   - include/grpcpp/impl/codegen/interceptor.h
   - include/grpcpp/impl/codegen/interceptor_common.h
+  - include/grpcpp/impl/codegen/message_allocator.h
   - include/grpcpp/impl/codegen/metadata_map.h
   - include/grpcpp/impl/codegen/method_handler_impl.h
   - include/grpcpp/impl/codegen/rpc_method.h
@@ -1274,6 +1291,7 @@ filegroups:
   - include/grpcpp/impl/codegen/time.h
   uses:
   - grpc_codegen
+  - grpc++_internal_hdrs_only
 - name: grpc++_codegen_base_src
   language: c++
   src:
@@ -1341,16 +1359,20 @@ filegroups:
   - include/grpcpp/alarm.h
   - include/grpcpp/alarm_impl.h
   - include/grpcpp/channel.h
+  - include/grpcpp/channel_impl.h
   - include/grpcpp/client_context.h
   - include/grpcpp/completion_queue.h
   - include/grpcpp/create_channel.h
+  - include/grpcpp/create_channel_impl.h
   - include/grpcpp/create_channel_posix.h
   - include/grpcpp/create_channel_posix_impl.h
   - include/grpcpp/ext/health_check_service_server_builder_option.h
   - include/grpcpp/generic/async_generic_service.h
   - include/grpcpp/generic/generic_stub.h
+  - include/grpcpp/generic/generic_stub_impl.h
   - include/grpcpp/grpcpp.h
   - include/grpcpp/health_check_service_interface.h
+  - include/grpcpp/health_check_service_interface_impl.h
   - include/grpcpp/impl/call.h
   - include/grpcpp/impl/channel_argument_option.h
   - include/grpcpp/impl/client_unary_call.h
@@ -1364,25 +1386,34 @@ filegroups:
   - include/grpcpp/impl/server_builder_option_impl.h
   - include/grpcpp/impl/server_builder_plugin.h
   - include/grpcpp/impl/server_initializer.h
+  - include/grpcpp/impl/server_initializer_impl.h
   - include/grpcpp/impl/service_type.h
   - include/grpcpp/resource_quota.h
+  - include/grpcpp/resource_quota_impl.h
   - include/grpcpp/security/auth_context.h
   - include/grpcpp/security/auth_metadata_processor.h
+  - include/grpcpp/security/auth_metadata_processor_impl.h
   - include/grpcpp/security/credentials.h
+  - include/grpcpp/security/credentials_impl.h
   - include/grpcpp/security/server_credentials.h
+  - include/grpcpp/security/server_credentials_impl.h
   - include/grpcpp/server.h
   - include/grpcpp/server_builder.h
+  - include/grpcpp/server_builder_impl.h
   - include/grpcpp/server_context.h
+  - 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_unary_call.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_interceptor.h
   - include/grpcpp/support/config.h
   - include/grpcpp/support/interceptor.h
+  - 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
@@ -1440,6 +1471,7 @@ filegroups:
   - grpc_base_headers
   - grpc_transport_inproc_headers
   - grpc++_codegen_base
+  - grpc++_internal_hdrs_only
   - nanopb_headers
   - health_proto
 - name: grpc++_config_proto
@@ -1447,6 +1479,10 @@ filegroups:
   public_headers:
   - include/grpc++/impl/codegen/config_protobuf.h
   - include/grpcpp/impl/codegen/config_protobuf.h
+- name: grpc++_internal_hdrs_only
+  language: c++
+  public_headers:
+  - include/grpcpp/impl/codegen/sync.h
 - name: grpc++_reflection_proto
   language: c++
   src:
@@ -1650,6 +1686,13 @@ libs:
   - grpc_test_util
   - grpc
   - gpr
+- name: dns_test_util
+  build: private
+  language: c++
+  headers:
+  - test/cpp/naming/dns_test_util.h
+  src:
+  - test/cpp/naming/dns_test_util.cc
 - name: grpc++
   build: all
   language: c++
@@ -1717,6 +1760,7 @@ libs:
   public_headers:
   - include/grpc++/support/error_details.h
   - include/grpcpp/support/error_details.h
+  - include/grpcpp/support/error_details_impl.h
   src:
   - src/proto/grpc/status/status.proto
   - src/cpp/util/error_details.cc
@@ -1742,6 +1786,7 @@ libs:
   public_headers:
   - include/grpc++/ext/proto_server_reflection_plugin.h
   - include/grpcpp/ext/proto_server_reflection_plugin.h
+  - include/grpcpp/ext/proto_server_reflection_plugin_impl.h
   headers:
   - src/cpp/ext/proto_server_reflection.h
   src:
@@ -4500,6 +4545,7 @@ targets:
   src:
   - test/cpp/end2end/client_crash_test_server.cc
   deps:
+  - grpc++_test_config
   - grpc++_test_util
   - grpc_test_util
   - grpc++
@@ -4704,6 +4750,24 @@ targets:
   - grpc++
   - grpc
   - gpr
+- name: global_config_env_test
+  build: test
+  language: c++
+  src:
+  - test/core/gprpp/global_config_env_test.cc
+  deps:
+  - gpr
+  - grpc_test_util_unsecure
+  uses_polling: false
+- name: global_config_test
+  build: test
+  language: c++
+  src:
+  - test/core/gprpp/global_config_test.cc
+  deps:
+  - gpr
+  - grpc_test_util_unsecure
+  uses_polling: false
 - name: golden_file_test
   gtest: true
   build: test
@@ -4712,6 +4776,7 @@ targets:
   - src/proto/grpc/testing/compiler_test.proto
   - test/cpp/codegen/golden_file_test.cc
   deps:
+  - grpc++_test_config
   - grpc++
   - grpc
   - gpr
@@ -4739,6 +4804,22 @@ targets:
   - grpc
   - gpr
   - grpc++_test_config
+- name: grpc_core_map_test
+  gtest: true
+  build: test
+  language: c++
+  headers:
+  - test/core/gprpp/map_tester.h
+  src:
+  - test/core/gprpp/map_test.cc
+  deps:
+  - grpc_test_util
+  - grpc++
+  - grpc
+  - gpr
+  uses:
+  - grpc++_test
+  uses_polling: false
 - name: grpc_cpp_plugin
   build: protoc
   language: c++
@@ -5033,6 +5114,19 @@ targets:
   uses:
   - grpc++_test
   uses_polling: false
+- name: message_allocator_end2end_test
+  gtest: true
+  cpu_cost: 0.5
+  build: test
+  language: c++
+  src:
+  - test/cpp/end2end/message_allocator_end2end_test.cc
+  deps:
+  - grpc++_test_util
+  - grpc_test_util
+  - grpc++
+  - grpc
+  - gpr
 - name: metrics_client
   build: test
   run: false
@@ -5398,6 +5492,7 @@ targets:
   src:
   - test/cpp/end2end/server_crash_test_client.cc
   deps:
+  - grpc++_test_config
   - grpc++_test_util
   - grpc_test_util
   - grpc++
@@ -5445,6 +5540,31 @@ targets:
   - grpc++_unsecure
   - grpc_unsecure
   - gpr
+- name: service_config_end2end_test
+  gtest: true
+  build: test
+  language: c++
+  src:
+  - test/cpp/end2end/service_config_end2end_test.cc
+  deps:
+  - grpc++_test_util
+  - grpc_test_util
+  - grpc++
+  - grpc
+  - gpr
+- name: service_config_test
+  gtest: true
+  build: test
+  language: c++
+  src:
+  - test/core/client_channel/service_config_test.cc
+  deps:
+  - grpc_test_util
+  - grpc++
+  - grpc
+  - gpr
+  uses:
+  - grpc++_test
 - name: shutdown_test
   gtest: true
   build: test
index 2c64a3e..bb30be5 100644 (file)
--- a/config.m4
+++ b/config.m4
@@ -45,7 +45,6 @@ if test "$PHP_GRPC" != "no"; then
     third_party/address_sorting/address_sorting_posix.c \
     third_party/address_sorting/address_sorting_windows.c \
     src/core/lib/gpr/alloc.cc \
-    src/core/lib/gpr/arena.cc \
     src/core/lib/gpr/atm.cc \
     src/core/lib/gpr/cpu_iphone.cc \
     src/core/lib/gpr/cpu_linux.cc \
@@ -78,7 +77,9 @@ if test "$PHP_GRPC" != "no"; then
     src/core/lib/gpr/tmpfile_posix.cc \
     src/core/lib/gpr/tmpfile_windows.cc \
     src/core/lib/gpr/wrap_memcpy.cc \
+    src/core/lib/gprpp/arena.cc \
     src/core/lib/gprpp/fork.cc \
+    src/core/lib/gprpp/global_config_env.cc \
     src/core/lib/gprpp/thd_posix.cc \
     src/core/lib/gprpp/thd_windows.cc \
     src/core/lib/profiling/basic_timers.cc \
@@ -97,6 +98,7 @@ if test "$PHP_GRPC" != "no"; then
     src/core/lib/channel/handshaker_registry.cc \
     src/core/lib/channel/status_util.cc \
     src/core/lib/compression/compression.cc \
+    src/core/lib/compression/compression_args.cc \
     src/core/lib/compression/compression_internal.cc \
     src/core/lib/compression/message_compress.cc \
     src/core/lib/compression/stream_compression.cc \
@@ -109,12 +111,15 @@ if test "$PHP_GRPC" != "no"; then
     src/core/lib/http/parser.cc \
     src/core/lib/iomgr/buffer_list.cc \
     src/core/lib/iomgr/call_combiner.cc \
+    src/core/lib/iomgr/cfstream_handle.cc \
     src/core/lib/iomgr/combiner.cc \
     src/core/lib/iomgr/endpoint.cc \
+    src/core/lib/iomgr/endpoint_cfstream.cc \
     src/core/lib/iomgr/endpoint_pair_posix.cc \
     src/core/lib/iomgr/endpoint_pair_uv.cc \
     src/core/lib/iomgr/endpoint_pair_windows.cc \
     src/core/lib/iomgr/error.cc \
+    src/core/lib/iomgr/error_cfstream.cc \
     src/core/lib/iomgr/ev_epoll1_linux.cc \
     src/core/lib/iomgr/ev_epollex_linux.cc \
     src/core/lib/iomgr/ev_poll_posix.cc \
@@ -135,6 +140,7 @@ if test "$PHP_GRPC" != "no"; then
     src/core/lib/iomgr/iomgr_custom.cc \
     src/core/lib/iomgr/iomgr_internal.cc \
     src/core/lib/iomgr/iomgr_posix.cc \
+    src/core/lib/iomgr/iomgr_posix_cfstream.cc \
     src/core/lib/iomgr/iomgr_uv.cc \
     src/core/lib/iomgr/iomgr_windows.cc \
     src/core/lib/iomgr/is_epollexclusive_available.cc \
@@ -163,6 +169,7 @@ if test "$PHP_GRPC" != "no"; then
     src/core/lib/iomgr/socket_utils_windows.cc \
     src/core/lib/iomgr/socket_windows.cc \
     src/core/lib/iomgr/tcp_client.cc \
+    src/core/lib/iomgr/tcp_client_cfstream.cc \
     src/core/lib/iomgr/tcp_client_custom.cc \
     src/core/lib/iomgr/tcp_client_posix.cc \
     src/core/lib/iomgr/tcp_client_windows.cc \
@@ -396,12 +403,15 @@ if test "$PHP_GRPC" != "no"; then
     src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc \
+    src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc \
+    src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc \
+    src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc \
     src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc \
     src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc \
     src/core/ext/filters/census/grpc_context.cc \
@@ -685,6 +695,7 @@ if test "$PHP_GRPC" != "no"; then
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/client_channel/lb_policy/pick_first)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/client_channel/lb_policy/round_robin)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/client_channel/lb_policy/xds)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/client_channel/resolver/dns)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/client_channel/resolver/dns/c_ares)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/client_channel/resolver/dns/native)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/client_channel/resolver/fake)
index 6897c10..c9faa8d 100644 (file)
@@ -20,7 +20,6 @@ if (PHP_GRPC != "no") {
     "third_party\\address_sorting\\address_sorting_posix.c " +
     "third_party\\address_sorting\\address_sorting_windows.c " +
     "src\\core\\lib\\gpr\\alloc.cc " +
-    "src\\core\\lib\\gpr\\arena.cc " +
     "src\\core\\lib\\gpr\\atm.cc " +
     "src\\core\\lib\\gpr\\cpu_iphone.cc " +
     "src\\core\\lib\\gpr\\cpu_linux.cc " +
@@ -53,7 +52,9 @@ if (PHP_GRPC != "no") {
     "src\\core\\lib\\gpr\\tmpfile_posix.cc " +
     "src\\core\\lib\\gpr\\tmpfile_windows.cc " +
     "src\\core\\lib\\gpr\\wrap_memcpy.cc " +
+    "src\\core\\lib\\gprpp\\arena.cc " +
     "src\\core\\lib\\gprpp\\fork.cc " +
+    "src\\core\\lib\\gprpp\\global_config_env.cc " +
     "src\\core\\lib\\gprpp\\thd_posix.cc " +
     "src\\core\\lib\\gprpp\\thd_windows.cc " +
     "src\\core\\lib\\profiling\\basic_timers.cc " +
@@ -72,6 +73,7 @@ if (PHP_GRPC != "no") {
     "src\\core\\lib\\channel\\handshaker_registry.cc " +
     "src\\core\\lib\\channel\\status_util.cc " +
     "src\\core\\lib\\compression\\compression.cc " +
+    "src\\core\\lib\\compression\\compression_args.cc " +
     "src\\core\\lib\\compression\\compression_internal.cc " +
     "src\\core\\lib\\compression\\message_compress.cc " +
     "src\\core\\lib\\compression\\stream_compression.cc " +
@@ -84,12 +86,15 @@ if (PHP_GRPC != "no") {
     "src\\core\\lib\\http\\parser.cc " +
     "src\\core\\lib\\iomgr\\buffer_list.cc " +
     "src\\core\\lib\\iomgr\\call_combiner.cc " +
+    "src\\core\\lib\\iomgr\\cfstream_handle.cc " +
     "src\\core\\lib\\iomgr\\combiner.cc " +
     "src\\core\\lib\\iomgr\\endpoint.cc " +
+    "src\\core\\lib\\iomgr\\endpoint_cfstream.cc " +
     "src\\core\\lib\\iomgr\\endpoint_pair_posix.cc " +
     "src\\core\\lib\\iomgr\\endpoint_pair_uv.cc " +
     "src\\core\\lib\\iomgr\\endpoint_pair_windows.cc " +
     "src\\core\\lib\\iomgr\\error.cc " +
+    "src\\core\\lib\\iomgr\\error_cfstream.cc " +
     "src\\core\\lib\\iomgr\\ev_epoll1_linux.cc " +
     "src\\core\\lib\\iomgr\\ev_epollex_linux.cc " +
     "src\\core\\lib\\iomgr\\ev_poll_posix.cc " +
@@ -110,6 +115,7 @@ if (PHP_GRPC != "no") {
     "src\\core\\lib\\iomgr\\iomgr_custom.cc " +
     "src\\core\\lib\\iomgr\\iomgr_internal.cc " +
     "src\\core\\lib\\iomgr\\iomgr_posix.cc " +
+    "src\\core\\lib\\iomgr\\iomgr_posix_cfstream.cc " +
     "src\\core\\lib\\iomgr\\iomgr_uv.cc " +
     "src\\core\\lib\\iomgr\\iomgr_windows.cc " +
     "src\\core\\lib\\iomgr\\is_epollexclusive_available.cc " +
@@ -138,6 +144,7 @@ if (PHP_GRPC != "no") {
     "src\\core\\lib\\iomgr\\socket_utils_windows.cc " +
     "src\\core\\lib\\iomgr\\socket_windows.cc " +
     "src\\core\\lib\\iomgr\\tcp_client.cc " +
+    "src\\core\\lib\\iomgr\\tcp_client_cfstream.cc " +
     "src\\core\\lib\\iomgr\\tcp_client_custom.cc " +
     "src\\core\\lib\\iomgr\\tcp_client_posix.cc " +
     "src\\core\\lib\\iomgr\\tcp_client_windows.cc " +
@@ -371,12 +378,15 @@ if (PHP_GRPC != "no") {
     "src\\core\\ext\\filters\\client_channel\\lb_policy\\round_robin\\round_robin.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\dns_resolver_ares.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_ev_driver.cc " +
+    "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_ev_driver_libuv.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_ev_driver_posix.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_ev_driver_windows.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_wrapper.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_wrapper_fallback.cc " +
+    "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_wrapper_libuv.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_wrapper_posix.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_wrapper_windows.cc " +
+    "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\dns_resolver_selection.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\native\\dns_resolver.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\sockaddr\\sockaddr_resolver.cc " +
     "src\\core\\ext\\filters\\census\\grpc_context.cc " +
index a354dad..e73e682 100644 (file)
@@ -61,6 +61,8 @@ the form shown above.
 
 If **Timeout** is omitted a server should assume an infinite timeout. Client implementations are free to send a default minimum timeout based on their deployment requirements.
 
+If **Content-Type** does not begin with "application/grpc", gRPC servers SHOULD respond with HTTP status of 415 (Unsupported Media Type).  This will prevent other HTTP/2 clients from interpreting a gRPC error response, which uses status 200 (OK), as successful.
+
 **Custom-Metadata** is an arbitrary set of key-value pairs defined by the application layer. Header names starting with "grpc-" but not listed here are reserved for future GRPC use and should not be used by applications as **Custom-Metadata**.
 
 Note that HTTP2 does not allow arbitrary octet sequences for header values so binary header values must be encoded using Base64 as per https://tools.ietf.org/html/rfc4648#section-4. Implementations MUST accept padded and un-padded values and should emit un-padded values. Applications define binary headers by having their names end with "-bin". Runtime libraries use this suffix to detect binary headers and properly apply base64 encoding & decoding as headers are sent and received.
@@ -255,5 +257,3 @@ to be used.
 * **Service-Name** â†’ ?( {_proto package name_} "." ) {_service name_}
 * **Message-Type** â†’ {_fully qualified proto message name_}
 * **Content-Type** â†’ "application/grpc+proto"
-
-
index dd5a765..f93f72b 100644 (file)
@@ -135,7 +135,7 @@ Code at `src/core/lib/iomgr/ev_epollex_posix.cc`
   - The same FD can be in multiple `Pollable`s (even if one of the `Pollable`s is of type PO_FD)
   - There cannot be two `Pollable`s of type PO_FD for the same fd
 
-- Why do we need `Pollable` of type PO_FD and PO_EMTPY ?
+- Why do we need `Pollable` of type PO_FD and PO_EMPTY ?
   - The main reason is the Sync client API
     - We create one new completion queue per call. If we didn’t have PO_EMPTY and PO_FD type pollables, then every call on a given channel will effectively have to create a `Pollable` and hence an epollset. This is because every completion queue automatically creates a pollset and the channel fd will have to be put in that pollset. This clearly requires an epollset to put that fd. Creating an epollset per call (even if we delete the epollset once the call is completed) would mean a lot of sys calls to create/delete epoll fds. This is clearly not a good idea.
     - With these new types of `Pollable`s, all pollsets (corresponding to the new per-call completion queue) will initially point to PO_EMPTY global epollset. Then once the channel fd is added to the pollset, the pollset will point to the `Pollable` of type PO_FD containing just that fd (i.e it will reuse the existing `Pollable`). This way, the epoll fd creation/deletion churn is avoided.
index e8d0dbd..778560d 100644 (file)
@@ -50,6 +50,7 @@ some configuration as environment variables that can be set.
     resolver and load balancing policy interaction
   - compression - traces compression operations
   - connectivity_state - traces connectivity state changes to channels
+  - cronet - traces state in the cronet transport engine
   - executor - traces grpc's internal thread pool ('the executor')
   - fd_trace - traces fd create(), shutdown() and close() calls for channel fds.
     Also traces epoll fd create()/close() calls in epollex polling engine
@@ -145,13 +146,3 @@ some configuration as environment variables that can be set.
 * grpc_cfstream
   set to 1 to turn on CFStream experiment. With this experiment gRPC uses CFStream API to make TCP
   connections. The option is only available on iOS platform and when macro GRPC_CFSTREAM is defined.
-
-* GRPC_ARENA_INIT_STRATEGY
-  Selects the initialization strategy for blocks allocated in the arena. Valid
-  values are:
-  - no_init (default): Do not initialize the arena block.
-  - zero_init: Initialize the arena blocks with 0.
-  - non_zero_init: Initialize the arena blocks with a non-zero value.
-
-  NOTE: This environment variable is experimental and will be removed. Thus, it
-        should not be relied upon.
index 5c5579d..ba0ba58 100644 (file)
@@ -20,3 +20,4 @@
 - 1.18 'g' stands for ['goose'](https://github.com/grpc/grpc/tree/v1.18.x)
 - 1.19 'g' stands for ['gold'](https://github.com/grpc/grpc/tree/v1.19.x)
 - 1.20 'g' stands for ['godric'](https://github.com/grpc/grpc/tree/v1.20.x)
+- 1.21 'g' stands for ['gandalf'](https://github.com/grpc/grpc/tree/v1.21.x)
index f534d25..0934db7 100644 (file)
@@ -172,3 +172,9 @@ Future Interfaces
 .. autoexception:: FutureTimeoutError
 .. autoexception:: FutureCancelledError
 .. autoclass:: Future
+
+
+Compression
+^^^^^^^^^^^
+
+.. autoclass:: Compression
index 3d4d87e..61e0d82 100644 (file)
@@ -20,7 +20,7 @@ statuses are defined as such:
 | OUT_OF_RANGE | 11 | The operation was attempted past the valid range. E.g., seeking or reading past end-of-file. Unlike `INVALID_ARGUMENT`, this error indicates a problem that may be fixed if the system state changes. For example, a 32-bit file system will generate `INVALID_ARGUMENT` if asked to read at an offset that is not in the range [0,2^32-1], but it will generate `OUT_OF_RANGE` if asked to read from an offset past the current file size. There is a fair bit of overlap between `FAILED_PRECONDITION` and `OUT_OF_RANGE`. We recommend using `OUT_OF_RANGE` (the more specific error) when it applies so that callers who are iterating through a space can easily look for an `OUT_OF_RANGE` error to detect when they are done. | 400 Bad Request |
 | UNIMPLEMENTED | 12 | The operation is not implemented or is not supported/enabled in this service. | 501 Not Implemented |
 | INTERNAL | 13 | Internal errors. This means that some invariants expected by the underlying system have been broken. This error code is reserved for serious errors. | 500 Internal Server Error |
-| UNAVAILABLE | 14 | The service is currently unavailable. This is most likely a transient condition, which can be corrected by retrying with a backoff. | 503 Service Unavailable |
+| UNAVAILABLE | 14 | The service is currently unavailable. This is most likely a transient condition, which can be corrected by retrying with a backoff. Note that it is not always safe to retry non-idempotent operations. | 503 Service Unavailable |
 | DATA_LOSS | 15 | Unrecoverable data loss or corruption. | 500 Internal Server Error |
 
 All RPCs started at a client return a `status` object composed of an integer
index be59871..22bd2ab 100644 (file)
@@ -4552,3 +4552,149 @@ Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh
 jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw
 3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0=
 -----END CERTIFICATE-----
+
+# Issuer: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI
+# Subject: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI
+# Label: "emSign Root CA - G1"
+# Serial: 235931866688319308814040
+# MD5 Fingerprint: 9c:42:84:57:dd:cb:0b:a7:2e:95:ad:b6:f3:da:bc:ac
+# SHA1 Fingerprint: 8a:c7:ad:8f:73:ac:4e:c1:b5:75:4d:a5:40:f4:fc:cf:7c:b5:8e:8c
+# SHA256 Fingerprint: 40:f6:af:03:46:a9:9a:a1:cd:1d:55:5a:4e:9c:ce:62:c7:f9:63:46:03:ee:40:66:15:83:3d:c8:c8:d0:03:67
+-----BEGIN CERTIFICATE-----
+MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD
+VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU
+ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH
+MTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklO
+MRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVkaHJhIFRlY2hub2xv
+Z2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQz
+f2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO
+8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq
+d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhM
+tTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSzt
+Od9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQAB
+o0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQD
+AgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31x
+PaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjM
+wiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6d
+GNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8EI6/6nAYH
+6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby
+RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx
+iN66zB+Afko=
+-----END CERTIFICATE-----
+
+# Issuer: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI
+# Subject: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI
+# Label: "emSign ECC Root CA - G3"
+# Serial: 287880440101571086945156
+# MD5 Fingerprint: ce:0b:72:d1:9f:88:8e:d0:50:03:e8:e3:b8:8b:67:40
+# SHA1 Fingerprint: 30:43:fa:4f:f2:57:dc:a0:c3:80:ee:2e:58:ea:78:b2:3f:e6:bb:c1
+# SHA256 Fingerprint: 86:a1:ec:ba:08:9c:4a:8d:3b:be:27:34:c6:12:ba:34:1d:81:3e:04:3c:f9:e8:a8:62:cd:5c:57:a3:6b:be:6b
+-----BEGIN CERTIFICATE-----
+MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG
+EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo
+bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g
+RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ
+TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s
+b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw
+djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0
+WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS
+fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB
+zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq
+hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB
+CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD
++JbNR6iC8hZVdyR+EhCVBCyj
+-----END CERTIFICATE-----
+
+# Issuer: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI
+# Subject: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI
+# Label: "emSign Root CA - C1"
+# Serial: 825510296613316004955058
+# MD5 Fingerprint: d8:e3:5d:01:21:fa:78:5a:b0:df:ba:d2:ee:2a:5f:68
+# SHA1 Fingerprint: e7:2e:f1:df:fc:b2:09:28:cf:5d:d4:d5:67:37:b1:51:cb:86:4f:01
+# SHA256 Fingerprint: 12:56:09:aa:30:1d:a0:a2:49:b9:7a:82:39:cb:6a:34:21:6f:44:dc:ac:9f:39:54:b1:42:92:f2:e8:c8:60:8f
+-----BEGIN CERTIFICATE-----
+MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkG
+A1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg
+SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAw
+MFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln
+biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNpZ24gUm9v
+dCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+upufGZ
+BczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZ
+HdPIWoU/Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH
+3DspVpNqs8FqOp099cGXOFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvH
+GPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4VI5b2P/AgNBbeCsbEBEV5f6f9vtKppa+c
+xSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleoomslMuoaJuvimUnzYnu3Yy1
+aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+XJGFehiq
+TbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
+BQADggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87
+/kOXSTKZEhVb3xEp/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4
+kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrG
+YQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcxHpvOJpkT
++xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo
+WXzhriKi4gp6D/piq1JM4fHfyr6DDUI=
+-----END CERTIFICATE-----
+
+# Issuer: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI
+# Subject: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI
+# Label: "emSign ECC Root CA - C3"
+# Serial: 582948710642506000014504
+# MD5 Fingerprint: 3e:53:b3:a3:81:ee:d7:10:f8:d3:b0:1d:17:92:f5:d5
+# SHA1 Fingerprint: b6:af:43:c2:9b:81:53:7d:f6:ef:6b:c3:1f:1f:60:15:0c:ee:48:66
+# SHA256 Fingerprint: bc:4d:80:9b:15:18:9d:78:db:3e:1d:8c:f4:f9:72:6a:79:5d:a1:64:3c:a5:f1:35:8e:1d:db:0e:dc:0d:7e:b3
+-----BEGIN CERTIFICATE-----
+MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQG
+EwJVUzETMBEGA1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMx
+IDAeBgNVBAMTF2VtU2lnbiBFQ0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAw
+MFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln
+biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQDExdlbVNpZ24gRUND
+IFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd6bci
+MK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4Ojavti
+sIGJAnB9SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0O
+BBYEFPtaSNCAIEDyqOkAB2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB
+Af8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQC02C8Cif22TGK6Q04ThHK1rt0c
+3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwUZOR8loMRnLDRWmFLpg9J
+0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Hongkong Post Root CA 3 O=Hongkong Post
+# Subject: CN=Hongkong Post Root CA 3 O=Hongkong Post
+# Label: "Hongkong Post Root CA 3"
+# Serial: 46170865288971385588281144162979347873371282084
+# MD5 Fingerprint: 11:fc:9f:bd:73:30:02:8a:fd:3f:f3:58:b9:cb:20:f0
+# SHA1 Fingerprint: 58:a2:d0:ec:20:52:81:5b:c1:f3:f8:64:02:24:4e:c2:8e:02:4b:02
+# SHA256 Fingerprint: 5a:2f:c0:3f:0c:83:b0:90:bb:fa:40:60:4b:09:88:44:6c:76:36:18:3d:f9:84:6e:17:10:1a:44:7f:b8:ef:d6
+-----BEGIN CERTIFICATE-----
+MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQEL
+BQAwbzELMAkGA1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJ
+SG9uZyBLb25nMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25n
+a29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2MDMwMjI5NDZaFw00MjA2MDMwMjI5
+NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtvbmcxEjAQBgNVBAcT
+CUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMXSG9u
+Z2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
+AoICAQCziNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFO
+dem1p+/l6TWZ5Mwc50tfjTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mI
+VoBc+L0sPOFMV4i707mV78vH9toxdCim5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV
+9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOesL4jpNrcyCse2m5FHomY
+2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj0mRiikKY
+vLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+Tt
+bNe/JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZb
+x39ri1UbSsUgYT2uy1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+
+l2oBlKN8W4UdKjk60FSh0Tlxnf0h+bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YK
+TE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsGxVd7GYYKecsAyVKvQv83j+Gj
+Hno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwIDAQABo2MwYTAP
+BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e
+i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEw
+DQYJKoZIhvcNAQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG
+7BJ8dNVI0lkUmcDrudHr9EgwW62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCk
+MpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWldy8joRTnU+kLBEUx3XZL7av9YROXr
+gZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov+BS5gLNdTaqX4fnk
+GMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDceqFS
+3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJm
+Ozj/2ZQw9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+
+l6mc1X5VTMbeRRAc6uk7nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6c
+JfTzPV4e0hz5sy229zdcxsshTrD3mUcYhcErulWuBurQB7Lcq9CClnXO0lD+mefP
+L5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB60PZ2Pierc+xYw5F9KBa
+LJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fqdBb9HxEG
+mpv0
+-----END CERTIFICATE-----
index d2b39b8..a9dd949 100644 (file)
@@ -16,9 +16,9 @@ licenses(["notice"])  # 3-clause BSD
 
 package(default_visibility = ["//visibility:public"])
 
-load("@grpc_python_dependencies//:requirements.bzl", "requirement")
 load("//bazel:grpc_build_system.bzl", "grpc_proto_library")
-load("@org_pubref_rules_protobuf//python:rules.bzl", "py_proto_library")
+load("//bazel:cc_grpc_library.bzl", "cc_grpc_library")
+load("//bazel:python_rules.bzl", "py_proto_library")
 
 grpc_proto_library(
     name = "auth_sample",
@@ -30,11 +30,25 @@ grpc_proto_library(
     srcs = ["protos/hellostreamingworld.proto"],
 )
 
-grpc_proto_library(
-    name = "helloworld",
+# The following three rules demonstrate the usage of the cc_grpc_library rule in
+# in a mode compatible with the native proto_library and cc_proto_library rules.
+proto_library(
+    name = "helloworld_proto",
     srcs = ["protos/helloworld.proto"],
 )
 
+cc_proto_library(
+    name = "helloworld_cc_proto",
+    deps = [":helloworld_proto"],
+)
+
+cc_grpc_library(
+    name = "helloworld_cc_grpc",
+    srcs = [":helloworld_proto"],
+    grpc_only = True,
+    deps = [":helloworld_cc_proto"],
+)
+
 grpc_proto_library(
     name = "route_guide",
     srcs = ["protos/route_guide.proto"],
@@ -45,11 +59,14 @@ grpc_proto_library(
     srcs = ["protos/keyvaluestore.proto"],
 )
 
+proto_library(
+    name = "helloworld_proto_descriptor",
+    srcs = ["protos/helloworld.proto"],
+)
+
 py_proto_library(
     name = "py_helloworld",
-    protos = ["protos/helloworld.proto"],
-    with_grpc = True,
-    deps = [requirement('protobuf'),],
+    deps = [":helloworld_proto_descriptor"],
 )
 
 cc_binary(
@@ -57,7 +74,7 @@ cc_binary(
     srcs = ["cpp/helloworld/greeter_client.cc"],
     defines = ["BAZEL_BUILD"],
     deps = [
-        ":helloworld",
+        ":helloworld_cc_grpc",
         "//:grpc++",
     ],
 )
@@ -67,7 +84,7 @@ cc_binary(
     srcs = ["cpp/helloworld/greeter_async_client.cc"],
     defines = ["BAZEL_BUILD"],
     deps = [
-        ":helloworld",
+        ":helloworld_cc_grpc",
         "//:grpc++",
     ],
 )
@@ -77,7 +94,7 @@ cc_binary(
     srcs = ["cpp/helloworld/greeter_async_client2.cc"],
     defines = ["BAZEL_BUILD"],
     deps = [
-        ":helloworld",
+        ":helloworld_cc_grpc",
         "//:grpc++",
     ],
 )
@@ -87,7 +104,7 @@ cc_binary(
     srcs = ["cpp/helloworld/greeter_server.cc"],
     defines = ["BAZEL_BUILD"],
     deps = [
-        ":helloworld",
+        ":helloworld_cc_grpc",
         "//:grpc++",
     ],
 )
@@ -97,7 +114,7 @@ cc_binary(
     srcs = ["cpp/helloworld/greeter_async_server.cc"],
     defines = ["BAZEL_BUILD"],
     deps = [
-        ":helloworld",
+        ":helloworld_cc_grpc",
         "//:grpc++",
     ],
 )
@@ -107,7 +124,7 @@ cc_binary(
     srcs = ["cpp/metadata/greeter_client.cc"],
     defines = ["BAZEL_BUILD"],
     deps = [
-        ":helloworld",
+        ":helloworld_cc_grpc",
         "//:grpc++",
     ],
 )
@@ -117,7 +134,7 @@ cc_binary(
     srcs = ["cpp/metadata/greeter_server.cc"],
     defines = ["BAZEL_BUILD"],
     deps = [
-        ":helloworld",
+        ":helloworld_cc_grpc",
         "//:grpc++",
     ],
 )
@@ -127,7 +144,7 @@ cc_binary(
     srcs = ["cpp/load_balancing/greeter_client.cc"],
     defines = ["BAZEL_BUILD"],
     deps = [
-        ":helloworld",
+        ":helloworld_cc_grpc",
         "//:grpc++",
     ],
 )
@@ -137,7 +154,7 @@ cc_binary(
     srcs = ["cpp/load_balancing/greeter_server.cc"],
     defines = ["BAZEL_BUILD"],
     deps = [
-        ":helloworld",
+        ":helloworld_cc_grpc",
         "//:grpc++",
     ],
 )
@@ -147,7 +164,7 @@ cc_binary(
     srcs = ["cpp/compression/greeter_client.cc"],
     defines = ["BAZEL_BUILD"],
     deps = [
-        ":helloworld",
+        ":helloworld_cc_grpc",
         "//:grpc++",
     ],
 )
@@ -157,15 +174,17 @@ cc_binary(
     srcs = ["cpp/compression/greeter_server.cc"],
     defines = ["BAZEL_BUILD"],
     deps = [
-        ":helloworld",
+        ":helloworld_cc_grpc",
         "//:grpc++",
     ],
 )
 
 cc_binary(
     name = "keyvaluestore_client",
-    srcs = ["cpp/keyvaluestore/caching_interceptor.h",
-            "cpp/keyvaluestore/client.cc"],    
+    srcs = [
+        "cpp/keyvaluestore/caching_interceptor.h",
+        "cpp/keyvaluestore/client.cc",
+    ],
     defines = ["BAZEL_BUILD"],
     deps = [
         ":keyvaluestore",
index 7816427..4923966 100644 (file)
@@ -151,24 +151,25 @@ class RouteGuideImpl final : public RouteGuide::Service {
 
   Status RouteChat(ServerContext* context,
                    ServerReaderWriter<RouteNote, RouteNote>* stream) override {
-    std::vector<RouteNote> received_notes;
     RouteNote note;
     while (stream->Read(&note)) {
-      for (const RouteNote& n : received_notes) {
+      std::unique_lock<std::mutex> lock(mu_);
+      for (const RouteNote& n : received_notes_) {
         if (n.location().latitude() == note.location().latitude() &&
             n.location().longitude() == note.location().longitude()) {
           stream->Write(n);
         }
       }
-      received_notes.push_back(note);
+      received_notes_.push_back(note);
     }
 
     return Status::OK;
   }
 
  private:
-
   std::vector<Feature> feature_list_;
+  std::mutex mu_;
+  std::vector<RouteNote> received_notes_;
 };
 
 void RunServer(const std::string& db_path) {
index e7620f9..16a071c 100644 (file)
@@ -53,7 +53,7 @@ namespace HelloworldXamarin.iOS
         public override void DidEnterBackground(UIApplication application)
         {
             // Use this method to release shared resources, save user data, invalidate timers and store the application state.
-            // If your application supports background exection this method is called instead of WillTerminate when the user quits.
+            // If your application supports background execution this method is called instead of WillTerminate when the user quits.
         }
 
         public override void WillEnterForeground(UIApplication application)
index 545deb5..648bbab 100644 (file)
@@ -46,8 +46,16 @@ static NSString * const kTestHostAddress = @"grpc-test.sandbox.googleapis.com";
 }
 @end
 
+@interface MakeRPCViewController ()<GRPCProtoResponseHandler>
+
+@end
+
 @implementation MakeRPCViewController
 
+- (dispatch_queue_t)dispatchQueue {
+  return dispatch_get_main_queue();
+}
+
 - (void)viewWillAppear:(BOOL)animated {
 
   // Create a service client and a proto request as usual.
@@ -57,28 +65,30 @@ static NSString * const kTestHostAddress = @"grpc-test.sandbox.googleapis.com";
   request.fillUsername = YES;
   request.fillOauthScope = YES;
 
-  // Create a not-yet-started RPC. We want to set the request headers on this object before starting
-  // it.
-  ProtoRPC *call =
-      [client RPCToUnaryCallWithRequest:request handler:^(AUTHResponse *response, NSError *error) {
-        if (response) {
-          // This test server responds with the email and scope of the access token it receives.
-          self.mainLabel.text = [NSString stringWithFormat:@"Used scope: %@ on behalf of user %@",
-                                 response.oauthScope, response.username];
-
-        } else {
-          self.mainLabel.text = error.UIDescription;
-        }
-      }];
-
-  // Set the access token to be used.
-  NSString *accessToken = GIDSignIn.sharedInstance.currentUser.authentication.accessToken;
-  call.requestHeaders[@"Authorization"] = [@"Bearer " stringByAppendingString:accessToken];
-
-  // Start the RPC.
+  // Set the request header with call options
+  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
+  options.oauth2AccessToken = GIDSignIn.sharedInstance.currentUser.authentication.accessToken;
+  GRPCUnaryProtoCall *call = [client unaryCallWithMessage:request
+                                          responseHandler:self
+                                              callOptions:options];
   [call start];
 
   self.mainLabel.text = @"Waiting for RPC to complete...";
 }
 
+- (void)didReceiveProtoMessage:(GPBMessage *)message {
+  AUTHResponse *response = (AUTHResponse *)message;
+  if (response) {
+    // This test server responds with the email and scope of the access token it receives.
+    self.mainLabel.text = [NSString stringWithFormat:@"Used scope: %@ on behalf of user %@",
+                           response.oauthScope, response.username];
+  }
+}
+
+- (void)didCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata error:(NSError *)error {
+  if (error) {
+    self.mainLabel.text = error.UIDescription;
+  }
+}
+
 @end
index c771375..649e65b 100644 (file)
 
 static NSString * const kHostAddress = @"localhost:50051";
 
+@interface HLWResponseHandler : NSObject<GRPCProtoResponseHandler>
+
+@end
+
+// A response handler object dispatching messages to main queue
+@implementation HLWResponseHandler
+
+- (dispatch_queue_t)dispatchQueue {
+  return dispatch_get_main_queue();
+}
+
+- (void)didReceiveProtoMessage:(GPBMessage *)message {
+  NSLog(@"%@", message);
+}
+
+@end
+
 int main(int argc, char * argv[]) {
   @autoreleasepool {
-    [GRPCCall useInsecureConnectionsForHost:kHostAddress];
-    [GRPCCall setUserAgentPrefix:@"HelloWorld/1.0" forHost:kHostAddress];
-
     HLWGreeter *client = [[HLWGreeter alloc] initWithHost:kHostAddress];
 
     HLWHelloRequest *request = [HLWHelloRequest message];
     request.name = @"Objective-C";
 
-    [client sayHelloWithRequest:request handler:^(HLWHelloReply *response, NSError *error) {
-      NSLog(@"%@", response.message);
-    }];
-    
+    GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
+    // this example does not use TLS (secure channel); use insecure channel instead
+    options.transportType = GRPCTransportTypeInsecure;
+    options.userAgentPrefix = @"HelloWorld/1.0";
+
+    GRPCUnaryProtoCall *call = [client sayHelloWithMessage:request
+                                           responseHandler:[[HLWResponseHandler alloc] init]
+                                               callOptions:options];
+
+    [call start];
+
     return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
   }
 }
index 2a98ec4..ce24a9b 100644 (file)
 
 static NSString * const kHostAddress = @"localhost:50051";
 
+@interface HLWResponseHandler : NSObject<GRPCProtoResponseHandler>
+
+@end
+
+// A response handler object dispatching messages to main queue
+@implementation HLWResponseHandler
+
+- (dispatch_queue_t)dispatchQueue {
+  return dispatch_get_main_queue();
+}
+
+- (void)didReceiveProtoMessage:(GPBMessage *)message {
+  NSLog(@"%@", message);
+}
+
+@end
+
 int main(int argc, const char * argv[]) {
   @autoreleasepool {
-    [GRPCCall useInsecureConnectionsForHost:kHostAddress];
-    [GRPCCall setUserAgentPrefix:@"HelloWorld/1.0" forHost:kHostAddress];
-
     HLWGreeter *client = [[HLWGreeter alloc] initWithHost:kHostAddress];
 
     HLWHelloRequest *request = [HLWHelloRequest message];
     request.name = @"Objective-C";
 
-    [client sayHelloWithRequest:request handler:^(HLWHelloReply *response, NSError *error) {
-      NSLog(@"%@", response.message);
-    }];
+    GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
+    // this example does not use TLS (secure channel); use insecure channel instead
+    options.transportType = GRPCTransportTypeInsecure;
+    options.userAgentPrefix = @"HelloWorld/1.0";
+
+    GRPCUnaryProtoCall *call = [client sayHelloWithMessage:request
+                                           responseHandler:[[HLWResponseHandler alloc] init]
+                                               callOptions:options];
+
+    [call start];
   }
 
   return NSApplicationMain(argc, argv);
index 0f116f3..43d2082 100644 (file)
  */
 
 #import <UIKit/UIKit.h>
-#import <GRPCClient/GRPCCall+Tests.h>
 #import <RouteGuide/RouteGuide.pbrpc.h>
-#import <RxLibrary/GRXWriter+Immediate.h>
-#import <RxLibrary/GRXWriter+Transformations.h>
 
 static NSString * const kHostAddress = @"localhost:50051";
 
@@ -65,7 +62,7 @@ static NSString * const kHostAddress = @"localhost:50051";
  * Run the getFeature demo. Calls getFeature with a point known to have a feature and a point known
  * not to have a feature.
  */
-@interface GetFeatureViewController : UIViewController
+@interface GetFeatureViewController : UIViewController<GRPCProtoResponseHandler>
 
 @property (weak, nonatomic) IBOutlet UILabel *outputLabel;
 
@@ -75,39 +72,56 @@ static NSString * const kHostAddress = @"localhost:50051";
   RTGRouteGuide *_service;
 }
 
-- (void)execRequest {
-  void (^handler)(RTGFeature *response, NSError *error) = ^(RTGFeature *response, NSError *error) {
-    // TODO(makdharma): Remove boilerplate by consolidating into one log function.
-    if (response.name.length) {
-      NSString *str =[NSString stringWithFormat:@"%@\nFound feature called %@ at %@.", self.outputLabel.text, response.location, response.name];
-      self.outputLabel.text = str;
-      NSLog(@"Found feature called %@ at %@.", response.name, response.location);
-    } else if (response) {
-      NSString *str =[NSString stringWithFormat:@"%@\nFound no features at %@",  self.outputLabel.text,response.location];
-      self.outputLabel.text = str;
-      NSLog(@"Found no features at %@", response.location);
-    } else {
-      NSString *str =[NSString stringWithFormat:@"%@\nRPC error: %@", self.outputLabel.text, error];
-      self.outputLabel.text = str;
-      NSLog(@"RPC error: %@", error);
-    }
-  };
+- (dispatch_queue_t)dispatchQueue {
+  return dispatch_get_main_queue();
+}
+
+- (void)didReceiveProtoMessage:(GPBMessage *)message {
+  RTGFeature *response = (RTGFeature *)message;
 
+  // TODO(makdharma): Remove boilerplate by consolidating into one log function.
+  if (response.name.length != 0) {
+    NSString *str =[NSString stringWithFormat:@"%@\nFound feature called %@ at %@.", self.outputLabel.text, response.location, response.name];
+    self.outputLabel.text = str;
+    NSLog(@"Found feature called %@ at %@.", response.name, response.location);
+  } else if (response) {
+    NSString *str =[NSString stringWithFormat:@"%@\nFound no features at %@",  self.outputLabel.text,response.location];
+    self.outputLabel.text = str;
+    NSLog(@"Found no features at %@", response.location);
+  }
+}
+
+- (void)didCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata error:(NSError *)error {
+  if (error) {
+    NSString *str =[NSString stringWithFormat:@"%@\nRPC error: %@", self.outputLabel.text, error];
+    self.outputLabel.text = str;
+    NSLog(@"RPC error: %@", error);
+  }
+}
+
+- (void)execRequest {
   RTGPoint *point = [RTGPoint message];
   point.latitude = 409146138;
   point.longitude = -746188906;
 
-  [_service getFeatureWithRequest:point handler:handler];
-  [_service getFeatureWithRequest:[RTGPoint message] handler:handler];
+  GRPCUnaryProtoCall *call = [_service getFeatureWithMessage:point
+                                             responseHandler:self
+                                                 callOptions:nil];
+  [call start];
+  call = [_service getFeatureWithMessage:[RTGPoint message]
+                         responseHandler:self
+                             callOptions:nil];
+  [call start];
+
 }
 
 - (void)viewDidLoad {
   [super viewDidLoad];
 
-  // This only needs to be done once per host, before creating service objects for that host.
-  [GRPCCall useInsecureConnectionsForHost:kHostAddress];
+  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
+  options.transportType = GRPCTransportTypeInsecure;
 
-  _service = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
+  _service = [[RTGRouteGuide alloc] initWithHost:kHostAddress callOptions:options];
 }
 
 - (void)viewDidAppear:(BOOL)animated {
@@ -126,7 +140,7 @@ static NSString * const kHostAddress = @"localhost:50051";
  * Run the listFeatures demo. Calls listFeatures with a rectangle containing all of the features in
  * the pre-generated database. Prints each response as it comes in.
  */
-@interface ListFeaturesViewController : UIViewController
+@interface ListFeaturesViewController : UIViewController<GRPCProtoResponseHandler>
 
 @property (weak, nonatomic) IBOutlet UILabel *outputLabel;
 
@@ -136,6 +150,10 @@ static NSString * const kHostAddress = @"localhost:50051";
   RTGRouteGuide *_service;
 }
 
+- (dispatch_queue_t)dispatchQueue {
+  return dispatch_get_main_queue();
+}
+
 - (void)execRequest {
   RTGRectangle *rectangle = [RTGRectangle message];
   rectangle.lo.latitude = 405E6;
@@ -144,24 +162,36 @@ static NSString * const kHostAddress = @"localhost:50051";
   rectangle.hi.longitude = -745E6;
 
   NSLog(@"Looking for features between %@ and %@", rectangle.lo, rectangle.hi);
-  [_service listFeaturesWithRequest:rectangle
-                      eventHandler:^(BOOL done, RTGFeature *response, NSError *error) {
-    if (response) {
-      NSString *str =[NSString stringWithFormat:@"%@\nFound feature at %@ called %@.", self.outputLabel.text, response.location, response.name];
-      self.outputLabel.text = str;
-      NSLog(@"Found feature at %@ called %@.", response.location, response.name);
-    } else if (error) {
-      NSString *str =[NSString stringWithFormat:@"%@\nRPC error: %@", self.outputLabel.text, error];
-      self.outputLabel.text = str;
-      NSLog(@"RPC error: %@", error);
-    }
-  }];
+  GRPCUnaryProtoCall *call = [_service listFeaturesWithMessage:rectangle
+                                               responseHandler:self
+                                                   callOptions:nil];
+  [call start];
+}
+
+- (void)didReceiveProtoMessage:(GPBMessage *)message {
+  RTGFeature *response = (RTGFeature *)message;
+  if (response) {
+    NSString *str =[NSString stringWithFormat:@"%@\nFound feature at %@ called %@.", self.outputLabel.text, response.location, response.name];
+    self.outputLabel.text = str;
+    NSLog(@"Found feature at %@ called %@.", response.location, response.name);
+  }
+}
+
+- (void)didCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata error:(NSError *)error {
+  if (error) {
+    NSString *str =[NSString stringWithFormat:@"%@\nRPC error: %@", self.outputLabel.text, error];
+    self.outputLabel.text = str;
+    NSLog(@"RPC error: %@", error);
+  }
 }
 
 - (void)viewDidLoad {
   [super viewDidLoad];
 
-  _service = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
+  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
+  options.transportType = GRPCTransportTypeInsecure;
+
+  _service = [[RTGRouteGuide alloc] initWithHost:kHostAddress callOptions:options];
 }
 
 - (void)viewDidAppear:(BOOL)animated {
@@ -173,7 +203,6 @@ static NSString * const kHostAddress = @"localhost:50051";
 
 @end
 
-
 #pragma mark Demo: Record Route
 
 /**
@@ -181,7 +210,7 @@ static NSString * const kHostAddress = @"localhost:50051";
  * database with a variable delay in between. Prints the statistics when they are sent from the
  * server.
  */
-@interface RecordRouteViewController : UIViewController
+@interface RecordRouteViewController : UIViewController<GRPCProtoResponseHandler>
 
 @property (weak, nonatomic) IBOutlet UILabel *outputLabel;
 
@@ -191,47 +220,71 @@ static NSString * const kHostAddress = @"localhost:50051";
   RTGRouteGuide *_service;
 }
 
+- (dispatch_queue_t)dispatchQueue {
+  return dispatch_get_main_queue();
+}
+
 - (void)execRequest {
   NSString *dataBasePath = [NSBundle.mainBundle pathForResource:@"route_guide_db"
                                                          ofType:@"json"];
   NSData *dataBaseContent = [NSData dataWithContentsOfFile:dataBasePath];
-  NSArray *features = [NSJSONSerialization JSONObjectWithData:dataBaseContent options:0 error:NULL];
+  NSError *error;
+  NSArray *features = [NSJSONSerialization JSONObjectWithData:dataBaseContent options:0 error:&error];
+
+  if (error) {
+    NSLog(@"Error reading database.");
+    NSString *str = @"Error reading database.";
+    self.outputLabel.text = str;
+    return;
+  }
 
-  GRXWriter *locations = [[GRXWriter writerWithContainer:features] map:^id(id feature) {
+  GRPCStreamingProtoCall *call = [_service recordRouteWithResponseHandler:self
+                                                              callOptions:nil];
+  [call start];
+  for (id feature in features) {
     RTGPoint *location = [RTGPoint message];
     location.longitude = [((NSNumber *) feature[@"location"][@"longitude"]) intValue];
     location.latitude = [((NSNumber *) feature[@"location"][@"latitude"]) intValue];
     NSString *str =[NSString stringWithFormat:@"%@\nVisiting point %@", self.outputLabel.text, location];
     self.outputLabel.text = str;
     NSLog(@"Visiting point %@", location);
-    return location;
-  }];
-
-  [_service recordRouteWithRequestsWriter:locations
-                                 handler:^(RTGRouteSummary *response, NSError *error) {
-    if (response) {
-      NSString *str =[NSString stringWithFormat:
-                      @"%@\nFinished trip with %i points\nPassed %i features\n"
-                      "Travelled %i meters\nIt took %i seconds",
-                      self.outputLabel.text, response.pointCount, response.featureCount,
-                      response.distance, response.elapsedTime];
-      self.outputLabel.text = str;
-      NSLog(@"Finished trip with %i points", response.pointCount);
-      NSLog(@"Passed %i features", response.featureCount);
-      NSLog(@"Travelled %i meters", response.distance);
-      NSLog(@"It took %i seconds", response.elapsedTime);
-    } else {
-      NSString *str =[NSString stringWithFormat:@"%@\nRPC error: %@", self.outputLabel.text, error];
-      self.outputLabel.text = str;
-      NSLog(@"RPC error: %@", error);
-    }
-  }];
+    [call writeMessage:location];
+  }
+  [call finish];
+}
+
+- (void)didReceiveProtoMessage:(GPBMessage *)message {
+  RTGRouteSummary *response = (RTGRouteSummary *)message;
+
+  if (response) {
+    NSString *str =[NSString stringWithFormat:
+                    @"%@\nFinished trip with %i points\nPassed %i features\n"
+                    "Travelled %i meters\nIt took %i seconds",
+                    self.outputLabel.text, response.pointCount, response.featureCount,
+                    response.distance, response.elapsedTime];
+    self.outputLabel.text = str;
+    NSLog(@"Finished trip with %i points", response.pointCount);
+    NSLog(@"Passed %i features", response.featureCount);
+    NSLog(@"Travelled %i meters", response.distance);
+    NSLog(@"It took %i seconds", response.elapsedTime);
+  }
+}
+
+- (void)didCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata error:(NSError *)error {
+  if (error) {
+    NSString *str =[NSString stringWithFormat:@"%@\nRPC error: %@", self.outputLabel.text, error];
+    self.outputLabel.text = str;
+    NSLog(@"RPC error: %@", error);
+  }
 }
 
 - (void)viewDidLoad {
   [super viewDidLoad];
 
-  _service = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
+  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
+  options.transportType = GRPCTransportTypeInsecure;
+
+  _service = [[RTGRouteGuide alloc] initWithHost:kHostAddress callOptions:options];
 }
 
 - (void)viewDidAppear:(BOOL)animated {
@@ -250,7 +303,7 @@ static NSString * const kHostAddress = @"localhost:50051";
  * Run the routeChat demo. Send some chat messages, and print any chat messages that are sent from
  * the server.
  */
-@interface RouteChatViewController : UIViewController
+@interface RouteChatViewController : UIViewController<GRPCProtoResponseHandler>
 
 @property (weak, nonatomic) IBOutlet UILabel *outputLabel;
 
@@ -260,38 +313,52 @@ static NSString * const kHostAddress = @"localhost:50051";
   RTGRouteGuide *_service;
 }
 
+- (dispatch_queue_t)dispatchQueue {
+  return dispatch_get_main_queue();
+}
+
 - (void)execRequest {
   NSArray *notes = @[[RTGRouteNote noteWithMessage:@"First message" latitude:0 longitude:0],
                      [RTGRouteNote noteWithMessage:@"Second message" latitude:0 longitude:1],
                      [RTGRouteNote noteWithMessage:@"Third message" latitude:1 longitude:0],
                      [RTGRouteNote noteWithMessage:@"Fourth message" latitude:0 longitude:0]];
-  GRXWriter *notesWriter = [[GRXWriter writerWithContainer:notes] map:^id(RTGRouteNote *note) {
-    NSLog(@"Sending message %@ at %@", note.message, note.location);
-    return note;
-  }];
-
-  [_service routeChatWithRequestsWriter:notesWriter
-                          eventHandler:^(BOOL done, RTGRouteNote *note, NSError *error) {
-    if (note) {
-      NSString *str =[NSString stringWithFormat:@"%@\nGot message %@ at %@",
-                      self.outputLabel.text, note.message, note.location];
-      self.outputLabel.text = str;
-      NSLog(@"Got message %@ at %@", note.message, note.location);
-    } else if (error) {
-      NSString *str =[NSString stringWithFormat:@"%@\nRPC error: %@", self.outputLabel.text, error];
-      self.outputLabel.text = str;
-      NSLog(@"RPC error: %@", error);
-    }
-    if (done) {
-      NSLog(@"Chat ended.");
-    }
-  }];
+
+  GRPCStreamingProtoCall *call = [_service routeChatWithResponseHandler:self
+                                                            callOptions:nil];
+  [call start];
+  for (RTGRouteNote *note in notes) {
+    [call writeMessage:note];
+  }
+  [call finish];
+}
+
+- (void)didReceiveProtoMessage:(GPBMessage *)message {
+  RTGRouteNote *note = (RTGRouteNote *)message;
+  if (note) {
+    NSString *str =[NSString stringWithFormat:@"%@\nGot message %@ at %@",
+                    self.outputLabel.text, note.message, note.location];
+    self.outputLabel.text = str;
+    NSLog(@"Got message %@ at %@", note.message, note.location);
+  }
+}
+
+- (void)didCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata error:(NSError *)error {
+  if (!error) {
+    NSLog(@"Chat ended.");
+  } else {
+    NSString *str =[NSString stringWithFormat:@"%@\nRPC error: %@", self.outputLabel.text, error];
+    self.outputLabel.text = str;
+    NSLog(@"RPC error: %@", error);
+  }
 }
 
 - (void)viewDidLoad {
   [super viewDidLoad];
 
-  _service = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
+  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
+  options.transportType = GRPCTransportTypeInsecure;
+
+  _service = [[RTGRouteGuide alloc] initWithHost:kHostAddress callOptions:options];
 }
 
 - (void)viewDidAppear:(BOOL)animated {
index a79b8fc..c762d79 100644 (file)
@@ -20,8 +20,8 @@ import grpc
 from grpc_status import rpc_status
 from google.rpc import error_details_pb2
 
-from examples.protos import helloworld_pb2
-from examples.protos import helloworld_pb2_grpc
+from examples import helloworld_pb2
+from examples import helloworld_pb2_grpc
 
 _LOGGER = logging.getLogger(__name__)
 
index f49586b..50d4a2a 100644 (file)
@@ -24,8 +24,8 @@ from grpc_status import rpc_status
 from google.protobuf import any_pb2
 from google.rpc import code_pb2, status_pb2, error_details_pb2
 
-from examples.protos import helloworld_pb2
-from examples.protos import helloworld_pb2_grpc
+from examples import helloworld_pb2
+from examples import helloworld_pb2_grpc
 
 _ONE_DAY_IN_SECONDS = 60 * 60 * 24
 
index a79ca45..9eb81ba 100644 (file)
@@ -26,7 +26,7 @@ import logging
 
 import grpc
 
-from examples.protos import helloworld_pb2_grpc
+from examples import helloworld_pb2_grpc
 from examples.python.errors import client as error_handling_client
 from examples.python.errors import server as error_handling_server
 
index 6de1e94..0e135f4 100644 (file)
 # limitations under the License.
 
 load("@grpc_python_dependencies//:requirements.bzl", "requirement")
-load("@org_pubref_rules_protobuf//python:rules.bzl", "py_proto_library")
+load("//bazel:python_rules.bzl", "py_proto_library")
 
-py_proto_library(
+proto_library(
     name = "prime_proto",
-    protos = ["prime.proto",],
-    deps = [requirement("protobuf")],
+    srcs = ["prime.proto"]
+)
+
+py_proto_library(
+    name = "prime_proto_pb2",
+    deps = [":prime_proto"],
+    well_known_protos = False,
 )
 
 py_binary(
@@ -29,7 +34,7 @@ py_binary(
     srcs = ["client.py"],
     deps = [
         "//src/python/grpcio/grpc:grpcio",
-        ":prime_proto",
+        ":prime_proto_pb2",
     ],
     default_python_version = "PY3",
 )
@@ -40,7 +45,7 @@ py_binary(
     srcs = ["server.py"],
     deps = [
         "//src/python/grpcio/grpc:grpcio",
-        ":prime_proto"
+        ":prime_proto_pb2"
     ] + select({
         "//conditions:default": [requirement("futures")],
         "//:python3": [],
diff --git a/examples/python/wait_for_ready/BUILD.bazel b/examples/python/wait_for_ready/BUILD.bazel
new file mode 100644 (file)
index 0000000..70daf83
--- /dev/null
@@ -0,0 +1,32 @@
+# 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.
+
+load("@grpc_python_dependencies//:requirements.bzl", "requirement")
+
+py_library(
+    name = "wait_for_ready_example",
+    testonly = 1,
+    srcs = ["wait_for_ready_example.py"],
+    deps = [
+        "//src/python/grpcio/grpc:grpcio",
+        "//examples:py_helloworld",
+    ],
+)
+
+py_test(
+    name = "test/_wait_for_ready_example_test",
+    srcs = ["test/_wait_for_ready_example_test.py"],
+    deps = [":wait_for_ready_example",],
+    size = "small",
+)
diff --git a/examples/python/wait_for_ready/README.md b/examples/python/wait_for_ready/README.md
new file mode 100644 (file)
index 0000000..6e873d2
--- /dev/null
@@ -0,0 +1,32 @@
+# gRPC Python Example for Wait-for-ready
+
+The default behavior of an RPC is to fail instantly if the server is not ready yet. This example demonstrates how to change that behavior.
+
+
+### Definition of 'wait-for-ready' semantics
+> If an RPC is issued but the channel is in TRANSIENT_FAILURE or SHUTDOWN states, the RPC is unable to be transmitted promptly. By default, gRPC implementations SHOULD fail such RPCs immediately. This is known as "fail fast," but the usage of the term is historical. RPCs SHOULD NOT fail as a result of the channel being in other states (CONNECTING, READY, or IDLE).
+> 
+> gRPC implementations MAY provide a per-RPC option to not fail RPCs as a result of the channel being in TRANSIENT_FAILURE state. Instead, the implementation queues the RPCs until the channel is READY. This is known as "wait for ready." The RPCs SHOULD still fail before READY if there are unrelated reasons, such as the channel is SHUTDOWN or the RPC's deadline is reached.
+> 
+> From https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md 
+
+
+### Use cases for 'wait-for-ready'
+
+When developers spin up gRPC clients and servers at the same time, it is very like to fail first couple RPC calls due to unavailability of the server. If developers failed to prepare for this situation, the result can be catastrophic. But with 'wait-for-ready' semantics, developers can initialize the client and server in any order, especially useful in testing.
+
+Also, developers may ensure the server is up before starting client. But in some cases like transient network failure may result in a temporary unavailability of the server. With 'wait-for-ready' semantics, those RPC calls will automatically wait until the server is ready to accept incoming requests.
+
+
+### DEMO Snippets
+
+```Python
+# Per RPC level
+stub = ...Stub(...)
+
+stub.important_transaction_1(..., wait_for_ready=True)
+stub.unimportant_transaction_2(...)
+stub.important_transaction_3(..., wait_for_ready=True)
+stub.unimportant_transaction_4(...)
+# The unimportant transactions can be status report, or health check, etc.
+```
diff --git a/examples/python/wait_for_ready/test/_wait_for_ready_example_test.py b/examples/python/wait_for_ready/test/_wait_for_ready_example_test.py
new file mode 100644 (file)
index 0000000..03e83a1
--- /dev/null
@@ -0,0 +1,31 @@
+# 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.
+"""Tests of the wait-for-ready example."""
+
+import unittest
+import logging
+
+from examples.python.wait_for_ready import wait_for_ready_example
+
+
+class WaitForReadyExampleTest(unittest.TestCase):
+
+    def test_wait_for_ready_example(self):
+        wait_for_ready_example.main()
+        # No unhandled exception raised, no deadlock, test passed!
+
+
+if __name__ == '__main__':
+    logging.basicConfig()
+    unittest.main(verbosity=2)
diff --git a/examples/python/wait_for_ready/wait_for_ready_example.py b/examples/python/wait_for_ready/wait_for_ready_example.py
new file mode 100644 (file)
index 0000000..a0f076e
--- /dev/null
@@ -0,0 +1,117 @@
+# 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.
+"""The Python example of utilizing wait-for-ready flag."""
+
+from __future__ import print_function
+import logging
+from concurrent import futures
+from contextlib import contextmanager
+import socket
+import threading
+
+import grpc
+
+from examples import helloworld_pb2
+from examples import helloworld_pb2_grpc
+
+_LOGGER = logging.getLogger(__name__)
+_LOGGER.setLevel(logging.INFO)
+
+_ONE_DAY_IN_SECONDS = 60 * 60 * 24
+
+
+@contextmanager
+def get_free_loopback_tcp_port():
+    if socket.has_ipv6:
+        tcp_socket = socket.socket(socket.AF_INET6)
+    else:
+        tcp_socket = socket.socket(socket.AF_INET)
+    tcp_socket.bind(('', 0))
+    address_tuple = tcp_socket.getsockname()
+    yield "localhost:%s" % (address_tuple[1])
+    tcp_socket.close()
+
+
+class Greeter(helloworld_pb2_grpc.GreeterServicer):
+
+    def SayHello(self, request, unused_context):
+        return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)
+
+
+def create_server(server_address):
+    server = grpc.server(futures.ThreadPoolExecutor())
+    helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
+    bound_port = server.add_insecure_port(server_address)
+    assert bound_port == int(server_address.split(':')[-1])
+    return server
+
+
+def process(stub, wait_for_ready=None):
+    try:
+        response = stub.SayHello(
+            helloworld_pb2.HelloRequest(name='you'),
+            wait_for_ready=wait_for_ready)
+        message = response.message
+    except grpc.RpcError as rpc_error:
+        assert rpc_error.code() == grpc.StatusCode.UNAVAILABLE
+        assert not wait_for_ready
+        message = rpc_error
+    else:
+        assert wait_for_ready
+    _LOGGER.info("Wait-for-ready %s, client received: %s", "enabled"
+                 if wait_for_ready else "disabled", message)
+
+
+def main():
+    # Pick a random free port
+    with get_free_loopback_tcp_port() as server_address:
+
+        # Register connectivity event to notify main thread
+        transient_failure_event = threading.Event()
+
+        def wait_for_transient_failure(channel_connectivity):
+            if channel_connectivity == grpc.ChannelConnectivity.TRANSIENT_FAILURE:
+                transient_failure_event.set()
+
+        # Create gRPC channel
+        channel = grpc.insecure_channel(server_address)
+        channel.subscribe(wait_for_transient_failure)
+        stub = helloworld_pb2_grpc.GreeterStub(channel)
+
+        # Fire an RPC without wait_for_ready
+        thread_disabled_wait_for_ready = threading.Thread(
+            target=process, args=(stub, False))
+        thread_disabled_wait_for_ready.start()
+        # Fire an RPC with wait_for_ready
+        thread_enabled_wait_for_ready = threading.Thread(
+            target=process, args=(stub, True))
+        thread_enabled_wait_for_ready.start()
+
+    # Wait for the channel entering TRANSIENT FAILURE state.
+    transient_failure_event.wait()
+    server = create_server(server_address)
+    server.start()
+
+    # Expected to fail with StatusCode.UNAVAILABLE.
+    thread_disabled_wait_for_ready.join()
+    # Expected to success.
+    thread_enabled_wait_for_ready.join()
+
+    server.stop(None)
+    channel.close()
+
+
+if __name__ == '__main__':
+    logging.basicConfig(level=logging.INFO)
+    main()
index 79ee540..7932aab 100755 (executable)
@@ -28,7 +28,7 @@ def main
   stub = EchoWithoutProtobuf::Stub.new('localhost:50051', :this_channel_is_insecure)
   user = ARGV.size > 0 ?  ARGV[0] : 'world'
   message = stub.echo("hello #{user}")
-  p "Reponse: #{message}"
+  p "Response: #{message}"
 end
 
 main
index f031cbf..91a72e7 100644 (file)
 Pod::Spec.new do |s|
   s.name     = 'gRPC-C++'
   # TODO (mxyan): use version that match gRPC version when pod is stabilized
-  # version = '1.20.1'
-  version = '0.0.8'
+  # version = '1.21.0'
+  version = '0.0.9'
   s.version  = version
   s.summary  = 'gRPC C++ library'
   s.homepage = 'https://grpc.io'
   s.license  = 'Apache License, Version 2.0'
   s.authors  = { 'The gRPC contributors' => 'grpc-packages@google.com' }
 
-  grpc_version = '1.20.1'
+  grpc_version = '1.21.0'
 
   s.source = {
     :git => 'https://github.com/grpc/grpc.git',
@@ -72,7 +72,7 @@ Pod::Spec.new do |s|
   s.default_subspecs = 'Interface', 'Implementation'
 
   # Certificates, to be able to establish TLS connections:
-  s.resource_bundles = { 'gRPCCertificates' => ['etc/roots.pem'] }
+  s.resource_bundles = { 'gRPCCertificates-Cpp' => ['etc/roots.pem'] }
 
   s.header_mappings_dir = 'include/grpcpp'
 
@@ -82,16 +82,20 @@ Pod::Spec.new do |s|
     ss.source_files = 'include/grpcpp/alarm.h',
                       'include/grpcpp/alarm_impl.h',
                       'include/grpcpp/channel.h',
+                      'include/grpcpp/channel_impl.h',
                       'include/grpcpp/client_context.h',
                       'include/grpcpp/completion_queue.h',
                       'include/grpcpp/create_channel.h',
+                      'include/grpcpp/create_channel_impl.h',
                       'include/grpcpp/create_channel_posix.h',
                       'include/grpcpp/create_channel_posix_impl.h',
                       'include/grpcpp/ext/health_check_service_server_builder_option.h',
                       'include/grpcpp/generic/async_generic_service.h',
                       'include/grpcpp/generic/generic_stub.h',
+                      'include/grpcpp/generic/generic_stub_impl.h',
                       'include/grpcpp/grpcpp.h',
                       'include/grpcpp/health_check_service_interface.h',
+                      'include/grpcpp/health_check_service_interface_impl.h',
                       'include/grpcpp/impl/call.h',
                       'include/grpcpp/impl/channel_argument_option.h',
                       'include/grpcpp/impl/client_unary_call.h',
@@ -105,25 +109,34 @@ Pod::Spec.new do |s|
                       'include/grpcpp/impl/server_builder_option_impl.h',
                       'include/grpcpp/impl/server_builder_plugin.h',
                       'include/grpcpp/impl/server_initializer.h',
+                      'include/grpcpp/impl/server_initializer_impl.h',
                       'include/grpcpp/impl/service_type.h',
                       'include/grpcpp/resource_quota.h',
+                      'include/grpcpp/resource_quota_impl.h',
                       'include/grpcpp/security/auth_context.h',
                       'include/grpcpp/security/auth_metadata_processor.h',
+                      'include/grpcpp/security/auth_metadata_processor_impl.h',
                       'include/grpcpp/security/credentials.h',
+                      'include/grpcpp/security/credentials_impl.h',
                       'include/grpcpp/security/server_credentials.h',
+                      'include/grpcpp/security/server_credentials_impl.h',
                       'include/grpcpp/server.h',
                       'include/grpcpp/server_builder.h',
+                      'include/grpcpp/server_builder_impl.h',
                       'include/grpcpp/server_context.h',
+                      '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_unary_call.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_interceptor.h',
                       'include/grpcpp/support/config.h',
                       'include/grpcpp/support/interceptor.h',
+                      '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',
@@ -150,6 +163,7 @@ Pod::Spec.new do |s|
                       'include/grpcpp/impl/codegen/client_interceptor.h',
                       'include/grpcpp/impl/codegen/client_unary_call.h',
                       'include/grpcpp/impl/codegen/completion_queue.h',
+                      'include/grpcpp/impl/codegen/completion_queue_impl.h',
                       'include/grpcpp/impl/codegen/completion_queue_tag.h',
                       'include/grpcpp/impl/codegen/config.h',
                       'include/grpcpp/impl/codegen/core_codegen_interface.h',
@@ -158,6 +172,7 @@ Pod::Spec.new do |s|
                       'include/grpcpp/impl/codegen/intercepted_channel.h',
                       'include/grpcpp/impl/codegen/interceptor.h',
                       'include/grpcpp/impl/codegen/interceptor_common.h',
+                      'include/grpcpp/impl/codegen/message_allocator.h',
                       'include/grpcpp/impl/codegen/metadata_map.h',
                       'include/grpcpp/impl/codegen/method_handler_impl.h',
                       'include/grpcpp/impl/codegen/rpc_method.h',
@@ -175,7 +190,8 @@ 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/time.h'
+                      'include/grpcpp/impl/codegen/time.h',
+                      'include/grpcpp/impl/codegen/sync.h'
   end
 
   s.subspec 'Implementation' do |ss|
@@ -253,11 +269,18 @@ Pod::Spec.new do |s|
                       'src/core/lib/gpr/tmpfile.h',
                       'src/core/lib/gpr/useful.h',
                       'src/core/lib/gprpp/abstract.h',
+                      'src/core/lib/gprpp/arena.h',
                       'src/core/lib/gprpp/atomic.h',
                       'src/core/lib/gprpp/fork.h',
+                      'src/core/lib/gprpp/global_config.h',
+                      'src/core/lib/gprpp/global_config_custom.h',
+                      'src/core/lib/gprpp/global_config_env.h',
+                      'src/core/lib/gprpp/global_config_generic.h',
                       'src/core/lib/gprpp/manual_constructor.h',
+                      'src/core/lib/gprpp/map.h',
                       'src/core/lib/gprpp/memory.h',
-                      'src/core/lib/gprpp/mutex_lock.h',
+                      'src/core/lib/gprpp/pair.h',
+                      'src/core/lib/gprpp/sync.h',
                       'src/core/lib/gprpp/thd.h',
                       'src/core/lib/profiling/timers.h',
                       'src/core/ext/transport/chttp2/transport/bin_decoder.h',
@@ -400,6 +423,7 @@ Pod::Spec.new do |s|
                       'src/core/lib/channel/handshaker_registry.h',
                       'src/core/lib/channel/status_util.h',
                       'src/core/lib/compression/algorithm_metadata.h',
+                      'src/core/lib/compression/compression_args.h',
                       'src/core/lib/compression/compression_internal.h',
                       'src/core/lib/compression/message_compress.h',
                       'src/core/lib/compression/stream_compression.h',
@@ -419,12 +443,15 @@ Pod::Spec.new do |s|
                       'src/core/lib/iomgr/block_annotate.h',
                       'src/core/lib/iomgr/buffer_list.h',
                       'src/core/lib/iomgr/call_combiner.h',
+                      'src/core/lib/iomgr/cfstream_handle.h',
                       'src/core/lib/iomgr/closure.h',
                       'src/core/lib/iomgr/combiner.h',
                       'src/core/lib/iomgr/dynamic_annotations.h',
                       'src/core/lib/iomgr/endpoint.h',
+                      'src/core/lib/iomgr/endpoint_cfstream.h',
                       'src/core/lib/iomgr/endpoint_pair.h',
                       'src/core/lib/iomgr/error.h',
+                      'src/core/lib/iomgr/error_cfstream.h',
                       'src/core/lib/iomgr/error_internal.h',
                       'src/core/lib/iomgr/ev_epoll1_linux.h',
                       'src/core/lib/iomgr/ev_epollex_linux.h',
@@ -535,6 +562,7 @@ Pod::Spec.new do |s|
                       'src/core/ext/filters/client_channel/lb_policy/subchannel_list.h',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h',
+                      'src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h',
                       'src/core/ext/filters/max_age/max_age_filter.h',
                       'src/core/ext/filters/message_size/message_size_filter.h',
                       'src/core/ext/filters/http/client_authority_filter.h',
@@ -568,11 +596,18 @@ Pod::Spec.new do |s|
                               'src/core/lib/gpr/tmpfile.h',
                               'src/core/lib/gpr/useful.h',
                               'src/core/lib/gprpp/abstract.h',
+                              'src/core/lib/gprpp/arena.h',
                               'src/core/lib/gprpp/atomic.h',
                               'src/core/lib/gprpp/fork.h',
+                              'src/core/lib/gprpp/global_config.h',
+                              'src/core/lib/gprpp/global_config_custom.h',
+                              'src/core/lib/gprpp/global_config_env.h',
+                              'src/core/lib/gprpp/global_config_generic.h',
                               'src/core/lib/gprpp/manual_constructor.h',
+                              'src/core/lib/gprpp/map.h',
                               'src/core/lib/gprpp/memory.h',
-                              'src/core/lib/gprpp/mutex_lock.h',
+                              'src/core/lib/gprpp/pair.h',
+                              'src/core/lib/gprpp/sync.h',
                               'src/core/lib/gprpp/thd.h',
                               'src/core/lib/profiling/timers.h',
                               'src/core/lib/avl/avl.h',
@@ -590,6 +625,7 @@ Pod::Spec.new do |s|
                               'src/core/lib/channel/handshaker_registry.h',
                               'src/core/lib/channel/status_util.h',
                               'src/core/lib/compression/algorithm_metadata.h',
+                              'src/core/lib/compression/compression_args.h',
                               'src/core/lib/compression/compression_internal.h',
                               'src/core/lib/compression/message_compress.h',
                               'src/core/lib/compression/stream_compression.h',
@@ -609,12 +645,15 @@ Pod::Spec.new do |s|
                               'src/core/lib/iomgr/block_annotate.h',
                               'src/core/lib/iomgr/buffer_list.h',
                               'src/core/lib/iomgr/call_combiner.h',
+                              'src/core/lib/iomgr/cfstream_handle.h',
                               'src/core/lib/iomgr/closure.h',
                               'src/core/lib/iomgr/combiner.h',
                               'src/core/lib/iomgr/dynamic_annotations.h',
                               'src/core/lib/iomgr/endpoint.h',
+                              'src/core/lib/iomgr/endpoint_cfstream.h',
                               'src/core/lib/iomgr/endpoint_pair.h',
                               'src/core/lib/iomgr/error.h',
+                              'src/core/lib/iomgr/error_cfstream.h',
                               'src/core/lib/iomgr/error_internal.h',
                               'src/core/lib/iomgr/ev_epoll1_linux.h',
                               'src/core/lib/iomgr/ev_epollex_linux.h',
index 50fb324..76f8cb3 100644 (file)
@@ -22,7 +22,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-Core'
-  version = '1.20.1'
+  version = '1.21.0'
   s.version  = version
   s.summary  = 'Core cross-platform gRPC library, written in C'
   s.homepage = 'https://grpc.io'
@@ -205,15 +205,21 @@ Pod::Spec.new do |s|
                       'src/core/lib/gpr/tmpfile.h',
                       'src/core/lib/gpr/useful.h',
                       'src/core/lib/gprpp/abstract.h',
+                      'src/core/lib/gprpp/arena.h',
                       'src/core/lib/gprpp/atomic.h',
                       'src/core/lib/gprpp/fork.h',
+                      'src/core/lib/gprpp/global_config.h',
+                      'src/core/lib/gprpp/global_config_custom.h',
+                      'src/core/lib/gprpp/global_config_env.h',
+                      'src/core/lib/gprpp/global_config_generic.h',
                       'src/core/lib/gprpp/manual_constructor.h',
+                      'src/core/lib/gprpp/map.h',
                       'src/core/lib/gprpp/memory.h',
-                      'src/core/lib/gprpp/mutex_lock.h',
+                      'src/core/lib/gprpp/pair.h',
+                      'src/core/lib/gprpp/sync.h',
                       'src/core/lib/gprpp/thd.h',
                       'src/core/lib/profiling/timers.h',
                       'src/core/lib/gpr/alloc.cc',
-                      'src/core/lib/gpr/arena.cc',
                       'src/core/lib/gpr/atm.cc',
                       'src/core/lib/gpr/cpu_iphone.cc',
                       'src/core/lib/gpr/cpu_linux.cc',
@@ -246,7 +252,9 @@ Pod::Spec.new do |s|
                       'src/core/lib/gpr/tmpfile_posix.cc',
                       'src/core/lib/gpr/tmpfile_windows.cc',
                       'src/core/lib/gpr/wrap_memcpy.cc',
+                      'src/core/lib/gprpp/arena.cc',
                       'src/core/lib/gprpp/fork.cc',
+                      'src/core/lib/gprpp/global_config_env.cc',
                       'src/core/lib/gprpp/thd_posix.cc',
                       'src/core/lib/gprpp/thd_windows.cc',
                       'src/core/lib/profiling/basic_timers.cc',
@@ -391,6 +399,7 @@ Pod::Spec.new do |s|
                       'src/core/lib/channel/handshaker_registry.h',
                       'src/core/lib/channel/status_util.h',
                       'src/core/lib/compression/algorithm_metadata.h',
+                      'src/core/lib/compression/compression_args.h',
                       'src/core/lib/compression/compression_internal.h',
                       'src/core/lib/compression/message_compress.h',
                       'src/core/lib/compression/stream_compression.h',
@@ -410,12 +419,15 @@ Pod::Spec.new do |s|
                       'src/core/lib/iomgr/block_annotate.h',
                       'src/core/lib/iomgr/buffer_list.h',
                       'src/core/lib/iomgr/call_combiner.h',
+                      'src/core/lib/iomgr/cfstream_handle.h',
                       'src/core/lib/iomgr/closure.h',
                       'src/core/lib/iomgr/combiner.h',
                       'src/core/lib/iomgr/dynamic_annotations.h',
                       'src/core/lib/iomgr/endpoint.h',
+                      'src/core/lib/iomgr/endpoint_cfstream.h',
                       'src/core/lib/iomgr/endpoint_pair.h',
                       'src/core/lib/iomgr/error.h',
+                      'src/core/lib/iomgr/error_cfstream.h',
                       'src/core/lib/iomgr/error_internal.h',
                       'src/core/lib/iomgr/ev_epoll1_linux.h',
                       'src/core/lib/iomgr/ev_epollex_linux.h',
@@ -526,6 +538,7 @@ Pod::Spec.new do |s|
                       'src/core/ext/filters/client_channel/lb_policy/subchannel_list.h',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h',
+                      'src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h',
                       'src/core/ext/filters/max_age/max_age_filter.h',
                       'src/core/ext/filters/message_size/message_size_filter.h',
                       'src/core/ext/filters/http/client_authority_filter.h',
@@ -545,6 +558,7 @@ Pod::Spec.new do |s|
                       'src/core/lib/channel/handshaker_registry.cc',
                       'src/core/lib/channel/status_util.cc',
                       'src/core/lib/compression/compression.cc',
+                      'src/core/lib/compression/compression_args.cc',
                       'src/core/lib/compression/compression_internal.cc',
                       'src/core/lib/compression/message_compress.cc',
                       'src/core/lib/compression/stream_compression.cc',
@@ -557,12 +571,15 @@ Pod::Spec.new do |s|
                       'src/core/lib/http/parser.cc',
                       'src/core/lib/iomgr/buffer_list.cc',
                       'src/core/lib/iomgr/call_combiner.cc',
+                      'src/core/lib/iomgr/cfstream_handle.cc',
                       'src/core/lib/iomgr/combiner.cc',
                       'src/core/lib/iomgr/endpoint.cc',
+                      'src/core/lib/iomgr/endpoint_cfstream.cc',
                       'src/core/lib/iomgr/endpoint_pair_posix.cc',
                       'src/core/lib/iomgr/endpoint_pair_uv.cc',
                       'src/core/lib/iomgr/endpoint_pair_windows.cc',
                       'src/core/lib/iomgr/error.cc',
+                      'src/core/lib/iomgr/error_cfstream.cc',
                       'src/core/lib/iomgr/ev_epoll1_linux.cc',
                       'src/core/lib/iomgr/ev_epollex_linux.cc',
                       'src/core/lib/iomgr/ev_poll_posix.cc',
@@ -583,6 +600,7 @@ Pod::Spec.new do |s|
                       'src/core/lib/iomgr/iomgr_custom.cc',
                       'src/core/lib/iomgr/iomgr_internal.cc',
                       'src/core/lib/iomgr/iomgr_posix.cc',
+                      'src/core/lib/iomgr/iomgr_posix_cfstream.cc',
                       'src/core/lib/iomgr/iomgr_uv.cc',
                       'src/core/lib/iomgr/iomgr_windows.cc',
                       'src/core/lib/iomgr/is_epollexclusive_available.cc',
@@ -611,6 +629,7 @@ Pod::Spec.new do |s|
                       'src/core/lib/iomgr/socket_utils_windows.cc',
                       'src/core/lib/iomgr/socket_windows.cc',
                       'src/core/lib/iomgr/tcp_client.cc',
+                      'src/core/lib/iomgr/tcp_client_cfstream.cc',
                       'src/core/lib/iomgr/tcp_client_custom.cc',
                       'src/core/lib/iomgr/tcp_client_posix.cc',
                       'src/core/lib/iomgr/tcp_client_windows.cc',
@@ -841,12 +860,15 @@ Pod::Spec.new do |s|
                       'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc',
+                      'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc',
+                      'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc',
+                      'src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc',
                       'src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc',
                       'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc',
                       'src/core/ext/filters/census/grpc_context.cc',
@@ -874,11 +896,18 @@ Pod::Spec.new do |s|
                               'src/core/lib/gpr/tmpfile.h',
                               'src/core/lib/gpr/useful.h',
                               'src/core/lib/gprpp/abstract.h',
+                              'src/core/lib/gprpp/arena.h',
                               'src/core/lib/gprpp/atomic.h',
                               'src/core/lib/gprpp/fork.h',
+                              'src/core/lib/gprpp/global_config.h',
+                              'src/core/lib/gprpp/global_config_custom.h',
+                              'src/core/lib/gprpp/global_config_env.h',
+                              'src/core/lib/gprpp/global_config_generic.h',
                               'src/core/lib/gprpp/manual_constructor.h',
+                              'src/core/lib/gprpp/map.h',
                               'src/core/lib/gprpp/memory.h',
-                              'src/core/lib/gprpp/mutex_lock.h',
+                              'src/core/lib/gprpp/pair.h',
+                              'src/core/lib/gprpp/sync.h',
                               'src/core/lib/gprpp/thd.h',
                               'src/core/lib/profiling/timers.h',
                               'src/core/ext/transport/chttp2/transport/bin_decoder.h',
@@ -1021,6 +1050,7 @@ Pod::Spec.new do |s|
                               'src/core/lib/channel/handshaker_registry.h',
                               'src/core/lib/channel/status_util.h',
                               'src/core/lib/compression/algorithm_metadata.h',
+                              'src/core/lib/compression/compression_args.h',
                               'src/core/lib/compression/compression_internal.h',
                               'src/core/lib/compression/message_compress.h',
                               'src/core/lib/compression/stream_compression.h',
@@ -1040,12 +1070,15 @@ Pod::Spec.new do |s|
                               'src/core/lib/iomgr/block_annotate.h',
                               'src/core/lib/iomgr/buffer_list.h',
                               'src/core/lib/iomgr/call_combiner.h',
+                              'src/core/lib/iomgr/cfstream_handle.h',
                               'src/core/lib/iomgr/closure.h',
                               'src/core/lib/iomgr/combiner.h',
                               'src/core/lib/iomgr/dynamic_annotations.h',
                               'src/core/lib/iomgr/endpoint.h',
+                              'src/core/lib/iomgr/endpoint_cfstream.h',
                               'src/core/lib/iomgr/endpoint_pair.h',
                               'src/core/lib/iomgr/error.h',
+                              'src/core/lib/iomgr/error_cfstream.h',
                               'src/core/lib/iomgr/error_internal.h',
                               'src/core/lib/iomgr/ev_epoll1_linux.h',
                               'src/core/lib/iomgr/ev_epollex_linux.h',
@@ -1156,6 +1189,7 @@ Pod::Spec.new do |s|
                               'src/core/ext/filters/client_channel/lb_policy/subchannel_list.h',
                               'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h',
                               'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h',
+                              'src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h',
                               'src/core/ext/filters/max_age/max_age_filter.h',
                               'src/core/ext/filters/message_size/message_size_filter.h',
                               'src/core/ext/filters/http/client_authority_filter.h',
@@ -1163,23 +1197,9 @@ Pod::Spec.new do |s|
                               'src/core/ext/filters/workarounds/workaround_utils.h'
   end
 
+  # CFStream is now default. Leaving this subspec only for compatibility purpose.
   s.subspec 'CFStream-Implementation' do |ss|
-    ss.header_mappings_dir = '.'
     ss.dependency "#{s.name}/Implementation", version
-    ss.pod_target_xcconfig = {
-      'GCC_PREPROCESSOR_DEFINITIONS' => 'GRPC_CFSTREAM=1'
-    }
-    ss.source_files = 'src/core/lib/iomgr/cfstream_handle.cc',
-                      'src/core/lib/iomgr/endpoint_cfstream.cc',
-                      'src/core/lib/iomgr/error_cfstream.cc',
-                      'src/core/lib/iomgr/iomgr_posix_cfstream.cc',
-                      'src/core/lib/iomgr/tcp_client_cfstream.cc',
-                      'src/core/lib/iomgr/cfstream_handle.h',
-                      'src/core/lib/iomgr/endpoint_cfstream.h',
-                      'src/core/lib/iomgr/error_cfstream.h'
-    ss.private_header_files = 'src/core/lib/iomgr/cfstream_handle.h',
-                              'src/core/lib/iomgr/endpoint_cfstream.h',
-                              'src/core/lib/iomgr/error_cfstream.h'
   end
 
   s.subspec 'Cronet-Interface' do |ss|
index 89c367c..4a48257 100644 (file)
@@ -21,7 +21,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-ProtoRPC'
-  version = '1.20.1'
+  version = '1.21.0'
   s.version  = version
   s.summary  = 'RPC library for Protocol Buffers, based on gRPC'
   s.homepage = 'https://grpc.io'
@@ -53,12 +53,10 @@ Pod::Spec.new do |s|
 
     ss.source_files = "#{src_dir}/*.{h,m}"
   end
+
+  # CFStream is now default. Leaving this subspec only for compatibility purpose.
   s.subspec 'CFStream' do |ss|
-    ss.dependency 'gRPC/CFStream', version
     ss.dependency "#{s.name}/Main", version
-    ss.pod_target_xcconfig = {
-      'GCC_PREPROCESSOR_DEFINITIONS' => 'GRPC_CFSTREAM=1'
-    }
   end
 
   s.pod_target_xcconfig = {
index 2dc3fe6..8db3c7d 100644 (file)
@@ -21,7 +21,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-RxLibrary'
-  version = '1.20.1'
+  version = '1.21.0'
   s.version  = version
   s.summary  = 'Reactive Extensions library for iOS/OSX.'
   s.homepage = 'https://grpc.io'
index ec9cd77..83317db 100644 (file)
@@ -20,7 +20,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC'
-  version = '1.20.1'
+  version = '1.21.0'
   s.version  = version
   s.summary  = 'gRPC client library for iOS/OSX'
   s.homepage = 'https://grpc.io'
@@ -64,14 +64,9 @@ Pod::Spec.new do |s|
     ss.dependency 'gRPC-Core', version
   end
 
-  # This subspec is mutually exclusive with the `Main` subspec
+  # CFStream is now default. Leaving this subspec only for compatibility purpose.
   s.subspec 'CFStream' do |ss|
-    ss.dependency 'gRPC-Core/CFStream-Implementation', version
     ss.dependency "#{s.name}/Main", version
-
-    ss.pod_target_xcconfig = {
-      'GCC_PREPROCESSOR_DEFINITIONS' => 'GRPC_CFSTREAM=1'
-    }
   end
 
   s.subspec 'GID' do |ss|
index 52db5ec..5a34687 100644 (file)
@@ -30,7 +30,7 @@ Gem::Specification.new do |s|
   s.platform      = Gem::Platform::RUBY
 
   s.add_dependency 'google-protobuf', '~> 3.7'
-  s.add_dependency 'googleapis-common-protos-types', '~> 1.0.0'
+  s.add_dependency 'googleapis-common-protos-types', '~> 1.0'
 
   s.add_development_dependency 'bundler',            '~> 1.9'
   s.add_development_dependency 'facter',             '~> 2.4'
@@ -99,15 +99,21 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/gpr/tmpfile.h )
   s.files += %w( src/core/lib/gpr/useful.h )
   s.files += %w( src/core/lib/gprpp/abstract.h )
+  s.files += %w( src/core/lib/gprpp/arena.h )
   s.files += %w( src/core/lib/gprpp/atomic.h )
   s.files += %w( src/core/lib/gprpp/fork.h )
+  s.files += %w( src/core/lib/gprpp/global_config.h )
+  s.files += %w( src/core/lib/gprpp/global_config_custom.h )
+  s.files += %w( src/core/lib/gprpp/global_config_env.h )
+  s.files += %w( src/core/lib/gprpp/global_config_generic.h )
   s.files += %w( src/core/lib/gprpp/manual_constructor.h )
+  s.files += %w( src/core/lib/gprpp/map.h )
   s.files += %w( src/core/lib/gprpp/memory.h )
-  s.files += %w( src/core/lib/gprpp/mutex_lock.h )
+  s.files += %w( src/core/lib/gprpp/pair.h )
+  s.files += %w( src/core/lib/gprpp/sync.h )
   s.files += %w( src/core/lib/gprpp/thd.h )
   s.files += %w( src/core/lib/profiling/timers.h )
   s.files += %w( src/core/lib/gpr/alloc.cc )
-  s.files += %w( src/core/lib/gpr/arena.cc )
   s.files += %w( src/core/lib/gpr/atm.cc )
   s.files += %w( src/core/lib/gpr/cpu_iphone.cc )
   s.files += %w( src/core/lib/gpr/cpu_linux.cc )
@@ -140,7 +146,9 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/gpr/tmpfile_posix.cc )
   s.files += %w( src/core/lib/gpr/tmpfile_windows.cc )
   s.files += %w( src/core/lib/gpr/wrap_memcpy.cc )
+  s.files += %w( src/core/lib/gprpp/arena.cc )
   s.files += %w( src/core/lib/gprpp/fork.cc )
+  s.files += %w( src/core/lib/gprpp/global_config_env.cc )
   s.files += %w( src/core/lib/gprpp/thd_posix.cc )
   s.files += %w( src/core/lib/gprpp/thd_windows.cc )
   s.files += %w( src/core/lib/profiling/basic_timers.cc )
@@ -325,6 +333,7 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/channel/handshaker_registry.h )
   s.files += %w( src/core/lib/channel/status_util.h )
   s.files += %w( src/core/lib/compression/algorithm_metadata.h )
+  s.files += %w( src/core/lib/compression/compression_args.h )
   s.files += %w( src/core/lib/compression/compression_internal.h )
   s.files += %w( src/core/lib/compression/message_compress.h )
   s.files += %w( src/core/lib/compression/stream_compression.h )
@@ -344,12 +353,15 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/iomgr/block_annotate.h )
   s.files += %w( src/core/lib/iomgr/buffer_list.h )
   s.files += %w( src/core/lib/iomgr/call_combiner.h )
+  s.files += %w( src/core/lib/iomgr/cfstream_handle.h )
   s.files += %w( src/core/lib/iomgr/closure.h )
   s.files += %w( src/core/lib/iomgr/combiner.h )
   s.files += %w( src/core/lib/iomgr/dynamic_annotations.h )
   s.files += %w( src/core/lib/iomgr/endpoint.h )
+  s.files += %w( src/core/lib/iomgr/endpoint_cfstream.h )
   s.files += %w( src/core/lib/iomgr/endpoint_pair.h )
   s.files += %w( src/core/lib/iomgr/error.h )
+  s.files += %w( src/core/lib/iomgr/error_cfstream.h )
   s.files += %w( src/core/lib/iomgr/error_internal.h )
   s.files += %w( src/core/lib/iomgr/ev_epoll1_linux.h )
   s.files += %w( src/core/lib/iomgr/ev_epollex_linux.h )
@@ -460,6 +472,7 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/subchannel_list.h )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h )
+  s.files += %w( src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h )
   s.files += %w( src/core/ext/filters/max_age/max_age_filter.h )
   s.files += %w( src/core/ext/filters/message_size/message_size_filter.h )
   s.files += %w( src/core/ext/filters/http/client_authority_filter.h )
@@ -479,6 +492,7 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/channel/handshaker_registry.cc )
   s.files += %w( src/core/lib/channel/status_util.cc )
   s.files += %w( src/core/lib/compression/compression.cc )
+  s.files += %w( src/core/lib/compression/compression_args.cc )
   s.files += %w( src/core/lib/compression/compression_internal.cc )
   s.files += %w( src/core/lib/compression/message_compress.cc )
   s.files += %w( src/core/lib/compression/stream_compression.cc )
@@ -491,12 +505,15 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/http/parser.cc )
   s.files += %w( src/core/lib/iomgr/buffer_list.cc )
   s.files += %w( src/core/lib/iomgr/call_combiner.cc )
+  s.files += %w( src/core/lib/iomgr/cfstream_handle.cc )
   s.files += %w( src/core/lib/iomgr/combiner.cc )
   s.files += %w( src/core/lib/iomgr/endpoint.cc )
+  s.files += %w( src/core/lib/iomgr/endpoint_cfstream.cc )
   s.files += %w( src/core/lib/iomgr/endpoint_pair_posix.cc )
   s.files += %w( src/core/lib/iomgr/endpoint_pair_uv.cc )
   s.files += %w( src/core/lib/iomgr/endpoint_pair_windows.cc )
   s.files += %w( src/core/lib/iomgr/error.cc )
+  s.files += %w( src/core/lib/iomgr/error_cfstream.cc )
   s.files += %w( src/core/lib/iomgr/ev_epoll1_linux.cc )
   s.files += %w( src/core/lib/iomgr/ev_epollex_linux.cc )
   s.files += %w( src/core/lib/iomgr/ev_poll_posix.cc )
@@ -517,6 +534,7 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/iomgr/iomgr_custom.cc )
   s.files += %w( src/core/lib/iomgr/iomgr_internal.cc )
   s.files += %w( src/core/lib/iomgr/iomgr_posix.cc )
+  s.files += %w( src/core/lib/iomgr/iomgr_posix_cfstream.cc )
   s.files += %w( src/core/lib/iomgr/iomgr_uv.cc )
   s.files += %w( src/core/lib/iomgr/iomgr_windows.cc )
   s.files += %w( src/core/lib/iomgr/is_epollexclusive_available.cc )
@@ -545,6 +563,7 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/iomgr/socket_utils_windows.cc )
   s.files += %w( src/core/lib/iomgr/socket_windows.cc )
   s.files += %w( src/core/lib/iomgr/tcp_client.cc )
+  s.files += %w( src/core/lib/iomgr/tcp_client_cfstream.cc )
   s.files += %w( src/core/lib/iomgr/tcp_client_custom.cc )
   s.files += %w( src/core/lib/iomgr/tcp_client_posix.cc )
   s.files += %w( src/core/lib/iomgr/tcp_client_windows.cc )
@@ -778,12 +797,15 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc )
+  s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc )
+  s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc )
+  s.files += %w( src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc )
   s.files += %w( src/core/ext/filters/census/grpc_context.cc )
index 322259d..1cc6e46 100644 (file)
--- a/grpc.gyp
+++ b/grpc.gyp
       ],
       'sources': [
         'src/core/lib/gpr/alloc.cc',
-        'src/core/lib/gpr/arena.cc',
         'src/core/lib/gpr/atm.cc',
         'src/core/lib/gpr/cpu_iphone.cc',
         'src/core/lib/gpr/cpu_linux.cc',
         'src/core/lib/gpr/tmpfile_posix.cc',
         'src/core/lib/gpr/tmpfile_windows.cc',
         'src/core/lib/gpr/wrap_memcpy.cc',
+        'src/core/lib/gprpp/arena.cc',
         'src/core/lib/gprpp/fork.cc',
+        'src/core/lib/gprpp/global_config_env.cc',
         'src/core/lib/gprpp/thd_posix.cc',
         'src/core/lib/gprpp/thd_windows.cc',
         'src/core/lib/profiling/basic_timers.cc',
         'src/core/lib/channel/handshaker_registry.cc',
         'src/core/lib/channel/status_util.cc',
         'src/core/lib/compression/compression.cc',
+        'src/core/lib/compression/compression_args.cc',
         'src/core/lib/compression/compression_internal.cc',
         'src/core/lib/compression/message_compress.cc',
         'src/core/lib/compression/stream_compression.cc',
         'src/core/lib/http/parser.cc',
         'src/core/lib/iomgr/buffer_list.cc',
         'src/core/lib/iomgr/call_combiner.cc',
+        'src/core/lib/iomgr/cfstream_handle.cc',
         'src/core/lib/iomgr/combiner.cc',
         'src/core/lib/iomgr/endpoint.cc',
+        'src/core/lib/iomgr/endpoint_cfstream.cc',
         'src/core/lib/iomgr/endpoint_pair_posix.cc',
         'src/core/lib/iomgr/endpoint_pair_uv.cc',
         'src/core/lib/iomgr/endpoint_pair_windows.cc',
         'src/core/lib/iomgr/error.cc',
+        'src/core/lib/iomgr/error_cfstream.cc',
         'src/core/lib/iomgr/ev_epoll1_linux.cc',
         'src/core/lib/iomgr/ev_epollex_linux.cc',
         'src/core/lib/iomgr/ev_poll_posix.cc',
         'src/core/lib/iomgr/iomgr_custom.cc',
         'src/core/lib/iomgr/iomgr_internal.cc',
         'src/core/lib/iomgr/iomgr_posix.cc',
+        'src/core/lib/iomgr/iomgr_posix_cfstream.cc',
         'src/core/lib/iomgr/iomgr_uv.cc',
         'src/core/lib/iomgr/iomgr_windows.cc',
         'src/core/lib/iomgr/is_epollexclusive_available.cc',
         'src/core/lib/iomgr/socket_utils_windows.cc',
         'src/core/lib/iomgr/socket_windows.cc',
         'src/core/lib/iomgr/tcp_client.cc',
+        'src/core/lib/iomgr/tcp_client_cfstream.cc',
         'src/core/lib/iomgr/tcp_client_custom.cc',
         'src/core/lib/iomgr/tcp_client_posix.cc',
         'src/core/lib/iomgr/tcp_client_windows.cc',
         'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc',
+        'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc',
+        'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc',
+        'src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc',
         'src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc',
         'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc',
         'src/core/ext/filters/census/grpc_context.cc',
         'src/core/lib/channel/handshaker_registry.cc',
         'src/core/lib/channel/status_util.cc',
         'src/core/lib/compression/compression.cc',
+        'src/core/lib/compression/compression_args.cc',
         'src/core/lib/compression/compression_internal.cc',
         'src/core/lib/compression/message_compress.cc',
         'src/core/lib/compression/stream_compression.cc',
         'src/core/lib/http/parser.cc',
         'src/core/lib/iomgr/buffer_list.cc',
         'src/core/lib/iomgr/call_combiner.cc',
+        'src/core/lib/iomgr/cfstream_handle.cc',
         'src/core/lib/iomgr/combiner.cc',
         'src/core/lib/iomgr/endpoint.cc',
+        'src/core/lib/iomgr/endpoint_cfstream.cc',
         'src/core/lib/iomgr/endpoint_pair_posix.cc',
         'src/core/lib/iomgr/endpoint_pair_uv.cc',
         'src/core/lib/iomgr/endpoint_pair_windows.cc',
         'src/core/lib/iomgr/error.cc',
+        'src/core/lib/iomgr/error_cfstream.cc',
         'src/core/lib/iomgr/ev_epoll1_linux.cc',
         'src/core/lib/iomgr/ev_epollex_linux.cc',
         'src/core/lib/iomgr/ev_poll_posix.cc',
         'src/core/lib/iomgr/iomgr_custom.cc',
         'src/core/lib/iomgr/iomgr_internal.cc',
         'src/core/lib/iomgr/iomgr_posix.cc',
+        'src/core/lib/iomgr/iomgr_posix_cfstream.cc',
         'src/core/lib/iomgr/iomgr_uv.cc',
         'src/core/lib/iomgr/iomgr_windows.cc',
         'src/core/lib/iomgr/is_epollexclusive_available.cc',
         'src/core/lib/iomgr/socket_utils_windows.cc',
         'src/core/lib/iomgr/socket_windows.cc',
         'src/core/lib/iomgr/tcp_client.cc',
+        'src/core/lib/iomgr/tcp_client_cfstream.cc',
         'src/core/lib/iomgr/tcp_client_custom.cc',
         'src/core/lib/iomgr/tcp_client_posix.cc',
         'src/core/lib/iomgr/tcp_client_windows.cc',
         'src/core/lib/channel/handshaker_registry.cc',
         'src/core/lib/channel/status_util.cc',
         'src/core/lib/compression/compression.cc',
+        'src/core/lib/compression/compression_args.cc',
         'src/core/lib/compression/compression_internal.cc',
         'src/core/lib/compression/message_compress.cc',
         'src/core/lib/compression/stream_compression.cc',
         'src/core/lib/http/parser.cc',
         'src/core/lib/iomgr/buffer_list.cc',
         'src/core/lib/iomgr/call_combiner.cc',
+        'src/core/lib/iomgr/cfstream_handle.cc',
         'src/core/lib/iomgr/combiner.cc',
         'src/core/lib/iomgr/endpoint.cc',
+        'src/core/lib/iomgr/endpoint_cfstream.cc',
         'src/core/lib/iomgr/endpoint_pair_posix.cc',
         'src/core/lib/iomgr/endpoint_pair_uv.cc',
         'src/core/lib/iomgr/endpoint_pair_windows.cc',
         'src/core/lib/iomgr/error.cc',
+        'src/core/lib/iomgr/error_cfstream.cc',
         'src/core/lib/iomgr/ev_epoll1_linux.cc',
         'src/core/lib/iomgr/ev_epollex_linux.cc',
         'src/core/lib/iomgr/ev_poll_posix.cc',
         'src/core/lib/iomgr/iomgr_custom.cc',
         'src/core/lib/iomgr/iomgr_internal.cc',
         'src/core/lib/iomgr/iomgr_posix.cc',
+        'src/core/lib/iomgr/iomgr_posix_cfstream.cc',
         'src/core/lib/iomgr/iomgr_uv.cc',
         'src/core/lib/iomgr/iomgr_windows.cc',
         'src/core/lib/iomgr/is_epollexclusive_available.cc',
         'src/core/lib/iomgr/socket_utils_windows.cc',
         'src/core/lib/iomgr/socket_windows.cc',
         'src/core/lib/iomgr/tcp_client.cc',
+        'src/core/lib/iomgr/tcp_client_cfstream.cc',
         'src/core/lib/iomgr/tcp_client_custom.cc',
         'src/core/lib/iomgr/tcp_client_posix.cc',
         'src/core/lib/iomgr/tcp_client_windows.cc',
         'src/core/lib/channel/handshaker_registry.cc',
         'src/core/lib/channel/status_util.cc',
         'src/core/lib/compression/compression.cc',
+        'src/core/lib/compression/compression_args.cc',
         'src/core/lib/compression/compression_internal.cc',
         'src/core/lib/compression/message_compress.cc',
         'src/core/lib/compression/stream_compression.cc',
         'src/core/lib/http/parser.cc',
         'src/core/lib/iomgr/buffer_list.cc',
         'src/core/lib/iomgr/call_combiner.cc',
+        'src/core/lib/iomgr/cfstream_handle.cc',
         'src/core/lib/iomgr/combiner.cc',
         'src/core/lib/iomgr/endpoint.cc',
+        'src/core/lib/iomgr/endpoint_cfstream.cc',
         'src/core/lib/iomgr/endpoint_pair_posix.cc',
         'src/core/lib/iomgr/endpoint_pair_uv.cc',
         'src/core/lib/iomgr/endpoint_pair_windows.cc',
         'src/core/lib/iomgr/error.cc',
+        'src/core/lib/iomgr/error_cfstream.cc',
         'src/core/lib/iomgr/ev_epoll1_linux.cc',
         'src/core/lib/iomgr/ev_epollex_linux.cc',
         'src/core/lib/iomgr/ev_poll_posix.cc',
         'src/core/lib/iomgr/iomgr_custom.cc',
         'src/core/lib/iomgr/iomgr_internal.cc',
         'src/core/lib/iomgr/iomgr_posix.cc',
+        'src/core/lib/iomgr/iomgr_posix_cfstream.cc',
         'src/core/lib/iomgr/iomgr_uv.cc',
         'src/core/lib/iomgr/iomgr_windows.cc',
         'src/core/lib/iomgr/is_epollexclusive_available.cc',
         'src/core/lib/iomgr/socket_utils_windows.cc',
         'src/core/lib/iomgr/socket_windows.cc',
         'src/core/lib/iomgr/tcp_client.cc',
+        'src/core/lib/iomgr/tcp_client_cfstream.cc',
         'src/core/lib/iomgr/tcp_client_custom.cc',
         'src/core/lib/iomgr/tcp_client_posix.cc',
         'src/core/lib/iomgr/tcp_client_windows.cc',
         'src/core/ext/transport/inproc/inproc_transport.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc',
+        'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc',
+        'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc',
+        'src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc',
         'src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc',
         'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc',
         'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc',
       ],
     },
     {
+      'target_name': 'dns_test_util',
+      'type': 'static_library',
+      'dependencies': [
+      ],
+      'sources': [
+        'test/cpp/naming/dns_test_util.cc',
+      ],
+    },
+    {
       'target_name': 'grpc++',
       'type': 'static_library',
       'dependencies': [
index f1185d2..5318909 100644 (file)
@@ -264,7 +264,7 @@ GRPCAPI grpc_call_credentials* grpc_google_refresh_token_credentials_create(
     const char* json_refresh_token, void* reserved);
 
 /** Creates an Oauth2 Access Token credentials with an access token that was
-   aquired by an out of band mechanism. */
+   acquired by an out of band mechanism. */
 GRPCAPI grpc_call_credentials* grpc_access_token_credentials_create(
     const char* access_token, void* reserved);
 
index 078db2b..65582e6 100644 (file)
@@ -315,11 +315,11 @@ typedef struct {
 #define GRPC_ARG_GRPCLB_CALL_TIMEOUT_MS "grpc.grpclb_call_timeout_ms"
 /* Timeout in milliseconds to wait for the serverlist from the grpclb load
    balancer before using fallback backend addresses from the resolver.
-   If 0, fallback will never be used. Default value is 10000. */
+   If 0, enter fallback mode immediately. Default value is 10000. */
 #define GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS "grpc.grpclb_fallback_timeout_ms"
 /* Timeout in milliseconds to wait for the serverlist from the xDS load
    balancer before using fallback backend addresses from the resolver.
-   If 0, fallback will never be used. Default value is 10000. */
+   If 0, enter fallback mode immediately. Default value is 10000. */
 #define GRPC_ARG_XDS_FALLBACK_TIMEOUT_MS "grpc.xds_fallback_timeout_ms"
 /** If non-zero, grpc server's cronet compression workaround will be enabled */
 #define GRPC_ARG_WORKAROUND_CRONET_COMPRESSION \
@@ -359,10 +359,12 @@ typedef struct {
  * load balancing policy. Note that this only works with the "ares"
  * DNS resolver, and isn't supported by the "native" DNS resolver. */
 #define GRPC_ARG_DNS_ENABLE_SRV_QUERIES "grpc.dns_enable_srv_queries"
-/** If set, determines the number of milliseconds that the c-ares based
- * DNS resolver will wait on queries before cancelling them. The default value
- * is 10000. Setting this to "0" will disable c-ares query timeouts
- * entirely. */
+/** If set, determines an upper bound on the number of milliseconds that the
+ * c-ares based DNS resolver will wait on queries before cancelling them.
+ * The default value is 120,000. Setting this to "0" will disable the
+ * overall timeout entirely. Note that this doesn't include internal c-ares
+ * timeouts/backoff/retry logic, and so the actual DNS resolution may time out
+ * sooner than the value specified here. */
 #define GRPC_ARG_DNS_ARES_QUERY_TIMEOUT_MS "grpc.dns_ares_query_timeout"
 /** If set, uses a local subchannel pool within the channel. Otherwise, uses the
  * global subchannel pool. */
@@ -494,7 +496,8 @@ typedef struct grpc_event {
       field is guaranteed to be 0 */
   int success;
   /** The tag passed to grpc_call_start_batch etc to start this operation.
-      Only GRPC_OP_COMPLETE has a tag. */
+      *Only* GRPC_OP_COMPLETE has a tag. For all other grpc_completion_type
+      values, tag is uninitialized. */
   void* tag;
 } grpc_event;
 
index b62bfcb..d7294d5 100644 (file)
 #define GPR_POSIX_SUBPROCESS 1
 #define GPR_POSIX_SYNC 1
 #define GPR_POSIX_TIME 1
+#define GPR_HAS_PTHREAD_H 1
 #define GPR_GETPID_IN_UNISTD_H 1
 #ifdef _LP64
 #define GPR_ARCH_64 1
 #define GPR_POSIX_SUBPROCESS 1
 #define GPR_POSIX_SYNC 1
 #define GPR_POSIX_TIME 1
+#define GPR_HAS_PTHREAD_H 1
 #define GPR_GETPID_IN_UNISTD_H 1
 #define GPR_SUPPORT_CHANNELS_FROM_FD 1
 #elif defined(__linux__)
 #define GPR_POSIX_SUBPROCESS 1
 #define GPR_POSIX_SYNC 1
 #define GPR_POSIX_TIME 1
+#define GPR_HAS_PTHREAD_H 1
 #define GPR_GETPID_IN_UNISTD_H 1
 #ifdef _LP64
 #define GPR_ARCH_64 1
 #define GPR_PLATFORM_STRING "ios"
 #define GPR_CPU_IPHONE 1
 #define GPR_PTHREAD_TLS 1
+#define GRPC_CFSTREAM 1
 /* the c-ares resolver isnt safe to enable on iOS */
 #define GRPC_ARES 0
 #else /* TARGET_OS_IPHONE */
 #define GPR_POSIX_SUBPROCESS 1
 #define GPR_POSIX_SYNC 1
 #define GPR_POSIX_TIME 1
+#define GPR_HAS_PTHREAD_H 1
 #define GPR_GETPID_IN_UNISTD_H 1
-/* TODO(mxyan): Remove when CFStream becomes default */
 #ifndef GRPC_CFSTREAM
 #define GPR_SUPPORT_CHANNELS_FROM_FD 1
 #endif
 #define GPR_POSIX_SUBPROCESS 1
 #define GPR_POSIX_SYNC 1
 #define GPR_POSIX_TIME 1
+#define GPR_HAS_PTHREAD_H 1
 #define GPR_GETPID_IN_UNISTD_H 1
 #define GPR_SUPPORT_CHANNELS_FROM_FD 1
 #ifdef _LP64
 #define GPR_POSIX_SUBPROCESS 1
 #define GPR_POSIX_SYNC 1
 #define GPR_POSIX_TIME 1
+#define GPR_HAS_PTHREAD_H 1
 #define GPR_GETPID_IN_UNISTD_H 1
 #define GPR_SUPPORT_CHANNELS_FROM_FD 1
 #ifdef _LP64
 #define GPR_POSIX_SUBPROCESS 1
 #define GPR_POSIX_SYNC 1
 #define GPR_POSIX_TIME 1
+#define GPR_HAS_PTHREAD_H 1
 #define GPR_GETPID_IN_UNISTD_H 1
 #ifdef _LP64
 #define GPR_ARCH_64 1
 #define GPR_POSIX_SUBPROCESS 1
 #define GPR_POSIX_SYNC 1
 #define GPR_POSIX_TIME 1
+#define GPR_HAS_PTHREAD_H 1
 #define GPR_GETPID_IN_UNISTD_H 1
 #ifdef _LP64
 #define GPR_ARCH_64 1
 #define GPR_POSIX_SUBPROCESS 1
 #define GPR_POSIX_SYNC 1
 #define GPR_POSIX_TIME 1
+#define GPR_HAS_PTHREAD_H 1
 #define GPR_GETPID_IN_UNISTD_H 1
 #ifdef _LP64
 #define GPR_ARCH_64 1
 #define GPR_POSIX_SYNC 1
 #define GPR_POSIX_STRING 1
 #define GPR_POSIX_TIME 1
+#define GPR_HAS_PTHREAD_H 1
 #define GPR_GETPID_IN_UNISTD_H 1
 #else
 #error "Could not auto-detect platform"
index 62339da..3567b1e 100644 (file)
@@ -40,27 +40,6 @@ typedef struct grpc_slice grpc_slice;
    reference ownership semantics (who should call unref?) and mutability
    constraints (is the callee allowed to modify the slice?) */
 
-typedef struct grpc_slice_refcount_vtable {
-  void (*ref)(void*);
-  void (*unref)(void*);
-  int (*eq)(grpc_slice a, grpc_slice b);
-  uint32_t (*hash)(grpc_slice slice);
-} grpc_slice_refcount_vtable;
-
-/** Reference count container for grpc_slice. Contains function pointers to
-   increment and decrement reference counts. Implementations should cleanup
-   when the reference count drops to zero.
-   Typically client code should not touch this, and use grpc_slice_malloc,
-   grpc_slice_new, or grpc_slice_new_with_len instead. */
-typedef struct grpc_slice_refcount {
-  const grpc_slice_refcount_vtable* vtable;
-  /** If a subset of this slice is taken, use this pointer for the refcount.
-     Typically points back to the refcount itself, however iterning
-     implementations can use this to avoid a verification step on each hash
-     or equality check */
-  struct grpc_slice_refcount* sub_refcount;
-} grpc_slice_refcount;
-
 /* Inlined half of grpc_slice is allowed to expand the size of the overall type
    by this many bytes */
 #define GRPC_SLICE_INLINE_EXTRA_SIZE sizeof(void*)
@@ -68,6 +47,7 @@ typedef struct grpc_slice_refcount {
 #define GRPC_SLICE_INLINED_SIZE \
   (sizeof(size_t) + sizeof(uint8_t*) - 1 + GRPC_SLICE_INLINE_EXTRA_SIZE)
 
+struct grpc_slice_refcount;
 /** A grpc_slice s, if initialized, represents the byte range
    s.bytes[0..s.length-1].
 
index 9bc3dc9..dec3b8f 100644 (file)
@@ -128,7 +128,8 @@ typedef enum {
 
   /** The service is currently unavailable.  This is a most likely a
      transient condition and may be corrected by retrying with
-     a backoff.
+     a backoff. Note that it is not always safe to retry non-idempotent
+     operations.
 
      WARNING: Although data MIGHT not have been transmitted when this
      status occurs, there is NOT A GUARANTEE that the server has not seen
index ce48292..192a8cf 100644 (file)
@@ -147,7 +147,7 @@ GPRAPI int grpc_slice_buf_start_eq(grpc_slice a, const void* b, size_t blen);
 GPRAPI int grpc_slice_rchr(grpc_slice s, char c);
 GPRAPI int grpc_slice_chr(grpc_slice s, char c);
 
-/** return the index of the first occurance of \a needle in \a haystack, or -1
+/** return the index of the first occurrence of \a needle in \a haystack, or -1
    if it's not found */
 GPRAPI int grpc_slice_slice(grpc_slice haystack, grpc_slice needle);
 
index ee83396..163c804 100644 (file)
 #ifndef GRPCPP_CHANNEL_H
 #define GRPCPP_CHANNEL_H
 
-#include <memory>
-#include <mutex>
-
-#include <grpc/grpc.h>
-#include <grpcpp/impl/call.h>
-#include <grpcpp/impl/codegen/channel_interface.h>
-#include <grpcpp/impl/codegen/client_interceptor.h>
-#include <grpcpp/impl/codegen/config.h>
-#include <grpcpp/impl/codegen/grpc_library.h>
-
-struct grpc_channel;
+#include <grpcpp/channel_impl.h>
 
 namespace grpc {
 
+typedef ::grpc_impl::Channel Channel;
+
 namespace experimental {
 /// Resets the channel's connection backoff.
 /// TODO(roth): Once we see whether this proves useful, either create a gRFC
@@ -40,75 +32,6 @@ namespace experimental {
 void ChannelResetConnectionBackoff(Channel* channel);
 }  // namespace experimental
 
-/// Channels represent a connection to an endpoint. Created by \a CreateChannel.
-class Channel final : public ChannelInterface,
-                      public internal::CallHook,
-                      public std::enable_shared_from_this<Channel>,
-                      private GrpcLibraryCodegen {
- public:
-  ~Channel();
-
-  /// Get the current channel state. If the channel is in IDLE and
-  /// \a try_to_connect is set to true, try to connect.
-  grpc_connectivity_state GetState(bool try_to_connect) override;
-
-  /// Returns the LB policy name, or the empty string if not yet available.
-  grpc::string GetLoadBalancingPolicyName() const;
-
-  /// Returns the service config in JSON form, or the empty string if
-  /// not available.
-  grpc::string GetServiceConfigJSON() const;
-
- private:
-  template <class InputMessage, class OutputMessage>
-  friend class internal::BlockingUnaryCallImpl;
-  friend void experimental::ChannelResetConnectionBackoff(Channel* channel);
-  friend std::shared_ptr<Channel> CreateChannelInternal(
-      const grpc::string& host, grpc_channel* c_channel,
-      std::vector<
-          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
-          interceptor_creators);
-  friend class internal::InterceptedChannel;
-  Channel(const grpc::string& host, grpc_channel* c_channel,
-          std::vector<
-              std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
-              interceptor_creators);
-
-  internal::Call CreateCall(const internal::RpcMethod& method,
-                            ClientContext* context,
-                            CompletionQueue* cq) override;
-  void PerformOpsOnCall(internal::CallOpSetInterface* ops,
-                        internal::Call* call) override;
-  void* RegisterMethod(const char* method) override;
-
-  void NotifyOnStateChangeImpl(grpc_connectivity_state last_observed,
-                               gpr_timespec deadline, CompletionQueue* cq,
-                               void* tag) override;
-  bool WaitForStateChangeImpl(grpc_connectivity_state last_observed,
-                              gpr_timespec deadline) override;
-
-  CompletionQueue* CallbackCQ() override;
-
-  internal::Call CreateCallInternal(const internal::RpcMethod& method,
-                                    ClientContext* context, CompletionQueue* cq,
-                                    size_t interceptor_pos) override;
-
-  const grpc::string host_;
-  grpc_channel* const c_channel_;  // owned
-
-  // mu_ protects callback_cq_ (the per-channel callbackable completion queue)
-  std::mutex mu_;
-
-  // callback_cq_ references the callbackable completion queue associated
-  // 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).
-  CompletionQueue* callback_cq_ = nullptr;
-
-  std::vector<std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
-      interceptor_creators_;
-};
-
 }  // namespace grpc
 
 #endif  // GRPCPP_CHANNEL_H
diff --git a/include/grpcpp/channel_impl.h b/include/grpcpp/channel_impl.h
new file mode 100644 (file)
index 0000000..39917d2
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ *
+ * 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_CHANNEL_IMPL_H
+#define GRPCPP_CHANNEL_IMPL_H
+
+#include <memory>
+#include <mutex>
+
+#include <grpc/grpc.h>
+#include <grpcpp/impl/call.h>
+#include <grpcpp/impl/codegen/channel_interface.h>
+#include <grpcpp/impl/codegen/client_interceptor.h>
+#include <grpcpp/impl/codegen/completion_queue.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+#include <grpcpp/impl/codegen/sync.h>
+
+struct grpc_channel;
+
+namespace grpc {
+
+std::shared_ptr<::grpc_impl::Channel> CreateChannelInternal(
+    const grpc::string& host, grpc_channel* c_channel,
+    std::vector<
+        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+        interceptor_creators);
+}  // namespace grpc
+namespace grpc_impl {
+
+namespace experimental {
+/// Resets the channel's connection backoff.
+/// TODO(roth): Once we see whether this proves useful, either create a gRFC
+/// and change this to be a method of the Channel class, or remove it.
+void ChannelResetConnectionBackoff(Channel* channel);
+}  // namespace experimental
+
+/// Channels represent a connection to an endpoint. Created by \a CreateChannel.
+class Channel final : public ::grpc::ChannelInterface,
+                      public ::grpc::internal::CallHook,
+                      public std::enable_shared_from_this<Channel>,
+                      private ::grpc::GrpcLibraryCodegen {
+ public:
+  ~Channel();
+
+  /// Get the current channel state. If the channel is in IDLE and
+  /// \a try_to_connect is set to true, try to connect.
+  grpc_connectivity_state GetState(bool try_to_connect) override;
+
+  /// Returns the LB policy name, or the empty string if not yet available.
+  grpc::string GetLoadBalancingPolicyName() const;
+
+  /// Returns the service config in JSON form, or the empty string if
+  /// not available.
+  grpc::string GetServiceConfigJSON() const;
+
+ private:
+  template <class InputMessage, class OutputMessage>
+  friend class ::grpc::internal::BlockingUnaryCallImpl;
+  friend void experimental::ChannelResetConnectionBackoff(Channel* channel);
+  friend std::shared_ptr<Channel> grpc::CreateChannelInternal(
+      const grpc::string& host, grpc_channel* c_channel,
+      std::vector<std::unique_ptr<
+          ::grpc::experimental::ClientInterceptorFactoryInterface>>
+          interceptor_creators);
+  friend class ::grpc::internal::InterceptedChannel;
+  Channel(const grpc::string& host, grpc_channel* c_channel,
+          std::vector<std::unique_ptr<
+              ::grpc::experimental::ClientInterceptorFactoryInterface>>
+              interceptor_creators);
+
+  ::grpc::internal::Call CreateCall(const ::grpc::internal::RpcMethod& method,
+                                    ::grpc::ClientContext* context,
+                                    ::grpc::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;
+  bool WaitForStateChangeImpl(grpc_connectivity_state last_observed,
+                              gpr_timespec deadline) override;
+
+  ::grpc::CompletionQueue* CallbackCQ() override;
+
+  ::grpc::internal::Call CreateCallInternal(
+      const ::grpc::internal::RpcMethod& method, ::grpc::ClientContext* context,
+      ::grpc::CompletionQueue* cq, size_t interceptor_pos) override;
+
+  const grpc::string host_;
+  grpc_channel* const c_channel_;  // owned
+
+  // mu_ protects callback_cq_ (the per-channel callbackable completion queue)
+  grpc::internal::Mutex mu_;
+
+  // callback_cq_ references the callbackable completion queue associated
+  // 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;
+
+  std::vector<
+      std::unique_ptr<::grpc::experimental::ClientInterceptorFactoryInterface>>
+      interceptor_creators_;
+};
+
+}  // namespace grpc_impl
+
+#endif  // GRPCPP_CHANNEL_IMPL_H
index e8a2a70..9b257ac 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * 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.
 #ifndef GRPCPP_CREATE_CHANNEL_H
 #define GRPCPP_CREATE_CHANNEL_H
 
-#include <memory>
-
-#include <grpcpp/channel.h>
-#include <grpcpp/impl/codegen/client_interceptor.h>
-#include <grpcpp/security/credentials.h>
+#include <grpcpp/create_channel_impl.h>
 #include <grpcpp/support/channel_arguments.h>
-#include <grpcpp/support/config.h>
 
 namespace grpc {
 
-/// Create a new \a Channel pointing to \a target.
-///
-/// \param target The URI of the endpoint to connect to.
-/// \param creds Credentials to use for the created channel. If it does not
-/// hold an object or is invalid, a lame channel (one on which all operations
-/// fail) is returned.
-std::shared_ptr<Channel> CreateChannel(
+static inline std::shared_ptr<::grpc::Channel> CreateChannel(
     const grpc::string& target,
-    const std::shared_ptr<ChannelCredentials>& creds);
+    const std::shared_ptr<ChannelCredentials>& creds) {
+  return ::grpc_impl::CreateChannelImpl(target, creds);
+}
 
-/// Create a new \em custom \a Channel pointing to \a target.
-///
-/// \warning For advanced use and testing ONLY. Override default channel
-/// arguments only if necessary.
-///
-/// \param target The URI of the endpoint to connect to.
-/// \param creds Credentials to use for the created channel. If it does not
-/// hold an object or is invalid, a lame channel (one on which all operations
-/// fail) is returned.
-/// \param args Options for channel creation.
-std::shared_ptr<Channel> CreateCustomChannel(
+static inline std::shared_ptr<::grpc::Channel> CreateCustomChannel(
     const grpc::string& target,
     const std::shared_ptr<ChannelCredentials>& creds,
-    const ChannelArguments& args);
+    const ChannelArguments& args) {
+  return ::grpc_impl::CreateCustomChannelImpl(target, creds, args);
+}
 
 namespace experimental {
-/// Create a new \em custom \a Channel pointing to \a target with \a
-/// interceptors being invoked per call.
-///
-/// \warning For advanced use and testing ONLY. Override default channel
-/// arguments only if necessary.
-///
-/// \param target The URI of the endpoint to connect to.
-/// \param creds Credentials to use for the created channel. If it does not
-/// hold an object or is invalid, a lame channel (one on which all operations
-/// fail) is returned.
-/// \param args Options for channel creation.
-std::shared_ptr<Channel> CreateCustomChannelWithInterceptors(
+
+static inline std::shared_ptr<::grpc::Channel>
+CreateCustomChannelWithInterceptors(
     const grpc::string& target,
     const std::shared_ptr<ChannelCredentials>& creds,
     const ChannelArguments& args,
     std::vector<
         std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
-        interceptor_creators);
+        interceptor_creators) {
+  return ::grpc_impl::experimental::CreateCustomChannelWithInterceptors(
+      target, creds, args, std::move(interceptor_creators));
+}
+
 }  // namespace experimental
 }  // namespace grpc
 
diff --git a/include/grpcpp/create_channel_impl.h b/include/grpcpp/create_channel_impl.h
new file mode 100644 (file)
index 0000000..02896e6
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ *
+ * 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_CREATE_CHANNEL_IMPL_H
+#define GRPCPP_CREATE_CHANNEL_IMPL_H
+
+#include <memory>
+
+#include <grpcpp/channel.h>
+#include <grpcpp/impl/codegen/client_interceptor.h>
+#include <grpcpp/security/credentials.h>
+#include <grpcpp/support/channel_arguments.h>
+#include <grpcpp/support/config.h>
+
+namespace grpc_impl {
+/// Create a new \a Channel pointing to \a target.
+///
+/// \param target The URI of the endpoint to connect to.
+/// \param creds Credentials to use for the created channel. If it does not
+/// hold an object or is invalid, a lame channel (one on which all operations
+/// fail) is returned.
+std::shared_ptr<::grpc::Channel> CreateChannelImpl(
+    const grpc::string& target,
+    const std::shared_ptr<::grpc::ChannelCredentials>& creds);
+
+/// Create a new \em custom \a Channel pointing to \a target.
+///
+/// \warning For advanced use and testing ONLY. Override default channel
+/// arguments only if necessary.
+///
+/// \param target The URI of the endpoint to connect to.
+/// \param creds Credentials to use for the created channel. If it does not
+/// hold an object or is invalid, a lame channel (one on which all operations
+/// fail) is returned.
+/// \param args Options for channel creation.
+std::shared_ptr<::grpc::Channel> CreateCustomChannelImpl(
+    const grpc::string& target,
+    const std::shared_ptr<::grpc::ChannelCredentials>& creds,
+    const ::grpc::ChannelArguments& args);
+
+namespace experimental {
+/// Create a new \em custom \a Channel pointing to \a target with \a
+/// interceptors being invoked per call.
+///
+/// \warning For advanced use and testing ONLY. Override default channel
+/// arguments only if necessary.
+///
+/// \param target The URI of the endpoint to connect to.
+/// \param creds Credentials to use for the created channel. If it does not
+/// hold an object or is invalid, a lame channel (one on which all operations
+/// fail) is returned.
+/// \param args Options for channel creation.
+std::shared_ptr<::grpc::Channel> CreateCustomChannelWithInterceptors(
+    const grpc::string& target,
+    const std::shared_ptr<grpc::ChannelCredentials>& creds,
+    const ::grpc::ChannelArguments& args,
+    std::vector<
+        std::unique_ptr<grpc::experimental::ClientInterceptorFactoryInterface>>
+        interceptor_creators);
+}  // namespace experimental
+}  // namespace grpc_impl
+
+#endif  // GRPCPP_CREATE_CHANNEL_IMPL_H
index 1cfdc1b..f6f2202 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * 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.
 #ifndef GRPCPP_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H
 #define GRPCPP_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H
 
-#include <grpcpp/impl/server_builder_plugin.h>
-#include <grpcpp/support/config.h>
-
-namespace grpc {
-class ServerInitializer;
-class ProtoServerReflection;
-}  // namespace grpc
+#include <grpcpp/ext/proto_server_reflection_plugin_impl.h>
 
 namespace grpc {
 namespace reflection {
 
-class ProtoServerReflectionPlugin : public ::grpc::ServerBuilderPlugin {
- public:
-  ProtoServerReflectionPlugin();
-  ::grpc::string name() override;
-  void InitServer(::grpc::ServerInitializer* si) override;
-  void Finish(::grpc::ServerInitializer* si) override;
-  void ChangeArguments(const ::grpc::string& name, void* value) override;
-  bool has_async_methods() const override;
-  bool has_sync_methods() const override;
-
- private:
-  std::shared_ptr<grpc::ProtoServerReflection> reflection_service_;
-};
+typedef ::grpc_impl::reflection::ProtoServerReflectionPlugin
+    ProtoServerReflectionPlugin;
 
-/// Add proto reflection plugin to \a ServerBuilder.
-/// This function should be called at the static initialization time.
-void InitProtoReflectionServerBuilderPlugin();
+static inline void InitProtoReflectionServerBuilderPlugin() {
+  ::grpc_impl::reflection::InitProtoReflectionServerBuilderPlugin();
+}
 
 }  // namespace reflection
 }  // namespace grpc
diff --git a/include/grpcpp/ext/proto_server_reflection_plugin_impl.h b/include/grpcpp/ext/proto_server_reflection_plugin_impl.h
new file mode 100644 (file)
index 0000000..a06fe14
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *
+ * 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_EXT_PROTO_SERVER_REFLECTION_PLUGIN_IMPL_H
+#define GRPCPP_EXT_PROTO_SERVER_REFLECTION_PLUGIN_IMPL_H
+
+#include <grpcpp/impl/server_builder_plugin.h>
+#include <grpcpp/support/config.h>
+
+namespace grpc {
+class ProtoServerReflection;
+}  // namespace grpc
+
+namespace grpc_impl {
+class ServerInitializer;
+
+namespace reflection {
+
+class ProtoServerReflectionPlugin : public ::grpc::ServerBuilderPlugin {
+ public:
+  ProtoServerReflectionPlugin();
+  ::grpc::string name() override;
+  void InitServer(::grpc_impl::ServerInitializer* si) override;
+  void Finish(::grpc_impl::ServerInitializer* si) override;
+  void ChangeArguments(const ::grpc::string& name, void* value) override;
+  bool has_async_methods() const override;
+  bool has_sync_methods() const override;
+
+ private:
+  std::shared_ptr<grpc::ProtoServerReflection> reflection_service_;
+};
+
+/// Add proto reflection plugin to \a ServerBuilder.
+/// This function should be called at the static initialization time.
+void InitProtoReflectionServerBuilderPlugin();
+
+}  // namespace reflection
+}  // namespace grpc_impl
+
+#endif  // GRPCPP_EXT_PROTO_SERVER_REFLECTION_PLUGIN_IMPL_H
index 939569c..0c39a9e 100644 (file)
 #ifndef GRPCPP_EXT_SERVER_LOAD_REPORTING_H
 #define GRPCPP_EXT_SERVER_LOAD_REPORTING_H
 
-#include <grpc/support/port_platform.h>
-
-#include <grpc/load_reporting.h>
-#include <grpcpp/impl/codegen/config.h>
-#include <grpcpp/impl/codegen/server_context.h>
-#include <grpcpp/impl/server_builder_option.h>
+#include <grpcpp/ext/server_load_reporting_impl.h>
 
 namespace grpc {
 namespace load_reporter {
 namespace experimental {
 
-// The ServerBuilderOption to enable server-side load reporting feature. To
-// enable the feature, please make sure the binary builds with the
-// grpcpp_server_load_reporting library and set this option in the
-// ServerBuilder.
-class LoadReportingServiceServerBuilderOption : public ServerBuilderOption {
- public:
-  void UpdateArguments(::grpc::ChannelArguments* args) override;
-  void UpdatePlugins(std::vector<std::unique_ptr<::grpc::ServerBuilderPlugin>>*
-                         plugins) override;
-};
+typedef ::grpc_impl::load_reporter::experimental::
+    LoadReportingServiceServerBuilderOption
+        LoadReportingServiceServerBuilderOption;
 
-// Adds the load reporting cost with \a cost_name and \a cost_value in the
-// trailing metadata of the server context.
-void AddLoadReportingCost(grpc::ServerContext* ctx,
-                          const grpc::string& cost_name, double cost_value);
+static inline void AddLoadReportingCost(grpc::ServerContext* ctx,
+                                        const grpc::string& cost_name,
+                                        double cost_value) {
+  ::grpc_impl::load_reporter::experimental::AddLoadReportingCost(ctx, cost_name,
+                                                                 cost_value);
+}
 
 }  // namespace experimental
 }  // namespace load_reporter
diff --git a/include/grpcpp/ext/server_load_reporting_impl.h b/include/grpcpp/ext/server_load_reporting_impl.h
new file mode 100644 (file)
index 0000000..1b27e0a
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright 2018 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_EXT_SERVER_LOAD_REPORTING_IMPL_H
+#define GRPCPP_EXT_SERVER_LOAD_REPORTING_IMPL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/load_reporting.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/server_context.h>
+#include <grpcpp/impl/server_builder_option.h>
+
+namespace grpc_impl {
+namespace load_reporter {
+namespace experimental {
+
+// The ServerBuilderOption to enable server-side load reporting feature. To
+// enable the feature, please make sure the binary builds with the
+// grpcpp_server_load_reporting library and set this option in the
+// ServerBuilder.
+class LoadReportingServiceServerBuilderOption
+    : public grpc::ServerBuilderOption {
+ public:
+  void UpdateArguments(::grpc::ChannelArguments* args) override;
+  void UpdatePlugins(std::vector<std::unique_ptr<::grpc::ServerBuilderPlugin>>*
+                         plugins) override;
+};
+
+// Adds the load reporting cost with \a cost_name and \a cost_value in the
+// trailing metadata of the server context.
+void AddLoadReportingCost(grpc::ServerContext* ctx,
+                          const grpc::string& cost_name, double cost_value);
+
+}  // namespace experimental
+}  // namespace load_reporter
+}  // namespace grpc_impl
+
+#endif  // GRPCPP_EXT_SERVER_LOAD_REPORTING_IMPL_H
index 9252599..f4c4664 100644 (file)
 #ifndef GRPCPP_GENERIC_GENERIC_STUB_H
 #define GRPCPP_GENERIC_GENERIC_STUB_H
 
-#include <functional>
-
-#include <grpcpp/support/async_stream.h>
-#include <grpcpp/support/async_unary_call.h>
-#include <grpcpp/support/byte_buffer.h>
-#include <grpcpp/support/client_callback.h>
-#include <grpcpp/support/status.h>
+#include <grpcpp/generic/generic_stub_impl.h>
 
 namespace grpc {
 
-class CompletionQueue;
-typedef ClientAsyncReaderWriter<ByteBuffer, ByteBuffer>
-    GenericClientAsyncReaderWriter;
-typedef ClientAsyncResponseReader<ByteBuffer> GenericClientAsyncResponseReader;
-
-/// Generic stubs provide a type-unsafe interface to call gRPC methods
-/// by name.
-class GenericStub final {
- public:
-  explicit GenericStub(std::shared_ptr<ChannelInterface> channel)
-      : channel_(channel) {}
-
-  /// Setup a call to a named method \a method using \a context, but don't
-  /// start it. Let it be started explicitly with StartCall and a tag.
-  /// 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<GenericClientAsyncReaderWriter> PrepareCall(
-      ClientContext* context, const grpc::string& method, 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<GenericClientAsyncResponseReader> PrepareUnaryCall(
-      ClientContext* context, const grpc::string& method,
-      const ByteBuffer& request, CompletionQueue* cq);
-
-  /// DEPRECATED for multi-threaded use
-  /// Begin a call to a named method \a method using \a context.
-  /// A tag \a tag will be delivered to \a cq when the call has been started
-  /// (i.e, initial metadata has been sent).
-  /// 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<GenericClientAsyncReaderWriter> Call(
-      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
-  ///              they are no longer experimental
-  class experimental_type {
-   public:
-    explicit experimental_type(GenericStub* stub) : stub_(stub) {}
-
-    /// 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(ClientContext* context, const grpc::string& method,
-                   const ByteBuffer* request, ByteBuffer* response,
-                   std::function<void(Status)> on_completion);
-
-    /// 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(
-        ClientContext* context, const grpc::string& method,
-        experimental::ClientBidiReactor<ByteBuffer, ByteBuffer>* reactor);
-
-   private:
-    GenericStub* stub_;
-  };
-
-  /// NOTE: The function experimental() is not stable public API. It is a view
-  /// to the experimental components of this class. It may be changed or removed
-  /// at any time.
-  experimental_type experimental() { return experimental_type(this); }
-
- private:
-  std::shared_ptr<ChannelInterface> channel_;
-};
+typedef ::grpc_impl::GenericStub GenericStub;
 
 }  // namespace grpc
 
diff --git a/include/grpcpp/generic/generic_stub_impl.h b/include/grpcpp/generic/generic_stub_impl.h
new file mode 100644 (file)
index 0000000..9041461
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ *
+ * 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_GENERIC_GENERIC_STUB_IMPL_H
+#define GRPCPP_GENERIC_GENERIC_STUB_IMPL_H
+
+#include <functional>
+
+#include <grpcpp/support/async_stream.h>
+#include <grpcpp/support/async_unary_call.h>
+#include <grpcpp/support/byte_buffer.h>
+#include <grpcpp/support/client_callback.h>
+#include <grpcpp/support/status.h>
+
+namespace grpc {
+
+typedef ClientAsyncReaderWriter<ByteBuffer, ByteBuffer>
+    GenericClientAsyncReaderWriter;
+typedef ClientAsyncResponseReader<ByteBuffer> GenericClientAsyncResponseReader;
+}  // namespace grpc
+namespace grpc_impl {
+class CompletionQueue;
+
+/// Generic stubs provide a type-unsafe interface to call gRPC methods
+/// by name.
+class GenericStub final {
+ public:
+  explicit GenericStub(std::shared_ptr<grpc::ChannelInterface> channel)
+      : channel_(channel) {}
+
+  /// Setup a call to a named method \a method using \a context, but don't
+  /// start it. Let it be started explicitly with StartCall and a tag.
+  /// 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<grpc::GenericClientAsyncReaderWriter> PrepareCall(
+      grpc::ClientContext* context, const grpc::string& method,
+      grpc::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<grpc::GenericClientAsyncResponseReader> PrepareUnaryCall(
+      grpc::ClientContext* context, const grpc::string& method,
+      const grpc::ByteBuffer& request, grpc::CompletionQueue* cq);
+
+  /// DEPRECATED for multi-threaded use
+  /// Begin a call to a named method \a method using \a context.
+  /// A tag \a tag will be delivered to \a cq when the call has been started
+  /// (i.e, initial metadata has been sent).
+  /// 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<grpc::GenericClientAsyncReaderWriter> Call(
+      grpc::ClientContext* context, const grpc::string& method,
+      grpc::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
+  ///              they are no longer experimental
+  class experimental_type {
+   public:
+    explicit experimental_type(GenericStub* stub) : stub_(stub) {}
+
+    /// 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,
+                   std::function<void(grpc::Status)> 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);
+
+    /// 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<grpc::ByteBuffer,
+                                              grpc::ByteBuffer>* reactor);
+
+   private:
+    GenericStub* stub_;
+  };
+
+  /// NOTE: The function experimental() is not stable public API. It is a view
+  /// to the experimental components of this class. It may be changed or removed
+  /// at any time.
+  experimental_type experimental() { return experimental_type(this); }
+
+ private:
+  std::shared_ptr<grpc::ChannelInterface> channel_;
+};
+
+}  // namespace grpc_impl
+
+#endif  // GRPCPP_GENERIC_GENERIC_STUB_IMPL_H
index dfd4c39..a51b0d1 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * 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.
 #ifndef GRPCPP_HEALTH_CHECK_SERVICE_INTERFACE_H
 #define GRPCPP_HEALTH_CHECK_SERVICE_INTERFACE_H
 
-#include <grpcpp/support/config.h>
+#include <grpcpp/health_check_service_interface_impl.h>
 
 namespace grpc {
 
 const char kHealthCheckServiceInterfaceArg[] =
     "grpc.health_check_service_interface";
 
-/// The gRPC server uses this interface to expose the health checking service
-/// without depending on protobuf.
-class HealthCheckServiceInterface {
- public:
-  virtual ~HealthCheckServiceInterface() {}
-
-  /// Set or change the serving status of the given \a service_name.
-  virtual void SetServingStatus(const grpc::string& service_name,
-                                bool serving) = 0;
-  /// Apply to all registered service names.
-  virtual void SetServingStatus(bool serving) = 0;
-
-  /// Set all registered service names to not serving and prevent future
-  /// state changes.
-  virtual void Shutdown() {}
-};
-
-/// Enable/disable the default health checking service. This applies to all C++
-/// servers created afterwards. For each server, user can override the default
-/// with a HealthCheckServiceServerBuilderOption.
-/// NOT thread safe.
-void EnableDefaultHealthCheckService(bool enable);
-
-/// Returns whether the default health checking service is enabled.
-/// NOT thread safe.
-bool DefaultHealthCheckServiceEnabled();
+typedef ::grpc_impl::HealthCheckServiceInterface HealthCheckServiceInterface;
+
+static inline void EnableDefaultHealthCheckService(bool enable) {
+  ::grpc_impl::EnableDefaultHealthCheckService(enable);
+}
+
+static inline bool DefaultHealthCheckServiceEnabled() {
+  return ::grpc_impl::DefaultHealthCheckServiceEnabled();
+}
 
 }  // namespace grpc
 
diff --git a/include/grpcpp/health_check_service_interface_impl.h b/include/grpcpp/health_check_service_interface_impl.h
new file mode 100644 (file)
index 0000000..025dadb
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *
+ * Copyright 2016 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_HEALTH_CHECK_SERVICE_INTERFACE_IMPL_H
+#define GRPCPP_HEALTH_CHECK_SERVICE_INTERFACE_IMPL_H
+
+#include <grpcpp/support/config.h>
+
+namespace grpc_impl {
+
+/// The gRPC server uses this interface to expose the health checking service
+/// without depending on protobuf.
+class HealthCheckServiceInterface {
+ public:
+  virtual ~HealthCheckServiceInterface() {}
+
+  /// Set or change the serving status of the given \a service_name.
+  virtual void SetServingStatus(const grpc::string& service_name,
+                                bool serving) = 0;
+  /// Apply to all registered service names.
+  virtual void SetServingStatus(bool serving) = 0;
+
+  /// Set all registered service names to not serving and prevent future
+  /// state changes.
+  virtual void Shutdown() {}
+};
+
+/// Enable/disable the default health checking service. This applies to all C++
+/// servers created afterwards. For each server, user can override the default
+/// with a HealthCheckServiceServerBuilderOption.
+/// NOT thread safe.
+void EnableDefaultHealthCheckService(bool enable);
+
+/// Returns whether the default health checking service is enabled.
+/// NOT thread safe.
+bool DefaultHealthCheckServiceEnabled();
+
+}  // namespace grpc_impl
+
+#endif  // GRPCPP_HEALTH_CHECK_SERVICE_INTERFACE_IMPL_H
index 759f668..46d0912 100644 (file)
@@ -39,7 +39,7 @@ class GenericServerContext final : public ServerContext {
   const grpc::string& host() const { return host_; }
 
  private:
-  friend class Server;
+  friend class grpc_impl::Server;
   friend class ServerInterface;
 
   void Clear() {
@@ -79,8 +79,8 @@ class AsyncGenericService final {
                    ServerCompletionQueue* notification_cq, void* tag);
 
  private:
-  friend class Server;
-  Server* server_;
+  friend class grpc_impl::Server;
+  grpc_impl::Server* server_;
 };
 
 namespace experimental {
@@ -135,14 +135,14 @@ class CallbackGenericService {
   }
 
  private:
-  friend class ::grpc::Server;
+  friend class ::grpc_impl::Server;
 
   internal::CallbackBidiHandler<ByteBuffer, ByteBuffer>* Handler() {
     return new internal::CallbackBidiHandler<ByteBuffer, ByteBuffer>(
         [this] { return CreateReactor(); });
   }
 
-  Server* server_{nullptr};
+  grpc_impl::Server* server_{nullptr};
 };
 }  // namespace experimental
 }  // namespace grpc
index bfb2df4..f957726 100644 (file)
@@ -28,8 +28,6 @@
 
 namespace grpc {
 
-class CompletionQueue;
-
 namespace internal {
 /// Common interface for all client side asynchronous streaming.
 class ClientAsyncStreamingInterface {
@@ -661,7 +659,7 @@ class ServerAsyncReaderInterface
   /// 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 to deallocate once Finish returns.
+  /// 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.
@@ -735,7 +733,7 @@ class ServerAsyncReader final : public ServerAsyncReaderInterface<W, R> {
   /// 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 to deallocate once Finish returns.
+  /// 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_) {
@@ -830,7 +828,7 @@ class ServerAsyncWriterInterface
   /// in a single step.
   ///
   /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it
-  /// is safe to to deallocate once WriteAndFinish returns.
+  /// 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.
@@ -897,7 +895,7 @@ class ServerAsyncWriter final : public ServerAsyncWriterInterface<W> {
   /// 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 to deallocate once WriteAndFinish returns.
+  /// 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);
@@ -993,7 +991,7 @@ class ServerAsyncReaderWriterInterface
   /// single step.
   ///
   /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it
-  /// is safe to to deallocate once WriteAndFinish returns.
+  /// 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.
@@ -1068,7 +1066,7 @@ class ServerAsyncReaderWriter final
   /// 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 to deallocate once WriteAndFinish returns.
+  /// 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);
@@ -1099,7 +1097,7 @@ class ServerAsyncReaderWriter final
   }
 
  private:
-  friend class ::grpc::Server;
+  friend class ::grpc_impl::Server;
 
   void BindCall(::grpc::internal::Call* call) override { call_ = *call; }
 
index 89dcb12..4b97cf2 100644 (file)
@@ -29,7 +29,6 @@
 
 namespace grpc {
 
-class CompletionQueue;
 extern CoreCodegenInterface* g_core_codegen_interface;
 
 /// An interface relevant for async client side unary RPCs (which send
index c040c30..eefa4a7 100644 (file)
 #include <grpc/impl/codegen/grpc_types.h>
 #include <grpcpp/impl/codegen/call_hook.h>
 
-namespace grpc {
+namespace grpc_impl {
 class CompletionQueue;
+}
 
+namespace grpc {
 namespace experimental {
 class ClientRpcInfo;
 class ServerRpcInfo;
@@ -41,13 +43,13 @@ class Call final {
         call_(nullptr),
         max_receive_message_size_(-1) {}
   /** call is owned by the caller */
-  Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq)
+  Call(grpc_call* call, CallHook* call_hook, ::grpc_impl::CompletionQueue* cq)
       : call_hook_(call_hook),
         cq_(cq),
         call_(call),
         max_receive_message_size_(-1) {}
 
-  Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq,
+  Call(grpc_call* call, CallHook* call_hook, ::grpc_impl::CompletionQueue* cq,
        experimental::ClientRpcInfo* rpc_info)
       : call_hook_(call_hook),
         cq_(cq),
@@ -55,7 +57,7 @@ class Call final {
         max_receive_message_size_(-1),
         client_rpc_info_(rpc_info) {}
 
-  Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq,
+  Call(grpc_call* call, CallHook* call_hook, ::grpc_impl::CompletionQueue* cq,
        int max_receive_message_size, experimental::ServerRpcInfo* rpc_info)
       : call_hook_(call_hook),
         cq_(cq),
@@ -68,7 +70,7 @@ class Call final {
   }
 
   grpc_call* call() const { return call_; }
-  CompletionQueue* cq() const { return cq_; }
+  ::grpc_impl::CompletionQueue* cq() const { return cq_; }
 
   int max_receive_message_size() const { return max_receive_message_size_; }
 
@@ -82,7 +84,7 @@ class Call final {
 
  private:
   CallHook* call_hook_;
-  CompletionQueue* cq_;
+  ::grpc_impl::CompletionQueue* cq_;
   grpc_call* call_;
   int max_receive_message_size_;
   experimental::ClientRpcInfo* client_rpc_info_ = nullptr;
index 4ca87a9..d810625 100644 (file)
@@ -48,7 +48,6 @@
 
 namespace grpc {
 
-class CompletionQueue;
 extern CoreCodegenInterface* g_core_codegen_interface;
 
 namespace internal {
index 5353f5f..9df233b 100644 (file)
 #include <grpcpp/impl/codegen/status.h>
 #include <grpcpp/impl/codegen/time.h>
 
+namespace grpc_impl {
+class CompletionQueue;
+}
+
 namespace grpc {
 class ChannelInterface;
 class ClientContext;
-class CompletionQueue;
 
 template <class R>
 class ClientReader;
@@ -58,6 +61,7 @@ template <class R>
 class ClientCallbackReaderFactory;
 template <class W>
 class ClientCallbackWriterFactory;
+class ClientCallbackUnaryFactory;
 class InterceptedChannel;
 }  // namespace internal
 
@@ -73,7 +77,7 @@ class ChannelInterface {
   /// deadline expires. \a GetState needs to called to get the current state.
   template <typename T>
   void NotifyOnStateChange(grpc_connectivity_state last_observed, T deadline,
-                           CompletionQueue* cq, void* tag) {
+                           ::grpc_impl::CompletionQueue* cq, void* tag) {
     TimePoint<T> deadline_tp(deadline);
     NotifyOnStateChangeImpl(last_observed, deadline_tp.raw_time(), cq, tag);
   }
@@ -117,6 +121,7 @@ class ChannelInterface {
   friend class ::grpc::internal::ClientCallbackReaderFactory;
   template <class W>
   friend class ::grpc::internal::ClientCallbackWriterFactory;
+  friend class ::grpc::internal::ClientCallbackUnaryFactory;
   template <class InputMessage, class OutputMessage>
   friend class ::grpc::internal::BlockingUnaryCallImpl;
   template <class InputMessage, class OutputMessage>
@@ -125,13 +130,14 @@ class ChannelInterface {
   friend class ::grpc::internal::InterceptedChannel;
   virtual internal::Call CreateCall(const internal::RpcMethod& method,
                                     ClientContext* context,
-                                    CompletionQueue* cq) = 0;
+                                    ::grpc_impl::CompletionQueue* cq) = 0;
   virtual void PerformOpsOnCall(internal::CallOpSetInterface* ops,
                                 internal::Call* call) = 0;
   virtual void* RegisterMethod(const char* method) = 0;
   virtual void NotifyOnStateChangeImpl(grpc_connectivity_state last_observed,
                                        gpr_timespec deadline,
-                                       CompletionQueue* cq, void* tag) = 0;
+                                       ::grpc_impl::CompletionQueue* cq,
+                                       void* tag) = 0;
   virtual bool WaitForStateChangeImpl(grpc_connectivity_state last_observed,
                                       gpr_timespec deadline) = 0;
 
@@ -144,7 +150,7 @@ class ChannelInterface {
   // change (even though this is private and non-API)
   virtual internal::Call CreateCallInternal(const internal::RpcMethod& method,
                                             ClientContext* context,
-                                            CompletionQueue* cq,
+                                            ::grpc_impl::CompletionQueue* cq,
                                             size_t interceptor_pos) {
     return internal::Call();
   }
@@ -157,7 +163,7 @@ class ChannelInterface {
   // Returns nullptr (rather than being pure) since this is a post-1.0 method
   // and adding a new pure method to an interface would be a breaking change
   // (even though this is private and non-API)
-  virtual CompletionQueue* CallbackCQ() { return nullptr; }
+  virtual ::grpc_impl::CompletionQueue* CallbackCQ() { return nullptr; }
 };
 }  // namespace grpc
 
index 53c57b5..dda9aec 100644 (file)
 #include <grpcpp/impl/codegen/core_codegen_interface.h>
 #include <grpcpp/impl/codegen/status.h>
 
+namespace grpc_impl {
+class Channel;
+}
+
 namespace grpc {
 
-class Channel;
 class ClientContext;
-class CompletionQueue;
 
 namespace internal {
 class RpcMethod;
@@ -100,6 +102,7 @@ template <class Response>
 class ClientReadReactor;
 template <class Request>
 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
@@ -157,6 +160,15 @@ class ClientCallbackWriter {
   }
 };
 
+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
@@ -164,6 +176,8 @@ class ClientCallbackWriter {
 // 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 Request, class Response>
@@ -346,6 +360,36 @@ class ClientWriteReactor {
   ClientCallbackWriter<Request>* 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 {
@@ -512,9 +556,9 @@ class ClientCallbackReaderWriterImpl
     this->BindReactor(reactor);
   }
 
-  ClientContext* context_;
+  ClientContext* const context_;
   Call call_;
-  ::grpc::experimental::ClientBidiReactor<Request, Response>* reactor_;
+  ::grpc::experimental::ClientBidiReactor<Request, Response>* const reactor_;
 
   CallOpSet<CallOpSendInitialMetadata, CallOpRecvInitialMetadata> start_ops_;
   CallbackWithSuccessTag start_tag_;
@@ -651,9 +695,9 @@ class ClientCallbackReaderImpl
     start_ops_.ClientSendClose();
   }
 
-  ClientContext* context_;
+  ClientContext* const context_;
   Call call_;
-  ::grpc::experimental::ClientReadReactor<Response>* reactor_;
+  ::grpc::experimental::ClientReadReactor<Response>* const reactor_;
 
   CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, CallOpClientSendClose,
             CallOpRecvInitialMetadata>
@@ -824,9 +868,9 @@ class ClientCallbackWriterImpl
     finish_ops_.AllowNoMessage();
   }
 
-  ClientContext* context_;
+  ClientContext* const context_;
   Call call_;
-  ::grpc::experimental::ClientWriteReactor<Request>* reactor_;
+  ::grpc::experimental::ClientWriteReactor<Request>* const reactor_;
 
   CallOpSet<CallOpSendInitialMetadata, CallOpRecvInitialMetadata> start_ops_;
   CallbackWithSuccessTag start_tag_;
@@ -867,6 +911,109 @@ class ClientCallbackWriterFactory {
   }
 };
 
+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 (--callbacks_outstanding_ == 0) {
+      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 <class Request, class Response>
+  ClientCallbackUnaryImpl(Call call, 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();
+  }
+
+  ClientContext* const context_;
+  Call call_;
+  ::grpc::experimental::ClientUnaryReactor* const reactor_;
+
+  CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, CallOpClientSendClose,
+            CallOpRecvInitialMetadata>
+      start_ops_;
+  CallbackWithSuccessTag start_tag_;
+
+  CallOpSet<CallOpGenericRecvMessage, CallOpClientRecvStatus> finish_ops_;
+  CallbackWithSuccessTag finish_tag_;
+  Status finish_status_;
+
+  // This call will have 2 callbacks: start and finish
+  std::atomic_int callbacks_outstanding_{2};
+  bool started_{false};
+};
+
+class ClientCallbackUnaryFactory {
+ public:
+  template <class Request, class Response>
+  static void Create(ChannelInterface* channel,
+                     const ::grpc::internal::RpcMethod& method,
+                     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
 }  // namespace grpc
 
index 5946488..999d8fc 100644 (file)
 #include <grpcpp/impl/codegen/slice.h>
 #include <grpcpp/impl/codegen/status.h>
 #include <grpcpp/impl/codegen/string_ref.h>
+#include <grpcpp/impl/codegen/sync.h>
 #include <grpcpp/impl/codegen/time.h>
 
 struct census_context;
 struct grpc_call;
 
-namespace grpc {
+namespace grpc_impl {
 
+class CallCredentials;
 class Channel;
-class ChannelInterface;
 class CompletionQueue;
-class CallCredentials;
+}  // namespace grpc_impl
+namespace grpc {
+
+class ChannelInterface;
 class ClientContext;
 
 namespace internal {
@@ -78,6 +82,7 @@ template <class Response>
 class ClientCallbackReaderImpl;
 template <class Request>
 class ClientCallbackWriterImpl;
+class ClientCallbackUnaryImpl;
 }  // namespace internal
 
 template <class R>
@@ -304,7 +309,8 @@ class ClientContext {
   /// call.
   ///
   /// \see  https://grpc.io/docs/guides/auth.html
-  void set_credentials(const std::shared_ptr<CallCredentials>& creds) {
+  void set_credentials(
+      const std::shared_ptr<grpc_impl::CallCredentials>& creds) {
     creds_ = creds;
   }
 
@@ -391,7 +397,7 @@ class ClientContext {
   friend class ::grpc::testing::InteropClientContextInspector;
   friend class ::grpc::internal::CallOpClientRecvStatus;
   friend class ::grpc::internal::CallOpRecvInitialMetadata;
-  friend class Channel;
+  friend class ::grpc_impl::Channel;
   template <class R>
   friend class ::grpc::ClientReader;
   template <class W>
@@ -416,6 +422,7 @@ class ClientContext {
   friend class ::grpc::internal::ClientCallbackReaderImpl;
   template <class Request>
   friend class ::grpc::internal::ClientCallbackWriterImpl;
+  friend class ::grpc::internal::ClientCallbackUnaryImpl;
 
   // Used by friend class CallOpClientRecvStatus
   void set_debug_error_string(const grpc::string& debug_error_string) {
@@ -423,7 +430,8 @@ class ClientContext {
   }
 
   grpc_call* call() const { return call_; }
-  void set_call(grpc_call* call, const std::shared_ptr<Channel>& channel);
+  void set_call(grpc_call* call,
+                const std::shared_ptr<::grpc_impl::Channel>& channel);
 
   experimental::ClientRpcInfo* set_client_rpc_info(
       const char* method, internal::RpcMethod::RpcType type,
@@ -456,13 +464,13 @@ class ClientContext {
   bool wait_for_ready_explicitly_set_;
   bool idempotent_;
   bool cacheable_;
-  std::shared_ptr<Channel> channel_;
-  std::mutex mu_;
+  std::shared_ptr<::grpc_impl::Channel> channel_;
+  grpc::internal::Mutex mu_;
   grpc_call* call_;
   bool call_canceled_;
   gpr_timespec deadline_;
   grpc::string authority_;
-  std::shared_ptr<CallCredentials> creds_;
+  std::shared_ptr<grpc_impl::CallCredentials> creds_;
   mutable std::shared_ptr<const AuthContext> auth_context_;
   struct census_context* census_context_;
   std::multimap<grpc::string, grpc::string> send_initial_metadata_;
index 1910654..a4c85a7 100644 (file)
 #include <grpcpp/impl/codegen/rpc_method.h>
 #include <grpcpp/impl/codegen/string_ref.h>
 
+namespace grpc_impl {
+
+class Channel;
+}
+
 namespace grpc {
 
 class ClientContext;
-class Channel;
 
 namespace internal {
 class InterceptorBatchMethodsImpl;
index b9f8e16..e0f692b 100644 (file)
@@ -27,9 +27,7 @@
 
 namespace grpc {
 
-class Channel;
 class ClientContext;
-class CompletionQueue;
 
 namespace internal {
 class RpcMethod;
index 4812f02..f67a378 100644 (file)
  *
  */
 
-/// A completion queue implements a concurrent producer-consumer queue, with
-/// two main API-exposed methods: \a Next and \a AsyncNext. These
-/// methods are the essential component of the gRPC C++ asynchronous API.
-/// There is also a \a Shutdown method to indicate that a given completion queue
-/// will no longer have regular events. This must be called before the
-/// completion queue is destroyed.
-/// All completion queue APIs are thread-safe and may be used concurrently with
-/// any other completion queue API invocation; it is acceptable to have
-/// multiple threads calling \a Next or \a AsyncNext on the same or different
-/// completion queues, or to call these methods concurrently with a \a Shutdown
-/// elsewhere.
-/// \remark{All other API calls on completion queue should be completed before
-/// a completion queue destructor is called.}
 #ifndef GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_H
 #define GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_H
 
-#include <grpc/impl/codegen/atm.h>
-#include <grpcpp/impl/codegen/completion_queue_tag.h>
-#include <grpcpp/impl/codegen/core_codegen_interface.h>
-#include <grpcpp/impl/codegen/grpc_library.h>
-#include <grpcpp/impl/codegen/status.h>
-#include <grpcpp/impl/codegen/time.h>
-
-struct grpc_completion_queue;
+#include <grpcpp/impl/codegen/completion_queue_impl.h>
 
 namespace grpc {
 
-template <class R>
-class ClientReader;
-template <class W>
-class ClientWriter;
-template <class W, class R>
-class ClientReaderWriter;
-template <class R>
-class ServerReader;
-template <class W>
-class ServerWriter;
-namespace internal {
-template <class W, class R>
-class ServerReaderWriterBody;
-}  // namespace internal
-
-class Channel;
-class ChannelInterface;
-class ClientContext;
-class CompletionQueue;
-class Server;
-class ServerBuilder;
-class ServerContext;
-class ServerInterface;
-
-namespace internal {
-class CompletionQueueTag;
-class RpcMethod;
-template <class ServiceType, class RequestType, class ResponseType>
-class RpcMethodHandler;
-template <class ServiceType, class RequestType, class ResponseType>
-class ClientStreamingHandler;
-template <class ServiceType, class RequestType, class ResponseType>
-class ServerStreamingHandler;
-template <class ServiceType, class RequestType, class ResponseType>
-class BidiStreamingHandler;
-template <class Streamer, bool WriteNeeded>
-class TemplatedBidiStreamingHandler;
-template <StatusCode code>
-class ErrorMethodHandler;
-template <class InputMessage, class OutputMessage>
-class BlockingUnaryCallImpl;
-template <class Op1, class Op2, class Op3, class Op4, class Op5, class Op6>
-class CallOpSet;
-}  // namespace internal
-
-extern CoreCodegenInterface* g_core_codegen_interface;
-
-/// A thin wrapper around \ref grpc_completion_queue (see \ref
-/// src/core/lib/surface/completion_queue.h).
-/// See \ref doc/cpp/perf_notes.md for notes on best practices for high
-/// performance servers.
-class CompletionQueue : private GrpcLibraryCodegen {
- public:
-  /// Default constructor. Implicitly creates a \a grpc_completion_queue
-  /// instance.
-  CompletionQueue()
-      : CompletionQueue(grpc_completion_queue_attributes{
-            GRPC_CQ_CURRENT_VERSION, GRPC_CQ_NEXT, GRPC_CQ_DEFAULT_POLLING,
-            nullptr}) {}
-
-  /// Wrap \a take, taking ownership of the instance.
-  ///
-  /// \param take The completion queue instance to wrap. Ownership is taken.
-  explicit CompletionQueue(grpc_completion_queue* take);
-
-  /// Destructor. Destroys the owned wrapped completion queue / instance.
-  ~CompletionQueue() {
-    g_core_codegen_interface->grpc_completion_queue_destroy(cq_);
-  }
-
-  /// Tri-state return for AsyncNext: SHUTDOWN, GOT_EVENT, TIMEOUT.
-  enum NextStatus {
-    SHUTDOWN,   ///< The completion queue has been shutdown and fully-drained
-    GOT_EVENT,  ///< Got a new event; \a tag will be filled in with its
-                ///< associated value; \a ok indicating its success.
-    TIMEOUT     ///< deadline was reached.
-  };
-
-  /// Read from the queue, blocking until an event is available or the queue is
-  /// shutting down.
-  ///
-  /// \param tag [out] Updated to point to the read event's tag.
-  /// \param ok [out] true if read a successful event, false otherwise.
-  ///
-  /// Note that each tag sent to the completion queue (through RPC operations
-  /// or alarms) will be delivered out of the completion queue by a call to
-  /// Next (or a related method), regardless of whether the operation succeeded
-  /// or not. Success here means that this operation completed in the normal
-  /// valid manner.
-  ///
-  /// Server-side RPC request: \a ok indicates that the RPC has indeed
-  /// been started. If it is false, the server has been Shutdown
-  /// before this particular call got matched to an incoming RPC.
-  ///
-  /// Client-side StartCall/RPC invocation: \a ok indicates that the RPC is
-  /// going to go to the wire. If it is false, it not going to the wire. This
-  /// would happen if the channel is either permanently broken or
-  /// transiently broken but with the fail-fast option. (Note that async unary
-  /// RPCs don't post a CQ tag at this point, nor do client-streaming
-  /// or bidi-streaming RPCs that have the initial metadata corked option set.)
-  ///
-  /// Client-side Write, Client-side WritesDone, Server-side Write,
-  /// Server-side Finish, Server-side SendInitialMetadata (which is
-  /// typically included in Write or Finish when not done explicitly):
-  /// \a ok means that the data/metadata/status/etc is going to go to the
-  /// wire. If it is false, it not going to the wire because the call
-  /// is already dead (i.e., canceled, deadline expired, other side
-  /// dropped the channel, etc).
-  ///
-  /// Client-side Read, Server-side Read, Client-side
-  /// RecvInitialMetadata (which is typically included in Read if not
-  /// done explicitly): \a ok indicates whether there is a valid message
-  /// that got read. If not, you know that there are certainly no more
-  /// messages that can ever be read from this stream. For the client-side
-  /// operations, this only happens because the call is dead. For the
-  /// server-sider operation, though, this could happen because the client
-  /// has done a WritesDone already.
-  ///
-  /// Client-side Finish: \a ok should always be true
-  ///
-  /// Server-side AsyncNotifyWhenDone: \a ok should always be true
-  ///
-  /// Alarm: \a ok is true if it expired, false if it was canceled
-  ///
-  /// \return true if got an event, false if the queue is fully drained and
-  ///         shut down.
-  bool Next(void** tag, bool* ok) {
-    return (AsyncNextInternal(tag, ok,
-                              g_core_codegen_interface->gpr_inf_future(
-                                  GPR_CLOCK_REALTIME)) != SHUTDOWN);
-  }
-
-  /// Read from the queue, blocking up to \a deadline (or the queue's shutdown).
-  /// Both \a tag and \a ok are updated upon success (if an event is available
-  /// within the \a deadline).  A \a tag points to an arbitrary location usually
-  /// employed to uniquely identify an event.
-  ///
-  /// \param tag [out] Upon sucess, updated to point to the event's tag.
-  /// \param ok [out] Upon sucess, true if a successful event, false otherwise
-  ///        See documentation for CompletionQueue::Next for explanation of ok
-  /// \param deadline [in] How long to block in wait for an event.
-  ///
-  /// \return The type of event read.
-  template <typename T>
-  NextStatus AsyncNext(void** tag, bool* ok, const T& deadline) {
-    TimePoint<T> deadline_tp(deadline);
-    return AsyncNextInternal(tag, ok, deadline_tp.raw_time());
-  }
-
-  /// EXPERIMENTAL
-  /// First executes \a F, then reads from the queue, blocking up to
-  /// \a deadline (or the queue's shutdown).
-  /// Both \a tag and \a ok are updated upon success (if an event is available
-  /// within the \a deadline).  A \a tag points to an arbitrary location usually
-  /// employed to uniquely identify an event.
-  ///
-  /// \param f [in] Function to execute before calling AsyncNext on this queue.
-  /// \param tag [out] Upon sucess, updated to point to the event's tag.
-  /// \param ok [out] Upon sucess, true if read a regular event, false
-  /// otherwise.
-  /// \param deadline [in] How long to block in wait for an event.
-  ///
-  /// \return The type of event read.
-  template <typename T, typename F>
-  NextStatus DoThenAsyncNext(F&& f, void** tag, bool* ok, const T& deadline) {
-    CompletionQueueTLSCache cache = CompletionQueueTLSCache(this);
-    f();
-    if (cache.Flush(tag, ok)) {
-      return GOT_EVENT;
-    } else {
-      return AsyncNext(tag, ok, deadline);
-    }
-  }
-
-  /// Request the shutdown of the queue.
-  ///
-  /// \warning This method must be called at some point if this completion queue
-  /// is accessed with Next or AsyncNext. \a Next will not return false
-  /// until this method has been called and all pending tags have been drained.
-  /// (Likewise for \a AsyncNext returning \a NextStatus::SHUTDOWN .)
-  /// Only once either one of these methods does that (that is, once the queue
-  /// has been \em drained) can an instance of this class be destroyed.
-  /// Also note that applications must ensure that no work is enqueued on this
-  /// completion queue after this method is called.
-  void Shutdown();
-
-  /// Returns a \em raw pointer to the underlying \a grpc_completion_queue
-  /// instance.
-  ///
-  /// \warning Remember that the returned instance is owned. No transfer of
-  /// owership is performed.
-  grpc_completion_queue* cq() { return cq_; }
-
- protected:
-  /// Private constructor of CompletionQueue only visible to friend classes
-  CompletionQueue(const grpc_completion_queue_attributes& attributes) {
-    cq_ = g_core_codegen_interface->grpc_completion_queue_create(
-        g_core_codegen_interface->grpc_completion_queue_factory_lookup(
-            &attributes),
-        &attributes, NULL);
-    InitialAvalanching();  // reserve this for the future shutdown
-  }
-
- private:
-  // Friend synchronous wrappers so that they can access Pluck(), which is
-  // a semi-private API geared towards the synchronous implementation.
-  template <class R>
-  friend class ::grpc::ClientReader;
-  template <class W>
-  friend class ::grpc::ClientWriter;
-  template <class W, class R>
-  friend class ::grpc::ClientReaderWriter;
-  template <class R>
-  friend class ::grpc::ServerReader;
-  template <class W>
-  friend class ::grpc::ServerWriter;
-  template <class W, class R>
-  friend class ::grpc::internal::ServerReaderWriterBody;
-  template <class ServiceType, class RequestType, class ResponseType>
-  friend class ::grpc::internal::RpcMethodHandler;
-  template <class ServiceType, class RequestType, class ResponseType>
-  friend class ::grpc::internal::ClientStreamingHandler;
-  template <class ServiceType, class RequestType, class ResponseType>
-  friend class ::grpc::internal::ServerStreamingHandler;
-  template <class Streamer, bool WriteNeeded>
-  friend class ::grpc::internal::TemplatedBidiStreamingHandler;
-  template <StatusCode code>
-  friend class ::grpc::internal::ErrorMethodHandler;
-  friend class ::grpc::Server;
-  friend class ::grpc::ServerContext;
-  friend class ::grpc::ServerInterface;
-  template <class InputMessage, class OutputMessage>
-  friend class ::grpc::internal::BlockingUnaryCallImpl;
-
-  // Friends that need access to constructor for callback CQ
-  friend class ::grpc::Channel;
-
-  // For access to Register/CompleteAvalanching
-  template <class Op1, class Op2, class Op3, class Op4, class Op5, class Op6>
-  friend class ::grpc::internal::CallOpSet;
-
-  /// EXPERIMENTAL
-  /// Creates a Thread Local cache to store the first event
-  /// On this completion queue queued from this thread.  Once
-  /// initialized, it must be flushed on the same thread.
-  class CompletionQueueTLSCache {
-   public:
-    CompletionQueueTLSCache(CompletionQueue* cq);
-    ~CompletionQueueTLSCache();
-    bool Flush(void** tag, bool* ok);
-
-   private:
-    CompletionQueue* cq_;
-    bool flushed_;
-  };
-
-  NextStatus AsyncNextInternal(void** tag, bool* ok, gpr_timespec deadline);
-
-  /// Wraps \a grpc_completion_queue_pluck.
-  /// \warning Must not be mixed with calls to \a Next.
-  bool Pluck(internal::CompletionQueueTag* tag) {
-    auto deadline =
-        g_core_codegen_interface->gpr_inf_future(GPR_CLOCK_REALTIME);
-    while (true) {
-      auto ev = g_core_codegen_interface->grpc_completion_queue_pluck(
-          cq_, tag, deadline, nullptr);
-      bool ok = ev.success != 0;
-      void* ignored = tag;
-      if (tag->FinalizeResult(&ignored, &ok)) {
-        GPR_CODEGEN_ASSERT(ignored == tag);
-        return ok;
-      }
-    }
-  }
-
-  /// Performs a single polling pluck on \a tag.
-  /// \warning Must not be mixed with calls to \a Next.
-  ///
-  /// TODO: sreek - This calls tag->FinalizeResult() even if the cq_ is already
-  /// shutdown. This is most likely a bug and if it is a bug, then change this
-  /// implementation to simple call the other TryPluck function with a zero
-  /// timeout. i.e:
-  ///      TryPluck(tag, gpr_time_0(GPR_CLOCK_REALTIME))
-  void TryPluck(internal::CompletionQueueTag* tag) {
-    auto deadline = g_core_codegen_interface->gpr_time_0(GPR_CLOCK_REALTIME);
-    auto ev = g_core_codegen_interface->grpc_completion_queue_pluck(
-        cq_, tag, deadline, nullptr);
-    if (ev.type == GRPC_QUEUE_TIMEOUT) return;
-    bool ok = ev.success != 0;
-    void* ignored = tag;
-    // the tag must be swallowed if using TryPluck
-    GPR_CODEGEN_ASSERT(!tag->FinalizeResult(&ignored, &ok));
-  }
-
-  /// Performs a single polling pluck on \a tag. Calls tag->FinalizeResult if
-  /// the pluck() was successful and returned the tag.
-  ///
-  /// This exects tag->FinalizeResult (if called) to return 'false' i.e expects
-  /// that the tag is internal not something that is returned to the user.
-  void TryPluck(internal::CompletionQueueTag* tag, gpr_timespec deadline) {
-    auto ev = g_core_codegen_interface->grpc_completion_queue_pluck(
-        cq_, tag, deadline, nullptr);
-    if (ev.type == GRPC_QUEUE_TIMEOUT || ev.type == GRPC_QUEUE_SHUTDOWN) {
-      return;
-    }
-
-    bool ok = ev.success != 0;
-    void* ignored = tag;
-    GPR_CODEGEN_ASSERT(!tag->FinalizeResult(&ignored, &ok));
-  }
-
-  /// Manage state of avalanching operations : completion queue tags that
-  /// trigger other completion queue operations. The underlying core completion
-  /// queue should not really shutdown until all avalanching operations have
-  /// been finalized. Note that we maintain the requirement that an avalanche
-  /// registration must take place before CQ shutdown (which must be maintained
-  /// elsehwere)
-  void InitialAvalanching() {
-    gpr_atm_rel_store(&avalanches_in_flight_, static_cast<gpr_atm>(1));
-  }
-  void RegisterAvalanching() {
-    gpr_atm_no_barrier_fetch_add(&avalanches_in_flight_,
-                                 static_cast<gpr_atm>(1));
-  }
-  void CompleteAvalanching() {
-    if (gpr_atm_no_barrier_fetch_add(&avalanches_in_flight_,
-                                     static_cast<gpr_atm>(-1)) == 1) {
-      g_core_codegen_interface->grpc_completion_queue_shutdown(cq_);
-    }
-  }
-
-  grpc_completion_queue* cq_;  // owned
-
-  gpr_atm avalanches_in_flight_;
-};
-
-/// A specific type of completion queue used by the processing of notifications
-/// by servers. Instantiated by \a ServerBuilder.
-class ServerCompletionQueue : public CompletionQueue {
- public:
-  bool IsFrequentlyPolled() { return polling_type_ != GRPC_CQ_NON_LISTENING; }
-
- protected:
-  /// Default constructor
-  ServerCompletionQueue() : polling_type_(GRPC_CQ_DEFAULT_POLLING) {}
-
- private:
-  /// \param completion_type indicates whether this is a NEXT or CALLBACK
-  /// completion queue.
-  /// \param polling_type Informs the GRPC library about the type of polling
-  /// allowed on this completion queue. See grpc_cq_polling_type's description
-  /// in grpc_types.h for more details.
-  /// \param shutdown_cb is the shutdown callback used for CALLBACK api queues
-  ServerCompletionQueue(grpc_cq_completion_type completion_type,
-                        grpc_cq_polling_type polling_type,
-                        grpc_experimental_completion_queue_functor* shutdown_cb)
-      : CompletionQueue(grpc_completion_queue_attributes{
-            GRPC_CQ_CURRENT_VERSION, completion_type, polling_type,
-            shutdown_cb}),
-        polling_type_(polling_type) {}
-
-  grpc_cq_polling_type polling_type_;
-  friend class ServerBuilder;
-  friend class Server;
-};
+typedef ::grpc_impl::CompletionQueue CompletionQueue;
+typedef ::grpc_impl::ServerCompletionQueue ServerCompletionQueue;
 
 }  // namespace grpc
 
diff --git a/include/grpcpp/impl/codegen/completion_queue_impl.h b/include/grpcpp/impl/codegen/completion_queue_impl.h
new file mode 100644 (file)
index 0000000..5435f2f
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ *
+ * Copyright 2015-2016 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.
+ *
+ */
+
+/// A completion queue implements a concurrent producer-consumer queue, with
+/// two main API-exposed methods: \a Next and \a AsyncNext. These
+/// methods are the essential component of the gRPC C++ asynchronous API.
+/// There is also a \a Shutdown method to indicate that a given completion queue
+/// will no longer have regular events. This must be called before the
+/// completion queue is destroyed.
+/// All completion queue APIs are thread-safe and may be used concurrently with
+/// any other completion queue API invocation; it is acceptable to have
+/// multiple threads calling \a Next or \a AsyncNext on the same or different
+/// completion queues, or to call these methods concurrently with a \a Shutdown
+/// elsewhere.
+/// \remark{All other API calls on completion queue should be completed before
+/// a completion queue destructor is called.}
+#ifndef GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_IMPL_H
+#define GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_IMPL_H
+
+#include <grpc/impl/codegen/atm.h>
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+#include <grpcpp/impl/codegen/status.h>
+#include <grpcpp/impl/codegen/time.h>
+
+struct grpc_completion_queue;
+
+namespace grpc_impl {
+
+class Channel;
+class Server;
+class ServerBuilder;
+}  // namespace grpc_impl
+namespace grpc {
+
+template <class R>
+class ClientReader;
+template <class W>
+class ClientWriter;
+template <class W, class R>
+class ClientReaderWriter;
+template <class R>
+class ServerReader;
+template <class W>
+class ServerWriter;
+namespace internal {
+template <class W, class R>
+class ServerReaderWriterBody;
+}  // namespace internal
+
+class ChannelInterface;
+class ClientContext;
+class ServerContext;
+class ServerInterface;
+
+namespace internal {
+class CompletionQueueTag;
+class RpcMethod;
+template <class ServiceType, class RequestType, class ResponseType>
+class RpcMethodHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class ClientStreamingHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class ServerStreamingHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class BidiStreamingHandler;
+template <class Streamer, bool WriteNeeded>
+class TemplatedBidiStreamingHandler;
+template <StatusCode code>
+class ErrorMethodHandler;
+template <class InputMessage, class OutputMessage>
+class BlockingUnaryCallImpl;
+template <class Op1, class Op2, class Op3, class Op4, class Op5, class Op6>
+class CallOpSet;
+}  // namespace internal
+
+extern CoreCodegenInterface* g_core_codegen_interface;
+
+}  // namespace grpc
+
+namespace grpc_impl {
+
+/// A thin wrapper around \ref grpc_completion_queue (see \ref
+/// src/core/lib/surface/completion_queue.h).
+/// See \ref doc/cpp/perf_notes.md for notes on best practices for high
+/// performance servers.
+class CompletionQueue : private ::grpc::GrpcLibraryCodegen {
+ public:
+  /// Default constructor. Implicitly creates a \a grpc_completion_queue
+  /// instance.
+  CompletionQueue()
+      : CompletionQueue(grpc_completion_queue_attributes{
+            GRPC_CQ_CURRENT_VERSION, GRPC_CQ_NEXT, GRPC_CQ_DEFAULT_POLLING,
+            nullptr}) {}
+
+  /// Wrap \a take, taking ownership of the instance.
+  ///
+  /// \param take The completion queue instance to wrap. Ownership is taken.
+  explicit CompletionQueue(grpc_completion_queue* take);
+
+  /// Destructor. Destroys the owned wrapped completion queue / instance.
+  ~CompletionQueue() {
+    ::grpc::g_core_codegen_interface->grpc_completion_queue_destroy(cq_);
+  }
+
+  /// Tri-state return for AsyncNext: SHUTDOWN, GOT_EVENT, TIMEOUT.
+  enum NextStatus {
+    SHUTDOWN,   ///< The completion queue has been shutdown and fully-drained
+    GOT_EVENT,  ///< Got a new event; \a tag will be filled in with its
+                ///< associated value; \a ok indicating its success.
+    TIMEOUT     ///< deadline was reached.
+  };
+
+  /// Read from the queue, blocking until an event is available or the queue is
+  /// shutting down.
+  ///
+  /// \param tag [out] Updated to point to the read event's tag.
+  /// \param ok [out] true if read a successful event, false otherwise.
+  ///
+  /// Note that each tag sent to the completion queue (through RPC operations
+  /// or alarms) will be delivered out of the completion queue by a call to
+  /// Next (or a related method), regardless of whether the operation succeeded
+  /// or not. Success here means that this operation completed in the normal
+  /// valid manner.
+  ///
+  /// Server-side RPC request: \a ok indicates that the RPC has indeed
+  /// been started. If it is false, the server has been Shutdown
+  /// before this particular call got matched to an incoming RPC.
+  ///
+  /// Client-side StartCall/RPC invocation: \a ok indicates that the RPC is
+  /// going to go to the wire. If it is false, it not going to the wire. This
+  /// would happen if the channel is either permanently broken or
+  /// transiently broken but with the fail-fast option. (Note that async unary
+  /// RPCs don't post a CQ tag at this point, nor do client-streaming
+  /// or bidi-streaming RPCs that have the initial metadata corked option set.)
+  ///
+  /// Client-side Write, Client-side WritesDone, Server-side Write,
+  /// Server-side Finish, Server-side SendInitialMetadata (which is
+  /// typically included in Write or Finish when not done explicitly):
+  /// \a ok means that the data/metadata/status/etc is going to go to the
+  /// wire. If it is false, it not going to the wire because the call
+  /// is already dead (i.e., canceled, deadline expired, other side
+  /// dropped the channel, etc).
+  ///
+  /// Client-side Read, Server-side Read, Client-side
+  /// RecvInitialMetadata (which is typically included in Read if not
+  /// done explicitly): \a ok indicates whether there is a valid message
+  /// that got read. If not, you know that there are certainly no more
+  /// messages that can ever be read from this stream. For the client-side
+  /// operations, this only happens because the call is dead. For the
+  /// server-sider operation, though, this could happen because the client
+  /// has done a WritesDone already.
+  ///
+  /// Client-side Finish: \a ok should always be true
+  ///
+  /// Server-side AsyncNotifyWhenDone: \a ok should always be true
+  ///
+  /// Alarm: \a ok is true if it expired, false if it was canceled
+  ///
+  /// \return true if got an event, false if the queue is fully drained and
+  ///         shut down.
+  bool Next(void** tag, bool* ok) {
+    return (AsyncNextInternal(tag, ok,
+                              ::grpc::g_core_codegen_interface->gpr_inf_future(
+                                  GPR_CLOCK_REALTIME)) != SHUTDOWN);
+  }
+
+  /// Read from the queue, blocking up to \a deadline (or the queue's shutdown).
+  /// Both \a tag and \a ok are updated upon success (if an event is available
+  /// within the \a deadline).  A \a tag points to an arbitrary location usually
+  /// employed to uniquely identify an event.
+  ///
+  /// \param tag [out] Upon sucess, updated to point to the event's tag.
+  /// \param ok [out] Upon sucess, true if a successful event, false otherwise
+  ///        See documentation for CompletionQueue::Next for explanation of ok
+  /// \param deadline [in] How long to block in wait for an event.
+  ///
+  /// \return The type of event read.
+  template <typename T>
+  NextStatus AsyncNext(void** tag, bool* ok, const T& deadline) {
+    ::grpc::TimePoint<T> deadline_tp(deadline);
+    return AsyncNextInternal(tag, ok, deadline_tp.raw_time());
+  }
+
+  /// EXPERIMENTAL
+  /// First executes \a F, then reads from the queue, blocking up to
+  /// \a deadline (or the queue's shutdown).
+  /// Both \a tag and \a ok are updated upon success (if an event is available
+  /// within the \a deadline).  A \a tag points to an arbitrary location usually
+  /// employed to uniquely identify an event.
+  ///
+  /// \param f [in] Function to execute before calling AsyncNext on this queue.
+  /// \param tag [out] Upon sucess, updated to point to the event's tag.
+  /// \param ok [out] Upon sucess, true if read a regular event, false
+  /// otherwise.
+  /// \param deadline [in] How long to block in wait for an event.
+  ///
+  /// \return The type of event read.
+  template <typename T, typename F>
+  NextStatus DoThenAsyncNext(F&& f, void** tag, bool* ok, const T& deadline) {
+    CompletionQueueTLSCache cache = CompletionQueueTLSCache(this);
+    f();
+    if (cache.Flush(tag, ok)) {
+      return GOT_EVENT;
+    } else {
+      return AsyncNext(tag, ok, deadline);
+    }
+  }
+
+  /// Request the shutdown of the queue.
+  ///
+  /// \warning This method must be called at some point if this completion queue
+  /// is accessed with Next or AsyncNext. \a Next will not return false
+  /// until this method has been called and all pending tags have been drained.
+  /// (Likewise for \a AsyncNext returning \a NextStatus::SHUTDOWN .)
+  /// Only once either one of these methods does that (that is, once the queue
+  /// has been \em drained) can an instance of this class be destroyed.
+  /// Also note that applications must ensure that no work is enqueued on this
+  /// completion queue after this method is called.
+  void Shutdown();
+
+  /// Returns a \em raw pointer to the underlying \a grpc_completion_queue
+  /// instance.
+  ///
+  /// \warning Remember that the returned instance is owned. No transfer of
+  /// owership is performed.
+  grpc_completion_queue* cq() { return cq_; }
+
+ protected:
+  /// Private constructor of CompletionQueue only visible to friend classes
+  CompletionQueue(const grpc_completion_queue_attributes& attributes) {
+    cq_ = ::grpc::g_core_codegen_interface->grpc_completion_queue_create(
+        ::grpc::g_core_codegen_interface->grpc_completion_queue_factory_lookup(
+            &attributes),
+        &attributes, NULL);
+    InitialAvalanching();  // reserve this for the future shutdown
+  }
+
+ private:
+  // Friend synchronous wrappers so that they can access Pluck(), which is
+  // a semi-private API geared towards the synchronous implementation.
+  template <class R>
+  friend class ::grpc::ClientReader;
+  template <class W>
+  friend class ::grpc::ClientWriter;
+  template <class W, class R>
+  friend class ::grpc::ClientReaderWriter;
+  template <class R>
+  friend class ::grpc::ServerReader;
+  template <class W>
+  friend class ::grpc::ServerWriter;
+  template <class W, class R>
+  friend class ::grpc::internal::ServerReaderWriterBody;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class ::grpc::internal::RpcMethodHandler;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class ::grpc::internal::ClientStreamingHandler;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class ::grpc::internal::ServerStreamingHandler;
+  template <class Streamer, bool WriteNeeded>
+  friend class ::grpc::internal::TemplatedBidiStreamingHandler;
+  template <::grpc::StatusCode code>
+  friend class ::grpc::internal::ErrorMethodHandler;
+  friend class ::grpc_impl::Server;
+  friend class ::grpc::ServerContext;
+  friend class ::grpc::ServerInterface;
+  template <class InputMessage, class OutputMessage>
+  friend class ::grpc::internal::BlockingUnaryCallImpl;
+
+  // Friends that need access to constructor for callback CQ
+  friend class ::grpc_impl::Channel;
+
+  // For access to Register/CompleteAvalanching
+  template <class Op1, class Op2, class Op3, class Op4, class Op5, class Op6>
+  friend class ::grpc::internal::CallOpSet;
+
+  /// EXPERIMENTAL
+  /// Creates a Thread Local cache to store the first event
+  /// On this completion queue queued from this thread.  Once
+  /// initialized, it must be flushed on the same thread.
+  class CompletionQueueTLSCache {
+   public:
+    CompletionQueueTLSCache(CompletionQueue* cq);
+    ~CompletionQueueTLSCache();
+    bool Flush(void** tag, bool* ok);
+
+   private:
+    CompletionQueue* cq_;
+    bool flushed_;
+  };
+
+  NextStatus AsyncNextInternal(void** tag, bool* ok, gpr_timespec deadline);
+
+  /// Wraps \a grpc_completion_queue_pluck.
+  /// \warning Must not be mixed with calls to \a Next.
+  bool Pluck(::grpc::internal::CompletionQueueTag* tag) {
+    auto deadline =
+        ::grpc::g_core_codegen_interface->gpr_inf_future(GPR_CLOCK_REALTIME);
+    while (true) {
+      auto ev = ::grpc::g_core_codegen_interface->grpc_completion_queue_pluck(
+          cq_, tag, deadline, nullptr);
+      bool ok = ev.success != 0;
+      void* ignored = tag;
+      if (tag->FinalizeResult(&ignored, &ok)) {
+        GPR_CODEGEN_ASSERT(ignored == tag);
+        return ok;
+      }
+    }
+  }
+
+  /// Performs a single polling pluck on \a tag.
+  /// \warning Must not be mixed with calls to \a Next.
+  ///
+  /// TODO: sreek - This calls tag->FinalizeResult() even if the cq_ is already
+  /// shutdown. This is most likely a bug and if it is a bug, then change this
+  /// implementation to simple call the other TryPluck function with a zero
+  /// timeout. i.e:
+  ///      TryPluck(tag, gpr_time_0(GPR_CLOCK_REALTIME))
+  void TryPluck(::grpc::internal::CompletionQueueTag* tag) {
+    auto deadline =
+        ::grpc::g_core_codegen_interface->gpr_time_0(GPR_CLOCK_REALTIME);
+    auto ev = ::grpc::g_core_codegen_interface->grpc_completion_queue_pluck(
+        cq_, tag, deadline, nullptr);
+    if (ev.type == GRPC_QUEUE_TIMEOUT) return;
+    bool ok = ev.success != 0;
+    void* ignored = tag;
+    // the tag must be swallowed if using TryPluck
+    GPR_CODEGEN_ASSERT(!tag->FinalizeResult(&ignored, &ok));
+  }
+
+  /// Performs a single polling pluck on \a tag. Calls tag->FinalizeResult if
+  /// the pluck() was successful and returned the tag.
+  ///
+  /// This exects tag->FinalizeResult (if called) to return 'false' i.e expects
+  /// that the tag is internal not something that is returned to the user.
+  void TryPluck(::grpc::internal::CompletionQueueTag* tag,
+                gpr_timespec deadline) {
+    auto ev = ::grpc::g_core_codegen_interface->grpc_completion_queue_pluck(
+        cq_, tag, deadline, nullptr);
+    if (ev.type == GRPC_QUEUE_TIMEOUT || ev.type == GRPC_QUEUE_SHUTDOWN) {
+      return;
+    }
+
+    bool ok = ev.success != 0;
+    void* ignored = tag;
+    GPR_CODEGEN_ASSERT(!tag->FinalizeResult(&ignored, &ok));
+  }
+
+  /// Manage state of avalanching operations : completion queue tags that
+  /// trigger other completion queue operations. The underlying core completion
+  /// queue should not really shutdown until all avalanching operations have
+  /// been finalized. Note that we maintain the requirement that an avalanche
+  /// registration must take place before CQ shutdown (which must be maintained
+  /// elsehwere)
+  void InitialAvalanching() {
+    gpr_atm_rel_store(&avalanches_in_flight_, static_cast<gpr_atm>(1));
+  }
+  void RegisterAvalanching() {
+    gpr_atm_no_barrier_fetch_add(&avalanches_in_flight_,
+                                 static_cast<gpr_atm>(1));
+  }
+  void CompleteAvalanching() {
+    if (gpr_atm_no_barrier_fetch_add(&avalanches_in_flight_,
+                                     static_cast<gpr_atm>(-1)) == 1) {
+      ::grpc::g_core_codegen_interface->grpc_completion_queue_shutdown(cq_);
+    }
+  }
+
+  grpc_completion_queue* cq_;  // owned
+
+  gpr_atm avalanches_in_flight_;
+};
+
+/// A specific type of completion queue used by the processing of notifications
+/// by servers. Instantiated by \a ServerBuilder.
+class ServerCompletionQueue : public CompletionQueue {
+ public:
+  bool IsFrequentlyPolled() { return polling_type_ != GRPC_CQ_NON_LISTENING; }
+
+ protected:
+  /// Default constructor
+  ServerCompletionQueue() : polling_type_(GRPC_CQ_DEFAULT_POLLING) {}
+
+ private:
+  /// \param completion_type indicates whether this is a NEXT or CALLBACK
+  /// completion queue.
+  /// \param polling_type Informs the GRPC library about the type of polling
+  /// allowed on this completion queue. See grpc_cq_polling_type's description
+  /// in grpc_types.h for more details.
+  /// \param shutdown_cb is the shutdown callback used for CALLBACK api queues
+  ServerCompletionQueue(grpc_cq_completion_type completion_type,
+                        grpc_cq_polling_type polling_type,
+                        grpc_experimental_completion_queue_functor* shutdown_cb)
+      : CompletionQueue(grpc_completion_queue_attributes{
+            GRPC_CQ_CURRENT_VERSION, completion_type, polling_type,
+            shutdown_cb}),
+        polling_type_(polling_type) {}
+
+  grpc_cq_polling_type polling_type_;
+  friend class ::grpc_impl::ServerBuilder;
+  friend class ::grpc_impl::Server;
+};
+
+}  // namespace grpc_impl
+
+#endif  // GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_IMPL_H
index 5255a6d..cd0fcc0 100644 (file)
 
 #include <grpcpp/impl/codegen/channel_interface.h>
 
+namespace grpc_impl {
+class CompletionQueue;
+}
+
 namespace grpc {
 
 namespace internal {
@@ -46,7 +50,7 @@ class InterceptedChannel : public ChannelInterface {
       : channel_(channel), interceptor_pos_(pos) {}
 
   Call CreateCall(const RpcMethod& method, ClientContext* context,
-                  CompletionQueue* cq) override {
+                  ::grpc_impl::CompletionQueue* cq) override {
     return channel_->CreateCallInternal(method, context, cq, interceptor_pos_);
   }
 
@@ -58,7 +62,8 @@ class InterceptedChannel : public ChannelInterface {
   }
 
   void NotifyOnStateChangeImpl(grpc_connectivity_state last_observed,
-                               gpr_timespec deadline, CompletionQueue* cq,
+                               gpr_timespec deadline,
+                               ::grpc_impl::CompletionQueue* cq,
                                void* tag) override {
     return channel_->NotifyOnStateChangeImpl(last_observed, deadline, cq, tag);
   }
@@ -67,7 +72,9 @@ class InterceptedChannel : public ChannelInterface {
     return channel_->WaitForStateChangeImpl(last_observed, deadline);
   }
 
-  CompletionQueue* CallbackCQ() override { return channel_->CallbackCQ(); }
+  ::grpc_impl::CompletionQueue* CallbackCQ() override {
+    return channel_->CallbackCQ();
+  }
 
   ChannelInterface* channel_;
   size_t interceptor_pos_;
diff --git a/include/grpcpp/impl/codegen/message_allocator.h b/include/grpcpp/impl/codegen/message_allocator.h
new file mode 100644 (file)
index 0000000..107bec6
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *
+ * Copyright 2019 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_MESSAGE_ALLOCATOR_H
+#define GRPCPP_IMPL_CODEGEN_MESSAGE_ALLOCATOR_H
+
+namespace grpc {
+namespace experimental {
+
+// This is per rpc struct for the allocator. We can potentially put the grpc
+// call arena in here in the future.
+template <typename RequestT, typename ResponseT>
+struct RpcAllocatorInfo {
+  RequestT* request;
+  ResponseT* response;
+  // per rpc allocator internal state. MessageAllocator can set it when
+  // AllocateMessages is called and use it later.
+  void* allocator_state;
+};
+
+// Implementations need to be thread-safe
+template <typename RequestT, typename ResponseT>
+class MessageAllocator {
+ public:
+  virtual ~MessageAllocator() = default;
+  // Allocate both request and response
+  virtual void AllocateMessages(
+      RpcAllocatorInfo<RequestT, ResponseT>* info) = 0;
+  // Optional: deallocate request early, called by
+  // ServerCallbackRpcController::ReleaseRequest
+  virtual void DeallocateRequest(RpcAllocatorInfo<RequestT, ResponseT>* info) {}
+  // Deallocate response and request (if applicable)
+  virtual void DeallocateMessages(
+      RpcAllocatorInfo<RequestT, ResponseT>* info) = 0;
+};
+
+}  // namespace experimental
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_MESSAGE_ALLOCATOR_H
index 0942862..dee1cb5 100644 (file)
@@ -86,8 +86,8 @@ class RpcMethodHandler : public MethodHandler {
     param.call->cq()->Pluck(&ops);
   }
 
-  void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
-                    Status* status) final {
+  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(
@@ -191,8 +191,8 @@ class ServerStreamingHandler : public MethodHandler {
     param.call->cq()->Pluck(&ops);
   }
 
-  void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
-                    Status* status) final {
+  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(
@@ -327,8 +327,8 @@ class ErrorMethodHandler : public MethodHandler {
     param.call->cq()->Pluck(&ops);
   }
 
-  void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
-                    Status* status) final {
+  void* Deserialize(grpc_call* call, grpc_byte_buffer* req, Status* status,
+                    void** handler_data) final {
     // We have to destroy any request payload
     if (req != nullptr) {
       g_core_codegen_interface->grpc_byte_buffer_destroy(req);
index 56df61c..21fb2ac 100644 (file)
@@ -46,21 +46,25 @@ class MethodHandler {
     /// \param context : the ServerContext structure for this server call
     /// \param req : the request payload, if appropriate for this RPC
     /// \param req_status : the request status after any interceptors have run
+    /// \param handler_data: internal data for the handler.
     /// \param requester : used only by the callback API. It is a function
     ///        called by the RPC Controller to request another RPC (and also
     ///        to set up the state required to make that request possible)
     HandlerParameter(Call* c, ServerContext* context, void* req,
-                     Status req_status, std::function<void()> requester)
+                     Status req_status, void* handler_data,
+                     std::function<void()> requester)
         : call(c),
           server_context(context),
           request(req),
           status(req_status),
+          internal_data(handler_data),
           call_requester(std::move(requester)) {}
     ~HandlerParameter() {}
     Call* call;
     ServerContext* server_context;
     void* request;
     Status status;
+    void* internal_data;
     std::function<void()> call_requester;
   };
   virtual void RunHandler(const HandlerParameter& param) = 0;
@@ -71,7 +75,7 @@ class MethodHandler {
      pointer after calling RunHandler. Ownership of the deserialized request is
      retained by the handler. Returns nullptr if deserialization failed. */
   virtual void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
-                            Status* status) {
+                            Status* status, void** handler_data) {
     GPR_CODEGEN_ASSERT(req == nullptr);
     return nullptr;
   }
index 87edea8..3f6d5cd 100644 (file)
@@ -28,6 +28,7 @@
 #include <grpcpp/impl/codegen/callback_common.h>
 #include <grpcpp/impl/codegen/config.h>
 #include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/message_allocator.h>
 #include <grpcpp/impl/codegen/server_context.h>
 #include <grpcpp/impl/codegen/server_interface.h>
 #include <grpcpp/impl/codegen/status.h>
@@ -37,11 +38,43 @@ namespace grpc {
 // Declare base class of all reactors as internal
 namespace internal {
 
+// Forward declarations
+template <class Request, class Response>
+class CallbackClientStreamingHandler;
+template <class Request, class Response>
+class CallbackServerStreamingHandler;
+template <class Request, class Response>
+class CallbackBidiHandler;
+
 class ServerReactor {
  public:
   virtual ~ServerReactor() = default;
   virtual void OnDone() = 0;
   virtual void OnCancel() = 0;
+
+ private:
+  friend class ::grpc::ServerContext;
+  template <class Request, class Response>
+  friend class CallbackClientStreamingHandler;
+  template <class Request, class Response>
+  friend class CallbackServerStreamingHandler;
+  template <class Request, class Response>
+  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 (on_cancel_conditions_remaining_.fetch_sub(
+            1, std::memory_order_acq_rel) == 1) {
+      OnCancel();
+    }
+  }
+
+  std::atomic_int on_cancel_conditions_remaining_{2};
 };
 
 }  // namespace internal
@@ -103,6 +136,14 @@ class ServerCallbackRpcController {
   /// to be called before the callback completes.
   virtual void SetCancelCallback(std::function<void()> callback) = 0;
   virtual void ClearCancelCallback() = 0;
+
+  // NOTE: This is an API for advanced users who need custom allocators.
+  // Optionally deallocate request early to reduce the size of working set.
+  // A custom MessageAllocator needs to be registered to make use of this.
+  virtual void FreeRequest() = 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 void* GetAllocatorState() = 0;
 };
 
 // NOTE: The actual streaming object classes are provided
@@ -253,7 +294,9 @@ class ServerBidiReactor : public internal::ServerReactor {
   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 initation method.
+  /// 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(ServerContext* context) {}
@@ -330,7 +373,7 @@ class ServerReadReactor : public internal::ServerReactor {
   ServerCallbackReader<Request>* reader_;
 };
 
-/// \a ServerReadReactor is the interface for a server-streaming RPC.
+/// \a ServerWriteReactor is the interface for a server-streaming RPC.
 template <class Request, class Response>
 class ServerWriteReactor : public internal::ServerReactor {
  public:
@@ -413,17 +456,24 @@ class CallbackUnaryHandler : public MethodHandler {
                          experimental::ServerCallbackRpcController*)>
           func)
       : func_(func) {}
+
+  void SetMessageAllocator(
+      experimental::MessageAllocator<RequestType, ResponseType>* 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_info =
+        static_cast<experimental::RpcAllocatorInfo<RequestType, ResponseType>*>(
+            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,
-            static_cast<RequestType*>(param.request),
-            std::move(param.call_requester));
+        ServerCallbackRpcControllerImpl(param.server_context, param.call,
+                                        allocator_info, allocator_,
+                                        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(),
@@ -434,18 +484,41 @@ class CallbackUnaryHandler : public MethodHandler {
     }
   }
 
-  void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
-                    Status* status) final {
+  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();
+    RequestType* request = nullptr;
+    experimental::RpcAllocatorInfo<RequestType, ResponseType>* allocator_info =
+        new (g_core_codegen_interface->grpc_call_arena_alloc(
+            call, sizeof(*allocator_info)))
+            experimental::RpcAllocatorInfo<RequestType, ResponseType>();
+    if (allocator_ != nullptr) {
+      allocator_->AllocateMessages(allocator_info);
+    } else {
+      allocator_info->request =
+          new (g_core_codegen_interface->grpc_call_arena_alloc(
+              call, sizeof(RequestType))) RequestType();
+      allocator_info->response =
+          new (g_core_codegen_interface->grpc_call_arena_alloc(
+              call, sizeof(ResponseType))) ResponseType();
+    }
+    *handler_data = allocator_info;
+    request = allocator_info->request;
     *status = SerializationTraits<RequestType>::Deserialize(&buf, request);
     buf.Release();
     if (status->ok()) {
       return request;
     }
-    request->~RequestType();
+    // Clean up on deserialization failure.
+    if (allocator_ != nullptr) {
+      allocator_->DeallocateMessages(allocator_info);
+    } else {
+      allocator_info->request->~RequestType();
+      allocator_info->response->~ResponseType();
+      allocator_info->request = nullptr;
+      allocator_info->response = nullptr;
+    }
     return nullptr;
   }
 
@@ -453,6 +526,8 @@ class CallbackUnaryHandler : public MethodHandler {
   std::function<void(ServerContext*, const RequestType*, ResponseType*,
                      experimental::ServerCallbackRpcController*)>
       func_;
+  experimental::MessageAllocator<RequestType, ResponseType>* allocator_ =
+      nullptr;
 
   // The implementation class of ServerCallbackRpcController is a private member
   // of CallbackUnaryHandler since it is never exposed anywhere, and this allows
@@ -473,8 +548,9 @@ class CallbackUnaryHandler : public MethodHandler {
       }
       // The response is dropped if the status is not OK.
       if (s.ok()) {
-        finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_,
-                                     finish_ops_.SendMessagePtr(&resp_));
+        finish_ops_.ServerSendStatus(
+            &ctx_->trailing_metadata_,
+            finish_ops_.SendMessagePtr(allocator_info_->response));
       } else {
         finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s);
       }
@@ -512,28 +588,50 @@ class CallbackUnaryHandler : public MethodHandler {
 
     void ClearCancelCallback() override { ctx_->ClearCancelCallback(); }
 
+    void FreeRequest() override {
+      if (allocator_ != nullptr) {
+        allocator_->DeallocateRequest(allocator_info_);
+      }
+    }
+
+    void* GetAllocatorState() override {
+      return allocator_info_->allocator_state;
+    }
+
    private:
     friend class CallbackUnaryHandler<RequestType, ResponseType>;
 
-    ServerCallbackRpcControllerImpl(ServerContext* ctx, Call* call,
-                                    const RequestType* req,
-                                    std::function<void()> call_requester)
+    ServerCallbackRpcControllerImpl(
+        ServerContext* ctx, Call* call,
+        experimental::RpcAllocatorInfo<RequestType, ResponseType>*
+            allocator_info,
+        experimental::MessageAllocator<RequestType, ResponseType>* allocator,
+        std::function<void()> call_requester)
         : ctx_(ctx),
           call_(*call),
-          req_(req),
+          allocator_info_(allocator_info),
+          allocator_(allocator),
           call_requester_(std::move(call_requester)) {
       ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, nullptr);
     }
 
-    ~ServerCallbackRpcControllerImpl() { req_->~RequestType(); }
-
-    const RequestType* request() { return req_; }
-    ResponseType* response() { return &resp_; }
+    const RequestType* request() { return allocator_info_->request; }
+    ResponseType* response() { return allocator_info_->response; }
 
     void MaybeDone() {
       if (--callbacks_outstanding_ == 0) {
         grpc_call* call = call_.call();
         auto call_requester = std::move(call_requester_);
+        if (allocator_ != nullptr) {
+          allocator_->DeallocateMessages(allocator_info_);
+        } else {
+          if (allocator_info_->request != nullptr) {
+            allocator_info_->request->~RequestType();
+          }
+          if (allocator_info_->response != nullptr) {
+            allocator_info_->response->~ResponseType();
+          }
+        }
         this->~ServerCallbackRpcControllerImpl();  // explicitly call destructor
         g_core_codegen_interface->grpc_call_unref(call);
         call_requester();
@@ -549,8 +647,8 @@ class CallbackUnaryHandler : public MethodHandler {
 
     ServerContext* ctx_;
     Call call_;
-    const RequestType* req_;
-    ResponseType resp_;
+    experimental::RpcAllocatorInfo<RequestType, ResponseType>* allocator_info_;
+    experimental::MessageAllocator<RequestType, ResponseType>* allocator_;
     std::function<void()> call_requester_;
     std::atomic_int callbacks_outstanding_{
         2};  // reserve for Finish and CompletionOp
@@ -588,6 +686,8 @@ class CallbackClientStreamingHandler : public MethodHandler {
 
     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();
   }
 
@@ -730,11 +830,13 @@ class CallbackServerStreamingHandler : public MethodHandler {
                                  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) final {
+  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(
@@ -906,6 +1008,8 @@ class CallbackBidiHandler : public MethodHandler {
 
     stream->BindReactor(reactor);
     reactor->OnStarted(param.server_context);
+    // The earliest that OnCancel can be called is after OnStarted is done.
+    reactor->MaybeCallOnCancel();
     stream->MaybeDone();
   }
 
index 591a9ff..c690374 100644 (file)
@@ -41,11 +41,14 @@ struct grpc_metadata;
 struct grpc_call;
 struct census_context;
 
+namespace grpc_impl {
+
+class CompletionQueue;
+class Server;
+}  // namespace grpc_impl
 namespace grpc {
 class ClientContext;
 class GenericServerContext;
-class CompletionQueue;
-class Server;
 class ServerInterface;
 template <class W, class R>
 class ServerAsyncReader;
@@ -87,6 +90,7 @@ class Call;
 class ServerReactor;
 }  // namespace internal
 
+class ServerInterface;
 namespace testing {
 class InteropServerContextInspector;
 class ServerContextTestSpouse;
@@ -269,7 +273,7 @@ class ServerContext {
   friend class ::grpc::testing::InteropServerContextInspector;
   friend class ::grpc::testing::ServerContextTestSpouse;
   friend class ::grpc::ServerInterface;
-  friend class ::grpc::Server;
+  friend class ::grpc_impl::Server;
   template <class W, class R>
   friend class ::grpc::ServerAsyncReader;
   template <class W>
@@ -351,7 +355,7 @@ class ServerContext {
 
   gpr_timespec deadline_;
   grpc_call* call_;
-  CompletionQueue* cq_;
+  ::grpc_impl::CompletionQueue* cq_;
   bool sent_initial_metadata_;
   mutable std::shared_ptr<const AuthContext> auth_context_;
   mutable internal::MetadataMap client_metadata_;
index f599e03..10dc6ab 100644 (file)
 #include <grpcpp/impl/codegen/rpc_service_method.h>
 #include <grpcpp/impl/codegen/server_context.h>
 
+namespace grpc_impl {
+
+class CompletionQueue;
+class ServerCompletionQueue;
+class Channel;
+class ServerCredentials;
+}  // namespace grpc_impl
 namespace grpc {
 
 class AsyncGenericService;
-class Channel;
 class GenericServerContext;
-class ServerCompletionQueue;
 class ServerContext;
-class ServerCredentials;
 class Service;
 
 extern CoreCodegenInterface* g_core_codegen_interface;
@@ -146,11 +150,11 @@ class ServerInterface : public internal::CallHook {
   /// 192.168.1.1:31416, [::1]:27182, etc.).
   /// \params creds The credentials associated with the server.
   ///
-  /// \return bound port number on sucess, 0 on failure.
+  /// \return bound port number on success, 0 on failure.
   ///
   /// \warning It's an error to call this method on an already started server.
   virtual int AddListeningPort(const grpc::string& addr,
-                               ServerCredentials* creds) = 0;
+                               grpc_impl::ServerCredentials* creds) = 0;
 
   /// Start the server.
   ///
@@ -158,7 +162,8 @@ class ServerInterface : public internal::CallHook {
   /// caller is required to keep all completion queues live until the server is
   /// destroyed.
   /// \param num_cqs How many completion queues does \a cqs hold.
-  virtual void Start(ServerCompletionQueue** cqs, size_t num_cqs) = 0;
+  virtual void Start(::grpc_impl::ServerCompletionQueue** cqs,
+                     size_t num_cqs) = 0;
 
   virtual void ShutdownInternal(gpr_timespec deadline) = 0;
 
@@ -173,9 +178,9 @@ class ServerInterface : public internal::CallHook {
    public:
     BaseAsyncRequest(ServerInterface* server, ServerContext* context,
                      internal::ServerAsyncStreamingInterface* stream,
-                     CompletionQueue* call_cq,
-                     ServerCompletionQueue* notification_cq, void* tag,
-                     bool delete_on_finalize);
+                     ::grpc_impl::CompletionQueue* call_cq,
+                     ::grpc_impl::ServerCompletionQueue* notification_cq,
+                     void* tag, bool delete_on_finalize);
     virtual ~BaseAsyncRequest();
 
     bool FinalizeResult(void** tag, bool* status) override;
@@ -187,8 +192,8 @@ class ServerInterface : public internal::CallHook {
     ServerInterface* const server_;
     ServerContext* const context_;
     internal::ServerAsyncStreamingInterface* const stream_;
-    CompletionQueue* const call_cq_;
-    ServerCompletionQueue* const notification_cq_;
+    ::grpc_impl::CompletionQueue* const call_cq_;
+    ::grpc_impl::ServerCompletionQueue* const notification_cq_;
     void* const tag_;
     const bool delete_on_finalize_;
     grpc_call* call_;
@@ -202,16 +207,17 @@ class ServerInterface : public internal::CallHook {
    public:
     RegisteredAsyncRequest(ServerInterface* server, ServerContext* context,
                            internal::ServerAsyncStreamingInterface* stream,
-                           CompletionQueue* call_cq,
-                           ServerCompletionQueue* notification_cq, void* tag,
-                           const char* name, internal::RpcMethod::RpcType type);
+                           ::grpc_impl::CompletionQueue* call_cq,
+                           ::grpc_impl::ServerCompletionQueue* notification_cq,
+                           void* tag, const char* name,
+                           internal::RpcMethod::RpcType type);
 
     virtual bool FinalizeResult(void** tag, bool* status) override {
       /* If we are done intercepting, then there is nothing more for us to do */
       if (done_intercepting_) {
         return BaseAsyncRequest::FinalizeResult(tag, status);
       }
-      call_wrapper_ = internal::Call(
+      call_wrapper_ = ::grpc::internal::Call(
           call_, server_, call_cq_, server_->max_receive_message_size(),
           context_->set_server_rpc_info(name_, type_,
                                         *server_->interceptor_creators()));
@@ -220,7 +226,7 @@ class ServerInterface : public internal::CallHook {
 
    protected:
     void IssueRequest(void* registered_method, grpc_byte_buffer** payload,
-                      ServerCompletionQueue* notification_cq);
+                      ::grpc_impl::ServerCompletionQueue* notification_cq);
     const char* name_;
     const internal::RpcMethod::RpcType type_;
   };
@@ -230,8 +236,9 @@ class ServerInterface : public internal::CallHook {
     NoPayloadAsyncRequest(internal::RpcServiceMethod* registered_method,
                           ServerInterface* server, ServerContext* context,
                           internal::ServerAsyncStreamingInterface* stream,
-                          CompletionQueue* call_cq,
-                          ServerCompletionQueue* notification_cq, void* tag)
+                          ::grpc_impl::CompletionQueue* call_cq,
+                          ::grpc_impl::ServerCompletionQueue* notification_cq,
+                          void* tag)
         : RegisteredAsyncRequest(
               server, context, stream, call_cq, notification_cq, tag,
               registered_method->name(), registered_method->method_type()) {
@@ -247,9 +254,9 @@ class ServerInterface : public internal::CallHook {
     PayloadAsyncRequest(internal::RpcServiceMethod* registered_method,
                         ServerInterface* server, ServerContext* context,
                         internal::ServerAsyncStreamingInterface* stream,
-                        CompletionQueue* call_cq,
-                        ServerCompletionQueue* notification_cq, void* tag,
-                        Message* request)
+                        ::grpc_impl::CompletionQueue* call_cq,
+                        ::grpc_impl::ServerCompletionQueue* notification_cq,
+                        void* tag, Message* request)
         : RegisteredAsyncRequest(
               server, context, stream, call_cq, notification_cq, tag,
               registered_method->name(), registered_method->method_type()),
@@ -304,9 +311,9 @@ class ServerInterface : public internal::CallHook {
     ServerInterface* const server_;
     ServerContext* const context_;
     internal::ServerAsyncStreamingInterface* const stream_;
-    CompletionQueue* const call_cq_;
+    ::grpc_impl::CompletionQueue* const call_cq_;
 
-    ServerCompletionQueue* const notification_cq_;
+    ::grpc_impl::ServerCompletionQueue* const notification_cq_;
     void* const tag_;
     Message* const request_;
     ByteBuffer payload_;
@@ -316,9 +323,9 @@ class ServerInterface : public internal::CallHook {
    public:
     GenericAsyncRequest(ServerInterface* server, GenericServerContext* context,
                         internal::ServerAsyncStreamingInterface* stream,
-                        CompletionQueue* call_cq,
-                        ServerCompletionQueue* notification_cq, void* tag,
-                        bool delete_on_finalize);
+                        ::grpc_impl::CompletionQueue* call_cq,
+                        ::grpc_impl::ServerCompletionQueue* notification_cq,
+                        void* tag, bool delete_on_finalize);
 
     bool FinalizeResult(void** tag, bool* status) override;
 
@@ -330,9 +337,9 @@ class ServerInterface : public internal::CallHook {
   void RequestAsyncCall(internal::RpcServiceMethod* method,
                         ServerContext* context,
                         internal::ServerAsyncStreamingInterface* stream,
-                        CompletionQueue* call_cq,
-                        ServerCompletionQueue* notification_cq, void* tag,
-                        Message* message) {
+                        ::grpc_impl::CompletionQueue* call_cq,
+                        ::grpc_impl::ServerCompletionQueue* notification_cq,
+                        void* tag, Message* message) {
     GPR_CODEGEN_ASSERT(method);
     new PayloadAsyncRequest<Message>(method, this, context, stream, call_cq,
                                      notification_cq, tag, message);
@@ -341,18 +348,19 @@ class ServerInterface : public internal::CallHook {
   void RequestAsyncCall(internal::RpcServiceMethod* method,
                         ServerContext* context,
                         internal::ServerAsyncStreamingInterface* stream,
-                        CompletionQueue* call_cq,
-                        ServerCompletionQueue* notification_cq, void* tag) {
+                        ::grpc_impl::CompletionQueue* call_cq,
+                        ::grpc_impl::ServerCompletionQueue* notification_cq,
+                        void* tag) {
     GPR_CODEGEN_ASSERT(method);
     new NoPayloadAsyncRequest(method, this, context, stream, call_cq,
                               notification_cq, tag);
   }
 
-  void RequestAsyncGenericCall(GenericServerContext* context,
-                               internal::ServerAsyncStreamingInterface* stream,
-                               CompletionQueue* call_cq,
-                               ServerCompletionQueue* notification_cq,
-                               void* tag) {
+  void RequestAsyncGenericCall(
+      GenericServerContext* context,
+      internal::ServerAsyncStreamingInterface* stream,
+      ::grpc_impl::CompletionQueue* call_cq,
+      ::grpc_impl::ServerCompletionQueue* notification_cq, void* tag) {
     new GenericAsyncRequest(this, context, stream, call_cq, notification_cq,
                             tag, true);
   }
@@ -377,7 +385,7 @@ class ServerInterface : public internal::CallHook {
   // Returns nullptr (rather than being pure) since this is a post-1.0 method
   // and adding a new pure method to an interface would be a breaking change
   // (even though this is private and non-API)
-  virtual CompletionQueue* CallbackCQ() { return nullptr; }
+  virtual ::grpc_impl::CompletionQueue* CallbackCQ() { return nullptr; }
 };
 
 }  // namespace grpc
index 332a04c..f1d1272 100644 (file)
 #include <grpcpp/impl/codegen/server_interface.h>
 #include <grpcpp/impl/codegen/status.h>
 
-namespace grpc {
+namespace grpc_impl {
 
-class CompletionQueue;
 class Server;
+class CompletionQueue;
+}  // namespace grpc_impl
+namespace grpc {
+
 class ServerInterface;
-class ServerCompletionQueue;
 class ServerContext;
 
 namespace internal {
@@ -132,6 +134,11 @@ class Service {
           internal::RpcServiceMethod::ApiType::RAW_CALL_BACK);
     }
 
+    internal::MethodHandler* GetHandler(int index) {
+      size_t idx = static_cast<size_t>(index);
+      return service_->methods_[idx]->handler();
+    }
+
    private:
     Service* service_;
   };
@@ -228,7 +235,7 @@ class Service {
   }
 
  private:
-  friend class Server;
+  friend class grpc_impl::Server;
   friend class ServerInterface;
   ServerInterface* server_;
   std::vector<std::unique_ptr<internal::RpcServiceMethod>> methods_;
index 09943f1..bdd7ead 100644 (file)
@@ -119,7 +119,8 @@ enum StatusCode {
   INTERNAL = 13,
 
   /// The service is currently unavailable. This is a most likely a transient
-  /// condition and may be corrected by retrying with a backoff.
+  /// condition and may be corrected by retrying with a backoff. Note that it is
+  /// not always safe to retry non-idempotent operations.
   ///
   /// \warning Although data MIGHT not have been transmitted when this
   /// status occurs, there is NOT A GUARANTEE that the server has not seen
diff --git a/include/grpcpp/impl/codegen/sync.h b/include/grpcpp/impl/codegen/sync.h
new file mode 100644 (file)
index 0000000..146f182
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ *
+ * 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_H
+#define GRPCPP_IMPL_CODEGEN_SYNC_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#ifdef GPR_HAS_PTHREAD_H
+#include <pthread.h>
+#endif
+
+#include <mutex>
+
+#include <grpc/impl/codegen/log.h>
+#include <grpc/impl/codegen/sync.h>
+
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+
+// The core library is not accessible in C++ codegen headers, and vice versa.
+// Thus, we need to have duplicate headers with similar functionality.
+// Make sure any change to this file is also reflected in
+// src/core/lib/gprpp/sync.h too.
+//
+// Whenever possible, prefer "src/core/lib/gprpp/sync.h" over this file,
+// since in core we do not rely on g_core_codegen_interface and hence do not
+// pay the costs of virtual function calls.
+
+namespace grpc {
+namespace internal {
+
+class Mutex {
+ public:
+  Mutex() { g_core_codegen_interface->gpr_mu_init(&mu_); }
+  ~Mutex() { g_core_codegen_interface->gpr_mu_destroy(&mu_); }
+
+  Mutex(const Mutex&) = delete;
+  Mutex& operator=(const Mutex&) = delete;
+
+  gpr_mu* get() { return &mu_; }
+  const gpr_mu* get() const { return &mu_; }
+
+ private:
+  union {
+    gpr_mu mu_;
+    std::mutex do_not_use_sth_;
+#ifdef GPR_HAS_PTHREAD_H
+    pthread_mutex_t do_not_use_pth_;
+#endif
+  };
+};
+
+// MutexLock is a std::
+class MutexLock {
+ public:
+  explicit MutexLock(Mutex* mu) : mu_(mu->get()) {
+    g_core_codegen_interface->gpr_mu_lock(mu_);
+  }
+  explicit MutexLock(gpr_mu* mu) : mu_(mu) {
+    g_core_codegen_interface->gpr_mu_lock(mu_);
+  }
+  ~MutexLock() { g_core_codegen_interface->gpr_mu_unlock(mu_); }
+
+  MutexLock(const MutexLock&) = delete;
+  MutexLock& operator=(const MutexLock&) = delete;
+
+ private:
+  gpr_mu* const mu_;
+};
+
+class ReleasableMutexLock {
+ public:
+  explicit ReleasableMutexLock(Mutex* mu) : mu_(mu->get()) {
+    g_core_codegen_interface->gpr_mu_lock(mu_);
+  }
+  explicit ReleasableMutexLock(gpr_mu* mu) : mu_(mu) {
+    g_core_codegen_interface->gpr_mu_lock(mu_);
+  }
+  ~ReleasableMutexLock() {
+    if (!released_) g_core_codegen_interface->gpr_mu_unlock(mu_);
+  }
+
+  ReleasableMutexLock(const ReleasableMutexLock&) = delete;
+  ReleasableMutexLock& operator=(const ReleasableMutexLock&) = delete;
+
+  void Lock() {
+    GPR_DEBUG_ASSERT(released_);
+    g_core_codegen_interface->gpr_mu_lock(mu_);
+    released_ = false;
+  }
+
+  void Unlock() {
+    GPR_DEBUG_ASSERT(!released_);
+    released_ = true;
+    g_core_codegen_interface->gpr_mu_unlock(mu_);
+  }
+
+ private:
+  gpr_mu* const mu_;
+  bool released_ = false;
+};
+
+class CondVar {
+ public:
+  CondVar() { g_core_codegen_interface->gpr_cv_init(&cv_); }
+  ~CondVar() { g_core_codegen_interface->gpr_cv_destroy(&cv_); }
+
+  CondVar(const CondVar&) = delete;
+  CondVar& operator=(const CondVar&) = delete;
+
+  void Signal() { g_core_codegen_interface->gpr_cv_signal(&cv_); }
+  void Broadcast() { g_core_codegen_interface->gpr_cv_broadcast(&cv_); }
+
+  int Wait(Mutex* mu) {
+    return Wait(mu,
+                g_core_codegen_interface->gpr_inf_future(GPR_CLOCK_REALTIME));
+  }
+  int Wait(Mutex* mu, const gpr_timespec& deadline) {
+    return g_core_codegen_interface->gpr_cv_wait(&cv_, mu->get(), deadline);
+  }
+
+  template <typename Predicate>
+  void WaitUntil(Mutex* mu, Predicate pred) {
+    while (!pred()) {
+      Wait(mu, g_core_codegen_interface->gpr_inf_future(GPR_CLOCK_REALTIME));
+    }
+  }
+
+ private:
+  gpr_cv cv_;
+};
+
+}  // namespace internal
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_SYNC_H
index d9edad4..0d3fdfc 100644 (file)
@@ -180,7 +180,7 @@ class ClientReader final : public ClientReaderInterface<R> {
   ///
   //  Side effect:
   ///   Once complete, the initial metadata read from
-  ///   the server will be accessable through the \a ClientContext used to
+  ///   the server will be accessible through the \a ClientContext used to
   ///   construct this object.
   void WaitForInitialMetadata() override {
     GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
@@ -298,7 +298,7 @@ class ClientWriter : public ClientWriterInterface<W> {
   ///
   //  Side effect:
   ///   Once complete, the initial metadata read from the server will be
-  ///   accessable through the \a ClientContext used to construct this object.
+  ///   accessible through the \a ClientContext used to construct this object.
   void WaitForInitialMetadata() {
     GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
 
@@ -449,7 +449,7 @@ class ClientReaderWriter final : public ClientReaderWriterInterface<W, R> {
   /// with or after the \a Finish method.
   ///
   /// Once complete, the initial metadata read from the server will be
-  /// accessable through the \a ClientContext used to construct this object.
+  /// accessible through the \a ClientContext used to construct this object.
   void WaitForInitialMetadata() override {
     GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
 
index 39450b4..84a88f2 100644 (file)
 
 #include <grpcpp/support/config.h>
 
-namespace grpc {
+namespace grpc_impl {
 
+class ChannelArguments;
 class ServerBuilder;
 class ServerInitializer;
-class ChannelArguments;
+}  // namespace grpc_impl
+namespace grpc {
 
 /// This interface is meant for internal usage only. Implementations of this
 /// interface should add themselves to a \a ServerBuilder instance through the
@@ -40,14 +42,14 @@ class ServerBuilderPlugin {
   /// UpdateServerBuilder will be called at an early stage in
   /// ServerBuilder::BuildAndStart(), right after the ServerBuilderOptions have
   /// done their updates.
-  virtual void UpdateServerBuilder(ServerBuilder* builder) {}
+  virtual void UpdateServerBuilder(grpc_impl::ServerBuilder* builder) {}
 
   /// InitServer will be called in ServerBuilder::BuildAndStart(), after the
   /// Server instance is created.
-  virtual void InitServer(ServerInitializer* si) = 0;
+  virtual void InitServer(grpc_impl::ServerInitializer* si) = 0;
 
   /// Finish will be called at the end of ServerBuilder::BuildAndStart().
-  virtual void Finish(ServerInitializer* si) = 0;
+  virtual void Finish(grpc_impl::ServerInitializer* si) = 0;
 
   /// ChangeArguments is an interface that can be used in
   /// ServerBuilderOption::UpdatePlugins
@@ -55,7 +57,7 @@ class ServerBuilderPlugin {
 
   /// UpdateChannelArguments will be called in ServerBuilder::BuildAndStart(),
   /// before the Server instance is created.
-  virtual void UpdateChannelArguments(ChannelArguments* args) {}
+  virtual void UpdateChannelArguments(grpc_impl::ChannelArguments* args) {}
 
   virtual bool has_sync_methods() const { return false; }
   virtual bool has_async_methods() const { return false; }
index f949fab..d40bb98 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * 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.
 #ifndef GRPCPP_IMPL_SERVER_INITIALIZER_H
 #define GRPCPP_IMPL_SERVER_INITIALIZER_H
 
-#include <memory>
-#include <vector>
-
-#include <grpcpp/server.h>
+#include <grpcpp/impl/server_initializer_impl.h>
 
 namespace grpc {
 
-class Server;
-class Service;
-
-class ServerInitializer {
- public:
-  ServerInitializer(Server* server) : server_(server) {}
-
-  bool RegisterService(std::shared_ptr<Service> service) {
-    if (!server_->RegisterService(nullptr, service.get())) {
-      return false;
-    }
-    default_services_.push_back(service);
-    return true;
-  }
-
-  const std::vector<grpc::string>* GetServiceList() {
-    return &server_->services_;
-  }
-
- private:
-  Server* server_;
-  std::vector<std::shared_ptr<Service> > default_services_;
-};
+typedef ::grpc_impl::ServerInitializer ServerInitializer;
 
 }  // namespace grpc
 
diff --git a/include/grpcpp/impl/server_initializer_impl.h b/include/grpcpp/impl/server_initializer_impl.h
new file mode 100644 (file)
index 0000000..0e2c65f
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ *
+ * Copyright 2016 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_SERVER_INITIALIZER_IMPL_H
+#define GRPCPP_IMPL_SERVER_INITIALIZER_IMPL_H
+
+#include <memory>
+#include <vector>
+
+#include <grpcpp/server.h>
+
+namespace grpc {
+
+class Service;
+}  // namespace grpc
+namespace grpc_impl {
+class Server;
+
+class ServerInitializer {
+ public:
+  ServerInitializer(grpc::Server* server) : server_(server) {}
+
+  bool RegisterService(std::shared_ptr<grpc::Service> service) {
+    if (!server_->RegisterService(nullptr, service.get())) {
+      return false;
+    }
+    default_services_.push_back(service);
+    return true;
+  }
+
+  const std::vector<grpc::string>* GetServiceList() {
+    return &server_->services_;
+  }
+
+ private:
+  grpc::Server* server_;
+  std::vector<std::shared_ptr<grpc::Service> > default_services_;
+};
+
+}  // namespace grpc_impl
+
+#endif  // GRPCPP_IMPL_SERVER_INITIALIZER_IMPL_H
index 50bd1cb..333767b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * 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.
 #ifndef GRPCPP_RESOURCE_QUOTA_H
 #define GRPCPP_RESOURCE_QUOTA_H
 
-struct grpc_resource_quota;
-
-#include <grpcpp/impl/codegen/config.h>
-#include <grpcpp/impl/codegen/grpc_library.h>
+#include <grpcpp/resource_quota_impl.h>
 
 namespace grpc {
 
-/// ResourceQuota represents a bound on memory and thread usage by the gRPC
-/// library. A ResourceQuota can be attached to a server (via \a ServerBuilder),
-/// or a client channel (via \a ChannelArguments).
-/// gRPC will attempt to keep memory and threads used by all attached entities
-/// below the ResourceQuota bound.
-class ResourceQuota final : private GrpcLibraryCodegen {
- public:
-  /// \param name - a unique name for this ResourceQuota.
-  explicit ResourceQuota(const grpc::string& name);
-  ResourceQuota();
-  ~ResourceQuota();
-
-  /// Resize this \a ResourceQuota to a new size. If \a new_size is smaller
-  /// than the current size of the pool, memory usage will be monotonically
-  /// decreased until it falls under \a new_size.
-  /// No time bound is given for this to occur however.
-  ResourceQuota& Resize(size_t new_size);
-
-  /// Set the max number of threads that can be allocated from this
-  /// ResourceQuota object.
-  ///
-  /// If the new_max_threads value is smaller than the current value, no new
-  /// threads are allocated until the number of active threads fall below
-  /// new_max_threads. There is no time bound on when this may happen i.e none
-  /// of the current threads are forcefully destroyed and all threads run their
-  /// normal course.
-  ResourceQuota& SetMaxThreads(int new_max_threads);
-
-  grpc_resource_quota* c_resource_quota() const { return impl_; }
-
- private:
-  ResourceQuota(const ResourceQuota& rhs);
-  ResourceQuota& operator=(const ResourceQuota& rhs);
-
-  grpc_resource_quota* const impl_;
-};
-
+typedef ::grpc_impl::ResourceQuota ResourceQuota;
 }  // namespace grpc
 
 #endif  // GRPCPP_RESOURCE_QUOTA_H
diff --git a/include/grpcpp/resource_quota_impl.h b/include/grpcpp/resource_quota_impl.h
new file mode 100644 (file)
index 0000000..16c0e35
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright 2016 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_RESOURCE_QUOTA_IMPL_H
+#define GRPCPP_RESOURCE_QUOTA_IMPL_H
+
+struct grpc_resource_quota;
+
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+
+namespace grpc_impl {
+
+/// ResourceQuota represents a bound on memory and thread usage by the gRPC
+/// library. A ResourceQuota can be attached to a server (via \a ServerBuilder),
+/// or a client channel (via \a ChannelArguments).
+/// gRPC will attempt to keep memory and threads used by all attached entities
+/// below the ResourceQuota bound.
+class ResourceQuota final : private ::grpc::GrpcLibraryCodegen {
+ public:
+  /// \param name - a unique name for this ResourceQuota.
+  explicit ResourceQuota(const grpc::string& name);
+  ResourceQuota();
+  ~ResourceQuota();
+
+  /// Resize this \a ResourceQuota to a new size. If \a new_size is smaller
+  /// than the current size of the pool, memory usage will be monotonically
+  /// decreased until it falls under \a new_size.
+  /// No time bound is given for this to occur however.
+  ResourceQuota& Resize(size_t new_size);
+
+  /// Set the max number of threads that can be allocated from this
+  /// ResourceQuota object.
+  ///
+  /// If the new_max_threads value is smaller than the current value, no new
+  /// threads are allocated until the number of active threads fall below
+  /// new_max_threads. There is no time bound on when this may happen i.e none
+  /// of the current threads are forcefully destroyed and all threads run their
+  /// normal course.
+  ResourceQuota& SetMaxThreads(int new_max_threads);
+
+  grpc_resource_quota* c_resource_quota() const { return impl_; }
+
+ private:
+  ResourceQuota(const ResourceQuota& rhs);
+  ResourceQuota& operator=(const ResourceQuota& rhs);
+
+  grpc_resource_quota* const impl_;
+};
+
+}  // namespace grpc_impl
+
+#endif  // GRPCPP_RESOURCE_QUOTA_IMPL_H
index 30e24c9..1b66b72 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * 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.
 #ifndef GRPCPP_SECURITY_AUTH_METADATA_PROCESSOR_H
 #define GRPCPP_SECURITY_AUTH_METADATA_PROCESSOR_H
 
-#include <map>
-
-#include <grpcpp/security/auth_context.h>
-#include <grpcpp/support/status.h>
-#include <grpcpp/support/string_ref.h>
+#include <grpcpp/security/auth_metadata_processor_impl.h>
 
 namespace grpc {
 
-/// Interface allowing custom server-side authorization based on credentials
-/// encoded in metadata.  Objects of this type can be passed to
-/// \a ServerCredentials::SetAuthMetadataProcessor().
-class AuthMetadataProcessor {
- public:
-  typedef std::multimap<grpc::string_ref, grpc::string_ref> InputMetadata;
-  typedef std::multimap<grpc::string, grpc::string> OutputMetadata;
-
-  virtual ~AuthMetadataProcessor() {}
-
-  /// If this method returns true, the \a Process function will be scheduled in
-  /// a different thread from the one processing the call.
-  virtual bool IsBlocking() const { return true; }
-
-  /// context is read/write: it contains the properties of the channel peer and
-  /// it is the job of the Process method to augment it with properties derived
-  /// from the passed-in auth_metadata.
-  /// consumed_auth_metadata needs to be filled with metadata that has been
-  /// consumed by the processor and will be removed from the call.
-  /// response_metadata is the metadata that will be sent as part of the
-  /// response.
-  /// If the return value is not Status::OK, the rpc call will be aborted with
-  /// the error code and error message sent back to the client.
-  virtual Status Process(const InputMetadata& auth_metadata,
-                         AuthContext* context,
-                         OutputMetadata* consumed_auth_metadata,
-                         OutputMetadata* response_metadata) = 0;
-};
+typedef ::grpc_impl::AuthMetadataProcessor AuthMetadataProcessor;
 
 }  // namespace grpc
 
diff --git a/include/grpcpp/security/auth_metadata_processor_impl.h b/include/grpcpp/security/auth_metadata_processor_impl.h
new file mode 100644 (file)
index 0000000..ae45420
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *
+ * 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_SECURITY_AUTH_METADATA_PROCESSOR_IMPL_H
+#define GRPCPP_SECURITY_AUTH_METADATA_PROCESSOR_IMPL_H
+
+#include <map>
+
+#include <grpcpp/security/auth_context.h>
+#include <grpcpp/support/status.h>
+#include <grpcpp/support/string_ref.h>
+
+namespace grpc_impl {
+
+/// Interface allowing custom server-side authorization based on credentials
+/// encoded in metadata.  Objects of this type can be passed to
+/// \a ServerCredentials::SetAuthMetadataProcessor().
+class AuthMetadataProcessor {
+ public:
+  typedef std::multimap<grpc::string_ref, grpc::string_ref> InputMetadata;
+  typedef std::multimap<grpc::string, grpc::string> OutputMetadata;
+
+  virtual ~AuthMetadataProcessor() {}
+
+  /// If this method returns true, the \a Process function will be scheduled in
+  /// a different thread from the one processing the call.
+  virtual bool IsBlocking() const { return true; }
+
+  /// context is read/write: it contains the properties of the channel peer and
+  /// it is the job of the Process method to augment it with properties derived
+  /// from the passed-in auth_metadata.
+  /// consumed_auth_metadata needs to be filled with metadata that has been
+  /// consumed by the processor and will be removed from the call.
+  /// response_metadata is the metadata that will be sent as part of the
+  /// response.
+  /// If the return value is not Status::OK, the rpc call will be aborted with
+  /// the error code and error message sent back to the client.
+  virtual grpc::Status Process(const InputMetadata& auth_metadata,
+                               grpc::AuthContext* context,
+                               OutputMetadata* consumed_auth_metadata,
+                               OutputMetadata* response_metadata) = 0;
+};
+
+}  // namespace grpc_impl
+
+#endif  // GRPCPP_SECURITY_AUTH_METADATA_PROCESSOR_IMPL_H
index dfea390..e924275 100644 (file)
 #ifndef GRPCPP_SECURITY_CREDENTIALS_H
 #define GRPCPP_SECURITY_CREDENTIALS_H
 
-#include <map>
-#include <memory>
-#include <vector>
-
-#include <grpc/grpc_security_constants.h>
-#include <grpcpp/impl/codegen/client_interceptor.h>
-#include <grpcpp/impl/codegen/grpc_library.h>
-#include <grpcpp/security/auth_context.h>
-#include <grpcpp/support/status.h>
-#include <grpcpp/support/string_ref.h>
-
-struct grpc_call;
+#include <grpcpp/security/credentials_impl.h>
 
 namespace grpc {
-class ChannelArguments;
-class Channel;
-class SecureChannelCredentials;
-class CallCredentials;
-class SecureCallCredentials;
-
-class ChannelCredentials;
-
-namespace experimental {
-std::shared_ptr<Channel> CreateCustomChannelWithInterceptors(
-    const grpc::string& target,
-    const std::shared_ptr<ChannelCredentials>& creds,
-    const ChannelArguments& args,
-    std::vector<
-        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
-        interceptor_creators);
-}  // namespace experimental
-
-/// A channel credentials object encapsulates all the state needed by a client
-/// to authenticate with a server for a given channel.
-/// It can make various assertions, e.g., about the client’s identity, role
-/// for all the calls on that channel.
-///
-/// \see https://grpc.io/docs/guides/auth.html
-class ChannelCredentials : private GrpcLibraryCodegen {
- public:
-  ChannelCredentials();
-  ~ChannelCredentials();
-
- protected:
-  friend std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
-      const std::shared_ptr<ChannelCredentials>& channel_creds,
-      const std::shared_ptr<CallCredentials>& call_creds);
-
-  virtual SecureChannelCredentials* AsSecureCredentials() = 0;
-
- private:
-  friend std::shared_ptr<Channel> CreateCustomChannel(
-      const grpc::string& target,
-      const std::shared_ptr<ChannelCredentials>& creds,
-      const ChannelArguments& args);
-
-  friend std::shared_ptr<Channel>
-  experimental::CreateCustomChannelWithInterceptors(
-      const grpc::string& target,
-      const std::shared_ptr<ChannelCredentials>& creds,
-      const ChannelArguments& args,
-      std::vector<
-          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
-          interceptor_creators);
-
-  virtual std::shared_ptr<Channel> CreateChannel(
-      const grpc::string& target, const ChannelArguments& args) = 0;
-
-  // This function should have been a pure virtual function, but it is
-  // implemented as a virtual function so that it does not break API.
-  virtual std::shared_ptr<Channel> CreateChannelWithInterceptors(
-      const grpc::string& target, const ChannelArguments& args,
-      std::vector<
-          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
-          interceptor_creators) {
-    return nullptr;
-  }
-};
-
-/// A call credentials object encapsulates the state needed by a client to
-/// authenticate with a server for a given call on a channel.
-///
-/// \see https://grpc.io/docs/guides/auth.html
-class CallCredentials : private GrpcLibraryCodegen {
- public:
-  CallCredentials();
-  ~CallCredentials();
-
-  /// Apply this instance's credentials to \a call.
-  virtual bool ApplyToCall(grpc_call* call) = 0;
-
- protected:
-  friend std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
-      const std::shared_ptr<ChannelCredentials>& channel_creds,
-      const std::shared_ptr<CallCredentials>& call_creds);
 
-  friend std::shared_ptr<CallCredentials> CompositeCallCredentials(
-      const std::shared_ptr<CallCredentials>& creds1,
-      const std::shared_ptr<CallCredentials>& creds2);
+typedef ::grpc_impl::ChannelCredentials ChannelCredentials;
+typedef ::grpc_impl::CallCredentials CallCredentials;
+typedef ::grpc_impl::SslCredentialsOptions SslCredentialsOptions;
+typedef ::grpc_impl::SecureCallCredentials SecureCallCredentials;
+typedef ::grpc_impl::SecureChannelCredentials SecureChannelCredentials;
 
-  virtual SecureCallCredentials* AsSecureCredentials() = 0;
-};
+static inline std::shared_ptr<grpc_impl::ChannelCredentials>
+GoogleDefaultCredentials() {
+  return ::grpc_impl::GoogleDefaultCredentials();
+}
 
-/// Options used to build SslCredentials.
-struct SslCredentialsOptions {
-  /// The buffer containing the PEM encoding of the server root certificates. If
-  /// this parameter is empty, the default roots will be used.  The default
-  /// roots can be overridden using the \a GRPC_DEFAULT_SSL_ROOTS_FILE_PATH
-  /// environment variable pointing to a file on the file system containing the
-  /// roots.
-  grpc::string pem_root_certs;
+static inline std::shared_ptr<ChannelCredentials> SslCredentials(
+    const SslCredentialsOptions& options) {
+  return ::grpc_impl::SslCredentials(options);
+}
 
-  /// The buffer containing the PEM encoding of the client's private key. This
-  /// parameter can be empty if the client does not have a private key.
-  grpc::string pem_private_key;
-
-  /// The buffer containing the PEM encoding of the client's certificate chain.
-  /// This parameter can be empty if the client does not have a certificate
-  /// chain.
-  grpc::string pem_cert_chain;
-};
-
-// Factories for building different types of Credentials The functions may
-// return empty shared_ptr when credentials cannot be created. If a
-// Credentials pointer is returned, it can still be invalid when used to create
-// a channel. A lame channel will be created then and all rpcs will fail on it.
-
-/// Builds credentials with reasonable defaults.
-///
-/// \warning Only use these credentials when connecting to a Google endpoint.
-/// Using these credentials to connect to any other service may result in this
-/// service being able to impersonate your client for requests to Google
-/// services.
-std::shared_ptr<ChannelCredentials> GoogleDefaultCredentials();
-
-/// Builds SSL Credentials given SSL specific options
-std::shared_ptr<ChannelCredentials> SslCredentials(
-    const SslCredentialsOptions& options);
-
-/// Builds credentials for use when running in GCE
-///
-/// \warning Only use these credentials when connecting to a Google endpoint.
-/// Using these credentials to connect to any other service may result in this
-/// service being able to impersonate your client for requests to Google
-/// services.
-std::shared_ptr<CallCredentials> GoogleComputeEngineCredentials();
+static inline std::shared_ptr<grpc_impl::CallCredentials>
+GoogleComputeEngineCredentials() {
+  return ::grpc_impl::GoogleComputeEngineCredentials();
+}
 
 /// Constant for maximum auth token lifetime.
-constexpr long kMaxAuthTokenLifetimeSecs = 3600;
+constexpr long kMaxAuthTokenLifetimeSecs =
+    ::grpc_impl::kMaxAuthTokenLifetimeSecs;
 
-/// Builds Service Account JWT Access credentials.
-/// json_key is the JSON key string containing the client's private key.
-/// token_lifetime_seconds is the lifetime in seconds of each Json Web Token
-/// (JWT) created with this credentials. It should not exceed
-/// \a kMaxAuthTokenLifetimeSecs or will be cropped to this value.
-std::shared_ptr<CallCredentials> ServiceAccountJWTAccessCredentials(
+static inline std::shared_ptr<grpc_impl::CallCredentials>
+ServiceAccountJWTAccessCredentials(
     const grpc::string& json_key,
-    long token_lifetime_seconds = kMaxAuthTokenLifetimeSecs);
-
-/// Builds refresh token credentials.
-/// json_refresh_token is the JSON string containing the refresh token along
-/// with a client_id and client_secret.
-///
-/// \warning Only use these credentials when connecting to a Google endpoint.
-/// Using these credentials to connect to any other service may result in this
-/// service being able to impersonate your client for requests to Google
-/// services.
-std::shared_ptr<CallCredentials> GoogleRefreshTokenCredentials(
-    const grpc::string& json_refresh_token);
-
-/// Builds access token credentials.
-/// access_token is an oauth2 access token that was fetched using an out of band
-/// mechanism.
-///
-/// \warning Only use these credentials when connecting to a Google endpoint.
-/// Using these credentials to connect to any other service may result in this
-/// service being able to impersonate your client for requests to Google
-/// services.
-std::shared_ptr<CallCredentials> AccessTokenCredentials(
-    const grpc::string& access_token);
-
-/// Builds IAM credentials.
-///
-/// \warning Only use these credentials when connecting to a Google endpoint.
-/// Using these credentials to connect to any other service may result in this
-/// service being able to impersonate your client for requests to Google
-/// services.
-std::shared_ptr<CallCredentials> GoogleIAMCredentials(
+    long token_lifetime_seconds = grpc::kMaxAuthTokenLifetimeSecs) {
+  return ::grpc_impl::ServiceAccountJWTAccessCredentials(
+      json_key, token_lifetime_seconds);
+}
+
+static inline std::shared_ptr<grpc_impl::CallCredentials>
+GoogleRefreshTokenCredentials(const grpc::string& json_refresh_token) {
+  return ::grpc_impl::GoogleRefreshTokenCredentials(json_refresh_token);
+}
+
+static inline std::shared_ptr<grpc_impl::CallCredentials>
+AccessTokenCredentials(const grpc::string& access_token) {
+  return ::grpc_impl::AccessTokenCredentials(access_token);
+}
+
+static inline std::shared_ptr<grpc_impl::CallCredentials> GoogleIAMCredentials(
     const grpc::string& authorization_token,
-    const grpc::string& authority_selector);
+    const grpc::string& authority_selector) {
+  return ::grpc_impl::GoogleIAMCredentials(authorization_token,
+                                           authority_selector);
+}
 
-/// Combines a channel credentials and a call credentials into a composite
-/// channel credentials.
-std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
+static inline std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
     const std::shared_ptr<ChannelCredentials>& channel_creds,
-    const std::shared_ptr<CallCredentials>& call_creds);
-
-/// Combines two call credentials objects into a composite call credentials.
-std::shared_ptr<CallCredentials> CompositeCallCredentials(
-    const std::shared_ptr<CallCredentials>& creds1,
-    const std::shared_ptr<CallCredentials>& creds2);
-
-/// Credentials for an unencrypted, unauthenticated channel
-std::shared_ptr<ChannelCredentials> InsecureChannelCredentials();
-
-/// Credentials for a channel using Cronet.
-std::shared_ptr<ChannelCredentials> CronetChannelCredentials(void* engine);
-
-/// User defined metadata credentials.
-class MetadataCredentialsPlugin {
- public:
-  virtual ~MetadataCredentialsPlugin() {}
-
-  /// If this method returns true, the Process function will be scheduled in
-  /// a different thread from the one processing the call.
-  virtual bool IsBlocking() const { return true; }
-
-  /// Type of credentials this plugin is implementing.
-  virtual const char* GetType() const { return ""; }
-
-  /// Gets the auth metatada produced by this plugin.
-  /// The fully qualified method name is:
-  /// service_url + "/" + method_name.
-  /// The channel_auth_context contains (among other things), the identity of
-  /// the server.
-  virtual Status GetMetadata(
-      grpc::string_ref service_url, grpc::string_ref method_name,
-      const AuthContext& channel_auth_context,
-      std::multimap<grpc::string, grpc::string>* metadata) = 0;
-};
-
-std::shared_ptr<CallCredentials> MetadataCredentialsFromPlugin(
-    std::unique_ptr<MetadataCredentialsPlugin> plugin);
+    const std::shared_ptr<CallCredentials>& call_creds) {
+  return ::grpc_impl::CompositeChannelCredentials(channel_creds, call_creds);
+}
+
+static inline std::shared_ptr<grpc_impl::CallCredentials>
+CompositeCallCredentials(const std::shared_ptr<CallCredentials>& creds1,
+                         const std::shared_ptr<CallCredentials>& creds2) {
+  return ::grpc_impl::CompositeCallCredentials(creds1, creds2);
+}
+
+static inline std::shared_ptr<grpc_impl::ChannelCredentials>
+InsecureChannelCredentials() {
+  return ::grpc_impl::InsecureChannelCredentials();
+}
+
+static inline std::shared_ptr<grpc_impl::ChannelCredentials>
+CronetChannelCredentials(void* engine) {
+  return ::grpc_impl::CronetChannelCredentials(engine);
+}
+
+typedef ::grpc_impl::MetadataCredentialsPlugin MetadataCredentialsPlugin;
+
+static inline std::shared_ptr<grpc_impl::CallCredentials>
+MetadataCredentialsFromPlugin(
+    std::unique_ptr<MetadataCredentialsPlugin> plugin) {
+  return ::grpc_impl::MetadataCredentialsFromPlugin(std::move(plugin));
+}
 
 namespace experimental {
 
-/// Options used to build AltsCredentials.
-struct AltsCredentialsOptions {
-  /// service accounts of target endpoint that will be acceptable
-  /// by the client. If service accounts are provided and none of them matches
-  /// that of the server, authentication will fail.
-  std::vector<grpc::string> target_service_accounts;
-};
+typedef ::grpc_impl::experimental::AltsCredentialsOptions
+    AltsCredentialsOptions;
 
-/// Builds ALTS Credentials given ALTS specific options
-std::shared_ptr<ChannelCredentials> AltsCredentials(
-    const AltsCredentialsOptions& options);
+static inline std::shared_ptr<grpc_impl::ChannelCredentials> AltsCredentials(
+    const AltsCredentialsOptions& options) {
+  return ::grpc_impl::experimental::AltsCredentials(options);
+}
 
-/// Builds Local Credentials.
-std::shared_ptr<ChannelCredentials> LocalCredentials(
-    grpc_local_connect_type type);
+static inline std::shared_ptr<grpc_impl::ChannelCredentials> LocalCredentials(
+    grpc_local_connect_type type) {
+  return ::grpc_impl::experimental::LocalCredentials(type);
+}
 
 }  // namespace experimental
 }  // namespace grpc
diff --git a/include/grpcpp/security/credentials_impl.h b/include/grpcpp/security/credentials_impl.h
new file mode 100644 (file)
index 0000000..29ba207
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ *
+ * 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_SECURITY_CREDENTIALS_IMPL_H
+#define GRPCPP_SECURITY_CREDENTIALS_IMPL_H
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <grpc/grpc_security_constants.h>
+#include <grpcpp/channel.h>
+#include <grpcpp/impl/codegen/client_interceptor.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+#include <grpcpp/security/auth_context.h>
+#include <grpcpp/support/channel_arguments.h>
+#include <grpcpp/support/status.h>
+#include <grpcpp/support/string_ref.h>
+
+struct grpc_call;
+
+namespace grpc_impl {
+
+class ChannelCredentials;
+class CallCredentials;
+class SecureCallCredentials;
+class SecureChannelCredentials;
+
+std::shared_ptr<::grpc::Channel> CreateCustomChannelImpl(
+    const grpc::string& target,
+    const std::shared_ptr<ChannelCredentials>& creds,
+    const grpc::ChannelArguments& args);
+
+namespace experimental {
+std::shared_ptr<::grpc::Channel> CreateCustomChannelWithInterceptors(
+    const grpc::string& target,
+    const std::shared_ptr<ChannelCredentials>& creds,
+    const grpc::ChannelArguments& args,
+    std::vector<
+        std::unique_ptr<grpc::experimental::ClientInterceptorFactoryInterface>>
+        interceptor_creators);
+}
+
+/// A channel credentials object encapsulates all the state needed by a client
+/// to authenticate with a server for a given channel.
+/// It can make various assertions, e.g., about the client’s identity, role
+/// for all the calls on that channel.
+///
+/// \see https://grpc.io/docs/guides/auth.html
+class ChannelCredentials : private grpc::GrpcLibraryCodegen {
+ public:
+  ChannelCredentials();
+  ~ChannelCredentials();
+
+ protected:
+  friend std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
+      const std::shared_ptr<ChannelCredentials>& channel_creds,
+      const std::shared_ptr<CallCredentials>& call_creds);
+
+  virtual SecureChannelCredentials* AsSecureCredentials() = 0;
+
+ private:
+  friend std::shared_ptr<::grpc::Channel> CreateCustomChannelImpl(
+      const grpc::string& target,
+      const std::shared_ptr<ChannelCredentials>& creds,
+      const grpc::ChannelArguments& args);
+
+  friend std::shared_ptr<::grpc::Channel>
+  grpc_impl::experimental::CreateCustomChannelWithInterceptors(
+      const grpc::string& target,
+      const std::shared_ptr<ChannelCredentials>& creds,
+      const grpc::ChannelArguments& args,
+      std::vector<std::unique_ptr<
+          grpc::experimental::ClientInterceptorFactoryInterface>>
+          interceptor_creators);
+
+  virtual std::shared_ptr<::grpc::Channel> CreateChannelImpl(
+      const grpc::string& target, const grpc::ChannelArguments& args) = 0;
+
+  // This function should have been a pure virtual function, but it is
+  // implemented as a virtual function so that it does not break API.
+  virtual std::shared_ptr<::grpc::Channel> CreateChannelWithInterceptors(
+      const grpc::string& target, const grpc::ChannelArguments& args,
+      std::vector<std::unique_ptr<
+          grpc::experimental::ClientInterceptorFactoryInterface>>
+          interceptor_creators) {
+    return nullptr;
+  }
+};
+
+/// A call credentials object encapsulates the state needed by a client to
+/// authenticate with a server for a given call on a channel.
+///
+/// \see https://grpc.io/docs/guides/auth.html
+class CallCredentials : private grpc::GrpcLibraryCodegen {
+ public:
+  CallCredentials();
+  ~CallCredentials();
+
+  /// Apply this instance's credentials to \a call.
+  virtual bool ApplyToCall(grpc_call* call) = 0;
+
+ protected:
+  friend std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
+      const std::shared_ptr<ChannelCredentials>& channel_creds,
+      const std::shared_ptr<CallCredentials>& call_creds);
+
+  friend std::shared_ptr<CallCredentials> CompositeCallCredentials(
+      const std::shared_ptr<CallCredentials>& creds1,
+      const std::shared_ptr<CallCredentials>& creds2);
+
+  virtual SecureCallCredentials* AsSecureCredentials() = 0;
+};
+
+/// Options used to build SslCredentials.
+struct SslCredentialsOptions {
+  /// The buffer containing the PEM encoding of the server root certificates. If
+  /// this parameter is empty, the default roots will be used.  The default
+  /// roots can be overridden using the \a GRPC_DEFAULT_SSL_ROOTS_FILE_PATH
+  /// environment variable pointing to a file on the file system containing the
+  /// roots.
+  grpc::string pem_root_certs;
+
+  /// The buffer containing the PEM encoding of the client's private key. This
+  /// parameter can be empty if the client does not have a private key.
+  grpc::string pem_private_key;
+
+  /// The buffer containing the PEM encoding of the client's certificate chain.
+  /// This parameter can be empty if the client does not have a certificate
+  /// chain.
+  grpc::string pem_cert_chain;
+};
+
+// Factories for building different types of Credentials The functions may
+// return empty shared_ptr when credentials cannot be created. If a
+// Credentials pointer is returned, it can still be invalid when used to create
+// a channel. A lame channel will be created then and all rpcs will fail on it.
+
+/// Builds credentials with reasonable defaults.
+///
+/// \warning Only use these credentials when connecting to a Google endpoint.
+/// Using these credentials to connect to any other service may result in this
+/// service being able to impersonate your client for requests to Google
+/// services.
+std::shared_ptr<ChannelCredentials> GoogleDefaultCredentials();
+
+/// Builds SSL Credentials given SSL specific options
+std::shared_ptr<ChannelCredentials> SslCredentials(
+    const SslCredentialsOptions& options);
+
+/// Builds credentials for use when running in GCE
+///
+/// \warning Only use these credentials when connecting to a Google endpoint.
+/// Using these credentials to connect to any other service may result in this
+/// service being able to impersonate your client for requests to Google
+/// services.
+std::shared_ptr<CallCredentials> GoogleComputeEngineCredentials();
+
+constexpr long kMaxAuthTokenLifetimeSecs = 3600;
+
+/// Builds Service Account JWT Access credentials.
+/// json_key is the JSON key string containing the client's private key.
+/// token_lifetime_seconds is the lifetime in seconds of each Json Web Token
+/// (JWT) created with this credentials. It should not exceed
+/// \a kMaxAuthTokenLifetimeSecs or will be cropped to this value.
+std::shared_ptr<CallCredentials> ServiceAccountJWTAccessCredentials(
+    const grpc::string& json_key,
+    long token_lifetime_seconds = grpc_impl::kMaxAuthTokenLifetimeSecs);
+
+/// Builds refresh token credentials.
+/// json_refresh_token is the JSON string containing the refresh token along
+/// with a client_id and client_secret.
+///
+/// \warning Only use these credentials when connecting to a Google endpoint.
+/// Using these credentials to connect to any other service may result in this
+/// service being able to impersonate your client for requests to Google
+/// services.
+std::shared_ptr<CallCredentials> GoogleRefreshTokenCredentials(
+    const grpc::string& json_refresh_token);
+
+/// Builds access token credentials.
+/// access_token is an oauth2 access token that was fetched using an out of band
+/// mechanism.
+///
+/// \warning Only use these credentials when connecting to a Google endpoint.
+/// Using these credentials to connect to any other service may result in this
+/// service being able to impersonate your client for requests to Google
+/// services.
+std::shared_ptr<CallCredentials> AccessTokenCredentials(
+    const grpc::string& access_token);
+
+/// Builds IAM credentials.
+///
+/// \warning Only use these credentials when connecting to a Google endpoint.
+/// Using these credentials to connect to any other service may result in this
+/// service being able to impersonate your client for requests to Google
+/// services.
+std::shared_ptr<CallCredentials> GoogleIAMCredentials(
+    const grpc::string& authorization_token,
+    const grpc::string& authority_selector);
+
+/// Combines a channel credentials and a call credentials into a composite
+/// channel credentials.
+std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
+    const std::shared_ptr<ChannelCredentials>& channel_creds,
+    const std::shared_ptr<CallCredentials>& call_creds);
+
+/// Combines two call credentials objects into a composite call credentials.
+std::shared_ptr<CallCredentials> CompositeCallCredentials(
+    const std::shared_ptr<CallCredentials>& creds1,
+    const std::shared_ptr<CallCredentials>& creds2);
+
+/// Credentials for an unencrypted, unauthenticated channel
+std::shared_ptr<ChannelCredentials> InsecureChannelCredentials();
+
+/// Credentials for a channel using Cronet.
+std::shared_ptr<ChannelCredentials> CronetChannelCredentials(void* engine);
+
+/// User defined metadata credentials.
+class MetadataCredentialsPlugin {
+ public:
+  virtual ~MetadataCredentialsPlugin() {}
+
+  /// If this method returns true, the Process function will be scheduled in
+  /// a different thread from the one processing the call.
+  virtual bool IsBlocking() const { return true; }
+
+  /// Type of credentials this plugin is implementing.
+  virtual const char* GetType() const { return ""; }
+
+  /// Gets the auth metatada produced by this plugin.
+  /// The fully qualified method name is:
+  /// service_url + "/" + method_name.
+  /// The channel_auth_context contains (among other things), the identity of
+  /// the server.
+  virtual grpc::Status GetMetadata(
+      grpc::string_ref service_url, grpc::string_ref method_name,
+      const grpc::AuthContext& channel_auth_context,
+      std::multimap<grpc::string, grpc::string>* metadata) = 0;
+};
+
+std::shared_ptr<CallCredentials> MetadataCredentialsFromPlugin(
+    std::unique_ptr<MetadataCredentialsPlugin> plugin);
+
+namespace experimental {
+
+/// Options used to build AltsCredentials.
+struct AltsCredentialsOptions {
+  /// service accounts of target endpoint that will be acceptable
+  /// by the client. If service accounts are provided and none of them matches
+  /// that of the server, authentication will fail.
+  std::vector<grpc::string> target_service_accounts;
+};
+
+/// Builds ALTS Credentials given ALTS specific options
+std::shared_ptr<ChannelCredentials> AltsCredentials(
+    const AltsCredentialsOptions& options);
+
+/// Builds Local Credentials.
+std::shared_ptr<ChannelCredentials> LocalCredentials(
+    grpc_local_connect_type type);
+
+}  // namespace experimental
+}  // namespace grpc_impl
+
+#endif  // GRPCPP_SECURITY_CREDENTIALS_IMPL_H
index bd00a0a..57f7338 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * 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.
 #ifndef GRPCPP_SECURITY_SERVER_CREDENTIALS_H
 #define GRPCPP_SECURITY_SERVER_CREDENTIALS_H
 
-#include <memory>
-#include <vector>
+#include <grpcpp/security/server_credentials_impl.h>
 
-#include <grpc/grpc_security_constants.h>
-#include <grpcpp/security/auth_metadata_processor.h>
-#include <grpcpp/support/config.h>
+namespace grpc_impl {
 
-struct grpc_server;
-
-namespace grpc {
 class Server;
+}  // namespace grpc_impl
+namespace grpc {
 
-/// Wrapper around \a grpc_server_credentials, a way to authenticate a server.
-class ServerCredentials {
- public:
-  virtual ~ServerCredentials();
-
-  /// This method is not thread-safe and has to be called before the server is
-  /// started. The last call to this function wins.
-  virtual void SetAuthMetadataProcessor(
-      const std::shared_ptr<AuthMetadataProcessor>& processor) = 0;
-
- private:
-  friend class ::grpc::Server;
-
-  /// Tries to bind \a server to the given \a addr (eg, localhost:1234,
-  /// 192.168.1.1:31416, [::1]:27182, etc.)
-  ///
-  /// \return bound port number on sucess, 0 on failure.
-  // TODO(dgq): the "port" part seems to be a misnomer.
-  virtual int AddPortToServer(const grpc::string& addr,
-                              grpc_server* server) = 0;
-};
+typedef ::grpc_impl::ServerCredentials ServerCredentials;
 
 /// Options to create ServerCredentials with SSL
 struct SslServerCredentialsOptions {
@@ -79,27 +55,29 @@ struct SslServerCredentialsOptions {
   grpc_ssl_client_certificate_request_type client_certificate_request;
 };
 
-/// Builds SSL ServerCredentials given SSL specific options
-std::shared_ptr<ServerCredentials> SslServerCredentials(
-    const SslServerCredentialsOptions& options);
+static inline std::shared_ptr<ServerCredentials> SslServerCredentials(
+    const SslServerCredentialsOptions& options) {
+  return ::grpc_impl::SslServerCredentials(options);
+}
 
-/// Builds insecure server credentials.
-std::shared_ptr<ServerCredentials> InsecureServerCredentials();
+static inline std::shared_ptr<ServerCredentials> InsecureServerCredentials() {
+  return ::grpc_impl::InsecureServerCredentials();
+}
 
 namespace experimental {
 
-/// Options to create ServerCredentials with ALTS
-struct AltsServerCredentialsOptions {
-  /// Add fields if needed.
-};
+typedef ::grpc_impl::experimental::AltsServerCredentialsOptions
+    AltsServerCredentialsOptions;
 
-/// Builds ALTS ServerCredentials given ALTS specific options
-std::shared_ptr<ServerCredentials> AltsServerCredentials(
-    const AltsServerCredentialsOptions& options);
+static inline std::shared_ptr<ServerCredentials> AltsServerCredentials(
+    const AltsServerCredentialsOptions& options) {
+  return ::grpc_impl::experimental::AltsServerCredentials(options);
+}
 
-/// Builds Local ServerCredentials.
-std::shared_ptr<ServerCredentials> LocalServerCredentials(
-    grpc_local_connect_type type);
+static inline std::shared_ptr<ServerCredentials> LocalServerCredentials(
+    grpc_local_connect_type type) {
+  return ::grpc_impl::experimental::LocalServerCredentials(type);
+}
 
 }  // namespace experimental
 }  // namespace grpc
diff --git a/include/grpcpp/security/server_credentials_impl.h b/include/grpcpp/security/server_credentials_impl.h
new file mode 100644 (file)
index 0000000..f088490
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ *
+ * 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_SECURITY_SERVER_CREDENTIALS_IMPL_H
+#define GRPCPP_SECURITY_SERVER_CREDENTIALS_IMPL_H
+
+#include <memory>
+#include <vector>
+
+#include <grpc/grpc_security_constants.h>
+#include <grpcpp/security/auth_metadata_processor.h>
+#include <grpcpp/support/config.h>
+
+struct grpc_server;
+
+namespace grpc {
+
+struct SslServerCredentialsOptions;
+}  // namespace grpc
+namespace grpc_impl {
+class Server;
+
+/// Wrapper around \a grpc_server_credentials, a way to authenticate a server.
+class ServerCredentials {
+ public:
+  virtual ~ServerCredentials();
+
+  /// This method is not thread-safe and has to be called before the server is
+  /// started. The last call to this function wins.
+  virtual void SetAuthMetadataProcessor(
+      const std::shared_ptr<grpc::AuthMetadataProcessor>& processor) = 0;
+
+ private:
+  friend class ::grpc_impl::Server;
+
+  /// Tries to bind \a server to the given \a addr (eg, localhost:1234,
+  /// 192.168.1.1:31416, [::1]:27182, etc.)
+  ///
+  /// \return bound port number on success, 0 on failure.
+  // TODO(dgq): the "port" part seems to be a misnomer.
+  virtual int AddPortToServer(const grpc::string& addr,
+                              grpc_server* server) = 0;
+};
+
+/// Builds SSL ServerCredentials given SSL specific options
+std::shared_ptr<ServerCredentials> SslServerCredentials(
+    const grpc::SslServerCredentialsOptions& options);
+
+/// Builds insecure server credentials.
+std::shared_ptr<ServerCredentials> InsecureServerCredentials();
+
+namespace experimental {
+
+/// Options to create ServerCredentials with ALTS
+struct AltsServerCredentialsOptions {
+  /// Add fields if needed.
+};
+
+/// Builds ALTS ServerCredentials given ALTS specific options
+std::shared_ptr<ServerCredentials> AltsServerCredentials(
+    const AltsServerCredentialsOptions& options);
+
+/// Builds Local ServerCredentials.
+std::shared_ptr<ServerCredentials> LocalServerCredentials(
+    grpc_local_connect_type type);
+
+}  // namespace experimental
+}  // namespace grpc_impl
+
+#endif  // GRPCPP_SECURITY_SERVER_CREDENTIALS_IMPL_H
index f5c99f2..3de2aba 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * 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.
 #ifndef GRPCPP_SERVER_H
 #define GRPCPP_SERVER_H
 
-#include <condition_variable>
-#include <list>
-#include <memory>
-#include <mutex>
-#include <vector>
-
-#include <grpc/compression.h>
-#include <grpc/support/atm.h>
-#include <grpcpp/completion_queue.h>
-#include <grpcpp/impl/call.h>
-#include <grpcpp/impl/codegen/client_interceptor.h>
-#include <grpcpp/impl/codegen/grpc_library.h>
-#include <grpcpp/impl/codegen/server_interface.h>
-#include <grpcpp/impl/rpc_service_method.h>
-#include <grpcpp/security/server_credentials.h>
-#include <grpcpp/support/channel_arguments.h>
-#include <grpcpp/support/config.h>
-#include <grpcpp/support/status.h>
-
-struct grpc_server;
+#include <grpcpp/server_impl.h>
 
 namespace grpc {
 
-class AsyncGenericService;
-class HealthCheckServiceInterface;
-class ServerContext;
-class ServerInitializer;
-
-/// Represents a gRPC server.
-///
-/// Use a \a grpc::ServerBuilder to create, configure, and start
-/// \a Server instances.
-class Server : public ServerInterface, private GrpcLibraryCodegen {
- public:
-  ~Server();
-
-  /// Block until the server shuts down.
-  ///
-  /// \warning The server must be either shutting down or some other thread must
-  /// call \a Shutdown for this function to ever return.
-  void Wait() override;
-
-  /// Global callbacks are a set of hooks that are called when server
-  /// events occur.  \a SetGlobalCallbacks method is used to register
-  /// the hooks with gRPC.  Note that
-  /// the \a GlobalCallbacks instance will be shared among all
-  /// \a Server instances in an application and can be set exactly
-  /// once per application.
-  class GlobalCallbacks {
-   public:
-    virtual ~GlobalCallbacks() {}
-    /// Called before server is created.
-    virtual void UpdateArguments(ChannelArguments* args) {}
-    /// Called before application callback for each synchronous server request
-    virtual void PreSynchronousRequest(ServerContext* context) = 0;
-    /// Called after application callback for each synchronous server request
-    virtual void PostSynchronousRequest(ServerContext* context) = 0;
-    /// Called before server is started.
-    virtual void PreServerStart(Server* server) {}
-    /// Called after a server port is added.
-    virtual void AddPort(Server* server, const grpc::string& addr,
-                         ServerCredentials* creds, int port) {}
-  };
-  /// Set the global callback object. Can only be called once per application.
-  /// Does not take ownership of callbacks, and expects the pointed to object
-  /// to be alive until all server objects in the process have been destroyed.
-  /// The same \a GlobalCallbacks object will be used throughout the
-  /// application and is shared among all \a Server objects.
-  static void SetGlobalCallbacks(GlobalCallbacks* callbacks);
-
-  /// Returns a \em raw pointer to the underlying \a grpc_server instance.
-  /// EXPERIMENTAL:  for internal/test use only
-  grpc_server* c_server();
-
-  /// Returns the health check service.
-  HealthCheckServiceInterface* GetHealthCheckService() const {
-    return health_check_service_.get();
-  }
-
-  /// Establish a channel for in-process communication
-  std::shared_ptr<Channel> InProcessChannel(const ChannelArguments& args);
-
-  /// NOTE: class experimental_type is not part of the public API of this class.
-  /// TODO(yashykt): Integrate into public API when this is no longer
-  /// experimental.
-  class experimental_type {
-   public:
-    explicit experimental_type(Server* server) : server_(server) {}
-
-    /// Establish a channel for in-process communication with client
-    /// interceptors
-    std::shared_ptr<Channel> InProcessChannelWithInterceptors(
-        const ChannelArguments& args,
-        std::vector<
-            std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
-            interceptor_creators);
-
-   private:
-    Server* server_;
-  };
-
-  /// NOTE: The function experimental() is not stable public API. It is a view
-  /// to the experimental components of this class. It may be changed or removed
-  /// at any time.
-  experimental_type experimental() { return experimental_type(this); }
-
- protected:
-  /// Register a service. This call does not take ownership of the service.
-  /// The service must exist for the lifetime of the Server instance.
-  bool RegisterService(const grpc::string* host, Service* service) override;
-
-  /// Try binding the server to the given \a addr endpoint
-  /// (port, and optionally including IP address to bind to).
-  ///
-  /// It can be invoked multiple times. Should be used before
-  /// starting the server.
-  ///
-  /// \param addr The address to try to bind to the server (eg, localhost:1234,
-  /// 192.168.1.1:31416, [::1]:27182, etc.).
-  /// \param creds The credentials associated with the server.
-  ///
-  /// \return bound port number on success, 0 on failure.
-  ///
-  /// \warning It is an error to call this method on an already started server.
-  int AddListeningPort(const grpc::string& addr,
-                       ServerCredentials* creds) override;
-
-  /// NOTE: This is *NOT* a public API. The server constructors are supposed to
-  /// be used by \a ServerBuilder class only. The constructor will be made
-  /// 'private' very soon.
-  ///
-  /// Server constructors. To be used by \a ServerBuilder only.
-  ///
-  /// \param max_message_size Maximum message length that the channel can
-  /// receive.
-  ///
-  /// \param args The channel args
-  ///
-  /// \param sync_server_cqs The completion queues to use if the server is a
-  /// synchronous server (or a hybrid server). The server polls for new RPCs on
-  /// these queues
-  ///
-  /// \param min_pollers The minimum number of polling threads per server
-  /// completion queue (in param sync_server_cqs) to use for listening to
-  /// incoming requests (used only in case of sync server)
-  ///
-  /// \param max_pollers The maximum number of polling threads per server
-  /// completion queue (in param sync_server_cqs) to use for listening to
-  /// incoming requests (used only in case of sync server)
-  ///
-  /// \param sync_cq_timeout_msec The timeout to use when calling AsyncNext() on
-  /// server completion queues passed via sync_server_cqs param.
-  Server(int max_message_size, ChannelArguments* args,
-         std::shared_ptr<std::vector<std::unique_ptr<ServerCompletionQueue>>>
-             sync_server_cqs,
-         int min_pollers, int max_pollers, int sync_cq_timeout_msec,
-         grpc_resource_quota* server_rq = nullptr,
-         std::vector<
-             std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
-             interceptor_creators = std::vector<std::unique_ptr<
-                 experimental::ServerInterceptorFactoryInterface>>());
-
-  /// Start the server.
-  ///
-  /// \param cqs Completion queues for handling asynchronous services. The
-  /// caller is required to keep all completion queues live until the server is
-  /// destroyed.
-  /// \param num_cqs How many completion queues does \a cqs hold.
-  void Start(ServerCompletionQueue** cqs, size_t num_cqs) override;
-
-  grpc_server* server() override { return server_; }
-
- private:
-  std::vector<std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>*
-  interceptor_creators() override {
-    return &interceptor_creators_;
-  }
-
-  friend class AsyncGenericService;
-  friend class ServerBuilder;
-  friend class ServerInitializer;
-
-  class SyncRequest;
-  class CallbackRequestBase;
-  template <class ServerContextType>
-  class CallbackRequest;
-  class UnimplementedAsyncRequest;
-  class UnimplementedAsyncResponse;
-
-  /// SyncRequestThreadManager is an implementation of ThreadManager. This class
-  /// is responsible for polling for incoming RPCs and calling the RPC handlers.
-  /// This is only used in case of a Sync server (i.e a server exposing a sync
-  /// interface)
-  class SyncRequestThreadManager;
-
-  /// Register a generic service. This call does not take ownership of the
-  /// service. The service must exist for the lifetime of the Server instance.
-  void RegisterAsyncGenericService(AsyncGenericService* service) override;
-
-  /// NOTE: class experimental_registration_type is not part of the public API
-  /// of this class
-  /// TODO(vjpai): Move these contents to the public API of Server when
-  ///              they are no longer experimental
-  class experimental_registration_type final
-      : public experimental_registration_interface {
-   public:
-    explicit experimental_registration_type(Server* server) : server_(server) {}
-    void RegisterCallbackGenericService(
-        experimental::CallbackGenericService* service) override {
-      server_->RegisterCallbackGenericService(service);
-    }
-
-   private:
-    Server* server_;
-  };
-
-  /// TODO(vjpai): Mark this override when experimental type above is deleted
-  void RegisterCallbackGenericService(
-      experimental::CallbackGenericService* service);
-
-  /// NOTE: The function experimental_registration() is not stable public API.
-  /// It is a view to the experimental components of this class. It may be
-  /// changed or removed at any time.
-  experimental_registration_interface* experimental_registration() override {
-    return &experimental_registration_;
-  }
-
-  void PerformOpsOnCall(internal::CallOpSetInterface* ops,
-                        internal::Call* call) override;
-
-  void ShutdownInternal(gpr_timespec deadline) override;
-
-  int max_receive_message_size() const override {
-    return max_receive_message_size_;
-  }
-
-  CompletionQueue* CallbackCQ() override;
-
-  ServerInitializer* initializer();
-
-  // A vector of interceptor factory objects.
-  // This should be destroyed after health_check_service_ and this requirement
-  // is satisfied by declaring interceptor_creators_ before
-  // health_check_service_. (C++ mandates that member objects be destroyed in
-  // the reverse order of initialization.)
-  std::vector<std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
-      interceptor_creators_;
-
-  const int max_receive_message_size_;
-
-  /// The following completion queues are ONLY used in case of Sync API
-  /// i.e. if the server has any services with sync methods. The server uses
-  /// these completion queues to poll for new RPCs
-  std::shared_ptr<std::vector<std::unique_ptr<ServerCompletionQueue>>>
-      sync_server_cqs_;
-
-  /// List of \a ThreadManager instances (one for each cq in
-  /// the \a sync_server_cqs)
-  std::vector<std::unique_ptr<SyncRequestThreadManager>> sync_req_mgrs_;
-
-  // Outstanding unmatched callback requests, indexed by method.
-  // NOTE: Using a gpr_atm rather than atomic_int because atomic_int isn't
-  //       copyable or movable and thus will cause compilation errors. We
-  //       actually only want to extend the vector before the threaded use
-  //       starts, but this is still a limitation.
-  std::vector<gpr_atm> callback_unmatched_reqs_count_;
-
-  // List of callback requests to start when server actually starts.
-  std::list<CallbackRequestBase*> callback_reqs_to_start_;
-
-  // For registering experimental callback generic service; remove when that
-  // method longer experimental
-  experimental_registration_type experimental_registration_{this};
-
-  // Server status
-  std::mutex mu_;
-  bool started_;
-  bool shutdown_;
-  bool shutdown_notified_;  // Was notify called on the shutdown_cv_
-
-  std::condition_variable shutdown_cv_;
-
-  // It is ok (but not required) to nest callback_reqs_mu_ under mu_ .
-  // Incrementing callback_reqs_outstanding_ is ok without a lock but it must be
-  // decremented under the lock in case it is the last request and enables the
-  // server shutdown. The increment is performance-critical since it happens
-  // during periods of increasing load; the decrement happens only when memory
-  // is maxed out, during server shutdown, or (possibly in a future version)
-  // during decreasing load, so it is less performance-critical.
-  std::mutex callback_reqs_mu_;
-  std::condition_variable callback_reqs_done_cv_;
-  std::atomic_int callback_reqs_outstanding_{0};
-
-  std::shared_ptr<GlobalCallbacks> global_callbacks_;
-
-  std::vector<grpc::string> services_;
-  bool has_async_generic_service_{false};
-  bool has_callback_generic_service_{false};
-
-  // Pointer to the wrapped grpc_server.
-  grpc_server* server_;
-
-  std::unique_ptr<ServerInitializer> server_initializer_;
-
-  std::unique_ptr<HealthCheckServiceInterface> health_check_service_;
-  bool health_check_service_disabled_;
-
-  // When appropriate, use a default callback generic service to handle
-  // unimplemented methods
-  std::unique_ptr<experimental::CallbackGenericService> unimplemented_service_;
-
-  // A special handler for resource exhausted in sync case
-  std::unique_ptr<internal::MethodHandler> resource_exhausted_handler_;
-
-  // Handler for callback generic service, if any
-  std::unique_ptr<internal::MethodHandler> generic_handler_;
-
-  // callback_cq_ references the callbackable completion queue associated
-  // with this server (if any). It is set on the first call to CallbackCQ().
-  // It is _not owned_ by the server; ownership belongs with its internal
-  // shutdown callback tag (invoked when the CQ is fully shutdown).
-  // It is protected by mu_
-  CompletionQueue* callback_cq_ = nullptr;
-};
+typedef ::grpc_impl::Server Server;
 
 }  // namespace grpc
 
index 18cfbb2..d9ec7c4 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015-2016 gRPC authors.
+ * 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.
 #ifndef GRPCPP_SERVER_BUILDER_H
 #define GRPCPP_SERVER_BUILDER_H
 
-#include <climits>
-#include <map>
-#include <memory>
-#include <vector>
-
-#include <grpc/compression.h>
-#include <grpc/support/cpu.h>
-#include <grpc/support/workaround_list.h>
-#include <grpcpp/impl/channel_argument_option.h>
-#include <grpcpp/impl/codegen/server_interceptor.h>
-#include <grpcpp/impl/server_builder_option.h>
-#include <grpcpp/impl/server_builder_plugin.h>
-#include <grpcpp/support/config.h>
-
-struct grpc_resource_quota;
+#include <grpcpp/server_builder_impl.h>
 
 namespace grpc {
 
-class AsyncGenericService;
-class ResourceQuota;
-class CompletionQueue;
-class Server;
-class ServerCompletionQueue;
-class ServerCredentials;
-class Service;
-
-namespace testing {
-class ServerBuilderPluginTest;
-}  // namespace testing
-
-namespace experimental {
-class CallbackGenericService;
-}  // namespace experimental
-
-/// A builder class for the creation and startup of \a grpc::Server instances.
-class ServerBuilder {
- public:
-  ServerBuilder();
-  virtual ~ServerBuilder();
-
-  //////////////////////////////////////////////////////////////////////////////
-  // Primary API's
-
-  /// Return a running server which is ready for processing calls.
-  /// Before calling, one typically needs to ensure that:
-  ///  1. a service is registered - so that the server knows what to serve
-  ///     (via RegisterService, or RegisterAsyncGenericService)
-  ///  2. a listening port has been added - so the server knows where to receive
-  ///     traffic (via AddListeningPort)
-  ///  3. [for async api only] completion queues have been added via
-  ///     AddCompletionQueue
-  virtual std::unique_ptr<Server> BuildAndStart();
-
-  /// Register a service. This call does not take ownership of the service.
-  /// The service must exist for the lifetime of the \a Server instance returned
-  /// by \a BuildAndStart().
-  /// Matches requests with any :authority
-  ServerBuilder& RegisterService(Service* service);
-
-  /// Enlists an endpoint \a addr (port with an optional IP address) to
-  /// bind the \a grpc::Server object to be created to.
-  ///
-  /// It can be invoked multiple times.
-  ///
-  /// \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., [::]:<port>, which also accepts IPv4
-  /// connections.  Valid values include dns:///localhost:1234, /
-  /// 192.168.1.1:31416, dns:///[::1]:27182, etc.).
-  /// \param creds The credentials associated with the server.
-  /// \param selected_port[out] If not `nullptr`, gets populated with the port
-  /// number bound to the \a grpc::Server for the corresponding endpoint after
-  /// it is successfully bound by BuildAndStart(), 0 otherwise. AddListeningPort
-  /// does not modify this pointer.
-  ServerBuilder& AddListeningPort(const grpc::string& addr_uri,
-                                  std::shared_ptr<ServerCredentials> creds,
-                                  int* selected_port = nullptr);
-
-  /// Add a completion queue for handling asynchronous services.
-  ///
-  /// Best performance is typically obtained by using one thread per polling
-  /// completion queue.
-  ///
-  /// Caller is required to shutdown the server prior to shutting down the
-  /// returned completion queue. Caller is also required to drain the
-  /// completion queue after shutting it down. A typical usage scenario:
-  ///
-  /// // While building the server:
-  /// ServerBuilder builder;
-  /// ...
-  /// cq_ = builder.AddCompletionQueue();
-  /// server_ = builder.BuildAndStart();
-  ///
-  /// // While shutting down the server;
-  /// server_->Shutdown();
-  /// cq_->Shutdown();  // Always *after* the associated server's Shutdown()!
-  /// // Drain the cq_ that was created
-  /// void* ignored_tag;
-  /// bool ignored_ok;
-  /// while (cq_->Next(&ignored_tag, &ignored_ok)) { }
-  ///
-  /// \param is_frequently_polled This is an optional parameter to inform gRPC
-  /// library about whether this completion queue would be frequently polled
-  /// (i.e. by calling \a Next() or \a AsyncNext()). The default value is
-  /// 'true' and is the recommended setting. Setting this to 'false' (i.e.
-  /// not polling the completion queue frequently) will have a significantly
-  /// negative performance impact and hence should not be used in production
-  /// use cases.
-  std::unique_ptr<ServerCompletionQueue> AddCompletionQueue(
-      bool is_frequently_polled = true);
-
-  //////////////////////////////////////////////////////////////////////////////
-  // Less commonly used RegisterService variants
-
-  /// Register a service. This call does not take ownership of the service.
-  /// The service must exist for the lifetime of the \a Server instance returned
-  /// by \a BuildAndStart().
-  /// Only matches requests with :authority \a host
-  ServerBuilder& RegisterService(const grpc::string& host, Service* service);
-
-  /// Register a generic service.
-  /// Matches requests with any :authority
-  /// This is mostly useful for writing generic gRPC Proxies where the exact
-  /// serialization format is unknown
-  ServerBuilder& RegisterAsyncGenericService(AsyncGenericService* service);
-
-  //////////////////////////////////////////////////////////////////////////////
-  // Fine control knobs
-
-  /// Set max receive message size in bytes.
-  /// The default is GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH.
-  ServerBuilder& SetMaxReceiveMessageSize(int max_receive_message_size) {
-    max_receive_message_size_ = max_receive_message_size;
-    return *this;
-  }
-
-  /// Set max send message size in bytes.
-  /// The default is GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH.
-  ServerBuilder& SetMaxSendMessageSize(int max_send_message_size) {
-    max_send_message_size_ = max_send_message_size;
-    return *this;
-  }
-
-  /// \deprecated For backward compatibility.
-  ServerBuilder& SetMaxMessageSize(int max_message_size) {
-    return SetMaxReceiveMessageSize(max_message_size);
-  }
-
-  /// Set the support status for compression algorithms. All algorithms are
-  /// enabled by default.
-  ///
-  /// Incoming calls compressed with an unsupported algorithm will fail with
-  /// \a GRPC_STATUS_UNIMPLEMENTED.
-  ServerBuilder& SetCompressionAlgorithmSupportStatus(
-      grpc_compression_algorithm algorithm, bool enabled);
-
-  /// The default compression level to use for all channel calls in the
-  /// absence of a call-specific level.
-  ServerBuilder& SetDefaultCompressionLevel(grpc_compression_level level);
-
-  /// The default compression algorithm to use for all channel calls in the
-  /// absence of a call-specific level. Note that it overrides any compression
-  /// level set by \a SetDefaultCompressionLevel.
-  ServerBuilder& SetDefaultCompressionAlgorithm(
-      grpc_compression_algorithm algorithm);
-
-  /// Set the attached buffer pool for this server
-  ServerBuilder& SetResourceQuota(const ResourceQuota& resource_quota);
-
-  ServerBuilder& SetOption(std::unique_ptr<ServerBuilderOption> option);
-
-  /// Options for synchronous servers.
-  enum SyncServerOption {
-    NUM_CQS,         ///< Number of completion queues.
-    MIN_POLLERS,     ///< Minimum number of polling threads.
-    MAX_POLLERS,     ///< Maximum number of polling threads.
-    CQ_TIMEOUT_MSEC  ///< Completion queue timeout in milliseconds.
-  };
-
-  /// Only useful if this is a Synchronous server.
-  ServerBuilder& SetSyncServerOption(SyncServerOption option, int value);
-
-  /// Add a channel argument (an escape hatch to tuning core library parameters
-  /// directly)
-  template <class T>
-  ServerBuilder& AddChannelArgument(const grpc::string& arg, const T& value) {
-    return SetOption(MakeChannelArgumentOption(arg, value));
-  }
-
-  /// For internal use only: Register a ServerBuilderPlugin factory function.
-  static void InternalAddPluginFactory(
-      std::unique_ptr<ServerBuilderPlugin> (*CreatePlugin)());
-
-  /// Enable a server workaround. Do not use unless you know what the workaround
-  /// does. For explanation and detailed descriptions of workarounds, see
-  /// doc/workarounds.md.
-  ServerBuilder& EnableWorkaround(grpc_workaround_list id);
-
-  /// NOTE: class experimental_type is not part of the public API of this class.
-  /// TODO(yashykt): Integrate into public API when this is no longer
-  /// experimental.
-  class experimental_type {
-   public:
-    explicit experimental_type(ServerBuilder* builder) : builder_(builder) {}
-
-    void SetInterceptorCreators(
-        std::vector<
-            std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
-            interceptor_creators) {
-      builder_->interceptor_creators_ = std::move(interceptor_creators);
-    }
-
-    /// Register a generic service that uses the callback API.
-    /// Matches requests with any :authority
-    /// This is mostly useful for writing generic gRPC Proxies where the exact
-    /// serialization format is unknown
-    ServerBuilder& RegisterCallbackGenericService(
-        experimental::CallbackGenericService* service);
-
-   private:
-    ServerBuilder* builder_;
-  };
-
-  /// NOTE: The function experimental() is not stable public API. It is a view
-  /// to the experimental components of this class. It may be changed or removed
-  /// at any time.
-  experimental_type experimental() { return experimental_type(this); }
-
- protected:
-  /// Experimental, to be deprecated
-  struct Port {
-    grpc::string addr;
-    std::shared_ptr<ServerCredentials> creds;
-    int* selected_port;
-  };
-
-  /// Experimental, to be deprecated
-  typedef std::unique_ptr<grpc::string> HostString;
-  struct NamedService {
-    explicit NamedService(Service* s) : service(s) {}
-    NamedService(const grpc::string& h, Service* s)
-        : host(new grpc::string(h)), service(s) {}
-    HostString host;
-    Service* service;
-  };
-
-  /// Experimental, to be deprecated
-  std::vector<Port> ports() { return ports_; }
-
-  /// Experimental, to be deprecated
-  std::vector<NamedService*> services() {
-    std::vector<NamedService*> service_refs;
-    for (auto& ptr : services_) {
-      service_refs.push_back(ptr.get());
-    }
-    return service_refs;
-  }
-
-  /// Experimental, to be deprecated
-  std::vector<ServerBuilderOption*> options() {
-    std::vector<ServerBuilderOption*> option_refs;
-    for (auto& ptr : options_) {
-      option_refs.push_back(ptr.get());
-    }
-    return option_refs;
-  }
-
- private:
-  friend class ::grpc::testing::ServerBuilderPluginTest;
-
-  struct SyncServerSettings {
-    SyncServerSettings()
-        : num_cqs(1), min_pollers(1), max_pollers(2), cq_timeout_msec(10000) {}
-
-    /// Number of server completion queues to create to listen to incoming RPCs.
-    int num_cqs;
-
-    /// Minimum number of threads per completion queue that should be listening
-    /// to incoming RPCs.
-    int min_pollers;
-
-    /// Maximum number of threads per completion queue that can be listening to
-    /// incoming RPCs.
-    int max_pollers;
-
-    /// The timeout for server completion queue's AsyncNext call.
-    int cq_timeout_msec;
-  };
-
-  int max_receive_message_size_;
-  int max_send_message_size_;
-  std::vector<std::unique_ptr<ServerBuilderOption>> options_;
-  std::vector<std::unique_ptr<NamedService>> services_;
-  std::vector<Port> ports_;
-
-  SyncServerSettings sync_server_settings_;
-
-  /// List of completion queues added via \a AddCompletionQueue method.
-  std::vector<ServerCompletionQueue*> cqs_;
-
-  std::shared_ptr<ServerCredentials> creds_;
-  std::vector<std::unique_ptr<ServerBuilderPlugin>> plugins_;
-  grpc_resource_quota* resource_quota_;
-  AsyncGenericService* generic_service_{nullptr};
-  experimental::CallbackGenericService* callback_generic_service_{nullptr};
-  struct {
-    bool is_set;
-    grpc_compression_level level;
-  } maybe_default_compression_level_;
-  struct {
-    bool is_set;
-    grpc_compression_algorithm algorithm;
-  } maybe_default_compression_algorithm_;
-  uint32_t enabled_compression_algorithms_bitset_;
-  std::vector<std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
-      interceptor_creators_;
-};
+typedef ::grpc_impl::ServerBuilder ServerBuilder;
 
 }  // namespace grpc
 
diff --git a/include/grpcpp/server_builder_impl.h b/include/grpcpp/server_builder_impl.h
new file mode 100644 (file)
index 0000000..57f8dc0
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ *
+ * Copyright 2015-2016 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_SERVER_BUILDER_IMPL_H
+#define GRPCPP_SERVER_BUILDER_IMPL_H
+
+#include <climits>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <grpc/compression.h>
+#include <grpc/support/cpu.h>
+#include <grpc/support/workaround_list.h>
+#include <grpcpp/impl/channel_argument_option.h>
+#include <grpcpp/impl/codegen/server_interceptor.h>
+#include <grpcpp/impl/server_builder_option.h>
+#include <grpcpp/impl/server_builder_plugin.h>
+#include <grpcpp/server.h>
+#include <grpcpp/support/config.h>
+
+struct grpc_resource_quota;
+
+namespace grpc_impl {
+
+class CompletionQueue;
+class ResourceQuota;
+class Server;
+class ServerCompletionQueue;
+class ServerCredentials;
+}  // namespace grpc_impl
+namespace grpc {
+
+class AsyncGenericService;
+class Service;
+
+namespace testing {
+class ServerBuilderPluginTest;
+}  // namespace testing
+
+namespace experimental {
+class CallbackGenericService;
+}
+}  // namespace grpc
+namespace grpc_impl {
+
+/// A builder class for the creation and startup of \a grpc::Server instances.
+class ServerBuilder {
+ public:
+  ServerBuilder();
+  virtual ~ServerBuilder();
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Primary API's
+
+  /// Return a running server which is ready for processing calls.
+  /// Before calling, one typically needs to ensure that:
+  ///  1. a service is registered - so that the server knows what to serve
+  ///     (via RegisterService, or RegisterAsyncGenericService)
+  ///  2. a listening port has been added - so the server knows where to receive
+  ///     traffic (via AddListeningPort)
+  ///  3. [for async api only] completion queues have been added via
+  ///     AddCompletionQueue
+  virtual std::unique_ptr<grpc::Server> BuildAndStart();
+
+  /// Register a service. This call does not take ownership of the service.
+  /// The service must exist for the lifetime of the \a Server instance returned
+  /// by \a BuildAndStart().
+  /// Matches requests with any :authority
+  ServerBuilder& RegisterService(grpc::Service* service);
+
+  /// Enlists an endpoint \a addr (port with an optional IP address) to
+  /// bind the \a grpc::Server object to be created to.
+  ///
+  /// It can be invoked multiple times.
+  ///
+  /// \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., [::]:<port>, which also accepts IPv4
+  /// connections.  Valid values include dns:///localhost:1234, /
+  /// 192.168.1.1:31416, dns:///[::1]:27182, etc.).
+  /// \param creds The credentials associated with the server.
+  /// \param selected_port[out] If not `nullptr`, gets populated with the port
+  /// number bound to the \a grpc::Server for the corresponding endpoint after
+  /// it is successfully bound by BuildAndStart(), 0 otherwise. AddListeningPort
+  /// does not modify this pointer.
+  ServerBuilder& AddListeningPort(
+      const grpc::string& addr_uri,
+      std::shared_ptr<grpc_impl::ServerCredentials> creds,
+      int* selected_port = nullptr);
+
+  /// Add a completion queue for handling asynchronous services.
+  ///
+  /// Best performance is typically obtained by using one thread per polling
+  /// completion queue.
+  ///
+  /// Caller is required to shutdown the server prior to shutting down the
+  /// returned completion queue. Caller is also required to drain the
+  /// completion queue after shutting it down. A typical usage scenario:
+  ///
+  /// // While building the server:
+  /// ServerBuilder builder;
+  /// ...
+  /// cq_ = builder.AddCompletionQueue();
+  /// server_ = builder.BuildAndStart();
+  ///
+  /// // While shutting down the server;
+  /// server_->Shutdown();
+  /// cq_->Shutdown();  // Always *after* the associated server's Shutdown()!
+  /// // Drain the cq_ that was created
+  /// void* ignored_tag;
+  /// bool ignored_ok;
+  /// while (cq_->Next(&ignored_tag, &ignored_ok)) { }
+  ///
+  /// \param is_frequently_polled This is an optional parameter to inform gRPC
+  /// library about whether this completion queue would be frequently polled
+  /// (i.e. by calling \a Next() or \a AsyncNext()). The default value is
+  /// 'true' and is the recommended setting. Setting this to 'false' (i.e.
+  /// not polling the completion queue frequently) will have a significantly
+  /// negative performance impact and hence should not be used in production
+  /// use cases.
+  std::unique_ptr<grpc_impl::ServerCompletionQueue> AddCompletionQueue(
+      bool is_frequently_polled = true);
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Less commonly used RegisterService variants
+
+  /// Register a service. This call does not take ownership of the service.
+  /// The service must exist for the lifetime of the \a Server instance
+  /// returned by \a BuildAndStart(). Only matches requests with :authority \a
+  /// host
+  ServerBuilder& RegisterService(const grpc::string& host,
+                                 grpc::Service* service);
+
+  /// Register a generic service.
+  /// Matches requests with any :authority
+  /// This is mostly useful for writing generic gRPC Proxies where the exact
+  /// serialization format is unknown
+  ServerBuilder& RegisterAsyncGenericService(
+      grpc::AsyncGenericService* service);
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Fine control knobs
+
+  /// Set max receive message size in bytes.
+  /// The default is GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH.
+  ServerBuilder& SetMaxReceiveMessageSize(int max_receive_message_size) {
+    max_receive_message_size_ = max_receive_message_size;
+    return *this;
+  }
+
+  /// Set max send message size in bytes.
+  /// The default is GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH.
+  ServerBuilder& SetMaxSendMessageSize(int max_send_message_size) {
+    max_send_message_size_ = max_send_message_size;
+    return *this;
+  }
+
+  /// \deprecated For backward compatibility.
+  ServerBuilder& SetMaxMessageSize(int max_message_size) {
+    return SetMaxReceiveMessageSize(max_message_size);
+  }
+
+  /// Set the support status for compression algorithms. All algorithms are
+  /// enabled by default.
+  ///
+  /// Incoming calls compressed with an unsupported algorithm will fail with
+  /// \a GRPC_STATUS_UNIMPLEMENTED.
+  ServerBuilder& SetCompressionAlgorithmSupportStatus(
+      grpc_compression_algorithm algorithm, bool enabled);
+
+  /// The default compression level to use for all channel calls in the
+  /// absence of a call-specific level.
+  ServerBuilder& SetDefaultCompressionLevel(grpc_compression_level level);
+
+  /// The default compression algorithm to use for all channel calls in the
+  /// absence of a call-specific level. Note that it overrides any compression
+  /// level set by \a SetDefaultCompressionLevel.
+  ServerBuilder& SetDefaultCompressionAlgorithm(
+      grpc_compression_algorithm algorithm);
+
+  /// Set the attached buffer pool for this server
+  ServerBuilder& SetResourceQuota(
+      const grpc_impl::ResourceQuota& resource_quota);
+
+  ServerBuilder& SetOption(std::unique_ptr<grpc::ServerBuilderOption> option);
+
+  /// Options for synchronous servers.
+  enum SyncServerOption {
+    NUM_CQS,         ///< Number of completion queues.
+    MIN_POLLERS,     ///< Minimum number of polling threads.
+    MAX_POLLERS,     ///< Maximum number of polling threads.
+    CQ_TIMEOUT_MSEC  ///< Completion queue timeout in milliseconds.
+  };
+
+  /// Only useful if this is a Synchronous server.
+  ServerBuilder& SetSyncServerOption(SyncServerOption option, int value);
+
+  /// Add a channel argument (an escape hatch to tuning core library parameters
+  /// directly)
+  template <class T>
+  ServerBuilder& AddChannelArgument(const grpc::string& arg, const T& value) {
+    return SetOption(grpc::MakeChannelArgumentOption(arg, value));
+  }
+
+  /// For internal use only: Register a ServerBuilderPlugin factory function.
+  static void InternalAddPluginFactory(
+      std::unique_ptr<grpc::ServerBuilderPlugin> (*CreatePlugin)());
+
+  /// Enable a server workaround. Do not use unless you know what the workaround
+  /// does. For explanation and detailed descriptions of workarounds, see
+  /// doc/workarounds.md.
+  ServerBuilder& EnableWorkaround(grpc_workaround_list id);
+
+  /// NOTE: class experimental_type is not part of the public API of this class.
+  /// TODO(yashykt): Integrate into public API when this is no longer
+  /// experimental.
+  class experimental_type {
+   public:
+    explicit experimental_type(grpc_impl::ServerBuilder* builder)
+        : builder_(builder) {}
+
+    void SetInterceptorCreators(
+        std::vector<std::unique_ptr<
+            grpc::experimental::ServerInterceptorFactoryInterface>>
+            interceptor_creators) {
+      builder_->interceptor_creators_ = std::move(interceptor_creators);
+    }
+
+    /// Register a generic service that uses the callback API.
+    /// Matches requests with any :authority
+    /// This is mostly useful for writing generic gRPC Proxies where the exact
+    /// serialization format is unknown
+    ServerBuilder& RegisterCallbackGenericService(
+        grpc::experimental::CallbackGenericService* service);
+
+   private:
+    ServerBuilder* builder_;
+  };
+
+  /// NOTE: The function experimental() is not stable public API. It is a view
+  /// to the experimental components of this class. It may be changed or removed
+  /// at any time.
+  experimental_type experimental() { return experimental_type(this); }
+
+ protected:
+  /// Experimental, to be deprecated
+  struct Port {
+    grpc::string addr;
+    std::shared_ptr<grpc_impl::ServerCredentials> creds;
+    int* selected_port;
+  };
+
+  /// Experimental, to be deprecated
+  typedef std::unique_ptr<grpc::string> HostString;
+  struct NamedService {
+    explicit NamedService(grpc::Service* s) : service(s) {}
+    NamedService(const grpc::string& h, grpc::Service* s)
+        : host(new grpc::string(h)), service(s) {}
+    HostString host;
+    grpc::Service* service;
+  };
+
+  /// Experimental, to be deprecated
+  std::vector<Port> ports() { return ports_; }
+
+  /// Experimental, to be deprecated
+  std::vector<NamedService*> services() {
+    std::vector<NamedService*> service_refs;
+    for (auto& ptr : services_) {
+      service_refs.push_back(ptr.get());
+    }
+    return service_refs;
+  }
+
+  /// Experimental, to be deprecated
+  std::vector<grpc::ServerBuilderOption*> options() {
+    std::vector<grpc::ServerBuilderOption*> option_refs;
+    for (auto& ptr : options_) {
+      option_refs.push_back(ptr.get());
+    }
+    return option_refs;
+  }
+
+ private:
+  friend class ::grpc::testing::ServerBuilderPluginTest;
+
+  struct SyncServerSettings {
+    SyncServerSettings()
+        : num_cqs(1), min_pollers(1), max_pollers(2), cq_timeout_msec(10000) {}
+
+    /// Number of server completion queues to create to listen to incoming RPCs.
+    int num_cqs;
+
+    /// Minimum number of threads per completion queue that should be listening
+    /// to incoming RPCs.
+    int min_pollers;
+
+    /// Maximum number of threads per completion queue that can be listening to
+    /// incoming RPCs.
+    int max_pollers;
+
+    /// The timeout for server completion queue's AsyncNext call.
+    int cq_timeout_msec;
+  };
+
+  int max_receive_message_size_;
+  int max_send_message_size_;
+  std::vector<std::unique_ptr<grpc::ServerBuilderOption>> options_;
+  std::vector<std::unique_ptr<NamedService>> services_;
+  std::vector<Port> ports_;
+
+  SyncServerSettings sync_server_settings_;
+
+  /// List of completion queues added via \a AddCompletionQueue method.
+  std::vector<grpc_impl::ServerCompletionQueue*> cqs_;
+
+  std::shared_ptr<grpc_impl::ServerCredentials> creds_;
+  std::vector<std::unique_ptr<grpc::ServerBuilderPlugin>> plugins_;
+  grpc_resource_quota* resource_quota_;
+  grpc::AsyncGenericService* generic_service_{nullptr};
+  grpc::experimental::CallbackGenericService* callback_generic_service_{
+      nullptr};
+  struct {
+    bool is_set;
+    grpc_compression_level level;
+  } maybe_default_compression_level_;
+  struct {
+    bool is_set;
+    grpc_compression_algorithm algorithm;
+  } maybe_default_compression_algorithm_;
+  uint32_t enabled_compression_algorithms_bitset_;
+  std::vector<
+      std::unique_ptr<grpc::experimental::ServerInterceptorFactoryInterface>>
+      interceptor_creators_;
+};
+
+}  // namespace grpc_impl
+
+#endif  // GRPCPP_SERVER_BUILDER_IMPL_H
diff --git a/include/grpcpp/server_impl.h b/include/grpcpp/server_impl.h
new file mode 100644 (file)
index 0000000..2a53265
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ *
+ * 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_SERVER_IMPL_H
+#define GRPCPP_SERVER_IMPL_H
+
+#include <condition_variable>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include <grpc/compression.h>
+#include <grpc/support/atm.h>
+#include <grpcpp/channel.h>
+#include <grpcpp/completion_queue.h>
+#include <grpcpp/health_check_service_interface.h>
+#include <grpcpp/impl/call.h>
+#include <grpcpp/impl/codegen/client_interceptor.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+#include <grpcpp/impl/codegen/server_interface.h>
+#include <grpcpp/impl/rpc_service_method.h>
+#include <grpcpp/security/server_credentials.h>
+#include <grpcpp/support/channel_arguments.h>
+#include <grpcpp/support/config.h>
+#include <grpcpp/support/status.h>
+
+struct grpc_server;
+
+namespace grpc {
+
+class AsyncGenericService;
+class ServerContext;
+
+}  // namespace grpc
+
+namespace grpc_impl {
+
+class ServerInitializer;
+
+/// Represents a gRPC server.
+///
+/// Use a \a grpc::ServerBuilder to create, configure, and start
+/// \a Server instances.
+class Server : public grpc::ServerInterface, private grpc::GrpcLibraryCodegen {
+ public:
+  ~Server();
+
+  /// Block until the server shuts down.
+  ///
+  /// \warning The server must be either shutting down or some other thread must
+  /// call \a Shutdown for this function to ever return.
+  void Wait() override;
+
+  /// Global callbacks are a set of hooks that are called when server
+  /// events occur.  \a SetGlobalCallbacks method is used to register
+  /// the hooks with gRPC.  Note that
+  /// the \a GlobalCallbacks instance will be shared among all
+  /// \a Server instances in an application and can be set exactly
+  /// once per application.
+  class GlobalCallbacks {
+   public:
+    virtual ~GlobalCallbacks() {}
+    /// Called before server is created.
+    virtual void UpdateArguments(grpc::ChannelArguments* args) {}
+    /// Called before application callback for each synchronous server request
+    virtual void PreSynchronousRequest(grpc::ServerContext* context) = 0;
+    /// Called after application callback for each synchronous server request
+    virtual void PostSynchronousRequest(grpc::ServerContext* context) = 0;
+    /// Called before server is started.
+    virtual void PreServerStart(Server* server) {}
+    /// Called after a server port is added.
+    virtual void AddPort(Server* server, const grpc::string& addr,
+                         grpc::ServerCredentials* creds, int port) {}
+  };
+  /// Set the global callback object. Can only be called once per application.
+  /// Does not take ownership of callbacks, and expects the pointed to object
+  /// to be alive until all server objects in the process have been destroyed.
+  /// The same \a GlobalCallbacks object will be used throughout the
+  /// application and is shared among all \a Server objects.
+  static void SetGlobalCallbacks(GlobalCallbacks* callbacks);
+
+  /// Returns a \em raw pointer to the underlying \a grpc_server instance.
+  /// EXPERIMENTAL:  for internal/test use only
+  grpc_server* c_server();
+
+  /// Returns the health check service.
+  grpc::HealthCheckServiceInterface* GetHealthCheckService() const {
+    return health_check_service_.get();
+  }
+
+  /// Establish a channel for in-process communication
+  std::shared_ptr<::grpc::Channel> InProcessChannel(
+      const grpc::ChannelArguments& args);
+
+  /// NOTE: class experimental_type is not part of the public API of this class.
+  /// TODO(yashykt): Integrate into public API when this is no longer
+  /// experimental.
+  class experimental_type {
+   public:
+    explicit experimental_type(Server* server) : server_(server) {}
+
+    /// Establish a channel for in-process communication with client
+    /// interceptors
+    std::shared_ptr<::grpc::Channel> InProcessChannelWithInterceptors(
+        const grpc::ChannelArguments& args,
+        std::vector<std::unique_ptr<
+            grpc::experimental::ClientInterceptorFactoryInterface>>
+            interceptor_creators);
+
+   private:
+    Server* server_;
+  };
+
+  /// NOTE: The function experimental() is not stable public API. It is a view
+  /// to the experimental components of this class. It may be changed or removed
+  /// at any time.
+  experimental_type experimental() { return experimental_type(this); }
+
+ protected:
+  /// Register a service. This call does not take ownership of the service.
+  /// The service must exist for the lifetime of the Server instance.
+  bool RegisterService(const grpc::string* host,
+                       grpc::Service* service) override;
+
+  /// Try binding the server to the given \a addr endpoint
+  /// (port, and optionally including IP address to bind to).
+  ///
+  /// It can be invoked multiple times. Should be used before
+  /// starting the server.
+  ///
+  /// \param addr The address to try to bind to the server (eg, localhost:1234,
+  /// 192.168.1.1:31416, [::1]:27182, etc.).
+  /// \param creds The credentials associated with the server.
+  ///
+  /// \return bound port number on success, 0 on failure.
+  ///
+  /// \warning It is an error to call this method on an already started server.
+  int AddListeningPort(const grpc::string& addr,
+                       grpc::ServerCredentials* creds) override;
+
+  /// NOTE: This is *NOT* a public API. The server constructors are supposed to
+  /// be used by \a ServerBuilder class only. The constructor will be made
+  /// 'private' very soon.
+  ///
+  /// Server constructors. To be used by \a ServerBuilder only.
+  ///
+  /// \param max_message_size Maximum message length that the channel can
+  /// receive.
+  ///
+  /// \param args The channel args
+  ///
+  /// \param sync_server_cqs The completion queues to use if the server is a
+  /// synchronous server (or a hybrid server). The server polls for new RPCs on
+  /// these queues
+  ///
+  /// \param min_pollers The minimum number of polling threads per server
+  /// completion queue (in param sync_server_cqs) to use for listening to
+  /// incoming requests (used only in case of sync server)
+  ///
+  /// \param max_pollers The maximum number of polling threads per server
+  /// completion queue (in param sync_server_cqs) to use for listening to
+  /// incoming requests (used only in case of sync server)
+  ///
+  /// \param sync_cq_timeout_msec The timeout to use when calling AsyncNext() on
+  /// server completion queues passed via sync_server_cqs param.
+  Server(
+      int max_message_size, grpc::ChannelArguments* args,
+      std::shared_ptr<std::vector<std::unique_ptr<grpc::ServerCompletionQueue>>>
+          sync_server_cqs,
+      int min_pollers, int max_pollers, int sync_cq_timeout_msec,
+      grpc_resource_quota* server_rq = nullptr,
+      std::vector<std::unique_ptr<
+          grpc::experimental::ServerInterceptorFactoryInterface>>
+          interceptor_creators = std::vector<std::unique_ptr<
+              grpc::experimental::ServerInterceptorFactoryInterface>>());
+
+  /// Start the server.
+  ///
+  /// \param cqs Completion queues for handling asynchronous services. The
+  /// caller is required to keep all completion queues live until the server is
+  /// destroyed.
+  /// \param num_cqs How many completion queues does \a cqs hold.
+  void Start(grpc::ServerCompletionQueue** cqs, size_t num_cqs) override;
+
+  grpc_server* server() override { return server_; }
+
+ private:
+  std::vector<
+      std::unique_ptr<grpc::experimental::ServerInterceptorFactoryInterface>>*
+  interceptor_creators() override {
+    return &interceptor_creators_;
+  }
+
+  friend class grpc::AsyncGenericService;
+  friend class grpc_impl::ServerBuilder;
+  friend class grpc_impl::ServerInitializer;
+
+  class SyncRequest;
+  class CallbackRequestBase;
+  template <class ServerContextType>
+  class CallbackRequest;
+  class UnimplementedAsyncRequest;
+  class UnimplementedAsyncResponse;
+
+  /// SyncRequestThreadManager is an implementation of ThreadManager. This class
+  /// is responsible for polling for incoming RPCs and calling the RPC handlers.
+  /// This is only used in case of a Sync server (i.e a server exposing a sync
+  /// interface)
+  class SyncRequestThreadManager;
+
+  /// Register a generic service. This call does not take ownership of the
+  /// service. The service must exist for the lifetime of the Server instance.
+  void RegisterAsyncGenericService(grpc::AsyncGenericService* service) override;
+
+  /// NOTE: class experimental_registration_type is not part of the public API
+  /// of this class
+  /// TODO(vjpai): Move these contents to the public API of Server when
+  ///              they are no longer experimental
+  class experimental_registration_type final
+      : public experimental_registration_interface {
+   public:
+    explicit experimental_registration_type(Server* server) : server_(server) {}
+    void RegisterCallbackGenericService(
+        grpc::experimental::CallbackGenericService* service) override {
+      server_->RegisterCallbackGenericService(service);
+    }
+
+   private:
+    Server* server_;
+  };
+
+  /// TODO(vjpai): Mark this override when experimental type above is deleted
+  void RegisterCallbackGenericService(
+      grpc::experimental::CallbackGenericService* service);
+
+  /// NOTE: The function experimental_registration() is not stable public API.
+  /// It is a view to the experimental components of this class. It may be
+  /// changed or removed at any time.
+  experimental_registration_interface* experimental_registration() override {
+    return &experimental_registration_;
+  }
+
+  void PerformOpsOnCall(grpc::internal::CallOpSetInterface* ops,
+                        grpc::internal::Call* call) override;
+
+  void ShutdownInternal(gpr_timespec deadline) override;
+
+  int max_receive_message_size() const override {
+    return max_receive_message_size_;
+  }
+
+  grpc::CompletionQueue* CallbackCQ() override;
+
+  grpc_impl::ServerInitializer* initializer();
+
+  // A vector of interceptor factory objects.
+  // This should be destroyed after health_check_service_ and this requirement
+  // is satisfied by declaring interceptor_creators_ before
+  // health_check_service_. (C++ mandates that member objects be destroyed in
+  // the reverse order of initialization.)
+  std::vector<
+      std::unique_ptr<grpc::experimental::ServerInterceptorFactoryInterface>>
+      interceptor_creators_;
+
+  const int max_receive_message_size_;
+
+  /// The following completion queues are ONLY used in case of Sync API
+  /// i.e. if the server has any services with sync methods. The server uses
+  /// these completion queues to poll for new RPCs
+  std::shared_ptr<std::vector<std::unique_ptr<grpc::ServerCompletionQueue>>>
+      sync_server_cqs_;
+
+  /// List of \a ThreadManager instances (one for each cq in
+  /// the \a sync_server_cqs)
+  std::vector<std::unique_ptr<SyncRequestThreadManager>> sync_req_mgrs_;
+
+  // Outstanding unmatched callback requests, indexed by method.
+  // NOTE: Using a gpr_atm rather than atomic_int because atomic_int isn't
+  //       copyable or movable and thus will cause compilation errors. We
+  //       actually only want to extend the vector before the threaded use
+  //       starts, but this is still a limitation.
+  std::vector<gpr_atm> callback_unmatched_reqs_count_;
+
+  // List of callback requests to start when server actually starts.
+  std::list<CallbackRequestBase*> callback_reqs_to_start_;
+
+  // For registering experimental callback generic service; remove when that
+  // method longer experimental
+  experimental_registration_type experimental_registration_{this};
+
+  // Server status
+  grpc::internal::Mutex mu_;
+  bool started_;
+  bool shutdown_;
+  bool shutdown_notified_;  // Was notify called on the shutdown_cv_
+
+  grpc::internal::CondVar shutdown_cv_;
+
+  // It is ok (but not required) to nest callback_reqs_mu_ under mu_ .
+  // Incrementing callback_reqs_outstanding_ is ok without a lock but it must be
+  // decremented under the lock in case it is the last request and enables the
+  // server shutdown. The increment is performance-critical since it happens
+  // during periods of increasing load; the decrement happens only when memory
+  // is maxed out, during server shutdown, or (possibly in a future version)
+  // during decreasing load, so it is less performance-critical.
+  grpc::internal::Mutex callback_reqs_mu_;
+  grpc::internal::CondVar callback_reqs_done_cv_;
+  std::atomic_int callback_reqs_outstanding_{0};
+
+  std::shared_ptr<GlobalCallbacks> global_callbacks_;
+
+  std::vector<grpc::string> services_;
+  bool has_async_generic_service_{false};
+  bool has_callback_generic_service_{false};
+
+  // Pointer to the wrapped grpc_server.
+  grpc_server* server_;
+
+  std::unique_ptr<grpc_impl::ServerInitializer> server_initializer_;
+
+  std::unique_ptr<grpc::HealthCheckServiceInterface> health_check_service_;
+  bool health_check_service_disabled_;
+
+  // When appropriate, use a default callback generic service to handle
+  // unimplemented methods
+  std::unique_ptr<grpc::experimental::CallbackGenericService>
+      unimplemented_service_;
+
+  // A special handler for resource exhausted in sync case
+  std::unique_ptr<grpc::internal::MethodHandler> resource_exhausted_handler_;
+
+  // Handler for callback generic service, if any
+  std::unique_ptr<grpc::internal::MethodHandler> generic_handler_;
+
+  // callback_cq_ references the callbackable completion queue associated
+  // with this server (if any). It is set on the first call to CallbackCQ().
+  // It is _not owned_ by the server; ownership belongs with its internal
+  // shutdown callback tag (invoked when the CQ is fully shutdown).
+  // It is protected by mu_
+  grpc::CompletionQueue* callback_cq_ = nullptr;
+};
+
+}  // namespace grpc_impl
+
+#endif  // GRPCPP_SERVER_IMPL_H
index 217929d..593aaec 100644 (file)
 #ifndef GRPCPP_SUPPORT_CHANNEL_ARGUMENTS_H
 #define GRPCPP_SUPPORT_CHANNEL_ARGUMENTS_H
 
-#include <list>
-#include <vector>
+#include <grpcpp/support/channel_arguments_impl.h>
 
-#include <grpc/compression.h>
-#include <grpc/grpc.h>
-#include <grpcpp/support/config.h>
-
-namespace grpc {
-namespace testing {
-class ChannelArgumentsTest;
-}  // namespace testing
+namespace grpc_impl {
 
+class SecureChannelCredentials;
 class ResourceQuota;
+}  // namespace grpc_impl
 
-/// Options for channel creation. The user can use generic setters to pass
-/// key value pairs down to C channel creation code. For gRPC related options,
-/// concrete setters are provided.
-class ChannelArguments {
- public:
-  ChannelArguments();
-  ~ChannelArguments();
-
-  ChannelArguments(const ChannelArguments& other);
-  ChannelArguments& operator=(ChannelArguments other) {
-    Swap(other);
-    return *this;
-  }
-
-  void Swap(ChannelArguments& other);
-
-  /// Dump arguments in this instance to \a channel_args. Does not take
-  /// ownership of \a channel_args.
-  ///
-  /// Note that the underlying arguments are shared. Changes made to either \a
-  /// channel_args or this instance would be reflected on both.
-  void SetChannelArgs(grpc_channel_args* channel_args) const;
-
-  // gRPC specific channel argument setters
-  /// Set target name override for SSL host name checking. This option is for
-  /// testing only and should never be used in production.
-  void SetSslTargetNameOverride(const grpc::string& name);
-  // TODO(yangg) add flow control options
-  /// Set the compression algorithm for the channel.
-  void SetCompressionAlgorithm(grpc_compression_algorithm algorithm);
-
-  /// Set the grpclb fallback timeout (in ms) for the channel. If this amount
-  /// of time has passed but we have not gotten any non-empty \a serverlist from
-  /// the balancer, we will fall back to use the backend address(es) returned by
-  /// the resolver.
-  void SetGrpclbFallbackTimeout(int fallback_timeout);
-
-  /// For client channel's, the socket mutator operates on
-  /// "channel" sockets. For server's, the socket mutator operates
-  /// only on "listen" sockets.
-  /// TODO(apolcyn): allow socket mutators to also operate
-  /// on server "channel" sockets, and adjust the socket mutator
-  /// object to be more speficic about which type of socket
-  /// it should operate on.
-  void SetSocketMutator(grpc_socket_mutator* mutator);
-
-  /// Set the string to prepend to the user agent.
-  void SetUserAgentPrefix(const grpc::string& user_agent_prefix);
-
-  /// Set the buffer pool to be attached to the constructed channel.
-  void SetResourceQuota(const ResourceQuota& resource_quota);
-
-  /// Set the max receive and send message sizes.
-  void SetMaxReceiveMessageSize(int size);
-  void SetMaxSendMessageSize(int size);
-
-  /// Set LB policy name.
-  /// Note that if the name resolver returns only balancer addresses, the
-  /// grpclb LB policy will be used, regardless of what is specified here.
-  void SetLoadBalancingPolicyName(const grpc::string& lb_policy_name);
-
-  /// Set service config in JSON form.
-  /// Primarily meant for use in unit tests.
-  void SetServiceConfigJSON(const grpc::string& service_config_json);
-
-  // Generic channel argument setters. Only for advanced use cases.
-  /// Set an integer argument \a value under \a key.
-  void SetInt(const grpc::string& key, int value);
-
-  // Generic channel argument setter. Only for advanced use cases.
-  /// Set a pointer argument \a value under \a key. Owership is not transferred.
-  void SetPointer(const grpc::string& key, void* value);
-
-  void SetPointerWithVtable(const grpc::string& key, void* value,
-                            const grpc_arg_pointer_vtable* vtable);
-
-  /// Set a textual argument \a value under \a key.
-  void SetString(const grpc::string& key, const grpc::string& value);
-
-  /// Return (by value) a C \a grpc_channel_args structure which points to
-  /// arguments owned by this \a ChannelArguments instance
-  grpc_channel_args c_channel_args() const {
-    grpc_channel_args out;
-    out.num_args = args_.size();
-    out.args = args_.empty() ? NULL : const_cast<grpc_arg*>(&args_[0]);
-    return out;
-  }
-
- private:
-  friend class SecureChannelCredentials;
-  friend class testing::ChannelArgumentsTest;
-
-  /// Default pointer argument operations.
-  struct PointerVtableMembers {
-    static void* Copy(void* in) { return in; }
-    static void Destroy(void* in) {}
-    static int Compare(void* a, void* b) {
-      if (a < b) return -1;
-      if (a > b) return 1;
-      return 0;
-    }
-  };
-
-  // Returns empty string when it is not set.
-  grpc::string GetSslTargetNameOverride() const;
+namespace grpc {
 
-  std::vector<grpc_arg> args_;
-  std::list<grpc::string> strings_;
-};
+typedef ::grpc_impl::ChannelArguments ChannelArguments;
 
 }  // namespace grpc
 
diff --git a/include/grpcpp/support/channel_arguments_impl.h b/include/grpcpp/support/channel_arguments_impl.h
new file mode 100644 (file)
index 0000000..0efeadc
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ *
+ * 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_SUPPORT_CHANNEL_ARGUMENTS_IMPL_H
+#define GRPCPP_SUPPORT_CHANNEL_ARGUMENTS_IMPL_H
+
+#include <list>
+#include <vector>
+
+#include <grpc/compression.h>
+#include <grpc/grpc.h>
+#include <grpcpp/resource_quota.h>
+#include <grpcpp/support/config.h>
+
+namespace grpc {
+namespace testing {
+class ChannelArgumentsTest;
+}  // namespace testing
+}  // namespace grpc
+
+namespace grpc_impl {
+
+class SecureChannelCredentials;
+
+/// Options for channel creation. The user can use generic setters to pass
+/// key value pairs down to C channel creation code. For gRPC related options,
+/// concrete setters are provided.
+class ChannelArguments {
+ public:
+  ChannelArguments();
+  ~ChannelArguments();
+
+  ChannelArguments(const ChannelArguments& other);
+  ChannelArguments& operator=(ChannelArguments other) {
+    Swap(other);
+    return *this;
+  }
+
+  void Swap(ChannelArguments& other);
+
+  /// Dump arguments in this instance to \a channel_args. Does not take
+  /// ownership of \a channel_args.
+  ///
+  /// Note that the underlying arguments are shared. Changes made to either \a
+  /// channel_args or this instance would be reflected on both.
+  void SetChannelArgs(grpc_channel_args* channel_args) const;
+
+  // gRPC specific channel argument setters
+  /// Set target name override for SSL host name checking. This option is for
+  /// testing only and should never be used in production.
+  void SetSslTargetNameOverride(const grpc::string& name);
+  // TODO(yangg) add flow control options
+  /// Set the compression algorithm for the channel.
+  void SetCompressionAlgorithm(grpc_compression_algorithm algorithm);
+
+  /// Set the grpclb fallback timeout (in ms) for the channel. If this amount
+  /// of time has passed but we have not gotten any non-empty \a serverlist from
+  /// the balancer, we will fall back to use the backend address(es) returned by
+  /// the resolver.
+  void SetGrpclbFallbackTimeout(int fallback_timeout);
+
+  /// For client channel's, the socket mutator operates on
+  /// "channel" sockets. For server's, the socket mutator operates
+  /// only on "listen" sockets.
+  /// TODO(apolcyn): allow socket mutators to also operate
+  /// on server "channel" sockets, and adjust the socket mutator
+  /// object to be more speficic about which type of socket
+  /// it should operate on.
+  void SetSocketMutator(grpc_socket_mutator* mutator);
+
+  /// Set the string to prepend to the user agent.
+  void SetUserAgentPrefix(const grpc::string& user_agent_prefix);
+
+  /// Set the buffer pool to be attached to the constructed channel.
+  void SetResourceQuota(const grpc::ResourceQuota& resource_quota);
+
+  /// Set the max receive and send message sizes.
+  void SetMaxReceiveMessageSize(int size);
+  void SetMaxSendMessageSize(int size);
+
+  /// Set LB policy name.
+  /// Note that if the name resolver returns only balancer addresses, the
+  /// grpclb LB policy will be used, regardless of what is specified here.
+  void SetLoadBalancingPolicyName(const grpc::string& lb_policy_name);
+
+  /// Set service config in JSON form.
+  /// Primarily meant for use in unit tests.
+  void SetServiceConfigJSON(const grpc::string& service_config_json);
+
+  // Generic channel argument setters. Only for advanced use cases.
+  /// Set an integer argument \a value under \a key.
+  void SetInt(const grpc::string& key, int value);
+
+  // Generic channel argument setter. Only for advanced use cases.
+  /// Set a pointer argument \a value under \a key. Owership is not transferred.
+  void SetPointer(const grpc::string& key, void* value);
+
+  void SetPointerWithVtable(const grpc::string& key, void* value,
+                            const grpc_arg_pointer_vtable* vtable);
+
+  /// Set a textual argument \a value under \a key.
+  void SetString(const grpc::string& key, const grpc::string& value);
+
+  /// Return (by value) a C \a grpc_channel_args structure which points to
+  /// arguments owned by this \a ChannelArguments instance
+  grpc_channel_args c_channel_args() const {
+    grpc_channel_args out;
+    out.num_args = args_.size();
+    out.args = args_.empty() ? NULL : const_cast<grpc_arg*>(&args_[0]);
+    return out;
+  }
+
+ private:
+  friend class grpc_impl::SecureChannelCredentials;
+  friend class grpc::testing::ChannelArgumentsTest;
+
+  /// Default pointer argument operations.
+  struct PointerVtableMembers {
+    static void* Copy(void* in) { return in; }
+    static void Destroy(void* in) {}
+    static int Compare(void* a, void* b) {
+      if (a < b) return -1;
+      if (a > b) return 1;
+      return 0;
+    }
+  };
+
+  // Returns empty string when it is not set.
+  grpc::string GetSslTargetNameOverride() const;
+
+  std::vector<grpc_arg> args_;
+  std::list<grpc::string> strings_;
+};
+
+}  // namespace grpc_impl
+
+#endif  // GRPCPP_SUPPORT_CHANNEL_ARGUMENTS_IMPL_H
index 84931d9..07bc750 100644 (file)
@@ -19,7 +19,7 @@
 #ifndef GRPCPP_SUPPORT_ERROR_DETAILS_H
 #define GRPCPP_SUPPORT_ERROR_DETAILS_H
 
-#include <grpcpp/support/status.h>
+#include <grpcpp/support/error_details_impl.h>
 
 namespace google {
 namespace rpc {
@@ -29,17 +29,15 @@ class Status;
 
 namespace grpc {
 
-/// Map a \a grpc::Status to a \a google::rpc::Status.
-/// The given \a to object will be cleared.
-/// On success, returns status with OK.
-/// Returns status with \a INVALID_ARGUMENT, if failed to deserialize.
-/// Returns status with \a FAILED_PRECONDITION, if \a to is nullptr.
-Status ExtractErrorDetails(const Status& from, ::google::rpc::Status* to);
-
-/// Map \a google::rpc::Status to a \a grpc::Status.
-/// Returns OK on success.
-/// Returns status with \a FAILED_PRECONDITION if \a to is nullptr.
-Status SetErrorDetails(const ::google::rpc::Status& from, Status* to);
+static inline Status ExtractErrorDetails(const Status& from,
+                                         ::google::rpc::Status* to) {
+  return ::grpc_impl::ExtractErrorDetails(from, to);
+}
+
+static inline Status SetErrorDetails(const ::google::rpc::Status& from,
+                                     Status* to) {
+  return ::grpc_impl::SetErrorDetails(from, to);
+}
 
 }  // namespace grpc
 
diff --git a/include/grpcpp/support/error_details_impl.h b/include/grpcpp/support/error_details_impl.h
new file mode 100644 (file)
index 0000000..ae5f04c
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright 2017 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_ERROR_DETAILS_IMPL_H
+#define GRPCPP_SUPPORT_ERROR_DETAILS_IMPL_H
+
+#include <grpcpp/support/status.h>
+
+namespace google {
+namespace rpc {
+class Status;
+}  // namespace rpc
+}  // namespace google
+
+namespace grpc_impl {
+
+/// Map a \a grpc::Status to a \a google::rpc::Status.
+/// The given \a to object will be cleared.
+/// On success, returns status with OK.
+/// Returns status with \a INVALID_ARGUMENT, if failed to deserialize.
+/// Returns status with \a FAILED_PRECONDITION, if \a to is nullptr.
+grpc::Status ExtractErrorDetails(const grpc::Status& from,
+                                 ::google::rpc::Status* to);
+
+/// Map \a google::rpc::Status to a \a grpc::Status.
+/// Returns OK on success.
+/// Returns status with \a FAILED_PRECONDITION if \a to is nullptr.
+grpc::Status SetErrorDetails(const ::google::rpc::Status& from,
+                             grpc::Status* to);
+
+}  // namespace grpc_impl
+
+#endif  // GRPCPP_SUPPORT_ERROR_DETAILS_IMPL_H
diff --git a/include/grpcpp/support/message_allocator.h b/include/grpcpp/support/message_allocator.h
new file mode 100644 (file)
index 0000000..20ce072
--- /dev/null
@@ -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_MESSAGE_ALLOCATOR_H
+#define GRPCPP_SUPPORT_MESSAGE_ALLOCATOR_H
+
+#include <grpcpp/impl/codegen/message_allocator.h>
+
+#endif  // GRPCPP_SUPPORT_MESSAGE_ALLOCATOR_H
index 9167164..96af492 100644 (file)
@@ -13,8 +13,8 @@
  <date>2018-01-19</date>
  <time>16:06:07</time>
  <version>
-  <release>1.20.1</release>
-  <api>1.20.1</api>
+  <release>1.21.0</release>
+  <api>1.21.0</api>
  </version>
  <stability>
   <release>stable</release>
     <file baseinstalldir="/" name="src/core/lib/gpr/tmpfile.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/useful.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gprpp/abstract.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/gprpp/arena.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gprpp/atomic.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gprpp/fork.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/gprpp/global_config.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/gprpp/global_config_custom.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/gprpp/global_config_env.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/gprpp/global_config_generic.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gprpp/manual_constructor.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/gprpp/map.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gprpp/memory.h" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/gprpp/mutex_lock.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/gprpp/pair.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/gprpp/sync.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gprpp/thd.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/profiling/timers.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/alloc.cc" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/gpr/arena.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/atm.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/cpu_iphone.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/cpu_linux.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/tmpfile_posix.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/tmpfile_windows.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/wrap_memcpy.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/gprpp/arena.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gprpp/fork.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/gprpp/global_config_env.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gprpp/thd_posix.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gprpp/thd_windows.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/profiling/basic_timers.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/handshaker_registry.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/status_util.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/algorithm_metadata.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/compression/compression_args.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/compression_internal.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/message_compress.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/stream_compression.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/block_annotate.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/buffer_list.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/call_combiner.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/cfstream_handle.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/closure.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/combiner.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/dynamic_annotations.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_cfstream.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_pair.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/error.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/error_cfstream.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/error_internal.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/ev_epoll1_linux.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/ev_epollex_linux.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/subchannel_list.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/max_age/max_age_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/message_size/message_size_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/http/client_authority_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/handshaker_registry.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/status_util.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/compression.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/compression/compression_args.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/compression_internal.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/message_compress.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/stream_compression.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/http/parser.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/buffer_list.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/call_combiner.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/cfstream_handle.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/combiner.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_cfstream.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_pair_posix.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_pair_uv.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_pair_windows.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/error.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/error_cfstream.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/ev_epoll1_linux.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/ev_epollex_linux.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/ev_poll_posix.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/iomgr_custom.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/iomgr_internal.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/iomgr_posix.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/iomgr_posix_cfstream.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/iomgr_uv.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/iomgr_windows.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/is_epollexclusive_available.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/socket_utils_windows.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/socket_windows.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/tcp_client.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/tcp_client_cfstream.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/tcp_client_custom.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/tcp_client_posix.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/tcp_client_windows.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/census/grpc_context.cc" role="src" />
index 0783425..b507552 100644 (file)
@@ -18,8 +18,8 @@
 
 #include <grpcpp/grpcpp.h>
 #include <jni.h>
-#include <src/core/lib/gpr/env.h>
 
+#include "src/core/lib/security/security_connector/ssl_utils.h"
 #include "test/cpp/interop/interop_client.h"
 
 extern "C" JNIEXPORT void JNICALL
@@ -28,7 +28,7 @@ Java_io_grpc_interop_cpp_InteropActivity_configureSslRoots(JNIEnv* env,
                                                            jstring path_raw) {
   const char* path = env->GetStringUTFChars(path_raw, (jboolean*)0);
 
-  gpr_setenv("GRPC_DEFAULT_SSL_ROOTS_FILE_PATH", path);
+  GPR_GLOBAL_CONFIG_SET(grpc_default_ssl_roots_file_path, path);
 }
 
 std::shared_ptr<grpc::testing::InteropClient> GetClient(const char* host,
@@ -45,7 +45,7 @@ std::shared_ptr<grpc::testing::InteropClient> GetClient(const char* host,
     credentials = grpc::InsecureChannelCredentials();
   }
 
-  grpc::testing::ChannelCreationFunc channel_creation_func = 
+  grpc::testing::ChannelCreationFunc channel_creation_func =
       std::bind(grpc::CreateChannel, host_port, credentials);
   return std::shared_ptr<grpc::testing::InteropClient>(
       new grpc::testing::InteropClient(channel_creation_func, true, false));
index 96e9ab8..77cd7b5 100644 (file)
@@ -84,7 +84,7 @@ void PrintIncludes(grpc_generator::Printer* printer,
 }
 
 grpc::string GetHeaderPrologue(grpc_generator::File* file,
-                               const Parameters& /*params*/) {
+                               const Parameters& params) {
   grpc::string output;
   {
     // Scope the output stream so it closes and finalizes output to the string.
@@ -94,7 +94,9 @@ grpc::string GetHeaderPrologue(grpc_generator::File* file,
     vars["filename"] = file->filename();
     vars["filename_identifier"] = FilenameIdentifier(file->filename());
     vars["filename_base"] = file->filename_without_ext();
-    vars["message_header_ext"] = kCppGeneratorMessageHeaderExt;
+    vars["message_header_ext"] = params.message_header_extension.empty()
+                                     ? kCppGeneratorMessageHeaderExt
+                                     : params.message_header_extension;
 
     printer->Print(vars, "// Generated by the gRPC C++ plugin.\n");
     printer->Print(vars,
@@ -115,6 +117,13 @@ grpc::string GetHeaderPrologue(grpc_generator::File* file,
   return output;
 }
 
+// Convert from "a/b/c.proto" to "#include \"a/b/c$message_header_ext$\"\n"
+grpc::string ImportInludeFromProtoName(const grpc::string& proto_name) {
+  return grpc::string("#include \"") +
+         proto_name.substr(0, proto_name.size() - 6) +
+         grpc::string("$message_header_ext$\"\n");
+}
+
 grpc::string GetHeaderIncludes(grpc_generator::File* file,
                                const Parameters& params) {
   grpc::string output;
@@ -145,13 +154,35 @@ grpc::string GetHeaderIncludes(grpc_generator::File* file,
     PrintIncludes(printer.get(), headers, params.use_system_headers,
                   params.grpc_search_path);
     printer->Print(vars, "\n");
-    printer->Print(vars, "namespace grpc {\n");
-    printer->Print(vars, "class CompletionQueue;\n");
+    printer->Print(vars, "namespace grpc_impl {\n");
     printer->Print(vars, "class Channel;\n");
+    printer->Print(vars, "class CompletionQueue;\n");
     printer->Print(vars, "class ServerCompletionQueue;\n");
+    printer->Print(vars, "}  // namespace grpc_impl\n\n");
+    printer->Print(vars, "namespace grpc {\n");
+    printer->Print(vars, "namespace experimental {\n");
+    printer->Print(vars, "template <typename RequestT, typename ResponseT>\n");
+    printer->Print(vars, "class MessageAllocator;\n");
+    printer->Print(vars, "}  // namespace experimental\n");
+    printer->Print(vars, "}  // namespace grpc_impl\n\n");
+    printer->Print(vars, "namespace grpc {\n");
     printer->Print(vars, "class ServerContext;\n");
     printer->Print(vars, "}  // namespace grpc\n\n");
 
+    vars["message_header_ext"] = params.message_header_extension.empty()
+                                     ? kCppGeneratorMessageHeaderExt
+                                     : params.message_header_extension;
+
+    if (params.include_import_headers) {
+      const std::vector<grpc::string> import_names = file->GetImportNames();
+      for (const auto& import_name : import_names) {
+        const grpc::string include_name =
+            ImportInludeFromProtoName(import_name);
+        printer->Print(vars, include_name.c_str());
+      }
+      printer->PrintRaw("\n");
+    }
+
     if (!file->package().empty()) {
       std::vector<grpc::string> parts = file->package_parts();
 
@@ -584,6 +615,14 @@ void PrintHeaderClientMethodCallbackInterfaces(
                    "virtual void $Method$(::grpc::ClientContext* context, "
                    "const ::grpc::ByteBuffer* request, $Response$* response, "
                    "std::function<void(::grpc::Status)>) = 0;\n");
+    printer->Print(*vars,
+                   "virtual void $Method$(::grpc::ClientContext* context, "
+                   "const $Request$* request, $Response$* response, "
+                   "::grpc::experimental::ClientUnaryReactor* reactor) = 0;\n");
+    printer->Print(*vars,
+                   "virtual void $Method$(::grpc::ClientContext* context, "
+                   "const ::grpc::ByteBuffer* request, $Response$* response, "
+                   "::grpc::experimental::ClientUnaryReactor* reactor) = 0;\n");
   } else if (ClientOnlyStreaming(method)) {
     printer->Print(*vars,
                    "virtual void $Method$(::grpc::ClientContext* context, "
@@ -650,6 +689,16 @@ void PrintHeaderClientMethodCallback(grpc_generator::Printer* printer,
                    "void $Method$(::grpc::ClientContext* context, "
                    "const ::grpc::ByteBuffer* request, $Response$* response, "
                    "std::function<void(::grpc::Status)>) override;\n");
+    printer->Print(
+        *vars,
+        "void $Method$(::grpc::ClientContext* context, "
+        "const $Request$* request, $Response$* response, "
+        "::grpc::experimental::ClientUnaryReactor* reactor) override;\n");
+    printer->Print(
+        *vars,
+        "void $Method$(::grpc::ClientContext* context, "
+        "const ::grpc::ByteBuffer* request, $Response$* response, "
+        "::grpc::experimental::ClientUnaryReactor* reactor) override;\n");
   } else if (ClientOnlyStreaming(method)) {
     printer->Print(*vars,
                    "void $Method$(::grpc::ClientContext* context, "
@@ -970,7 +1019,15 @@ void PrintHeaderServerMethodCallback(
         "controller) {\n"
         "               return this->$"
         "Method$(context, request, response, controller);\n"
-        "             }));\n");
+        "             }));\n}\n");
+    printer->Print(*vars,
+                   "void SetMessageAllocatorFor_$Method$(\n"
+                   "    ::grpc::experimental::MessageAllocator< "
+                   "$RealRequest$, $RealResponse$>* allocator) {\n"
+                   "  static_cast<::grpc::internal::CallbackUnaryHandler< "
+                   "$RealRequest$, $RealResponse$>*>(\n"
+                   "      ::grpc::Service::experimental().GetHandler($Idx$))\n"
+                   "          ->SetMessageAllocator(allocator);\n");
   } else if (ClientOnlyStreaming(method)) {
     printer->Print(
         *vars,
@@ -1556,7 +1613,7 @@ grpc::string GetHeaderEpilogue(grpc_generator::File* file,
 }
 
 grpc::string GetSourcePrologue(grpc_generator::File* file,
-                               const Parameters& /*params*/) {
+                               const Parameters& params) {
   grpc::string output;
   {
     // Scope the output stream so it closes and finalizes output to the string.
@@ -1565,7 +1622,9 @@ grpc::string GetSourcePrologue(grpc_generator::File* file,
 
     vars["filename"] = file->filename();
     vars["filename_base"] = file->filename_without_ext();
-    vars["message_header_ext"] = kCppGeneratorMessageHeaderExt;
+    vars["message_header_ext"] = params.message_header_extension.empty()
+                                     ? kCppGeneratorMessageHeaderExt
+                                     : params.message_header_extension;
     vars["service_header_ext"] = kCppGeneratorServiceHeaderExt;
 
     printer->Print(vars, "// Generated by the gRPC C++ plugin.\n");
@@ -1587,7 +1646,6 @@ grpc::string GetSourceIncludes(grpc_generator::File* file,
     // Scope the output stream so it closes and finalizes output to the string.
     auto printer = file->CreatePrinter(&output);
     std::map<grpc::string, grpc::string> vars;
-
     static const char* headers_strs[] = {
         "functional",
         "grpcpp/impl/codegen/async_stream.h",
@@ -1647,7 +1705,7 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer,
                    "const $Request$* request, $Response$* response, "
                    "std::function<void(::grpc::Status)> f) {\n");
     printer->Print(*vars,
-                   "  return ::grpc::internal::CallbackUnaryCall"
+                   "  ::grpc::internal::CallbackUnaryCall"
                    "(stub_->channel_.get(), stub_->rpcmethod_$Method$_, "
                    "context, request, response, std::move(f));\n}\n\n");
 
@@ -1657,10 +1715,30 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer,
                    "const ::grpc::ByteBuffer* request, $Response$* response, "
                    "std::function<void(::grpc::Status)> f) {\n");
     printer->Print(*vars,
-                   "  return ::grpc::internal::CallbackUnaryCall"
+                   "  ::grpc::internal::CallbackUnaryCall"
                    "(stub_->channel_.get(), stub_->rpcmethod_$Method$_, "
                    "context, request, response, std::move(f));\n}\n\n");
 
+    printer->Print(*vars,
+                   "void $ns$$Service$::Stub::experimental_async::$Method$("
+                   "::grpc::ClientContext* context, "
+                   "const $Request$* request, $Response$* response, "
+                   "::grpc::experimental::ClientUnaryReactor* reactor) {\n");
+    printer->Print(*vars,
+                   "  ::grpc::internal::ClientCallbackUnaryFactory::Create"
+                   "(stub_->channel_.get(), stub_->rpcmethod_$Method$_, "
+                   "context, request, response, reactor);\n}\n\n");
+
+    printer->Print(*vars,
+                   "void $ns$$Service$::Stub::experimental_async::$Method$("
+                   "::grpc::ClientContext* context, "
+                   "const ::grpc::ByteBuffer* request, $Response$* response, "
+                   "::grpc::experimental::ClientUnaryReactor* reactor) {\n");
+    printer->Print(*vars,
+                   "  ::grpc::internal::ClientCallbackUnaryFactory::Create"
+                   "(stub_->channel_.get(), stub_->rpcmethod_$Method$_, "
+                   "context, request, response, reactor);\n}\n\n");
+
     for (auto async_prefix : async_prefixes) {
       (*vars)["AsyncPrefix"] = async_prefix.prefix;
       (*vars)["AsyncStart"] = async_prefix.start;
@@ -2046,7 +2124,7 @@ grpc::string GetSourceEpilogue(grpc_generator::File* file,
 
 // TODO(mmukhi): Make sure we need parameters or not.
 grpc::string GetMockPrologue(grpc_generator::File* file,
-                             const Parameters& /*params*/) {
+                             const Parameters& params) {
   grpc::string output;
   {
     // Scope the output stream so it closes and finalizes output to the string.
@@ -2055,7 +2133,9 @@ grpc::string GetMockPrologue(grpc_generator::File* file,
 
     vars["filename"] = file->filename();
     vars["filename_base"] = file->filename_without_ext();
-    vars["message_header_ext"] = kCppGeneratorMessageHeaderExt;
+    vars["message_header_ext"] = params.message_header_extension.empty()
+                                     ? kCppGeneratorMessageHeaderExt
+                                     : params.message_header_extension;
     vars["service_header_ext"] = kCppGeneratorServiceHeaderExt;
 
     printer->Print(vars, "// Generated by the gRPC C++ plugin.\n");
@@ -2065,6 +2145,15 @@ grpc::string GetMockPrologue(grpc_generator::File* file,
 
     printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n");
     printer->Print(vars, "#include \"$filename_base$$service_header_ext$\"\n");
+    if (params.include_import_headers) {
+      const std::vector<grpc::string> import_names = file->GetImportNames();
+      for (const auto& import_name : import_names) {
+        const grpc::string include_name =
+            ImportInludeFromProtoName(import_name);
+        printer->Print(vars, include_name.c_str());
+      }
+      printer->PrintRaw("\n");
+    }
     printer->Print(vars, file->additional_headers().c_str());
     printer->Print(vars, "\n");
   }
index d88ef75..c1d64e6 100644 (file)
@@ -56,6 +56,10 @@ struct Parameters {
   grpc::string gmock_search_path;
   // *EXPERIMENTAL* Additional include files in grpc.pb.h
   std::vector<grpc::string> additional_header_includes;
+  // By default, use "pb.h"
+  grpc::string message_header_extension;
+  // Whether to include headers corresponding to imports in source file.
+  bool include_import_headers;
 };
 
 // Return the prologue of the generated header file.
index c8ab788..2de2745 100644 (file)
 
 // Generates cpp gRPC service interface out of Protobuf IDL.
 //
-
-#include <memory>
-#include <sstream>
-
-#include "src/compiler/config.h"
-
-#include "src/compiler/cpp_generator.h"
-#include "src/compiler/generator_helpers.h"
-#include "src/compiler/protobuf_plugin.h"
-
-class CppGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
- public:
-  CppGrpcGenerator() {}
-  virtual ~CppGrpcGenerator() {}
-
-  virtual bool Generate(const grpc::protobuf::FileDescriptor* file,
-                        const grpc::string& parameter,
-                        grpc::protobuf::compiler::GeneratorContext* context,
-                        grpc::string* error) const {
-    if (file->options().cc_generic_services()) {
-      *error =
-          "cpp grpc proto compiler plugin does not work with generic "
-          "services. To generate cpp grpc APIs, please set \""
-          "cc_generic_service = false\".";
-      return false;
-    }
-
-    grpc_cpp_generator::Parameters generator_parameters;
-    generator_parameters.use_system_headers = true;
-    generator_parameters.generate_mock_code = false;
-
-    ProtoBufFile pbfile(file);
-
-    if (!parameter.empty()) {
-      std::vector<grpc::string> parameters_list =
-          grpc_generator::tokenize(parameter, ",");
-      for (auto parameter_string = parameters_list.begin();
-           parameter_string != parameters_list.end(); parameter_string++) {
-        std::vector<grpc::string> param =
-            grpc_generator::tokenize(*parameter_string, "=");
-        if (param[0] == "services_namespace") {
-          generator_parameters.services_namespace = param[1];
-        } else if (param[0] == "use_system_headers") {
-          if (param[1] == "true") {
-            generator_parameters.use_system_headers = true;
-          } else if (param[1] == "false") {
-            generator_parameters.use_system_headers = false;
-          } else {
-            *error = grpc::string("Invalid parameter: ") + *parameter_string;
-            return false;
-          }
-        } else if (param[0] == "grpc_search_path") {
-          generator_parameters.grpc_search_path = param[1];
-        } else if (param[0] == "generate_mock_code") {
-          if (param[1] == "true") {
-            generator_parameters.generate_mock_code = true;
-          } else if (param[1] != "false") {
-            *error = grpc::string("Invalid parameter: ") + *parameter_string;
-            return false;
-          }
-        } else if (param[0] == "gmock_search_path") {
-          generator_parameters.gmock_search_path = param[1];
-        } else if (param[0] == "additional_header_includes") {
-          generator_parameters.additional_header_includes =
-              grpc_generator::tokenize(param[1], ":");
-        } else {
-          *error = grpc::string("Unknown parameter: ") + *parameter_string;
-          return false;
-        }
-      }
-    }
-
-    grpc::string file_name = grpc_generator::StripProto(file->name());
-
-    grpc::string header_code =
-        grpc_cpp_generator::GetHeaderPrologue(&pbfile, generator_parameters) +
-        grpc_cpp_generator::GetHeaderIncludes(&pbfile, generator_parameters) +
-        grpc_cpp_generator::GetHeaderServices(&pbfile, generator_parameters) +
-        grpc_cpp_generator::GetHeaderEpilogue(&pbfile, generator_parameters);
-    std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> header_output(
-        context->Open(file_name + ".grpc.pb.h"));
-    grpc::protobuf::io::CodedOutputStream header_coded_out(header_output.get());
-    header_coded_out.WriteRaw(header_code.data(), header_code.size());
-
-    grpc::string source_code =
-        grpc_cpp_generator::GetSourcePrologue(&pbfile, generator_parameters) +
-        grpc_cpp_generator::GetSourceIncludes(&pbfile, generator_parameters) +
-        grpc_cpp_generator::GetSourceServices(&pbfile, generator_parameters) +
-        grpc_cpp_generator::GetSourceEpilogue(&pbfile, generator_parameters);
-    std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> source_output(
-        context->Open(file_name + ".grpc.pb.cc"));
-    grpc::protobuf::io::CodedOutputStream source_coded_out(source_output.get());
-    source_coded_out.WriteRaw(source_code.data(), source_code.size());
-
-    if (!generator_parameters.generate_mock_code) {
-      return true;
-    }
-    grpc::string mock_code =
-        grpc_cpp_generator::GetMockPrologue(&pbfile, generator_parameters) +
-        grpc_cpp_generator::GetMockIncludes(&pbfile, generator_parameters) +
-        grpc_cpp_generator::GetMockServices(&pbfile, generator_parameters) +
-        grpc_cpp_generator::GetMockEpilogue(&pbfile, generator_parameters);
-    std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> mock_output(
-        context->Open(file_name + "_mock.grpc.pb.h"));
-    grpc::protobuf::io::CodedOutputStream mock_coded_out(mock_output.get());
-    mock_coded_out.WriteRaw(mock_code.data(), mock_code.size());
-
-    return true;
-  }
-
- private:
-  // Insert the given code into the given file at the given insertion point.
-  void Insert(grpc::protobuf::compiler::GeneratorContext* context,
-              const grpc::string& filename, const grpc::string& insertion_point,
-              const grpc::string& code) const {
-    std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> output(
-        context->OpenForInsert(filename, insertion_point));
-    grpc::protobuf::io::CodedOutputStream coded_out(output.get());
-    coded_out.WriteRaw(code.data(), code.size());
-  }
-};
+#include "src/compiler/cpp_plugin.h"
 
 int main(int argc, char* argv[]) {
   CppGrpcGenerator generator;
diff --git a/src/compiler/cpp_plugin.h b/src/compiler/cpp_plugin.h
new file mode 100644 (file)
index 0000000..1cdf0b3
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ *
+ * 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 GRPC_INTERNAL_COMPILER_CPP_PLUGIN_H
+#define GRPC_INTERNAL_COMPILER_CPP_PLUGIN_H
+
+#include <memory>
+#include <sstream>
+
+#include "src/compiler/config.h"
+
+#include "src/compiler/cpp_generator.h"
+#include "src/compiler/generator_helpers.h"
+#include "src/compiler/protobuf_plugin.h"
+
+// Cpp Generator for Protobug IDL
+class CppGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
+ public:
+  CppGrpcGenerator() {}
+  virtual ~CppGrpcGenerator() {}
+
+  virtual bool Generate(const grpc::protobuf::FileDescriptor* file,
+                        const grpc::string& parameter,
+                        grpc::protobuf::compiler::GeneratorContext* context,
+                        grpc::string* error) const {
+    if (file->options().cc_generic_services()) {
+      *error =
+          "cpp grpc proto compiler plugin does not work with generic "
+          "services. To generate cpp grpc APIs, please set \""
+          "cc_generic_service = false\".";
+      return false;
+    }
+
+    grpc_cpp_generator::Parameters generator_parameters;
+    generator_parameters.use_system_headers = true;
+    generator_parameters.generate_mock_code = false;
+    generator_parameters.include_import_headers = false;
+
+    ProtoBufFile pbfile(file);
+
+    if (!parameter.empty()) {
+      std::vector<grpc::string> parameters_list =
+          grpc_generator::tokenize(parameter, ",");
+      for (auto parameter_string = parameters_list.begin();
+           parameter_string != parameters_list.end(); parameter_string++) {
+        std::vector<grpc::string> param =
+            grpc_generator::tokenize(*parameter_string, "=");
+        if (param[0] == "services_namespace") {
+          generator_parameters.services_namespace = param[1];
+        } else if (param[0] == "use_system_headers") {
+          if (param[1] == "true") {
+            generator_parameters.use_system_headers = true;
+          } else if (param[1] == "false") {
+            generator_parameters.use_system_headers = false;
+          } else {
+            *error = grpc::string("Invalid parameter: ") + *parameter_string;
+            return false;
+          }
+        } else if (param[0] == "grpc_search_path") {
+          generator_parameters.grpc_search_path = param[1];
+        } else if (param[0] == "generate_mock_code") {
+          if (param[1] == "true") {
+            generator_parameters.generate_mock_code = true;
+          } else if (param[1] != "false") {
+            *error = grpc::string("Invalid parameter: ") + *parameter_string;
+            return false;
+          }
+        } else if (param[0] == "gmock_search_path") {
+          generator_parameters.gmock_search_path = param[1];
+        } else if (param[0] == "additional_header_includes") {
+          generator_parameters.additional_header_includes =
+              grpc_generator::tokenize(param[1], ":");
+        } else if (param[0] == "message_header_extension") {
+          generator_parameters.message_header_extension = param[1];
+        } else if (param[0] == "include_import_headers") {
+          if (param[1] == "true") {
+            generator_parameters.include_import_headers = true;
+          } else if (param[1] != "false") {
+            *error = grpc::string("Invalid parameter: ") + *parameter_string;
+            return false;
+          }
+        } else {
+          *error = grpc::string("Unknown parameter: ") + *parameter_string;
+          return false;
+        }
+      }
+    }
+
+    grpc::string file_name = grpc_generator::StripProto(file->name());
+
+    grpc::string header_code =
+        grpc_cpp_generator::GetHeaderPrologue(&pbfile, generator_parameters) +
+        grpc_cpp_generator::GetHeaderIncludes(&pbfile, generator_parameters) +
+        grpc_cpp_generator::GetHeaderServices(&pbfile, generator_parameters) +
+        grpc_cpp_generator::GetHeaderEpilogue(&pbfile, generator_parameters);
+    std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> header_output(
+        context->Open(file_name + ".grpc.pb.h"));
+    grpc::protobuf::io::CodedOutputStream header_coded_out(header_output.get());
+    header_coded_out.WriteRaw(header_code.data(), header_code.size());
+
+    grpc::string source_code =
+        grpc_cpp_generator::GetSourcePrologue(&pbfile, generator_parameters) +
+        grpc_cpp_generator::GetSourceIncludes(&pbfile, generator_parameters) +
+        grpc_cpp_generator::GetSourceServices(&pbfile, generator_parameters) +
+        grpc_cpp_generator::GetSourceEpilogue(&pbfile, generator_parameters);
+    std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> source_output(
+        context->Open(file_name + ".grpc.pb.cc"));
+    grpc::protobuf::io::CodedOutputStream source_coded_out(source_output.get());
+    source_coded_out.WriteRaw(source_code.data(), source_code.size());
+
+    if (!generator_parameters.generate_mock_code) {
+      return true;
+    }
+    grpc::string mock_code =
+        grpc_cpp_generator::GetMockPrologue(&pbfile, generator_parameters) +
+        grpc_cpp_generator::GetMockIncludes(&pbfile, generator_parameters) +
+        grpc_cpp_generator::GetMockServices(&pbfile, generator_parameters) +
+        grpc_cpp_generator::GetMockEpilogue(&pbfile, generator_parameters);
+    std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> mock_output(
+        context->Open(file_name + "_mock.grpc.pb.h"));
+    grpc::protobuf::io::CodedOutputStream mock_coded_out(mock_output.get());
+    mock_coded_out.WriteRaw(mock_code.data(), mock_code.size());
+
+    return true;
+  }
+
+ private:
+  // Insert the given code into the given file at the given insertion point.
+  void Insert(grpc::protobuf::compiler::GeneratorContext* context,
+              const grpc::string& filename, const grpc::string& insertion_point,
+              const grpc::string& code) const {
+    std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> output(
+        context->OpenForInsert(filename, insertion_point));
+    grpc::protobuf::io::CodedOutputStream coded_out(output.get());
+    coded_out.WriteRaw(code.data(), code.size());
+  }
+};
+
+#endif  // GRPC_INTERNAL_COMPILER_CPP_PLUGIN_H
index ac0af33..778e5c3 100644 (file)
@@ -382,6 +382,10 @@ void GenerateServerClass(Printer* out, const ServiceDescriptor* service) {
       "/// <summary>Base class for server-side implementations of "
       "$servicename$</summary>\n",
       "servicename", GetServiceClassName(service));
+  out->Print(
+      "[grpc::BindServiceMethod(typeof($classname$), "
+      "\"BindService\")]\n",
+      "classname", GetServiceClassName(service));
   out->Print("public abstract partial class $name$\n", "name",
              GetServerClassName(service));
   out->Print("{\n");
index e806f46..24845ec 100644 (file)
@@ -50,7 +50,8 @@ void PrintProtoRpcDeclarationAsPragma(
 }
 
 template <typename DescriptorType>
-static void PrintAllComments(const DescriptorType* desc, Printer* printer) {
+static void PrintAllComments(const DescriptorType* desc, Printer* printer,
+                             bool deprecated = false) {
   std::vector<grpc::string> comments;
   grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING_DETACHED,
                              &comments);
@@ -70,13 +71,20 @@ static void PrintAllComments(const DescriptorType* desc, Printer* printer) {
     }
     printer->Print("\n");
   }
+  if (deprecated) {
+    printer->Print(" *\n");
+    printer->Print(
+        " * This method belongs to a set of APIs that have been deprecated. "
+        "Using"
+        " the v2 API is recommended.\n");
+  }
   printer->Print(" */\n");
 }
 
 void PrintMethodSignature(Printer* printer, const MethodDescriptor* method,
                           const map< ::grpc::string, ::grpc::string>& vars) {
   // Print comment
-  PrintAllComments(method, printer);
+  PrintAllComments(method, printer, true);
 
   printer->Print(vars, "- ($return_type$)$method_name$With");
   if (method->client_streaming()) {
@@ -278,6 +286,13 @@ void PrintMethodImplementations(Printer* printer,
   map< ::grpc::string, ::grpc::string> vars = {
       {"service_class", ServiceClassName(service)}};
 
+  printer.Print(vars,
+                "/**\n"
+                " * The methods in this protocol belong to a set of old APIs "
+                "that have been deprecated. They do not\n"
+                " * recognize call options provided in the initializer. Using "
+                "the v2 protocol is recommended.\n"
+                " */\n");
   printer.Print(vars, "@protocol $service_class$ <NSObject>\n\n");
   for (int i = 0; i < service->method_count(); i++) {
     PrintMethodDeclarations(&printer, service->method(i));
@@ -329,10 +344,13 @@ void PrintMethodImplementations(Printer* printer,
       "callOptions:(GRPCCallOptions "
       "*_Nullable)callOptions"
       " NS_DESIGNATED_INITIALIZER;\n");
-  printer.Print("- (instancetype)initWithHost:(NSString *)host;\n");
   printer.Print(
       "+ (instancetype)serviceWithHost:(NSString *)host "
       "callOptions:(GRPCCallOptions *_Nullable)callOptions;\n");
+  printer.Print(
+      "// The following methods belong to a set of old APIs that have been "
+      "deprecated.\n");
+  printer.Print("- (instancetype)initWithHost:(NSString *)host;\n");
   printer.Print("+ (instancetype)serviceWithHost:(NSString *)host;\n");
   printer.Print("@end\n");
 
index a3e448a..06cda5f 100644 (file)
@@ -55,22 +55,23 @@ class ProtoBufMethod : public grpc_generator::Method {
     return method_->output_type()->file()->name();
   }
 
-  bool get_module_and_message_path_input(grpc::string* str,
-                                         grpc::string generator_file_name,
-                                         bool generate_in_pb2_grpc,
-                                         grpc::string import_prefix) const {
+  // TODO(https://github.com/grpc/grpc/issues/18800): Clean this up.
+  bool get_module_and_message_path_input(
+      grpc::string* str, grpc::string generator_file_name,
+      bool generate_in_pb2_grpc, grpc::string import_prefix,
+      const std::vector<grpc::string>& prefixes_to_filter) const final {
     return grpc_python_generator::GetModuleAndMessagePath(
         method_->input_type(), str, generator_file_name, generate_in_pb2_grpc,
-        import_prefix);
+        import_prefix, prefixes_to_filter);
   }
 
-  bool get_module_and_message_path_output(grpc::string* str,
-                                          grpc::string generator_file_name,
-                                          bool generate_in_pb2_grpc,
-                                          grpc::string import_prefix) const {
+  bool get_module_and_message_path_output(
+      grpc::string* str, grpc::string generator_file_name,
+      bool generate_in_pb2_grpc, grpc::string import_prefix,
+      const std::vector<grpc::string>& prefixes_to_filter) const final {
     return grpc_python_generator::GetModuleAndMessagePath(
         method_->output_type(), str, generator_file_name, generate_in_pb2_grpc,
-        import_prefix);
+        import_prefix, prefixes_to_filter);
   }
 
   bool NoStreaming() const {
@@ -189,6 +190,15 @@ class ProtoBufFile : public grpc_generator::File {
     return grpc_python_generator::get_all_comments(file_);
   }
 
+  vector<grpc::string> GetImportNames() const {
+    vector<grpc::string> proto_names;
+    for (int i = 0; i < file_->dependency_count(); ++i) {
+      const auto& dep = *file_->dependency(i);
+      proto_names.push_back(dep.name());
+    }
+    return proto_names;
+  }
+
  private:
   const grpc::protobuf::FileDescriptor* file_;
 };
index 8a0b889..705aef1 100644 (file)
@@ -214,13 +214,15 @@ bool PrivateGenerator::PrintBetaServerFactory(
       grpc::string input_message_module_and_class;
       if (!method->get_module_and_message_path_input(
               &input_message_module_and_class, generator_file_name,
-              generate_in_pb2_grpc, config.import_prefix)) {
+              generate_in_pb2_grpc, config.import_prefix,
+              config.prefixes_to_filter)) {
         return false;
       }
       grpc::string output_message_module_and_class;
       if (!method->get_module_and_message_path_output(
               &output_message_module_and_class, generator_file_name,
-              generate_in_pb2_grpc, config.import_prefix)) {
+              generate_in_pb2_grpc, config.import_prefix,
+              config.prefixes_to_filter)) {
         return false;
       }
       method_implementation_constructors.insert(
@@ -320,13 +322,15 @@ bool PrivateGenerator::PrintBetaStubFactory(
       grpc::string input_message_module_and_class;
       if (!method->get_module_and_message_path_input(
               &input_message_module_and_class, generator_file_name,
-              generate_in_pb2_grpc, config.import_prefix)) {
+              generate_in_pb2_grpc, config.import_prefix,
+              config.prefixes_to_filter)) {
         return false;
       }
       grpc::string output_message_module_and_class;
       if (!method->get_module_and_message_path_output(
               &output_message_module_and_class, generator_file_name,
-              generate_in_pb2_grpc, config.import_prefix)) {
+              generate_in_pb2_grpc, config.import_prefix,
+              config.prefixes_to_filter)) {
         return false;
       }
       method_cardinalities.insert(
@@ -425,13 +429,15 @@ bool PrivateGenerator::PrintStub(
         grpc::string request_module_and_class;
         if (!method->get_module_and_message_path_input(
                 &request_module_and_class, generator_file_name,
-                generate_in_pb2_grpc, config.import_prefix)) {
+                generate_in_pb2_grpc, config.import_prefix,
+                config.prefixes_to_filter)) {
           return false;
         }
         grpc::string response_module_and_class;
         if (!method->get_module_and_message_path_output(
                 &response_module_and_class, generator_file_name,
-                generate_in_pb2_grpc, config.import_prefix)) {
+                generate_in_pb2_grpc, config.import_prefix,
+                config.prefixes_to_filter)) {
           return false;
         }
         StringMap method_dict;
@@ -516,13 +522,15 @@ bool PrivateGenerator::PrintAddServicerToServer(
         grpc::string request_module_and_class;
         if (!method->get_module_and_message_path_input(
                 &request_module_and_class, generator_file_name,
-                generate_in_pb2_grpc, config.import_prefix)) {
+                generate_in_pb2_grpc, config.import_prefix,
+                config.prefixes_to_filter)) {
           return false;
         }
         grpc::string response_module_and_class;
         if (!method->get_module_and_message_path_output(
                 &response_module_and_class, generator_file_name,
-                generate_in_pb2_grpc, config.import_prefix)) {
+                generate_in_pb2_grpc, config.import_prefix,
+                config.prefixes_to_filter)) {
           return false;
         }
         StringMap method_dict;
@@ -589,17 +597,21 @@ bool PrivateGenerator::PrintPreamble(grpc_generator::Printer* out) {
 
         grpc::string input_type_file_name = method->get_input_type_name();
         grpc::string input_module_name =
-            ModuleName(input_type_file_name, config.import_prefix);
+            ModuleName(input_type_file_name, config.import_prefix,
+                       config.prefixes_to_filter);
         grpc::string input_module_alias =
-            ModuleAlias(input_type_file_name, config.import_prefix);
+            ModuleAlias(input_type_file_name, config.import_prefix,
+                        config.prefixes_to_filter);
         imports_set.insert(
             std::make_tuple(input_module_name, input_module_alias));
 
         grpc::string output_type_file_name = method->get_output_type_name();
         grpc::string output_module_name =
-            ModuleName(output_type_file_name, config.import_prefix);
+            ModuleName(output_type_file_name, config.import_prefix,
+                       config.prefixes_to_filter);
         grpc::string output_module_alias =
-            ModuleAlias(output_type_file_name, config.import_prefix);
+            ModuleAlias(output_type_file_name, config.import_prefix,
+                        config.prefixes_to_filter);
         imports_set.insert(
             std::make_tuple(output_module_name, output_module_alias));
       }
index 9139307..6077ac4 100644 (file)
@@ -20,6 +20,7 @@
 #define GRPC_INTERNAL_COMPILER_PYTHON_GENERATOR_H
 
 #include <utility>
+#include <vector>
 
 #include "src/compiler/config.h"
 #include "src/compiler/schema_interface.h"
@@ -35,6 +36,7 @@ struct GeneratorConfiguration {
   grpc::string beta_package_root;
   // TODO(https://github.com/google/protobuf/issues/888): Drop this.
   grpc::string import_prefix;
+  std::vector<grpc::string> prefixes_to_filter;
 };
 
 class PythonGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
index b1b58be..171dd73 100644 (file)
@@ -49,23 +49,39 @@ namespace {
 typedef vector<const Descriptor*> DescriptorVector;
 typedef vector<grpc::string> StringVector;
 
+static grpc::string StripModulePrefixes(
+    const grpc::string& raw_module_name,
+    const std::vector<grpc::string>& prefixes_to_filter) {
+  for (const auto& prefix : prefixes_to_filter) {
+    if (raw_module_name.rfind(prefix, 0) == 0) {
+      return raw_module_name.substr(prefix.size(),
+                                    raw_module_name.size() - prefix.size());
+    }
+  }
+  return raw_module_name;
+}
+
 // TODO(https://github.com/google/protobuf/issues/888):
 // Export `ModuleName` from protobuf's
 // `src/google/protobuf/compiler/python/python_generator.cc` file.
 grpc::string ModuleName(const grpc::string& filename,
-                        const grpc::string& import_prefix) {
+                        const grpc::string& import_prefix,
+                        const std::vector<grpc::string>& prefixes_to_filter) {
   grpc::string basename = StripProto(filename);
   basename = StringReplace(basename, "-", "_");
   basename = StringReplace(basename, "/", ".");
-  return import_prefix + basename + "_pb2";
+  return StripModulePrefixes(import_prefix + basename + "_pb2",
+                             prefixes_to_filter);
 }
 
 // TODO(https://github.com/google/protobuf/issues/888):
 // Export `ModuleAlias` from protobuf's
 // `src/google/protobuf/compiler/python/python_generator.cc` file.
 grpc::string ModuleAlias(const grpc::string& filename,
-                         const grpc::string& import_prefix) {
-  grpc::string module_name = ModuleName(filename, import_prefix);
+                         const grpc::string& import_prefix,
+                         const std::vector<grpc::string>& prefixes_to_filter) {
+  grpc::string module_name =
+      ModuleName(filename, import_prefix, prefixes_to_filter);
   // We can't have dots in the module name, so we replace each with _dot_.
   // But that could lead to a collision between a.b and a_dot_b, so we also
   // duplicate each underscore.
@@ -74,10 +90,10 @@ grpc::string ModuleAlias(const grpc::string& filename,
   return module_name;
 }
 
-bool GetModuleAndMessagePath(const Descriptor* type, grpc::string* out,
-                             grpc::string generator_file_name,
-                             bool generate_in_pb2_grpc,
-                             grpc::string& import_prefix) {
+bool GetModuleAndMessagePath(
+    const Descriptor* type, grpc::string* out, grpc::string generator_file_name,
+    bool generate_in_pb2_grpc, grpc::string& import_prefix,
+    const std::vector<grpc::string>& prefixes_to_filter) {
   const Descriptor* path_elem_type = type;
   DescriptorVector message_path;
   do {
@@ -93,7 +109,7 @@ bool GetModuleAndMessagePath(const Descriptor* type, grpc::string* out,
 
   grpc::string module;
   if (generator_file_name != file_name || generate_in_pb2_grpc) {
-    module = ModuleAlias(file_name, import_prefix) + ".";
+    module = ModuleAlias(file_name, import_prefix, prefixes_to_filter) + ".";
   } else {
     module = "";
   }
index c000478..e7b427d 100644 (file)
@@ -57,10 +57,12 @@ struct Method : public CommentHolder {
 
   virtual bool get_module_and_message_path_input(
       grpc::string* str, grpc::string generator_file_name,
-      bool generate_in_pb2_grpc, grpc::string import_prefix) const = 0;
+      bool generate_in_pb2_grpc, grpc::string import_prefix,
+      const std::vector<grpc::string>& prefixes_to_filter) const = 0;
   virtual bool get_module_and_message_path_output(
       grpc::string* str, grpc::string generator_file_name,
-      bool generate_in_pb2_grpc, grpc::string import_prefix) const = 0;
+      bool generate_in_pb2_grpc, grpc::string import_prefix,
+      const std::vector<grpc::string>& prefixes_to_filter) const = 0;
 
   virtual grpc::string get_input_type_name() const = 0;
   virtual grpc::string get_output_type_name() const = 0;
@@ -101,6 +103,7 @@ struct File : public CommentHolder {
   virtual grpc::string package() const = 0;
   virtual std::vector<grpc::string> package_parts() const = 0;
   virtual grpc::string additional_headers() const = 0;
+  virtual std::vector<grpc::string> GetImportNames() const { return {}; }
 
   virtual int service_count() const = 0;
   virtual std::unique_ptr<const Service> service(int i) const = 0;
index 3e2faa5..dd76169 100644 (file)
@@ -25,8 +25,8 @@
 #include <grpc/support/log.h>
 #include <grpc/support/sync.h>
 #include "src/core/ext/filters/client_channel/client_channel.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/global_config.h"
 #include "src/core/lib/iomgr/error.h"
 #include "src/core/lib/iomgr/pollset.h"
 #include "src/core/lib/iomgr/timer.h"
@@ -56,21 +56,27 @@ static backup_poller* g_poller = nullptr;  // guarded by g_poller_mu
 // treated as const.
 static int g_poll_interval_ms = DEFAULT_POLL_INTERVAL_MS;
 
+GPR_GLOBAL_CONFIG_DEFINE_INT32(
+    grpc_client_channel_backup_poll_interval_ms, DEFAULT_POLL_INTERVAL_MS,
+    "Declares the interval in ms between two backup polls on client channels. "
+    "These polls are run in the timer thread so that gRPC can process "
+    "connection failures while there is no active polling thread. "
+    "They help reconnect disconnected client channels (mostly due to "
+    "idleness), so that the next RPC on this channel won't fail. Set to 0 to "
+    "turn off the backup polls.");
+
 static void init_globals() {
   gpr_mu_init(&g_poller_mu);
-  char* env = gpr_getenv("GRPC_CLIENT_CHANNEL_BACKUP_POLL_INTERVAL_MS");
-  if (env != nullptr) {
-    int poll_interval_ms = gpr_parse_nonnegative_int(env);
-    if (poll_interval_ms == -1) {
-      gpr_log(GPR_ERROR,
-              "Invalid GRPC_CLIENT_CHANNEL_BACKUP_POLL_INTERVAL_MS: %s, "
-              "default value %d will be used.",
-              env, g_poll_interval_ms);
-    } else {
-      g_poll_interval_ms = poll_interval_ms;
-    }
+  int32_t poll_interval_ms =
+      GPR_GLOBAL_CONFIG_GET(grpc_client_channel_backup_poll_interval_ms);
+  if (poll_interval_ms < 0) {
+    gpr_log(GPR_ERROR,
+            "Invalid GRPC_CLIENT_CHANNEL_BACKUP_POLL_INTERVAL_MS: %d, "
+            "default value %d will be used.",
+            poll_interval_ms, g_poll_interval_ms);
+  } else {
+    g_poll_interval_ms = poll_interval_ms;
   }
-  gpr_free(env);
 }
 
 static void backup_poller_shutdown_unref(backup_poller* p) {
index 8f132f9..e1bf4f8 100644 (file)
@@ -23,6 +23,9 @@
 
 #include <grpc/grpc.h>
 #include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/gprpp/global_config.h"
+
+GPR_GLOBAL_CONFIG_DECLARE_INT32(grpc_client_channel_backup_poll_interval_ms);
 
 /* Start polling \a interested_parties periodically in the timer thread  */
 void grpc_client_channel_start_backup_polling(
index 9f970f6..232183d 100644 (file)
@@ -125,7 +125,7 @@ static void partly_done(state_watcher* w, bool due_to_completion,
   gpr_mu_lock(&w->mu);
 
   if (due_to_completion) {
-    if (grpc_trace_operation_failures.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_operation_failures)) {
       GRPC_LOG_IF_ERROR("watch_completion_error", GRPC_ERROR_REF(error));
     }
     GRPC_ERROR_UNREF(error);
index 82ce253..f774c98 100644 (file)
@@ -51,6 +51,7 @@
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gprpp/inlined_vector.h"
 #include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/iomgr/iomgr.h"
 #include "src/core/lib/iomgr/polling_entity.h"
 #include "src/core/lib/transport/static_metadata.h"
 #include "src/core/lib/transport/status_metadata.h"
 
-using grpc_core::internal::ClientChannelMethodParams;
-using grpc_core::internal::ClientChannelMethodParamsTable;
-using grpc_core::internal::ProcessedResolverResult;
+using grpc_core::internal::ClientChannelMethodParsedObject;
 using grpc_core::internal::ServerRetryThrottleData;
 
-using grpc_core::LoadBalancingPolicy;
-
-/* Client channel implementation */
+//
+// Client channel filter
+//
 
 // By default, we buffer 256 KiB per RPC for retries.
 // TODO(roth): Do we have any data to suggest a better value?
@@ -82,160 +81,916 @@ using grpc_core::LoadBalancingPolicy;
 // any even moderately compelling reason to do so.
 #define RETRY_BACKOFF_JITTER 0.2
 
-grpc_core::TraceFlag grpc_client_channel_call_trace(false,
-                                                    "client_channel_call");
-grpc_core::TraceFlag grpc_client_channel_routing_trace(
-    false, "client_channel_routing");
+// Max number of batches that can be pending on a call at any given
+// time.  This includes one batch for each of the following ops:
+//   recv_initial_metadata
+//   send_initial_metadata
+//   recv_message
+//   send_message
+//   recv_trailing_metadata
+//   send_trailing_metadata
+#define MAX_PENDING_BATCHES 6
 
-/*************************************************************************
- * CHANNEL-WIDE FUNCTIONS
- */
+namespace grpc_core {
 
-struct external_connectivity_watcher;
+TraceFlag grpc_client_channel_call_trace(false, "client_channel_call");
+TraceFlag grpc_client_channel_routing_trace(false, "client_channel_routing");
 
-struct QueuedPick {
-  LoadBalancingPolicy::PickArgs pick;
-  grpc_call_element* elem;
-  QueuedPick* next = nullptr;
+namespace {
+
+//
+// ChannelData definition
+//
+
+class ChannelData {
+ public:
+  struct QueuedPick {
+    LoadBalancingPolicy::PickArgs pick;
+    grpc_call_element* elem;
+    QueuedPick* next = nullptr;
+  };
+
+  static grpc_error* Init(grpc_channel_element* elem,
+                          grpc_channel_element_args* args);
+  static void Destroy(grpc_channel_element* elem);
+  static void StartTransportOp(grpc_channel_element* elem,
+                               grpc_transport_op* op);
+  static void GetChannelInfo(grpc_channel_element* elem,
+                             const grpc_channel_info* info);
+
+  void set_channelz_node(channelz::ClientChannelNode* node) {
+    channelz_node_ = node;
+    resolving_lb_policy_->set_channelz_node(node->Ref());
+  }
+  void FillChildRefsForChannelz(channelz::ChildRefsList* child_subchannels,
+                                channelz::ChildRefsList* child_channels) {
+    if (resolving_lb_policy_ != nullptr) {
+      resolving_lb_policy_->FillChildRefsForChannelz(child_subchannels,
+                                                     child_channels);
+    }
+  }
+
+  bool deadline_checking_enabled() const { return deadline_checking_enabled_; }
+  bool enable_retries() const { return enable_retries_; }
+  size_t per_rpc_retry_buffer_size() const {
+    return per_rpc_retry_buffer_size_;
+  }
+
+  // Note: Does NOT return a new ref.
+  grpc_error* disconnect_error() const {
+    return disconnect_error_.Load(MemoryOrder::ACQUIRE);
+  }
+
+  grpc_combiner* data_plane_combiner() const { return data_plane_combiner_; }
+
+  LoadBalancingPolicy::SubchannelPicker* picker() const {
+    return picker_.get();
+  }
+  void AddQueuedPick(QueuedPick* pick, grpc_polling_entity* pollent);
+  void RemoveQueuedPick(QueuedPick* to_remove, grpc_polling_entity* pollent);
+
+  bool received_service_config_data() const {
+    return received_service_config_data_;
+  }
+  RefCountedPtr<ServerRetryThrottleData> retry_throttle_data() const {
+    return retry_throttle_data_;
+  }
+  RefCountedPtr<ServiceConfig> service_config() const {
+    return service_config_;
+  }
+
+  grpc_connectivity_state CheckConnectivityState(bool try_to_connect);
+  void AddExternalConnectivityWatcher(grpc_polling_entity pollent,
+                                      grpc_connectivity_state* state,
+                                      grpc_closure* on_complete,
+                                      grpc_closure* watcher_timer_init) {
+    // Will delete itself.
+    New<ExternalConnectivityWatcher>(this, pollent, state, on_complete,
+                                     watcher_timer_init);
+  }
+  int NumExternalConnectivityWatchers() const {
+    return external_connectivity_watcher_list_.size();
+  }
+
+ private:
+  class ConnectivityStateAndPickerSetter;
+  class ServiceConfigSetter;
+  class ClientChannelControlHelper;
+
+  class ExternalConnectivityWatcher {
+   public:
+    class WatcherList {
+     public:
+      WatcherList() { gpr_mu_init(&mu_); }
+      ~WatcherList() { gpr_mu_destroy(&mu_); }
+
+      int size() const;
+      ExternalConnectivityWatcher* Lookup(grpc_closure* on_complete) const;
+      void Add(ExternalConnectivityWatcher* watcher);
+      void Remove(const ExternalConnectivityWatcher* watcher);
+
+     private:
+      // head_ is guarded by a mutex, since the size() method needs to
+      // iterate over the list, and it's called from the C-core API
+      // function grpc_channel_num_external_connectivity_watchers(), which
+      // is synchronous and therefore cannot run in the combiner.
+      mutable gpr_mu mu_;
+      ExternalConnectivityWatcher* head_ = nullptr;
+    };
+
+    ExternalConnectivityWatcher(ChannelData* chand, grpc_polling_entity pollent,
+                                grpc_connectivity_state* state,
+                                grpc_closure* on_complete,
+                                grpc_closure* watcher_timer_init);
+
+    ~ExternalConnectivityWatcher();
+
+   private:
+    static void OnWatchCompleteLocked(void* arg, grpc_error* error);
+    static void WatchConnectivityStateLocked(void* arg, grpc_error* ignored);
+
+    ChannelData* chand_;
+    grpc_polling_entity pollent_;
+    grpc_connectivity_state* state_;
+    grpc_closure* on_complete_;
+    grpc_closure* watcher_timer_init_;
+    grpc_closure my_closure_;
+    ExternalConnectivityWatcher* next_ = nullptr;
+  };
+
+  ChannelData(grpc_channel_element_args* args, grpc_error** error);
+  ~ChannelData();
+
+  static bool ProcessResolverResultLocked(
+      void* arg, const Resolver::Result& result, const char** lb_policy_name,
+      RefCountedPtr<ParsedLoadBalancingConfig>* lb_policy_config,
+      grpc_error** service_config_error);
+
+  grpc_error* DoPingLocked(grpc_transport_op* op);
+
+  static void StartTransportOpLocked(void* arg, grpc_error* ignored);
+
+  static void TryToConnectLocked(void* arg, grpc_error* error_ignored);
+
+  void ProcessLbPolicy(
+      const Resolver::Result& resolver_result,
+      const internal::ClientChannelGlobalParsedObject* parsed_service_config,
+      UniquePtr<char>* lb_policy_name,
+      RefCountedPtr<ParsedLoadBalancingConfig>* lb_policy_config);
+
+  //
+  // Fields set at construction and never modified.
+  //
+  const bool deadline_checking_enabled_;
+  const bool enable_retries_;
+  const size_t per_rpc_retry_buffer_size_;
+  grpc_channel_stack* owning_stack_;
+  ClientChannelFactory* client_channel_factory_;
+  UniquePtr<char> server_name_;
+  RefCountedPtr<ServiceConfig> default_service_config_;
+  // Initialized shortly after construction.
+  channelz::ClientChannelNode* channelz_node_ = nullptr;
+
+  //
+  // Fields used in the data plane.  Guarded by data_plane_combiner.
+  //
+  grpc_combiner* data_plane_combiner_;
+  UniquePtr<LoadBalancingPolicy::SubchannelPicker> picker_;
+  QueuedPick* queued_picks_ = nullptr;  // Linked list of queued picks.
+  // Data from service config.
+  bool received_service_config_data_ = false;
+  RefCountedPtr<ServerRetryThrottleData> retry_throttle_data_;
+  RefCountedPtr<ServiceConfig> service_config_;
+
+  //
+  // Fields used in the control plane.  Guarded by combiner.
+  //
+  grpc_combiner* combiner_;
+  grpc_pollset_set* interested_parties_;
+  RefCountedPtr<SubchannelPoolInterface> subchannel_pool_;
+  OrphanablePtr<LoadBalancingPolicy> resolving_lb_policy_;
+  grpc_connectivity_state_tracker state_tracker_;
+  ExternalConnectivityWatcher::WatcherList external_connectivity_watcher_list_;
+  UniquePtr<char> health_check_service_name_;
+  RefCountedPtr<ServiceConfig> saved_service_config_;
+  bool received_first_resolver_result_ = false;
+
+  //
+  // Fields accessed from both data plane and control plane combiners.
+  //
+  Atomic<grpc_error*> disconnect_error_;
+
+  //
+  // Fields guarded by a mutex, since they need to be accessed
+  // synchronously via get_channel_info().
+  //
+  gpr_mu info_mu_;
+  UniquePtr<char> info_lb_policy_name_;
+  UniquePtr<char> info_service_config_json_;
 };
 
-typedef struct client_channel_channel_data {
-  bool deadline_checking_enabled;
-  bool enable_retries;
-  size_t per_rpc_retry_buffer_size;
-
-  /** combiner protecting all variables below in this data structure */
-  grpc_combiner* combiner;
-  /** owning stack */
-  grpc_channel_stack* owning_stack;
-  /** interested parties (owned) */
-  grpc_pollset_set* interested_parties;
-  // Client channel factory.
-  grpc_core::ClientChannelFactory* client_channel_factory;
-  // Subchannel pool.
-  grpc_core::RefCountedPtr<grpc_core::SubchannelPoolInterface> subchannel_pool;
-
-  grpc_core::channelz::ClientChannelNode* channelz_node;
-
-  // Resolving LB policy.
-  grpc_core::OrphanablePtr<LoadBalancingPolicy> resolving_lb_policy;
-  // Subchannel picker from LB policy.
-  grpc_core::UniquePtr<LoadBalancingPolicy::SubchannelPicker> picker;
-  // Linked list of queued picks.
-  QueuedPick* queued_picks;
-
-  bool have_service_config;
-  /** retry throttle data from service config */
-  grpc_core::RefCountedPtr<ServerRetryThrottleData> retry_throttle_data;
-  /** per-method service config data */
-  grpc_core::RefCountedPtr<ClientChannelMethodParamsTable> method_params_table;
-
-  /* the following properties are guarded by a mutex since APIs require them
-     to be instantaneously available */
-  gpr_mu info_mu;
-  grpc_core::UniquePtr<char> info_lb_policy_name;
-  grpc_core::UniquePtr<char> info_service_config_json;
-
-  grpc_connectivity_state_tracker state_tracker;
-  grpc_error* disconnect_error;
-
-  /* external_connectivity_watcher_list head is guarded by its own mutex, since
-   * counts need to be grabbed immediately without polling on a cq */
-  gpr_mu external_connectivity_watcher_list_mu;
-  struct external_connectivity_watcher* external_connectivity_watcher_list_head;
-} channel_data;
-
-// Forward declarations.
-static void start_pick_locked(void* arg, grpc_error* ignored);
-static void maybe_apply_service_config_to_call_locked(grpc_call_element* elem);
-
-static const char* get_channel_connectivity_state_change_string(
-    grpc_connectivity_state state) {
-  switch (state) {
-    case GRPC_CHANNEL_IDLE:
-      return "Channel state change to IDLE";
-    case GRPC_CHANNEL_CONNECTING:
-      return "Channel state change to CONNECTING";
-    case GRPC_CHANNEL_READY:
-      return "Channel state change to READY";
-    case GRPC_CHANNEL_TRANSIENT_FAILURE:
-      return "Channel state change to TRANSIENT_FAILURE";
-    case GRPC_CHANNEL_SHUTDOWN:
-      return "Channel state change to SHUTDOWN";
+//
+// CallData definition
+//
+
+class CallData {
+ public:
+  static grpc_error* Init(grpc_call_element* elem,
+                          const grpc_call_element_args* args);
+  static void Destroy(grpc_call_element* elem,
+                      const grpc_call_final_info* final_info,
+                      grpc_closure* then_schedule_closure);
+  static void StartTransportStreamOpBatch(
+      grpc_call_element* elem, grpc_transport_stream_op_batch* batch);
+  static void SetPollent(grpc_call_element* elem, grpc_polling_entity* pollent);
+
+  RefCountedPtr<SubchannelCall> subchannel_call() { return subchannel_call_; }
+
+  // Invoked by channel for queued picks once resolver results are available.
+  void MaybeApplyServiceConfigToCallLocked(grpc_call_element* elem);
+
+  // Invoked by channel for queued picks when the picker is updated.
+  static void StartPickLocked(void* arg, grpc_error* error);
+
+ private:
+  class QueuedPickCanceller;
+
+  // State used for starting a retryable batch on a subchannel call.
+  // This provides its own grpc_transport_stream_op_batch and other data
+  // structures needed to populate the ops in the batch.
+  // We allocate one struct on the arena for each attempt at starting a
+  // batch on a given subchannel call.
+  struct SubchannelCallBatchData {
+    // Creates a SubchannelCallBatchData object on the call's arena with the
+    // specified refcount.  If set_on_complete is true, the batch's
+    // on_complete callback will be set to point to on_complete();
+    // otherwise, the batch's on_complete callback will be null.
+    static SubchannelCallBatchData* Create(grpc_call_element* elem,
+                                           int refcount, bool set_on_complete);
+
+    void Unref() {
+      if (gpr_unref(&refs)) Destroy();
+    }
+
+    SubchannelCallBatchData(grpc_call_element* elem, CallData* calld,
+                            int refcount, bool set_on_complete);
+    // All dtor code must be added in `Destroy()`. This is because we may
+    // call closures in `SubchannelCallBatchData` after they are unrefed by
+    // `Unref()`, and msan would complain about accessing this class
+    // after calling dtor. As a result we cannot call the `dtor` in `Unref()`.
+    // TODO(soheil): We should try to call the dtor in `Unref()`.
+    ~SubchannelCallBatchData() { Destroy(); }
+    void Destroy();
+
+    gpr_refcount refs;
+    grpc_call_element* elem;
+    RefCountedPtr<SubchannelCall> subchannel_call;
+    // The batch to use in the subchannel call.
+    // Its payload field points to SubchannelCallRetryState::batch_payload.
+    grpc_transport_stream_op_batch batch;
+    // For intercepting on_complete.
+    grpc_closure on_complete;
+  };
+
+  // Retry state associated with a subchannel call.
+  // Stored in the parent_data of the subchannel call object.
+  struct SubchannelCallRetryState {
+    explicit SubchannelCallRetryState(grpc_call_context_element* context)
+        : batch_payload(context),
+          started_send_initial_metadata(false),
+          completed_send_initial_metadata(false),
+          started_send_trailing_metadata(false),
+          completed_send_trailing_metadata(false),
+          started_recv_initial_metadata(false),
+          completed_recv_initial_metadata(false),
+          started_recv_trailing_metadata(false),
+          completed_recv_trailing_metadata(false),
+          retry_dispatched(false) {}
+
+    // SubchannelCallBatchData.batch.payload points to this.
+    grpc_transport_stream_op_batch_payload batch_payload;
+    // For send_initial_metadata.
+    // Note that we need to make a copy of the initial metadata for each
+    // subchannel call instead of just referring to the copy in call_data,
+    // because filters in the subchannel stack will probably add entries,
+    // so we need to start in a pristine state for each attempt of the call.
+    grpc_linked_mdelem* send_initial_metadata_storage;
+    grpc_metadata_batch send_initial_metadata;
+    // For send_message.
+    // TODO(roth): Restructure this to eliminate use of ManualConstructor.
+    ManualConstructor<ByteStreamCache::CachingByteStream> send_message;
+    // For send_trailing_metadata.
+    grpc_linked_mdelem* send_trailing_metadata_storage;
+    grpc_metadata_batch send_trailing_metadata;
+    // For intercepting recv_initial_metadata.
+    grpc_metadata_batch recv_initial_metadata;
+    grpc_closure recv_initial_metadata_ready;
+    bool trailing_metadata_available = false;
+    // For intercepting recv_message.
+    grpc_closure recv_message_ready;
+    OrphanablePtr<ByteStream> recv_message;
+    // For intercepting recv_trailing_metadata.
+    grpc_metadata_batch recv_trailing_metadata;
+    grpc_transport_stream_stats collect_stats;
+    grpc_closure recv_trailing_metadata_ready;
+    // These fields indicate which ops have been started and completed on
+    // this subchannel call.
+    size_t started_send_message_count = 0;
+    size_t completed_send_message_count = 0;
+    size_t started_recv_message_count = 0;
+    size_t completed_recv_message_count = 0;
+    bool started_send_initial_metadata : 1;
+    bool completed_send_initial_metadata : 1;
+    bool started_send_trailing_metadata : 1;
+    bool completed_send_trailing_metadata : 1;
+    bool started_recv_initial_metadata : 1;
+    bool completed_recv_initial_metadata : 1;
+    bool started_recv_trailing_metadata : 1;
+    bool completed_recv_trailing_metadata : 1;
+    // State for callback processing.
+    SubchannelCallBatchData* recv_initial_metadata_ready_deferred_batch =
+        nullptr;
+    grpc_error* recv_initial_metadata_error = GRPC_ERROR_NONE;
+    SubchannelCallBatchData* recv_message_ready_deferred_batch = nullptr;
+    grpc_error* recv_message_error = GRPC_ERROR_NONE;
+    SubchannelCallBatchData* recv_trailing_metadata_internal_batch = nullptr;
+    // NOTE: Do not move this next to the metadata bitfields above. That would
+    //       save space but will also result in a data race because compiler
+    //       will generate a 2 byte store which overwrites the meta-data
+    //       fields upon setting this field.
+    bool retry_dispatched : 1;
+  };
+
+  // Pending batches stored in call data.
+  struct PendingBatch {
+    // The pending batch.  If nullptr, this slot is empty.
+    grpc_transport_stream_op_batch* batch;
+    // Indicates whether payload for send ops has been cached in CallData.
+    bool send_ops_cached;
+  };
+
+  CallData(grpc_call_element* elem, const ChannelData& chand,
+           const grpc_call_element_args& args);
+  ~CallData();
+
+  // Caches data for send ops so that it can be retried later, if not
+  // already cached.
+  void MaybeCacheSendOpsForBatch(PendingBatch* pending);
+  void FreeCachedSendInitialMetadata(ChannelData* chand);
+  // Frees cached send_message at index idx.
+  void FreeCachedSendMessage(ChannelData* chand, size_t idx);
+  void FreeCachedSendTrailingMetadata(ChannelData* chand);
+  // Frees cached send ops that have already been completed after
+  // committing the call.
+  void FreeCachedSendOpDataAfterCommit(grpc_call_element* elem,
+                                       SubchannelCallRetryState* retry_state);
+  // Frees cached send ops that were completed by the completed batch in
+  // batch_data.  Used when batches are completed after the call is committed.
+  void FreeCachedSendOpDataForCompletedBatch(
+      grpc_call_element* elem, SubchannelCallBatchData* batch_data,
+      SubchannelCallRetryState* retry_state);
+
+  static void MaybeInjectRecvTrailingMetadataReadyForLoadBalancingPolicy(
+      const LoadBalancingPolicy::PickArgs& pick,
+      grpc_transport_stream_op_batch* batch);
+
+  // Returns the index into pending_batches_ to be used for batch.
+  static size_t GetBatchIndex(grpc_transport_stream_op_batch* batch);
+  void PendingBatchesAdd(grpc_call_element* elem,
+                         grpc_transport_stream_op_batch* batch);
+  void PendingBatchClear(PendingBatch* pending);
+  void MaybeClearPendingBatch(grpc_call_element* elem, PendingBatch* pending);
+  static void FailPendingBatchInCallCombiner(void* arg, grpc_error* error);
+  // A predicate type and some useful implementations for PendingBatchesFail().
+  typedef bool (*YieldCallCombinerPredicate)(
+      const CallCombinerClosureList& closures);
+  static bool YieldCallCombiner(const CallCombinerClosureList& closures) {
+    return true;
   }
-  GPR_UNREACHABLE_CODE(return "UNKNOWN");
+  static bool NoYieldCallCombiner(const CallCombinerClosureList& closures) {
+    return false;
+  }
+  static bool YieldCallCombinerIfPendingBatchesFound(
+      const CallCombinerClosureList& closures) {
+    return closures.size() > 0;
+  }
+  // Fails all pending batches.
+  // If yield_call_combiner_predicate returns true, assumes responsibility for
+  // yielding the call combiner.
+  void PendingBatchesFail(
+      grpc_call_element* elem, grpc_error* error,
+      YieldCallCombinerPredicate yield_call_combiner_predicate);
+  static void ResumePendingBatchInCallCombiner(void* arg, grpc_error* ignored);
+  // Resumes all pending batches on subchannel_call_.
+  void PendingBatchesResume(grpc_call_element* elem);
+  // Returns a pointer to the first pending batch for which predicate(batch)
+  // returns true, or null if not found.
+  template <typename Predicate>
+  PendingBatch* PendingBatchFind(grpc_call_element* elem,
+                                 const char* log_message, Predicate predicate);
+
+  // Commits the call so that no further retry attempts will be performed.
+  void RetryCommit(grpc_call_element* elem,
+                   SubchannelCallRetryState* retry_state);
+  // Starts a retry after appropriate back-off.
+  void DoRetry(grpc_call_element* elem, SubchannelCallRetryState* retry_state,
+               grpc_millis server_pushback_ms);
+  // Returns true if the call is being retried.
+  bool MaybeRetry(grpc_call_element* elem, SubchannelCallBatchData* batch_data,
+                  grpc_status_code status, grpc_mdelem* server_pushback_md);
+
+  // Invokes recv_initial_metadata_ready for a subchannel batch.
+  static void InvokeRecvInitialMetadataCallback(void* arg, grpc_error* error);
+  // Intercepts recv_initial_metadata_ready callback for retries.
+  // Commits the call and returns the initial metadata up the stack.
+  static void RecvInitialMetadataReady(void* arg, grpc_error* error);
+
+  // Invokes recv_message_ready for a subchannel batch.
+  static void InvokeRecvMessageCallback(void* arg, grpc_error* error);
+  // Intercepts recv_message_ready callback for retries.
+  // Commits the call and returns the message up the stack.
+  static void RecvMessageReady(void* arg, grpc_error* error);
+
+  // Sets *status and *server_pushback_md based on md_batch and error.
+  // Only sets *server_pushback_md if server_pushback_md != nullptr.
+  void GetCallStatus(grpc_call_element* elem, grpc_metadata_batch* md_batch,
+                     grpc_error* error, grpc_status_code* status,
+                     grpc_mdelem** server_pushback_md);
+  // Adds recv_trailing_metadata_ready closure to closures.
+  void AddClosureForRecvTrailingMetadataReady(
+      grpc_call_element* elem, SubchannelCallBatchData* batch_data,
+      grpc_error* error, CallCombinerClosureList* closures);
+  // Adds any necessary closures for deferred recv_initial_metadata and
+  // recv_message callbacks to closures.
+  static void AddClosuresForDeferredRecvCallbacks(
+      SubchannelCallBatchData* batch_data,
+      SubchannelCallRetryState* retry_state, CallCombinerClosureList* closures);
+  // Returns true if any op in the batch was not yet started.
+  // Only looks at send ops, since recv ops are always started immediately.
+  bool PendingBatchIsUnstarted(PendingBatch* pending,
+                               SubchannelCallRetryState* retry_state);
+  // For any pending batch containing an op that has not yet been started,
+  // adds the pending batch's completion closures to closures.
+  void AddClosuresToFailUnstartedPendingBatches(
+      grpc_call_element* elem, SubchannelCallRetryState* retry_state,
+      grpc_error* error, CallCombinerClosureList* closures);
+  // Runs necessary closures upon completion of a call attempt.
+  void RunClosuresForCompletedCall(SubchannelCallBatchData* batch_data,
+                                   grpc_error* error);
+  // Intercepts recv_trailing_metadata_ready callback for retries.
+  // Commits the call and returns the trailing metadata up the stack.
+  static void RecvTrailingMetadataReady(void* arg, grpc_error* error);
+
+  // Adds the on_complete closure for the pending batch completed in
+  // batch_data to closures.
+  void AddClosuresForCompletedPendingBatch(
+      grpc_call_element* elem, SubchannelCallBatchData* batch_data,
+      SubchannelCallRetryState* retry_state, grpc_error* error,
+      CallCombinerClosureList* closures);
+
+  // If there are any cached ops to replay or pending ops to start on the
+  // subchannel call, adds a closure to closures to invoke
+  // StartRetriableSubchannelBatches().
+  void AddClosuresForReplayOrPendingSendOps(
+      grpc_call_element* elem, SubchannelCallBatchData* batch_data,
+      SubchannelCallRetryState* retry_state, CallCombinerClosureList* closures);
+
+  // Callback used to intercept on_complete from subchannel calls.
+  // Called only when retries are enabled.
+  static void OnComplete(void* arg, grpc_error* error);
+
+  static void StartBatchInCallCombiner(void* arg, grpc_error* ignored);
+  // Adds a closure to closures that will execute batch in the call combiner.
+  void AddClosureForSubchannelBatch(grpc_call_element* elem,
+                                    grpc_transport_stream_op_batch* batch,
+                                    CallCombinerClosureList* closures);
+  // Adds retriable send_initial_metadata op to batch_data.
+  void AddRetriableSendInitialMetadataOp(SubchannelCallRetryState* retry_state,
+                                         SubchannelCallBatchData* batch_data);
+  // Adds retriable send_message op to batch_data.
+  void AddRetriableSendMessageOp(grpc_call_element* elem,
+                                 SubchannelCallRetryState* retry_state,
+                                 SubchannelCallBatchData* batch_data);
+  // Adds retriable send_trailing_metadata op to batch_data.
+  void AddRetriableSendTrailingMetadataOp(SubchannelCallRetryState* retry_state,
+                                          SubchannelCallBatchData* batch_data);
+  // Adds retriable recv_initial_metadata op to batch_data.
+  void AddRetriableRecvInitialMetadataOp(SubchannelCallRetryState* retry_state,
+                                         SubchannelCallBatchData* batch_data);
+  // Adds retriable recv_message op to batch_data.
+  void AddRetriableRecvMessageOp(SubchannelCallRetryState* retry_state,
+                                 SubchannelCallBatchData* batch_data);
+  // Adds retriable recv_trailing_metadata op to batch_data.
+  void AddRetriableRecvTrailingMetadataOp(SubchannelCallRetryState* retry_state,
+                                          SubchannelCallBatchData* batch_data);
+  // Helper function used to start a recv_trailing_metadata batch.  This
+  // is used in the case where a recv_initial_metadata or recv_message
+  // op fails in a way that we know the call is over but when the application
+  // has not yet started its own recv_trailing_metadata op.
+  void StartInternalRecvTrailingMetadata(grpc_call_element* elem);
+  // If there are any cached send ops that need to be replayed on the
+  // current subchannel call, creates and returns a new subchannel batch
+  // to replay those ops.  Otherwise, returns nullptr.
+  SubchannelCallBatchData* MaybeCreateSubchannelBatchForReplay(
+      grpc_call_element* elem, SubchannelCallRetryState* retry_state);
+  // Adds subchannel batches for pending batches to closures.
+  void AddSubchannelBatchesForPendingBatches(
+      grpc_call_element* elem, SubchannelCallRetryState* retry_state,
+      CallCombinerClosureList* closures);
+  // Constructs and starts whatever subchannel batches are needed on the
+  // subchannel call.
+  static void StartRetriableSubchannelBatches(void* arg, grpc_error* ignored);
+
+  void CreateSubchannelCall(grpc_call_element* elem);
+  // Invoked when a pick is completed, on both success or failure.
+  static void PickDone(void* arg, grpc_error* error);
+  // Removes the call from the channel's list of queued picks.
+  void RemoveCallFromQueuedPicksLocked(grpc_call_element* elem);
+  // Adds the call to the channel's list of queued picks.
+  void AddCallToQueuedPicksLocked(grpc_call_element* elem);
+  // Applies service config to the call.  Must be invoked once we know
+  // that the resolver has returned results to the channel.
+  void ApplyServiceConfigToCallLocked(grpc_call_element* elem);
+
+  // State for handling deadlines.
+  // The code in deadline_filter.c requires this to be the first field.
+  // TODO(roth): This is slightly sub-optimal in that grpc_deadline_state
+  // and this struct both independently store pointers to the call stack
+  // and call combiner.  If/when we have time, find a way to avoid this
+  // without breaking the grpc_deadline_state abstraction.
+  grpc_deadline_state deadline_state_;
+
+  grpc_slice path_;  // Request path.
+  gpr_timespec call_start_time_;
+  grpc_millis deadline_;
+  Arena* arena_;
+  grpc_call_stack* owning_call_;
+  CallCombiner* call_combiner_;
+  grpc_call_context_element* call_context_;
+
+  RefCountedPtr<ServerRetryThrottleData> retry_throttle_data_;
+  ServiceConfig::CallData service_config_call_data_;
+  const ClientChannelMethodParsedObject* method_params_ = nullptr;
+
+  RefCountedPtr<SubchannelCall> subchannel_call_;
+
+  // Set when we get a cancel_stream op.
+  grpc_error* cancel_error_ = GRPC_ERROR_NONE;
+
+  ChannelData::QueuedPick pick_;
+  bool pick_queued_ = false;
+  bool service_config_applied_ = false;
+  QueuedPickCanceller* pick_canceller_ = nullptr;
+  grpc_closure pick_closure_;
+
+  grpc_polling_entity* pollent_ = nullptr;
+
+  // Batches are added to this list when received from above.
+  // They are removed when we are done handling the batch (i.e., when
+  // either we have invoked all of the batch's callbacks or we have
+  // passed the batch down to the subchannel call and are not
+  // intercepting any of its callbacks).
+  PendingBatch pending_batches_[MAX_PENDING_BATCHES] = {};
+  bool pending_send_initial_metadata_ : 1;
+  bool pending_send_message_ : 1;
+  bool pending_send_trailing_metadata_ : 1;
+
+  // Retry state.
+  bool enable_retries_ : 1;
+  bool retry_committed_ : 1;
+  bool last_attempt_got_server_pushback_ : 1;
+  int num_attempts_completed_ = 0;
+  size_t bytes_buffered_for_retry_ = 0;
+  // TODO(roth): Restructure this to eliminate use of ManualConstructor.
+  ManualConstructor<BackOff> retry_backoff_;
+  grpc_timer retry_timer_;
+
+  // The number of pending retriable subchannel batches containing send ops.
+  // We hold a ref to the call stack while this is non-zero, since replay
+  // batches may not complete until after all callbacks have been returned
+  // to the surface, and we need to make sure that the call is not destroyed
+  // until all of these batches have completed.
+  // Note that we actually only need to track replay batches, but it's
+  // easier to track all batches with send ops.
+  int num_pending_retriable_subchannel_send_batches_ = 0;
+
+  // Cached data for retrying send ops.
+  // send_initial_metadata
+  bool seen_send_initial_metadata_ = false;
+  grpc_linked_mdelem* send_initial_metadata_storage_ = nullptr;
+  grpc_metadata_batch send_initial_metadata_;
+  uint32_t send_initial_metadata_flags_;
+  gpr_atm* peer_string_;
+  // send_message
+  // When we get a send_message op, we replace the original byte stream
+  // with a CachingByteStream that caches the slices to a local buffer for
+  // use in retries.
+  // Note: We inline the cache for the first 3 send_message ops and use
+  // dynamic allocation after that.  This number was essentially picked
+  // at random; it could be changed in the future to tune performance.
+  InlinedVector<ByteStreamCache*, 3> send_messages_;
+  // send_trailing_metadata
+  bool seen_send_trailing_metadata_ = false;
+  grpc_linked_mdelem* send_trailing_metadata_storage_ = nullptr;
+  grpc_metadata_batch send_trailing_metadata_;
+};
+
+//
+// ChannelData::ConnectivityStateAndPickerSetter
+//
+
+// A fire-and-forget class that sets the channel's connectivity state
+// and then hops into the data plane combiner to update the picker.
+// Must be instantiated while holding the control plane combiner.
+// Deletes itself when done.
+class ChannelData::ConnectivityStateAndPickerSetter {
+ public:
+  ConnectivityStateAndPickerSetter(
+      ChannelData* chand, grpc_connectivity_state state, const char* reason,
+      UniquePtr<LoadBalancingPolicy::SubchannelPicker> picker)
+      : chand_(chand), picker_(std::move(picker)) {
+    // Update connectivity state here, while holding control plane combiner.
+    grpc_connectivity_state_set(&chand->state_tracker_, state, reason);
+    if (chand->channelz_node_ != nullptr) {
+      chand->channelz_node_->AddTraceEvent(
+          channelz::ChannelTrace::Severity::Info,
+          grpc_slice_from_static_string(
+              GetChannelConnectivityStateChangeString(state)));
+    }
+    // Bounce into the data plane combiner to reset the picker.
+    GRPC_CHANNEL_STACK_REF(chand->owning_stack_,
+                           "ConnectivityStateAndPickerSetter");
+    GRPC_CLOSURE_INIT(&closure_, SetPicker, this,
+                      grpc_combiner_scheduler(chand->data_plane_combiner_));
+    GRPC_CLOSURE_SCHED(&closure_, GRPC_ERROR_NONE);
+  }
+
+ private:
+  static const char* GetChannelConnectivityStateChangeString(
+      grpc_connectivity_state state) {
+    switch (state) {
+      case GRPC_CHANNEL_IDLE:
+        return "Channel state change to IDLE";
+      case GRPC_CHANNEL_CONNECTING:
+        return "Channel state change to CONNECTING";
+      case GRPC_CHANNEL_READY:
+        return "Channel state change to READY";
+      case GRPC_CHANNEL_TRANSIENT_FAILURE:
+        return "Channel state change to TRANSIENT_FAILURE";
+      case GRPC_CHANNEL_SHUTDOWN:
+        return "Channel state change to SHUTDOWN";
+    }
+    GPR_UNREACHABLE_CODE(return "UNKNOWN");
+  }
+
+  static void SetPicker(void* arg, grpc_error* ignored) {
+    auto* self = static_cast<ConnectivityStateAndPickerSetter*>(arg);
+    // Update picker.
+    self->chand_->picker_ = std::move(self->picker_);
+    // Re-process queued picks.
+    for (QueuedPick* pick = self->chand_->queued_picks_; pick != nullptr;
+         pick = pick->next) {
+      CallData::StartPickLocked(pick->elem, GRPC_ERROR_NONE);
+    }
+    // Clean up.
+    GRPC_CHANNEL_STACK_UNREF(self->chand_->owning_stack_,
+                             "ConnectivityStateAndPickerSetter");
+    Delete(self);
+  }
+
+  ChannelData* chand_;
+  UniquePtr<LoadBalancingPolicy::SubchannelPicker> picker_;
+  grpc_closure closure_;
+};
+
+//
+// ChannelData::ServiceConfigSetter
+//
+
+// A fire-and-forget class that sets the channel's service config data
+// in the data plane combiner.  Deletes itself when done.
+class ChannelData::ServiceConfigSetter {
+ public:
+  ServiceConfigSetter(
+      ChannelData* chand,
+      Optional<internal::ClientChannelGlobalParsedObject::RetryThrottling>
+          retry_throttle_data,
+      RefCountedPtr<ServiceConfig> service_config)
+      : chand_(chand),
+        retry_throttle_data_(retry_throttle_data),
+        service_config_(std::move(service_config)) {
+    GRPC_CHANNEL_STACK_REF(chand->owning_stack_, "ServiceConfigSetter");
+    GRPC_CLOSURE_INIT(&closure_, SetServiceConfigData, this,
+                      grpc_combiner_scheduler(chand->data_plane_combiner_));
+    GRPC_CLOSURE_SCHED(&closure_, GRPC_ERROR_NONE);
+  }
+
+ private:
+  static void SetServiceConfigData(void* arg, grpc_error* ignored) {
+    ServiceConfigSetter* self = static_cast<ServiceConfigSetter*>(arg);
+    ChannelData* chand = self->chand_;
+    // Update channel state.
+    chand->received_service_config_data_ = true;
+    if (self->retry_throttle_data_.has_value()) {
+      chand->retry_throttle_data_ =
+          internal::ServerRetryThrottleMap::GetDataForServer(
+              chand->server_name_.get(),
+              self->retry_throttle_data_.value().max_milli_tokens,
+              self->retry_throttle_data_.value().milli_token_ratio);
+    }
+    chand->service_config_ = std::move(self->service_config_);
+    // Apply service config to queued picks.
+    for (QueuedPick* pick = chand->queued_picks_; pick != nullptr;
+         pick = pick->next) {
+      CallData* calld = static_cast<CallData*>(pick->elem->call_data);
+      calld->MaybeApplyServiceConfigToCallLocked(pick->elem);
+    }
+    // Clean up.
+    GRPC_CHANNEL_STACK_UNREF(self->chand_->owning_stack_,
+                             "ServiceConfigSetter");
+    Delete(self);
+  }
+
+  ChannelData* chand_;
+  Optional<internal::ClientChannelGlobalParsedObject::RetryThrottling>
+      retry_throttle_data_;
+  RefCountedPtr<ServiceConfig> service_config_;
+  grpc_closure closure_;
+};
+
+//
+// ChannelData::ExternalConnectivityWatcher::WatcherList
+//
+
+int ChannelData::ExternalConnectivityWatcher::WatcherList::size() const {
+  MutexLock lock(&mu_);
+  int count = 0;
+  for (ExternalConnectivityWatcher* w = head_; w != nullptr; w = w->next_) {
+    ++count;
+  }
+  return count;
 }
 
-static void set_connectivity_state_and_picker_locked(
-    channel_data* chand, grpc_connectivity_state state, grpc_error* state_error,
-    const char* reason,
-    grpc_core::UniquePtr<LoadBalancingPolicy::SubchannelPicker> picker) {
-  // Update connectivity state.
-  grpc_connectivity_state_set(&chand->state_tracker, state, state_error,
-                              reason);
-  if (chand->channelz_node != nullptr) {
-    chand->channelz_node->AddTraceEvent(
-        grpc_core::channelz::ChannelTrace::Severity::Info,
-        grpc_slice_from_static_string(
-            get_channel_connectivity_state_change_string(state)));
+ChannelData::ExternalConnectivityWatcher*
+ChannelData::ExternalConnectivityWatcher::WatcherList::Lookup(
+    grpc_closure* on_complete) const {
+  MutexLock lock(&mu_);
+  ExternalConnectivityWatcher* w = head_;
+  while (w != nullptr && w->on_complete_ != on_complete) {
+    w = w->next_;
   }
-  // Update picker.
-  chand->picker = std::move(picker);
-  // Re-process queued picks.
-  for (QueuedPick* pick = chand->queued_picks; pick != nullptr;
-       pick = pick->next) {
-    start_pick_locked(pick->elem, GRPC_ERROR_NONE);
+  return w;
+}
+
+void ChannelData::ExternalConnectivityWatcher::WatcherList::Add(
+    ExternalConnectivityWatcher* watcher) {
+  GPR_ASSERT(Lookup(watcher->on_complete_) == nullptr);
+  MutexLock lock(&mu_);
+  GPR_ASSERT(watcher->next_ == nullptr);
+  watcher->next_ = head_;
+  head_ = watcher;
+}
+
+void ChannelData::ExternalConnectivityWatcher::WatcherList::Remove(
+    const ExternalConnectivityWatcher* watcher) {
+  MutexLock lock(&mu_);
+  if (watcher == head_) {
+    head_ = watcher->next_;
+    return;
+  }
+  for (ExternalConnectivityWatcher* w = head_; w != nullptr; w = w->next_) {
+    if (w->next_ == watcher) {
+      w->next_ = w->next_->next_;
+      return;
+    }
   }
+  GPR_UNREACHABLE_CODE(return );
 }
 
-namespace grpc_core {
-namespace {
+//
+// ChannelData::ExternalConnectivityWatcher
+//
+
+ChannelData::ExternalConnectivityWatcher::ExternalConnectivityWatcher(
+    ChannelData* chand, grpc_polling_entity pollent,
+    grpc_connectivity_state* state, grpc_closure* on_complete,
+    grpc_closure* watcher_timer_init)
+    : chand_(chand),
+      pollent_(pollent),
+      state_(state),
+      on_complete_(on_complete),
+      watcher_timer_init_(watcher_timer_init) {
+  grpc_polling_entity_add_to_pollset_set(&pollent_,
+                                         chand_->interested_parties_);
+  GRPC_CHANNEL_STACK_REF(chand_->owning_stack_, "ExternalConnectivityWatcher");
+  GRPC_CLOSURE_SCHED(
+      GRPC_CLOSURE_INIT(&my_closure_, WatchConnectivityStateLocked, this,
+                        grpc_combiner_scheduler(chand_->combiner_)),
+      GRPC_ERROR_NONE);
+}
+
+ChannelData::ExternalConnectivityWatcher::~ExternalConnectivityWatcher() {
+  grpc_polling_entity_del_from_pollset_set(&pollent_,
+                                           chand_->interested_parties_);
+  GRPC_CHANNEL_STACK_UNREF(chand_->owning_stack_,
+                           "ExternalConnectivityWatcher");
+}
+
+void ChannelData::ExternalConnectivityWatcher::OnWatchCompleteLocked(
+    void* arg, grpc_error* error) {
+  ExternalConnectivityWatcher* self =
+      static_cast<ExternalConnectivityWatcher*>(arg);
+  grpc_closure* on_complete = self->on_complete_;
+  self->chand_->external_connectivity_watcher_list_.Remove(self);
+  Delete(self);
+  GRPC_CLOSURE_SCHED(on_complete, GRPC_ERROR_REF(error));
+}
+
+void ChannelData::ExternalConnectivityWatcher::WatchConnectivityStateLocked(
+    void* arg, grpc_error* ignored) {
+  ExternalConnectivityWatcher* self =
+      static_cast<ExternalConnectivityWatcher*>(arg);
+  if (self->state_ == nullptr) {
+    // Handle cancellation.
+    GPR_ASSERT(self->watcher_timer_init_ == nullptr);
+    ExternalConnectivityWatcher* found =
+        self->chand_->external_connectivity_watcher_list_.Lookup(
+            self->on_complete_);
+    if (found != nullptr) {
+      grpc_connectivity_state_notify_on_state_change(
+          &found->chand_->state_tracker_, nullptr, &found->my_closure_);
+    }
+    Delete(self);
+    return;
+  }
+  // New watcher.
+  self->chand_->external_connectivity_watcher_list_.Add(self);
+  // This assumes that the closure is scheduled on the ExecCtx scheduler
+  // and that GRPC_CLOSURE_RUN would run the closure immediately.
+  GRPC_CLOSURE_RUN(self->watcher_timer_init_, GRPC_ERROR_NONE);
+  GRPC_CLOSURE_INIT(&self->my_closure_, OnWatchCompleteLocked, self,
+                    grpc_combiner_scheduler(self->chand_->combiner_));
+  grpc_connectivity_state_notify_on_state_change(
+      &self->chand_->state_tracker_, self->state_, &self->my_closure_);
+}
+
+//
+// ChannelData::ClientChannelControlHelper
+//
 
-class ClientChannelControlHelper
+class ChannelData::ClientChannelControlHelper
     : public LoadBalancingPolicy::ChannelControlHelper {
  public:
-  explicit ClientChannelControlHelper(channel_data* chand) : chand_(chand) {
-    GRPC_CHANNEL_STACK_REF(chand_->owning_stack, "ClientChannelControlHelper");
+  explicit ClientChannelControlHelper(ChannelData* chand) : chand_(chand) {
+    GRPC_CHANNEL_STACK_REF(chand_->owning_stack_, "ClientChannelControlHelper");
   }
 
   ~ClientChannelControlHelper() override {
-    GRPC_CHANNEL_STACK_UNREF(chand_->owning_stack,
+    GRPC_CHANNEL_STACK_UNREF(chand_->owning_stack_,
                              "ClientChannelControlHelper");
   }
 
   Subchannel* CreateSubchannel(const grpc_channel_args& args) override {
-    grpc_arg arg = SubchannelPoolInterface::CreateChannelArg(
-        chand_->subchannel_pool.get());
+    grpc_arg args_to_add[2];
+    int num_args_to_add = 0;
+    if (chand_->health_check_service_name_ != nullptr) {
+      args_to_add[0] = grpc_channel_arg_string_create(
+          const_cast<char*>("grpc.temp.health_check"),
+          const_cast<char*>(chand_->health_check_service_name_.get()));
+      num_args_to_add++;
+    }
+    args_to_add[num_args_to_add++] = SubchannelPoolInterface::CreateChannelArg(
+        chand_->subchannel_pool_.get());
     grpc_channel_args* new_args =
-        grpc_channel_args_copy_and_add(&args, &arg, 1);
+        grpc_channel_args_copy_and_add(&args, args_to_add, num_args_to_add);
     Subchannel* subchannel =
-        chand_->client_channel_factory->CreateSubchannel(new_args);
+        chand_->client_channel_factory_->CreateSubchannel(new_args);
     grpc_channel_args_destroy(new_args);
     return subchannel;
   }
 
   grpc_channel* CreateChannel(const char* target,
                               const grpc_channel_args& args) override {
-    return chand_->client_channel_factory->CreateChannel(target, &args);
+    return chand_->client_channel_factory_->CreateChannel(target, &args);
   }
 
   void UpdateState(
-      grpc_connectivity_state state, grpc_error* state_error,
+      grpc_connectivity_state state,
       UniquePtr<LoadBalancingPolicy::SubchannelPicker> picker) override {
-    if (grpc_client_channel_routing_trace.enabled()) {
-      const char* extra = chand_->disconnect_error == GRPC_ERROR_NONE
+    grpc_error* disconnect_error =
+        chand_->disconnect_error_.Load(MemoryOrder::ACQUIRE);
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
+      const char* extra = disconnect_error == GRPC_ERROR_NONE
                               ? ""
                               : " (ignoring -- channel shutting down)";
-      gpr_log(GPR_INFO, "chand=%p: update: state=%s error=%s picker=%p%s",
-              chand_, grpc_connectivity_state_name(state),
-              grpc_error_string(state_error), picker.get(), extra);
+      gpr_log(GPR_INFO, "chand=%p: update: state=%s picker=%p%s", chand_,
+              grpc_connectivity_state_name(state), picker.get(), extra);
     }
     // Do update only if not shutting down.
-    if (chand_->disconnect_error == GRPC_ERROR_NONE) {
-      set_connectivity_state_and_picker_locked(chand_, state, state_error,
-                                               "helper", std::move(picker));
-    } else {
-      GRPC_ERROR_UNREF(state_error);
+    if (disconnect_error == GRPC_ERROR_NONE) {
+      // Will delete itself.
+      New<ConnectivityStateAndPickerSetter>(chand_, state, "helper",
+                                            std::move(picker));
     }
   }
 
@@ -243,63 +998,326 @@ class ClientChannelControlHelper
   void RequestReresolution() override {}
 
  private:
-  channel_data* chand_;
+  ChannelData* chand_;
 };
 
-}  // namespace
-}  // namespace grpc_core
+//
+// ChannelData implementation
+//
+
+grpc_error* ChannelData::Init(grpc_channel_element* elem,
+                              grpc_channel_element_args* args) {
+  GPR_ASSERT(args->is_last);
+  GPR_ASSERT(elem->filter == &grpc_client_channel_filter);
+  grpc_error* error = GRPC_ERROR_NONE;
+  new (elem->channel_data) ChannelData(args, &error);
+  return error;
+}
 
-// Synchronous callback from chand->resolving_lb_policy to process a resolver
-// result update.
-static bool process_resolver_result_locked(
-    void* arg, grpc_core::Resolver::Result* result, const char** lb_policy_name,
-    grpc_core::RefCountedPtr<LoadBalancingPolicy::Config>* lb_policy_config) {
-  channel_data* chand = static_cast<channel_data*>(arg);
-  chand->have_service_config = true;
-  ProcessedResolverResult resolver_result(result, chand->enable_retries);
-  grpc_core::UniquePtr<char> service_config_json =
-      resolver_result.service_config_json();
-  if (grpc_client_channel_routing_trace.enabled()) {
-    gpr_log(GPR_INFO, "chand=%p: resolver returned service config: \"%s\"",
-            chand, service_config_json.get());
-  }
-  // Update channel state.
-  chand->retry_throttle_data = resolver_result.retry_throttle_data();
-  chand->method_params_table = resolver_result.method_params_table();
-  // Swap out the data used by cc_get_channel_info().
-  gpr_mu_lock(&chand->info_mu);
-  chand->info_lb_policy_name = resolver_result.lb_policy_name();
+void ChannelData::Destroy(grpc_channel_element* elem) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  chand->~ChannelData();
+}
+
+bool GetEnableRetries(const grpc_channel_args* args) {
+  return grpc_channel_arg_get_bool(
+      grpc_channel_args_find(args, GRPC_ARG_ENABLE_RETRIES), true);
+}
+
+size_t GetMaxPerRpcRetryBufferSize(const grpc_channel_args* args) {
+  return static_cast<size_t>(grpc_channel_arg_get_integer(
+      grpc_channel_args_find(args, GRPC_ARG_PER_RPC_RETRY_BUFFER_SIZE),
+      {DEFAULT_PER_RPC_RETRY_BUFFER_SIZE, 0, INT_MAX}));
+}
+
+RefCountedPtr<SubchannelPoolInterface> GetSubchannelPool(
+    const grpc_channel_args* args) {
+  const bool use_local_subchannel_pool = grpc_channel_arg_get_bool(
+      grpc_channel_args_find(args, GRPC_ARG_USE_LOCAL_SUBCHANNEL_POOL), false);
+  if (use_local_subchannel_pool) {
+    return MakeRefCounted<LocalSubchannelPool>();
+  }
+  return GlobalSubchannelPool::instance();
+}
+
+ChannelData::ChannelData(grpc_channel_element_args* args, grpc_error** error)
+    : deadline_checking_enabled_(
+          grpc_deadline_checking_enabled(args->channel_args)),
+      enable_retries_(GetEnableRetries(args->channel_args)),
+      per_rpc_retry_buffer_size_(
+          GetMaxPerRpcRetryBufferSize(args->channel_args)),
+      owning_stack_(args->channel_stack),
+      client_channel_factory_(
+          ClientChannelFactory::GetFromChannelArgs(args->channel_args)),
+      data_plane_combiner_(grpc_combiner_create()),
+      combiner_(grpc_combiner_create()),
+      interested_parties_(grpc_pollset_set_create()),
+      subchannel_pool_(GetSubchannelPool(args->channel_args)),
+      disconnect_error_(GRPC_ERROR_NONE) {
+  // Initialize data members.
+  grpc_connectivity_state_init(&state_tracker_, GRPC_CHANNEL_IDLE,
+                               "client_channel");
+  gpr_mu_init(&info_mu_);
+  // Start backup polling.
+  grpc_client_channel_start_backup_polling(interested_parties_);
+  // Check client channel factory.
+  if (client_channel_factory_ == nullptr) {
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Missing client channel factory in args for client channel filter");
+    return;
+  }
+  // Get server name to resolve, using proxy mapper if needed.
+  const char* server_uri = grpc_channel_arg_get_string(
+      grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVER_URI));
+  if (server_uri == nullptr) {
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "server URI channel arg missing or wrong type in client channel "
+        "filter");
+    return;
+  }
+  // Get default service config
+  const char* service_config_json = grpc_channel_arg_get_string(
+      grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVICE_CONFIG));
+  // TODO(yashkt): Make sure we set the channel in TRANSIENT_FAILURE on an
+  // invalid default service config
+  if (service_config_json != nullptr) {
+    *error = GRPC_ERROR_NONE;
+    default_service_config_ = ServiceConfig::Create(service_config_json, error);
+    if (*error != GRPC_ERROR_NONE) {
+      default_service_config_.reset();
+      return;
+    }
+  }
+  grpc_uri* uri = grpc_uri_parse(server_uri, true);
+  if (uri != nullptr && uri->path[0] != '\0') {
+    server_name_.reset(
+        gpr_strdup(uri->path[0] == '/' ? uri->path + 1 : uri->path));
+  }
+  grpc_uri_destroy(uri);
+  char* proxy_name = nullptr;
+  grpc_channel_args* new_args = nullptr;
+  grpc_proxy_mappers_map_name(server_uri, args->channel_args, &proxy_name,
+                              &new_args);
+  UniquePtr<char> target_uri(proxy_name != nullptr ? proxy_name
+                                                   : gpr_strdup(server_uri));
+  // Instantiate resolving LB policy.
+  LoadBalancingPolicy::Args lb_args;
+  lb_args.combiner = combiner_;
+  lb_args.channel_control_helper =
+      UniquePtr<LoadBalancingPolicy::ChannelControlHelper>(
+          New<ClientChannelControlHelper>(this));
+  lb_args.args = new_args != nullptr ? new_args : args->channel_args;
+  resolving_lb_policy_.reset(New<ResolvingLoadBalancingPolicy>(
+      std::move(lb_args), &grpc_client_channel_routing_trace,
+      std::move(target_uri), ProcessResolverResultLocked, this, error));
+  grpc_channel_args_destroy(new_args);
+  if (*error != GRPC_ERROR_NONE) {
+    // Orphan the resolving LB policy and flush the exec_ctx to ensure
+    // that it finishes shutting down.  This ensures that if we are
+    // failing, we destroy the ClientChannelControlHelper (and thus
+    // unref the channel stack) before we return.
+    // TODO(roth): This is not a complete solution, because it only
+    // catches the case where channel stack initialization fails in this
+    // particular filter.  If there is a failure in a different filter, we
+    // will leave a dangling ref here, which can cause a crash.  Fortunately,
+    // in practice, there are no other filters that can cause failures in
+    // channel stack initialization, so this works for now.
+    resolving_lb_policy_.reset();
+    ExecCtx::Get()->Flush();
+  } else {
+    grpc_pollset_set_add_pollset_set(resolving_lb_policy_->interested_parties(),
+                                     interested_parties_);
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
+      gpr_log(GPR_INFO, "chand=%p: created resolving_lb_policy=%p", this,
+              resolving_lb_policy_.get());
+    }
+  }
+}
+
+ChannelData::~ChannelData() {
+  if (resolving_lb_policy_ != nullptr) {
+    grpc_pollset_set_del_pollset_set(resolving_lb_policy_->interested_parties(),
+                                     interested_parties_);
+    resolving_lb_policy_.reset();
+  }
+  // Stop backup polling.
+  grpc_client_channel_stop_backup_polling(interested_parties_);
+  grpc_pollset_set_destroy(interested_parties_);
+  GRPC_COMBINER_UNREF(data_plane_combiner_, "client_channel");
+  GRPC_COMBINER_UNREF(combiner_, "client_channel");
+  GRPC_ERROR_UNREF(disconnect_error_.Load(MemoryOrder::RELAXED));
+  grpc_connectivity_state_destroy(&state_tracker_);
+  gpr_mu_destroy(&info_mu_);
+}
+
+void ChannelData::ProcessLbPolicy(
+    const Resolver::Result& resolver_result,
+    const internal::ClientChannelGlobalParsedObject* parsed_service_config,
+    UniquePtr<char>* lb_policy_name,
+    RefCountedPtr<ParsedLoadBalancingConfig>* lb_policy_config) {
+  // Prefer the LB policy name found in the service config.
+  if (parsed_service_config != nullptr &&
+      parsed_service_config->parsed_lb_config() != nullptr) {
+    lb_policy_name->reset(
+        gpr_strdup(parsed_service_config->parsed_lb_config()->name()));
+    *lb_policy_config = parsed_service_config->parsed_lb_config();
+    return;
+  }
+  const char* local_policy_name = nullptr;
+  if (parsed_service_config != nullptr &&
+      parsed_service_config->parsed_deprecated_lb_policy() != nullptr) {
+    local_policy_name = parsed_service_config->parsed_deprecated_lb_policy();
+  } else {
+    const grpc_arg* channel_arg =
+        grpc_channel_args_find(resolver_result.args, GRPC_ARG_LB_POLICY_NAME);
+    local_policy_name = grpc_channel_arg_get_string(channel_arg);
+  }
+  // Special case: If at least one balancer address is present, we use
+  // the grpclb policy, regardless of what the resolver has returned.
+  bool found_balancer_address = false;
+  for (size_t i = 0; i < resolver_result.addresses.size(); ++i) {
+    const ServerAddress& address = resolver_result.addresses[i];
+    if (address.IsBalancer()) {
+      found_balancer_address = true;
+      break;
+    }
+  }
+  if (found_balancer_address) {
+    if (local_policy_name != nullptr &&
+        strcmp(local_policy_name, "grpclb") != 0) {
+      gpr_log(GPR_INFO,
+              "resolver requested LB policy %s but provided at least one "
+              "balancer address -- forcing use of grpclb LB policy",
+              local_policy_name);
+    }
+    local_policy_name = "grpclb";
+  }
+  // Use pick_first if nothing was specified and we didn't select grpclb
+  // above.
+  lb_policy_name->reset(gpr_strdup(
+      local_policy_name == nullptr ? "pick_first" : local_policy_name));
+}
+
+// Synchronous callback from ResolvingLoadBalancingPolicy to process a
+// resolver result update.
+bool ChannelData::ProcessResolverResultLocked(
+    void* arg, const Resolver::Result& result, const char** lb_policy_name,
+    RefCountedPtr<ParsedLoadBalancingConfig>* lb_policy_config,
+    grpc_error** service_config_error) {
+  ChannelData* chand = static_cast<ChannelData*>(arg);
+  RefCountedPtr<ServiceConfig> service_config;
+  // If resolver did not return a service config or returned an invalid service
+  // config, we need a fallback service config.
+  if (result.service_config_error != GRPC_ERROR_NONE) {
+    // If the service config was invalid, then fallback to the saved service
+    // config. If there is no saved config either, use the default service
+    // config.
+    if (chand->saved_service_config_ != nullptr) {
+      service_config = chand->saved_service_config_;
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
+        gpr_log(GPR_INFO,
+                "chand=%p: resolver returned invalid service config. "
+                "Continuing to use previous service config.",
+                chand);
+      }
+    } else if (chand->default_service_config_ != nullptr) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
+        gpr_log(GPR_INFO,
+                "chand=%p: resolver returned invalid service config. Using "
+                "default service config provided by client API.",
+                chand);
+      }
+      service_config = chand->default_service_config_;
+    }
+  } else if (result.service_config == nullptr) {
+    if (chand->default_service_config_ != nullptr) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
+        gpr_log(GPR_INFO,
+                "chand=%p: resolver returned no service config. Using default "
+                "service config provided by client API.",
+                chand);
+      }
+      service_config = chand->default_service_config_;
+    }
+  } else {
+    service_config = result.service_config;
+  }
+  *service_config_error = GRPC_ERROR_REF(result.service_config_error);
+  if (service_config == nullptr &&
+      result.service_config_error != GRPC_ERROR_NONE) {
+    return false;
+  }
+  UniquePtr<char> service_config_json;
+  // Process service config.
+  const internal::ClientChannelGlobalParsedObject* parsed_service_config =
+      nullptr;
+  if (service_config != nullptr) {
+    parsed_service_config =
+        static_cast<const internal::ClientChannelGlobalParsedObject*>(
+            service_config->GetParsedGlobalServiceConfigObject(
+                internal::ClientChannelServiceConfigParser::ParserIndex()));
+  }
   const bool service_config_changed =
-      ((service_config_json == nullptr) !=
-       (chand->info_service_config_json == nullptr)) ||
-      (service_config_json != nullptr &&
-       strcmp(service_config_json.get(),
-              chand->info_service_config_json.get()) != 0);
-  chand->info_service_config_json = std::move(service_config_json);
-  gpr_mu_unlock(&chand->info_mu);
-  // Return results.
-  *lb_policy_name = chand->info_lb_policy_name.get();
-  *lb_policy_config = resolver_result.lb_policy_config();
-  // Apply service config to queued picks.
-  for (QueuedPick* pick = chand->queued_picks; pick != nullptr;
-       pick = pick->next) {
-    maybe_apply_service_config_to_call_locked(pick->elem);
+      ((service_config == nullptr) !=
+       (chand->saved_service_config_ == nullptr)) ||
+      (service_config != nullptr &&
+       strcmp(service_config->service_config_json(),
+              chand->saved_service_config_->service_config_json()) != 0);
+  if (service_config_changed) {
+    service_config_json.reset(gpr_strdup(
+        service_config != nullptr ? service_config->service_config_json()
+                                  : ""));
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
+      gpr_log(GPR_INFO,
+              "chand=%p: resolver returned updated service config: \"%s\"",
+              chand, service_config_json.get());
+    }
+    chand->saved_service_config_ = std::move(service_config);
+    if (parsed_service_config != nullptr) {
+      chand->health_check_service_name_.reset(
+          gpr_strdup(parsed_service_config->health_check_service_name()));
+    } else {
+      chand->health_check_service_name_.reset();
+    }
+  }
+  // We want to set the service config at least once. This should not really be
+  // needed, but we are doing it as a defensive approach. This can be removed,
+  // if we feel it is unnecessary.
+  if (service_config_changed || !chand->received_first_resolver_result_) {
+    chand->received_first_resolver_result_ = true;
+    Optional<internal::ClientChannelGlobalParsedObject::RetryThrottling>
+        retry_throttle_data;
+    if (parsed_service_config != nullptr) {
+      retry_throttle_data = parsed_service_config->retry_throttling();
+    }
+    // Create service config setter to update channel state in the data
+    // plane combiner.  Destroys itself when done.
+    New<ServiceConfigSetter>(chand, retry_throttle_data,
+                             chand->saved_service_config_);
+  }
+  UniquePtr<char> processed_lb_policy_name;
+  chand->ProcessLbPolicy(result, parsed_service_config,
+                         &processed_lb_policy_name, lb_policy_config);
+  // Swap out the data used by GetChannelInfo().
+  {
+    MutexLock lock(&chand->info_mu_);
+    chand->info_lb_policy_name_ = std::move(processed_lb_policy_name);
+    if (service_config_json != nullptr) {
+      chand->info_service_config_json_ = std::move(service_config_json);
+    }
   }
+  // Return results.
+  *lb_policy_name = chand->info_lb_policy_name_.get();
   return service_config_changed;
 }
 
-static grpc_error* do_ping_locked(channel_data* chand, grpc_transport_op* op) {
-  grpc_error* error = GRPC_ERROR_NONE;
-  grpc_connectivity_state state =
-      grpc_connectivity_state_get(&chand->state_tracker, &error);
-  if (state != GRPC_CHANNEL_READY) {
-    grpc_error* new_error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
-        "channel not connected", &error, 1);
-    GRPC_ERROR_UNREF(error);
-    return new_error;
+grpc_error* ChannelData::DoPingLocked(grpc_transport_op* op) {
+  if (grpc_connectivity_state_check(&state_tracker_) != GRPC_CHANNEL_READY) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("channel not connected");
   }
   LoadBalancingPolicy::PickArgs pick;
-  chand->picker->Pick(&pick, &error);
+  grpc_error* error = GRPC_ERROR_NONE;
+  picker_->Pick(&pick, &error);
   if (pick.connected_subchannel != nullptr) {
     pick.connected_subchannel->Ping(op->send_ping.on_initiate,
                                     op->send_ping.on_ack);
@@ -312,22 +1330,22 @@ static grpc_error* do_ping_locked(channel_data* chand, grpc_transport_op* op) {
   return error;
 }
 
-static void start_transport_op_locked(void* arg, grpc_error* error_ignored) {
+void ChannelData::StartTransportOpLocked(void* arg, grpc_error* ignored) {
   grpc_transport_op* op = static_cast<grpc_transport_op*>(arg);
   grpc_channel_element* elem =
       static_cast<grpc_channel_element*>(op->handler_private.extra_arg);
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  // Connectivity watch.
   if (op->on_connectivity_state_change != nullptr) {
     grpc_connectivity_state_notify_on_state_change(
-        &chand->state_tracker, op->connectivity_state,
+        &chand->state_tracker_, op->connectivity_state,
         op->on_connectivity_state_change);
     op->on_connectivity_state_change = nullptr;
     op->connectivity_state = nullptr;
   }
-
+  // Ping.
   if (op->send_ping.on_initiate != nullptr || op->send_ping.on_ack != nullptr) {
-    grpc_error* error = do_ping_locked(chand, op);
+    grpc_error* error = chand->DoPingLocked(op);
     if (error != GRPC_ERROR_NONE) {
       GRPC_CLOSURE_SCHED(op->send_ping.on_initiate, GRPC_ERROR_REF(error));
       GRPC_CLOSURE_SCHED(op->send_ping.on_ack, error);
@@ -336,201 +1354,111 @@ static void start_transport_op_locked(void* arg, grpc_error* error_ignored) {
     op->send_ping.on_initiate = nullptr;
     op->send_ping.on_ack = nullptr;
   }
-
+  // Reset backoff.
   if (op->reset_connect_backoff) {
-    chand->resolving_lb_policy->ResetBackoffLocked();
+    if (chand->resolving_lb_policy_ != nullptr) {
+      chand->resolving_lb_policy_->ResetBackoffLocked();
+    }
   }
-
+  // Disconnect.
   if (op->disconnect_with_error != GRPC_ERROR_NONE) {
-    chand->disconnect_error = op->disconnect_with_error;
+    grpc_error* error = GRPC_ERROR_NONE;
+    GPR_ASSERT(chand->disconnect_error_.CompareExchangeStrong(
+        &error, op->disconnect_with_error, MemoryOrder::ACQ_REL,
+        MemoryOrder::ACQUIRE));
     grpc_pollset_set_del_pollset_set(
-        chand->resolving_lb_policy->interested_parties(),
-        chand->interested_parties);
-    chand->resolving_lb_policy.reset();
-    set_connectivity_state_and_picker_locked(
-        chand, GRPC_CHANNEL_SHUTDOWN, GRPC_ERROR_REF(op->disconnect_with_error),
-        "shutdown from API",
-        grpc_core::UniquePtr<LoadBalancingPolicy::SubchannelPicker>(
-            grpc_core::New<LoadBalancingPolicy::TransientFailurePicker>(
+        chand->resolving_lb_policy_->interested_parties(),
+        chand->interested_parties_);
+    chand->resolving_lb_policy_.reset();
+    // Will delete itself.
+    New<ConnectivityStateAndPickerSetter>(
+        chand, GRPC_CHANNEL_SHUTDOWN, "shutdown from API",
+        UniquePtr<LoadBalancingPolicy::SubchannelPicker>(
+            New<LoadBalancingPolicy::TransientFailurePicker>(
                 GRPC_ERROR_REF(op->disconnect_with_error))));
   }
-
-  GRPC_CHANNEL_STACK_UNREF(chand->owning_stack, "start_transport_op");
+  GRPC_CHANNEL_STACK_UNREF(chand->owning_stack_, "start_transport_op");
   GRPC_CLOSURE_SCHED(op->on_consumed, GRPC_ERROR_NONE);
 }
 
-static void cc_start_transport_op(grpc_channel_element* elem,
-                                  grpc_transport_op* op) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-
+void ChannelData::StartTransportOp(grpc_channel_element* elem,
+                                   grpc_transport_op* op) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
   GPR_ASSERT(op->set_accept_stream == false);
+  // Handle bind_pollset.
   if (op->bind_pollset != nullptr) {
-    grpc_pollset_set_add_pollset(chand->interested_parties, op->bind_pollset);
+    grpc_pollset_set_add_pollset(chand->interested_parties_, op->bind_pollset);
   }
-
+  // Pop into control plane combiner for remaining ops.
   op->handler_private.extra_arg = elem;
-  GRPC_CHANNEL_STACK_REF(chand->owning_stack, "start_transport_op");
+  GRPC_CHANNEL_STACK_REF(chand->owning_stack_, "start_transport_op");
   GRPC_CLOSURE_SCHED(
-      GRPC_CLOSURE_INIT(&op->handler_private.closure, start_transport_op_locked,
-                        op, grpc_combiner_scheduler(chand->combiner)),
+      GRPC_CLOSURE_INIT(&op->handler_private.closure,
+                        ChannelData::StartTransportOpLocked, op,
+                        grpc_combiner_scheduler(chand->combiner_)),
       GRPC_ERROR_NONE);
 }
 
-static void cc_get_channel_info(grpc_channel_element* elem,
-                                const grpc_channel_info* info) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  gpr_mu_lock(&chand->info_mu);
+void ChannelData::GetChannelInfo(grpc_channel_element* elem,
+                                 const grpc_channel_info* info) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  MutexLock lock(&chand->info_mu_);
   if (info->lb_policy_name != nullptr) {
-    *info->lb_policy_name = gpr_strdup(chand->info_lb_policy_name.get());
+    *info->lb_policy_name = gpr_strdup(chand->info_lb_policy_name_.get());
   }
   if (info->service_config_json != nullptr) {
     *info->service_config_json =
-        gpr_strdup(chand->info_service_config_json.get());
+        gpr_strdup(chand->info_service_config_json_.get());
   }
-  gpr_mu_unlock(&chand->info_mu);
 }
 
-/* Constructor for channel_data */
-static grpc_error* cc_init_channel_elem(grpc_channel_element* elem,
-                                        grpc_channel_element_args* args) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  GPR_ASSERT(args->is_last);
-  GPR_ASSERT(elem->filter == &grpc_client_channel_filter);
-  // Initialize data members.
-  chand->combiner = grpc_combiner_create();
-  grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE,
-                               "client_channel");
-  chand->disconnect_error = GRPC_ERROR_NONE;
-  gpr_mu_init(&chand->info_mu);
-  gpr_mu_init(&chand->external_connectivity_watcher_list_mu);
-
-  gpr_mu_lock(&chand->external_connectivity_watcher_list_mu);
-  chand->external_connectivity_watcher_list_head = nullptr;
-  gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu);
-
-  chand->owning_stack = args->channel_stack;
-  chand->deadline_checking_enabled =
-      grpc_deadline_checking_enabled(args->channel_args);
-  chand->interested_parties = grpc_pollset_set_create();
-  grpc_client_channel_start_backup_polling(chand->interested_parties);
-  // Record max per-RPC retry buffer size.
-  const grpc_arg* arg = grpc_channel_args_find(
-      args->channel_args, GRPC_ARG_PER_RPC_RETRY_BUFFER_SIZE);
-  chand->per_rpc_retry_buffer_size = (size_t)grpc_channel_arg_get_integer(
-      arg, {DEFAULT_PER_RPC_RETRY_BUFFER_SIZE, 0, INT_MAX});
-  // Record enable_retries.
-  arg = grpc_channel_args_find(args->channel_args, GRPC_ARG_ENABLE_RETRIES);
-  chand->enable_retries = grpc_channel_arg_get_bool(arg, true);
-  // Record client channel factory.
-  chand->client_channel_factory =
-      grpc_core::ClientChannelFactory::GetFromChannelArgs(args->channel_args);
-  if (chand->client_channel_factory == nullptr) {
-    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-        "Missing client channel factory in args for client channel filter");
-  }
-  // Get server name to resolve, using proxy mapper if needed.
-  arg = grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVER_URI);
-  if (arg == nullptr) {
-    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-        "Missing server uri in args for client channel filter");
-  }
-  if (arg->type != GRPC_ARG_STRING) {
-    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-        "server uri arg must be a string");
-  }
-  char* proxy_name = nullptr;
-  grpc_channel_args* new_args = nullptr;
-  grpc_proxy_mappers_map_name(arg->value.string, args->channel_args,
-                              &proxy_name, &new_args);
-  grpc_core::UniquePtr<char> target_uri(
-      proxy_name != nullptr ? proxy_name : gpr_strdup(arg->value.string));
-  // Instantiate subchannel pool.
-  arg = grpc_channel_args_find(args->channel_args,
-                               GRPC_ARG_USE_LOCAL_SUBCHANNEL_POOL);
-  if (grpc_channel_arg_get_bool(arg, false)) {
-    chand->subchannel_pool =
-        grpc_core::MakeRefCounted<grpc_core::LocalSubchannelPool>();
-  } else {
-    chand->subchannel_pool = grpc_core::GlobalSubchannelPool::instance();
-  }
-  // Instantiate resolving LB policy.
-  LoadBalancingPolicy::Args lb_args;
-  lb_args.combiner = chand->combiner;
-  lb_args.channel_control_helper =
-      grpc_core::UniquePtr<LoadBalancingPolicy::ChannelControlHelper>(
-          grpc_core::New<grpc_core::ClientChannelControlHelper>(chand));
-  lb_args.args = new_args != nullptr ? new_args : args->channel_args;
-  grpc_error* error = GRPC_ERROR_NONE;
-  chand->resolving_lb_policy.reset(
-      grpc_core::New<grpc_core::ResolvingLoadBalancingPolicy>(
-          std::move(lb_args), &grpc_client_channel_routing_trace,
-          std::move(target_uri), process_resolver_result_locked, chand,
-          &error));
-  grpc_channel_args_destroy(new_args);
-  if (error != GRPC_ERROR_NONE) {
-    // Orphan the resolving LB policy and flush the exec_ctx to ensure
-    // that it finishes shutting down.  This ensures that if we are
-    // failing, we destroy the ClientChannelControlHelper (and thus
-    // unref the channel stack) before we return.
-    // TODO(roth): This is not a complete solution, because it only
-    // catches the case where channel stack initialization fails in this
-    // particular filter.  If there is a failure in a different filter, we
-    // will leave a dangling ref here, which can cause a crash.  Fortunately,
-    // in practice, there are no other filters that can cause failures in
-    // channel stack initialization, so this works for now.
-    chand->resolving_lb_policy.reset();
-    grpc_core::ExecCtx::Get()->Flush();
-  } else {
-    grpc_pollset_set_add_pollset_set(
-        chand->resolving_lb_policy->interested_parties(),
-        chand->interested_parties);
-    if (grpc_client_channel_routing_trace.enabled()) {
-      gpr_log(GPR_INFO, "chand=%p: created resolving_lb_policy=%p", chand,
-              chand->resolving_lb_policy.get());
+void ChannelData::AddQueuedPick(QueuedPick* pick,
+                                grpc_polling_entity* pollent) {
+  // Add call to queued picks list.
+  pick->next = queued_picks_;
+  queued_picks_ = pick;
+  // Add call's pollent to channel's interested_parties, so that I/O
+  // can be done under the call's CQ.
+  grpc_polling_entity_add_to_pollset_set(pollent, interested_parties_);
+}
+
+void ChannelData::RemoveQueuedPick(QueuedPick* to_remove,
+                                   grpc_polling_entity* pollent) {
+  // Remove call's pollent from channel's interested_parties.
+  grpc_polling_entity_del_from_pollset_set(pollent, interested_parties_);
+  // Remove from queued picks list.
+  for (QueuedPick** pick = &queued_picks_; *pick != nullptr;
+       pick = &(*pick)->next) {
+    if (*pick == to_remove) {
+      *pick = to_remove->next;
+      return;
     }
   }
-  return error;
 }
 
-/* Destructor for channel_data */
-static void cc_destroy_channel_elem(grpc_channel_element* elem) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  if (chand->resolving_lb_policy != nullptr) {
-    grpc_pollset_set_del_pollset_set(
-        chand->resolving_lb_policy->interested_parties(),
-        chand->interested_parties);
-    chand->resolving_lb_policy.reset();
-  }
-  // TODO(roth): Once we convert the filter API to C++, there will no
-  // longer be any need to explicitly reset these smart pointer data members.
-  chand->picker.reset();
-  chand->subchannel_pool.reset();
-  chand->info_lb_policy_name.reset();
-  chand->info_service_config_json.reset();
-  chand->retry_throttle_data.reset();
-  chand->method_params_table.reset();
-  grpc_client_channel_stop_backup_polling(chand->interested_parties);
-  grpc_pollset_set_destroy(chand->interested_parties);
-  GRPC_COMBINER_UNREF(chand->combiner, "client_channel");
-  GRPC_ERROR_UNREF(chand->disconnect_error);
-  grpc_connectivity_state_destroy(&chand->state_tracker);
-  gpr_mu_destroy(&chand->info_mu);
-  gpr_mu_destroy(&chand->external_connectivity_watcher_list_mu);
+void ChannelData::TryToConnectLocked(void* arg, grpc_error* error_ignored) {
+  auto* chand = static_cast<ChannelData*>(arg);
+  if (chand->resolving_lb_policy_ != nullptr) {
+    chand->resolving_lb_policy_->ExitIdleLocked();
+  }
+  GRPC_CHANNEL_STACK_UNREF(chand->owning_stack_, "TryToConnect");
 }
 
-/*************************************************************************
- * PER-CALL FUNCTIONS
- */
+grpc_connectivity_state ChannelData::CheckConnectivityState(
+    bool try_to_connect) {
+  grpc_connectivity_state out = grpc_connectivity_state_check(&state_tracker_);
+  if (out == GRPC_CHANNEL_IDLE && try_to_connect) {
+    GRPC_CHANNEL_STACK_REF(owning_stack_, "TryToConnect");
+    GRPC_CLOSURE_SCHED(GRPC_CLOSURE_CREATE(TryToConnectLocked, this,
+                                           grpc_combiner_scheduler(combiner_)),
+                       GRPC_ERROR_NONE);
+  }
+  return out;
+}
 
-// Max number of batches that can be pending on a call at any given
-// time.  This includes one batch for each of the following ops:
-//   recv_initial_metadata
-//   send_initial_metadata
-//   recv_message
-//   send_message
-//   recv_trailing_metadata
-//   send_trailing_metadata
-#define MAX_PENDING_BATCHES 6
+//
+// CallData implementation
+//
 
 // Retry support:
 //
@@ -567,362 +1495,244 @@ static void cc_destroy_channel_elem(grpc_channel_element* elem) {
 //   (census filter is on top of this one)
 // - add census stats for retries
 
-namespace grpc_core {
-namespace {
-class QueuedPickCanceller;
-}  // namespace
-}  // namespace grpc_core
-
-namespace {
-
-struct call_data;
-
-// State used for starting a retryable batch on a subchannel call.
-// This provides its own grpc_transport_stream_op_batch and other data
-// structures needed to populate the ops in the batch.
-// We allocate one struct on the arena for each attempt at starting a
-// batch on a given subchannel call.
-struct subchannel_batch_data {
-  subchannel_batch_data(grpc_call_element* elem, call_data* calld, int refcount,
-                        bool set_on_complete);
-  // All dtor code must be added in `destroy`. This is because we may
-  // call closures in `subchannel_batch_data` after they are unrefed by
-  // `batch_data_unref`, and msan would complain about accessing this class
-  // after calling dtor. As a result we cannot call the `dtor` in
-  // `batch_data_unref`.
-  // TODO(soheil): We should try to call the dtor in `batch_data_unref`.
-  ~subchannel_batch_data() { destroy(); }
-  void destroy();
-
-  gpr_refcount refs;
-  grpc_call_element* elem;
-  grpc_core::RefCountedPtr<grpc_core::SubchannelCall> subchannel_call;
-  // The batch to use in the subchannel call.
-  // Its payload field points to subchannel_call_retry_state.batch_payload.
-  grpc_transport_stream_op_batch batch;
-  // For intercepting on_complete.
-  grpc_closure on_complete;
-};
-
-// Retry state associated with a subchannel call.
-// Stored in the parent_data of the subchannel call object.
-struct subchannel_call_retry_state {
-  explicit subchannel_call_retry_state(grpc_call_context_element* context)
-      : batch_payload(context),
-        started_send_initial_metadata(false),
-        completed_send_initial_metadata(false),
-        started_send_trailing_metadata(false),
-        completed_send_trailing_metadata(false),
-        started_recv_initial_metadata(false),
-        completed_recv_initial_metadata(false),
-        started_recv_trailing_metadata(false),
-        completed_recv_trailing_metadata(false),
-        retry_dispatched(false) {}
-
-  // subchannel_batch_data.batch.payload points to this.
-  grpc_transport_stream_op_batch_payload batch_payload;
-  // For send_initial_metadata.
-  // Note that we need to make a copy of the initial metadata for each
-  // subchannel call instead of just referring to the copy in call_data,
-  // because filters in the subchannel stack will probably add entries,
-  // so we need to start in a pristine state for each attempt of the call.
-  grpc_linked_mdelem* send_initial_metadata_storage;
-  grpc_metadata_batch send_initial_metadata;
-  // For send_message.
-  grpc_core::ManualConstructor<grpc_core::ByteStreamCache::CachingByteStream>
-      send_message;
-  // For send_trailing_metadata.
-  grpc_linked_mdelem* send_trailing_metadata_storage;
-  grpc_metadata_batch send_trailing_metadata;
-  // For intercepting recv_initial_metadata.
-  grpc_metadata_batch recv_initial_metadata;
-  grpc_closure recv_initial_metadata_ready;
-  bool trailing_metadata_available = false;
-  // For intercepting recv_message.
-  grpc_closure recv_message_ready;
-  grpc_core::OrphanablePtr<grpc_core::ByteStream> recv_message;
-  // For intercepting recv_trailing_metadata.
-  grpc_metadata_batch recv_trailing_metadata;
-  grpc_transport_stream_stats collect_stats;
-  grpc_closure recv_trailing_metadata_ready;
-  // These fields indicate which ops have been started and completed on
-  // this subchannel call.
-  size_t started_send_message_count = 0;
-  size_t completed_send_message_count = 0;
-  size_t started_recv_message_count = 0;
-  size_t completed_recv_message_count = 0;
-  bool started_send_initial_metadata : 1;
-  bool completed_send_initial_metadata : 1;
-  bool started_send_trailing_metadata : 1;
-  bool completed_send_trailing_metadata : 1;
-  bool started_recv_initial_metadata : 1;
-  bool completed_recv_initial_metadata : 1;
-  bool started_recv_trailing_metadata : 1;
-  bool completed_recv_trailing_metadata : 1;
-  // State for callback processing.
-  subchannel_batch_data* recv_initial_metadata_ready_deferred_batch = nullptr;
-  grpc_error* recv_initial_metadata_error = GRPC_ERROR_NONE;
-  subchannel_batch_data* recv_message_ready_deferred_batch = nullptr;
-  grpc_error* recv_message_error = GRPC_ERROR_NONE;
-  subchannel_batch_data* recv_trailing_metadata_internal_batch = nullptr;
-  // NOTE: Do not move this next to the metadata bitfields above. That would
-  //       save space but will also result in a data race because compiler will
-  //       generate a 2 byte store which overwrites the meta-data fields upon
-  //       setting this field.
-  bool retry_dispatched : 1;
-};
+CallData::CallData(grpc_call_element* elem, const ChannelData& chand,
+                   const grpc_call_element_args& args)
+    : deadline_state_(elem, args.call_stack, args.call_combiner,
+                      GPR_LIKELY(chand.deadline_checking_enabled())
+                          ? args.deadline
+                          : GRPC_MILLIS_INF_FUTURE),
+      path_(grpc_slice_ref_internal(args.path)),
+      call_start_time_(args.start_time),
+      deadline_(args.deadline),
+      arena_(args.arena),
+      owning_call_(args.call_stack),
+      call_combiner_(args.call_combiner),
+      call_context_(args.context),
+      pending_send_initial_metadata_(false),
+      pending_send_message_(false),
+      pending_send_trailing_metadata_(false),
+      enable_retries_(chand.enable_retries()),
+      retry_committed_(false),
+      last_attempt_got_server_pushback_(false) {}
+
+CallData::~CallData() {
+  grpc_slice_unref_internal(path_);
+  GRPC_ERROR_UNREF(cancel_error_);
+  // Make sure there are no remaining pending batches.
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(pending_batches_); ++i) {
+    GPR_ASSERT(pending_batches_[i].batch == nullptr);
+  }
+}
+
+grpc_error* CallData::Init(grpc_call_element* elem,
+                           const grpc_call_element_args* args) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  new (elem->call_data) CallData(elem, *chand, *args);
+  return GRPC_ERROR_NONE;
+}
 
-// Pending batches stored in call data.
-struct pending_batch {
-  // The pending batch.  If nullptr, this slot is empty.
-  grpc_transport_stream_op_batch* batch;
-  // Indicates whether payload for send ops has been cached in call data.
-  bool send_ops_cached;
-};
+void CallData::Destroy(grpc_call_element* elem,
+                       const grpc_call_final_info* final_info,
+                       grpc_closure* then_schedule_closure) {
+  CallData* calld = static_cast<CallData*>(elem->call_data);
+  if (GPR_LIKELY(calld->subchannel_call_ != nullptr)) {
+    calld->subchannel_call_->SetAfterCallStackDestroy(then_schedule_closure);
+    then_schedule_closure = nullptr;
+  }
+  calld->~CallData();
+  GRPC_CLOSURE_SCHED(then_schedule_closure, GRPC_ERROR_NONE);
+}
 
-/** Call data.  Holds a pointer to SubchannelCall and the
-    associated machinery to create such a pointer.
-    Handles queueing of stream ops until a call object is ready, waiting
-    for initial metadata before trying to create a call object,
-    and handling cancellation gracefully. */
-struct call_data {
-  call_data(grpc_call_element* elem, const channel_data& chand,
-            const grpc_call_element_args& args)
-      : deadline_state(elem, args.call_stack, args.call_combiner,
-                       GPR_LIKELY(chand.deadline_checking_enabled)
-                           ? args.deadline
-                           : GRPC_MILLIS_INF_FUTURE),
-        path(grpc_slice_ref_internal(args.path)),
-        call_start_time(args.start_time),
-        deadline(args.deadline),
-        arena(args.arena),
-        owning_call(args.call_stack),
-        call_combiner(args.call_combiner),
-        call_context(args.context),
-        pending_send_initial_metadata(false),
-        pending_send_message(false),
-        pending_send_trailing_metadata(false),
-        enable_retries(chand.enable_retries),
-        retry_committed(false),
-        last_attempt_got_server_pushback(false) {}
-
-  ~call_data() {
-    grpc_slice_unref_internal(path);
-    GRPC_ERROR_UNREF(cancel_error);
-    for (size_t i = 0; i < GPR_ARRAY_SIZE(pending_batches); ++i) {
-      GPR_ASSERT(pending_batches[i].batch == nullptr);
+void CallData::StartTransportStreamOpBatch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
+  GPR_TIMER_SCOPE("cc_start_transport_stream_op_batch", 0);
+  CallData* calld = static_cast<CallData*>(elem->call_data);
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  if (GPR_LIKELY(chand->deadline_checking_enabled())) {
+    grpc_deadline_state_client_start_transport_stream_op_batch(elem, batch);
+  }
+  // If we've previously been cancelled, immediately fail any new batches.
+  if (GPR_UNLIKELY(calld->cancel_error_ != GRPC_ERROR_NONE)) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
+      gpr_log(GPR_INFO, "chand=%p calld=%p: failing batch with error: %s",
+              chand, calld, grpc_error_string(calld->cancel_error_));
+    }
+    // Note: This will release the call combiner.
+    grpc_transport_stream_op_batch_finish_with_failure(
+        batch, GRPC_ERROR_REF(calld->cancel_error_), calld->call_combiner_);
+    return;
+  }
+  // Handle cancellation.
+  if (GPR_UNLIKELY(batch->cancel_stream)) {
+    // Stash a copy of cancel_error in our call data, so that we can use
+    // it for subsequent operations.  This ensures that if the call is
+    // cancelled before any batches are passed down (e.g., if the deadline
+    // is in the past when the call starts), we can return the right
+    // error to the caller when the first batch does get passed down.
+    GRPC_ERROR_UNREF(calld->cancel_error_);
+    calld->cancel_error_ =
+        GRPC_ERROR_REF(batch->payload->cancel_stream.cancel_error);
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
+      gpr_log(GPR_INFO, "chand=%p calld=%p: recording cancel_error=%s", chand,
+              calld, grpc_error_string(calld->cancel_error_));
+    }
+    // If we do not have a subchannel call (i.e., a pick has not yet
+    // been started), fail all pending batches.  Otherwise, send the
+    // cancellation down to the subchannel call.
+    if (calld->subchannel_call_ == nullptr) {
+      // TODO(roth): If there is a pending retry callback, do we need to
+      // cancel it here?
+      calld->PendingBatchesFail(elem, GRPC_ERROR_REF(calld->cancel_error_),
+                                NoYieldCallCombiner);
+      // Note: This will release the call combiner.
+      grpc_transport_stream_op_batch_finish_with_failure(
+          batch, GRPC_ERROR_REF(calld->cancel_error_), calld->call_combiner_);
+    } else {
+      // Note: This will release the call combiner.
+      calld->subchannel_call_->StartTransportStreamOpBatch(batch);
+    }
+    return;
+  }
+  // Add the batch to the pending list.
+  calld->PendingBatchesAdd(elem, batch);
+  // Check if we've already gotten a subchannel call.
+  // Note that once we have completed the pick, we do not need to enter
+  // the channel combiner, which is more efficient (especially for
+  // streaming calls).
+  if (calld->subchannel_call_ != nullptr) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
+      gpr_log(GPR_INFO,
+              "chand=%p calld=%p: starting batch on subchannel_call=%p", chand,
+              calld, calld->subchannel_call_.get());
+    }
+    calld->PendingBatchesResume(elem);
+    return;
+  }
+  // We do not yet have a subchannel call.
+  // For batches containing a send_initial_metadata op, enter the channel
+  // combiner to start a pick.
+  if (GPR_LIKELY(batch->send_initial_metadata)) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
+      gpr_log(GPR_INFO, "chand=%p calld=%p: entering client_channel combiner",
+              chand, calld);
+    }
+    GRPC_CLOSURE_SCHED(
+        GRPC_CLOSURE_INIT(
+            &batch->handler_private.closure, StartPickLocked, elem,
+            grpc_combiner_scheduler(chand->data_plane_combiner())),
+        GRPC_ERROR_NONE);
+  } else {
+    // For all other batches, release the call combiner.
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
+      gpr_log(GPR_INFO,
+              "chand=%p calld=%p: saved batch, yielding call combiner", chand,
+              calld);
     }
+    GRPC_CALL_COMBINER_STOP(calld->call_combiner_,
+                            "batch does not include send_initial_metadata");
   }
+}
 
-  // State for handling deadlines.
-  // The code in deadline_filter.c requires this to be the first field.
-  // TODO(roth): This is slightly sub-optimal in that grpc_deadline_state
-  // and this struct both independently store pointers to the call stack
-  // and call combiner.  If/when we have time, find a way to avoid this
-  // without breaking the grpc_deadline_state abstraction.
-  grpc_deadline_state deadline_state;
-
-  grpc_slice path;  // Request path.
-  gpr_timespec call_start_time;
-  grpc_millis deadline;
-  gpr_arena* arena;
-  grpc_call_stack* owning_call;
-  grpc_call_combiner* call_combiner;
-  grpc_call_context_element* call_context;
-
-  grpc_core::RefCountedPtr<ServerRetryThrottleData> retry_throttle_data;
-  grpc_core::RefCountedPtr<ClientChannelMethodParams> method_params;
-
-  grpc_core::RefCountedPtr<grpc_core::SubchannelCall> subchannel_call;
-
-  // Set when we get a cancel_stream op.
-  grpc_error* cancel_error = GRPC_ERROR_NONE;
-
-  QueuedPick pick;
-  bool pick_queued = false;
-  bool service_config_applied = false;
-  grpc_core::QueuedPickCanceller* pick_canceller = nullptr;
-  grpc_closure pick_closure;
-
-  grpc_polling_entity* pollent = nullptr;
-
-  // Batches are added to this list when received from above.
-  // They are removed when we are done handling the batch (i.e., when
-  // either we have invoked all of the batch's callbacks or we have
-  // passed the batch down to the subchannel call and are not
-  // intercepting any of its callbacks).
-  pending_batch pending_batches[MAX_PENDING_BATCHES] = {};
-  bool pending_send_initial_metadata : 1;
-  bool pending_send_message : 1;
-  bool pending_send_trailing_metadata : 1;
-
-  // Retry state.
-  bool enable_retries : 1;
-  bool retry_committed : 1;
-  bool last_attempt_got_server_pushback : 1;
-  int num_attempts_completed = 0;
-  size_t bytes_buffered_for_retry = 0;
-  grpc_core::ManualConstructor<grpc_core::BackOff> retry_backoff;
-  grpc_timer retry_timer;
-
-  // The number of pending retriable subchannel batches containing send ops.
-  // We hold a ref to the call stack while this is non-zero, since replay
-  // batches may not complete until after all callbacks have been returned
-  // to the surface, and we need to make sure that the call is not destroyed
-  // until all of these batches have completed.
-  // Note that we actually only need to track replay batches, but it's
-  // easier to track all batches with send ops.
-  int num_pending_retriable_subchannel_send_batches = 0;
-
-  // Cached data for retrying send ops.
-  // send_initial_metadata
-  bool seen_send_initial_metadata = false;
-  grpc_linked_mdelem* send_initial_metadata_storage = nullptr;
-  grpc_metadata_batch send_initial_metadata;
-  uint32_t send_initial_metadata_flags;
-  gpr_atm* peer_string;
-  // send_message
-  // When we get a send_message op, we replace the original byte stream
-  // with a CachingByteStream that caches the slices to a local buffer for
-  // use in retries.
-  // Note: We inline the cache for the first 3 send_message ops and use
-  // dynamic allocation after that.  This number was essentially picked
-  // at random; it could be changed in the future to tune performance.
-  grpc_core::InlinedVector<grpc_core::ByteStreamCache*, 3> send_messages;
-  // send_trailing_metadata
-  bool seen_send_trailing_metadata = false;
-  grpc_linked_mdelem* send_trailing_metadata_storage = nullptr;
-  grpc_metadata_batch send_trailing_metadata;
-};
-
-}  // namespace
-
-// Forward declarations.
-static void retry_commit(grpc_call_element* elem,
-                         subchannel_call_retry_state* retry_state);
-static void start_internal_recv_trailing_metadata(grpc_call_element* elem);
-static void on_complete(void* arg, grpc_error* error);
-static void start_retriable_subchannel_batches(void* arg, grpc_error* ignored);
-static void remove_call_from_queued_picks_locked(grpc_call_element* elem);
+void CallData::SetPollent(grpc_call_element* elem,
+                          grpc_polling_entity* pollent) {
+  CallData* calld = static_cast<CallData*>(elem->call_data);
+  calld->pollent_ = pollent;
+}
 
 //
 // send op data caching
 //
 
-// Caches data for send ops so that it can be retried later, if not
-// already cached.
-static void maybe_cache_send_ops_for_batch(call_data* calld,
-                                           pending_batch* pending) {
+void CallData::MaybeCacheSendOpsForBatch(PendingBatch* pending) {
   if (pending->send_ops_cached) return;
   pending->send_ops_cached = true;
   grpc_transport_stream_op_batch* batch = pending->batch;
   // Save a copy of metadata for send_initial_metadata ops.
   if (batch->send_initial_metadata) {
-    calld->seen_send_initial_metadata = true;
-    GPR_ASSERT(calld->send_initial_metadata_storage == nullptr);
+    seen_send_initial_metadata_ = true;
+    GPR_ASSERT(send_initial_metadata_storage_ == nullptr);
     grpc_metadata_batch* send_initial_metadata =
         batch->payload->send_initial_metadata.send_initial_metadata;
-    calld->send_initial_metadata_storage = (grpc_linked_mdelem*)gpr_arena_alloc(
-        calld->arena,
+    send_initial_metadata_storage_ = (grpc_linked_mdelem*)arena_->Alloc(
         sizeof(grpc_linked_mdelem) * send_initial_metadata->list.count);
-    grpc_metadata_batch_copy(send_initial_metadata,
-                             &calld->send_initial_metadata,
-                             calld->send_initial_metadata_storage);
-    calld->send_initial_metadata_flags =
+    grpc_metadata_batch_copy(send_initial_metadata, &send_initial_metadata_,
+                             send_initial_metadata_storage_);
+    send_initial_metadata_flags_ =
         batch->payload->send_initial_metadata.send_initial_metadata_flags;
-    calld->peer_string = batch->payload->send_initial_metadata.peer_string;
+    peer_string_ = batch->payload->send_initial_metadata.peer_string;
   }
   // Set up cache for send_message ops.
   if (batch->send_message) {
-    grpc_core::ByteStreamCache* cache =
-        static_cast<grpc_core::ByteStreamCache*>(
-            gpr_arena_alloc(calld->arena, sizeof(grpc_core::ByteStreamCache)));
-    new (cache) grpc_core::ByteStreamCache(
+    ByteStreamCache* cache = arena_->New<ByteStreamCache>(
         std::move(batch->payload->send_message.send_message));
-    calld->send_messages.push_back(cache);
+    send_messages_.push_back(cache);
   }
   // Save metadata batch for send_trailing_metadata ops.
   if (batch->send_trailing_metadata) {
-    calld->seen_send_trailing_metadata = true;
-    GPR_ASSERT(calld->send_trailing_metadata_storage == nullptr);
+    seen_send_trailing_metadata_ = true;
+    GPR_ASSERT(send_trailing_metadata_storage_ == nullptr);
     grpc_metadata_batch* send_trailing_metadata =
         batch->payload->send_trailing_metadata.send_trailing_metadata;
-    calld->send_trailing_metadata_storage =
-        (grpc_linked_mdelem*)gpr_arena_alloc(
-            calld->arena,
-            sizeof(grpc_linked_mdelem) * send_trailing_metadata->list.count);
-    grpc_metadata_batch_copy(send_trailing_metadata,
-                             &calld->send_trailing_metadata,
-                             calld->send_trailing_metadata_storage);
+    send_trailing_metadata_storage_ = (grpc_linked_mdelem*)arena_->Alloc(
+        sizeof(grpc_linked_mdelem) * send_trailing_metadata->list.count);
+    grpc_metadata_batch_copy(send_trailing_metadata, &send_trailing_metadata_,
+                             send_trailing_metadata_storage_);
   }
 }
 
-// Frees cached send_initial_metadata.
-static void free_cached_send_initial_metadata(channel_data* chand,
-                                              call_data* calld) {
-  if (grpc_client_channel_call_trace.enabled()) {
+void CallData::FreeCachedSendInitialMetadata(ChannelData* chand) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
     gpr_log(GPR_INFO,
             "chand=%p calld=%p: destroying calld->send_initial_metadata", chand,
-            calld);
+            this);
   }
-  grpc_metadata_batch_destroy(&calld->send_initial_metadata);
+  grpc_metadata_batch_destroy(&send_initial_metadata_);
 }
 
-// Frees cached send_message at index idx.
-static void free_cached_send_message(channel_data* chand, call_data* calld,
-                                     size_t idx) {
-  if (grpc_client_channel_call_trace.enabled()) {
+void CallData::FreeCachedSendMessage(ChannelData* chand, size_t idx) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
     gpr_log(GPR_INFO,
             "chand=%p calld=%p: destroying calld->send_messages[%" PRIuPTR "]",
-            chand, calld, idx);
+            chand, this, idx);
   }
-  calld->send_messages[idx]->Destroy();
+  send_messages_[idx]->Destroy();
 }
 
-// Frees cached send_trailing_metadata.
-static void free_cached_send_trailing_metadata(channel_data* chand,
-                                               call_data* calld) {
-  if (grpc_client_channel_call_trace.enabled()) {
+void CallData::FreeCachedSendTrailingMetadata(ChannelData* chand) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
     gpr_log(GPR_INFO,
             "chand=%p calld=%p: destroying calld->send_trailing_metadata",
-            chand, calld);
+            chand, this);
   }
-  grpc_metadata_batch_destroy(&calld->send_trailing_metadata);
+  grpc_metadata_batch_destroy(&send_trailing_metadata_);
 }
 
-// Frees cached send ops that have already been completed after
-// committing the call.
-static void free_cached_send_op_data_after_commit(
-    grpc_call_element* elem, subchannel_call_retry_state* retry_state) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
+void CallData::FreeCachedSendOpDataAfterCommit(
+    grpc_call_element* elem, SubchannelCallRetryState* retry_state) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
   if (retry_state->completed_send_initial_metadata) {
-    free_cached_send_initial_metadata(chand, calld);
+    FreeCachedSendInitialMetadata(chand);
   }
   for (size_t i = 0; i < retry_state->completed_send_message_count; ++i) {
-    free_cached_send_message(chand, calld, i);
+    FreeCachedSendMessage(chand, i);
   }
   if (retry_state->completed_send_trailing_metadata) {
-    free_cached_send_trailing_metadata(chand, calld);
+    FreeCachedSendTrailingMetadata(chand);
   }
 }
 
-// Frees cached send ops that were completed by the completed batch in
-// batch_data.  Used when batches are completed after the call is committed.
-static void free_cached_send_op_data_for_completed_batch(
-    grpc_call_element* elem, subchannel_batch_data* batch_data,
-    subchannel_call_retry_state* retry_state) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
+void CallData::FreeCachedSendOpDataForCompletedBatch(
+    grpc_call_element* elem, SubchannelCallBatchData* batch_data,
+    SubchannelCallRetryState* retry_state) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
   if (batch_data->batch.send_initial_metadata) {
-    free_cached_send_initial_metadata(chand, calld);
+    FreeCachedSendInitialMetadata(chand);
   }
   if (batch_data->batch.send_message) {
-    free_cached_send_message(chand, calld,
-                             retry_state->completed_send_message_count - 1);
+    FreeCachedSendMessage(chand, retry_state->completed_send_message_count - 1);
   }
   if (batch_data->batch.send_trailing_metadata) {
-    free_cached_send_trailing_metadata(chand, calld);
+    FreeCachedSendTrailingMetadata(chand);
   }
 }
 
@@ -930,7 +1740,7 @@ static void free_cached_send_op_data_for_completed_batch(
 // LB recv_trailing_metadata_ready handling
 //
 
-void maybe_inject_recv_trailing_metadata_ready_for_lb(
+void CallData::MaybeInjectRecvTrailingMetadataReadyForLoadBalancingPolicy(
     const LoadBalancingPolicy::PickArgs& pick,
     grpc_transport_stream_op_batch* batch) {
   if (pick.recv_trailing_metadata_ready != nullptr) {
@@ -949,8 +1759,7 @@ void maybe_inject_recv_trailing_metadata_ready_for_lb(
 // pending_batches management
 //
 
-// Returns the index into calld->pending_batches to be used for batch.
-static size_t get_batch_index(grpc_transport_stream_op_batch* batch) {
+size_t CallData::GetBatchIndex(grpc_transport_stream_op_batch* batch) {
   // Note: It is important the send_initial_metadata be the first entry
   // here, since the code in pick_subchannel_locked() assumes it will be.
   if (batch->send_initial_metadata) return 0;
@@ -963,240 +1772,215 @@ static size_t get_batch_index(grpc_transport_stream_op_batch* batch) {
 }
 
 // This is called via the call combiner, so access to calld is synchronized.
-static void pending_batches_add(grpc_call_element* elem,
-                                grpc_transport_stream_op_batch* batch) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  const size_t idx = get_batch_index(batch);
-  if (grpc_client_channel_call_trace.enabled()) {
+void CallData::PendingBatchesAdd(grpc_call_element* elem,
+                                 grpc_transport_stream_op_batch* batch) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  const size_t idx = GetBatchIndex(batch);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
     gpr_log(GPR_INFO,
             "chand=%p calld=%p: adding pending batch at index %" PRIuPTR, chand,
-            calld, idx);
+            this, idx);
   }
-  pending_batch* pending = &calld->pending_batches[idx];
+  PendingBatch* pending = &pending_batches_[idx];
   GPR_ASSERT(pending->batch == nullptr);
   pending->batch = batch;
   pending->send_ops_cached = false;
-  if (calld->enable_retries) {
+  if (enable_retries_) {
     // Update state in calld about pending batches.
     // Also check if the batch takes us over the retry buffer limit.
     // Note: We don't check the size of trailing metadata here, because
     // gRPC clients do not send trailing metadata.
     if (batch->send_initial_metadata) {
-      calld->pending_send_initial_metadata = true;
-      calld->bytes_buffered_for_retry += grpc_metadata_batch_size(
+      pending_send_initial_metadata_ = true;
+      bytes_buffered_for_retry_ += grpc_metadata_batch_size(
           batch->payload->send_initial_metadata.send_initial_metadata);
     }
     if (batch->send_message) {
-      calld->pending_send_message = true;
-      calld->bytes_buffered_for_retry +=
+      pending_send_message_ = true;
+      bytes_buffered_for_retry_ +=
           batch->payload->send_message.send_message->length();
     }
     if (batch->send_trailing_metadata) {
-      calld->pending_send_trailing_metadata = true;
+      pending_send_trailing_metadata_ = true;
     }
-    if (GPR_UNLIKELY(calld->bytes_buffered_for_retry >
-                     chand->per_rpc_retry_buffer_size)) {
-      if (grpc_client_channel_call_trace.enabled()) {
+    if (GPR_UNLIKELY(bytes_buffered_for_retry_ >
+                     chand->per_rpc_retry_buffer_size())) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
         gpr_log(GPR_INFO,
                 "chand=%p calld=%p: exceeded retry buffer size, committing",
-                chand, calld);
+                chand, this);
       }
-      subchannel_call_retry_state* retry_state =
-          calld->subchannel_call == nullptr
-              ? nullptr
-              : static_cast<subchannel_call_retry_state*>(
-
-                    calld->subchannel_call->GetParentData());
-      retry_commit(elem, retry_state);
+      SubchannelCallRetryState* retry_state =
+          subchannel_call_ == nullptr ? nullptr
+                                      : static_cast<SubchannelCallRetryState*>(
+                                            subchannel_call_->GetParentData());
+      RetryCommit(elem, retry_state);
       // If we are not going to retry and have not yet started, pretend
       // retries are disabled so that we don't bother with retry overhead.
-      if (calld->num_attempts_completed == 0) {
-        if (grpc_client_channel_call_trace.enabled()) {
+      if (num_attempts_completed_ == 0) {
+        if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
           gpr_log(GPR_INFO,
                   "chand=%p calld=%p: disabling retries before first attempt",
-                  chand, calld);
+                  chand, this);
         }
-        calld->enable_retries = false;
+        enable_retries_ = false;
       }
     }
   }
 }
 
-static void pending_batch_clear(call_data* calld, pending_batch* pending) {
-  if (calld->enable_retries) {
+void CallData::PendingBatchClear(PendingBatch* pending) {
+  if (enable_retries_) {
     if (pending->batch->send_initial_metadata) {
-      calld->pending_send_initial_metadata = false;
+      pending_send_initial_metadata_ = false;
     }
     if (pending->batch->send_message) {
-      calld->pending_send_message = false;
+      pending_send_message_ = false;
     }
     if (pending->batch->send_trailing_metadata) {
-      calld->pending_send_trailing_metadata = false;
+      pending_send_trailing_metadata_ = false;
     }
   }
   pending->batch = nullptr;
 }
 
+void CallData::MaybeClearPendingBatch(grpc_call_element* elem,
+                                      PendingBatch* pending) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  grpc_transport_stream_op_batch* batch = pending->batch;
+  // We clear the pending batch if all of its callbacks have been
+  // scheduled and reset to nullptr.
+  if (batch->on_complete == nullptr &&
+      (!batch->recv_initial_metadata ||
+       batch->payload->recv_initial_metadata.recv_initial_metadata_ready ==
+           nullptr) &&
+      (!batch->recv_message ||
+       batch->payload->recv_message.recv_message_ready == nullptr) &&
+      (!batch->recv_trailing_metadata ||
+       batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready ==
+           nullptr)) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
+      gpr_log(GPR_INFO, "chand=%p calld=%p: clearing pending batch", chand,
+              this);
+    }
+    PendingBatchClear(pending);
+  }
+}
+
 // This is called via the call combiner, so access to calld is synchronized.
-static void fail_pending_batch_in_call_combiner(void* arg, grpc_error* error) {
+void CallData::FailPendingBatchInCallCombiner(void* arg, grpc_error* error) {
   grpc_transport_stream_op_batch* batch =
       static_cast<grpc_transport_stream_op_batch*>(arg);
-  call_data* calld = static_cast<call_data*>(batch->handler_private.extra_arg);
+  CallData* calld = static_cast<CallData*>(batch->handler_private.extra_arg);
   // Note: This will release the call combiner.
   grpc_transport_stream_op_batch_finish_with_failure(
-      batch, GRPC_ERROR_REF(error), calld->call_combiner);
+      batch, GRPC_ERROR_REF(error), calld->call_combiner_);
 }
 
 // This is called via the call combiner, so access to calld is synchronized.
-// If yield_call_combiner_predicate returns true, assumes responsibility for
-// yielding the call combiner.
-typedef bool (*YieldCallCombinerPredicate)(
-    const grpc_core::CallCombinerClosureList& closures);
-static bool yield_call_combiner(
-    const grpc_core::CallCombinerClosureList& closures) {
-  return true;
-}
-static bool no_yield_call_combiner(
-    const grpc_core::CallCombinerClosureList& closures) {
-  return false;
-}
-static bool yield_call_combiner_if_pending_batches_found(
-    const grpc_core::CallCombinerClosureList& closures) {
-  return closures.size() > 0;
-}
-static void pending_batches_fail(
+void CallData::PendingBatchesFail(
     grpc_call_element* elem, grpc_error* error,
     YieldCallCombinerPredicate yield_call_combiner_predicate) {
   GPR_ASSERT(error != GRPC_ERROR_NONE);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  if (grpc_client_channel_call_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
     size_t num_batches = 0;
-    for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
-      if (calld->pending_batches[i].batch != nullptr) ++num_batches;
+    for (size_t i = 0; i < GPR_ARRAY_SIZE(pending_batches_); ++i) {
+      if (pending_batches_[i].batch != nullptr) ++num_batches;
     }
     gpr_log(GPR_INFO,
             "chand=%p calld=%p: failing %" PRIuPTR " pending batches: %s",
-            elem->channel_data, calld, num_batches, grpc_error_string(error));
+            elem->channel_data, this, num_batches, grpc_error_string(error));
   }
-  grpc_core::CallCombinerClosureList closures;
-  for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
-    pending_batch* pending = &calld->pending_batches[i];
+  CallCombinerClosureList closures;
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(pending_batches_); ++i) {
+    PendingBatch* pending = &pending_batches_[i];
     grpc_transport_stream_op_batch* batch = pending->batch;
     if (batch != nullptr) {
       if (batch->recv_trailing_metadata) {
-        maybe_inject_recv_trailing_metadata_ready_for_lb(calld->pick.pick,
-                                                         batch);
+        MaybeInjectRecvTrailingMetadataReadyForLoadBalancingPolicy(pick_.pick,
+                                                                   batch);
       }
-      batch->handler_private.extra_arg = calld;
+      batch->handler_private.extra_arg = this;
       GRPC_CLOSURE_INIT(&batch->handler_private.closure,
-                        fail_pending_batch_in_call_combiner, batch,
+                        FailPendingBatchInCallCombiner, batch,
                         grpc_schedule_on_exec_ctx);
       closures.Add(&batch->handler_private.closure, GRPC_ERROR_REF(error),
-                   "pending_batches_fail");
-      pending_batch_clear(calld, pending);
+                   "PendingBatchesFail");
+      PendingBatchClear(pending);
     }
   }
   if (yield_call_combiner_predicate(closures)) {
-    closures.RunClosures(calld->call_combiner);
+    closures.RunClosures(call_combiner_);
   } else {
-    closures.RunClosuresWithoutYielding(calld->call_combiner);
+    closures.RunClosuresWithoutYielding(call_combiner_);
   }
   GRPC_ERROR_UNREF(error);
 }
 
 // This is called via the call combiner, so access to calld is synchronized.
-static void resume_pending_batch_in_call_combiner(void* arg,
-                                                  grpc_error* ignored) {
+void CallData::ResumePendingBatchInCallCombiner(void* arg,
+                                                grpc_error* ignored) {
   grpc_transport_stream_op_batch* batch =
       static_cast<grpc_transport_stream_op_batch*>(arg);
-  grpc_core::SubchannelCall* subchannel_call =
-      static_cast<grpc_core::SubchannelCall*>(batch->handler_private.extra_arg);
+  SubchannelCall* subchannel_call =
+      static_cast<SubchannelCall*>(batch->handler_private.extra_arg);
   // Note: This will release the call combiner.
   subchannel_call->StartTransportStreamOpBatch(batch);
 }
 
 // This is called via the call combiner, so access to calld is synchronized.
-static void pending_batches_resume(grpc_call_element* elem) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  if (calld->enable_retries) {
-    start_retriable_subchannel_batches(elem, GRPC_ERROR_NONE);
+void CallData::PendingBatchesResume(grpc_call_element* elem) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  if (enable_retries_) {
+    StartRetriableSubchannelBatches(elem, GRPC_ERROR_NONE);
     return;
   }
   // Retries not enabled; send down batches as-is.
-  if (grpc_client_channel_call_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
     size_t num_batches = 0;
-    for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
-      if (calld->pending_batches[i].batch != nullptr) ++num_batches;
+    for (size_t i = 0; i < GPR_ARRAY_SIZE(pending_batches_); ++i) {
+      if (pending_batches_[i].batch != nullptr) ++num_batches;
     }
     gpr_log(GPR_INFO,
             "chand=%p calld=%p: starting %" PRIuPTR
             " pending batches on subchannel_call=%p",
-            chand, calld, num_batches, calld->subchannel_call.get());
+            chand, this, num_batches, subchannel_call_.get());
   }
-  grpc_core::CallCombinerClosureList closures;
-  for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
-    pending_batch* pending = &calld->pending_batches[i];
+  CallCombinerClosureList closures;
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(pending_batches_); ++i) {
+    PendingBatch* pending = &pending_batches_[i];
     grpc_transport_stream_op_batch* batch = pending->batch;
     if (batch != nullptr) {
       if (batch->recv_trailing_metadata) {
-        maybe_inject_recv_trailing_metadata_ready_for_lb(calld->pick.pick,
-                                                         batch);
+        MaybeInjectRecvTrailingMetadataReadyForLoadBalancingPolicy(pick_.pick,
+                                                                   batch);
       }
-      batch->handler_private.extra_arg = calld->subchannel_call.get();
+      batch->handler_private.extra_arg = subchannel_call_.get();
       GRPC_CLOSURE_INIT(&batch->handler_private.closure,
-                        resume_pending_batch_in_call_combiner, batch,
+                        ResumePendingBatchInCallCombiner, batch,
                         grpc_schedule_on_exec_ctx);
       closures.Add(&batch->handler_private.closure, GRPC_ERROR_NONE,
-                   "pending_batches_resume");
-      pending_batch_clear(calld, pending);
+                   "PendingBatchesResume");
+      PendingBatchClear(pending);
     }
   }
   // Note: This will release the call combiner.
-  closures.RunClosures(calld->call_combiner);
-}
-
-static void maybe_clear_pending_batch(grpc_call_element* elem,
-                                      pending_batch* pending) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  grpc_transport_stream_op_batch* batch = pending->batch;
-  // We clear the pending batch if all of its callbacks have been
-  // scheduled and reset to nullptr.
-  if (batch->on_complete == nullptr &&
-      (!batch->recv_initial_metadata ||
-       batch->payload->recv_initial_metadata.recv_initial_metadata_ready ==
-           nullptr) &&
-      (!batch->recv_message ||
-       batch->payload->recv_message.recv_message_ready == nullptr) &&
-      (!batch->recv_trailing_metadata ||
-       batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready ==
-           nullptr)) {
-    if (grpc_client_channel_call_trace.enabled()) {
-      gpr_log(GPR_INFO, "chand=%p calld=%p: clearing pending batch", chand,
-              calld);
-    }
-    pending_batch_clear(calld, pending);
-  }
+  closures.RunClosures(call_combiner_);
 }
 
-// Returns a pointer to the first pending batch for which predicate(batch)
-// returns true, or null if not found.
 template <typename Predicate>
-static pending_batch* pending_batch_find(grpc_call_element* elem,
-                                         const char* log_message,
-                                         Predicate predicate) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
-    pending_batch* pending = &calld->pending_batches[i];
+CallData::PendingBatch* CallData::PendingBatchFind(grpc_call_element* elem,
+                                                   const char* log_message,
+                                                   Predicate predicate) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(pending_batches_); ++i) {
+    PendingBatch* pending = &pending_batches_[i];
     grpc_transport_stream_op_batch* batch = pending->batch;
     if (batch != nullptr && predicate(batch)) {
-      if (grpc_client_channel_call_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
         gpr_log(GPR_INFO,
                 "chand=%p calld=%p: %s pending batch at index %" PRIuPTR, chand,
-                calld, log_message, i);
+                this, log_message, i);
       }
       return pending;
     }
@@ -1208,108 +1992,99 @@ static pending_batch* pending_batch_find(grpc_call_element* elem,
 // retry code
 //
 
-// Commits the call so that no further retry attempts will be performed.
-static void retry_commit(grpc_call_element* elem,
-                         subchannel_call_retry_state* retry_state) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  if (calld->retry_committed) return;
-  calld->retry_committed = true;
-  if (grpc_client_channel_call_trace.enabled()) {
-    gpr_log(GPR_INFO, "chand=%p calld=%p: committing retries", chand, calld);
+void CallData::RetryCommit(grpc_call_element* elem,
+                           SubchannelCallRetryState* retry_state) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  if (retry_committed_) return;
+  retry_committed_ = true;
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
+    gpr_log(GPR_INFO, "chand=%p calld=%p: committing retries", chand, this);
   }
   if (retry_state != nullptr) {
-    free_cached_send_op_data_after_commit(elem, retry_state);
+    FreeCachedSendOpDataAfterCommit(elem, retry_state);
   }
 }
 
-// Starts a retry after appropriate back-off.
-static void do_retry(grpc_call_element* elem,
-                     subchannel_call_retry_state* retry_state,
-                     grpc_millis server_pushback_ms) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  GPR_ASSERT(calld->method_params != nullptr);
-  const ClientChannelMethodParams::RetryPolicy* retry_policy =
-      calld->method_params->retry_policy();
+void CallData::DoRetry(grpc_call_element* elem,
+                       SubchannelCallRetryState* retry_state,
+                       grpc_millis server_pushback_ms) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  GPR_ASSERT(method_params_ != nullptr);
+  const auto* retry_policy = method_params_->retry_policy();
   GPR_ASSERT(retry_policy != nullptr);
   // Reset subchannel call and connected subchannel.
-  calld->subchannel_call.reset();
-  calld->pick.pick.connected_subchannel.reset();
+  subchannel_call_.reset();
+  pick_.pick.connected_subchannel.reset();
   // Compute backoff delay.
   grpc_millis next_attempt_time;
   if (server_pushback_ms >= 0) {
-    next_attempt_time = grpc_core::ExecCtx::Get()->Now() + server_pushback_ms;
-    calld->last_attempt_got_server_pushback = true;
+    next_attempt_time = ExecCtx::Get()->Now() + server_pushback_ms;
+    last_attempt_got_server_pushback_ = true;
   } else {
-    if (calld->num_attempts_completed == 1 ||
-        calld->last_attempt_got_server_pushback) {
-      calld->retry_backoff.Init(
-          grpc_core::BackOff::Options()
+    if (num_attempts_completed_ == 1 || last_attempt_got_server_pushback_) {
+      retry_backoff_.Init(
+          BackOff::Options()
               .set_initial_backoff(retry_policy->initial_backoff)
               .set_multiplier(retry_policy->backoff_multiplier)
               .set_jitter(RETRY_BACKOFF_JITTER)
               .set_max_backoff(retry_policy->max_backoff));
-      calld->last_attempt_got_server_pushback = false;
+      last_attempt_got_server_pushback_ = false;
     }
-    next_attempt_time = calld->retry_backoff->NextAttemptTime();
+    next_attempt_time = retry_backoff_->NextAttemptTime();
   }
-  if (grpc_client_channel_call_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
     gpr_log(GPR_INFO,
             "chand=%p calld=%p: retrying failed call in %" PRId64 " ms", chand,
-            calld, next_attempt_time - grpc_core::ExecCtx::Get()->Now());
+            this, next_attempt_time - ExecCtx::Get()->Now());
   }
   // Schedule retry after computed delay.
-  GRPC_CLOSURE_INIT(&calld->pick_closure, start_pick_locked, elem,
-                    grpc_combiner_scheduler(chand->combiner));
-  grpc_timer_init(&calld->retry_timer, next_attempt_time, &calld->pick_closure);
+  GRPC_CLOSURE_INIT(&pick_closure_, StartPickLocked, elem,
+                    grpc_combiner_scheduler(chand->data_plane_combiner()));
+  grpc_timer_init(&retry_timer_, next_attempt_time, &pick_closure_);
   // Update bookkeeping.
   if (retry_state != nullptr) retry_state->retry_dispatched = true;
 }
 
-// Returns true if the call is being retried.
-static bool maybe_retry(grpc_call_element* elem,
-                        subchannel_batch_data* batch_data,
-                        grpc_status_code status,
-                        grpc_mdelem* server_pushback_md) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
+bool CallData::MaybeRetry(grpc_call_element* elem,
+                          SubchannelCallBatchData* batch_data,
+                          grpc_status_code status,
+                          grpc_mdelem* server_pushback_md) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
   // Get retry policy.
-  if (calld->method_params == nullptr) return false;
-  const ClientChannelMethodParams::RetryPolicy* retry_policy =
-      calld->method_params->retry_policy();
+  if (method_params_ == nullptr) return false;
+  const auto* retry_policy = method_params_->retry_policy();
   if (retry_policy == nullptr) return false;
   // If we've already dispatched a retry from this call, return true.
   // This catches the case where the batch has multiple callbacks
   // (i.e., it includes either recv_message or recv_initial_metadata).
-  subchannel_call_retry_state* retry_state = nullptr;
+  SubchannelCallRetryState* retry_state = nullptr;
   if (batch_data != nullptr) {
-    retry_state = static_cast<subchannel_call_retry_state*>(
+    retry_state = static_cast<SubchannelCallRetryState*>(
         batch_data->subchannel_call->GetParentData());
     if (retry_state->retry_dispatched) {
-      if (grpc_client_channel_call_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
         gpr_log(GPR_INFO, "chand=%p calld=%p: retry already dispatched", chand,
-                calld);
+                this);
       }
       return true;
     }
   }
   // Check status.
   if (GPR_LIKELY(status == GRPC_STATUS_OK)) {
-    if (calld->retry_throttle_data != nullptr) {
-      calld->retry_throttle_data->RecordSuccess();
+    if (retry_throttle_data_ != nullptr) {
+      retry_throttle_data_->RecordSuccess();
     }
-    if (grpc_client_channel_call_trace.enabled()) {
-      gpr_log(GPR_INFO, "chand=%p calld=%p: call succeeded", chand, calld);
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
+      gpr_log(GPR_INFO, "chand=%p calld=%p: call succeeded", chand, this);
     }
     return false;
   }
   // Status is not OK.  Check whether the status is retryable.
   if (!retry_policy->retryable_status_codes.Contains(status)) {
-    if (grpc_client_channel_call_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
       gpr_log(GPR_INFO,
               "chand=%p calld=%p: status %s not configured as retryable", chand,
-              calld, grpc_status_code_to_string(status));
+              this, grpc_status_code_to_string(status));
     }
     return false;
   }
@@ -1320,36 +2095,36 @@ static bool maybe_retry(grpc_call_element* elem,
   // things like failures due to malformed requests (INVALID_ARGUMENT).
   // Conversely, it's important for this to come before the remaining
   // checks, so that we don't fail to record failures due to other factors.
-  if (calld->retry_throttle_data != nullptr &&
-      !calld->retry_throttle_data->RecordFailure()) {
-    if (grpc_client_channel_call_trace.enabled()) {
-      gpr_log(GPR_INFO, "chand=%p calld=%p: retries throttled", chand, calld);
+  if (retry_throttle_data_ != nullptr &&
+      !retry_throttle_data_->RecordFailure()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
+      gpr_log(GPR_INFO, "chand=%p calld=%p: retries throttled", chand, this);
     }
     return false;
   }
   // Check whether the call is committed.
-  if (calld->retry_committed) {
-    if (grpc_client_channel_call_trace.enabled()) {
+  if (retry_committed_) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
       gpr_log(GPR_INFO, "chand=%p calld=%p: retries already committed", chand,
-              calld);
+              this);
     }
     return false;
   }
   // Check whether we have retries remaining.
-  ++calld->num_attempts_completed;
-  if (calld->num_attempts_completed >= retry_policy->max_attempts) {
-    if (grpc_client_channel_call_trace.enabled()) {
+  ++num_attempts_completed_;
+  if (num_attempts_completed_ >= retry_policy->max_attempts) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
       gpr_log(GPR_INFO, "chand=%p calld=%p: exceeded %d retry attempts", chand,
-              calld, retry_policy->max_attempts);
+              this, retry_policy->max_attempts);
     }
     return false;
   }
   // If the call was cancelled from the surface, don't retry.
-  if (calld->cancel_error != GRPC_ERROR_NONE) {
-    if (grpc_client_channel_call_trace.enabled()) {
+  if (cancel_error_ != GRPC_ERROR_NONE) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
       gpr_log(GPR_INFO,
               "chand=%p calld=%p: call cancelled from surface, not retrying",
-              chand, calld);
+              chand, this);
     }
     return false;
   }
@@ -1359,51 +2134,55 @@ static bool maybe_retry(grpc_call_element* elem,
     // If the value is "-1" or any other unparseable string, we do not retry.
     uint32_t ms;
     if (!grpc_parse_slice_to_uint32(GRPC_MDVALUE(*server_pushback_md), &ms)) {
-      if (grpc_client_channel_call_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
         gpr_log(GPR_INFO,
                 "chand=%p calld=%p: not retrying due to server push-back",
-                chand, calld);
+                chand, this);
       }
       return false;
     } else {
-      if (grpc_client_channel_call_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
         gpr_log(GPR_INFO, "chand=%p calld=%p: server push-back: retry in %u ms",
-                chand, calld, ms);
+                chand, this, ms);
       }
       server_pushback_ms = (grpc_millis)ms;
     }
   }
-  do_retry(elem, retry_state, server_pushback_ms);
+  DoRetry(elem, retry_state, server_pushback_ms);
   return true;
 }
 
 //
-// subchannel_batch_data
+// CallData::SubchannelCallBatchData
 //
 
-namespace {
+CallData::SubchannelCallBatchData* CallData::SubchannelCallBatchData::Create(
+    grpc_call_element* elem, int refcount, bool set_on_complete) {
+  CallData* calld = static_cast<CallData*>(elem->call_data);
+  return calld->arena_->New<SubchannelCallBatchData>(elem, calld, refcount,
+                                                     set_on_complete);
+}
 
-subchannel_batch_data::subchannel_batch_data(grpc_call_element* elem,
-                                             call_data* calld, int refcount,
-                                             bool set_on_complete)
-    : elem(elem), subchannel_call(calld->subchannel_call) {
-  subchannel_call_retry_state* retry_state =
-      static_cast<subchannel_call_retry_state*>(
-          calld->subchannel_call->GetParentData());
+CallData::SubchannelCallBatchData::SubchannelCallBatchData(
+    grpc_call_element* elem, CallData* calld, int refcount,
+    bool set_on_complete)
+    : elem(elem), subchannel_call(calld->subchannel_call_) {
+  SubchannelCallRetryState* retry_state =
+      static_cast<SubchannelCallRetryState*>(
+          calld->subchannel_call_->GetParentData());
   batch.payload = &retry_state->batch_payload;
   gpr_ref_init(&refs, refcount);
   if (set_on_complete) {
-    GRPC_CLOSURE_INIT(&on_complete, ::on_complete, this,
+    GRPC_CLOSURE_INIT(&on_complete, CallData::OnComplete, this,
                       grpc_schedule_on_exec_ctx);
     batch.on_complete = &on_complete;
   }
-  GRPC_CALL_STACK_REF(calld->owning_call, "batch_data");
+  GRPC_CALL_STACK_REF(calld->owning_call_, "batch_data");
 }
 
-void subchannel_batch_data::destroy() {
-  subchannel_call_retry_state* retry_state =
-      static_cast<subchannel_call_retry_state*>(
-          subchannel_call->GetParentData());
+void CallData::SubchannelCallBatchData::Destroy() {
+  SubchannelCallRetryState* retry_state =
+      static_cast<SubchannelCallRetryState*>(subchannel_call->GetParentData());
   if (batch.send_initial_metadata) {
     grpc_metadata_batch_destroy(&retry_state->send_initial_metadata);
   }
@@ -1417,42 +2196,20 @@ void subchannel_batch_data::destroy() {
     grpc_metadata_batch_destroy(&retry_state->recv_trailing_metadata);
   }
   subchannel_call.reset();
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  GRPC_CALL_STACK_UNREF(calld->owning_call, "batch_data");
-}
-
-}  // namespace
-
-// Creates a subchannel_batch_data object on the call's arena with the
-// specified refcount.  If set_on_complete is true, the batch's
-// on_complete callback will be set to point to on_complete();
-// otherwise, the batch's on_complete callback will be null.
-static subchannel_batch_data* batch_data_create(grpc_call_element* elem,
-                                                int refcount,
-                                                bool set_on_complete) {
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  subchannel_batch_data* batch_data =
-      new (gpr_arena_alloc(calld->arena, sizeof(*batch_data)))
-          subchannel_batch_data(elem, calld, refcount, set_on_complete);
-  return batch_data;
-}
-
-static void batch_data_unref(subchannel_batch_data* batch_data) {
-  if (gpr_unref(&batch_data->refs)) {
-    batch_data->destroy();
-  }
+  CallData* calld = static_cast<CallData*>(elem->call_data);
+  GRPC_CALL_STACK_UNREF(calld->owning_call_, "batch_data");
 }
 
 //
 // recv_initial_metadata callback handling
 //
 
-// Invokes recv_initial_metadata_ready for a subchannel batch.
-static void invoke_recv_initial_metadata_callback(void* arg,
-                                                  grpc_error* error) {
-  subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg);
+void CallData::InvokeRecvInitialMetadataCallback(void* arg, grpc_error* error) {
+  SubchannelCallBatchData* batch_data =
+      static_cast<SubchannelCallBatchData*>(arg);
+  CallData* calld = static_cast<CallData*>(batch_data->elem->call_data);
   // Find pending batch.
-  pending_batch* pending = pending_batch_find(
+  PendingBatch* pending = calld->PendingBatchFind(
       batch_data->elem, "invoking recv_initial_metadata_ready for",
       [](grpc_transport_stream_op_batch* batch) {
         return batch->recv_initial_metadata &&
@@ -1461,8 +2218,8 @@ static void invoke_recv_initial_metadata_callback(void* arg,
       });
   GPR_ASSERT(pending != nullptr);
   // Return metadata.
-  subchannel_call_retry_state* retry_state =
-      static_cast<subchannel_call_retry_state*>(
+  SubchannelCallRetryState* retry_state =
+      static_cast<SubchannelCallRetryState*>(
           batch_data->subchannel_call->GetParentData());
   grpc_metadata_batch_move(
       &retry_state->recv_initial_metadata,
@@ -1475,33 +2232,32 @@ static void invoke_recv_initial_metadata_callback(void* arg,
           .recv_initial_metadata_ready;
   pending->batch->payload->recv_initial_metadata.recv_initial_metadata_ready =
       nullptr;
-  maybe_clear_pending_batch(batch_data->elem, pending);
-  batch_data_unref(batch_data);
+  calld->MaybeClearPendingBatch(batch_data->elem, pending);
+  batch_data->Unref();
   // Invoke callback.
   GRPC_CLOSURE_RUN(recv_initial_metadata_ready, GRPC_ERROR_REF(error));
 }
 
-// Intercepts recv_initial_metadata_ready callback for retries.
-// Commits the call and returns the initial metadata up the stack.
-static void recv_initial_metadata_ready(void* arg, grpc_error* error) {
-  subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg);
+void CallData::RecvInitialMetadataReady(void* arg, grpc_error* error) {
+  SubchannelCallBatchData* batch_data =
+      static_cast<SubchannelCallBatchData*>(arg);
   grpc_call_element* elem = batch_data->elem;
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  if (grpc_client_channel_call_trace.enabled()) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  CallData* calld = static_cast<CallData*>(elem->call_data);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
     gpr_log(GPR_INFO,
             "chand=%p calld=%p: got recv_initial_metadata_ready, error=%s",
             chand, calld, grpc_error_string(error));
   }
-  subchannel_call_retry_state* retry_state =
-      static_cast<subchannel_call_retry_state*>(
+  SubchannelCallRetryState* retry_state =
+      static_cast<SubchannelCallRetryState*>(
           batch_data->subchannel_call->GetParentData());
   retry_state->completed_recv_initial_metadata = true;
   // If a retry was already dispatched, then we're not going to use the
   // result of this recv_initial_metadata op, so do nothing.
   if (retry_state->retry_dispatched) {
     GRPC_CALL_COMBINER_STOP(
-        calld->call_combiner,
+        calld->call_combiner_,
         "recv_initial_metadata_ready after retry dispatched");
     return;
   }
@@ -1512,7 +2268,7 @@ static void recv_initial_metadata_ready(void* arg, grpc_error* error) {
   if (GPR_UNLIKELY((retry_state->trailing_metadata_available ||
                     error != GRPC_ERROR_NONE) &&
                    !retry_state->completed_recv_trailing_metadata)) {
-    if (grpc_client_channel_call_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
       gpr_log(GPR_INFO,
               "chand=%p calld=%p: deferring recv_initial_metadata_ready "
               "(Trailers-Only)",
@@ -1523,30 +2279,31 @@ static void recv_initial_metadata_ready(void* arg, grpc_error* error) {
     if (!retry_state->started_recv_trailing_metadata) {
       // recv_trailing_metadata not yet started by application; start it
       // ourselves to get status.
-      start_internal_recv_trailing_metadata(elem);
+      calld->StartInternalRecvTrailingMetadata(elem);
     } else {
       GRPC_CALL_COMBINER_STOP(
-          calld->call_combiner,
+          calld->call_combiner_,
           "recv_initial_metadata_ready trailers-only or error");
     }
     return;
   }
   // Received valid initial metadata, so commit the call.
-  retry_commit(elem, retry_state);
+  calld->RetryCommit(elem, retry_state);
   // Invoke the callback to return the result to the surface.
   // Manually invoking a callback function; it does not take ownership of error.
-  invoke_recv_initial_metadata_callback(batch_data, error);
+  calld->InvokeRecvInitialMetadataCallback(batch_data, error);
 }
 
 //
 // recv_message callback handling
 //
 
-// Invokes recv_message_ready for a subchannel batch.
-static void invoke_recv_message_callback(void* arg, grpc_error* error) {
-  subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg);
+void CallData::InvokeRecvMessageCallback(void* arg, grpc_error* error) {
+  SubchannelCallBatchData* batch_data =
+      static_cast<SubchannelCallBatchData*>(arg);
+  CallData* calld = static_cast<CallData*>(batch_data->elem->call_data);
   // Find pending op.
-  pending_batch* pending = pending_batch_find(
+  PendingBatch* pending = calld->PendingBatchFind(
       batch_data->elem, "invoking recv_message_ready for",
       [](grpc_transport_stream_op_batch* batch) {
         return batch->recv_message &&
@@ -1554,8 +2311,8 @@ static void invoke_recv_message_callback(void* arg, grpc_error* error) {
       });
   GPR_ASSERT(pending != nullptr);
   // Return payload.
-  subchannel_call_retry_state* retry_state =
-      static_cast<subchannel_call_retry_state*>(
+  SubchannelCallRetryState* retry_state =
+      static_cast<SubchannelCallRetryState*>(
           batch_data->subchannel_call->GetParentData());
   *pending->batch->payload->recv_message.recv_message =
       std::move(retry_state->recv_message);
@@ -1565,31 +2322,30 @@ static void invoke_recv_message_callback(void* arg, grpc_error* error) {
   grpc_closure* recv_message_ready =
       pending->batch->payload->recv_message.recv_message_ready;
   pending->batch->payload->recv_message.recv_message_ready = nullptr;
-  maybe_clear_pending_batch(batch_data->elem, pending);
-  batch_data_unref(batch_data);
+  calld->MaybeClearPendingBatch(batch_data->elem, pending);
+  batch_data->Unref();
   // Invoke callback.
   GRPC_CLOSURE_RUN(recv_message_ready, GRPC_ERROR_REF(error));
 }
 
-// Intercepts recv_message_ready callback for retries.
-// Commits the call and returns the message up the stack.
-static void recv_message_ready(void* arg, grpc_error* error) {
-  subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg);
+void CallData::RecvMessageReady(void* arg, grpc_error* error) {
+  SubchannelCallBatchData* batch_data =
+      static_cast<SubchannelCallBatchData*>(arg);
   grpc_call_element* elem = batch_data->elem;
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  if (grpc_client_channel_call_trace.enabled()) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  CallData* calld = static_cast<CallData*>(elem->call_data);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
     gpr_log(GPR_INFO, "chand=%p calld=%p: got recv_message_ready, error=%s",
             chand, calld, grpc_error_string(error));
   }
-  subchannel_call_retry_state* retry_state =
-      static_cast<subchannel_call_retry_state*>(
+  SubchannelCallRetryState* retry_state =
+      static_cast<SubchannelCallRetryState*>(
           batch_data->subchannel_call->GetParentData());
   ++retry_state->completed_recv_message_count;
   // If a retry was already dispatched, then we're not going to use the
   // result of this recv_message op, so do nothing.
   if (retry_state->retry_dispatched) {
-    GRPC_CALL_COMBINER_STOP(calld->call_combiner,
+    GRPC_CALL_COMBINER_STOP(calld->call_combiner_,
                             "recv_message_ready after retry dispatched");
     return;
   }
@@ -1600,7 +2356,7 @@ static void recv_message_ready(void* arg, grpc_error* error) {
   if (GPR_UNLIKELY(
           (retry_state->recv_message == nullptr || error != GRPC_ERROR_NONE) &&
           !retry_state->completed_recv_trailing_metadata)) {
-    if (grpc_client_channel_call_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
       gpr_log(GPR_INFO,
               "chand=%p calld=%p: deferring recv_message_ready (nullptr "
               "message and recv_trailing_metadata pending)",
@@ -1611,33 +2367,29 @@ static void recv_message_ready(void* arg, grpc_error* error) {
     if (!retry_state->started_recv_trailing_metadata) {
       // recv_trailing_metadata not yet started by application; start it
       // ourselves to get status.
-      start_internal_recv_trailing_metadata(elem);
+      calld->StartInternalRecvTrailingMetadata(elem);
     } else {
-      GRPC_CALL_COMBINER_STOP(calld->call_combiner, "recv_message_ready null");
+      GRPC_CALL_COMBINER_STOP(calld->call_combiner_, "recv_message_ready null");
     }
     return;
   }
   // Received a valid message, so commit the call.
-  retry_commit(elem, retry_state);
+  calld->RetryCommit(elem, retry_state);
   // Invoke the callback to return the result to the surface.
   // Manually invoking a callback function; it does not take ownership of error.
-  invoke_recv_message_callback(batch_data, error);
+  calld->InvokeRecvMessageCallback(batch_data, error);
 }
 
 //
 // recv_trailing_metadata handling
 //
 
-// Sets *status and *server_pushback_md based on md_batch and error.
-// Only sets *server_pushback_md if server_pushback_md != nullptr.
-static void get_call_status(grpc_call_element* elem,
-                            grpc_metadata_batch* md_batch, grpc_error* error,
-                            grpc_status_code* status,
-                            grpc_mdelem** server_pushback_md) {
-  call_data* calld = static_cast<call_data*>(elem->call_data);
+void CallData::GetCallStatus(grpc_call_element* elem,
+                             grpc_metadata_batch* md_batch, grpc_error* error,
+                             grpc_status_code* status,
+                             grpc_mdelem** server_pushback_md) {
   if (error != GRPC_ERROR_NONE) {
-    grpc_error_get_status(error, calld->deadline, status, nullptr, nullptr,
-                          nullptr);
+    grpc_error_get_status(error, deadline_, status, nullptr, nullptr, nullptr);
   } else {
     GPR_ASSERT(md_batch->idx.named.grpc_status != nullptr);
     *status =
@@ -1650,12 +2402,11 @@ static void get_call_status(grpc_call_element* elem,
   GRPC_ERROR_UNREF(error);
 }
 
-// Adds recv_trailing_metadata_ready closure to closures.
-static void add_closure_for_recv_trailing_metadata_ready(
-    grpc_call_element* elem, subchannel_batch_data* batch_data,
-    grpc_error* error, grpc_core::CallCombinerClosureList* closures) {
+void CallData::AddClosureForRecvTrailingMetadataReady(
+    grpc_call_element* elem, SubchannelCallBatchData* batch_data,
+    grpc_error* error, CallCombinerClosureList* closures) {
   // Find pending batch.
-  pending_batch* pending = pending_batch_find(
+  PendingBatch* pending = PendingBatchFind(
       elem, "invoking recv_trailing_metadata for",
       [](grpc_transport_stream_op_batch* batch) {
         return batch->recv_trailing_metadata &&
@@ -1663,15 +2414,14 @@ static void add_closure_for_recv_trailing_metadata_ready(
                        .recv_trailing_metadata_ready != nullptr;
       });
   // If we generated the recv_trailing_metadata op internally via
-  // start_internal_recv_trailing_metadata(), then there will be no
-  // pending batch.
+  // StartInternalRecvTrailingMetadata(), then there will be no pending batch.
   if (pending == nullptr) {
     GRPC_ERROR_UNREF(error);
     return;
   }
   // Return metadata.
-  subchannel_call_retry_state* retry_state =
-      static_cast<subchannel_call_retry_state*>(
+  SubchannelCallRetryState* retry_state =
+      static_cast<SubchannelCallRetryState*>(
           batch_data->subchannel_call->GetParentData());
   grpc_metadata_batch_move(
       &retry_state->recv_trailing_metadata,
@@ -1683,20 +2433,18 @@ static void add_closure_for_recv_trailing_metadata_ready(
   // Update bookkeeping.
   pending->batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
       nullptr;
-  maybe_clear_pending_batch(elem, pending);
+  MaybeClearPendingBatch(elem, pending);
 }
 
-// Adds any necessary closures for deferred recv_initial_metadata and
-// recv_message callbacks to closures.
-static void add_closures_for_deferred_recv_callbacks(
-    subchannel_batch_data* batch_data, subchannel_call_retry_state* retry_state,
-    grpc_core::CallCombinerClosureList* closures) {
+void CallData::AddClosuresForDeferredRecvCallbacks(
+    SubchannelCallBatchData* batch_data, SubchannelCallRetryState* retry_state,
+    CallCombinerClosureList* closures) {
   if (batch_data->batch.recv_trailing_metadata) {
     // Add closure for deferred recv_initial_metadata_ready.
     if (GPR_UNLIKELY(retry_state->recv_initial_metadata_ready_deferred_batch !=
                      nullptr)) {
       GRPC_CLOSURE_INIT(&retry_state->recv_initial_metadata_ready,
-                        invoke_recv_initial_metadata_callback,
+                        InvokeRecvInitialMetadataCallback,
                         retry_state->recv_initial_metadata_ready_deferred_batch,
                         grpc_schedule_on_exec_ctx);
       closures->Add(&retry_state->recv_initial_metadata_ready,
@@ -1708,7 +2456,7 @@ static void add_closures_for_deferred_recv_callbacks(
     if (GPR_UNLIKELY(retry_state->recv_message_ready_deferred_batch !=
                      nullptr)) {
       GRPC_CLOSURE_INIT(&retry_state->recv_message_ready,
-                        invoke_recv_message_callback,
+                        InvokeRecvMessageCallback,
                         retry_state->recv_message_ready_deferred_batch,
                         grpc_schedule_on_exec_ctx);
       closures->Add(&retry_state->recv_message_ready,
@@ -1719,11 +2467,8 @@ static void add_closures_for_deferred_recv_callbacks(
   }
 }
 
-// Returns true if any op in the batch was not yet started.
-// Only looks at send ops, since recv ops are always started immediately.
-static bool pending_batch_is_unstarted(
-    pending_batch* pending, call_data* calld,
-    subchannel_call_retry_state* retry_state) {
+bool CallData::PendingBatchIsUnstarted(PendingBatch* pending,
+                                       SubchannelCallRetryState* retry_state) {
   if (pending->batch == nullptr || pending->batch->on_complete == nullptr) {
     return false;
   }
@@ -1732,7 +2477,7 @@ static bool pending_batch_is_unstarted(
     return true;
   }
   if (pending->batch->send_message &&
-      retry_state->started_send_message_count < calld->send_messages.size()) {
+      retry_state->started_send_message_count < send_messages_.size()) {
     return true;
   }
   if (pending->batch->send_trailing_metadata &&
@@ -1742,72 +2487,66 @@ static bool pending_batch_is_unstarted(
   return false;
 }
 
-// For any pending batch containing an op that has not yet been started,
-// adds the pending batch's completion closures to closures.
-static void add_closures_to_fail_unstarted_pending_batches(
-    grpc_call_element* elem, subchannel_call_retry_state* retry_state,
-    grpc_error* error, grpc_core::CallCombinerClosureList* closures) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
-    pending_batch* pending = &calld->pending_batches[i];
-    if (pending_batch_is_unstarted(pending, calld, retry_state)) {
-      if (grpc_client_channel_call_trace.enabled()) {
+void CallData::AddClosuresToFailUnstartedPendingBatches(
+    grpc_call_element* elem, SubchannelCallRetryState* retry_state,
+    grpc_error* error, CallCombinerClosureList* closures) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(pending_batches_); ++i) {
+    PendingBatch* pending = &pending_batches_[i];
+    if (PendingBatchIsUnstarted(pending, retry_state)) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
         gpr_log(GPR_INFO,
                 "chand=%p calld=%p: failing unstarted pending batch at index "
                 "%" PRIuPTR,
-                chand, calld, i);
+                chand, this, i);
       }
       closures->Add(pending->batch->on_complete, GRPC_ERROR_REF(error),
                     "failing on_complete for pending batch");
       pending->batch->on_complete = nullptr;
-      maybe_clear_pending_batch(elem, pending);
+      MaybeClearPendingBatch(elem, pending);
     }
   }
   GRPC_ERROR_UNREF(error);
 }
 
-// Runs necessary closures upon completion of a call attempt.
-static void run_closures_for_completed_call(subchannel_batch_data* batch_data,
-                                            grpc_error* error) {
+void CallData::RunClosuresForCompletedCall(SubchannelCallBatchData* batch_data,
+                                           grpc_error* error) {
   grpc_call_element* elem = batch_data->elem;
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  subchannel_call_retry_state* retry_state =
-      static_cast<subchannel_call_retry_state*>(
+  SubchannelCallRetryState* retry_state =
+      static_cast<SubchannelCallRetryState*>(
           batch_data->subchannel_call->GetParentData());
   // Construct list of closures to execute.
-  grpc_core::CallCombinerClosureList closures;
+  CallCombinerClosureList closures;
   // First, add closure for recv_trailing_metadata_ready.
-  add_closure_for_recv_trailing_metadata_ready(
-      elem, batch_data, GRPC_ERROR_REF(error), &closures);
+  AddClosureForRecvTrailingMetadataReady(elem, batch_data,
+                                         GRPC_ERROR_REF(error), &closures);
   // If there are deferred recv_initial_metadata_ready or recv_message_ready
   // callbacks, add them to closures.
-  add_closures_for_deferred_recv_callbacks(batch_data, retry_state, &closures);
+  AddClosuresForDeferredRecvCallbacks(batch_data, retry_state, &closures);
   // Add closures to fail any pending batches that have not yet been started.
-  add_closures_to_fail_unstarted_pending_batches(
-      elem, retry_state, GRPC_ERROR_REF(error), &closures);
+  AddClosuresToFailUnstartedPendingBatches(elem, retry_state,
+                                           GRPC_ERROR_REF(error), &closures);
   // Don't need batch_data anymore.
-  batch_data_unref(batch_data);
+  batch_data->Unref();
   // Schedule all of the closures identified above.
   // Note: This will release the call combiner.
-  closures.RunClosures(calld->call_combiner);
+  closures.RunClosures(call_combiner_);
   GRPC_ERROR_UNREF(error);
 }
 
-// Intercepts recv_trailing_metadata_ready callback for retries.
-// Commits the call and returns the trailing metadata up the stack.
-static void recv_trailing_metadata_ready(void* arg, grpc_error* error) {
-  subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg);
+void CallData::RecvTrailingMetadataReady(void* arg, grpc_error* error) {
+  SubchannelCallBatchData* batch_data =
+      static_cast<SubchannelCallBatchData*>(arg);
   grpc_call_element* elem = batch_data->elem;
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  if (grpc_client_channel_call_trace.enabled()) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  CallData* calld = static_cast<CallData*>(elem->call_data);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
     gpr_log(GPR_INFO,
             "chand=%p calld=%p: got recv_trailing_metadata_ready, error=%s",
             chand, calld, grpc_error_string(error));
   }
-  subchannel_call_retry_state* retry_state =
-      static_cast<subchannel_call_retry_state*>(
+  SubchannelCallRetryState* retry_state =
+      static_cast<SubchannelCallRetryState*>(
           batch_data->subchannel_call->GetParentData());
   retry_state->completed_recv_trailing_metadata = true;
   // Get the call's status and check for server pushback metadata.
@@ -1815,44 +2554,42 @@ static void recv_trailing_metadata_ready(void* arg, grpc_error* error) {
   grpc_mdelem* server_pushback_md = nullptr;
   grpc_metadata_batch* md_batch =
       batch_data->batch.payload->recv_trailing_metadata.recv_trailing_metadata;
-  get_call_status(elem, md_batch, GRPC_ERROR_REF(error), &status,
-                  &server_pushback_md);
-  if (grpc_client_channel_call_trace.enabled()) {
+  calld->GetCallStatus(elem, md_batch, GRPC_ERROR_REF(error), &status,
+                       &server_pushback_md);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
     gpr_log(GPR_INFO, "chand=%p calld=%p: call finished, status=%s", chand,
             calld, grpc_status_code_to_string(status));
   }
   // Check if we should retry.
-  if (maybe_retry(elem, batch_data, status, server_pushback_md)) {
+  if (calld->MaybeRetry(elem, batch_data, status, server_pushback_md)) {
     // Unref batch_data for deferred recv_initial_metadata_ready or
     // recv_message_ready callbacks, if any.
     if (retry_state->recv_initial_metadata_ready_deferred_batch != nullptr) {
-      batch_data_unref(batch_data);
+      batch_data->Unref();
       GRPC_ERROR_UNREF(retry_state->recv_initial_metadata_error);
     }
     if (retry_state->recv_message_ready_deferred_batch != nullptr) {
-      batch_data_unref(batch_data);
+      batch_data->Unref();
       GRPC_ERROR_UNREF(retry_state->recv_message_error);
     }
-    batch_data_unref(batch_data);
+    batch_data->Unref();
     return;
   }
   // Not retrying, so commit the call.
-  retry_commit(elem, retry_state);
+  calld->RetryCommit(elem, retry_state);
   // Run any necessary closures.
-  run_closures_for_completed_call(batch_data, GRPC_ERROR_REF(error));
+  calld->RunClosuresForCompletedCall(batch_data, GRPC_ERROR_REF(error));
 }
 
 //
 // on_complete callback handling
 //
 
-// Adds the on_complete closure for the pending batch completed in
-// batch_data to closures.
-static void add_closure_for_completed_pending_batch(
-    grpc_call_element* elem, subchannel_batch_data* batch_data,
-    subchannel_call_retry_state* retry_state, grpc_error* error,
-    grpc_core::CallCombinerClosureList* closures) {
-  pending_batch* pending = pending_batch_find(
+void CallData::AddClosuresForCompletedPendingBatch(
+    grpc_call_element* elem, SubchannelCallBatchData* batch_data,
+    SubchannelCallRetryState* retry_state, grpc_error* error,
+    CallCombinerClosureList* closures) {
+  PendingBatch* pending = PendingBatchFind(
       elem, "completed", [batch_data](grpc_transport_stream_op_batch* batch) {
         // Match the pending batch with the same set of send ops as the
         // subchannel batch we've just completed.
@@ -1873,27 +2610,22 @@ static void add_closure_for_completed_pending_batch(
   closures->Add(pending->batch->on_complete, error,
                 "on_complete for pending batch");
   pending->batch->on_complete = nullptr;
-  maybe_clear_pending_batch(elem, pending);
-}
-
-// If there are any cached ops to replay or pending ops to start on the
-// subchannel call, adds a closure to closures to invoke
-// start_retriable_subchannel_batches().
-static void add_closures_for_replay_or_pending_send_ops(
-    grpc_call_element* elem, subchannel_batch_data* batch_data,
-    subchannel_call_retry_state* retry_state,
-    grpc_core::CallCombinerClosureList* closures) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
+  MaybeClearPendingBatch(elem, pending);
+}
+
+void CallData::AddClosuresForReplayOrPendingSendOps(
+    grpc_call_element* elem, SubchannelCallBatchData* batch_data,
+    SubchannelCallRetryState* retry_state, CallCombinerClosureList* closures) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
   bool have_pending_send_message_ops =
-      retry_state->started_send_message_count < calld->send_messages.size();
+      retry_state->started_send_message_count < send_messages_.size();
   bool have_pending_send_trailing_metadata_op =
-      calld->seen_send_trailing_metadata &&
+      seen_send_trailing_metadata_ &&
       !retry_state->started_send_trailing_metadata;
   if (!have_pending_send_message_ops &&
       !have_pending_send_trailing_metadata_op) {
-    for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
-      pending_batch* pending = &calld->pending_batches[i];
+    for (size_t i = 0; i < GPR_ARRAY_SIZE(pending_batches_); ++i) {
+      PendingBatch* pending = &pending_batches_[i];
       grpc_transport_stream_op_batch* batch = pending->batch;
       if (batch == nullptr || pending->send_ops_cached) continue;
       if (batch->send_message) have_pending_send_message_ops = true;
@@ -1903,34 +2635,33 @@ static void add_closures_for_replay_or_pending_send_ops(
     }
   }
   if (have_pending_send_message_ops || have_pending_send_trailing_metadata_op) {
-    if (grpc_client_channel_call_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
       gpr_log(GPR_INFO,
               "chand=%p calld=%p: starting next batch for pending send op(s)",
-              chand, calld);
+              chand, this);
     }
     GRPC_CLOSURE_INIT(&batch_data->batch.handler_private.closure,
-                      start_retriable_subchannel_batches, elem,
+                      StartRetriableSubchannelBatches, elem,
                       grpc_schedule_on_exec_ctx);
     closures->Add(&batch_data->batch.handler_private.closure, GRPC_ERROR_NONE,
                   "starting next batch for send_* op(s)");
   }
 }
 
-// Callback used to intercept on_complete from subchannel calls.
-// Called only when retries are enabled.
-static void on_complete(void* arg, grpc_error* error) {
-  subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg);
+void CallData::OnComplete(void* arg, grpc_error* error) {
+  SubchannelCallBatchData* batch_data =
+      static_cast<SubchannelCallBatchData*>(arg);
   grpc_call_element* elem = batch_data->elem;
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  if (grpc_client_channel_call_trace.enabled()) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  CallData* calld = static_cast<CallData*>(elem->call_data);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
     char* batch_str = grpc_transport_stream_op_batch_string(&batch_data->batch);
     gpr_log(GPR_INFO, "chand=%p calld=%p: got on_complete, error=%s, batch=%s",
             chand, calld, grpc_error_string(error), batch_str);
     gpr_free(batch_str);
   }
-  subchannel_call_retry_state* retry_state =
-      static_cast<subchannel_call_retry_state*>(
+  SubchannelCallRetryState* retry_state =
+      static_cast<SubchannelCallRetryState*>(
           batch_data->subchannel_call->GetParentData());
   // Update bookkeeping in retry_state.
   if (batch_data->batch.send_initial_metadata) {
@@ -1944,38 +2675,38 @@ static void on_complete(void* arg, grpc_error* error) {
   }
   // If the call is committed, free cached data for send ops that we've just
   // completed.
-  if (calld->retry_committed) {
-    free_cached_send_op_data_for_completed_batch(elem, batch_data, retry_state);
+  if (calld->retry_committed_) {
+    calld->FreeCachedSendOpDataForCompletedBatch(elem, batch_data, retry_state);
   }
   // Construct list of closures to execute.
-  grpc_core::CallCombinerClosureList closures;
+  CallCombinerClosureList closures;
   // If a retry was already dispatched, that means we saw
   // recv_trailing_metadata before this, so we do nothing here.
   // Otherwise, invoke the callback to return the result to the surface.
   if (!retry_state->retry_dispatched) {
     // Add closure for the completed pending batch, if any.
-    add_closure_for_completed_pending_batch(elem, batch_data, retry_state,
-                                            GRPC_ERROR_REF(error), &closures);
+    calld->AddClosuresForCompletedPendingBatch(
+        elem, batch_data, retry_state, GRPC_ERROR_REF(error), &closures);
     // If needed, add a callback to start any replay or pending send ops on
     // the subchannel call.
     if (!retry_state->completed_recv_trailing_metadata) {
-      add_closures_for_replay_or_pending_send_ops(elem, batch_data, retry_state,
+      calld->AddClosuresForReplayOrPendingSendOps(elem, batch_data, retry_state,
                                                   &closures);
     }
   }
   // Track number of pending subchannel send batches and determine if this
   // was the last one.
-  --calld->num_pending_retriable_subchannel_send_batches;
+  --calld->num_pending_retriable_subchannel_send_batches_;
   const bool last_send_batch_complete =
-      calld->num_pending_retriable_subchannel_send_batches == 0;
+      calld->num_pending_retriable_subchannel_send_batches_ == 0;
   // Don't need batch_data anymore.
-  batch_data_unref(batch_data);
+  batch_data->Unref();
   // Schedule all of the closures identified above.
   // Note: This yeilds the call combiner.
-  closures.RunClosures(calld->call_combiner);
+  closures.RunClosures(calld->call_combiner_);
   // If this was the last subchannel send batch, unref the call stack.
   if (last_send_batch_complete) {
-    GRPC_CALL_STACK_UNREF(calld->owning_call, "subchannel_send_batches");
+    GRPC_CALL_STACK_UNREF(calld->owning_call_, "subchannel_send_batches");
   }
 }
 
@@ -1983,40 +2714,35 @@ static void on_complete(void* arg, grpc_error* error) {
 // subchannel batch construction
 //
 
-// Helper function used to start a subchannel batch in the call combiner.
-static void start_batch_in_call_combiner(void* arg, grpc_error* ignored) {
+void CallData::StartBatchInCallCombiner(void* arg, grpc_error* ignored) {
   grpc_transport_stream_op_batch* batch =
       static_cast<grpc_transport_stream_op_batch*>(arg);
-  grpc_core::SubchannelCall* subchannel_call =
-      static_cast<grpc_core::SubchannelCall*>(batch->handler_private.extra_arg);
+  SubchannelCall* subchannel_call =
+      static_cast<SubchannelCall*>(batch->handler_private.extra_arg);
   // Note: This will release the call combiner.
   subchannel_call->StartTransportStreamOpBatch(batch);
 }
 
-// Adds a closure to closures that will execute batch in the call combiner.
-static void add_closure_for_subchannel_batch(
+void CallData::AddClosureForSubchannelBatch(
     grpc_call_element* elem, grpc_transport_stream_op_batch* batch,
-    grpc_core::CallCombinerClosureList* closures) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  batch->handler_private.extra_arg = calld->subchannel_call.get();
-  GRPC_CLOSURE_INIT(&batch->handler_private.closure,
-                    start_batch_in_call_combiner, batch,
-                    grpc_schedule_on_exec_ctx);
-  if (grpc_client_channel_call_trace.enabled()) {
+    CallCombinerClosureList* closures) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  batch->handler_private.extra_arg = subchannel_call_.get();
+  GRPC_CLOSURE_INIT(&batch->handler_private.closure, StartBatchInCallCombiner,
+                    batch, grpc_schedule_on_exec_ctx);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
     char* batch_str = grpc_transport_stream_op_batch_string(batch);
     gpr_log(GPR_INFO, "chand=%p calld=%p: starting subchannel batch: %s", chand,
-            calld, batch_str);
+            this, batch_str);
     gpr_free(batch_str);
   }
   closures->Add(&batch->handler_private.closure, GRPC_ERROR_NONE,
                 "start_subchannel_batch");
 }
 
-// Adds retriable send_initial_metadata op to batch_data.
-static void add_retriable_send_initial_metadata_op(
-    call_data* calld, subchannel_call_retry_state* retry_state,
-    subchannel_batch_data* batch_data) {
+void CallData::AddRetriableSendInitialMetadataOp(
+    SubchannelCallRetryState* retry_state,
+    SubchannelCallBatchData* batch_data) {
   // Maps the number of retries to the corresponding metadata value slice.
   static const grpc_slice* retry_count_strings[] = {
       &GRPC_MDSTR_1, &GRPC_MDSTR_2, &GRPC_MDSTR_3, &GRPC_MDSTR_4};
@@ -2027,11 +2753,10 @@ static void add_retriable_send_initial_metadata_op(
   // If we've already completed one or more attempts, add the
   // grpc-retry-attempts header.
   retry_state->send_initial_metadata_storage =
-      static_cast<grpc_linked_mdelem*>(gpr_arena_alloc(
-          calld->arena, sizeof(grpc_linked_mdelem) *
-                            (calld->send_initial_metadata.list.count +
-                             (calld->num_attempts_completed > 0))));
-  grpc_metadata_batch_copy(&calld->send_initial_metadata,
+      static_cast<grpc_linked_mdelem*>(arena_->Alloc(
+          sizeof(grpc_linked_mdelem) *
+          (send_initial_metadata_.list.count + (num_attempts_completed_ > 0))));
+  grpc_metadata_batch_copy(&send_initial_metadata_,
                            &retry_state->send_initial_metadata,
                            retry_state->send_initial_metadata_storage);
   if (GPR_UNLIKELY(retry_state->send_initial_metadata.idx.named
@@ -2040,14 +2765,14 @@ static void add_retriable_send_initial_metadata_op(
                                retry_state->send_initial_metadata.idx.named
                                    .grpc_previous_rpc_attempts);
   }
-  if (GPR_UNLIKELY(calld->num_attempts_completed > 0)) {
+  if (GPR_UNLIKELY(num_attempts_completed_ > 0)) {
     grpc_mdelem retry_md = grpc_mdelem_create(
         GRPC_MDSTR_GRPC_PREVIOUS_RPC_ATTEMPTS,
-        *retry_count_strings[calld->num_attempts_completed - 1], nullptr);
+        *retry_count_strings[num_attempts_completed_ - 1], nullptr);
     grpc_error* error = grpc_metadata_batch_add_tail(
         &retry_state->send_initial_metadata,
-        &retry_state->send_initial_metadata_storage[calld->send_initial_metadata
-                                                        .list.count],
+        &retry_state
+             ->send_initial_metadata_storage[send_initial_metadata_.list.count],
         retry_md);
     if (GPR_UNLIKELY(error != GRPC_ERROR_NONE)) {
       gpr_log(GPR_ERROR, "error adding retry metadata: %s",
@@ -2060,24 +2785,21 @@ static void add_retriable_send_initial_metadata_op(
   batch_data->batch.payload->send_initial_metadata.send_initial_metadata =
       &retry_state->send_initial_metadata;
   batch_data->batch.payload->send_initial_metadata.send_initial_metadata_flags =
-      calld->send_initial_metadata_flags;
-  batch_data->batch.payload->send_initial_metadata.peer_string =
-      calld->peer_string;
-}
-
-// Adds retriable send_message op to batch_data.
-static void add_retriable_send_message_op(
-    grpc_call_element* elem, subchannel_call_retry_state* retry_state,
-    subchannel_batch_data* batch_data) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  if (grpc_client_channel_call_trace.enabled()) {
+      send_initial_metadata_flags_;
+  batch_data->batch.payload->send_initial_metadata.peer_string = peer_string_;
+}
+
+void CallData::AddRetriableSendMessageOp(grpc_call_element* elem,
+                                         SubchannelCallRetryState* retry_state,
+                                         SubchannelCallBatchData* batch_data) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
     gpr_log(GPR_INFO,
             "chand=%p calld=%p: starting calld->send_messages[%" PRIuPTR "]",
-            chand, calld, retry_state->started_send_message_count);
+            chand, this, retry_state->started_send_message_count);
   }
-  grpc_core::ByteStreamCache* cache =
-      calld->send_messages[retry_state->started_send_message_count];
+  ByteStreamCache* cache =
+      send_messages_[retry_state->started_send_message_count];
   ++retry_state->started_send_message_count;
   retry_state->send_message.Init(cache);
   batch_data->batch.send_message = true;
@@ -2085,18 +2807,16 @@ static void add_retriable_send_message_op(
       retry_state->send_message.get());
 }
 
-// Adds retriable send_trailing_metadata op to batch_data.
-static void add_retriable_send_trailing_metadata_op(
-    call_data* calld, subchannel_call_retry_state* retry_state,
-    subchannel_batch_data* batch_data) {
+void CallData::AddRetriableSendTrailingMetadataOp(
+    SubchannelCallRetryState* retry_state,
+    SubchannelCallBatchData* batch_data) {
   // We need to make a copy of the metadata batch for each attempt, since
   // the filters in the subchannel stack may modify this batch, and we don't
   // want those modifications to be passed forward to subsequent attempts.
   retry_state->send_trailing_metadata_storage =
-      static_cast<grpc_linked_mdelem*>(gpr_arena_alloc(
-          calld->arena, sizeof(grpc_linked_mdelem) *
-                            calld->send_trailing_metadata.list.count));
-  grpc_metadata_batch_copy(&calld->send_trailing_metadata,
+      static_cast<grpc_linked_mdelem*>(arena_->Alloc(
+          sizeof(grpc_linked_mdelem) * send_trailing_metadata_.list.count));
+  grpc_metadata_batch_copy(&send_trailing_metadata_,
                            &retry_state->send_trailing_metadata,
                            retry_state->send_trailing_metadata_storage);
   retry_state->started_send_trailing_metadata = true;
@@ -2105,10 +2825,9 @@ static void add_retriable_send_trailing_metadata_op(
       &retry_state->send_trailing_metadata;
 }
 
-// Adds retriable recv_initial_metadata op to batch_data.
-static void add_retriable_recv_initial_metadata_op(
-    call_data* calld, subchannel_call_retry_state* retry_state,
-    subchannel_batch_data* batch_data) {
+void CallData::AddRetriableRecvInitialMetadataOp(
+    SubchannelCallRetryState* retry_state,
+    SubchannelCallBatchData* batch_data) {
   retry_state->started_recv_initial_metadata = true;
   batch_data->batch.recv_initial_metadata = true;
   grpc_metadata_batch_init(&retry_state->recv_initial_metadata);
@@ -2117,30 +2836,27 @@ static void add_retriable_recv_initial_metadata_op(
   batch_data->batch.payload->recv_initial_metadata.trailing_metadata_available =
       &retry_state->trailing_metadata_available;
   GRPC_CLOSURE_INIT(&retry_state->recv_initial_metadata_ready,
-                    recv_initial_metadata_ready, batch_data,
+                    RecvInitialMetadataReady, batch_data,
                     grpc_schedule_on_exec_ctx);
   batch_data->batch.payload->recv_initial_metadata.recv_initial_metadata_ready =
       &retry_state->recv_initial_metadata_ready;
 }
 
-// Adds retriable recv_message op to batch_data.
-static void add_retriable_recv_message_op(
-    call_data* calld, subchannel_call_retry_state* retry_state,
-    subchannel_batch_data* batch_data) {
+void CallData::AddRetriableRecvMessageOp(SubchannelCallRetryState* retry_state,
+                                         SubchannelCallBatchData* batch_data) {
   ++retry_state->started_recv_message_count;
   batch_data->batch.recv_message = true;
   batch_data->batch.payload->recv_message.recv_message =
       &retry_state->recv_message;
-  GRPC_CLOSURE_INIT(&retry_state->recv_message_ready, recv_message_ready,
+  GRPC_CLOSURE_INIT(&retry_state->recv_message_ready, RecvMessageReady,
                     batch_data, grpc_schedule_on_exec_ctx);
   batch_data->batch.payload->recv_message.recv_message_ready =
       &retry_state->recv_message_ready;
 }
 
-// Adds retriable recv_trailing_metadata op to batch_data.
-static void add_retriable_recv_trailing_metadata_op(
-    call_data* calld, subchannel_call_retry_state* retry_state,
-    subchannel_batch_data* batch_data) {
+void CallData::AddRetriableRecvTrailingMetadataOp(
+    SubchannelCallRetryState* retry_state,
+    SubchannelCallBatchData* batch_data) {
   retry_state->started_recv_trailing_metadata = true;
   batch_data->batch.recv_trailing_metadata = true;
   grpc_metadata_batch_init(&retry_state->recv_trailing_metadata);
@@ -2149,115 +2865,105 @@ static void add_retriable_recv_trailing_metadata_op(
   batch_data->batch.payload->recv_trailing_metadata.collect_stats =
       &retry_state->collect_stats;
   GRPC_CLOSURE_INIT(&retry_state->recv_trailing_metadata_ready,
-                    recv_trailing_metadata_ready, batch_data,
+                    RecvTrailingMetadataReady, batch_data,
                     grpc_schedule_on_exec_ctx);
   batch_data->batch.payload->recv_trailing_metadata
       .recv_trailing_metadata_ready =
       &retry_state->recv_trailing_metadata_ready;
-  maybe_inject_recv_trailing_metadata_ready_for_lb(calld->pick.pick,
-                                                   &batch_data->batch);
-}
-
-// Helper function used to start a recv_trailing_metadata batch.  This
-// is used in the case where a recv_initial_metadata or recv_message
-// op fails in a way that we know the call is over but when the application
-// has not yet started its own recv_trailing_metadata op.
-static void start_internal_recv_trailing_metadata(grpc_call_element* elem) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  if (grpc_client_channel_call_trace.enabled()) {
+  MaybeInjectRecvTrailingMetadataReadyForLoadBalancingPolicy(
+      pick_.pick, &batch_data->batch);
+}
+
+void CallData::StartInternalRecvTrailingMetadata(grpc_call_element* elem) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
     gpr_log(GPR_INFO,
             "chand=%p calld=%p: call failed but recv_trailing_metadata not "
             "started; starting it internally",
-            chand, calld);
+            chand, this);
   }
-  subchannel_call_retry_state* retry_state =
-      static_cast<subchannel_call_retry_state*>(
-          calld->subchannel_call->GetParentData());
+  SubchannelCallRetryState* retry_state =
+      static_cast<SubchannelCallRetryState*>(subchannel_call_->GetParentData());
   // Create batch_data with 2 refs, since this batch will be unreffed twice:
   // once for the recv_trailing_metadata_ready callback when the subchannel
   // batch returns, and again when we actually get a recv_trailing_metadata
   // op from the surface.
-  subchannel_batch_data* batch_data =
-      batch_data_create(elem, 2, false /* set_on_complete */);
-  add_retriable_recv_trailing_metadata_op(calld, retry_state, batch_data);
+  SubchannelCallBatchData* batch_data =
+      SubchannelCallBatchData::Create(elem, 2, false /* set_on_complete */);
+  AddRetriableRecvTrailingMetadataOp(retry_state, batch_data);
   retry_state->recv_trailing_metadata_internal_batch = batch_data;
   // Note: This will release the call combiner.
-  calld->subchannel_call->StartTransportStreamOpBatch(&batch_data->batch);
+  subchannel_call_->StartTransportStreamOpBatch(&batch_data->batch);
 }
 
 // If there are any cached send ops that need to be replayed on the
 // current subchannel call, creates and returns a new subchannel batch
 // to replay those ops.  Otherwise, returns nullptr.
-static subchannel_batch_data* maybe_create_subchannel_batch_for_replay(
-    grpc_call_element* elem, subchannel_call_retry_state* retry_state) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  subchannel_batch_data* replay_batch_data = nullptr;
+CallData::SubchannelCallBatchData*
+CallData::MaybeCreateSubchannelBatchForReplay(
+    grpc_call_element* elem, SubchannelCallRetryState* retry_state) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  SubchannelCallBatchData* replay_batch_data = nullptr;
   // send_initial_metadata.
-  if (calld->seen_send_initial_metadata &&
+  if (seen_send_initial_metadata_ &&
       !retry_state->started_send_initial_metadata &&
-      !calld->pending_send_initial_metadata) {
-    if (grpc_client_channel_call_trace.enabled()) {
+      !pending_send_initial_metadata_) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
       gpr_log(GPR_INFO,
               "chand=%p calld=%p: replaying previously completed "
               "send_initial_metadata op",
-              chand, calld);
+              chand, this);
     }
-    replay_batch_data = batch_data_create(elem, 1, true /* set_on_complete */);
-    add_retriable_send_initial_metadata_op(calld, retry_state,
-                                           replay_batch_data);
+    replay_batch_data =
+        SubchannelCallBatchData::Create(elem, 1, true /* set_on_complete */);
+    AddRetriableSendInitialMetadataOp(retry_state, replay_batch_data);
   }
   // send_message.
   // Note that we can only have one send_message op in flight at a time.
-  if (retry_state->started_send_message_count < calld->send_messages.size() &&
+  if (retry_state->started_send_message_count < send_messages_.size() &&
       retry_state->started_send_message_count ==
           retry_state->completed_send_message_count &&
-      !calld->pending_send_message) {
-    if (grpc_client_channel_call_trace.enabled()) {
+      !pending_send_message_) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
       gpr_log(GPR_INFO,
               "chand=%p calld=%p: replaying previously completed "
               "send_message op",
-              chand, calld);
+              chand, this);
     }
     if (replay_batch_data == nullptr) {
       replay_batch_data =
-          batch_data_create(elem, 1, true /* set_on_complete */);
+          SubchannelCallBatchData::Create(elem, 1, true /* set_on_complete */);
     }
-    add_retriable_send_message_op(elem, retry_state, replay_batch_data);
+    AddRetriableSendMessageOp(elem, retry_state, replay_batch_data);
   }
   // send_trailing_metadata.
   // Note that we only add this op if we have no more send_message ops
   // to start, since we can't send down any more send_message ops after
   // send_trailing_metadata.
-  if (calld->seen_send_trailing_metadata &&
-      retry_state->started_send_message_count == calld->send_messages.size() &&
+  if (seen_send_trailing_metadata_ &&
+      retry_state->started_send_message_count == send_messages_.size() &&
       !retry_state->started_send_trailing_metadata &&
-      !calld->pending_send_trailing_metadata) {
-    if (grpc_client_channel_call_trace.enabled()) {
+      !pending_send_trailing_metadata_) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
       gpr_log(GPR_INFO,
               "chand=%p calld=%p: replaying previously completed "
               "send_trailing_metadata op",
-              chand, calld);
+              chand, this);
     }
     if (replay_batch_data == nullptr) {
       replay_batch_data =
-          batch_data_create(elem, 1, true /* set_on_complete */);
+          SubchannelCallBatchData::Create(elem, 1, true /* set_on_complete */);
     }
-    add_retriable_send_trailing_metadata_op(calld, retry_state,
-                                            replay_batch_data);
+    AddRetriableSendTrailingMetadataOp(retry_state, replay_batch_data);
   }
   return replay_batch_data;
 }
 
-// Adds subchannel batches for pending batches to batches, updating
-// *num_batches as needed.
-static void add_subchannel_batches_for_pending_batches(
-    grpc_call_element* elem, subchannel_call_retry_state* retry_state,
-    grpc_core::CallCombinerClosureList* closures) {
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
-    pending_batch* pending = &calld->pending_batches[i];
+void CallData::AddSubchannelBatchesForPendingBatches(
+    grpc_call_element* elem, SubchannelCallRetryState* retry_state,
+    CallCombinerClosureList* closures) {
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(pending_batches_); ++i) {
+    PendingBatch* pending = &pending_batches_[i];
     grpc_transport_stream_op_batch* batch = pending->batch;
     if (batch == nullptr) continue;
     // Skip any batch that either (a) has already been started on this
@@ -2283,7 +2989,7 @@ static void add_subchannel_batches_for_pending_batches(
     // send_message ops after send_trailing_metadata.
     if (batch->send_trailing_metadata &&
         (retry_state->started_send_message_count + batch->send_message <
-             calld->send_messages.size() ||
+             send_messages_.size() ||
          retry_state->started_send_trailing_metadata)) {
       continue;
     }
@@ -2298,7 +3004,7 @@ static void add_subchannel_batches_for_pending_batches(
     if (batch->recv_trailing_metadata &&
         retry_state->started_recv_trailing_metadata) {
       // If we previously completed a recv_trailing_metadata op
-      // initiated by start_internal_recv_trailing_metadata(), use the
+      // initiated by StartInternalRecvTrailingMetadata(), use the
       // result of that instead of trying to re-start this op.
       if (GPR_UNLIKELY((retry_state->recv_trailing_metadata_internal_batch !=
                         nullptr))) {
@@ -2314,18 +3020,19 @@ static void add_subchannel_batches_for_pending_batches(
               "re-executing recv_trailing_metadata_ready to propagate "
               "internally triggered result");
         } else {
-          batch_data_unref(retry_state->recv_trailing_metadata_internal_batch);
+          retry_state->recv_trailing_metadata_internal_batch->Unref();
         }
         retry_state->recv_trailing_metadata_internal_batch = nullptr;
       }
       continue;
     }
     // If we're not retrying, just send the batch as-is.
-    if (calld->method_params == nullptr ||
-        calld->method_params->retry_policy() == nullptr ||
-        calld->retry_committed) {
-      add_closure_for_subchannel_batch(elem, batch, closures);
-      pending_batch_clear(calld, pending);
+    if (method_params_ == nullptr ||
+        method_params_->retry_policy() == nullptr || retry_committed_) {
+      // TODO(roth) : We should probably call
+      // MaybeInjectRecvTrailingMetadataReadyForLoadBalancingPolicy here.
+      AddClosureForSubchannelBatch(elem, batch, closures);
+      PendingBatchClear(pending);
       continue;
     }
     // Create batch with the right number of callbacks.
@@ -2335,183 +3042,168 @@ static void add_subchannel_batches_for_pending_batches(
     const int num_callbacks = has_send_ops + batch->recv_initial_metadata +
                               batch->recv_message +
                               batch->recv_trailing_metadata;
-    subchannel_batch_data* batch_data = batch_data_create(
+    SubchannelCallBatchData* batch_data = SubchannelCallBatchData::Create(
         elem, num_callbacks, has_send_ops /* set_on_complete */);
     // Cache send ops if needed.
-    maybe_cache_send_ops_for_batch(calld, pending);
+    MaybeCacheSendOpsForBatch(pending);
     // send_initial_metadata.
     if (batch->send_initial_metadata) {
-      add_retriable_send_initial_metadata_op(calld, retry_state, batch_data);
+      AddRetriableSendInitialMetadataOp(retry_state, batch_data);
     }
     // send_message.
     if (batch->send_message) {
-      add_retriable_send_message_op(elem, retry_state, batch_data);
+      AddRetriableSendMessageOp(elem, retry_state, batch_data);
     }
     // send_trailing_metadata.
     if (batch->send_trailing_metadata) {
-      add_retriable_send_trailing_metadata_op(calld, retry_state, batch_data);
+      AddRetriableSendTrailingMetadataOp(retry_state, batch_data);
     }
     // recv_initial_metadata.
     if (batch->recv_initial_metadata) {
       // recv_flags is only used on the server side.
       GPR_ASSERT(batch->payload->recv_initial_metadata.recv_flags == nullptr);
-      add_retriable_recv_initial_metadata_op(calld, retry_state, batch_data);
+      AddRetriableRecvInitialMetadataOp(retry_state, batch_data);
     }
     // recv_message.
     if (batch->recv_message) {
-      add_retriable_recv_message_op(calld, retry_state, batch_data);
+      AddRetriableRecvMessageOp(retry_state, batch_data);
     }
     // recv_trailing_metadata.
     if (batch->recv_trailing_metadata) {
-      add_retriable_recv_trailing_metadata_op(calld, retry_state, batch_data);
+      AddRetriableRecvTrailingMetadataOp(retry_state, batch_data);
     }
-    add_closure_for_subchannel_batch(elem, &batch_data->batch, closures);
+    AddClosureForSubchannelBatch(elem, &batch_data->batch, closures);
     // Track number of pending subchannel send batches.
     // If this is the first one, take a ref to the call stack.
     if (batch->send_initial_metadata || batch->send_message ||
         batch->send_trailing_metadata) {
-      if (calld->num_pending_retriable_subchannel_send_batches == 0) {
-        GRPC_CALL_STACK_REF(calld->owning_call, "subchannel_send_batches");
+      if (num_pending_retriable_subchannel_send_batches_ == 0) {
+        GRPC_CALL_STACK_REF(owning_call_, "subchannel_send_batches");
       }
-      ++calld->num_pending_retriable_subchannel_send_batches;
+      ++num_pending_retriable_subchannel_send_batches_;
     }
   }
 }
 
-// Constructs and starts whatever subchannel batches are needed on the
-// subchannel call.
-static void start_retriable_subchannel_batches(void* arg, grpc_error* ignored) {
+void CallData::StartRetriableSubchannelBatches(void* arg, grpc_error* ignored) {
   grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  if (grpc_client_channel_call_trace.enabled()) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  CallData* calld = static_cast<CallData*>(elem->call_data);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
     gpr_log(GPR_INFO, "chand=%p calld=%p: constructing retriable batches",
             chand, calld);
   }
-  subchannel_call_retry_state* retry_state =
-      static_cast<subchannel_call_retry_state*>(
-          calld->subchannel_call->GetParentData());
+  SubchannelCallRetryState* retry_state =
+      static_cast<SubchannelCallRetryState*>(
+          calld->subchannel_call_->GetParentData());
   // Construct list of closures to execute, one for each pending batch.
-  grpc_core::CallCombinerClosureList closures;
+  CallCombinerClosureList closures;
   // Replay previously-returned send_* ops if needed.
-  subchannel_batch_data* replay_batch_data =
-      maybe_create_subchannel_batch_for_replay(elem, retry_state);
+  SubchannelCallBatchData* replay_batch_data =
+      calld->MaybeCreateSubchannelBatchForReplay(elem, retry_state);
   if (replay_batch_data != nullptr) {
-    add_closure_for_subchannel_batch(elem, &replay_batch_data->batch,
-                                     &closures);
+    calld->AddClosureForSubchannelBatch(elem, &replay_batch_data->batch,
+                                        &closures);
     // Track number of pending subchannel send batches.
     // If this is the first one, take a ref to the call stack.
-    if (calld->num_pending_retriable_subchannel_send_batches == 0) {
-      GRPC_CALL_STACK_REF(calld->owning_call, "subchannel_send_batches");
+    if (calld->num_pending_retriable_subchannel_send_batches_ == 0) {
+      GRPC_CALL_STACK_REF(calld->owning_call_, "subchannel_send_batches");
     }
-    ++calld->num_pending_retriable_subchannel_send_batches;
+    ++calld->num_pending_retriable_subchannel_send_batches_;
   }
   // Now add pending batches.
-  add_subchannel_batches_for_pending_batches(elem, retry_state, &closures);
+  calld->AddSubchannelBatchesForPendingBatches(elem, retry_state, &closures);
   // Start batches on subchannel call.
-  if (grpc_client_channel_call_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
     gpr_log(GPR_INFO,
             "chand=%p calld=%p: starting %" PRIuPTR
             " retriable batches on subchannel_call=%p",
-            chand, calld, closures.size(), calld->subchannel_call.get());
+            chand, calld, closures.size(), calld->subchannel_call_.get());
   }
   // Note: This will yield the call combiner.
-  closures.RunClosures(calld->call_combiner);
+  closures.RunClosures(calld->call_combiner_);
 }
 
 //
 // LB pick
 //
 
-static void create_subchannel_call(grpc_call_element* elem) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
+void CallData::CreateSubchannelCall(grpc_call_element* elem) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
   const size_t parent_data_size =
-      calld->enable_retries ? sizeof(subchannel_call_retry_state) : 0;
-  const grpc_core::ConnectedSubchannel::CallArgs call_args = {
-      calld->pollent,          // pollent
-      calld->path,             // path
-      calld->call_start_time,  // start_time
-      calld->deadline,         // deadline
-      calld->arena,            // arena
+      enable_retries_ ? sizeof(SubchannelCallRetryState) : 0;
+  const ConnectedSubchannel::CallArgs call_args = {
+      pollent_, path_, call_start_time_, deadline_, arena_,
       // TODO(roth): When we implement hedging support, we will probably
       // need to use a separate call context for each subchannel call.
-      calld->call_context,   // context
-      calld->call_combiner,  // call_combiner
-      parent_data_size       // parent_data_size
-  };
+      call_context_, call_combiner_, parent_data_size};
   grpc_error* error = GRPC_ERROR_NONE;
-  calld->subchannel_call =
-      calld->pick.pick.connected_subchannel->CreateCall(call_args, &error);
-  if (grpc_client_channel_routing_trace.enabled()) {
+  subchannel_call_ =
+      pick_.pick.connected_subchannel->CreateCall(call_args, &error);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
     gpr_log(GPR_INFO, "chand=%p calld=%p: create subchannel_call=%p: error=%s",
-            chand, calld, calld->subchannel_call.get(),
-            grpc_error_string(error));
+            chand, this, subchannel_call_.get(), grpc_error_string(error));
   }
   if (GPR_UNLIKELY(error != GRPC_ERROR_NONE)) {
-    pending_batches_fail(elem, error, yield_call_combiner);
+    PendingBatchesFail(elem, error, YieldCallCombiner);
   } else {
     if (parent_data_size > 0) {
-      new (calld->subchannel_call->GetParentData())
-          subchannel_call_retry_state(calld->call_context);
+      new (subchannel_call_->GetParentData())
+          SubchannelCallRetryState(call_context_);
     }
-    pending_batches_resume(elem);
+    PendingBatchesResume(elem);
   }
 }
 
-// Invoked when a pick is completed, on both success or failure.
-static void pick_done(void* arg, grpc_error* error) {
+void CallData::PickDone(void* arg, grpc_error* error) {
   grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  CallData* calld = static_cast<CallData*>(elem->call_data);
   if (error != GRPC_ERROR_NONE) {
-    if (grpc_client_channel_routing_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
       gpr_log(GPR_INFO,
               "chand=%p calld=%p: failed to pick subchannel: error=%s", chand,
               calld, grpc_error_string(error));
     }
-    pending_batches_fail(elem, GRPC_ERROR_REF(error), yield_call_combiner);
+    calld->PendingBatchesFail(elem, GRPC_ERROR_REF(error), YieldCallCombiner);
     return;
   }
-  create_subchannel_call(elem);
+  calld->CreateSubchannelCall(elem);
 }
 
-namespace grpc_core {
-namespace {
-
 // A class to handle the call combiner cancellation callback for a
 // queued pick.
-class QueuedPickCanceller {
+class CallData::QueuedPickCanceller {
  public:
   explicit QueuedPickCanceller(grpc_call_element* elem) : elem_(elem) {
-    auto* calld = static_cast<call_data*>(elem->call_data);
-    auto* chand = static_cast<channel_data*>(elem->channel_data);
-    GRPC_CALL_STACK_REF(calld->owning_call, "QueuedPickCanceller");
+    auto* calld = static_cast<CallData*>(elem->call_data);
+    auto* chand = static_cast<ChannelData*>(elem->channel_data);
+    GRPC_CALL_STACK_REF(calld->owning_call_, "QueuedPickCanceller");
     GRPC_CLOSURE_INIT(&closure_, &CancelLocked, this,
-                      grpc_combiner_scheduler(chand->combiner));
-    grpc_call_combiner_set_notify_on_cancel(calld->call_combiner, &closure_);
+                      grpc_combiner_scheduler(chand->data_plane_combiner()));
+    calld->call_combiner_->SetNotifyOnCancel(&closure_);
   }
 
  private:
   static void CancelLocked(void* arg, grpc_error* error) {
     auto* self = static_cast<QueuedPickCanceller*>(arg);
-    auto* chand = static_cast<channel_data*>(self->elem_->channel_data);
-    auto* calld = static_cast<call_data*>(self->elem_->call_data);
-    if (grpc_client_channel_routing_trace.enabled()) {
+    auto* chand = static_cast<ChannelData*>(self->elem_->channel_data);
+    auto* calld = static_cast<CallData*>(self->elem_->call_data);
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
       gpr_log(GPR_INFO,
               "chand=%p calld=%p: cancelling queued pick: "
               "error=%s self=%p calld->pick_canceller=%p",
               chand, calld, grpc_error_string(error), self,
-              calld->pick_canceller);
+              calld->pick_canceller_);
     }
-    if (calld->pick_canceller == self && error != GRPC_ERROR_NONE) {
+    if (calld->pick_canceller_ == self && error != GRPC_ERROR_NONE) {
       // Remove pick from list of queued picks.
-      remove_call_from_queued_picks_locked(self->elem_);
+      calld->RemoveCallFromQueuedPicksLocked(self->elem_);
       // Fail pending batches on the call.
-      pending_batches_fail(self->elem_, GRPC_ERROR_REF(error),
-                           yield_call_combiner_if_pending_batches_found);
+      calld->PendingBatchesFail(self->elem_, GRPC_ERROR_REF(error),
+                                YieldCallCombinerIfPendingBatchesFound);
     }
-    GRPC_CALL_STACK_UNREF(calld->owning_call, "QueuedPickCanceller");
+    GRPC_CALL_STACK_UNREF(calld->owning_call_, "QueuedPickCanceller");
     Delete(self);
   }
 
@@ -2519,123 +3211,96 @@ class QueuedPickCanceller {
   grpc_closure closure_;
 };
 
-}  // namespace
-}  // namespace grpc_core
-
-// Removes the call from the channel's list of queued picks.
-static void remove_call_from_queued_picks_locked(grpc_call_element* elem) {
-  auto* chand = static_cast<channel_data*>(elem->channel_data);
-  auto* calld = static_cast<call_data*>(elem->call_data);
-  for (QueuedPick** pick = &chand->queued_picks; *pick != nullptr;
-       pick = &(*pick)->next) {
-    if (*pick == &calld->pick) {
-      if (grpc_client_channel_routing_trace.enabled()) {
-        gpr_log(GPR_INFO, "chand=%p calld=%p: removing from queued picks list",
-                chand, calld);
-      }
-      calld->pick_queued = false;
-      *pick = calld->pick.next;
-      // Remove call's pollent from channel's interested_parties.
-      grpc_polling_entity_del_from_pollset_set(calld->pollent,
-                                               chand->interested_parties);
-      // Lame the call combiner canceller.
-      calld->pick_canceller = nullptr;
-      break;
-    }
+void CallData::RemoveCallFromQueuedPicksLocked(grpc_call_element* elem) {
+  auto* chand = static_cast<ChannelData*>(elem->channel_data);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
+    gpr_log(GPR_INFO, "chand=%p calld=%p: removing from queued picks list",
+            chand, this);
   }
+  chand->RemoveQueuedPick(&pick_, pollent_);
+  pick_queued_ = false;
+  // Lame the call combiner canceller.
+  pick_canceller_ = nullptr;
 }
 
-// Adds the call to the channel's list of queued picks.
-static void add_call_to_queued_picks_locked(grpc_call_element* elem) {
-  auto* chand = static_cast<channel_data*>(elem->channel_data);
-  auto* calld = static_cast<call_data*>(elem->call_data);
-  if (grpc_client_channel_routing_trace.enabled()) {
+void CallData::AddCallToQueuedPicksLocked(grpc_call_element* elem) {
+  auto* chand = static_cast<ChannelData*>(elem->channel_data);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
     gpr_log(GPR_INFO, "chand=%p calld=%p: adding to queued picks list", chand,
-            calld);
+            this);
   }
-  calld->pick_queued = true;
-  // Add call to queued picks list.
-  calld->pick.elem = elem;
-  calld->pick.next = chand->queued_picks;
-  chand->queued_picks = &calld->pick;
-  // Add call's pollent to channel's interested_parties, so that I/O
-  // can be done under the call's CQ.
-  grpc_polling_entity_add_to_pollset_set(calld->pollent,
-                                         chand->interested_parties);
+  pick_queued_ = true;
+  pick_.elem = elem;
+  chand->AddQueuedPick(&pick_, pollent_);
   // Register call combiner cancellation callback.
-  calld->pick_canceller = grpc_core::New<grpc_core::QueuedPickCanceller>(elem);
+  pick_canceller_ = New<QueuedPickCanceller>(elem);
 }
 
-// Applies service config to the call.  Must be invoked once we know
-// that the resolver has returned results to the channel.
-static void apply_service_config_to_call_locked(grpc_call_element* elem) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  if (grpc_client_channel_routing_trace.enabled()) {
+void CallData::ApplyServiceConfigToCallLocked(grpc_call_element* elem) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
     gpr_log(GPR_INFO, "chand=%p calld=%p: applying service config to call",
-            chand, calld);
-  }
-  if (chand->retry_throttle_data != nullptr) {
-    calld->retry_throttle_data = chand->retry_throttle_data->Ref();
-  }
-  if (chand->method_params_table != nullptr) {
-    calld->method_params = grpc_core::ServiceConfig::MethodConfigTableLookup(
-        *chand->method_params_table, calld->path);
-    if (calld->method_params != nullptr) {
-      // If the deadline from the service config is shorter than the one
-      // from the client API, reset the deadline timer.
-      if (chand->deadline_checking_enabled &&
-          calld->method_params->timeout() != 0) {
-        const grpc_millis per_method_deadline =
-            grpc_timespec_to_millis_round_up(calld->call_start_time) +
-            calld->method_params->timeout();
-        if (per_method_deadline < calld->deadline) {
-          calld->deadline = per_method_deadline;
-          grpc_deadline_state_reset(elem, calld->deadline);
-        }
+            chand, this);
+  }
+  // Store a ref to the service_config in service_config_call_data_. Also, save
+  // a pointer to this in the call_context so that all future filters can access
+  // it.
+  service_config_call_data_ =
+      ServiceConfig::CallData(chand->service_config(), path_);
+  if (service_config_call_data_.service_config() != nullptr) {
+    call_context_[GRPC_SERVICE_CONFIG_CALL_DATA].value =
+        &service_config_call_data_;
+    method_params_ = static_cast<ClientChannelMethodParsedObject*>(
+        service_config_call_data_.GetMethodParsedObject(
+            internal::ClientChannelServiceConfigParser::ParserIndex()));
+  }
+  retry_throttle_data_ = chand->retry_throttle_data();
+  if (method_params_ != nullptr) {
+    // If the deadline from the service config is shorter than the one
+    // from the client API, reset the deadline timer.
+    if (chand->deadline_checking_enabled() && method_params_->timeout() != 0) {
+      const grpc_millis per_method_deadline =
+          grpc_timespec_to_millis_round_up(call_start_time_) +
+          method_params_->timeout();
+      if (per_method_deadline < deadline_) {
+        deadline_ = per_method_deadline;
+        grpc_deadline_state_reset(elem, deadline_);
       }
-      // If the service config set wait_for_ready and the application
-      // did not explicitly set it, use the value from the service config.
-      uint32_t* send_initial_metadata_flags =
-          &calld->pending_batches[0]
-               .batch->payload->send_initial_metadata
-               .send_initial_metadata_flags;
-      if (GPR_UNLIKELY(
-              calld->method_params->wait_for_ready() !=
-                  ClientChannelMethodParams::WAIT_FOR_READY_UNSET &&
-              !(*send_initial_metadata_flags &
-                GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET))) {
-        if (calld->method_params->wait_for_ready() ==
-            ClientChannelMethodParams::WAIT_FOR_READY_TRUE) {
-          *send_initial_metadata_flags |= GRPC_INITIAL_METADATA_WAIT_FOR_READY;
-        } else {
-          *send_initial_metadata_flags &= ~GRPC_INITIAL_METADATA_WAIT_FOR_READY;
-        }
+    }
+    // If the service config set wait_for_ready and the application
+    // did not explicitly set it, use the value from the service config.
+    uint32_t* send_initial_metadata_flags =
+        &pending_batches_[0]
+             .batch->payload->send_initial_metadata.send_initial_metadata_flags;
+    if (method_params_->wait_for_ready().has_value() &&
+        !(*send_initial_metadata_flags &
+          GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET)) {
+      if (method_params_->wait_for_ready().value()) {
+        *send_initial_metadata_flags |= GRPC_INITIAL_METADATA_WAIT_FOR_READY;
+      } else {
+        *send_initial_metadata_flags &= ~GRPC_INITIAL_METADATA_WAIT_FOR_READY;
       }
     }
   }
   // If no retry policy, disable retries.
   // TODO(roth): Remove this when adding support for transparent retries.
-  if (calld->method_params == nullptr ||
-      calld->method_params->retry_policy() == nullptr) {
-    calld->enable_retries = false;
+  if (method_params_ == nullptr || method_params_->retry_policy() == nullptr) {
+    enable_retries_ = false;
   }
 }
 
-// Invoked once resolver results are available.
-static void maybe_apply_service_config_to_call_locked(grpc_call_element* elem) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
+void CallData::MaybeApplyServiceConfigToCallLocked(grpc_call_element* elem) {
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
   // Apply service config data to the call only once, and only if the
   // channel has the data available.
-  if (GPR_LIKELY(chand->have_service_config &&
-                 !calld->service_config_applied)) {
-    calld->service_config_applied = true;
-    apply_service_config_to_call_locked(elem);
+  if (GPR_LIKELY(chand->received_service_config_data() &&
+                 !service_config_applied_)) {
+    service_config_applied_ = true;
+    ApplyServiceConfigToCallLocked(elem);
   }
 }
 
-static const char* pick_result_name(LoadBalancingPolicy::PickResult result) {
+const char* PickResultName(LoadBalancingPolicy::PickResult result) {
   switch (result) {
     case LoadBalancingPolicy::PICK_COMPLETE:
       return "COMPLETE";
@@ -2647,56 +3312,57 @@ static const char* pick_result_name(LoadBalancingPolicy::PickResult result) {
   GPR_UNREACHABLE_CODE(return "UNKNOWN");
 }
 
-static void start_pick_locked(void* arg, grpc_error* error) {
+void CallData::StartPickLocked(void* arg, grpc_error* error) {
   grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  GPR_ASSERT(calld->pick.pick.connected_subchannel == nullptr);
-  GPR_ASSERT(calld->subchannel_call == nullptr);
+  CallData* calld = static_cast<CallData*>(elem->call_data);
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  GPR_ASSERT(calld->pick_.pick.connected_subchannel == nullptr);
+  GPR_ASSERT(calld->subchannel_call_ == nullptr);
   // If this is a retry, use the send_initial_metadata payload that
   // we've cached; otherwise, use the pending batch.  The
   // send_initial_metadata batch will be the first pending batch in the
-  // list, as set by get_batch_index() above.
+  // list, as set by GetBatchIndex() above.
   // TODO(roth): What if the LB policy needs to add something to the
   // call's initial metadata, and then there's a retry?  We don't want
   // the new metadata to be added twice.  We might need to somehow
   // allocate the subchannel batch earlier so that we can give the
   // subchannel's copy of the metadata batch (which is copied for each
   // attempt) to the LB policy instead the one from the parent channel.
-  calld->pick.pick.initial_metadata =
-      calld->seen_send_initial_metadata
-          ? &calld->send_initial_metadata
-          : calld->pending_batches[0]
+  calld->pick_.pick.initial_metadata =
+      calld->seen_send_initial_metadata_
+          ? &calld->send_initial_metadata_
+          : calld->pending_batches_[0]
                 .batch->payload->send_initial_metadata.send_initial_metadata;
   uint32_t* send_initial_metadata_flags =
-      calld->seen_send_initial_metadata
-          ? &calld->send_initial_metadata_flags
-          : &calld->pending_batches[0]
+      calld->seen_send_initial_metadata_
+          ? &calld->send_initial_metadata_flags_
+          : &calld->pending_batches_[0]
                  .batch->payload->send_initial_metadata
                  .send_initial_metadata_flags;
   // Apply service config to call if needed.
-  maybe_apply_service_config_to_call_locked(elem);
-  // When done, we schedule this closure to leave the channel combiner.
-  GRPC_CLOSURE_INIT(&calld->pick_closure, pick_done, elem,
+  calld->MaybeApplyServiceConfigToCallLocked(elem);
+  // When done, we schedule this closure to leave the data plane combiner.
+  GRPC_CLOSURE_INIT(&calld->pick_closure_, PickDone, elem,
                     grpc_schedule_on_exec_ctx);
   // Attempt pick.
   error = GRPC_ERROR_NONE;
-  auto pick_result = chand->picker->Pick(&calld->pick.pick, &error);
-  if (grpc_client_channel_routing_trace.enabled()) {
+  auto pick_result = chand->picker()->Pick(&calld->pick_.pick, &error);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
     gpr_log(GPR_INFO,
             "chand=%p calld=%p: LB pick returned %s (connected_subchannel=%p, "
             "error=%s)",
-            chand, calld, pick_result_name(pick_result),
-            calld->pick.pick.connected_subchannel.get(),
+            chand, calld, PickResultName(pick_result),
+            calld->pick_.pick.connected_subchannel.get(),
             grpc_error_string(error));
   }
   switch (pick_result) {
-    case LoadBalancingPolicy::PICK_TRANSIENT_FAILURE:
+    case LoadBalancingPolicy::PICK_TRANSIENT_FAILURE: {
       // If we're shutting down, fail all RPCs.
-      if (chand->disconnect_error != GRPC_ERROR_NONE) {
+      grpc_error* disconnect_error = chand->disconnect_error();
+      if (disconnect_error != GRPC_ERROR_NONE) {
         GRPC_ERROR_UNREF(error);
-        GRPC_CLOSURE_SCHED(&calld->pick_closure,
-                           GRPC_ERROR_REF(chand->disconnect_error));
+        GRPC_CLOSURE_SCHED(&calld->pick_closure_,
+                           GRPC_ERROR_REF(disconnect_error));
         break;
       }
       // If wait_for_ready is false, then the error indicates the RPC
@@ -2705,355 +3371,100 @@ static void start_pick_locked(void* arg, grpc_error* error) {
            GRPC_INITIAL_METADATA_WAIT_FOR_READY) == 0) {
         // Retry if appropriate; otherwise, fail.
         grpc_status_code status = GRPC_STATUS_OK;
-        grpc_error_get_status(error, calld->deadline, &status, nullptr, nullptr,
-                              nullptr);
-        if (!calld->enable_retries ||
-            !maybe_retry(elem, nullptr /* batch_data */, status,
-                         nullptr /* server_pushback_md */)) {
+        grpc_error_get_status(error, calld->deadline_, &status, nullptr,
+                              nullptr, nullptr);
+        if (!calld->enable_retries_ ||
+            !calld->MaybeRetry(elem, nullptr /* batch_data */, status,
+                               nullptr /* server_pushback_md */)) {
           grpc_error* new_error =
               GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
-                  "Failed to create subchannel", &error, 1);
+                  "Failed to pick subchannel", &error, 1);
           GRPC_ERROR_UNREF(error);
-          GRPC_CLOSURE_SCHED(&calld->pick_closure, new_error);
+          GRPC_CLOSURE_SCHED(&calld->pick_closure_, new_error);
         }
-        if (calld->pick_queued) remove_call_from_queued_picks_locked(elem);
+        if (calld->pick_queued_) calld->RemoveCallFromQueuedPicksLocked(elem);
         break;
       }
       // If wait_for_ready is true, then queue to retry when we get a new
       // picker.
       GRPC_ERROR_UNREF(error);
-      // Fallthrough
+    }
+    // Fallthrough
     case LoadBalancingPolicy::PICK_QUEUE:
-      if (!calld->pick_queued) add_call_to_queued_picks_locked(elem);
+      if (!calld->pick_queued_) calld->AddCallToQueuedPicksLocked(elem);
       break;
     default:  // PICK_COMPLETE
       // Handle drops.
-      if (GPR_UNLIKELY(calld->pick.pick.connected_subchannel == nullptr)) {
+      if (GPR_UNLIKELY(calld->pick_.pick.connected_subchannel == nullptr)) {
         error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
             "Call dropped by load balancing policy");
       }
-      GRPC_CLOSURE_SCHED(&calld->pick_closure, error);
-      if (calld->pick_queued) remove_call_from_queued_picks_locked(elem);
-  }
-}
-
-//
-// filter call vtable functions
-//
-
-static void cc_start_transport_stream_op_batch(
-    grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
-  GPR_TIMER_SCOPE("cc_start_transport_stream_op_batch", 0);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  if (GPR_LIKELY(chand->deadline_checking_enabled)) {
-    grpc_deadline_state_client_start_transport_stream_op_batch(elem, batch);
-  }
-  // If we've previously been cancelled, immediately fail any new batches.
-  if (GPR_UNLIKELY(calld->cancel_error != GRPC_ERROR_NONE)) {
-    if (grpc_client_channel_call_trace.enabled()) {
-      gpr_log(GPR_INFO, "chand=%p calld=%p: failing batch with error: %s",
-              chand, calld, grpc_error_string(calld->cancel_error));
-    }
-    // Note: This will release the call combiner.
-    grpc_transport_stream_op_batch_finish_with_failure(
-        batch, GRPC_ERROR_REF(calld->cancel_error), calld->call_combiner);
-    return;
-  }
-  // Handle cancellation.
-  if (GPR_UNLIKELY(batch->cancel_stream)) {
-    // Stash a copy of cancel_error in our call data, so that we can use
-    // it for subsequent operations.  This ensures that if the call is
-    // cancelled before any batches are passed down (e.g., if the deadline
-    // is in the past when the call starts), we can return the right
-    // error to the caller when the first batch does get passed down.
-    GRPC_ERROR_UNREF(calld->cancel_error);
-    calld->cancel_error =
-        GRPC_ERROR_REF(batch->payload->cancel_stream.cancel_error);
-    if (grpc_client_channel_call_trace.enabled()) {
-      gpr_log(GPR_INFO, "chand=%p calld=%p: recording cancel_error=%s", chand,
-              calld, grpc_error_string(calld->cancel_error));
-    }
-    // If we do not have a subchannel call (i.e., a pick has not yet
-    // been started), fail all pending batches.  Otherwise, send the
-    // cancellation down to the subchannel call.
-    if (calld->subchannel_call == nullptr) {
-      // TODO(roth): If there is a pending retry callback, do we need to
-      // cancel it here?
-      pending_batches_fail(elem, GRPC_ERROR_REF(calld->cancel_error),
-                           no_yield_call_combiner);
-      // Note: This will release the call combiner.
-      grpc_transport_stream_op_batch_finish_with_failure(
-          batch, GRPC_ERROR_REF(calld->cancel_error), calld->call_combiner);
-    } else {
-      // Note: This will release the call combiner.
-      calld->subchannel_call->StartTransportStreamOpBatch(batch);
-    }
-    return;
-  }
-  // Add the batch to the pending list.
-  pending_batches_add(elem, batch);
-  // Check if we've already gotten a subchannel call.
-  // Note that once we have completed the pick, we do not need to enter
-  // the channel combiner, which is more efficient (especially for
-  // streaming calls).
-  if (calld->subchannel_call != nullptr) {
-    if (grpc_client_channel_call_trace.enabled()) {
-      gpr_log(GPR_INFO,
-              "chand=%p calld=%p: starting batch on subchannel_call=%p", chand,
-              calld, calld->subchannel_call.get());
-    }
-    pending_batches_resume(elem);
-    return;
+      GRPC_CLOSURE_SCHED(&calld->pick_closure_, error);
+      if (calld->pick_queued_) calld->RemoveCallFromQueuedPicksLocked(elem);
   }
-  // We do not yet have a subchannel call.
-  // For batches containing a send_initial_metadata op, enter the channel
-  // combiner to start a pick.
-  if (GPR_LIKELY(batch->send_initial_metadata)) {
-    if (grpc_client_channel_call_trace.enabled()) {
-      gpr_log(GPR_INFO, "chand=%p calld=%p: entering client_channel combiner",
-              chand, calld);
-    }
-    GRPC_CLOSURE_SCHED(
-        GRPC_CLOSURE_INIT(&batch->handler_private.closure, start_pick_locked,
-                          elem, grpc_combiner_scheduler(chand->combiner)),
-        GRPC_ERROR_NONE);
-  } else {
-    // For all other batches, release the call combiner.
-    if (grpc_client_channel_call_trace.enabled()) {
-      gpr_log(GPR_INFO,
-              "chand=%p calld=%p: saved batch, yielding call combiner", chand,
-              calld);
-    }
-    GRPC_CALL_COMBINER_STOP(calld->call_combiner,
-                            "batch does not include send_initial_metadata");
-  }
-}
-
-/* Constructor for call_data */
-static grpc_error* cc_init_call_elem(grpc_call_element* elem,
-                                     const grpc_call_element_args* args) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  new (elem->call_data) call_data(elem, *chand, *args);
-  return GRPC_ERROR_NONE;
-}
-
-/* Destructor for call_data */
-static void cc_destroy_call_elem(grpc_call_element* elem,
-                                 const grpc_call_final_info* final_info,
-                                 grpc_closure* then_schedule_closure) {
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  if (GPR_LIKELY(calld->subchannel_call != nullptr)) {
-    calld->subchannel_call->SetAfterCallStackDestroy(then_schedule_closure);
-    then_schedule_closure = nullptr;
-  }
-  calld->~call_data();
-  GRPC_CLOSURE_SCHED(then_schedule_closure, GRPC_ERROR_NONE);
 }
 
-static void cc_set_pollset_or_pollset_set(grpc_call_element* elem,
-                                          grpc_polling_entity* pollent) {
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  calld->pollent = pollent;
-}
+}  // namespace
+}  // namespace grpc_core
 
 /*************************************************************************
  * EXPORTED SYMBOLS
  */
 
+using grpc_core::CallData;
+using grpc_core::ChannelData;
+
 const grpc_channel_filter grpc_client_channel_filter = {
-    cc_start_transport_stream_op_batch,
-    cc_start_transport_op,
-    sizeof(call_data),
-    cc_init_call_elem,
-    cc_set_pollset_or_pollset_set,
-    cc_destroy_call_elem,
-    sizeof(channel_data),
-    cc_init_channel_elem,
-    cc_destroy_channel_elem,
-    cc_get_channel_info,
+    CallData::StartTransportStreamOpBatch,
+    ChannelData::StartTransportOp,
+    sizeof(CallData),
+    CallData::Init,
+    CallData::SetPollent,
+    CallData::Destroy,
+    sizeof(ChannelData),
+    ChannelData::Init,
+    ChannelData::Destroy,
+    ChannelData::GetChannelInfo,
     "client-channel",
 };
 
 void grpc_client_channel_set_channelz_node(
     grpc_channel_element* elem, grpc_core::channelz::ClientChannelNode* node) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  chand->channelz_node = node;
-  chand->resolving_lb_policy->set_channelz_node(node->Ref());
+  auto* chand = static_cast<ChannelData*>(elem->channel_data);
+  chand->set_channelz_node(node);
 }
 
 void grpc_client_channel_populate_child_refs(
     grpc_channel_element* elem,
     grpc_core::channelz::ChildRefsList* child_subchannels,
     grpc_core::channelz::ChildRefsList* child_channels) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  if (chand->resolving_lb_policy != nullptr) {
-    chand->resolving_lb_policy->FillChildRefsForChannelz(child_subchannels,
-                                                         child_channels);
-  }
-}
-
-static void try_to_connect_locked(void* arg, grpc_error* error_ignored) {
-  channel_data* chand = static_cast<channel_data*>(arg);
-  chand->resolving_lb_policy->ExitIdleLocked();
-  GRPC_CHANNEL_STACK_UNREF(chand->owning_stack, "try_to_connect");
+  auto* chand = static_cast<ChannelData*>(elem->channel_data);
+  chand->FillChildRefsForChannelz(child_subchannels, child_channels);
 }
 
 grpc_connectivity_state grpc_client_channel_check_connectivity_state(
     grpc_channel_element* elem, int try_to_connect) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  grpc_connectivity_state out =
-      grpc_connectivity_state_check(&chand->state_tracker);
-  if (out == GRPC_CHANNEL_IDLE && try_to_connect) {
-    GRPC_CHANNEL_STACK_REF(chand->owning_stack, "try_to_connect");
-    GRPC_CLOSURE_SCHED(
-        GRPC_CLOSURE_CREATE(try_to_connect_locked, chand,
-                            grpc_combiner_scheduler(chand->combiner)),
-        GRPC_ERROR_NONE);
-  }
-  return out;
-}
-
-typedef struct external_connectivity_watcher {
-  channel_data* chand;
-  grpc_polling_entity pollent;
-  grpc_closure* on_complete;
-  grpc_closure* watcher_timer_init;
-  grpc_connectivity_state* state;
-  grpc_closure my_closure;
-  struct external_connectivity_watcher* next;
-} external_connectivity_watcher;
-
-static external_connectivity_watcher* lookup_external_connectivity_watcher(
-    channel_data* chand, grpc_closure* on_complete) {
-  gpr_mu_lock(&chand->external_connectivity_watcher_list_mu);
-  external_connectivity_watcher* w =
-      chand->external_connectivity_watcher_list_head;
-  while (w != nullptr && w->on_complete != on_complete) {
-    w = w->next;
-  }
-  gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu);
-  return w;
-}
-
-static void external_connectivity_watcher_list_append(
-    channel_data* chand, external_connectivity_watcher* w) {
-  GPR_ASSERT(!lookup_external_connectivity_watcher(chand, w->on_complete));
-
-  gpr_mu_lock(&w->chand->external_connectivity_watcher_list_mu);
-  GPR_ASSERT(!w->next);
-  w->next = chand->external_connectivity_watcher_list_head;
-  chand->external_connectivity_watcher_list_head = w;
-  gpr_mu_unlock(&w->chand->external_connectivity_watcher_list_mu);
-}
-
-static void external_connectivity_watcher_list_remove(
-    channel_data* chand, external_connectivity_watcher* to_remove) {
-  GPR_ASSERT(
-      lookup_external_connectivity_watcher(chand, to_remove->on_complete));
-  gpr_mu_lock(&chand->external_connectivity_watcher_list_mu);
-  if (to_remove == chand->external_connectivity_watcher_list_head) {
-    chand->external_connectivity_watcher_list_head = to_remove->next;
-    gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu);
-    return;
-  }
-  external_connectivity_watcher* w =
-      chand->external_connectivity_watcher_list_head;
-  while (w != nullptr) {
-    if (w->next == to_remove) {
-      w->next = w->next->next;
-      gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu);
-      return;
-    }
-    w = w->next;
-  }
-  GPR_UNREACHABLE_CODE(return );
+  auto* chand = static_cast<ChannelData*>(elem->channel_data);
+  return chand->CheckConnectivityState(try_to_connect);
 }
 
 int grpc_client_channel_num_external_connectivity_watchers(
     grpc_channel_element* elem) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  int count = 0;
-
-  gpr_mu_lock(&chand->external_connectivity_watcher_list_mu);
-  external_connectivity_watcher* w =
-      chand->external_connectivity_watcher_list_head;
-  while (w != nullptr) {
-    count++;
-    w = w->next;
-  }
-  gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu);
-
-  return count;
-}
-
-static void on_external_watch_complete_locked(void* arg, grpc_error* error) {
-  external_connectivity_watcher* w =
-      static_cast<external_connectivity_watcher*>(arg);
-  grpc_closure* follow_up = w->on_complete;
-  grpc_polling_entity_del_from_pollset_set(&w->pollent,
-                                           w->chand->interested_parties);
-  GRPC_CHANNEL_STACK_UNREF(w->chand->owning_stack,
-                           "external_connectivity_watcher");
-  external_connectivity_watcher_list_remove(w->chand, w);
-  gpr_free(w);
-  GRPC_CLOSURE_SCHED(follow_up, GRPC_ERROR_REF(error));
-}
-
-static void watch_connectivity_state_locked(void* arg,
-                                            grpc_error* error_ignored) {
-  external_connectivity_watcher* w =
-      static_cast<external_connectivity_watcher*>(arg);
-  external_connectivity_watcher* found = nullptr;
-  if (w->state != nullptr) {
-    external_connectivity_watcher_list_append(w->chand, w);
-    // An assumption is being made that the closure is scheduled on the exec ctx
-    // scheduler and that GRPC_CLOSURE_RUN would run the closure immediately.
-    GRPC_CLOSURE_RUN(w->watcher_timer_init, GRPC_ERROR_NONE);
-    GRPC_CLOSURE_INIT(&w->my_closure, on_external_watch_complete_locked, w,
-                      grpc_combiner_scheduler(w->chand->combiner));
-    grpc_connectivity_state_notify_on_state_change(&w->chand->state_tracker,
-                                                   w->state, &w->my_closure);
-  } else {
-    GPR_ASSERT(w->watcher_timer_init == nullptr);
-    found = lookup_external_connectivity_watcher(w->chand, w->on_complete);
-    if (found) {
-      GPR_ASSERT(found->on_complete == w->on_complete);
-      grpc_connectivity_state_notify_on_state_change(
-          &found->chand->state_tracker, nullptr, &found->my_closure);
-    }
-    grpc_polling_entity_del_from_pollset_set(&w->pollent,
-                                             w->chand->interested_parties);
-    GRPC_CHANNEL_STACK_UNREF(w->chand->owning_stack,
-                             "external_connectivity_watcher");
-    gpr_free(w);
-  }
+  auto* chand = static_cast<ChannelData*>(elem->channel_data);
+  return chand->NumExternalConnectivityWatchers();
 }
 
 void grpc_client_channel_watch_connectivity_state(
     grpc_channel_element* elem, grpc_polling_entity pollent,
     grpc_connectivity_state* state, grpc_closure* closure,
     grpc_closure* watcher_timer_init) {
-  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  external_connectivity_watcher* w =
-      static_cast<external_connectivity_watcher*>(gpr_zalloc(sizeof(*w)));
-  w->chand = chand;
-  w->pollent = pollent;
-  w->on_complete = closure;
-  w->state = state;
-  w->watcher_timer_init = watcher_timer_init;
-  grpc_polling_entity_add_to_pollset_set(&w->pollent,
-                                         chand->interested_parties);
-  GRPC_CHANNEL_STACK_REF(w->chand->owning_stack,
-                         "external_connectivity_watcher");
-  GRPC_CLOSURE_SCHED(
-      GRPC_CLOSURE_INIT(&w->my_closure, watch_connectivity_state_locked, w,
-                        grpc_combiner_scheduler(chand->combiner)),
-      GRPC_ERROR_NONE);
+  auto* chand = static_cast<ChannelData*>(elem->channel_data);
+  return chand->AddExternalConnectivityWatcher(pollent, state, closure,
+                                               watcher_timer_init);
 }
 
 grpc_core::RefCountedPtr<grpc_core::SubchannelCall>
 grpc_client_channel_get_subchannel_call(grpc_call_element* elem) {
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  return calld->subchannel_call;
+  auto* calld = static_cast<CallData*>(elem->call_data);
+  return calld->subchannel_call();
 }
index 76c5a78..a7a47e9 100644 (file)
@@ -49,8 +49,8 @@ ClientChannelNode::ClientChannelNode(grpc_channel* channel,
     : ChannelNode(channel, channel_tracer_max_nodes, is_top_level_channel) {
   client_channel_ =
       grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel));
-  grpc_client_channel_set_channelz_node(client_channel_, this);
   GPR_ASSERT(client_channel_->filter == &grpc_client_channel_filter);
+  grpc_client_channel_set_channelz_node(client_channel_, this);
 }
 
 void ClientChannelNode::PopulateConnectivityState(grpc_json* json) {
@@ -127,8 +127,7 @@ void SubchannelNode::PopulateConnectivityState(grpc_json* json) {
   if (subchannel_ == nullptr) {
     state = GRPC_CHANNEL_SHUTDOWN;
   } else {
-    state = subchannel_->CheckConnectivity(nullptr,
-                                           true /* inhibit_health_checking */);
+    state = subchannel_->CheckConnectivity(true /* inhibit_health_checking */);
   }
   json = grpc_json_create_child(nullptr, json, "state", nullptr,
                                 GRPC_JSON_OBJECT, false);
index 2031ab4..e564df8 100644 (file)
@@ -32,6 +32,7 @@
 #include "src/core/ext/filters/client_channel/lb_policy_registry.h"
 #include "src/core/ext/filters/client_channel/proxy_mapper_registry.h"
 #include "src/core/ext/filters/client_channel/resolver_registry.h"
+#include "src/core/ext/filters/client_channel/resolver_result_parsing.h"
 #include "src/core/ext/filters/client_channel/retry_throttle.h"
 #include "src/core/lib/surface/channel_init.h"
 
@@ -49,6 +50,8 @@ static bool append_filter(grpc_channel_stack_builder* builder, void* arg) {
 }
 
 void grpc_client_channel_init(void) {
+  grpc_core::ServiceConfig::Init();
+  grpc_core::internal::ClientChannelServiceConfigParser::Register();
   grpc_core::LoadBalancingPolicyRegistry::Builder::InitRegistry();
   grpc_core::ResolverRegistry::Builder::InitRegistry();
   grpc_core::internal::ServerRetryThrottleMap::Init();
@@ -68,4 +71,5 @@ void grpc_client_channel_shutdown(void) {
   grpc_core::internal::ServerRetryThrottleMap::Shutdown();
   grpc_core::ResolverRegistry::Builder::ShutdownRegistry();
   grpc_core::LoadBalancingPolicyRegistry::Builder::ShutdownRegistry();
+  grpc_core::ServiceConfig::Shutdown();
 }
index 84a7f35..bfac773 100644 (file)
@@ -27,7 +27,7 @@
 #include "pb_encode.h"
 #include "src/core/ext/filters/client_channel/health/health.pb.h"
 #include "src/core/lib/debug/trace.h"
-#include "src/core/lib/gprpp/mutex_lock.h"
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/transport/error_utils.h"
 #include "src/core/lib/transport/status_metadata.h"
 #define HEALTH_CHECK_RECONNECT_MAX_BACKOFF_SECONDS 120
 #define HEALTH_CHECK_RECONNECT_JITTER 0.2
 
-grpc_core::TraceFlag grpc_health_check_client_trace(false,
-                                                    "health_check_client");
-
 namespace grpc_core {
 
+TraceFlag grpc_health_check_client_trace(false, "health_check_client");
+
 //
 // HealthCheckClient
 //
@@ -50,7 +49,7 @@ HealthCheckClient::HealthCheckClient(
     const char* service_name,
     RefCountedPtr<ConnectedSubchannel> connected_subchannel,
     grpc_pollset_set* interested_parties,
-    grpc_core::RefCountedPtr<grpc_core::channelz::SubchannelNode> channelz_node)
+    RefCountedPtr<channelz::SubchannelNode> channelz_node)
     : InternallyRefCounted<HealthCheckClient>(&grpc_health_check_client_trace),
       service_name_(service_name),
       connected_subchannel_(std::move(connected_subchannel)),
@@ -64,21 +63,19 @@ HealthCheckClient::HealthCheckClient(
               .set_jitter(HEALTH_CHECK_RECONNECT_JITTER)
               .set_max_backoff(HEALTH_CHECK_RECONNECT_MAX_BACKOFF_SECONDS *
                                1000)) {
-  if (grpc_health_check_client_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_health_check_client_trace)) {
     gpr_log(GPR_INFO, "created HealthCheckClient %p", this);
   }
   GRPC_CLOSURE_INIT(&retry_timer_callback_, OnRetryTimer, this,
                     grpc_schedule_on_exec_ctx);
-  gpr_mu_init(&mu_);
   StartCall();
 }
 
 HealthCheckClient::~HealthCheckClient() {
-  if (grpc_health_check_client_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_health_check_client_trace)) {
     gpr_log(GPR_INFO, "destroying HealthCheckClient %p", this);
   }
   GRPC_ERROR_UNREF(error_);
-  gpr_mu_destroy(&mu_);
 }
 
 void HealthCheckClient::NotifyOnHealthChange(grpc_connectivity_state* state,
@@ -102,7 +99,7 @@ void HealthCheckClient::SetHealthStatus(grpc_connectivity_state state,
 
 void HealthCheckClient::SetHealthStatusLocked(grpc_connectivity_state state,
                                               grpc_error* error) {
-  if (grpc_health_check_client_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_health_check_client_trace)) {
     gpr_log(GPR_INFO, "HealthCheckClient %p: setting state=%d error=%s", this,
             state, grpc_error_string(error));
   }
@@ -118,7 +115,7 @@ void HealthCheckClient::SetHealthStatusLocked(grpc_connectivity_state state,
 }
 
 void HealthCheckClient::Orphan() {
-  if (grpc_health_check_client_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_health_check_client_trace)) {
     gpr_log(GPR_INFO, "HealthCheckClient %p: shutting down", this);
   }
   {
@@ -148,7 +145,7 @@ void HealthCheckClient::StartCallLocked() {
   GPR_ASSERT(call_state_ == nullptr);
   SetHealthStatusLocked(GRPC_CHANNEL_CONNECTING, GRPC_ERROR_NONE);
   call_state_ = MakeOrphanable<CallState>(Ref(), interested_parties_);
-  if (grpc_health_check_client_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_health_check_client_trace)) {
     gpr_log(GPR_INFO, "HealthCheckClient %p: created CallState %p", this,
             call_state_.get());
   }
@@ -162,7 +159,7 @@ void HealthCheckClient::StartRetryTimer() {
       GRPC_ERROR_CREATE_FROM_STATIC_STRING(
           "health check call failed; will retry after backoff"));
   grpc_millis next_try = retry_backoff_.NextAttemptTime();
-  if (grpc_health_check_client_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_health_check_client_trace)) {
     gpr_log(GPR_INFO, "HealthCheckClient %p: health check call lost...", this);
     grpc_millis timeout = next_try - ExecCtx::Get()->Now();
     if (timeout > 0) {
@@ -187,7 +184,7 @@ void HealthCheckClient::OnRetryTimer(void* arg, grpc_error* error) {
     self->retry_timer_callback_pending_ = false;
     if (!self->shutting_down_ && error == GRPC_ERROR_NONE &&
         self->call_state_ == nullptr) {
-      if (grpc_health_check_client_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_health_check_client_trace)) {
         gpr_log(GPR_INFO, "HealthCheckClient %p: restarting health check call",
                 self);
       }
@@ -280,24 +277,17 @@ bool DecodeResponse(grpc_slice_buffer* slice_buffer, grpc_error** error) {
 HealthCheckClient::CallState::CallState(
     RefCountedPtr<HealthCheckClient> health_check_client,
     grpc_pollset_set* interested_parties)
-    : InternallyRefCounted<CallState>(&grpc_health_check_client_trace),
-      health_check_client_(std::move(health_check_client)),
+    : health_check_client_(std::move(health_check_client)),
       pollent_(grpc_polling_entity_create_from_pollset_set(interested_parties)),
-      arena_(gpr_arena_create(health_check_client_->connected_subchannel_
-                                  ->GetInitialCallSizeEstimate(0))),
-      payload_(context_) {
-  grpc_call_combiner_init(&call_combiner_);
-  gpr_atm_rel_store(&seen_response_, static_cast<gpr_atm>(0));
-}
+      arena_(Arena::Create(health_check_client_->connected_subchannel_
+                               ->GetInitialCallSizeEstimate(0))),
+      payload_(context_) {}
 
 HealthCheckClient::CallState::~CallState() {
-  if (grpc_health_check_client_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_health_check_client_trace)) {
     gpr_log(GPR_INFO, "HealthCheckClient %p: destroying CallState %p",
             health_check_client_.get(), this);
   }
-  // The subchannel call is in the arena, so reset the pointer before we destroy
-  // the arena.
-  call_.reset();
   for (size_t i = 0; i < GRPC_CONTEXT_COUNT; i++) {
     if (context_[i].destroy != nullptr) {
       context_[i].destroy(context_[i].value);
@@ -309,14 +299,13 @@ HealthCheckClient::CallState::~CallState() {
   // holding to the call stack. Also flush the closures on exec_ctx so that
   // filters that schedule cancel notification closures on exec_ctx do not
   // need to take a ref of the call stack to guarantee closure liveness.
-  grpc_call_combiner_set_notify_on_cancel(&call_combiner_, nullptr);
-  grpc_core::ExecCtx::Get()->Flush();
-  grpc_call_combiner_destroy(&call_combiner_);
-  gpr_arena_destroy(arena_);
+  call_combiner_.SetNotifyOnCancel(nullptr);
+  ExecCtx::Get()->Flush();
+  arena_->Destroy();
 }
 
 void HealthCheckClient::CallState::Orphan() {
-  grpc_call_combiner_cancel(&call_combiner_, GRPC_ERROR_CANCELLED);
+  call_combiner_.Cancel(GRPC_ERROR_CANCELLED);
   Cancel();
 }
 
@@ -332,7 +321,8 @@ void HealthCheckClient::CallState::StartCall() {
       0,  // parent_data_size
   };
   grpc_error* error = GRPC_ERROR_NONE;
-  call_ = health_check_client_->connected_subchannel_->CreateCall(args, &error);
+  call_ = health_check_client_->connected_subchannel_->CreateCall(args, &error)
+              .release();
   if (error != GRPC_ERROR_NONE) {
     gpr_log(GPR_ERROR,
             "HealthCheckClient %p CallState %p: error creating health "
@@ -341,18 +331,22 @@ void HealthCheckClient::CallState::StartCall() {
     GRPC_ERROR_UNREF(error);
     // Schedule instead of running directly, since we must not be
     // holding health_check_client_->mu_ when CallEnded() is called.
-    Ref(DEBUG_LOCATION, "call_end_closure").release();
+    call_->Ref(DEBUG_LOCATION, "call_end_closure").release();
     GRPC_CLOSURE_SCHED(
         GRPC_CLOSURE_INIT(&batch_.handler_private.closure, CallEndedRetry, this,
                           grpc_schedule_on_exec_ctx),
         GRPC_ERROR_NONE);
     return;
   }
+  // Register after-destruction callback.
+  GRPC_CLOSURE_INIT(&after_call_stack_destruction_, AfterCallStackDestruction,
+                    this, grpc_schedule_on_exec_ctx);
+  call_->SetAfterCallStackDestroy(&after_call_stack_destruction_);
   // Initialize payload and batch.
   payload_.context = context_;
   batch_.payload = &payload_;
   // on_complete callback takes ref, handled manually.
-  Ref(DEBUG_LOCATION, "on_complete").release();
+  call_->Ref(DEBUG_LOCATION, "on_complete").release();
   batch_.on_complete = GRPC_CLOSURE_INIT(&on_complete_, OnComplete, this,
                                          grpc_schedule_on_exec_ctx);
   // Add send_initial_metadata op.
@@ -385,7 +379,7 @@ void HealthCheckClient::CallState::StartCall() {
   payload_.recv_initial_metadata.trailing_metadata_available = nullptr;
   payload_.recv_initial_metadata.peer_string = nullptr;
   // recv_initial_metadata_ready callback takes ref, handled manually.
-  Ref(DEBUG_LOCATION, "recv_initial_metadata_ready").release();
+  call_->Ref(DEBUG_LOCATION, "recv_initial_metadata_ready").release();
   payload_.recv_initial_metadata.recv_initial_metadata_ready =
       GRPC_CLOSURE_INIT(&recv_initial_metadata_ready_, RecvInitialMetadataReady,
                         this, grpc_schedule_on_exec_ctx);
@@ -393,7 +387,7 @@ void HealthCheckClient::CallState::StartCall() {
   // Add recv_message op.
   payload_.recv_message.recv_message = &recv_message_;
   // recv_message callback takes ref, handled manually.
-  Ref(DEBUG_LOCATION, "recv_message_ready").release();
+  call_->Ref(DEBUG_LOCATION, "recv_message_ready").release();
   payload_.recv_message.recv_message_ready = GRPC_CLOSURE_INIT(
       &recv_message_ready_, RecvMessageReady, this, grpc_schedule_on_exec_ctx);
   batch_.recv_message = true;
@@ -429,19 +423,26 @@ void HealthCheckClient::CallState::StartBatchInCallCombiner(void* arg,
 
 void HealthCheckClient::CallState::StartBatch(
     grpc_transport_stream_op_batch* batch) {
-  batch->handler_private.extra_arg = call_.get();
+  batch->handler_private.extra_arg = call_;
   GRPC_CLOSURE_INIT(&batch->handler_private.closure, StartBatchInCallCombiner,
                     batch, grpc_schedule_on_exec_ctx);
   GRPC_CALL_COMBINER_START(&call_combiner_, &batch->handler_private.closure,
                            GRPC_ERROR_NONE, "start_subchannel_batch");
 }
 
+void HealthCheckClient::CallState::AfterCallStackDestruction(
+    void* arg, grpc_error* error) {
+  HealthCheckClient::CallState* self =
+      static_cast<HealthCheckClient::CallState*>(arg);
+  Delete(self);
+}
+
 void HealthCheckClient::CallState::OnCancelComplete(void* arg,
                                                     grpc_error* error) {
   HealthCheckClient::CallState* self =
       static_cast<HealthCheckClient::CallState*>(arg);
   GRPC_CALL_COMBINER_STOP(&self->call_combiner_, "health_cancel");
-  self->Unref(DEBUG_LOCATION, "cancel");
+  self->call_->Unref(DEBUG_LOCATION, "cancel");
 }
 
 void HealthCheckClient::CallState::StartCancel(void* arg, grpc_error* error) {
@@ -455,8 +456,10 @@ void HealthCheckClient::CallState::StartCancel(void* arg, grpc_error* error) {
 }
 
 void HealthCheckClient::CallState::Cancel() {
-  if (call_ != nullptr) {
-    Ref(DEBUG_LOCATION, "cancel").release();
+  bool expected = false;
+  if (cancelled_.CompareExchangeStrong(&expected, true, MemoryOrder::ACQ_REL,
+                                       MemoryOrder::ACQUIRE)) {
+    call_->Ref(DEBUG_LOCATION, "cancel").release();
     GRPC_CALL_COMBINER_START(
         &call_combiner_,
         GRPC_CLOSURE_CREATE(StartCancel, this, grpc_schedule_on_exec_ctx),
@@ -470,7 +473,7 @@ void HealthCheckClient::CallState::OnComplete(void* arg, grpc_error* error) {
   GRPC_CALL_COMBINER_STOP(&self->call_combiner_, "on_complete");
   grpc_metadata_batch_destroy(&self->send_initial_metadata_);
   grpc_metadata_batch_destroy(&self->send_trailing_metadata_);
-  self->Unref(DEBUG_LOCATION, "on_complete");
+  self->call_->Unref(DEBUG_LOCATION, "on_complete");
 }
 
 void HealthCheckClient::CallState::RecvInitialMetadataReady(void* arg,
@@ -479,7 +482,7 @@ void HealthCheckClient::CallState::RecvInitialMetadataReady(void* arg,
       static_cast<HealthCheckClient::CallState*>(arg);
   GRPC_CALL_COMBINER_STOP(&self->call_combiner_, "recv_initial_metadata_ready");
   grpc_metadata_batch_destroy(&self->recv_initial_metadata_);
-  self->Unref(DEBUG_LOCATION, "recv_initial_metadata_ready");
+  self->call_->Unref(DEBUG_LOCATION, "recv_initial_metadata_ready");
 }
 
 void HealthCheckClient::CallState::DoneReadingRecvMessage(grpc_error* error) {
@@ -488,7 +491,7 @@ void HealthCheckClient::CallState::DoneReadingRecvMessage(grpc_error* error) {
     GRPC_ERROR_UNREF(error);
     Cancel();
     grpc_slice_buffer_destroy_internal(&recv_message_buffer_);
-    Unref(DEBUG_LOCATION, "recv_message_ready");
+    call_->Unref(DEBUG_LOCATION, "recv_message_ready");
     return;
   }
   const bool healthy = DecodeResponse(&recv_message_buffer_, &error);
@@ -498,7 +501,7 @@ void HealthCheckClient::CallState::DoneReadingRecvMessage(grpc_error* error) {
     error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("backend unhealthy");
   }
   health_check_client_->SetHealthStatus(state, error);
-  gpr_atm_rel_store(&seen_response_, static_cast<gpr_atm>(1));
+  seen_response_.Store(true, MemoryOrder::RELEASE);
   grpc_slice_buffer_destroy_internal(&recv_message_buffer_);
   // Start another recv_message batch.
   // This re-uses the ref we're holding.
@@ -561,7 +564,7 @@ void HealthCheckClient::CallState::RecvMessageReady(void* arg,
       static_cast<HealthCheckClient::CallState*>(arg);
   GRPC_CALL_COMBINER_STOP(&self->call_combiner_, "recv_message_ready");
   if (self->recv_message_ == nullptr) {
-    self->Unref(DEBUG_LOCATION, "recv_message_ready");
+    self->call_->Unref(DEBUG_LOCATION, "recv_message_ready");
     return;
   }
   grpc_slice_buffer_init(&self->recv_message_buffer_);
@@ -587,7 +590,7 @@ void HealthCheckClient::CallState::RecvTrailingMetadataReady(
     status = grpc_get_status_code_from_metadata(
         self->recv_trailing_metadata_.idx.named.grpc_status->md);
   }
-  if (grpc_health_check_client_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_health_check_client_trace)) {
     gpr_log(GPR_INFO,
             "HealthCheckClient %p CallState %p: health watch failed with "
             "status %d",
@@ -619,7 +622,7 @@ void HealthCheckClient::CallState::CallEndedRetry(void* arg,
   HealthCheckClient::CallState* self =
       static_cast<HealthCheckClient::CallState*>(arg);
   self->CallEnded(true /* retry */);
-  self->Unref(DEBUG_LOCATION, "call_end_closure");
+  self->call_->Unref(DEBUG_LOCATION, "call_end_closure");
 }
 
 void HealthCheckClient::CallState::CallEnded(bool retry) {
@@ -631,7 +634,7 @@ void HealthCheckClient::CallState::CallEnded(bool retry) {
     health_check_client_->call_state_.reset();
     if (retry) {
       GPR_ASSERT(!health_check_client_->shutting_down_);
-      if (static_cast<bool>(gpr_atm_acq_load(&seen_response_))) {
+      if (seen_response_.Load(MemoryOrder::ACQUIRE)) {
         // If the call fails after we've gotten a successful response, reset
         // the backoff and restart the call immediately.
         health_check_client_->retry_backoff_.Reset();
@@ -642,7 +645,9 @@ void HealthCheckClient::CallState::CallEnded(bool retry) {
       }
     }
   }
-  Unref(DEBUG_LOCATION, "call_ended");
+  // When the last ref to the call stack goes away, the CallState object
+  // will be automatically destroyed.
+  call_->Unref(DEBUG_LOCATION, "call_ended");
 }
 
 }  // namespace grpc_core
index 7af88a5..956c109 100644 (file)
 #include <grpc/support/port_platform.h>
 
 #include <grpc/grpc.h>
-#include <grpc/support/atm.h>
 #include <grpc/support/sync.h>
 
 #include "src/core/ext/filters/client_channel/client_channel_channelz.h"
 #include "src/core/ext/filters/client_channel/subchannel.h"
 #include "src/core/lib/backoff/backoff.h"
-#include "src/core/lib/gpr/arena.h"
+#include "src/core/lib/gprpp/arena.h"
+#include "src/core/lib/gprpp/atomic.h"
 #include "src/core/lib/gprpp/orphanable.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/iomgr/call_combiner.h"
 #include "src/core/lib/iomgr/closure.h"
 #include "src/core/lib/iomgr/polling_entity.h"
@@ -60,7 +61,7 @@ class HealthCheckClient : public InternallyRefCounted<HealthCheckClient> {
 
  private:
   // Contains a call to the backend and all the data related to the call.
-  class CallState : public InternallyRefCounted<CallState> {
+  class CallState : public Orphanable {
    public:
     CallState(RefCountedPtr<HealthCheckClient> health_check_client,
               grpc_pollset_set* interested_parties_);
@@ -91,15 +92,19 @@ class HealthCheckClient : public InternallyRefCounted<HealthCheckClient> {
     grpc_error* PullSliceFromRecvMessage();
     void DoneReadingRecvMessage(grpc_error* error);
 
+    static void AfterCallStackDestruction(void* arg, grpc_error* error);
+
     RefCountedPtr<HealthCheckClient> health_check_client_;
     grpc_polling_entity pollent_;
 
-    gpr_arena* arena_;
-    grpc_call_combiner call_combiner_;
+    Arena* arena_;
+    grpc_core::CallCombiner call_combiner_;
     grpc_call_context_element context_[GRPC_CONTEXT_COUNT] = {};
 
-    // The streaming call to the backend. Always non-NULL.
-    RefCountedPtr<SubchannelCall> call_;
+    // The streaming call to the backend. Always non-null.
+    // Refs are tracked manually; when the last ref is released, the
+    // CallState object will be automatically destroyed.
+    SubchannelCall* call_;
 
     grpc_transport_stream_op_batch_payload payload_;
     grpc_transport_stream_op_batch batch_;
@@ -126,12 +131,18 @@ class HealthCheckClient : public InternallyRefCounted<HealthCheckClient> {
     OrphanablePtr<ByteStream> recv_message_;
     grpc_closure recv_message_ready_;
     grpc_slice_buffer recv_message_buffer_;
-    gpr_atm seen_response_;
+    Atomic<bool> seen_response_{false};
 
     // recv_trailing_metadata
     grpc_metadata_batch recv_trailing_metadata_;
     grpc_transport_stream_stats collect_stats_;
     grpc_closure recv_trailing_metadata_ready_;
+
+    // True if the cancel_stream batch has been started.
+    Atomic<bool> cancelled_{false};
+
+    // Closure for call stack destruction.
+    grpc_closure after_call_stack_destruction_;
   };
 
   void StartCall();
@@ -149,7 +160,7 @@ class HealthCheckClient : public InternallyRefCounted<HealthCheckClient> {
   grpc_pollset_set* interested_parties_;  // Do not own.
   RefCountedPtr<channelz::SubchannelNode> channelz_node_;
 
-  gpr_mu mu_;
+  Mutex mu_;
   grpc_connectivity_state state_ = GRPC_CHANNEL_CONNECTING;
   grpc_error* error_ = GRPC_ERROR_NONE;
   grpc_connectivity_state* notify_state_ = nullptr;
index 2b1eb92..95366b5 100644 (file)
@@ -31,9 +31,8 @@
 #include "src/core/ext/filters/client_channel/resolver_registry.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/handshaker_registry.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/string.h"
-#include "src/core/lib/gprpp/mutex_lock.h"
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/http/format_request.h"
 #include "src/core/lib/http/parser.h"
 #include "src/core/lib/slice/slice_internal.h"
index 928a23d..26c31f2 100644 (file)
@@ -25,7 +25,7 @@
 
 /// Channel arg indicating HTTP CONNECT headers (string).
 /// Multiple headers are separated by newlines.  Key/value pairs are
-/// seperated by colons.
+/// separated by colons.
 #define GRPC_ARG_HTTP_CONNECT_HEADERS "grpc.http_connect_headers"
 
 /// Registers handshaker factory.
index c8f8e82..6fa7993 100644 (file)
@@ -62,32 +62,6 @@ void LoadBalancingPolicy::ShutdownAndUnrefLocked(void* arg,
   policy->Unref();
 }
 
-grpc_json* LoadBalancingPolicy::ParseLoadBalancingConfig(
-    const grpc_json* lb_config_array) {
-  if (lb_config_array == nullptr || lb_config_array->type != GRPC_JSON_ARRAY) {
-    return nullptr;
-  }
-  // Find the first LB policy that this client supports.
-  for (const grpc_json* lb_config = lb_config_array->child;
-       lb_config != nullptr; lb_config = lb_config->next) {
-    if (lb_config->type != GRPC_JSON_OBJECT) return nullptr;
-    grpc_json* policy = nullptr;
-    for (grpc_json* field = lb_config->child; field != nullptr;
-         field = field->next) {
-      if (field->key == nullptr || field->type != GRPC_JSON_OBJECT)
-        return nullptr;
-      if (policy != nullptr) return nullptr;  // Violate "oneof" type.
-      policy = field;
-    }
-    if (policy == nullptr) return nullptr;
-    // If we support this policy, then select it.
-    if (LoadBalancingPolicyRegistry::LoadBalancingPolicyExists(policy->key)) {
-      return policy;
-    }
-  }
-  return nullptr;
-}
-
 //
 // LoadBalancingPolicy::UpdateArgs
 //
@@ -140,10 +114,9 @@ LoadBalancingPolicy::PickResult LoadBalancingPolicy::QueuePicker::Pick(
   //    the time this function returns, the pick will already have
   //    been processed, and we'll be trying to re-process the same
   //    pick again, leading to a crash.
-  // 2. In a subsequent PR, we will split the data plane and control
-  //    plane synchronization into separate combiners, at which
-  //    point this will need to hop from the data plane combiner into
-  //    the control plane combiner.
+  // 2. We are currently running in the data plane combiner, but we
+  //    need to bounce into the control plane combiner to call
+  //    ExitIdleLocked().
   if (!exit_idle_called_) {
     exit_idle_called_ = true;
     parent_->Ref().release();  // ref held by closure.
index 1c17f95..2ac7df6 100644 (file)
@@ -36,6 +36,18 @@ extern grpc_core::DebugOnlyTraceFlag grpc_trace_lb_policy_refcount;
 
 namespace grpc_core {
 
+/// Interface for parsed forms of load balancing configs found in a service
+/// config.
+class ParsedLoadBalancingConfig : public RefCounted<ParsedLoadBalancingConfig> {
+ public:
+  virtual ~ParsedLoadBalancingConfig() = default;
+
+  // Returns the load balancing policy name
+  virtual const char* name() const GRPC_ABSTRACT;
+
+  GRPC_ABSTRACT_BASE_CLASS;
+};
+
 /// Interface for load balancing policies.
 ///
 /// The following concepts are used here:
@@ -167,6 +179,9 @@ class LoadBalancingPolicy : public InternallyRefCounted<LoadBalancingPolicy> {
 
   /// A proxy object used by the LB policy to communicate with the client
   /// channel.
+  // TODO(juanlishen): Consider adding a mid-layer subclass that helps handle
+  // things like swapping in pending policy when it's ready. Currently, we are
+  // duplicating the logic in many subclasses.
   class ChannelControlHelper {
    public:
     ChannelControlHelper() = default;
@@ -185,7 +200,6 @@ class LoadBalancingPolicy : public InternallyRefCounted<LoadBalancingPolicy> {
     /// Sets the connectivity state and returns a new picker to be used
     /// by the client channel.
     virtual void UpdateState(grpc_connectivity_state state,
-                             grpc_error* state_error,
                              UniquePtr<SubchannelPicker>) GRPC_ABSTRACT;
 
     /// Requests that the resolver re-resolve.
@@ -194,30 +208,11 @@ class LoadBalancingPolicy : public InternallyRefCounted<LoadBalancingPolicy> {
     GRPC_ABSTRACT_BASE_CLASS
   };
 
-  /// Configuration for an LB policy instance.
-  // TODO(roth): Find a better JSON representation for this API.
-  class Config : public RefCounted<Config> {
-   public:
-    Config(const grpc_json* lb_config,
-           RefCountedPtr<ServiceConfig> service_config)
-        : json_(lb_config), service_config_(std::move(service_config)) {}
-
-    const char* name() const { return json_->key; }
-    const grpc_json* config() const { return json_->child; }
-    RefCountedPtr<ServiceConfig> service_config() const {
-      return service_config_;
-    }
-
-   private:
-    const grpc_json* json_;
-    RefCountedPtr<ServiceConfig> service_config_;
-  };
-
   /// Data passed to the UpdateLocked() method when new addresses and
   /// config are available.
   struct UpdateArgs {
     ServerAddressList addresses;
-    RefCountedPtr<Config> config;
+    RefCountedPtr<ParsedLoadBalancingConfig> config;
     const grpc_channel_args* args = nullptr;
 
     // TODO(roth): Remove everything below once channel args is
@@ -288,10 +283,6 @@ class LoadBalancingPolicy : public InternallyRefCounted<LoadBalancingPolicy> {
 
   void Orphan() override;
 
-  /// Returns the JSON node of policy (with both policy name and config content)
-  /// given the JSON node of a LoadBalancingConfig array.
-  static grpc_json* ParseLoadBalancingConfig(const grpc_json* lb_config_array);
-
   // A picker that returns PICK_QUEUE for all picks.
   // Also calls the parent LB policy's ExitIdleLocked() method when the
   // first pick is seen.
index 02fe06c..ed6e8de 100644 (file)
@@ -88,7 +88,6 @@
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gprpp/manual_constructor.h"
 #include "src/core/lib/gprpp/memory.h"
-#include "src/core/lib/gprpp/mutex_lock.h"
 #include "src/core/lib/gprpp/orphanable.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
 #include "src/core/lib/iomgr/combiner.h"
@@ -119,6 +118,21 @@ namespace {
 
 constexpr char kGrpclb[] = "grpclb";
 
+class ParsedGrpcLbConfig : public ParsedLoadBalancingConfig {
+ public:
+  explicit ParsedGrpcLbConfig(
+      RefCountedPtr<ParsedLoadBalancingConfig> child_policy)
+      : child_policy_(std::move(child_policy)) {}
+  const char* name() const override { return kGrpclb; }
+
+  RefCountedPtr<ParsedLoadBalancingConfig> child_policy() const {
+    return child_policy_;
+  }
+
+ private:
+  RefCountedPtr<ParsedLoadBalancingConfig> child_policy_;
+};
+
 class GrpcLb : public LoadBalancingPolicy {
  public:
   explicit GrpcLb(Args args);
@@ -234,12 +248,19 @@ class GrpcLb : public LoadBalancingPolicy {
 
     // Returns the LB token to use for a drop, or null if the call
     // should not be dropped.
-    // Intended to be called from picker, so calls will be externally
-    // synchronized.
+    //
+    // Note: This is called from the picker, so it will be invoked in
+    // the channel's data plane combiner, NOT the control plane
+    // combiner.  It should not be accessed by any other part of the LB
+    // policy.
     const char* ShouldDrop();
 
    private:
     grpc_grpclb_serverlist* serverlist_;
+
+    // Guarded by the channel's data plane combiner, NOT the control
+    // plane combiner.  It should not be accessed by anything but the
+    // picker via the ShouldDrop() method.
     size_t drop_index_ = 0;
   };
 
@@ -275,7 +296,7 @@ class GrpcLb : public LoadBalancingPolicy {
     Subchannel* CreateSubchannel(const grpc_channel_args& args) override;
     grpc_channel* CreateChannel(const char* target,
                                 const grpc_channel_args& args) override;
-    void UpdateState(grpc_connectivity_state state, grpc_error* state_error,
+    void UpdateState(grpc_connectivity_state state,
                      UniquePtr<SubchannelPicker> picker) override;
     void RequestReresolution() override;
 
@@ -296,7 +317,6 @@ class GrpcLb : public LoadBalancingPolicy {
   // Helper functions used in UpdateLocked().
   void ProcessAddressesAndChannelArgsLocked(const ServerAddressList& addresses,
                                             const grpc_channel_args& args);
-  void ParseLbConfig(Config* grpclb_config);
   static void OnBalancerChannelConnectivityChangedLocked(void* arg,
                                                          grpc_error* error);
   void CancelBalancerChannelConnectivityWatchLocked();
@@ -374,7 +394,7 @@ class GrpcLb : public LoadBalancingPolicy {
   // until it reports READY, at which point it will be moved to child_policy_.
   OrphanablePtr<LoadBalancingPolicy> pending_child_policy_;
   // The child policy config.
-  RefCountedPtr<Config> child_policy_config_;
+  RefCountedPtr<ParsedLoadBalancingConfig> child_policy_config_;
   // Child policy in state READY.
   bool child_policy_ready_ = false;
 };
@@ -551,7 +571,7 @@ GrpcLb::PickResult GrpcLb::Picker::Pick(PickArgs* pick, grpc_error** error) {
     // subchannel call (and therefore no client_load_reporting filter)
     // for dropped calls.
     if (client_stats_ != nullptr) {
-      client_stats_->AddCallDroppedLocked(drop_token);
+      client_stats_->AddCallDropped(drop_token);
     }
     return PICK_COMPLETE;
   }
@@ -615,25 +635,18 @@ grpc_channel* GrpcLb::Helper::CreateChannel(const char* target,
 }
 
 void GrpcLb::Helper::UpdateState(grpc_connectivity_state state,
-                                 grpc_error* state_error,
                                  UniquePtr<SubchannelPicker> picker) {
-  if (parent_->shutting_down_) {
-    GRPC_ERROR_UNREF(state_error);
-    return;
-  }
+  if (parent_->shutting_down_) return;
   // If this request is from the pending child policy, ignore it until
   // it reports READY, at which point we swap it into place.
   if (CalledByPendingChild()) {
-    if (grpc_lb_glb_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_glb_trace)) {
       gpr_log(GPR_INFO,
               "[grpclb %p helper %p] pending child policy %p reports state=%s",
               parent_.get(), this, parent_->pending_child_policy_.get(),
               grpc_connectivity_state_name(state));
     }
-    if (state != GRPC_CHANNEL_READY) {
-      GRPC_ERROR_UNREF(state_error);
-      return;
-    }
+    if (state != GRPC_CHANNEL_READY) return;
     grpc_pollset_set_del_pollset_set(
         parent_->child_policy_->interested_parties(),
         parent_->interested_parties());
@@ -641,7 +654,6 @@ void GrpcLb::Helper::UpdateState(grpc_connectivity_state state,
     parent_->child_policy_ = std::move(parent_->pending_child_policy_);
   } else if (!CalledByCurrentChild()) {
     // This request is from an outdated child, so ignore it.
-    GRPC_ERROR_UNREF(state_error);
     return;
   }
   // Record whether child policy reports READY.
@@ -670,18 +682,17 @@ void GrpcLb::Helper::UpdateState(grpc_connectivity_state state,
   if (parent_->serverlist_ == nullptr ||
       (!parent_->serverlist_->ContainsAllDropEntries() &&
        state != GRPC_CHANNEL_READY)) {
-    if (grpc_lb_glb_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_glb_trace)) {
       gpr_log(GPR_INFO,
               "[grpclb %p helper %p] state=%s passing child picker %p as-is",
               parent_.get(), this, grpc_connectivity_state_name(state),
               picker.get());
     }
-    parent_->channel_control_helper()->UpdateState(state, state_error,
-                                                   std::move(picker));
+    parent_->channel_control_helper()->UpdateState(state, std::move(picker));
     return;
   }
   // Cases 2 and 3a: wrap picker from the child in our own picker.
-  if (grpc_lb_glb_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_glb_trace)) {
     gpr_log(GPR_INFO, "[grpclb %p helper %p] state=%s wrapping child picker %p",
             parent_.get(), this, grpc_connectivity_state_name(state),
             picker.get());
@@ -692,10 +703,9 @@ void GrpcLb::Helper::UpdateState(grpc_connectivity_state state,
     client_stats = parent_->lb_calld_->client_stats()->Ref();
   }
   parent_->channel_control_helper()->UpdateState(
-      state, state_error,
-      UniquePtr<SubchannelPicker>(
-          New<Picker>(parent_.get(), parent_->serverlist_, std::move(picker),
-                      std::move(client_stats))));
+      state, UniquePtr<SubchannelPicker>(
+                 New<Picker>(parent_.get(), parent_->serverlist_,
+                             std::move(picker), std::move(client_stats))));
 }
 
 void GrpcLb::Helper::RequestReresolution() {
@@ -705,7 +715,7 @@ void GrpcLb::Helper::RequestReresolution() {
           ? parent_->pending_child_policy_.get()
           : parent_->child_policy_.get();
   if (child_ != latest_child_policy) return;
-  if (grpc_lb_glb_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_glb_trace)) {
     gpr_log(GPR_INFO,
             "[grpclb %p] Re-resolution requested from %schild policy (%p).",
             parent_.get(), CalledByPendingChild() ? "pending " : "", child_);
@@ -792,7 +802,7 @@ void GrpcLb::BalancerCallState::Orphan() {
 
 void GrpcLb::BalancerCallState::StartQuery() {
   GPR_ASSERT(lb_call_ != nullptr);
-  if (grpc_lb_glb_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_glb_trace)) {
     gpr_log(GPR_INFO, "[grpclb %p] lb_calld=%p: Starting LB call %p",
             grpclb_policy_.get(), this, lb_call_);
   }
@@ -910,7 +920,7 @@ void GrpcLb::BalancerCallState::SendClientLoadReportLocked() {
   // Construct message payload.
   GPR_ASSERT(send_message_payload_ == nullptr);
   grpc_grpclb_request* request =
-      grpc_grpclb_load_report_request_create_locked(client_stats_.get());
+      grpc_grpclb_load_report_request_create(client_stats_.get());
   // Skip client load report if the counters were all zero in the last
   // report and they are still zero in this one.
   if (LoadReportCountersAreZero(request)) {
@@ -999,7 +1009,7 @@ void GrpcLb::BalancerCallState::OnBalancerMessageReceivedLocked(
       lb_calld->client_stats_report_interval_ = GPR_MAX(
           GPR_MS_PER_SEC, grpc_grpclb_duration_to_millis(
                               &initial_response->client_stats_report_interval));
-      if (grpc_lb_glb_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_glb_trace)) {
         gpr_log(GPR_INFO,
                 "[grpclb %p] lb_calld=%p: Received initial LB response "
                 "message; client load reporting interval = %" PRId64
@@ -1007,7 +1017,7 @@ void GrpcLb::BalancerCallState::OnBalancerMessageReceivedLocked(
                 grpclb_policy, lb_calld,
                 lb_calld->client_stats_report_interval_);
       }
-    } else if (grpc_lb_glb_trace.enabled()) {
+    } else if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_glb_trace)) {
       gpr_log(GPR_INFO,
               "[grpclb %p] lb_calld=%p: Received initial LB response message; "
               "client load reporting NOT enabled",
@@ -1020,7 +1030,7 @@ void GrpcLb::BalancerCallState::OnBalancerMessageReceivedLocked(
     // Have seen initial response, look for serverlist.
     GPR_ASSERT(lb_calld->lb_call_ != nullptr);
     auto serverlist_wrapper = MakeRefCounted<Serverlist>(serverlist);
-    if (grpc_lb_glb_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_glb_trace)) {
       UniquePtr<char> serverlist_text = serverlist_wrapper->AsText();
       gpr_log(GPR_INFO,
               "[grpclb %p] lb_calld=%p: Serverlist with %" PRIuPTR
@@ -1041,7 +1051,7 @@ void GrpcLb::BalancerCallState::OnBalancerMessageReceivedLocked(
     // Check if the serverlist differs from the previous one.
     if (grpclb_policy->serverlist_ != nullptr &&
         *grpclb_policy->serverlist_ == *serverlist_wrapper) {
-      if (grpc_lb_glb_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_glb_trace)) {
         gpr_log(GPR_INFO,
                 "[grpclb %p] lb_calld=%p: Incoming server list identical to "
                 "current, ignoring.",
@@ -1119,7 +1129,7 @@ void GrpcLb::BalancerCallState::OnBalancerStatusReceivedLocked(
   BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg);
   GrpcLb* grpclb_policy = lb_calld->grpclb_policy();
   GPR_ASSERT(lb_calld->lb_call_ != nullptr);
-  if (grpc_lb_glb_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_glb_trace)) {
     char* status_details =
         grpc_slice_to_c_string(lb_calld->lb_call_status_details_);
     gpr_log(GPR_INFO,
@@ -1133,13 +1143,13 @@ void GrpcLb::BalancerCallState::OnBalancerStatusReceivedLocked(
   // we want to retry connecting. Otherwise, we have deliberately ended this
   // call and no further action is required.
   if (lb_calld == grpclb_policy->lb_calld_.get()) {
-    // If we did not receive a serverlist and the fallback-at-startup checks
-    // are pending, go into fallback mode immediately.  This short-circuits
-    // the timeout for the fallback-at-startup case.
-    if (!lb_calld->seen_serverlist_ &&
-        grpclb_policy->fallback_at_startup_checks_pending_) {
+    // If the fallback-at-startup checks are pending, go into fallback mode
+    // immediately.  This short-circuits the timeout for the fallback-at-startup
+    // case.
+    if (grpclb_policy->fallback_at_startup_checks_pending_) {
+      GPR_ASSERT(!lb_calld->seen_serverlist_);
       gpr_log(GPR_INFO,
-              "[grpclb %p] balancer call finished without receiving "
+              "[grpclb %p] Balancer call finished without receiving "
               "serverlist; entering fallback mode",
               grpclb_policy);
       grpclb_policy->fallback_at_startup_checks_pending_ = false;
@@ -1281,7 +1291,7 @@ GrpcLb::GrpcLb(Args args)
   grpc_uri* uri = grpc_uri_parse(server_uri, true);
   GPR_ASSERT(uri->path[0] != '\0');
   server_name_ = gpr_strdup(uri->path[0] == '/' ? uri->path + 1 : uri->path);
-  if (grpc_lb_glb_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_glb_trace)) {
     gpr_log(GPR_INFO,
             "[grpclb %p] Will use '%s' as the server name for LB request.",
             this, server_name_);
@@ -1377,7 +1387,13 @@ void GrpcLb::FillChildRefsForChannelz(
 
 void GrpcLb::UpdateLocked(UpdateArgs args) {
   const bool is_initial_update = lb_channel_ == nullptr;
-  ParseLbConfig(args.config.get());
+  auto* grpclb_config =
+      static_cast<const ParsedGrpcLbConfig*>(args.config.get());
+  if (grpclb_config != nullptr) {
+    child_policy_config_ = grpclb_config->child_policy();
+  } else {
+    child_policy_config_ = nullptr;
+  }
   ProcessAddressesAndChannelArgsLocked(args.addresses, *args.args);
   // Update the existing child policy.
   if (child_policy_ != nullptr) CreateOrUpdateChildPolicyLocked();
@@ -1466,27 +1482,6 @@ void GrpcLb::ProcessAddressesAndChannelArgsLocked(
   response_generator_->SetResponse(std::move(result));
 }
 
-void GrpcLb::ParseLbConfig(Config* grpclb_config) {
-  const grpc_json* child_policy = nullptr;
-  if (grpclb_config != nullptr) {
-    const grpc_json* grpclb_config_json = grpclb_config->config();
-    for (const grpc_json* field = grpclb_config_json; field != nullptr;
-         field = field->next) {
-      if (field->key == nullptr) return;
-      if (strcmp(field->key, "childPolicy") == 0) {
-        if (child_policy != nullptr) return;  // Duplicate.
-        child_policy = ParseLoadBalancingConfig(field);
-      }
-    }
-  }
-  if (child_policy != nullptr) {
-    child_policy_config_ =
-        MakeRefCounted<Config>(child_policy, grpclb_config->service_config());
-  } else {
-    child_policy_config_.reset();
-  }
-}
-
 void GrpcLb::OnBalancerChannelConnectivityChangedLocked(void* arg,
                                                         grpc_error* error) {
   GrpcLb* self = static_cast<GrpcLb*>(arg);
@@ -1540,7 +1535,7 @@ void GrpcLb::StartBalancerCallLocked() {
   // Init the LB call data.
   GPR_ASSERT(lb_calld_ == nullptr);
   lb_calld_ = MakeOrphanable<BalancerCallState>(Ref());
-  if (grpc_lb_glb_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_glb_trace)) {
     gpr_log(GPR_INFO,
             "[grpclb %p] Query for backends (lb_channel: %p, lb_calld: %p)",
             this, lb_channel_, lb_calld_.get());
@@ -1550,7 +1545,7 @@ void GrpcLb::StartBalancerCallLocked() {
 
 void GrpcLb::StartBalancerCallRetryTimerLocked() {
   grpc_millis next_try = lb_call_backoff_.NextAttemptTime();
-  if (grpc_lb_glb_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_glb_trace)) {
     gpr_log(GPR_INFO, "[grpclb %p] Connection to LB server lost...", this);
     grpc_millis timeout = next_try - ExecCtx::Get()->Now();
     if (timeout > 0) {
@@ -1577,7 +1572,7 @@ void GrpcLb::OnBalancerCallRetryTimerLocked(void* arg, grpc_error* error) {
   grpclb_policy->retry_timer_callback_pending_ = false;
   if (!grpclb_policy->shutting_down_ && error == GRPC_ERROR_NONE &&
       grpclb_policy->lb_calld_ == nullptr) {
-    if (grpc_lb_glb_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_glb_trace)) {
       gpr_log(GPR_INFO, "[grpclb %p] Restarting call to LB server",
               grpclb_policy);
     }
@@ -1632,20 +1627,16 @@ void GrpcLb::OnFallbackTimerLocked(void* arg, grpc_error* error) {
 
 grpc_channel_args* GrpcLb::CreateChildPolicyArgsLocked(
     bool is_backend_from_grpclb_load_balancer) {
-  grpc_arg args_to_add[2] = {
-      // A channel arg indicating if the target is a backend inferred from a
-      // grpclb load balancer.
-      grpc_channel_arg_integer_create(
-          const_cast<char*>(
-              GRPC_ARG_ADDRESS_IS_BACKEND_FROM_GRPCLB_LOAD_BALANCER),
-          is_backend_from_grpclb_load_balancer),
-  };
-  size_t num_args_to_add = 1;
+  InlinedVector<grpc_arg, 2> args_to_add;
+  args_to_add.emplace_back(grpc_channel_arg_integer_create(
+      const_cast<char*>(GRPC_ARG_ADDRESS_IS_BACKEND_FROM_GRPCLB_LOAD_BALANCER),
+      is_backend_from_grpclb_load_balancer));
   if (is_backend_from_grpclb_load_balancer) {
-    args_to_add[num_args_to_add++] = grpc_channel_arg_integer_create(
-        const_cast<char*>(GRPC_ARG_INHIBIT_HEALTH_CHECKING), 1);
+    args_to_add.emplace_back(grpc_channel_arg_integer_create(
+        const_cast<char*>(GRPC_ARG_INHIBIT_HEALTH_CHECKING), 1));
   }
-  return grpc_channel_args_copy_and_add(args_, args_to_add, num_args_to_add);
+  return grpc_channel_args_copy_and_add(args_, args_to_add.data(),
+                                        args_to_add.size());
 }
 
 OrphanablePtr<LoadBalancingPolicy> GrpcLb::CreateChildPolicyLocked(
@@ -1665,7 +1656,7 @@ OrphanablePtr<LoadBalancingPolicy> GrpcLb::CreateChildPolicyLocked(
     return nullptr;
   }
   helper->set_child(lb_policy.get());
-  if (grpc_lb_glb_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_glb_trace)) {
     gpr_log(GPR_INFO, "[grpclb %p] Created new child policy %s (%p)", this,
             name, lb_policy.get());
   }
@@ -1764,7 +1755,7 @@ void GrpcLb::CreateOrUpdateChildPolicyLocked() {
     // Cases 1, 2b, and 3b: create a new child policy.
     // If child_policy_ is null, we set it (case 1), else we set
     // pending_child_policy_ (cases 2b and 3b).
-    if (grpc_lb_glb_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_glb_trace)) {
       gpr_log(GPR_INFO, "[grpclb %p] Creating new %schild policy %s", this,
               child_policy_ == nullptr ? "" : "pending ", child_policy_name);
     }
@@ -1788,7 +1779,7 @@ void GrpcLb::CreateOrUpdateChildPolicyLocked() {
   }
   GPR_ASSERT(policy_to_update != nullptr);
   // Update the policy.
-  if (grpc_lb_glb_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_glb_trace)) {
     gpr_log(GPR_INFO, "[grpclb %p] Updating %schild policy %p", this,
             policy_to_update == pending_child_policy_.get() ? "pending " : "",
             policy_to_update);
@@ -1808,6 +1799,40 @@ class GrpcLbFactory : public LoadBalancingPolicyFactory {
   }
 
   const char* name() const override { return kGrpclb; }
+
+  RefCountedPtr<ParsedLoadBalancingConfig> ParseLoadBalancingConfig(
+      const grpc_json* json, grpc_error** error) const override {
+    GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
+    if (json == nullptr) {
+      return RefCountedPtr<ParsedLoadBalancingConfig>(
+          New<ParsedGrpcLbConfig>(nullptr));
+    }
+    InlinedVector<grpc_error*, 2> error_list;
+    RefCountedPtr<ParsedLoadBalancingConfig> child_policy;
+    for (const grpc_json* field = json->child; field != nullptr;
+         field = field->next) {
+      if (field->key == nullptr) continue;
+      if (strcmp(field->key, "childPolicy") == 0) {
+        if (child_policy != nullptr) {
+          error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "field:childPolicy error:Duplicate entry"));
+        }
+        grpc_error* parse_error = GRPC_ERROR_NONE;
+        child_policy = LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(
+            field, &parse_error);
+        if (parse_error != GRPC_ERROR_NONE) {
+          error_list.push_back(parse_error);
+        }
+      }
+    }
+    if (error_list.empty()) {
+      return RefCountedPtr<ParsedLoadBalancingConfig>(
+          New<ParsedGrpcLbConfig>(std::move(child_policy)));
+    } else {
+      *error = GRPC_ERROR_CREATE_FROM_VECTOR("GrpcLb Parser", &error_list);
+      return nullptr;
+    }
+  }
 };
 
 }  // namespace
index 1c7ed87..3512363 100644 (file)
@@ -25,6 +25,8 @@
 #include <grpc/support/atm.h>
 #include <grpc/support/string_util.h>
 
+#include "src/core/lib/gprpp/sync.h"
+
 namespace grpc_core {
 
 void GrpcLbClientStats::AddCallStarted() {
@@ -43,11 +45,12 @@ void GrpcLbClientStats::AddCallFinished(
   }
 }
 
-void GrpcLbClientStats::AddCallDroppedLocked(const char* token) {
+void GrpcLbClientStats::AddCallDropped(const char* token) {
   // Increment num_calls_started and num_calls_finished.
   gpr_atm_full_fetch_add(&num_calls_started_, (gpr_atm)1);
   gpr_atm_full_fetch_add(&num_calls_finished_, (gpr_atm)1);
   // Record the drop.
+  MutexLock lock(&drop_count_mu_);
   if (drop_token_counts_ == nullptr) {
     drop_token_counts_.reset(New<DroppedCallCounts>());
   }
@@ -69,7 +72,7 @@ void AtomicGetAndResetCounter(int64_t* value, gpr_atm* counter) {
 
 }  // namespace
 
-void GrpcLbClientStats::GetLocked(
+void GrpcLbClientStats::Get(
     int64_t* num_calls_started, int64_t* num_calls_finished,
     int64_t* num_calls_finished_with_client_failed_to_send,
     int64_t* num_calls_finished_known_received,
@@ -80,6 +83,7 @@ void GrpcLbClientStats::GetLocked(
                            &num_calls_finished_with_client_failed_to_send_);
   AtomicGetAndResetCounter(num_calls_finished_known_received,
                            &num_calls_finished_known_received_);
+  MutexLock lock(&drop_count_mu_);
   *drop_token_counts = std::move(drop_token_counts_);
 }
 
index cb261ee..bcc6598 100644 (file)
@@ -26,6 +26,7 @@
 #include "src/core/lib/gprpp/inlined_vector.h"
 #include "src/core/lib/gprpp/memory.h"
 #include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/sync.h"
 
 namespace grpc_core {
 
@@ -41,20 +42,16 @@ class GrpcLbClientStats : public RefCounted<GrpcLbClientStats> {
 
   typedef InlinedVector<DropTokenCount, 10> DroppedCallCounts;
 
-  GrpcLbClientStats() {}
-
   void AddCallStarted();
   void AddCallFinished(bool finished_with_client_failed_to_send,
                        bool finished_known_received);
 
-  // This method is not thread-safe; caller must synchronize.
-  void AddCallDroppedLocked(const char* token);
+  void AddCallDropped(const char* token);
 
-  // This method is not thread-safe; caller must synchronize.
-  void GetLocked(int64_t* num_calls_started, int64_t* num_calls_finished,
-                 int64_t* num_calls_finished_with_client_failed_to_send,
-                 int64_t* num_calls_finished_known_received,
-                 UniquePtr<DroppedCallCounts>* drop_token_counts);
+  void Get(int64_t* num_calls_started, int64_t* num_calls_finished,
+           int64_t* num_calls_finished_with_client_failed_to_send,
+           int64_t* num_calls_finished_known_received,
+           UniquePtr<DroppedCallCounts>* drop_token_counts);
 
   // A destruction function to use as the user_data key when attaching
   // client stats to a grpc_mdelem.
@@ -63,13 +60,12 @@ class GrpcLbClientStats : public RefCounted<GrpcLbClientStats> {
   }
 
  private:
-  // This field must only be accessed via *_locked() methods.
-  UniquePtr<DroppedCallCounts> drop_token_counts_;
-  // These fields may be accessed from multiple threads at a time.
   gpr_atm num_calls_started_ = 0;
   gpr_atm num_calls_finished_ = 0;
   gpr_atm num_calls_finished_with_client_failed_to_send_ = 0;
   gpr_atm num_calls_finished_known_received_ = 0;
+  Mutex drop_count_mu_;  // Guards drop_token_counts_.
+  UniquePtr<DroppedCallCounts> drop_token_counts_;
 };
 
 }  // namespace grpc_core
index 594c8cf..b51db11 100644 (file)
@@ -107,7 +107,7 @@ static bool encode_drops(pb_ostream_t* stream, const pb_field_t* field,
   return true;
 }
 
-grpc_grpclb_request* grpc_grpclb_load_report_request_create_locked(
+grpc_grpclb_request* grpc_grpclb_load_report_request_create(
     grpc_core::GrpcLbClientStats* client_stats) {
   grpc_grpclb_request* req = static_cast<grpc_grpclb_request*>(
       gpr_zalloc(sizeof(grpc_grpclb_request)));
@@ -122,7 +122,7 @@ grpc_grpclb_request* grpc_grpclb_load_report_request_create_locked(
   req->client_stats.calls_finished_with_drop.funcs.encode = encode_drops;
   grpc_core::UniquePtr<grpc_core::GrpcLbClientStats::DroppedCallCounts>
       drop_counts;
-  client_stats->GetLocked(
+  client_stats->Get(
       &req->client_stats.num_calls_started,
       &req->client_stats.num_calls_finished,
       &req->client_stats.num_calls_finished_with_client_failed_to_send,
index 3c1d41a..8005f6f 100644 (file)
@@ -43,7 +43,7 @@ typedef struct {
 
 /** Create a request for a gRPC LB service under \a lb_service_name */
 grpc_grpclb_request* grpc_grpclb_request_create(const char* lb_service_name);
-grpc_grpclb_request* grpc_grpclb_load_report_request_create_locked(
+grpc_grpclb_request* grpc_grpclb_load_report_request_create(
     grpc_core::GrpcLbClientStats* client_stats);
 
 /** Protocol Buffers v3-encode \a request */
index 3da0b59..bc2f6e5 100644 (file)
@@ -27,7 +27,7 @@
 #include "src/core/ext/filters/client_channel/server_address.h"
 #include "src/core/ext/filters/client_channel/subchannel.h"
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/gprpp/mutex_lock.h"
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/transport/connectivity_state.h"
@@ -73,7 +73,7 @@ class PickFirst : public LoadBalancingPolicy {
         : SubchannelData(subchannel_list, address, subchannel, combiner) {}
 
     void ProcessConnectivityChangeLocked(
-        grpc_connectivity_state connectivity_state, grpc_error* error) override;
+        grpc_connectivity_state connectivity_state) override;
 
     // Processes the connectivity change to READY for an unselected subchannel.
     void ProcessUnselectedReadyLocked();
@@ -154,30 +154,28 @@ class PickFirst : public LoadBalancingPolicy {
 
   /// Lock and data used to capture snapshots of this channels child
   /// channels and subchannels. This data is consumed by channelz.
-  gpr_mu child_refs_mu_;
+  Mutex child_refs_mu_;
   channelz::ChildRefsList child_subchannels_;
   channelz::ChildRefsList child_channels_;
 };
 
 PickFirst::PickFirst(Args args) : LoadBalancingPolicy(std::move(args)) {
-  gpr_mu_init(&child_refs_mu_);
-  if (grpc_lb_pick_first_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_pick_first_trace)) {
     gpr_log(GPR_INFO, "Pick First %p created.", this);
   }
 }
 
 PickFirst::~PickFirst() {
-  if (grpc_lb_pick_first_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_pick_first_trace)) {
     gpr_log(GPR_INFO, "Destroying Pick First %p", this);
   }
-  gpr_mu_destroy(&child_refs_mu_);
   GPR_ASSERT(subchannel_list_ == nullptr);
   GPR_ASSERT(latest_pending_subchannel_list_ == nullptr);
 }
 
 void PickFirst::ShutdownLocked() {
   AutoChildRefsUpdater guard(this);
-  if (grpc_lb_pick_first_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_pick_first_trace)) {
     gpr_log(GPR_INFO, "Pick First %p Shutting down", this);
   }
   shutdown_ = true;
@@ -186,14 +184,16 @@ void PickFirst::ShutdownLocked() {
 }
 
 void PickFirst::ExitIdleLocked() {
+  if (shutdown_) return;
   if (idle_) {
     idle_ = false;
     if (subchannel_list_ == nullptr ||
         subchannel_list_->num_subchannels() == 0) {
-      grpc_error* error =
-          GRPC_ERROR_CREATE_FROM_STATIC_STRING("No addresses to connect to");
+      grpc_error* error = grpc_error_set_int(
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("No addresses to connect to"),
+          GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE);
       channel_control_helper()->UpdateState(
-          GRPC_CHANNEL_TRANSIENT_FAILURE, GRPC_ERROR_REF(error),
+          GRPC_CHANNEL_TRANSIENT_FAILURE,
           UniquePtr<SubchannelPicker>(New<TransientFailurePicker>(error)));
     } else {
       subchannel_list_->subchannel(0)
@@ -245,7 +245,7 @@ void PickFirst::UpdateChildRefsLocked() {
 
 void PickFirst::UpdateLocked(UpdateArgs args) {
   AutoChildRefsUpdater guard(this);
-  if (grpc_lb_pick_first_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_pick_first_trace)) {
     gpr_log(GPR_INFO,
             "Pick First %p received update with %" PRIuPTR " addresses", this,
             args.addresses.size());
@@ -267,9 +267,11 @@ void PickFirst::UpdateLocked(UpdateArgs args) {
     // haven't gotten a non-empty update by the time the application tries
     // to start a new call.)
     if (!idle_) {
-      grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty update");
+      grpc_error* error = grpc_error_set_int(
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty update"),
+          GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE);
       channel_control_helper()->UpdateState(
-          GRPC_CHANNEL_TRANSIENT_FAILURE, GRPC_ERROR_REF(error),
+          GRPC_CHANNEL_TRANSIENT_FAILURE,
           UniquePtr<SubchannelPicker>(New<TransientFailurePicker>(error)));
     }
     return;
@@ -283,9 +285,7 @@ void PickFirst::UpdateLocked(UpdateArgs args) {
   // check and instead do it in ExitIdleLocked().
   for (size_t i = 0; i < subchannel_list->num_subchannels(); ++i) {
     PickFirstSubchannelData* sd = subchannel_list->subchannel(i);
-    grpc_error* error = GRPC_ERROR_NONE;
-    grpc_connectivity_state state = sd->CheckConnectivityStateLocked(&error);
-    GRPC_ERROR_UNREF(error);
+    grpc_connectivity_state state = sd->CheckConnectivityStateLocked();
     if (state == GRPC_CHANNEL_READY) {
       subchannel_list_ = std::move(subchannel_list);
       sd->StartConnectivityWatchLocked();
@@ -317,7 +317,7 @@ void PickFirst::UpdateLocked(UpdateArgs args) {
     // We do have a selected subchannel (which means it's READY), so keep
     // using it until one of the subchannels in the new list reports READY.
     if (latest_pending_subchannel_list_ != nullptr) {
-      if (grpc_lb_pick_first_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_pick_first_trace)) {
         gpr_log(GPR_INFO,
                 "Pick First %p Shutting down latest pending subchannel list "
                 "%p, about to be replaced by newer latest %p",
@@ -339,7 +339,7 @@ void PickFirst::UpdateLocked(UpdateArgs args) {
 }
 
 void PickFirst::PickFirstSubchannelData::ProcessConnectivityChangeLocked(
-    grpc_connectivity_state connectivity_state, grpc_error* error) {
+    grpc_connectivity_state connectivity_state) {
   PickFirst* p = static_cast<PickFirst*>(subchannel_list()->policy());
   AutoChildRefsUpdater guard(p);
   // The notification must be for a subchannel in either the current or
@@ -349,7 +349,7 @@ void PickFirst::PickFirstSubchannelData::ProcessConnectivityChangeLocked(
   GPR_ASSERT(connectivity_state != GRPC_CHANNEL_SHUTDOWN);
   // Handle updates for the currently selected subchannel.
   if (p->selected_ == this) {
-    if (grpc_lb_pick_first_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_pick_first_trace)) {
       gpr_log(GPR_INFO,
               "Pick First %p selected subchannel connectivity changed to %s", p,
               grpc_connectivity_state_name(connectivity_state));
@@ -358,7 +358,7 @@ void PickFirst::PickFirstSubchannelData::ProcessConnectivityChangeLocked(
     // pending update, switch to the pending update.
     if (connectivity_state != GRPC_CHANNEL_READY &&
         p->latest_pending_subchannel_list_ != nullptr) {
-      if (grpc_lb_pick_first_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_pick_first_trace)) {
         gpr_log(GPR_INFO,
                 "Pick First %p promoting pending subchannel list %p to "
                 "replace %p",
@@ -370,17 +370,16 @@ void PickFirst::PickFirstSubchannelData::ProcessConnectivityChangeLocked(
       p->subchannel_list_ = std::move(p->latest_pending_subchannel_list_);
       // Set our state to that of the pending subchannel list.
       if (p->subchannel_list_->in_transient_failure()) {
-        grpc_error* new_error =
-            GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
-                "selected subchannel failed; switching to pending update",
-                &error, 1);
+        grpc_error* error = grpc_error_set_int(
+            GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                "selected subchannel failed; switching to pending update"),
+            GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE);
         p->channel_control_helper()->UpdateState(
-            GRPC_CHANNEL_TRANSIENT_FAILURE, GRPC_ERROR_REF(new_error),
-            UniquePtr<SubchannelPicker>(
-                New<TransientFailurePicker>(new_error)));
+            GRPC_CHANNEL_TRANSIENT_FAILURE,
+            UniquePtr<SubchannelPicker>(New<TransientFailurePicker>(error)));
       } else {
         p->channel_control_helper()->UpdateState(
-            GRPC_CHANNEL_CONNECTING, GRPC_ERROR_NONE,
+            GRPC_CHANNEL_CONNECTING,
             UniquePtr<SubchannelPicker>(New<QueuePicker>(p->Ref())));
       }
     } else {
@@ -394,7 +393,7 @@ void PickFirst::PickFirstSubchannelData::ProcessConnectivityChangeLocked(
         p->selected_ = nullptr;
         StopConnectivityWatchLocked();
         p->channel_control_helper()->UpdateState(
-            GRPC_CHANNEL_IDLE, GRPC_ERROR_NONE,
+            GRPC_CHANNEL_IDLE,
             UniquePtr<SubchannelPicker>(New<QueuePicker>(p->Ref())));
       } else {
         // This is unlikely but can happen when a subchannel has been asked
@@ -402,19 +401,17 @@ void PickFirst::PickFirstSubchannelData::ProcessConnectivityChangeLocked(
         // some connectivity state notifications.
         if (connectivity_state == GRPC_CHANNEL_READY) {
           p->channel_control_helper()->UpdateState(
-              GRPC_CHANNEL_READY, GRPC_ERROR_NONE,
-              UniquePtr<SubchannelPicker>(
-                  New<Picker>(connected_subchannel()->Ref())));
+              GRPC_CHANNEL_READY, UniquePtr<SubchannelPicker>(New<Picker>(
+                                      connected_subchannel()->Ref())));
         } else {  // CONNECTING
           p->channel_control_helper()->UpdateState(
-              connectivity_state, GRPC_ERROR_REF(error),
+              connectivity_state,
               UniquePtr<SubchannelPicker>(New<QueuePicker>(p->Ref())));
         }
         // Renew notification.
         RenewConnectivityWatchLocked();
       }
     }
-    GRPC_ERROR_UNREF(error);
     return;
   }
   // If we get here, there are two possible cases:
@@ -451,13 +448,13 @@ void PickFirst::PickFirstSubchannelData::ProcessConnectivityChangeLocked(
         subchannel_list()->set_in_transient_failure(true);
         // Only report new state in case 1.
         if (subchannel_list() == p->subchannel_list_.get()) {
-          grpc_error* new_error =
-              GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
-                  "failed to connect to all addresses", &error, 1);
+          grpc_error* error = grpc_error_set_int(
+              GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                  "failed to connect to all addresses"),
+              GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE);
           p->channel_control_helper()->UpdateState(
-              GRPC_CHANNEL_TRANSIENT_FAILURE, GRPC_ERROR_REF(new_error),
-              UniquePtr<SubchannelPicker>(
-                  New<TransientFailurePicker>(new_error)));
+              GRPC_CHANNEL_TRANSIENT_FAILURE,
+              UniquePtr<SubchannelPicker>(New<TransientFailurePicker>(error)));
         }
       }
       sd->CheckConnectivityStateAndStartWatchingLocked();
@@ -468,7 +465,7 @@ void PickFirst::PickFirstSubchannelData::ProcessConnectivityChangeLocked(
       // Only update connectivity state in case 1.
       if (subchannel_list() == p->subchannel_list_.get()) {
         p->channel_control_helper()->UpdateState(
-            GRPC_CHANNEL_CONNECTING, GRPC_ERROR_NONE,
+            GRPC_CHANNEL_CONNECTING,
             UniquePtr<SubchannelPicker>(New<QueuePicker>(p->Ref())));
       }
       // Renew notification.
@@ -478,7 +475,6 @@ void PickFirst::PickFirstSubchannelData::ProcessConnectivityChangeLocked(
     case GRPC_CHANNEL_SHUTDOWN:
       GPR_UNREACHABLE_CODE(break);
   }
-  GRPC_ERROR_UNREF(error);
 }
 
 void PickFirst::PickFirstSubchannelData::ProcessUnselectedReadyLocked() {
@@ -496,7 +492,7 @@ void PickFirst::PickFirstSubchannelData::ProcessUnselectedReadyLocked() {
              subchannel_list() == p->latest_pending_subchannel_list_.get());
   // Case 2.  Promote p->latest_pending_subchannel_list_ to p->subchannel_list_.
   if (subchannel_list() == p->latest_pending_subchannel_list_.get()) {
-    if (grpc_lb_pick_first_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_pick_first_trace)) {
       gpr_log(GPR_INFO,
               "Pick First %p promoting pending subchannel list %p to "
               "replace %p",
@@ -508,9 +504,9 @@ void PickFirst::PickFirstSubchannelData::ProcessUnselectedReadyLocked() {
   // Cases 1 and 2.
   p->selected_ = this;
   p->channel_control_helper()->UpdateState(
-      GRPC_CHANNEL_READY, GRPC_ERROR_NONE,
+      GRPC_CHANNEL_READY,
       UniquePtr<SubchannelPicker>(New<Picker>(connected_subchannel()->Ref())));
-  if (grpc_lb_pick_first_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_pick_first_trace)) {
     gpr_log(GPR_INFO, "Pick First %p selected subchannel %p", p, subchannel());
   }
 }
@@ -519,9 +515,7 @@ void PickFirst::PickFirstSubchannelData::
     CheckConnectivityStateAndStartWatchingLocked() {
   PickFirst* p = static_cast<PickFirst*>(subchannel_list()->policy());
   // Check current state.
-  grpc_error* error = GRPC_ERROR_NONE;
-  grpc_connectivity_state current_state = CheckConnectivityStateLocked(&error);
-  GRPC_ERROR_UNREF(error);
+  grpc_connectivity_state current_state = CheckConnectivityStateLocked();
   // Start watch.
   StartConnectivityWatchLocked();
   // If current state is READY, select the subchannel now, since we started
@@ -532,6 +526,11 @@ void PickFirst::PickFirstSubchannelData::
   }
 }
 
+class ParsedPickFirstConfig : public ParsedLoadBalancingConfig {
+ public:
+  const char* name() const override { return kPickFirst; }
+};
+
 //
 // factory
 //
@@ -544,6 +543,15 @@ class PickFirstFactory : public LoadBalancingPolicyFactory {
   }
 
   const char* name() const override { return kPickFirst; }
+
+  RefCountedPtr<ParsedLoadBalancingConfig> ParseLoadBalancingConfig(
+      const grpc_json* json, grpc_error** error) const override {
+    if (json != nullptr) {
+      GPR_DEBUG_ASSERT(strcmp(json->key, name()) == 0);
+    }
+    return RefCountedPtr<ParsedLoadBalancingConfig>(
+        New<ParsedPickFirstConfig>());
+  }
 };
 
 }  // namespace
index d3faaad..6d60391 100644 (file)
@@ -36,8 +36,8 @@
 #include "src/core/ext/filters/client_channel/subchannel.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/debug/trace.h"
-#include "src/core/lib/gprpp/mutex_lock.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/transport/connectivity_state.h"
@@ -92,11 +92,11 @@ class RoundRobin : public LoadBalancingPolicy {
     }
 
     void UpdateConnectivityStateLocked(
-        grpc_connectivity_state connectivity_state, grpc_error* error);
+        grpc_connectivity_state connectivity_state);
 
    private:
     void ProcessConnectivityChangeLocked(
-        grpc_connectivity_state connectivity_state, grpc_error* error) override;
+        grpc_connectivity_state connectivity_state) override;
 
     grpc_connectivity_state last_connectivity_state_ = GRPC_CHANNEL_IDLE;
   };
@@ -119,7 +119,6 @@ class RoundRobin : public LoadBalancingPolicy {
     }
 
     ~RoundRobinSubchannelList() {
-      GRPC_ERROR_UNREF(last_transient_failure_error_);
       RoundRobin* p = static_cast<RoundRobin*>(policy());
       p->Unref(DEBUG_LOCATION, "subchannel_list");
     }
@@ -129,11 +128,8 @@ class RoundRobin : public LoadBalancingPolicy {
 
     // Updates the counters of subchannels in each state when a
     // subchannel transitions from old_state to new_state.
-    // transient_failure_error is the error that is reported when
-    // new_state is TRANSIENT_FAILURE.
     void UpdateStateCountersLocked(grpc_connectivity_state old_state,
-                                   grpc_connectivity_state new_state,
-                                   grpc_error* transient_failure_error);
+                                   grpc_connectivity_state new_state);
 
     // If this subchannel list is the RR policy's current subchannel
     // list, updates the RR policy's connectivity state based on the
@@ -148,7 +144,6 @@ class RoundRobin : public LoadBalancingPolicy {
     size_t num_ready_ = 0;
     size_t num_connecting_ = 0;
     size_t num_transient_failure_ = 0;
-    grpc_error* last_transient_failure_error_ = GRPC_ERROR_NONE;
   };
 
   class Picker : public SubchannelPicker {
@@ -193,7 +188,7 @@ class RoundRobin : public LoadBalancingPolicy {
   bool shutdown_ = false;
   /// Lock and data used to capture snapshots of this channel's child
   /// channels and subchannels. This data is consumed by channelz.
-  gpr_mu child_refs_mu_;
+  Mutex child_refs_mu_;
   channelz::ChildRefsList child_subchannels_;
   channelz::ChildRefsList child_channels_;
 };
@@ -217,7 +212,7 @@ RoundRobin::Picker::Picker(RoundRobin* parent,
   // TODO(roth): rand(3) is not thread-safe.  This should be replaced with
   // something better as part of https://github.com/grpc/grpc/issues/17891.
   last_picked_index_ = rand() % subchannels_.size();
-  if (grpc_lb_round_robin_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_round_robin_trace)) {
     gpr_log(GPR_INFO,
             "[RR %p picker %p] created picker from subchannel_list=%p "
             "with %" PRIuPTR " READY subchannels; last_picked_index_=%" PRIuPTR,
@@ -229,7 +224,7 @@ RoundRobin::Picker::Picker(RoundRobin* parent,
 RoundRobin::PickResult RoundRobin::Picker::Pick(PickArgs* pick,
                                                 grpc_error** error) {
   last_picked_index_ = (last_picked_index_ + 1) % subchannels_.size();
-  if (grpc_lb_round_robin_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_round_robin_trace)) {
     gpr_log(GPR_INFO,
             "[RR %p picker %p] returning index %" PRIuPTR
             ", connected_subchannel=%p",
@@ -245,24 +240,22 @@ RoundRobin::PickResult RoundRobin::Picker::Pick(PickArgs* pick,
 //
 
 RoundRobin::RoundRobin(Args args) : LoadBalancingPolicy(std::move(args)) {
-  gpr_mu_init(&child_refs_mu_);
-  if (grpc_lb_round_robin_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_round_robin_trace)) {
     gpr_log(GPR_INFO, "[RR %p] Created", this);
   }
 }
 
 RoundRobin::~RoundRobin() {
-  if (grpc_lb_round_robin_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_round_robin_trace)) {
     gpr_log(GPR_INFO, "[RR %p] Destroying Round Robin policy", this);
   }
-  gpr_mu_destroy(&child_refs_mu_);
   GPR_ASSERT(subchannel_list_ == nullptr);
   GPR_ASSERT(latest_pending_subchannel_list_ == nullptr);
 }
 
 void RoundRobin::ShutdownLocked() {
   AutoChildRefsUpdater guard(this);
-  if (grpc_lb_round_robin_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_round_robin_trace)) {
     gpr_log(GPR_INFO, "[RR %p] Shutting down", this);
   }
   shutdown_ = true;
@@ -317,11 +310,10 @@ void RoundRobin::RoundRobinSubchannelList::StartWatchingLocked() {
   // subchannel already used by some other channel may have a non-IDLE
   // state.
   for (size_t i = 0; i < num_subchannels(); ++i) {
-    grpc_error* error = GRPC_ERROR_NONE;
     grpc_connectivity_state state =
-        subchannel(i)->CheckConnectivityStateLocked(&error);
+        subchannel(i)->CheckConnectivityStateLocked();
     if (state != GRPC_CHANNEL_IDLE) {
-      subchannel(i)->UpdateConnectivityStateLocked(state, error);
+      subchannel(i)->UpdateConnectivityStateLocked(state);
     }
   }
   // Start connectivity watch for each subchannel.
@@ -335,8 +327,7 @@ void RoundRobin::RoundRobinSubchannelList::StartWatchingLocked() {
 }
 
 void RoundRobin::RoundRobinSubchannelList::UpdateStateCountersLocked(
-    grpc_connectivity_state old_state, grpc_connectivity_state new_state,
-    grpc_error* transient_failure_error) {
+    grpc_connectivity_state old_state, grpc_connectivity_state new_state) {
   GPR_ASSERT(old_state != GRPC_CHANNEL_SHUTDOWN);
   GPR_ASSERT(new_state != GRPC_CHANNEL_SHUTDOWN);
   if (old_state == GRPC_CHANNEL_READY) {
@@ -356,8 +347,6 @@ void RoundRobin::RoundRobinSubchannelList::UpdateStateCountersLocked(
   } else if (new_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
     ++num_transient_failure_;
   }
-  GRPC_ERROR_UNREF(last_transient_failure_error_);
-  last_transient_failure_error_ = transient_failure_error;
 }
 
 // Sets the RR policy's connectivity state and generates a new picker based
@@ -384,20 +373,21 @@ void RoundRobin::RoundRobinSubchannelList::
   if (num_ready_ > 0) {
     /* 1) READY */
     p->channel_control_helper()->UpdateState(
-        GRPC_CHANNEL_READY, GRPC_ERROR_NONE,
-        UniquePtr<SubchannelPicker>(New<Picker>(p, this)));
+        GRPC_CHANNEL_READY, UniquePtr<SubchannelPicker>(New<Picker>(p, this)));
   } else if (num_connecting_ > 0) {
     /* 2) CONNECTING */
     p->channel_control_helper()->UpdateState(
-        GRPC_CHANNEL_CONNECTING, GRPC_ERROR_NONE,
+        GRPC_CHANNEL_CONNECTING,
         UniquePtr<SubchannelPicker>(New<QueuePicker>(p->Ref())));
   } else if (num_transient_failure_ == num_subchannels()) {
     /* 3) TRANSIENT_FAILURE */
+    grpc_error* error =
+        grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                               "connections to all backends failing"),
+                           GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE);
     p->channel_control_helper()->UpdateState(
         GRPC_CHANNEL_TRANSIENT_FAILURE,
-        GRPC_ERROR_REF(last_transient_failure_error_),
-        UniquePtr<SubchannelPicker>(New<TransientFailurePicker>(
-            GRPC_ERROR_REF(last_transient_failure_error_))));
+        UniquePtr<SubchannelPicker>(New<TransientFailurePicker>(error)));
   }
 }
 
@@ -413,7 +403,7 @@ void RoundRobin::RoundRobinSubchannelList::
       // therefore we would not be receiving a notification for them.
       GPR_ASSERT(p->latest_pending_subchannel_list_.get() == this);
       GPR_ASSERT(!shutting_down());
-      if (grpc_lb_round_robin_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_round_robin_trace)) {
         const size_t old_num_subchannels =
             p->subchannel_list_ != nullptr
                 ? p->subchannel_list_->num_subchannels()
@@ -432,9 +422,9 @@ void RoundRobin::RoundRobinSubchannelList::
 }
 
 void RoundRobin::RoundRobinSubchannelData::UpdateConnectivityStateLocked(
-    grpc_connectivity_state connectivity_state, grpc_error* error) {
+    grpc_connectivity_state connectivity_state) {
   RoundRobin* p = static_cast<RoundRobin*>(subchannel_list()->policy());
-  if (grpc_lb_round_robin_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_round_robin_trace)) {
     gpr_log(
         GPR_INFO,
         "[RR %p] connectivity changed for subchannel %p, subchannel_list %p "
@@ -445,12 +435,12 @@ void RoundRobin::RoundRobinSubchannelData::UpdateConnectivityStateLocked(
         grpc_connectivity_state_name(connectivity_state));
   }
   subchannel_list()->UpdateStateCountersLocked(last_connectivity_state_,
-                                               connectivity_state, error);
+                                               connectivity_state);
   last_connectivity_state_ = connectivity_state;
 }
 
 void RoundRobin::RoundRobinSubchannelData::ProcessConnectivityChangeLocked(
-    grpc_connectivity_state connectivity_state, grpc_error* error) {
+    grpc_connectivity_state connectivity_state) {
   RoundRobin* p = static_cast<RoundRobin*>(subchannel_list()->policy());
   GPR_ASSERT(subchannel() != nullptr);
   // If the new state is TRANSIENT_FAILURE, re-resolve.
@@ -459,7 +449,7 @@ void RoundRobin::RoundRobinSubchannelData::ProcessConnectivityChangeLocked(
   // when the subchannel list was created, we'd wind up in a constant
   // loop of re-resolution.
   if (connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
-    if (grpc_lb_round_robin_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_round_robin_trace)) {
       gpr_log(GPR_INFO,
               "[RR %p] Subchannel %p has gone into TRANSIENT_FAILURE. "
               "Requesting re-resolution",
@@ -470,20 +460,20 @@ void RoundRobin::RoundRobinSubchannelData::ProcessConnectivityChangeLocked(
   // Renew connectivity watch.
   RenewConnectivityWatchLocked();
   // Update state counters.
-  UpdateConnectivityStateLocked(connectivity_state, error);
+  UpdateConnectivityStateLocked(connectivity_state);
   // Update overall state and renew notification.
   subchannel_list()->UpdateRoundRobinStateFromSubchannelStateCountsLocked();
 }
 
 void RoundRobin::UpdateLocked(UpdateArgs args) {
   AutoChildRefsUpdater guard(this);
-  if (grpc_lb_round_robin_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_round_robin_trace)) {
     gpr_log(GPR_INFO, "[RR %p] received update with %" PRIuPTR " addresses",
             this, args.addresses.size());
   }
   // Replace latest_pending_subchannel_list_.
   if (latest_pending_subchannel_list_ != nullptr) {
-    if (grpc_lb_round_robin_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_round_robin_trace)) {
       gpr_log(GPR_INFO,
               "[RR %p] Shutting down previous pending subchannel list %p", this,
               latest_pending_subchannel_list_.get());
@@ -494,9 +484,11 @@ void RoundRobin::UpdateLocked(UpdateArgs args) {
   if (latest_pending_subchannel_list_->num_subchannels() == 0) {
     // If the new list is empty, immediately promote the new list to the
     // current list and transition to TRANSIENT_FAILURE.
-    grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty update");
+    grpc_error* error =
+        grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty update"),
+                           GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE);
     channel_control_helper()->UpdateState(
-        GRPC_CHANNEL_TRANSIENT_FAILURE, GRPC_ERROR_REF(error),
+        GRPC_CHANNEL_TRANSIENT_FAILURE,
         UniquePtr<SubchannelPicker>(New<TransientFailurePicker>(error)));
     subchannel_list_ = std::move(latest_pending_subchannel_list_);
   } else if (subchannel_list_ == nullptr) {
@@ -511,6 +503,11 @@ void RoundRobin::UpdateLocked(UpdateArgs args) {
   }
 }
 
+class ParsedRoundRobinConfig : public ParsedLoadBalancingConfig {
+ public:
+  const char* name() const override { return kRoundRobin; }
+};
+
 //
 // factory
 //
@@ -523,6 +520,15 @@ class RoundRobinFactory : public LoadBalancingPolicyFactory {
   }
 
   const char* name() const override { return kRoundRobin; }
+
+  RefCountedPtr<ParsedLoadBalancingConfig> ParseLoadBalancingConfig(
+      const grpc_json* json, grpc_error** error) const override {
+    if (json != nullptr) {
+      GPR_DEBUG_ASSERT(strcmp(json->key, name()) == 0);
+    }
+    return RefCountedPtr<ParsedLoadBalancingConfig>(
+        New<ParsedRoundRobinConfig>());
+  }
 };
 
 }  // namespace
index 4fde90c..4c48045 100644 (file)
@@ -51,7 +51,7 @@ class MySubchannelData
     : public SubchannelData<MySubchannelList, MySubchannelData> {
  public:
   void ProcessConnectivityChangeLocked(
-      grpc_connectivity_state connectivity_state, grpc_error* error) override {
+      grpc_connectivity_state connectivity_state) override {
     // ...code to handle connectivity changes...
   }
 };
@@ -101,10 +101,10 @@ class SubchannelData {
   // pending (i.e., between calling StartConnectivityWatchLocked() or
   // RenewConnectivityWatchLocked() and the resulting invocation of
   // ProcessConnectivityChangeLocked()).
-  grpc_connectivity_state CheckConnectivityStateLocked(grpc_error** error) {
+  grpc_connectivity_state CheckConnectivityStateLocked() {
     GPR_ASSERT(!connectivity_notification_pending_);
     pending_connectivity_state_unsafe_ = subchannel()->CheckConnectivity(
-        error, subchannel_list_->inhibit_health_checking());
+        subchannel_list_->inhibit_health_checking());
     UpdateConnectedSubchannelLocked();
     return pending_connectivity_state_unsafe_;
   }
@@ -153,8 +153,7 @@ class SubchannelData {
   // Implementations must invoke either RenewConnectivityWatchLocked() or
   // StopConnectivityWatchLocked() before returning.
   virtual void ProcessConnectivityChangeLocked(
-      grpc_connectivity_state connectivity_state,
-      grpc_error* error) GRPC_ABSTRACT;
+      grpc_connectivity_state connectivity_state) GRPC_ABSTRACT;
 
   // Unrefs the subchannel.
   void UnrefSubchannelLocked(const char* reason);
@@ -299,7 +298,7 @@ template <typename SubchannelListType, typename SubchannelDataType>
 void SubchannelData<SubchannelListType, SubchannelDataType>::
     UnrefSubchannelLocked(const char* reason) {
   if (subchannel_ != nullptr) {
-    if (subchannel_list_->tracer()->enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(*subchannel_list_->tracer())) {
       gpr_log(GPR_INFO,
               "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
               " (subchannel %p): unreffing subchannel",
@@ -324,7 +323,7 @@ void SubchannelData<SubchannelListType,
 template <typename SubchannelListType, typename SubchannelDataType>
 void SubchannelData<SubchannelListType,
                     SubchannelDataType>::StartConnectivityWatchLocked() {
-  if (subchannel_list_->tracer()->enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(*subchannel_list_->tracer())) {
     gpr_log(GPR_INFO,
             "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
             " (subchannel %p): starting watch: requesting connectivity change "
@@ -346,7 +345,7 @@ void SubchannelData<SubchannelListType,
 template <typename SubchannelListType, typename SubchannelDataType>
 void SubchannelData<SubchannelListType,
                     SubchannelDataType>::RenewConnectivityWatchLocked() {
-  if (subchannel_list_->tracer()->enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(*subchannel_list_->tracer())) {
     gpr_log(GPR_INFO,
             "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
             " (subchannel %p): renewing watch: requesting connectivity change "
@@ -366,7 +365,7 @@ void SubchannelData<SubchannelListType,
 template <typename SubchannelListType, typename SubchannelDataType>
 void SubchannelData<SubchannelListType,
                     SubchannelDataType>::StopConnectivityWatchLocked() {
-  if (subchannel_list_->tracer()->enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(*subchannel_list_->tracer())) {
     gpr_log(GPR_INFO,
             "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
             " (subchannel %p): stopping connectivity watch",
@@ -382,7 +381,7 @@ void SubchannelData<SubchannelListType,
 template <typename SubchannelListType, typename SubchannelDataType>
 void SubchannelData<SubchannelListType, SubchannelDataType>::
     CancelConnectivityWatchLocked(const char* reason) {
-  if (subchannel_list_->tracer()->enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(*subchannel_list_->tracer())) {
     gpr_log(GPR_INFO,
             "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
             " (subchannel %p): canceling connectivity watch (%s)",
@@ -414,7 +413,7 @@ bool SubchannelData<SubchannelListType,
     // is READY again (e.g., if the subchannel has transitioned back to
     // READY before the next watch gets requested).
     if (connected_subchannel_ == nullptr) {
-      if (subchannel_list_->tracer()->enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(*subchannel_list_->tracer())) {
         gpr_log(GPR_INFO,
                 "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
                 " (subchannel %p): state is READY but connected subchannel is "
@@ -437,7 +436,7 @@ template <typename SubchannelListType, typename SubchannelDataType>
 void SubchannelData<SubchannelListType, SubchannelDataType>::
     OnConnectivityChangedLocked(void* arg, grpc_error* error) {
   SubchannelData* sd = static_cast<SubchannelData*>(arg);
-  if (sd->subchannel_list_->tracer()->enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(*sd->subchannel_list_->tracer())) {
     gpr_log(
         GPR_INFO,
         "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
@@ -462,8 +461,7 @@ void SubchannelData<SubchannelListType, SubchannelDataType>::
     return;
   }
   // Call the subclass's ProcessConnectivityChangeLocked() method.
-  sd->ProcessConnectivityChangeLocked(sd->pending_connectivity_state_unsafe_,
-                                      GRPC_ERROR_REF(error));
+  sd->ProcessConnectivityChangeLocked(sd->pending_connectivity_state_unsafe_);
 }
 
 template <typename SubchannelListType, typename SubchannelDataType>
@@ -492,7 +490,7 @@ SubchannelList<SubchannelListType, SubchannelDataType>::SubchannelList(
       policy_(policy),
       tracer_(tracer),
       combiner_(GRPC_COMBINER_REF(combiner, "subchannel_list")) {
-  if (tracer_->enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(*tracer_)) {
     gpr_log(GPR_INFO,
             "[%s %p] Creating subchannel list %p for %" PRIuPTR " subchannels",
             tracer_->name(), policy, this, addresses.size());
@@ -508,7 +506,13 @@ SubchannelList<SubchannelListType, SubchannelDataType>::SubchannelList(
                                          GRPC_ARG_INHIBIT_HEALTH_CHECKING};
   // Create a subchannel for each address.
   for (size_t i = 0; i < addresses.size(); i++) {
-    GPR_ASSERT(!addresses[i].IsBalancer());
+    // TODO(roth): we should ideally hide this from the LB policy code. In
+    // principle, if we're dealing with this special case in the client_channel
+    // code for selecting grpclb, then we should also strip out these addresses
+    // there if we're not using grpclb.
+    if (addresses[i].IsBalancer()) {
+      continue;
+    }
     InlinedVector<grpc_arg, 3> args_to_add;
     const size_t subchannel_address_arg_index = args_to_add.size();
     args_to_add.emplace_back(
@@ -526,7 +530,7 @@ SubchannelList<SubchannelListType, SubchannelDataType>::SubchannelList(
     grpc_channel_args_destroy(new_args);
     if (subchannel == nullptr) {
       // Subchannel could not be created.
-      if (tracer_->enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(*tracer_)) {
         char* address_uri = grpc_sockaddr_to_uri(&addresses[i].address());
         gpr_log(GPR_INFO,
                 "[%s %p] could not create subchannel for address uri %s, "
@@ -536,7 +540,7 @@ SubchannelList<SubchannelListType, SubchannelDataType>::SubchannelList(
       }
       continue;
     }
-    if (tracer_->enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(*tracer_)) {
       char* address_uri = grpc_sockaddr_to_uri(&addresses[i].address());
       gpr_log(GPR_INFO,
               "[%s %p] subchannel list %p index %" PRIuPTR
@@ -551,7 +555,7 @@ SubchannelList<SubchannelListType, SubchannelDataType>::SubchannelList(
 
 template <typename SubchannelListType, typename SubchannelDataType>
 SubchannelList<SubchannelListType, SubchannelDataType>::~SubchannelList() {
-  if (tracer_->enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(*tracer_)) {
     gpr_log(GPR_INFO, "[%s %p] Destroying subchannel_list %p", tracer_->name(),
             policy_, this);
   }
@@ -560,7 +564,7 @@ SubchannelList<SubchannelListType, SubchannelDataType>::~SubchannelList() {
 
 template <typename SubchannelListType, typename SubchannelDataType>
 void SubchannelList<SubchannelListType, SubchannelDataType>::ShutdownLocked() {
-  if (tracer_->enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(*tracer_)) {
     gpr_log(GPR_INFO, "[%s %p] Shutting down subchannel_list %p",
             tracer_->name(), policy_, this);
   }
index d3b13a6..3eb9437 100644 (file)
@@ -68,7 +68,9 @@
 #include <grpc/support/string_util.h>
 #include <grpc/support/time.h>
 
+#include "include/grpc/support/alloc.h"
 #include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/ext/filters/client_channel/lb_policy.h"
 #include "src/core/ext/filters/client_channel/lb_policy/xds/xds.h"
 #include "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.h"
 #include "src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/gprpp/map.h"
 #include "src/core/lib/gprpp/memory.h"
-#include "src/core/lib/gprpp/mutex_lock.h"
 #include "src/core/lib/gprpp/orphanable.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/iomgr/sockaddr.h"
 #include "src/core/lib/iomgr/sockaddr_utils.h"
@@ -114,6 +117,35 @@ TraceFlag grpc_lb_xds_trace(false, "xds");
 namespace {
 
 constexpr char kXds[] = "xds_experimental";
+constexpr char kDefaultLocalityName[] = "xds_default_locality";
+constexpr uint32_t kDefaultLocalityWeight = 3;
+
+class ParsedXdsConfig : public ParsedLoadBalancingConfig {
+ public:
+  ParsedXdsConfig(const char* balancer_name,
+                  RefCountedPtr<ParsedLoadBalancingConfig> child_policy,
+                  RefCountedPtr<ParsedLoadBalancingConfig> fallback_policy)
+      : balancer_name_(balancer_name),
+        child_policy_(std::move(child_policy)),
+        fallback_policy_(std::move(fallback_policy)) {}
+
+  const char* name() const override { return kXds; }
+
+  const char* balancer_name() const { return balancer_name_; };
+
+  RefCountedPtr<ParsedLoadBalancingConfig> child_policy() const {
+    return child_policy_;
+  }
+
+  RefCountedPtr<ParsedLoadBalancingConfig> fallback_policy() const {
+    return fallback_policy_;
+  }
+
+ private:
+  const char* balancer_name_ = nullptr;
+  RefCountedPtr<ParsedLoadBalancingConfig> child_policy_;
+  RefCountedPtr<ParsedLoadBalancingConfig> fallback_policy_;
+};
 
 class XdsLb : public LoadBalancingPolicy {
  public:
@@ -128,6 +160,9 @@ class XdsLb : public LoadBalancingPolicy {
       channelz::ChildRefsList* child_channels) override;
 
  private:
+  struct LocalityServerlistEntry;
+  using LocalityList = InlinedVector<UniquePtr<LocalityServerlistEntry>, 1>;
+
   /// Contains a channel to the LB server and all the data related to the
   /// channel.
   class BalancerChannelState
@@ -232,6 +267,10 @@ class XdsLb : public LoadBalancingPolicy {
     static void OnCallRetryTimerLocked(void* arg, grpc_error* error);
     void StartCallLocked();
 
+    void StartConnectivityWatchLocked();
+    void CancelConnectivityWatchLocked();
+    static void OnConnectivityChangedLocked(void* arg, grpc_error* error);
+
    private:
     // The owning LB policy.
     RefCountedPtr<XdsLb> xdslb_policy_;
@@ -239,6 +278,8 @@ class XdsLb : public LoadBalancingPolicy {
     // The channel and its status.
     grpc_channel* channel_;
     bool shutting_down_ = false;
+    grpc_connectivity_state connectivity_ = GRPC_CHANNEL_IDLE;
+    grpc_closure on_connectivity_changed_;
 
     // The data associated with the current LB call. It holds a ref to this LB
     // channel. It's instantiated every time we query for backends. It's reset
@@ -252,41 +293,151 @@ class XdsLb : public LoadBalancingPolicy {
     bool retry_timer_callback_pending_ = false;
   };
 
+  // Since pickers are UniquePtrs we use this RefCounted wrapper
+  // to control references to it by the xds picker and the locality
+  // entry
+  class PickerRef : public RefCounted<PickerRef> {
+   public:
+    explicit PickerRef(UniquePtr<SubchannelPicker> picker)
+        : picker_(std::move(picker)) {}
+    PickResult Pick(PickArgs* pick, grpc_error** error) {
+      return picker_->Pick(pick, error);
+    }
+
+   private:
+    UniquePtr<SubchannelPicker> picker_;
+  };
+
+  // The picker will use a stateless weighting algorithm to pick the locality to
+  // use for each request.
   class Picker : public SubchannelPicker {
    public:
-    Picker(UniquePtr<SubchannelPicker> child_picker,
-           RefCountedPtr<XdsLbClientStats> client_stats)
-        : child_picker_(std::move(child_picker)),
-          client_stats_(std::move(client_stats)) {}
+    // Maintains a weighted list of pickers from each locality that is in ready
+    // state. The first element in the pair represents the end of a range
+    // proportional to the locality's weight. The start of the range is the
+    // previous value in the vector and is 0 for the first element.
+    using PickerList =
+        InlinedVector<Pair<uint32_t, RefCountedPtr<PickerRef>>, 1>;
+    Picker(RefCountedPtr<XdsLbClientStats> client_stats, PickerList pickers)
+        : client_stats_(std::move(client_stats)),
+          pickers_(std::move(pickers)) {}
 
     PickResult Pick(PickArgs* pick, grpc_error** error) override;
 
    private:
-    UniquePtr<SubchannelPicker> child_picker_;
+    // Calls the picker of the locality that the key falls within
+    PickResult PickFromLocality(const uint32_t key, PickArgs* pick,
+                                grpc_error** error);
     RefCountedPtr<XdsLbClientStats> client_stats_;
+    PickerList pickers_;
   };
 
-  class Helper : public ChannelControlHelper {
+  class FallbackHelper : public ChannelControlHelper {
    public:
-    explicit Helper(RefCountedPtr<XdsLb> parent) : parent_(std::move(parent)) {}
+    explicit FallbackHelper(RefCountedPtr<XdsLb> parent)
+        : parent_(std::move(parent)) {}
 
     Subchannel* CreateSubchannel(const grpc_channel_args& args) override;
     grpc_channel* CreateChannel(const char* target,
                                 const grpc_channel_args& args) override;
-    void UpdateState(grpc_connectivity_state state, grpc_error* state_error,
+    void UpdateState(grpc_connectivity_state state,
                      UniquePtr<SubchannelPicker> picker) override;
     void RequestReresolution() override;
 
     void set_child(LoadBalancingPolicy* child) { child_ = child; }
 
    private:
-    bool CalledByPendingChild() const;
-    bool CalledByCurrentChild() const;
+    bool CalledByPendingFallback() const;
+    bool CalledByCurrentFallback() const;
 
     RefCountedPtr<XdsLb> parent_;
     LoadBalancingPolicy* child_ = nullptr;
   };
 
+  class LocalityMap {
+   public:
+    class LocalityEntry : public InternallyRefCounted<LocalityEntry> {
+     public:
+      LocalityEntry(RefCountedPtr<XdsLb> parent, uint32_t locality_weight)
+          : parent_(std::move(parent)), locality_weight_(locality_weight) {}
+      ~LocalityEntry() = default;
+
+      void UpdateLocked(xds_grpclb_serverlist* serverlist,
+                        ParsedLoadBalancingConfig* child_policy_config,
+                        const grpc_channel_args* args);
+      void ShutdownLocked();
+      void ResetBackoffLocked();
+      void FillChildRefsForChannelz(channelz::ChildRefsList* child_subchannels,
+                                    channelz::ChildRefsList* child_channels);
+      void Orphan() override;
+
+     private:
+      class Helper : public ChannelControlHelper {
+       public:
+        explicit Helper(RefCountedPtr<LocalityEntry> entry)
+            : entry_(std::move(entry)) {}
+
+        Subchannel* CreateSubchannel(const grpc_channel_args& args) override;
+        grpc_channel* CreateChannel(const char* target,
+                                    const grpc_channel_args& args) override;
+        void UpdateState(grpc_connectivity_state state,
+                         UniquePtr<SubchannelPicker> picker) override;
+        void RequestReresolution() override;
+        void set_child(LoadBalancingPolicy* child) { child_ = child; }
+
+       private:
+        bool CalledByPendingChild() const;
+        bool CalledByCurrentChild() const;
+
+        RefCountedPtr<LocalityEntry> entry_;
+        LoadBalancingPolicy* child_ = nullptr;
+      };
+      // Methods for dealing with the child policy.
+      OrphanablePtr<LoadBalancingPolicy> CreateChildPolicyLocked(
+          const char* name, const grpc_channel_args* args);
+      grpc_channel_args* CreateChildPolicyArgsLocked(
+          const grpc_channel_args* args);
+
+      OrphanablePtr<LoadBalancingPolicy> child_policy_;
+      OrphanablePtr<LoadBalancingPolicy> pending_child_policy_;
+      // Lock held when modifying the value of child_policy_ or
+      // pending_child_policy_.
+      Mutex child_policy_mu_;
+      RefCountedPtr<XdsLb> parent_;
+      RefCountedPtr<PickerRef> picker_ref_;
+      grpc_connectivity_state connectivity_state_;
+      uint32_t locality_weight_;
+    };
+
+    void UpdateLocked(const LocalityList& locality_list,
+                      ParsedLoadBalancingConfig* child_policy_config,
+                      const grpc_channel_args* args, XdsLb* parent);
+    void ShutdownLocked();
+    void ResetBackoffLocked();
+    void FillChildRefsForChannelz(channelz::ChildRefsList* child_subchannels,
+                                  channelz::ChildRefsList* child_channels);
+
+   private:
+    void PruneLocalities(const LocalityList& locality_list);
+    Map<UniquePtr<char>, OrphanablePtr<LocalityEntry>, StringLess> map_;
+    // Lock held while filling child refs for all localities
+    // inside the map
+    Mutex child_refs_mu_;
+  };
+
+  struct LocalityServerlistEntry {
+    ~LocalityServerlistEntry() {
+      gpr_free(locality_name);
+      xds_grpclb_destroy_serverlist(serverlist);
+    }
+
+    char* locality_name;
+    uint32_t locality_weight;
+    // The deserialized response from the balancer. May be nullptr until one
+    // such response has arrived.
+    xds_grpclb_serverlist* serverlist;
+  };
+
   ~XdsLb();
 
   void ShutdownLocked() override;
@@ -299,21 +450,20 @@ class XdsLb : public LoadBalancingPolicy {
   // If parsing succeeds, updates \a balancer_name, and updates \a
   // child_policy_config_ and \a fallback_policy_config_ if they are also
   // found. Does nothing upon failure.
-  void ParseLbConfig(Config* xds_config);
+  void ParseLbConfig(const ParsedXdsConfig* xds_config);
 
   BalancerChannelState* LatestLbChannel() const {
     return pending_lb_chand_ != nullptr ? pending_lb_chand_.get()
                                         : lb_chand_.get();
   }
 
-  // Callback to enter fallback mode.
+  // Methods for dealing with fallback state.
+  void MaybeCancelFallbackAtStartupChecks();
   static void OnFallbackTimerLocked(void* arg, grpc_error* error);
-
-  // Methods for dealing with the child policy.
-  void CreateOrUpdateChildPolicyLocked();
-  grpc_channel_args* CreateChildPolicyArgsLocked();
-  OrphanablePtr<LoadBalancingPolicy> CreateChildPolicyLocked(
+  void UpdateFallbackPolicyLocked();
+  OrphanablePtr<LoadBalancingPolicy> CreateFallbackPolicyLocked(
       const char* name, const grpc_channel_args* args);
+  void MaybeExitFallbackMode();
 
   // Who the client is trying to communicate with.
   const char* server_name_ = nullptr;
@@ -333,33 +483,48 @@ class XdsLb : public LoadBalancingPolicy {
   // Mutex to protect the channel to the LB server. This is used when
   // processing a channelz request.
   // TODO(juanlishen): Replace this with atomic.
-  gpr_mu lb_chand_mu_;
+  Mutex lb_chand_mu_;
 
   // Timeout in milliseconds for the LB call. 0 means no deadline.
   int lb_call_timeout_ms_ = 0;
 
-  // The deserialized response from the balancer. May be nullptr until one
-  // such response has arrived.
-  xds_grpclb_serverlist* serverlist_ = nullptr;
-
+  // Whether the checks for fallback at startup are ALL pending. There are
+  // several cases where this can be reset:
+  // 1. The fallback timer fires, we enter fallback mode.
+  // 2. Before the fallback timer fires, the LB channel becomes
+  // TRANSIENT_FAILURE or the LB call fails, we enter fallback mode.
+  // 3. Before the fallback timer fires, we receive a response from the
+  // balancer, we cancel the fallback timer and use the response to update the
+  // locality map.
+  bool fallback_at_startup_checks_pending_ = false;
   // Timeout in milliseconds for before using fallback backend addresses.
   // 0 means not using fallback.
-  RefCountedPtr<Config> fallback_policy_config_;
   int lb_fallback_timeout_ms_ = 0;
   // The backend addresses from the resolver.
-  UniquePtr<ServerAddressList> fallback_backend_addresses_;
+  ServerAddressList fallback_backend_addresses_;
   // Fallback timer.
-  bool fallback_timer_callback_pending_ = false;
   grpc_timer lb_fallback_timer_;
   grpc_closure lb_on_fallback_;
 
+  // The policy to use for the fallback backends.
+  RefCountedPtr<ParsedLoadBalancingConfig> fallback_policy_config_;
+  // Lock held when modifying the value of fallback_policy_ or
+  // pending_fallback_policy_.
+  Mutex fallback_policy_mu_;
+  // Non-null iff we are in fallback mode.
+  OrphanablePtr<LoadBalancingPolicy> fallback_policy_;
+  OrphanablePtr<LoadBalancingPolicy> pending_fallback_policy_;
+
   // The policy to use for the backends.
-  RefCountedPtr<Config> child_policy_config_;
-  OrphanablePtr<LoadBalancingPolicy> child_policy_;
-  OrphanablePtr<LoadBalancingPolicy> pending_child_policy_;
-  // Lock held when modifying the value of child_policy_ or
-  // pending_child_policy_.
-  gpr_mu child_policy_mu_;
+  RefCountedPtr<ParsedLoadBalancingConfig> child_policy_config_;
+  // Map of policies to use in the backend
+  LocalityMap locality_map_;
+  // TODO(mhaidry) : Add support for multiple maps of localities
+  // with different priorities
+  LocalityList locality_serverlist_;
+  // TODO(mhaidry) : Add a pending locality map that may be swapped with the
+  // the current one when new localities in the pending map are ready
+  // to accept connections
 };
 
 //
@@ -368,8 +533,12 @@ class XdsLb : public LoadBalancingPolicy {
 
 XdsLb::PickResult XdsLb::Picker::Pick(PickArgs* pick, grpc_error** error) {
   // TODO(roth): Add support for drop handling.
-  // Forward pick to child policy.
-  PickResult result = child_picker_->Pick(pick, error);
+  // Generate a random number between 0 and the total weight
+  const uint32_t key =
+      (rand() * pickers_[pickers_.size() - 1].first) / RAND_MAX;
+  // Forward pick to whichever locality maps to the range in which the
+  // random number falls in.
+  PickResult result = PickFromLocality(key, pick, error);
   // If pick succeeded, add client stats.
   if (result == PickResult::PICK_COMPLETE &&
       pick->connected_subchannel != nullptr && client_stats_ != nullptr) {
@@ -378,103 +547,101 @@ XdsLb::PickResult XdsLb::Picker::Pick(PickArgs* pick, grpc_error** error) {
   return result;
 }
 
+XdsLb::PickResult XdsLb::Picker::PickFromLocality(const uint32_t key,
+                                                  PickArgs* pick,
+                                                  grpc_error** error) {
+  size_t mid = 0;
+  size_t start_index = 0;
+  size_t end_index = pickers_.size() - 1;
+  size_t index = 0;
+  while (end_index > start_index) {
+    mid = (start_index + end_index) / 2;
+    if (pickers_[mid].first > key) {
+      end_index = mid;
+    } else if (pickers_[mid].first < key) {
+      start_index = mid + 1;
+    } else {
+      index = mid + 1;
+      break;
+    }
+  }
+  if (index == 0) index = start_index;
+  GPR_ASSERT(pickers_[index].first > key);
+  return pickers_[index].second->Pick(pick, error);
+}
+
 //
-// XdsLb::Helper
+// XdsLb::FallbackHelper
 //
 
-bool XdsLb::Helper::CalledByPendingChild() const {
+bool XdsLb::FallbackHelper::CalledByPendingFallback() const {
   GPR_ASSERT(child_ != nullptr);
-  return child_ == parent_->pending_child_policy_.get();
+  return child_ == parent_->pending_fallback_policy_.get();
 }
 
-bool XdsLb::Helper::CalledByCurrentChild() const {
+bool XdsLb::FallbackHelper::CalledByCurrentFallback() const {
   GPR_ASSERT(child_ != nullptr);
-  return child_ == parent_->child_policy_.get();
+  return child_ == parent_->fallback_policy_.get();
 }
 
-Subchannel* XdsLb::Helper::CreateSubchannel(const grpc_channel_args& args) {
+Subchannel* XdsLb::FallbackHelper::CreateSubchannel(
+    const grpc_channel_args& args) {
   if (parent_->shutting_down_ ||
-      (!CalledByPendingChild() && !CalledByCurrentChild())) {
+      (!CalledByPendingFallback() && !CalledByCurrentFallback())) {
     return nullptr;
   }
   return parent_->channel_control_helper()->CreateSubchannel(args);
 }
 
-grpc_channel* XdsLb::Helper::CreateChannel(const char* target,
-                                           const grpc_channel_args& args) {
+grpc_channel* XdsLb::FallbackHelper::CreateChannel(
+    const char* target, const grpc_channel_args& args) {
   if (parent_->shutting_down_ ||
-      (!CalledByPendingChild() && !CalledByCurrentChild())) {
+      (!CalledByPendingFallback() && !CalledByCurrentFallback())) {
     return nullptr;
   }
   return parent_->channel_control_helper()->CreateChannel(target, args);
 }
 
-void XdsLb::Helper::UpdateState(grpc_connectivity_state state,
-                                grpc_error* state_error,
-                                UniquePtr<SubchannelPicker> picker) {
-  if (parent_->shutting_down_) {
-    GRPC_ERROR_UNREF(state_error);
-    return;
-  }
-  // If this request is from the pending child policy, ignore it until
+void XdsLb::FallbackHelper::UpdateState(grpc_connectivity_state state,
+                                        UniquePtr<SubchannelPicker> picker) {
+  if (parent_->shutting_down_) return;
+  // If this request is from the pending fallback policy, ignore it until
   // it reports READY, at which point we swap it into place.
-  if (CalledByPendingChild()) {
-    if (grpc_lb_xds_trace.enabled()) {
-      gpr_log(GPR_INFO,
-              "[xdslb %p helper %p] pending child policy %p reports state=%s",
-              parent_.get(), this, parent_->pending_child_policy_.get(),
-              grpc_connectivity_state_name(state));
-    }
-    if (state != GRPC_CHANNEL_READY) {
-      GRPC_ERROR_UNREF(state_error);
-      return;
+  if (CalledByPendingFallback()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
+      gpr_log(
+          GPR_INFO,
+          "[xdslb %p helper %p] pending fallback policy %p reports state=%s",
+          parent_.get(), this, parent_->pending_fallback_policy_.get(),
+          grpc_connectivity_state_name(state));
     }
+    if (state != GRPC_CHANNEL_READY) return;
     grpc_pollset_set_del_pollset_set(
-        parent_->child_policy_->interested_parties(),
+        parent_->fallback_policy_->interested_parties(),
         parent_->interested_parties());
-    MutexLock lock(&parent_->child_policy_mu_);
-    parent_->child_policy_ = std::move(parent_->pending_child_policy_);
-  } else if (!CalledByCurrentChild()) {
-    // This request is from an outdated child, so ignore it.
-    GRPC_ERROR_UNREF(state_error);
+    MutexLock lock(&parent_->fallback_policy_mu_);
+    parent_->fallback_policy_ = std::move(parent_->pending_fallback_policy_);
+  } else if (!CalledByCurrentFallback()) {
+    // This request is from an outdated fallback policy, so ignore it.
     return;
   }
-  // TODO(juanlishen): When in fallback mode, pass the child picker
-  // through without wrapping it.  (Or maybe use a different helper for
-  // the fallback policy?)
-  GPR_ASSERT(parent_->lb_chand_ != nullptr);
-  RefCountedPtr<XdsLbClientStats> client_stats =
-      parent_->lb_chand_->lb_calld() == nullptr
-          ? nullptr
-          : parent_->lb_chand_->lb_calld()->client_stats();
-  parent_->channel_control_helper()->UpdateState(
-      state, state_error,
-      UniquePtr<SubchannelPicker>(
-          New<Picker>(std::move(picker), std::move(client_stats))));
+  parent_->channel_control_helper()->UpdateState(state, std::move(picker));
 }
 
-void XdsLb::Helper::RequestReresolution() {
+void XdsLb::FallbackHelper::RequestReresolution() {
   if (parent_->shutting_down_) return;
-  // If there is a pending child policy, ignore re-resolution requests
-  // from the current child policy (or any outdated child).
-  if (parent_->pending_child_policy_ != nullptr && !CalledByPendingChild()) {
-    return;
-  }
-  if (grpc_lb_xds_trace.enabled()) {
+  const LoadBalancingPolicy* latest_fallback_policy =
+      parent_->pending_fallback_policy_ != nullptr
+          ? parent_->pending_fallback_policy_.get()
+          : parent_->fallback_policy_.get();
+  if (child_ != latest_fallback_policy) return;
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
     gpr_log(GPR_INFO,
-            "[xdslb %p] Re-resolution requested from the internal RR policy "
-            "(%p).",
-            parent_.get(), parent_->child_policy_.get());
+            "[xdslb %p] Re-resolution requested from the fallback policy (%p).",
+            parent_.get(), child_);
   }
   GPR_ASSERT(parent_->lb_chand_ != nullptr);
-  // If we are talking to a balancer, we expect to get updated addresses
-  // from the balancer, so we can ignore the re-resolution request from
-  // the child policy. Otherwise, pass the re-resolution request up to the
-  // channel.
-  if (parent_->lb_chand_->lb_calld() == nullptr ||
-      !parent_->lb_chand_->lb_calld()->seen_initial_response()) {
-    parent_->channel_control_helper()->RequestReresolution();
-  }
+  parent_->channel_control_helper()->RequestReresolution();
 }
 
 //
@@ -482,12 +649,11 @@ void XdsLb::Helper::RequestReresolution() {
 //
 
 // Returns the backend addresses extracted from the given addresses.
-UniquePtr<ServerAddressList> ExtractBackendAddresses(
-    const ServerAddressList& addresses) {
-  auto backend_addresses = MakeUnique<ServerAddressList>();
+ServerAddressList ExtractBackendAddresses(const ServerAddressList& addresses) {
+  ServerAddressList backend_addresses;
   for (size_t i = 0; i < addresses.size(); ++i) {
     if (!addresses[i].IsBalancer()) {
-      backend_addresses->emplace_back(addresses[i]);
+      backend_addresses.emplace_back(addresses[i]);
     }
   }
   return backend_addresses;
@@ -567,6 +733,9 @@ XdsLb::BalancerChannelState::BalancerChannelState(
               .set_multiplier(GRPC_XDS_RECONNECT_BACKOFF_MULTIPLIER)
               .set_jitter(GRPC_XDS_RECONNECT_JITTER)
               .set_max_backoff(GRPC_XDS_RECONNECT_MAX_BACKOFF_SECONDS * 1000)) {
+  GRPC_CLOSURE_INIT(&on_connectivity_changed_,
+                    &XdsLb::BalancerChannelState::OnConnectivityChangedLocked,
+                    this, grpc_combiner_scheduler(xdslb_policy_->combiner()));
   channel_ = xdslb_policy_->channel_control_helper()->CreateChannel(
       balancer_name, args);
   GPR_ASSERT(channel_ != nullptr);
@@ -586,7 +755,7 @@ void XdsLb::BalancerChannelState::Orphan() {
 
 void XdsLb::BalancerChannelState::StartCallRetryTimerLocked() {
   grpc_millis next_try = lb_call_backoff_.NextAttemptTime();
-  if (grpc_lb_xds_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
     gpr_log(GPR_INFO,
             "[xdslb %p] Failed to connect to LB server (lb_chand: %p)...",
             xdslb_policy_.get(), this);
@@ -612,7 +781,7 @@ void XdsLb::BalancerChannelState::OnCallRetryTimerLocked(void* arg,
   lb_chand->retry_timer_callback_pending_ = false;
   if (!lb_chand->shutting_down_ && error == GRPC_ERROR_NONE &&
       lb_chand->lb_calld_ == nullptr) {
-    if (grpc_lb_xds_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
       gpr_log(GPR_INFO,
               "[xdslb %p] Restarting call to LB server (lb_chand: %p)",
               lb_chand->xdslb_policy_.get(), lb_chand);
@@ -627,7 +796,7 @@ void XdsLb::BalancerChannelState::StartCallLocked() {
   GPR_ASSERT(channel_ != nullptr);
   GPR_ASSERT(lb_calld_ == nullptr);
   lb_calld_ = MakeOrphanable<BalancerCallState>(Ref());
-  if (grpc_lb_xds_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
     gpr_log(GPR_INFO,
             "[xdslb %p] Query for backends (lb_chand: %p, lb_calld: %p)",
             xdslb_policy_.get(), this, lb_calld_.get());
@@ -635,6 +804,62 @@ void XdsLb::BalancerChannelState::StartCallLocked() {
   lb_calld_->StartQuery();
 }
 
+void XdsLb::BalancerChannelState::StartConnectivityWatchLocked() {
+  grpc_channel_element* client_channel_elem =
+      grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel_));
+  GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter);
+  // Ref held by callback.
+  Ref(DEBUG_LOCATION, "watch_lb_channel_connectivity").release();
+  grpc_client_channel_watch_connectivity_state(
+      client_channel_elem,
+      grpc_polling_entity_create_from_pollset_set(
+          xdslb_policy_->interested_parties()),
+      &connectivity_, &on_connectivity_changed_, nullptr);
+}
+
+void XdsLb::BalancerChannelState::CancelConnectivityWatchLocked() {
+  grpc_channel_element* client_channel_elem =
+      grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel_));
+  GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter);
+  grpc_client_channel_watch_connectivity_state(
+      client_channel_elem,
+      grpc_polling_entity_create_from_pollset_set(
+          xdslb_policy_->interested_parties()),
+      nullptr, &on_connectivity_changed_, nullptr);
+}
+
+void XdsLb::BalancerChannelState::OnConnectivityChangedLocked(
+    void* arg, grpc_error* error) {
+  BalancerChannelState* self = static_cast<BalancerChannelState*>(arg);
+  if (!self->shutting_down_ &&
+      self->xdslb_policy_->fallback_at_startup_checks_pending_) {
+    if (self->connectivity_ != GRPC_CHANNEL_TRANSIENT_FAILURE) {
+      // Not in TRANSIENT_FAILURE.  Renew connectivity watch.
+      grpc_channel_element* client_channel_elem =
+          grpc_channel_stack_last_element(
+              grpc_channel_get_channel_stack(self->channel_));
+      GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter);
+      grpc_client_channel_watch_connectivity_state(
+          client_channel_elem,
+          grpc_polling_entity_create_from_pollset_set(
+              self->xdslb_policy_->interested_parties()),
+          &self->connectivity_, &self->on_connectivity_changed_, nullptr);
+      return;  // Early out so we don't drop the ref below.
+    }
+    // In TRANSIENT_FAILURE.  Cancel the fallback timer and go into
+    // fallback mode immediately.
+    gpr_log(GPR_INFO,
+            "[xdslb %p] Balancer channel in state TRANSIENT_FAILURE; "
+            "entering fallback mode",
+            self);
+    self->xdslb_policy_->fallback_at_startup_checks_pending_ = false;
+    grpc_timer_cancel(&self->xdslb_policy_->lb_fallback_timer_);
+    self->xdslb_policy_->UpdateFallbackPolicyLocked();
+  }
+  // Done watching connectivity state, so drop ref.
+  self->Unref(DEBUG_LOCATION, "watch_lb_channel_connectivity");
+}
+
 //
 // XdsLb::BalancerChannelState::BalancerCallState
 //
@@ -707,7 +932,7 @@ void XdsLb::BalancerChannelState::BalancerCallState::Orphan() {
 
 void XdsLb::BalancerChannelState::BalancerCallState::StartQuery() {
   GPR_ASSERT(lb_call_ != nullptr);
-  if (grpc_lb_xds_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
     gpr_log(GPR_INFO, "[xdslb %p] Starting LB call (lb_calld: %p, lb_call: %p)",
             xdslb_policy(), this, lb_call_);
   }
@@ -880,6 +1105,14 @@ void XdsLb::BalancerChannelState::BalancerCallState::
       (initial_response = xds_grpclb_initial_response_parse(response_slice)) !=
           nullptr) {
     // Have NOT seen initial response, look for initial response.
+    // TODO(juanlishen): When we convert this to use the xds protocol, the
+    // balancer will send us a fallback timeout such that we should go into
+    // fallback mode if we have lost contact with the balancer after a certain
+    // period of time. We will need to save the timeout value here, and then
+    // when the balancer call ends, we will need to start a timer for the
+    // specified period of time, and if the timer fires, we go into fallback
+    // mode. We will also need to cancel the timer when we receive a serverlist
+    // from the balancer.
     if (initial_response->has_client_stats_report_interval) {
       const grpc_millis interval = xds_grpclb_duration_to_millis(
           &initial_response->client_stats_report_interval);
@@ -888,7 +1121,7 @@ void XdsLb::BalancerChannelState::BalancerCallState::
             GPR_MAX(GPR_MS_PER_SEC, interval);
       }
     }
-    if (grpc_lb_xds_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
       if (lb_calld->client_stats_report_interval_ != 0) {
         gpr_log(GPR_INFO,
                 "[xdslb %p] Received initial LB response message; "
@@ -907,7 +1140,7 @@ void XdsLb::BalancerChannelState::BalancerCallState::
                   response_slice)) != nullptr) {
     // Have seen initial response, look for serverlist.
     GPR_ASSERT(lb_calld->lb_call_ != nullptr);
-    if (grpc_lb_xds_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
       gpr_log(GPR_INFO,
               "[xdslb %p] Serverlist with %" PRIuPTR " servers received",
               xdslb_policy, serverlist->num_servers);
@@ -921,67 +1154,69 @@ void XdsLb::BalancerChannelState::BalancerCallState::
         gpr_free(ipport);
       }
     }
-    /* update serverlist */
-    // TODO(juanlishen): Don't ingore empty serverlist.
-    if (serverlist->num_servers > 0) {
-      // Pending LB channel receives a serverlist; promote it.
-      // Note that this call can't be on a discarded pending channel, because
-      // such channels don't have any current call but we have checked this call
-      // is a current call.
-      if (!lb_calld->lb_chand_->IsCurrentChannel()) {
-        if (grpc_lb_xds_trace.enabled()) {
-          gpr_log(GPR_INFO,
-                  "[xdslb %p] Promoting pending LB channel %p to replace "
-                  "current LB channel %p",
-                  xdslb_policy, lb_calld->lb_chand_.get(),
-                  lb_calld->xdslb_policy()->lb_chand_.get());
-        }
-        lb_calld->xdslb_policy()->lb_chand_ =
-            std::move(lb_calld->xdslb_policy()->pending_lb_chand_);
-      }
-      // Start sending client load report only after we start using the
-      // serverlist returned from the current LB call.
-      if (lb_calld->client_stats_report_interval_ > 0 &&
-          lb_calld->client_stats_ == nullptr) {
-        lb_calld->client_stats_ = MakeRefCounted<XdsLbClientStats>();
-        // TODO(roth): We currently track this ref manually.  Once the
-        // ClosureRef API is ready, we should pass the RefCountedPtr<> along
-        // with the callback.
-        auto self = lb_calld->Ref(DEBUG_LOCATION, "client_load_report");
-        self.release();
-        lb_calld->ScheduleNextClientLoadReportLocked();
-      }
-      if (xds_grpclb_serverlist_equals(xdslb_policy->serverlist_, serverlist)) {
-        if (grpc_lb_xds_trace.enabled()) {
-          gpr_log(GPR_INFO,
-                  "[xdslb %p] Incoming server list identical to current, "
-                  "ignoring.",
-                  xdslb_policy);
-        }
-        xds_grpclb_destroy_serverlist(serverlist);
-      } else { /* new serverlist */
-        if (xdslb_policy->serverlist_ != nullptr) {
-          /* dispose of the old serverlist */
-          xds_grpclb_destroy_serverlist(xdslb_policy->serverlist_);
-        } else {
-          /* or dispose of the fallback */
-          xdslb_policy->fallback_backend_addresses_.reset();
-          if (xdslb_policy->fallback_timer_callback_pending_) {
-            grpc_timer_cancel(&xdslb_policy->lb_fallback_timer_);
-          }
-        }
-        // and update the copy in the XdsLb instance. This
-        // serverlist instance will be destroyed either upon the next
-        // update or when the XdsLb instance is destroyed.
-        xdslb_policy->serverlist_ = serverlist;
-        xdslb_policy->CreateOrUpdateChildPolicyLocked();
+    // Pending LB channel receives a serverlist; promote it.
+    // Note that this call can't be on a discarded pending channel, because
+    // such channels don't have any current call but we have checked this call
+    // is a current call.
+    if (!lb_calld->lb_chand_->IsCurrentChannel()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
+        gpr_log(GPR_INFO,
+                "[xdslb %p] Promoting pending LB channel %p to replace "
+                "current LB channel %p",
+                xdslb_policy, lb_calld->lb_chand_.get(),
+                lb_calld->xdslb_policy()->lb_chand_.get());
       }
-    } else {
-      if (grpc_lb_xds_trace.enabled()) {
-        gpr_log(GPR_INFO, "[xdslb %p] Received empty server list, ignoring.",
+      lb_calld->xdslb_policy()->lb_chand_ =
+          std::move(lb_calld->xdslb_policy()->pending_lb_chand_);
+    }
+    // Start sending client load report only after we start using the
+    // serverlist returned from the current LB call.
+    if (lb_calld->client_stats_report_interval_ > 0 &&
+        lb_calld->client_stats_ == nullptr) {
+      lb_calld->client_stats_ = MakeRefCounted<XdsLbClientStats>();
+      lb_calld->Ref(DEBUG_LOCATION, "client_load_report").release();
+      lb_calld->ScheduleNextClientLoadReportLocked();
+    }
+    if (!xdslb_policy->locality_serverlist_.empty() &&
+        xds_grpclb_serverlist_equals(
+            xdslb_policy->locality_serverlist_[0]->serverlist, serverlist)) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
+        gpr_log(GPR_INFO,
+                "[xdslb %p] Incoming server list identical to current, "
+                "ignoring.",
                 xdslb_policy);
       }
       xds_grpclb_destroy_serverlist(serverlist);
+    } else {  // New serverlist.
+      // If the balancer tells us to drop all the calls, we should exit fallback
+      // mode immediately.
+      // TODO(juanlishen): When we add EDS drop, we should change to check
+      // drop_percentage.
+      if (serverlist->num_servers == 0) xdslb_policy->MaybeExitFallbackMode();
+      if (!xdslb_policy->locality_serverlist_.empty()) {
+        xds_grpclb_destroy_serverlist(
+            xdslb_policy->locality_serverlist_[0]->serverlist);
+      } else {
+        // This is the first serverlist we've received, don't enter fallback
+        // mode.
+        xdslb_policy->MaybeCancelFallbackAtStartupChecks();
+        // Initialize locality serverlist, currently the list only handles
+        // one child.
+        xdslb_policy->locality_serverlist_.emplace_back(
+            MakeUnique<LocalityServerlistEntry>());
+        xdslb_policy->locality_serverlist_[0]->locality_name =
+            static_cast<char*>(gpr_strdup(kDefaultLocalityName));
+        xdslb_policy->locality_serverlist_[0]->locality_weight =
+            kDefaultLocalityWeight;
+      }
+      // Update the serverlist in the XdsLb instance. This serverlist
+      // instance will be destroyed either upon the next update or when the
+      // XdsLb instance is destroyed.
+      xdslb_policy->locality_serverlist_[0]->serverlist = serverlist;
+      xdslb_policy->locality_map_.UpdateLocked(
+          xdslb_policy->locality_serverlist_,
+          xdslb_policy->child_policy_config_.get(), xdslb_policy->args_,
+          xdslb_policy);
     }
   } else {
     // No valid initial response or serverlist found.
@@ -1017,7 +1252,7 @@ void XdsLb::BalancerChannelState::BalancerCallState::
   XdsLb* xdslb_policy = lb_calld->xdslb_policy();
   BalancerChannelState* lb_chand = lb_calld->lb_chand_.get();
   GPR_ASSERT(lb_calld->lb_call_ != nullptr);
-  if (grpc_lb_xds_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
     char* status_details =
         grpc_slice_to_c_string(lb_calld->lb_call_status_details_);
     gpr_log(GPR_INFO,
@@ -1036,7 +1271,7 @@ void XdsLb::BalancerChannelState::BalancerCallState::
     if (lb_chand != xdslb_policy->LatestLbChannel()) {
       // This channel must be the current one and there is a pending one. Swap
       // in the pending one and we are done.
-      if (grpc_lb_xds_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
         gpr_log(GPR_INFO,
                 "[xdslb %p] Promoting pending LB channel %p to replace "
                 "current LB channel %p",
@@ -1058,6 +1293,18 @@ void XdsLb::BalancerChannelState::BalancerCallState::
         lb_chand->StartCallRetryTimerLocked();
       }
       xdslb_policy->channel_control_helper()->RequestReresolution();
+      // If the fallback-at-startup checks are pending, go into fallback mode
+      // immediately.  This short-circuits the timeout for the
+      // fallback-at-startup case.
+      if (xdslb_policy->fallback_at_startup_checks_pending_) {
+        gpr_log(GPR_INFO,
+                "[xdslb %p] Balancer call finished; entering fallback mode",
+                xdslb_policy);
+        xdslb_policy->fallback_at_startup_checks_pending_ = false;
+        grpc_timer_cancel(&xdslb_policy->lb_fallback_timer_);
+        lb_chand->CancelConnectivityWatchLocked();
+        xdslb_policy->UpdateFallbackPolicyLocked();
+      }
     }
   }
   lb_calld->Unref(DEBUG_LOCATION, "lb_call_ended");
@@ -1112,9 +1359,10 @@ grpc_channel_args* BuildBalancerChannelArgs(const grpc_channel_args* args) {
 // ctor and dtor
 //
 
-XdsLb::XdsLb(Args args) : LoadBalancingPolicy(std::move(args)) {
-  gpr_mu_init(&lb_chand_mu_);
-  gpr_mu_init(&child_policy_mu_);
+XdsLb::XdsLb(Args args)
+    : LoadBalancingPolicy(std::move(args)),
+      locality_map_(),
+      locality_serverlist_() {
   // Record server name.
   const grpc_arg* arg = grpc_channel_args_find(args.args, GRPC_ARG_SERVER_URI);
   const char* server_uri = grpc_channel_arg_get_string(arg);
@@ -1122,7 +1370,7 @@ XdsLb::XdsLb(Args args) : LoadBalancingPolicy(std::move(args)) {
   grpc_uri* uri = grpc_uri_parse(server_uri, true);
   GPR_ASSERT(uri->path[0] != '\0');
   server_name_ = gpr_strdup(uri->path[0] == '/' ? uri->path + 1 : uri->path);
-  if (grpc_lb_xds_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
     gpr_log(GPR_INFO,
             "[xdslb %p] Will use '%s' as the server name for LB request.", this,
             server_name_);
@@ -1132,43 +1380,38 @@ XdsLb::XdsLb(Args args) : LoadBalancingPolicy(std::move(args)) {
   arg = grpc_channel_args_find(args.args, GRPC_ARG_GRPCLB_CALL_TIMEOUT_MS);
   lb_call_timeout_ms_ = grpc_channel_arg_get_integer(arg, {0, 0, INT_MAX});
   // Record fallback timeout.
-  arg = grpc_channel_args_find(args.args, GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS);
+  arg = grpc_channel_args_find(args.args, GRPC_ARG_XDS_FALLBACK_TIMEOUT_MS);
   lb_fallback_timeout_ms_ = grpc_channel_arg_get_integer(
       arg, {GRPC_XDS_DEFAULT_FALLBACK_TIMEOUT_MS, 0, INT_MAX});
 }
 
 XdsLb::~XdsLb() {
-  gpr_mu_destroy(&lb_chand_mu_);
   gpr_free((void*)server_name_);
   grpc_channel_args_destroy(args_);
-  if (serverlist_ != nullptr) {
-    xds_grpclb_destroy_serverlist(serverlist_);
-  }
-  gpr_mu_destroy(&child_policy_mu_);
+  locality_serverlist_.clear();
 }
 
 void XdsLb::ShutdownLocked() {
   shutting_down_ = true;
-  if (fallback_timer_callback_pending_) {
+  if (fallback_at_startup_checks_pending_) {
     grpc_timer_cancel(&lb_fallback_timer_);
   }
-  if (child_policy_ != nullptr) {
-    grpc_pollset_set_del_pollset_set(child_policy_->interested_parties(),
+  locality_map_.ShutdownLocked();
+  if (fallback_policy_ != nullptr) {
+    grpc_pollset_set_del_pollset_set(fallback_policy_->interested_parties(),
                                      interested_parties());
   }
-  if (pending_child_policy_ != nullptr) {
+  if (pending_fallback_policy_ != nullptr) {
     grpc_pollset_set_del_pollset_set(
-        pending_child_policy_->interested_parties(), interested_parties());
+        pending_fallback_policy_->interested_parties(), interested_parties());
   }
   {
-    MutexLock lock(&child_policy_mu_);
-    child_policy_.reset();
-    pending_child_policy_.reset();
+    MutexLock lock(&fallback_policy_mu_);
+    fallback_policy_.reset();
+    pending_fallback_policy_.reset();
   }
-  // We destroy the LB channel here instead of in our destructor because
-  // destroying the channel triggers a last callback to
-  // OnBalancerChannelConnectivityChangedLocked(), and we need to be
-  // alive when that callback is invoked.
+  // We reset the LB channels here instead of in our destructor because they
+  // hold refs to XdsLb.
   {
     MutexLock lock(&lb_chand_mu_);
     lb_chand_.reset();
@@ -1187,28 +1430,30 @@ void XdsLb::ResetBackoffLocked() {
   if (pending_lb_chand_ != nullptr) {
     grpc_channel_reset_connect_backoff(pending_lb_chand_->channel());
   }
-  if (child_policy_ != nullptr) {
-    child_policy_->ResetBackoffLocked();
+  locality_map_.ResetBackoffLocked();
+  if (fallback_policy_ != nullptr) {
+    fallback_policy_->ResetBackoffLocked();
   }
-  if (pending_child_policy_ != nullptr) {
-    pending_child_policy_->ResetBackoffLocked();
+  if (pending_fallback_policy_ != nullptr) {
+    pending_fallback_policy_->ResetBackoffLocked();
   }
 }
 
 void XdsLb::FillChildRefsForChannelz(channelz::ChildRefsList* child_subchannels,
                                      channelz::ChildRefsList* child_channels) {
+  // Delegate to the locality_map_ to fill the children subchannels.
+  locality_map_.FillChildRefsForChannelz(child_subchannels, child_channels);
   {
-    // Delegate to the child_policy_ to fill the children subchannels.
-    // This must be done holding child_policy_mu_, since this method does not
+    // This must be done holding fallback_policy_mu_, since this method does not
     // run in the combiner.
-    MutexLock lock(&child_policy_mu_);
-    if (child_policy_ != nullptr) {
-      child_policy_->FillChildRefsForChannelz(child_subchannels,
-                                              child_channels);
+    MutexLock lock(&fallback_policy_mu_);
+    if (fallback_policy_ != nullptr) {
+      fallback_policy_->FillChildRefsForChannelz(child_subchannels,
+                                                 child_channels);
     }
-    if (pending_child_policy_ != nullptr) {
-      pending_child_policy_->FillChildRefsForChannelz(child_subchannels,
-                                                      child_channels);
+    if (pending_fallback_policy_ != nullptr) {
+      pending_fallback_policy_->FillChildRefsForChannelz(child_subchannels,
+                                                         child_channels);
     }
   }
   MutexLock lock(&lb_chand_mu_);
@@ -1267,96 +1512,290 @@ void XdsLb::ProcessAddressesAndChannelArgsLocked(
   grpc_channel_args_destroy(lb_channel_args);
 }
 
-void XdsLb::ParseLbConfig(Config* xds_config) {
-  const grpc_json* xds_config_json = xds_config->config();
-  const char* balancer_name = nullptr;
-  grpc_json* child_policy = nullptr;
-  grpc_json* fallback_policy = nullptr;
-  for (const grpc_json* field = xds_config_json; field != nullptr;
-       field = field->next) {
-    if (field->key == nullptr) return;
-    if (strcmp(field->key, "balancerName") == 0) {
-      if (balancer_name != nullptr) return;  // Duplicate.
-      if (field->type != GRPC_JSON_STRING) return;
-      balancer_name = field->value;
-    } else if (strcmp(field->key, "childPolicy") == 0) {
-      if (child_policy != nullptr) return;  // Duplicate.
-      child_policy = ParseLoadBalancingConfig(field);
-    } else if (strcmp(field->key, "fallbackPolicy") == 0) {
-      if (fallback_policy != nullptr) return;  // Duplicate.
-      fallback_policy = ParseLoadBalancingConfig(field);
-    }
-  }
-  if (balancer_name == nullptr) return;  // Required field.
-  balancer_name_ = UniquePtr<char>(gpr_strdup(balancer_name));
-  if (child_policy != nullptr) {
-    child_policy_config_ =
-        MakeRefCounted<Config>(child_policy, xds_config->service_config());
-  }
-  if (fallback_policy != nullptr) {
-    fallback_policy_config_ =
-        MakeRefCounted<Config>(fallback_policy, xds_config->service_config());
-  }
+void XdsLb::ParseLbConfig(const ParsedXdsConfig* xds_config) {
+  if (xds_config == nullptr || xds_config->balancer_name() == nullptr) return;
+  // TODO(yashykt) : does this need to be a gpr_strdup
+  balancer_name_ = UniquePtr<char>(gpr_strdup(xds_config->balancer_name()));
+  child_policy_config_ = xds_config->child_policy();
+  fallback_policy_config_ = xds_config->fallback_policy();
 }
 
 void XdsLb::UpdateLocked(UpdateArgs args) {
   const bool is_initial_update = lb_chand_ == nullptr;
-  ParseLbConfig(args.config.get());
-  // TODO(juanlishen): Pass fallback policy config update after fallback policy
-  // is added.
+  ParseLbConfig(static_cast<const ParsedXdsConfig*>(args.config.get()));
   if (balancer_name_ == nullptr) {
     gpr_log(GPR_ERROR, "[xdslb %p] LB config parsing fails.", this);
     return;
   }
   ProcessAddressesAndChannelArgsLocked(args.addresses, *args.args);
-  // Update the existing child policy.
-  // Note: We have disabled fallback mode in the code, so this child policy must
-  // have been created from a serverlist.
-  // TODO(vpowar): Handle the fallback_address changes when we add support for
-  // fallback in xDS.
-  if (child_policy_ != nullptr) CreateOrUpdateChildPolicyLocked();
-  // If this is the initial update, start the fallback timer.
+  locality_map_.UpdateLocked(locality_serverlist_, child_policy_config_.get(),
+                             args_, this);
+  // Update the existing fallback policy. The fallback policy config and/or the
+  // fallback addresses may be new.
+  if (fallback_policy_ != nullptr) UpdateFallbackPolicyLocked();
+  // If this is the initial update, start the fallback-at-startup checks.
   if (is_initial_update) {
-    if (lb_fallback_timeout_ms_ > 0 && serverlist_ == nullptr &&
-        !fallback_timer_callback_pending_) {
-      grpc_millis deadline = ExecCtx::Get()->Now() + lb_fallback_timeout_ms_;
-      Ref(DEBUG_LOCATION, "on_fallback_timer").release();  // Held by closure
-      GRPC_CLOSURE_INIT(&lb_on_fallback_, &XdsLb::OnFallbackTimerLocked, this,
-                        grpc_combiner_scheduler(combiner()));
-      fallback_timer_callback_pending_ = true;
-      grpc_timer_init(&lb_fallback_timer_, deadline, &lb_on_fallback_);
-      // TODO(juanlishen): Monitor the connectivity state of the balancer
-      // channel.  If the channel reports TRANSIENT_FAILURE before the
-      // fallback timeout expires, go into fallback mode early.
-    }
+    grpc_millis deadline = ExecCtx::Get()->Now() + lb_fallback_timeout_ms_;
+    Ref(DEBUG_LOCATION, "on_fallback_timer").release();  // Held by closure
+    GRPC_CLOSURE_INIT(&lb_on_fallback_, &XdsLb::OnFallbackTimerLocked, this,
+                      grpc_combiner_scheduler(combiner()));
+    fallback_at_startup_checks_pending_ = true;
+    grpc_timer_init(&lb_fallback_timer_, deadline, &lb_on_fallback_);
+    // Start watching the channel's connectivity state.  If the channel
+    // goes into state TRANSIENT_FAILURE, we go into fallback mode even if
+    // the fallback timeout has not elapsed.
+    lb_chand_->StartConnectivityWatchLocked();
   }
 }
 
 //
-// code for balancer channel and call
+// fallback-related methods
 //
 
+void XdsLb::MaybeCancelFallbackAtStartupChecks() {
+  if (!fallback_at_startup_checks_pending_) return;
+  gpr_log(GPR_INFO,
+          "[xdslb %p] Cancelling fallback timer and LB channel connectivity "
+          "watch",
+          this);
+  grpc_timer_cancel(&lb_fallback_timer_);
+  lb_chand_->CancelConnectivityWatchLocked();
+  fallback_at_startup_checks_pending_ = false;
+}
+
 void XdsLb::OnFallbackTimerLocked(void* arg, grpc_error* error) {
   XdsLb* xdslb_policy = static_cast<XdsLb*>(arg);
-  xdslb_policy->fallback_timer_callback_pending_ = false;
-  // If we receive a serverlist after the timer fires but before this callback
-  // actually runs, don't fall back.
-  if (xdslb_policy->serverlist_ == nullptr && !xdslb_policy->shutting_down_ &&
-      error == GRPC_ERROR_NONE) {
-    if (grpc_lb_xds_trace.enabled()) {
+  // If some fallback-at-startup check is done after the timer fires but before
+  // this callback actually runs, don't fall back.
+  if (xdslb_policy->fallback_at_startup_checks_pending_ &&
+      !xdslb_policy->shutting_down_ && error == GRPC_ERROR_NONE) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
       gpr_log(GPR_INFO,
-              "[xdslb %p] Fallback timer fired. Not using fallback backends",
+              "[xdslb %p] Child policy not ready after fallback timeout; "
+              "entering fallback mode",
               xdslb_policy);
     }
+    xdslb_policy->fallback_at_startup_checks_pending_ = false;
+    xdslb_policy->UpdateFallbackPolicyLocked();
+    xdslb_policy->lb_chand_->CancelConnectivityWatchLocked();
   }
   xdslb_policy->Unref(DEBUG_LOCATION, "on_fallback_timer");
 }
 
+void XdsLb::UpdateFallbackPolicyLocked() {
+  if (shutting_down_) return;
+  // Construct update args.
+  UpdateArgs update_args;
+  update_args.addresses = fallback_backend_addresses_;
+  update_args.config = fallback_policy_config_ == nullptr
+                           ? nullptr
+                           : fallback_policy_config_->Ref();
+  update_args.args = grpc_channel_args_copy(args_);
+  // If the child policy name changes, we need to create a new child
+  // policy.  When this happens, we leave child_policy_ as-is and store
+  // the new child policy in pending_child_policy_.  Once the new child
+  // policy transitions into state READY, we swap it into child_policy_,
+  // replacing the original child policy.  So pending_child_policy_ is
+  // non-null only between when we apply an update that changes the child
+  // policy name and when the new child reports state READY.
+  //
+  // Updates can arrive at any point during this transition.  We always
+  // apply updates relative to the most recently created child policy,
+  // even if the most recent one is still in pending_child_policy_.  This
+  // is true both when applying the updates to an existing child policy
+  // and when determining whether we need to create a new policy.
+  //
+  // As a result of this, there are several cases to consider here:
+  //
+  // 1. We have no existing child policy (i.e., we have started up but
+  //    have not yet received a serverlist from the balancer or gone
+  //    into fallback mode; in this case, both child_policy_ and
+  //    pending_child_policy_ are null).  In this case, we create a
+  //    new child policy and store it in child_policy_.
+  //
+  // 2. We have an existing child policy and have no pending child policy
+  //    from a previous update (i.e., either there has not been a
+  //    previous update that changed the policy name, or we have already
+  //    finished swapping in the new policy; in this case, child_policy_
+  //    is non-null but pending_child_policy_ is null).  In this case:
+  //    a. If child_policy_->name() equals child_policy_name, then we
+  //       update the existing child policy.
+  //    b. If child_policy_->name() does not equal child_policy_name,
+  //       we create a new policy.  The policy will be stored in
+  //       pending_child_policy_ and will later be swapped into
+  //       child_policy_ by the helper when the new child transitions
+  //       into state READY.
+  //
+  // 3. We have an existing child policy and have a pending child policy
+  //    from a previous update (i.e., a previous update set
+  //    pending_child_policy_ as per case 2b above and that policy has
+  //    not yet transitioned into state READY and been swapped into
+  //    child_policy_; in this case, both child_policy_ and
+  //    pending_child_policy_ are non-null).  In this case:
+  //    a. If pending_child_policy_->name() equals child_policy_name,
+  //       then we update the existing pending child policy.
+  //    b. If pending_child_policy->name() does not equal
+  //       child_policy_name, then we create a new policy.  The new
+  //       policy is stored in pending_child_policy_ (replacing the one
+  //       that was there before, which will be immediately shut down)
+  //       and will later be swapped into child_policy_ by the helper
+  //       when the new child transitions into state READY.
+  const char* fallback_policy_name = fallback_policy_config_ == nullptr
+                                         ? "round_robin"
+                                         : fallback_policy_config_->name();
+  const bool create_policy =
+      // case 1
+      fallback_policy_ == nullptr ||
+      // case 2b
+      (pending_fallback_policy_ == nullptr &&
+       strcmp(fallback_policy_->name(), fallback_policy_name) != 0) ||
+      // case 3b
+      (pending_fallback_policy_ != nullptr &&
+       strcmp(pending_fallback_policy_->name(), fallback_policy_name) != 0);
+  LoadBalancingPolicy* policy_to_update = nullptr;
+  if (create_policy) {
+    // Cases 1, 2b, and 3b: create a new child policy.
+    // If child_policy_ is null, we set it (case 1), else we set
+    // pending_child_policy_ (cases 2b and 3b).
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
+      gpr_log(GPR_INFO, "[xdslb %p] Creating new %sfallback policy %s", this,
+              fallback_policy_ == nullptr ? "" : "pending ",
+              fallback_policy_name);
+    }
+    auto new_policy =
+        CreateFallbackPolicyLocked(fallback_policy_name, update_args.args);
+    auto& lb_policy = fallback_policy_ == nullptr ? fallback_policy_
+                                                  : pending_fallback_policy_;
+    {
+      MutexLock lock(&fallback_policy_mu_);
+      lb_policy = std::move(new_policy);
+    }
+    policy_to_update = lb_policy.get();
+  } else {
+    // Cases 2a and 3a: update an existing policy.
+    // If we have a pending child policy, send the update to the pending
+    // policy (case 3a), else send it to the current policy (case 2a).
+    policy_to_update = pending_fallback_policy_ != nullptr
+                           ? pending_fallback_policy_.get()
+                           : fallback_policy_.get();
+  }
+  GPR_ASSERT(policy_to_update != nullptr);
+  // Update the policy.
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
+    gpr_log(
+        GPR_INFO, "[xdslb %p] Updating %sfallback policy %p", this,
+        policy_to_update == pending_fallback_policy_.get() ? "pending " : "",
+        policy_to_update);
+  }
+  policy_to_update->UpdateLocked(std::move(update_args));
+}
+
+OrphanablePtr<LoadBalancingPolicy> XdsLb::CreateFallbackPolicyLocked(
+    const char* name, const grpc_channel_args* args) {
+  FallbackHelper* helper = New<FallbackHelper>(Ref());
+  LoadBalancingPolicy::Args lb_policy_args;
+  lb_policy_args.combiner = combiner();
+  lb_policy_args.args = args;
+  lb_policy_args.channel_control_helper =
+      UniquePtr<ChannelControlHelper>(helper);
+  OrphanablePtr<LoadBalancingPolicy> lb_policy =
+      LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
+          name, std::move(lb_policy_args));
+  if (GPR_UNLIKELY(lb_policy == nullptr)) {
+    gpr_log(GPR_ERROR, "[xdslb %p] Failure creating fallback policy %s", this,
+            name);
+    return nullptr;
+  }
+  helper->set_child(lb_policy.get());
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
+    gpr_log(GPR_INFO, "[xdslb %p] Created new fallback policy %s (%p)", this,
+            name, lb_policy.get());
+  }
+  // Add the xDS's interested_parties pollset_set to that of the newly created
+  // child policy. This will make the child policy progress upon activity on xDS
+  // LB, which in turn is tied to the application's call.
+  grpc_pollset_set_add_pollset_set(lb_policy->interested_parties(),
+                                   interested_parties());
+  return lb_policy;
+}
+
+void XdsLb::MaybeExitFallbackMode() {
+  if (fallback_policy_ == nullptr) return;
+  gpr_log(GPR_INFO, "[xdslb %p] Exiting fallback mode", this);
+  fallback_policy_.reset();
+  pending_fallback_policy_.reset();
+}
+
+//
+// XdsLb::LocalityMap
+//
+
+void XdsLb::LocalityMap::PruneLocalities(const LocalityList& locality_list) {
+  for (auto iter = map_.begin(); iter != map_.end();) {
+    bool found = false;
+    for (size_t i = 0; i < locality_list.size(); i++) {
+      if (!gpr_stricmp(locality_list[i]->locality_name, iter->first.get())) {
+        found = true;
+      }
+    }
+    if (!found) {  // Remove entries not present in the locality list
+      MutexLock lock(&child_refs_mu_);
+      iter = map_.erase(iter);
+    } else
+      iter++;
+  }
+}
+
+void XdsLb::LocalityMap::UpdateLocked(
+    const LocalityList& locality_serverlist,
+    ParsedLoadBalancingConfig* child_policy_config,
+    const grpc_channel_args* args, XdsLb* parent) {
+  if (parent->shutting_down_) return;
+  for (size_t i = 0; i < locality_serverlist.size(); i++) {
+    UniquePtr<char> locality_name(
+        gpr_strdup(locality_serverlist[i]->locality_name));
+    auto iter = map_.find(locality_name);
+    if (iter == map_.end()) {
+      OrphanablePtr<LocalityEntry> new_entry = MakeOrphanable<LocalityEntry>(
+          parent->Ref(), locality_serverlist[i]->locality_weight);
+      MutexLock lock(&child_refs_mu_);
+      iter = map_.emplace(std::move(locality_name), std::move(new_entry)).first;
+    }
+    // Don't create new child policies if not directed to
+    xds_grpclb_serverlist* serverlist =
+        parent->locality_serverlist_[i]->serverlist;
+    iter->second->UpdateLocked(serverlist, child_policy_config, args);
+  }
+  PruneLocalities(locality_serverlist);
+}
+
+void XdsLb::LocalityMap::ShutdownLocked() {
+  MutexLock lock(&child_refs_mu_);
+  map_.clear();
+}
+
+void XdsLb::LocalityMap::ResetBackoffLocked() {
+  for (auto& p : map_) {
+    p.second->ResetBackoffLocked();
+  }
+}
+
+void XdsLb::LocalityMap::FillChildRefsForChannelz(
+    channelz::ChildRefsList* child_subchannels,
+    channelz::ChildRefsList* child_channels) {
+  MutexLock lock(&child_refs_mu_);
+  for (auto& p : map_) {
+    p.second->FillChildRefsForChannelz(child_subchannels, child_channels);
+  }
+}
+
 //
-// code for interacting with the child policy
+// XdsLb::LocalityMap::LocalityEntry
 //
 
-grpc_channel_args* XdsLb::CreateChildPolicyArgsLocked() {
+grpc_channel_args*
+XdsLb::LocalityMap::LocalityEntry::CreateChildPolicyArgsLocked(
+    const grpc_channel_args* args_in) {
   const grpc_arg args_to_add[] = {
       // A channel arg indicating if the target is a backend inferred from a
       // grpclb load balancer.
@@ -1368,15 +1807,16 @@ grpc_channel_args* XdsLb::CreateChildPolicyArgsLocked() {
       grpc_channel_arg_integer_create(
           const_cast<char*>(GRPC_ARG_INHIBIT_HEALTH_CHECKING), 1),
   };
-  return grpc_channel_args_copy_and_add(args_, args_to_add,
+  return grpc_channel_args_copy_and_add(args_in, args_to_add,
                                         GPR_ARRAY_SIZE(args_to_add));
 }
 
-OrphanablePtr<LoadBalancingPolicy> XdsLb::CreateChildPolicyLocked(
+OrphanablePtr<LoadBalancingPolicy>
+XdsLb::LocalityMap::LocalityEntry::CreateChildPolicyLocked(
     const char* name, const grpc_channel_args* args) {
-  Helper* helper = New<Helper>(Ref());
+  Helper* helper = New<Helper>(this->Ref());
   LoadBalancingPolicy::Args lb_policy_args;
-  lb_policy_args.combiner = combiner();
+  lb_policy_args.combiner = parent_->combiner();
   lb_policy_args.args = args;
   lb_policy_args.channel_control_helper =
       UniquePtr<ChannelControlHelper>(helper);
@@ -1389,7 +1829,7 @@ OrphanablePtr<LoadBalancingPolicy> XdsLb::CreateChildPolicyLocked(
     return nullptr;
   }
   helper->set_child(lb_policy.get());
-  if (grpc_lb_xds_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
     gpr_log(GPR_INFO, "[xdslb %p] Created new child policy %s (%p)", this, name,
             lb_policy.get());
   }
@@ -1397,22 +1837,21 @@ OrphanablePtr<LoadBalancingPolicy> XdsLb::CreateChildPolicyLocked(
   // child policy. This will make the child policy progress upon activity on xDS
   // LB, which in turn is tied to the application's call.
   grpc_pollset_set_add_pollset_set(lb_policy->interested_parties(),
-                                   interested_parties());
+                                   parent_->interested_parties());
   return lb_policy;
 }
 
-void XdsLb::CreateOrUpdateChildPolicyLocked() {
-  if (shutting_down_) return;
-  // This should never be invoked if we do not have serverlist_, as fallback
-  // mode is disabled for xDS plugin.
-  // TODO(juanlishen): Change this as part of implementing fallback mode.
-  GPR_ASSERT(serverlist_ != nullptr);
-  GPR_ASSERT(serverlist_->num_servers > 0);
+void XdsLb::LocalityMap::LocalityEntry::UpdateLocked(
+    xds_grpclb_serverlist* serverlist,
+    ParsedLoadBalancingConfig* child_policy_config,
+    const grpc_channel_args* args_in) {
+  if (parent_->shutting_down_) return;
   // Construct update args.
   UpdateArgs update_args;
-  update_args.addresses = ProcessServerlist(serverlist_);
-  update_args.config = child_policy_config_;
-  update_args.args = CreateChildPolicyArgsLocked();
+  update_args.addresses = ProcessServerlist(serverlist);
+  update_args.config =
+      child_policy_config == nullptr ? nullptr : child_policy_config->Ref();
+  update_args.args = CreateChildPolicyArgsLocked(args_in);
   // If the child policy name changes, we need to create a new child
   // policy.  When this happens, we leave child_policy_ as-is and store
   // the new child policy in pending_child_policy_.  Once the new child
@@ -1464,9 +1903,9 @@ void XdsLb::CreateOrUpdateChildPolicyLocked() {
   //       when the new child transitions into state READY.
   // TODO(juanlishen): If the child policy is not configured via service config,
   // use whatever algorithm is specified by the balancer.
-  const char* child_policy_name = child_policy_config_ == nullptr
+  const char* child_policy_name = child_policy_config == nullptr
                                       ? "round_robin"
-                                      : child_policy_config_->name();
+                                      : child_policy_config->name();
   const bool create_policy =
       // case 1
       child_policy_ == nullptr ||
@@ -1481,7 +1920,7 @@ void XdsLb::CreateOrUpdateChildPolicyLocked() {
     // Cases 1, 2b, and 3b: create a new child policy.
     // If child_policy_ is null, we set it (case 1), else we set
     // pending_child_policy_ (cases 2b and 3b).
-    if (grpc_lb_xds_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
       gpr_log(GPR_INFO, "[xdslb %p] Creating new %schild policy %s", this,
               child_policy_ == nullptr ? "" : "pending ", child_policy_name);
     }
@@ -1504,7 +1943,7 @@ void XdsLb::CreateOrUpdateChildPolicyLocked() {
   }
   GPR_ASSERT(policy_to_update != nullptr);
   // Update the policy.
-  if (grpc_lb_xds_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
     gpr_log(GPR_INFO, "[xdslb %p] Updating %schild policy %p", this,
             policy_to_update == pending_child_policy_.get() ? "pending " : "",
             policy_to_update);
@@ -1512,6 +1951,201 @@ void XdsLb::CreateOrUpdateChildPolicyLocked() {
   policy_to_update->UpdateLocked(std::move(update_args));
 }
 
+void XdsLb::LocalityMap::LocalityEntry::ShutdownLocked() {
+  // Remove the child policy's interested_parties pollset_set from the
+  // xDS policy.
+  grpc_pollset_set_del_pollset_set(child_policy_->interested_parties(),
+                                   parent_->interested_parties());
+  if (pending_child_policy_ != nullptr) {
+    grpc_pollset_set_del_pollset_set(
+        pending_child_policy_->interested_parties(),
+        parent_->interested_parties());
+  }
+  {
+    MutexLock lock(&child_policy_mu_);
+    child_policy_.reset();
+    pending_child_policy_.reset();
+  }
+}
+
+void XdsLb::LocalityMap::LocalityEntry::ResetBackoffLocked() {
+  child_policy_->ResetBackoffLocked();
+  if (pending_child_policy_ != nullptr) {
+    pending_child_policy_->ResetBackoffLocked();
+  }
+}
+
+void XdsLb::LocalityMap::LocalityEntry::FillChildRefsForChannelz(
+    channelz::ChildRefsList* child_subchannels,
+    channelz::ChildRefsList* child_channels) {
+  MutexLock lock(&child_policy_mu_);
+  child_policy_->FillChildRefsForChannelz(child_subchannels, child_channels);
+  if (pending_child_policy_ != nullptr) {
+    pending_child_policy_->FillChildRefsForChannelz(child_subchannels,
+                                                    child_channels);
+  }
+}
+
+void XdsLb::LocalityMap::LocalityEntry::Orphan() {
+  ShutdownLocked();
+  Unref();
+}
+
+//
+// XdsLb::LocalityEntry::Helper
+//
+
+bool XdsLb::LocalityMap::LocalityEntry::Helper::CalledByPendingChild() const {
+  GPR_ASSERT(child_ != nullptr);
+  return child_ == entry_->pending_child_policy_.get();
+}
+
+bool XdsLb::LocalityMap::LocalityEntry::Helper::CalledByCurrentChild() const {
+  GPR_ASSERT(child_ != nullptr);
+  return child_ == entry_->child_policy_.get();
+}
+
+Subchannel* XdsLb::LocalityMap::LocalityEntry::Helper::CreateSubchannel(
+    const grpc_channel_args& args) {
+  if (entry_->parent_->shutting_down_ ||
+      (!CalledByPendingChild() && !CalledByCurrentChild())) {
+    return nullptr;
+  }
+  return entry_->parent_->channel_control_helper()->CreateSubchannel(args);
+}
+
+grpc_channel* XdsLb::LocalityMap::LocalityEntry::Helper::CreateChannel(
+    const char* target, const grpc_channel_args& args) {
+  if (entry_->parent_->shutting_down_ ||
+      (!CalledByPendingChild() && !CalledByCurrentChild())) {
+    return nullptr;
+  }
+  return entry_->parent_->channel_control_helper()->CreateChannel(target, args);
+}
+
+void XdsLb::LocalityMap::LocalityEntry::Helper::UpdateState(
+    grpc_connectivity_state state, UniquePtr<SubchannelPicker> picker) {
+  if (entry_->parent_->shutting_down_) return;
+  // If this request is from the pending child policy, ignore it until
+  // it reports READY, at which point we swap it into place.
+  if (CalledByPendingChild()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
+      gpr_log(GPR_INFO,
+              "[xdslb %p helper %p] pending child policy %p reports state=%s",
+              entry_->parent_.get(), this, entry_->pending_child_policy_.get(),
+              grpc_connectivity_state_name(state));
+    }
+    if (state != GRPC_CHANNEL_READY) return;
+    grpc_pollset_set_del_pollset_set(
+        entry_->child_policy_->interested_parties(),
+        entry_->parent_->interested_parties());
+    MutexLock lock(&entry_->child_policy_mu_);
+    entry_->child_policy_ = std::move(entry_->pending_child_policy_);
+  } else if (!CalledByCurrentChild()) {
+    // This request is from an outdated child, so ignore it.
+    return;
+  }
+  // At this point, child_ must be the current child policy.
+  if (state == GRPC_CHANNEL_READY) entry_->parent_->MaybeExitFallbackMode();
+  // If we are in fallback mode, ignore update request from the child policy.
+  if (entry_->parent_->fallback_policy_ != nullptr) return;
+  GPR_ASSERT(entry_->parent_->lb_chand_ != nullptr);
+  RefCountedPtr<XdsLbClientStats> client_stats =
+      entry_->parent_->lb_chand_->lb_calld() == nullptr
+          ? nullptr
+          : entry_->parent_->lb_chand_->lb_calld()->client_stats();
+  // Cache the picker and its state in the entry
+  entry_->picker_ref_ = MakeRefCounted<PickerRef>(std::move(picker));
+  entry_->connectivity_state_ = state;
+  // Construct a new xds picker which maintains a map of all locality pickers
+  // that are ready. Each locality is represented by a portion of the range
+  // proportional to its weight, such that the total range is the sum of the
+  // weights of all localities
+  uint32_t end = 0;
+  size_t num_connecting = 0;
+  size_t num_idle = 0;
+  size_t num_transient_failures = 0;
+  auto& locality_map = this->entry_->parent_->locality_map_.map_;
+  Picker::PickerList pickers;
+  for (auto& p : locality_map) {
+    const LocalityEntry* entry = p.second.get();
+    grpc_connectivity_state connectivity_state = entry->connectivity_state_;
+    switch (connectivity_state) {
+      case GRPC_CHANNEL_READY: {
+        end += entry->locality_weight_;
+        pickers.push_back(MakePair(end, entry->picker_ref_));
+        break;
+      }
+      case GRPC_CHANNEL_CONNECTING: {
+        num_connecting++;
+        break;
+      }
+      case GRPC_CHANNEL_IDLE: {
+        num_idle++;
+        break;
+      }
+      case GRPC_CHANNEL_TRANSIENT_FAILURE: {
+        num_transient_failures++;
+        break;
+      }
+      default: {
+        gpr_log(GPR_ERROR, "Invalid locality connectivity state - %d",
+                connectivity_state);
+      }
+    }
+  }
+  // Pass on the constructed xds picker if it has any ready pickers in their map
+  // otherwise pass a QueuePicker if any of the locality pickers are in a
+  // connecting or idle state, finally return a transient failure picker if all
+  // locality pickers are in transient failure
+  if (pickers.size() > 0) {
+    entry_->parent_->channel_control_helper()->UpdateState(
+        GRPC_CHANNEL_READY,
+        UniquePtr<LoadBalancingPolicy::SubchannelPicker>(
+            New<Picker>(std::move(client_stats), std::move(pickers))));
+  } else if (num_connecting > 0) {
+    entry_->parent_->channel_control_helper()->UpdateState(
+        GRPC_CHANNEL_CONNECTING,
+        UniquePtr<SubchannelPicker>(New<QueuePicker>(this->entry_->parent_)));
+  } else if (num_idle > 0) {
+    entry_->parent_->channel_control_helper()->UpdateState(
+        GRPC_CHANNEL_IDLE,
+        UniquePtr<SubchannelPicker>(New<QueuePicker>(this->entry_->parent_)));
+  } else {
+    GPR_ASSERT(num_transient_failures == locality_map.size());
+    grpc_error* error =
+        grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                               "connections to all localities failing"),
+                           GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE);
+    entry_->parent_->channel_control_helper()->UpdateState(
+        state, UniquePtr<SubchannelPicker>(New<TransientFailurePicker>(error)));
+  }
+}
+
+void XdsLb::LocalityMap::LocalityEntry::Helper::RequestReresolution() {
+  if (entry_->parent_->shutting_down_) return;
+  // If there is a pending child policy, ignore re-resolution requests
+  // from the current child policy (or any outdated child).
+  if (entry_->pending_child_policy_ != nullptr && !CalledByPendingChild()) {
+    return;
+  }
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
+    gpr_log(GPR_INFO,
+            "[xdslb %p] Re-resolution requested from the internal RR policy "
+            "(%p).",
+            entry_->parent_.get(), entry_->child_policy_.get());
+  }
+  GPR_ASSERT(entry_->parent_->lb_chand_ != nullptr);
+  // If we are talking to a balancer, we expect to get updated addresses
+  // from the balancer, so we can ignore the re-resolution request from
+  // the child policy. Otherwise, pass the re-resolution request up to the
+  // channel.
+  if (entry_->parent_->lb_chand_->lb_calld() == nullptr ||
+      !entry_->parent_->lb_chand_->lb_calld()->seen_initial_response()) {
+    entry_->parent_->channel_control_helper()->RequestReresolution();
+  }
+}
+
 //
 // factory
 //
@@ -1524,6 +2158,77 @@ class XdsFactory : public LoadBalancingPolicyFactory {
   }
 
   const char* name() const override { return kXds; }
+
+  RefCountedPtr<ParsedLoadBalancingConfig> ParseLoadBalancingConfig(
+      const grpc_json* json, grpc_error** error) const override {
+    GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
+    if (json == nullptr) {
+      // xds was mentioned as a policy in the deprecated loadBalancingPolicy
+      // field or in the client API.
+      *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "field:loadBalancingPolicy error:Xds Parser has required field - "
+          "balancerName. Please use loadBalancingConfig field of service "
+          "config instead.");
+      return nullptr;
+    }
+    GPR_DEBUG_ASSERT(strcmp(json->key, name()) == 0);
+
+    InlinedVector<grpc_error*, 3> error_list;
+    const char* balancer_name = nullptr;
+    RefCountedPtr<ParsedLoadBalancingConfig> child_policy;
+    RefCountedPtr<ParsedLoadBalancingConfig> fallback_policy;
+    for (const grpc_json* field = json->child; field != nullptr;
+         field = field->next) {
+      if (field->key == nullptr) continue;
+      if (strcmp(field->key, "balancerName") == 0) {
+        if (balancer_name != nullptr) {
+          error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "field:balancerName error:Duplicate entry"));
+        }
+        if (field->type != GRPC_JSON_STRING) {
+          error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "field:balancerName error:type should be string"));
+          continue;
+        }
+        balancer_name = field->value;
+      } else if (strcmp(field->key, "childPolicy") == 0) {
+        if (child_policy != nullptr) {
+          error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "field:childPolicy error:Duplicate entry"));
+        }
+        grpc_error* parse_error = GRPC_ERROR_NONE;
+        child_policy = LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(
+            field, &parse_error);
+        if (child_policy == nullptr) {
+          GPR_DEBUG_ASSERT(parse_error != GRPC_ERROR_NONE);
+          error_list.push_back(parse_error);
+        }
+      } else if (strcmp(field->key, "fallbackPolicy") == 0) {
+        if (fallback_policy != nullptr) {
+          error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "field:fallbackPolicy error:Duplicate entry"));
+        }
+        grpc_error* parse_error = GRPC_ERROR_NONE;
+        fallback_policy = LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(
+            field, &parse_error);
+        if (fallback_policy == nullptr) {
+          GPR_DEBUG_ASSERT(parse_error != GRPC_ERROR_NONE);
+          error_list.push_back(parse_error);
+        }
+      }
+    }
+    if (balancer_name == nullptr) {
+      error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "field:balancerName error:not found"));
+    }
+    if (error_list.empty()) {
+      return RefCountedPtr<ParsedLoadBalancingConfig>(New<ParsedXdsConfig>(
+          balancer_name, std::move(child_policy), std::move(fallback_policy)));
+    } else {
+      *error = GRPC_ERROR_CREATE_FROM_VECTOR("Xds Parser", &error_list);
+      return nullptr;
+    }
+  }
 };
 
 }  // namespace
index 1da4b7c..aaf3e95 100644 (file)
@@ -37,9 +37,12 @@ class LoadBalancingPolicyFactory {
   /// Caller does NOT take ownership of result.
   virtual const char* name() const GRPC_ABSTRACT;
 
+  virtual RefCountedPtr<ParsedLoadBalancingConfig> ParseLoadBalancingConfig(
+      const grpc_json* json, grpc_error** error) const GRPC_ABSTRACT;
+
   virtual ~LoadBalancingPolicyFactory() {}
 
-  GRPC_ABSTRACT_BASE_CLASS
+  GRPC_ABSTRACT_BASE_CLASS;
 };
 
 }  // namespace grpc_core
index 99980d5..973aa26 100644 (file)
@@ -94,9 +94,112 @@ LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
   return factory->CreateLoadBalancingPolicy(std::move(args));
 }
 
-bool LoadBalancingPolicyRegistry::LoadBalancingPolicyExists(const char* name) {
+bool LoadBalancingPolicyRegistry::LoadBalancingPolicyExists(
+    const char* name, bool* requires_config) {
   GPR_ASSERT(g_state != nullptr);
-  return g_state->GetLoadBalancingPolicyFactory(name) != nullptr;
+  auto* factory = g_state->GetLoadBalancingPolicyFactory(name);
+  if (factory == nullptr) {
+    return false;
+  }
+  if (requires_config != nullptr) {
+    grpc_error* error = GRPC_ERROR_NONE;
+    // Check if the load balancing policy allows an empty config
+    *requires_config =
+        factory->ParseLoadBalancingConfig(nullptr, &error) == nullptr;
+    GRPC_ERROR_UNREF(error);
+  }
+  return true;
+}
+
+namespace {
+// Returns the JSON node of policy (with both policy name and config content)
+// given the JSON node of a LoadBalancingConfig array.
+grpc_json* ParseLoadBalancingConfigHelper(const grpc_json* lb_config_array,
+                                          grpc_error** error) {
+  GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
+  char* error_msg;
+  if (lb_config_array == nullptr || lb_config_array->type != GRPC_JSON_ARRAY) {
+    gpr_asprintf(&error_msg, "field:%s error:type should be array",
+                 lb_config_array->key);
+    *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
+    gpr_free(error_msg);
+    return nullptr;
+  }
+  const char* field_name = lb_config_array->key;
+  // Find the first LB policy that this client supports.
+  for (const grpc_json* lb_config = lb_config_array->child;
+       lb_config != nullptr; lb_config = lb_config->next) {
+    if (lb_config->type != GRPC_JSON_OBJECT) {
+      gpr_asprintf(&error_msg,
+                   "field:%s error:child entry should be of type object",
+                   field_name);
+      *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
+      gpr_free(error_msg);
+      return nullptr;
+    }
+    grpc_json* policy = nullptr;
+    for (grpc_json* field = lb_config->child; field != nullptr;
+         field = field->next) {
+      if (field->key == nullptr || field->type != GRPC_JSON_OBJECT) {
+        gpr_asprintf(&error_msg,
+                     "field:%s error:child entry should be of type object",
+                     field_name);
+        *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
+        gpr_free(error_msg);
+        return nullptr;
+      }
+      if (policy != nullptr) {
+        gpr_asprintf(&error_msg, "field:%s error:oneOf violation", field_name);
+        *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
+        gpr_free(error_msg);
+        return nullptr;
+      }  // Violate "oneof" type.
+      policy = field;
+    }
+    if (policy == nullptr) {
+      gpr_asprintf(&error_msg, "field:%s error:no policy found in child entry",
+                   field_name);
+      *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
+      gpr_free(error_msg);
+      return nullptr;
+    }
+    // If we support this policy, then select it.
+    if (LoadBalancingPolicyRegistry::LoadBalancingPolicyExists(policy->key,
+                                                               nullptr)) {
+      return policy;
+    }
+  }
+  gpr_asprintf(&error_msg, "field:%s error:No known policy", field_name);
+  *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
+  gpr_free(error_msg);
+  return nullptr;
+}
+}  // namespace
+
+RefCountedPtr<ParsedLoadBalancingConfig>
+LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(const grpc_json* json,
+                                                      grpc_error** error) {
+  GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
+  GPR_ASSERT(g_state != nullptr);
+  const grpc_json* policy = ParseLoadBalancingConfigHelper(json, error);
+  if (policy == nullptr) {
+    return nullptr;
+  } else {
+    GPR_DEBUG_ASSERT(*error == GRPC_ERROR_NONE && json != nullptr);
+    // Find factory.
+    LoadBalancingPolicyFactory* factory =
+        g_state->GetLoadBalancingPolicyFactory(policy->key);
+    if (factory == nullptr) {
+      char* msg;
+      gpr_asprintf(&msg, "field:%s error:Factory not found to create policy",
+                   json->key);
+      *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+      gpr_free(msg);
+      return nullptr;
+    }
+    // Parse load balancing config via factory.
+    return factory->ParseLoadBalancingConfig(policy, error);
+  }
 }
 
 }  // namespace grpc_core
index 7472ba9..6820cfc 100644 (file)
@@ -49,8 +49,15 @@ class LoadBalancingPolicyRegistry {
       const char* name, LoadBalancingPolicy::Args args);
 
   /// Returns true if the LB policy factory specified by \a name exists in this
-  /// registry.
-  static bool LoadBalancingPolicyExists(const char* name);
+  /// registry. If the load balancing policy requires a config to be specified
+  /// then sets \a requires_config to true.
+  static bool LoadBalancingPolicyExists(const char* name,
+                                        bool* requires_config);
+
+  /// Returns a parsed object of the load balancing policy to be used from a
+  /// LoadBalancingConfig array \a json.
+  static RefCountedPtr<ParsedLoadBalancingConfig> ParseLoadBalancingConfig(
+      const grpc_json* json, grpc_error** error);
 };
 
 }  // namespace grpc_core
index 13fde4a..0035873 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <grpc/support/port_platform.h>
 
-#if GRPC_ARES == 1 && !defined(GRPC_UV)
+#if GRPC_ARES == 1
 
 #include <limits.h>
 #include <stdio.h>
 #include "src/core/ext/filters/client_channel/http_connect_handshaker.h"
 #include "src/core/ext/filters/client_channel/lb_policy_registry.h"
 #include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
+#include "src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h"
 #include "src/core/ext/filters/client_channel/resolver_registry.h"
 #include "src/core/ext/filters/client_channel/server_address.h"
 #include "src/core/ext/filters/client_channel/service_config.h"
 #include "src/core/lib/backoff/backoff.h"
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gprpp/manual_constructor.h"
@@ -227,65 +227,94 @@ bool ValueInJsonArray(grpc_json* array, const char* value) {
   return false;
 }
 
-char* ChooseServiceConfig(char* service_config_choice_json) {
+char* ChooseServiceConfig(char* service_config_choice_json,
+                          grpc_error** error) {
   grpc_json* choices_json = grpc_json_parse_string(service_config_choice_json);
-  if (choices_json == nullptr || choices_json->type != GRPC_JSON_ARRAY) {
-    gpr_log(GPR_ERROR, "cannot parse service config JSON string");
+  if (choices_json == nullptr) {
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Service Config JSON Parsing, error: could not parse");
+    return nullptr;
+  }
+  if (choices_json->type != GRPC_JSON_ARRAY) {
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Service Config Choices, error: should be of type array");
     return nullptr;
   }
   char* service_config = nullptr;
+  InlinedVector<grpc_error*, 4> error_list;
+  bool found_choice = false;  // have we found a choice?
   for (grpc_json* choice = choices_json->child; choice != nullptr;
        choice = choice->next) {
     if (choice->type != GRPC_JSON_OBJECT) {
-      gpr_log(GPR_ERROR, "cannot parse service config JSON string");
-      break;
+      error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "Service Config Choice, error: should be of type object"));
+      continue;
     }
     grpc_json* service_config_json = nullptr;
+    bool selected = true;  // has this choice been rejected?
     for (grpc_json* field = choice->child; field != nullptr;
          field = field->next) {
       // Check client language, if specified.
       if (strcmp(field->key, "clientLanguage") == 0) {
-        if (field->type != GRPC_JSON_ARRAY || !ValueInJsonArray(field, "c++")) {
-          service_config_json = nullptr;
-          break;
+        if (field->type != GRPC_JSON_ARRAY) {
+          error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "field:clientLanguage error:should be of type array"));
+        } else if (!ValueInJsonArray(field, "c++")) {
+          selected = false;
         }
       }
       // Check client hostname, if specified.
       if (strcmp(field->key, "clientHostname") == 0) {
+        if (field->type != GRPC_JSON_ARRAY) {
+          error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "field:clientHostname error:should be of type array"));
+          continue;
+        }
         char* hostname = grpc_gethostname();
-        if (hostname == nullptr || field->type != GRPC_JSON_ARRAY ||
-            !ValueInJsonArray(field, hostname)) {
-          service_config_json = nullptr;
-          break;
+        if (hostname == nullptr || !ValueInJsonArray(field, hostname)) {
+          selected = false;
         }
       }
       // Check percentage, if specified.
       if (strcmp(field->key, "percentage") == 0) {
         if (field->type != GRPC_JSON_NUMBER) {
-          service_config_json = nullptr;
-          break;
+          error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "field:percentage error:should be of type number"));
+          continue;
         }
         int random_pct = rand() % 100;
         int percentage;
-        if (sscanf(field->value, "%d", &percentage) != 1 ||
-            random_pct > percentage || percentage == 0) {
-          service_config_json = nullptr;
-          break;
+        if (sscanf(field->value, "%d", &percentage) != 1) {
+          error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "field:percentage error:should be of type integer"));
+          continue;
+        }
+        if (random_pct > percentage || percentage == 0) {
+          selected = false;
         }
       }
       // Save service config.
       if (strcmp(field->key, "serviceConfig") == 0) {
         if (field->type == GRPC_JSON_OBJECT) {
           service_config_json = field;
+        } else {
+          error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "field:serviceConfig error:should be of type object"));
         }
       }
     }
-    if (service_config_json != nullptr) {
+    if (!found_choice && selected && service_config_json != nullptr) {
       service_config = grpc_json_dump_to_string(service_config_json, 0);
-      break;
+      found_choice = true;
     }
   }
   grpc_json_destroy(choices_json);
+  if (!error_list.empty()) {
+    gpr_free(service_config);
+    service_config = nullptr;
+    *error = GRPC_ERROR_CREATE_FROM_VECTOR("Service Config Choices Parser",
+                                           &error_list);
+  }
   return service_config;
 }
 
@@ -303,13 +332,15 @@ void AresDnsResolver::OnResolvedLocked(void* arg, grpc_error* error) {
     Result result;
     result.addresses = std::move(*r->addresses_);
     if (r->service_config_json_ != nullptr) {
-      char* service_config_string =
-          ChooseServiceConfig(r->service_config_json_);
+      char* service_config_string = ChooseServiceConfig(
+          r->service_config_json_, &result.service_config_error);
       gpr_free(r->service_config_json_);
-      if (service_config_string != nullptr) {
+      if (result.service_config_error == GRPC_ERROR_NONE &&
+          service_config_string != nullptr) {
         GRPC_CARES_TRACE_LOG("resolver:%p selected service config choice: %s",
                              r, service_config_string);
-        result.service_config = ServiceConfig::Create(service_config_string);
+        result.service_config = ServiceConfig::Create(
+            service_config_string, &result.service_config_error);
       }
       gpr_free(service_config_string);
     }
@@ -426,6 +457,13 @@ static grpc_error* blocking_resolve_address_ares(
 static grpc_address_resolver_vtable ares_resolver = {
     grpc_resolve_address_ares, blocking_resolve_address_ares};
 
+#ifdef GRPC_UV
+/* TODO(murgatroid99): Remove this when we want the cares resolver to be the
+ * default when using libuv */
+static bool should_use_ares(const char* resolver_env) {
+  return resolver_env != nullptr && gpr_stricmp(resolver_env, "ares") == 0;
+}
+#else  /* GRPC_UV */
 static bool should_use_ares(const char* resolver_env) {
   // TODO(lidiz): Remove the "g_custom_iomgr_enabled" flag once c-ares support
   // custom IO managers (e.g. gevent).
@@ -433,10 +471,12 @@ static bool should_use_ares(const char* resolver_env) {
          (resolver_env == nullptr || strlen(resolver_env) == 0 ||
           gpr_stricmp(resolver_env, "ares") == 0);
 }
+#endif /* GRPC_UV */
 
 void grpc_resolver_dns_ares_init() {
-  char* resolver_env = gpr_getenv("GRPC_DNS_RESOLVER");
-  if (should_use_ares(resolver_env)) {
+  grpc_core::UniquePtr<char> resolver =
+      GPR_GLOBAL_CONFIG_GET(grpc_dns_resolver);
+  if (should_use_ares(resolver.get())) {
     gpr_log(GPR_DEBUG, "Using ares dns resolver");
     address_sorting_init();
     grpc_error* error = grpc_ares_init();
@@ -452,22 +492,21 @@ void grpc_resolver_dns_ares_init() {
         grpc_core::UniquePtr<grpc_core::ResolverFactory>(
             grpc_core::New<grpc_core::AresDnsResolverFactory>()));
   }
-  gpr_free(resolver_env);
 }
 
 void grpc_resolver_dns_ares_shutdown() {
-  char* resolver_env = gpr_getenv("GRPC_DNS_RESOLVER");
-  if (should_use_ares(resolver_env)) {
+  grpc_core::UniquePtr<char> resolver =
+      GPR_GLOBAL_CONFIG_GET(grpc_dns_resolver);
+  if (should_use_ares(resolver.get())) {
     address_sorting_shutdown();
     grpc_ares_cleanup();
   }
-  gpr_free(resolver_env);
 }
 
-#else /* GRPC_ARES == 1 && !defined(GRPC_UV) */
+#else /* GRPC_ARES == 1 */
 
 void grpc_resolver_dns_ares_init(void) {}
 
 void grpc_resolver_dns_ares_shutdown(void) {}
 
-#endif /* GRPC_ARES == 1 && !defined(GRPC_UV) */
+#endif /* GRPC_ARES == 1 */
index d99c2e3..4ad8078 100644 (file)
@@ -18,7 +18,7 @@
 #include <grpc/support/port_platform.h>
 
 #include "src/core/lib/iomgr/port.h"
-#if GRPC_ARES == 1 && !defined(GRPC_UV)
+#if GRPC_ARES == 1
 
 #include <ares.h>
 #include <string.h>
@@ -84,6 +84,10 @@ struct grpc_ares_ev_driver {
   grpc_timer query_timeout;
   /** cancels queries on a timeout */
   grpc_closure on_timeout_locked;
+  /** alarm to poll ares_process on in case fd events don't happen */
+  grpc_timer ares_backup_poll_alarm;
+  /** polls ares_process on a periodic timer */
+  grpc_closure on_ares_backup_poll_alarm_locked;
 };
 
 static void grpc_ares_notify_on_event_locked(grpc_ares_ev_driver* ev_driver);
@@ -130,6 +134,13 @@ static void fd_node_shutdown_locked(fd_node* fdn, const char* reason) {
 
 static void on_timeout_locked(void* arg, grpc_error* error);
 
+static void on_ares_backup_poll_alarm_locked(void* arg, grpc_error* error);
+
+static void noop_inject_channel_config(ares_channel channel) {}
+
+void (*grpc_ares_test_only_inject_config)(ares_channel channel) =
+    noop_inject_channel_config;
+
 grpc_error* grpc_ares_ev_driver_create_locked(grpc_ares_ev_driver** ev_driver,
                                               grpc_pollset_set* pollset_set,
                                               int query_timeout_ms,
@@ -140,6 +151,7 @@ grpc_error* grpc_ares_ev_driver_create_locked(grpc_ares_ev_driver** ev_driver,
   memset(&opts, 0, sizeof(opts));
   opts.flags |= ARES_FLAG_STAYOPEN;
   int status = ares_init_options(&(*ev_driver)->channel, &opts, ARES_OPT_FLAGS);
+  grpc_ares_test_only_inject_config((*ev_driver)->channel);
   GRPC_CARES_TRACE_LOG("request:%p grpc_ares_ev_driver_create_locked", request);
   if (status != ARES_SUCCESS) {
     char* err_msg;
@@ -163,6 +175,9 @@ grpc_error* grpc_ares_ev_driver_create_locked(grpc_ares_ev_driver** ev_driver,
       ->polled_fd_factory->ConfigureAresChannelLocked((*ev_driver)->channel);
   GRPC_CLOSURE_INIT(&(*ev_driver)->on_timeout_locked, on_timeout_locked,
                     *ev_driver, grpc_combiner_scheduler(combiner));
+  GRPC_CLOSURE_INIT(&(*ev_driver)->on_ares_backup_poll_alarm_locked,
+                    on_ares_backup_poll_alarm_locked, *ev_driver,
+                    grpc_combiner_scheduler(combiner));
   (*ev_driver)->query_timeout_ms = query_timeout_ms;
   return GRPC_ERROR_NONE;
 }
@@ -174,6 +189,7 @@ void grpc_ares_ev_driver_on_queries_complete_locked(
   // fds; if it's not working, there are no fds to shut down.
   ev_driver->shutting_down = true;
   grpc_timer_cancel(&ev_driver->query_timeout);
+  grpc_timer_cancel(&ev_driver->ares_backup_poll_alarm);
   grpc_ares_ev_driver_unref(ev_driver);
 }
 
@@ -204,6 +220,21 @@ static fd_node* pop_fd_node_locked(fd_node** head, ares_socket_t as) {
   return nullptr;
 }
 
+static grpc_millis calculate_next_ares_backup_poll_alarm_ms(
+    grpc_ares_ev_driver* driver) {
+  // An alternative here could be to use ares_timeout to try to be more
+  // accurate, but that would require using "struct timeval"'s, which just makes
+  // things a bit more complicated. So just poll every second, as suggested
+  // by the c-ares code comments.
+  grpc_millis ms_until_next_ares_backup_poll_alarm = 1000;
+  GRPC_CARES_TRACE_LOG(
+      "request:%p ev_driver=%p. next ares process poll time in "
+      "%" PRId64 " ms",
+      driver->request, driver, ms_until_next_ares_backup_poll_alarm);
+  return ms_until_next_ares_backup_poll_alarm +
+         grpc_core::ExecCtx::Get()->Now();
+}
+
 static void on_timeout_locked(void* arg, grpc_error* error) {
   grpc_ares_ev_driver* driver = static_cast<grpc_ares_ev_driver*>(arg);
   GRPC_CARES_TRACE_LOG(
@@ -216,8 +247,50 @@ static void on_timeout_locked(void* arg, grpc_error* error) {
   grpc_ares_ev_driver_unref(driver);
 }
 
+/* In case of non-responsive DNS servers, dropped packets, etc., c-ares has
+ * intelligent timeout and retry logic, which we can take advantage of by
+ * polling ares_process_fd on time intervals. Overall, the c-ares library is
+ * meant to be called into and given a chance to proceed name resolution:
+ *   a) when fd events happen
+ *   b) when some time has passed without fd events having happened
+ * For the latter, we use this backup poller. Also see
+ * https://github.com/grpc/grpc/pull/17688 description for more details. */
+static void on_ares_backup_poll_alarm_locked(void* arg, grpc_error* error) {
+  grpc_ares_ev_driver* driver = static_cast<grpc_ares_ev_driver*>(arg);
+  GRPC_CARES_TRACE_LOG(
+      "request:%p ev_driver=%p on_ares_backup_poll_alarm_locked. "
+      "driver->shutting_down=%d. "
+      "err=%s",
+      driver->request, driver, driver->shutting_down, grpc_error_string(error));
+  if (!driver->shutting_down && error == GRPC_ERROR_NONE) {
+    fd_node* fdn = driver->fds;
+    while (fdn != nullptr) {
+      if (!fdn->already_shutdown) {
+        GRPC_CARES_TRACE_LOG(
+            "request:%p ev_driver=%p on_ares_backup_poll_alarm_locked; "
+            "ares_process_fd. fd=%s",
+            driver->request, driver, fdn->grpc_polled_fd->GetName());
+        ares_socket_t as = fdn->grpc_polled_fd->GetWrappedAresSocketLocked();
+        ares_process_fd(driver->channel, as, as);
+      }
+      fdn = fdn->next;
+    }
+    if (!driver->shutting_down) {
+      grpc_millis next_ares_backup_poll_alarm =
+          calculate_next_ares_backup_poll_alarm_ms(driver);
+      grpc_ares_ev_driver_ref(driver);
+      grpc_timer_init(&driver->ares_backup_poll_alarm,
+                      next_ares_backup_poll_alarm,
+                      &driver->on_ares_backup_poll_alarm_locked);
+    }
+    grpc_ares_notify_on_event_locked(driver);
+  }
+  grpc_ares_ev_driver_unref(driver);
+}
+
 static void on_readable_locked(void* arg, grpc_error* error) {
   fd_node* fdn = static_cast<fd_node*>(arg);
+  GPR_ASSERT(fdn->readable_registered);
   grpc_ares_ev_driver* ev_driver = fdn->ev_driver;
   const ares_socket_t as = fdn->grpc_polled_fd->GetWrappedAresSocketLocked();
   fdn->readable_registered = false;
@@ -242,6 +315,7 @@ static void on_readable_locked(void* arg, grpc_error* error) {
 
 static void on_writable_locked(void* arg, grpc_error* error) {
   fd_node* fdn = static_cast<fd_node*>(arg);
+  GPR_ASSERT(fdn->writable_registered);
   grpc_ares_ev_driver* ev_driver = fdn->ev_driver;
   const ares_socket_t as = fdn->grpc_polled_fd->GetWrappedAresSocketLocked();
   fdn->writable_registered = false;
@@ -351,6 +425,7 @@ void grpc_ares_ev_driver_start_locked(grpc_ares_ev_driver* ev_driver) {
   if (!ev_driver->working) {
     ev_driver->working = true;
     grpc_ares_notify_on_event_locked(ev_driver);
+    // Initialize overall DNS resolution timeout alarm
     grpc_millis timeout =
         ev_driver->query_timeout_ms == 0
             ? GRPC_MILLIS_INF_FUTURE
@@ -362,7 +437,14 @@ void grpc_ares_ev_driver_start_locked(grpc_ares_ev_driver* ev_driver) {
     grpc_ares_ev_driver_ref(ev_driver);
     grpc_timer_init(&ev_driver->query_timeout, timeout,
                     &ev_driver->on_timeout_locked);
+    // Initialize the backup poll alarm
+    grpc_millis next_ares_backup_poll_alarm =
+        calculate_next_ares_backup_poll_alarm_ms(ev_driver);
+    grpc_ares_ev_driver_ref(ev_driver);
+    grpc_timer_init(&ev_driver->ares_backup_poll_alarm,
+                    next_ares_backup_poll_alarm,
+                    &ev_driver->on_ares_backup_poll_alarm_locked);
   }
 }
 
-#endif /* GRPC_ARES == 1 && !defined(GRPC_UV) */
+#endif /* GRPC_ARES == 1 */
index b8cefd9..2d172eb 100644 (file)
@@ -54,6 +54,9 @@ void grpc_ares_ev_driver_on_queries_complete_locked(
 /* Shutdown all the grpc_fds used by \a ev_driver */
 void grpc_ares_ev_driver_shutdown_locked(grpc_ares_ev_driver* ev_driver);
 
+/* Exposed in this header for C-core tests only */
+extern void (*grpc_ares_test_only_inject_config)(ares_channel channel);
+
 namespace grpc_core {
 
 /* A wrapped fd that integrates with the grpc iomgr of the current platform.
diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc
new file mode 100644 (file)
index 0000000..04e36fb
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ *
+ * Copyright 2019 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+#if GRPC_ARES == 1 && defined(GRPC_UV)
+
+#include <ares.h>
+#include <uv.h>
+
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/combiner.h"
+
+namespace grpc_core {
+
+void ares_uv_poll_cb(uv_poll_t* handle, int status, int events);
+
+void ares_uv_poll_close_cb(uv_handle_t* handle) { Delete(handle); }
+
+class GrpcPolledFdLibuv : public GrpcPolledFd {
+ public:
+  GrpcPolledFdLibuv(ares_socket_t as, grpc_combiner* combiner)
+      : as_(as), combiner_(combiner) {
+    gpr_asprintf(&name_, "c-ares socket: %" PRIdPTR, (intptr_t)as);
+    handle_ = New<uv_poll_t>();
+    uv_poll_init_socket(uv_default_loop(), handle_, as);
+    handle_->data = this;
+    GRPC_COMBINER_REF(combiner_, "libuv ares event driver");
+  }
+
+  ~GrpcPolledFdLibuv() {
+    gpr_free(name_);
+    GRPC_COMBINER_UNREF(combiner_, "libuv ares event driver");
+  }
+
+  void RegisterForOnReadableLocked(grpc_closure* read_closure) override {
+    GPR_ASSERT(read_closure_ == nullptr);
+    GPR_ASSERT((poll_events_ & UV_READABLE) == 0);
+    read_closure_ = read_closure;
+    poll_events_ |= UV_READABLE;
+    uv_poll_start(handle_, poll_events_, ares_uv_poll_cb);
+  }
+
+  void RegisterForOnWriteableLocked(grpc_closure* write_closure) override {
+    GPR_ASSERT(write_closure_ == nullptr);
+    GPR_ASSERT((poll_events_ & UV_WRITABLE) == 0);
+    write_closure_ = write_closure;
+    poll_events_ |= UV_WRITABLE;
+    uv_poll_start(handle_, poll_events_, ares_uv_poll_cb);
+  }
+
+  bool IsFdStillReadableLocked() override {
+    /* uv_poll_t is based on poll, which is level triggered. So, if cares
+     * leaves some data unread, the event will trigger again. */
+    return false;
+  }
+
+  void ShutdownInternalLocked(grpc_error* error) {
+    uv_poll_stop(handle_);
+    uv_close(reinterpret_cast<uv_handle_t*>(handle_), ares_uv_poll_close_cb);
+    if (read_closure_ != nullptr) {
+      GRPC_CLOSURE_SCHED(read_closure_, GRPC_ERROR_CANCELLED);
+    }
+    if (write_closure_ != nullptr) {
+      GRPC_CLOSURE_SCHED(write_closure_, GRPC_ERROR_CANCELLED);
+    }
+  }
+
+  void ShutdownLocked(grpc_error* error) override {
+    if (grpc_core::ExecCtx::Get() == nullptr) {
+      grpc_core::ExecCtx exec_ctx;
+      ShutdownInternalLocked(error);
+    } else {
+      ShutdownInternalLocked(error);
+    }
+  }
+
+  ares_socket_t GetWrappedAresSocketLocked() override { return as_; }
+
+  const char* GetName() override { return name_; }
+
+  char* name_;
+  ares_socket_t as_;
+  uv_poll_t* handle_;
+  grpc_closure* read_closure_ = nullptr;
+  grpc_closure* write_closure_ = nullptr;
+  int poll_events_ = 0;
+  grpc_combiner* combiner_;
+};
+
+struct AresUvPollCbArg {
+  AresUvPollCbArg(uv_poll_t* handle, int status, int events)
+      : handle(handle), status(status), events(events) {}
+
+  uv_poll_t* handle;
+  int status;
+  int events;
+};
+
+static void ares_uv_poll_cb_locked(void* arg, grpc_error* error) {
+  grpc_core::UniquePtr<AresUvPollCbArg> arg_struct(
+      reinterpret_cast<AresUvPollCbArg*>(arg));
+  uv_poll_t* handle = arg_struct->handle;
+  int status = arg_struct->status;
+  int events = arg_struct->events;
+  GrpcPolledFdLibuv* polled_fd =
+      reinterpret_cast<GrpcPolledFdLibuv*>(handle->data);
+  if (status < 0) {
+    error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("cares polling error");
+    error =
+        grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR,
+                           grpc_slice_from_static_string(uv_strerror(status)));
+  }
+  if (events & UV_READABLE) {
+    GPR_ASSERT(polled_fd->read_closure_ != nullptr);
+    GRPC_CLOSURE_SCHED(polled_fd->read_closure_, error);
+    polled_fd->read_closure_ = nullptr;
+    polled_fd->poll_events_ &= ~UV_READABLE;
+  }
+  if (events & UV_WRITABLE) {
+    GPR_ASSERT(polled_fd->write_closure_ != nullptr);
+    GRPC_CLOSURE_SCHED(polled_fd->write_closure_, error);
+    polled_fd->write_closure_ = nullptr;
+    polled_fd->poll_events_ &= ~UV_WRITABLE;
+  }
+  uv_poll_start(handle, polled_fd->poll_events_, ares_uv_poll_cb);
+}
+
+void ares_uv_poll_cb(uv_poll_t* handle, int status, int events) {
+  grpc_core::ExecCtx exec_ctx;
+  GrpcPolledFdLibuv* polled_fd =
+      reinterpret_cast<GrpcPolledFdLibuv*>(handle->data);
+  AresUvPollCbArg* arg = New<AresUvPollCbArg>(handle, status, events);
+  GRPC_CLOSURE_SCHED(
+      GRPC_CLOSURE_CREATE(ares_uv_poll_cb_locked, arg,
+                          grpc_combiner_scheduler(polled_fd->combiner_)),
+      GRPC_ERROR_NONE);
+}
+
+class GrpcPolledFdFactoryLibuv : public GrpcPolledFdFactory {
+ public:
+  GrpcPolledFd* NewGrpcPolledFdLocked(ares_socket_t as,
+                                      grpc_pollset_set* driver_pollset_set,
+                                      grpc_combiner* combiner) override {
+    return New<GrpcPolledFdLibuv>(as, combiner);
+  }
+
+  void ConfigureAresChannelLocked(ares_channel channel) override {}
+};
+
+UniquePtr<GrpcPolledFdFactory> NewGrpcPolledFdFactory(grpc_combiner* combiner) {
+  return UniquePtr<GrpcPolledFdFactory>(New<GrpcPolledFdFactoryLibuv>());
+}
+
+}  // namespace grpc_core
+
+#endif /* GRPC_ARES == 1 && defined(GRPC_UV) */
index 9570e32..85f5cd8 100644 (file)
@@ -18,7 +18,7 @@
 #include <grpc/support/port_platform.h>
 
 #include "src/core/lib/iomgr/port.h"
-#if GRPC_ARES == 1 && defined(GPR_WINDOWS)
+#if GRPC_ARES == 1 && defined(GRPC_WINDOWS_SOCKET_ARES_EV_DRIVER)
 
 #include <ares.h>
 
@@ -109,6 +109,7 @@ class GrpcPolledFdWindows : public GrpcPolledFd {
     read_closure_ = read_closure;
     GPR_ASSERT(GRPC_SLICE_LENGTH(read_buf_) == 0);
     grpc_slice_unref_internal(read_buf_);
+    GPR_ASSERT(!read_buf_has_data_);
     read_buf_ = GRPC_SLICE_MALLOC(4192);
     WSABUF buffer;
     buffer.buf = (char*)GRPC_SLICE_START_PTR(read_buf_);
@@ -175,7 +176,7 @@ class GrpcPolledFdWindows : public GrpcPolledFd {
     GRPC_CARES_TRACE_LOG(
         "RecvFrom called on fd:|%s|. Current read buf length:|%d|", GetName(),
         GRPC_SLICE_LENGTH(read_buf_));
-    if (GRPC_SLICE_LENGTH(read_buf_) == 0) {
+    if (!read_buf_has_data_) {
       WSASetLastError(WSAEWOULDBLOCK);
       return -1;
     }
@@ -186,6 +187,9 @@ class GrpcPolledFdWindows : public GrpcPolledFd {
     }
     read_buf_ = grpc_slice_sub_no_ref(read_buf_, bytes_read,
                                       GRPC_SLICE_LENGTH(read_buf_));
+    if (GRPC_SLICE_LENGTH(read_buf_) == 0) {
+      read_buf_has_data_ = false;
+    }
     /* c-ares overloads this recv_from virtual socket function to receive
      * data on both UDP and TCP sockets, and from is nullptr for TCP. */
     if (from != nullptr) {
@@ -302,6 +306,11 @@ class GrpcPolledFdWindows : public GrpcPolledFd {
     polled_fd->OnIocpReadableInner(error);
   }
 
+  // TODO(apolcyn): improve this error handling to be less conversative.
+  // An e.g. ECONNRESET error here should result in errors when
+  // c-ares reads from this socket later, but it shouldn't necessarily cancel
+  // the entire resolution attempt. Doing so will allow the "inject broken
+  // nameserver list" test to pass on Windows.
   void OnIocpReadableInner(grpc_error* error) {
     if (error == GRPC_ERROR_NONE) {
       if (winsocket_->read_info.wsa_error != 0) {
@@ -323,6 +332,7 @@ class GrpcPolledFdWindows : public GrpcPolledFd {
     if (error == GRPC_ERROR_NONE) {
       read_buf_ = grpc_slice_sub_no_ref(read_buf_, 0,
                                         winsocket_->read_info.bytes_transfered);
+      read_buf_has_data_ = true;
     } else {
       grpc_slice_unref_internal(read_buf_);
       read_buf_ = grpc_empty_slice();
@@ -370,6 +380,7 @@ class GrpcPolledFdWindows : public GrpcPolledFd {
   char recv_from_source_addr_[200];
   ares_socklen_t recv_from_source_addr_len_;
   grpc_slice read_buf_;
+  bool read_buf_has_data_ = false;
   grpc_slice write_buf_;
   grpc_closure* read_closure_ = nullptr;
   grpc_closure* write_closure_ = nullptr;
@@ -445,7 +456,8 @@ class SockToPolledFdMap {
    */
   static ares_socket_t Socket(int af, int type, int protocol, void* user_data) {
     SockToPolledFdMap* map = static_cast<SockToPolledFdMap*>(user_data);
-    SOCKET s = WSASocket(af, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
+    SOCKET s = WSASocket(af, type, protocol, nullptr, 0,
+                         grpc_get_default_wsa_socket_flags());
     if (s == INVALID_SOCKET) {
       return s;
     }
index 37b0b36..ad0f146 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <grpc/support/port_platform.h>
 
-#if GRPC_ARES == 1 && !defined(GRPC_UV)
+#if GRPC_ARES == 1
 
 #include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
 #include "src/core/lib/iomgr/sockaddr.h"
@@ -101,7 +101,7 @@ static void log_address_sorting_list(const ServerAddressList& addresses,
 }
 
 void grpc_cares_wrapper_address_sorting_sort(ServerAddressList* addresses) {
-  if (grpc_trace_cares_address_sorting.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_cares_address_sorting)) {
     log_address_sorting_list(*addresses, "input");
   }
   address_sorting_sortable* sortables = (address_sorting_sortable*)gpr_zalloc(
@@ -120,7 +120,7 @@ void grpc_cares_wrapper_address_sorting_sort(ServerAddressList* addresses) {
   }
   gpr_free(sortables);
   *addresses = std::move(sorted);
-  if (grpc_trace_cares_address_sorting.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_cares_address_sorting)) {
     log_address_sorting_list(*addresses, "output");
   }
 }
@@ -154,6 +154,10 @@ void grpc_ares_complete_request_locked(grpc_ares_request* r) {
 static grpc_ares_hostbyname_request* create_hostbyname_request_locked(
     grpc_ares_request* parent_request, char* host, uint16_t port,
     bool is_balancer) {
+  GRPC_CARES_TRACE_LOG(
+      "request:%p create_hostbyname_request_locked host:%s port:%d "
+      "is_balancer:%d",
+      parent_request, host, port, is_balancer);
   grpc_ares_hostbyname_request* hr = static_cast<grpc_ares_hostbyname_request*>(
       gpr_zalloc(sizeof(grpc_ares_hostbyname_request)));
   hr->parent_request = parent_request;
@@ -251,6 +255,8 @@ static void on_srv_query_done_locked(void* arg, int status, int timeouts,
     GRPC_CARES_TRACE_LOG("request:%p on_srv_query_done_locked ARES_SUCCESS", r);
     struct ares_srv_reply* reply;
     const int parse_status = ares_parse_srv_reply(abuf, alen, &reply);
+    GRPC_CARES_TRACE_LOG("request:%p ares_parse_srv_reply: %d", r,
+                         parse_status);
     if (parse_status == ARES_SUCCESS) {
       ares_channel* channel =
           grpc_ares_ev_driver_get_channel_locked(r->ev_driver);
@@ -516,6 +522,76 @@ static bool target_matches_localhost(const char* name) {
   return out;
 }
 
+#ifdef GRPC_ARES_RESOLVE_LOCALHOST_MANUALLY
+static bool inner_maybe_resolve_localhost_manually_locked(
+    const char* name, const char* default_port,
+    grpc_core::UniquePtr<grpc_core::ServerAddressList>* addrs, char** host,
+    char** port) {
+  gpr_split_host_port(name, host, port);
+  if (*host == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Failed to parse %s into host:port during manual localhost "
+            "resolution check.",
+            name);
+    return false;
+  }
+  if (*port == nullptr) {
+    if (default_port == nullptr) {
+      gpr_log(GPR_ERROR,
+              "No port or default port for %s during manual localhost "
+              "resolution check.",
+              name);
+      return false;
+    }
+    *port = gpr_strdup(default_port);
+  }
+  if (gpr_stricmp(*host, "localhost") == 0) {
+    GPR_ASSERT(*addrs == nullptr);
+    *addrs = grpc_core::MakeUnique<grpc_core::ServerAddressList>();
+    uint16_t numeric_port = grpc_strhtons(*port);
+    // Append the ipv6 loopback address.
+    struct sockaddr_in6 ipv6_loopback_addr;
+    memset(&ipv6_loopback_addr, 0, sizeof(ipv6_loopback_addr));
+    ((char*)&ipv6_loopback_addr.sin6_addr)[15] = 1;
+    ipv6_loopback_addr.sin6_family = AF_INET6;
+    ipv6_loopback_addr.sin6_port = numeric_port;
+    (*addrs)->emplace_back(&ipv6_loopback_addr, sizeof(ipv6_loopback_addr),
+                           nullptr /* args */);
+    // Append the ipv4 loopback address.
+    struct sockaddr_in ipv4_loopback_addr;
+    memset(&ipv4_loopback_addr, 0, sizeof(ipv4_loopback_addr));
+    ((char*)&ipv4_loopback_addr.sin_addr)[0] = 0x7f;
+    ((char*)&ipv4_loopback_addr.sin_addr)[3] = 0x01;
+    ipv4_loopback_addr.sin_family = AF_INET;
+    ipv4_loopback_addr.sin_port = numeric_port;
+    (*addrs)->emplace_back(&ipv4_loopback_addr, sizeof(ipv4_loopback_addr),
+                           nullptr /* args */);
+    // Let the address sorter figure out which one should be tried first.
+    grpc_cares_wrapper_address_sorting_sort(addrs->get());
+    return true;
+  }
+  return false;
+}
+
+static bool grpc_ares_maybe_resolve_localhost_manually_locked(
+    const char* name, const char* default_port,
+    grpc_core::UniquePtr<grpc_core::ServerAddressList>* addrs) {
+  char* host = nullptr;
+  char* port = nullptr;
+  bool out = inner_maybe_resolve_localhost_manually_locked(name, default_port,
+                                                           addrs, &host, &port);
+  gpr_free(host);
+  gpr_free(port);
+  return out;
+}
+#else  /* GRPC_ARES_RESOLVE_LOCALHOST_MANUALLY */
+static bool grpc_ares_maybe_resolve_localhost_manually_locked(
+    const char* name, const char* default_port,
+    grpc_core::UniquePtr<grpc_core::ServerAddressList>* addrs) {
+  return false;
+}
+#endif /* GRPC_ARES_RESOLVE_LOCALHOST_MANUALLY */
+
 static grpc_ares_request* grpc_dns_lookup_ares_locked_impl(
     const char* dns_server, const char* name, const char* default_port,
     grpc_pollset_set* interested_parties, grpc_closure* on_done,
@@ -687,4 +763,4 @@ void (*grpc_resolve_address_ares)(
     grpc_pollset_set* interested_parties, grpc_closure* on_done,
     grpc_resolved_addresses** addrs) = grpc_resolve_address_ares_impl;
 
-#endif /* GRPC_ARES == 1 && !defined(GRPC_UV) */
+#endif /* GRPC_ARES == 1 */
index 2808250..cc977c0 100644 (file)
 #include "src/core/lib/iomgr/polling_entity.h"
 #include "src/core/lib/iomgr/resolve_address.h"
 
-#define GRPC_DNS_ARES_DEFAULT_QUERY_TIMEOUT_MS 10000
+#define GRPC_DNS_ARES_DEFAULT_QUERY_TIMEOUT_MS 120000
 
 extern grpc_core::TraceFlag grpc_trace_cares_address_sorting;
 
 extern grpc_core::TraceFlag grpc_trace_cares_resolver;
 
-#define GRPC_CARES_TRACE_LOG(format, ...)                         \
-  if (grpc_trace_cares_resolver.enabled()) {                      \
-    gpr_log(GPR_DEBUG, "(c-ares resolver) " format, __VA_ARGS__); \
-  }
+#define GRPC_CARES_TRACE_LOG(format, ...)                           \
+  do {                                                              \
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_cares_resolver)) {       \
+      gpr_log(GPR_DEBUG, "(c-ares resolver) " format, __VA_ARGS__); \
+    }                                                               \
+  } while (0)
 
 typedef struct grpc_ares_request grpc_ares_request;
 
@@ -85,14 +87,6 @@ void grpc_ares_complete_request_locked(grpc_ares_request* request);
 /* E.g., return false if ipv6 is known to not be available. */
 bool grpc_ares_query_ipv6();
 
-/* Maybe (depending on the current platform) checks if "name" matches
- * "localhost" and if so fills in addrs with the correct sockaddr structures.
- * Returns a bool indicating whether or not such an action was performed.
- * See https://github.com/grpc/grpc/issues/15158. */
-bool grpc_ares_maybe_resolve_localhost_manually_locked(
-    const char* name, const char* default_port,
-    grpc_core::UniquePtr<grpc_core::ServerAddressList>* addrs);
-
 /* Sorts destinations in lb_addrs according to RFC 6724. */
 void grpc_cares_wrapper_address_sorting_sort(
     grpc_core::ServerAddressList* addresses);
index 1f4701c..d2de88e 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <grpc/support/port_platform.h>
 
-#if GRPC_ARES != 1 || defined(GRPC_UV)
+#if GRPC_ARES != 1
 
 #include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
 
@@ -62,4 +62,4 @@ void (*grpc_resolve_address_ares)(
     grpc_pollset_set* interested_parties, grpc_closure* on_done,
     grpc_resolved_addresses** addrs) = grpc_resolve_address_ares_impl;
 
-#endif /* GRPC_ARES != 1 || defined(GRPC_UV) */
+#endif /* GRPC_ARES != 1 */
diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc
new file mode 100644 (file)
index 0000000..f85feb6
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+#if GRPC_ARES == 1 && defined(GRPC_UV)
+
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/filters/client_channel/parse_address.h"
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
+#include "src/core/ext/filters/client_channel/server_address.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+
+bool grpc_ares_query_ipv6() {
+  /* The libuv grpc code currently does not have the code to probe for this,
+   * so we assume for now that IPv6 is always available in contexts where this
+   * code will be used. */
+  return true;
+}
+
+#endif /* GRPC_ARES == 1 && defined(GRPC_UV) */
index 028d844..23c0fec 100644 (file)
 
 bool grpc_ares_query_ipv6() { return grpc_ipv6_loopback_available(); }
 
-bool grpc_ares_maybe_resolve_localhost_manually_locked(
-    const char* name, const char* default_port,
-    grpc_core::UniquePtr<grpc_core::ServerAddressList>* addrs) {
-  return false;
-}
-
 #endif /* GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER) */
index 202452f..06cd572 100644 (file)
@@ -19,7 +19,7 @@
 #include <grpc/support/port_platform.h>
 
 #include "src/core/lib/iomgr/port.h"
-#if GRPC_ARES == 1 && defined(GPR_WINDOWS)
+#if GRPC_ARES == 1 && defined(GRPC_WINDOWS_SOCKET_ARES_EV_DRIVER)
 
 #include <grpc/support/string_util.h>
 
 
 bool grpc_ares_query_ipv6() { return grpc_ipv6_loopback_available(); }
 
-static bool inner_maybe_resolve_localhost_manually_locked(
-    const char* name, const char* default_port,
-    grpc_core::UniquePtr<grpc_core::ServerAddressList>* addrs, char** host,
-    char** port) {
-  gpr_split_host_port(name, host, port);
-  if (*host == nullptr) {
-    gpr_log(GPR_ERROR,
-            "Failed to parse %s into host:port during Windows localhost "
-            "resolution check.",
-            name);
-    return false;
-  }
-  if (*port == nullptr) {
-    if (default_port == nullptr) {
-      gpr_log(GPR_ERROR,
-              "No port or default port for %s during Windows localhost "
-              "resolution check.",
-              name);
-      return false;
-    }
-    *port = gpr_strdup(default_port);
-  }
-  if (gpr_stricmp(*host, "localhost") == 0) {
-    GPR_ASSERT(*addrs == nullptr);
-    *addrs = grpc_core::MakeUnique<grpc_core::ServerAddressList>();
-    uint16_t numeric_port = grpc_strhtons(*port);
-    // Append the ipv6 loopback address.
-    struct sockaddr_in6 ipv6_loopback_addr;
-    memset(&ipv6_loopback_addr, 0, sizeof(ipv6_loopback_addr));
-    ((char*)&ipv6_loopback_addr.sin6_addr)[15] = 1;
-    ipv6_loopback_addr.sin6_family = AF_INET6;
-    ipv6_loopback_addr.sin6_port = numeric_port;
-    (*addrs)->emplace_back(&ipv6_loopback_addr, sizeof(ipv6_loopback_addr),
-                           nullptr /* args */);
-    // Append the ipv4 loopback address.
-    struct sockaddr_in ipv4_loopback_addr;
-    memset(&ipv4_loopback_addr, 0, sizeof(ipv4_loopback_addr));
-    ((char*)&ipv4_loopback_addr.sin_addr)[0] = 0x7f;
-    ((char*)&ipv4_loopback_addr.sin_addr)[3] = 0x01;
-    ipv4_loopback_addr.sin_family = AF_INET;
-    ipv4_loopback_addr.sin_port = numeric_port;
-    (*addrs)->emplace_back(&ipv4_loopback_addr, sizeof(ipv4_loopback_addr),
-                           nullptr /* args */);
-    // Let the address sorter figure out which one should be tried first.
-    grpc_cares_wrapper_address_sorting_sort(addrs->get());
-    return true;
-  }
-  return false;
-}
-
-bool grpc_ares_maybe_resolve_localhost_manually_locked(
-    const char* name, const char* default_port,
-    grpc_core::UniquePtr<grpc_core::ServerAddressList>* addrs) {
-  char* host = nullptr;
-  char* port = nullptr;
-  bool out = inner_maybe_resolve_localhost_manually_locked(name, default_port,
-                                                           addrs, &host, &port);
-  gpr_free(host);
-  gpr_free(port);
-  return out;
-}
-
-#endif /* GRPC_ARES == 1 && defined(GPR_WINDOWS) */
+#endif /* GRPC_ARES == 1 && defined(GRPC_WINDOWS_SOCKET_ARES_EV_DRIVER) */
diff --git a/src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc b/src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc
new file mode 100644 (file)
index 0000000..07a617c
--- /dev/null
@@ -0,0 +1,28 @@
+//
+// 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.
+//
+
+// This is similar to the sockaddr resolver, except that it supports a
+// bunch of query args that are useful for dependency injection in tests.
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h"
+
+GPR_GLOBAL_CONFIG_DEFINE_STRING(
+    grpc_dns_resolver, "",
+    "Declares which DNS resolver to use. The default is ares if gRPC is built "
+    "with c-ares support. Otherwise, the value of this environment variable is "
+    "ignored.")
diff --git a/src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h b/src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h
new file mode 100644 (file)
index 0000000..d0a3486
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *
+ * 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 GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_DNS_RESOLVER_SELECTION_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_DNS_RESOLVER_SELECTION_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/gprpp/global_config.h"
+
+GPR_GLOBAL_CONFIG_DECLARE_STRING(grpc_dns_resolver);
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_DNS_RESOLVER_SELECTION_H \
+        */
index 164d308..5ab75d0 100644 (file)
 #include <grpc/support/string_util.h>
 #include <grpc/support/time.h>
 
+#include "src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h"
 #include "src/core/ext/filters/client_channel/resolver_registry.h"
 #include "src/core/ext/filters/client_channel/server_address.h"
 #include "src/core/lib/backoff/backoff.h"
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gprpp/manual_constructor.h"
@@ -274,8 +274,9 @@ class NativeDnsResolverFactory : public ResolverFactory {
 }  // namespace grpc_core
 
 void grpc_resolver_dns_native_init() {
-  char* resolver_env = gpr_getenv("GRPC_DNS_RESOLVER");
-  if (resolver_env != nullptr && gpr_stricmp(resolver_env, "native") == 0) {
+  grpc_core::UniquePtr<char> resolver =
+      GPR_GLOBAL_CONFIG_GET(grpc_dns_resolver);
+  if (gpr_stricmp(resolver.get(), "native") == 0) {
     gpr_log(GPR_DEBUG, "Using native dns resolver");
     grpc_core::ResolverRegistry::Builder::RegisterResolverFactory(
         grpc_core::UniquePtr<grpc_core::ResolverFactory>(
@@ -291,7 +292,6 @@ void grpc_resolver_dns_native_init() {
               grpc_core::New<grpc_core::NativeDnsResolverFactory>()));
     }
   }
-  gpr_free(resolver_env);
 }
 
 void grpc_resolver_dns_native_shutdown() {}
index daac4f0..ac6bc7e 100644 (file)
@@ -35,6 +35,7 @@
 #include "src/core/lib/channel/status_util.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/optional.h"
 #include "src/core/lib/uri/uri_parser.h"
 
 // As per the retry design, we do not allow more than 5 retry attempts.
 namespace grpc_core {
 namespace internal {
 
-ProcessedResolverResult::ProcessedResolverResult(
-    Resolver::Result* resolver_result, bool parse_retry)
-    : service_config_(resolver_result->service_config) {
-  // If resolver did not return a service config, use the default
-  // specified via the client API.
-  if (service_config_ == nullptr) {
-    const char* service_config_json = grpc_channel_arg_get_string(
-        grpc_channel_args_find(resolver_result->args, GRPC_ARG_SERVICE_CONFIG));
-    if (service_config_json != nullptr) {
-      service_config_ = ServiceConfig::Create(service_config_json);
-    }
-  } else {
-    // Add the service config JSON to channel args so that it's
-    // accessible in the subchannel.
-    // TODO(roth): Consider whether there's a better way to pass the
-    // service config down into the subchannel stack, such as maybe via
-    // call context or metadata.  This would avoid the problem of having
-    // to recreate all subchannels whenever the service config changes.
-    // It would also avoid the need to pass in the resolver result in
-    // mutable form, both here and in
-    // ResolvingLoadBalancingPolicy::ProcessResolverResultCallback().
-    grpc_arg arg = grpc_channel_arg_string_create(
-        const_cast<char*>(GRPC_ARG_SERVICE_CONFIG),
-        const_cast<char*>(service_config_->service_config_json()));
-    grpc_channel_args* new_args =
-        grpc_channel_args_copy_and_add(resolver_result->args, &arg, 1);
-    grpc_channel_args_destroy(resolver_result->args);
-    resolver_result->args = new_args;
-  }
-  // Process service config.
-  ProcessServiceConfig(*resolver_result, parse_retry);
-  // If no LB config was found above, just find the LB policy name then.
-  if (lb_policy_name_ == nullptr) ProcessLbPolicyName(*resolver_result);
-}
-
-void ProcessedResolverResult::ProcessServiceConfig(
-    const Resolver::Result& resolver_result, bool parse_retry) {
-  if (service_config_ == nullptr) return;
-  service_config_json_ =
-      UniquePtr<char>(gpr_strdup(service_config_->service_config_json()));
-  if (parse_retry) {
-    const grpc_arg* channel_arg =
-        grpc_channel_args_find(resolver_result.args, GRPC_ARG_SERVER_URI);
-    const char* server_uri = grpc_channel_arg_get_string(channel_arg);
-    GPR_ASSERT(server_uri != nullptr);
-    grpc_uri* uri = grpc_uri_parse(server_uri, true);
-    GPR_ASSERT(uri->path[0] != '\0');
-    server_name_ = uri->path[0] == '/' ? uri->path + 1 : uri->path;
-    service_config_->ParseGlobalParams(ParseServiceConfig, this);
-    grpc_uri_destroy(uri);
-  } else {
-    service_config_->ParseGlobalParams(ParseServiceConfig, this);
-  }
-  method_params_table_ = service_config_->CreateMethodConfigTable(
-      ClientChannelMethodParams::CreateFromJson);
-}
-
-void ProcessedResolverResult::ProcessLbPolicyName(
-    const Resolver::Result& resolver_result) {
-  // Prefer the LB policy name found in the service config. Note that this is
-  // checking the deprecated loadBalancingPolicy field, rather than the new
-  // loadBalancingConfig field.
-  if (service_config_ != nullptr) {
-    lb_policy_name_.reset(
-        gpr_strdup(service_config_->GetLoadBalancingPolicyName()));
-    // Convert to lower-case.
-    if (lb_policy_name_ != nullptr) {
-      char* lb_policy_name = lb_policy_name_.get();
-      for (size_t i = 0; i < strlen(lb_policy_name); ++i) {
-        lb_policy_name[i] = tolower(lb_policy_name[i]);
-      }
-    }
-  }
-  // Otherwise, find the LB policy name set by the client API.
-  if (lb_policy_name_ == nullptr) {
-    const grpc_arg* channel_arg =
-        grpc_channel_args_find(resolver_result.args, GRPC_ARG_LB_POLICY_NAME);
-    lb_policy_name_.reset(gpr_strdup(grpc_channel_arg_get_string(channel_arg)));
-  }
-  // Special case: If at least one balancer address is present, we use
-  // the grpclb policy, regardless of what the resolver has returned.
-  bool found_balancer_address = false;
-  for (size_t i = 0; i < resolver_result.addresses.size(); ++i) {
-    const ServerAddress& address = resolver_result.addresses[i];
-    if (address.IsBalancer()) {
-      found_balancer_address = true;
-      break;
-    }
-  }
-  if (found_balancer_address) {
-    if (lb_policy_name_ != nullptr &&
-        strcmp(lb_policy_name_.get(), "grpclb") != 0) {
-      gpr_log(GPR_INFO,
-              "resolver requested LB policy %s but provided at least one "
-              "balancer address -- forcing use of grpclb LB policy",
-              lb_policy_name_.get());
-    }
-    lb_policy_name_.reset(gpr_strdup("grpclb"));
-  }
-  // Use pick_first if nothing was specified and we didn't select grpclb
-  // above.
-  if (lb_policy_name_ == nullptr) {
-    lb_policy_name_.reset(gpr_strdup("pick_first"));
-  }
-}
-
-void ProcessedResolverResult::ParseServiceConfig(
-    const grpc_json* field, ProcessedResolverResult* parsing_state) {
-  parsing_state->ParseLbConfigFromServiceConfig(field);
-  if (parsing_state->server_name_ != nullptr) {
-    parsing_state->ParseRetryThrottleParamsFromServiceConfig(field);
-  }
+namespace {
+size_t g_client_channel_service_config_parser_index;
 }
 
-void ProcessedResolverResult::ParseLbConfigFromServiceConfig(
-    const grpc_json* field) {
-  if (lb_policy_config_ != nullptr) return;  // Already found.
-  if (field->key == nullptr || strcmp(field->key, "loadBalancingConfig") != 0) {
-    return;  // Not the LB config global parameter.
-  }
-  const grpc_json* policy =
-      LoadBalancingPolicy::ParseLoadBalancingConfig(field);
-  if (policy != nullptr) {
-    lb_policy_name_.reset(gpr_strdup(policy->key));
-    lb_policy_config_ =
-        MakeRefCounted<LoadBalancingPolicy::Config>(policy, service_config_);
-  }
+size_t ClientChannelServiceConfigParser::ParserIndex() {
+  return g_client_channel_service_config_parser_index;
 }
 
-void ProcessedResolverResult::ParseRetryThrottleParamsFromServiceConfig(
-    const grpc_json* field) {
-  if (strcmp(field->key, "retryThrottling") == 0) {
-    if (retry_throttle_data_ != nullptr) return;  // Duplicate.
-    if (field->type != GRPC_JSON_OBJECT) return;
-    int max_milli_tokens = 0;
-    int milli_token_ratio = 0;
-    for (grpc_json* sub_field = field->child; sub_field != nullptr;
-         sub_field = sub_field->next) {
-      if (sub_field->key == nullptr) return;
-      if (strcmp(sub_field->key, "maxTokens") == 0) {
-        if (max_milli_tokens != 0) return;  // Duplicate.
-        if (sub_field->type != GRPC_JSON_NUMBER) return;
-        max_milli_tokens = gpr_parse_nonnegative_int(sub_field->value);
-        if (max_milli_tokens == -1) return;
-        max_milli_tokens *= 1000;
-      } else if (strcmp(sub_field->key, "tokenRatio") == 0) {
-        if (milli_token_ratio != 0) return;  // Duplicate.
-        if (sub_field->type != GRPC_JSON_NUMBER) return;
-        // We support up to 3 decimal digits.
-        size_t whole_len = strlen(sub_field->value);
-        uint32_t multiplier = 1;
-        uint32_t decimal_value = 0;
-        const char* decimal_point = strchr(sub_field->value, '.');
-        if (decimal_point != nullptr) {
-          whole_len = static_cast<size_t>(decimal_point - sub_field->value);
-          multiplier = 1000;
-          size_t decimal_len = strlen(decimal_point + 1);
-          if (decimal_len > 3) decimal_len = 3;
-          if (!gpr_parse_bytes_to_uint32(decimal_point + 1, decimal_len,
-                                         &decimal_value)) {
-            return;
-          }
-          uint32_t decimal_multiplier = 1;
-          for (size_t i = 0; i < (3 - decimal_len); ++i) {
-            decimal_multiplier *= 10;
-          }
-          decimal_value *= decimal_multiplier;
-        }
-        uint32_t whole_value;
-        if (!gpr_parse_bytes_to_uint32(sub_field->value, whole_len,
-                                       &whole_value)) {
-          return;
-        }
-        milli_token_ratio =
-            static_cast<int>((whole_value * multiplier) + decimal_value);
-        if (milli_token_ratio <= 0) return;
-      }
-    }
-    retry_throttle_data_ =
-        grpc_core::internal::ServerRetryThrottleMap::GetDataForServer(
-            server_name_, max_milli_tokens, milli_token_ratio);
-  }
+void ClientChannelServiceConfigParser::Register() {
+  g_client_channel_service_config_parser_index =
+      ServiceConfig::RegisterParser(UniquePtr<ServiceConfig::Parser>(
+          New<ClientChannelServiceConfigParser>()));
 }
 
 namespace {
 
-bool ParseWaitForReady(
-    grpc_json* field, ClientChannelMethodParams::WaitForReady* wait_for_ready) {
-  if (field->type != GRPC_JSON_TRUE && field->type != GRPC_JSON_FALSE) {
-    return false;
-  }
-  *wait_for_ready = field->type == GRPC_JSON_TRUE
-                        ? ClientChannelMethodParams::WAIT_FOR_READY_TRUE
-                        : ClientChannelMethodParams::WAIT_FOR_READY_FALSE;
-  return true;
-}
-
 // Parses a JSON field of the form generated for a google.proto.Duration
 // proto message, as per:
 //   https://developers.google.com/protocol-buffers/docs/proto3#json
@@ -272,18 +92,36 @@ bool ParseDuration(grpc_json* field, grpc_millis* duration) {
   return true;
 }
 
-UniquePtr<ClientChannelMethodParams::RetryPolicy> ParseRetryPolicy(
-    grpc_json* field) {
-  auto retry_policy = MakeUnique<ClientChannelMethodParams::RetryPolicy>();
-  if (field->type != GRPC_JSON_OBJECT) return nullptr;
+UniquePtr<ClientChannelMethodParsedObject::RetryPolicy> ParseRetryPolicy(
+    grpc_json* field, grpc_error** error) {
+  GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
+  auto retry_policy =
+      MakeUnique<ClientChannelMethodParsedObject::RetryPolicy>();
+  if (field->type != GRPC_JSON_OBJECT) {
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "field:retryPolicy error:should be of type object");
+    return nullptr;
+  }
+  InlinedVector<grpc_error*, 4> error_list;
   for (grpc_json* sub_field = field->child; sub_field != nullptr;
        sub_field = sub_field->next) {
-    if (sub_field->key == nullptr) return nullptr;
+    if (sub_field->key == nullptr) continue;
     if (strcmp(sub_field->key, "maxAttempts") == 0) {
-      if (retry_policy->max_attempts != 0) return nullptr;  // Duplicate.
-      if (sub_field->type != GRPC_JSON_NUMBER) return nullptr;
+      if (retry_policy->max_attempts != 0) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:maxAttempts error:Duplicate entry"));
+      }  // Duplicate. Continue Parsing
+      if (sub_field->type != GRPC_JSON_NUMBER) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:maxAttempts error:should be of type number"));
+        continue;
+      }
       retry_policy->max_attempts = gpr_parse_nonnegative_int(sub_field->value);
-      if (retry_policy->max_attempts <= 1) return nullptr;
+      if (retry_policy->max_attempts <= 1) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:maxAttempts error:should be at least 2"));
+        continue;
+      }
       if (retry_policy->max_attempts > MAX_MAX_RETRY_ATTEMPTS) {
         gpr_log(GPR_ERROR,
                 "service config: clamped retryPolicy.maxAttempts at %d",
@@ -291,78 +129,375 @@ UniquePtr<ClientChannelMethodParams::RetryPolicy> ParseRetryPolicy(
         retry_policy->max_attempts = MAX_MAX_RETRY_ATTEMPTS;
       }
     } else if (strcmp(sub_field->key, "initialBackoff") == 0) {
-      if (retry_policy->initial_backoff > 0) return nullptr;  // Duplicate.
+      if (retry_policy->initial_backoff > 0) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:initialBackoff error:Duplicate entry"));
+      }  // Duplicate, continue parsing.
       if (!ParseDuration(sub_field, &retry_policy->initial_backoff)) {
-        return nullptr;
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:initialBackoff error:Failed to parse"));
+        continue;
+      }
+      if (retry_policy->initial_backoff == 0) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:initialBackoff error:must be greater than 0"));
       }
-      if (retry_policy->initial_backoff == 0) return nullptr;
     } else if (strcmp(sub_field->key, "maxBackoff") == 0) {
-      if (retry_policy->max_backoff > 0) return nullptr;  // Duplicate.
+      if (retry_policy->max_backoff > 0) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:maxBackoff error:Duplicate entry"));
+      }  // Duplicate, continue parsing.
       if (!ParseDuration(sub_field, &retry_policy->max_backoff)) {
-        return nullptr;
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:maxBackoff error:failed to parse"));
+        continue;
+      }
+      if (retry_policy->max_backoff == 0) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:maxBackoff error:should be greater than 0"));
       }
-      if (retry_policy->max_backoff == 0) return nullptr;
     } else if (strcmp(sub_field->key, "backoffMultiplier") == 0) {
-      if (retry_policy->backoff_multiplier != 0) return nullptr;  // Duplicate.
-      if (sub_field->type != GRPC_JSON_NUMBER) return nullptr;
+      if (retry_policy->backoff_multiplier != 0) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:backoffMultiplier error:Duplicate entry"));
+      }  // Duplicate, continue parsing.
+      if (sub_field->type != GRPC_JSON_NUMBER) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:backoffMultiplier error:should be of type number"));
+        continue;
+      }
       if (sscanf(sub_field->value, "%f", &retry_policy->backoff_multiplier) !=
           1) {
-        return nullptr;
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:backoffMultiplier error:failed to parse"));
+        continue;
+      }
+      if (retry_policy->backoff_multiplier <= 0) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:backoffMultiplier error:should be greater than 0"));
       }
-      if (retry_policy->backoff_multiplier <= 0) return nullptr;
     } else if (strcmp(sub_field->key, "retryableStatusCodes") == 0) {
       if (!retry_policy->retryable_status_codes.Empty()) {
-        return nullptr;  // Duplicate.
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:retryableStatusCodes error:Duplicate entry"));
+      }  // Duplicate, continue parsing.
+      if (sub_field->type != GRPC_JSON_ARRAY) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:retryableStatusCodes error:should be of type array"));
+        continue;
       }
-      if (sub_field->type != GRPC_JSON_ARRAY) return nullptr;
       for (grpc_json* element = sub_field->child; element != nullptr;
            element = element->next) {
-        if (element->type != GRPC_JSON_STRING) return nullptr;
+        if (element->type != GRPC_JSON_STRING) {
+          error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "field:retryableStatusCodes error:status codes should be of type "
+              "string"));
+          continue;
+        }
         grpc_status_code status;
         if (!grpc_status_code_from_string(element->value, &status)) {
-          return nullptr;
+          error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "field:retryableStatusCodes error:failed to parse status code"));
+          continue;
         }
         retry_policy->retryable_status_codes.Add(status);
       }
-      if (retry_policy->retryable_status_codes.Empty()) return nullptr;
+      if (retry_policy->retryable_status_codes.Empty()) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:retryableStatusCodes error:should be non-empty"));
+      };
     }
   }
   // Make sure required fields are set.
-  if (retry_policy->max_attempts == 0 || retry_policy->initial_backoff == 0 ||
-      retry_policy->max_backoff == 0 || retry_policy->backoff_multiplier == 0 ||
-      retry_policy->retryable_status_codes.Empty()) {
+  if (error_list.empty()) {
+    if (retry_policy->max_attempts == 0 || retry_policy->initial_backoff == 0 ||
+        retry_policy->max_backoff == 0 ||
+        retry_policy->backoff_multiplier == 0 ||
+        retry_policy->retryable_status_codes.Empty()) {
+      *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "field:retryPolicy error:Missing required field(s)");
+      return nullptr;
+    }
+  }
+  *error = GRPC_ERROR_CREATE_FROM_VECTOR("retryPolicy", &error_list);
+  return *error == GRPC_ERROR_NONE ? std::move(retry_policy) : nullptr;
+}
+
+const char* ParseHealthCheckConfig(const grpc_json* field, grpc_error** error) {
+  GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
+  const char* service_name = nullptr;
+  GPR_DEBUG_ASSERT(strcmp(field->key, "healthCheckConfig") == 0);
+  if (field->type != GRPC_JSON_OBJECT) {
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "field:healthCheckConfig error:should be of type object");
+    return nullptr;
+  }
+  InlinedVector<grpc_error*, 2> error_list;
+  for (grpc_json* sub_field = field->child; sub_field != nullptr;
+       sub_field = sub_field->next) {
+    if (sub_field->key == nullptr) {
+      GPR_DEBUG_ASSERT(false);
+      continue;
+    }
+    if (strcmp(sub_field->key, "serviceName") == 0) {
+      if (service_name != nullptr) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:serviceName error:Duplicate "
+            "entry"));
+      }  // Duplicate. Continue parsing
+      if (sub_field->type != GRPC_JSON_STRING) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:serviceName error:should be of type string"));
+        continue;
+      }
+      service_name = sub_field->value;
+    }
+  }
+  if (!error_list.empty()) {
     return nullptr;
   }
-  return retry_policy;
+  *error =
+      GRPC_ERROR_CREATE_FROM_VECTOR("field:healthCheckConfig", &error_list);
+  return service_name;
 }
 
 }  // namespace
 
-RefCountedPtr<ClientChannelMethodParams>
-ClientChannelMethodParams::CreateFromJson(const grpc_json* json) {
-  RefCountedPtr<ClientChannelMethodParams> method_params =
-      MakeRefCounted<ClientChannelMethodParams>();
+UniquePtr<ServiceConfig::ParsedConfig>
+ClientChannelServiceConfigParser::ParseGlobalParams(const grpc_json* json,
+                                                    grpc_error** error) {
+  GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
+  InlinedVector<grpc_error*, 4> error_list;
+  RefCountedPtr<ParsedLoadBalancingConfig> parsed_lb_config;
+  UniquePtr<char> lb_policy_name;
+  Optional<ClientChannelGlobalParsedObject::RetryThrottling> retry_throttling;
+  const char* health_check_service_name = nullptr;
+  for (grpc_json* field = json->child; field != nullptr; field = field->next) {
+    if (field->key == nullptr) {
+      continue;  // Not the LB config global parameter
+    }
+    // Parsed Load balancing config
+    if (strcmp(field->key, "loadBalancingConfig") == 0) {
+      if (parsed_lb_config != nullptr) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:loadBalancingConfig error:Duplicate entry"));
+      }  // Duplicate, continue parsing.
+      grpc_error* parse_error = GRPC_ERROR_NONE;
+      parsed_lb_config = LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(
+          field, &parse_error);
+      if (parsed_lb_config == nullptr) {
+        error_list.push_back(parse_error);
+      }
+    }
+    // Parse deprecated loadBalancingPolicy
+    if (strcmp(field->key, "loadBalancingPolicy") == 0) {
+      if (lb_policy_name != nullptr) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:loadBalancingPolicy error:Duplicate entry"));
+      }  // Duplicate, continue parsing.
+      if (field->type != GRPC_JSON_STRING) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:loadBalancingPolicy error:type should be string"));
+        continue;
+      }
+      lb_policy_name.reset(gpr_strdup(field->value));
+      char* lb_policy = lb_policy_name.get();
+      if (lb_policy != nullptr) {
+        for (size_t i = 0; i < strlen(lb_policy); ++i) {
+          lb_policy[i] = tolower(lb_policy[i]);
+        }
+      }
+      bool requires_config = false;
+      if (!LoadBalancingPolicyRegistry::LoadBalancingPolicyExists(
+              lb_policy, &requires_config)) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:loadBalancingPolicy error:Unknown lb policy"));
+      } else if (requires_config) {
+        char* error_msg;
+        gpr_asprintf(&error_msg,
+                     "field:loadBalancingPolicy error:%s requires a config. "
+                     "Please use loadBalancingConfig instead.",
+                     lb_policy);
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg));
+        gpr_free(error_msg);
+      }
+    }
+    // Parse retry throttling
+    if (strcmp(field->key, "retryThrottling") == 0) {
+      if (retry_throttling.has_value()) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:retryThrottling error:Duplicate entry"));
+      }  // Duplicate, continue parsing.
+      if (field->type != GRPC_JSON_OBJECT) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:retryThrottling error:Type should be object"));
+        continue;
+      }
+      Optional<int> max_milli_tokens;
+      Optional<int> milli_token_ratio;
+      for (grpc_json* sub_field = field->child; sub_field != nullptr;
+           sub_field = sub_field->next) {
+        if (sub_field->key == nullptr) continue;
+        if (strcmp(sub_field->key, "maxTokens") == 0) {
+          if (max_milli_tokens.has_value()) {
+            error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                "field:retryThrottling field:maxTokens error:Duplicate "
+                "entry"));
+          }  // Duplicate, continue parsing.
+          if (sub_field->type != GRPC_JSON_NUMBER) {
+            error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                "field:retryThrottling field:maxTokens error:Type should be "
+                "number"));
+          } else {
+            max_milli_tokens.set(gpr_parse_nonnegative_int(sub_field->value) *
+                                 1000);
+            if (max_milli_tokens.value() <= 0) {
+              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                  "field:retryThrottling field:maxTokens error:should be "
+                  "greater than zero"));
+            }
+          }
+        } else if (strcmp(sub_field->key, "tokenRatio") == 0) {
+          if (milli_token_ratio.has_value()) {
+            error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                "field:retryThrottling field:tokenRatio error:Duplicate "
+                "entry"));
+          }  // Duplicate, continue parsing.
+          if (sub_field->type != GRPC_JSON_NUMBER) {
+            error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                "field:retryThrottling field:tokenRatio error:type should be "
+                "number"));
+          } else {
+            // We support up to 3 decimal digits.
+            size_t whole_len = strlen(sub_field->value);
+            uint32_t multiplier = 1;
+            uint32_t decimal_value = 0;
+            const char* decimal_point = strchr(sub_field->value, '.');
+            if (decimal_point != nullptr) {
+              whole_len = static_cast<size_t>(decimal_point - sub_field->value);
+              multiplier = 1000;
+              size_t decimal_len = strlen(decimal_point + 1);
+              if (decimal_len > 3) decimal_len = 3;
+              if (!gpr_parse_bytes_to_uint32(decimal_point + 1, decimal_len,
+                                             &decimal_value)) {
+                error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                    "field:retryThrottling field:tokenRatio error:Failed "
+                    "parsing"));
+                continue;
+              }
+              uint32_t decimal_multiplier = 1;
+              for (size_t i = 0; i < (3 - decimal_len); ++i) {
+                decimal_multiplier *= 10;
+              }
+              decimal_value *= decimal_multiplier;
+            }
+            uint32_t whole_value;
+            if (!gpr_parse_bytes_to_uint32(sub_field->value, whole_len,
+                                           &whole_value)) {
+              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                  "field:retryThrottling field:tokenRatio error:Failed "
+                  "parsing"));
+              continue;
+            }
+            milli_token_ratio.set(
+                static_cast<int>((whole_value * multiplier) + decimal_value));
+            if (milli_token_ratio.value() <= 0) {
+              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                  "field:retryThrottling field:tokenRatio error:value should "
+                  "be greater than 0"));
+            }
+          }
+        }
+      }
+      ClientChannelGlobalParsedObject::RetryThrottling data;
+      if (!max_milli_tokens.has_value()) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:retryThrottling field:maxTokens error:Not found"));
+      } else {
+        data.max_milli_tokens = max_milli_tokens.value();
+      }
+      if (!milli_token_ratio.has_value()) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:retryThrottling field:tokenRatio error:Not found"));
+      } else {
+        data.milli_token_ratio = milli_token_ratio.value();
+      }
+      retry_throttling.set(data);
+    }
+    if (strcmp(field->key, "healthCheckConfig") == 0) {
+      if (health_check_service_name != nullptr) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:healthCheckConfig error:Duplicate entry"));
+      }  // Duplicate continue parsing
+      grpc_error* parsing_error = GRPC_ERROR_NONE;
+      health_check_service_name = ParseHealthCheckConfig(field, &parsing_error);
+      if (parsing_error != GRPC_ERROR_NONE) {
+        error_list.push_back(parsing_error);
+      }
+    }
+  }
+  *error = GRPC_ERROR_CREATE_FROM_VECTOR("Client channel global parser",
+                                         &error_list);
+  if (*error == GRPC_ERROR_NONE) {
+    return UniquePtr<ServiceConfig::ParsedConfig>(
+        New<ClientChannelGlobalParsedObject>(
+            std::move(parsed_lb_config), std::move(lb_policy_name),
+            retry_throttling, health_check_service_name));
+  }
+  return nullptr;
+}
+
+UniquePtr<ServiceConfig::ParsedConfig>
+ClientChannelServiceConfigParser::ParsePerMethodParams(const grpc_json* json,
+                                                       grpc_error** error) {
+  GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
+  InlinedVector<grpc_error*, 4> error_list;
+  Optional<bool> wait_for_ready;
+  grpc_millis timeout = 0;
+  UniquePtr<ClientChannelMethodParsedObject::RetryPolicy> retry_policy;
   for (grpc_json* field = json->child; field != nullptr; field = field->next) {
     if (field->key == nullptr) continue;
     if (strcmp(field->key, "waitForReady") == 0) {
-      if (method_params->wait_for_ready_ != WAIT_FOR_READY_UNSET) {
-        return nullptr;  // Duplicate.
-      }
-      if (!ParseWaitForReady(field, &method_params->wait_for_ready_)) {
-        return nullptr;
+      if (wait_for_ready.has_value()) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:waitForReady error:Duplicate entry"));
+      }  // Duplicate, continue parsing.
+      if (field->type == GRPC_JSON_TRUE) {
+        wait_for_ready.set(true);
+      } else if (field->type == GRPC_JSON_FALSE) {
+        wait_for_ready.set(false);
+      } else {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:waitForReady error:Type should be true/false"));
       }
     } else if (strcmp(field->key, "timeout") == 0) {
-      if (method_params->timeout_ > 0) return nullptr;  // Duplicate.
-      if (!ParseDuration(field, &method_params->timeout_)) return nullptr;
+      if (timeout > 0) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:timeout error:Duplicate entry"));
+      }  // Duplicate, continue parsing.
+      if (!ParseDuration(field, &timeout)) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:timeout error:Failed parsing"));
+      };
     } else if (strcmp(field->key, "retryPolicy") == 0) {
-      if (method_params->retry_policy_ != nullptr) {
-        return nullptr;  // Duplicate.
+      if (retry_policy != nullptr) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:retryPolicy error:Duplicate entry"));
+      }  // Duplicate, continue parsing.
+      grpc_error* error = GRPC_ERROR_NONE;
+      retry_policy = ParseRetryPolicy(field, &error);
+      if (retry_policy == nullptr) {
+        error_list.push_back(error);
       }
-      method_params->retry_policy_ = ParseRetryPolicy(field);
-      if (method_params->retry_policy_ == nullptr) return nullptr;
     }
   }
-  return method_params;
+  *error = GRPC_ERROR_CREATE_FROM_VECTOR("Client channel parser", &error_list);
+  if (*error == GRPC_ERROR_NONE) {
+    return UniquePtr<ServiceConfig::ParsedConfig>(
+        New<ClientChannelMethodParsedObject>(timeout, wait_for_ready,
+                                             std::move(retry_policy)));
+  }
+  return nullptr;
 }
 
 }  // namespace internal
index 1a46278..9af8b16 100644 (file)
 #include <grpc/support/port_platform.h>
 
 #include "src/core/ext/filters/client_channel/lb_policy.h"
+#include "src/core/ext/filters/client_channel/lb_policy_factory.h"
 #include "src/core/ext/filters/client_channel/resolver.h"
 #include "src/core/ext/filters/client_channel/retry_throttle.h"
 #include "src/core/ext/filters/client_channel/service_config.h"
 #include "src/core/lib/channel/status_util.h"
+#include "src/core/lib/gprpp/optional.h"
 #include "src/core/lib/gprpp/ref_counted.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
 #include "src/core/lib/iomgr/exec_ctx.h"  // for grpc_millis
 namespace grpc_core {
 namespace internal {
 
-class ClientChannelMethodParams;
-
-// A table mapping from a method name to its method parameters.
-typedef SliceHashTable<RefCountedPtr<ClientChannelMethodParams>>
-    ClientChannelMethodParamsTable;
-
-// A container of processed fields from the resolver result. Simplifies the
-// usage of resolver result.
-class ProcessedResolverResult {
+class ClientChannelGlobalParsedObject : public ServiceConfig::ParsedConfig {
  public:
-  // Processes the resolver result and populates the relative members
-  // for later consumption. Tries to parse retry parameters only if parse_retry
-  // is true.
-  ProcessedResolverResult(Resolver::Result* resolver_result, bool parse_retry);
-
-  // Getters. Any managed object's ownership is transferred.
-  UniquePtr<char> service_config_json() {
-    return std::move(service_config_json_);
+  struct RetryThrottling {
+    intptr_t max_milli_tokens = 0;
+    intptr_t milli_token_ratio = 0;
+  };
+
+  ClientChannelGlobalParsedObject(
+      RefCountedPtr<ParsedLoadBalancingConfig> parsed_lb_config,
+      UniquePtr<char> parsed_deprecated_lb_policy,
+      const Optional<RetryThrottling>& retry_throttling,
+      const char* health_check_service_name)
+      : parsed_lb_config_(std::move(parsed_lb_config)),
+        parsed_deprecated_lb_policy_(std::move(parsed_deprecated_lb_policy)),
+        retry_throttling_(retry_throttling),
+        health_check_service_name_(health_check_service_name) {}
+
+  Optional<RetryThrottling> retry_throttling() const {
+    return retry_throttling_;
   }
-  RefCountedPtr<ServerRetryThrottleData> retry_throttle_data() {
-    return std::move(retry_throttle_data_);
+
+  RefCountedPtr<ParsedLoadBalancingConfig> parsed_lb_config() const {
+    return parsed_lb_config_;
   }
-  RefCountedPtr<ClientChannelMethodParamsTable> method_params_table() {
-    return std::move(method_params_table_);
+
+  const char* parsed_deprecated_lb_policy() const {
+    return parsed_deprecated_lb_policy_.get();
   }
-  UniquePtr<char> lb_policy_name() { return std::move(lb_policy_name_); }
-  RefCountedPtr<LoadBalancingPolicy::Config> lb_policy_config() {
-    return std::move(lb_policy_config_);
+
+  const char* health_check_service_name() const {
+    return health_check_service_name_;
   }
 
  private:
-  // Finds the service config; extracts LB config and (maybe) retry throttle
-  // params from it.
-  void ProcessServiceConfig(const Resolver::Result& resolver_result,
-                            bool parse_retry);
-
-  // Finds the LB policy name (when no LB config was found).
-  void ProcessLbPolicyName(const Resolver::Result& resolver_result);
-
-  // Parses the service config. Intended to be used by
-  // ServiceConfig::ParseGlobalParams.
-  static void ParseServiceConfig(const grpc_json* field,
-                                 ProcessedResolverResult* parsing_state);
-  // Parses the LB config from service config.
-  void ParseLbConfigFromServiceConfig(const grpc_json* field);
-  // Parses the retry throttle parameters from service config.
-  void ParseRetryThrottleParamsFromServiceConfig(const grpc_json* field);
-
-  // Service config.
-  UniquePtr<char> service_config_json_;
-  RefCountedPtr<ServiceConfig> service_config_;
-  // LB policy.
-  UniquePtr<char> lb_policy_name_;
-  RefCountedPtr<LoadBalancingPolicy::Config> lb_policy_config_;
-  // Retry throttle data.
-  char* server_name_ = nullptr;
-  RefCountedPtr<ServerRetryThrottleData> retry_throttle_data_;
-  // Method params table.
-  RefCountedPtr<ClientChannelMethodParamsTable> method_params_table_;
+  RefCountedPtr<ParsedLoadBalancingConfig> parsed_lb_config_;
+  UniquePtr<char> parsed_deprecated_lb_policy_;
+  Optional<RetryThrottling> retry_throttling_;
+  const char* health_check_service_name_;
 };
 
-// The parameters of a method.
-class ClientChannelMethodParams : public RefCounted<ClientChannelMethodParams> {
+class ClientChannelMethodParsedObject : public ServiceConfig::ParsedConfig {
  public:
-  enum WaitForReady {
-    WAIT_FOR_READY_UNSET = 0,
-    WAIT_FOR_READY_FALSE,
-    WAIT_FOR_READY_TRUE
-  };
-
   struct RetryPolicy {
     int max_attempts = 0;
     grpc_millis initial_backoff = 0;
@@ -113,30 +87,35 @@ class ClientChannelMethodParams : public RefCounted<ClientChannelMethodParams> {
     StatusCodeSet retryable_status_codes;
   };
 
-  /// Creates a method_parameters object from \a json.
-  /// Intended for use with ServiceConfig::CreateMethodConfigTable().
-  static RefCountedPtr<ClientChannelMethodParams> CreateFromJson(
-      const grpc_json* json);
+  ClientChannelMethodParsedObject(grpc_millis timeout,
+                                  const Optional<bool>& wait_for_ready,
+                                  UniquePtr<RetryPolicy> retry_policy)
+      : timeout_(timeout),
+        wait_for_ready_(wait_for_ready),
+        retry_policy_(std::move(retry_policy)) {}
 
   grpc_millis timeout() const { return timeout_; }
-  WaitForReady wait_for_ready() const { return wait_for_ready_; }
+
+  Optional<bool> wait_for_ready() const { return wait_for_ready_; }
+
   const RetryPolicy* retry_policy() const { return retry_policy_.get(); }
 
  private:
-  // So New() can call our private ctor.
-  template <typename T, typename... Args>
-  friend T* grpc_core::New(Args&&... args);
+  grpc_millis timeout_ = 0;
+  Optional<bool> wait_for_ready_;
+  UniquePtr<RetryPolicy> retry_policy_;
+};
 
-  // So Delete() can call our private dtor.
-  template <typename T>
-  friend void grpc_core::Delete(T*);
+class ClientChannelServiceConfigParser : public ServiceConfig::Parser {
+ public:
+  UniquePtr<ServiceConfig::ParsedConfig> ParseGlobalParams(
+      const grpc_json* json, grpc_error** error) override;
 
-  ClientChannelMethodParams() {}
-  virtual ~ClientChannelMethodParams() {}
+  UniquePtr<ServiceConfig::ParsedConfig> ParsePerMethodParams(
+      const grpc_json* json, grpc_error** error) override;
 
-  grpc_millis timeout_ = 0;
-  WaitForReady wait_for_ready_ = WAIT_FOR_READY_UNSET;
-  UniquePtr<RetryPolicy> retry_policy_;
+  static size_t ParserIndex();
+  static void Register();
 };
 
 }  // namespace internal
index d15af90..b6bc3ea 100644 (file)
@@ -48,7 +48,7 @@
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gprpp/inlined_vector.h"
 #include "src/core/lib/gprpp/manual_constructor.h"
-#include "src/core/lib/gprpp/mutex_lock.h"
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/iomgr/iomgr.h"
 #include "src/core/lib/iomgr/polling_entity.h"
@@ -77,7 +77,7 @@ class ResolvingLoadBalancingPolicy::ResolverResultHandler
       : parent_(std::move(parent)) {}
 
   ~ResolverResultHandler() {
-    if (parent_->tracer_->enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(*(parent_->tracer_))) {
       gpr_log(GPR_INFO, "resolving_lb=%p: resolver shutdown complete",
               parent_.get());
     }
@@ -119,27 +119,20 @@ class ResolvingLoadBalancingPolicy::ResolvingControlHelper
     return parent_->channel_control_helper()->CreateChannel(target, args);
   }
 
-  void UpdateState(grpc_connectivity_state state, grpc_error* state_error,
+  void UpdateState(grpc_connectivity_state state,
                    UniquePtr<SubchannelPicker> picker) override {
-    if (parent_->resolver_ == nullptr) {
-      // shutting down.
-      GRPC_ERROR_UNREF(state_error);
-      return;
-    }
+    if (parent_->resolver_ == nullptr) return;  // Shutting down.
     // If this request is from the pending child policy, ignore it until
     // it reports READY, at which point we swap it into place.
     if (CalledByPendingChild()) {
-      if (parent_->tracer_->enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(*(parent_->tracer_))) {
         gpr_log(GPR_INFO,
                 "resolving_lb=%p helper=%p: pending child policy %p reports "
                 "state=%s",
                 parent_.get(), this, child_,
                 grpc_connectivity_state_name(state));
       }
-      if (state != GRPC_CHANNEL_READY) {
-        GRPC_ERROR_UNREF(state_error);
-        return;
-      }
+      if (state != GRPC_CHANNEL_READY) return;
       grpc_pollset_set_del_pollset_set(
           parent_->lb_policy_->interested_parties(),
           parent_->interested_parties());
@@ -147,11 +140,9 @@ class ResolvingLoadBalancingPolicy::ResolvingControlHelper
       parent_->lb_policy_ = std::move(parent_->pending_lb_policy_);
     } else if (!CalledByCurrentChild()) {
       // This request is from an outdated child, so ignore it.
-      GRPC_ERROR_UNREF(state_error);
       return;
     }
-    parent_->channel_control_helper()->UpdateState(state, state_error,
-                                                   std::move(picker));
+    parent_->channel_control_helper()->UpdateState(state, std::move(picker));
   }
 
   void RequestReresolution() override {
@@ -160,7 +151,7 @@ class ResolvingLoadBalancingPolicy::ResolvingControlHelper
     if (parent_->pending_lb_policy_ != nullptr && !CalledByPendingChild()) {
       return;
     }
-    if (parent_->tracer_->enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(*(parent_->tracer_))) {
       gpr_log(GPR_INFO, "resolving_lb=%p: started name re-resolving",
               parent_.get());
     }
@@ -192,7 +183,8 @@ class ResolvingLoadBalancingPolicy::ResolvingControlHelper
 
 ResolvingLoadBalancingPolicy::ResolvingLoadBalancingPolicy(
     Args args, TraceFlag* tracer, UniquePtr<char> target_uri,
-    UniquePtr<char> child_policy_name, RefCountedPtr<Config> child_lb_config,
+    UniquePtr<char> child_policy_name,
+    RefCountedPtr<ParsedLoadBalancingConfig> child_lb_config,
     grpc_error** error)
     : LoadBalancingPolicy(std::move(args)),
       tracer_(tracer),
@@ -234,8 +226,7 @@ grpc_error* ResolvingLoadBalancingPolicy::Init(const grpc_channel_args& args) {
   }
   // Return our picker to the channel.
   channel_control_helper()->UpdateState(
-      GRPC_CHANNEL_IDLE, GRPC_ERROR_NONE,
-      UniquePtr<SubchannelPicker>(New<QueuePicker>(Ref())));
+      GRPC_CHANNEL_IDLE, UniquePtr<SubchannelPicker>(New<QueuePicker>(Ref())));
   return GRPC_ERROR_NONE;
 }
 
@@ -250,7 +241,7 @@ void ResolvingLoadBalancingPolicy::ShutdownLocked() {
     resolver_.reset();
     MutexLock lock(&lb_policy_mu_);
     if (lb_policy_ != nullptr) {
-      if (tracer_->enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(*tracer_)) {
         gpr_log(GPR_INFO, "resolving_lb=%p: shutting down lb_policy=%p", this,
                 lb_policy_.get());
       }
@@ -259,7 +250,7 @@ void ResolvingLoadBalancingPolicy::ShutdownLocked() {
       lb_policy_.reset();
     }
     if (pending_lb_policy_ != nullptr) {
-      if (tracer_->enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(*tracer_)) {
         gpr_log(GPR_INFO, "resolving_lb=%p: shutting down pending lb_policy=%p",
                 this, pending_lb_policy_.get());
       }
@@ -307,13 +298,13 @@ void ResolvingLoadBalancingPolicy::FillChildRefsForChannelz(
 }
 
 void ResolvingLoadBalancingPolicy::StartResolvingLocked() {
-  if (tracer_->enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(*tracer_)) {
     gpr_log(GPR_INFO, "resolving_lb=%p: starting name resolution", this);
   }
   GPR_ASSERT(!started_resolving_);
   started_resolving_ = true;
   channel_control_helper()->UpdateState(
-      GRPC_CHANNEL_CONNECTING, GRPC_ERROR_NONE,
+      GRPC_CHANNEL_CONNECTING,
       UniquePtr<SubchannelPicker>(New<QueuePicker>(Ref())));
   resolver_->StartLocked();
 }
@@ -323,7 +314,7 @@ void ResolvingLoadBalancingPolicy::OnResolverError(grpc_error* error) {
     GRPC_ERROR_UNREF(error);
     return;
   }
-  if (tracer_->enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(*tracer_)) {
     gpr_log(GPR_INFO, "resolving_lb=%p: resolver transient failure: %s", this,
             grpc_error_string(error));
   }
@@ -334,14 +325,15 @@ void ResolvingLoadBalancingPolicy::OnResolverError(grpc_error* error) {
     grpc_error* state_error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
         "Resolver transient failure", &error, 1);
     channel_control_helper()->UpdateState(
-        GRPC_CHANNEL_TRANSIENT_FAILURE, GRPC_ERROR_REF(state_error),
+        GRPC_CHANNEL_TRANSIENT_FAILURE,
         UniquePtr<SubchannelPicker>(New<TransientFailurePicker>(state_error)));
   }
   GRPC_ERROR_UNREF(error);
 }
 
 void ResolvingLoadBalancingPolicy::CreateOrUpdateLbPolicyLocked(
-    const char* lb_policy_name, RefCountedPtr<Config> lb_policy_config,
+    const char* lb_policy_name,
+    RefCountedPtr<ParsedLoadBalancingConfig> lb_policy_config,
     Resolver::Result result, TraceStringVector* trace_strings) {
   // If the child policy name changes, we need to create a new child
   // policy.  When this happens, we leave child_policy_ as-is and store
@@ -406,7 +398,7 @@ void ResolvingLoadBalancingPolicy::CreateOrUpdateLbPolicyLocked(
     // Cases 1, 2b, and 3b: create a new child policy.
     // If lb_policy_ is null, we set it (case 1), else we set
     // pending_lb_policy_ (cases 2b and 3b).
-    if (tracer_->enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(*tracer_)) {
       gpr_log(GPR_INFO, "resolving_lb=%p: Creating new %schild policy %s", this,
               lb_policy_ == nullptr ? "" : "pending ", lb_policy_name);
     }
@@ -427,7 +419,7 @@ void ResolvingLoadBalancingPolicy::CreateOrUpdateLbPolicyLocked(
   }
   GPR_ASSERT(policy_to_update != nullptr);
   // Update the policy.
-  if (tracer_->enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(*tracer_)) {
     gpr_log(GPR_INFO, "resolving_lb=%p: Updating %schild policy %p", this,
             policy_to_update == pending_lb_policy_.get() ? "pending " : "",
             policy_to_update);
@@ -466,7 +458,7 @@ ResolvingLoadBalancingPolicy::CreateLbPolicyLocked(
     return nullptr;
   }
   helper->set_child(lb_policy.get());
-  if (tracer_->enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(*tracer_)) {
     gpr_log(GPR_INFO, "resolving_lb=%p: created new LB policy \"%s\" (%p)",
             this, lb_policy_name, lb_policy.get());
   }
@@ -522,7 +514,7 @@ void ResolvingLoadBalancingPolicy::OnResolverResultChangedLocked(
     Resolver::Result result) {
   // Handle race conditions.
   if (resolver_ == nullptr) return;
-  if (tracer_->enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(*tracer_)) {
     gpr_log(GPR_INFO, "resolving_lb=%p: got resolver result", this);
   }
   // We only want to trace the address resolution in the follow cases:
@@ -538,20 +530,34 @@ void ResolvingLoadBalancingPolicy::OnResolverResultChangedLocked(
   const bool resolution_contains_addresses = result.addresses.size() > 0;
   // Process the resolver result.
   const char* lb_policy_name = nullptr;
-  RefCountedPtr<Config> lb_policy_config;
+  RefCountedPtr<ParsedLoadBalancingConfig> lb_policy_config;
   bool service_config_changed = false;
+  char* service_config_error_string = nullptr;
   if (process_resolver_result_ != nullptr) {
-    service_config_changed =
-        process_resolver_result_(process_resolver_result_user_data_, &result,
-                                 &lb_policy_name, &lb_policy_config);
+    grpc_error* service_config_error = GRPC_ERROR_NONE;
+    service_config_changed = process_resolver_result_(
+        process_resolver_result_user_data_, result, &lb_policy_name,
+        &lb_policy_config, &service_config_error);
+    if (service_config_error != GRPC_ERROR_NONE) {
+      service_config_error_string =
+          gpr_strdup(grpc_error_string(service_config_error));
+      if (lb_policy_name == nullptr) {
+        // Use an empty lb_policy_name as an indicator that we received an
+        // invalid service config and we don't have a fallback service config.
+        OnResolverError(service_config_error);
+      } else {
+        GRPC_ERROR_UNREF(service_config_error);
+      }
+    }
   } else {
     lb_policy_name = child_policy_name_.get();
     lb_policy_config = child_lb_config_;
   }
-  GPR_ASSERT(lb_policy_name != nullptr);
-  // Create or update LB policy, as needed.
-  CreateOrUpdateLbPolicyLocked(lb_policy_name, std::move(lb_policy_config),
-                               std::move(result), &trace_strings);
+  if (lb_policy_name != nullptr) {
+    // Create or update LB policy, as needed.
+    CreateOrUpdateLbPolicyLocked(lb_policy_name, lb_policy_config,
+                                 std::move(result), &trace_strings);
+  }
   // Add channel trace event.
   if (channelz_node() != nullptr) {
     if (service_config_changed) {
@@ -559,10 +565,15 @@ void ResolvingLoadBalancingPolicy::OnResolverResultChangedLocked(
       // config in the trace, at the risk of bloating the trace logs.
       trace_strings.push_back(gpr_strdup("Service config changed"));
     }
+    if (service_config_error_string != nullptr) {
+      trace_strings.push_back(service_config_error_string);
+      service_config_error_string = nullptr;
+    }
     MaybeAddTraceMessagesForAddressChangesLocked(resolution_contains_addresses,
                                                  &trace_strings);
     ConcatenateAndAddChannelTraceLocked(&trace_strings);
   }
+  gpr_free(service_config_error_string);
 }
 
 }  // namespace grpc_core
index c934976..b7d99dc 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "src/core/ext/filters/client_channel/client_channel_channelz.h"
 #include "src/core/ext/filters/client_channel/lb_policy.h"
+#include "src/core/ext/filters/client_channel/lb_policy_factory.h"
 #include "src/core/ext/filters/client_channel/resolver.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/channel_stack.h"
@@ -53,20 +54,25 @@ class ResolvingLoadBalancingPolicy : public LoadBalancingPolicy {
  public:
   // If error is set when this returns, then construction failed, and
   // the caller may not use the new object.
-  ResolvingLoadBalancingPolicy(Args args, TraceFlag* tracer,
-                               UniquePtr<char> target_uri,
-                               UniquePtr<char> child_policy_name,
-                               RefCountedPtr<Config> child_lb_config,
-                               grpc_error** error);
+  ResolvingLoadBalancingPolicy(
+      Args args, TraceFlag* tracer, UniquePtr<char> target_uri,
+      UniquePtr<char> child_policy_name,
+      RefCountedPtr<ParsedLoadBalancingConfig> child_lb_config,
+      grpc_error** error);
 
   // Private ctor, to be used by client_channel only!
   //
   // Synchronous callback that takes the resolver result and sets
   // lb_policy_name and lb_policy_config to point to the right data.
   // Returns true if the service config has changed since the last result.
+  // If the returned service_config_error is not none and lb_policy_name is
+  // empty, it means that we don't have a valid service config to use, and we
+  // should set the channel to be in TRANSIENT_FAILURE.
   typedef bool (*ProcessResolverResultCallback)(
-      void* user_data, Resolver::Result* result, const char** lb_policy_name,
-      RefCountedPtr<Config>* lb_policy_config);
+      void* user_data, const Resolver::Result& result,
+      const char** lb_policy_name,
+      RefCountedPtr<ParsedLoadBalancingConfig>* lb_policy_config,
+      grpc_error** service_config_error);
   // If error is set when this returns, then construction failed, and
   // the caller may not use the new object.
   ResolvingLoadBalancingPolicy(
@@ -102,10 +108,10 @@ class ResolvingLoadBalancingPolicy : public LoadBalancingPolicy {
 
   void StartResolvingLocked();
   void OnResolverError(grpc_error* error);
-  void CreateOrUpdateLbPolicyLocked(const char* lb_policy_name,
-                                    RefCountedPtr<Config> lb_policy_config,
-                                    Resolver::Result result,
-                                    TraceStringVector* trace_strings);
+  void CreateOrUpdateLbPolicyLocked(
+      const char* lb_policy_name,
+      RefCountedPtr<ParsedLoadBalancingConfig> lb_policy_config,
+      Resolver::Result result, TraceStringVector* trace_strings);
   OrphanablePtr<LoadBalancingPolicy> CreateLbPolicyLocked(
       const char* lb_policy_name, const grpc_channel_args& args,
       TraceStringVector* trace_strings);
@@ -121,7 +127,7 @@ class ResolvingLoadBalancingPolicy : public LoadBalancingPolicy {
   ProcessResolverResultCallback process_resolver_result_ = nullptr;
   void* process_resolver_result_user_data_ = nullptr;
   UniquePtr<char> child_policy_name_;
-  RefCountedPtr<Config> child_lb_config_;
+  RefCountedPtr<ParsedLoadBalancingConfig> child_lb_config_;
 
   // Resolver and associated state.
   OrphanablePtr<Resolver> resolver_;
index bbf671d..86d4f73 100644 (file)
 
 namespace grpc_core {
 
-RefCountedPtr<ServiceConfig> ServiceConfig::Create(const char* json) {
+namespace {
+typedef InlinedVector<UniquePtr<ServiceConfig::Parser>,
+                      ServiceConfig::kNumPreallocatedParsers>
+    ServiceConfigParserList;
+ServiceConfigParserList* g_registered_parsers;
+}  // namespace
+
+RefCountedPtr<ServiceConfig> ServiceConfig::Create(const char* json,
+                                                   grpc_error** error) {
   UniquePtr<char> service_config_json(gpr_strdup(json));
   UniquePtr<char> json_string(gpr_strdup(json));
+  GPR_DEBUG_ASSERT(error != nullptr);
   grpc_json* json_tree = grpc_json_parse_string(json_string.get());
   if (json_tree == nullptr) {
-    gpr_log(GPR_INFO, "failed to parse JSON for service config");
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "failed to parse JSON for service config");
     return nullptr;
   }
-  return MakeRefCounted<ServiceConfig>(std::move(service_config_json),
-                                       std::move(json_string), json_tree);
+  return MakeRefCounted<ServiceConfig>(
+      std::move(service_config_json), std::move(json_string), json_tree, error);
 }
 
 ServiceConfig::ServiceConfig(UniquePtr<char> service_config_json,
-                             UniquePtr<char> json_string, grpc_json* json_tree)
+                             UniquePtr<char> json_string, grpc_json* json_tree,
+                             grpc_error** error)
     : service_config_json_(std::move(service_config_json)),
       json_string_(std::move(json_string)),
-      json_tree_(json_tree) {}
+      json_tree_(json_tree) {
+  GPR_DEBUG_ASSERT(error != nullptr);
+  if (json_tree->type != GRPC_JSON_OBJECT || json_tree->key != nullptr) {
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Malformed service Config JSON object");
+    return;
+  }
+  grpc_error* error_list[2];
+  int error_count = 0;
+  grpc_error* global_error = ParseGlobalParams(json_tree);
+  grpc_error* local_error = ParsePerMethodParams(json_tree);
+  if (global_error != GRPC_ERROR_NONE) {
+    error_list[error_count++] = global_error;
+  }
+  if (local_error != GRPC_ERROR_NONE) {
+    error_list[error_count++] = local_error;
+  }
+  if (error_count > 0) {
+    *error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+        "Service config parsing error", error_list, error_count);
+    GRPC_ERROR_UNREF(global_error);
+    GRPC_ERROR_UNREF(local_error);
+  }
+}
 
-ServiceConfig::~ServiceConfig() { grpc_json_destroy(json_tree_); }
+grpc_error* ServiceConfig::ParseGlobalParams(const grpc_json* json_tree) {
+  GPR_DEBUG_ASSERT(json_tree_->type == GRPC_JSON_OBJECT);
+  GPR_DEBUG_ASSERT(json_tree_->key == nullptr);
+  InlinedVector<grpc_error*, 4> error_list;
+  for (size_t i = 0; i < g_registered_parsers->size(); i++) {
+    grpc_error* parser_error = GRPC_ERROR_NONE;
+    auto parsed_obj =
+        (*g_registered_parsers)[i]->ParseGlobalParams(json_tree, &parser_error);
+    if (parser_error != GRPC_ERROR_NONE) {
+      error_list.push_back(parser_error);
+    }
+    parsed_global_service_config_objects_.push_back(std::move(parsed_obj));
+  }
+  return GRPC_ERROR_CREATE_FROM_VECTOR("Global Params", &error_list);
+}
 
-const char* ServiceConfig::GetLoadBalancingPolicyName() const {
-  if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) {
-    return nullptr;
+grpc_error* ServiceConfig::ParseJsonMethodConfigToServiceConfigObjectsTable(
+    const grpc_json* json,
+    SliceHashTable<const ServiceConfigObjectsVector*>::Entry* entries,
+    size_t* idx) {
+  auto objs_vector = MakeUnique<ServiceConfigObjectsVector>();
+  InlinedVector<grpc_error*, 4> error_list;
+  for (size_t i = 0; i < g_registered_parsers->size(); i++) {
+    grpc_error* parser_error = GRPC_ERROR_NONE;
+    auto parsed_obj =
+        (*g_registered_parsers)[i]->ParsePerMethodParams(json, &parser_error);
+    if (parser_error != GRPC_ERROR_NONE) {
+      error_list.push_back(parser_error);
+    }
+    objs_vector->push_back(std::move(parsed_obj));
   }
-  const char* lb_policy_name = nullptr;
-  for (grpc_json* field = json_tree_->child; field != nullptr;
+  service_config_objects_vectors_storage_.push_back(std::move(objs_vector));
+  const auto* vector_ptr =
+      service_config_objects_vectors_storage_
+          [service_config_objects_vectors_storage_.size() - 1]
+              .get();
+  // Construct list of paths.
+  InlinedVector<UniquePtr<char>, 10> paths;
+  for (grpc_json* child = json->child; child != nullptr; child = child->next) {
+    if (child->key == nullptr) continue;
+    if (strcmp(child->key, "name") == 0) {
+      if (child->type != GRPC_JSON_ARRAY) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:name error:not of type Array"));
+        goto wrap_error;
+      }
+      for (grpc_json* name = child->child; name != nullptr; name = name->next) {
+        grpc_error* parse_error = GRPC_ERROR_NONE;
+        UniquePtr<char> path = ParseJsonMethodName(name, &parse_error);
+        if (path == nullptr) {
+          error_list.push_back(parse_error);
+        } else {
+          GPR_DEBUG_ASSERT(parse_error == GRPC_ERROR_NONE);
+          paths.push_back(std::move(path));
+        }
+      }
+    }
+  }
+  if (paths.size() == 0) {
+    error_list.push_back(
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("No names specified"));
+  }
+  // Add entry for each path.
+  for (size_t i = 0; i < paths.size(); ++i) {
+    entries[*idx].key = grpc_slice_from_copied_string(paths[i].get());
+    entries[*idx].value = vector_ptr;
+    ++*idx;
+  }
+wrap_error:
+  return GRPC_ERROR_CREATE_FROM_VECTOR("methodConfig", &error_list);
+}
+
+grpc_error* ServiceConfig::ParsePerMethodParams(const grpc_json* json_tree) {
+  GPR_DEBUG_ASSERT(json_tree_->type == GRPC_JSON_OBJECT);
+  GPR_DEBUG_ASSERT(json_tree_->key == nullptr);
+  SliceHashTable<const ServiceConfigObjectsVector*>::Entry* entries = nullptr;
+  size_t num_entries = 0;
+  InlinedVector<grpc_error*, 4> error_list;
+  for (grpc_json* field = json_tree->child; field != nullptr;
        field = field->next) {
-    if (field->key == nullptr) return nullptr;
-    if (strcmp(field->key, "loadBalancingPolicy") == 0) {
-      if (lb_policy_name != nullptr) return nullptr;  // Duplicate.
-      if (field->type != GRPC_JSON_STRING) return nullptr;
-      lb_policy_name = field->value;
+    if (field->key == nullptr) {
+      error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "error:Illegal key value - NULL"));
+      continue;
     }
+    if (strcmp(field->key, "methodConfig") == 0) {
+      if (entries != nullptr) {
+        GPR_ASSERT(false);
+      }
+      if (field->type != GRPC_JSON_ARRAY) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:methodConfig error:not of type Array"));
+      }
+      for (grpc_json* method = field->child; method != nullptr;
+           method = method->next) {
+        int count = CountNamesInMethodConfig(method);
+        if (count <= 0) {
+          error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "field:methodConfig error:No names found"));
+        }
+        num_entries += static_cast<size_t>(count);
+      }
+      entries = static_cast<
+          SliceHashTable<const ServiceConfigObjectsVector*>::Entry*>(gpr_zalloc(
+          num_entries *
+          sizeof(SliceHashTable<const ServiceConfigObjectsVector*>::Entry)));
+      size_t idx = 0;
+      for (grpc_json* method = field->child; method != nullptr;
+           method = method->next) {
+        grpc_error* error = ParseJsonMethodConfigToServiceConfigObjectsTable(
+            method, entries, &idx);
+        if (error != GRPC_ERROR_NONE) {
+          error_list.push_back(error);
+        }
+      }
+      // idx might not be equal to num_entries due to parsing errors
+      num_entries = idx;
+      break;
+    }
+  }
+  if (entries != nullptr) {
+    parsed_method_service_config_objects_table_ =
+        SliceHashTable<const ServiceConfigObjectsVector*>::Create(
+            num_entries, entries, nullptr);
+    gpr_free(entries);
   }
-  return lb_policy_name;
+  return GRPC_ERROR_CREATE_FROM_VECTOR("Method Params", &error_list);
 }
 
+ServiceConfig::~ServiceConfig() { grpc_json_destroy(json_tree_); }
+
 int ServiceConfig::CountNamesInMethodConfig(grpc_json* json) {
   int num_names = 0;
   for (grpc_json* field = json->child; field != nullptr; field = field->next) {
@@ -84,28 +230,102 @@ int ServiceConfig::CountNamesInMethodConfig(grpc_json* json) {
   return num_names;
 }
 
-UniquePtr<char> ServiceConfig::ParseJsonMethodName(grpc_json* json) {
-  if (json->type != GRPC_JSON_OBJECT) return nullptr;
+UniquePtr<char> ServiceConfig::ParseJsonMethodName(grpc_json* json,
+                                                   grpc_error** error) {
+  if (json->type != GRPC_JSON_OBJECT) {
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "field:name error:type is not object");
+    return nullptr;
+  }
   const char* service_name = nullptr;
   const char* method_name = nullptr;
   for (grpc_json* child = json->child; child != nullptr; child = child->next) {
-    if (child->key == nullptr) return nullptr;
-    if (child->type != GRPC_JSON_STRING) return nullptr;
+    if (child->key == nullptr) {
+      *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "field:name error:Child entry with no key");
+      return nullptr;
+    }
+    if (child->type != GRPC_JSON_STRING) {
+      *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "field:name error:Child entry not of type string");
+      return nullptr;
+    }
     if (strcmp(child->key, "service") == 0) {
-      if (service_name != nullptr) return nullptr;  // Duplicate.
-      if (child->value == nullptr) return nullptr;
+      if (service_name != nullptr) {
+        *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:name error: field:service error:Multiple entries");
+        return nullptr;  // Duplicate.
+      }
+      if (child->value == nullptr) {
+        *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:name error: field:service error:empty value");
+        return nullptr;
+      }
       service_name = child->value;
     } else if (strcmp(child->key, "method") == 0) {
-      if (method_name != nullptr) return nullptr;  // Duplicate.
-      if (child->value == nullptr) return nullptr;
+      if (method_name != nullptr) {
+        *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:name error: field:method error:multiple entries");
+        return nullptr;  // Duplicate.
+      }
+      if (child->value == nullptr) {
+        *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:name error: field:method error:empty value");
+        return nullptr;
+      }
       method_name = child->value;
     }
   }
-  if (service_name == nullptr) return nullptr;  // Required field.
+  if (service_name == nullptr) {
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "field:name error: field:service error:not found");
+    return nullptr;  // Required field.
+  }
   char* path;
   gpr_asprintf(&path, "/%s/%s", service_name,
                method_name == nullptr ? "*" : method_name);
   return UniquePtr<char>(path);
 }
 
+const ServiceConfig::ServiceConfigObjectsVector*
+ServiceConfig::GetMethodServiceConfigObjectsVector(const grpc_slice& path) {
+  if (parsed_method_service_config_objects_table_.get() == nullptr) {
+    return nullptr;
+  }
+  const auto* value = parsed_method_service_config_objects_table_->Get(path);
+  // If we didn't find a match for the path, try looking for a wildcard
+  // entry (i.e., change "/service/method" to "/service/*").
+  if (value == nullptr) {
+    char* path_str = grpc_slice_to_c_string(path);
+    const char* sep = strrchr(path_str, '/') + 1;
+    const size_t len = (size_t)(sep - path_str);
+    char* buf = (char*)gpr_malloc(len + 2);  // '*' and NUL
+    memcpy(buf, path_str, len);
+    buf[len] = '*';
+    buf[len + 1] = '\0';
+    grpc_slice wildcard_path = grpc_slice_from_copied_string(buf);
+    gpr_free(buf);
+    value = parsed_method_service_config_objects_table_->Get(wildcard_path);
+    grpc_slice_unref_internal(wildcard_path);
+    gpr_free(path_str);
+    if (value == nullptr) return nullptr;
+  }
+  return *value;
+}
+
+size_t ServiceConfig::RegisterParser(UniquePtr<Parser> parser) {
+  g_registered_parsers->push_back(std::move(parser));
+  return g_registered_parsers->size() - 1;
+}
+
+void ServiceConfig::Init() {
+  GPR_ASSERT(g_registered_parsers == nullptr);
+  g_registered_parsers = New<ServiceConfigParserList>();
+}
+
+void ServiceConfig::Shutdown() {
+  Delete(g_registered_parsers);
+  g_registered_parsers = nullptr;
+}
+
 }  // namespace grpc_core
index d906347..e6f855a 100644 (file)
@@ -25,6 +25,7 @@
 #include "src/core/lib/gprpp/inlined_vector.h"
 #include "src/core/lib/gprpp/ref_counted.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/error.h"
 #include "src/core/lib/json/json.h"
 #include "src/core/lib/slice/slice_hash_table.h"
 
@@ -56,45 +57,106 @@ namespace grpc_core {
 
 class ServiceConfig : public RefCounted<ServiceConfig> {
  public:
+  /// This is the base class that all service config parsers MUST use to store
+  /// parsed service config data.
+  class ParsedConfig {
+   public:
+    virtual ~ParsedConfig() = default;
+
+    GRPC_ABSTRACT_BASE_CLASS;
+  };
+
+  /// This is the base class that all service config parsers should derive from.
+  class Parser {
+   public:
+    virtual ~Parser() = default;
+
+    virtual UniquePtr<ParsedConfig> ParseGlobalParams(const grpc_json* json,
+                                                      grpc_error** error) {
+      GPR_DEBUG_ASSERT(error != nullptr);
+      return nullptr;
+    }
+
+    virtual UniquePtr<ParsedConfig> ParsePerMethodParams(const grpc_json* json,
+                                                         grpc_error** error) {
+      GPR_DEBUG_ASSERT(error != nullptr);
+      return nullptr;
+    }
+
+    GRPC_ABSTRACT_BASE_CLASS;
+  };
+
+  static constexpr int kNumPreallocatedParsers = 4;
+  typedef InlinedVector<UniquePtr<ParsedConfig>, kNumPreallocatedParsers>
+      ServiceConfigObjectsVector;
+
+  /// When a service config is applied to a call in the client_channel_filter,
+  /// we create an instance of this object and store it in the call_data for
+  /// client_channel. A pointer to this object is also stored in the
+  /// call_context, so that future filters can easily access method and global
+  /// parameters for the call.
+  class CallData {
+   public:
+    CallData() = default;
+    CallData(RefCountedPtr<ServiceConfig> svc_cfg, const grpc_slice& path)
+        : service_config_(std::move(svc_cfg)) {
+      if (service_config_ != nullptr) {
+        method_params_vector_ =
+            service_config_->GetMethodServiceConfigObjectsVector(path);
+      }
+    }
+
+    ServiceConfig* service_config() { return service_config_.get(); }
+
+    ParsedConfig* GetMethodParsedObject(size_t index) const {
+      return method_params_vector_ != nullptr
+                 ? (*method_params_vector_)[index].get()
+                 : nullptr;
+    }
+
+    ParsedConfig* GetGlobalParsedObject(size_t index) const {
+      return service_config_->GetParsedGlobalServiceConfigObject(index);
+    }
+
+   private:
+    RefCountedPtr<ServiceConfig> service_config_;
+    const ServiceConfigObjectsVector* method_params_vector_ = nullptr;
+  };
+
   /// Creates a new service config from parsing \a json_string.
   /// Returns null on parse error.
-  static RefCountedPtr<ServiceConfig> Create(const char* json);
+  static RefCountedPtr<ServiceConfig> Create(const char* json,
+                                             grpc_error** error);
 
   ~ServiceConfig();
 
   const char* service_config_json() const { return service_config_json_.get(); }
 
-  /// Invokes \a process_json() for each global parameter in the service
-  /// config.  \a arg is passed as the second argument to \a process_json().
-  template <typename T>
-  using ProcessJson = void (*)(const grpc_json*, T*);
-  template <typename T>
-  void ParseGlobalParams(ProcessJson<T> process_json, T* arg) const;
-
-  /// Gets the LB policy name from \a service_config.
-  /// Returns NULL if no LB policy name was specified.
-  /// Caller does NOT take ownership.
-  const char* GetLoadBalancingPolicyName() const;
-
-  /// Creates a method config table based on the data in \a json.
-  /// The table's keys are request paths.  The table's value type is
-  /// returned by \a create_value(), based on data parsed from the JSON tree.
-  /// Returns null on error.
-  template <typename T>
-  using CreateValue = RefCountedPtr<T> (*)(const grpc_json* method_config_json);
-  template <typename T>
-  RefCountedPtr<SliceHashTable<RefCountedPtr<T>>> CreateMethodConfigTable(
-      CreateValue<T> create_value) const;
-
-  /// A helper function for looking up values in the table returned by
-  /// \a CreateMethodConfigTable().
-  /// Gets the method config for the specified \a path, which should be of
-  /// the form "/service/method".
-  /// Returns null if the method has no config.
-  /// Caller does NOT own a reference to the result.
-  template <typename T>
-  static RefCountedPtr<T> MethodConfigTableLookup(
-      const SliceHashTable<RefCountedPtr<T>>& table, const grpc_slice& path);
+  /// Retrieves the parsed global service config object at index \a index. The
+  /// lifetime of the returned object is tied to the lifetime of the
+  /// ServiceConfig object.
+  ParsedConfig* GetParsedGlobalServiceConfigObject(size_t index) {
+    GPR_DEBUG_ASSERT(index < parsed_global_service_config_objects_.size());
+    return parsed_global_service_config_objects_[index].get();
+  }
+
+  /// Retrieves the vector of method service config objects for a given path \a
+  /// path. The lifetime of the returned vector and contained objects is tied to
+  /// the lifetime of the ServiceConfig object.
+  const ServiceConfigObjectsVector* GetMethodServiceConfigObjectsVector(
+      const grpc_slice& path);
+
+  /// Globally register a service config parser. On successful registration, it
+  /// returns the index at which the parser was registered. On failure, -1 is
+  /// returned. Each new service config update will go through all the
+  /// registered parser. Each parser is responsible for reading the service
+  /// config json and returning a parsed object. This parsed object can later be
+  /// retrieved using the same index that was returned at registration time.
+  static size_t RegisterParser(UniquePtr<Parser> parser);
+
+  static void Init();
+
+  static void Shutdown();
 
  private:
   // So New() can call our private ctor.
@@ -103,151 +165,42 @@ class ServiceConfig : public RefCounted<ServiceConfig> {
 
   // Takes ownership of \a json_tree.
   ServiceConfig(UniquePtr<char> service_config_json,
-                UniquePtr<char> json_string, grpc_json* json_tree);
+                UniquePtr<char> json_string, grpc_json* json_tree,
+                grpc_error** error);
+
+  // Helper functions to parse the service config
+  grpc_error* ParseGlobalParams(const grpc_json* json_tree);
+  grpc_error* ParsePerMethodParams(const grpc_json* json_tree);
 
   // Returns the number of names specified in the method config \a json.
   static int CountNamesInMethodConfig(grpc_json* json);
 
   // Returns a path string for the JSON name object specified by \a json.
-  // Returns null on error.
-  static UniquePtr<char> ParseJsonMethodName(grpc_json* json);
+  // Returns null on error, and stores error in \a error.
+  static UniquePtr<char> ParseJsonMethodName(grpc_json* json,
+                                             grpc_error** error);
 
-  // Parses the method config from \a json.  Adds an entry to \a entries for
-  // each name found, incrementing \a idx for each entry added.
-  // Returns false on error.
-  template <typename T>
-  static bool ParseJsonMethodConfig(
-      grpc_json* json, CreateValue<T> create_value,
-      typename SliceHashTable<RefCountedPtr<T>>::Entry* entries, size_t* idx);
+  grpc_error* ParseJsonMethodConfigToServiceConfigObjectsTable(
+      const grpc_json* json,
+      SliceHashTable<const ServiceConfigObjectsVector*>::Entry* entries,
+      size_t* idx);
 
   UniquePtr<char> service_config_json_;
   UniquePtr<char> json_string_;  // Underlying storage for json_tree.
   grpc_json* json_tree_;
-};
 
-//
-// implementation -- no user-serviceable parts below
-//
-
-template <typename T>
-void ServiceConfig::ParseGlobalParams(ProcessJson<T> process_json,
-                                      T* arg) const {
-  if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) {
-    return;
-  }
-  for (grpc_json* field = json_tree_->child; field != nullptr;
-       field = field->next) {
-    if (field->key == nullptr) return;
-    if (strcmp(field->key, "methodConfig") == 0) continue;
-    process_json(field, arg);
-  }
-}
-
-template <typename T>
-bool ServiceConfig::ParseJsonMethodConfig(
-    grpc_json* json, CreateValue<T> create_value,
-    typename SliceHashTable<RefCountedPtr<T>>::Entry* entries, size_t* idx) {
-  // Construct value.
-  RefCountedPtr<T> method_config = create_value(json);
-  if (method_config == nullptr) return false;
-  // Construct list of paths.
-  InlinedVector<UniquePtr<char>, 10> paths;
-  for (grpc_json* child = json->child; child != nullptr; child = child->next) {
-    if (child->key == nullptr) continue;
-    if (strcmp(child->key, "name") == 0) {
-      if (child->type != GRPC_JSON_ARRAY) return false;
-      for (grpc_json* name = child->child; name != nullptr; name = name->next) {
-        UniquePtr<char> path = ParseJsonMethodName(name);
-        if (path == nullptr) return false;
-        paths.push_back(std::move(path));
-      }
-    }
-  }
-  if (paths.size() == 0) return false;  // No names specified.
-  // Add entry for each path.
-  for (size_t i = 0; i < paths.size(); ++i) {
-    entries[*idx].key = grpc_slice_from_copied_string(paths[i].get());
-    entries[*idx].value = method_config;  // Takes a new ref.
-    ++*idx;
-  }
-  // Success.
-  return true;
-}
-
-template <typename T>
-RefCountedPtr<SliceHashTable<RefCountedPtr<T>>>
-ServiceConfig::CreateMethodConfigTable(CreateValue<T> create_value) const {
-  // Traverse parsed JSON tree.
-  if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) {
-    return nullptr;
-  }
-  size_t num_entries = 0;
-  typename SliceHashTable<RefCountedPtr<T>>::Entry* entries = nullptr;
-  for (grpc_json* field = json_tree_->child; field != nullptr;
-       field = field->next) {
-    if (field->key == nullptr) return nullptr;
-    if (strcmp(field->key, "methodConfig") == 0) {
-      if (entries != nullptr) return nullptr;  // Duplicate.
-      if (field->type != GRPC_JSON_ARRAY) return nullptr;
-      // Find number of entries.
-      for (grpc_json* method = field->child; method != nullptr;
-           method = method->next) {
-        int count = CountNamesInMethodConfig(method);
-        if (count <= 0) return nullptr;
-        num_entries += static_cast<size_t>(count);
-      }
-      // Populate method config table entries.
-      entries = static_cast<typename SliceHashTable<RefCountedPtr<T>>::Entry*>(
-          gpr_zalloc(num_entries *
-                     sizeof(typename SliceHashTable<RefCountedPtr<T>>::Entry)));
-      size_t idx = 0;
-      for (grpc_json* method = field->child; method != nullptr;
-           method = method->next) {
-        if (!ParseJsonMethodConfig(method, create_value, entries, &idx)) {
-          for (size_t i = 0; i < idx; ++i) {
-            grpc_slice_unref_internal(entries[i].key);
-            entries[i].value.reset();
-          }
-          gpr_free(entries);
-          return nullptr;
-        }
-      }
-      GPR_ASSERT(idx == num_entries);
-    }
-  }
-  // Instantiate method config table.
-  RefCountedPtr<SliceHashTable<RefCountedPtr<T>>> method_config_table;
-  if (entries != nullptr) {
-    method_config_table =
-        SliceHashTable<RefCountedPtr<T>>::Create(num_entries, entries, nullptr);
-    gpr_free(entries);
-  }
-  return method_config_table;
-}
-
-template <typename T>
-RefCountedPtr<T> ServiceConfig::MethodConfigTableLookup(
-    const SliceHashTable<RefCountedPtr<T>>& table, const grpc_slice& path) {
-  const RefCountedPtr<T>* value = table.Get(path);
-  // If we didn't find a match for the path, try looking for a wildcard
-  // entry (i.e., change "/service/method" to "/service/*").
-  if (value == nullptr) {
-    char* path_str = grpc_slice_to_c_string(path);
-    const char* sep = strrchr(path_str, '/') + 1;
-    const size_t len = (size_t)(sep - path_str);
-    char* buf = (char*)gpr_malloc(len + 2);  // '*' and NUL
-    memcpy(buf, path_str, len);
-    buf[len] = '*';
-    buf[len + 1] = '\0';
-    grpc_slice wildcard_path = grpc_slice_from_copied_string(buf);
-    gpr_free(buf);
-    value = table.Get(wildcard_path);
-    grpc_slice_unref_internal(wildcard_path);
-    gpr_free(path_str);
-    if (value == nullptr) return nullptr;
-  }
-  return RefCountedPtr<T>(*value);
-}
+  InlinedVector<UniquePtr<ParsedConfig>, kNumPreallocatedParsers>
+      parsed_global_service_config_objects_;
+  // A map from the method name to the service config objects vector. Note that
+  // we are using a raw pointer and not a unique pointer so that we can use the
+  // same vector for multiple names.
+  RefCountedPtr<SliceHashTable<const ServiceConfigObjectsVector*>>
+      parsed_method_service_config_objects_table_;
+  // Storage for all the vectors that are being used in
+  // parsed_method_service_config_objects_table_.
+  InlinedVector<UniquePtr<ServiceConfigObjectsVector>, 32>
+      service_config_objects_vectors_storage_;
+};
 
 }  // namespace grpc_core
 
index 8bb0c4c..a284e69 100644 (file)
@@ -42,8 +42,8 @@
 #include "src/core/lib/gpr/alloc.h"
 #include "src/core/lib/gprpp/debug_location.h"
 #include "src/core/lib/gprpp/manual_constructor.h"
-#include "src/core/lib/gprpp/mutex_lock.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/slice/slice_internal.h"
@@ -121,7 +121,7 @@ RefCountedPtr<SubchannelCall> ConnectedSubchannel::CreateCall(
   const size_t allocation_size =
       GetInitialCallSizeEstimate(args.parent_data_size);
   RefCountedPtr<SubchannelCall> call(
-      new (gpr_arena_alloc(args.arena, allocation_size))
+      new (args.arena->Alloc(allocation_size))
           SubchannelCall(Ref(DEBUG_LOCATION, "subchannel_call"), args));
   grpc_call_stack* callstk = SUBCHANNEL_CALL_TO_CALL_STACK(call.get());
   const grpc_call_element_args call_args = {
@@ -332,10 +332,9 @@ class Subchannel::ConnectedSubchannelStateWatcher
       health_state = GRPC_CHANNEL_CONNECTING;
     }
     // Report initial state.
-    c->SetConnectivityStateLocked(GRPC_CHANNEL_READY, GRPC_ERROR_NONE,
-                                  "subchannel_connected");
+    c->SetConnectivityStateLocked(GRPC_CHANNEL_READY, "subchannel_connected");
     grpc_connectivity_state_set(&c->state_and_health_tracker_, health_state,
-                                GRPC_ERROR_NONE, "subchannel_connected");
+                                "subchannel_connected");
   }
 
   ~ConnectedSubchannelStateWatcher() {
@@ -367,11 +366,10 @@ class Subchannel::ConnectedSubchannelStateWatcher
             c->connected_subchannel_watcher_.reset();
             self->last_connectivity_state_ = GRPC_CHANNEL_TRANSIENT_FAILURE;
             c->SetConnectivityStateLocked(GRPC_CHANNEL_TRANSIENT_FAILURE,
-                                          GRPC_ERROR_REF(error),
                                           "reflect_child");
             grpc_connectivity_state_set(&c->state_and_health_tracker_,
                                         GRPC_CHANNEL_TRANSIENT_FAILURE,
-                                        GRPC_ERROR_REF(error), "reflect_child");
+                                        "reflect_child");
             c->backoff_begun_ = false;
             c->backoff_.Reset();
             c->MaybeStartConnectingLocked();
@@ -388,11 +386,11 @@ class Subchannel::ConnectedSubchannelStateWatcher
           // from READY to CONNECTING or IDLE.
           self->last_connectivity_state_ = self->pending_connectivity_state_;
           c->SetConnectivityStateLocked(self->pending_connectivity_state_,
-                                        GRPC_ERROR_REF(error), "reflect_child");
+                                        "reflect_child");
           if (self->pending_connectivity_state_ != GRPC_CHANNEL_READY) {
             grpc_connectivity_state_set(&c->state_and_health_tracker_,
                                         self->pending_connectivity_state_,
-                                        GRPC_ERROR_REF(error), "reflect_child");
+                                        "reflect_child");
           }
           c->connected_subchannel_->NotifyOnStateChange(
               nullptr, &self->pending_connectivity_state_,
@@ -415,8 +413,7 @@ class Subchannel::ConnectedSubchannelStateWatcher
           self->health_check_client_ != nullptr) {
         if (self->last_connectivity_state_ == GRPC_CHANNEL_READY) {
           grpc_connectivity_state_set(&c->state_and_health_tracker_,
-                                      self->health_state_,
-                                      GRPC_ERROR_REF(error), "health_changed");
+                                      self->health_state_, "health_changed");
         }
         self->health_check_client_->NotifyOnHealthChange(
             &self->health_state_, &self->on_health_changed_);
@@ -457,13 +454,14 @@ struct Subchannel::ExternalStateWatcher {
       grpc_pollset_set_del_pollset_set(w->subchannel->pollset_set_,
                                        w->pollset_set);
     }
-    gpr_mu_lock(&w->subchannel->mu_);
-    if (w->subchannel->external_state_watcher_list_ == w) {
-      w->subchannel->external_state_watcher_list_ = w->next;
+    {
+      MutexLock lock(&w->subchannel->mu_);
+      if (w->subchannel->external_state_watcher_list_ == w) {
+        w->subchannel->external_state_watcher_list_ = w->next;
+      }
+      if (w->next != nullptr) w->next->prev = w->prev;
+      if (w->prev != nullptr) w->prev->next = w->next;
     }
-    if (w->next != nullptr) w->next->prev = w->prev;
-    if (w->prev != nullptr) w->prev->next = w->next;
-    gpr_mu_unlock(&w->subchannel->mu_);
     GRPC_SUBCHANNEL_WEAK_UNREF(w->subchannel, "external_state_watcher+done");
     Delete(w);
     GRPC_CLOSURE_SCHED(follow_up, GRPC_ERROR_REF(error));
@@ -531,25 +529,6 @@ BackOff::Options ParseArgsForBackoffValues(
       .set_max_backoff(max_backoff_ms);
 }
 
-struct HealthCheckParams {
-  UniquePtr<char> service_name;
-
-  static void Parse(const grpc_json* field, HealthCheckParams* params) {
-    if (strcmp(field->key, "healthCheckConfig") == 0) {
-      if (field->type != GRPC_JSON_OBJECT) return;
-      for (grpc_json* sub_field = field->child; sub_field != nullptr;
-           sub_field = sub_field->next) {
-        if (sub_field->key == nullptr) return;
-        if (strcmp(sub_field->key, "serviceName") == 0) {
-          if (params->service_name != nullptr) return;  // Duplicate.
-          if (sub_field->type != GRPC_JSON_STRING) return;
-          params->service_name.reset(gpr_strdup(sub_field->value));
-        }
-      }
-    }
-  }
-};
-
 }  // namespace
 
 Subchannel::Subchannel(SubchannelKey* key, grpc_connector* connector,
@@ -585,19 +564,9 @@ Subchannel::Subchannel(SubchannelKey* key, grpc_connector* connector,
                                "subchannel");
   grpc_connectivity_state_init(&state_and_health_tracker_, GRPC_CHANNEL_IDLE,
                                "subchannel");
-  gpr_mu_init(&mu_);
-  // Check whether we should enable health checking.
-  const char* service_config_json = grpc_channel_arg_get_string(
-      grpc_channel_args_find(args_, GRPC_ARG_SERVICE_CONFIG));
-  if (service_config_json != nullptr) {
-    RefCountedPtr<ServiceConfig> service_config =
-        ServiceConfig::Create(service_config_json);
-    if (service_config != nullptr) {
-      HealthCheckParams params;
-      service_config->ParseGlobalParams(HealthCheckParams::Parse, &params);
-      health_check_service_name_ = std::move(params.service_name);
-    }
-  }
+  health_check_service_name_ =
+      UniquePtr<char>(gpr_strdup(grpc_channel_arg_get_string(
+          grpc_channel_args_find(args_, "grpc.temp.health_check"))));
   const grpc_arg* arg = grpc_channel_args_find(args_, GRPC_ARG_ENABLE_CHANNELZ);
   const bool channelz_enabled =
       grpc_channel_arg_get_bool(arg, GRPC_ENABLE_CHANNELZ_DEFAULT);
@@ -629,7 +598,6 @@ Subchannel::~Subchannel() {
   grpc_connector_unref(connector_);
   grpc_pollset_set_destroy(pollset_set_);
   Delete(key_);
-  gpr_mu_destroy(&mu_);
 }
 
 Subchannel* Subchannel::Create(grpc_connector* connector,
@@ -740,11 +708,10 @@ channelz::SubchannelNode* Subchannel::channelz_node() {
 }
 
 grpc_connectivity_state Subchannel::CheckConnectivity(
-    grpc_error** error, bool inhibit_health_checking) {
-  MutexLock lock(&mu_);
+    bool inhibit_health_checking) {
   grpc_connectivity_state_tracker* tracker =
       inhibit_health_checking ? &state_tracker_ : &state_and_health_tracker_;
-  grpc_connectivity_state state = grpc_connectivity_state_get(tracker, error);
+  grpc_connectivity_state state = grpc_connectivity_state_check(tracker);
   return state;
 }
 
@@ -852,7 +819,6 @@ const char* SubchannelConnectivityStateChangeString(
 }  // namespace
 
 void Subchannel::SetConnectivityStateLocked(grpc_connectivity_state state,
-                                            grpc_error* error,
                                             const char* reason) {
   if (channelz_node_ != nullptr) {
     channelz_node_->AddTraceEvent(
@@ -860,7 +826,7 @@ void Subchannel::SetConnectivityStateLocked(grpc_connectivity_state state,
         grpc_slice_from_static_string(
             SubchannelConnectivityStateChangeString(state)));
   }
-  grpc_connectivity_state_set(&state_tracker_, state, error, reason);
+  grpc_connectivity_state_set(&state_tracker_, state, reason);
 }
 
 void Subchannel::MaybeStartConnectingLocked() {
@@ -905,7 +871,9 @@ void Subchannel::MaybeStartConnectingLocked() {
 
 void Subchannel::OnRetryAlarm(void* arg, grpc_error* error) {
   Subchannel* c = static_cast<Subchannel*>(arg);
-  gpr_mu_lock(&c->mu_);
+  // TODO(soheilhy): Once subchannel refcounting is simplified, we can get use
+  //                 MutexLock instead of ReleasableMutexLock, here.
+  ReleasableMutexLock lock(&c->mu_);
   c->have_retry_alarm_ = false;
   if (c->disconnected_) {
     error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING("Disconnected",
@@ -919,9 +887,9 @@ void Subchannel::OnRetryAlarm(void* arg, grpc_error* error) {
   if (error == GRPC_ERROR_NONE) {
     gpr_log(GPR_INFO, "Failed to connect to channel, retrying");
     c->ContinueConnectingLocked();
-    gpr_mu_unlock(&c->mu_);
+    lock.Unlock();
   } else {
-    gpr_mu_unlock(&c->mu_);
+    lock.Unlock();
     GRPC_SUBCHANNEL_WEAK_UNREF(c, "connecting");
   }
   GRPC_ERROR_UNREF(error);
@@ -935,11 +903,9 @@ void Subchannel::ContinueConnectingLocked() {
   next_attempt_deadline_ = backoff_.NextAttemptTime();
   args.deadline = std::max(next_attempt_deadline_, min_deadline);
   args.channel_args = args_;
-  SetConnectivityStateLocked(GRPC_CHANNEL_CONNECTING, GRPC_ERROR_NONE,
-                             "connecting");
+  SetConnectivityStateLocked(GRPC_CHANNEL_CONNECTING, "connecting");
   grpc_connectivity_state_set(&state_and_health_tracker_,
-                              GRPC_CHANNEL_CONNECTING, GRPC_ERROR_NONE,
-                              "connecting");
+                              GRPC_CHANNEL_CONNECTING, "connecting");
   grpc_connector_connect(connector_, &args, &connecting_result_,
                          &on_connecting_finished_);
 }
@@ -948,29 +914,25 @@ void Subchannel::OnConnectingFinished(void* arg, grpc_error* error) {
   auto* c = static_cast<Subchannel*>(arg);
   grpc_channel_args* delete_channel_args = c->connecting_result_.channel_args;
   GRPC_SUBCHANNEL_WEAK_REF(c, "on_connecting_finished");
-  gpr_mu_lock(&c->mu_);
-  c->connecting_ = false;
-  if (c->connecting_result_.transport != nullptr &&
-      c->PublishTransportLocked()) {
-    // Do nothing, transport was published.
-  } else if (c->disconnected_) {
-    GRPC_SUBCHANNEL_WEAK_UNREF(c, "connecting");
-  } else {
-    const char* errmsg = grpc_error_string(error);
-    gpr_log(GPR_INFO, "Connect failed: %s", errmsg);
-    error =
-        grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
-                               "Connect Failed", &error, 1),
-                           GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE);
-    c->SetConnectivityStateLocked(GRPC_CHANNEL_TRANSIENT_FAILURE,
-                                  GRPC_ERROR_REF(error), "connect_failed");
-    grpc_connectivity_state_set(&c->state_and_health_tracker_,
-                                GRPC_CHANNEL_TRANSIENT_FAILURE, error,
-                                "connect_failed");
-    c->MaybeStartConnectingLocked();
-    GRPC_SUBCHANNEL_WEAK_UNREF(c, "connecting");
+  {
+    MutexLock lock(&c->mu_);
+    c->connecting_ = false;
+    if (c->connecting_result_.transport != nullptr &&
+        c->PublishTransportLocked()) {
+      // Do nothing, transport was published.
+    } else if (c->disconnected_) {
+      GRPC_SUBCHANNEL_WEAK_UNREF(c, "connecting");
+    } else {
+      gpr_log(GPR_INFO, "Connect failed: %s", grpc_error_string(error));
+      c->SetConnectivityStateLocked(GRPC_CHANNEL_TRANSIENT_FAILURE,
+                                    "connect_failed");
+      grpc_connectivity_state_set(&c->state_and_health_tracker_,
+                                  GRPC_CHANNEL_TRANSIENT_FAILURE,
+                                  "connect_failed");
+      c->MaybeStartConnectingLocked();
+      GRPC_SUBCHANNEL_WEAK_UNREF(c, "connecting");
+    }
   }
-  gpr_mu_unlock(&c->mu_);
   GRPC_SUBCHANNEL_WEAK_UNREF(c, "on_connecting_finished");
   grpc_channel_args_destroy(delete_channel_args);
 }
index 968fc74..e3efc89 100644 (file)
 #include "src/core/ext/filters/client_channel/subchannel_pool_interface.h"
 #include "src/core/lib/backoff/backoff.h"
 #include "src/core/lib/channel/channel_stack.h"
-#include "src/core/lib/gpr/arena.h"
+#include "src/core/lib/gprpp/arena.h"
 #include "src/core/lib/gprpp/ref_counted.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/iomgr/polling_entity.h"
 #include "src/core/lib/iomgr/timer.h"
 #include "src/core/lib/transport/connectivity_state.h"
@@ -74,9 +75,9 @@ class ConnectedSubchannel : public RefCounted<ConnectedSubchannel> {
     grpc_slice path;
     gpr_timespec start_time;
     grpc_millis deadline;
-    gpr_arena* arena;
+    Arena* arena;
     grpc_call_context_element* context;
-    grpc_call_combiner* call_combiner;
+    grpc_core::CallCombiner* call_combiner;
     size_t parent_data_size;
   };
 
@@ -207,8 +208,7 @@ class Subchannel {
   channelz::SubchannelNode* channelz_node();
 
   // Polls the current connectivity state of the subchannel.
-  grpc_connectivity_state CheckConnectivity(grpc_error** error,
-                                            bool inhibit_health_checking);
+  grpc_connectivity_state CheckConnectivity(bool inhibit_health_checking);
 
   // When the connectivity state of the subchannel changes from \a *state,
   // invokes \a notify and updates \a *state with the new state.
@@ -241,7 +241,7 @@ class Subchannel {
 
   // Sets the subchannel's connectivity state to \a state.
   void SetConnectivityStateLocked(grpc_connectivity_state state,
-                                  grpc_error* error, const char* reason);
+                                  const char* reason);
 
   // Methods for connection.
   void MaybeStartConnectingLocked();
@@ -264,7 +264,7 @@ class Subchannel {
   // pollset_set tracking who's interested in a connection being setup.
   grpc_pollset_set* pollset_set_;
   // Protects the other members.
-  gpr_mu mu_;
+  Mutex mu_;
   // Refcount
   //    - lower INTERNAL_REF_BITS bits are for internal references:
   //      these do not keep the subchannel open.
index b4cb07f..20a2953 100644 (file)
@@ -68,8 +68,7 @@ static void timer_callback(void* arg, grpc_error* error) {
     error = grpc_error_set_int(
         GRPC_ERROR_CREATE_FROM_STATIC_STRING("Deadline Exceeded"),
         GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_DEADLINE_EXCEEDED);
-    grpc_call_combiner_cancel(deadline_state->call_combiner,
-                              GRPC_ERROR_REF(error));
+    deadline_state->call_combiner->Cancel(GRPC_ERROR_REF(error));
     GRPC_CLOSURE_INIT(&deadline_state->timer_callback,
                       send_cancel_op_in_call_combiner, elem,
                       grpc_schedule_on_exec_ctx);
@@ -124,7 +123,7 @@ static void cancel_timer_if_needed(grpc_deadline_state* deadline_state) {
     deadline_state->timer_state = GRPC_DEADLINE_STATE_FINISHED;
     grpc_timer_cancel(&deadline_state->timer);
   } else {
-    // timer was either in STATE_INITAL (nothing to cancel)
+    // timer was either in STATE_INITIAL (nothing to cancel)
     // OR in STATE_FINISHED (again nothing to cancel)
   }
 }
@@ -183,7 +182,7 @@ static void start_timer_after_init(void* arg, grpc_error* error) {
 
 grpc_deadline_state::grpc_deadline_state(grpc_call_element* elem,
                                          grpc_call_stack* call_stack,
-                                         grpc_call_combiner* call_combiner,
+                                         grpc_core::CallCombiner* call_combiner,
                                          grpc_millis deadline)
     : call_stack(call_stack), call_combiner(call_combiner) {
   // Deadline will always be infinite on servers, so the timer will only be
index e370329..7c4e9aa 100644 (file)
@@ -32,12 +32,13 @@ enum grpc_deadline_timer_state {
 // Must be the first field in the filter's call_data.
 struct grpc_deadline_state {
   grpc_deadline_state(grpc_call_element* elem, grpc_call_stack* call_stack,
-                      grpc_call_combiner* call_combiner, grpc_millis deadline);
+                      grpc_core::CallCombiner* call_combiner,
+                      grpc_millis deadline);
   ~grpc_deadline_state();
 
   // We take a reference to the call stack for the timer callback.
   grpc_call_stack* call_stack;
-  grpc_call_combiner* call_combiner;
+  grpc_core::CallCombiner* call_combiner;
   grpc_deadline_timer_state timer_state = GRPC_DEADLINE_STATE_INITIAL;
   grpc_timer timer;
   grpc_closure timer_callback;
index bf9a01f..4ef6c1f 100644 (file)
@@ -36,7 +36,7 @@
 #define EXPECTED_CONTENT_TYPE "application/grpc"
 #define EXPECTED_CONTENT_TYPE_LENGTH sizeof(EXPECTED_CONTENT_TYPE) - 1
 
-/* default maximum size of payload eligable for GET request */
+/* default maximum size of payload eligible for GET request */
 static constexpr size_t kMaxPayloadSizeForGet = 2048;
 
 static void recv_initial_metadata_ready(void* user_data, grpc_error* error);
@@ -62,7 +62,7 @@ struct call_data {
 
   ~call_data() { GRPC_ERROR_UNREF(recv_initial_metadata_error); }
 
-  grpc_call_combiner* call_combiner;
+  grpc_core::CallCombiner* call_combiner;
   // State for handling send_initial_metadata ops.
   grpc_linked_mdelem method;
   grpc_linked_mdelem scheme;
@@ -107,7 +107,8 @@ static grpc_error* client_filter_incoming_metadata(grpc_call_element* elem,
      * https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md.
      */
     if (b->idx.named.grpc_status != nullptr ||
-        grpc_mdelem_eq(b->idx.named.status->md, GRPC_MDELEM_STATUS_200)) {
+        grpc_mdelem_static_value_eq(b->idx.named.status->md,
+                                    GRPC_MDELEM_STATUS_200)) {
       grpc_metadata_batch_remove(b, b->idx.named.status);
     } else {
       char* val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.status->md),
@@ -140,8 +141,9 @@ static grpc_error* client_filter_incoming_metadata(grpc_call_element* elem,
   }
 
   if (b->idx.named.content_type != nullptr) {
-    if (!grpc_mdelem_eq(b->idx.named.content_type->md,
-                        GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)) {
+    if (!grpc_mdelem_static_value_eq(
+            b->idx.named.content_type->md,
+            GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)) {
       if (grpc_slice_buf_start_eq(GRPC_MDVALUE(b->idx.named.content_type->md),
                                   EXPECTED_CONTENT_TYPE,
                                   EXPECTED_CONTENT_TYPE_LENGTH) &&
index b7cef33..a2f16dd 100644 (file)
@@ -25,7 +25,7 @@
 /* Processes metadata on the client side for HTTP2 transports */
 extern const grpc_channel_filter grpc_http_client_filter;
 
-/* Channel arg to determine maximum size of payload eligable for GET request */
+/* Channel arg to determine maximum size of payload eligible for GET request */
 #define GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET "grpc.max_payload_size_for_get"
 
 #endif /* GRPC_CORE_EXT_FILTERS_HTTP_CLIENT_HTTP_CLIENT_FILTER_H */
index 125059c..85b30bc 100644 (file)
@@ -40,7 +40,7 @@ namespace {
 
 struct call_data {
   grpc_linked_mdelem authority_storage;
-  grpc_call_combiner* call_combiner;
+  grpc_core::CallCombiner* call_combiner;
 };
 
 struct channel_data {
index 9c8c8d9..d2b1f67 100644 (file)
@@ -29,6 +29,7 @@
 #include "src/core/ext/filters/http/message_compress/message_compress_filter.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/compression/algorithm_metadata.h"
+#include "src/core/lib/compression/compression_args.h"
 #include "src/core/lib/compression/compression_internal.h"
 #include "src/core/lib/compression/message_compress.h"
 #include "src/core/lib/gpr/string.h"
@@ -71,7 +72,7 @@ struct call_data {
     GRPC_ERROR_UNREF(cancel_error);
   }
 
-  grpc_call_combiner* call_combiner;
+  grpc_core::CallCombiner* call_combiner;
   grpc_linked_mdelem compression_algorithm_storage;
   grpc_linked_mdelem stream_compression_algorithm_storage;
   grpc_linked_mdelem accept_encoding_storage;
@@ -248,7 +249,7 @@ static void finish_send_message(grpc_call_element* elem) {
   bool did_compress = grpc_msg_compress(calld->message_compression_algorithm,
                                         &calld->slices, &tmp);
   if (did_compress) {
-    if (grpc_compression_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_compression_trace)) {
       const char* algo_name;
       const size_t before_size = calld->slices.length;
       const size_t after_size = tmp.length;
@@ -264,7 +265,7 @@ static void finish_send_message(grpc_call_element* elem) {
     grpc_slice_buffer_swap(&calld->slices, &tmp);
     send_flags |= GRPC_WRITE_INTERNAL_COMPRESS;
   } else {
-    if (grpc_compression_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_compression_trace)) {
       const char* algo_name;
       GPR_ASSERT(grpc_message_compression_algorithm_name(
           calld->message_compression_algorithm, &algo_name));
index ce1be83..028d268 100644 (file)
@@ -61,7 +61,7 @@ struct call_data {
     }
   }
 
-  grpc_call_combiner* call_combiner;
+  grpc_core::CallCombiner* call_combiner;
 
   // Outgoing headers to add to send_initial_metadata.
   grpc_linked_mdelem status;
@@ -131,18 +131,19 @@ static grpc_error* hs_filter_incoming_metadata(grpc_call_element* elem,
   static const char* error_name = "Failed processing incoming headers";
 
   if (b->idx.named.method != nullptr) {
-    if (grpc_mdelem_eq(b->idx.named.method->md, GRPC_MDELEM_METHOD_POST)) {
+    if (grpc_mdelem_static_value_eq(b->idx.named.method->md,
+                                    GRPC_MDELEM_METHOD_POST)) {
       *calld->recv_initial_metadata_flags &=
           ~(GRPC_INITIAL_METADATA_CACHEABLE_REQUEST |
             GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST);
-    } else if (grpc_mdelem_eq(b->idx.named.method->md,
-                              GRPC_MDELEM_METHOD_PUT)) {
+    } else if (grpc_mdelem_static_value_eq(b->idx.named.method->md,
+                                           GRPC_MDELEM_METHOD_PUT)) {
       *calld->recv_initial_metadata_flags &=
           ~GRPC_INITIAL_METADATA_CACHEABLE_REQUEST;
       *calld->recv_initial_metadata_flags |=
           GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST;
-    } else if (grpc_mdelem_eq(b->idx.named.method->md,
-                              GRPC_MDELEM_METHOD_GET)) {
+    } else if (grpc_mdelem_static_value_eq(b->idx.named.method->md,
+                                           GRPC_MDELEM_METHOD_GET)) {
       *calld->recv_initial_metadata_flags |=
           GRPC_INITIAL_METADATA_CACHEABLE_REQUEST;
       *calld->recv_initial_metadata_flags &=
@@ -163,7 +164,8 @@ static grpc_error* hs_filter_incoming_metadata(grpc_call_element* elem,
   }
 
   if (b->idx.named.te != nullptr) {
-    if (!grpc_mdelem_eq(b->idx.named.te->md, GRPC_MDELEM_TE_TRAILERS)) {
+    if (!grpc_mdelem_static_value_eq(b->idx.named.te->md,
+                                     GRPC_MDELEM_TE_TRAILERS)) {
       hs_add_error(error_name, &error,
                    grpc_attach_md_to_error(
                        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"),
@@ -178,9 +180,12 @@ static grpc_error* hs_filter_incoming_metadata(grpc_call_element* elem,
   }
 
   if (b->idx.named.scheme != nullptr) {
-    if (!grpc_mdelem_eq(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_HTTP) &&
-        !grpc_mdelem_eq(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_HTTPS) &&
-        !grpc_mdelem_eq(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_GRPC)) {
+    if (!grpc_mdelem_static_value_eq(b->idx.named.scheme->md,
+                                     GRPC_MDELEM_SCHEME_HTTP) &&
+        !grpc_mdelem_static_value_eq(b->idx.named.scheme->md,
+                                     GRPC_MDELEM_SCHEME_HTTPS) &&
+        !grpc_mdelem_static_value_eq(b->idx.named.scheme->md,
+                                     GRPC_MDELEM_SCHEME_GRPC)) {
       hs_add_error(error_name, &error,
                    grpc_attach_md_to_error(
                        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"),
@@ -196,8 +201,9 @@ static grpc_error* hs_filter_incoming_metadata(grpc_call_element* elem,
   }
 
   if (b->idx.named.content_type != nullptr) {
-    if (!grpc_mdelem_eq(b->idx.named.content_type->md,
-                        GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)) {
+    if (!grpc_mdelem_static_value_eq(
+            b->idx.named.content_type->md,
+            GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)) {
       if (grpc_slice_buf_start_eq(GRPC_MDVALUE(b->idx.named.content_type->md),
                                   EXPECTED_CONTENT_TYPE,
                                   EXPECTED_CONTENT_TYPE_LENGTH) &&
index 8a422dd..a973cbc 100644 (file)
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gprpp/ref_counted.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/surface/call.h"
 #include "src/core/lib/surface/channel_init.h"
 
-typedef struct {
-  int max_send_size;
-  int max_recv_size;
-} message_size_limits;
+static void recv_message_ready(void* user_data, grpc_error* error);
+static void recv_trailing_metadata_ready(void* user_data, grpc_error* error);
 
 namespace grpc_core {
-namespace {
-
-class MessageSizeLimits : public RefCounted<MessageSizeLimits> {
- public:
-  static RefCountedPtr<MessageSizeLimits> CreateFromJson(const grpc_json* json);
-
-  const message_size_limits& limits() const { return limits_; }
 
- private:
-  // So New() can call our private ctor.
-  template <typename T, typename... Args>
-  friend T* grpc_core::New(Args&&... args);
-
-  MessageSizeLimits(int max_send_size, int max_recv_size) {
-    limits_.max_send_size = max_send_size;
-    limits_.max_recv_size = max_recv_size;
-  }
-
-  message_size_limits limits_;
-};
+namespace {
+size_t g_message_size_parser_index;
+}  // namespace
 
-RefCountedPtr<MessageSizeLimits> MessageSizeLimits::CreateFromJson(
-    const grpc_json* json) {
+UniquePtr<ServiceConfig::ParsedConfig> MessageSizeParser::ParsePerMethodParams(
+    const grpc_json* json, grpc_error** error) {
+  GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
   int max_request_message_bytes = -1;
   int max_response_message_bytes = -1;
+  InlinedVector<grpc_error*, 4> error_list;
   for (grpc_json* field = json->child; field != nullptr; field = field->next) {
     if (field->key == nullptr) continue;
     if (strcmp(field->key, "maxRequestMessageBytes") == 0) {
-      if (max_request_message_bytes >= 0) return nullptr;  // Duplicate.
+      if (max_request_message_bytes >= 0) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:maxRequestMessageBytes error:Duplicate entry"));
+      }  // Duplicate, continue parsing.
       if (field->type != GRPC_JSON_STRING && field->type != GRPC_JSON_NUMBER) {
-        return nullptr;
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:maxRequestMessageBytes error:should be of type number"));
+      } else {
+        max_request_message_bytes = gpr_parse_nonnegative_int(field->value);
+        if (max_request_message_bytes == -1) {
+          error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "field:maxRequestMessageBytes error:should be non-negative"));
+        }
       }
-      max_request_message_bytes = gpr_parse_nonnegative_int(field->value);
-      if (max_request_message_bytes == -1) return nullptr;
     } else if (strcmp(field->key, "maxResponseMessageBytes") == 0) {
-      if (max_response_message_bytes >= 0) return nullptr;  // Duplicate.
+      if (max_response_message_bytes >= 0) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:maxResponseMessageBytes error:Duplicate entry"));
+      }  // Duplicate, continue parsing
       if (field->type != GRPC_JSON_STRING && field->type != GRPC_JSON_NUMBER) {
-        return nullptr;
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:maxResponseMessageBytes error:should be of type number"));
+      } else {
+        max_response_message_bytes = gpr_parse_nonnegative_int(field->value);
+        if (max_response_message_bytes == -1) {
+          error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "field:maxResponseMessageBytes error:should be non-negative"));
+        }
       }
-      max_response_message_bytes = gpr_parse_nonnegative_int(field->value);
-      if (max_response_message_bytes == -1) return nullptr;
     }
   }
-  return MakeRefCounted<MessageSizeLimits>(max_request_message_bytes,
-                                           max_response_message_bytes);
+  if (!error_list.empty()) {
+    *error = GRPC_ERROR_CREATE_FROM_VECTOR("Message size parser", &error_list);
+    return nullptr;
+  }
+  return UniquePtr<ServiceConfig::ParsedConfig>(New<MessageSizeParsedObject>(
+      max_request_message_bytes, max_response_message_bytes));
 }
 
-}  // namespace
-}  // namespace grpc_core
+void MessageSizeParser::Register() {
+  g_message_size_parser_index = ServiceConfig::RegisterParser(
+      UniquePtr<ServiceConfig::Parser>(New<MessageSizeParser>()));
+}
 
-static void recv_message_ready(void* user_data, grpc_error* error);
-static void recv_trailing_metadata_ready(void* user_data, grpc_error* error);
+size_t MessageSizeParser::ParserIndex() { return g_message_size_parser_index; }
+}  // namespace grpc_core
 
 namespace {
-
 struct channel_data {
-  message_size_limits limits;
-  // Maps path names to refcounted_message_size_limits structs.
-  grpc_core::RefCountedPtr<grpc_core::SliceHashTable<
-      grpc_core::RefCountedPtr<grpc_core::MessageSizeLimits>>>
-      method_limit_table;
+  grpc_core::MessageSizeParsedObject::message_size_limits limits;
+  grpc_core::RefCountedPtr<grpc_core::ServiceConfig> svc_cfg;
 };
 
 struct call_data {
@@ -116,29 +119,42 @@ struct call_data {
     // Note: Per-method config is only available on the client, so we
     // apply the max request size to the send limit and the max response
     // size to the receive limit.
-    if (chand.method_limit_table != nullptr) {
-      grpc_core::RefCountedPtr<grpc_core::MessageSizeLimits> limits =
-          grpc_core::ServiceConfig::MethodConfigTableLookup(
-              *chand.method_limit_table, args.path);
-      if (limits != nullptr) {
-        if (limits->limits().max_send_size >= 0 &&
-            (limits->limits().max_send_size < this->limits.max_send_size ||
-             this->limits.max_send_size < 0)) {
-          this->limits.max_send_size = limits->limits().max_send_size;
-        }
-        if (limits->limits().max_recv_size >= 0 &&
-            (limits->limits().max_recv_size < this->limits.max_recv_size ||
-             this->limits.max_recv_size < 0)) {
-          this->limits.max_recv_size = limits->limits().max_recv_size;
-        }
+    const grpc_core::MessageSizeParsedObject* limits = nullptr;
+    grpc_core::ServiceConfig::CallData* svc_cfg_call_data = nullptr;
+    if (args.context != nullptr) {
+      svc_cfg_call_data = static_cast<grpc_core::ServiceConfig::CallData*>(
+          args.context[GRPC_SERVICE_CONFIG_CALL_DATA].value);
+    }
+    if (svc_cfg_call_data != nullptr) {
+      limits = static_cast<const grpc_core::MessageSizeParsedObject*>(
+          svc_cfg_call_data->GetMethodParsedObject(
+              grpc_core::MessageSizeParser::ParserIndex()));
+    } else if (chand.svc_cfg != nullptr) {
+      const auto* objs_vector =
+          chand.svc_cfg->GetMethodServiceConfigObjectsVector(args.path);
+      if (objs_vector != nullptr) {
+        limits = static_cast<const grpc_core::MessageSizeParsedObject*>(
+            (*objs_vector)[grpc_core::MessageSizeParser::ParserIndex()].get());
+      }
+    }
+    if (limits != nullptr) {
+      if (limits->limits().max_send_size >= 0 &&
+          (limits->limits().max_send_size < this->limits.max_send_size ||
+           this->limits.max_send_size < 0)) {
+        this->limits.max_send_size = limits->limits().max_send_size;
+      }
+      if (limits->limits().max_recv_size >= 0 &&
+          (limits->limits().max_recv_size < this->limits.max_recv_size ||
+           this->limits.max_recv_size < 0)) {
+        this->limits.max_recv_size = limits->limits().max_recv_size;
       }
     }
   }
 
   ~call_data() { GRPC_ERROR_UNREF(error); }
 
-  grpc_call_combiner* call_combiner;
-  message_size_limits limits;
+  grpc_core::CallCombiner* call_combiner;
+  grpc_core::MessageSizeParsedObject::message_size_limits limits;
   // Receive closures are chained: we inject this closure as the
   // recv_message_ready up-call on transport_stream_op, and remember to
   // call our next_recv_message_ready member after handling it.
@@ -284,9 +300,9 @@ static int default_size(const grpc_channel_args* args,
   return without_minimal_stack;
 }
 
-message_size_limits get_message_size_limits(
+grpc_core::MessageSizeParsedObject::message_size_limits get_message_size_limits(
     const grpc_channel_args* channel_args) {
-  message_size_limits lim;
+  grpc_core::MessageSizeParsedObject::message_size_limits lim;
   lim.max_send_size =
       default_size(channel_args, GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH);
   lim.max_recv_size =
@@ -313,18 +329,27 @@ static grpc_error* init_channel_elem(grpc_channel_element* elem,
                                      grpc_channel_element_args* args) {
   GPR_ASSERT(!args->is_last);
   channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  new (chand) channel_data();
   chand->limits = get_message_size_limits(args->channel_args);
-  // Get method config table from channel args.
+  // TODO(yashykt): We only need to read GRPC_ARG_SERVICE_CONFIG in the case of
+  // direct channels. (Service config is otherwise stored in the call_context by
+  // client_channel filter.) If we ever need a second filter that also needs to
+  // parse GRPC_ARG_SERVICE_CONFIG, we should refactor this code and add a
+  // separate filter that reads GRPC_ARG_SERVICE_CONFIG and saves the parsed
+  // config in the call_context.
   const grpc_arg* channel_arg =
       grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVICE_CONFIG);
   const char* service_config_str = grpc_channel_arg_get_string(channel_arg);
   if (service_config_str != nullptr) {
-    grpc_core::RefCountedPtr<grpc_core::ServiceConfig> service_config =
-        grpc_core::ServiceConfig::Create(service_config_str);
-    if (service_config != nullptr) {
-      chand->method_limit_table = service_config->CreateMethodConfigTable(
-          grpc_core::MessageSizeLimits::CreateFromJson);
+    grpc_error* service_config_error = GRPC_ERROR_NONE;
+    auto svc_cfg = grpc_core::ServiceConfig::Create(service_config_str,
+                                                    &service_config_error);
+    if (service_config_error == GRPC_ERROR_NONE) {
+      chand->svc_cfg = std::move(svc_cfg);
+    } else {
+      gpr_log(GPR_ERROR, "%s", grpc_error_string(service_config_error));
     }
+    GRPC_ERROR_UNREF(service_config_error);
   }
   return GRPC_ERROR_NONE;
 }
@@ -332,7 +357,7 @@ static grpc_error* init_channel_elem(grpc_channel_element* elem,
 // Destructor for channel_data.
 static void destroy_channel_elem(grpc_channel_element* elem) {
   channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  chand->method_limit_table.reset();
+  chand->~channel_data();
 }
 
 const grpc_channel_filter grpc_message_size_filter = {
@@ -348,18 +373,34 @@ const grpc_channel_filter grpc_message_size_filter = {
     grpc_channel_next_get_info,
     "message_size"};
 
+// Used for GRPC_CLIENT_SUBCHANNEL
+static bool maybe_add_message_size_filter_subchannel(
+    grpc_channel_stack_builder* builder, void* arg) {
+  const grpc_channel_args* channel_args =
+      grpc_channel_stack_builder_get_channel_arguments(builder);
+  if (grpc_channel_args_want_minimal_stack(channel_args)) {
+    return true;
+  }
+  return grpc_channel_stack_builder_prepend_filter(
+      builder, &grpc_message_size_filter, nullptr, nullptr);
+}
+
+// Used for GRPC_CLIENT_DIRECT_CHANNEL and GRPC_SERVER_CHANNEL. Adds the filter
+// only if message size limits or service config is specified.
 static bool maybe_add_message_size_filter(grpc_channel_stack_builder* builder,
                                           void* arg) {
   const grpc_channel_args* channel_args =
       grpc_channel_stack_builder_get_channel_arguments(builder);
   bool enable = false;
-  message_size_limits lim = get_message_size_limits(channel_args);
+  grpc_core::MessageSizeParsedObject::message_size_limits lim =
+      get_message_size_limits(channel_args);
   if (lim.max_send_size != -1 || lim.max_recv_size != -1) {
     enable = true;
   }
   const grpc_arg* a =
       grpc_channel_args_find(channel_args, GRPC_ARG_SERVICE_CONFIG);
-  if (a != nullptr) {
+  const char* svc_cfg_str = grpc_channel_arg_get_string(a);
+  if (svc_cfg_str != nullptr) {
     enable = true;
   }
   if (enable) {
@@ -371,15 +412,16 @@ static bool maybe_add_message_size_filter(grpc_channel_stack_builder* builder,
 }
 
 void grpc_message_size_filter_init(void) {
-  grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL,
-                                   GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
-                                   maybe_add_message_size_filter, nullptr);
+  grpc_channel_init_register_stage(
+      GRPC_CLIENT_SUBCHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+      maybe_add_message_size_filter_subchannel, nullptr);
   grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL,
                                    GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
                                    maybe_add_message_size_filter, nullptr);
   grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL,
                                    GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
                                    maybe_add_message_size_filter, nullptr);
+  grpc_core::MessageSizeParser::Register();
 }
 
 void grpc_message_size_filter_shutdown(void) {}
index f66636e..cab8bd9 100644 (file)
 
 #include <grpc/support/port_platform.h>
 
+#include "src/core/ext/filters/client_channel/service_config.h"
 #include "src/core/lib/channel/channel_stack.h"
 
 extern const grpc_channel_filter grpc_message_size_filter;
 
+namespace grpc_core {
+
+class MessageSizeParsedObject : public ServiceConfig::ParsedConfig {
+ public:
+  struct message_size_limits {
+    int max_send_size;
+    int max_recv_size;
+  };
+
+  MessageSizeParsedObject(int max_send_size, int max_recv_size) {
+    limits_.max_send_size = max_send_size;
+    limits_.max_recv_size = max_recv_size;
+  }
+
+  const message_size_limits& limits() const { return limits_; }
+
+ private:
+  message_size_limits limits_;
+};
+
+class MessageSizeParser : public ServiceConfig::Parser {
+ public:
+  UniquePtr<ServiceConfig::ParsedConfig> ParsePerMethodParams(
+      const grpc_json* json, grpc_error** error) override;
+
+  static void Register();
+
+  static size_t ParserIndex();
+};
+
+}  // namespace grpc_core
+
 #endif /* GRPC_CORE_EXT_FILTERS_MESSAGE_SIZE_MESSAGE_SIZE_FILTER_H */
index 0042eaf..e2ffe4e 100644 (file)
@@ -23,7 +23,7 @@
 
 #include <string.h>
 
-/* Retuns 1 if the version is supported, 0 otherwise. */
+/* Returns 1 if the version is supported, 0 otherwise. */
 int grpc_chttp2_is_alpn_version_supported(const char* version, size_t size);
 
 /* Returns the number of protocol versions to advertise */
index 531ea73..ac13d73 100644 (file)
 
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/debug/trace.h"
-#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gprpp/global_config.h"
 #include "src/core/lib/transport/metadata.h"
 
+GPR_GLOBAL_CONFIG_DEFINE_BOOL(
+    grpc_experimental_disable_flow_control, false,
+    "If set, flow control will be effectively disabled. Max out all values and "
+    "assume the remote peer does the same. Thus we can ignore any flow control "
+    "bookkeeping, error checking, and decision making");
+
 void grpc_chttp2_plugin_init(void) {
-  g_flow_control_enabled = true;
-  char* env_variable = gpr_getenv("GRPC_EXPERIMENTAL_DISABLE_FLOW_CONTROL");
-  if (env_variable != nullptr) {
-    g_flow_control_enabled = false;
-    gpr_free(env_variable);
-  }
+  g_flow_control_enabled =
+      !GPR_GLOBAL_CONFIG_GET(grpc_experimental_disable_flow_control);
 }
 
 void grpc_chttp2_plugin_shutdown(void) {}
index 404539c..ccc3077 100644 (file)
@@ -119,7 +119,7 @@ static void maybe_start_some_streams(grpc_chttp2_transport* t);
 
 static void connectivity_state_set(grpc_chttp2_transport* t,
                                    grpc_connectivity_state state,
-                                   grpc_error* error, const char* reason);
+                                   const char* reason);
 
 static void benign_reclaimer_locked(void* t, grpc_error* error);
 static void destructive_reclaimer_locked(void* t, grpc_error* error);
@@ -592,8 +592,7 @@ static void close_transport_locked(grpc_chttp2_transport* t,
     }
     GPR_ASSERT(error != GRPC_ERROR_NONE);
     t->closed_with_error = GRPC_ERROR_REF(error);
-    connectivity_state_set(t, GRPC_CHANNEL_SHUTDOWN, GRPC_ERROR_REF(error),
-                           "close_transport");
+    connectivity_state_set(t, GRPC_CHANNEL_SHUTDOWN, "close_transport");
     if (t->ping_state.is_delayed_ping_timer_set) {
       grpc_timer_cancel(&t->ping_state.delayed_ping_timer);
     }
@@ -645,17 +644,23 @@ void grpc_chttp2_stream_unref(grpc_chttp2_stream* s) {
 }
 #endif
 
-grpc_chttp2_stream::grpc_chttp2_stream(grpc_chttp2_transport* t,
-                                       grpc_stream_refcount* refcount,
-                                       const void* server_data,
-                                       gpr_arena* arena)
-    : t(t), refcount(refcount), metadata_buffer{{arena}, {arena}} {
+grpc_chttp2_stream::Reffer::Reffer(grpc_chttp2_stream* s) {
   /* We reserve one 'active stream' that's dropped when the stream is
      read-closed. The others are for Chttp2IncomingByteStreams that are
      actively reading */
-  GRPC_CHTTP2_STREAM_REF(this, "chttp2");
-  GRPC_CHTTP2_REF_TRANSPORT(t, "stream");
+  GRPC_CHTTP2_STREAM_REF(s, "chttp2");
+  GRPC_CHTTP2_REF_TRANSPORT(s->t, "stream");
+}
 
+grpc_chttp2_stream::grpc_chttp2_stream(grpc_chttp2_transport* t,
+                                       grpc_stream_refcount* refcount,
+                                       const void* server_data,
+                                       grpc_core::Arena* arena)
+    : t(t),
+      refcount(refcount),
+      reffer(this),
+      metadata_buffer{grpc_chttp2_incoming_metadata_buffer(arena),
+                      grpc_chttp2_incoming_metadata_buffer(arena)} {
   if (server_data) {
     id = static_cast<uint32_t>((uintptr_t)server_data);
     *t->accepting_stream = this;
@@ -674,8 +679,6 @@ grpc_chttp2_stream::grpc_chttp2_stream(grpc_chttp2_transport* t,
   grpc_slice_buffer_init(&frame_storage);
   grpc_slice_buffer_init(&unprocessed_incoming_frames_buffer);
   grpc_slice_buffer_init(&flow_controlled_buffer);
-  grpc_slice_buffer_init(&compressed_data_buffer);
-  grpc_slice_buffer_init(&decompressed_data_buffer);
 
   GRPC_CLOSURE_INIT(&complete_fetch_locked, ::complete_fetch_locked, this,
                     grpc_combiner_scheduler(t->combiner));
@@ -699,8 +702,13 @@ grpc_chttp2_stream::~grpc_chttp2_stream() {
 
   grpc_slice_buffer_destroy_internal(&unprocessed_incoming_frames_buffer);
   grpc_slice_buffer_destroy_internal(&frame_storage);
-  grpc_slice_buffer_destroy_internal(&compressed_data_buffer);
-  grpc_slice_buffer_destroy_internal(&decompressed_data_buffer);
+  if (stream_compression_method != GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS) {
+    grpc_slice_buffer_destroy_internal(&compressed_data_buffer);
+  }
+  if (stream_decompression_method !=
+      GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS) {
+    grpc_slice_buffer_destroy_internal(&decompressed_data_buffer);
+  }
 
   grpc_chttp2_list_remove_stalled_by_transport(t, this);
   grpc_chttp2_list_remove_stalled_by_stream(t, this);
@@ -736,7 +744,7 @@ grpc_chttp2_stream::~grpc_chttp2_stream() {
 
 static int init_stream(grpc_transport* gt, grpc_stream* gs,
                        grpc_stream_refcount* refcount, const void* server_data,
-                       gpr_arena* arena) {
+                       grpc_core::Arena* arena) {
   GPR_TIMER_SCOPE("init_stream", 0);
   grpc_chttp2_transport* t = reinterpret_cast<grpc_chttp2_transport*>(gt);
   new (gs) grpc_chttp2_stream(t, refcount, server_data, arena);
@@ -754,12 +762,15 @@ static void destroy_stream(grpc_transport* gt, grpc_stream* gs,
   GPR_TIMER_SCOPE("destroy_stream", 0);
   grpc_chttp2_transport* t = reinterpret_cast<grpc_chttp2_transport*>(gt);
   grpc_chttp2_stream* s = reinterpret_cast<grpc_chttp2_stream*>(gs);
-
-  if (s->stream_compression_ctx != nullptr) {
+  if (s->stream_compression_method !=
+          GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS &&
+      s->stream_compression_ctx != nullptr) {
     grpc_stream_compression_context_destroy(s->stream_compression_ctx);
     s->stream_compression_ctx = nullptr;
   }
-  if (s->stream_decompression_ctx != nullptr) {
+  if (s->stream_decompression_method !=
+          GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS &&
+      s->stream_decompression_ctx != nullptr) {
     grpc_stream_compression_context_destroy(s->stream_decompression_ctx);
     s->stream_decompression_ctx = nullptr;
   }
@@ -1166,8 +1177,7 @@ void grpc_chttp2_add_incoming_goaway(grpc_chttp2_transport* t,
 
   /* lie: use transient failure from the transport to indicate goaway has been
    * received */
-  connectivity_state_set(t, GRPC_CHANNEL_TRANSIENT_FAILURE,
-                         GRPC_ERROR_REF(t->goaway_error), "got_goaway");
+  connectivity_state_set(t, GRPC_CHANNEL_TRANSIENT_FAILURE, "got_goaway");
 }
 
 static void maybe_start_some_streams(grpc_chttp2_transport* t) {
@@ -1189,10 +1199,8 @@ static void maybe_start_some_streams(grpc_chttp2_transport* t) {
     t->next_stream_id += 2;
 
     if (t->next_stream_id >= MAX_CLIENT_STREAM_ID) {
-      connectivity_state_set(
-          t, GRPC_CHANNEL_TRANSIENT_FAILURE,
-          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Stream IDs exhausted"),
-          "no_more_stream_ids");
+      connectivity_state_set(t, GRPC_CHANNEL_TRANSIENT_FAILURE,
+                             "no_more_stream_ids");
     }
 
     grpc_chttp2_stream_map_add(&t->stream_map, s->id, s);
@@ -1241,7 +1249,7 @@ void grpc_chttp2_complete_closure_step(grpc_chttp2_transport* t,
     return;
   }
   closure->next_data.scratch -= CLOSURE_BARRIER_FIRST_REF_BIT;
-  if (grpc_http_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
     const char* errstr = grpc_error_string(error);
     gpr_log(
         GPR_INFO,
@@ -1280,8 +1288,8 @@ void grpc_chttp2_complete_closure_step(grpc_chttp2_transport* t,
 
 static bool contains_non_ok_status(grpc_metadata_batch* batch) {
   if (batch->idx.named.grpc_status != nullptr) {
-    return !grpc_mdelem_eq(batch->idx.named.grpc_status->md,
-                           GRPC_MDELEM_GRPC_STATUS_0);
+    return !grpc_mdelem_static_value_eq(batch->idx.named.grpc_status->md,
+                                        GRPC_MDELEM_GRPC_STATUS_0);
   }
   return false;
 }
@@ -1393,7 +1401,7 @@ static void perform_stream_op_locked(void* stream_op,
 
   s->context = op->payload->context;
   s->traced = op->is_traced;
-  if (grpc_http_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
     char* str = grpc_transport_stream_op_batch_string(op);
     gpr_log(GPR_INFO, "perform_stream_op_locked: %s; on_complete = %p", str,
             op->on_complete);
@@ -1412,7 +1420,7 @@ static void perform_stream_op_locked(void* stream_op,
   // on_complete will be null if and only if there are no send ops in the batch.
   if (on_complete != nullptr) {
     // This batch has send ops. Use final_data as a barrier until enqueue time;
-    // the inital counter is dropped at the end of this function.
+    // the initial counter is dropped at the end of this function.
     on_complete->next_data.scratch = CLOSURE_BARRIER_FIRST_REF_BIT;
     on_complete->error_data.error = GRPC_ERROR_NONE;
   }
@@ -1440,7 +1448,12 @@ static void perform_stream_op_locked(void* stream_op,
             true, &s->stream_compression_method) == 0) {
       s->stream_compression_method = GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS;
     }
-
+    if (s->stream_compression_method !=
+        GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS) {
+      s->uncompressed_data_size = 0;
+      s->stream_compression_ctx = nullptr;
+      grpc_slice_buffer_init(&s->compressed_data_buffer);
+    }
     s->send_initial_metadata_finished = add_closure_barrier(on_complete);
     s->send_initial_metadata =
         op_payload->send_initial_metadata.send_initial_metadata;
@@ -1692,7 +1705,7 @@ static void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
     }
   }
 
-  if (grpc_http_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
     char* str = grpc_transport_stream_op_batch_string(op);
     gpr_log(GPR_INFO, "perform_stream_op[s=%p]: %s", s, str);
     gpr_free(str);
@@ -1859,7 +1872,7 @@ static void perform_transport_op_locked(void* stream_op,
 
 static void perform_transport_op(grpc_transport* gt, grpc_transport_op* op) {
   grpc_chttp2_transport* t = reinterpret_cast<grpc_chttp2_transport*>(gt);
-  if (grpc_http_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
     char* msg = grpc_transport_op_string(op);
     gpr_log(GPR_INFO, "perform_transport_op[t=%p]: %s", t, msg);
     gpr_free(msg);
@@ -1996,27 +2009,39 @@ void grpc_chttp2_maybe_complete_recv_trailing_metadata(grpc_chttp2_transport* t,
         !s->seen_error && s->recv_trailing_metadata_finished != nullptr) {
       /* Maybe some SYNC_FLUSH data is left in frame_storage. Consume them and
        * maybe decompress the next 5 bytes in the stream. */
-      bool end_of_context;
-      if (!s->stream_decompression_ctx) {
-        s->stream_decompression_ctx = grpc_stream_compression_context_create(
-            s->stream_decompression_method);
-      }
-      if (!grpc_stream_decompress(
-              s->stream_decompression_ctx, &s->frame_storage,
-              &s->unprocessed_incoming_frames_buffer, nullptr,
-              GRPC_HEADER_SIZE_IN_BYTES, &end_of_context)) {
-        grpc_slice_buffer_reset_and_unref_internal(&s->frame_storage);
-        grpc_slice_buffer_reset_and_unref_internal(
-            &s->unprocessed_incoming_frames_buffer);
-        s->seen_error = true;
-      } else {
+      if (s->stream_decompression_method ==
+          GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS) {
+        grpc_slice_buffer_move_first(&s->frame_storage,
+                                     GRPC_HEADER_SIZE_IN_BYTES,
+                                     &s->unprocessed_incoming_frames_buffer);
         if (s->unprocessed_incoming_frames_buffer.length > 0) {
           s->unprocessed_incoming_frames_decompressed = true;
           pending_data = true;
         }
-        if (end_of_context) {
-          grpc_stream_compression_context_destroy(s->stream_decompression_ctx);
-          s->stream_decompression_ctx = nullptr;
+      } else {
+        bool end_of_context;
+        if (!s->stream_decompression_ctx) {
+          s->stream_decompression_ctx = grpc_stream_compression_context_create(
+              s->stream_decompression_method);
+        }
+        if (!grpc_stream_decompress(
+                s->stream_decompression_ctx, &s->frame_storage,
+                &s->unprocessed_incoming_frames_buffer, nullptr,
+                GRPC_HEADER_SIZE_IN_BYTES, &end_of_context)) {
+          grpc_slice_buffer_reset_and_unref_internal(&s->frame_storage);
+          grpc_slice_buffer_reset_and_unref_internal(
+              &s->unprocessed_incoming_frames_buffer);
+          s->seen_error = true;
+        } else {
+          if (s->unprocessed_incoming_frames_buffer.length > 0) {
+            s->unprocessed_incoming_frames_decompressed = true;
+            pending_data = true;
+          }
+          if (end_of_context) {
+            grpc_stream_compression_context_destroy(
+                s->stream_decompression_ctx);
+            s->stream_decompression_ctx = nullptr;
+          }
         }
       }
     }
@@ -2594,10 +2619,13 @@ static void schedule_bdp_ping_locked(grpc_chttp2_transport* t) {
 
 static void start_bdp_ping_locked(void* tp, grpc_error* error) {
   grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(tp);
-  if (grpc_http_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
     gpr_log(GPR_INFO, "%s: Start BDP ping err=%s", t->peer_string,
             grpc_error_string(error));
   }
+  if (error != GRPC_ERROR_NONE || t->closed_with_error != GRPC_ERROR_NONE) {
+    return;
+  }
   /* Reset the keepalive ping timer */
   if (t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_WAITING) {
     grpc_timer_cancel(&t->keepalive_ping_timer);
@@ -2607,11 +2635,11 @@ static void start_bdp_ping_locked(void* tp, grpc_error* error) {
 
 static void finish_bdp_ping_locked(void* tp, grpc_error* error) {
   grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(tp);
-  if (grpc_http_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
     gpr_log(GPR_INFO, "%s: Complete BDP ping err=%s", t->peer_string,
             grpc_error_string(error));
   }
-  if (error != GRPC_ERROR_NONE) {
+  if (error != GRPC_ERROR_NONE || t->closed_with_error != GRPC_ERROR_NONE) {
     GRPC_CHTTP2_UNREF_TRANSPORT(t, "bdp_ping");
     return;
   }
@@ -2739,7 +2767,7 @@ static void start_keepalive_ping_locked(void* arg, grpc_error* error) {
   if (t->channelz_socket != nullptr) {
     t->channelz_socket->RecordKeepaliveSent();
   }
-  if (grpc_http_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
     gpr_log(GPR_INFO, "%s: Start keepalive ping", t->peer_string);
   }
   GRPC_CHTTP2_REF_TRANSPORT(t, "keepalive watchdog");
@@ -2752,7 +2780,7 @@ static void finish_keepalive_ping_locked(void* arg, grpc_error* error) {
   grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(arg);
   if (t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_PINGING) {
     if (error == GRPC_ERROR_NONE) {
-      if (grpc_http_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
         gpr_log(GPR_INFO, "%s: Finish keepalive ping", t->peer_string);
       }
       t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_WAITING;
@@ -2796,9 +2824,9 @@ static void keepalive_watchdog_fired_locked(void* arg, grpc_error* error) {
 
 static void connectivity_state_set(grpc_chttp2_transport* t,
                                    grpc_connectivity_state state,
-                                   grpc_error* error, const char* reason) {
+                                   const char* reason) {
   GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "set connectivity_state=%d", state));
-  grpc_connectivity_state_set(&t->channel_callback.state_tracker, state, error,
+  grpc_connectivity_state_set(&t->channel_callback.state_tracker, state,
                               reason);
 }
 
@@ -2936,6 +2964,8 @@ bool Chttp2IncomingByteStream::Next(size_t max_size_hint,
 }
 
 void Chttp2IncomingByteStream::MaybeCreateStreamDecompressionCtx() {
+  GPR_DEBUG_ASSERT(stream_->stream_decompression_method !=
+                   GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS);
   if (!stream_->stream_decompression_ctx) {
     stream_->stream_decompression_ctx = grpc_stream_compression_context_create(
         stream_->stream_decompression_method);
@@ -2946,7 +2976,9 @@ grpc_error* Chttp2IncomingByteStream::Pull(grpc_slice* slice) {
   GPR_TIMER_SCOPE("incoming_byte_stream_pull", 0);
   grpc_error* error;
   if (stream_->unprocessed_incoming_frames_buffer.length > 0) {
-    if (!stream_->unprocessed_incoming_frames_decompressed) {
+    if (!stream_->unprocessed_incoming_frames_decompressed &&
+        stream_->stream_decompression_method !=
+            GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS) {
       bool end_of_context;
       MaybeCreateStreamDecompressionCtx();
       if (!grpc_stream_decompress(stream_->stream_decompression_ctx,
@@ -3058,7 +3090,7 @@ static void benign_reclaimer_locked(void* arg, grpc_error* error) {
       grpc_chttp2_stream_map_size(&t->stream_map) == 0) {
     /* Channel with no active streams: send a goaway to try and make it
      * disconnect cleanly */
-    if (grpc_resource_quota_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_resource_quota_trace)) {
       gpr_log(GPR_INFO, "HTTP2: %s - send goaway to free memory",
               t->peer_string);
     }
@@ -3066,7 +3098,8 @@ static void benign_reclaimer_locked(void* arg, grpc_error* error) {
                 grpc_error_set_int(
                     GRPC_ERROR_CREATE_FROM_STATIC_STRING("Buffers full"),
                     GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_ENHANCE_YOUR_CALM));
-  } else if (error == GRPC_ERROR_NONE && grpc_resource_quota_trace.enabled()) {
+  } else if (error == GRPC_ERROR_NONE &&
+             GRPC_TRACE_FLAG_ENABLED(grpc_resource_quota_trace)) {
     gpr_log(GPR_INFO,
             "HTTP2: %s - skip benign reclamation, there are still %" PRIdPTR
             " streams",
@@ -3087,7 +3120,7 @@ static void destructive_reclaimer_locked(void* arg, grpc_error* error) {
   if (error == GRPC_ERROR_NONE && n > 0) {
     grpc_chttp2_stream* s = static_cast<grpc_chttp2_stream*>(
         grpc_chttp2_stream_map_rand(&t->stream_map));
-    if (grpc_resource_quota_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_resource_quota_trace)) {
       gpr_log(GPR_INFO, "HTTP2: %s - abandon stream id %d", t->peer_string,
               s->id);
     }
index 120fefc..49e206f 100644 (file)
@@ -127,7 +127,7 @@ class FlowControlTrace {
             StreamFlowControl* sfc);
   void Finish();
 
-  const bool enabled_ = grpc_flowctl_trace.enabled();
+  const bool enabled_ = GRPC_TRACE_FLAG_ENABLED(grpc_flowctl_trace);
 
   TransportFlowControl* tfc_;
   StreamFlowControl* sfc_;
index ed1554e..3f84679 100644 (file)
@@ -217,19 +217,20 @@ grpc_error* grpc_chttp2_settings_parser_parse(void* p, grpc_chttp2_transport* t,
               parser->incoming_settings[id] != parser->value) {
             t->initial_window_update += static_cast<int64_t>(parser->value) -
                                         parser->incoming_settings[id];
-            if (grpc_http_trace.enabled() || grpc_flowctl_trace.enabled()) {
+            if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace) ||
+                GRPC_TRACE_FLAG_ENABLED(grpc_flowctl_trace)) {
               gpr_log(GPR_INFO, "%p[%s] adding %d for initial_window change", t,
                       t->is_client ? "cli" : "svr",
                       static_cast<int>(t->initial_window_update));
             }
           }
           parser->incoming_settings[id] = parser->value;
-          if (grpc_http_trace.enabled()) {
+          if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
             gpr_log(GPR_INFO, "CHTTP2:%s:%s: got setting %s = %d",
                     t->is_client ? "CLI" : "SVR", t->peer_string, sp->name,
                     parser->value);
           }
-        } else if (grpc_http_trace.enabled()) {
+        } else if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
           gpr_log(GPR_ERROR, "CHTTP2: Ignoring unknown setting %d (value %d)",
                   parser->id, parser->value);
         }
index 9b4c3ce..d2607e9 100644 (file)
@@ -56,7 +56,7 @@
 /* don't consider adding anything bigger than this to the hpack table */
 #define MAX_DECODER_SPACE_USAGE 512
 
-static grpc_slice_refcount terminal_slice_refcount = {nullptr, nullptr};
+static grpc_slice_refcount terminal_slice_refcount;
 static const grpc_slice terminal_slice = {
     &terminal_slice_refcount, /* refcount */
     {{0, nullptr}}            /* data.refcounted */
@@ -461,7 +461,7 @@ static void hpack_enc(grpc_chttp2_hpack_compressor* c, grpc_mdelem elem,
         "Reserved header (colon-prefixed) happening after regular ones.");
   }
 
-  if (grpc_http_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
     char* k = grpc_slice_to_c_string(GRPC_MDKEY(elem));
     char* v = nullptr;
     if (grpc_is_binary_header(GRPC_MDKEY(elem))) {
@@ -660,7 +660,7 @@ void grpc_chttp2_hpack_compressor_set_max_table_size(
     }
   }
   c->advertise_table_size_change = 1;
-  if (grpc_http_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
     gpr_log(GPR_INFO, "set max table size from encoder to %d", max_table_size);
   }
 }
index 5bcdb4e..6e42212 100644 (file)
@@ -624,7 +624,7 @@ static const uint8_t inverse_base64[256] = {
 /* emission helpers */
 static grpc_error* on_hdr(grpc_chttp2_hpack_parser* p, grpc_mdelem md,
                           int add_to_table) {
-  if (grpc_http_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
     char* k = grpc_slice_to_c_string(GRPC_MDKEY(md));
     char* v = nullptr;
     if (grpc_is_binary_header(GRPC_MDKEY(md))) {
@@ -994,7 +994,7 @@ static grpc_error* parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser* p,
 /* finish parsing a max table size change */
 static grpc_error* finish_max_tbl_size(grpc_chttp2_hpack_parser* p,
                                        const uint8_t* cur, const uint8_t* end) {
-  if (grpc_http_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
     gpr_log(GPR_INFO, "MAX TABLE SIZE: %d", p->index);
   }
   grpc_error* err =
@@ -1616,6 +1616,12 @@ static void parse_stream_compression_md(grpc_chttp2_transport* t,
     s->stream_decompression_method =
         GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS;
   }
+
+  if (s->stream_decompression_method !=
+      GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS) {
+    s->stream_decompression_ctx = nullptr;
+    grpc_slice_buffer_init(&s->decompressed_data_buffer);
+  }
 }
 
 grpc_error* grpc_chttp2_header_parser_parse(void* hpack_parser,
index fcfb018..16aeb49 100644 (file)
@@ -247,7 +247,7 @@ void grpc_chttp2_hptbl_set_max_bytes(grpc_chttp2_hptbl* tbl,
   if (tbl->max_bytes == max_bytes) {
     return;
   }
-  if (grpc_http_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
     gpr_log(GPR_INFO, "Update hpack parser max size to %d", max_bytes);
   }
   while (tbl->mem_used > max_bytes) {
@@ -270,7 +270,7 @@ grpc_error* grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl* tbl,
     gpr_free(msg);
     return err;
   }
-  if (grpc_http_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
     gpr_log(GPR_INFO, "Update hpack parser table size to %d", bytes);
   }
   while (tbl->mem_used > bytes) {
index 1e04be7..02623c9 100644 (file)
@@ -36,7 +36,7 @@ grpc_error* grpc_chttp2_incoming_metadata_buffer_add(
     buffer->count++;
   } else {
     storage = static_cast<grpc_linked_mdelem*>(
-        gpr_arena_alloc(buffer->arena, sizeof(grpc_linked_mdelem)));
+        buffer->arena->Alloc(sizeof(grpc_linked_mdelem)));
   }
   return grpc_metadata_batch_add_tail(&buffer->batch, storage, elem);
 }
index 4a9a592..b63caa1 100644 (file)
@@ -24,7 +24,8 @@
 #include "src/core/lib/transport/transport.h"
 
 struct grpc_chttp2_incoming_metadata_buffer {
-  grpc_chttp2_incoming_metadata_buffer(gpr_arena* arena) : arena(arena) {
+  explicit grpc_chttp2_incoming_metadata_buffer(grpc_core::Arena* arena)
+      : arena(arena) {
     grpc_metadata_batch_init(&batch);
     batch.deadline = GRPC_MILLIS_INF_FUTURE;
   }
@@ -34,7 +35,7 @@ struct grpc_chttp2_incoming_metadata_buffer {
 
   static constexpr size_t kPreallocatedMDElem = 10;
 
-  gpr_arena* arena;
+  grpc_core::Arena* arena;
   size_t size = 0;   // total size of metadata.
   size_t count = 0;  // minimum of count of metadata and kPreallocatedMDElem.
   // These preallocated mdelems are used while count < kPreallocatedMDElem.
index 760324c..bfe7745 100644 (file)
@@ -238,7 +238,7 @@ class Chttp2IncomingByteStream : public ByteStream {
   // switch to std::shared_ptr<>.
   void Ref() { refs_.Ref(); }
   void Unref() {
-    if (refs_.Unref()) {
+    if (GPR_UNLIKELY(refs_.Unref())) {
       grpc_core::Delete(this);
     }
   }
@@ -504,12 +504,18 @@ typedef enum {
 
 struct grpc_chttp2_stream {
   grpc_chttp2_stream(grpc_chttp2_transport* t, grpc_stream_refcount* refcount,
-                     const void* server_data, gpr_arena* arena);
+                     const void* server_data, grpc_core::Arena* arena);
   ~grpc_chttp2_stream();
 
   void* context;
   grpc_chttp2_transport* t;
   grpc_stream_refcount* refcount;
+  // Reffer is a 0-len structure, simply reffing `t` and `refcount` in its ctor
+  // before initializing the rest of the stream, to avoid cache misses. This
+  // field MUST be right after `t` and `refcount`.
+  struct Reffer {
+    explicit Reffer(grpc_chttp2_stream* s);
+  } reffer;
 
   grpc_closure destroy_stream;
   grpc_closure* destroy_stream_arg;
@@ -577,10 +583,6 @@ struct grpc_chttp2_stream {
 
   grpc_slice_buffer frame_storage; /* protected by t combiner */
 
-  /* Accessed only by transport thread when stream->pending_byte_stream == false
-   * Accessed only by application thread when stream->pending_byte_stream ==
-   * true */
-  grpc_slice_buffer unprocessed_incoming_frames_buffer;
   grpc_closure* on_next = nullptr;  /* protected by t combiner */
   bool pending_byte_stream = false; /* protected by t combiner */
   // cached length of buffer to be used by the transport thread in cases where
@@ -588,6 +590,10 @@ struct grpc_chttp2_stream {
   // application threads are allowed to modify
   // unprocessed_incoming_frames_buffer
   size_t unprocessed_incoming_frames_buffer_cached_length = 0;
+  /* Accessed only by transport thread when stream->pending_byte_stream == false
+   * Accessed only by application thread when stream->pending_byte_stream ==
+   * true */
+  grpc_slice_buffer unprocessed_incoming_frames_buffer;
   grpc_closure reset_byte_stream;
   grpc_error* byte_stream_error = GRPC_ERROR_NONE; /* protected by t combiner */
   bool received_last_frame = false;                /* protected by t combiner */
@@ -627,19 +633,8 @@ struct grpc_chttp2_stream {
       GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS;
   /* Stream decompression method to be used. */
   grpc_stream_compression_method stream_decompression_method =
-      GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS;
-  /** Stream compression decompress context */
-  grpc_stream_compression_context* stream_decompression_ctx = nullptr;
-  /** Stream compression compress context */
-  grpc_stream_compression_context* stream_compression_ctx = nullptr;
+      GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS;
 
-  /** Buffer storing data that is compressed but not sent */
-  grpc_slice_buffer compressed_data_buffer;
-  /** Amount of uncompressed bytes sent out when compressed_data_buffer is
-   * emptied */
-  size_t uncompressed_data_size = 0;
-  /** Temporary buffer storing decompressed data */
-  grpc_slice_buffer decompressed_data_buffer;
   /** Whether bytes stored in unprocessed_incoming_byte_stream is decompressed
    */
   bool unprocessed_incoming_frames_decompressed = false;
@@ -649,6 +644,22 @@ struct grpc_chttp2_stream {
   size_t decompressed_header_bytes = 0;
   /** Byte counter for number of bytes written */
   size_t byte_counter = 0;
+
+  /** Amount of uncompressed bytes sent out when compressed_data_buffer is
+   * emptied */
+  size_t uncompressed_data_size;
+  /** Stream compression compress context */
+  grpc_stream_compression_context* stream_compression_ctx;
+  /** Buffer storing data that is compressed but not sent */
+  grpc_slice_buffer compressed_data_buffer;
+
+  /** Stream compression decompress context */
+  grpc_stream_compression_context* stream_decompression_ctx;
+  /** Temporary buffer storing decompressed data.
+   * Initialized, used, and destroyed only when stream uses (non-identity)
+   * compression.
+   */
+  grpc_slice_buffer decompressed_data_buffer;
 };
 
 /** Transport writing call flow:
@@ -760,11 +771,12 @@ void grpc_chttp2_complete_closure_step(grpc_chttp2_transport* t,
 // extern grpc_core::TraceFlag grpc_http_trace;
 // extern grpc_core::TraceFlag grpc_flowctl_trace;
 
-#define GRPC_CHTTP2_IF_TRACING(stmt) \
-  if (!(grpc_http_trace.enabled()))  \
-    ;                                \
-  else                               \
-    stmt
+#define GRPC_CHTTP2_IF_TRACING(stmt)                \
+  do {                                              \
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) { \
+      (stmt);                                       \
+    }                                               \
+  } while (0)
 
 void grpc_chttp2_fake_status(grpc_chttp2_transport* t,
                              grpc_chttp2_stream* stream, grpc_error* error);
index 84b2275..15648c0 100644 (file)
@@ -304,7 +304,7 @@ static grpc_error* init_frame_parser(grpc_chttp2_transport* t) {
     case GRPC_CHTTP2_FRAME_GOAWAY:
       return init_goaway_parser(t);
     default:
-      if (grpc_http_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
         gpr_log(GPR_ERROR, "Unknown frame type %02x", t->incoming_frame_type);
       }
       return init_skip_frame_parser(t, 0);
@@ -400,7 +400,7 @@ static void on_initial_header(void* tp, grpc_mdelem md) {
   grpc_chttp2_stream* s = t->incoming_stream;
   GPR_ASSERT(s != nullptr);
 
-  if (grpc_http_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
     char* key = grpc_slice_to_c_string(GRPC_MDKEY(md));
     char* value =
         grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII);
@@ -496,7 +496,7 @@ static void on_trailing_header(void* tp, grpc_mdelem md) {
   grpc_chttp2_stream* s = t->incoming_stream;
   GPR_ASSERT(s != nullptr);
 
-  if (grpc_http_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
     char* key = grpc_slice_to_c_string(GRPC_MDKEY(md));
     char* value =
         grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII);
@@ -761,7 +761,7 @@ static grpc_error* parse_frame_slice(grpc_chttp2_transport* t,
   if (GPR_LIKELY(err == GRPC_ERROR_NONE)) {
     return err;
   } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, &unused)) {
-    if (grpc_http_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
       const char* msg = grpc_error_string(err);
       gpr_log(GPR_ERROR, "%s", msg);
     }
index 6626170..db79899 100644 (file)
@@ -67,7 +67,7 @@ static bool stream_list_pop(grpc_chttp2_transport* t,
     s->included[id] = 0;
   }
   *stream = s;
-  if (s && grpc_trace_http2_stream_state.enabled()) {
+  if (s && GRPC_TRACE_FLAG_ENABLED(grpc_trace_http2_stream_state)) {
     gpr_log(GPR_INFO, "%p[%d][%s]: pop from %s", t, s->id,
             t->is_client ? "cli" : "svr", stream_list_id_string(id));
   }
@@ -89,7 +89,7 @@ static void stream_list_remove(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
   } else {
     t->lists[id].tail = s->links[id].prev;
   }
-  if (grpc_trace_http2_stream_state.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_http2_stream_state)) {
     gpr_log(GPR_INFO, "%p[%d][%s]: remove from %s", t, s->id,
             t->is_client ? "cli" : "svr", stream_list_id_string(id));
   }
@@ -121,7 +121,7 @@ static void stream_list_add_tail(grpc_chttp2_transport* t,
   }
   t->lists[id].tail = s;
   s->included[id] = 1;
-  if (grpc_trace_http2_stream_state.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_http2_stream_state)) {
     gpr_log(GPR_INFO, "%p[%d][%s]: add to %s", t, s->id,
             t->is_client ? "cli" : "svr", stream_list_id_string(id));
   }
index bc8968a..f3cb390 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <grpc/support/log.h>
 
+#include "src/core/lib/compression/stream_compression.h"
 #include "src/core/lib/debug/stats.h"
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/slice/slice_internal.h"
@@ -52,7 +53,8 @@ static void maybe_initiate_ping(grpc_chttp2_transport* t) {
   }
   if (!grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_INFLIGHT])) {
     /* ping already in-flight: wait */
-    if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace) ||
+        GRPC_TRACE_FLAG_ENABLED(grpc_bdp_estimator_trace)) {
       gpr_log(GPR_INFO, "%s: Ping delayed [%p]: already pinging",
               t->is_client ? "CLIENT" : "SERVER", t->peer_string);
     }
@@ -61,7 +63,8 @@ static void maybe_initiate_ping(grpc_chttp2_transport* t) {
   if (t->ping_state.pings_before_data_required == 0 &&
       t->ping_policy.max_pings_without_data != 0) {
     /* need to receive something of substance before sending a ping again */
-    if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace) ||
+        GRPC_TRACE_FLAG_ENABLED(grpc_bdp_estimator_trace)) {
       gpr_log(GPR_INFO, "%s: Ping delayed [%p]: too many recent pings: %d/%d",
               t->is_client ? "CLIENT" : "SERVER", t->peer_string,
               t->ping_state.pings_before_data_required,
@@ -81,7 +84,8 @@ static void maybe_initiate_ping(grpc_chttp2_transport* t) {
 
   if (next_allowed_ping > now) {
     /* not enough elapsed time between successive pings */
-    if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace) ||
+        GRPC_TRACE_FLAG_ENABLED(grpc_bdp_estimator_trace)) {
       gpr_log(GPR_INFO,
               "%s: Ping delayed [%p]: not enough time elapsed since last ping. "
               " Last ping %f: Next ping %f: Now %f",
@@ -107,7 +111,8 @@ static void maybe_initiate_ping(grpc_chttp2_transport* t) {
                         grpc_chttp2_ping_create(false, pq->inflight_id));
   GRPC_STATS_INC_HTTP2_PINGS_SENT();
   t->ping_state.last_ping_sent_time = now;
-  if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace) ||
+      GRPC_TRACE_FLAG_ENABLED(grpc_bdp_estimator_trace)) {
     gpr_log(GPR_INFO, "%s: Ping sent [%s]: %d/%d",
             t->is_client ? "CLIENT" : "SERVER", t->peer_string,
             t->ping_state.pings_before_data_required,
@@ -140,7 +145,7 @@ static bool update_list(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
 
 static void report_stall(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
                          const char* staller) {
-  if (grpc_flowctl_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_flowctl_trace)) {
     gpr_log(
         GPR_DEBUG,
         "%s:%p stream %d moved to stalled list by %s. This is FULLY expected "
@@ -150,7 +155,11 @@ static void report_stall(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
         ":flowed=%" PRId64 ":peer_initwin=%d:t_win=%" PRId64
         ":s_win=%d:s_delta=%" PRId64 "]",
         t->peer_string, t, s->id, staller, s->flow_controlled_buffer.length,
-        s->compressed_data_buffer.length, s->flow_controlled_bytes_flowed,
+        s->stream_compression_method ==
+                GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS
+            ? 0
+            : s->compressed_data_buffer.length,
+        s->flow_controlled_bytes_flowed,
         t->settings[GRPC_ACKED_SETTINGS]
                    [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
         t->flow_control->remote_window(),
@@ -163,15 +172,6 @@ static void report_stall(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
   }
 }
 
-static bool stream_ref_if_not_destroyed(gpr_refcount* r) {
-  gpr_atm count;
-  do {
-    count = gpr_atm_acq_load(&r->count);
-    if (count == 0) return false;
-  } while (!gpr_atm_rel_cas(&r->count, count, count + 1));
-  return true;
-}
-
 /* How many bytes would we like to put on the wire during a single syscall */
 static uint32_t target_write_size(grpc_chttp2_transport* t) {
   return 1024 * 1024;
@@ -254,7 +254,7 @@ class WriteContext {
     while (grpc_chttp2_list_pop_stalled_by_transport(t_, &s)) {
       if (t_->closed_with_error == GRPC_ERROR_NONE &&
           grpc_chttp2_list_add_writable_stream(t_, s)) {
-        if (!stream_ref_if_not_destroyed(&s->refcount->refs)) {
+        if (!s->refcount->refs.RefIfNonZero()) {
           grpc_chttp2_list_remove_writable_stream(t_, s);
         }
       }
@@ -334,7 +334,23 @@ class DataSendContext {
 
   bool AnyOutgoing() const { return max_outgoing() > 0; }
 
+  void FlushUncompressedBytes() {
+    uint32_t send_bytes = static_cast<uint32_t> GPR_MIN(
+        max_outgoing(), s_->flow_controlled_buffer.length);
+    is_last_frame_ = send_bytes == s_->flow_controlled_buffer.length &&
+                     s_->fetching_send_message == nullptr &&
+                     s_->send_trailing_metadata != nullptr &&
+                     grpc_metadata_batch_is_empty(s_->send_trailing_metadata);
+    grpc_chttp2_encode_data(s_->id, &s_->flow_controlled_buffer, send_bytes,
+                            is_last_frame_, &s_->stats.outgoing, &t_->outbuf);
+    s_->flow_control->SentData(send_bytes);
+    s_->sending_bytes += send_bytes;
+  }
+
   void FlushCompressedBytes() {
+    GPR_DEBUG_ASSERT(s_->stream_compression_method !=
+                     GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS);
+
     uint32_t send_bytes = static_cast<uint32_t> GPR_MIN(
         max_outgoing(), s_->compressed_data_buffer.length);
     bool is_last_data_frame =
@@ -369,6 +385,9 @@ class DataSendContext {
   }
 
   void CompressMoreBytes() {
+    GPR_DEBUG_ASSERT(s_->stream_compression_method !=
+                     GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS);
+
     if (s_->stream_compression_ctx == nullptr) {
       s_->stream_compression_ctx =
           grpc_stream_compression_context_create(s_->stream_compression_method);
@@ -426,7 +445,7 @@ class StreamWriteContext {
     // https://github.com/grpc/proposal/blob/master/A6-client-retries.md#when-retries-are-valid
     if (!t_->is_client && s_->fetching_send_message == nullptr &&
         s_->flow_controlled_buffer.length == 0 &&
-        s_->compressed_data_buffer.length == 0 &&
+        compressed_data_buffer_len() == 0 &&
         s_->send_trailing_metadata != nullptr &&
         is_default_initial_metadata(s_->send_initial_metadata)) {
       ConvertInitialMetadataToTrailingMetadata();
@@ -455,6 +474,13 @@ class StreamWriteContext {
         "send_initial_metadata_finished");
   }
 
+  bool compressed_data_buffer_len() {
+    return s_->stream_compression_method ==
+                   GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS
+               ? 0
+               : s_->compressed_data_buffer.length;
+  }
+
   void FlushWindowUpdates() {
     /* send any window updates */
     const uint32_t stream_announce = s_->flow_control->MaybeSendUpdate();
@@ -471,7 +497,7 @@ class StreamWriteContext {
     if (!s_->sent_initial_metadata) return;
 
     if (s_->flow_controlled_buffer.length == 0 &&
-        s_->compressed_data_buffer.length == 0) {
+        compressed_data_buffer_len() == 0) {
       return;  // early out: nothing to do
     }
 
@@ -488,13 +514,21 @@ class StreamWriteContext {
       return;  // early out: nothing to do
     }
 
-    while ((s_->flow_controlled_buffer.length > 0 ||
-            s_->compressed_data_buffer.length > 0) &&
-           data_send_context.max_outgoing() > 0) {
-      if (s_->compressed_data_buffer.length > 0) {
-        data_send_context.FlushCompressedBytes();
-      } else {
-        data_send_context.CompressMoreBytes();
+    if (s_->stream_compression_method ==
+        GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS) {
+      while (s_->flow_controlled_buffer.length > 0 &&
+             data_send_context.max_outgoing() > 0) {
+        data_send_context.FlushUncompressedBytes();
+      }
+    } else {
+      while ((s_->flow_controlled_buffer.length > 0 ||
+              s_->compressed_data_buffer.length > 0) &&
+             data_send_context.max_outgoing() > 0) {
+        if (s_->compressed_data_buffer.length > 0) {
+          data_send_context.FlushCompressedBytes();
+        } else {
+          data_send_context.CompressMoreBytes();
+        }
       }
     }
     write_context_->ResetPingClock();
@@ -504,7 +538,7 @@ class StreamWriteContext {
     data_send_context.CallCallbacks();
     stream_became_writable_ = true;
     if (s_->flow_controlled_buffer.length > 0 ||
-        s_->compressed_data_buffer.length > 0) {
+        compressed_data_buffer_len() > 0) {
       GRPC_CHTTP2_STREAM_REF(s_, "chttp2_writing:fork");
       grpc_chttp2_list_add_writable_stream(t_, s_);
     }
@@ -517,7 +551,7 @@ class StreamWriteContext {
     if (s_->send_trailing_metadata == nullptr) return;
     if (s_->fetching_send_message != nullptr) return;
     if (s_->flow_controlled_buffer.length != 0) return;
-    if (s_->compressed_data_buffer.length != 0) return;
+    if (compressed_data_buffer_len() != 0) return;
 
     GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "sending trailing_metadata"));
     if (grpc_metadata_batch_is_empty(s_->send_trailing_metadata)) {
index 9551b4b..320b529 100644 (file)
@@ -29,6 +29,7 @@
 #include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
 #include "src/core/ext/transport/chttp2/transport/incoming_metadata.h"
 #include "src/core/ext/transport/cronet/transport/cronet_transport.h"
+#include "src/core/lib/debug/trace.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gprpp/manual_constructor.h"
 #define GRPC_HEADER_SIZE_IN_BYTES 5
 #define GRPC_FLUSH_READ_SIZE 4096
 
-#define CRONET_LOG(...)                          \
-  do {                                           \
-    if (grpc_cronet_trace) gpr_log(__VA_ARGS__); \
+grpc_core::TraceFlag grpc_cronet_trace(false, "cronet");
+#define CRONET_LOG(...)                                    \
+  do {                                                     \
+    if (grpc_cronet_trace.enabled()) gpr_log(__VA_ARGS__); \
   } while (0)
 
-/* TODO (makdharma): Hook up into the wider tracing mechanism */
-int grpc_cronet_trace = 0;
-
 enum e_op_result {
   ACTION_TAKEN_WITH_CALLBACK,
   ACTION_TAKEN_NO_CALLBACK,
@@ -111,7 +110,7 @@ typedef struct grpc_cronet_transport grpc_cronet_transport;
 /* TODO (makdharma): reorder structure for memory efficiency per
    http://www.catb.org/esr/structure-packing/#_structure_reordering: */
 struct read_state {
-  read_state(gpr_arena* arena)
+  read_state(grpc_core::Arena* arena)
       : trailing_metadata(arena), initial_metadata(arena) {
     grpc_slice_buffer_init(&read_slice_buffer);
   }
@@ -145,7 +144,7 @@ struct write_state {
 
 /* track state of one stream op */
 struct op_state {
-  op_state(gpr_arena* arena) : rs(arena) {}
+  op_state(grpc_core::Arena* arena) : rs(arena) {}
 
   bool state_op_done[OP_NUM_OPS] = {};
   bool state_callback_received[OP_NUM_OPS] = {};
@@ -177,7 +176,7 @@ struct op_and_state {
   bool done = false;
   struct stream_obj* s; /* Pointer back to the stream object */
   /* next op_and_state in the linked list */
-  struct op_and_state* next;
+  struct op_and_state* next = nullptr;
 };
 
 struct op_storage {
@@ -187,10 +186,10 @@ struct op_storage {
 
 struct stream_obj {
   stream_obj(grpc_transport* gt, grpc_stream* gs,
-             grpc_stream_refcount* refcount, gpr_arena* arena);
+             grpc_stream_refcount* refcount, grpc_core::Arena* arena);
   ~stream_obj();
 
-  gpr_arena* arena;
+  grpc_core::Arena* arena;
   struct op_and_state* oas = nullptr;
   grpc_transport_stream_op_batch* curr_op = nullptr;
   grpc_cronet_transport* curr_ct;
@@ -324,7 +323,7 @@ static grpc_error* make_error_with_desc(int error_code, const char* desc) {
 
 inline op_and_state::op_and_state(stream_obj* s,
                                   const grpc_transport_stream_op_batch& op)
-    : op(op), state(s->arena), s(s), next(s->storage.head) {}
+    : op(op), state(s->arena), s(s) {}
 
 /*
   Add a new stream op to op storage.
@@ -335,10 +334,8 @@ static void add_to_storage(struct stream_obj* s,
   /* add new op at the beginning of the linked list. The memory is freed
   in remove_from_storage */
   op_and_state* new_op = grpc_core::New<op_and_state>(s, *op);
-  // Pontential fix to crash on GPR_ASSERT(!curr->done)
-  // TODO (mxyan): check if this is indeed necessary.
-  new_op->done = false;
   gpr_mu_lock(&s->mu);
+  new_op->next = storage->head;
   storage->head = new_op;
   storage->num_pending_ops++;
   if (op->send_message) {
@@ -1371,7 +1368,8 @@ static enum e_op_result execute_stream_op(struct op_and_state* oas) {
 */
 
 inline stream_obj::stream_obj(grpc_transport* gt, grpc_stream* gs,
-                              grpc_stream_refcount* refcount, gpr_arena* arena)
+                              grpc_stream_refcount* refcount,
+                              grpc_core::Arena* arena)
     : arena(arena),
       curr_ct(reinterpret_cast<grpc_cronet_transport*>(gt)),
       curr_gs(gs),
@@ -1390,7 +1388,7 @@ inline stream_obj::~stream_obj() {
 
 static int init_stream(grpc_transport* gt, grpc_stream* gs,
                        grpc_stream_refcount* refcount, const void* server_data,
-                       gpr_arena* arena) {
+                       grpc_core::Arena* arena) {
   new (gs) stream_obj(gt, gs, refcount, arena);
   return 0;
 }
index d46c24a..8da8985 100644 (file)
 #include "src/core/lib/transport/error_utils.h"
 #include "src/core/lib/transport/transport_impl.h"
 
-#define INPROC_LOG(...)                                    \
-  do {                                                     \
-    if (grpc_inproc_trace.enabled()) gpr_log(__VA_ARGS__); \
+#define INPROC_LOG(...)                               \
+  do {                                                \
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_inproc_trace)) { \
+      gpr_log(__VA_ARGS__);                           \
+    }                                                 \
   } while (0)
 
 namespace {
@@ -120,7 +122,7 @@ struct inproc_transport {
 
 struct inproc_stream {
   inproc_stream(inproc_transport* t, grpc_stream_refcount* refcount,
-                const void* server_data, gpr_arena* arena)
+                const void* server_data, grpc_core::Arena* arena)
       : t(t), refs(refcount), arena(arena) {
     // Ref this stream right now for ctor and list.
     ref("inproc_init_stream:init");
@@ -250,7 +252,7 @@ struct inproc_stream {
   grpc_stream_refcount* refs;
   grpc_closure* closure_at_destroy = nullptr;
 
-  gpr_arena* arena;
+  grpc_core::Arena* arena;
 
   grpc_transport_stream_op_batch* send_message_op = nullptr;
   grpc_transport_stream_op_batch* send_trailing_md_op = nullptr;
@@ -296,7 +298,7 @@ grpc_error* fill_in_metadata(inproc_stream* s,
                              const grpc_metadata_batch* metadata,
                              uint32_t flags, grpc_metadata_batch* out_md,
                              uint32_t* outflags, bool* markfilled) {
-  if (grpc_inproc_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_inproc_trace)) {
     log_metadata(metadata, s->t->is_client, outflags != nullptr);
   }
 
@@ -309,8 +311,8 @@ grpc_error* fill_in_metadata(inproc_stream* s,
   grpc_error* error = GRPC_ERROR_NONE;
   for (grpc_linked_mdelem* elem = metadata->list.head;
        (elem != nullptr) && (error == GRPC_ERROR_NONE); elem = elem->next) {
-    grpc_linked_mdelem* nelem = static_cast<grpc_linked_mdelem*>(
-        gpr_arena_alloc(s->arena, sizeof(*nelem)));
+    grpc_linked_mdelem* nelem =
+        static_cast<grpc_linked_mdelem*>(s->arena->Alloc(sizeof(*nelem)));
     nelem->md =
         grpc_mdelem_from_slices(grpc_slice_intern(GRPC_MDKEY(elem->md)),
                                 grpc_slice_intern(GRPC_MDVALUE(elem->md)));
@@ -322,7 +324,7 @@ grpc_error* fill_in_metadata(inproc_stream* s,
 
 int init_stream(grpc_transport* gt, grpc_stream* gs,
                 grpc_stream_refcount* refcount, const void* server_data,
-                gpr_arena* arena) {
+                grpc_core::Arena* arena) {
   INPROC_LOG(GPR_INFO, "init_stream %p %p %p", gt, gs, server_data);
   inproc_transport* t = reinterpret_cast<inproc_transport*>(gt);
   new (gs) inproc_stream(t, refcount, server_data, arena);
@@ -436,13 +438,13 @@ void fail_helper_locked(inproc_stream* s, grpc_error* error) {
       // since it expects that as well as no error yet
       grpc_metadata_batch fake_md;
       grpc_metadata_batch_init(&fake_md);
-      grpc_linked_mdelem* path_md = static_cast<grpc_linked_mdelem*>(
-          gpr_arena_alloc(s->arena, sizeof(*path_md)));
+      grpc_linked_mdelem* path_md =
+          static_cast<grpc_linked_mdelem*>(s->arena->Alloc(sizeof(*path_md)));
       path_md->md = grpc_mdelem_from_slices(g_fake_path_key, g_fake_path_value);
       GPR_ASSERT(grpc_metadata_batch_link_tail(&fake_md, path_md) ==
                  GRPC_ERROR_NONE);
-      grpc_linked_mdelem* auth_md = static_cast<grpc_linked_mdelem*>(
-          gpr_arena_alloc(s->arena, sizeof(*auth_md)));
+      grpc_linked_mdelem* auth_md =
+          static_cast<grpc_linked_mdelem*>(s->arena->Alloc(sizeof(*auth_md)));
       auth_md->md = grpc_mdelem_from_slices(g_fake_auth_key, g_fake_auth_value);
       GPR_ASSERT(grpc_metadata_batch_link_tail(&fake_md, auth_md) ==
                  GRPC_ERROR_NONE);
@@ -907,7 +909,7 @@ void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
   gpr_mu* mu = &s->t->mu->mu;  // save aside in case s gets closed
   gpr_mu_lock(mu);
 
-  if (grpc_inproc_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_inproc_trace)) {
     if (op->send_initial_metadata) {
       log_metadata(op->payload->send_initial_metadata.send_initial_metadata,
                    s->t->is_client, true);
@@ -1088,10 +1090,8 @@ void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
 
 void close_transport_locked(inproc_transport* t) {
   INPROC_LOG(GPR_INFO, "close_transport %p %d", t, t->is_closed);
-  grpc_connectivity_state_set(
-      &t->connectivity, GRPC_CHANNEL_SHUTDOWN,
-      GRPC_ERROR_CREATE_FROM_STATIC_STRING("Closing transport."),
-      "close transport");
+  grpc_connectivity_state_set(&t->connectivity, GRPC_CHANNEL_SHUTDOWN,
+                              "close transport");
   if (!t->is_closed) {
     t->is_closed = true;
     /* Also end all streams on this transport */
diff --git a/src/core/ext/upb-generated/envoy/api/v2/endpoint/load_report.upb.c b/src/core/ext/upb-generated/envoy/api/v2/endpoint/load_report.upb.c
new file mode 100644 (file)
index 0000000..2af77c5
--- /dev/null
@@ -0,0 +1,105 @@
+/* This file was generated by upbc (the upb compiler) from the input
+ * file:
+ *
+ *     envoy/api/v2/endpoint/load_report.proto
+ *
+ * Do not edit -- your changes will be discarded when the file is
+ * regenerated. */
+
+#include <stddef.h>
+#include "upb/msg.h"
+#include "envoy/api/v2/endpoint/load_report.upb.h"
+#include "envoy/api/v2/core/address.upb.h"
+#include "envoy/api/v2/core/base.upb.h"
+#include "google/protobuf/duration.upb.h"
+#include "validate/validate.upb.h"
+#include "gogoproto/gogo.upb.h"
+
+#include "upb/port_def.inc"
+
+static const upb_msglayout *const envoy_api_v2_endpoint_UpstreamLocalityStats_submsgs[3] = {
+  &envoy_api_v2_core_Locality_msginit,
+  &envoy_api_v2_endpoint_EndpointLoadMetricStats_msginit,
+  &envoy_api_v2_endpoint_UpstreamEndpointStats_msginit,
+};
+
+static const upb_msglayout_field envoy_api_v2_endpoint_UpstreamLocalityStats__fields[7] = {
+  {1, UPB_SIZE(28, 32), 0, 0, 11, 1},
+  {2, UPB_SIZE(0, 0), 0, 0, 4, 1},
+  {3, UPB_SIZE(8, 8), 0, 0, 4, 1},
+  {4, UPB_SIZE(16, 16), 0, 0, 4, 1},
+  {5, UPB_SIZE(32, 40), 0, 1, 11, 3},
+  {6, UPB_SIZE(24, 24), 0, 0, 13, 1},
+  {7, UPB_SIZE(36, 48), 0, 2, 11, 3},
+};
+
+const upb_msglayout envoy_api_v2_endpoint_UpstreamLocalityStats_msginit = {
+  &envoy_api_v2_endpoint_UpstreamLocalityStats_submsgs[0],
+  &envoy_api_v2_endpoint_UpstreamLocalityStats__fields[0],
+  UPB_SIZE(40, 56), 7, false,
+};
+
+static const upb_msglayout *const envoy_api_v2_endpoint_UpstreamEndpointStats_submsgs[2] = {
+  &envoy_api_v2_core_Address_msginit,
+  &envoy_api_v2_endpoint_EndpointLoadMetricStats_msginit,
+};
+
+static const upb_msglayout_field envoy_api_v2_endpoint_UpstreamEndpointStats__fields[5] = {
+  {1, UPB_SIZE(24, 24), 0, 0, 11, 1},
+  {2, UPB_SIZE(0, 0), 0, 0, 4, 1},
+  {3, UPB_SIZE(8, 8), 0, 0, 4, 1},
+  {4, UPB_SIZE(16, 16), 0, 0, 4, 1},
+  {5, UPB_SIZE(28, 32), 0, 1, 11, 3},
+};
+
+const upb_msglayout envoy_api_v2_endpoint_UpstreamEndpointStats_msginit = {
+  &envoy_api_v2_endpoint_UpstreamEndpointStats_submsgs[0],
+  &envoy_api_v2_endpoint_UpstreamEndpointStats__fields[0],
+  UPB_SIZE(32, 40), 5, false,
+};
+
+static const upb_msglayout_field envoy_api_v2_endpoint_EndpointLoadMetricStats__fields[3] = {
+  {1, UPB_SIZE(16, 16), 0, 0, 9, 1},
+  {2, UPB_SIZE(0, 0), 0, 0, 4, 1},
+  {3, UPB_SIZE(8, 8), 0, 0, 1, 1},
+};
+
+const upb_msglayout envoy_api_v2_endpoint_EndpointLoadMetricStats_msginit = {
+  NULL,
+  &envoy_api_v2_endpoint_EndpointLoadMetricStats__fields[0],
+  UPB_SIZE(24, 32), 3, false,
+};
+
+static const upb_msglayout *const envoy_api_v2_endpoint_ClusterStats_submsgs[3] = {
+  &envoy_api_v2_endpoint_ClusterStats_DroppedRequests_msginit,
+  &envoy_api_v2_endpoint_UpstreamLocalityStats_msginit,
+  &google_protobuf_Duration_msginit,
+};
+
+static const upb_msglayout_field envoy_api_v2_endpoint_ClusterStats__fields[5] = {
+  {1, UPB_SIZE(8, 8), 0, 0, 9, 1},
+  {2, UPB_SIZE(20, 32), 0, 1, 11, 3},
+  {3, UPB_SIZE(0, 0), 0, 0, 4, 1},
+  {4, UPB_SIZE(16, 24), 0, 2, 11, 1},
+  {5, UPB_SIZE(24, 40), 0, 0, 11, 3},
+};
+
+const upb_msglayout envoy_api_v2_endpoint_ClusterStats_msginit = {
+  &envoy_api_v2_endpoint_ClusterStats_submsgs[0],
+  &envoy_api_v2_endpoint_ClusterStats__fields[0],
+  UPB_SIZE(32, 48), 5, false,
+};
+
+static const upb_msglayout_field envoy_api_v2_endpoint_ClusterStats_DroppedRequests__fields[2] = {
+  {1, UPB_SIZE(8, 8), 0, 0, 9, 1},
+  {2, UPB_SIZE(0, 0), 0, 0, 4, 1},
+};
+
+const upb_msglayout envoy_api_v2_endpoint_ClusterStats_DroppedRequests_msginit = {
+  NULL,
+  &envoy_api_v2_endpoint_ClusterStats_DroppedRequests__fields[0],
+  UPB_SIZE(16, 32), 2, false,
+};
+
+#include "upb/port_undef.inc"
+
diff --git a/src/core/ext/upb-generated/envoy/api/v2/endpoint/load_report.upb.h b/src/core/ext/upb-generated/envoy/api/v2/endpoint/load_report.upb.h
new file mode 100644 (file)
index 0000000..7ee2129
--- /dev/null
@@ -0,0 +1,299 @@
+/* This file was generated by upbc (the upb compiler) from the input
+ * file:
+ *
+ *     envoy/api/v2/endpoint/load_report.proto
+ *
+ * Do not edit -- your changes will be discarded when the file is
+ * regenerated. */
+
+#ifndef ENVOY_API_V2_ENDPOINT_LOAD_REPORT_PROTO_UPB_H_
+#define ENVOY_API_V2_ENDPOINT_LOAD_REPORT_PROTO_UPB_H_
+
+#include "upb/generated_util.h"
+
+#include "upb/msg.h"
+
+#include "upb/decode.h"
+#include "upb/encode.h"
+#include "upb/port_def.inc"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct envoy_api_v2_endpoint_UpstreamLocalityStats;
+struct envoy_api_v2_endpoint_UpstreamEndpointStats;
+struct envoy_api_v2_endpoint_EndpointLoadMetricStats;
+struct envoy_api_v2_endpoint_ClusterStats;
+struct envoy_api_v2_endpoint_ClusterStats_DroppedRequests;
+typedef struct envoy_api_v2_endpoint_UpstreamLocalityStats envoy_api_v2_endpoint_UpstreamLocalityStats;
+typedef struct envoy_api_v2_endpoint_UpstreamEndpointStats envoy_api_v2_endpoint_UpstreamEndpointStats;
+typedef struct envoy_api_v2_endpoint_EndpointLoadMetricStats envoy_api_v2_endpoint_EndpointLoadMetricStats;
+typedef struct envoy_api_v2_endpoint_ClusterStats envoy_api_v2_endpoint_ClusterStats;
+typedef struct envoy_api_v2_endpoint_ClusterStats_DroppedRequests envoy_api_v2_endpoint_ClusterStats_DroppedRequests;
+extern const upb_msglayout envoy_api_v2_endpoint_UpstreamLocalityStats_msginit;
+extern const upb_msglayout envoy_api_v2_endpoint_UpstreamEndpointStats_msginit;
+extern const upb_msglayout envoy_api_v2_endpoint_EndpointLoadMetricStats_msginit;
+extern const upb_msglayout envoy_api_v2_endpoint_ClusterStats_msginit;
+extern const upb_msglayout envoy_api_v2_endpoint_ClusterStats_DroppedRequests_msginit;
+struct envoy_api_v2_core_Address;
+struct envoy_api_v2_core_Locality;
+struct google_protobuf_Duration;
+extern const upb_msglayout envoy_api_v2_core_Address_msginit;
+extern const upb_msglayout envoy_api_v2_core_Locality_msginit;
+extern const upb_msglayout google_protobuf_Duration_msginit;
+
+/* Enums */
+
+
+/* envoy.api.v2.endpoint.UpstreamLocalityStats */
+
+UPB_INLINE envoy_api_v2_endpoint_UpstreamLocalityStats *envoy_api_v2_endpoint_UpstreamLocalityStats_new(upb_arena *arena) {
+  return (envoy_api_v2_endpoint_UpstreamLocalityStats *)upb_msg_new(&envoy_api_v2_endpoint_UpstreamLocalityStats_msginit, arena);
+}
+UPB_INLINE envoy_api_v2_endpoint_UpstreamLocalityStats *envoy_api_v2_endpoint_UpstreamLocalityStats_parsenew(upb_strview buf, upb_arena *arena) {
+  envoy_api_v2_endpoint_UpstreamLocalityStats *ret = envoy_api_v2_endpoint_UpstreamLocalityStats_new(arena);
+  return (ret && upb_decode(buf, ret, &envoy_api_v2_endpoint_UpstreamLocalityStats_msginit)) ? ret : NULL;
+}
+UPB_INLINE char *envoy_api_v2_endpoint_UpstreamLocalityStats_serialize(const envoy_api_v2_endpoint_UpstreamLocalityStats *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &envoy_api_v2_endpoint_UpstreamLocalityStats_msginit, arena, len);
+}
+
+UPB_INLINE const struct envoy_api_v2_core_Locality* envoy_api_v2_endpoint_UpstreamLocalityStats_locality(const envoy_api_v2_endpoint_UpstreamLocalityStats *msg) { return UPB_FIELD_AT(msg, const struct envoy_api_v2_core_Locality*, UPB_SIZE(28, 32)); }
+UPB_INLINE uint64_t envoy_api_v2_endpoint_UpstreamLocalityStats_total_successful_requests(const envoy_api_v2_endpoint_UpstreamLocalityStats *msg) { return UPB_FIELD_AT(msg, uint64_t, UPB_SIZE(0, 0)); }
+UPB_INLINE uint64_t envoy_api_v2_endpoint_UpstreamLocalityStats_total_requests_in_progress(const envoy_api_v2_endpoint_UpstreamLocalityStats *msg) { return UPB_FIELD_AT(msg, uint64_t, UPB_SIZE(8, 8)); }
+UPB_INLINE uint64_t envoy_api_v2_endpoint_UpstreamLocalityStats_total_error_requests(const envoy_api_v2_endpoint_UpstreamLocalityStats *msg) { return UPB_FIELD_AT(msg, uint64_t, UPB_SIZE(16, 16)); }
+UPB_INLINE const envoy_api_v2_endpoint_EndpointLoadMetricStats* const* envoy_api_v2_endpoint_UpstreamLocalityStats_load_metric_stats(const envoy_api_v2_endpoint_UpstreamLocalityStats *msg, size_t *len) { return (const envoy_api_v2_endpoint_EndpointLoadMetricStats* const*)_upb_array_accessor(msg, UPB_SIZE(32, 40), len); }
+UPB_INLINE uint32_t envoy_api_v2_endpoint_UpstreamLocalityStats_priority(const envoy_api_v2_endpoint_UpstreamLocalityStats *msg) { return UPB_FIELD_AT(msg, uint32_t, UPB_SIZE(24, 24)); }
+UPB_INLINE const envoy_api_v2_endpoint_UpstreamEndpointStats* const* envoy_api_v2_endpoint_UpstreamLocalityStats_upstream_endpoint_stats(const envoy_api_v2_endpoint_UpstreamLocalityStats *msg, size_t *len) { return (const envoy_api_v2_endpoint_UpstreamEndpointStats* const*)_upb_array_accessor(msg, UPB_SIZE(36, 48), len); }
+
+UPB_INLINE void envoy_api_v2_endpoint_UpstreamLocalityStats_set_locality(envoy_api_v2_endpoint_UpstreamLocalityStats *msg, struct envoy_api_v2_core_Locality* value) {
+  UPB_FIELD_AT(msg, struct envoy_api_v2_core_Locality*, UPB_SIZE(28, 32)) = value;
+}
+UPB_INLINE struct envoy_api_v2_core_Locality* envoy_api_v2_endpoint_UpstreamLocalityStats_mutable_locality(envoy_api_v2_endpoint_UpstreamLocalityStats *msg, upb_arena *arena) {
+  struct envoy_api_v2_core_Locality* sub = (struct envoy_api_v2_core_Locality*)envoy_api_v2_endpoint_UpstreamLocalityStats_locality(msg);
+  if (sub == NULL) {
+    sub = (struct envoy_api_v2_core_Locality*)upb_msg_new(&envoy_api_v2_core_Locality_msginit, arena);
+    if (!sub) return NULL;
+    envoy_api_v2_endpoint_UpstreamLocalityStats_set_locality(msg, sub);
+  }
+  return sub;
+}
+UPB_INLINE void envoy_api_v2_endpoint_UpstreamLocalityStats_set_total_successful_requests(envoy_api_v2_endpoint_UpstreamLocalityStats *msg, uint64_t value) {
+  UPB_FIELD_AT(msg, uint64_t, UPB_SIZE(0, 0)) = value;
+}
+UPB_INLINE void envoy_api_v2_endpoint_UpstreamLocalityStats_set_total_requests_in_progress(envoy_api_v2_endpoint_UpstreamLocalityStats *msg, uint64_t value) {
+  UPB_FIELD_AT(msg, uint64_t, UPB_SIZE(8, 8)) = value;
+}
+UPB_INLINE void envoy_api_v2_endpoint_UpstreamLocalityStats_set_total_error_requests(envoy_api_v2_endpoint_UpstreamLocalityStats *msg, uint64_t value) {
+  UPB_FIELD_AT(msg, uint64_t, UPB_SIZE(16, 16)) = value;
+}
+UPB_INLINE envoy_api_v2_endpoint_EndpointLoadMetricStats** envoy_api_v2_endpoint_UpstreamLocalityStats_mutable_load_metric_stats(envoy_api_v2_endpoint_UpstreamLocalityStats *msg, size_t *len) {
+  return (envoy_api_v2_endpoint_EndpointLoadMetricStats**)_upb_array_mutable_accessor(msg, UPB_SIZE(32, 40), len);
+}
+UPB_INLINE envoy_api_v2_endpoint_EndpointLoadMetricStats** envoy_api_v2_endpoint_UpstreamLocalityStats_resize_load_metric_stats(envoy_api_v2_endpoint_UpstreamLocalityStats *msg, size_t len, upb_arena *arena) {
+  return (envoy_api_v2_endpoint_EndpointLoadMetricStats**)_upb_array_resize_accessor(msg, UPB_SIZE(32, 40), len, UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct envoy_api_v2_endpoint_EndpointLoadMetricStats* envoy_api_v2_endpoint_UpstreamLocalityStats_add_load_metric_stats(envoy_api_v2_endpoint_UpstreamLocalityStats *msg, upb_arena *arena) {
+  struct envoy_api_v2_endpoint_EndpointLoadMetricStats* sub = (struct envoy_api_v2_endpoint_EndpointLoadMetricStats*)upb_msg_new(&envoy_api_v2_endpoint_EndpointLoadMetricStats_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(32, 40), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+UPB_INLINE void envoy_api_v2_endpoint_UpstreamLocalityStats_set_priority(envoy_api_v2_endpoint_UpstreamLocalityStats *msg, uint32_t value) {
+  UPB_FIELD_AT(msg, uint32_t, UPB_SIZE(24, 24)) = value;
+}
+UPB_INLINE envoy_api_v2_endpoint_UpstreamEndpointStats** envoy_api_v2_endpoint_UpstreamLocalityStats_mutable_upstream_endpoint_stats(envoy_api_v2_endpoint_UpstreamLocalityStats *msg, size_t *len) {
+  return (envoy_api_v2_endpoint_UpstreamEndpointStats**)_upb_array_mutable_accessor(msg, UPB_SIZE(36, 48), len);
+}
+UPB_INLINE envoy_api_v2_endpoint_UpstreamEndpointStats** envoy_api_v2_endpoint_UpstreamLocalityStats_resize_upstream_endpoint_stats(envoy_api_v2_endpoint_UpstreamLocalityStats *msg, size_t len, upb_arena *arena) {
+  return (envoy_api_v2_endpoint_UpstreamEndpointStats**)_upb_array_resize_accessor(msg, UPB_SIZE(36, 48), len, UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct envoy_api_v2_endpoint_UpstreamEndpointStats* envoy_api_v2_endpoint_UpstreamLocalityStats_add_upstream_endpoint_stats(envoy_api_v2_endpoint_UpstreamLocalityStats *msg, upb_arena *arena) {
+  struct envoy_api_v2_endpoint_UpstreamEndpointStats* sub = (struct envoy_api_v2_endpoint_UpstreamEndpointStats*)upb_msg_new(&envoy_api_v2_endpoint_UpstreamEndpointStats_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(36, 48), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+
+
+/* envoy.api.v2.endpoint.UpstreamEndpointStats */
+
+UPB_INLINE envoy_api_v2_endpoint_UpstreamEndpointStats *envoy_api_v2_endpoint_UpstreamEndpointStats_new(upb_arena *arena) {
+  return (envoy_api_v2_endpoint_UpstreamEndpointStats *)upb_msg_new(&envoy_api_v2_endpoint_UpstreamEndpointStats_msginit, arena);
+}
+UPB_INLINE envoy_api_v2_endpoint_UpstreamEndpointStats *envoy_api_v2_endpoint_UpstreamEndpointStats_parsenew(upb_strview buf, upb_arena *arena) {
+  envoy_api_v2_endpoint_UpstreamEndpointStats *ret = envoy_api_v2_endpoint_UpstreamEndpointStats_new(arena);
+  return (ret && upb_decode(buf, ret, &envoy_api_v2_endpoint_UpstreamEndpointStats_msginit)) ? ret : NULL;
+}
+UPB_INLINE char *envoy_api_v2_endpoint_UpstreamEndpointStats_serialize(const envoy_api_v2_endpoint_UpstreamEndpointStats *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &envoy_api_v2_endpoint_UpstreamEndpointStats_msginit, arena, len);
+}
+
+UPB_INLINE const struct envoy_api_v2_core_Address* envoy_api_v2_endpoint_UpstreamEndpointStats_address(const envoy_api_v2_endpoint_UpstreamEndpointStats *msg) { return UPB_FIELD_AT(msg, const struct envoy_api_v2_core_Address*, UPB_SIZE(24, 24)); }
+UPB_INLINE uint64_t envoy_api_v2_endpoint_UpstreamEndpointStats_total_successful_requests(const envoy_api_v2_endpoint_UpstreamEndpointStats *msg) { return UPB_FIELD_AT(msg, uint64_t, UPB_SIZE(0, 0)); }
+UPB_INLINE uint64_t envoy_api_v2_endpoint_UpstreamEndpointStats_total_requests_in_progress(const envoy_api_v2_endpoint_UpstreamEndpointStats *msg) { return UPB_FIELD_AT(msg, uint64_t, UPB_SIZE(8, 8)); }
+UPB_INLINE uint64_t envoy_api_v2_endpoint_UpstreamEndpointStats_total_error_requests(const envoy_api_v2_endpoint_UpstreamEndpointStats *msg) { return UPB_FIELD_AT(msg, uint64_t, UPB_SIZE(16, 16)); }
+UPB_INLINE const envoy_api_v2_endpoint_EndpointLoadMetricStats* const* envoy_api_v2_endpoint_UpstreamEndpointStats_load_metric_stats(const envoy_api_v2_endpoint_UpstreamEndpointStats *msg, size_t *len) { return (const envoy_api_v2_endpoint_EndpointLoadMetricStats* const*)_upb_array_accessor(msg, UPB_SIZE(28, 32), len); }
+
+UPB_INLINE void envoy_api_v2_endpoint_UpstreamEndpointStats_set_address(envoy_api_v2_endpoint_UpstreamEndpointStats *msg, struct envoy_api_v2_core_Address* value) {
+  UPB_FIELD_AT(msg, struct envoy_api_v2_core_Address*, UPB_SIZE(24, 24)) = value;
+}
+UPB_INLINE struct envoy_api_v2_core_Address* envoy_api_v2_endpoint_UpstreamEndpointStats_mutable_address(envoy_api_v2_endpoint_UpstreamEndpointStats *msg, upb_arena *arena) {
+  struct envoy_api_v2_core_Address* sub = (struct envoy_api_v2_core_Address*)envoy_api_v2_endpoint_UpstreamEndpointStats_address(msg);
+  if (sub == NULL) {
+    sub = (struct envoy_api_v2_core_Address*)upb_msg_new(&envoy_api_v2_core_Address_msginit, arena);
+    if (!sub) return NULL;
+    envoy_api_v2_endpoint_UpstreamEndpointStats_set_address(msg, sub);
+  }
+  return sub;
+}
+UPB_INLINE void envoy_api_v2_endpoint_UpstreamEndpointStats_set_total_successful_requests(envoy_api_v2_endpoint_UpstreamEndpointStats *msg, uint64_t value) {
+  UPB_FIELD_AT(msg, uint64_t, UPB_SIZE(0, 0)) = value;
+}
+UPB_INLINE void envoy_api_v2_endpoint_UpstreamEndpointStats_set_total_requests_in_progress(envoy_api_v2_endpoint_UpstreamEndpointStats *msg, uint64_t value) {
+  UPB_FIELD_AT(msg, uint64_t, UPB_SIZE(8, 8)) = value;
+}
+UPB_INLINE void envoy_api_v2_endpoint_UpstreamEndpointStats_set_total_error_requests(envoy_api_v2_endpoint_UpstreamEndpointStats *msg, uint64_t value) {
+  UPB_FIELD_AT(msg, uint64_t, UPB_SIZE(16, 16)) = value;
+}
+UPB_INLINE envoy_api_v2_endpoint_EndpointLoadMetricStats** envoy_api_v2_endpoint_UpstreamEndpointStats_mutable_load_metric_stats(envoy_api_v2_endpoint_UpstreamEndpointStats *msg, size_t *len) {
+  return (envoy_api_v2_endpoint_EndpointLoadMetricStats**)_upb_array_mutable_accessor(msg, UPB_SIZE(28, 32), len);
+}
+UPB_INLINE envoy_api_v2_endpoint_EndpointLoadMetricStats** envoy_api_v2_endpoint_UpstreamEndpointStats_resize_load_metric_stats(envoy_api_v2_endpoint_UpstreamEndpointStats *msg, size_t len, upb_arena *arena) {
+  return (envoy_api_v2_endpoint_EndpointLoadMetricStats**)_upb_array_resize_accessor(msg, UPB_SIZE(28, 32), len, UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct envoy_api_v2_endpoint_EndpointLoadMetricStats* envoy_api_v2_endpoint_UpstreamEndpointStats_add_load_metric_stats(envoy_api_v2_endpoint_UpstreamEndpointStats *msg, upb_arena *arena) {
+  struct envoy_api_v2_endpoint_EndpointLoadMetricStats* sub = (struct envoy_api_v2_endpoint_EndpointLoadMetricStats*)upb_msg_new(&envoy_api_v2_endpoint_EndpointLoadMetricStats_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(28, 32), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+
+
+/* envoy.api.v2.endpoint.EndpointLoadMetricStats */
+
+UPB_INLINE envoy_api_v2_endpoint_EndpointLoadMetricStats *envoy_api_v2_endpoint_EndpointLoadMetricStats_new(upb_arena *arena) {
+  return (envoy_api_v2_endpoint_EndpointLoadMetricStats *)upb_msg_new(&envoy_api_v2_endpoint_EndpointLoadMetricStats_msginit, arena);
+}
+UPB_INLINE envoy_api_v2_endpoint_EndpointLoadMetricStats *envoy_api_v2_endpoint_EndpointLoadMetricStats_parsenew(upb_strview buf, upb_arena *arena) {
+  envoy_api_v2_endpoint_EndpointLoadMetricStats *ret = envoy_api_v2_endpoint_EndpointLoadMetricStats_new(arena);
+  return (ret && upb_decode(buf, ret, &envoy_api_v2_endpoint_EndpointLoadMetricStats_msginit)) ? ret : NULL;
+}
+UPB_INLINE char *envoy_api_v2_endpoint_EndpointLoadMetricStats_serialize(const envoy_api_v2_endpoint_EndpointLoadMetricStats *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &envoy_api_v2_endpoint_EndpointLoadMetricStats_msginit, arena, len);
+}
+
+UPB_INLINE upb_strview envoy_api_v2_endpoint_EndpointLoadMetricStats_metric_name(const envoy_api_v2_endpoint_EndpointLoadMetricStats *msg) { return UPB_FIELD_AT(msg, upb_strview, UPB_SIZE(16, 16)); }
+UPB_INLINE uint64_t envoy_api_v2_endpoint_EndpointLoadMetricStats_num_requests_finished_with_metric(const envoy_api_v2_endpoint_EndpointLoadMetricStats *msg) { return UPB_FIELD_AT(msg, uint64_t, UPB_SIZE(0, 0)); }
+UPB_INLINE double envoy_api_v2_endpoint_EndpointLoadMetricStats_total_metric_value(const envoy_api_v2_endpoint_EndpointLoadMetricStats *msg) { return UPB_FIELD_AT(msg, double, UPB_SIZE(8, 8)); }
+
+UPB_INLINE void envoy_api_v2_endpoint_EndpointLoadMetricStats_set_metric_name(envoy_api_v2_endpoint_EndpointLoadMetricStats *msg, upb_strview value) {
+  UPB_FIELD_AT(msg, upb_strview, UPB_SIZE(16, 16)) = value;
+}
+UPB_INLINE void envoy_api_v2_endpoint_EndpointLoadMetricStats_set_num_requests_finished_with_metric(envoy_api_v2_endpoint_EndpointLoadMetricStats *msg, uint64_t value) {
+  UPB_FIELD_AT(msg, uint64_t, UPB_SIZE(0, 0)) = value;
+}
+UPB_INLINE void envoy_api_v2_endpoint_EndpointLoadMetricStats_set_total_metric_value(envoy_api_v2_endpoint_EndpointLoadMetricStats *msg, double value) {
+  UPB_FIELD_AT(msg, double, UPB_SIZE(8, 8)) = value;
+}
+
+
+/* envoy.api.v2.endpoint.ClusterStats */
+
+UPB_INLINE envoy_api_v2_endpoint_ClusterStats *envoy_api_v2_endpoint_ClusterStats_new(upb_arena *arena) {
+  return (envoy_api_v2_endpoint_ClusterStats *)upb_msg_new(&envoy_api_v2_endpoint_ClusterStats_msginit, arena);
+}
+UPB_INLINE envoy_api_v2_endpoint_ClusterStats *envoy_api_v2_endpoint_ClusterStats_parsenew(upb_strview buf, upb_arena *arena) {
+  envoy_api_v2_endpoint_ClusterStats *ret = envoy_api_v2_endpoint_ClusterStats_new(arena);
+  return (ret && upb_decode(buf, ret, &envoy_api_v2_endpoint_ClusterStats_msginit)) ? ret : NULL;
+}
+UPB_INLINE char *envoy_api_v2_endpoint_ClusterStats_serialize(const envoy_api_v2_endpoint_ClusterStats *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &envoy_api_v2_endpoint_ClusterStats_msginit, arena, len);
+}
+
+UPB_INLINE upb_strview envoy_api_v2_endpoint_ClusterStats_cluster_name(const envoy_api_v2_endpoint_ClusterStats *msg) { return UPB_FIELD_AT(msg, upb_strview, UPB_SIZE(8, 8)); }
+UPB_INLINE const envoy_api_v2_endpoint_UpstreamLocalityStats* const* envoy_api_v2_endpoint_ClusterStats_upstream_locality_stats(const envoy_api_v2_endpoint_ClusterStats *msg, size_t *len) { return (const envoy_api_v2_endpoint_UpstreamLocalityStats* const*)_upb_array_accessor(msg, UPB_SIZE(20, 32), len); }
+UPB_INLINE uint64_t envoy_api_v2_endpoint_ClusterStats_total_dropped_requests(const envoy_api_v2_endpoint_ClusterStats *msg) { return UPB_FIELD_AT(msg, uint64_t, UPB_SIZE(0, 0)); }
+UPB_INLINE const struct google_protobuf_Duration* envoy_api_v2_endpoint_ClusterStats_load_report_interval(const envoy_api_v2_endpoint_ClusterStats *msg) { return UPB_FIELD_AT(msg, const struct google_protobuf_Duration*, UPB_SIZE(16, 24)); }
+UPB_INLINE const envoy_api_v2_endpoint_ClusterStats_DroppedRequests* const* envoy_api_v2_endpoint_ClusterStats_dropped_requests(const envoy_api_v2_endpoint_ClusterStats *msg, size_t *len) { return (const envoy_api_v2_endpoint_ClusterStats_DroppedRequests* const*)_upb_array_accessor(msg, UPB_SIZE(24, 40), len); }
+
+UPB_INLINE void envoy_api_v2_endpoint_ClusterStats_set_cluster_name(envoy_api_v2_endpoint_ClusterStats *msg, upb_strview value) {
+  UPB_FIELD_AT(msg, upb_strview, UPB_SIZE(8, 8)) = value;
+}
+UPB_INLINE envoy_api_v2_endpoint_UpstreamLocalityStats** envoy_api_v2_endpoint_ClusterStats_mutable_upstream_locality_stats(envoy_api_v2_endpoint_ClusterStats *msg, size_t *len) {
+  return (envoy_api_v2_endpoint_UpstreamLocalityStats**)_upb_array_mutable_accessor(msg, UPB_SIZE(20, 32), len);
+}
+UPB_INLINE envoy_api_v2_endpoint_UpstreamLocalityStats** envoy_api_v2_endpoint_ClusterStats_resize_upstream_locality_stats(envoy_api_v2_endpoint_ClusterStats *msg, size_t len, upb_arena *arena) {
+  return (envoy_api_v2_endpoint_UpstreamLocalityStats**)_upb_array_resize_accessor(msg, UPB_SIZE(20, 32), len, UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct envoy_api_v2_endpoint_UpstreamLocalityStats* envoy_api_v2_endpoint_ClusterStats_add_upstream_locality_stats(envoy_api_v2_endpoint_ClusterStats *msg, upb_arena *arena) {
+  struct envoy_api_v2_endpoint_UpstreamLocalityStats* sub = (struct envoy_api_v2_endpoint_UpstreamLocalityStats*)upb_msg_new(&envoy_api_v2_endpoint_UpstreamLocalityStats_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(20, 32), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+UPB_INLINE void envoy_api_v2_endpoint_ClusterStats_set_total_dropped_requests(envoy_api_v2_endpoint_ClusterStats *msg, uint64_t value) {
+  UPB_FIELD_AT(msg, uint64_t, UPB_SIZE(0, 0)) = value;
+}
+UPB_INLINE void envoy_api_v2_endpoint_ClusterStats_set_load_report_interval(envoy_api_v2_endpoint_ClusterStats *msg, struct google_protobuf_Duration* value) {
+  UPB_FIELD_AT(msg, struct google_protobuf_Duration*, UPB_SIZE(16, 24)) = value;
+}
+UPB_INLINE struct google_protobuf_Duration* envoy_api_v2_endpoint_ClusterStats_mutable_load_report_interval(envoy_api_v2_endpoint_ClusterStats *msg, upb_arena *arena) {
+  struct google_protobuf_Duration* sub = (struct google_protobuf_Duration*)envoy_api_v2_endpoint_ClusterStats_load_report_interval(msg);
+  if (sub == NULL) {
+    sub = (struct google_protobuf_Duration*)upb_msg_new(&google_protobuf_Duration_msginit, arena);
+    if (!sub) return NULL;
+    envoy_api_v2_endpoint_ClusterStats_set_load_report_interval(msg, sub);
+  }
+  return sub;
+}
+UPB_INLINE envoy_api_v2_endpoint_ClusterStats_DroppedRequests** envoy_api_v2_endpoint_ClusterStats_mutable_dropped_requests(envoy_api_v2_endpoint_ClusterStats *msg, size_t *len) {
+  return (envoy_api_v2_endpoint_ClusterStats_DroppedRequests**)_upb_array_mutable_accessor(msg, UPB_SIZE(24, 40), len);
+}
+UPB_INLINE envoy_api_v2_endpoint_ClusterStats_DroppedRequests** envoy_api_v2_endpoint_ClusterStats_resize_dropped_requests(envoy_api_v2_endpoint_ClusterStats *msg, size_t len, upb_arena *arena) {
+  return (envoy_api_v2_endpoint_ClusterStats_DroppedRequests**)_upb_array_resize_accessor(msg, UPB_SIZE(24, 40), len, UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct envoy_api_v2_endpoint_ClusterStats_DroppedRequests* envoy_api_v2_endpoint_ClusterStats_add_dropped_requests(envoy_api_v2_endpoint_ClusterStats *msg, upb_arena *arena) {
+  struct envoy_api_v2_endpoint_ClusterStats_DroppedRequests* sub = (struct envoy_api_v2_endpoint_ClusterStats_DroppedRequests*)upb_msg_new(&envoy_api_v2_endpoint_ClusterStats_DroppedRequests_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(24, 40), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+
+
+/* envoy.api.v2.endpoint.ClusterStats.DroppedRequests */
+
+UPB_INLINE envoy_api_v2_endpoint_ClusterStats_DroppedRequests *envoy_api_v2_endpoint_ClusterStats_DroppedRequests_new(upb_arena *arena) {
+  return (envoy_api_v2_endpoint_ClusterStats_DroppedRequests *)upb_msg_new(&envoy_api_v2_endpoint_ClusterStats_DroppedRequests_msginit, arena);
+}
+UPB_INLINE envoy_api_v2_endpoint_ClusterStats_DroppedRequests *envoy_api_v2_endpoint_ClusterStats_DroppedRequests_parsenew(upb_strview buf, upb_arena *arena) {
+  envoy_api_v2_endpoint_ClusterStats_DroppedRequests *ret = envoy_api_v2_endpoint_ClusterStats_DroppedRequests_new(arena);
+  return (ret && upb_decode(buf, ret, &envoy_api_v2_endpoint_ClusterStats_DroppedRequests_msginit)) ? ret : NULL;
+}
+UPB_INLINE char *envoy_api_v2_endpoint_ClusterStats_DroppedRequests_serialize(const envoy_api_v2_endpoint_ClusterStats_DroppedRequests *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &envoy_api_v2_endpoint_ClusterStats_DroppedRequests_msginit, arena, len);
+}
+
+UPB_INLINE upb_strview envoy_api_v2_endpoint_ClusterStats_DroppedRequests_category(const envoy_api_v2_endpoint_ClusterStats_DroppedRequests *msg) { return UPB_FIELD_AT(msg, upb_strview, UPB_SIZE(8, 8)); }
+UPB_INLINE uint64_t envoy_api_v2_endpoint_ClusterStats_DroppedRequests_dropped_count(const envoy_api_v2_endpoint_ClusterStats_DroppedRequests *msg) { return UPB_FIELD_AT(msg, uint64_t, UPB_SIZE(0, 0)); }
+
+UPB_INLINE void envoy_api_v2_endpoint_ClusterStats_DroppedRequests_set_category(envoy_api_v2_endpoint_ClusterStats_DroppedRequests *msg, upb_strview value) {
+  UPB_FIELD_AT(msg, upb_strview, UPB_SIZE(8, 8)) = value;
+}
+UPB_INLINE void envoy_api_v2_endpoint_ClusterStats_DroppedRequests_set_dropped_count(envoy_api_v2_endpoint_ClusterStats_DroppedRequests *msg, uint64_t value) {
+  UPB_FIELD_AT(msg, uint64_t, UPB_SIZE(0, 0)) = value;
+}
+
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+#include "upb/port_undef.inc"
+
+#endif  /* ENVOY_API_V2_ENDPOINT_LOAD_REPORT_PROTO_UPB_H_ */
diff --git a/src/core/ext/upb-generated/envoy/service/load_stats/v2/lrs.upb.c b/src/core/ext/upb-generated/envoy/service/load_stats/v2/lrs.upb.c
new file mode 100644 (file)
index 0000000..a6e4bd2
--- /dev/null
@@ -0,0 +1,52 @@
+/* This file was generated by upbc (the upb compiler) from the input
+ * file:
+ *
+ *     envoy/service/load_stats/v2/lrs.proto
+ *
+ * Do not edit -- your changes will be discarded when the file is
+ * regenerated. */
+
+#include <stddef.h>
+#include "upb/msg.h"
+#include "envoy/service/load_stats/v2/lrs.upb.h"
+#include "envoy/api/v2/core/base.upb.h"
+#include "envoy/api/v2/endpoint/load_report.upb.h"
+#include "google/protobuf/duration.upb.h"
+#include "validate/validate.upb.h"
+
+#include "upb/port_def.inc"
+
+static const upb_msglayout *const envoy_service_load_stats_v2_LoadStatsRequest_submsgs[2] = {
+  &envoy_api_v2_core_Node_msginit,
+  &envoy_api_v2_endpoint_ClusterStats_msginit,
+};
+
+static const upb_msglayout_field envoy_service_load_stats_v2_LoadStatsRequest__fields[2] = {
+  {1, UPB_SIZE(0, 0), 0, 0, 11, 1},
+  {2, UPB_SIZE(4, 8), 0, 1, 11, 3},
+};
+
+const upb_msglayout envoy_service_load_stats_v2_LoadStatsRequest_msginit = {
+  &envoy_service_load_stats_v2_LoadStatsRequest_submsgs[0],
+  &envoy_service_load_stats_v2_LoadStatsRequest__fields[0],
+  UPB_SIZE(8, 16), 2, false,
+};
+
+static const upb_msglayout *const envoy_service_load_stats_v2_LoadStatsResponse_submsgs[1] = {
+  &google_protobuf_Duration_msginit,
+};
+
+static const upb_msglayout_field envoy_service_load_stats_v2_LoadStatsResponse__fields[3] = {
+  {1, UPB_SIZE(8, 16), 0, 0, 9, 3},
+  {2, UPB_SIZE(4, 8), 0, 0, 11, 1},
+  {3, UPB_SIZE(0, 0), 0, 0, 8, 1},
+};
+
+const upb_msglayout envoy_service_load_stats_v2_LoadStatsResponse_msginit = {
+  &envoy_service_load_stats_v2_LoadStatsResponse_submsgs[0],
+  &envoy_service_load_stats_v2_LoadStatsResponse__fields[0],
+  UPB_SIZE(12, 24), 3, false,
+};
+
+#include "upb/port_undef.inc"
+
diff --git a/src/core/ext/upb-generated/envoy/service/load_stats/v2/lrs.upb.h b/src/core/ext/upb-generated/envoy/service/load_stats/v2/lrs.upb.h
new file mode 100644 (file)
index 0000000..99db767
--- /dev/null
@@ -0,0 +1,132 @@
+/* This file was generated by upbc (the upb compiler) from the input
+ * file:
+ *
+ *     envoy/service/load_stats/v2/lrs.proto
+ *
+ * Do not edit -- your changes will be discarded when the file is
+ * regenerated. */
+
+#ifndef ENVOY_SERVICE_LOAD_STATS_V2_LRS_PROTO_UPB_H_
+#define ENVOY_SERVICE_LOAD_STATS_V2_LRS_PROTO_UPB_H_
+
+#include "upb/generated_util.h"
+
+#include "upb/msg.h"
+
+#include "upb/decode.h"
+#include "upb/encode.h"
+#include "upb/port_def.inc"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct envoy_service_load_stats_v2_LoadStatsRequest;
+struct envoy_service_load_stats_v2_LoadStatsResponse;
+typedef struct envoy_service_load_stats_v2_LoadStatsRequest envoy_service_load_stats_v2_LoadStatsRequest;
+typedef struct envoy_service_load_stats_v2_LoadStatsResponse envoy_service_load_stats_v2_LoadStatsResponse;
+extern const upb_msglayout envoy_service_load_stats_v2_LoadStatsRequest_msginit;
+extern const upb_msglayout envoy_service_load_stats_v2_LoadStatsResponse_msginit;
+struct envoy_api_v2_core_Node;
+struct envoy_api_v2_endpoint_ClusterStats;
+struct google_protobuf_Duration;
+extern const upb_msglayout envoy_api_v2_core_Node_msginit;
+extern const upb_msglayout envoy_api_v2_endpoint_ClusterStats_msginit;
+extern const upb_msglayout google_protobuf_Duration_msginit;
+
+/* Enums */
+
+
+/* envoy.service.load_stats.v2.LoadStatsRequest */
+
+UPB_INLINE envoy_service_load_stats_v2_LoadStatsRequest *envoy_service_load_stats_v2_LoadStatsRequest_new(upb_arena *arena) {
+  return (envoy_service_load_stats_v2_LoadStatsRequest *)upb_msg_new(&envoy_service_load_stats_v2_LoadStatsRequest_msginit, arena);
+}
+UPB_INLINE envoy_service_load_stats_v2_LoadStatsRequest *envoy_service_load_stats_v2_LoadStatsRequest_parsenew(upb_strview buf, upb_arena *arena) {
+  envoy_service_load_stats_v2_LoadStatsRequest *ret = envoy_service_load_stats_v2_LoadStatsRequest_new(arena);
+  return (ret && upb_decode(buf, ret, &envoy_service_load_stats_v2_LoadStatsRequest_msginit)) ? ret : NULL;
+}
+UPB_INLINE char *envoy_service_load_stats_v2_LoadStatsRequest_serialize(const envoy_service_load_stats_v2_LoadStatsRequest *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &envoy_service_load_stats_v2_LoadStatsRequest_msginit, arena, len);
+}
+
+UPB_INLINE const struct envoy_api_v2_core_Node* envoy_service_load_stats_v2_LoadStatsRequest_node(const envoy_service_load_stats_v2_LoadStatsRequest *msg) { return UPB_FIELD_AT(msg, const struct envoy_api_v2_core_Node*, UPB_SIZE(0, 0)); }
+UPB_INLINE const struct envoy_api_v2_endpoint_ClusterStats* const* envoy_service_load_stats_v2_LoadStatsRequest_cluster_stats(const envoy_service_load_stats_v2_LoadStatsRequest *msg, size_t *len) { return (const struct envoy_api_v2_endpoint_ClusterStats* const*)_upb_array_accessor(msg, UPB_SIZE(4, 8), len); }
+
+UPB_INLINE void envoy_service_load_stats_v2_LoadStatsRequest_set_node(envoy_service_load_stats_v2_LoadStatsRequest *msg, struct envoy_api_v2_core_Node* value) {
+  UPB_FIELD_AT(msg, struct envoy_api_v2_core_Node*, UPB_SIZE(0, 0)) = value;
+}
+UPB_INLINE struct envoy_api_v2_core_Node* envoy_service_load_stats_v2_LoadStatsRequest_mutable_node(envoy_service_load_stats_v2_LoadStatsRequest *msg, upb_arena *arena) {
+  struct envoy_api_v2_core_Node* sub = (struct envoy_api_v2_core_Node*)envoy_service_load_stats_v2_LoadStatsRequest_node(msg);
+  if (sub == NULL) {
+    sub = (struct envoy_api_v2_core_Node*)upb_msg_new(&envoy_api_v2_core_Node_msginit, arena);
+    if (!sub) return NULL;
+    envoy_service_load_stats_v2_LoadStatsRequest_set_node(msg, sub);
+  }
+  return sub;
+}
+UPB_INLINE struct envoy_api_v2_endpoint_ClusterStats** envoy_service_load_stats_v2_LoadStatsRequest_mutable_cluster_stats(envoy_service_load_stats_v2_LoadStatsRequest *msg, size_t *len) {
+  return (struct envoy_api_v2_endpoint_ClusterStats**)_upb_array_mutable_accessor(msg, UPB_SIZE(4, 8), len);
+}
+UPB_INLINE struct envoy_api_v2_endpoint_ClusterStats** envoy_service_load_stats_v2_LoadStatsRequest_resize_cluster_stats(envoy_service_load_stats_v2_LoadStatsRequest *msg, size_t len, upb_arena *arena) {
+  return (struct envoy_api_v2_endpoint_ClusterStats**)_upb_array_resize_accessor(msg, UPB_SIZE(4, 8), len, UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct envoy_api_v2_endpoint_ClusterStats* envoy_service_load_stats_v2_LoadStatsRequest_add_cluster_stats(envoy_service_load_stats_v2_LoadStatsRequest *msg, upb_arena *arena) {
+  struct envoy_api_v2_endpoint_ClusterStats* sub = (struct envoy_api_v2_endpoint_ClusterStats*)upb_msg_new(&envoy_api_v2_endpoint_ClusterStats_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(4, 8), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+
+
+/* envoy.service.load_stats.v2.LoadStatsResponse */
+
+UPB_INLINE envoy_service_load_stats_v2_LoadStatsResponse *envoy_service_load_stats_v2_LoadStatsResponse_new(upb_arena *arena) {
+  return (envoy_service_load_stats_v2_LoadStatsResponse *)upb_msg_new(&envoy_service_load_stats_v2_LoadStatsResponse_msginit, arena);
+}
+UPB_INLINE envoy_service_load_stats_v2_LoadStatsResponse *envoy_service_load_stats_v2_LoadStatsResponse_parsenew(upb_strview buf, upb_arena *arena) {
+  envoy_service_load_stats_v2_LoadStatsResponse *ret = envoy_service_load_stats_v2_LoadStatsResponse_new(arena);
+  return (ret && upb_decode(buf, ret, &envoy_service_load_stats_v2_LoadStatsResponse_msginit)) ? ret : NULL;
+}
+UPB_INLINE char *envoy_service_load_stats_v2_LoadStatsResponse_serialize(const envoy_service_load_stats_v2_LoadStatsResponse *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &envoy_service_load_stats_v2_LoadStatsResponse_msginit, arena, len);
+}
+
+UPB_INLINE upb_strview const* envoy_service_load_stats_v2_LoadStatsResponse_clusters(const envoy_service_load_stats_v2_LoadStatsResponse *msg, size_t *len) { return (upb_strview const*)_upb_array_accessor(msg, UPB_SIZE(8, 16), len); }
+UPB_INLINE const struct google_protobuf_Duration* envoy_service_load_stats_v2_LoadStatsResponse_load_reporting_interval(const envoy_service_load_stats_v2_LoadStatsResponse *msg) { return UPB_FIELD_AT(msg, const struct google_protobuf_Duration*, UPB_SIZE(4, 8)); }
+UPB_INLINE bool envoy_service_load_stats_v2_LoadStatsResponse_report_endpoint_granularity(const envoy_service_load_stats_v2_LoadStatsResponse *msg) { return UPB_FIELD_AT(msg, bool, UPB_SIZE(0, 0)); }
+
+UPB_INLINE upb_strview* envoy_service_load_stats_v2_LoadStatsResponse_mutable_clusters(envoy_service_load_stats_v2_LoadStatsResponse *msg, size_t *len) {
+  return (upb_strview*)_upb_array_mutable_accessor(msg, UPB_SIZE(8, 16), len);
+}
+UPB_INLINE upb_strview* envoy_service_load_stats_v2_LoadStatsResponse_resize_clusters(envoy_service_load_stats_v2_LoadStatsResponse *msg, size_t len, upb_arena *arena) {
+  return (upb_strview*)_upb_array_resize_accessor(msg, UPB_SIZE(8, 16), len, UPB_SIZE(8, 16), UPB_TYPE_STRING, arena);
+}
+UPB_INLINE bool envoy_service_load_stats_v2_LoadStatsResponse_add_clusters(envoy_service_load_stats_v2_LoadStatsResponse *msg, upb_strview val, upb_arena *arena) {
+  return _upb_array_append_accessor(
+      msg, UPB_SIZE(8, 16), UPB_SIZE(8, 16), UPB_TYPE_STRING, &val, arena);
+}
+UPB_INLINE void envoy_service_load_stats_v2_LoadStatsResponse_set_load_reporting_interval(envoy_service_load_stats_v2_LoadStatsResponse *msg, struct google_protobuf_Duration* value) {
+  UPB_FIELD_AT(msg, struct google_protobuf_Duration*, UPB_SIZE(4, 8)) = value;
+}
+UPB_INLINE struct google_protobuf_Duration* envoy_service_load_stats_v2_LoadStatsResponse_mutable_load_reporting_interval(envoy_service_load_stats_v2_LoadStatsResponse *msg, upb_arena *arena) {
+  struct google_protobuf_Duration* sub = (struct google_protobuf_Duration*)envoy_service_load_stats_v2_LoadStatsResponse_load_reporting_interval(msg);
+  if (sub == NULL) {
+    sub = (struct google_protobuf_Duration*)upb_msg_new(&google_protobuf_Duration_msginit, arena);
+    if (!sub) return NULL;
+    envoy_service_load_stats_v2_LoadStatsResponse_set_load_reporting_interval(msg, sub);
+  }
+  return sub;
+}
+UPB_INLINE void envoy_service_load_stats_v2_LoadStatsResponse_set_report_endpoint_granularity(envoy_service_load_stats_v2_LoadStatsResponse *msg, bool value) {
+  UPB_FIELD_AT(msg, bool, UPB_SIZE(0, 0)) = value;
+}
+
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+#include "upb/port_undef.inc"
+
+#endif  /* ENVOY_SERVICE_LOAD_STATS_V2_LRS_PROTO_UPB_H_ */
index 2d9a1bc..a35db18 100644 (file)
@@ -21,7 +21,6 @@
 #include <limits.h>
 #include <string.h>
 
-#include <grpc/compression.h>
 #include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
@@ -213,106 +212,6 @@ void grpc_channel_args_destroy(grpc_channel_args* a) {
   gpr_free(a);
 }
 
-grpc_compression_algorithm grpc_channel_args_get_compression_algorithm(
-    const grpc_channel_args* a) {
-  size_t i;
-  if (a == nullptr) return GRPC_COMPRESS_NONE;
-  for (i = 0; i < a->num_args; ++i) {
-    if (a->args[i].type == GRPC_ARG_INTEGER &&
-        !strcmp(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, a->args[i].key)) {
-      return static_cast<grpc_compression_algorithm>(a->args[i].value.integer);
-      break;
-    }
-  }
-  return GRPC_COMPRESS_NONE;
-}
-
-grpc_channel_args* grpc_channel_args_set_compression_algorithm(
-    grpc_channel_args* a, grpc_compression_algorithm algorithm) {
-  GPR_ASSERT(algorithm < GRPC_COMPRESS_ALGORITHMS_COUNT);
-  grpc_arg tmp;
-  tmp.type = GRPC_ARG_INTEGER;
-  tmp.key = (char*)GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM;
-  tmp.value.integer = algorithm;
-  return grpc_channel_args_copy_and_add(a, &tmp, 1);
-}
-
-/** Returns 1 if the argument for compression algorithm's enabled states bitset
- * was found in \a a, returning the arg's value in \a states. Otherwise, returns
- * 0. */
-static int find_compression_algorithm_states_bitset(const grpc_channel_args* a,
-                                                    int** states_arg) {
-  if (a != nullptr) {
-    size_t i;
-    for (i = 0; i < a->num_args; ++i) {
-      if (a->args[i].type == GRPC_ARG_INTEGER &&
-          !strcmp(GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET,
-                  a->args[i].key)) {
-        *states_arg = &a->args[i].value.integer;
-        **states_arg |= 0x1; /* forcefully enable support for no compression */
-        return 1;
-      }
-    }
-  }
-  return 0; /* GPR_FALSE */
-}
-
-grpc_channel_args* grpc_channel_args_compression_algorithm_set_state(
-    grpc_channel_args** a, grpc_compression_algorithm algorithm, int state) {
-  int* states_arg = nullptr;
-  grpc_channel_args* result = *a;
-  const int states_arg_found =
-      find_compression_algorithm_states_bitset(*a, &states_arg);
-
-  if (grpc_channel_args_get_compression_algorithm(*a) == algorithm &&
-      state == 0) {
-    const char* algo_name = nullptr;
-    GPR_ASSERT(grpc_compression_algorithm_name(algorithm, &algo_name) != 0);
-    gpr_log(GPR_ERROR,
-            "Tried to disable default compression algorithm '%s'. The "
-            "operation has been ignored.",
-            algo_name);
-  } else if (states_arg_found) {
-    if (state != 0) {
-      GPR_BITSET((unsigned*)states_arg, algorithm);
-    } else if (algorithm != GRPC_COMPRESS_NONE) {
-      GPR_BITCLEAR((unsigned*)states_arg, algorithm);
-    }
-  } else {
-    /* create a new arg */
-    grpc_arg tmp;
-    tmp.type = GRPC_ARG_INTEGER;
-    tmp.key = (char*)GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET;
-    /* all enabled by default */
-    tmp.value.integer = (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1;
-    if (state != 0) {
-      GPR_BITSET((unsigned*)&tmp.value.integer, algorithm);
-    } else if (algorithm != GRPC_COMPRESS_NONE) {
-      GPR_BITCLEAR((unsigned*)&tmp.value.integer, algorithm);
-    }
-    result = grpc_channel_args_copy_and_add(*a, &tmp, 1);
-    grpc_channel_args_destroy(*a);
-    *a = result;
-  }
-  return result;
-}
-
-uint32_t grpc_channel_args_compression_algorithm_get_states(
-    const grpc_channel_args* a) {
-  int* states_arg;
-  if (find_compression_algorithm_states_bitset(a, &states_arg)) {
-    return static_cast<uint32_t>(*states_arg);
-  } else {
-    return (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; /* All algs. enabled */
-  }
-}
-
-grpc_channel_args* grpc_channel_args_set_socket_mutator(
-    grpc_channel_args* a, grpc_socket_mutator* mutator) {
-  grpc_arg tmp = grpc_socket_mutator_to_arg(mutator);
-  return grpc_channel_args_copy_and_add(a, &tmp, 1);
-}
-
 int grpc_channel_args_compare(const grpc_channel_args* a,
                               const grpc_channel_args* b) {
   int c = GPR_ICMP(a->num_args, b->num_args);
index c47c027..2b698a6 100644 (file)
@@ -21,9 +21,7 @@
 
 #include <grpc/support/port_platform.h>
 
-#include <grpc/compression.h>
 #include <grpc/grpc.h>
-#include "src/core/lib/iomgr/socket_mutator.h"
 
 // Channel args are intentionally immutable, to avoid the need for locking.
 
@@ -60,44 +58,9 @@ inline void grpc_channel_args_destroy(const grpc_channel_args* a) {
   grpc_channel_args_destroy(const_cast<grpc_channel_args*>(a));
 }
 
-/** Returns the compression algorithm set in \a a. */
-grpc_compression_algorithm grpc_channel_args_get_compression_algorithm(
-    const grpc_channel_args* a);
-
-/** Returns a channel arg instance with compression enabled. If \a a is
- * non-NULL, its args are copied. N.B. GRPC_COMPRESS_NONE disables compression
- * for the channel. */
-grpc_channel_args* grpc_channel_args_set_compression_algorithm(
-    grpc_channel_args* a, grpc_compression_algorithm algorithm);
-
-/** Sets the support for the given compression algorithm. By default, all
- * compression algorithms are enabled. It's an error to disable an algorithm set
- * by grpc_channel_args_set_compression_algorithm.
- *
- * Returns an instance with the updated algorithm states. The \a a pointer is
- * modified to point to the returned instance (which may be different from the
- * input value of \a a). */
-grpc_channel_args* grpc_channel_args_compression_algorithm_set_state(
-    grpc_channel_args** a, grpc_compression_algorithm algorithm, int enabled);
-
-/** Returns the bitset representing the support state (true for enabled, false
- * for disabled) for compression algorithms.
- *
- * The i-th bit of the returned bitset corresponds to the i-th entry in the
- * grpc_compression_algorithm enum. */
-uint32_t grpc_channel_args_compression_algorithm_get_states(
-    const grpc_channel_args* a);
-
 int grpc_channel_args_compare(const grpc_channel_args* a,
                               const grpc_channel_args* b);
 
-/** Returns a channel arg instance with socket mutator added. The socket mutator
- * will perform its mutate_fd method on all file descriptors used by the
- * channel.
- * If \a a is non-MULL, its args are copied. */
-grpc_channel_args* grpc_channel_args_set_socket_mutator(
-    grpc_channel_args* a, grpc_socket_mutator* mutator);
-
 /** Returns the value of argument \a name from \a args, or NULL if not found. */
 const grpc_arg* grpc_channel_args_find(const grpc_channel_args* args,
                                        const char* name);
index 580e1e5..a7c28d0 100644 (file)
@@ -42,7 +42,7 @@
 #include <grpc/support/time.h>
 
 #include "src/core/lib/debug/trace.h"
-#include "src/core/lib/gpr/arena.h"
+#include "src/core/lib/gprpp/arena.h"
 #include "src/core/lib/iomgr/call_combiner.h"
 #include "src/core/lib/iomgr/polling_entity.h"
 #include "src/core/lib/transport/transport.h"
@@ -69,8 +69,8 @@ typedef struct {
   const grpc_slice& path;
   gpr_timespec start_time;
   grpc_millis deadline;
-  gpr_arena* arena;
-  grpc_call_combiner* call_combiner;
+  grpc_core::Arena* arena;
+  grpc_core::CallCombiner* call_combiner;
 } grpc_call_element_args;
 
 typedef struct {
@@ -274,7 +274,11 @@ void grpc_call_log_op(const char* file, int line, gpr_log_severity severity,
 
 extern grpc_core::TraceFlag grpc_trace_channel;
 
-#define GRPC_CALL_LOG_OP(sev, elem, op) \
-  if (grpc_trace_channel.enabled()) grpc_call_log_op(sev, elem, op)
+#define GRPC_CALL_LOG_OP(sev, elem, op)                \
+  do {                                                 \
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_channel)) { \
+      grpc_call_log_op(sev, elem, op);                 \
+    }                                                  \
+  } while (0)
 
 #endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_H */
index 9f0169a..b6a660b 100644 (file)
@@ -23,7 +23,7 @@
 #include "src/core/lib/channel/channelz_registry.h"
 #include "src/core/lib/gpr/useful.h"
 #include "src/core/lib/gprpp/memory.h"
-#include "src/core/lib/gprpp/mutex_lock.h"
+#include "src/core/lib/gprpp/sync.h"
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
index e2ea334..bd30c36 100644 (file)
@@ -41,12 +41,12 @@ typedef struct connected_channel_channel_data {
 typedef struct {
   grpc_closure closure;
   grpc_closure* original_closure;
-  grpc_call_combiner* call_combiner;
+  grpc_core::CallCombiner* call_combiner;
   const char* reason;
 } callback_state;
 
 typedef struct connected_channel_call_data {
-  grpc_call_combiner* call_combiner;
+  grpc_core::CallCombiner* call_combiner;
   // Closures used for returning results on the call combiner.
   callback_state on_complete[6];  // Max number of pending batches.
   callback_state recv_initial_metadata_ready;
index 81b84f1..fd9d0ce 100644 (file)
@@ -35,6 +35,9 @@ typedef enum {
   /// Reserved for traffic_class_context.
   GRPC_CONTEXT_TRAFFIC,
 
+  /// Holds a pointer to ServiceConfig::CallData associated with this call.
+  GRPC_SERVICE_CONFIG_CALL_DATA,
+
   GRPC_CONTEXT_COUNT
 } grpc_context_index;
 
index 6bb05ce..27ff250 100644 (file)
@@ -94,7 +94,7 @@ void HandshakeManager::ShutdownAllPending(grpc_error* why) {
 }
 
 void HandshakeManager::Add(RefCountedPtr<Handshaker> handshaker) {
-  if (grpc_handshaker_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_handshaker_trace)) {
     gpr_log(
         GPR_INFO,
         "handshake_manager %p: adding handshaker %s [%p] at index %" PRIuPTR,
@@ -125,7 +125,7 @@ void HandshakeManager::Shutdown(grpc_error* why) {
 // on_handshake_done callback.
 // Returns true if we've scheduled the on_handshake_done callback.
 bool HandshakeManager::CallNextHandshakerLocked(grpc_error* error) {
-  if (grpc_handshaker_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_handshaker_trace)) {
     char* args_str = HandshakerArgsString(&args_);
     gpr_log(GPR_INFO,
             "handshake_manager %p: error=%s shutdown=%d index=%" PRIuPTR
@@ -159,7 +159,7 @@ bool HandshakeManager::CallNextHandshakerLocked(grpc_error* error) {
         args_.read_buffer = nullptr;
       }
     }
-    if (grpc_handshaker_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_handshaker_trace)) {
       gpr_log(GPR_INFO,
               "handshake_manager %p: handshaking complete -- scheduling "
               "on_handshake_done with error=%s",
@@ -172,7 +172,7 @@ bool HandshakeManager::CallNextHandshakerLocked(grpc_error* error) {
     is_shutdown_ = true;
   } else {
     auto handshaker = handshakers_[index_];
-    if (grpc_handshaker_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_handshaker_trace)) {
       gpr_log(
           GPR_INFO,
           "handshake_manager %p: calling handshaker %s [%p] at index %" PRIuPTR,
index 912d524..b68799b 100644 (file)
@@ -27,8 +27,8 @@
 
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/gprpp/inlined_vector.h"
-#include "src/core/lib/gprpp/mutex_lock.h"
 #include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/iomgr/closure.h"
 #include "src/core/lib/iomgr/endpoint.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
diff --git a/src/core/lib/compression/compression_args.cc b/src/core/lib/compression/compression_args.cc
new file mode 100644 (file)
index 0000000..6a8232d
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include <limits.h>
+#include <string.h>
+
+#include <grpc/compression.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/compression/compression_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+
+grpc_compression_algorithm grpc_channel_args_get_compression_algorithm(
+    const grpc_channel_args* a) {
+  size_t i;
+  if (a == nullptr) return GRPC_COMPRESS_NONE;
+  for (i = 0; i < a->num_args; ++i) {
+    if (a->args[i].type == GRPC_ARG_INTEGER &&
+        !strcmp(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, a->args[i].key)) {
+      return static_cast<grpc_compression_algorithm>(a->args[i].value.integer);
+      break;
+    }
+  }
+  return GRPC_COMPRESS_NONE;
+}
+
+grpc_channel_args* grpc_channel_args_set_compression_algorithm(
+    grpc_channel_args* a, grpc_compression_algorithm algorithm) {
+  GPR_ASSERT(algorithm < GRPC_COMPRESS_ALGORITHMS_COUNT);
+  grpc_arg tmp;
+  tmp.type = GRPC_ARG_INTEGER;
+  tmp.key = (char*)GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM;
+  tmp.value.integer = algorithm;
+  return grpc_channel_args_copy_and_add(a, &tmp, 1);
+}
+
+/** Returns 1 if the argument for compression algorithm's enabled states bitset
+ * was found in \a a, returning the arg's value in \a states. Otherwise, returns
+ * 0. */
+static int find_compression_algorithm_states_bitset(const grpc_channel_args* a,
+                                                    int** states_arg) {
+  if (a != nullptr) {
+    size_t i;
+    for (i = 0; i < a->num_args; ++i) {
+      if (a->args[i].type == GRPC_ARG_INTEGER &&
+          !strcmp(GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET,
+                  a->args[i].key)) {
+        *states_arg = &a->args[i].value.integer;
+        **states_arg |= 0x1; /* forcefully enable support for no compression */
+        return 1;
+      }
+    }
+  }
+  return 0; /* GPR_FALSE */
+}
+
+grpc_channel_args* grpc_channel_args_compression_algorithm_set_state(
+    grpc_channel_args** a, grpc_compression_algorithm algorithm, int state) {
+  int* states_arg = nullptr;
+  grpc_channel_args* result = *a;
+  const int states_arg_found =
+      find_compression_algorithm_states_bitset(*a, &states_arg);
+
+  if (grpc_channel_args_get_compression_algorithm(*a) == algorithm &&
+      state == 0) {
+    const char* algo_name = nullptr;
+    GPR_ASSERT(grpc_compression_algorithm_name(algorithm, &algo_name) != 0);
+    gpr_log(GPR_ERROR,
+            "Tried to disable default compression algorithm '%s'. The "
+            "operation has been ignored.",
+            algo_name);
+  } else if (states_arg_found) {
+    if (state != 0) {
+      GPR_BITSET((unsigned*)states_arg, algorithm);
+    } else if (algorithm != GRPC_COMPRESS_NONE) {
+      GPR_BITCLEAR((unsigned*)states_arg, algorithm);
+    }
+  } else {
+    /* create a new arg */
+    grpc_arg tmp;
+    tmp.type = GRPC_ARG_INTEGER;
+    tmp.key = (char*)GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET;
+    /* all enabled by default */
+    tmp.value.integer = (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1;
+    if (state != 0) {
+      GPR_BITSET((unsigned*)&tmp.value.integer, algorithm);
+    } else if (algorithm != GRPC_COMPRESS_NONE) {
+      GPR_BITCLEAR((unsigned*)&tmp.value.integer, algorithm);
+    }
+    result = grpc_channel_args_copy_and_add(*a, &tmp, 1);
+    grpc_channel_args_destroy(*a);
+    *a = result;
+  }
+  return result;
+}
+
+uint32_t grpc_channel_args_compression_algorithm_get_states(
+    const grpc_channel_args* a) {
+  int* states_arg;
+  if (find_compression_algorithm_states_bitset(a, &states_arg)) {
+    return static_cast<uint32_t>(*states_arg);
+  } else {
+    return (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; /* All algs. enabled */
+  }
+}
diff --git a/src/core/lib/compression/compression_args.h b/src/core/lib/compression/compression_args.h
new file mode 100644 (file)
index 0000000..407d6e2
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *
+ * 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 GRPC_CORE_LIB_COMPRESSION_COMPRESSION_ARGS_H
+#define GRPC_CORE_LIB_COMPRESSION_COMPRESSION_ARGS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/compression.h>
+#include <grpc/impl/codegen/grpc_types.h>
+
+/** Returns the compression algorithm set in \a a. */
+grpc_compression_algorithm grpc_channel_args_get_compression_algorithm(
+    const grpc_channel_args* a);
+
+/** Returns a channel arg instance with compression enabled. If \a a is
+ * non-NULL, its args are copied. N.B. GRPC_COMPRESS_NONE disables compression
+ * for the channel. */
+grpc_channel_args* grpc_channel_args_set_compression_algorithm(
+    grpc_channel_args* a, grpc_compression_algorithm algorithm);
+
+/** Sets the support for the given compression algorithm. By default, all
+ * compression algorithms are enabled. It's an error to disable an algorithm set
+ * by grpc_channel_args_set_compression_algorithm.
+ *
+ * Returns an instance with the updated algorithm states. The \a a pointer is
+ * modified to point to the returned instance (which may be different from the
+ * input value of \a a). */
+grpc_channel_args* grpc_channel_args_compression_algorithm_set_state(
+    grpc_channel_args** a, grpc_compression_algorithm algorithm, int state);
+
+/** Returns the bitset representing the support state (true for enabled, false
+ * for disabled) for compression algorithms.
+ *
+ * The i-th bit of the returned bitset corresponds to the i-th entry in the
+ * grpc_compression_algorithm enum. */
+uint32_t grpc_channel_args_compression_algorithm_get_states(
+    const grpc_channel_args* a);
+
+#endif /* GRPC_CORE_LIB_COMPRESSION_COMPRESSION_ARGS_H */
index cafdb15..84c0a38 100644 (file)
 #include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
-#include "src/core/lib/gpr/env.h"
+
+GPR_GLOBAL_CONFIG_DEFINE_STRING(
+    grpc_trace, "",
+    "A comma separated list of tracers that provide additional insight into "
+    "how gRPC C core is processing requests via debug logs.");
 
 int grpc_tracer_set_enabled(const char* name, int enabled);
 
@@ -133,12 +137,14 @@ static void parse(const char* s) {
   gpr_free(strings);
 }
 
-void grpc_tracer_init(const char* env_var) {
-  char* e = gpr_getenv(env_var);
-  if (e != nullptr) {
-    parse(e);
-    gpr_free(e);
-  }
+void grpc_tracer_init(const char* env_var_name) {
+  (void)env_var_name;  // suppress unused variable error
+  grpc_tracer_init();
+}
+
+void grpc_tracer_init() {
+  grpc_core::UniquePtr<char> value = GPR_GLOBAL_CONFIG_GET(grpc_trace);
+  parse(value.get());
 }
 
 void grpc_tracer_shutdown(void) {}
index 6108fb2..6a4a803 100644 (file)
 #include <grpc/support/atm.h>
 #include <stdbool.h>
 
+#include "src/core/lib/gprpp/global_config.h"
+
+GPR_GLOBAL_CONFIG_DECLARE_STRING(grpc_trace);
+
+// TODO(veblush): Remove this deprecated function once codes depending on this
+// function are updated in the internal repo.
 void grpc_tracer_init(const char* env_var_name);
+
+void grpc_tracer_init();
 void grpc_tracer_shutdown(void);
 
 #if defined(__has_feature)
@@ -65,6 +73,8 @@ class TraceFlag {
 // wrapped language (wr don't want to force recompilation to get tracing).
 // Internally, however, for performance reasons, we compile them out by
 // default, since internal build systems make recompiling trivial.
+//
+// Prefer GRPC_TRACE_FLAG_ENABLED() macro instead of using enabled() directly.
 #define GRPC_USE_TRACERS  // tracers on by default in OSS
 #if defined(GRPC_USE_TRACERS) || !defined(NDEBUG)
   bool enabled() {
@@ -99,6 +109,8 @@ class TraceFlag {
 #endif
 };
 
+#define GRPC_TRACE_FLAG_ENABLED(f) GPR_UNLIKELY((f).enabled())
+
 #ifndef NDEBUG
 typedef TraceFlag DebugOnlyTraceFlag;
 #else
diff --git a/src/core/lib/gpr/arena.cc b/src/core/lib/gpr/arena.cc
deleted file mode 100644 (file)
index 836a7ca..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- *
- * Copyright 2017 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#include <grpc/support/port_platform.h>
-
-#include "src/core/lib/gpr/arena.h"
-
-#include <string.h>
-#include <new>
-
-#include <grpc/support/alloc.h>
-#include <grpc/support/atm.h>
-#include <grpc/support/log.h>
-#include <grpc/support/sync.h>
-
-#include "src/core/lib/gpr/alloc.h"
-#include "src/core/lib/gpr/env.h"
-#include "src/core/lib/gprpp/memory.h"
-
-namespace {
-enum init_strategy {
-  NO_INIT,        // Do not initialize the arena blocks.
-  ZERO_INIT,      // Initialize arena blocks with 0.
-  NON_ZERO_INIT,  // Initialize arena blocks with a non-zero value.
-};
-
-gpr_once g_init_strategy_once = GPR_ONCE_INIT;
-init_strategy g_init_strategy = NO_INIT;
-}  // namespace
-
-static void set_strategy_from_env() {
-  char* str = gpr_getenv("GRPC_ARENA_INIT_STRATEGY");
-  if (str == nullptr) {
-    g_init_strategy = NO_INIT;
-  } else if (strcmp(str, "zero_init") == 0) {
-    g_init_strategy = ZERO_INIT;
-  } else if (strcmp(str, "non_zero_init") == 0) {
-    g_init_strategy = NON_ZERO_INIT;
-  } else {
-    g_init_strategy = NO_INIT;
-  }
-  gpr_free(str);
-}
-
-static void* gpr_arena_alloc_maybe_init(size_t size) {
-  void* mem = gpr_malloc_aligned(size, GPR_MAX_ALIGNMENT);
-  gpr_once_init(&g_init_strategy_once, set_strategy_from_env);
-  if (GPR_UNLIKELY(g_init_strategy != NO_INIT)) {
-    if (g_init_strategy == ZERO_INIT) {
-      memset(mem, 0, size);
-    } else {  // NON_ZERO_INIT.
-      memset(mem, 0xFE, size);
-    }
-  }
-  return mem;
-}
-
-void gpr_arena_init() {
-  gpr_once_init(&g_init_strategy_once, set_strategy_from_env);
-}
-
-// Uncomment this to use a simple arena that simply allocates the
-// requested amount of memory for each call to gpr_arena_alloc().  This
-// effectively eliminates the efficiency gain of using an arena, but it
-// may be useful for debugging purposes.
-//#define SIMPLE_ARENA_FOR_DEBUGGING
-#ifdef SIMPLE_ARENA_FOR_DEBUGGING
-
-struct gpr_arena {
-  gpr_arena() { gpr_mu_init(&mu); }
-  ~gpr_arena() {
-    gpr_mu_destroy(&mu);
-    for (size_t i = 0; i < num_ptrs; ++i) {
-      gpr_free_aligned(ptrs[i]);
-    }
-    gpr_free(ptrs);
-  }
-
-  gpr_mu mu;
-  void** ptrs = nullptr;
-  size_t num_ptrs = 0;
-};
-
-gpr_arena* gpr_arena_create(size_t ignored_initial_size) {
-  return grpc_core::New<gpr_arena>();
-}
-
-size_t gpr_arena_destroy(gpr_arena* arena) {
-  grpc_core::Delete(arena);
-  return 1;  // Value doesn't matter, since it won't be used.
-}
-
-void* gpr_arena_alloc(gpr_arena* arena, size_t size) {
-  gpr_mu_lock(&arena->mu);
-  arena->ptrs =
-      (void**)gpr_realloc(arena->ptrs, sizeof(void*) * (arena->num_ptrs + 1));
-  void* retval = arena->ptrs[arena->num_ptrs++] =
-      gpr_arena_alloc_maybe_init(size);
-  gpr_mu_unlock(&arena->mu);
-  return retval;
-}
-
-#else  // SIMPLE_ARENA_FOR_DEBUGGING
-
-// TODO(roth): We currently assume that all callers need alignment of 16
-// bytes, which may be wrong in some cases.  As part of converting the
-// arena API to C++, we should consider replacing gpr_arena_alloc() with a
-// template that takes the type of the value being allocated, which
-// would allow us to use the alignment actually needed by the caller.
-
-typedef struct zone {
-  zone* next = nullptr;
-} zone;
-
-struct gpr_arena {
-  gpr_arena(size_t initial_size)
-      : initial_zone_size(initial_size), last_zone(&initial_zone) {
-    gpr_mu_init(&arena_growth_mutex);
-  }
-  ~gpr_arena() {
-    gpr_mu_destroy(&arena_growth_mutex);
-    zone* z = initial_zone.next;
-    while (z) {
-      zone* next_z = z->next;
-      z->~zone();
-      gpr_free_aligned(z);
-      z = next_z;
-    }
-  }
-
-  // Keep track of the total used size. We use this in our call sizing
-  // historesis.
-  gpr_atm total_used = 0;
-  size_t initial_zone_size;
-  zone initial_zone;
-  zone* last_zone;
-  gpr_mu arena_growth_mutex;
-};
-
-gpr_arena* gpr_arena_create(size_t initial_size) {
-  initial_size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(initial_size);
-  return new (gpr_arena_alloc_maybe_init(
-      GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(gpr_arena)) + initial_size))
-      gpr_arena(initial_size);
-}
-
-size_t gpr_arena_destroy(gpr_arena* arena) {
-  const gpr_atm size = gpr_atm_no_barrier_load(&arena->total_used);
-  arena->~gpr_arena();
-  gpr_free_aligned(arena);
-  return static_cast<size_t>(size);
-}
-
-void* gpr_arena_alloc(gpr_arena* arena, size_t size) {
-  size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(size);
-  size_t begin = gpr_atm_no_barrier_fetch_add(&arena->total_used, size);
-  if (begin + size <= arena->initial_zone_size) {
-    return reinterpret_cast<char*>(arena) +
-           GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(gpr_arena)) + begin;
-  } else {
-    // If the allocation isn't able to end in the initial zone, create a new
-    // zone for this allocation, and any unused space in the initial zone is
-    // wasted. This overflowing and wasting is uncommon because of our arena
-    // sizing historesis (that is, most calls should have a large enough initial
-    // zone and will not need to grow the arena).
-    gpr_mu_lock(&arena->arena_growth_mutex);
-    zone* z = new (gpr_arena_alloc_maybe_init(
-        GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone)) + size)) zone();
-    arena->last_zone->next = z;
-    arena->last_zone = z;
-    gpr_mu_unlock(&arena->arena_growth_mutex);
-    return reinterpret_cast<char*>(z) +
-           GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone));
-  }
-}
-
-#endif  // SIMPLE_ARENA_FOR_DEBUGGING
index 069892b..4d70695 100644 (file)
 // the arena as a whole is freed
 // Tracks the total memory allocated against it, so that future arenas can
 // pre-allocate the right amount of memory
+// This transitional API is deprecated and will be removed soon in favour of
+// src/core/lib/gprpp/arena.h .
 
 #ifndef GRPC_CORE_LIB_GPR_ARENA_H
 #define GRPC_CORE_LIB_GPR_ARENA_H
 
 #include <grpc/support/port_platform.h>
 
-#include <stddef.h>
-
-typedef struct gpr_arena gpr_arena;
+#include "src/core/lib/gprpp/arena.h"
 
+// TODO(arjunroy) : Remove deprecated gpr_arena API once all callers are gone.
+typedef class grpc_core::Arena gpr_arena;
 // Create an arena, with \a initial_size bytes in the first allocated buffer
-gpr_arena* gpr_arena_create(size_t initial_size);
-// Allocate \a size bytes from the arena
-void* gpr_arena_alloc(gpr_arena* arena, size_t size);
+inline gpr_arena* gpr_arena_create(size_t initial_size) {
+  return grpc_core::Arena::Create(initial_size);
+}
 // Destroy an arena, returning the total number of bytes allocated
-size_t gpr_arena_destroy(gpr_arena* arena);
-// Initializes the Arena component.
-void gpr_arena_init();
+inline size_t gpr_arena_destroy(gpr_arena* arena) { return arena->Destroy(); }
+// Allocate \a size bytes from the arena
+inline void* gpr_arena_alloc(gpr_arena* arena, size_t size) {
+  return arena->Alloc(size);
+}
 
 #endif /* GRPC_CORE_LIB_GPR_ARENA_H */
index aec8a31..11db17c 100644 (file)
@@ -34,10 +34,7 @@ char* gpr_getenv(const char* name);
 /* Sets the environment with the specified name to the specified value. */
 void gpr_setenv(const char* name, const char* value);
 
-/* This is a version of gpr_getenv that does not produce any output if it has to
-   use an insecure version of the function. It is ONLY to be used to solve the
-   problem in which we need to check an env variable to configure the verbosity
-   level of logging. So DO NOT USE THIS. */
-const char* gpr_getenv_silent(const char* name, char** dst);
+/* Deletes the variable name from the environment. */
+void gpr_unsetenv(const char* name);
 
 #endif /* GRPC_CORE_LIB_GPR_ENV_H */
index fadc42f..3a3aa54 100644 (file)
@@ -38,7 +38,7 @@
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gpr/useful.h"
 
-const char* gpr_getenv_silent(const char* name, char** dst) {
+static const char* gpr_getenv_silent(const char* name, char** dst) {
   const char* insecure_func_used = nullptr;
   char* result = nullptr;
 #if defined(GPR_BACKWARDS_COMPATIBILITY_MODE)
@@ -79,4 +79,9 @@ void gpr_setenv(const char* name, const char* value) {
   GPR_ASSERT(res == 0);
 }
 
+void gpr_unsetenv(const char* name) {
+  int res = unsetenv(name);
+  GPR_ASSERT(res == 0);
+}
+
 #endif /* GPR_LINUX_ENV */
index 599f85a..30ddc50 100644 (file)
@@ -44,4 +44,9 @@ void gpr_setenv(const char* name, const char* value) {
   GPR_ASSERT(res == 0);
 }
 
+void gpr_unsetenv(const char* name) {
+  int res = unsetenv(name);
+  GPR_ASSERT(res == 0);
+}
+
 #endif /* GPR_POSIX_ENV */
index cf8ed60..76c45fb 100644 (file)
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gpr/string_windows.h"
 
-const char* gpr_getenv_silent(const char* name, char** dst) {
-  *dst = gpr_getenv(name);
-  return NULL;
-}
-
 char* gpr_getenv(const char* name) {
   char* result = NULL;
   DWORD size;
@@ -69,4 +64,11 @@ void gpr_setenv(const char* name, const char* value) {
   GPR_ASSERT(res);
 }
 
+void gpr_unsetenv(const char* name) {
+  LPTSTR tname = gpr_char_to_tchar(name);
+  BOOL res = SetEnvironmentVariable(tname, NULL);
+  gpr_free(tname);
+  GPR_ASSERT(res);
+}
+
 #endif /* GPR_WINDOWS_ENV */
index 01ef112..8a229b2 100644 (file)
 #include <grpc/support/atm.h>
 #include <grpc/support/log.h>
 
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/global_config.h"
 
 #include <stdio.h>
 #include <string.h>
 
+GPR_GLOBAL_CONFIG_DEFINE_STRING(grpc_verbosity, "ERROR",
+                                "Default gRPC logging verbosity")
+
 void gpr_default_log(gpr_log_func_args* args);
 static gpr_atm g_log_func = (gpr_atm)gpr_default_log;
 static gpr_atm g_min_severity_to_print = GPR_LOG_VERBOSITY_UNSET;
@@ -72,29 +75,22 @@ void gpr_set_log_verbosity(gpr_log_severity min_severity_to_print) {
 }
 
 void gpr_log_verbosity_init() {
-  char* verbosity = nullptr;
-  const char* insecure_getenv = gpr_getenv_silent("GRPC_VERBOSITY", &verbosity);
+  grpc_core::UniquePtr<char> verbosity = GPR_GLOBAL_CONFIG_GET(grpc_verbosity);
 
   gpr_atm min_severity_to_print = GPR_LOG_SEVERITY_ERROR;
-  if (verbosity != nullptr) {
-    if (gpr_stricmp(verbosity, "DEBUG") == 0) {
+  if (strlen(verbosity.get()) > 0) {
+    if (gpr_stricmp(verbosity.get(), "DEBUG") == 0) {
       min_severity_to_print = static_cast<gpr_atm>(GPR_LOG_SEVERITY_DEBUG);
-    } else if (gpr_stricmp(verbosity, "INFO") == 0) {
+    } else if (gpr_stricmp(verbosity.get(), "INFO") == 0) {
       min_severity_to_print = static_cast<gpr_atm>(GPR_LOG_SEVERITY_INFO);
-    } else if (gpr_stricmp(verbosity, "ERROR") == 0) {
+    } else if (gpr_stricmp(verbosity.get(), "ERROR") == 0) {
       min_severity_to_print = static_cast<gpr_atm>(GPR_LOG_SEVERITY_ERROR);
     }
-    gpr_free(verbosity);
   }
   if ((gpr_atm_no_barrier_load(&g_min_severity_to_print)) ==
       GPR_LOG_VERBOSITY_UNSET) {
     gpr_atm_no_barrier_store(&g_min_severity_to_print, min_severity_to_print);
   }
-
-  if (insecure_getenv != nullptr) {
-    gpr_log(GPR_DEBUG, "Warning: insecure environment read function '%s' used",
-            insecure_getenv);
-  }
 }
 
 void gpr_set_log_function(gpr_log_func f) {
index 0a76fc1..31d5fde 100644 (file)
@@ -332,16 +332,22 @@ void* gpr_memrchr(const void* s, int c, size_t n) {
   return nullptr;
 }
 
-bool gpr_is_true(const char* s) {
-  size_t i;
+bool gpr_parse_bool_value(const char* s, bool* dst) {
+  const char* kTrue[] = {"1", "t", "true", "y", "yes"};
+  const char* kFalse[] = {"0", "f", "false", "n", "no"};
+  static_assert(sizeof(kTrue) == sizeof(kFalse), "true_false_equal");
+
   if (s == nullptr) {
     return false;
   }
-  static const char* truthy[] = {"yes", "true", "1"};
-  for (i = 0; i < GPR_ARRAY_SIZE(truthy); i++) {
-    if (0 == gpr_stricmp(s, truthy[i])) {
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(kTrue); ++i) {
+    if (gpr_stricmp(s, kTrue[i]) == 0) {
+      *dst = true;
+      return true;
+    } else if (gpr_stricmp(s, kFalse[i]) == 0) {
+      *dst = false;
       return true;
     }
   }
-  return false;
+  return false;  // didn't match a legal input
 }
index ce51fe4..c5efcec 100644 (file)
@@ -113,7 +113,9 @@ int gpr_stricmp(const char* a, const char* b);
 
 void* gpr_memrchr(const void* s, int c, size_t n);
 
-/** Return true if lower(s) equals "true", "yes" or "1", otherwise false. */
-bool gpr_is_true(const char* s);
+/* Try to parse given string into a boolean value.
+   When parsed successfully, dst will have the value and returns true.
+   Otherwise, it returns false. */
+bool gpr_parse_bool_value(const char* value, bool* dst);
 
 #endif /* GRPC_CORE_LIB_GPR_STRING_H */
index 1b3e364..b5a4767 100644 (file)
@@ -108,6 +108,9 @@ static gpr_timespec now_impl(gpr_clock_type clock) {
   now.clock_type = clock;
   switch (clock) {
     case GPR_CLOCK_REALTIME:
+      // gettimeofday(...) function may return with a value whose tv_usec is
+      // greater than 1e6 on iOS The case is resolved with the guard at end of
+      // this function.
       gettimeofday(&now_tv, nullptr);
       now.tv_sec = now_tv.tv_sec;
       now.tv_nsec = now_tv.tv_usec * 1000;
@@ -124,6 +127,16 @@ static gpr_timespec now_impl(gpr_clock_type clock) {
       abort();
   }
 
+  // Guard the tv_nsec field in valid range for all clock types
+  while (GPR_UNLIKELY(now.tv_nsec >= 1e9)) {
+    now.tv_sec++;
+    now.tv_nsec -= 1e9;
+  }
+  while (GPR_UNLIKELY(now.tv_nsec < 0)) {
+    now.tv_sec--;
+    now.tv_nsec += 1e9;
+  }
+
   return now;
 }
 #endif
diff --git a/src/core/lib/gprpp/arena.cc b/src/core/lib/gprpp/arena.cc
new file mode 100644 (file)
index 0000000..5c344db
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/gprpp/arena.h"
+
+#include <string.h>
+#include <new>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/atm.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/gpr/alloc.h"
+#include "src/core/lib/gprpp/memory.h"
+
+namespace {
+
+void* ArenaStorage(size_t initial_size) {
+  static constexpr size_t base_size =
+      GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_core::Arena));
+  initial_size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(initial_size);
+  size_t alloc_size = base_size + initial_size;
+  static constexpr size_t alignment =
+      (GPR_CACHELINE_SIZE > GPR_MAX_ALIGNMENT &&
+       GPR_CACHELINE_SIZE % GPR_MAX_ALIGNMENT == 0)
+          ? GPR_CACHELINE_SIZE
+          : GPR_MAX_ALIGNMENT;
+  return gpr_malloc_aligned(alloc_size, alignment);
+}
+
+}  // namespace
+
+namespace grpc_core {
+
+Arena::~Arena() {
+  Zone* z = last_zone_;
+  while (z) {
+    Zone* prev_z = z->prev;
+    z->~Zone();
+    gpr_free_aligned(z);
+    z = prev_z;
+  }
+}
+
+Arena* Arena::Create(size_t initial_size) {
+  return new (ArenaStorage(initial_size)) Arena(initial_size);
+}
+
+Pair<Arena*, void*> Arena::CreateWithAlloc(size_t initial_size,
+                                           size_t alloc_size) {
+  static constexpr size_t base_size =
+      GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(Arena));
+  auto* new_arena =
+      new (ArenaStorage(initial_size)) Arena(initial_size, alloc_size);
+  void* first_alloc = reinterpret_cast<char*>(new_arena) + base_size;
+  return MakePair(new_arena, first_alloc);
+}
+
+size_t Arena::Destroy() {
+  size_t size = total_used_.Load(MemoryOrder::RELAXED);
+  this->~Arena();
+  gpr_free_aligned(this);
+  return size;
+}
+
+void* Arena::AllocZone(size_t size) {
+  // If the allocation isn't able to end in the initial zone, create a new
+  // zone for this allocation, and any unused space in the initial zone is
+  // wasted. This overflowing and wasting is uncommon because of our arena
+  // sizing hysteresis (that is, most calls should have a large enough initial
+  // zone and will not need to grow the arena).
+  static constexpr size_t zone_base_size =
+      GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(Zone));
+  size_t alloc_size = zone_base_size + size;
+  Zone* z = new (gpr_malloc_aligned(alloc_size, GPR_MAX_ALIGNMENT)) Zone();
+  {
+    gpr_spinlock_lock(&arena_growth_spinlock_);
+    z->prev = last_zone_;
+    last_zone_ = z;
+    gpr_spinlock_unlock(&arena_growth_spinlock_);
+  }
+  return reinterpret_cast<char*>(z) + zone_base_size;
+}
+
+}  // namespace grpc_core
diff --git a/src/core/lib/gprpp/arena.h b/src/core/lib/gprpp/arena.h
new file mode 100644 (file)
index 0000000..b1b0c4a
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ *
+ * Copyright 2017 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.
+ *
+ */
+
+// \file Arena based allocator
+// Allows very fast allocation of memory, but that memory cannot be freed until
+// the arena as a whole is freed
+// Tracks the total memory allocated against it, so that future arenas can
+// pre-allocate the right amount of memory
+
+#ifndef GRPC_CORE_LIB_GPRPP_ARENA_H
+#define GRPC_CORE_LIB_GPRPP_ARENA_H
+
+#include <grpc/support/port_platform.h>
+
+#include <new>
+#include <utility>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/gpr/alloc.h"
+#include "src/core/lib/gpr/spinlock.h"
+#include "src/core/lib/gprpp/atomic.h"
+#include "src/core/lib/gprpp/pair.h"
+
+#include <stddef.h>
+
+namespace grpc_core {
+
+class Arena {
+ public:
+  // Create an arena, with \a initial_size bytes in the first allocated buffer.
+  static Arena* Create(size_t initial_size);
+
+  // Create an arena, with \a initial_size bytes in the first allocated buffer,
+  // and return both a void pointer to the returned arena and a void* with the
+  // first allocation.
+  static Pair<Arena*, void*> CreateWithAlloc(size_t initial_size,
+                                             size_t alloc_size);
+
+  // Destroy an arena, returning the total number of bytes allocated.
+  size_t Destroy();
+  // Allocate \a size bytes from the arena.
+  void* Alloc(size_t size) {
+    static constexpr size_t base_size =
+        GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(Arena));
+    size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(size);
+    size_t begin = total_used_.FetchAdd(size, MemoryOrder::RELAXED);
+    if (begin + size <= initial_zone_size_) {
+      return reinterpret_cast<char*>(this) + base_size + begin;
+    } else {
+      return AllocZone(size);
+    }
+  }
+
+  // TODO(roth): We currently assume that all callers need alignment of 16
+  // bytes, which may be wrong in some cases. When we have time, we should
+  // change this to instead use the alignment of the type being allocated by
+  // this method.
+  template <typename T, typename... Args>
+  T* New(Args&&... args) {
+    T* t = static_cast<T*>(Alloc(sizeof(T)));
+    new (t) T(std::forward<Args>(args)...);
+    return t;
+  }
+
+ private:
+  struct Zone {
+    Zone* prev;
+  };
+
+  // Initialize an arena.
+  // Parameters:
+  //   initial_size: The initial size of the whole arena in bytes. These bytes
+  //   are contained within 'zone 0'. If the arena user ends up requiring more
+  //   memory than the arena contains in zone 0, subsequent zones are allocated
+  //   on demand and maintained in a tail-linked list.
+  //
+  //   initial_alloc: Optionally, construct the arena as though a call to
+  //   Alloc() had already been made for initial_alloc bytes. This provides a
+  //   quick optimization (avoiding an atomic fetch-add) for the common case
+  //   where we wish to create an arena and then perform an immediate
+  //   allocation.
+  explicit Arena(size_t initial_size, size_t initial_alloc = 0)
+      : total_used_(initial_alloc), initial_zone_size_(initial_size) {}
+
+  ~Arena();
+
+  void* AllocZone(size_t size);
+
+  // Keep track of the total used size. We use this in our call sizing
+  // hysteresis.
+  Atomic<size_t> total_used_;
+  size_t initial_zone_size_;
+  gpr_spinlock arena_growth_spinlock_ = GPR_SPINLOCK_STATIC_INITIALIZER;
+  // If the initial arena allocation wasn't enough, we allocate additional zones
+  // in a reverse linked list. Each additional zone consists of (1) a pointer to
+  // the zone added before this zone (null if this is the first additional zone)
+  // and (2) the allocated memory. The arena itself maintains a pointer to the
+  // last zone; the zone list is reverse-walked during arena destruction only.
+  Zone* last_zone_ = nullptr;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_GPRPP_ARENA_H */
index c4b1cbc..cacf5e8 100644 (file)
@@ -26,8 +26,8 @@
 #include <grpc/support/sync.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/global_config.h"
 #include "src/core/lib/gprpp/memory.h"
 
 /*
  *       AROUND VERY SPECIFIC USE CASES.
  */
 
+#ifdef GRPC_ENABLE_FORK_SUPPORT
+#define GRPC_ENABLE_FORK_SUPPORT_DEFAULT true
+#else
+#define GRPC_ENABLE_FORK_SUPPORT_DEFAULT false
+#endif  // GRPC_ENABLE_FORK_SUPPORT
+
+GPR_GLOBAL_CONFIG_DEFINE_BOOL(grpc_enable_fork_support,
+                              GRPC_ENABLE_FORK_SUPPORT_DEFAULT,
+                              "Enable folk support");
+
 namespace grpc_core {
 namespace internal {
 // The exec_ctx_count has 2 modes, blocked and unblocked.
@@ -158,34 +168,7 @@ class ThreadState {
 
 void Fork::GlobalInit() {
   if (!override_enabled_) {
-#ifdef GRPC_ENABLE_FORK_SUPPORT
-    support_enabled_ = true;
-#endif
-    bool env_var_set = false;
-    char* env = gpr_getenv("GRPC_ENABLE_FORK_SUPPORT");
-    if (env != nullptr) {
-      static const char* truthy[] = {"yes",  "Yes",  "YES", "true",
-                                     "True", "TRUE", "1"};
-      static const char* falsey[] = {"no",    "No",    "NO", "false",
-                                     "False", "FALSE", "0"};
-      for (size_t i = 0; i < GPR_ARRAY_SIZE(truthy); i++) {
-        if (0 == strcmp(env, truthy[i])) {
-          support_enabled_ = true;
-          env_var_set = true;
-          break;
-        }
-      }
-      if (!env_var_set) {
-        for (size_t i = 0; i < GPR_ARRAY_SIZE(falsey); i++) {
-          if (0 == strcmp(env, falsey[i])) {
-            support_enabled_ = false;
-            env_var_set = true;
-            break;
-          }
-        }
-      }
-      gpr_free(env);
-    }
+    support_enabled_ = GPR_GLOBAL_CONFIG_GET(grpc_enable_fork_support);
   }
   if (support_enabled_) {
     exec_ctx_state_ = grpc_core::New<internal::ExecCtxState>();
diff --git a/src/core/lib/gprpp/global_config.h b/src/core/lib/gprpp/global_config.h
new file mode 100644 (file)
index 0000000..a1bbf07
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ *
+ * 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 GRPC_CORE_LIB_GPRPP_GLOBAL_CONFIG_H
+#define GRPC_CORE_LIB_GPRPP_GLOBAL_CONFIG_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdint.h>
+
+// --------------------------------------------------------------------
+// How to use global configuration variables:
+//
+// Defining config variables of a specified type:
+//   GPR_GLOBAL_CONFIG_DEFINE_*TYPE*(name, default_value, help);
+//
+// Supported TYPEs: BOOL, INT32, STRING
+//
+// It's recommended to use lowercase letters for 'name' like
+// regular variables. The builtin configuration system uses
+// environment variable and the name is converted to uppercase
+// when looking up the value. For example,
+// GPR_GLOBAL_CONFIG_DEFINE(grpc_latency) looks up the value with the
+// name, "GRPC_LATENCY".
+//
+// The variable initially has the specified 'default_value'
+// which must be an expression convertible to 'Type'.
+// 'default_value' may be evaluated 0 or more times,
+// and at an unspecified time; keep it
+// simple and usually free of side-effects.
+//
+// GPR_GLOBAL_CONFIG_DEFINE_*TYPE* should not be called in a C++ header.
+// It should be called at the top-level (outside any namespaces)
+// in a .cc file.
+//
+// Getting the variables:
+//   GPR_GLOBAL_CONFIG_GET(name)
+//
+// If error happens during getting variables, error messages will
+// be logged and default value will be returned.
+//
+// Setting the variables with new value:
+//   GPR_GLOBAL_CONFIG_SET(name, new_value)
+//
+// Declaring config variables for other modules to access:
+//   GPR_GLOBAL_CONFIG_DECLARE_*TYPE*(name)
+
+// --------------------------------------------------------------------
+// How to customize the global configuration system:
+//
+// How to read and write configuration value can be customized.
+// Builtin system uses environment variables but it can be extended to
+// support command-line flag, file, etc.
+//
+// To customize it, following macros should be redefined.
+//
+//   GPR_GLOBAL_CONFIG_DEFINE_BOOL
+//   GPR_GLOBAL_CONFIG_DEFINE_INT32
+//   GPR_GLOBAL_CONFIG_DEFINE_STRING
+//
+// These macros should define functions for getting and setting variable.
+// For example, GPR_GLOBAL_CONFIG_DEFINE_BOOL(test, ...) would define two
+// functions.
+//
+//   bool gpr_global_config_get_test();
+//   void gpr_global_config_set_test(bool value);
+
+#include "src/core/lib/gprpp/global_config_env.h"
+
+#include "src/core/lib/gprpp/global_config_custom.h"
+
+#endif /* GRPC_CORE_LIB_GPRPP_GLOBAL_CONFIG_H */
diff --git a/src/core/lib/gprpp/global_config_custom.h b/src/core/lib/gprpp/global_config_custom.h
new file mode 100644 (file)
index 0000000..dd011fb
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *
+ * 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 GRPC_CORE_LIB_GPRPP_GLOBAL_CONFIG_CUSTOM_H
+#define GRPC_CORE_LIB_GPRPP_GLOBAL_CONFIG_CUSTOM_H
+
+// This is a placeholder for custom global configuration implementaion.
+// To use the custom one, please define following macros here.
+//
+//   GPR_GLOBAL_CONFIG_DEFINE_BOOL
+//   GPR_GLOBAL_CONFIG_DEFINE_INT32
+//   GPR_GLOBAL_CONFIG_DEFINE_STRING
+
+#endif /* GRPC_CORE_LIB_GPRPP_GLOBAL_CONFIG_CUSTOM_H */
diff --git a/src/core/lib/gprpp/global_config_env.cc b/src/core/lib/gprpp/global_config_env.cc
new file mode 100644 (file)
index 0000000..fb14805
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ *
+ * Copyright 2019 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/gprpp/global_config_env.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gpr/string.h"
+
+#include <ctype.h>
+#include <string.h>
+
+namespace grpc_core {
+
+namespace {
+
+void DefaultGlobalConfigEnvErrorFunction(const char* error_message) {
+  gpr_log(GPR_ERROR, "%s", error_message);
+}
+
+GlobalConfigEnvErrorFunctionType g_global_config_env_error_func =
+    DefaultGlobalConfigEnvErrorFunction;
+
+void LogParsingError(const char* name, const char* value) {
+  char* error_message;
+  gpr_asprintf(&error_message,
+               "Illegal value '%s' specified for environment variable '%s'",
+               value, name);
+  (*g_global_config_env_error_func)(error_message);
+  gpr_free(error_message);
+}
+
+}  // namespace
+
+void SetGlobalConfigEnvErrorFunction(GlobalConfigEnvErrorFunctionType func) {
+  g_global_config_env_error_func = func;
+}
+
+UniquePtr<char> GlobalConfigEnv::GetValue() {
+  return UniquePtr<char>(gpr_getenv(GetName()));
+}
+
+void GlobalConfigEnv::SetValue(const char* value) {
+  gpr_setenv(GetName(), value);
+}
+
+void GlobalConfigEnv::Unset() { gpr_unsetenv(GetName()); }
+
+char* GlobalConfigEnv::GetName() {
+  // This makes sure that name_ is in a canonical form having uppercase
+  // letters. This is okay to be called serveral times.
+  for (char* c = name_; *c != 0; ++c) {
+    *c = toupper(*c);
+  }
+  return name_;
+}
+static_assert(std::is_trivially_destructible<GlobalConfigEnvBool>::value,
+              "GlobalConfigEnvBool needs to be trivially destructible.");
+
+bool GlobalConfigEnvBool::Get() {
+  UniquePtr<char> str = GetValue();
+  if (str == nullptr) {
+    return default_value_;
+  }
+  // parsing given value string.
+  bool result = false;
+  if (!gpr_parse_bool_value(str.get(), &result)) {
+    LogParsingError(GetName(), str.get());
+    result = default_value_;
+  }
+  return result;
+}
+
+void GlobalConfigEnvBool::Set(bool value) {
+  SetValue(value ? "true" : "false");
+}
+
+static_assert(std::is_trivially_destructible<GlobalConfigEnvInt32>::value,
+              "GlobalConfigEnvInt32 needs to be trivially destructible.");
+
+int32_t GlobalConfigEnvInt32::Get() {
+  UniquePtr<char> str = GetValue();
+  if (str == nullptr) {
+    return default_value_;
+  }
+  // parsing given value string.
+  char* end = str.get();
+  long result = strtol(str.get(), &end, 10);
+  if (*end != 0) {
+    LogParsingError(GetName(), str.get());
+    result = default_value_;
+  }
+  return static_cast<int32_t>(result);
+}
+
+void GlobalConfigEnvInt32::Set(int32_t value) {
+  char buffer[GPR_LTOA_MIN_BUFSIZE];
+  gpr_ltoa(value, buffer);
+  SetValue(buffer);
+}
+
+static_assert(std::is_trivially_destructible<GlobalConfigEnvString>::value,
+              "GlobalConfigEnvString needs to be trivially destructible.");
+
+UniquePtr<char> GlobalConfigEnvString::Get() {
+  UniquePtr<char> str = GetValue();
+  if (str == nullptr) {
+    return UniquePtr<char>(gpr_strdup(default_value_));
+  }
+  return str;
+}
+
+void GlobalConfigEnvString::Set(const char* value) { SetValue(value); }
+
+}  // namespace grpc_core
diff --git a/src/core/lib/gprpp/global_config_env.h b/src/core/lib/gprpp/global_config_env.h
new file mode 100644 (file)
index 0000000..3d30388
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ *
+ * 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 GRPC_CORE_LIB_GPRPP_GLOBAL_CONFIG_ENV_H
+#define GRPC_CORE_LIB_GPRPP_GLOBAL_CONFIG_ENV_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/gprpp/global_config_generic.h"
+#include "src/core/lib/gprpp/memory.h"
+
+namespace grpc_core {
+
+typedef void (*GlobalConfigEnvErrorFunctionType)(const char* error_message);
+
+/*
+ * Set global_config_env_error_function which is called when config system
+ * encounters errors such as parsing error. What the default function does
+ * is logging error message.
+ */
+void SetGlobalConfigEnvErrorFunction(GlobalConfigEnvErrorFunctionType func);
+
+// Base class for all classes to access environment variables.
+class GlobalConfigEnv {
+ protected:
+  // `name` should be writable and alive after constructor is called.
+  constexpr explicit GlobalConfigEnv(char* name) : name_(name) {}
+
+ public:
+  // Returns the value of `name` variable.
+  UniquePtr<char> GetValue();
+
+  // Sets the value of `name` variable.
+  void SetValue(const char* value);
+
+  // Unsets `name` variable.
+  void Unset();
+
+ protected:
+  char* GetName();
+
+ private:
+  char* name_;
+};
+
+class GlobalConfigEnvBool : public GlobalConfigEnv {
+ public:
+  constexpr GlobalConfigEnvBool(char* name, bool default_value)
+      : GlobalConfigEnv(name), default_value_(default_value) {}
+
+  bool Get();
+  void Set(bool value);
+
+ private:
+  bool default_value_;
+};
+
+class GlobalConfigEnvInt32 : public GlobalConfigEnv {
+ public:
+  constexpr GlobalConfigEnvInt32(char* name, int32_t default_value)
+      : GlobalConfigEnv(name), default_value_(default_value) {}
+
+  int32_t Get();
+  void Set(int32_t value);
+
+ private:
+  int32_t default_value_;
+};
+
+class GlobalConfigEnvString : public GlobalConfigEnv {
+ public:
+  constexpr GlobalConfigEnvString(char* name, const char* default_value)
+      : GlobalConfigEnv(name), default_value_(default_value) {}
+
+  UniquePtr<char> Get();
+  void Set(const char* value);
+
+ private:
+  const char* default_value_;
+};
+
+}  // namespace grpc_core
+
+// Macros for defining global config instances using environment variables.
+// This defines a GlobalConfig*Type* instance with arguments for
+// mutable variable name and default value.
+// Mutable name (g_env_str_##name) is here for having an array
+// for the canonical name without dynamic allocation.
+// `help` argument is ignored for this implementation.
+
+#define GPR_GLOBAL_CONFIG_DEFINE_BOOL(name, default_value, help)         \
+  static char g_env_str_##name[] = #name;                                \
+  static ::grpc_core::GlobalConfigEnvBool g_env_##name(g_env_str_##name, \
+                                                       default_value);   \
+  bool gpr_global_config_get_##name() { return g_env_##name.Get(); }     \
+  void gpr_global_config_set_##name(bool value) { g_env_##name.Set(value); }
+
+#define GPR_GLOBAL_CONFIG_DEFINE_INT32(name, default_value, help)         \
+  static char g_env_str_##name[] = #name;                                 \
+  static ::grpc_core::GlobalConfigEnvInt32 g_env_##name(g_env_str_##name, \
+                                                        default_value);   \
+  int32_t gpr_global_config_get_##name() { return g_env_##name.Get(); }   \
+  void gpr_global_config_set_##name(int32_t value) { g_env_##name.Set(value); }
+
+#define GPR_GLOBAL_CONFIG_DEFINE_STRING(name, default_value, help)         \
+  static char g_env_str_##name[] = #name;                                  \
+  static ::grpc_core::GlobalConfigEnvString g_env_##name(g_env_str_##name, \
+                                                         default_value);   \
+  ::grpc_core::UniquePtr<char> gpr_global_config_get_##name() {            \
+    return g_env_##name.Get();                                             \
+  }                                                                        \
+  void gpr_global_config_set_##name(const char* value) {                   \
+    g_env_##name.Set(value);                                               \
+  }
+
+#endif /* GRPC_CORE_LIB_GPRPP_GLOBAL_CONFIG_ENV_H */
diff --git a/src/core/lib/gprpp/global_config_generic.h b/src/core/lib/gprpp/global_config_generic.h
new file mode 100644 (file)
index 0000000..d3e3e2a
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ *
+ * 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 GRPC_CORE_LIB_GPRPP_GLOBAL_CONFIG_GENERIC_H
+#define GRPC_CORE_LIB_GPRPP_GLOBAL_CONFIG_GENERIC_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/gprpp/memory.h"
+
+#include <stdint.h>
+
+#define GPR_GLOBAL_CONFIG_GET(name) gpr_global_config_get_##name()
+
+#define GPR_GLOBAL_CONFIG_SET(name, value) gpr_global_config_set_##name(value)
+
+#define GPR_GLOBAL_CONFIG_DECLARE_BOOL(name)  \
+  extern bool gpr_global_config_get_##name(); \
+  extern void gpr_global_config_set_##name(bool value)
+
+#define GPR_GLOBAL_CONFIG_DECLARE_INT32(name)    \
+  extern int32_t gpr_global_config_get_##name(); \
+  extern void gpr_global_config_set_##name(int32_t value)
+
+#define GPR_GLOBAL_CONFIG_DECLARE_STRING(name)                      \
+  extern grpc_core::UniquePtr<char> gpr_global_config_get_##name(); \
+  extern void gpr_global_config_set_##name(const char* value)
+
+#endif /* GRPC_CORE_LIB_GPRPP_GLOBAL_CONFIG_GENERIC_H */
diff --git a/src/core/lib/gprpp/map.h b/src/core/lib/gprpp/map.h
new file mode 100644 (file)
index 0000000..b210c26
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ *
+ * Copyright 2017 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 GRPC_CORE_LIB_GPRPP_MAP_H
+#define GRPC_CORE_LIB_GPRPP_MAP_H
+
+#include <grpc/support/port_platform.h>
+
+#include <string.h>
+#include <functional>
+#include <iterator>
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/pair.h"
+
+namespace grpc_core {
+struct StringLess {
+  bool operator()(const char* a, const char* b) const {
+    return strcmp(a, b) < 0;
+  }
+  bool operator()(const UniquePtr<char>& k1, const UniquePtr<char>& k2) {
+    return strcmp(k1.get(), k2.get()) < 0;
+  }
+};
+
+namespace testing {
+class MapTest;
+}
+
+// Alternative map implementation for grpc_core
+template <class Key, class T, class Compare = std::less<Key>>
+class Map {
+ public:
+  typedef Key key_type;
+  typedef T mapped_type;
+  typedef Pair<key_type, mapped_type> value_type;
+  typedef Compare key_compare;
+  class iterator;
+
+  ~Map() { clear(); }
+
+  T& operator[](key_type&& key);
+  T& operator[](const key_type& key);
+  iterator find(const key_type& k);
+  size_t erase(const key_type& key);
+  // Removes the current entry and points to the next one
+  iterator erase(iterator iter);
+
+  size_t size() { return size_; }
+
+  template <class... Args>
+  Pair<iterator, bool> emplace(Args&&... args);
+
+  Pair<iterator, bool> insert(value_type&& pair) {
+    return emplace(std::move(pair));
+  }
+
+  Pair<iterator, bool> insert(const value_type& pair) { return emplace(pair); }
+
+  bool empty() const { return root_ == nullptr; }
+
+  void clear() {
+    auto iter = begin();
+    while (!empty()) {
+      iter = erase(iter);
+    }
+  }
+
+  iterator begin() {
+    Entry* curr = GetMinEntry(root_);
+    return iterator(this, curr);
+  }
+
+  iterator end() { return iterator(this, nullptr); }
+
+ private:
+  friend class testing::MapTest;
+  struct Entry {
+    explicit Entry(value_type&& pair) : pair(std::move(pair)) {}
+    value_type pair;
+    Entry* left = nullptr;
+    Entry* right = nullptr;
+    int32_t height = 1;
+  };
+
+  static int32_t EntryHeight(const Entry* e) {
+    return e == nullptr ? 0 : e->height;
+  }
+
+  static Entry* GetMinEntry(Entry* e);
+  Entry* InOrderSuccessor(const Entry* e) const;
+  static Entry* RotateLeft(Entry* e);
+  static Entry* RotateRight(Entry* e);
+  static Entry* RebalanceTreeAfterInsertion(Entry* root, const key_type& k);
+  static Entry* RebalanceTreeAfterDeletion(Entry* root);
+  // Returns a pair with the first value being an iterator pointing to the
+  // inserted entry and the second value being the new root of the subtree
+  // after a rebalance
+  Pair<iterator, Entry*> InsertRecursive(Entry* root, value_type&& p);
+  static Entry* RemoveRecursive(Entry* root, const key_type& k);
+  // Return 0 if lhs = rhs
+  //        1 if lhs > rhs
+  //       -1 if lhs < rhs
+  static int CompareKeys(const Key& lhs, const Key& rhs);
+
+  Entry* root_ = nullptr;
+  size_t size_ = 0;
+};
+
+template <class Key, class T, class Compare>
+class Map<Key, T, Compare>::iterator
+    : public std::iterator<std::input_iterator_tag, Pair<Key, T>, int32_t,
+                           Pair<Key, T>*, Pair<Key, T>&> {
+ public:
+  iterator(const iterator& iter) : curr_(iter.curr_), map_(iter.map_) {}
+  bool operator==(const iterator& rhs) const { return (curr_ == rhs.curr_); }
+  bool operator!=(const iterator& rhs) const { return (curr_ != rhs.curr_); }
+
+  iterator& operator++() {
+    curr_ = map_->InOrderSuccessor(curr_);
+    return *this;
+  }
+
+  iterator operator++(int) {
+    Entry* prev = curr_;
+    curr_ = map_->InOrderSuccessor(curr_);
+    return iterator(map_, prev);
+  }
+
+  iterator& operator=(const iterator& other) {
+    if (this != &other) {
+      this->curr_ = other.curr_;
+      this->map_ = other.map_;
+    }
+    return *this;
+  }
+
+  // operator*()
+  value_type& operator*() { return curr_->pair; }
+  const value_type& operator*() const { return curr_->pair; }
+
+  // operator->()
+  value_type* operator->() { return &curr_->pair; }
+  value_type const* operator->() const { return &curr_->pair; }
+
+ private:
+  friend class Map<key_type, mapped_type, key_compare>;
+  using GrpcMap = typename ::grpc_core::Map<Key, T, Compare>;
+  iterator(GrpcMap* map, Entry* curr) : curr_(curr), map_(map) {}
+  Entry* curr_;
+  GrpcMap* map_;
+};
+
+template <class Key, class T, class Compare>
+T& Map<Key, T, Compare>::operator[](key_type&& key) {
+  auto iter = find(key);
+  if (iter == end()) {
+    return emplace(std::move(key), T()).first->second;
+  }
+  return iter->second;
+}
+
+template <class Key, class T, class Compare>
+T& Map<Key, T, Compare>::operator[](const key_type& key) {
+  auto iter = find(key);
+  if (iter == end()) {
+    return emplace(key, T()).first->second;
+  }
+  return iter->second;
+}
+
+template <class Key, class T, class Compare>
+typename Map<Key, T, Compare>::iterator Map<Key, T, Compare>::find(
+    const key_type& k) {
+  Entry* iter = root_;
+  while (iter != nullptr) {
+    int comp = CompareKeys(iter->pair.first, k);
+    if (comp == 0) {
+      return iterator(this, iter);
+    } else if (comp < 0) {
+      iter = iter->right;
+    } else {
+      iter = iter->left;
+    }
+  }
+  return end();
+}
+
+template <class Key, class T, class Compare>
+template <class... Args>
+typename ::grpc_core::Pair<typename Map<Key, T, Compare>::iterator, bool>
+Map<Key, T, Compare>::emplace(Args&&... args) {
+  Pair<key_type, mapped_type> pair(std::forward<Args>(args)...);
+  iterator ret = find(pair.first);
+  bool insertion = false;
+  if (ret == end()) {
+    Pair<iterator, Entry*> p = InsertRecursive(root_, std::move(pair));
+    root_ = p.second;
+    ret = p.first;
+    insertion = true;
+    size_++;
+  }
+  return MakePair(ret, insertion);
+}
+
+template <class Key, class T, class Compare>
+size_t Map<Key, T, Compare>::erase(const key_type& key) {
+  iterator it = find(key);
+  if (it == end()) return 0;
+  erase(it);
+  return 1;
+}
+
+// TODO(mhaidry): Modify erase to use the iterator location
+// to create an efficient erase method
+template <class Key, class T, class Compare>
+typename Map<Key, T, Compare>::iterator Map<Key, T, Compare>::erase(
+    iterator iter) {
+  if (iter == end()) return iter;
+  key_type& del_key = iter->first;
+  iter++;
+  root_ = RemoveRecursive(root_, del_key);
+  size_--;
+  return iter;
+}
+
+template <class Key, class T, class Compare>
+typename Map<Key, T, Compare>::Entry* Map<Key, T, Compare>::InOrderSuccessor(
+    const Entry* e) const {
+  if (e->right != nullptr) {
+    return GetMinEntry(e->right);
+  }
+  Entry* successor = nullptr;
+  Entry* iter = root_;
+  while (iter != nullptr) {
+    int comp = CompareKeys(iter->pair.first, e->pair.first);
+    if (comp > 0) {
+      successor = iter;
+      iter = iter->left;
+    } else if (comp < 0) {
+      iter = iter->right;
+    } else
+      break;
+  }
+  return successor;
+}
+
+// Returns a pair with the first value being an iterator pointing to the
+// inserted entry and the second value being the new root of the subtree
+// after a rebalance
+template <class Key, class T, class Compare>
+typename ::grpc_core::Pair<typename Map<Key, T, Compare>::iterator,
+                           typename Map<Key, T, Compare>::Entry*>
+Map<Key, T, Compare>::InsertRecursive(Entry* root, value_type&& p) {
+  if (root == nullptr) {
+    Entry* e = New<Entry>(std::move(p));
+    return MakePair(iterator(this, e), e);
+  }
+  int comp = CompareKeys(root->pair.first, p.first);
+  if (comp > 0) {
+    Pair<iterator, Entry*> ret = InsertRecursive(root->left, std::move(p));
+    root->left = ret.second;
+    ret.second = RebalanceTreeAfterInsertion(root, ret.first->first);
+    return ret;
+  } else if (comp < 0) {
+    Pair<iterator, Entry*> ret = InsertRecursive(root->right, std::move(p));
+    root->right = ret.second;
+    ret.second = RebalanceTreeAfterInsertion(root, ret.first->first);
+    return ret;
+  } else {
+    root->pair = std::move(p);
+    return MakePair(iterator(this, root), root);
+  }
+}
+
+template <class Key, class T, class Compare>
+typename Map<Key, T, Compare>::Entry* Map<Key, T, Compare>::GetMinEntry(
+    Entry* e) {
+  if (e != nullptr) {
+    while (e->left != nullptr) {
+      e = e->left;
+    }
+  }
+  return e;
+}
+
+template <class Key, class T, class Compare>
+typename Map<Key, T, Compare>::Entry* Map<Key, T, Compare>::RotateLeft(
+    Entry* e) {
+  Entry* rightChild = e->right;
+  Entry* rightLeftChild = rightChild->left;
+  rightChild->left = e;
+  e->right = rightLeftChild;
+  e->height = 1 + GPR_MAX(EntryHeight(e->left), EntryHeight(e->right));
+  rightChild->height = 1 + GPR_MAX(EntryHeight(rightChild->left),
+                                   EntryHeight(rightChild->right));
+  return rightChild;
+}
+
+template <class Key, class T, class Compare>
+typename Map<Key, T, Compare>::Entry* Map<Key, T, Compare>::RotateRight(
+    Entry* e) {
+  Entry* leftChild = e->left;
+  Entry* leftRightChild = leftChild->right;
+  leftChild->right = e;
+  e->left = leftRightChild;
+  e->height = 1 + GPR_MAX(EntryHeight(e->left), EntryHeight(e->right));
+  leftChild->height =
+      1 + GPR_MAX(EntryHeight(leftChild->left), EntryHeight(leftChild->right));
+  return leftChild;
+}
+
+template <class Key, class T, class Compare>
+typename Map<Key, T, Compare>::Entry*
+Map<Key, T, Compare>::RebalanceTreeAfterInsertion(Entry* root,
+                                                  const key_type& k) {
+  root->height = 1 + GPR_MAX(EntryHeight(root->left), EntryHeight(root->right));
+  int32_t heightDifference = EntryHeight(root->left) - EntryHeight(root->right);
+  if (heightDifference > 1 && CompareKeys(root->left->pair.first, k) > 0) {
+    return RotateRight(root);
+  }
+  if (heightDifference < -1 && CompareKeys(root->right->pair.first, k) < 0) {
+    return RotateLeft(root);
+  }
+  if (heightDifference > 1 && CompareKeys(root->left->pair.first, k) < 0) {
+    root->left = RotateLeft(root->left);
+    return RotateRight(root);
+  }
+  if (heightDifference < -1 && CompareKeys(root->right->pair.first, k) > 0) {
+    root->right = RotateRight(root->right);
+    return RotateLeft(root);
+  }
+  return root;
+}
+
+template <class Key, class T, class Compare>
+typename Map<Key, T, Compare>::Entry*
+Map<Key, T, Compare>::RebalanceTreeAfterDeletion(Entry* root) {
+  root->height = 1 + GPR_MAX(EntryHeight(root->left), EntryHeight(root->right));
+  int32_t heightDifference = EntryHeight(root->left) - EntryHeight(root->right);
+  if (heightDifference > 1) {
+    int leftHeightDifference =
+        EntryHeight(root->left->left) - EntryHeight(root->left->right);
+    if (leftHeightDifference < 0) {
+      root->left = RotateLeft(root->left);
+    }
+    return RotateRight(root);
+  }
+  if (heightDifference < -1) {
+    int rightHeightDifference =
+        EntryHeight(root->right->left) - EntryHeight(root->right->right);
+    if (rightHeightDifference > 0) {
+      root->right = RotateRight(root->right);
+    }
+    return RotateLeft(root);
+  }
+  return root;
+}
+
+template <class Key, class T, class Compare>
+typename Map<Key, T, Compare>::Entry* Map<Key, T, Compare>::RemoveRecursive(
+    Entry* root, const key_type& k) {
+  if (root == nullptr) return root;
+  int comp = CompareKeys(root->pair.first, k);
+  if (comp > 0) {
+    root->left = RemoveRecursive(root->left, k);
+  } else if (comp < 0) {
+    root->right = RemoveRecursive(root->right, k);
+  } else {
+    Entry* ret;
+    if (root->left == nullptr) {
+      ret = root->right;
+      Delete(root);
+      return ret;
+    } else if (root->right == nullptr) {
+      ret = root->left;
+      Delete(root);
+      return ret;
+    } else {
+      ret = root->right;
+      while (ret->left != nullptr) {
+        ret = ret->left;
+      }
+      root->pair.swap(ret->pair);
+      root->right = RemoveRecursive(root->right, ret->pair.first);
+    }
+  }
+  return RebalanceTreeAfterDeletion(root);
+}
+
+template <class Key, class T, class Compare>
+int Map<Key, T, Compare>::CompareKeys(const key_type& lhs,
+                                      const key_type& rhs) {
+  key_compare compare;
+  bool left_comparison = compare(lhs, rhs);
+  bool right_comparison = compare(rhs, lhs);
+  // Both values are equal
+  if (!left_comparison && !right_comparison) {
+    return 0;
+  }
+  return left_comparison ? -1 : 1;
+}
+}  // namespace grpc_core
+#endif /* GRPC_CORE_LIB_GPRPP_MAP_H */
index a8e3ce1..ab5f863 100644 (file)
@@ -26,6 +26,7 @@ namespace grpc_core {
 template <typename T>
 class Optional {
  public:
+  Optional() : value_() {}
   void set(const T& val) {
     value_ = val;
     set_ = true;
index dda5026..2e467e4 100644 (file)
@@ -110,12 +110,12 @@ class InternallyRefCounted : public Orphanable {
   }
 
   void Unref() {
-    if (refs_.Unref()) {
+    if (GPR_UNLIKELY(refs_.Unref())) {
       Delete(static_cast<Child*>(this));
     }
   }
   void Unref(const DebugLocation& location, const char* reason) {
-    if (refs_.Unref(location, reason)) {
+    if (GPR_UNLIKELY(refs_.Unref(location, reason))) {
       Delete(static_cast<Child*>(this));
     }
   }
similarity index 56%
rename from src/core/lib/gprpp/mutex_lock.h
rename to src/core/lib/gprpp/pair.h
index 54751d5..ca8294c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2018 gRPC authors.
+ * Copyright 2017 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  *
  */
 
-#ifndef GRPC_CORE_LIB_GPRPP_MUTEX_LOCK_H
-#define GRPC_CORE_LIB_GPRPP_MUTEX_LOCK_H
+#ifndef GRPC_CORE_LIB_GPRPP_PAIR_H
+#define GRPC_CORE_LIB_GPRPP_PAIR_H
 
 #include <grpc/support/port_platform.h>
 
-#include <grpc/support/sync.h>
+#include <utility>
 
 namespace grpc_core {
-
-class MutexLock {
- public:
-  explicit MutexLock(gpr_mu* mu) : mu_(mu) { gpr_mu_lock(mu); }
-  ~MutexLock() { gpr_mu_unlock(mu_); }
-
-  MutexLock(const MutexLock&) = delete;
-  MutexLock& operator=(const MutexLock&) = delete;
-
- private:
-  gpr_mu* const mu_;
-};
-
+template <class T1, class T2>
+using Pair = std::pair<T1, T2>;
+
+template <class T1, class T2>
+inline Pair<typename std::decay<T1>::type, typename std::decay<T2>::type>
+MakePair(T1&& u, T2&& v) {
+  typedef typename std::decay<T1>::type V1;
+  typedef typename std::decay<T2>::type V2;
+  return Pair<V1, V2>(std::forward<T1>(u), std::forward<T2>(v));
+}
 }  // namespace grpc_core
-
-#endif /* GRPC_CORE_LIB_GPRPP_MUTEX_LOCK_H */
+#endif /* GRPC_CORE_LIB_GPRPP_PAIR_H */
index 4937f31..83e9429 100644 (file)
@@ -123,6 +123,22 @@ class RefCount {
     RefNonZero();
   }
 
+  bool RefIfNonZero() { return value_.IncrementIfNonzero(); }
+
+  bool RefIfNonZero(const DebugLocation& location, const char* reason) {
+#ifndef NDEBUG
+    if (location.Log() && trace_flag_ != nullptr && trace_flag_->enabled()) {
+      const RefCount::Value old_refs = get();
+      gpr_log(GPR_INFO,
+              "%s:%p %s:%d ref_if_non_zero "
+              "%" PRIdPTR " -> %" PRIdPTR " %s",
+              trace_flag_->name(), this, location.file(), location.line(),
+              old_refs, old_refs + 1, reason);
+    }
+#endif
+    return RefIfNonZero();
+  }
+
   // Decrements the ref-count and returns true if the ref-count reaches 0.
   bool Unref() {
     const Value prior = value_.FetchSub(1, MemoryOrder::ACQ_REL);
@@ -195,12 +211,12 @@ class RefCounted : public Impl {
   // private, since it will only be used by RefCountedPtr<>, which is a
   // friend of this class.
   void Unref() {
-    if (refs_.Unref()) {
+    if (GPR_UNLIKELY(refs_.Unref())) {
       Delete(static_cast<Child*>(this));
     }
   }
   void Unref(const DebugLocation& location, const char* reason) {
-    if (refs_.Unref(location, reason)) {
+    if (GPR_UNLIKELY(refs_.Unref(location, reason))) {
       Delete(static_cast<Child*>(this));
     }
   }
diff --git a/src/core/lib/gprpp/sync.h b/src/core/lib/gprpp/sync.h
new file mode 100644 (file)
index 0000000..895ca60
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ *
+ * 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 GRPC_CORE_LIB_GPRPP_SYNC_H
+#define GRPC_CORE_LIB_GPRPP_SYNC_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <grpc/impl/codegen/log.h>
+#include <grpc/impl/codegen/sync.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+// The core library is not accessible in C++ codegen headers, and vice versa.
+// Thus, we need to have duplicate headers with similar functionality.
+// Make sure any change to this file is also reflected in
+// include/grpcpp/impl/codegen/sync.h.
+//
+// Whenever possible, prefer using this file over <grpcpp/impl/codegen/sync.h>
+// since this file doesn't rely on g_core_codegen_interface and hence does not
+// pay the costs of virtual function calls.
+
+namespace grpc_core {
+
+class Mutex {
+ public:
+  Mutex() { gpr_mu_init(&mu_); }
+  ~Mutex() { gpr_mu_destroy(&mu_); }
+
+  Mutex(const Mutex&) = delete;
+  Mutex& operator=(const Mutex&) = delete;
+
+  gpr_mu* get() { return &mu_; }
+  const gpr_mu* get() const { return &mu_; }
+
+ private:
+  gpr_mu mu_;
+};
+
+// MutexLock is a std::
+class MutexLock {
+ public:
+  explicit MutexLock(Mutex* mu) : mu_(mu->get()) { gpr_mu_lock(mu_); }
+  explicit MutexLock(gpr_mu* mu) : mu_(mu) { gpr_mu_lock(mu_); }
+  ~MutexLock() { gpr_mu_unlock(mu_); }
+
+  MutexLock(const MutexLock&) = delete;
+  MutexLock& operator=(const MutexLock&) = delete;
+
+ private:
+  gpr_mu* const mu_;
+};
+
+class ReleasableMutexLock {
+ public:
+  explicit ReleasableMutexLock(Mutex* mu) : mu_(mu->get()) { gpr_mu_lock(mu_); }
+  explicit ReleasableMutexLock(gpr_mu* mu) : mu_(mu) { gpr_mu_lock(mu_); }
+  ~ReleasableMutexLock() {
+    if (!released_) gpr_mu_unlock(mu_);
+  }
+
+  ReleasableMutexLock(const ReleasableMutexLock&) = delete;
+  ReleasableMutexLock& operator=(const ReleasableMutexLock&) = delete;
+
+  void Lock() {
+    GPR_DEBUG_ASSERT(released_);
+    gpr_mu_lock(mu_);
+    released_ = false;
+  }
+
+  void Unlock() {
+    GPR_DEBUG_ASSERT(!released_);
+    released_ = true;
+    gpr_mu_unlock(mu_);
+  }
+
+ private:
+  gpr_mu* const mu_;
+  bool released_ = false;
+};
+
+class CondVar {
+ public:
+  CondVar() { gpr_cv_init(&cv_); }
+  ~CondVar() { gpr_cv_destroy(&cv_); }
+
+  CondVar(const CondVar&) = delete;
+  CondVar& operator=(const CondVar&) = delete;
+
+  void Signal() { gpr_cv_signal(&cv_); }
+  void Broadcast() { gpr_cv_broadcast(&cv_); }
+
+  int Wait(Mutex* mu) { return Wait(mu, gpr_inf_future(GPR_CLOCK_REALTIME)); }
+  int Wait(Mutex* mu, const gpr_timespec& deadline) {
+    return gpr_cv_wait(&cv_, mu->get(), deadline);
+  }
+
+  template <typename Predicate>
+  void WaitUntil(Mutex* mu, Predicate pred) {
+    while (!pred()) {
+      Wait(mu, gpr_inf_future(GPR_CLOCK_REALTIME));
+    }
+  }
+
+ private:
+  gpr_cv cv_;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_GPRPP_SYNC_H */
index 7ca1cc9..58608da 100644 (file)
@@ -300,7 +300,7 @@ static grpc_error* addbyte(grpc_http_parser* parser, uint8_t byte,
     case GRPC_HTTP_FIRST_LINE:
     case GRPC_HTTP_HEADERS:
       if (parser->cur_line_length >= GRPC_HTTP_PARSER_MAX_HEADER_LENGTH) {
-        if (grpc_http1_trace.enabled())
+        if (GRPC_TRACE_FLAG_ENABLED(grpc_http1_trace))
           gpr_log(GPR_ERROR, "HTTP header max line length (%d) exceeded",
                   GRPC_HTTP_PARSER_MAX_HEADER_LENGTH);
         return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
index 6b5759a..6a4e85d 100644 (file)
 #include "src/core/lib/debug/stats.h"
 #include "src/core/lib/profiling/timers.h"
 
-grpc_core::TraceFlag grpc_call_combiner_trace(false, "call_combiner");
+namespace grpc_core {
 
-static grpc_error* decode_cancel_state_error(gpr_atm cancel_state) {
+TraceFlag grpc_call_combiner_trace(false, "call_combiner");
+
+namespace {
+
+grpc_error* DecodeCancelStateError(gpr_atm cancel_state) {
   if (cancel_state & 1) {
     return (grpc_error*)(cancel_state & ~static_cast<gpr_atm>(1));
   }
   return GRPC_ERROR_NONE;
 }
 
-static gpr_atm encode_cancel_state_error(grpc_error* error) {
+gpr_atm EncodeCancelStateError(grpc_error* error) {
   return static_cast<gpr_atm>(1) | (gpr_atm)error;
 }
 
+}  // namespace
+
+CallCombiner::CallCombiner() {
+  gpr_atm_no_barrier_store(&cancel_state_, 0);
+  gpr_atm_no_barrier_store(&size_, 0);
+  gpr_mpscq_init(&queue_);
+#ifdef GRPC_TSAN_ENABLED
+  GRPC_CLOSURE_INIT(&tsan_closure_, TsanClosure, this,
+                    grpc_schedule_on_exec_ctx);
+#endif
+}
+
+CallCombiner::~CallCombiner() {
+  gpr_mpscq_destroy(&queue_);
+  GRPC_ERROR_UNREF(DecodeCancelStateError(cancel_state_));
+}
+
 #ifdef GRPC_TSAN_ENABLED
-static void tsan_closure(void* user_data, grpc_error* error) {
-  grpc_call_combiner* call_combiner =
-      static_cast<grpc_call_combiner*>(user_data);
+void CallCombiner::TsanClosure(void* arg, grpc_error* error) {
+  CallCombiner* self = static_cast<CallCombiner*>(arg);
   // We ref-count the lock, and check if it's already taken.
   // If it was taken, we should do nothing. Otherwise, we will mark it as
   // locked. Note that if two different threads try to do this, only one of
@@ -51,18 +71,18 @@ static void tsan_closure(void* user_data, grpc_error* error) {
   // TSAN will correctly produce an error.
   //
   // TODO(soheil): This only covers the callbacks scheduled by
-  //               grpc_call_combiner_(start|finish). If in the future, a
-  //               callback gets scheduled using other mechanisms, we will need
-  //               to add APIs to externally lock call combiners.
-  grpc_core::RefCountedPtr<grpc_call_combiner::TsanLock> lock =
-      call_combiner->tsan_lock;
+  //               CallCombiner::Start() and CallCombiner::Stop().
+  //               If in the future, a callback gets scheduled using other
+  //               mechanisms, we will need to add APIs to externally lock
+  //               call combiners.
+  RefCountedPtr<TsanLock> lock = self->tsan_lock_;
   bool prev = false;
   if (lock->taken.compare_exchange_strong(prev, true)) {
     TSAN_ANNOTATE_RWLOCK_ACQUIRED(&lock->taken, true);
   } else {
     lock.reset();
   }
-  GRPC_CLOSURE_RUN(call_combiner->original_closure, GRPC_ERROR_REF(error));
+  GRPC_CLOSURE_RUN(self->original_closure_, GRPC_ERROR_REF(error));
   if (lock != nullptr) {
     TSAN_ANNOTATE_RWLOCK_RELEASED(&lock->taken, true);
     bool prev = true;
@@ -71,34 +91,17 @@ static void tsan_closure(void* user_data, grpc_error* error) {
 }
 #endif
 
-static void call_combiner_sched_closure(grpc_call_combiner* call_combiner,
-                                        grpc_closure* closure,
-                                        grpc_error* error) {
+void CallCombiner::ScheduleClosure(grpc_closure* closure, grpc_error* error) {
 #ifdef GRPC_TSAN_ENABLED
-  call_combiner->original_closure = closure;
-  GRPC_CLOSURE_SCHED(&call_combiner->tsan_closure, error);
+  original_closure_ = closure;
+  GRPC_CLOSURE_SCHED(&tsan_closure_, error);
 #else
   GRPC_CLOSURE_SCHED(closure, error);
 #endif
 }
 
-void grpc_call_combiner_init(grpc_call_combiner* call_combiner) {
-  gpr_atm_no_barrier_store(&call_combiner->cancel_state, 0);
-  gpr_atm_no_barrier_store(&call_combiner->size, 0);
-  gpr_mpscq_init(&call_combiner->queue);
-#ifdef GRPC_TSAN_ENABLED
-  GRPC_CLOSURE_INIT(&call_combiner->tsan_closure, tsan_closure, call_combiner,
-                    grpc_schedule_on_exec_ctx);
-#endif
-}
-
-void grpc_call_combiner_destroy(grpc_call_combiner* call_combiner) {
-  gpr_mpscq_destroy(&call_combiner->queue);
-  GRPC_ERROR_UNREF(decode_cancel_state_error(call_combiner->cancel_state));
-}
-
 #ifndef NDEBUG
-#define DEBUG_ARGS , const char *file, int line
+#define DEBUG_ARGS const char *file, int line,
 #define DEBUG_FMT_STR "%s:%d: "
 #define DEBUG_FMT_ARGS , file, line
 #else
@@ -107,123 +110,113 @@ void grpc_call_combiner_destroy(grpc_call_combiner* call_combiner) {
 #define DEBUG_FMT_ARGS
 #endif
 
-void grpc_call_combiner_start(grpc_call_combiner* call_combiner,
-                              grpc_closure* closure,
-                              grpc_error* error DEBUG_ARGS,
-                              const char* reason) {
-  GPR_TIMER_SCOPE("call_combiner_start", 0);
-  if (grpc_call_combiner_trace.enabled()) {
+void CallCombiner::Start(grpc_closure* closure, grpc_error* error,
+                         DEBUG_ARGS const char* reason) {
+  GPR_TIMER_SCOPE("CallCombiner::Start", 0);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_call_combiner_trace)) {
     gpr_log(GPR_INFO,
-            "==> grpc_call_combiner_start() [%p] closure=%p [" DEBUG_FMT_STR
+            "==> CallCombiner::Start() [%p] closure=%p [" DEBUG_FMT_STR
             "%s] error=%s",
-            call_combiner, closure DEBUG_FMT_ARGS, reason,
-            grpc_error_string(error));
+            this, closure DEBUG_FMT_ARGS, reason, grpc_error_string(error));
   }
-  size_t prev_size = static_cast<size_t>(
-      gpr_atm_full_fetch_add(&call_combiner->size, (gpr_atm)1));
-  if (grpc_call_combiner_trace.enabled()) {
+  size_t prev_size =
+      static_cast<size_t>(gpr_atm_full_fetch_add(&size_, (gpr_atm)1));
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_call_combiner_trace)) {
     gpr_log(GPR_INFO, "  size: %" PRIdPTR " -> %" PRIdPTR, prev_size,
             prev_size + 1);
   }
   GRPC_STATS_INC_CALL_COMBINER_LOCKS_SCHEDULED_ITEMS();
   if (prev_size == 0) {
     GRPC_STATS_INC_CALL_COMBINER_LOCKS_INITIATED();
-
     GPR_TIMER_MARK("call_combiner_initiate", 0);
-    if (grpc_call_combiner_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_call_combiner_trace)) {
       gpr_log(GPR_INFO, "  EXECUTING IMMEDIATELY");
     }
     // Queue was empty, so execute this closure immediately.
-    call_combiner_sched_closure(call_combiner, closure, error);
+    ScheduleClosure(closure, error);
   } else {
-    if (grpc_call_combiner_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_call_combiner_trace)) {
       gpr_log(GPR_INFO, "  QUEUING");
     }
     // Queue was not empty, so add closure to queue.
     closure->error_data.error = error;
-    gpr_mpscq_push(&call_combiner->queue,
-                   reinterpret_cast<gpr_mpscq_node*>(closure));
+    gpr_mpscq_push(&queue_, reinterpret_cast<gpr_mpscq_node*>(closure));
   }
 }
 
-void grpc_call_combiner_stop(grpc_call_combiner* call_combiner DEBUG_ARGS,
-                             const char* reason) {
-  GPR_TIMER_SCOPE("call_combiner_stop", 0);
-  if (grpc_call_combiner_trace.enabled()) {
-    gpr_log(GPR_INFO,
-            "==> grpc_call_combiner_stop() [%p] [" DEBUG_FMT_STR "%s]",
-            call_combiner DEBUG_FMT_ARGS, reason);
+void CallCombiner::Stop(DEBUG_ARGS const char* reason) {
+  GPR_TIMER_SCOPE("CallCombiner::Stop", 0);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_call_combiner_trace)) {
+    gpr_log(GPR_INFO, "==> CallCombiner::Stop() [%p] [" DEBUG_FMT_STR "%s]",
+            this DEBUG_FMT_ARGS, reason);
   }
-  size_t prev_size = static_cast<size_t>(
-      gpr_atm_full_fetch_add(&call_combiner->size, (gpr_atm)-1));
-  if (grpc_call_combiner_trace.enabled()) {
+  size_t prev_size =
+      static_cast<size_t>(gpr_atm_full_fetch_add(&size_, (gpr_atm)-1));
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_call_combiner_trace)) {
     gpr_log(GPR_INFO, "  size: %" PRIdPTR " -> %" PRIdPTR, prev_size,
             prev_size - 1);
   }
   GPR_ASSERT(prev_size >= 1);
   if (prev_size > 1) {
     while (true) {
-      if (grpc_call_combiner_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_call_combiner_trace)) {
         gpr_log(GPR_INFO, "  checking queue");
       }
       bool empty;
       grpc_closure* closure = reinterpret_cast<grpc_closure*>(
-          gpr_mpscq_pop_and_check_end(&call_combiner->queue, &empty));
+          gpr_mpscq_pop_and_check_end(&queue_, &empty));
       if (closure == nullptr) {
         // This can happen either due to a race condition within the mpscq
-        // code or because of a race with grpc_call_combiner_start().
-        if (grpc_call_combiner_trace.enabled()) {
+        // code or because of a race with Start().
+        if (GRPC_TRACE_FLAG_ENABLED(grpc_call_combiner_trace)) {
           gpr_log(GPR_INFO, "  queue returned no result; checking again");
         }
         continue;
       }
-      if (grpc_call_combiner_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_call_combiner_trace)) {
         gpr_log(GPR_INFO, "  EXECUTING FROM QUEUE: closure=%p error=%s",
                 closure, grpc_error_string(closure->error_data.error));
       }
-      call_combiner_sched_closure(call_combiner, closure,
-                                  closure->error_data.error);
+      ScheduleClosure(closure, closure->error_data.error);
       break;
     }
-  } else if (grpc_call_combiner_trace.enabled()) {
+  } else if (GRPC_TRACE_FLAG_ENABLED(grpc_call_combiner_trace)) {
     gpr_log(GPR_INFO, "  queue empty");
   }
 }
 
-void grpc_call_combiner_set_notify_on_cancel(grpc_call_combiner* call_combiner,
-                                             grpc_closure* closure) {
+void CallCombiner::SetNotifyOnCancel(grpc_closure* closure) {
   GRPC_STATS_INC_CALL_COMBINER_SET_NOTIFY_ON_CANCEL();
   while (true) {
     // Decode original state.
-    gpr_atm original_state = gpr_atm_acq_load(&call_combiner->cancel_state);
-    grpc_error* original_error = decode_cancel_state_error(original_state);
+    gpr_atm original_state = gpr_atm_acq_load(&cancel_state_);
+    grpc_error* original_error = DecodeCancelStateError(original_state);
     // If error is set, invoke the cancellation closure immediately.
     // Otherwise, store the new closure.
     if (original_error != GRPC_ERROR_NONE) {
-      if (grpc_call_combiner_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_call_combiner_trace)) {
         gpr_log(GPR_INFO,
                 "call_combiner=%p: scheduling notify_on_cancel callback=%p "
                 "for pre-existing cancellation",
-                call_combiner, closure);
+                this, closure);
       }
       GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_REF(original_error));
       break;
     } else {
-      if (gpr_atm_full_cas(&call_combiner->cancel_state, original_state,
-                           (gpr_atm)closure)) {
-        if (grpc_call_combiner_trace.enabled()) {
+      if (gpr_atm_full_cas(&cancel_state_, original_state, (gpr_atm)closure)) {
+        if (GRPC_TRACE_FLAG_ENABLED(grpc_call_combiner_trace)) {
           gpr_log(GPR_INFO, "call_combiner=%p: setting notify_on_cancel=%p",
-                  call_combiner, closure);
+                  this, closure);
         }
         // If we replaced an earlier closure, invoke the original
         // closure with GRPC_ERROR_NONE.  This allows callers to clean
         // up any resources they may be holding for the callback.
         if (original_state != 0) {
           closure = (grpc_closure*)original_state;
-          if (grpc_call_combiner_trace.enabled()) {
+          if (GRPC_TRACE_FLAG_ENABLED(grpc_call_combiner_trace)) {
             gpr_log(GPR_INFO,
-                    "call_combiner=%p: scheduling old cancel callback=%p",
-                    call_combiner, closure);
+                    "call_combiner=%p: scheduling old cancel callback=%p", this,
+                    closure);
           }
           GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE);
         }
@@ -234,24 +227,23 @@ void grpc_call_combiner_set_notify_on_cancel(grpc_call_combiner* call_combiner,
   }
 }
 
-void grpc_call_combiner_cancel(grpc_call_combiner* call_combiner,
-                               grpc_error* error) {
+void CallCombiner::Cancel(grpc_error* error) {
   GRPC_STATS_INC_CALL_COMBINER_CANCELLED();
   while (true) {
-    gpr_atm original_state = gpr_atm_acq_load(&call_combiner->cancel_state);
-    grpc_error* original_error = decode_cancel_state_error(original_state);
+    gpr_atm original_state = gpr_atm_acq_load(&cancel_state_);
+    grpc_error* original_error = DecodeCancelStateError(original_state);
     if (original_error != GRPC_ERROR_NONE) {
       GRPC_ERROR_UNREF(error);
       break;
     }
-    if (gpr_atm_full_cas(&call_combiner->cancel_state, original_state,
-                         encode_cancel_state_error(error))) {
+    if (gpr_atm_full_cas(&cancel_state_, original_state,
+                         EncodeCancelStateError(error))) {
       if (original_state != 0) {
         grpc_closure* notify_on_cancel = (grpc_closure*)original_state;
-        if (grpc_call_combiner_trace.enabled()) {
+        if (GRPC_TRACE_FLAG_ENABLED(grpc_call_combiner_trace)) {
           gpr_log(GPR_INFO,
                   "call_combiner=%p: scheduling notify_on_cancel callback=%p",
-                  call_combiner, notify_on_cancel);
+                  this, notify_on_cancel);
         }
         GRPC_CLOSURE_SCHED(notify_on_cancel, GRPC_ERROR_REF(error));
       }
@@ -260,3 +252,5 @@ void grpc_call_combiner_cancel(grpc_call_combiner* call_combiner,
     // cas failed, try again.
   }
 }
+
+}  // namespace grpc_core
index 4ec0044..a10b437 100644 (file)
 // when it is done with the action that was kicked off by the original
 // callback.
 
-extern grpc_core::TraceFlag grpc_call_combiner_trace;
+namespace grpc_core {
+
+extern TraceFlag grpc_call_combiner_trace;
+
+class CallCombiner {
+ public:
+  CallCombiner();
+  ~CallCombiner();
+
+#ifndef NDEBUG
+#define GRPC_CALL_COMBINER_START(call_combiner, closure, error, reason) \
+  (call_combiner)->Start((closure), (error), __FILE__, __LINE__, (reason))
+#define GRPC_CALL_COMBINER_STOP(call_combiner, reason) \
+  (call_combiner)->Stop(__FILE__, __LINE__, (reason))
+  /// Starts processing \a closure.
+  void Start(grpc_closure* closure, grpc_error* error, const char* file,
+             int line, const char* reason);
+  /// Yields the call combiner to the next closure in the queue, if any.
+  void Stop(const char* file, int line, const char* reason);
+#else
+#define GRPC_CALL_COMBINER_START(call_combiner, closure, error, reason) \
+  (call_combiner)->Start((closure), (error), (reason))
+#define GRPC_CALL_COMBINER_STOP(call_combiner, reason) \
+  (call_combiner)->Stop((reason))
+  /// Starts processing \a closure.
+  void Start(grpc_closure* closure, grpc_error* error, const char* reason);
+  /// Yields the call combiner to the next closure in the queue, if any.
+  void Stop(const char* reason);
+#endif
+
+  /// Registers \a closure to be invoked when Cancel() is called.
+  ///
+  /// Once a closure is registered, it will always be scheduled exactly
+  /// once; this allows the closure to hold references that will be freed
+  /// regardless of whether or not the call was cancelled.  If a cancellation
+  /// does occur, the closure will be scheduled with the cancellation error;
+  /// otherwise, it will be scheduled with GRPC_ERROR_NONE.
+  ///
+  /// The closure will be scheduled in the following cases:
+  /// - If Cancel() was called prior to registering the closure, it will be
+  ///   scheduled immediately with the cancelation error.
+  /// - If Cancel() is called after registering the closure, the closure will
+  ///   be scheduled with the cancellation error.
+  /// - If SetNotifyOnCancel() is called again to register a new cancellation
+  ///   closure, the previous cancellation closure will be scheduled with
+  ///   GRPC_ERROR_NONE.
+  ///
+  /// If \a closure is NULL, then no closure will be invoked on
+  /// cancellation; this effectively unregisters the previously set closure.
+  /// However, most filters will not need to explicitly unregister their
+  /// callbacks, as this is done automatically when the call is destroyed.
+  /// Filters that schedule the cancellation closure on ExecCtx do not need
+  /// to take a ref on the call stack to guarantee closure liveness. This is
+  /// done by explicitly flushing ExecCtx after the unregistration during
+  /// call destruction.
+  void SetNotifyOnCancel(grpc_closure* closure);
+
+  /// Indicates that the call has been cancelled.
+  void Cancel(grpc_error* error);
+
+ private:
+  void ScheduleClosure(grpc_closure* closure, grpc_error* error);
+#ifdef GRPC_TSAN_ENABLED
+  static void TsanClosure(void* arg, grpc_error* error);
+#endif
 
-struct grpc_call_combiner {
-  gpr_atm size = 0;  // size_t, num closures in queue or currently executing
-  gpr_mpscq queue;
+  gpr_atm size_ = 0;  // size_t, num closures in queue or currently executing
+  gpr_mpscq queue_;
   // Either 0 (if not cancelled and no cancellation closure set),
   // a grpc_closure* (if the lowest bit is 0),
   // or a grpc_error* (if the lowest bit is 1).
-  gpr_atm cancel_state = 0;
+  gpr_atm cancel_state_ = 0;
 #ifdef GRPC_TSAN_ENABLED
   // A fake ref-counted lock that is kept alive after the destruction of
   // grpc_call_combiner, when we are running the original closure.
@@ -58,90 +121,20 @@ struct grpc_call_combiner {
   // callback is called. However, original_closure is free to trigger
   // anything on the call combiner (including destruction of grpc_call).
   // Thus, we need a ref-counted structure that can outlive the call combiner.
-  struct TsanLock
-      : public grpc_core::RefCounted<TsanLock,
-                                     grpc_core::NonPolymorphicRefCount> {
+  struct TsanLock : public RefCounted<TsanLock, NonPolymorphicRefCount> {
     TsanLock() { TSAN_ANNOTATE_RWLOCK_CREATE(&taken); }
     ~TsanLock() { TSAN_ANNOTATE_RWLOCK_DESTROY(&taken); }
-
     // To avoid double-locking by the same thread, we should acquire/release
     // the lock only when taken is false. On each acquire taken must be set to
     // true.
     std::atomic<bool> taken{false};
   };
-  grpc_core::RefCountedPtr<TsanLock> tsan_lock =
-      grpc_core::MakeRefCounted<TsanLock>();
-  grpc_closure tsan_closure;
-  grpc_closure* original_closure;
+  RefCountedPtr<TsanLock> tsan_lock_ = MakeRefCounted<TsanLock>();
+  grpc_closure tsan_closure_;
+  grpc_closure* original_closure_;
 #endif
 };
 
-// Assumes memory was initialized to zero.
-void grpc_call_combiner_init(grpc_call_combiner* call_combiner);
-
-void grpc_call_combiner_destroy(grpc_call_combiner* call_combiner);
-
-#ifndef NDEBUG
-#define GRPC_CALL_COMBINER_START(call_combiner, closure, error, reason)   \
-  grpc_call_combiner_start((call_combiner), (closure), (error), __FILE__, \
-                           __LINE__, (reason))
-#define GRPC_CALL_COMBINER_STOP(call_combiner, reason) \
-  grpc_call_combiner_stop((call_combiner), __FILE__, __LINE__, (reason))
-/// Starts processing \a closure on \a call_combiner.
-void grpc_call_combiner_start(grpc_call_combiner* call_combiner,
-                              grpc_closure* closure, grpc_error* error,
-                              const char* file, int line, const char* reason);
-/// Yields the call combiner to the next closure in the queue, if any.
-void grpc_call_combiner_stop(grpc_call_combiner* call_combiner,
-                             const char* file, int line, const char* reason);
-#else
-#define GRPC_CALL_COMBINER_START(call_combiner, closure, error, reason) \
-  grpc_call_combiner_start((call_combiner), (closure), (error), (reason))
-#define GRPC_CALL_COMBINER_STOP(call_combiner, reason) \
-  grpc_call_combiner_stop((call_combiner), (reason))
-/// Starts processing \a closure on \a call_combiner.
-void grpc_call_combiner_start(grpc_call_combiner* call_combiner,
-                              grpc_closure* closure, grpc_error* error,
-                              const char* reason);
-/// Yields the call combiner to the next closure in the queue, if any.
-void grpc_call_combiner_stop(grpc_call_combiner* call_combiner,
-                             const char* reason);
-#endif
-
-/// Registers \a closure to be invoked by \a call_combiner when
-/// grpc_call_combiner_cancel() is called.
-///
-/// Once a closure is registered, it will always be scheduled exactly
-/// once; this allows the closure to hold references that will be freed
-/// regardless of whether or not the call was cancelled.  If a cancellation
-/// does occur, the closure will be scheduled with the cancellation error;
-/// otherwise, it will be scheduled with GRPC_ERROR_NONE.
-///
-/// The closure will be scheduled in the following cases:
-/// - If grpc_call_combiner_cancel() was called prior to registering the
-///   closure, it will be scheduled immediately with the cancelation error.
-/// - If grpc_call_combiner_cancel() is called after registering the
-///   closure, the closure will be scheduled with the cancellation error.
-/// - If grpc_call_combiner_set_notify_on_cancel() is called again to
-///   register a new cancellation closure, the previous cancellation
-///   closure will be scheduled with GRPC_ERROR_NONE.
-///
-/// If \a closure is NULL, then no closure will be invoked on
-/// cancellation; this effectively unregisters the previously set closure.
-/// However, most filters will not need to explicitly unregister their
-/// callbacks, as this is done automatically when the call is destroyed. Filters
-/// that schedule the cancellation closure on ExecCtx do not need to take a ref
-/// on the call stack to guarantee closure liveness. This is done by explicitly
-/// flushing ExecCtx after the unregistration during call destruction.
-void grpc_call_combiner_set_notify_on_cancel(grpc_call_combiner* call_combiner,
-                                             grpc_closure* closure);
-
-/// Indicates that the call has been cancelled.
-void grpc_call_combiner_cancel(grpc_call_combiner* call_combiner,
-                               grpc_error* error);
-
-namespace grpc_core {
-
 // Helper for running a list of closures in a call combiner.
 //
 // Each callback running in the call combiner will eventually be
@@ -166,7 +159,7 @@ class CallCombinerClosureList {
   // scheduled via GRPC_CLOSURE_SCHED(), which will eventually result in
   // yielding the call combiner.  If the list is empty, then the call
   // combiner will be yielded immediately.
-  void RunClosures(grpc_call_combiner* call_combiner) {
+  void RunClosures(CallCombiner* call_combiner) {
     if (closures_.empty()) {
       GRPC_CALL_COMBINER_STOP(call_combiner, "no closures to schedule");
       return;
@@ -176,7 +169,7 @@ class CallCombinerClosureList {
       GRPC_CALL_COMBINER_START(call_combiner, closure.closure, closure.error,
                                closure.reason);
     }
-    if (grpc_call_combiner_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_call_combiner_trace)) {
       gpr_log(GPR_INFO,
               "CallCombinerClosureList executing closure while already "
               "holding call_combiner %p: closure=%p error=%s reason=%s",
@@ -190,7 +183,7 @@ class CallCombinerClosureList {
 
   // Runs all closures in the call combiner, but does NOT yield the call
   // combiner.  All closures will be scheduled via GRPC_CALL_COMBINER_START().
-  void RunClosuresWithoutYielding(grpc_call_combiner* call_combiner) {
+  void RunClosuresWithoutYielding(CallCombiner* call_combiner) {
     for (size_t i = 0; i < closures_.size(); ++i) {
       auto& closure = closures_[i];
       GRPC_CALL_COMBINER_START(call_combiner, closure.closure, closure.error,
index 93ec5f0..4f4d159 100644 (file)
@@ -37,8 +37,8 @@ class CFStreamHandle final {
   static CFStreamHandle* CreateStreamHandle(CFReadStreamRef read_stream,
                                             CFWriteStreamRef write_stream);
   ~CFStreamHandle();
-  CFStreamHandle(const CFReadStreamRef& ref) = delete;
-  CFStreamHandle(CFReadStreamRef&& ref) = delete;
+  CFStreamHandle(const CFStreamHandle& ref) = delete;
+  CFStreamHandle(CFStreamHandle&& ref) = delete;
   CFStreamHandle& operator=(const CFStreamHandle& rhs) = delete;
 
   void NotifyOnOpen(grpc_closure* closure);
index 3c947bf..b927421 100644 (file)
@@ -31,7 +31,7 @@
 // Provides serialized access to some resource.
 // Each action queued on a combiner is executed serially in a borrowed thread.
 // The actual thread executing actions may change over time (but there will only
-// every be one at a time).
+// ever be one at a time).
 
 // Initialize the lock, with an optional workqueue to shift load to when
 // necessary
index 177331d..9962809 100644 (file)
@@ -41,7 +41,7 @@ static void create_sockets(SOCKET sv[2]) {
   int addr_len = sizeof(addr);
 
   lst_sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
-                       WSA_FLAG_OVERLAPPED);
+                       grpc_get_default_wsa_socket_flags());
   GPR_ASSERT(lst_sock != INVALID_SOCKET);
 
   memset(&addr, 0, sizeof(addr));
@@ -54,7 +54,7 @@ static void create_sockets(SOCKET sv[2]) {
              SOCKET_ERROR);
 
   cli_sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
-                       WSA_FLAG_OVERLAPPED);
+                       grpc_get_default_wsa_socket_flags());
   GPR_ASSERT(cli_sock != INVALID_SOCKET);
 
   GPR_ASSERT(WSAConnect(cli_sock, (grpc_sockaddr*)&addr, addr_len, NULL, NULL,
index fcc6f07..7b04888 100644 (file)
@@ -30,6 +30,7 @@
 #include <grpc/support/time.h>
 
 #include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
 
 /// Opaque representation of an error.
 /// See https://github.com/grpc/grpc/blob/master/doc/core/grpc-error.md for a
@@ -165,6 +166,9 @@ grpc_error* grpc_error_create(const char* file, int line,
   grpc_error_create(__FILE__, __LINE__, grpc_slice_from_copied_string(desc), \
                     errs, count)
 
+#define GRPC_ERROR_CREATE_FROM_VECTOR(desc, error_list) \
+  grpc_error_create_from_vector(__FILE__, __LINE__, desc, error_list)
+
 #ifndef NDEBUG
 grpc_error* grpc_error_do_ref(grpc_error* err, const char* file, int line);
 void grpc_error_do_unref(grpc_error* err, const char* file, int line);
@@ -193,6 +197,25 @@ inline void grpc_error_unref(grpc_error* err) {
 #define GRPC_ERROR_UNREF(err) grpc_error_unref(err)
 #endif
 
+// Consumes all the errors in the vector and forms a referencing error from
+// them. If the vector is empty, return GRPC_ERROR_NONE.
+template <size_t N>
+static grpc_error* grpc_error_create_from_vector(
+    const char* file, int line, const char* desc,
+    grpc_core::InlinedVector<grpc_error*, N>* error_list) {
+  grpc_error* error = GRPC_ERROR_NONE;
+  if (error_list->size() != 0) {
+    error = grpc_error_create(file, line, grpc_slice_from_static_string(desc),
+                              error_list->data(), error_list->size());
+    // Remove refs to all errors in error_list.
+    for (size_t i = 0; i < error_list->size(); i++) {
+      GRPC_ERROR_UNREF((*error_list)[i]);
+    }
+    error_list->clear();
+  }
+  return error;
+}
+
 grpc_error* grpc_error_set_int(grpc_error* src, grpc_error_ints which,
                                intptr_t value) GRPC_MUST_USE_RESULT;
 /// It is an error to pass nullptr as `p`. Caller should allocate a dummy
index b6f804c..cacf7f9 100644 (file)
@@ -213,7 +213,7 @@ struct grpc_pollset {
      poll */
   bool seen_inactive;
   bool shutting_down;             /* Is the pollset shutting down ? */
-  grpc_closure* shutdown_closure; /* Called after after shutdown is complete */
+  grpc_closure* shutdown_closure; /* Called after shutdown is complete */
 
   /* Number of workers who are *about-to* attach themselves to the pollset
    * worker list */
@@ -351,7 +351,7 @@ static grpc_fd* fd_create(int fd, const char* name, bool track_err) {
   grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name);
   fork_fd_list_add_grpc_fd(new_fd);
 #ifndef NDEBUG
-  if (grpc_trace_fd_refcount.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_fd_refcount)) {
     gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, new_fd, fd_name);
   }
 #endif
@@ -383,6 +383,13 @@ static void fd_shutdown_internal(grpc_fd* fd, grpc_error* why,
   if (fd->read_closure->SetShutdown(GRPC_ERROR_REF(why))) {
     if (!releasing_fd) {
       shutdown(fd->fd, SHUT_RDWR);
+    } else {
+      /* we need a dummy event for earlier linux versions. */
+      epoll_event dummy_event;
+      if (epoll_ctl(g_epoll_set.epfd, EPOLL_CTL_DEL, fd->fd, &dummy_event) !=
+          0) {
+        gpr_log(GPR_ERROR, "epoll_ctl failed: %s", strerror(errno));
+      }
     }
     fd->write_closure->SetShutdown(GRPC_ERROR_REF(why));
     fd->error_closure->SetShutdown(GRPC_ERROR_REF(why));
@@ -724,7 +731,7 @@ static grpc_error* do_epoll_wait(grpc_pollset* ps, grpc_millis deadline) {
 
   GRPC_STATS_INC_POLL_EVENTS_RETURNED(r);
 
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_INFO, "ps: %p poll got %d events", ps, r);
   }
 
@@ -744,7 +751,7 @@ static bool begin_worker(grpc_pollset* pollset, grpc_pollset_worker* worker,
   worker->schedule_on_end_work = (grpc_closure_list)GRPC_CLOSURE_LIST_INIT;
   pollset->begin_refs++;
 
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_INFO, "PS:%p BEGIN_STARTS:%p", pollset, worker);
   }
 
@@ -763,7 +770,7 @@ static bool begin_worker(grpc_pollset* pollset, grpc_pollset_worker* worker,
   retry_lock_neighborhood:
     gpr_mu_lock(&neighborhood->mu);
     gpr_mu_lock(&pollset->mu);
-    if (grpc_polling_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
       gpr_log(GPR_INFO, "PS:%p BEGIN_REORG:%p kick_state=%s is_reassigning=%d",
               pollset, worker, kick_state_string(worker->state),
               is_reassigning);
@@ -815,7 +822,7 @@ static bool begin_worker(grpc_pollset* pollset, grpc_pollset_worker* worker,
     worker->initialized_cv = true;
     gpr_cv_init(&worker->cv);
     while (worker->state == UNKICKED && !pollset->shutting_down) {
-      if (grpc_polling_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
         gpr_log(GPR_INFO, "PS:%p BEGIN_WAIT:%p kick_state=%s shutdown=%d",
                 pollset, worker, kick_state_string(worker->state),
                 pollset->shutting_down);
@@ -832,7 +839,7 @@ static bool begin_worker(grpc_pollset* pollset, grpc_pollset_worker* worker,
     grpc_core::ExecCtx::Get()->InvalidateNow();
   }
 
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_INFO,
             "PS:%p BEGIN_DONE:%p kick_state=%s shutdown=%d "
             "kicked_without_poller: %d",
@@ -875,7 +882,7 @@ static bool check_neighborhood_for_available_poller(
           case UNKICKED:
             if (gpr_atm_no_barrier_cas(&g_active_poller, 0,
                                        (gpr_atm)inspect_worker)) {
-              if (grpc_polling_trace.enabled()) {
+              if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
                 gpr_log(GPR_INFO, " .. choose next poller to be %p",
                         inspect_worker);
               }
@@ -886,7 +893,7 @@ static bool check_neighborhood_for_available_poller(
                 gpr_cv_signal(&inspect_worker->cv);
               }
             } else {
-              if (grpc_polling_trace.enabled()) {
+              if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
                 gpr_log(GPR_INFO, " .. beaten to choose next poller");
               }
             }
@@ -904,7 +911,7 @@ static bool check_neighborhood_for_available_poller(
       } while (!found_worker && inspect_worker != inspect->root_worker);
     }
     if (!found_worker) {
-      if (grpc_polling_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
         gpr_log(GPR_INFO, " .. mark pollset %p inactive", inspect);
       }
       inspect->seen_inactive = true;
@@ -924,7 +931,7 @@ static bool check_neighborhood_for_available_poller(
 static void end_worker(grpc_pollset* pollset, grpc_pollset_worker* worker,
                        grpc_pollset_worker** worker_hdl) {
   GPR_TIMER_SCOPE("end_worker", 0);
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_INFO, "PS:%p END_WORKER:%p", pollset, worker);
   }
   if (worker_hdl != nullptr) *worker_hdl = nullptr;
@@ -934,7 +941,7 @@ static void end_worker(grpc_pollset* pollset, grpc_pollset_worker* worker,
                          grpc_core::ExecCtx::Get()->closure_list());
   if (gpr_atm_no_barrier_load(&g_active_poller) == (gpr_atm)worker) {
     if (worker->next != worker && worker->next->state == UNKICKED) {
-      if (grpc_polling_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
         gpr_log(GPR_INFO, " .. choose next poller to be peer %p", worker);
       }
       GPR_ASSERT(worker->next->initialized_cv);
@@ -986,7 +993,7 @@ static void end_worker(grpc_pollset* pollset, grpc_pollset_worker* worker,
   if (worker->initialized_cv) {
     gpr_cv_destroy(&worker->cv);
   }
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_INFO, " .. remove worker");
   }
   if (EMPTIED == worker_remove(pollset, worker)) {
@@ -1055,7 +1062,7 @@ static grpc_error* pollset_kick(grpc_pollset* pollset,
   GPR_TIMER_SCOPE("pollset_kick", 0);
   GRPC_STATS_INC_POLLSET_KICK();
   grpc_error* ret_err = GRPC_ERROR_NONE;
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_strvec log;
     gpr_strvec_init(&log);
     char* tmp;
@@ -1088,7 +1095,7 @@ static grpc_error* pollset_kick(grpc_pollset* pollset,
       if (root_worker == nullptr) {
         GRPC_STATS_INC_POLLSET_KICKED_WITHOUT_POLLER();
         pollset->kicked_without_poller = true;
-        if (grpc_polling_trace.enabled()) {
+        if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
           gpr_log(GPR_INFO, " .. kicked_without_poller");
         }
         goto done;
@@ -1096,14 +1103,14 @@ static grpc_error* pollset_kick(grpc_pollset* pollset,
       grpc_pollset_worker* next_worker = root_worker->next;
       if (root_worker->state == KICKED) {
         GRPC_STATS_INC_POLLSET_KICKED_AGAIN();
-        if (grpc_polling_trace.enabled()) {
+        if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
           gpr_log(GPR_INFO, " .. already kicked %p", root_worker);
         }
         SET_KICK_STATE(root_worker, KICKED);
         goto done;
       } else if (next_worker->state == KICKED) {
         GRPC_STATS_INC_POLLSET_KICKED_AGAIN();
-        if (grpc_polling_trace.enabled()) {
+        if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
           gpr_log(GPR_INFO, " .. already kicked %p", next_worker);
         }
         SET_KICK_STATE(next_worker, KICKED);
@@ -1114,7 +1121,7 @@ static grpc_error* pollset_kick(grpc_pollset* pollset,
                  root_worker == (grpc_pollset_worker*)gpr_atm_no_barrier_load(
                                     &g_active_poller)) {
         GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD();
-        if (grpc_polling_trace.enabled()) {
+        if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
           gpr_log(GPR_INFO, " .. kicked %p", root_worker);
         }
         SET_KICK_STATE(root_worker, KICKED);
@@ -1122,7 +1129,7 @@ static grpc_error* pollset_kick(grpc_pollset* pollset,
         goto done;
       } else if (next_worker->state == UNKICKED) {
         GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV();
-        if (grpc_polling_trace.enabled()) {
+        if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
           gpr_log(GPR_INFO, " .. kicked %p", next_worker);
         }
         GPR_ASSERT(next_worker->initialized_cv);
@@ -1131,7 +1138,7 @@ static grpc_error* pollset_kick(grpc_pollset* pollset,
         goto done;
       } else if (next_worker->state == DESIGNATED_POLLER) {
         if (root_worker->state != DESIGNATED_POLLER) {
-          if (grpc_polling_trace.enabled()) {
+          if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
             gpr_log(
                 GPR_INFO,
                 " .. kicked root non-poller %p (initialized_cv=%d) (poller=%p)",
@@ -1145,7 +1152,7 @@ static grpc_error* pollset_kick(grpc_pollset* pollset,
           goto done;
         } else {
           GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD();
-          if (grpc_polling_trace.enabled()) {
+          if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
             gpr_log(GPR_INFO, " .. non-root poller %p (root=%p)", next_worker,
                     root_worker);
           }
@@ -1161,7 +1168,7 @@ static grpc_error* pollset_kick(grpc_pollset* pollset,
       }
     } else {
       GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD();
-      if (grpc_polling_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
         gpr_log(GPR_INFO, " .. kicked while waking up");
       }
       goto done;
@@ -1171,14 +1178,14 @@ static grpc_error* pollset_kick(grpc_pollset* pollset,
   }
 
   if (specific_worker->state == KICKED) {
-    if (grpc_polling_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
       gpr_log(GPR_INFO, " .. specific worker already kicked");
     }
     goto done;
   } else if (gpr_tls_get(&g_current_thread_worker) ==
              (intptr_t)specific_worker) {
     GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD();
-    if (grpc_polling_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
       gpr_log(GPR_INFO, " .. mark %p kicked", specific_worker);
     }
     SET_KICK_STATE(specific_worker, KICKED);
@@ -1186,7 +1193,7 @@ static grpc_error* pollset_kick(grpc_pollset* pollset,
   } else if (specific_worker ==
              (grpc_pollset_worker*)gpr_atm_no_barrier_load(&g_active_poller)) {
     GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD();
-    if (grpc_polling_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
       gpr_log(GPR_INFO, " .. kick active poller");
     }
     SET_KICK_STATE(specific_worker, KICKED);
@@ -1194,7 +1201,7 @@ static grpc_error* pollset_kick(grpc_pollset* pollset,
     goto done;
   } else if (specific_worker->initialized_cv) {
     GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV();
-    if (grpc_polling_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
       gpr_log(GPR_INFO, " .. kick waiting worker");
     }
     SET_KICK_STATE(specific_worker, KICKED);
@@ -1202,7 +1209,7 @@ static grpc_error* pollset_kick(grpc_pollset* pollset,
     goto done;
   } else {
     GRPC_STATS_INC_POLLSET_KICKED_AGAIN();
-    if (grpc_polling_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
       gpr_log(GPR_INFO, " .. kick non-waiting worker");
     }
     SET_KICK_STATE(specific_worker, KICKED);
index 01be46c..08116b3 100644 (file)
@@ -47,7 +47,7 @@
 #include "src/core/lib/gpr/useful.h"
 #include "src/core/lib/gprpp/inlined_vector.h"
 #include "src/core/lib/gprpp/manual_constructor.h"
-#include "src/core/lib/gprpp/mutex_lock.h"
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/iomgr/block_annotate.h"
 #include "src/core/lib/iomgr/iomgr_internal.h"
 #include "src/core/lib/iomgr/is_epollexclusive_available.h"
@@ -164,7 +164,7 @@ struct grpc_fd {
     gpr_asprintf(&fd_name, "%s fd=%d", name, fd);
     grpc_iomgr_register_object(&iomgr_object, fd_name);
 #ifndef NDEBUG
-    if (grpc_trace_fd_refcount.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_fd_refcount)) {
       gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, this, fd_name);
     }
 #endif
@@ -335,7 +335,7 @@ static gpr_mu fd_freelist_mu;
 #define UNREF_BY(fd, n, reason) unref_by(fd, n, reason, __FILE__, __LINE__)
 static void ref_by(grpc_fd* fd, int n, const char* reason, const char* file,
                    int line) {
-  if (grpc_trace_fd_refcount.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_fd_refcount)) {
     gpr_log(GPR_DEBUG,
             "FD %d %p   ref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
             fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
@@ -364,7 +364,7 @@ static void fd_destroy(void* arg, grpc_error* error) {
 #ifndef NDEBUG
 static void unref_by(grpc_fd* fd, int n, const char* reason, const char* file,
                      int line) {
-  if (grpc_trace_fd_refcount.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_fd_refcount)) {
     gpr_log(GPR_DEBUG,
             "FD %d %p unref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
             fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
@@ -586,7 +586,7 @@ static grpc_error* pollable_create(pollable_type type, pollable** p) {
 static pollable* pollable_ref(pollable* p) {
 #else
 static pollable* pollable_ref(pollable* p, int line, const char* reason) {
-  if (grpc_trace_pollable_refcount.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_pollable_refcount)) {
     int r = static_cast<int> gpr_atm_no_barrier_load(&p->refs.count);
     gpr_log(__FILE__, line, GPR_LOG_SEVERITY_DEBUG,
             "POLLABLE:%p   ref %d->%d %s", p, r, r + 1, reason);
@@ -601,7 +601,7 @@ static void pollable_unref(pollable* p) {
 #else
 static void pollable_unref(pollable* p, int line, const char* reason) {
   if (p == nullptr) return;
-  if (grpc_trace_pollable_refcount.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_pollable_refcount)) {
     int r = static_cast<int> gpr_atm_no_barrier_load(&p->refs.count);
     gpr_log(__FILE__, line, GPR_LOG_SEVERITY_DEBUG,
             "POLLABLE:%p unref %d->%d %s", p, r, r - 1, reason);
@@ -621,7 +621,7 @@ static grpc_error* pollable_add_fd(pollable* p, grpc_fd* fd) {
   grpc_error* error = GRPC_ERROR_NONE;
   static const char* err_desc = "pollable_add_fd";
   const int epfd = p->epfd;
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_INFO, "add fd %p (%d) to pollable %p", fd, fd->fd, p);
   }
 
@@ -669,7 +669,7 @@ static void pollset_global_shutdown(void) {
 
 /* pollset->mu must be held while calling this function */
 static void pollset_maybe_finish_shutdown(grpc_pollset* pollset) {
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_INFO,
             "PS:%p (pollable:%p) maybe_finish_shutdown sc=%p (target:!NULL) "
             "rw=%p (target:NULL) cpsc=%d (target:0)",
@@ -694,14 +694,14 @@ static grpc_error* kick_one_worker(grpc_pollset_worker* specific_worker) {
   grpc_core::MutexLock lock(&p->mu);
   GPR_ASSERT(specific_worker != nullptr);
   if (specific_worker->kicked) {
-    if (grpc_polling_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
       gpr_log(GPR_INFO, "PS:%p kicked_specific_but_already_kicked", p);
     }
     GRPC_STATS_INC_POLLSET_KICKED_AGAIN();
     return GRPC_ERROR_NONE;
   }
   if (gpr_tls_get(&g_current_thread_worker) == (intptr_t)specific_worker) {
-    if (grpc_polling_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
       gpr_log(GPR_INFO, "PS:%p kicked_specific_but_awake", p);
     }
     GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD();
@@ -710,7 +710,7 @@ static grpc_error* kick_one_worker(grpc_pollset_worker* specific_worker) {
   }
   if (specific_worker == p->root_worker) {
     GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD();
-    if (grpc_polling_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
       gpr_log(GPR_INFO, "PS:%p kicked_specific_via_wakeup_fd", p);
     }
     specific_worker->kicked = true;
@@ -719,7 +719,7 @@ static grpc_error* kick_one_worker(grpc_pollset_worker* specific_worker) {
   }
   if (specific_worker->initialized_cv) {
     GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV();
-    if (grpc_polling_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
       gpr_log(GPR_INFO, "PS:%p kicked_specific_via_cv", p);
     }
     specific_worker->kicked = true;
@@ -735,7 +735,7 @@ static grpc_error* pollset_kick(grpc_pollset* pollset,
                                 grpc_pollset_worker* specific_worker) {
   GPR_TIMER_SCOPE("pollset_kick", 0);
   GRPC_STATS_INC_POLLSET_KICK();
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_INFO,
             "PS:%p kick %p tls_pollset=%p tls_worker=%p pollset.root_worker=%p",
             pollset, specific_worker,
@@ -745,7 +745,7 @@ static grpc_error* pollset_kick(grpc_pollset* pollset,
   if (specific_worker == nullptr) {
     if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)pollset) {
       if (pollset->root_worker == nullptr) {
-        if (grpc_polling_trace.enabled()) {
+        if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
           gpr_log(GPR_INFO, "PS:%p kicked_any_without_poller", pollset);
         }
         GRPC_STATS_INC_POLLSET_KICKED_WITHOUT_POLLER();
@@ -771,7 +771,7 @@ static grpc_error* pollset_kick(grpc_pollset* pollset,
             pollset->root_worker->links[PWLINK_POLLSET].next);
       }
     } else {
-      if (grpc_polling_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
         gpr_log(GPR_INFO, "PS:%p kicked_any_but_awake", pollset);
       }
       GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD();
@@ -891,7 +891,7 @@ static grpc_error* pollable_process_events(grpc_pollset* pollset,
     struct epoll_event* ev = &pollable_obj->events[n];
     void* data_ptr = ev->data.ptr;
     if (1 & (intptr_t)data_ptr) {
-      if (grpc_polling_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
         gpr_log(GPR_INFO, "PS:%p got pollset_wakeup %p", pollset, data_ptr);
       }
       append_error(&error,
@@ -909,7 +909,7 @@ static grpc_error* pollable_process_events(grpc_pollset* pollset,
       bool write_ev = (ev->events & EPOLLOUT) != 0;
       bool err_fallback = error && !track_err;
 
-      if (grpc_polling_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
         gpr_log(GPR_INFO,
                 "PS:%p got fd %p: cancel=%d read=%d "
                 "write=%d",
@@ -941,7 +941,7 @@ static grpc_error* pollable_epoll(pollable* p, grpc_millis deadline) {
   GPR_TIMER_SCOPE("pollable_epoll", 0);
   int timeout = poll_deadline_to_millis_timeout(deadline);
 
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     char* desc = pollable_desc(p);
     gpr_log(GPR_INFO, "POLLABLE:%p[%s] poll for %dms", p, desc, timeout);
     gpr_free(desc);
@@ -961,7 +961,7 @@ static grpc_error* pollable_epoll(pollable* p, grpc_millis deadline) {
 
   if (r < 0) return GRPC_OS_ERROR(errno, "epoll_wait");
 
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_INFO, "POLLABLE:%p got %d events", p, r);
   }
 
@@ -1031,7 +1031,7 @@ static bool begin_worker(grpc_pollset* pollset, grpc_pollset_worker* worker,
     worker->initialized_cv = true;
     gpr_cv_init(&worker->cv);
     gpr_mu_unlock(&pollset->mu);
-    if (grpc_polling_trace.enabled() &&
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace) &&
         worker->pollable_obj->root_worker != worker) {
       gpr_log(GPR_INFO, "PS:%p wait %p w=%p for %dms", pollset,
               worker->pollable_obj, worker,
@@ -1040,18 +1040,18 @@ static bool begin_worker(grpc_pollset* pollset, grpc_pollset_worker* worker,
     while (do_poll && worker->pollable_obj->root_worker != worker) {
       if (gpr_cv_wait(&worker->cv, &worker->pollable_obj->mu,
                       grpc_millis_to_timespec(deadline, GPR_CLOCK_REALTIME))) {
-        if (grpc_polling_trace.enabled()) {
+        if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
           gpr_log(GPR_INFO, "PS:%p timeout_wait %p w=%p", pollset,
                   worker->pollable_obj, worker);
         }
         do_poll = false;
       } else if (worker->kicked) {
-        if (grpc_polling_trace.enabled()) {
+        if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
           gpr_log(GPR_INFO, "PS:%p wakeup %p w=%p", pollset,
                   worker->pollable_obj, worker);
         }
         do_poll = false;
-      } else if (grpc_polling_trace.enabled() &&
+      } else if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace) &&
                  worker->pollable_obj->root_worker != worker) {
         gpr_log(GPR_INFO, "PS:%p spurious_wakeup %p w=%p", pollset,
                 worker->pollable_obj, worker);
@@ -1124,7 +1124,7 @@ static grpc_error* pollset_work(grpc_pollset* pollset,
 #ifndef NDEBUG
   WORKER_PTR->originator = gettid();
 #endif
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_INFO,
             "PS:%p work hdl=%p worker=%p now=%" PRId64 " deadline=%" PRId64
             " kwp=%d pollable=%p",
@@ -1165,7 +1165,7 @@ static grpc_error* pollset_transition_pollable_from_empty_to_fd_locked(
     grpc_pollset* pollset, grpc_fd* fd) {
   static const char* err_desc = "pollset_transition_pollable_from_empty_to_fd";
   grpc_error* error = GRPC_ERROR_NONE;
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_INFO,
             "PS:%p add fd %p (%d); transition pollable from empty to fd",
             pollset, fd, fd->fd);
@@ -1181,7 +1181,7 @@ static grpc_error* pollset_transition_pollable_from_fd_to_multi_locked(
     grpc_pollset* pollset, grpc_fd* and_add_fd) {
   static const char* err_desc = "pollset_transition_pollable_from_fd_to_multi";
   grpc_error* error = GRPC_ERROR_NONE;
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(
         GPR_INFO,
         "PS:%p add fd %p (%d); transition pollable from fd %p to multipoller",
@@ -1253,7 +1253,7 @@ static grpc_error* pollset_as_multipollable_locked(grpc_pollset* pollset,
       error = pollable_create(PO_MULTI, &pollset->active_pollable);
       /* Any workers currently polling on this pollset must now be woked up so
        * that they can pick up the new active_pollable */
-      if (grpc_polling_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
         gpr_log(GPR_INFO,
                 "PS:%p active pollable transition from empty to multi",
                 pollset);
@@ -1357,7 +1357,7 @@ static void pollset_set_unref(grpc_pollset_set* pss) {
 
 static void pollset_set_add_fd(grpc_pollset_set* pss, grpc_fd* fd) {
   GPR_TIMER_SCOPE("pollset_set_add_fd", 0);
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_INFO, "PSS:%p: add fd %p (%d)", pss, fd, fd->fd);
   }
   grpc_error* error = GRPC_ERROR_NONE;
@@ -1381,7 +1381,7 @@ static void pollset_set_add_fd(grpc_pollset_set* pss, grpc_fd* fd) {
 
 static void pollset_set_del_fd(grpc_pollset_set* pss, grpc_fd* fd) {
   GPR_TIMER_SCOPE("pollset_set_del_fd", 0);
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_INFO, "PSS:%p: del fd %p", pss, fd);
   }
   pss = pss_lock_adam(pss);
@@ -1402,7 +1402,7 @@ static void pollset_set_del_fd(grpc_pollset_set* pss, grpc_fd* fd) {
 
 static void pollset_set_del_pollset(grpc_pollset_set* pss, grpc_pollset* ps) {
   GPR_TIMER_SCOPE("pollset_set_del_pollset", 0);
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_INFO, "PSS:%p: del pollset %p", pss, ps);
   }
   pss = pss_lock_adam(pss);
@@ -1454,7 +1454,7 @@ static grpc_error* add_fds_to_pollsets(grpc_fd** fds, size_t fd_count,
 
 static void pollset_set_add_pollset(grpc_pollset_set* pss, grpc_pollset* ps) {
   GPR_TIMER_SCOPE("pollset_set_add_pollset", 0);
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_INFO, "PSS:%p: add pollset %p", pss, ps);
   }
   grpc_error* error = GRPC_ERROR_NONE;
@@ -1491,7 +1491,7 @@ static void pollset_set_add_pollset(grpc_pollset_set* pss, grpc_pollset* ps) {
 static void pollset_set_add_pollset_set(grpc_pollset_set* a,
                                         grpc_pollset_set* b) {
   GPR_TIMER_SCOPE("pollset_set_add_pollset_set", 0);
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_INFO, "PSS: merge (%p, %p)", a, b);
   }
   grpc_error* error = GRPC_ERROR_NONE;
@@ -1525,7 +1525,7 @@ static void pollset_set_add_pollset_set(grpc_pollset_set* a,
   if (b_size > a_size) {
     GPR_SWAP(grpc_pollset_set*, a, b);
   }
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_INFO, "PSS: parent %p to %p", b, a);
   }
   gpr_ref(&a->refs);
index 0c95cb7..cee6dcb 100644 (file)
@@ -316,7 +316,7 @@ static void fork_fd_list_add_wakeup_fd(grpc_cached_wakeup_fd* fd) {
 #define UNREF_BY(fd, n, reason) unref_by(fd, n, reason, __FILE__, __LINE__)
 static void ref_by(grpc_fd* fd, int n, const char* reason, const char* file,
                    int line) {
-  if (grpc_trace_fd_refcount.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_fd_refcount)) {
     gpr_log(GPR_DEBUG,
             "FD %d %p   ref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
             fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
@@ -333,7 +333,7 @@ static void ref_by(grpc_fd* fd, int n) {
 #ifndef NDEBUG
 static void unref_by(grpc_fd* fd, int n, const char* reason, const char* file,
                      int line) {
-  if (grpc_trace_fd_refcount.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_fd_refcount)) {
     gpr_log(GPR_DEBUG,
             "FD %d %p unref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
             fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
@@ -561,7 +561,7 @@ static void fd_notify_on_write(grpc_fd* fd, grpc_closure* closure) {
 }
 
 static void fd_notify_on_error(grpc_fd* fd, grpc_closure* closure) {
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_ERROR, "Polling engine does not support tracking errors.");
   }
   GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_CANCELLED);
@@ -580,7 +580,7 @@ static void fd_set_writable(grpc_fd* fd) {
 }
 
 static void fd_set_error(grpc_fd* fd) {
-  if (grpc_polling_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
     gpr_log(GPR_ERROR, "Polling engine does not support tracking errors.");
   }
 }
@@ -1012,7 +1012,7 @@ static grpc_error* pollset_work(grpc_pollset* pollset,
       r = grpc_poll_function(pfds, pfd_count, timeout);
       GRPC_SCHEDULING_END_BLOCKING_REGION;
 
-      if (grpc_polling_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
         gpr_log(GPR_INFO, "%p poll=%d", pollset, r);
       }
 
@@ -1036,7 +1036,7 @@ static grpc_error* pollset_work(grpc_pollset* pollset,
         }
       } else {
         if (pfds[0].revents & POLLIN_CHECK) {
-          if (grpc_polling_trace.enabled()) {
+          if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
             gpr_log(GPR_INFO, "%p: got_wakeup", pollset);
           }
           work_combine_error(
@@ -1046,7 +1046,7 @@ static grpc_error* pollset_work(grpc_pollset* pollset,
           if (watchers[i].fd == nullptr) {
             fd_end_poll(&watchers[i], 0, 0);
           } else {
-            if (grpc_polling_trace.enabled()) {
+            if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
               gpr_log(GPR_INFO, "%p got_event: %d r:%d w:%d [%d]", pollset,
                       pfds[i].fd, (pfds[i].revents & POLLIN_CHECK) != 0,
                       (pfds[i].revents & POLLOUT_CHECK) != 0, pfds[i].revents);
index 898686b..ddafb7b 100644 (file)
 #include <grpc/support/string_util.h>
 
 #include "src/core/lib/debug/trace.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/global_config.h"
 #include "src/core/lib/iomgr/ev_epoll1_linux.h"
 #include "src/core/lib/iomgr/ev_epollex_linux.h"
 #include "src/core/lib/iomgr/ev_poll_posix.h"
 #include "src/core/lib/iomgr/internal_errqueue.h"
 
+GPR_GLOBAL_CONFIG_DEFINE_STRING(
+    grpc_poll_strategy, "all",
+    "Declares which polling engines to try when starting gRPC. "
+    "This is a comma-separated list of engines, which are tried in priority "
+    "order first -> last.")
+
 grpc_core::TraceFlag grpc_polling_trace(false,
                                         "polling"); /* Disabled by default */
 
@@ -46,16 +52,15 @@ grpc_core::TraceFlag grpc_fd_trace(false, "fd_trace");
 grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount(false, "fd_refcount");
 grpc_core::DebugOnlyTraceFlag grpc_polling_api_trace(false, "polling_api");
 
-#ifndef NDEBUG
-
 // Polling API trace only enabled in debug builds
+#ifndef NDEBUG
 #define GRPC_POLLING_API_TRACE(format, ...)                  \
-  if (grpc_polling_api_trace.enabled()) {                    \
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_api_trace)) {     \
     gpr_log(GPR_INFO, "(polling-api) " format, __VA_ARGS__); \
   }
 #else
 #define GRPC_POLLING_API_TRACE(...)
-#endif
+#endif  // NDEBUG
 
 /** Default poll() function - a pointer so that it can be overridden by some
  *  tests */
@@ -66,7 +71,7 @@ int aix_poll(struct pollfd fds[], nfds_t nfds, int timeout) {
   return poll(fds, nfds, timeout);
 }
 grpc_poll_function_type grpc_poll_function = aix_poll;
-#endif
+#endif  // GPR_AIX
 
 grpc_wakeup_fd grpc_global_wakeup_fd;
 
@@ -205,14 +210,11 @@ void grpc_register_event_engine_factory(const char* name,
 const char* grpc_get_poll_strategy_name() { return g_poll_strategy_name; }
 
 void grpc_event_engine_init(void) {
-  char* s = gpr_getenv("GRPC_POLL_STRATEGY");
-  if (s == nullptr) {
-    s = gpr_strdup("all");
-  }
+  grpc_core::UniquePtr<char> value = GPR_GLOBAL_CONFIG_GET(grpc_poll_strategy);
 
   char** strings = nullptr;
   size_t nstrings = 0;
-  split(s, &strings, &nstrings);
+  split(value.get(), &strings, &nstrings);
 
   for (size_t i = 0; g_event_engine == nullptr && i < nstrings; i++) {
     try_engine(strings[i]);
@@ -224,10 +226,10 @@ void grpc_event_engine_init(void) {
   gpr_free(strings);
 
   if (g_event_engine == nullptr) {
-    gpr_log(GPR_ERROR, "No event engine could be initialized from %s", s);
+    gpr_log(GPR_ERROR, "No event engine could be initialized from %s",
+            value.get());
     abort();
   }
-  gpr_free(s);
 }
 
 void grpc_event_engine_shutdown(void) {
index 699173f..30bb5e4 100644 (file)
 #include <poll.h>
 
 #include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gprpp/global_config.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/iomgr/pollset.h"
 #include "src/core/lib/iomgr/pollset_set.h"
 #include "src/core/lib/iomgr/wakeup_fd_posix.h"
 
+GPR_GLOBAL_CONFIG_DECLARE_STRING(grpc_poll_strategy);
+
 extern grpc_core::TraceFlag grpc_fd_trace;      /* Disabled by default */
 extern grpc_core::TraceFlag grpc_polling_trace; /* Disabled by default */
 
 #define GRPC_FD_TRACE(format, ...)                        \
-  if (grpc_fd_trace.enabled()) {                          \
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_fd_trace)) {           \
     gpr_log(GPR_INFO, "(fd-trace) " format, __VA_ARGS__); \
   }
 
index 47836ac..8adc090 100644 (file)
 
 #define MAX_DEPTH 2
 
-#define EXECUTOR_TRACE(format, ...)                     \
-  if (executor_trace.enabled()) {                       \
-    gpr_log(GPR_INFO, "EXECUTOR " format, __VA_ARGS__); \
-  }
-
-#define EXECUTOR_TRACE0(str)            \
-  if (executor_trace.enabled()) {       \
-    gpr_log(GPR_INFO, "EXECUTOR " str); \
-  }
+#define EXECUTOR_TRACE(format, ...)                       \
+  do {                                                    \
+    if (GRPC_TRACE_FLAG_ENABLED(executor_trace)) {        \
+      gpr_log(GPR_INFO, "EXECUTOR " format, __VA_ARGS__); \
+    }                                                     \
+  } while (0)
+
+#define EXECUTOR_TRACE0(str)                       \
+  do {                                             \
+    if (GRPC_TRACE_FLAG_ENABLED(executor_trace)) { \
+      gpr_log(GPR_INFO, "EXECUTOR " str);          \
+    }                                              \
+  } while (0)
 
 namespace grpc_core {
 namespace {
index 7f8fb7e..629b081 100644 (file)
@@ -28,7 +28,6 @@
 #include <grpc/grpc.h>
 #include <grpc/support/log.h>
 
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gprpp/fork.h"
 #include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/ev_posix.h"
index 4e2bfe3..b68c66b 100644 (file)
@@ -36,7 +36,7 @@ static bool errqueue_supported = false;
 bool kernel_supports_errqueue() { return errqueue_supported; }
 
 void grpc_errqueue_init() {
-/* Both-compile time and run-time linux kernel versions should be atleast 4.0.0
+/* Both-compile time and run-time linux kernel versions should be at least 4.0.0
  */
 #ifdef GRPC_LINUX_ERRQUEUE
   struct utsname buffer;
index 0fbfcfc..b86aa6f 100644 (file)
@@ -29,9 +29,9 @@
 #include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
 
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/global_config.h"
 #include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/buffer_list.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/iomgr/timer.h"
 #include "src/core/lib/iomgr/timer_manager.h"
 
+GPR_GLOBAL_CONFIG_DEFINE_BOOL(grpc_abort_on_leaks, false,
+                              "A debugging aid to cause a call to abort() when "
+                              "gRPC objects are leaked past grpc_shutdown()");
+
 static gpr_mu g_mu;
 static gpr_cv g_rcv;
 static int g_shutdown;
@@ -186,8 +190,5 @@ void grpc_iomgr_unregister_object(grpc_iomgr_object* obj) {
 }
 
 bool grpc_iomgr_abort_on_leaks(void) {
-  char* env = gpr_getenv("GRPC_ABORT_ON_LEAKS");
-  bool should_we = gpr_is_true(env);
-  gpr_free(env);
-  return should_we;
+  return GPR_GLOBAL_CONFIG_GET(grpc_abort_on_leaks);
 }
index 13b5f87..728d404 100644 (file)
@@ -61,6 +61,7 @@ static void iomgr_platform_init(void) {
   winsock_init();
   grpc_iocp_init();
   grpc_pollset_global_init();
+  grpc_wsa_socket_flags_init();
 }
 
 static void iomgr_platform_flush(void) { grpc_iocp_flush(); }
index 085fea4..f0c40b4 100644 (file)
@@ -94,7 +94,7 @@ void LockfreeEvent::NotifyOn(grpc_closure* closure) {
      * sure that the shutdown error has been initialized properly before us
      * referencing it. */
     gpr_atm curr = gpr_atm_acq_load(&state_);
-    if (grpc_polling_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
       gpr_log(GPR_ERROR, "LockfreeEvent::NotifyOn: %p curr=%p closure=%p", this,
               (void*)curr, closure);
     }
@@ -160,7 +160,7 @@ bool LockfreeEvent::SetShutdown(grpc_error* shutdown_err) {
 
   while (true) {
     gpr_atm curr = gpr_atm_no_barrier_load(&state_);
-    if (grpc_polling_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
       gpr_log(GPR_ERROR, "LockfreeEvent::SetShutdown: %p curr=%p err=%s",
               &state_, (void*)curr, grpc_error_string(shutdown_err));
     }
@@ -209,7 +209,7 @@ void LockfreeEvent::SetReady() {
   while (true) {
     gpr_atm curr = gpr_atm_no_barrier_load(&state_);
 
-    if (grpc_polling_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
       gpr_log(GPR_ERROR, "LockfreeEvent::SetReady: %p curr=%p", &state_,
               (void*)curr);
     }
index ccb4c31..605692a 100644 (file)
@@ -44,6 +44,8 @@
 #elif defined(GPR_WINDOWS)
 #define GRPC_WINSOCK_SOCKET 1
 #define GRPC_WINDOWS_SOCKETUTILS 1
+#define GRPC_WINDOWS_SOCKET_ARES_EV_DRIVER 1
+#define GRPC_ARES_RESOLVE_LOCALHOST_MANUALLY 1
 #elif defined(GPR_ANDROID)
 #define GRPC_HAVE_IPV6_RECVPKTINFO 1
 #define GRPC_HAVE_IP_PKTINFO 1
 #ifdef LINUX_VERSION_CODE
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
 #define GRPC_HAVE_TCP_USER_TIMEOUT
+#ifdef __GLIBC_PREREQ
+#if !(__GLIBC_PREREQ(2, 17))
+/*
+ * TCP_USER_TIMEOUT wasn't imported to glibc until 2.17. Use Linux system
+ * header instead.
+ */
+#define GRPC_LINUX_TCP_H 1
+#endif /* __GLIBC_PREREQ(2, 17) */
+#endif /* ifdef __GLIBC_PREREQ */
 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) */
 #endif /* LINUX_VERSION_CODE */
 #ifndef __GLIBC__
index 61c3660..dffac34 100644 (file)
@@ -32,6 +32,7 @@
 
 #include "src/core/lib/gpr/useful.h"
 #include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/slice/slice_internal.h"
 
 grpc_core::TraceFlag grpc_resource_quota_trace(false, "resource_quota");
 
@@ -316,7 +317,7 @@ static bool rq_alloc(grpc_resource_quota* resource_quota) {
   while ((resource_user = rulist_pop_head(resource_quota,
                                           GRPC_RULIST_AWAITING_ALLOCATION))) {
     gpr_mu_lock(&resource_user->mu);
-    if (grpc_resource_quota_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_resource_quota_trace)) {
       gpr_log(GPR_INFO,
               "RQ: check allocation for user %p shutdown=%" PRIdPTR
               " free_pool=%" PRId64,
@@ -342,14 +343,14 @@ static bool rq_alloc(grpc_resource_quota* resource_quota) {
       resource_user->free_pool = 0;
       resource_quota->free_pool -= amt;
       rq_update_estimate(resource_quota);
-      if (grpc_resource_quota_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_resource_quota_trace)) {
         gpr_log(GPR_INFO,
                 "RQ %s %s: grant alloc %" PRId64
                 " bytes; rq_free_pool -> %" PRId64,
                 resource_quota->name, resource_user->name, amt,
                 resource_quota->free_pool);
       }
-    } else if (grpc_resource_quota_trace.enabled() &&
+    } else if (GRPC_TRACE_FLAG_ENABLED(grpc_resource_quota_trace) &&
                resource_user->free_pool >= 0) {
       gpr_log(GPR_INFO, "RQ %s %s: discard already satisfied alloc request",
               resource_quota->name, resource_user->name);
@@ -381,7 +382,7 @@ static bool rq_reclaim_from_per_user_free_pool(
       resource_user->free_pool = 0;
       resource_quota->free_pool += amt;
       rq_update_estimate(resource_quota);
-      if (grpc_resource_quota_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_resource_quota_trace)) {
         gpr_log(GPR_INFO,
                 "RQ %s %s: reclaim_from_per_user_free_pool %" PRId64
                 " bytes; rq_free_pool -> %" PRId64,
@@ -391,7 +392,7 @@ static bool rq_reclaim_from_per_user_free_pool(
       gpr_mu_unlock(&resource_user->mu);
       return true;
     } else {
-      if (grpc_resource_quota_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_resource_quota_trace)) {
         gpr_log(GPR_INFO,
                 "RQ %s %s: failed to reclaim_from_per_user_free_pool; "
                 "free_pool = %" PRId64 "; rq_free_pool = %" PRId64,
@@ -411,7 +412,7 @@ static bool rq_reclaim(grpc_resource_quota* resource_quota, bool destructive) {
                                  : GRPC_RULIST_RECLAIMER_BENIGN;
   grpc_resource_user* resource_user = rulist_pop_head(resource_quota, list);
   if (resource_user == nullptr) return false;
-  if (grpc_resource_quota_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_resource_quota_trace)) {
     gpr_log(GPR_INFO, "RQ %s %s: initiate %s reclamation", resource_quota->name,
             resource_user->name, destructive ? "destructive" : "benign");
   }
@@ -430,41 +431,43 @@ static bool rq_reclaim(grpc_resource_quota* resource_quota, bool destructive) {
  * ru_slice: a slice implementation that is backed by a grpc_resource_user
  */
 
-typedef struct {
-  grpc_slice_refcount base;
-  gpr_refcount refs;
-  grpc_resource_user* resource_user;
-  size_t size;
-} ru_slice_refcount;
+namespace grpc_core {
 
-static void ru_slice_ref(void* p) {
-  ru_slice_refcount* rc = static_cast<ru_slice_refcount*>(p);
-  gpr_ref(&rc->refs);
-}
-
-static void ru_slice_unref(void* p) {
-  ru_slice_refcount* rc = static_cast<ru_slice_refcount*>(p);
-  if (gpr_unref(&rc->refs)) {
-    grpc_resource_user_free(rc->resource_user, rc->size);
+class RuSliceRefcount {
+ public:
+  static void Destroy(void* p) {
+    auto* rc = static_cast<RuSliceRefcount*>(p);
+    rc->~RuSliceRefcount();
     gpr_free(rc);
   }
-}
+  RuSliceRefcount(grpc_resource_user* resource_user, size_t size)
+      : base_(grpc_slice_refcount::Type::REGULAR, &refs_, Destroy, this,
+              &base_),
+        resource_user_(resource_user),
+        size_(size) {
+    // Nothing to do here.
+  }
+  ~RuSliceRefcount() { grpc_resource_user_free(resource_user_, size_); }
+
+  grpc_slice_refcount* base_refcount() { return &base_; }
 
-static const grpc_slice_refcount_vtable ru_slice_vtable = {
-    ru_slice_ref, ru_slice_unref, grpc_slice_default_eq_impl,
-    grpc_slice_default_hash_impl};
+ private:
+  grpc_slice_refcount base_;
+  RefCount refs_;
+  grpc_resource_user* resource_user_;
+  size_t size_;
+};
+
+}  // namespace grpc_core
 
 static grpc_slice ru_slice_create(grpc_resource_user* resource_user,
                                   size_t size) {
-  ru_slice_refcount* rc = static_cast<ru_slice_refcount*>(
-      gpr_malloc(sizeof(ru_slice_refcount) + size));
-  rc->base.vtable = &ru_slice_vtable;
-  rc->base.sub_refcount = &rc->base;
-  gpr_ref_init(&rc->refs, 1);
-  rc->resource_user = resource_user;
-  rc->size = size;
+  auto* rc = static_cast<grpc_core::RuSliceRefcount*>(
+      gpr_malloc(sizeof(grpc_core::RuSliceRefcount) + size));
+  new (rc) grpc_core::RuSliceRefcount(resource_user, size);
   grpc_slice slice;
-  slice.refcount = &rc->base;
+
+  slice.refcount = rc->base_refcount();
   slice.data.refcounted.bytes = reinterpret_cast<uint8_t*>(rc + 1);
   slice.data.refcounted.length = size;
   return slice;
@@ -540,7 +543,7 @@ static void ru_post_destructive_reclaimer(void* ru, grpc_error* error) {
 }
 
 static void ru_shutdown(void* ru, grpc_error* error) {
-  if (grpc_resource_quota_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_resource_quota_trace)) {
     gpr_log(GPR_INFO, "RU shutdown %p", ru);
   }
   grpc_resource_user* resource_user = static_cast<grpc_resource_user*>(ru);
@@ -882,7 +885,7 @@ static void resource_user_alloc_locked(grpc_resource_user* resource_user,
                                        grpc_closure* optional_on_done) {
   ru_ref_by(resource_user, static_cast<gpr_atm>(size));
   resource_user->free_pool -= static_cast<int64_t>(size);
-  if (grpc_resource_quota_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_resource_quota_trace)) {
     gpr_log(GPR_INFO, "RQ %s %s: alloc %" PRIdPTR "; free_pool -> %" PRId64,
             resource_user->resource_quota->name, resource_user->name, size,
             resource_user->free_pool);
@@ -941,7 +944,7 @@ void grpc_resource_user_free(grpc_resource_user* resource_user, size_t size) {
   GPR_ASSERT(prior >= static_cast<long>(size));
   bool was_zero_or_negative = resource_user->free_pool <= 0;
   resource_user->free_pool += static_cast<int64_t>(size);
-  if (grpc_resource_quota_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_resource_quota_trace)) {
     gpr_log(GPR_INFO, "RQ %s %s: free %" PRIdPTR "; free_pool -> %" PRId64,
             resource_user->resource_quota->name, resource_user->name, size,
             resource_user->free_pool);
@@ -967,7 +970,7 @@ void grpc_resource_user_post_reclaimer(grpc_resource_user* resource_user,
 }
 
 void grpc_resource_user_finish_reclamation(grpc_resource_user* resource_user) {
-  if (grpc_resource_quota_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_resource_quota_trace)) {
     gpr_log(GPR_INFO, "RQ %s %s: reclamation complete",
             resource_user->resource_quota->name, resource_user->name);
   }
index 4c337a0..2101651 100644 (file)
 #include <fcntl.h>
 #include <limits.h>
 #include <netinet/in.h>
+#ifdef GRPC_LINUX_TCP_H
+#include <linux/tcp.h>
+#else
 #include <netinet/tcp.h>
+#endif
 #include <stdio.h>
 #include <string.h>
 #include <sys/socket.h>
@@ -288,7 +292,7 @@ grpc_error* grpc_set_socket_tcp_user_timeout(
   }
   if (enable) {
     extern grpc_core::TraceFlag grpc_tcp_trace;
-    if (grpc_tcp_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       gpr_log(GPR_INFO, "Enabling TCP_USER_TIMEOUT with a timeout of %d ms",
               timeout);
     }
@@ -311,7 +315,7 @@ grpc_error* grpc_set_socket_tcp_user_timeout(
   }
 #else
   extern grpc_core::TraceFlag grpc_tcp_trace;
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "TCP_USER_TIMEOUT not supported for this platform");
   }
 #endif /* GRPC_HAVE_TCP_USER_TIMEOUT */
index 999c664..c87cfa8 100644 (file)
@@ -39,6 +39,8 @@
 #include "src/core/lib/iomgr/sockaddr_windows.h"
 #include "src/core/lib/iomgr/socket_windows.h"
 
+static DWORD s_wsa_socket_flags;
+
 grpc_winsocket* grpc_winsocket_create(SOCKET socket, const char* name) {
   char* final_name;
   grpc_winsocket* r = (grpc_winsocket*)gpr_malloc(sizeof(grpc_winsocket));
@@ -181,4 +183,21 @@ int grpc_ipv6_loopback_available(void) {
   return g_ipv6_loopback_available;
 }
 
+DWORD grpc_get_default_wsa_socket_flags() { return s_wsa_socket_flags; }
+
+void grpc_wsa_socket_flags_init() {
+  s_wsa_socket_flags = WSA_FLAG_OVERLAPPED;
+  /* WSA_FLAG_NO_HANDLE_INHERIT may be not supported on the older Windows
+     versions, see
+     https://msdn.microsoft.com/en-us/library/windows/desktop/ms742212(v=vs.85).aspx
+     for details. */
+  SOCKET sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
+                          s_wsa_socket_flags | WSA_FLAG_NO_HANDLE_INHERIT);
+  if (sock != INVALID_SOCKET) {
+    /* Windows 7, Windows 2008 R2 with SP1 or later */
+    s_wsa_socket_flags |= WSA_FLAG_NO_HANDLE_INHERIT;
+    closesocket(sock);
+  }
+}
+
 #endif /* GRPC_WINSOCK_SOCKET */
index 46d7d58..5fed690 100644 (file)
 #include "src/core/lib/iomgr/closure.h"
 #include "src/core/lib/iomgr/iomgr_internal.h"
 
+#ifndef WSA_FLAG_NO_HANDLE_INHERIT
+#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
+#endif
+
 /* This holds the data for an outstanding read or write on a socket.
    The mutex to protect the concurrent access to that data is the one
    inside the winsocket wrapper. */
@@ -114,6 +118,10 @@ void grpc_socket_become_ready(grpc_winsocket* winsocket,
    The value is probed once, and cached for the life of the process. */
 int grpc_ipv6_loopback_available(void);
 
+void grpc_wsa_socket_flags_init();
+
+DWORD grpc_get_default_wsa_socket_flags();
+
 #endif
 
 #endif /* GRPC_CORE_LIB_IOMGR_SOCKET_WINDOWS_H */
index 73344b1..14a8b78 100644 (file)
@@ -64,7 +64,7 @@ static void on_alarm(void* acp, grpc_error* error) {
   int done;
   grpc_custom_socket* socket = (grpc_custom_socket*)acp;
   grpc_custom_tcp_connect* connect = socket->connector;
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     const char* str = grpc_error_string(error);
     gpr_log(GPR_INFO, "CLIENT_CONNECT: %s: on_alarm: error=%s",
             connect->addr_name, str);
@@ -146,7 +146,7 @@ static void tcp_connect(grpc_closure* closure, grpc_endpoint** ep,
   socket->listener = nullptr;
   connect->refs = 2;
 
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "CLIENT_CONNECT: %p %s: asynchronously connecting",
             socket, connect->addr_name);
   }
index 0bff74e..ad9d779 100644 (file)
@@ -108,7 +108,7 @@ done:
 static void tc_on_alarm(void* acp, grpc_error* error) {
   int done;
   async_connect* ac = static_cast<async_connect*>(acp);
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     const char* str = grpc_error_string(error);
     gpr_log(GPR_INFO, "CLIENT_CONNECT: %s: on_alarm: error=%s", ac->addr_str,
             str);
@@ -145,7 +145,7 @@ static void on_writable(void* acp, grpc_error* error) {
 
   GRPC_ERROR_REF(error);
 
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     const char* str = grpc_error_string(error);
     gpr_log(GPR_INFO, "CLIENT_CONNECT: %s: on_writable: error=%s", ac->addr_str,
             str);
@@ -328,7 +328,7 @@ void grpc_tcp_client_create_from_prepared_fd(
                     grpc_schedule_on_exec_ctx);
   ac->channel_args = grpc_channel_args_copy(channel_args);
 
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "CLIENT_CONNECT: %s: asynchronously connecting fd %p",
             ac->addr_str, fdobj);
   }
index e24431b..6669953 100644 (file)
@@ -148,7 +148,7 @@ static void tcp_connect(grpc_closure* on_done, grpc_endpoint** endpoint,
   }
 
   sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
-                   WSA_FLAG_OVERLAPPED);
+                   grpc_get_default_wsa_socket_flags());
   if (sock == INVALID_SOCKET) {
     error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket");
     goto failure;
index f7ad120..66df508 100644 (file)
@@ -88,7 +88,7 @@ static void tcp_free(grpc_custom_socket* s) {
 #define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__)
 static void tcp_unref(custom_tcp_endpoint* tcp, const char* reason,
                       const char* file, int line) {
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_ERROR,
             "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp->socket, reason,
@@ -101,7 +101,7 @@ static void tcp_unref(custom_tcp_endpoint* tcp, const char* reason,
 
 static void tcp_ref(custom_tcp_endpoint* tcp, const char* reason,
                     const char* file, int line) {
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_ERROR,
             "TCP   ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp->socket, reason,
@@ -123,7 +123,7 @@ static void tcp_ref(custom_tcp_endpoint* tcp) { gpr_ref(&tcp->refcount); }
 
 static void call_read_cb(custom_tcp_endpoint* tcp, grpc_error* error) {
   grpc_closure* cb = tcp->read_cb;
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "TCP:%p call_cb %p %p:%p", tcp->socket, cb, cb->cb,
             cb->cb_arg);
     size_t i;
@@ -169,7 +169,7 @@ static void custom_read_callback(grpc_custom_socket* socket, size_t nread,
 
 static void tcp_read_allocation_done(void* tcpp, grpc_error* error) {
   custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)tcpp;
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "TCP:%p read_allocation_done: %s", tcp->socket,
             grpc_error_string(error));
   }
@@ -185,7 +185,7 @@ static void tcp_read_allocation_done(void* tcpp, grpc_error* error) {
     grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices);
     call_read_cb(tcp, GRPC_ERROR_REF(error));
   }
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     const char* str = grpc_error_string(error);
     gpr_log(GPR_INFO, "Initiating read on %p: error=%s", tcp->socket, str);
   }
@@ -211,7 +211,7 @@ static void custom_write_callback(grpc_custom_socket* socket,
   custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)socket->endpoint;
   grpc_closure* cb = tcp->write_cb;
   tcp->write_cb = nullptr;
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     const char* str = grpc_error_string(error);
     gpr_log(GPR_INFO, "write complete on %p: error=%s", tcp->socket, str);
   }
@@ -224,7 +224,7 @@ static void endpoint_write(grpc_endpoint* ep, grpc_slice_buffer* write_slices,
   custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep;
   GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
 
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     size_t j;
 
     for (j = 0; j < write_slices->count; j++) {
@@ -280,7 +280,7 @@ static void endpoint_delete_from_pollset_set(grpc_endpoint* ep,
 static void endpoint_shutdown(grpc_endpoint* ep, grpc_error* why) {
   custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep;
   if (!tcp->shutting_down) {
-    if (grpc_tcp_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       const char* str = grpc_error_string(why);
       gpr_log(GPR_INFO, "TCP %p shutdown why=%s", tcp->socket, str);
     }
@@ -345,7 +345,7 @@ grpc_endpoint* custom_tcp_endpoint_create(grpc_custom_socket* socket,
       (custom_tcp_endpoint*)gpr_malloc(sizeof(custom_tcp_endpoint));
   grpc_core::ExecCtx exec_ctx;
 
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "Creating TCP endpoint %p", socket);
   }
   memset(tcp, 0, sizeof(custom_tcp_endpoint));
index 30305a9..b9376b3 100644 (file)
@@ -163,7 +163,7 @@ static void tcp_drop_uncovered_then_handle_write(void* arg /* grpc_tcp */,
 
 static void done_poller(void* bp, grpc_error* error_ignored) {
   backup_poller* p = static_cast<backup_poller*>(bp);
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "BACKUP_POLLER:%p destroy", p);
   }
   grpc_pollset_destroy(BACKUP_POLLER_POLLSET(p));
@@ -172,7 +172,7 @@ static void done_poller(void* bp, grpc_error* error_ignored) {
 
 static void run_poller(void* bp, grpc_error* error_ignored) {
   backup_poller* p = static_cast<backup_poller*>(bp);
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "BACKUP_POLLER:%p run", p);
   }
   gpr_mu_lock(p->pollset_mu);
@@ -188,18 +188,18 @@ static void run_poller(void* bp, grpc_error* error_ignored) {
       gpr_atm_full_cas(&g_uncovered_notifications_pending, 1, 0)) {
     gpr_mu_lock(p->pollset_mu);
     bool cas_ok = gpr_atm_full_cas(&g_backup_poller, (gpr_atm)p, 0);
-    if (grpc_tcp_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       gpr_log(GPR_INFO, "BACKUP_POLLER:%p done cas_ok=%d", p, cas_ok);
     }
     gpr_mu_unlock(p->pollset_mu);
-    if (grpc_tcp_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       gpr_log(GPR_INFO, "BACKUP_POLLER:%p shutdown", p);
     }
     grpc_pollset_shutdown(BACKUP_POLLER_POLLSET(p),
                           GRPC_CLOSURE_INIT(&p->run_poller, done_poller, p,
                                             grpc_schedule_on_exec_ctx));
   } else {
-    if (grpc_tcp_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       gpr_log(GPR_INFO, "BACKUP_POLLER:%p reschedule", p);
     }
     GRPC_CLOSURE_SCHED(&p->run_poller, GRPC_ERROR_NONE);
@@ -210,7 +210,7 @@ static void drop_uncovered(grpc_tcp* tcp) {
   backup_poller* p = (backup_poller*)gpr_atm_acq_load(&g_backup_poller);
   gpr_atm old_count =
       gpr_atm_full_fetch_add(&g_uncovered_notifications_pending, -1);
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "BACKUP_POLLER:%p uncover cnt %d->%d", p,
             static_cast<int>(old_count), static_cast<int>(old_count) - 1);
   }
@@ -228,7 +228,7 @@ static void cover_self(grpc_tcp* tcp) {
   backup_poller* p;
   gpr_atm old_count =
       gpr_atm_no_barrier_fetch_add(&g_uncovered_notifications_pending, 2);
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "BACKUP_POLLER: cover cnt %d->%d",
             static_cast<int>(old_count), 2 + static_cast<int>(old_count));
   }
@@ -236,7 +236,7 @@ static void cover_self(grpc_tcp* tcp) {
     GRPC_STATS_INC_TCP_BACKUP_POLLERS_CREATED();
     p = static_cast<backup_poller*>(
         gpr_zalloc(sizeof(*p) + grpc_pollset_size()));
-    if (grpc_tcp_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       gpr_log(GPR_INFO, "BACKUP_POLLER:%p create", p);
     }
     grpc_pollset_init(BACKUP_POLLER_POLLSET(p), &p->pollset_mu);
@@ -251,7 +251,7 @@ static void cover_self(grpc_tcp* tcp) {
       // spin waiting for backup poller
     }
   }
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "BACKUP_POLLER:%p add %p", p, tcp);
   }
   grpc_pollset_add_fd(BACKUP_POLLER_POLLSET(p), tcp->em_fd);
@@ -261,32 +261,24 @@ static void cover_self(grpc_tcp* tcp) {
 }
 
 static void notify_on_read(grpc_tcp* tcp) {
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "TCP:%p notify_on_read", tcp);
   }
   grpc_fd_notify_on_read(tcp->em_fd, &tcp->read_done_closure);
 }
 
 static void notify_on_write(grpc_tcp* tcp) {
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "TCP:%p notify_on_write", tcp);
   }
-  if (grpc_event_engine_run_in_background()) {
-    // If there is a polling engine always running in the background, there is
-    // no need to run the backup poller.
-    GRPC_CLOSURE_INIT(&tcp->write_done_closure, tcp_handle_write, tcp,
-                      grpc_schedule_on_exec_ctx);
-  } else {
+  if (!grpc_event_engine_run_in_background()) {
     cover_self(tcp);
-    GRPC_CLOSURE_INIT(&tcp->write_done_closure,
-                      tcp_drop_uncovered_then_handle_write, tcp,
-                      grpc_schedule_on_exec_ctx);
   }
   grpc_fd_notify_on_write(tcp->em_fd, &tcp->write_done_closure);
 }
 
 static void tcp_drop_uncovered_then_handle_write(void* arg, grpc_error* error) {
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "TCP:%p got_write: %s", arg, grpc_error_string(error));
   }
   drop_uncovered(static_cast<grpc_tcp*>(arg));
@@ -371,7 +363,7 @@ static void tcp_free(grpc_tcp* tcp) {
 #define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__)
 static void tcp_unref(grpc_tcp* tcp, const char* reason, const char* file,
                       int line) {
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
@@ -384,7 +376,7 @@ static void tcp_unref(grpc_tcp* tcp, const char* reason, const char* file,
 
 static void tcp_ref(grpc_tcp* tcp, const char* reason, const char* file,
                     int line) {
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "TCP   ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
@@ -417,7 +409,7 @@ static void tcp_destroy(grpc_endpoint* ep) {
 static void call_read_cb(grpc_tcp* tcp, grpc_error* error) {
   grpc_closure* cb = tcp->read_cb;
 
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "TCP:%p call_cb %p %p:%p", tcp, cb, cb->cb, cb->cb_arg);
     size_t i;
     const char* str = grpc_error_string(error);
@@ -581,7 +573,7 @@ static void tcp_do_read(grpc_tcp* tcp) {
 
 static void tcp_read_allocation_done(void* tcpp, grpc_error* error) {
   grpc_tcp* tcp = static_cast<grpc_tcp*>(tcpp);
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "TCP:%p read_allocation_done: %s", tcp,
             grpc_error_string(error));
   }
@@ -600,13 +592,13 @@ static void tcp_continue_read(grpc_tcp* tcp) {
   /* Wait for allocation only when there is no buffer left. */
   if (tcp->incoming_buffer->length == 0 &&
       tcp->incoming_buffer->count < MAX_READ_IOVEC) {
-    if (grpc_tcp_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       gpr_log(GPR_INFO, "TCP:%p alloc_slices", tcp);
     }
     grpc_resource_user_alloc_slices(&tcp->slice_allocator, target_read_size, 1,
                                     tcp->incoming_buffer);
   } else {
-    if (grpc_tcp_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       gpr_log(GPR_INFO, "TCP:%p do_read", tcp);
     }
     tcp_do_read(tcp);
@@ -615,7 +607,7 @@ static void tcp_continue_read(grpc_tcp* tcp) {
 
 static void tcp_handle_read(void* arg /* grpc_tcp */, grpc_error* error) {
   grpc_tcp* tcp = static_cast<grpc_tcp*>(arg);
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "TCP:%p got_read: %s", tcp, grpc_error_string(error));
   }
 
@@ -694,7 +686,7 @@ static bool tcp_write_with_timestamps(grpc_tcp* tcp, struct msghdr* msg,
     if (setsockopt(tcp->fd, SOL_SOCKET, SO_TIMESTAMPING,
                    static_cast<void*>(&opt), sizeof(opt)) != 0) {
       grpc_slice_buffer_reset_and_unref_internal(tcp->outgoing_buffer);
-      if (grpc_tcp_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
         gpr_log(GPR_ERROR, "Failed to set timestamping options on the socket.");
       }
       return false;
@@ -743,7 +735,7 @@ struct cmsghdr* process_timestamp(grpc_tcp* tcp, msghdr* msg,
   auto next_cmsg = CMSG_NXTHDR(msg, cmsg);
   cmsghdr* opt_stats = nullptr;
   if (next_cmsg == nullptr) {
-    if (grpc_tcp_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       gpr_log(GPR_ERROR, "Received timestamp without extended error");
     }
     return cmsg;
@@ -755,7 +747,7 @@ struct cmsghdr* process_timestamp(grpc_tcp* tcp, msghdr* msg,
     opt_stats = next_cmsg;
     next_cmsg = CMSG_NXTHDR(msg, opt_stats);
     if (next_cmsg == nullptr) {
-      if (grpc_tcp_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
         gpr_log(GPR_ERROR, "Received timestamp without extended error");
       }
       return opt_stats;
@@ -765,7 +757,7 @@ struct cmsghdr* process_timestamp(grpc_tcp* tcp, msghdr* msg,
   if (!(next_cmsg->cmsg_level == SOL_IP || next_cmsg->cmsg_level == SOL_IPV6) ||
       !(next_cmsg->cmsg_type == IP_RECVERR ||
         next_cmsg->cmsg_type == IPV6_RECVERR)) {
-    if (grpc_tcp_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       gpr_log(GPR_ERROR, "Unexpected control message");
     }
     return cmsg;
@@ -847,7 +839,7 @@ static void process_errors(grpc_tcp* tcp) {
           cmsg->cmsg_type != SCM_TIMESTAMPING) {
         /* Got a control message that is not a timestamp. Don't know how to
          * handle this. */
-        if (grpc_tcp_trace.enabled()) {
+        if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
           gpr_log(GPR_INFO,
                   "unknown control message cmsg_level:%d cmsg_type:%d",
                   cmsg->cmsg_level, cmsg->cmsg_type);
@@ -865,7 +857,7 @@ static void process_errors(grpc_tcp* tcp) {
 
 static void tcp_handle_error(void* arg /* grpc_tcp */, grpc_error* error) {
   grpc_tcp* tcp = static_cast<grpc_tcp*>(arg);
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "TCP:%p got_error: %s", tcp, grpc_error_string(error));
   }
 
@@ -884,8 +876,6 @@ static void tcp_handle_error(void* arg /* grpc_tcp */, grpc_error* error) {
    * ready. */
   grpc_fd_set_readable(tcp->em_fd);
   grpc_fd_set_writable(tcp->em_fd);
-  GRPC_CLOSURE_INIT(&tcp->error_closure, tcp_handle_error, tcp,
-                    grpc_schedule_on_exec_ctx);
   grpc_fd_notify_on_error(tcp->em_fd, &tcp->error_closure);
 }
 
@@ -1044,14 +1034,14 @@ static void tcp_handle_write(void* arg /* grpc_tcp */, grpc_error* error) {
   }
 
   if (!tcp_flush(tcp, &error)) {
-    if (grpc_tcp_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       gpr_log(GPR_INFO, "write: delayed");
     }
     notify_on_write(tcp);
   } else {
     cb = tcp->write_cb;
     tcp->write_cb = nullptr;
-    if (grpc_tcp_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       const char* str = grpc_error_string(error);
       gpr_log(GPR_INFO, "write: %s", str);
     }
@@ -1066,7 +1056,7 @@ static void tcp_write(grpc_endpoint* ep, grpc_slice_buffer* buf,
   grpc_tcp* tcp = reinterpret_cast<grpc_tcp*>(ep);
   grpc_error* error = GRPC_ERROR_NONE;
 
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     size_t i;
 
     for (i = 0; i < buf->count; i++) {
@@ -1101,12 +1091,12 @@ static void tcp_write(grpc_endpoint* ep, grpc_slice_buffer* buf,
   if (!tcp_flush(tcp, &error)) {
     TCP_REF(tcp, "write");
     tcp->write_cb = cb;
-    if (grpc_tcp_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       gpr_log(GPR_INFO, "write: delayed");
     }
     notify_on_write(tcp);
   } else {
-    if (grpc_tcp_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       const char* str = grpc_error_string(error);
       gpr_log(GPR_INFO, "write: %s", str);
     }
@@ -1248,6 +1238,16 @@ grpc_endpoint* grpc_tcp_create(grpc_fd* em_fd,
   tcp->tb_head = nullptr;
   GRPC_CLOSURE_INIT(&tcp->read_done_closure, tcp_handle_read, tcp,
                     grpc_schedule_on_exec_ctx);
+  if (grpc_event_engine_run_in_background()) {
+    // If there is a polling engine always running in the background, there is
+    // no need to run the backup poller.
+    GRPC_CLOSURE_INIT(&tcp->write_done_closure, tcp_handle_write, tcp,
+                      grpc_schedule_on_exec_ctx);
+  } else {
+    GRPC_CLOSURE_INIT(&tcp->write_done_closure,
+                      tcp_drop_uncovered_then_handle_write, tcp,
+                      grpc_schedule_on_exec_ctx);
+  }
   /* Always assume there is something on the queue to read. */
   tcp->inq = 1;
 #ifdef GRPC_HAVE_TCP_INQ
index 019b354..133847d 100644 (file)
@@ -220,7 +220,7 @@ static void finish_accept(grpc_tcp_listener* sp, grpc_custom_socket* socket) {
     GRPC_LOG_IF_ERROR("getpeername error", err);
     GRPC_ERROR_UNREF(err);
   }
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     if (peer_name_string) {
       gpr_log(GPR_INFO, "SERVER_CONNECT: %p accepted connection: %s",
               sp->server, peer_name_string);
@@ -372,7 +372,7 @@ static grpc_error* tcp_server_add_port(grpc_tcp_server* s,
     addr = &wildcard;
   }
 
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     char* port_string;
     grpc_sockaddr_to_string(&port_string, addr, 0);
     const char* str = grpc_error_string(error);
@@ -418,7 +418,7 @@ static void tcp_server_start(grpc_tcp_server* server, grpc_pollset** pollsets,
   (void)pollsets;
   (void)pollset_count;
   GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
-  if (grpc_tcp_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     gpr_log(GPR_INFO, "SERVER_START %p", server);
   }
   GPR_ASSERT(on_accept_cb);
index 824db07..0133972 100644 (file)
@@ -217,12 +217,25 @@ static void on_read(void* arg, grpc_error* err) {
       }
     }
 
+    /* For UNIX sockets, the accept call might not fill up the member sun_path
+     * of sockaddr_un, so explicitly call getsockname to get it. */
+    if (grpc_is_unix_socket(&addr)) {
+      memset(&addr, 0, sizeof(addr));
+      addr.len = static_cast<socklen_t>(sizeof(struct sockaddr_storage));
+      if (getsockname(fd, reinterpret_cast<struct sockaddr*>(addr.addr),
+                      &(addr.len)) < 0) {
+        gpr_log(GPR_ERROR, "Failed getsockname: %s", strerror(errno));
+        close(fd);
+        goto error;
+      }
+    }
+
     grpc_set_socket_no_sigpipe_if_possible(fd);
 
     addr_str = grpc_sockaddr_to_uri(&addr);
     gpr_asprintf(&name, "tcp-server-connection:%s", addr_str);
 
-    if (grpc_tcp_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       gpr_log(GPR_INFO, "SERVER_CONNECT: incoming connection: %s", addr_str);
     }
 
index b01afdc..7ac4234 100644 (file)
@@ -255,7 +255,7 @@ static grpc_error* start_accept_locked(grpc_tcp_listener* port) {
   }
 
   sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
-                   WSA_FLAG_OVERLAPPED);
+                   grpc_get_default_wsa_socket_flags());
   if (sock == INVALID_SOCKET) {
     error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket");
     goto failure;
@@ -493,7 +493,7 @@ static grpc_error* tcp_server_add_port(grpc_tcp_server* s,
   }
 
   sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
-                   WSA_FLAG_OVERLAPPED);
+                   grpc_get_default_wsa_socket_flags());
   if (sock == INVALID_SOCKET) {
     error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket");
     goto done;
index 1fb0ef8..ae6b2b6 100644 (file)
@@ -74,12 +74,26 @@ static grpc_error* set_dualstack(SOCKET sock) {
              : GRPC_WSA_ERROR(WSAGetLastError(), "setsockopt(IPV6_V6ONLY)");
 }
 
+static grpc_error* enable_socket_low_latency(SOCKET sock) {
+  int status;
+  BOOL param = TRUE;
+  status = ::setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+                        reinterpret_cast<char*>(&param), sizeof(param));
+  if (status == SOCKET_ERROR) {
+    status = WSAGetLastError();
+  }
+  return status == 0 ? GRPC_ERROR_NONE
+                     : GRPC_WSA_ERROR(status, "setsockopt(TCP_NODELAY)");
+}
+
 grpc_error* grpc_tcp_prepare_socket(SOCKET sock) {
   grpc_error* err;
   err = grpc_tcp_set_non_block(sock);
   if (err != GRPC_ERROR_NONE) return err;
   err = set_dualstack(sock);
   if (err != GRPC_ERROR_NONE) return err;
+  err = enable_socket_low_latency(sock);
+  if (err != GRPC_ERROR_NONE) return err;
   return GRPC_ERROR_NONE;
 }
 
index 6a925ad..00c1878 100644 (file)
@@ -361,7 +361,7 @@ static void timer_init(grpc_timer* timer, grpc_millis deadline,
   timer->hash_table_next = nullptr;
 #endif
 
-  if (grpc_timer_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_trace)) {
     gpr_log(GPR_INFO, "TIMER %p: SET %" PRId64 " now %" PRId64 " call %p[%p]",
             timer, deadline, grpc_core::ExecCtx::Get()->Now(), closure,
             closure->cb);
@@ -397,7 +397,7 @@ static void timer_init(grpc_timer* timer, grpc_millis deadline,
     timer->heap_index = INVALID_HEAP_INDEX;
     list_join(&shard->list, timer);
   }
-  if (grpc_timer_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_trace)) {
     gpr_log(GPR_INFO,
             "  .. add to shard %d with queue_deadline_cap=%" PRId64
             " => is_first_timer=%s",
@@ -419,7 +419,7 @@ static void timer_init(grpc_timer* timer, grpc_millis deadline,
      grpc_timer_check. */
   if (is_first_timer) {
     gpr_mu_lock(&g_shared_mutables.mu);
-    if (grpc_timer_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_trace)) {
       gpr_log(GPR_INFO, "  .. old shard min_deadline=%" PRId64,
               shard->min_deadline);
     }
@@ -463,7 +463,7 @@ static void timer_cancel(grpc_timer* timer) {
 
   timer_shard* shard = &g_shards[GPR_HASH_POINTER(timer, g_num_shards)];
   gpr_mu_lock(&shard->mu);
-  if (grpc_timer_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_trace)) {
     gpr_log(GPR_INFO, "TIMER %p: CANCEL pending=%s", timer,
             timer->pending ? "true" : "false");
   }
@@ -487,7 +487,7 @@ static void timer_cancel(grpc_timer* timer) {
 /* Rebalances the timer shard by computing a new 'queue_deadline_cap' and moving
    all relevant timers in shard->list (i.e timers with deadlines earlier than
    'queue_deadline_cap') into into shard->heap.
-   Returns 'true' if shard->heap has atleast ONE element
+   Returns 'true' if shard->heap has at least ONE element
    REQUIRES: shard->mu locked */
 static bool refill_heap(timer_shard* shard, grpc_millis now) {
   /* Compute the new queue window width and bound by the limits: */
@@ -504,7 +504,7 @@ static bool refill_heap(timer_shard* shard, grpc_millis now) {
       saturating_add(GPR_MAX(now, shard->queue_deadline_cap),
                      static_cast<grpc_millis>(deadline_delta * 1000.0));
 
-  if (grpc_timer_check_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
     gpr_log(GPR_INFO, "  .. shard[%d]->queue_deadline_cap --> %" PRId64,
             static_cast<int>(shard - g_shards), shard->queue_deadline_cap);
   }
@@ -512,7 +512,7 @@ static bool refill_heap(timer_shard* shard, grpc_millis now) {
     next = timer->next;
 
     if (timer->deadline < shard->queue_deadline_cap) {
-      if (grpc_timer_check_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
         gpr_log(GPR_INFO, "  .. add timer with deadline %" PRId64 " to heap",
                 timer->deadline);
       }
@@ -529,7 +529,7 @@ static bool refill_heap(timer_shard* shard, grpc_millis now) {
 static grpc_timer* pop_one(timer_shard* shard, grpc_millis now) {
   grpc_timer* timer;
   for (;;) {
-    if (grpc_timer_check_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
       gpr_log(GPR_INFO, "  .. shard[%d]: heap_empty=%s",
               static_cast<int>(shard - g_shards),
               grpc_timer_heap_is_empty(&shard->heap) ? "true" : "false");
@@ -539,13 +539,13 @@ static grpc_timer* pop_one(timer_shard* shard, grpc_millis now) {
       if (!refill_heap(shard, now)) return nullptr;
     }
     timer = grpc_timer_heap_top(&shard->heap);
-    if (grpc_timer_check_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
       gpr_log(GPR_INFO,
               "  .. check top timer deadline=%" PRId64 " now=%" PRId64,
               timer->deadline, now);
     }
     if (timer->deadline > now) return nullptr;
-    if (grpc_timer_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_trace)) {
       gpr_log(GPR_INFO, "TIMER %p: FIRE %" PRId64 "ms late via %s scheduler",
               timer, now - timer->deadline,
               timer->closure->scheduler->vtable->name);
@@ -569,7 +569,7 @@ static size_t pop_timers(timer_shard* shard, grpc_millis now,
   }
   *new_min_deadline = compute_min_deadline(shard);
   gpr_mu_unlock(&shard->mu);
-  if (grpc_timer_check_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
     gpr_log(GPR_INFO, "  .. shard[%d] popped %" PRIdPTR,
             static_cast<int>(shard - g_shards), n);
   }
@@ -606,7 +606,7 @@ static grpc_timer_check_result run_some_expired_timers(grpc_millis now,
     gpr_mu_lock(&g_shared_mutables.mu);
     result = GRPC_TIMERS_CHECKED_AND_EMPTY;
 
-    if (grpc_timer_check_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
       gpr_log(GPR_INFO, "  .. shard[%d]->min_deadline = %" PRId64,
               static_cast<int>(g_shard_queue[0] - g_shards),
               g_shard_queue[0]->min_deadline);
@@ -624,7 +624,7 @@ static grpc_timer_check_result run_some_expired_timers(grpc_millis now,
         result = GRPC_TIMERS_FIRED;
       }
 
-      if (grpc_timer_check_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
         gpr_log(GPR_INFO,
                 "  .. result --> %d"
                 ", shard[%d]->min_deadline %" PRId64 " --> %" PRId64
@@ -691,7 +691,7 @@ static grpc_timer_check_result timer_check(grpc_millis* next) {
     if (next != nullptr) {
       *next = GPR_MIN(*next, min_timer);
     }
-    if (grpc_timer_check_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
       gpr_log(GPR_INFO, "TIMER CHECK SKIP: now=%" PRId64 " min_timer=%" PRId64,
               now, min_timer);
     }
@@ -704,7 +704,7 @@ static grpc_timer_check_result timer_check(grpc_millis* next) {
           : GRPC_ERROR_CREATE_FROM_STATIC_STRING("Shutting down timer system");
 
   // tracing
-  if (grpc_timer_check_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
     char* next_str;
     if (next == nullptr) {
       next_str = gpr_strdup("NULL");
@@ -728,7 +728,7 @@ static grpc_timer_check_result timer_check(grpc_millis* next) {
   grpc_timer_check_result r =
       run_some_expired_timers(now, next, shutdown_error);
   // tracing
-  if (grpc_timer_check_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
     char* next_str;
     if (next == nullptr) {
       next_str = gpr_strdup("NULL");
index 4469db7..bdf5490 100644 (file)
@@ -90,7 +90,7 @@ static void start_timer_thread_and_unlock(void) {
   ++g_waiter_count;
   ++g_thread_count;
   gpr_mu_unlock(&g_mu);
-  if (grpc_timer_check_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
     gpr_log(GPR_INFO, "Spawn timer thread");
   }
   completed_thread* ct =
@@ -126,7 +126,7 @@ static void run_some_timers() {
     // if there's no thread waiting with a timeout, kick an existing untimed
     // waiter so that the next deadline is not missed
     if (!g_has_timed_waiter) {
-      if (grpc_timer_check_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
         gpr_log(GPR_INFO, "kick untimed waiter");
       }
       gpr_cv_signal(&g_cv_wait);
@@ -134,7 +134,7 @@ static void run_some_timers() {
     gpr_mu_unlock(&g_mu);
   }
   // without our lock, flush the exec_ctx
-  if (grpc_timer_check_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
     gpr_log(GPR_INFO, "flush exec_ctx");
   }
   grpc_core::ExecCtx::Get()->Flush();
@@ -189,7 +189,7 @@ static bool wait_until(grpc_millis next) {
         g_has_timed_waiter = true;
         g_timed_waiter_deadline = next;
 
-        if (grpc_timer_check_trace.enabled()) {
+        if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
           grpc_millis wait_time = next - grpc_core::ExecCtx::Get()->Now();
           gpr_log(GPR_INFO, "sleep for a %" PRId64 " milliseconds", wait_time);
         }
@@ -198,7 +198,8 @@ static bool wait_until(grpc_millis next) {
       }
     }
 
-    if (grpc_timer_check_trace.enabled() && next == GRPC_MILLIS_INF_FUTURE) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace) &&
+        next == GRPC_MILLIS_INF_FUTURE) {
       gpr_log(GPR_INFO, "sleep until kicked");
     }
 
@@ -210,7 +211,7 @@ static bool wait_until(grpc_millis next) {
     gpr_cv_wait(&g_cv_wait, &g_mu,
                 grpc_millis_to_timespec(next, GPR_CLOCK_MONOTONIC));
 
-    if (grpc_timer_check_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
       gpr_log(GPR_INFO, "wait ended: was_timed:%d kicked:%d",
               my_timed_waiter_generation == g_timed_waiter_generation,
               g_kicked);
@@ -255,7 +256,7 @@ static void timer_main_loop() {
 
            Consequently, we can just sleep forever here and be happy at some
            saved wakeup cycles. */
-        if (grpc_timer_check_trace.enabled()) {
+        if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
           gpr_log(GPR_INFO, "timers not checked: expect another thread to");
         }
         next = GRPC_MILLIS_INF_FUTURE;
@@ -281,7 +282,7 @@ static void timer_thread_cleanup(completed_thread* ct) {
   ct->next = g_completed_threads;
   g_completed_threads = ct;
   gpr_mu_unlock(&g_mu);
-  if (grpc_timer_check_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
     gpr_log(GPR_INFO, "End timer thread");
   }
 }
@@ -327,18 +328,18 @@ void grpc_timer_manager_init(void) {
 
 static void stop_threads(void) {
   gpr_mu_lock(&g_mu);
-  if (grpc_timer_check_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
     gpr_log(GPR_INFO, "stop timer threads: threaded=%d", g_threaded);
   }
   if (g_threaded) {
     g_threaded = false;
     gpr_cv_broadcast(&g_cv_wait);
-    if (grpc_timer_check_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
       gpr_log(GPR_INFO, "num timer threads: %d", g_thread_count);
     }
     while (g_thread_count > 0) {
       gpr_cv_wait(&g_cv_shutdown, &g_mu, gpr_inf_future(GPR_CLOCK_MONOTONIC));
-      if (grpc_timer_check_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) {
         gpr_log(GPR_INFO, "num timer threads: %d", g_thread_count);
       }
       gc_completed_threads();
index b19ad9f..37689fe 100644 (file)
@@ -31,7 +31,8 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gprpp/global_config.h"
+#include "src/core/lib/profiling/timers.h"
 
 typedef enum { BEGIN = '{', END = '}', MARK = '.' } marker_type;
 
@@ -74,11 +75,16 @@ static __thread int g_thread_id;
 static int g_next_thread_id;
 static int g_writing_enabled = 1;
 
+GPR_GLOBAL_CONFIG_DEFINE_STRING(grpc_latency_trace, "latency_trace.txt",
+                                "Output file name for latency trace")
+
 static const char* output_filename() {
   if (output_filename_or_null == NULL) {
-    output_filename_or_null = gpr_getenv("LATENCY_TRACE");
-    if (output_filename_or_null == NULL ||
-        strlen(output_filename_or_null) == 0) {
+    grpc_core::UniquePtr<char> value =
+        GPR_GLOBAL_CONFIG_GET(grpc_latency_trace);
+    if (strlen(value.get()) > 0) {
+      output_filename_or_null = value.release();
+    } else {
       output_filename_or_null = "latency_trace.txt";
     }
   }
index 8443ee0..ef60165 100644 (file)
@@ -21,8 +21,8 @@
 #include <string.h>
 
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/gpr/arena.h"
 #include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/arena.h"
 #include "src/core/lib/gprpp/ref_counted.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
 #include "src/core/lib/security/context/security_context.h"
@@ -102,9 +102,9 @@ grpc_client_security_context::~grpc_client_security_context() {
 }
 
 grpc_client_security_context* grpc_client_security_context_create(
-    gpr_arena* arena, grpc_call_credentials* creds) {
-  return new (gpr_arena_alloc(arena, sizeof(grpc_client_security_context)))
-      grpc_client_security_context(creds != nullptr ? creds->Ref() : nullptr);
+    grpc_core::Arena* arena, grpc_call_credentials* creds) {
+  return arena->New<grpc_client_security_context>(
+      creds != nullptr ? creds->Ref() : nullptr);
 }
 
 void grpc_client_security_context_destroy(void* ctx) {
@@ -123,9 +123,8 @@ grpc_server_security_context::~grpc_server_security_context() {
 }
 
 grpc_server_security_context* grpc_server_security_context_create(
-    gpr_arena* arena) {
-  return new (gpr_arena_alloc(arena, sizeof(grpc_server_security_context)))
-      grpc_server_security_context();
+    grpc_core::Arena* arena) {
+  return arena->New<grpc_server_security_context>();
 }
 
 void grpc_server_security_context_destroy(void* ctx) {
index b43ee5e..b199108 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <grpc/support/port_platform.h>
 
+#include "src/core/lib/gprpp/arena.h"
 #include "src/core/lib/gprpp/ref_counted.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
 #include "src/core/lib/iomgr/pollset.h"
@@ -28,8 +29,6 @@
 
 extern grpc_core::DebugOnlyTraceFlag grpc_trace_auth_context_refcount;
 
-struct gpr_arena;
-
 /* --- grpc_auth_context ---
 
    High level authentication context object. Can optionally be chained. */
@@ -121,7 +120,7 @@ struct grpc_client_security_context {
 };
 
 grpc_client_security_context* grpc_client_security_context_create(
-    gpr_arena* arena, grpc_call_credentials* creds);
+    grpc_core::Arena* arena, grpc_call_credentials* creds);
 void grpc_client_security_context_destroy(void* ctx);
 
 /* --- grpc_server_security_context ---
@@ -137,7 +136,7 @@ struct grpc_server_security_context {
 };
 
 grpc_server_security_context* grpc_server_security_context_create(
-    gpr_arena* arena);
+    grpc_core::Arena* arena);
 void grpc_server_security_context_destroy(void* ctx);
 
 /* --- Channel args for auth context --- */
index 70fe45e..df1d05c 100644 (file)
@@ -160,7 +160,7 @@ static char* redact_private_key(const char* json_key) {
 
 grpc_call_credentials* grpc_service_account_jwt_access_credentials_create(
     const char* json_key, gpr_timespec token_lifetime, void* reserved) {
-  if (grpc_api_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_api_trace)) {
     char* clean_json = redact_private_key(json_key);
     gpr_log(GPR_INFO,
             "grpc_service_account_jwt_access_credentials_create("
index d573c30..5b120ed 100644 (file)
@@ -624,9 +624,8 @@ static int verify_jwt_signature(EVP_PKEY* key, const char* alg,
     gpr_log(GPR_ERROR, "EVP_DigestVerifyUpdate failed.");
     goto end;
   }
-  if (EVP_DigestVerifyFinal(
-          md_ctx, const_cast<uint8_t*>(GRPC_SLICE_START_PTR(signature)),
-          GRPC_SLICE_LENGTH(signature)) != 1) {
+  if (EVP_DigestVerifyFinal(md_ctx, GRPC_SLICE_START_PTR(signature),
+                            GRPC_SLICE_LENGTH(signature)) != 1) {
     gpr_log(GPR_ERROR, "JWT signature verification failed.");
     goto end;
   }
index b9af757..b001868 100644 (file)
@@ -459,7 +459,7 @@ grpc_call_credentials* grpc_google_refresh_token_credentials_create(
     const char* json_refresh_token, void* reserved) {
   grpc_auth_refresh_token token =
       grpc_auth_refresh_token_create_from_string(json_refresh_token);
-  if (grpc_api_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_api_trace)) {
     char* loggable_token = create_loggable_refresh_token(&token);
     gpr_log(GPR_INFO,
             "grpc_refresh_token_credentials_create(json_refresh_token=%s, "
index 59fecbc..333366d 100644 (file)
@@ -119,7 +119,7 @@ static void plugin_md_request_metadata_ready(void* request,
                               GRPC_EXEC_CTX_FLAG_THREAD_RESOURCE_LOOP);
   grpc_plugin_credentials::pending_request* r =
       static_cast<grpc_plugin_credentials::pending_request*>(request);
-  if (grpc_plugin_credentials_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_plugin_credentials_trace)) {
     gpr_log(GPR_INFO,
             "plugin_credentials[%p]: request %p: plugin returned "
             "asynchronously",
@@ -132,7 +132,7 @@ static void plugin_md_request_metadata_ready(void* request,
     grpc_error* error =
         process_plugin_result(r, md, num_md, status, error_details);
     GRPC_CLOSURE_SCHED(r->on_request_metadata, error);
-  } else if (grpc_plugin_credentials_trace.enabled()) {
+  } else if (GRPC_TRACE_FLAG_ENABLED(grpc_plugin_credentials_trace)) {
     gpr_log(GPR_INFO,
             "plugin_credentials[%p]: request %p: plugin was previously "
             "cancelled",
@@ -162,7 +162,7 @@ bool grpc_plugin_credentials::get_request_metadata(
     pending_requests_ = request;
     gpr_mu_unlock(&mu_);
     // Invoke the plugin.  The callback holds a ref to us.
-    if (grpc_plugin_credentials_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_plugin_credentials_trace)) {
       gpr_log(GPR_INFO, "plugin_credentials[%p]: request %p: invoking plugin",
               this, request);
     }
@@ -174,7 +174,7 @@ bool grpc_plugin_credentials::get_request_metadata(
     if (!plugin_.get_metadata(
             plugin_.state, context, plugin_md_request_metadata_ready, request,
             creds_md, &num_creds_md, &status, &error_details)) {
-      if (grpc_plugin_credentials_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_plugin_credentials_trace)) {
         gpr_log(GPR_INFO,
                 "plugin_credentials[%p]: request %p: plugin will return "
                 "asynchronously",
@@ -189,7 +189,7 @@ bool grpc_plugin_credentials::get_request_metadata(
     // asynchronously by plugin_cancel_get_request_metadata(), so return
     // false.  Otherwise, process the result.
     if (request->cancelled) {
-      if (grpc_plugin_credentials_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_plugin_credentials_trace)) {
         gpr_log(GPR_INFO,
                 "plugin_credentials[%p]: request %p was cancelled, error "
                 "will be returned asynchronously",
@@ -197,7 +197,7 @@ bool grpc_plugin_credentials::get_request_metadata(
       }
       retval = false;
     } else {
-      if (grpc_plugin_credentials_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_plugin_credentials_trace)) {
         gpr_log(GPR_INFO,
                 "plugin_credentials[%p]: request %p: plugin returned "
                 "synchronously",
@@ -223,7 +223,7 @@ void grpc_plugin_credentials::cancel_get_request_metadata(
   for (pending_request* pending_request = pending_requests_;
        pending_request != nullptr; pending_request = pending_request->next) {
     if (pending_request->md_array == md_array) {
-      if (grpc_plugin_credentials_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_plugin_credentials_trace)) {
         gpr_log(GPR_INFO, "plugin_credentials[%p]: cancelling request %p", this,
                 pending_request);
       }
index 924fa8a..82d5bf6 100644 (file)
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/global_config.h"
 #include "src/core/lib/gprpp/inlined_vector.h"
 #include "src/core/lib/iomgr/load_file.h"
 
+GPR_GLOBAL_CONFIG_DEFINE_STRING(grpc_system_ssl_roots_dir, "",
+                                "Custom directory to SSL Roots");
+
 namespace grpc_core {
 namespace {
 
@@ -139,10 +142,9 @@ grpc_slice CreateRootCertsBundle(const char* certs_directory) {
 grpc_slice LoadSystemRootCerts() {
   grpc_slice result = grpc_empty_slice();
   // Prioritize user-specified custom directory if flag is set.
-  char* custom_dir = gpr_getenv("GRPC_SYSTEM_SSL_ROOTS_DIR");
-  if (custom_dir != nullptr) {
-    result = CreateRootCertsBundle(custom_dir);
-    gpr_free(custom_dir);
+  UniquePtr<char> custom_dir = GPR_GLOBAL_CONFIG_GET(grpc_system_ssl_roots_dir);
+  if (strlen(custom_dir.get()) > 0) {
+    result = CreateRootCertsBundle(custom_dir.get());
   }
   // If the custom directory is empty/invalid/not specified, fallback to
   // distribution-specific directory.
index 96a1960..47c0ad5 100644 (file)
@@ -28,7 +28,6 @@
 #include "src/core/ext/transport/chttp2/alpn/alpn.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/handshaker.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/iomgr/load_file.h"
index ad492f3..e76f4f1 100644 (file)
@@ -314,7 +314,6 @@ class grpc_ssl_server_security_connector
   bool try_fetch_ssl_server_credentials() {
     grpc_ssl_server_certificate_config* certificate_config = nullptr;
     bool status;
-
     if (!has_cert_config_fetcher()) return false;
 
     grpc_ssl_server_credentials* server_creds =
@@ -374,7 +373,9 @@ class grpc_ssl_server_security_connector
     options.num_alpn_protocols = static_cast<uint16_t>(num_alpn_protocols);
     tsi_result result = tsi_create_ssl_server_handshaker_factory_with_options(
         &options, &new_handshaker_factory);
-    gpr_free((void*)options.pem_key_cert_pairs);
+    grpc_tsi_ssl_pem_key_cert_pairs_destroy(
+        const_cast<tsi_ssl_pem_key_cert_pair*>(options.pem_key_cert_pairs),
+        options.num_key_cert_pairs);
     gpr_free((void*)alpn_protocol_strings);
 
     if (result != TSI_OK) {
index c9af5ca..cb0d543 100644 (file)
@@ -27,9 +27,9 @@
 
 #include "src/core/ext/transport/chttp2/alpn/alpn.h"
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/global_config.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
 #include "src/core/lib/iomgr/load_file.h"
 #include "src/core/lib/security/context/security_context.h"
@@ -45,11 +45,16 @@ static const char* installed_roots_path =
     INSTALL_PREFIX "/share/grpc/roots.pem";
 #endif
 
-/** Environment variable used as a flag to enable/disable loading system root
+/** Config variable that points to the default SSL roots file. This file
+   must be a PEM encoded file with all the roots such as the one that can be
+   downloaded from https://pki.google.com/roots.pem.  */
+GPR_GLOBAL_CONFIG_DEFINE_STRING(grpc_default_ssl_roots_file_path, "",
+                                "Path to the default SSL roots file.");
+
+/** Config variable used as a flag to enable/disable loading system root
     certificates from the OS trust store. */
-#ifndef GRPC_NOT_USE_SYSTEM_SSL_ROOTS_ENV_VAR
-#define GRPC_NOT_USE_SYSTEM_SSL_ROOTS_ENV_VAR "GRPC_NOT_USE_SYSTEM_SSL_ROOTS"
-#endif
+GPR_GLOBAL_CONFIG_DEFINE_BOOL(grpc_not_use_system_ssl_roots, false,
+                              "Disable loading system root certificates.");
 
 #ifndef TSI_OPENSSL_ALPN_SUPPORT
 #define TSI_OPENSSL_ALPN_SUPPORT 1
@@ -65,20 +70,22 @@ void grpc_set_ssl_roots_override_callback(grpc_ssl_roots_override_callback cb) {
 
 /* -- Cipher suites. -- */
 
-/* Defines the cipher suites that we accept by default. All these cipher suites
-   are compliant with HTTP2. */
-#define GRPC_SSL_CIPHER_SUITES     \
-  "ECDHE-ECDSA-AES128-GCM-SHA256:" \
-  "ECDHE-ECDSA-AES256-GCM-SHA384:" \
-  "ECDHE-RSA-AES128-GCM-SHA256:"   \
-  "ECDHE-RSA-AES256-GCM-SHA384"
-
 static gpr_once cipher_suites_once = GPR_ONCE_INIT;
 static const char* cipher_suites = nullptr;
 
+// All cipher suites for default are compliant with HTTP2.
+GPR_GLOBAL_CONFIG_DEFINE_STRING(
+    grpc_ssl_cipher_suites,
+    "ECDHE-ECDSA-AES128-GCM-SHA256:"
+    "ECDHE-ECDSA-AES256-GCM-SHA384:"
+    "ECDHE-RSA-AES128-GCM-SHA256:"
+    "ECDHE-RSA-AES256-GCM-SHA384",
+    "A colon separated list of cipher suites to use with OpenSSL")
+
 static void init_cipher_suites(void) {
-  char* overridden = gpr_getenv("GRPC_SSL_CIPHER_SUITES");
-  cipher_suites = overridden != nullptr ? overridden : GRPC_SSL_CIPHER_SUITES;
+  grpc_core::UniquePtr<char> value =
+      GPR_GLOBAL_CONFIG_GET(grpc_ssl_cipher_suites);
+  cipher_suites = value.release();
 }
 
 /* --- Util --- */
@@ -428,17 +435,14 @@ const char* DefaultSslRootStore::GetPemRootCerts() {
 
 grpc_slice DefaultSslRootStore::ComputePemRootCerts() {
   grpc_slice result = grpc_empty_slice();
-  char* not_use_system_roots_env_value =
-      gpr_getenv(GRPC_NOT_USE_SYSTEM_SSL_ROOTS_ENV_VAR);
-  const bool not_use_system_roots = gpr_is_true(not_use_system_roots_env_value);
-  gpr_free(not_use_system_roots_env_value);
-  // First try to load the roots from the environment.
-  char* default_root_certs_path =
-      gpr_getenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR);
-  if (default_root_certs_path != nullptr) {
-    GRPC_LOG_IF_ERROR("load_file",
-                      grpc_load_file(default_root_certs_path, 1, &result));
-    gpr_free(default_root_certs_path);
+  const bool not_use_system_roots =
+      GPR_GLOBAL_CONFIG_GET(grpc_not_use_system_ssl_roots);
+  // First try to load the roots from the configuration.
+  UniquePtr<char> default_root_certs_path =
+      GPR_GLOBAL_CONFIG_GET(grpc_default_ssl_roots_file_path);
+  if (strlen(default_root_certs_path.get()) > 0) {
+    GRPC_LOG_IF_ERROR(
+        "load_file", grpc_load_file(default_root_certs_path.get(), 1, &result));
   }
   // Try overridden roots if needed.
   grpc_ssl_roots_override_result ovrd_res = GRPC_SSL_ROOTS_OVERRIDE_FAIL;
index 080e277..1765a34 100644 (file)
@@ -26,6 +26,7 @@
 #include <grpc/grpc_security.h>
 #include <grpc/slice_buffer.h>
 
+#include "src/core/lib/gprpp/global_config.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
 #include "src/core/lib/iomgr/error.h"
 #include "src/core/lib/security/security_connector/security_connector.h"
 #include "src/core/tsi/transport_security.h"
 #include "src/core/tsi/transport_security_interface.h"
 
-/* --- Util. --- */
+GPR_GLOBAL_CONFIG_DECLARE_STRING(grpc_default_ssl_roots_file_path);
+GPR_GLOBAL_CONFIG_DECLARE_BOOL(grpc_not_use_system_ssl_roots);
+
+/* --- Util --- */
 
 /* --- URL schemes. --- */
 #define GRPC_SSL_URL_SCHEME "https"
index f90c92e..0c40dd7 100644 (file)
@@ -92,7 +92,7 @@ struct call_data {
   }
 
   grpc_call_stack* owning_call;
-  grpc_call_combiner* call_combiner;
+  grpc_core::CallCombiner* call_combiner;
   grpc_core::RefCountedPtr<grpc_call_credentials> creds;
   grpc_slice host = grpc_empty_slice();
   grpc_slice method = grpc_empty_slice();
@@ -270,11 +270,9 @@ static void send_security_metadata(grpc_call_element* elem,
     GRPC_ERROR_UNREF(error);
   } else {
     // Async return; register cancellation closure with call combiner.
-    grpc_call_combiner_set_notify_on_cancel(
-        calld->call_combiner,
-        GRPC_CLOSURE_INIT(&calld->get_request_metadata_cancel_closure,
-                          cancel_get_request_metadata, elem,
-                          grpc_schedule_on_exec_ctx));
+    calld->call_combiner->SetNotifyOnCancel(GRPC_CLOSURE_INIT(
+        &calld->get_request_metadata_cancel_closure,
+        cancel_get_request_metadata, elem, grpc_schedule_on_exec_ctx));
   }
 }
 
@@ -345,11 +343,9 @@ static void auth_start_transport_stream_op_batch(
         GRPC_ERROR_UNREF(error);
       } else {
         // Async return; register cancellation closure with call combiner.
-        grpc_call_combiner_set_notify_on_cancel(
-            calld->call_combiner,
-            GRPC_CLOSURE_INIT(&calld->check_call_host_cancel_closure,
-                              cancel_check_call_host, elem,
-                              grpc_schedule_on_exec_ctx));
+        calld->call_combiner->SetNotifyOnCancel(GRPC_CLOSURE_INIT(
+            &calld->check_call_host_cancel_closure, cancel_check_call_host,
+            elem, grpc_schedule_on_exec_ctx));
       }
       gpr_free(call_host);
       return; /* early exit */
index 2a86249..0aac7d8 100644 (file)
@@ -113,7 +113,7 @@ static void destroy(secure_endpoint* ep) { grpc_core::Delete(ep); }
   secure_endpoint_ref((ep), (reason), __FILE__, __LINE__)
 static void secure_endpoint_unref(secure_endpoint* ep, const char* reason,
                                   const char* file, int line) {
-  if (grpc_trace_secure_endpoint.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_secure_endpoint)) {
     gpr_atm val = gpr_atm_no_barrier_load(&ep->ref.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "SECENDP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep, reason, val,
@@ -126,7 +126,7 @@ static void secure_endpoint_unref(secure_endpoint* ep, const char* reason,
 
 static void secure_endpoint_ref(secure_endpoint* ep, const char* reason,
                                 const char* file, int line) {
-  if (grpc_trace_secure_endpoint.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_secure_endpoint)) {
     gpr_atm val = gpr_atm_no_barrier_load(&ep->ref.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "SECENDP   ref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep, reason, val,
@@ -155,7 +155,7 @@ static void flush_read_staging_buffer(secure_endpoint* ep, uint8_t** cur,
 }
 
 static void call_read_cb(secure_endpoint* ep, grpc_error* error) {
-  if (grpc_trace_secure_endpoint.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_secure_endpoint)) {
     size_t i;
     for (i = 0; i < ep->read_buffer->count; i++) {
       char* data = grpc_dump_slice(ep->read_buffer->slices[i],
@@ -292,7 +292,7 @@ static void endpoint_write(grpc_endpoint* secure_ep, grpc_slice_buffer* slices,
 
   grpc_slice_buffer_reset_and_unref_internal(&ep->output_buffer);
 
-  if (grpc_trace_secure_endpoint.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_secure_endpoint)) {
     for (i = 0; i < slices->count; i++) {
       char* data =
           grpc_dump_slice(slices->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII);
index 81b9c2c..43509e6 100644 (file)
@@ -74,7 +74,7 @@ struct call_data {
 
   ~call_data() { GRPC_ERROR_UNREF(recv_initial_metadata_error); }
 
-  grpc_call_combiner* call_combiner;
+  grpc_core::CallCombiner* call_combiner;
   grpc_call_stack* owning_call;
   grpc_transport_stream_op_batch* recv_initial_metadata_batch;
   grpc_closure* original_recv_initial_metadata_ready;
@@ -219,8 +219,7 @@ static void recv_initial_metadata_ready(void* arg, grpc_error* error) {
       // to drop the call combiner early if we get cancelled.
       GRPC_CLOSURE_INIT(&calld->cancel_closure, cancel_call, elem,
                         grpc_schedule_on_exec_ctx);
-      grpc_call_combiner_set_notify_on_cancel(calld->call_combiner,
-                                              &calld->cancel_closure);
+      calld->call_combiner->SetNotifyOnCancel(&calld->cancel_closure);
       GRPC_CALL_STACK_REF(calld->owning_call, "server_auth_metadata");
       calld->md = metadata_batch_to_md_array(
           batch->payload->recv_initial_metadata.recv_initial_metadata);
index ac935f1..2bf2b0f 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <string.h>
 
+#include "src/core/lib/gprpp/memory.h"
 #include "src/core/lib/gprpp/ref_counted.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 
@@ -67,17 +68,10 @@ void grpc_slice_unref(grpc_slice slice) {
 
 /* grpc_slice_from_static_string support structure - a refcount that does
    nothing */
-static void noop_ref(void* unused) {}
-static void noop_unref(void* unused) {}
-
-static const grpc_slice_refcount_vtable noop_refcount_vtable = {
-    noop_ref, noop_unref, grpc_slice_default_eq_impl,
-    grpc_slice_default_hash_impl};
-static grpc_slice_refcount noop_refcount = {&noop_refcount_vtable,
-                                            &noop_refcount};
+static grpc_slice_refcount NoopRefcount;
 
 size_t grpc_slice_memory_usage(grpc_slice s) {
-  if (s.refcount == nullptr || s.refcount == &noop_refcount) {
+  if (s.refcount == nullptr || s.refcount == &NoopRefcount) {
     return 0;
   } else {
     return s.data.refcounted.length;
@@ -86,7 +80,7 @@ size_t grpc_slice_memory_usage(grpc_slice s) {
 
 grpc_slice grpc_slice_from_static_buffer(const void* s, size_t len) {
   grpc_slice slice;
-  slice.refcount = &noop_refcount;
+  slice.refcount = &NoopRefcount;
   slice.data.refcounted.bytes = (uint8_t*)s;
   slice.data.refcounted.length = len;
   return slice;
@@ -96,45 +90,43 @@ grpc_slice grpc_slice_from_static_string(const char* s) {
   return grpc_slice_from_static_buffer(s, strlen(s));
 }
 
+namespace grpc_core {
+
 /* grpc_slice_new support structures - we create a refcount object extended
    with the user provided data pointer & destroy function */
-typedef struct new_slice_refcount {
-  grpc_slice_refcount rc;
-  gpr_refcount refs;
-  void (*user_destroy)(void*);
-  void* user_data;
-} new_slice_refcount;
-
-static void new_slice_ref(void* p) {
-  new_slice_refcount* r = static_cast<new_slice_refcount*>(p);
-  gpr_ref(&r->refs);
-}
-
-static void new_slice_unref(void* p) {
-  new_slice_refcount* r = static_cast<new_slice_refcount*>(p);
-  if (gpr_unref(&r->refs)) {
-    r->user_destroy(r->user_data);
-    gpr_free(r);
+class NewSliceRefcount {
+ public:
+  static void Destroy(void* arg) {
+    Delete(static_cast<NewSliceRefcount*>(arg));
   }
-}
 
-static const grpc_slice_refcount_vtable new_slice_vtable = {
-    new_slice_ref, new_slice_unref, grpc_slice_default_eq_impl,
-    grpc_slice_default_hash_impl};
+  NewSliceRefcount(void (*destroy)(void*), void* user_data)
+      : rc_(grpc_slice_refcount::Type::REGULAR, &refs_, Destroy, this, &rc_),
+        user_destroy_(destroy),
+        user_data_(user_data) {}
+
+  GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
+
+  grpc_slice_refcount* base_refcount() { return &rc_; }
+
+ private:
+  ~NewSliceRefcount() { user_destroy_(user_data_); }
+
+  grpc_slice_refcount rc_;
+  RefCount refs_;
+  void (*user_destroy_)(void*);
+  void* user_data_;
+};
+
+}  // namespace grpc_core
 
 grpc_slice grpc_slice_new_with_user_data(void* p, size_t len,
                                          void (*destroy)(void*),
                                          void* user_data) {
   grpc_slice slice;
-  new_slice_refcount* rc =
-      static_cast<new_slice_refcount*>(gpr_malloc(sizeof(new_slice_refcount)));
-  gpr_ref_init(&rc->refs, 1);
-  rc->rc.vtable = &new_slice_vtable;
-  rc->rc.sub_refcount = &rc->rc;
-  rc->user_destroy = destroy;
-  rc->user_data = user_data;
-
-  slice.refcount = &rc->rc;
+  slice.refcount =
+      grpc_core::New<grpc_core::NewSliceRefcount>(destroy, user_data)
+          ->base_refcount();
   slice.data.refcounted.bytes = static_cast<uint8_t*>(p);
   slice.data.refcounted.length = len;
   return slice;
@@ -145,46 +137,45 @@ grpc_slice grpc_slice_new(void* p, size_t len, void (*destroy)(void*)) {
   return grpc_slice_new_with_user_data(p, len, destroy, p);
 }
 
+namespace grpc_core {
 /* grpc_slice_new_with_len support structures - we create a refcount object
    extended with the user provided data pointer & destroy function */
-typedef struct new_with_len_slice_refcount {
-  grpc_slice_refcount rc;
-  gpr_refcount refs;
-  void* user_data;
-  size_t user_length;
-  void (*user_destroy)(void*, size_t);
-} new_with_len_slice_refcount;
-
-static void new_with_len_ref(void* p) {
-  new_with_len_slice_refcount* r = static_cast<new_with_len_slice_refcount*>(p);
-  gpr_ref(&r->refs);
-}
 
-static void new_with_len_unref(void* p) {
-  new_with_len_slice_refcount* r = static_cast<new_with_len_slice_refcount*>(p);
-  if (gpr_unref(&r->refs)) {
-    r->user_destroy(r->user_data, r->user_length);
-    gpr_free(r);
+class NewWithLenSliceRefcount {
+ public:
+  static void Destroy(void* arg) {
+    Delete(static_cast<NewWithLenSliceRefcount*>(arg));
   }
-}
 
-static const grpc_slice_refcount_vtable new_with_len_vtable = {
-    new_with_len_ref, new_with_len_unref, grpc_slice_default_eq_impl,
-    grpc_slice_default_hash_impl};
+  NewWithLenSliceRefcount(void (*destroy)(void*, size_t), void* user_data,
+                          size_t user_length)
+      : rc_(grpc_slice_refcount::Type::REGULAR, &refs_, Destroy, this, &rc_),
+        user_data_(user_data),
+        user_length_(user_length),
+        user_destroy_(destroy) {}
+
+  GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
+
+  grpc_slice_refcount* base_refcount() { return &rc_; }
+
+ private:
+  ~NewWithLenSliceRefcount() { user_destroy_(user_data_, user_length_); }
+
+  grpc_slice_refcount rc_;
+  RefCount refs_;
+  void* user_data_;
+  size_t user_length_;
+  void (*user_destroy_)(void*, size_t);
+};
+
+}  // namespace grpc_core
 
 grpc_slice grpc_slice_new_with_len(void* p, size_t len,
                                    void (*destroy)(void*, size_t)) {
   grpc_slice slice;
-  new_with_len_slice_refcount* rc = static_cast<new_with_len_slice_refcount*>(
-      gpr_malloc(sizeof(new_with_len_slice_refcount)));
-  gpr_ref_init(&rc->refs, 1);
-  rc->rc.vtable = &new_with_len_vtable;
-  rc->rc.sub_refcount = &rc->rc;
-  rc->user_destroy = destroy;
-  rc->user_data = p;
-  rc->user_length = len;
-
-  slice.refcount = &rc->rc;
+  slice.refcount =
+      grpc_core::New<grpc_core::NewWithLenSliceRefcount>(destroy, p, len)
+          ->base_refcount();
   slice.data.refcounted.bytes = static_cast<uint8_t*>(p);
   slice.data.refcounted.length = len;
   return slice;
@@ -203,39 +194,28 @@ grpc_slice grpc_slice_from_copied_string(const char* source) {
 
 namespace {
 
-struct MallocRefCount {
-  MallocRefCount(const grpc_slice_refcount_vtable* vtable) {
-    base.vtable = vtable;
-    base.sub_refcount = &base;
+class MallocRefCount {
+ public:
+  static void Destroy(void* arg) {
+    MallocRefCount* r = static_cast<MallocRefCount*>(arg);
+    r->~MallocRefCount();
+    gpr_free(r);
   }
 
-  void Ref() { refs.Ref(); }
-  void Unref() {
-    if (refs.Unref()) {
-      gpr_free(this);
-    }
-  }
+  MallocRefCount()
+      : base_(grpc_slice_refcount::Type::REGULAR, &refs_, Destroy, this,
+              &base_) {}
+  ~MallocRefCount() = default;
+
+  grpc_slice_refcount* base_refcount() { return &base_; }
 
-  grpc_slice_refcount base;
-  grpc_core::RefCount refs;
+ private:
+  grpc_slice_refcount base_;
+  grpc_core::RefCount refs_;
 };
 
 }  // namespace
 
-static void malloc_ref(void* p) {
-  MallocRefCount* r = static_cast<MallocRefCount*>(p);
-  r->Ref();
-}
-
-static void malloc_unref(void* p) {
-  MallocRefCount* r = static_cast<MallocRefCount*>(p);
-  r->Unref();
-}
-
-static const grpc_slice_refcount_vtable malloc_vtable = {
-    malloc_ref, malloc_unref, grpc_slice_default_eq_impl,
-    grpc_slice_default_hash_impl};
-
 grpc_slice grpc_slice_malloc_large(size_t length) {
   grpc_slice slice;
 
@@ -248,14 +228,16 @@ grpc_slice grpc_slice_malloc_large(size_t length) {
      refcount is a malloc_refcount
      bytes is an array of bytes of the requested length
      Both parts are placed in the same allocation returned from gpr_malloc */
-  void* data =
+  auto* rc =
       static_cast<MallocRefCount*>(gpr_malloc(sizeof(MallocRefCount) + length));
 
-  auto* rc = new (data) MallocRefCount(&malloc_vtable);
+  /* Initial refcount on rc is 1 - and it's up to the caller to release
+     this reference. */
+  new (rc) MallocRefCount();
 
   /* Build up the slice to be returned. */
   /* The slices refcount points back to the allocated block. */
-  slice.refcount = &rc->base;
+  slice.refcount = rc->base_refcount();
   /* The data bytes are placed immediately after the refcount struct */
   slice.data.refcounted.bytes = reinterpret_cast<uint8_t*>(rc + 1);
   /* And the length of the block is set to the requested length */
@@ -286,7 +268,7 @@ grpc_slice grpc_slice_sub_no_ref(grpc_slice source, size_t begin, size_t end) {
     GPR_ASSERT(source.data.refcounted.length >= end);
 
     /* Build the result */
-    subset.refcount = source.refcount->sub_refcount;
+    subset.refcount = source.refcount->sub_refcount();
     /* Point into the source array */
     subset.data.refcounted.bytes = source.data.refcounted.bytes + begin;
     subset.data.refcounted.length = end - begin;
@@ -312,7 +294,7 @@ grpc_slice grpc_slice_sub(grpc_slice source, size_t begin, size_t end) {
   } else {
     subset = grpc_slice_sub_no_ref(source, begin, end);
     /* Bump the refcount */
-    subset.refcount->vtable->ref(subset.refcount);
+    subset.refcount->Ref();
   }
   return subset;
 }
@@ -340,23 +322,23 @@ grpc_slice grpc_slice_split_tail_maybe_ref(grpc_slice* source, size_t split,
       tail.data.inlined.length = static_cast<uint8_t>(tail_length);
       memcpy(tail.data.inlined.bytes, source->data.refcounted.bytes + split,
              tail_length);
-      source->refcount = source->refcount->sub_refcount;
+      source->refcount = source->refcount->sub_refcount();
     } else {
       /* Build the result */
       switch (ref_whom) {
         case GRPC_SLICE_REF_TAIL:
-          tail.refcount = source->refcount->sub_refcount;
-          source->refcount = &noop_refcount;
+          tail.refcount = source->refcount->sub_refcount();
+          source->refcount = &NoopRefcount;
           break;
         case GRPC_SLICE_REF_HEAD:
-          tail.refcount = &noop_refcount;
-          source->refcount = source->refcount->sub_refcount;
+          tail.refcount = &NoopRefcount;
+          source->refcount = source->refcount->sub_refcount();
           break;
         case GRPC_SLICE_REF_BOTH:
-          tail.refcount = source->refcount->sub_refcount;
-          source->refcount = source->refcount->sub_refcount;
+          tail.refcount = source->refcount->sub_refcount();
+          source->refcount = source->refcount->sub_refcount();
           /* Bump the refcount */
-          tail.refcount->vtable->ref(tail.refcount);
+          tail.refcount->Ref();
           break;
       }
       /* Point into the source array */
@@ -392,20 +374,20 @@ grpc_slice grpc_slice_split_head(grpc_slice* source, size_t split) {
     head.refcount = nullptr;
     head.data.inlined.length = static_cast<uint8_t>(split);
     memcpy(head.data.inlined.bytes, source->data.refcounted.bytes, split);
-    source->refcount = source->refcount->sub_refcount;
+    source->refcount = source->refcount->sub_refcount();
     source->data.refcounted.bytes += split;
     source->data.refcounted.length -= split;
   } else {
     GPR_ASSERT(source->data.refcounted.length >= split);
 
     /* Build the result */
-    head.refcount = source->refcount->sub_refcount;
+    head.refcount = source->refcount->sub_refcount();
     /* Bump the refcount */
-    head.refcount->vtable->ref(head.refcount);
+    head.refcount->Ref();
     /* Point into the source array */
     head.data.refcounted.bytes = source->data.refcounted.bytes;
     head.data.refcounted.length = split;
-    source->refcount = source->refcount->sub_refcount;
+    source->refcount = source->refcount->sub_refcount();
     source->data.refcounted.bytes += split;
     source->data.refcounted.length -= split;
   }
@@ -421,8 +403,9 @@ int grpc_slice_default_eq_impl(grpc_slice a, grpc_slice b) {
 }
 
 int grpc_slice_eq(grpc_slice a, grpc_slice b) {
-  if (a.refcount && b.refcount && a.refcount->vtable == b.refcount->vtable) {
-    return a.refcount->vtable->eq(a, b);
+  if (a.refcount && b.refcount &&
+      a.refcount->GetType() == b.refcount->GetType()) {
+    return a.refcount->Eq(a, b);
   }
   return grpc_slice_default_eq_impl(a, b);
 }
index 1f1c08b..111d3c9 100644 (file)
 #define GROW(x) (3 * (x) / 2)
 
 static void maybe_embiggen(grpc_slice_buffer* sb) {
+  if (sb->count == 0) {
+    sb->slices = sb->base_slices;
+  }
+
   /* How far away from sb->base_slices is sb->slices pointer */
   size_t slice_offset = static_cast<size_t>(sb->slices - sb->base_slices);
   size_t slice_count = sb->count + slice_offset;
@@ -177,6 +181,7 @@ void grpc_slice_buffer_reset_and_unref_internal(grpc_slice_buffer* sb) {
 
   sb->count = 0;
   sb->length = 0;
+  sb->slices = sb->base_slices;
 }
 
 void grpc_slice_buffer_reset_and_unref(grpc_slice_buffer* sb) {
index 0eef38d..0f190c1 100644 (file)
@@ -27,6 +27,7 @@
 #include <grpc/support/log.h>
 
 #include "src/core/lib/gpr/murmur_hash.h"
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/iomgr/iomgr_internal.h" /* for iomgr_abort_on_leaks() */
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
 #define TABLE_IDX(hash, capacity) (((hash) >> LOG2_SHARD_COUNT) % (capacity))
 #define SHARD_IDX(hash) ((hash) & ((1 << LOG2_SHARD_COUNT) - 1))
 
-typedef struct interned_slice_refcount {
-  grpc_slice_refcount base;
-  grpc_slice_refcount sub;
-  size_t length;
-  gpr_atm refcnt;
-  uint32_t hash;
-  struct interned_slice_refcount* bucket_next;
-} interned_slice_refcount;
+using grpc_core::InternedSliceRefcount;
 
 typedef struct slice_shard {
   gpr_mu mu;
-  interned_slice_refcount** strs;
+  InternedSliceRefcount** strs;
   size_t count;
   size_t capacity;
 } slice_shard;
 
 /* hash seed: decided at initialization time */
-static uint32_t g_hash_seed;
+uint32_t g_hash_seed;
 static int g_forced_hash_seed = 0;
 
 static slice_shard g_shards[SHARD_COUNT];
@@ -69,73 +63,35 @@ typedef struct {
 static static_metadata_hash_ent
     static_metadata_hash[4 * GRPC_STATIC_MDSTR_COUNT];
 static uint32_t max_static_metadata_hash_probe;
-static uint32_t static_metadata_hash_values[GRPC_STATIC_MDSTR_COUNT];
+uint32_t grpc_static_metadata_hash_values[GRPC_STATIC_MDSTR_COUNT];
 
-static void interned_slice_ref(void* p) {
-  interned_slice_refcount* s = static_cast<interned_slice_refcount*>(p);
-  GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&s->refcnt, 1) > 0);
-}
+namespace grpc_core {
 
-static void interned_slice_destroy(interned_slice_refcount* s) {
-  slice_shard* shard = &g_shards[SHARD_IDX(s->hash)];
-  gpr_mu_lock(&shard->mu);
-  GPR_ASSERT(0 == gpr_atm_no_barrier_load(&s->refcnt));
-  interned_slice_refcount** prev_next;
-  interned_slice_refcount* cur;
-  for (prev_next = &shard->strs[TABLE_IDX(s->hash, shard->capacity)],
+InternedSliceRefcount::~InternedSliceRefcount() {
+  slice_shard* shard = &g_shards[SHARD_IDX(this->hash)];
+  MutexLock lock(&shard->mu);
+  InternedSliceRefcount** prev_next;
+  InternedSliceRefcount* cur;
+  for (prev_next = &shard->strs[TABLE_IDX(this->hash, shard->capacity)],
       cur = *prev_next;
-       cur != s; prev_next = &cur->bucket_next, cur = cur->bucket_next)
+       cur != this; prev_next = &cur->bucket_next, cur = cur->bucket_next)
     ;
   *prev_next = cur->bucket_next;
   shard->count--;
-  gpr_free(s);
-  gpr_mu_unlock(&shard->mu);
-}
-
-static void interned_slice_unref(void* p) {
-  interned_slice_refcount* s = static_cast<interned_slice_refcount*>(p);
-  if (1 == gpr_atm_full_fetch_add(&s->refcnt, -1)) {
-    interned_slice_destroy(s);
-  }
-}
-
-static void interned_slice_sub_ref(void* p) {
-  interned_slice_ref((static_cast<char*>(p)) -
-                     offsetof(interned_slice_refcount, sub));
-}
-
-static void interned_slice_sub_unref(void* p) {
-  interned_slice_unref((static_cast<char*>(p)) -
-                       offsetof(interned_slice_refcount, sub));
-}
-
-static uint32_t interned_slice_hash(grpc_slice slice) {
-  interned_slice_refcount* s =
-      reinterpret_cast<interned_slice_refcount*>(slice.refcount);
-  return s->hash;
-}
-
-static int interned_slice_eq(grpc_slice a, grpc_slice b) {
-  return a.refcount == b.refcount;
 }
 
-static const grpc_slice_refcount_vtable interned_slice_vtable = {
-    interned_slice_ref, interned_slice_unref, interned_slice_eq,
-    interned_slice_hash};
-static const grpc_slice_refcount_vtable interned_slice_sub_vtable = {
-    interned_slice_sub_ref, interned_slice_sub_unref,
-    grpc_slice_default_eq_impl, grpc_slice_default_hash_impl};
+}  // namespace grpc_core
 
 static void grow_shard(slice_shard* shard) {
   GPR_TIMER_SCOPE("grow_strtab", 0);
 
   size_t capacity = shard->capacity * 2;
   size_t i;
-  interned_slice_refcount** strtab;
-  interned_slice_refcount *s, *next;
+  InternedSliceRefcount** strtab;
+  InternedSliceRefcount *s, *next;
 
-  strtab = static_cast<interned_slice_refcount**>(
-      gpr_zalloc(sizeof(interned_slice_refcount*) * capacity));
+  strtab = static_cast<InternedSliceRefcount**>(
+      gpr_zalloc(sizeof(InternedSliceRefcount*) * capacity));
 
   for (i = 0; i < shard->capacity; i++) {
     for (s = shard->strs[i]; s; s = next) {
@@ -150,7 +106,7 @@ static void grow_shard(slice_shard* shard) {
   shard->capacity = capacity;
 }
 
-static grpc_slice materialize(interned_slice_refcount* s) {
+static grpc_slice materialize(InternedSliceRefcount* s) {
   grpc_slice slice;
   slice.refcount = &s->base;
   slice.data.refcounted.bytes = reinterpret_cast<uint8_t*>(s + 1);
@@ -164,7 +120,7 @@ uint32_t grpc_slice_default_hash_impl(grpc_slice s) {
 }
 
 uint32_t grpc_static_slice_hash(grpc_slice s) {
-  return static_metadata_hash_values[GRPC_STATIC_METADATA_INDEX(s)];
+  return grpc_static_metadata_hash_values[GRPC_STATIC_METADATA_INDEX(s)];
 }
 
 int grpc_static_slice_eq(grpc_slice a, grpc_slice b) {
@@ -173,7 +129,7 @@ int grpc_static_slice_eq(grpc_slice a, grpc_slice b) {
 
 uint32_t grpc_slice_hash(grpc_slice s) {
   return s.refcount == nullptr ? grpc_slice_default_hash_impl(s)
-                               : s.refcount->vtable->hash(s);
+                               : s.refcount->Hash(s);
 }
 
 grpc_slice grpc_slice_maybe_static_intern(grpc_slice slice,
@@ -197,8 +153,9 @@ grpc_slice grpc_slice_maybe_static_intern(grpc_slice slice,
 }
 
 bool grpc_slice_is_interned(const grpc_slice& slice) {
-  return (slice.refcount && slice.refcount->vtable == &interned_slice_vtable) ||
-         GRPC_IS_STATIC_METADATA_STRING(slice);
+  return (slice.refcount &&
+          (slice.refcount->GetType() == grpc_slice_refcount::Type::INTERNED ||
+           GRPC_IS_STATIC_METADATA_STRING(slice)));
 }
 
 grpc_slice grpc_slice_intern(grpc_slice slice) {
@@ -208,6 +165,7 @@ grpc_slice grpc_slice_intern(grpc_slice slice) {
   }
 
   uint32_t hash = grpc_slice_hash(slice);
+
   for (uint32_t i = 0; i <= max_static_metadata_hash_probe; i++) {
     static_metadata_hash_ent ent =
         static_metadata_hash[(hash + i) % GPR_ARRAY_SIZE(static_metadata_hash)];
@@ -217,7 +175,7 @@ grpc_slice grpc_slice_intern(grpc_slice slice) {
     }
   }
 
-  interned_slice_refcount* s;
+  InternedSliceRefcount* s;
   slice_shard* shard = &g_shards[SHARD_IDX(hash)];
 
   gpr_mu_lock(&shard->mu);
@@ -226,14 +184,7 @@ grpc_slice grpc_slice_intern(grpc_slice slice) {
   size_t idx = TABLE_IDX(hash, shard->capacity);
   for (s = shard->strs[idx]; s; s = s->bucket_next) {
     if (s->hash == hash && grpc_slice_eq(slice, materialize(s))) {
-      if (gpr_atm_no_barrier_fetch_add(&s->refcnt, 1) == 0) {
-        /* If we get here, we've added a ref to something that was about to
-         * die - drop it immediately.
-         * The *only* possible path here (given the shard mutex) should be to
-         * drop from one ref back to zero - assert that with a CAS */
-        GPR_ASSERT(gpr_atm_rel_cas(&s->refcnt, 1, 0));
-        /* and treat this as if we were never here... sshhh */
-      } else {
+      if (s->refcnt.RefIfNonZero()) {
         gpr_mu_unlock(&shard->mu);
         return materialize(s);
       }
@@ -242,27 +193,20 @@ grpc_slice grpc_slice_intern(grpc_slice slice) {
 
   /* not found: create a new string */
   /* string data goes after the internal_string header */
-  s = static_cast<interned_slice_refcount*>(
+  s = static_cast<InternedSliceRefcount*>(
       gpr_malloc(sizeof(*s) + GRPC_SLICE_LENGTH(slice)));
-  gpr_atm_rel_store(&s->refcnt, 1);
-  s->length = GRPC_SLICE_LENGTH(slice);
-  s->hash = hash;
-  s->base.vtable = &interned_slice_vtable;
-  s->base.sub_refcount = &s->sub;
-  s->sub.vtable = &interned_slice_sub_vtable;
-  s->sub.sub_refcount = &s->sub;
-  s->bucket_next = shard->strs[idx];
-  shard->strs[idx] = s;
-  memcpy(s + 1, GRPC_SLICE_START_PTR(slice), GRPC_SLICE_LENGTH(slice));
 
+  new (s) grpc_core::InternedSliceRefcount(GRPC_SLICE_LENGTH(slice), hash,
+                                           shard->strs[idx]);
+  memcpy(reinterpret_cast<char*>(s + 1), GRPC_SLICE_START_PTR(slice),
+         GRPC_SLICE_LENGTH(slice));
+  shard->strs[idx] = s;
   shard->count++;
-
   if (shard->count > shard->capacity * 2) {
     grow_shard(shard);
   }
 
   gpr_mu_unlock(&shard->mu);
-
   return materialize(s);
 }
 
@@ -280,7 +224,7 @@ void grpc_slice_intern_init(void) {
     gpr_mu_init(&shard->mu);
     shard->count = 0;
     shard->capacity = INITIAL_SHARD_CAPACITY;
-    shard->strs = static_cast<interned_slice_refcount**>(
+    shard->strs = static_cast<InternedSliceRefcount**>(
         gpr_zalloc(sizeof(*shard->strs) * shard->capacity));
   }
   for (size_t i = 0; i < GPR_ARRAY_SIZE(static_metadata_hash); i++) {
@@ -289,13 +233,13 @@ void grpc_slice_intern_init(void) {
   }
   max_static_metadata_hash_probe = 0;
   for (size_t i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) {
-    static_metadata_hash_values[i] =
+    grpc_static_metadata_hash_values[i] =
         grpc_slice_default_hash_impl(grpc_static_slice_table[i]);
     for (size_t j = 0; j < GPR_ARRAY_SIZE(static_metadata_hash); j++) {
-      size_t slot = (static_metadata_hash_values[i] + j) %
+      size_t slot = (grpc_static_metadata_hash_values[i] + j) %
                     GPR_ARRAY_SIZE(static_metadata_hash);
       if (static_metadata_hash[slot].idx == GRPC_STATIC_MDSTR_COUNT) {
-        static_metadata_hash[slot].hash = static_metadata_hash_values[i];
+        static_metadata_hash[slot].hash = grpc_static_metadata_hash_values[i];
         static_metadata_hash[slot].idx = static_cast<uint32_t>(i);
         if (j > max_static_metadata_hash_probe) {
           max_static_metadata_hash_probe = static_cast<uint32_t>(j);
@@ -315,8 +259,7 @@ void grpc_slice_intern_shutdown(void) {
       gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata strings were leaked",
               shard->count);
       for (size_t j = 0; j < shard->capacity; j++) {
-        for (interned_slice_refcount* s = shard->strs[j]; s;
-             s = s->bucket_next) {
+        for (InternedSliceRefcount* s = shard->strs[j]; s; s = s->bucket_next) {
           char* text =
               grpc_dump_slice(materialize(s), GPR_DUMP_HEX | GPR_DUMP_ASCII);
           gpr_log(GPR_DEBUG, "LEAKED: %s", text);
index 0e50866..a9f6087 100644 (file)
 
 #include <grpc/slice.h>
 #include <grpc/slice_buffer.h>
+#include <string.h>
+
+#include "src/core/lib/gpr/murmur_hash.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+// Interned slices have specific fast-path operations for hashing. To inline
+// these operations, we need to forward declare them here.
+extern uint32_t grpc_static_metadata_hash_values[GRPC_STATIC_MDSTR_COUNT];
+extern uint32_t g_hash_seed;
+
+// grpc_slice_refcount : A reference count for grpc_slice.
+//
+// Non-inlined grpc_slice objects are refcounted. Historically this was
+// implemented via grpc_slice_refcount, a C-style polymorphic class using a
+// manually managed vtable of operations. Subclasses would define their own
+// vtable; the 'virtual' methods (ref, unref, equals and hash) would simply call
+// the function pointers in the vtable as necessary.
+//
+// Unfortunately, this leads to some inefficiencies in the generated code that
+// can be improved upon. For example, equality checking for interned slices is a
+// simple equality check on the refcount pointer. With the vtable approach, this
+// would translate to roughly the following (high-level) instructions:
+//
+// grpc_slice_equals(slice1, slice2):
+//   load vtable->eq -> eq_func
+//   call eq_func(slice1, slice2)
+//
+// interned_slice_equals(slice1, slice2)
+//   load slice1.ref -> r1
+//   load slice2.ref -> r2
+//   cmp r1, r2 -> retval
+//   ret retval
+//
+// This leads to a function call for a function defined in another translation
+// unit, which imposes memory barriers, which reduces the compiler's ability to
+// optimize (in addition to the added overhead of call/ret). Additionally, it
+// may be harder to reason about branch prediction when we're jumping to
+// essentially arbitrarily provided function pointers.
+//
+// In addition, it is arguable that while virtualization was helpful for
+// Equals()/Hash() methods, that it was fundamentally unnecessary for
+// Ref()/Unref().
+//
+// Instead, grpc_slice_refcount provides the same functionality as the C-style
+// virtual class, but in a de-virtualized manner - Eq(), Hash(), Ref() and
+// Unref() are provided within this header file. Fastpaths for Eq()/Hash()
+// (interned and static metadata slices), as well as the Ref() operation, can
+// all be inlined without any memory barriers.
+//
+// It does this by:
+// 1. Using grpc_core::RefCount<> (header-only) for Ref/Unref. Two special cases
+//    need support: No-op ref/unref (eg. static metadata slices) and stream
+//    slice references (where all the slices share the streamref). This is in
+//    addition to the normal case of '1 slice, 1 ref'.
+//    To support these cases, we explicitly track a nullable pointer to the
+//    underlying RefCount<>. No-op ref/unref is used by checking the pointer for
+//    null, and doing nothing if it is. Both stream slice refs and 'normal'
+//    slices use the same path for Ref/Unref (by targeting the non-null
+//    pointer).
+//
+// 2. introducing the notion of grpc_slice_refcount::Type. This describes if a
+//    slice ref is used by a static metadata slice, an interned slice, or other
+//    slices. We switch on the slice ref type in order to provide fastpaths for
+//    Equals() and Hash().
+//
+// In total, this saves us roughly 1-2% latency for unary calls, with smaller
+// calls benefitting. The effect is present, but not as useful, for larger calls
+// where the cost of sending the data dominates.
+struct grpc_slice_refcount {
+ public:
+  enum class Type {
+    STATIC,    // Refcount for a static metadata slice.
+    INTERNED,  // Refcount for an interned slice.
+    REGULAR    // Refcount for non-static-metadata, non-interned slices.
+  };
+  typedef void (*DestroyerFn)(void*);
+
+  grpc_slice_refcount() = default;
+
+  explicit grpc_slice_refcount(grpc_slice_refcount* sub) : sub_refcount_(sub) {}
+  // Regular constructor for grpc_slice_refcount.
+  //
+  // Parameters:
+  //  1. grpc_slice_refcount::Type type
+  //  Whether we are the refcount for a static
+  //  metadata slice, an interned slice, or any other kind of slice.
+  //
+  //  2. RefCount* ref
+  //  The pointer to the actual underlying grpc_core::RefCount. Rather than
+  //  performing struct offset computations as in the original implementation to
+  //  get to the refcount, which requires a virtual method, we devirtualize by
+  //  using a nullable pointer to allow a single pair of Ref/Unref methods.
+  //
+  //  3. DestroyerFn destroyer_fn
+  //  Called when the refcount goes to 0, with destroyer_arg as parameter.
+  //
+  //  4. void* destroyer_arg
+  //  Argument for the virtualized destructor.
+  //
+  //  5. grpc_slice_refcount* sub
+  //  Argument used for interned slices.
+  grpc_slice_refcount(grpc_slice_refcount::Type type, grpc_core::RefCount* ref,
+                      DestroyerFn destroyer_fn, void* destroyer_arg,
+                      grpc_slice_refcount* sub)
+      : ref_(ref),
+        ref_type_(type),
+        sub_refcount_(sub),
+        dest_fn_(destroyer_fn),
+        destroy_fn_arg_(destroyer_arg) {}
+  // Initializer for static refcounts.
+  grpc_slice_refcount(grpc_slice_refcount* sub, Type type)
+      : ref_type_(type), sub_refcount_(sub) {}
+
+  Type GetType() const { return ref_type_; }
+
+  int Eq(const grpc_slice& a, const grpc_slice& b);
+
+  uint32_t Hash(const grpc_slice& slice);
+  void Ref() {
+    if (ref_ == nullptr) return;
+    ref_->RefNonZero();
+  }
+  void Unref() {
+    if (ref_ == nullptr) return;
+    if (ref_->Unref()) {
+      dest_fn_(destroy_fn_arg_);
+    }
+  }
+
+  grpc_slice_refcount* sub_refcount() const { return sub_refcount_; }
+
+ private:
+  grpc_core::RefCount* ref_ = nullptr;
+  const Type ref_type_ = Type::REGULAR;
+  grpc_slice_refcount* sub_refcount_ = this;
+  DestroyerFn dest_fn_ = nullptr;
+  void* destroy_fn_arg_ = nullptr;
+};
+
+namespace grpc_core {
+
+struct InternedSliceRefcount {
+  static void Destroy(void* arg) {
+    auto* rc = static_cast<InternedSliceRefcount*>(arg);
+    rc->~InternedSliceRefcount();
+    gpr_free(rc);
+  }
+
+  InternedSliceRefcount(size_t length, uint32_t hash,
+                        InternedSliceRefcount* bucket_next)
+      : base(grpc_slice_refcount::Type::INTERNED, &refcnt, Destroy, this, &sub),
+        sub(grpc_slice_refcount::Type::REGULAR, &refcnt, Destroy, this, &sub),
+        length(length),
+        hash(hash),
+        bucket_next(bucket_next) {}
+
+  ~InternedSliceRefcount();
+
+  grpc_slice_refcount base;
+  grpc_slice_refcount sub;
+  const size_t length;
+  RefCount refcnt;
+  const uint32_t hash;
+  InternedSliceRefcount* bucket_next;
+};
+
+}  // namespace grpc_core
+
+inline int grpc_slice_refcount::Eq(const grpc_slice& a, const grpc_slice& b) {
+  switch (ref_type_) {
+    case Type::STATIC:
+      return GRPC_STATIC_METADATA_INDEX(a) == GRPC_STATIC_METADATA_INDEX(b);
+    case Type::INTERNED:
+      return a.refcount == b.refcount;
+    case Type::REGULAR:
+      break;
+  }
+  if (GRPC_SLICE_LENGTH(a) != GRPC_SLICE_LENGTH(b)) return false;
+  if (GRPC_SLICE_LENGTH(a) == 0) return true;
+  return 0 == memcmp(GRPC_SLICE_START_PTR(a), GRPC_SLICE_START_PTR(b),
+                     GRPC_SLICE_LENGTH(a));
+}
+
+inline uint32_t grpc_slice_refcount::Hash(const grpc_slice& slice) {
+  switch (ref_type_) {
+    case Type::STATIC:
+      return ::grpc_static_metadata_hash_values[GRPC_STATIC_METADATA_INDEX(
+          slice)];
+    case Type::INTERNED:
+      return reinterpret_cast<grpc_core::InternedSliceRefcount*>(slice.refcount)
+          ->hash;
+    case Type::REGULAR:
+      break;
+  }
+  return gpr_murmur_hash3(GRPC_SLICE_START_PTR(slice), GRPC_SLICE_LENGTH(slice),
+                          g_hash_seed);
+}
 
 inline const grpc_slice& grpc_slice_ref_internal(const grpc_slice& slice) {
   if (slice.refcount) {
-    slice.refcount->vtable->ref(slice.refcount);
+    slice.refcount->Ref();
   }
   return slice;
 }
 
 inline void grpc_slice_unref_internal(const grpc_slice& slice) {
   if (slice.refcount) {
-    slice.refcount->vtable->unref(slice.refcount);
+    slice.refcount->Unref();
   }
 }
 
index 72ed830..51d1f52 100644 (file)
@@ -45,7 +45,7 @@ extern grpc_core::TraceFlag grpc_api_trace;
 /* Due to the limitations of C89's preprocessor, the arity of the var-arg list
    'nargs' must be specified. */
 #define GRPC_API_TRACE(fmt, nargs, args)                      \
-  if (grpc_api_trace.enabled()) {                             \
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_api_trace)) {              \
     gpr_log(GPR_INFO, fmt GRPC_API_TRACE_UNWRAP##nargs args); \
   }
 
index 8aaff4a..275111f 100644 (file)
 #include "src/core/lib/compression/algorithm_metadata.h"
 #include "src/core/lib/debug/stats.h"
 #include "src/core/lib/gpr/alloc.h"
-#include "src/core/lib/gpr/arena.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/arena.h"
 #include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/gprpp/ref_counted.h"
 #include "src/core/lib/iomgr/timer.h"
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/slice/slice_internal.h"
@@ -124,14 +125,12 @@ struct child_call {
 #define RECV_INITIAL_METADATA_FIRST ((gpr_atm)1)
 
 struct grpc_call {
-  grpc_call(gpr_arena* arena, const grpc_call_create_args& args)
+  grpc_call(grpc_core::Arena* arena, const grpc_call_create_args& args)
       : arena(arena),
         cq(args.cq),
         channel(args.channel),
         is_client(args.server_transport_data == nullptr),
         stream_op_payload(context) {
-    gpr_ref_init(&ext_ref, 1);
-    grpc_call_combiner_init(&call_combiner);
     for (int i = 0; i < 2; i++) {
       for (int j = 0; j < 2; j++) {
         metadata_batch[i][j].deadline = GRPC_MILLIS_INF_FUTURE;
@@ -141,12 +140,11 @@ struct grpc_call {
 
   ~grpc_call() {
     gpr_free(static_cast<void*>(const_cast<char*>(final_info.error_string)));
-    grpc_call_combiner_destroy(&call_combiner);
   }
 
-  gpr_refcount ext_ref;
-  gpr_arena* arena;
-  grpc_call_combiner call_combiner;
+  grpc_core::RefCount ext_ref;
+  grpc_core::Arena* arena;
+  grpc_core::CallCombiner call_combiner;
   grpc_completion_queue* cq;
   grpc_polling_entity pollent;
   grpc_channel* channel;
@@ -292,13 +290,13 @@ static void add_init_error(grpc_error** composite, grpc_error* new_err) {
 }
 
 void* grpc_call_arena_alloc(grpc_call* call, size_t size) {
-  return gpr_arena_alloc(call->arena, size);
+  return call->arena->Alloc(size);
 }
 
 static parent_call* get_or_create_parent_call(grpc_call* call) {
   parent_call* p = (parent_call*)gpr_atm_acq_load(&call->parent_call_atm);
   if (p == nullptr) {
-    p = new (gpr_arena_alloc(call->arena, sizeof(*p))) parent_call();
+    p = call->arena->New<parent_call>();
     if (!gpr_atm_rel_cas(&call->parent_call_atm, (gpr_atm) nullptr,
                          (gpr_atm)p)) {
       p->~parent_call();
@@ -323,16 +321,23 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args,
 
   GRPC_CHANNEL_INTERNAL_REF(args->channel, "call");
 
+  grpc_core::Arena* arena;
+  grpc_call* call;
   grpc_error* error = GRPC_ERROR_NONE;
   grpc_channel_stack* channel_stack =
       grpc_channel_get_channel_stack(args->channel);
-  grpc_call* call;
   size_t initial_size = grpc_channel_get_call_size_estimate(args->channel);
   GRPC_STATS_INC_CALL_INITIAL_SIZE(initial_size);
-  gpr_arena* arena = gpr_arena_create(initial_size);
-  call = new (gpr_arena_alloc(
-      arena, GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call)) +
-                 channel_stack->call_stack_size)) grpc_call(arena, *args);
+  size_t call_and_stack_size =
+      GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call)) +
+      channel_stack->call_stack_size;
+  size_t call_alloc_size =
+      call_and_stack_size + (args->parent ? sizeof(child_call) : 0);
+
+  std::pair<grpc_core::Arena*, void*> arena_with_call =
+      grpc_core::Arena::CreateWithAlloc(initial_size, call_alloc_size);
+  arena = arena_with_call.first;
+  call = new (arena_with_call.second) grpc_call(arena, *args);
   *out_call = call;
   grpc_slice path = grpc_empty_slice();
   if (call->is_client) {
@@ -364,8 +369,8 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args,
   bool immediately_cancel = false;
 
   if (args->parent != nullptr) {
-    call->child = new (gpr_arena_alloc(arena, sizeof(child_call)))
-        child_call(args->parent);
+    call->child = new (reinterpret_cast<char*>(arena_with_call.second) +
+                       call_and_stack_size) child_call(args->parent);
 
     GRPC_CALL_INTERNAL_REF(args->parent, "child");
     GPR_ASSERT(call->is_client);
@@ -502,9 +507,9 @@ void grpc_call_internal_unref(grpc_call* c REF_ARG) {
 static void release_call(void* call, grpc_error* error) {
   grpc_call* c = static_cast<grpc_call*>(call);
   grpc_channel* channel = c->channel;
-  gpr_arena* arena = c->arena;
+  grpc_core::Arena* arena = c->arena;
   c->~grpc_call();
-  grpc_channel_update_call_size_estimate(channel, gpr_arena_destroy(arena));
+  grpc_channel_update_call_size_estimate(channel, arena->Destroy());
   GRPC_CHANNEL_INTERNAL_UNREF(channel, "call");
 }
 
@@ -548,10 +553,10 @@ static void destroy_call(void* call, grpc_error* error) {
                                             grpc_schedule_on_exec_ctx));
 }
 
-void grpc_call_ref(grpc_call* c) { gpr_ref(&c->ext_ref); }
+void grpc_call_ref(grpc_call* c) { c->ext_ref.Ref(); }
 
 void grpc_call_unref(grpc_call* c) {
-  if (!gpr_unref(&c->ext_ref)) return;
+  if (GPR_LIKELY(!c->ext_ref.Unref())) return;
 
   GPR_TIMER_SCOPE("grpc_call_unref", 0);
 
@@ -589,7 +594,7 @@ void grpc_call_unref(grpc_call* c) {
     // holding to the call stack. Also flush the closures on exec_ctx so that
     // filters that schedule cancel notification closures on exec_ctx do not
     // need to take a ref of the call stack to guarantee closure liveness.
-    grpc_call_combiner_set_notify_on_cancel(&c->call_combiner, nullptr);
+    c->call_combiner.SetNotifyOnCancel(nullptr);
     grpc_core::ExecCtx::Get()->Flush();
   }
   GRPC_CALL_INTERNAL_UNREF(c, "destroy");
@@ -685,7 +690,7 @@ static void cancel_with_error(grpc_call* c, grpc_error* error) {
   // any in-flight asynchronous actions that may be holding the call
   // combiner.  This ensures that the cancel_stream batch can be sent
   // down the filter stack in a timely manner.
-  grpc_call_combiner_cancel(&c->call_combiner, GRPC_ERROR_REF(error));
+  c->call_combiner.Cancel(GRPC_ERROR_REF(error));
   cancel_state* state = static_cast<cancel_state*>(gpr_malloc(sizeof(*state)));
   state->call = c;
   GRPC_CLOSURE_INIT(&state->finish_batch, done_termination, state,
@@ -718,7 +723,7 @@ static void cancel_with_status(grpc_call* c, grpc_status_code status,
 }
 
 static void set_final_status(grpc_call* call, grpc_error* error) {
-  if (grpc_call_error_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_call_error_trace)) {
     gpr_log(GPR_DEBUG, "set_final_status %s", call->is_client ? "CLI" : "SVR");
     gpr_log(GPR_DEBUG, "%s", grpc_error_string(error));
   }
@@ -1069,7 +1074,7 @@ static void recv_trailing_filter(void* args, grpc_metadata_batch* b,
   publish_app_metadata(call, b, true);
 }
 
-gpr_arena* grpc_call_get_arena(grpc_call* call) { return call->arena; }
+grpc_core::Arena* grpc_call_get_arena(grpc_call* call) { return call->arena; }
 
 grpc_call_stack* grpc_call_get_call_stack(grpc_call* call) {
   return CALL_STACK_FROM_CALL(call);
@@ -1130,8 +1135,7 @@ static batch_control* reuse_or_allocate_batch_control(grpc_call* call,
     bctl->~batch_control();
     bctl->op = {};
   } else {
-    bctl = new (gpr_arena_alloc(call->arena, sizeof(batch_control)))
-        batch_control();
+    bctl = call->arena->New<batch_control>();
     *pslot = bctl;
   }
   bctl->call = call;
@@ -1221,7 +1225,7 @@ static void post_batch_completion(batch_control* bctl) {
 }
 
 static void finish_batch_step(batch_control* bctl) {
-  if (gpr_unref(&bctl->steps_to_complete)) {
+  if (GPR_UNLIKELY(gpr_unref(&bctl->steps_to_complete))) {
     post_batch_completion(bctl);
   }
 }
@@ -1276,7 +1280,7 @@ static void receiving_slice_ready(void* bctlp, grpc_error* error) {
   }
 
   if (error != GRPC_ERROR_NONE) {
-    if (grpc_trace_operation_failures.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_operation_failures)) {
       GRPC_LOG_IF_ERROR("receiving_slice_ready", GRPC_ERROR_REF(error));
     }
     call->receiving_stream.reset();
@@ -1400,7 +1404,7 @@ static void validate_filtered_metadata(batch_control* bctl) {
 
     GPR_ASSERT(call->encodings_accepted_by_peer != 0);
     if (!GPR_BITGET(call->encodings_accepted_by_peer, compression_algorithm)) {
-      if (grpc_compression_trace.enabled()) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_compression_trace)) {
         const char* algo_name = nullptr;
         grpc_compression_algorithm_name(compression_algorithm, &algo_name);
         gpr_log(GPR_ERROR,
@@ -1559,7 +1563,10 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
           goto done_with_error;
         }
         /* process compression level */
-        memset(&call->compression_md, 0, sizeof(call->compression_md));
+        grpc_metadata& compression_md = call->compression_md;
+        compression_md.key = grpc_empty_slice();
+        compression_md.value = grpc_empty_slice();
+        compression_md.flags = 0;
         size_t additional_metadata_count = 0;
         grpc_compression_level effective_compression_level =
             GRPC_COMPRESS_LEVEL_NONE;
@@ -1582,8 +1589,8 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
                   call, effective_compression_level);
           /* the following will be picked up by the compress filter and used
            * as the call's compression algorithm. */
-          call->compression_md.key = GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST;
-          call->compression_md.value = grpc_compression_algorithm_slice(calgo);
+          compression_md.key = GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST;
+          compression_md.value = grpc_compression_algorithm_slice(calgo);
           additional_metadata_count++;
         }
 
@@ -1597,8 +1604,7 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
         if (!prepare_application_metadata(
                 call, static_cast<int>(op->data.send_initial_metadata.count),
                 op->data.send_initial_metadata.metadata, 0, call->is_client,
-                &call->compression_md,
-                static_cast<int>(additional_metadata_count))) {
+                &compression_md, static_cast<int>(additional_metadata_count))) {
           error = GRPC_CALL_ERROR_INVALID_METADATA;
           goto done_with_error;
         }
index bd7295f..15392fe 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/core/lib/channel/context.h"
+#include "src/core/lib/gprpp/arena.h"
 #include "src/core/lib/surface/api_trace.h"
 
 #include <grpc/grpc.h>
@@ -72,7 +73,7 @@ void grpc_call_internal_unref(grpc_call* call);
 #define GRPC_CALL_INTERNAL_UNREF(call, reason) grpc_call_internal_unref(call)
 #endif
 
-gpr_arena* grpc_call_get_arena(grpc_call* call);
+grpc_core::Arena* grpc_call_get_arena(grpc_call* call);
 
 grpc_call_stack* grpc_call_get_call_stack(grpc_call* call);
 
@@ -101,7 +102,11 @@ void grpc_call_context_set(grpc_call* call, grpc_context_index elem,
 void* grpc_call_context_get(grpc_call* call, grpc_context_index elem);
 
 #define GRPC_CALL_LOG_BATCH(sev, call, ops, nops, tag) \
-  if (grpc_api_trace.enabled()) grpc_call_log_batch(sev, call, ops, nops, tag)
+  do {                                                 \
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_api_trace)) {     \
+      grpc_call_log_batch(sev, call, ops, nops, tag);  \
+    }                                                  \
+  } while (0)
 
 uint8_t grpc_call_is_client(grpc_call* call);
 
index 7f20b1d..55e9e34 100644 (file)
@@ -29,7 +29,6 @@
 
 void grpc_call_details_init(grpc_call_details* cd) {
   GRPC_API_TRACE("grpc_call_details_init(cd=%p)", 1, (cd));
-  memset(cd, 0, sizeof(*cd));
   cd->method = grpc_empty_slice();
   cd->host = grpc_empty_slice();
 }
index 7d67920..e796071 100644 (file)
@@ -411,13 +411,16 @@ static const cq_vtable g_cq_vtable[] = {
 
 grpc_core::TraceFlag grpc_cq_pluck_trace(false, "queue_pluck");
 
-#define GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, event)                       \
-  if (grpc_api_trace.enabled() && (grpc_cq_pluck_trace.enabled() ||        \
-                                   (event)->type != GRPC_QUEUE_TIMEOUT)) { \
-    char* _ev = grpc_event_string(event);                                  \
-    gpr_log(GPR_INFO, "RETURN_EVENT[%p]: %s", cq, _ev);                    \
-    gpr_free(_ev);                                                         \
-  }
+#define GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, event)      \
+  do {                                                    \
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_api_trace) &&        \
+        (GRPC_TRACE_FLAG_ENABLED(grpc_cq_pluck_trace) ||  \
+         (event)->type != GRPC_QUEUE_TIMEOUT)) {          \
+      char* _ev = grpc_event_string(event);               \
+      gpr_log(GPR_INFO, "RETURN_EVENT[%p]: %s", cq, _ev); \
+      gpr_free(_ev);                                      \
+    }                                                     \
+  } while (0)
 
 static void on_pollset_shutdown_done(void* cq, grpc_error* error);
 
@@ -572,7 +575,7 @@ int grpc_get_cq_poll_num(grpc_completion_queue* cq) {
 #ifndef NDEBUG
 void grpc_cq_internal_ref(grpc_completion_queue* cq, const char* reason,
                           const char* file, int line) {
-  if (grpc_trace_cq_refcount.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_cq_refcount)) {
     gpr_atm val = gpr_atm_no_barrier_load(&cq->owning_refs.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "CQ:%p   ref %" PRIdPTR " -> %" PRIdPTR " %s", cq, val, val + 1,
@@ -592,7 +595,7 @@ static void on_pollset_shutdown_done(void* arg, grpc_error* error) {
 #ifndef NDEBUG
 void grpc_cq_internal_unref(grpc_completion_queue* cq, const char* reason,
                             const char* file, int line) {
-  if (grpc_trace_cq_refcount.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_cq_refcount)) {
     gpr_atm val = gpr_atm_no_barrier_load(&cq->owning_refs.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "CQ:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", cq, val, val - 1,
@@ -678,14 +681,16 @@ static void cq_end_op_for_next(grpc_completion_queue* cq, void* tag,
                                void* done_arg, grpc_cq_completion* storage) {
   GPR_TIMER_SCOPE("cq_end_op_for_next", 0);
 
-  if (grpc_api_trace.enabled() ||
-      (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE)) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_api_trace) ||
+      (GRPC_TRACE_FLAG_ENABLED(grpc_trace_operation_failures) &&
+       error != GRPC_ERROR_NONE)) {
     const char* errmsg = grpc_error_string(error);
     GRPC_API_TRACE(
         "cq_end_op_for_next(cq=%p, tag=%p, error=%s, "
         "done=%p, done_arg=%p, storage=%p)",
         6, (cq, tag, errmsg, done, done_arg, storage));
-    if (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_operation_failures) &&
+        error != GRPC_ERROR_NONE) {
       gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg);
     }
   }
@@ -759,14 +764,16 @@ static void cq_end_op_for_pluck(grpc_completion_queue* cq, void* tag,
   cq_pluck_data* cqd = static_cast<cq_pluck_data*> DATA_FROM_CQ(cq);
   int is_success = (error == GRPC_ERROR_NONE);
 
-  if (grpc_api_trace.enabled() ||
-      (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE)) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_api_trace) ||
+      (GRPC_TRACE_FLAG_ENABLED(grpc_trace_operation_failures) &&
+       error != GRPC_ERROR_NONE)) {
     const char* errmsg = grpc_error_string(error);
     GRPC_API_TRACE(
         "cq_end_op_for_pluck(cq=%p, tag=%p, error=%s, "
         "done=%p, done_arg=%p, storage=%p)",
         6, (cq, tag, errmsg, done, done_arg, storage));
-    if (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_operation_failures) &&
+        error != GRPC_ERROR_NONE) {
       gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg);
     }
   }
@@ -824,14 +831,16 @@ static void cq_end_op_for_callback(
   cq_callback_data* cqd = static_cast<cq_callback_data*> DATA_FROM_CQ(cq);
   bool is_success = (error == GRPC_ERROR_NONE);
 
-  if (grpc_api_trace.enabled() ||
-      (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE)) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_api_trace) ||
+      (GRPC_TRACE_FLAG_ENABLED(grpc_trace_operation_failures) &&
+       error != GRPC_ERROR_NONE)) {
     const char* errmsg = grpc_error_string(error);
     GRPC_API_TRACE(
         "cq_end_op_for_callback(cq=%p, tag=%p, error=%s, "
         "done=%p, done_arg=%p, storage=%p)",
         6, (cq, tag, errmsg, done, done_arg, storage));
-    if (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_operation_failures) &&
+        error != GRPC_ERROR_NONE) {
       gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg);
     }
   }
@@ -906,7 +915,7 @@ class ExecCtxNext : public grpc_core::ExecCtx {
 
 #ifndef NDEBUG
 static void dump_pending_tags(grpc_completion_queue* cq) {
-  if (!grpc_trace_pending_tags.enabled()) return;
+  if (!GRPC_TRACE_FLAG_ENABLED(grpc_trace_pending_tags)) return;
 
   gpr_strvec v;
   gpr_strvec_init(&v);
@@ -1002,15 +1011,15 @@ static grpc_event cq_next(grpc_completion_queue* cq, gpr_timespec deadline,
         continue;
       }
 
-      memset(&ret, 0, sizeof(ret));
       ret.type = GRPC_QUEUE_SHUTDOWN;
+      ret.success = 0;
       break;
     }
 
     if (!is_finished_arg.first_loop &&
         grpc_core::ExecCtx::Get()->Now() >= deadline_millis) {
-      memset(&ret, 0, sizeof(ret));
       ret.type = GRPC_QUEUE_TIMEOUT;
+      ret.success = 0;
       dump_pending_tags(cq);
       break;
     }
@@ -1027,8 +1036,8 @@ static grpc_event cq_next(grpc_completion_queue* cq, gpr_timespec deadline,
       gpr_log(GPR_ERROR, "Completion queue next failed: %s", msg);
 
       GRPC_ERROR_UNREF(err);
-      memset(&ret, 0, sizeof(ret));
       ret.type = GRPC_QUEUE_TIMEOUT;
+      ret.success = 0;
       dump_pending_tags(cq);
       break;
     }
@@ -1176,7 +1185,7 @@ static grpc_event cq_pluck(grpc_completion_queue* cq, void* tag,
   grpc_pollset_worker* worker = nullptr;
   cq_pluck_data* cqd = static_cast<cq_pluck_data*> DATA_FROM_CQ(cq);
 
-  if (grpc_cq_pluck_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_cq_pluck_trace)) {
     GRPC_API_TRACE(
         "grpc_completion_queue_pluck("
         "cq=%p, tag=%p, "
@@ -1234,8 +1243,8 @@ static grpc_event cq_pluck(grpc_completion_queue* cq, void* tag,
     }
     if (cqd->shutdown.Load(grpc_core::MemoryOrder::RELAXED)) {
       gpr_mu_unlock(cq->mu);
-      memset(&ret, 0, sizeof(ret));
       ret.type = GRPC_QUEUE_SHUTDOWN;
+      ret.success = 0;
       break;
     }
     if (!add_plucker(cq, tag, &worker)) {
@@ -1244,9 +1253,9 @@ static grpc_event cq_pluck(grpc_completion_queue* cq, void* tag,
               "is %d",
               GRPC_MAX_COMPLETION_QUEUE_PLUCKERS);
       gpr_mu_unlock(cq->mu);
-      memset(&ret, 0, sizeof(ret));
       /* TODO(ctiller): should we use a different result here */
       ret.type = GRPC_QUEUE_TIMEOUT;
+      ret.success = 0;
       dump_pending_tags(cq);
       break;
     }
@@ -1254,8 +1263,8 @@ static grpc_event cq_pluck(grpc_completion_queue* cq, void* tag,
         grpc_core::ExecCtx::Get()->Now() >= deadline_millis) {
       del_plucker(cq, tag, &worker);
       gpr_mu_unlock(cq->mu);
-      memset(&ret, 0, sizeof(ret));
       ret.type = GRPC_QUEUE_TIMEOUT;
+      ret.success = 0;
       dump_pending_tags(cq);
       break;
     }
@@ -1269,8 +1278,8 @@ static grpc_event cq_pluck(grpc_completion_queue* cq, void* tag,
       gpr_log(GPR_ERROR, "Completion queue pluck failed: %s", msg);
 
       GRPC_ERROR_UNREF(err);
-      memset(&ret, 0, sizeof(ret));
       ret.type = GRPC_QUEUE_TIMEOUT;
+      ret.success = 0;
       dump_pending_tags(cq);
       break;
     }
index fdb584d..2a6d307 100644 (file)
@@ -33,7 +33,7 @@
 #include "src/core/lib/debug/stats.h"
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/gprpp/fork.h"
-#include "src/core/lib/gprpp/mutex_lock.h"
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/http/parser.h"
 #include "src/core/lib/iomgr/call_combiner.h"
 #include "src/core/lib/iomgr/combiner.h"
@@ -73,6 +73,7 @@ static void do_basic_init(void) {
   g_shutting_down = false;
   grpc_register_built_in_plugins();
   grpc_cq_global_init();
+  gpr_time_init();
   g_initializations = 0;
 }
 
@@ -132,8 +133,6 @@ void grpc_init(void) {
     }
     grpc_core::Fork::GlobalInit();
     grpc_fork_handlers_auto_register();
-    gpr_time_init();
-    gpr_arena_init();
     grpc_stats_init();
     grpc_slice_intern_init();
     grpc_mdctx_global_init();
@@ -155,7 +154,7 @@ void grpc_init(void) {
      * at the appropriate time */
     grpc_register_security_filters();
     register_builtin_channel_init();
-    grpc_tracer_init("GRPC_TRACE");
+    grpc_tracer_init();
     /* no more changes to channel init pipelines */
     grpc_channel_init_finalize();
     grpc_iomgr_start();
index 5f5f10d..dde39b8 100644 (file)
@@ -39,7 +39,7 @@ namespace grpc_core {
 namespace {
 
 struct CallData {
-  grpc_call_combiner* call_combiner;
+  grpc_core::CallCombiner* call_combiner;
   grpc_linked_mdelem status;
   grpc_linked_mdelem details;
   grpc_core::Atomic<bool> filled_metadata;
index 1e0ac0e..2377c4d 100644 (file)
@@ -123,7 +123,7 @@ typedef struct shutdown_tag {
 typedef enum {
   /* waiting for metadata */
   NOT_STARTED,
-  /* inital metadata read, not flow controlled in yet */
+  /* initial metadata read, not flow controlled in yet */
   PENDING,
   /* flow controlled in, on completion queue */
   ACTIVATED,
@@ -190,7 +190,7 @@ struct call_data {
   grpc_closure publish;
 
   call_data* pending_next = nullptr;
-  grpc_call_combiner* call_combiner;
+  grpc_core::CallCombiner* call_combiner;
 };
 
 struct request_matcher {
@@ -347,8 +347,8 @@ static void channel_broadcaster_shutdown(channel_broadcaster* cb,
  */
 
 static void request_matcher_init(request_matcher* rm, grpc_server* server) {
-  memset(rm, 0, sizeof(*rm));
   rm->server = server;
+  rm->pending_head = rm->pending_tail = nullptr;
   rm->requests_per_cq = static_cast<gpr_locked_mpscq*>(
       gpr_malloc(sizeof(*rm->requests_per_cq) * server->cq_count));
   for (size_t i = 0; i < server->cq_count; i++) {
@@ -464,7 +464,8 @@ static void destroy_channel(channel_data* chand, grpc_error* error) {
   GRPC_CLOSURE_INIT(&chand->finish_destroy_channel_closure,
                     finish_destroy_channel, chand, grpc_schedule_on_exec_ctx);
 
-  if (grpc_server_channel_trace.enabled() && error != GRPC_ERROR_NONE) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_server_channel_trace) &&
+      error != GRPC_ERROR_NONE) {
     const char* msg = grpc_error_string(error);
     gpr_log(GPR_INFO, "Disconnected client: %s", msg);
   }
@@ -601,8 +602,9 @@ static void finish_start_new_rpc(
       break;
     case GRPC_SRM_PAYLOAD_READ_INITIAL_BYTE_BUFFER: {
       grpc_op op;
-      memset(&op, 0, sizeof(op));
       op.op = GRPC_OP_RECV_MESSAGE;
+      op.flags = 0;
+      op.reserved = nullptr;
       op.data.recv_message.recv_message = &calld->payload;
       GRPC_CLOSURE_INIT(&calld->publish, publish_new_rpc, elem,
                         grpc_schedule_on_exec_ctx);
@@ -1098,20 +1100,6 @@ void* grpc_server_register_method(
   return m;
 }
 
-static void start_listeners(void* s, grpc_error* error) {
-  grpc_server* server = static_cast<grpc_server*>(s);
-  for (listener* l = server->listeners; l; l = l->next) {
-    l->start(server, l->arg, server->pollsets, server->pollset_count);
-  }
-
-  gpr_mu_lock(&server->mu_global);
-  server->starting = false;
-  gpr_cv_signal(&server->starting_cv);
-  gpr_mu_unlock(&server->mu_global);
-
-  server_unref(server);
-}
-
 void grpc_server_start(grpc_server* server) {
   size_t i;
   grpc_core::ExecCtx exec_ctx;
@@ -1133,13 +1121,18 @@ void grpc_server_start(grpc_server* server) {
     request_matcher_init(&rm->matcher, server);
   }
 
-  server_ref(server);
+  gpr_mu_lock(&server->mu_global);
   server->starting = true;
-  GRPC_CLOSURE_SCHED(
-      GRPC_CLOSURE_CREATE(
-          start_listeners, server,
-          grpc_core::Executor::Scheduler(grpc_core::ExecutorJobType::SHORT)),
-      GRPC_ERROR_NONE);
+  gpr_mu_unlock(&server->mu_global);
+
+  for (listener* l = server->listeners; l; l = l->next) {
+    l->start(server, l->arg, server->pollsets, server->pollset_count);
+  }
+
+  gpr_mu_lock(&server->mu_global);
+  server->starting = false;
+  gpr_cv_signal(&server->starting_cv);
+  gpr_mu_unlock(&server->mu_global);
 }
 
 void grpc_server_get_pollsets(grpc_server* server, grpc_pollset*** pollsets,
index bf2e6c9..8aeadaf 100644 (file)
@@ -25,4 +25,4 @@
 
 const char* grpc_version_string(void) { return "7.0.0"; }
 
-const char* grpc_g_stands_for(void) { return "godric"; }
+const char* grpc_g_stands_for(void) { return "gandalf"; }
index 8e71f86..8835e32 100644 (file)
@@ -46,7 +46,7 @@ grpc_millis BdpEstimator::CompletePing() {
               1e-9 * static_cast<double>(dt_ts.tv_nsec);
   double bw = dt > 0 ? (static_cast<double>(accumulator_) / dt) : 0;
   int start_inter_ping_delay = inter_ping_delay_;
-  if (grpc_bdp_estimator_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_bdp_estimator_trace)) {
     gpr_log(GPR_INFO,
             "bdp[%s]:complete acc=%" PRId64 " est=%" PRId64
             " dt=%lf bw=%lfMbs bw_est=%lfMbs",
@@ -57,7 +57,7 @@ grpc_millis BdpEstimator::CompletePing() {
   if (accumulator_ > 2 * estimate_ / 3 && bw > bw_est_) {
     estimate_ = GPR_MAX(accumulator_, estimate_ * 2);
     bw_est_ = bw;
-    if (grpc_bdp_estimator_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_bdp_estimator_trace)) {
       gpr_log(GPR_INFO, "bdp[%s]: estimate increased to %" PRId64, name_,
               estimate_);
     }
@@ -74,7 +74,7 @@ grpc_millis BdpEstimator::CompletePing() {
   }
   if (start_inter_ping_delay != inter_ping_delay_) {
     stable_estimate_count_ = 0;
-    if (grpc_bdp_estimator_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_bdp_estimator_trace)) {
       gpr_log(GPR_INFO, "bdp[%s]:update_inter_time to %dms", name_,
               inter_ping_delay_);
     }
index ab13ae4..6dc4d6b 100644 (file)
@@ -49,7 +49,7 @@ class BdpEstimator {
   // grpc_bdp_estimator_add_incoming_bytes once a ping has been scheduled by a
   // transport (but not necessarily started)
   void SchedulePing() {
-    if (grpc_bdp_estimator_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_bdp_estimator_trace)) {
       gpr_log(GPR_INFO, "bdp[%s]:sched acc=%" PRId64 " est=%" PRId64, name_,
               accumulator_, estimate_);
     }
@@ -62,7 +62,7 @@ class BdpEstimator {
   // once
   // the ping is on the wire
   void StartPing() {
-    if (grpc_bdp_estimator_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_bdp_estimator_trace)) {
       gpr_log(GPR_INFO, "bdp[%s]:start acc=%" PRId64 " est=%" PRId64, name_,
               accumulator_, estimate_);
     }
index db6b6c0..bf35fd0 100644 (file)
@@ -48,7 +48,6 @@ void grpc_connectivity_state_init(grpc_connectivity_state_tracker* tracker,
                                   grpc_connectivity_state init_state,
                                   const char* name) {
   gpr_atm_no_barrier_store(&tracker->current_state_atm, init_state);
-  tracker->current_error = GRPC_ERROR_NONE;
   tracker->watchers = nullptr;
   tracker->name = gpr_strdup(name);
 }
@@ -69,7 +68,6 @@ void grpc_connectivity_state_destroy(grpc_connectivity_state_tracker* tracker) {
     GRPC_CLOSURE_SCHED(w->notify, error);
     gpr_free(w);
   }
-  GRPC_ERROR_UNREF(tracker->current_error);
   gpr_free(tracker->name);
 }
 
@@ -77,27 +75,13 @@ grpc_connectivity_state grpc_connectivity_state_check(
     grpc_connectivity_state_tracker* tracker) {
   grpc_connectivity_state cur = static_cast<grpc_connectivity_state>(
       gpr_atm_no_barrier_load(&tracker->current_state_atm));
-  if (grpc_connectivity_state_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_connectivity_state_trace)) {
     gpr_log(GPR_INFO, "CONWATCH: %p %s: get %s", tracker, tracker->name,
             grpc_connectivity_state_name(cur));
   }
   return cur;
 }
 
-grpc_connectivity_state grpc_connectivity_state_get(
-    grpc_connectivity_state_tracker* tracker, grpc_error** error) {
-  grpc_connectivity_state cur = static_cast<grpc_connectivity_state>(
-      gpr_atm_no_barrier_load(&tracker->current_state_atm));
-  if (grpc_connectivity_state_trace.enabled()) {
-    gpr_log(GPR_INFO, "CONWATCH: %p %s: get %s", tracker, tracker->name,
-            grpc_connectivity_state_name(cur));
-  }
-  if (error != nullptr) {
-    *error = GRPC_ERROR_REF(tracker->current_error);
-  }
-  return cur;
-}
-
 bool grpc_connectivity_state_has_watchers(
     grpc_connectivity_state_tracker* connectivity_state) {
   return connectivity_state->watchers != nullptr;
@@ -108,7 +92,7 @@ bool grpc_connectivity_state_notify_on_state_change(
     grpc_closure* notify) {
   grpc_connectivity_state cur = static_cast<grpc_connectivity_state>(
       gpr_atm_no_barrier_load(&tracker->current_state_atm));
-  if (grpc_connectivity_state_trace.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_connectivity_state_trace)) {
     if (current == nullptr) {
       gpr_log(GPR_INFO, "CONWATCH: %p %s: unsubscribe notify=%p", tracker,
               tracker->name, notify);
@@ -140,7 +124,7 @@ bool grpc_connectivity_state_notify_on_state_change(
   } else {
     if (cur != *current) {
       *current = cur;
-      GRPC_CLOSURE_SCHED(notify, GRPC_ERROR_REF(tracker->current_error));
+      GRPC_CLOSURE_SCHED(notify, GRPC_ERROR_NONE);
     } else {
       grpc_connectivity_state_watcher* w =
           static_cast<grpc_connectivity_state_watcher*>(gpr_malloc(sizeof(*w)));
@@ -155,29 +139,15 @@ bool grpc_connectivity_state_notify_on_state_change(
 
 void grpc_connectivity_state_set(grpc_connectivity_state_tracker* tracker,
                                  grpc_connectivity_state state,
-                                 grpc_error* error, const char* reason) {
+                                 const char* reason) {
   grpc_connectivity_state cur = static_cast<grpc_connectivity_state>(
       gpr_atm_no_barrier_load(&tracker->current_state_atm));
   grpc_connectivity_state_watcher* w;
-  if (grpc_connectivity_state_trace.enabled()) {
-    const char* error_string = grpc_error_string(error);
-    gpr_log(GPR_INFO, "SET: %p %s: %s --> %s [%s] error=%p %s", tracker,
-            tracker->name, grpc_connectivity_state_name(cur),
-            grpc_connectivity_state_name(state), reason, error, error_string);
-  }
-  switch (state) {
-    case GRPC_CHANNEL_CONNECTING:
-    case GRPC_CHANNEL_IDLE:
-    case GRPC_CHANNEL_READY:
-      GPR_ASSERT(error == GRPC_ERROR_NONE);
-      break;
-    case GRPC_CHANNEL_SHUTDOWN:
-    case GRPC_CHANNEL_TRANSIENT_FAILURE:
-      GPR_ASSERT(error != GRPC_ERROR_NONE);
-      break;
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_connectivity_state_trace)) {
+    gpr_log(GPR_INFO, "SET: %p %s: %s --> %s [%s]", tracker, tracker->name,
+            grpc_connectivity_state_name(cur),
+            grpc_connectivity_state_name(state), reason);
   }
-  GRPC_ERROR_UNREF(tracker->current_error);
-  tracker->current_error = error;
   if (cur == state) {
     return;
   }
@@ -186,10 +156,10 @@ void grpc_connectivity_state_set(grpc_connectivity_state_tracker* tracker,
   while ((w = tracker->watchers) != nullptr) {
     *w->current = state;
     tracker->watchers = w->next;
-    if (grpc_connectivity_state_trace.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_connectivity_state_trace)) {
       gpr_log(GPR_INFO, "NOTIFY: %p %s: %p", tracker, tracker->name, w->notify);
     }
-    GRPC_CLOSURE_SCHED(w->notify, GRPC_ERROR_REF(tracker->current_error));
+    GRPC_CLOSURE_SCHED(w->notify, GRPC_ERROR_NONE);
     gpr_free(w);
   }
 }
index ecb083c..0ff1432 100644 (file)
@@ -37,8 +37,6 @@ typedef struct grpc_connectivity_state_watcher {
 typedef struct {
   /** current grpc_connectivity_state */
   gpr_atm current_state_atm;
-  /** error associated with state */
-  grpc_error* current_error;
   /** all our watchers */
   grpc_connectivity_state_watcher* watchers;
   /** a name to help debugging */
@@ -59,7 +57,6 @@ void grpc_connectivity_state_destroy(grpc_connectivity_state_tracker* tracker);
  *  external lock */
 void grpc_connectivity_state_set(grpc_connectivity_state_tracker* tracker,
                                  grpc_connectivity_state state,
-                                 grpc_error* associated_error,
                                  const char* reason);
 
 /** Return true if this connectivity state has watchers.
@@ -71,11 +68,6 @@ bool grpc_connectivity_state_has_watchers(
 grpc_connectivity_state grpc_connectivity_state_check(
     grpc_connectivity_state_tracker* tracker);
 
-/** Return the last seen connectivity state, and the associated error.
-    Access must be serialized with an external lock. */
-grpc_connectivity_state grpc_connectivity_state_get(
-    grpc_connectivity_state_tracker* tracker, grpc_error** error);
-
 /** Return 1 if the channel should start connecting, 0 otherwise.
     If current==NULL cancel notify if it is already queued (success==0 in that
     case).
index 558f1d4..eb4e8c3 100644 (file)
@@ -48,6 +48,18 @@ void grpc_error_get_status(grpc_error* error, grpc_millis deadline,
                            grpc_status_code* code, grpc_slice* slice,
                            grpc_http2_error_code* http_error,
                            const char** error_string) {
+  // Fast path: We expect no error.
+  if (GPR_LIKELY(error == GRPC_ERROR_NONE)) {
+    if (code != nullptr) *code = GRPC_STATUS_OK;
+    if (slice != nullptr) {
+      grpc_error_get_str(error, GRPC_ERROR_STR_GRPC_MESSAGE, slice);
+    }
+    if (http_error != nullptr) {
+      *http_error = GRPC_HTTP2_NO_ERROR;
+    }
+    return;
+  }
+
   // Start with the parent error and recurse through the tree of children
   // until we find the first one that has a status code.
   grpc_error* found_error =
index b7e7fd4..4609eb4 100644 (file)
 #include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/transport/static_metadata.h"
 
+using grpc_core::AllocatedMetadata;
+using grpc_core::InternedMetadata;
+using grpc_core::UserData;
+
 /* There are two kinds of mdelem and mdstr instances.
  * Static instances are declared in static_metadata.{h,c} and
  * are initialized by grpc_mdctx_global_init().
@@ -54,13 +58,40 @@ grpc_core::DebugOnlyTraceFlag grpc_trace_metadata(false, "metadata");
 
 #ifndef NDEBUG
 #define DEBUG_ARGS , const char *file, int line
-#define FWD_DEBUG_ARGS , file, line
-#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s), __FILE__, __LINE__)
-#else
+#define FWD_DEBUG_ARGS file, line
+
+void grpc_mdelem_trace_ref(void* md, const grpc_slice& key,
+                           const grpc_slice& value, intptr_t refcnt,
+                           const char* file, int line) {
+  if (grpc_trace_metadata.enabled()) {
+    char* key_str = grpc_slice_to_c_string(key);
+    char* value_str = grpc_slice_to_c_string(value);
+    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+            "ELM   REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", md, refcnt,
+            refcnt + 1, key_str, value_str);
+    gpr_free(key_str);
+    gpr_free(value_str);
+  }
+}
+
+void grpc_mdelem_trace_unref(void* md, const grpc_slice& key,
+                             const grpc_slice& value, intptr_t refcnt,
+                             const char* file, int line) {
+  if (grpc_trace_metadata.enabled()) {
+    char* key_str = grpc_slice_to_c_string(key);
+    char* value_str = grpc_slice_to_c_string(value);
+    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+            "ELM   UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", md,
+            refcnt, refcnt - 1, key_str, value_str);
+    gpr_free(key_str);
+    gpr_free(value_str);
+  }
+}
+
+#else  // ifndef NDEBUG
 #define DEBUG_ARGS
 #define FWD_DEBUG_ARGS
-#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s))
-#endif
+#endif  // ifndef NDEBUG
 
 #define INITIAL_SHARD_CAPACITY 8
 #define LOG2_SHARD_COUNT 4
@@ -69,43 +100,87 @@ grpc_core::DebugOnlyTraceFlag grpc_trace_metadata(false, "metadata");
 #define TABLE_IDX(hash, capacity) (((hash) >> (LOG2_SHARD_COUNT)) % (capacity))
 #define SHARD_IDX(hash) ((hash) & ((1 << (LOG2_SHARD_COUNT)) - 1))
 
-typedef void (*destroy_user_data_func)(void* user_data);
-
-struct UserData {
-  gpr_mu mu_user_data;
-  gpr_atm destroy_user_data;
-  gpr_atm user_data;
-};
-
-/* Shadow structure for grpc_mdelem_data for interned elements */
-typedef struct interned_metadata {
-  /* must be byte compatible with grpc_mdelem_data */
-  grpc_slice key;
-  grpc_slice value;
-
-  /* private only data */
-  gpr_atm refcnt;
-
-  UserData user_data;
+AllocatedMetadata::AllocatedMetadata(const grpc_slice& key,
+                                     const grpc_slice& value)
+    : key_(grpc_slice_ref_internal(key)),
+      value_(grpc_slice_ref_internal(value)),
+      refcnt_(1) {
+#ifndef NDEBUG
+  if (grpc_trace_metadata.enabled()) {
+    char* key_str = grpc_slice_to_c_string(key_);
+    char* value_str = grpc_slice_to_c_string(value_);
+    gpr_log(GPR_DEBUG, "ELM ALLOC:%p:%" PRIdPTR ": '%s' = '%s'", this,
+            RefValue(), key_str, value_str);
+    gpr_free(key_str);
+    gpr_free(value_str);
+  }
+#endif
+}
 
-  struct interned_metadata* bucket_next;
-} interned_metadata;
+AllocatedMetadata::~AllocatedMetadata() {
+  grpc_slice_unref_internal(key_);
+  grpc_slice_unref_internal(value_);
+  void* user_data = user_data_.data.Load(grpc_core::MemoryOrder::RELAXED);
+  if (user_data) {
+    destroy_user_data_func destroy_user_data =
+        user_data_.destroy_user_data.Load(grpc_core::MemoryOrder::RELAXED);
+    destroy_user_data(user_data);
+  }
+}
 
-/* Shadow structure for grpc_mdelem_data for allocated elements */
-typedef struct allocated_metadata {
-  /* must be byte compatible with grpc_mdelem_data */
-  grpc_slice key;
-  grpc_slice value;
+InternedMetadata::InternedMetadata(const grpc_slice& key,
+                                   const grpc_slice& value, uint32_t hash,
+                                   InternedMetadata* next)
+    : key_(grpc_slice_ref_internal(key)),
+      value_(grpc_slice_ref_internal(value)),
+      refcnt_(1),
+      hash_(hash),
+      link_(next) {
+#ifndef NDEBUG
+  if (grpc_trace_metadata.enabled()) {
+    char* key_str = grpc_slice_to_c_string(key_);
+    char* value_str = grpc_slice_to_c_string(value_);
+    gpr_log(GPR_DEBUG, "ELM   NEW:%p:%" PRIdPTR ": '%s' = '%s'", this,
+            RefValue(), key_str, value_str);
+    gpr_free(key_str);
+    gpr_free(value_str);
+  }
+#endif
+}
 
-  /* private only data */
-  gpr_atm refcnt;
+InternedMetadata::~InternedMetadata() {
+  grpc_slice_unref_internal(key_);
+  grpc_slice_unref_internal(value_);
+  void* user_data = user_data_.data.Load(grpc_core::MemoryOrder::RELAXED);
+  if (user_data) {
+    destroy_user_data_func destroy_user_data =
+        user_data_.destroy_user_data.Load(grpc_core::MemoryOrder::RELAXED);
+    destroy_user_data(user_data);
+  }
+}
 
-  UserData user_data;
-} allocated_metadata;
+size_t InternedMetadata::CleanupLinkedMetadata(
+    InternedMetadata::BucketLink* head) {
+  size_t num_freed = 0;
+  InternedMetadata::BucketLink* prev_next = head;
+  InternedMetadata *md, *next;
+
+  for (md = head->next; md; md = next) {
+    next = md->link_.next;
+    if (md->AllRefsDropped()) {
+      prev_next->next = next;
+      grpc_core::Delete(md);
+      num_freed++;
+    } else {
+      prev_next = &md->link_;
+    }
+  }
+  return num_freed;
+}
 
 typedef struct mdtab_shard {
   gpr_mu mu;
-  interned_metadata** elems;
+  InternedMetadata::BucketLink* elems;
   size_t count;
   size_t capacity;
   /** Estimate of the number of unreferenced mdelems in the hash table.
@@ -126,7 +201,7 @@ void grpc_mdctx_global_init(void) {
     shard->count = 0;
     gpr_atm_no_barrier_store(&shard->free_estimate, 0);
     shard->capacity = INITIAL_SHARD_CAPACITY;
-    shard->elems = static_cast<interned_metadata**>(
+    shard->elems = static_cast<InternedMetadata::BucketLink*>(
         gpr_zalloc(sizeof(*shard->elems) * shard->capacity));
   }
 }
@@ -136,7 +211,6 @@ void grpc_mdctx_global_shutdown() {
     mdtab_shard* shard = &g_shards[i];
     gpr_mu_destroy(&shard->mu);
     gc_mdtab(shard);
-    /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */
     if (shard->count != 0) {
       gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata elements were leaked",
               shard->count);
@@ -144,6 +218,7 @@ void grpc_mdctx_global_shutdown() {
         abort();
       }
     }
+    GPR_DEBUG_ASSERT(shard->count == 0);
     gpr_free(shard->elems);
   }
 }
@@ -154,57 +229,34 @@ static int is_mdelem_static(grpc_mdelem e) {
              &grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
 }
 
-static void ref_md_locked(mdtab_shard* shard,
-                          interned_metadata* md DEBUG_ARGS) {
+void InternedMetadata::RefWithShardLocked(mdtab_shard* shard) {
 #ifndef NDEBUG
   if (grpc_trace_metadata.enabled()) {
-    char* key_str = grpc_slice_to_c_string(md->key);
-    char* value_str = grpc_slice_to_c_string(md->value);
-    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
-            "ELM   REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", (void*)md,
-            gpr_atm_no_barrier_load(&md->refcnt),
-            gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
+    char* key_str = grpc_slice_to_c_string(key_);
+    char* value_str = grpc_slice_to_c_string(value_);
+    intptr_t value = RefValue();
+    gpr_log(__FILE__, __LINE__, GPR_LOG_SEVERITY_DEBUG,
+            "ELM   REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", this, value,
+            value + 1, key_str, value_str);
     gpr_free(key_str);
     gpr_free(value_str);
   }
 #endif
-  if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 1)) {
+  if (FirstRef()) {
     gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -1);
   }
 }
 
 static void gc_mdtab(mdtab_shard* shard) {
   GPR_TIMER_SCOPE("gc_mdtab", 0);
-
-  size_t i;
-  interned_metadata** prev_next;
-  interned_metadata *md, *next;
-  gpr_atm num_freed = 0;
-
-  for (i = 0; i < shard->capacity; i++) {
-    prev_next = &shard->elems[i];
-    for (md = shard->elems[i]; md; md = next) {
-      void* user_data =
-          (void*)gpr_atm_no_barrier_load(&md->user_data.user_data);
-      next = md->bucket_next;
-      if (gpr_atm_acq_load(&md->refcnt) == 0) {
-        grpc_slice_unref_internal(md->key);
-        grpc_slice_unref_internal(md->value);
-        if (md->user_data.user_data) {
-          ((destroy_user_data_func)gpr_atm_no_barrier_load(
-              &md->user_data.destroy_user_data))(user_data);
-        }
-        gpr_mu_destroy(&md->user_data.mu_user_data);
-        gpr_free(md);
-        *prev_next = next;
-        num_freed++;
-        shard->count--;
-      } else {
-        prev_next = &md->bucket_next;
-      }
-    }
+  size_t num_freed = 0;
+  for (size_t i = 0; i < shard->capacity; ++i) {
+    intptr_t freed = InternedMetadata::CleanupLinkedMetadata(&shard->elems[i]);
+    num_freed += freed;
+    shard->count -= freed;
   }
-  gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -num_freed);
+  gpr_atm_no_barrier_fetch_add(&shard->free_estimate,
+                               -static_cast<intptr_t>(num_freed));
 }
 
 static void grow_mdtab(mdtab_shard* shard) {
@@ -212,22 +264,21 @@ static void grow_mdtab(mdtab_shard* shard) {
 
   size_t capacity = shard->capacity * 2;
   size_t i;
-  interned_metadata** mdtab;
-  interned_metadata *md, *next;
+  InternedMetadata::BucketLink* mdtab;
+  InternedMetadata *md, *next;
   uint32_t hash;
 
-  mdtab = static_cast<interned_metadata**>(
-      gpr_zalloc(sizeof(interned_metadata*) * capacity));
+  mdtab = static_cast<InternedMetadata::BucketLink*>(
+      gpr_zalloc(sizeof(InternedMetadata::BucketLink) * capacity));
 
   for (i = 0; i < shard->capacity; i++) {
-    for (md = shard->elems[i]; md; md = next) {
+    for (md = shard->elems[i].next; md; md = next) {
       size_t idx;
-      hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key),
-                                grpc_slice_hash(md->value));
-      next = md->bucket_next;
+      hash = md->hash();
+      next = md->bucket_next();
       idx = TABLE_IDX(hash, capacity);
-      md->bucket_next = mdtab[idx];
-      mdtab[idx] = md;
+      md->set_bucket_next(mdtab[idx].next);
+      mdtab[idx].next = md;
     }
   }
   gpr_free(shard->elems);
@@ -247,34 +298,22 @@ static void rehash_mdtab(mdtab_shard* shard) {
 grpc_mdelem grpc_mdelem_create(
     const grpc_slice& key, const grpc_slice& value,
     grpc_mdelem_data* compatible_external_backing_store) {
+  // External storage if either slice is not interned and the caller already
+  // created a backing store. If no backing store, we allocate one.
   if (!grpc_slice_is_interned(key) || !grpc_slice_is_interned(value)) {
     if (compatible_external_backing_store != nullptr) {
+      // Caller provided backing store.
       return GRPC_MAKE_MDELEM(compatible_external_backing_store,
                               GRPC_MDELEM_STORAGE_EXTERNAL);
+    } else {
+      // We allocate backing store.
+      return GRPC_MAKE_MDELEM(grpc_core::New<AllocatedMetadata>(key, value),
+                              GRPC_MDELEM_STORAGE_ALLOCATED);
     }
-
-    allocated_metadata* allocated =
-        static_cast<allocated_metadata*>(gpr_malloc(sizeof(*allocated)));
-    allocated->key = grpc_slice_ref_internal(key);
-    allocated->value = grpc_slice_ref_internal(value);
-    gpr_atm_rel_store(&allocated->refcnt, 1);
-    allocated->user_data.user_data = 0;
-    allocated->user_data.destroy_user_data = 0;
-    gpr_mu_init(&allocated->user_data.mu_user_data);
-#ifndef NDEBUG
-    if (grpc_trace_metadata.enabled()) {
-      char* key_str = grpc_slice_to_c_string(allocated->key);
-      char* value_str = grpc_slice_to_c_string(allocated->value);
-      gpr_log(GPR_DEBUG, "ELM ALLOC:%p:%" PRIdPTR ": '%s' = '%s'",
-              (void*)allocated, gpr_atm_no_barrier_load(&allocated->refcnt),
-              key_str, value_str);
-      gpr_free(key_str);
-      gpr_free(value_str);
-    }
-#endif
-    return GRPC_MAKE_MDELEM(allocated, GRPC_MDELEM_STORAGE_ALLOCATED);
   }
 
+  // Not all static slice input yields a statically stored metadata element.
+  // It may be worth documenting why.
   if (GRPC_IS_STATIC_METADATA_STRING(key) &&
       GRPC_IS_STATIC_METADATA_STRING(value)) {
     grpc_mdelem static_elem = grpc_static_mdelem_for_static_strings(
@@ -286,7 +325,7 @@ grpc_mdelem grpc_mdelem_create(
 
   uint32_t hash =
       GRPC_MDSTR_KV_HASH(grpc_slice_hash(key), grpc_slice_hash(value));
-  interned_metadata* md;
+  InternedMetadata* md;
   mdtab_shard* shard = &g_shards[SHARD_IDX(hash)];
   size_t idx;
 
@@ -296,34 +335,18 @@ grpc_mdelem grpc_mdelem_create(
 
   idx = TABLE_IDX(hash, shard->capacity);
   /* search for an existing pair */
-  for (md = shard->elems[idx]; md; md = md->bucket_next) {
-    if (grpc_slice_eq(key, md->key) && grpc_slice_eq(value, md->value)) {
-      REF_MD_LOCKED(shard, md);
+  for (md = shard->elems[idx].next; md; md = md->bucket_next()) {
+    if (grpc_slice_eq(key, md->key()) && grpc_slice_eq(value, md->value())) {
+      md->RefWithShardLocked(shard);
       gpr_mu_unlock(&shard->mu);
       return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED);
     }
   }
 
   /* not found: create a new pair */
-  md = static_cast<interned_metadata*>(gpr_malloc(sizeof(interned_metadata)));
-  gpr_atm_rel_store(&md->refcnt, 1);
-  md->key = grpc_slice_ref_internal(key);
-  md->value = grpc_slice_ref_internal(value);
-  md->user_data.user_data = 0;
-  md->user_data.destroy_user_data = 0;
-  md->bucket_next = shard->elems[idx];
-  shard->elems[idx] = md;
-  gpr_mu_init(&md->user_data.mu_user_data);
-#ifndef NDEBUG
-  if (grpc_trace_metadata.enabled()) {
-    char* key_str = grpc_slice_to_c_string(md->key);
-    char* value_str = grpc_slice_to_c_string(md->value);
-    gpr_log(GPR_DEBUG, "ELM   NEW:%p:%" PRIdPTR ": '%s' = '%s'", (void*)md,
-            gpr_atm_no_barrier_load(&md->refcnt), key_str, value_str);
-    gpr_free(key_str);
-    gpr_free(value_str);
-  }
-#endif
+  md = grpc_core::New<InternedMetadata>(key, value, hash,
+                                        shard->elems[idx].next);
+  shard->elems[idx].next = md;
   shard->count++;
 
   if (shard->count > shard->capacity * 2) {
@@ -354,130 +377,10 @@ grpc_mdelem grpc_mdelem_from_grpc_metadata(grpc_metadata* metadata) {
       changed ? nullptr : reinterpret_cast<grpc_mdelem_data*>(metadata));
 }
 
-grpc_mdelem grpc_mdelem_ref(grpc_mdelem gmd DEBUG_ARGS) {
-  switch (GRPC_MDELEM_STORAGE(gmd)) {
-    case GRPC_MDELEM_STORAGE_EXTERNAL:
-    case GRPC_MDELEM_STORAGE_STATIC:
-      break;
-    case GRPC_MDELEM_STORAGE_INTERNED: {
-      interned_metadata* md =
-          reinterpret_cast<interned_metadata*> GRPC_MDELEM_DATA(gmd);
-#ifndef NDEBUG
-      if (grpc_trace_metadata.enabled()) {
-        char* key_str = grpc_slice_to_c_string(md->key);
-        char* value_str = grpc_slice_to_c_string(md->value);
-        gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
-                "ELM   REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
-                (void*)md, gpr_atm_no_barrier_load(&md->refcnt),
-                gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
-        gpr_free(key_str);
-        gpr_free(value_str);
-      }
-#endif
-      /* we can assume the ref count is >= 1 as the application is calling
-         this function - meaning that no adjustment to mdtab_free is necessary,
-         simplifying the logic here to be just an atomic increment */
-      /* use C assert to have this removed in opt builds */
-      GPR_ASSERT(gpr_atm_no_barrier_load(&md->refcnt) >= 1);
-      gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
-      break;
-    }
-    case GRPC_MDELEM_STORAGE_ALLOCATED: {
-      allocated_metadata* md =
-          reinterpret_cast<allocated_metadata*> GRPC_MDELEM_DATA(gmd);
-#ifndef NDEBUG
-      if (grpc_trace_metadata.enabled()) {
-        char* key_str = grpc_slice_to_c_string(md->key);
-        char* value_str = grpc_slice_to_c_string(md->value);
-        gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
-                "ELM   REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
-                (void*)md, gpr_atm_no_barrier_load(&md->refcnt),
-                gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
-        gpr_free(key_str);
-        gpr_free(value_str);
-      }
-#endif
-      /* we can assume the ref count is >= 1 as the application is calling
-         this function - meaning that no adjustment to mdtab_free is necessary,
-         simplifying the logic here to be just an atomic increment */
-      /* use C assert to have this removed in opt builds */
-      gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
-      break;
-    }
-  }
-  return gmd;
-}
-
-void grpc_mdelem_unref(grpc_mdelem gmd DEBUG_ARGS) {
-  switch (GRPC_MDELEM_STORAGE(gmd)) {
-    case GRPC_MDELEM_STORAGE_EXTERNAL:
-    case GRPC_MDELEM_STORAGE_STATIC:
-      break;
-    case GRPC_MDELEM_STORAGE_INTERNED: {
-      interned_metadata* md =
-          reinterpret_cast<interned_metadata*> GRPC_MDELEM_DATA(gmd);
-#ifndef NDEBUG
-      if (grpc_trace_metadata.enabled()) {
-        char* key_str = grpc_slice_to_c_string(md->key);
-        char* value_str = grpc_slice_to_c_string(md->value);
-        gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
-                "ELM UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
-                (void*)md, gpr_atm_no_barrier_load(&md->refcnt),
-                gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str);
-        gpr_free(key_str);
-        gpr_free(value_str);
-      }
-#endif
-      uint32_t hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key),
-                                         grpc_slice_hash(md->value));
-      const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1);
-      GPR_ASSERT(prev_refcount >= 1);
-      if (1 == prev_refcount) {
-        /* once the refcount hits zero, some other thread can come along and
-           free md at any time: it's unsafe from this point on to access it */
-        mdtab_shard* shard = &g_shards[SHARD_IDX(hash)];
-        gpr_atm_no_barrier_fetch_add(&shard->free_estimate, 1);
-      }
-      break;
-    }
-    case GRPC_MDELEM_STORAGE_ALLOCATED: {
-      allocated_metadata* md =
-          reinterpret_cast<allocated_metadata*> GRPC_MDELEM_DATA(gmd);
-#ifndef NDEBUG
-      if (grpc_trace_metadata.enabled()) {
-        char* key_str = grpc_slice_to_c_string(md->key);
-        char* value_str = grpc_slice_to_c_string(md->value);
-        gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
-                "ELM UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
-                (void*)md, gpr_atm_no_barrier_load(&md->refcnt),
-                gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str);
-        gpr_free(key_str);
-        gpr_free(value_str);
-      }
-#endif
-      const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1);
-      GPR_ASSERT(prev_refcount >= 1);
-      if (1 == prev_refcount) {
-        grpc_slice_unref_internal(md->key);
-        grpc_slice_unref_internal(md->value);
-        if (md->user_data.user_data) {
-          destroy_user_data_func destroy_user_data =
-              (destroy_user_data_func)gpr_atm_no_barrier_load(
-                  &md->user_data.destroy_user_data);
-          destroy_user_data((void*)md->user_data.user_data);
-        }
-        gpr_mu_destroy(&md->user_data.mu_user_data);
-        gpr_free(md);
-      }
-      break;
-    }
-  }
-}
-
 static void* get_user_data(UserData* user_data, void (*destroy_func)(void*)) {
-  if (gpr_atm_acq_load(&user_data->destroy_user_data) ==
-      (gpr_atm)destroy_func) {
-    return (void*)gpr_atm_no_barrier_load(&user_data->user_data);
+  if (user_data->destroy_user_data.Load(grpc_core::MemoryOrder::ACQUIRE) ==
+      destroy_func) {
+    return user_data->data.Load(grpc_core::MemoryOrder::RELAXED);
   } else {
     return nullptr;
   }
@@ -491,57 +394,52 @@ void* grpc_mdelem_get_user_data(grpc_mdelem md, void (*destroy_func)(void*)) {
       return (void*)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) -
                                                  grpc_static_mdelem_table];
     case GRPC_MDELEM_STORAGE_ALLOCATED: {
-      allocated_metadata* am =
-          reinterpret_cast<allocated_metadata*>(GRPC_MDELEM_DATA(md));
-      return get_user_data(&am->user_data, destroy_func);
+      auto* am = reinterpret_cast<AllocatedMetadata*>(GRPC_MDELEM_DATA(md));
+      return get_user_data(am->user_data(), destroy_func);
     }
     case GRPC_MDELEM_STORAGE_INTERNED: {
-      interned_metadata* im =
-          reinterpret_cast<interned_metadata*> GRPC_MDELEM_DATA(md);
-      return get_user_data(&im->user_data, destroy_func);
+      auto* im = reinterpret_cast<InternedMetadata*> GRPC_MDELEM_DATA(md);
+      return get_user_data(im->user_data(), destroy_func);
     }
   }
   GPR_UNREACHABLE_CODE(return nullptr);
 }
 
 static void* set_user_data(UserData* ud, void (*destroy_func)(void*),
-                           void* user_data) {
-  GPR_ASSERT((user_data == nullptr) == (destroy_func == nullptr));
-  gpr_mu_lock(&ud->mu_user_data);
-  if (gpr_atm_no_barrier_load(&ud->destroy_user_data)) {
+                           void* data) {
+  GPR_ASSERT((data == nullptr) == (destroy_func == nullptr));
+  grpc_core::ReleasableMutexLock lock(&ud->mu_user_data);
+  if (ud->destroy_user_data.Load(grpc_core::MemoryOrder::RELAXED)) {
     /* user data can only be set once */
-    gpr_mu_unlock(&ud->mu_user_data);
+    lock.Unlock();
     if (destroy_func != nullptr) {
-      destroy_func(user_data);
+      destroy_func(data);
     }
-    return (void*)gpr_atm_no_barrier_load(&ud->user_data);
+    return ud->data.Load(grpc_core::MemoryOrder::RELAXED);
   }
-  gpr_atm_no_barrier_store(&ud->user_data, (gpr_atm)user_data);
-  gpr_atm_rel_store(&ud->destroy_user_data, (gpr_atm)destroy_func);
-  gpr_mu_unlock(&ud->mu_user_data);
-  return user_data;
+  ud->data.Store(data, grpc_core::MemoryOrder::RELAXED);
+  ud->destroy_user_data.Store(destroy_func, grpc_core::MemoryOrder::RELEASE);
+  return data;
 }
 
 void* grpc_mdelem_set_user_data(grpc_mdelem md, void (*destroy_func)(void*),
-                                void* user_data) {
+                                void* data) {
   switch (GRPC_MDELEM_STORAGE(md)) {
     case GRPC_MDELEM_STORAGE_EXTERNAL:
-      destroy_func(user_data);
+      destroy_func(data);
       return nullptr;
     case GRPC_MDELEM_STORAGE_STATIC:
-      destroy_func(user_data);
+      destroy_func(data);
       return (void*)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) -
                                                  grpc_static_mdelem_table];
     case GRPC_MDELEM_STORAGE_ALLOCATED: {
-      allocated_metadata* am =
-          reinterpret_cast<allocated_metadata*>(GRPC_MDELEM_DATA(md));
-      return set_user_data(&am->user_data, destroy_func, user_data);
+      auto* am = reinterpret_cast<AllocatedMetadata*>(GRPC_MDELEM_DATA(md));
+      return set_user_data(am->user_data(), destroy_func, data);
     }
     case GRPC_MDELEM_STORAGE_INTERNED: {
-      interned_metadata* im =
-          reinterpret_cast<interned_metadata*> GRPC_MDELEM_DATA(md);
+      auto* im = reinterpret_cast<InternedMetadata*> GRPC_MDELEM_DATA(md);
       GPR_ASSERT(!is_mdelem_static(md));
-      return set_user_data(&im->user_data, destroy_func, user_data);
+      return set_user_data(im->user_data(), destroy_func, data);
     }
   }
   GPR_UNREACHABLE_CODE(return nullptr);
@@ -554,3 +452,33 @@ bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b) {
   return grpc_slice_eq(GRPC_MDKEY(a), GRPC_MDKEY(b)) &&
          grpc_slice_eq(GRPC_MDVALUE(a), GRPC_MDVALUE(b));
 }
+
+static void note_disposed_interned_metadata(uint32_t hash) {
+  mdtab_shard* shard = &g_shards[SHARD_IDX(hash)];
+  gpr_atm_no_barrier_fetch_add(&shard->free_estimate, 1);
+}
+
+void grpc_mdelem_do_unref(grpc_mdelem gmd DEBUG_ARGS) {
+  switch (GRPC_MDELEM_STORAGE(gmd)) {
+    case GRPC_MDELEM_STORAGE_EXTERNAL:
+    case GRPC_MDELEM_STORAGE_STATIC:
+      return;
+    case GRPC_MDELEM_STORAGE_INTERNED: {
+      auto* md = reinterpret_cast<InternedMetadata*> GRPC_MDELEM_DATA(gmd);
+      uint32_t hash = md->hash();
+      if (GPR_UNLIKELY(md->Unref(FWD_DEBUG_ARGS))) {
+        /* once the refcount hits zero, some other thread can come along and
+           free md at any time: it's unsafe from this point on to access it */
+        note_disposed_interned_metadata(hash);
+      }
+      break;
+    }
+    case GRPC_MDELEM_STORAGE_ALLOCATED: {
+      auto* md = reinterpret_cast<AllocatedMetadata*> GRPC_MDELEM_DATA(gmd);
+      if (GPR_UNLIKELY(md->Unref(FWD_DEBUG_ARGS))) {
+        grpc_core::Delete(md);
+      }
+      break;
+    }
+  }
+}
index 989c754..c0d1ab3 100644 (file)
 
 #include <grpc/support/port_platform.h>
 
+#include "include/grpc/impl/codegen/log.h"
+
 #include <grpc/grpc.h>
 #include <grpc/slice.h>
 
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/atomic.h"
+#include "src/core/lib/gprpp/sync.h"
 
 extern grpc_core::DebugOnlyTraceFlag grpc_trace_metadata;
 
@@ -63,7 +67,7 @@ extern grpc_core::DebugOnlyTraceFlag grpc_trace_metadata;
 typedef struct grpc_mdelem grpc_mdelem;
 
 /* if changing this, make identical changes in:
-   - interned_metadata, allocated_metadata in metadata.c
+   - grpc_core::{InternedMetadata, AllocatedMetadata}
    - grpc_metadata in grpc_types.h */
 typedef struct grpc_mdelem_data {
   const grpc_slice key;
@@ -124,28 +128,219 @@ grpc_mdelem grpc_mdelem_create(
     const grpc_slice& key, const grpc_slice& value,
     grpc_mdelem_data* compatible_external_backing_store);
 
+#define GRPC_MDKEY(md) (GRPC_MDELEM_DATA(md)->key)
+#define GRPC_MDVALUE(md) (GRPC_MDELEM_DATA(md)->value)
+
 bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b);
+/* Often we compare metadata where we know a-priori that the second parameter is
+ * static, and that the keys match. This most commonly happens when processing
+ * metadata batch callouts in initial/trailing filters. In this case, fastpath
+ * grpc_mdelem_eq and remove unnecessary checks. */
+inline bool grpc_mdelem_static_value_eq(grpc_mdelem a, grpc_mdelem b_static) {
+  if (a.payload == b_static.payload) return true;
+  return grpc_slice_eq(GRPC_MDVALUE(a), GRPC_MDVALUE(b_static));
+}
 
 /* Mutator and accessor for grpc_mdelem user data. The destructor function
    is used as a type tag and is checked during user_data fetch. */
 void* grpc_mdelem_get_user_data(grpc_mdelem md, void (*if_destroy_func)(void*));
 void* grpc_mdelem_set_user_data(grpc_mdelem md, void (*destroy_func)(void*),
-                                void* user_data);
+                                void* data);
+
+// Defined in metadata.cc.
+struct mdtab_shard;
+
+#ifndef NDEBUG
+void grpc_mdelem_trace_ref(void* md, const grpc_slice& key,
+                           const grpc_slice& value, intptr_t refcnt,
+                           const char* file, int line);
+void grpc_mdelem_trace_unref(void* md, const grpc_slice& key,
+                             const grpc_slice& value, intptr_t refcnt,
+                             const char* file, int line);
+#endif
+namespace grpc_core {
+
+typedef void (*destroy_user_data_func)(void* data);
+
+struct UserData {
+  Mutex mu_user_data;
+  grpc_core::Atomic<destroy_user_data_func> destroy_user_data;
+  grpc_core::Atomic<void*> data;
+};
+
+class InternedMetadata {
+ public:
+  struct BucketLink {
+    explicit BucketLink(InternedMetadata* md) : next(md) {}
+
+    InternedMetadata* next = nullptr;
+  };
+
+  InternedMetadata(const grpc_slice& key, const grpc_slice& value,
+                   uint32_t hash, InternedMetadata* next);
+  ~InternedMetadata();
+
+#ifndef NDEBUG
+  void Ref(const char* file, int line) {
+    grpc_mdelem_trace_ref(this, key_, value_, RefValue(), file, line);
+    const intptr_t prior = refcnt_.FetchAdd(1, MemoryOrder::RELAXED);
+    GPR_ASSERT(prior > 0);
+  }
+  bool Unref(const char* file, int line) {
+    grpc_mdelem_trace_unref(this, key_, value_, RefValue(), file, line);
+    return Unref();
+  }
+#else
+  // We define a naked Ref() in the else-clause to make sure we don't
+  // inadvertently skip the assert on debug builds.
+  void Ref() {
+    /* we can assume the ref count is >= 1 as the application is calling
+       this function - meaning that no adjustment to mdtab_free is necessary,
+       simplifying the logic here to be just an atomic increment */
+    refcnt_.FetchAdd(1, MemoryOrder::RELAXED);
+  }
+#endif  // ifndef NDEBUG
+  bool Unref() {
+    const intptr_t prior = refcnt_.FetchSub(1, MemoryOrder::ACQ_REL);
+    GPR_DEBUG_ASSERT(prior > 0);
+    return prior == 1;
+  }
+
+  void RefWithShardLocked(mdtab_shard* shard);
+  const grpc_slice& key() const { return key_; }
+  const grpc_slice& value() const { return value_; }
+  UserData* user_data() { return &user_data_; }
+  uint32_t hash() { return hash_; }
+  InternedMetadata* bucket_next() { return link_.next; }
+  void set_bucket_next(InternedMetadata* md) { link_.next = md; }
+
+  static size_t CleanupLinkedMetadata(BucketLink* head);
+
+ private:
+  bool AllRefsDropped() { return refcnt_.Load(MemoryOrder::ACQUIRE) == 0; }
+  bool FirstRef() { return refcnt_.FetchAdd(1, MemoryOrder::RELAXED) == 0; }
+  intptr_t RefValue() { return refcnt_.Load(MemoryOrder::RELAXED); }
+
+  /* must be byte compatible with grpc_mdelem_data */
+  grpc_slice key_;
+  grpc_slice value_;
+
+  /* private only data */
+  grpc_core::Atomic<intptr_t> refcnt_;
+  uint32_t hash_;
+
+  UserData user_data_;
+
+  BucketLink link_;
+};
+
+/* Shadow structure for grpc_mdelem_data for allocated elements */
+class AllocatedMetadata {
+ public:
+  AllocatedMetadata(const grpc_slice& key, const grpc_slice& value);
+  ~AllocatedMetadata();
+
+  const grpc_slice& key() const { return key_; }
+  const grpc_slice& value() const { return value_; }
+  UserData* user_data() { return &user_data_; }
+
+#ifndef NDEBUG
+  void Ref(const char* file, int line) {
+    grpc_mdelem_trace_ref(this, key_, value_, RefValue(), file, line);
+    Ref();
+  }
+  bool Unref(const char* file, int line) {
+    grpc_mdelem_trace_unref(this, key_, value_, RefValue(), file, line);
+    return Unref();
+  }
+#endif  // ifndef NDEBUG
+  void Ref() {
+    /* we can assume the ref count is >= 1 as the application is calling
+       this function - meaning that no adjustment to mdtab_free is necessary,
+       simplifying the logic here to be just an atomic increment */
+    refcnt_.FetchAdd(1, MemoryOrder::RELAXED);
+  }
+  bool Unref() {
+    const intptr_t prior = refcnt_.FetchSub(1, MemoryOrder::ACQ_REL);
+    GPR_DEBUG_ASSERT(prior > 0);
+    return prior == 1;
+  }
+
+ private:
+  intptr_t RefValue() { return refcnt_.Load(MemoryOrder::RELAXED); }
+
+  /* must be byte compatible with grpc_mdelem_data */
+  grpc_slice key_;
+  grpc_slice value_;
+
+  /* private only data */
+  grpc_core::Atomic<intptr_t> refcnt_;
+
+  UserData user_data_;
+};
+
+}  // namespace grpc_core
 
 #ifndef NDEBUG
 #define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s), __FILE__, __LINE__)
+inline grpc_mdelem grpc_mdelem_ref(grpc_mdelem gmd, const char* file,
+                                   int line) {
+#else  // ifndef NDEBUG
+#define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s))
+inline grpc_mdelem grpc_mdelem_ref(grpc_mdelem gmd) {
+#endif  // ifndef NDEBUG
+  switch (GRPC_MDELEM_STORAGE(gmd)) {
+    case GRPC_MDELEM_STORAGE_EXTERNAL:
+    case GRPC_MDELEM_STORAGE_STATIC:
+      break;
+    case GRPC_MDELEM_STORAGE_INTERNED: {
+      auto* md =
+          reinterpret_cast<grpc_core::InternedMetadata*> GRPC_MDELEM_DATA(gmd);
+      /* use C assert to have this removed in opt builds */
+#ifndef NDEBUG
+      md->Ref(file, line);
+#else
+      md->Ref();
+#endif
+      break;
+    }
+    case GRPC_MDELEM_STORAGE_ALLOCATED: {
+      auto* md =
+          reinterpret_cast<grpc_core::AllocatedMetadata*> GRPC_MDELEM_DATA(gmd);
+#ifndef NDEBUG
+      md->Ref(file, line);
+#else
+      md->Ref();
+#endif
+      break;
+    }
+  }
+  return gmd;
+}
+
+#ifndef NDEBUG
 #define GRPC_MDELEM_UNREF(s) grpc_mdelem_unref((s), __FILE__, __LINE__)
-grpc_mdelem grpc_mdelem_ref(grpc_mdelem md, const char* file, int line);
-void grpc_mdelem_unref(grpc_mdelem md, const char* file, int line);
+void grpc_mdelem_do_unref(grpc_mdelem gmd, const char* file, int line);
+inline void grpc_mdelem_unref(grpc_mdelem gmd, const char* file, int line) {
 #else
-#define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s))
 #define GRPC_MDELEM_UNREF(s) grpc_mdelem_unref((s))
-grpc_mdelem grpc_mdelem_ref(grpc_mdelem md);
-void grpc_mdelem_unref(grpc_mdelem md);
+void grpc_mdelem_do_unref(grpc_mdelem gmd);
+inline void grpc_mdelem_unref(grpc_mdelem gmd) {
 #endif
-
-#define GRPC_MDKEY(md) (GRPC_MDELEM_DATA(md)->key)
-#define GRPC_MDVALUE(md) (GRPC_MDELEM_DATA(md)->value)
+  switch (GRPC_MDELEM_STORAGE(gmd)) {
+    case GRPC_MDELEM_STORAGE_EXTERNAL:
+    case GRPC_MDELEM_STORAGE_STATIC:
+      return;
+    case GRPC_MDELEM_STORAGE_INTERNED:
+    case GRPC_MDELEM_STORAGE_ALLOCATED:
+#ifndef NDEBUG
+      grpc_mdelem_do_unref(gmd, file, line);
+#else
+      grpc_mdelem_do_unref(gmd);
+#endif
+      return;
+  }
+}
 
 #define GRPC_MDNULL GRPC_MAKE_MDELEM(NULL, GRPC_MDELEM_STORAGE_EXTERNAL)
 #define GRPC_MDISNULL(md) (GRPC_MDELEM_DATA(md) == NULL)
index 963626a..fc5bb64 100644 (file)
@@ -116,123 +116,115 @@ static uint8_t g_bytes[] = {
     103, 122, 105, 112, 105, 100, 101, 110, 116, 105, 116, 121, 44,  100, 101,
     102, 108, 97,  116, 101, 44,  103, 122, 105, 112};
 
-static void static_ref(void* unused) {}
-static void static_unref(void* unused) {}
-static const grpc_slice_refcount_vtable static_sub_vtable = {
-    static_ref, static_unref, grpc_slice_default_eq_impl,
-    grpc_slice_default_hash_impl};
-const grpc_slice_refcount_vtable grpc_static_metadata_vtable = {
-    static_ref, static_unref, grpc_static_slice_eq, grpc_static_slice_hash};
-static grpc_slice_refcount static_sub_refcnt = {&static_sub_vtable,
-                                                &static_sub_refcnt};
+static grpc_slice_refcount static_sub_refcnt;
 grpc_slice_refcount grpc_static_metadata_refcounts[GRPC_STATIC_MDSTR_COUNT] = {
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
-    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
+    grpc_slice_refcount(&static_sub_refcnt, grpc_slice_refcount::Type::STATIC),
 };
 
 const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT] = {
index 4f96702..88293ae 100644 (file)
@@ -256,12 +256,11 @@ extern const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT];
 #define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \
   (grpc_static_slice_table[106])
 
-extern const grpc_slice_refcount_vtable grpc_static_metadata_vtable;
 extern grpc_slice_refcount
     grpc_static_metadata_refcounts[GRPC_STATIC_MDSTR_COUNT];
 #define GRPC_IS_STATIC_METADATA_STRING(slice) \
   ((slice).refcount != NULL &&                \
-   (slice).refcount->vtable == &grpc_static_metadata_vtable)
+   (slice).refcount->GetType() == grpc_slice_refcount::Type::STATIC)
 
 #define GRPC_STATIC_METADATA_INDEX(static_slice) \
   ((int)((static_slice).refcount - grpc_static_metadata_refcounts))
index f896053..8ef9682 100644 (file)
 static void destroy_status(void* ignored) {}
 
 grpc_status_code grpc_get_status_code_from_metadata(grpc_mdelem md) {
-  if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) {
+  if (grpc_mdelem_static_value_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) {
     return GRPC_STATUS_OK;
   }
-  if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_1)) {
+  if (grpc_mdelem_static_value_eq(md, GRPC_MDELEM_GRPC_STATUS_1)) {
     return GRPC_STATUS_CANCELLED;
   }
-  if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_2)) {
+  if (grpc_mdelem_static_value_eq(md, GRPC_MDELEM_GRPC_STATUS_2)) {
     return GRPC_STATUS_UNKNOWN;
   }
   void* user_data = grpc_mdelem_get_user_data(md, destroy_status);
index 0930611..29c1e56 100644 (file)
 grpc_core::DebugOnlyTraceFlag grpc_trace_stream_refcount(false,
                                                          "stream_refcount");
 
-#ifndef NDEBUG
-void grpc_stream_ref(grpc_stream_refcount* refcount, const char* reason) {
-  if (grpc_trace_stream_refcount.enabled()) {
-    gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count);
-    gpr_log(GPR_DEBUG, "%s %p:%p   REF %" PRIdPTR "->%" PRIdPTR " %s",
-            refcount->object_type, refcount, refcount->destroy.cb_arg, val,
-            val + 1, reason);
+void grpc_stream_destroy(grpc_stream_refcount* refcount) {
+  if (!grpc_iomgr_is_any_background_poller_thread() &&
+      (grpc_core::ExecCtx::Get()->flags() &
+       GRPC_EXEC_CTX_FLAG_THREAD_RESOURCE_LOOP)) {
+    /* Ick.
+       The thread we're running on MAY be owned (indirectly) by a call-stack.
+       If that's the case, destroying the call-stack MAY try to destroy the
+       thread, which is a tangled mess that we just don't want to ever have to
+       cope with.
+       Throw this over to the executor (on a core-owned thread) and process it
+       there. */
+    refcount->destroy.scheduler =
+        grpc_core::Executor::Scheduler(grpc_core::ExecutorJobType::SHORT);
   }
-#else
-void grpc_stream_ref(grpc_stream_refcount* refcount) {
-#endif
-  gpr_ref_non_zero(&refcount->refs);
+  GRPC_CLOSURE_SCHED(&refcount->destroy, GRPC_ERROR_NONE);
 }
 
-#ifndef NDEBUG
-void grpc_stream_unref(grpc_stream_refcount* refcount, const char* reason) {
-  if (grpc_trace_stream_refcount.enabled()) {
-    gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count);
-    gpr_log(GPR_DEBUG, "%s %p:%p UNREF %" PRIdPTR "->%" PRIdPTR " %s",
-            refcount->object_type, refcount, refcount->destroy.cb_arg, val,
-            val - 1, reason);
-  }
-#else
-void grpc_stream_unref(grpc_stream_refcount* refcount) {
-#endif
-  if (gpr_unref(&refcount->refs)) {
-    if (!grpc_iomgr_is_any_background_poller_thread() &&
-        (grpc_core::ExecCtx::Get()->flags() &
-         GRPC_EXEC_CTX_FLAG_THREAD_RESOURCE_LOOP)) {
-      /* Ick.
-         The thread we're running on MAY be owned (indirectly) by a call-stack.
-         If that's the case, destroying the call-stack MAY try to destroy the
-         thread, which is a tangled mess that we just don't want to ever have to
-         cope with.
-         Throw this over to the executor (on a core-owned thread) and process it
-         there. */
-      refcount->destroy.scheduler =
-          grpc_core::Executor::Scheduler(grpc_core::ExecutorJobType::SHORT);
-    }
-    GRPC_CLOSURE_SCHED(&refcount->destroy, GRPC_ERROR_NONE);
-  }
+void slice_stream_destroy(void* arg) {
+  grpc_stream_destroy(static_cast<grpc_stream_refcount*>(arg));
 }
 
 #define STREAM_REF_FROM_SLICE_REF(p)       \
   ((grpc_stream_refcount*)(((uint8_t*)p) - \
                            offsetof(grpc_stream_refcount, slice_refcount)))
 
-static void slice_stream_ref(void* p) {
-#ifndef NDEBUG
-  grpc_stream_ref(STREAM_REF_FROM_SLICE_REF(p), "slice");
-#else
-  grpc_stream_ref(STREAM_REF_FROM_SLICE_REF(p));
-#endif
-}
-
-static void slice_stream_unref(void* p) {
+grpc_slice grpc_slice_from_stream_owned_buffer(grpc_stream_refcount* refcount,
+                                               void* buffer, size_t length) {
 #ifndef NDEBUG
-  grpc_stream_unref(STREAM_REF_FROM_SLICE_REF(p), "slice");
+  grpc_stream_ref(STREAM_REF_FROM_SLICE_REF(&refcount->slice_refcount),
+                  "slice");
 #else
-  grpc_stream_unref(STREAM_REF_FROM_SLICE_REF(p));
+  grpc_stream_ref(STREAM_REF_FROM_SLICE_REF(&refcount->slice_refcount));
 #endif
-}
-
-grpc_slice grpc_slice_from_stream_owned_buffer(grpc_stream_refcount* refcount,
-                                               void* buffer, size_t length) {
-  slice_stream_ref(&refcount->slice_refcount);
   grpc_slice res;
   res.refcount = &refcount->slice_refcount;
   res.data.refcounted.bytes = static_cast<uint8_t*>(buffer);
@@ -112,13 +79,6 @@ grpc_slice grpc_slice_from_stream_owned_buffer(grpc_stream_refcount* refcount,
   return res;
 }
 
-static const grpc_slice_refcount_vtable stream_ref_slice_vtable = {
-    slice_stream_ref,            /* ref */
-    slice_stream_unref,          /* unref */
-    grpc_slice_default_eq_impl,  /* eq */
-    grpc_slice_default_hash_impl /* hash */
-};
-
 #ifndef NDEBUG
 void grpc_stream_ref_init(grpc_stream_refcount* refcount, int initial_refs,
                           grpc_iomgr_cb_func cb, void* cb_arg,
@@ -128,10 +88,12 @@ void grpc_stream_ref_init(grpc_stream_refcount* refcount, int initial_refs,
 void grpc_stream_ref_init(grpc_stream_refcount* refcount, int initial_refs,
                           grpc_iomgr_cb_func cb, void* cb_arg) {
 #endif
-  gpr_ref_init(&refcount->refs, initial_refs);
   GRPC_CLOSURE_INIT(&refcount->destroy, cb, cb_arg, grpc_schedule_on_exec_ctx);
-  refcount->slice_refcount.vtable = &stream_ref_slice_vtable;
-  refcount->slice_refcount.sub_refcount = &refcount->slice_refcount;
+
+  new (&refcount->refs) grpc_core::RefCount();
+  new (&refcount->slice_refcount) grpc_slice_refcount(
+      grpc_slice_refcount::Type::REGULAR, &refcount->refs, slice_stream_destroy,
+      refcount, &refcount->slice_refcount);
 }
 
 static void move64(uint64_t* from, uint64_t* to) {
@@ -162,7 +124,8 @@ void grpc_transport_destroy(grpc_transport* transport) {
 
 int grpc_transport_init_stream(grpc_transport* transport, grpc_stream* stream,
                                grpc_stream_refcount* refcount,
-                               const void* server_data, gpr_arena* arena) {
+                               const void* server_data,
+                               grpc_core::Arena* arena) {
   return transport->vtable->init_stream(transport, stream, refcount,
                                         server_data, arena);
 }
@@ -212,7 +175,7 @@ grpc_endpoint* grpc_transport_get_endpoint(grpc_transport* transport) {
 // it's grpc_transport_stream_op_batch_finish_with_failure
 void grpc_transport_stream_op_batch_finish_with_failure(
     grpc_transport_stream_op_batch* batch, grpc_error* error,
-    grpc_call_combiner* call_combiner) {
+    grpc_core::CallCombiner* call_combiner) {
   if (batch->send_message) {
     batch->payload->send_message.send_message.reset();
   }
index 8631a1a..a6a6e90 100644 (file)
 #include <stddef.h>
 
 #include "src/core/lib/channel/context.h"
-#include "src/core/lib/gpr/arena.h"
+#include "src/core/lib/gprpp/arena.h"
 #include "src/core/lib/iomgr/call_combiner.h"
 #include "src/core/lib/iomgr/endpoint.h"
 #include "src/core/lib/iomgr/polling_entity.h"
 #include "src/core/lib/iomgr/pollset.h"
 #include "src/core/lib/iomgr/pollset_set.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/transport/byte_stream.h"
 #include "src/core/lib/transport/metadata_batch.h"
 
@@ -51,7 +52,7 @@ typedef struct grpc_stream grpc_stream;
 extern grpc_core::DebugOnlyTraceFlag grpc_trace_stream_refcount;
 
 typedef struct grpc_stream_refcount {
-  gpr_refcount refs;
+  grpc_core::RefCount refs;
   grpc_closure destroy;
 #ifndef NDEBUG
   const char* object_type;
@@ -63,19 +64,45 @@ typedef struct grpc_stream_refcount {
 void grpc_stream_ref_init(grpc_stream_refcount* refcount, int initial_refs,
                           grpc_iomgr_cb_func cb, void* cb_arg,
                           const char* object_type);
-void grpc_stream_ref(grpc_stream_refcount* refcount, const char* reason);
-void grpc_stream_unref(grpc_stream_refcount* refcount, const char* reason);
 #define GRPC_STREAM_REF_INIT(rc, ir, cb, cb_arg, objtype) \
   grpc_stream_ref_init(rc, ir, cb, cb_arg, objtype)
 #else
 void grpc_stream_ref_init(grpc_stream_refcount* refcount, int initial_refs,
                           grpc_iomgr_cb_func cb, void* cb_arg);
-void grpc_stream_ref(grpc_stream_refcount* refcount);
-void grpc_stream_unref(grpc_stream_refcount* refcount);
 #define GRPC_STREAM_REF_INIT(rc, ir, cb, cb_arg, objtype) \
   grpc_stream_ref_init(rc, ir, cb, cb_arg)
 #endif
 
+#ifndef NDEBUG
+inline void grpc_stream_ref(grpc_stream_refcount* refcount,
+                            const char* reason) {
+  if (grpc_trace_stream_refcount.enabled()) {
+    gpr_log(GPR_DEBUG, "%s %p:%p REF %s", refcount->object_type, refcount,
+            refcount->destroy.cb_arg, reason);
+  }
+#else
+inline void grpc_stream_ref(grpc_stream_refcount* refcount) {
+#endif
+  refcount->refs.RefNonZero();
+}
+
+void grpc_stream_destroy(grpc_stream_refcount* refcount);
+
+#ifndef NDEBUG
+inline void grpc_stream_unref(grpc_stream_refcount* refcount,
+                              const char* reason) {
+  if (grpc_trace_stream_refcount.enabled()) {
+    gpr_log(GPR_DEBUG, "%s %p:%p UNREF %s", refcount->object_type, refcount,
+            refcount->destroy.cb_arg, reason);
+  }
+#else
+inline void grpc_stream_unref(grpc_stream_refcount* refcount) {
+#endif
+  if (GPR_UNLIKELY(refcount->refs.Unref())) {
+    grpc_stream_destroy(refcount);
+  }
+}
+
 /* Wrap a buffer that is owned by some stream object into a slice that shares
    the same refcount */
 grpc_slice grpc_slice_from_stream_owned_buffer(grpc_stream_refcount* refcount,
@@ -331,7 +358,8 @@ size_t grpc_transport_stream_size(grpc_transport* transport);
                    supplied from the accept_stream callback function */
 int grpc_transport_init_stream(grpc_transport* transport, grpc_stream* stream,
                                grpc_stream_refcount* refcount,
-                               const void* server_data, gpr_arena* arena);
+                               const void* server_data,
+                               grpc_core::Arena* arena);
 
 void grpc_transport_set_pops(grpc_transport* transport, grpc_stream* stream,
                              grpc_polling_entity* pollent);
@@ -352,7 +380,7 @@ void grpc_transport_destroy_stream(grpc_transport* transport,
 
 void grpc_transport_stream_op_batch_finish_with_failure(
     grpc_transport_stream_op_batch* op, grpc_error* error,
-    grpc_call_combiner* call_combiner);
+    grpc_core::CallCombiner* call_combiner);
 
 char* grpc_transport_stream_op_batch_string(grpc_transport_stream_op_batch* op);
 char* grpc_transport_op_string(grpc_transport_op* op);
index ba5e05d..526cc1b 100644 (file)
@@ -34,7 +34,7 @@ typedef struct grpc_transport_vtable {
   /* implementation of grpc_transport_init_stream */
   int (*init_stream)(grpc_transport* self, grpc_stream* stream,
                      grpc_stream_refcount* refcount, const void* server_data,
-                     gpr_arena* arena);
+                     grpc_core::Arena* arena);
 
   /* implementation of grpc_transport_set_pollset */
   void (*set_pollset)(grpc_transport* self, grpc_stream* stream,
index 4d4c495..fde88dd 100644 (file)
@@ -585,7 +585,7 @@ static tsi_result fake_handshaker_get_bytes_to_send_to_peer(
     if (next_message_to_send > TSI_FAKE_HANDSHAKE_MESSAGE_MAX) {
       next_message_to_send = TSI_FAKE_HANDSHAKE_MESSAGE_MAX;
     }
-    if (tsi_tracing_enabled.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(tsi_tracing_enabled)) {
       gpr_log(GPR_INFO, "%s prepared %s.",
               impl->is_client ? "Client" : "Server",
               tsi_fake_handshake_message_to_string(impl->next_message_to_send));
@@ -597,7 +597,7 @@ static tsi_result fake_handshaker_get_bytes_to_send_to_peer(
   if (!impl->is_client &&
       impl->next_message_to_send == TSI_FAKE_HANDSHAKE_MESSAGE_MAX) {
     /* We're done. */
-    if (tsi_tracing_enabled.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(tsi_tracing_enabled)) {
       gpr_log(GPR_INFO, "Server is done.");
     }
     impl->result = TSI_OK;
@@ -636,7 +636,7 @@ static tsi_result fake_handshaker_process_bytes_from_peer(
             tsi_fake_handshake_message_to_string(received_msg),
             tsi_fake_handshake_message_to_string(expected_msg));
   }
-  if (tsi_tracing_enabled.enabled()) {
+  if (GRPC_TRACE_FLAG_ENABLED(tsi_tracing_enabled)) {
     gpr_log(GPR_INFO, "%s received %s.", impl->is_client ? "Client" : "Server",
             tsi_fake_handshake_message_to_string(received_msg));
   }
@@ -644,7 +644,7 @@ static tsi_result fake_handshaker_process_bytes_from_peer(
   impl->needs_incoming_message = 0;
   if (impl->next_message_to_send == TSI_FAKE_HANDSHAKE_MESSAGE_MAX) {
     /* We're done. */
-    if (tsi_tracing_enabled.enabled()) {
+    if (GRPC_TRACE_FLAG_ENABLED(tsi_tracing_enabled)) {
       gpr_log(GPR_INFO, "%s is done.", impl->is_client ? "Client" : "Server");
     }
     impl->result = TSI_OK;
index f9184bc..ba0745a 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <grpc/support/port_platform.h>
 
-#include "src/core/lib/gprpp/mutex_lock.h"
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/slice/slice_internal.h"
 #include "src/core/tsi/ssl/session_cache/ssl_session.h"
 #include "src/core/tsi/ssl/session_cache/ssl_session_cache.h"
index cbdb422..25ae2ce 100644 (file)
@@ -213,7 +213,7 @@ static const char* ssl_error_string(int error) {
 /* TODO(jboeuf): Remove when we are past the debugging phase with this code. */
 static void ssl_log_where_info(const SSL* ssl, int where, int flag,
                                const char* msg) {
-  if ((where & flag) && tsi_tracing_enabled.enabled()) {
+  if ((where & flag) && GRPC_TRACE_FLAG_ENABLED(tsi_tracing_enabled)) {
     gpr_log(GPR_INFO, "%20.20s - %30.30s  - %5.10s", msg,
             SSL_state_string_long(ssl), SSL_state_string(ssl));
   }
index db59d4d..58f012d 100644 (file)
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/surface/completion_queue.h"
 
-namespace grpc {
-
-static internal::GrpcLibraryInitializer g_gli_initializer;
-Channel::Channel(
-    const grpc::string& host, grpc_channel* channel,
-    std::vector<
-        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
-        interceptor_creators)
+void grpc::experimental::ChannelResetConnectionBackoff(
+    ::grpc::Channel* channel) {
+  grpc_impl::experimental::ChannelResetConnectionBackoff(channel);
+}
+
+namespace grpc_impl {
+
+static ::grpc::internal::GrpcLibraryInitializer g_gli_initializer;
+Channel::Channel(const grpc::string& host, grpc_channel* channel,
+                 std::vector<std::unique_ptr<
+                     ::grpc::experimental::ClientInterceptorFactoryInterface>>
+                     interceptor_creators)
     : host_(host), c_channel_(channel) {
   interceptor_creators_ = std::move(interceptor_creators);
   g_gli_initializer.summon();
@@ -65,7 +69,8 @@ Channel::~Channel() {
 namespace {
 
 inline grpc_slice SliceFromArray(const char* arr, size_t len) {
-  return g_core_codegen_interface->grpc_slice_from_copied_buffer(arr, len);
+  return ::grpc::g_core_codegen_interface->grpc_slice_from_copied_buffer(arr,
+                                                                         len);
 }
 
 grpc::string GetChannelInfoField(grpc_channel* channel,
@@ -103,10 +108,9 @@ void ChannelResetConnectionBackoff(Channel* channel) {
 
 }  // namespace experimental
 
-internal::Call Channel::CreateCallInternal(const internal::RpcMethod& method,
-                                           ClientContext* context,
-                                           CompletionQueue* cq,
-                                           size_t interceptor_pos) {
+::grpc::internal::Call Channel::CreateCallInternal(
+    const ::grpc::internal::RpcMethod& method, ::grpc::ClientContext* context,
+    ::grpc::CompletionQueue* cq, size_t interceptor_pos) {
   const bool kRegistered = method.channel_tag() && context->authority().empty();
   grpc_call* c_call = nullptr;
   if (kRegistered) {
@@ -115,7 +119,7 @@ internal::Call Channel::CreateCallInternal(const internal::RpcMethod& method,
         context->propagation_options_.c_bitmask(), cq->cq(),
         method.channel_tag(), context->raw_deadline(), nullptr);
   } else {
-    const string* host_str = nullptr;
+    const ::grpc::string* host_str = nullptr;
     if (!context->authority_.empty()) {
       host_str = &context->authority_;
     } else if (!host_.empty()) {
@@ -125,7 +129,7 @@ internal::Call Channel::CreateCallInternal(const internal::RpcMethod& method,
         SliceFromArray(method.name(), strlen(method.name()));
     grpc_slice host_slice;
     if (host_str != nullptr) {
-      host_slice = SliceFromCopiedString(*host_str);
+      host_slice = ::grpc::SliceFromCopiedString(*host_str);
     }
     c_call = grpc_channel_create_call(
         c_channel_, context->propagate_from_call_,
@@ -147,17 +151,17 @@ internal::Call Channel::CreateCallInternal(const internal::RpcMethod& method,
                                    interceptor_creators_, interceptor_pos);
   context->set_call(c_call, shared_from_this());
 
-  return internal::Call(c_call, this, cq, info);
+  return ::grpc::internal::Call(c_call, this, cq, info);
 }
 
-internal::Call Channel::CreateCall(const internal::RpcMethod& method,
-                                   ClientContext* context,
-                                   CompletionQueue* cq) {
+::grpc::internal::Call Channel::CreateCall(
+    const ::grpc::internal::RpcMethod& method, ::grpc::ClientContext* context,
+    ::grpc::CompletionQueue* cq) {
   return CreateCallInternal(method, context, cq, 0);
 }
 
-void Channel::PerformOpsOnCall(internal::CallOpSetInterface* ops,
-                               internal::Call* call) {
+void Channel::PerformOpsOnCall(::grpc::internal::CallOpSetInterface* ops,
+                               ::grpc::internal::Call* call) {
   ops->FillOps(
       call);  // Make a copy of call. It's fine since Call just has pointers
 }
@@ -173,7 +177,7 @@ grpc_connectivity_state Channel::GetState(bool try_to_connect) {
 
 namespace {
 
-class TagSaver final : public internal::CompletionQueueTag {
+class TagSaver final : public ::grpc::internal::CompletionQueueTag {
  public:
   explicit TagSaver(void* tag) : tag_(tag) {}
   ~TagSaver() override {}
@@ -191,7 +195,7 @@ class TagSaver final : public internal::CompletionQueueTag {
 
 void Channel::NotifyOnStateChangeImpl(grpc_connectivity_state last_observed,
                                       gpr_timespec deadline,
-                                      CompletionQueue* cq, void* tag) {
+                                      ::grpc::CompletionQueue* cq, void* tag) {
   TagSaver* tag_saver = new TagSaver(tag);
   grpc_channel_watch_connectivity_state(c_channel_, last_observed, deadline,
                                         cq->cq(), tag_saver);
@@ -199,7 +203,7 @@ void Channel::NotifyOnStateChangeImpl(grpc_connectivity_state last_observed,
 
 bool Channel::WaitForStateChangeImpl(grpc_connectivity_state last_observed,
                                      gpr_timespec deadline) {
-  CompletionQueue cq;
+  ::grpc::CompletionQueue cq;
   bool ok = false;
   void* tag = nullptr;
   NotifyOnStateChangeImpl(last_observed, deadline, &cq, nullptr);
@@ -214,7 +218,7 @@ class ShutdownCallback : public grpc_experimental_completion_queue_functor {
   ShutdownCallback() { functor_run = &ShutdownCallback::Run; }
   // TakeCQ takes ownership of the cq into the shutdown callback
   // so that the shutdown callback will be responsible for destroying it
-  void TakeCQ(CompletionQueue* cq) { cq_ = cq; }
+  void TakeCQ(::grpc::CompletionQueue* cq) { cq_ = cq; }
 
   // The Run function will get invoked by the completion queue library
   // when the shutdown is actually complete
@@ -225,17 +229,17 @@ class ShutdownCallback : public grpc_experimental_completion_queue_functor {
   }
 
  private:
-  CompletionQueue* cq_ = nullptr;
+  ::grpc::CompletionQueue* cq_ = nullptr;
 };
 }  // namespace
 
-CompletionQueue* Channel::CallbackCQ() {
+::grpc::CompletionQueue* Channel::CallbackCQ() {
   // TODO(vjpai): Consider using a single global CQ for the default CQ
   // if there is no explicit per-channel CQ registered
-  std::lock_guard<std::mutex> l(mu_);
+  grpc::internal::MutexLock l(&mu_);
   if (callback_cq_ == nullptr) {
     auto* shutdown_callback = new ShutdownCallback;
-    callback_cq_ = new CompletionQueue(grpc_completion_queue_attributes{
+    callback_cq_ = new ::grpc::CompletionQueue(grpc_completion_queue_attributes{
         GRPC_CQ_CURRENT_VERSION, GRPC_CQ_CALLBACK, GRPC_CQ_DEFAULT_POLLING,
         shutdown_callback});
 
@@ -245,4 +249,4 @@ CompletionQueue* Channel::CallbackCQ() {
   return callback_cq_;
 }
 
-}  // namespace grpc
+}  // namespace grpc_impl
index efb59c7..0ae1ecb 100644 (file)
 #include <grpc/support/string_util.h>
 
 #include <grpcpp/impl/codegen/interceptor_common.h>
+#include <grpcpp/impl/codegen/sync.h>
 #include <grpcpp/impl/grpc_library.h>
 #include <grpcpp/security/credentials.h>
 #include <grpcpp/server_context.h>
 #include <grpcpp/support/time.h>
 
+namespace grpc_impl {
+
+class Channel;
+}
+
 namespace grpc {
 
 class DefaultGlobalClientCallbacks final
@@ -82,9 +88,9 @@ void ClientContext::AddMetadata(const grpc::string& meta_key,
   send_initial_metadata_.insert(std::make_pair(meta_key, meta_value));
 }
 
-void ClientContext::set_call(grpc_call* call,
-                             const std::shared_ptr<Channel>& channel) {
-  std::unique_lock<std::mutex> lock(mu_);
+void ClientContext::set_call(
+    grpc_call* call, const std::shared_ptr<::grpc_impl::Channel>& channel) {
+  grpc::internal::MutexLock lock(&mu_);
   GPR_ASSERT(call_ == nullptr);
   call_ = call;
   channel_ = channel;
@@ -114,7 +120,7 @@ void ClientContext::set_compression_algorithm(
 }
 
 void ClientContext::TryCancel() {
-  std::unique_lock<std::mutex> lock(mu_);
+  grpc::internal::MutexLock lock(&mu_);
   if (call_) {
     SendCancelToInterceptors();
     grpc_call_cancel(call_, nullptr);
index 457daa6..3318ded 100644 (file)
 #include <grpcpp/channel.h>
 #include <grpcpp/create_channel.h>
 #include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/security/credentials.h>
 #include <grpcpp/support/channel_arguments.h>
 
 #include "src/cpp/client/create_channel_internal.h"
 
-namespace grpc {
-class ChannelArguments;
-
-std::shared_ptr<Channel> CreateChannel(
+namespace grpc_impl {
+std::shared_ptr<grpc::Channel> CreateChannelImpl(
     const grpc::string& target,
-    const std::shared_ptr<ChannelCredentials>& creds) {
-  return CreateCustomChannel(target, creds, ChannelArguments());
+    const std::shared_ptr<grpc::ChannelCredentials>& creds) {
+  return CreateCustomChannelImpl(target, creds, grpc::ChannelArguments());
 }
 
-std::shared_ptr<Channel> CreateCustomChannel(
+std::shared_ptr<grpc::Channel> CreateCustomChannelImpl(
     const grpc::string& target,
-    const std::shared_ptr<ChannelCredentials>& creds,
-    const ChannelArguments& args) {
-  GrpcLibraryCodegen init_lib;  // We need to call init in case of a bad creds.
-  return creds ? creds->CreateChannel(target, args)
-               : CreateChannelInternal(
+    const std::shared_ptr<grpc::ChannelCredentials>& creds,
+    const grpc::ChannelArguments& args) {
+  grpc::GrpcLibraryCodegen
+      init_lib;  // We need to call init in case of a bad creds.
+  return creds ? creds->CreateChannelImpl(target, args)
+               : grpc::CreateChannelInternal(
                      "",
                      grpc_lame_client_channel_create(
                          nullptr, GRPC_STATUS_INVALID_ARGUMENT,
                          "Invalid credentials."),
                      std::vector<std::unique_ptr<
-                         experimental::ClientInterceptorFactoryInterface>>());
+                         grpc::experimental::
+                             ClientInterceptorFactoryInterface>>());
 }
 
 namespace experimental {
@@ -61,23 +62,22 @@ namespace experimental {
 /// hold an object or is invalid, a lame channel (one on which all operations
 /// fail) is returned.
 /// \param args Options for channel creation.
-std::shared_ptr<Channel> CreateCustomChannelWithInterceptors(
+std::shared_ptr<grpc::Channel> CreateCustomChannelWithInterceptors(
     const grpc::string& target,
-    const std::shared_ptr<ChannelCredentials>& creds,
-    const ChannelArguments& args,
+    const std::shared_ptr<grpc::ChannelCredentials>& creds,
+    const grpc::ChannelArguments& args,
     std::vector<
-        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+        std::unique_ptr<grpc::experimental::ClientInterceptorFactoryInterface>>
         interceptor_creators) {
   return creds ? creds->CreateChannelWithInterceptors(
                      target, args, std::move(interceptor_creators))
-               : CreateChannelInternal(
+               : ::grpc::CreateChannelInternal(
                      "",
                      grpc_lame_client_channel_create(
                          nullptr, GRPC_STATUS_INVALID_ARGUMENT,
                          "Invalid credentials."),
-                     std::vector<std::unique_ptr<
-                         experimental::ClientInterceptorFactoryInterface>>());
+                     std::move(interceptor_creators));
 }
 }  // namespace experimental
 
-}  // namespace grpc
+}  // namespace grpc_impl
index a0efb97..63e1d14 100644 (file)
@@ -26,10 +26,11 @@ namespace grpc {
 
 std::shared_ptr<Channel> CreateChannelInternal(
     const grpc::string& host, grpc_channel* c_channel,
-    std::vector<
-        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+    std::vector<std::unique_ptr<
+        ::grpc::experimental::ClientInterceptorFactoryInterface>>
         interceptor_creators) {
   return std::shared_ptr<Channel>(
       new Channel(host, c_channel, std::move(interceptor_creators)));
 }
+
 }  // namespace grpc
index a90c92c..3b201af 100644 (file)
 struct grpc_channel;
 
 namespace grpc {
-class Channel;
 
 std::shared_ptr<Channel> CreateChannelInternal(
     const grpc::string& host, grpc_channel* c_channel,
-    std::vector<
-        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+    std::vector<std::unique_ptr<
+        ::grpc::experimental::ClientInterceptorFactoryInterface>>
         interceptor_creators);
 
 }  // namespace grpc
index 6de3735..ca26c3f 100644 (file)
 #include <grpc/grpc.h>
 #include <grpc/grpc_posix.h>
 #include <grpcpp/channel.h>
-#include <grpcpp/create_channel.h>
 #include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/support/channel_arguments.h>
 
 #include "src/cpp/client/create_channel_internal.h"
 
 namespace grpc_impl {
 
+class ChannelArguments;
+
 #ifdef GPR_SUPPORT_CHANNELS_FROM_FD
 
 std::shared_ptr<grpc::Channel> CreateInsecureChannelFromFd(
     const grpc::string& target, int fd) {
   grpc::internal::GrpcLibrary init_lib;
   init_lib.init();
-  return grpc::CreateChannelInternal(
+  return ::grpc::CreateChannelInternal(
       "", grpc_insecure_channel_create_from_fd(target.c_str(), fd, nullptr),
       std::vector<std::unique_ptr<
           grpc::experimental::ClientInterceptorFactoryInterface>>());
@@ -44,7 +46,7 @@ std::shared_ptr<grpc::Channel> CreateCustomInsecureChannelFromFd(
   init_lib.init();
   grpc_channel_args channel_args;
   args.SetChannelArgs(&channel_args);
-  return grpc::CreateChannelInternal(
+  return ::grpc::CreateChannelInternal(
       "",
       grpc_insecure_channel_create_from_fd(target.c_str(), fd, &channel_args),
       std::vector<std::unique_ptr<
@@ -63,7 +65,7 @@ CreateCustomInsecureChannelWithInterceptorsFromFd(
   init_lib.init();
   grpc_channel_args channel_args;
   args.SetChannelArgs(&channel_args);
-  return grpc::CreateChannelInternal(
+  return ::grpc::CreateChannelInternal(
       "",
       grpc_insecure_channel_create_from_fd(target.c_str(), fd, &channel_args),
       std::move(interceptor_creators));
index 2a0f06f..62334bd 100644 (file)
@@ -19,9 +19,9 @@
 #include <grpcpp/impl/grpc_library.h>
 #include <grpcpp/security/credentials.h>
 
-namespace grpc {
+namespace grpc_impl {
 
-static internal::GrpcLibraryInitializer g_gli_initializer;
+static grpc::internal::GrpcLibraryInitializer g_gli_initializer;
 ChannelCredentials::ChannelCredentials() { g_gli_initializer.summon(); }
 
 ChannelCredentials::~ChannelCredentials() {}
@@ -30,4 +30,4 @@ CallCredentials::CallCredentials() { g_gli_initializer.summon(); }
 
 CallCredentials::~CallCredentials() {}
 
-}  // namespace grpc
+}  // namespace grpc_impl
index b280176..f4ead14 100644 (file)
@@ -29,7 +29,7 @@ class CronetChannelCredentialsImpl final : public ChannelCredentials {
  public:
   CronetChannelCredentialsImpl(void* engine) : engine_(engine) {}
 
-  std::shared_ptr<grpc::Channel> CreateChannel(
+  std::shared_ptr<grpc::Channel> CreateChannelImpl(
       const string& target, const grpc::ChannelArguments& args) override {
     return CreateChannelWithInterceptors(
         target, args,
@@ -55,10 +55,10 @@ class CronetChannelCredentialsImpl final : public ChannelCredentials {
   }
   void* engine_;
 };
-
+}  // namespace grpc
+namespace grpc_impl {
 std::shared_ptr<ChannelCredentials> CronetChannelCredentials(void* engine) {
   return std::shared_ptr<ChannelCredentials>(
-      new CronetChannelCredentialsImpl(engine));
+      new grpc::CronetChannelCredentialsImpl(engine));
 }
-
-}  // namespace grpc
+}  // namespace grpc_impl
index f61c1b5..41631c7 100644 (file)
 #include <grpcpp/impl/rpc_method.h>
 #include <grpcpp/support/client_callback.h>
 
-namespace grpc {
+namespace grpc_impl {
 
 namespace {
-std::unique_ptr<GenericClientAsyncReaderWriter> CallInternal(
-    ChannelInterface* channel, ClientContext* context,
-    const grpc::string& method, CompletionQueue* cq, bool start, void* tag) {
-  return std::unique_ptr<GenericClientAsyncReaderWriter>(
-      internal::ClientAsyncReaderWriterFactory<ByteBuffer, ByteBuffer>::Create(
-          channel, cq,
-          internal::RpcMethod(method.c_str(),
-                              internal::RpcMethod::BIDI_STREAMING),
-          context, start, tag));
+std::unique_ptr<grpc::GenericClientAsyncReaderWriter> CallInternal(
+    grpc::ChannelInterface* channel, grpc::ClientContext* context,
+    const grpc::string& method, grpc::CompletionQueue* cq, bool start,
+    void* tag) {
+  return std::unique_ptr<grpc::GenericClientAsyncReaderWriter>(
+      grpc::internal::ClientAsyncReaderWriterFactory<grpc::ByteBuffer,
+                                                     grpc::ByteBuffer>::
+          Create(channel, cq,
+                 grpc::internal::RpcMethod(
+                     method.c_str(), grpc::internal::RpcMethod::BIDI_STREAMING),
+                 context, start, tag));
 }
 
 }  // namespace
 
 // begin a call to a named method
-std::unique_ptr<GenericClientAsyncReaderWriter> GenericStub::Call(
-    ClientContext* context, const grpc::string& method, CompletionQueue* cq,
-    void* tag) {
+std::unique_ptr<grpc::GenericClientAsyncReaderWriter> GenericStub::Call(
+    grpc::ClientContext* context, const grpc::string& method,
+    grpc::CompletionQueue* cq, void* tag) {
   return CallInternal(channel_.get(), context, method, cq, true, tag);
 }
 
 // setup a call to a named method
-std::unique_ptr<GenericClientAsyncReaderWriter> GenericStub::PrepareCall(
-    ClientContext* context, const grpc::string& method, CompletionQueue* cq) {
+std::unique_ptr<grpc::GenericClientAsyncReaderWriter> GenericStub::PrepareCall(
+    grpc::ClientContext* context, const grpc::string& method,
+    grpc::CompletionQueue* cq) {
   return CallInternal(channel_.get(), context, method, cq, false, nullptr);
 }
 
 // setup a unary call to a named method
-std::unique_ptr<GenericClientAsyncResponseReader> GenericStub::PrepareUnaryCall(
-    ClientContext* context, const grpc::string& method,
-    const ByteBuffer& request, CompletionQueue* cq) {
-  return std::unique_ptr<GenericClientAsyncResponseReader>(
-      internal::ClientAsyncResponseReaderFactory<ByteBuffer>::Create(
-          channel_.get(), cq,
-          internal::RpcMethod(method.c_str(), internal::RpcMethod::NORMAL_RPC),
-          context, request, false));
+std::unique_ptr<grpc::GenericClientAsyncResponseReader>
+GenericStub::PrepareUnaryCall(grpc::ClientContext* context,
+                              const grpc::string& method,
+                              const grpc::ByteBuffer& request,
+                              grpc::CompletionQueue* cq) {
+  return std::unique_ptr<grpc::GenericClientAsyncResponseReader>(
+      grpc::internal::ClientAsyncResponseReaderFactory<
+          grpc::ByteBuffer>::Create(channel_.get(), cq,
+                                    grpc::internal::RpcMethod(
+                                        method.c_str(),
+                                        grpc::internal::RpcMethod::NORMAL_RPC),
+                                    context, request, false));
 }
 
 void GenericStub::experimental_type::UnaryCall(
-    ClientContext* context, const grpc::string& method,
-    const ByteBuffer* request, ByteBuffer* response,
-    std::function<void(Status)> on_completion) {
-  internal::CallbackUnaryCall(
+    grpc::ClientContext* context, const grpc::string& method,
+    const grpc::ByteBuffer* request, grpc::ByteBuffer* response,
+    std::function<void(grpc::Status)> on_completion) {
+  grpc::internal::CallbackUnaryCall(
       stub_->channel_.get(),
-      internal::RpcMethod(method.c_str(), internal::RpcMethod::NORMAL_RPC),
+      grpc::internal::RpcMethod(method.c_str(),
+                                grpc::internal::RpcMethod::NORMAL_RPC),
       context, request, response, std::move(on_completion));
 }
 
 void GenericStub::experimental_type::PrepareBidiStreamingCall(
-    ClientContext* context, const grpc::string& method,
-    experimental::ClientBidiReactor<ByteBuffer, ByteBuffer>* reactor) {
-  internal::ClientCallbackReaderWriterFactory<ByteBuffer, ByteBuffer>::Create(
-      stub_->channel_.get(),
-      internal::RpcMethod(method.c_str(), internal::RpcMethod::BIDI_STREAMING),
-      context, reactor);
+    grpc::ClientContext* context, const grpc::string& method,
+    grpc::experimental::ClientBidiReactor<grpc::ByteBuffer, grpc::ByteBuffer>*
+        reactor) {
+  grpc::internal::ClientCallbackReaderWriterFactory<
+      grpc::ByteBuffer,
+      grpc::ByteBuffer>::Create(stub_->channel_.get(),
+                                grpc::internal::RpcMethod(
+                                    method.c_str(),
+                                    grpc::internal::RpcMethod::BIDI_STREAMING),
+                                context, reactor);
 }
 
-}  // namespace grpc
+}  // namespace grpc_impl
index 241ce91..dcbb56d 100644 (file)
 #include <grpc/grpc.h>
 #include <grpc/support/log.h>
 #include <grpcpp/channel.h>
+#include <grpcpp/security/credentials.h>
 #include <grpcpp/support/channel_arguments.h>
 #include <grpcpp/support/config.h>
 #include "src/cpp/client/create_channel_internal.h"
 
-namespace grpc {
+namespace grpc_impl {
 
 namespace {
 class InsecureChannelCredentialsImpl final : public ChannelCredentials {
  public:
-  std::shared_ptr<grpc::Channel> CreateChannel(
-      const string& target, const grpc::ChannelArguments& args) override {
+  std::shared_ptr<::grpc::Channel> CreateChannelImpl(
+      const grpc::string& target, const grpc::ChannelArguments& args) override {
     return CreateChannelWithInterceptors(
         target, args,
         std::vector<std::unique_ptr<
-            experimental::ClientInterceptorFactoryInterface>>());
+            grpc::experimental::ClientInterceptorFactoryInterface>>());
   }
 
-  std::shared_ptr<grpc::Channel> CreateChannelWithInterceptors(
-      const string& target, const grpc::ChannelArguments& args,
-      std::vector<
-          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+  std::shared_ptr<::grpc::Channel> CreateChannelWithInterceptors(
+      const grpc::string& target, const grpc::ChannelArguments& args,
+      std::vector<std::unique_ptr<
+          grpc::experimental::ClientInterceptorFactoryInterface>>
           interceptor_creators) override {
     grpc_channel_args channel_args;
     args.SetChannelArgs(&channel_args);
-    return CreateChannelInternal(
+    return ::grpc::CreateChannelInternal(
         "",
         grpc_insecure_channel_create(target.c_str(), &channel_args, nullptr),
         std::move(interceptor_creators));
@@ -60,4 +61,4 @@ std::shared_ptr<ChannelCredentials> InsecureChannelCredentials() {
       new InsecureChannelCredentialsImpl());
 }
 
-}  // namespace grpc
+}  // namespace grpc_impl
index 4d0ed35..724a43a 100644 (file)
 #include "src/cpp/client/create_channel_internal.h"
 #include "src/cpp/common/secure_auth_context.h"
 
-namespace grpc {
+namespace grpc_impl {
 
-static internal::GrpcLibraryInitializer g_gli_initializer;
+static grpc::internal::GrpcLibraryInitializer g_gli_initializer;
 SecureChannelCredentials::SecureChannelCredentials(
     grpc_channel_credentials* c_creds)
     : c_creds_(c_creds) {
   g_gli_initializer.summon();
 }
 
-std::shared_ptr<grpc::Channel> SecureChannelCredentials::CreateChannel(
-    const string& target, const grpc::ChannelArguments& args) {
+std::shared_ptr<grpc::Channel> SecureChannelCredentials::CreateChannelImpl(
+    const grpc::string& target, const grpc::ChannelArguments& args) {
   return CreateChannelWithInterceptors(
       target, args,
-      std::vector<
-          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
+      std::vector<std::unique_ptr<
+          grpc::experimental::ClientInterceptorFactoryInterface>>());
 }
 
 std::shared_ptr<grpc::Channel>
 SecureChannelCredentials::CreateChannelWithInterceptors(
-    const string& target, const grpc::ChannelArguments& args,
+    const grpc::string& target, const grpc::ChannelArguments& args,
     std::vector<
-        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+        std::unique_ptr<grpc::experimental::ClientInterceptorFactoryInterface>>
         interceptor_creators) {
   grpc_channel_args channel_args;
   args.SetChannelArgs(&channel_args);
-  return CreateChannelInternal(
+  return ::grpc::CreateChannelInternal(
       args.GetSslTargetNameOverride(),
       grpc_secure_channel_create(c_creds_, target.c_str(), &channel_args,
                                  nullptr),
@@ -83,14 +83,14 @@ std::shared_ptr<CallCredentials> WrapCallCredentials(
 }  // namespace
 
 std::shared_ptr<ChannelCredentials> GoogleDefaultCredentials() {
-  GrpcLibraryCodegen init;  // To call grpc_init().
+  grpc::GrpcLibraryCodegen init;  // To call grpc_init().
   return WrapChannelCredentials(grpc_google_default_credentials_create());
 }
 
 // Builds SSL Credentials given SSL specific options
 std::shared_ptr<ChannelCredentials> SslCredentials(
     const SslCredentialsOptions& options) {
-  GrpcLibraryCodegen init;  // To call grpc_init().
+  grpc::GrpcLibraryCodegen init;  // To call grpc_init().
   grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {
       options.pem_private_key.c_str(), options.pem_cert_chain.c_str()};
 
@@ -106,7 +106,7 @@ namespace experimental {
 // Builds ALTS Credentials given ALTS specific options
 std::shared_ptr<ChannelCredentials> AltsCredentials(
     const AltsCredentialsOptions& options) {
-  GrpcLibraryCodegen init;  // To call grpc_init().
+  grpc::GrpcLibraryCodegen init;  // To call grpc_init().
   grpc_alts_credentials_options* c_options =
       grpc_alts_credentials_client_options_create();
   for (auto service_account = options.target_service_accounts.begin();
@@ -123,7 +123,7 @@ std::shared_ptr<ChannelCredentials> AltsCredentials(
 // Builds Local Credentials
 std::shared_ptr<ChannelCredentials> LocalCredentials(
     grpc_local_connect_type type) {
-  GrpcLibraryCodegen init;  // To call grpc_init().
+  grpc::GrpcLibraryCodegen init;  // To call grpc_init().
   return WrapChannelCredentials(grpc_local_credentials_create(type));
 }
 
@@ -131,7 +131,7 @@ std::shared_ptr<ChannelCredentials> LocalCredentials(
 
 // Builds credentials for use when running in GCE
 std::shared_ptr<CallCredentials> GoogleComputeEngineCredentials() {
-  GrpcLibraryCodegen init;  // To call grpc_init().
+  grpc::GrpcLibraryCodegen init;  // To call grpc_init().
   return WrapCallCredentials(
       grpc_google_compute_engine_credentials_create(nullptr));
 }
@@ -139,7 +139,7 @@ std::shared_ptr<CallCredentials> GoogleComputeEngineCredentials() {
 // Builds JWT credentials.
 std::shared_ptr<CallCredentials> ServiceAccountJWTAccessCredentials(
     const grpc::string& json_key, long token_lifetime_seconds) {
-  GrpcLibraryCodegen init;  // To call grpc_init().
+  grpc::GrpcLibraryCodegen init;  // To call grpc_init().
   if (token_lifetime_seconds <= 0) {
     gpr_log(GPR_ERROR,
             "Trying to create JWTCredentials with non-positive lifetime");
@@ -154,7 +154,7 @@ std::shared_ptr<CallCredentials> ServiceAccountJWTAccessCredentials(
 // Builds refresh token credentials.
 std::shared_ptr<CallCredentials> GoogleRefreshTokenCredentials(
     const grpc::string& json_refresh_token) {
-  GrpcLibraryCodegen init;  // To call grpc_init().
+  grpc::GrpcLibraryCodegen init;  // To call grpc_init().
   return WrapCallCredentials(grpc_google_refresh_token_credentials_create(
       json_refresh_token.c_str(), nullptr));
 }
@@ -162,7 +162,7 @@ std::shared_ptr<CallCredentials> GoogleRefreshTokenCredentials(
 // Builds access token credentials.
 std::shared_ptr<CallCredentials> AccessTokenCredentials(
     const grpc::string& access_token) {
-  GrpcLibraryCodegen init;  // To call grpc_init().
+  grpc::GrpcLibraryCodegen init;  // To call grpc_init().
   return WrapCallCredentials(
       grpc_access_token_credentials_create(access_token.c_str(), nullptr));
 }
@@ -171,7 +171,7 @@ std::shared_ptr<CallCredentials> AccessTokenCredentials(
 std::shared_ptr<CallCredentials> GoogleIAMCredentials(
     const grpc::string& authorization_token,
     const grpc::string& authority_selector) {
-  GrpcLibraryCodegen init;  // To call grpc_init().
+  grpc::GrpcLibraryCodegen init;  // To call grpc_init().
   return WrapCallCredentials(grpc_google_iam_credentials_create(
       authorization_token.c_str(), authority_selector.c_str(), nullptr));
 }
@@ -207,6 +207,23 @@ std::shared_ptr<CallCredentials> CompositeCallCredentials(
   return nullptr;
 }
 
+std::shared_ptr<grpc_impl::CallCredentials> MetadataCredentialsFromPlugin(
+    std::unique_ptr<MetadataCredentialsPlugin> plugin) {
+  grpc::GrpcLibraryCodegen init;  // To call grpc_init().
+  const char* type = plugin->GetType();
+  grpc::MetadataCredentialsPluginWrapper* wrapper =
+      new grpc::MetadataCredentialsPluginWrapper(std::move(plugin));
+  grpc_metadata_credentials_plugin c_plugin = {
+      grpc::MetadataCredentialsPluginWrapper::GetMetadata,
+      grpc::MetadataCredentialsPluginWrapper::Destroy, wrapper, type};
+  return WrapCallCredentials(
+      grpc_metadata_credentials_create_from_plugin(c_plugin, nullptr));
+}
+
+}  // namespace grpc_impl
+
+namespace grpc {
+
 void MetadataCredentialsPluginWrapper::Destroy(void* wrapper) {
   if (wrapper == nullptr) return;
   MetadataCredentialsPluginWrapper* w =
@@ -308,17 +325,4 @@ MetadataCredentialsPluginWrapper::MetadataCredentialsPluginWrapper(
     std::unique_ptr<MetadataCredentialsPlugin> plugin)
     : thread_pool_(CreateDefaultThreadPool()), plugin_(std::move(plugin)) {}
 
-std::shared_ptr<CallCredentials> MetadataCredentialsFromPlugin(
-    std::unique_ptr<MetadataCredentialsPlugin> plugin) {
-  GrpcLibraryCodegen init;  // To call grpc_init().
-  const char* type = plugin->GetType();
-  MetadataCredentialsPluginWrapper* wrapper =
-      new MetadataCredentialsPluginWrapper(std::move(plugin));
-  grpc_metadata_credentials_plugin c_plugin = {
-      MetadataCredentialsPluginWrapper::GetMetadata,
-      MetadataCredentialsPluginWrapper::Destroy, wrapper, type};
-  return WrapCallCredentials(
-      grpc_metadata_credentials_create_from_plugin(c_plugin, nullptr));
-}
-
 }  // namespace grpc
index 4918bd5..c4eef6c 100644 (file)
@@ -27,7 +27,9 @@
 #include "src/core/lib/security/credentials/credentials.h"
 #include "src/cpp/server/thread_pool_interface.h"
 
-namespace grpc {
+namespace grpc_impl {
+
+class Channel;
 
 class SecureChannelCredentials final : public ChannelCredentials {
  public:
@@ -37,16 +39,16 @@ class SecureChannelCredentials final : public ChannelCredentials {
   }
   grpc_channel_credentials* GetRawCreds() { return c_creds_; }
 
-  std::shared_ptr<grpc::Channel> CreateChannel(
-      const string& target, const grpc::ChannelArguments& args) override;
+  std::shared_ptr<::grpc::Channel> CreateChannelImpl(
+      const grpc::string& target, const grpc::ChannelArguments& args) override;
 
   SecureChannelCredentials* AsSecureCredentials() override { return this; }
 
  private:
-  std::shared_ptr<grpc::Channel> CreateChannelWithInterceptors(
-      const string& target, const grpc::ChannelArguments& args,
-      std::vector<
-          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+  std::shared_ptr<::grpc::Channel> CreateChannelWithInterceptors(
+      const grpc::string& target, const grpc::ChannelArguments& args,
+      std::vector<std::unique_ptr<
+          ::grpc::experimental::ClientInterceptorFactoryInterface>>
           interceptor_creators) override;
   grpc_channel_credentials* const c_creds_;
 };
@@ -66,6 +68,10 @@ class SecureCallCredentials final : public CallCredentials {
   grpc_call_credentials* const c_creds_;
 };
 
+}  // namespace grpc_impl
+
+namespace grpc {
+
 class MetadataCredentialsPluginWrapper final : private GrpcLibraryCodegen {
  public:
   static void Destroy(void* wrapper);
index 214d72f..9321398 100644 (file)
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/iomgr/socket_mutator.h"
 
-namespace grpc {
+namespace grpc_impl {
 
 ChannelArguments::ChannelArguments() {
   // This will be ignored if used on the server side.
-  SetString(GRPC_ARG_PRIMARY_USER_AGENT_STRING, "grpc-c++/" + Version());
+  SetString(GRPC_ARG_PRIMARY_USER_AGENT_STRING, "grpc-c++/" + grpc::Version());
 }
 
 ChannelArguments::ChannelArguments(const ChannelArguments& other)
@@ -143,7 +143,7 @@ void ChannelArguments::SetUserAgentPrefix(
 }
 
 void ChannelArguments::SetResourceQuota(
-    const grpc::ResourceQuota& resource_quota) {
+    const grpc_impl::ResourceQuota& resource_quota) {
   SetPointerWithVtable(GRPC_ARG_RESOURCE_QUOTA,
                        resource_quota.c_resource_quota(),
                        grpc_resource_quota_arg_vtable());
@@ -215,4 +215,4 @@ void ChannelArguments::SetChannelArgs(grpc_channel_args* channel_args) const {
   }
 }
 
-}  // namespace grpc
+}  // namespace grpc_impl
index 4bb3bcb..43c2eee 100644 (file)
@@ -24,9 +24,9 @@
 #include <grpcpp/impl/grpc_library.h>
 #include <grpcpp/support/time.h>
 
-namespace grpc {
+namespace grpc_impl {
 
-static internal::GrpcLibraryInitializer g_gli_initializer;
+static ::grpc::internal::GrpcLibraryInitializer g_gli_initializer;
 
 // 'CompletionQueue' constructor can safely call GrpcLibraryCodegen(false) here
 // i.e not have GrpcLibraryCodegen call grpc_init(). This is because, to create
@@ -52,7 +52,8 @@ CompletionQueue::NextStatus CompletionQueue::AsyncNextInternal(
       case GRPC_QUEUE_SHUTDOWN:
         return SHUTDOWN;
       case GRPC_OP_COMPLETE:
-        auto core_cq_tag = static_cast<internal::CompletionQueueTag*>(ev.tag);
+        auto core_cq_tag =
+            static_cast<::grpc::internal::CompletionQueueTag*>(ev.tag);
         *ok = ev.success != 0;
         *tag = core_cq_tag;
         if (core_cq_tag->FinalizeResult(tag, ok)) {
@@ -79,7 +80,8 @@ bool CompletionQueue::CompletionQueueTLSCache::Flush(void** tag, bool* ok) {
   flushed_ = true;
   if (grpc_completion_queue_thread_local_cache_flush(cq_->cq_, &res_tag,
                                                      &res)) {
-    auto core_cq_tag = static_cast<internal::CompletionQueueTag*>(res_tag);
+    auto core_cq_tag =
+        static_cast<::grpc::internal::CompletionQueueTag*>(res_tag);
     *ok = res == 1;
     if (core_cq_tag->FinalizeResult(tag, ok)) {
       return true;
@@ -88,4 +90,4 @@ bool CompletionQueue::CompletionQueueTLSCache::Flush(void** tag, bool* ok) {
   return false;
 }
 
-}  // namespace grpc
+}  // namespace grpc_impl
index 276e5f7..4fab297 100644 (file)
@@ -19,7 +19,7 @@
 #include <grpc/grpc.h>
 #include <grpcpp/resource_quota.h>
 
-namespace grpc {
+namespace grpc_impl {
 
 ResourceQuota::ResourceQuota() : impl_(grpc_resource_quota_create(nullptr)) {}
 
@@ -37,4 +37,4 @@ ResourceQuota& ResourceQuota::SetMaxThreads(int new_max_threads) {
   grpc_resource_quota_set_max_threads(impl_, new_max_threads);
   return *this;
 }
-}  // namespace grpc
+}  // namespace grpc_impl
index 2fb8ea4..e5d03cd 100644 (file)
@@ -21,7 +21,7 @@
 #include <grpc/grpc_security.h>
 #include "src/core/lib/channel/channel_args.h"
 
-namespace grpc {
+namespace grpc_impl {
 
 void ChannelArguments::SetSslTargetNameOverride(const grpc::string& name) {
   SetString(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, name);
@@ -36,4 +36,4 @@ grpc::string ChannelArguments::GetSslTargetNameOverride() const {
   return "";
 }
 
-}  // namespace grpc
+}  // namespace grpc_impl
index 2c17f47..10801e5 100644 (file)
@@ -22,5 +22,5 @@
 #include <grpcpp/grpcpp.h>
 
 namespace grpc {
-grpc::string Version() { return "1.20.1"; }
+grpc::string Version() { return "1.21.0"; }
 }  // namespace grpc
index ee3ac3f..0ac4cb8 100644 (file)
@@ -23,7 +23,7 @@
 
 #include "src/cpp/ext/proto_server_reflection.h"
 
-namespace grpc {
+namespace grpc_impl {
 namespace reflection {
 
 ProtoServerReflectionPlugin::ProtoServerReflectionPlugin()
@@ -79,4 +79,4 @@ struct StaticProtoReflectionPluginInitializer {
 } static_proto_reflection_plugin_initializer;
 
 }  // namespace reflection
-}  // namespace grpc
+}  // namespace grpc_impl
index c44a9ac..0409991 100644 (file)
 #include <grpc/support/alloc.h>
 
 namespace grpc {
+grpc::protobuf::util::Status ParseJson(const char* json_str,
+                                       grpc::protobuf::Message* message) {
+  grpc::protobuf::json::JsonParseOptions options;
+  options.case_insensitive_enum_parsing = true;
+  return grpc::protobuf::json::JsonStringToMessage(json_str, message, options);
+}
 
 Status ChannelzService::GetTopChannels(
     ServerContext* unused, const channelz::v1::GetTopChannelsRequest* request,
@@ -33,8 +39,7 @@ Status ChannelzService::GetTopChannels(
     return Status(StatusCode::INTERNAL,
                   "grpc_channelz_get_top_channels returned null");
   }
-  grpc::protobuf::util::Status s =
-      grpc::protobuf::json::JsonStringToMessage(json_str, response);
+  grpc::protobuf::util::Status s = ParseJson(json_str, response);
   gpr_free(json_str);
   if (!s.ok()) {
     return Status(StatusCode::INTERNAL, s.ToString());
@@ -50,8 +55,7 @@ Status ChannelzService::GetServers(
     return Status(StatusCode::INTERNAL,
                   "grpc_channelz_get_servers returned null");
   }
-  grpc::protobuf::util::Status s =
-      grpc::protobuf::json::JsonStringToMessage(json_str, response);
+  grpc::protobuf::util::Status s = ParseJson(json_str, response);
   gpr_free(json_str);
   if (!s.ok()) {
     return Status(StatusCode::INTERNAL, s.ToString());
@@ -67,8 +71,7 @@ Status ChannelzService::GetServer(ServerContext* unused,
     return Status(StatusCode::INTERNAL,
                   "grpc_channelz_get_server returned null");
   }
-  grpc::protobuf::util::Status s =
-      grpc::protobuf::json::JsonStringToMessage(json_str, response);
+  grpc::protobuf::util::Status s = ParseJson(json_str, response);
   gpr_free(json_str);
   if (!s.ok()) {
     return Status(StatusCode::INTERNAL, s.ToString());
@@ -85,8 +88,7 @@ Status ChannelzService::GetServerSockets(
     return Status(StatusCode::INTERNAL,
                   "grpc_channelz_get_server_sockets returned null");
   }
-  grpc::protobuf::util::Status s =
-      grpc::protobuf::json::JsonStringToMessage(json_str, response);
+  grpc::protobuf::util::Status s = ParseJson(json_str, response);
   gpr_free(json_str);
   if (!s.ok()) {
     return Status(StatusCode::INTERNAL, s.ToString());
@@ -101,8 +103,7 @@ Status ChannelzService::GetChannel(
   if (json_str == nullptr) {
     return Status(StatusCode::NOT_FOUND, "No object found for that ChannelId");
   }
-  grpc::protobuf::util::Status s =
-      grpc::protobuf::json::JsonStringToMessage(json_str, response);
+  grpc::protobuf::util::Status s = ParseJson(json_str, response);
   gpr_free(json_str);
   if (!s.ok()) {
     return Status(StatusCode::INTERNAL, s.ToString());
@@ -118,8 +119,7 @@ Status ChannelzService::GetSubchannel(
     return Status(StatusCode::NOT_FOUND,
                   "No object found for that SubchannelId");
   }
-  grpc::protobuf::util::Status s =
-      grpc::protobuf::json::JsonStringToMessage(json_str, response);
+  grpc::protobuf::util::Status s = ParseJson(json_str, response);
   gpr_free(json_str);
   if (!s.ok()) {
     return Status(StatusCode::INTERNAL, s.ToString());
@@ -134,8 +134,7 @@ Status ChannelzService::GetSocket(ServerContext* unused,
   if (json_str == nullptr) {
     return Status(StatusCode::NOT_FOUND, "No object found for that SocketId");
   }
-  grpc::protobuf::util::Status s =
-      grpc::protobuf::json::JsonStringToMessage(json_str, response);
+  grpc::protobuf::util::Status s = ParseJson(json_str, response);
   gpr_free(json_str);
   if (!s.ok()) {
     return Status(StatusCode::INTERNAL, s.ToString());
index ef99d64..c8bdbde 100644 (file)
@@ -21,6 +21,7 @@
 #include <mutex>
 
 #include <grpc/support/log.h>
+#include <grpcpp/impl/codegen/sync.h>
 
 #include "src/core/lib/gprpp/thd.h"
 
@@ -40,27 +41,27 @@ DynamicThreadPool::DynamicThread::~DynamicThread() { thd_.Join(); }
 void DynamicThreadPool::DynamicThread::ThreadFunc() {
   pool_->ThreadFunc();
   // Now that we have killed ourselves, we should reduce the thread count
-  std::unique_lock<std::mutex> lock(pool_->mu_);
+  grpc_core::MutexLock lock(&pool_->mu_);
   pool_->nthreads_--;
   // Move ourselves to dead list
   pool_->dead_threads_.push_back(this);
 
   if ((pool_->shutdown_) && (pool_->nthreads_ == 0)) {
-    pool_->shutdown_cv_.notify_one();
+    pool_->shutdown_cv_.Signal();
   }
 }
 
 void DynamicThreadPool::ThreadFunc() {
   for (;;) {
     // Wait until work is available or we are shutting down.
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc_core::ReleasableMutexLock lock(&mu_);
     if (!shutdown_ && callbacks_.empty()) {
       // If there are too many threads waiting, then quit this thread
       if (threads_waiting_ >= reserve_threads_) {
         break;
       }
       threads_waiting_++;
-      cv_.wait(lock);
+      cv_.Wait(&mu_);
       threads_waiting_--;
     }
     // Drain callbacks before considering shutdown to ensure all work
@@ -68,7 +69,7 @@ void DynamicThreadPool::ThreadFunc() {
     if (!callbacks_.empty()) {
       auto cb = callbacks_.front();
       callbacks_.pop();
-      lock.unlock();
+      lock.Unlock();
       cb();
     } else if (shutdown_) {
       break;
@@ -82,7 +83,7 @@ DynamicThreadPool::DynamicThreadPool(int reserve_threads)
       nthreads_(0),
       threads_waiting_(0) {
   for (int i = 0; i < reserve_threads_; i++) {
-    std::lock_guard<std::mutex> lock(mu_);
+    grpc_core::MutexLock lock(&mu_);
     nthreads_++;
     new DynamicThread(this);
   }
@@ -95,17 +96,17 @@ void DynamicThreadPool::ReapThreads(std::list<DynamicThread*>* tlist) {
 }
 
 DynamicThreadPool::~DynamicThreadPool() {
-  std::unique_lock<std::mutex> lock(mu_);
+  grpc_core::MutexLock lock(&mu_);
   shutdown_ = true;
-  cv_.notify_all();
+  cv_.Broadcast();
   while (nthreads_ != 0) {
-    shutdown_cv_.wait(lock);
+    shutdown_cv_.Wait(&mu_);
   }
   ReapThreads(&dead_threads_);
 }
 
 void DynamicThreadPool::Add(const std::function<void()>& callback) {
-  std::lock_guard<std::mutex> lock(mu_);
+  grpc_core::MutexLock lock(&mu_);
   // Add works to the callbacks list
   callbacks_.push(callback);
   // Increase pool size or notify as needed
@@ -114,7 +115,7 @@ void DynamicThreadPool::Add(const std::function<void()>& callback) {
     nthreads_++;
     new DynamicThread(this);
   } else {
-    cv_.notify_one();
+    cv_.Signal();
   }
   // Also use this chance to harvest dead threads
   if (!dead_threads_.empty()) {
index 5df8cf2..4ae0257 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <grpcpp/support/config.h>
 
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/gprpp/thd.h"
 #include "src/cpp/server/thread_pool_interface.h"
 
@@ -50,9 +51,9 @@ class DynamicThreadPool final : public ThreadPoolInterface {
     grpc_core::Thread thd_;
     void ThreadFunc();
   };
-  std::mutex mu_;
-  std::condition_variable cv_;
-  std::condition_variable shutdown_cv_;
+  grpc_core::Mutex mu_;
+  grpc_core::CondVar cv_;
+  grpc_core::CondVar shutdown_cv_;
   bool shutdown_;
   std::queue<std::function<void()>> callbacks_;
   int reserve_threads_;
index 44aebd2..01bc51a 100644 (file)
@@ -41,7 +41,7 @@ DefaultHealthCheckService::DefaultHealthCheckService() {
 
 void DefaultHealthCheckService::SetServingStatus(
     const grpc::string& service_name, bool serving) {
-  std::unique_lock<std::mutex> lock(mu_);
+  grpc_core::MutexLock lock(&mu_);
   if (shutdown_) {
     // Set to NOT_SERVING in case service_name is not in the map.
     serving = false;
@@ -51,7 +51,7 @@ void DefaultHealthCheckService::SetServingStatus(
 
 void DefaultHealthCheckService::SetServingStatus(bool serving) {
   const ServingStatus status = serving ? SERVING : NOT_SERVING;
-  std::unique_lock<std::mutex> lock(mu_);
+  grpc_core::MutexLock lock(&mu_);
   if (shutdown_) {
     return;
   }
@@ -62,7 +62,7 @@ void DefaultHealthCheckService::SetServingStatus(bool serving) {
 }
 
 void DefaultHealthCheckService::Shutdown() {
-  std::unique_lock<std::mutex> lock(mu_);
+  grpc_core::MutexLock lock(&mu_);
   if (shutdown_) {
     return;
   }
@@ -76,7 +76,7 @@ void DefaultHealthCheckService::Shutdown() {
 DefaultHealthCheckService::ServingStatus
 DefaultHealthCheckService::GetServingStatus(
     const grpc::string& service_name) const {
-  std::lock_guard<std::mutex> lock(mu_);
+  grpc_core::MutexLock lock(&mu_);
   auto it = services_map_.find(service_name);
   if (it == services_map_.end()) {
     return NOT_FOUND;
@@ -88,7 +88,7 @@ DefaultHealthCheckService::GetServingStatus(
 void DefaultHealthCheckService::RegisterCallHandler(
     const grpc::string& service_name,
     std::shared_ptr<HealthCheckServiceImpl::CallHandler> handler) {
-  std::unique_lock<std::mutex> lock(mu_);
+  grpc_core::MutexLock lock(&mu_);
   ServiceData& service_data = services_map_[service_name];
   service_data.AddCallHandler(handler /* copies ref */);
   HealthCheckServiceImpl::CallHandler* h = handler.get();
@@ -98,7 +98,7 @@ void DefaultHealthCheckService::RegisterCallHandler(
 void DefaultHealthCheckService::UnregisterCallHandler(
     const grpc::string& service_name,
     const std::shared_ptr<HealthCheckServiceImpl::CallHandler>& handler) {
-  std::unique_lock<std::mutex> lock(mu_);
+  grpc_core::MutexLock lock(&mu_);
   auto it = services_map_.find(service_name);
   if (it == services_map_.end()) return;
   ServiceData& service_data = it->second;
@@ -166,7 +166,7 @@ DefaultHealthCheckService::HealthCheckServiceImpl::~HealthCheckServiceImpl() {
   // We will reach here after the server starts shutting down.
   shutdown_ = true;
   {
-    std::unique_lock<std::mutex> lock(cq_shutdown_mu_);
+    grpc_core::MutexLock lock(&cq_shutdown_mu_);
     cq_->Shutdown();
   }
   thread_->Join();
@@ -266,7 +266,7 @@ void DefaultHealthCheckService::HealthCheckServiceImpl::CheckCallHandler::
       std::make_shared<CheckCallHandler>(cq, database, service);
   CheckCallHandler* handler = static_cast<CheckCallHandler*>(self.get());
   {
-    std::unique_lock<std::mutex> lock(service->cq_shutdown_mu_);
+    grpc_core::MutexLock lock(&service->cq_shutdown_mu_);
     if (service->shutdown_) return;
     // Request a Check() call.
     handler->next_ =
@@ -311,7 +311,7 @@ void DefaultHealthCheckService::HealthCheckServiceImpl::CheckCallHandler::
   }
   // Send response.
   {
-    std::unique_lock<std::mutex> lock(service_->cq_shutdown_mu_);
+    grpc_core::MutexLock lock(&service_->cq_shutdown_mu_);
     if (!service_->shutdown_) {
       next_ =
           CallableTag(std::bind(&CheckCallHandler::OnFinishDone, this,
@@ -347,7 +347,7 @@ void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler::
       std::make_shared<WatchCallHandler>(cq, database, service);
   WatchCallHandler* handler = static_cast<WatchCallHandler*>(self.get());
   {
-    std::unique_lock<std::mutex> lock(service->cq_shutdown_mu_);
+    grpc_core::MutexLock lock(&service->cq_shutdown_mu_);
     if (service->shutdown_) return;
     // Request AsyncNotifyWhenDone().
     handler->on_done_notified_ =
@@ -402,7 +402,7 @@ void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler::
 
 void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler::
     SendHealth(std::shared_ptr<CallHandler> self, ServingStatus status) {
-  std::unique_lock<std::mutex> lock(send_mu_);
+  grpc_core::MutexLock lock(&send_mu_);
   // If there's already a send in flight, cache the new status, and
   // we'll start a new send for it when the one in flight completes.
   if (send_in_flight_) {
@@ -420,7 +420,7 @@ void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler::
   ByteBuffer response;
   bool success = service_->EncodeResponse(status, &response);
   // Grab shutdown lock and send response.
-  std::unique_lock<std::mutex> cq_lock(service_->cq_shutdown_mu_);
+  grpc_core::MutexLock cq_lock(&service_->cq_shutdown_mu_);
   if (service_->shutdown_) {
     SendFinishLocked(std::move(self), Status::CANCELLED);
     return;
@@ -442,7 +442,7 @@ void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler::
     SendFinish(std::move(self), Status::CANCELLED);
     return;
   }
-  std::unique_lock<std::mutex> lock(send_mu_);
+  grpc_core::MutexLock lock(&send_mu_);
   send_in_flight_ = false;
   // If we got a new status since we started the last send, start a
   // new send for it.
@@ -456,7 +456,7 @@ void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler::
 void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler::
     SendFinish(std::shared_ptr<CallHandler> self, const Status& status) {
   if (finish_called_) return;
-  std::unique_lock<std::mutex> cq_lock(service_->cq_shutdown_mu_);
+  grpc_core::MutexLock cq_lock(&service_->cq_shutdown_mu_);
   if (service_->shutdown_) return;
   SendFinishLocked(std::move(self), status);
 }
index 9551cd2..4b926ef 100644 (file)
@@ -31,6 +31,7 @@
 #include <grpcpp/impl/codegen/service_type.h>
 #include <grpcpp/support/byte_buffer.h>
 
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/gprpp/thd.h"
 
 namespace grpc {
@@ -197,7 +198,7 @@ class DefaultHealthCheckService final : public HealthCheckServiceInterface {
       GenericServerAsyncWriter stream_;
       ServerContext ctx_;
 
-      std::mutex send_mu_;
+      grpc_core::Mutex send_mu_;
       bool send_in_flight_ = false;               // Guarded by mu_.
       ServingStatus pending_status_ = NOT_FOUND;  // Guarded by mu_.
 
@@ -226,7 +227,7 @@ class DefaultHealthCheckService final : public HealthCheckServiceInterface {
 
     // To synchronize the operations related to shutdown state of cq_, so that
     // we don't enqueue new tags into cq_ after it is already shut down.
-    std::mutex cq_shutdown_mu_;
+    grpc_core::Mutex cq_shutdown_mu_;
     std::atomic_bool shutdown_{false};
     std::unique_ptr<::grpc_core::Thread> thread_;
   };
@@ -273,7 +274,7 @@ class DefaultHealthCheckService final : public HealthCheckServiceInterface {
       const grpc::string& service_name,
       const std::shared_ptr<HealthCheckServiceImpl::CallHandler>& handler);
 
-  mutable std::mutex mu_;
+  mutable grpc_core::Mutex mu_;
   bool shutdown_ = false;                             // Guarded by mu_.
   std::map<grpc::string, ServiceData> services_map_;  // Guarded by mu_.
   std::unique_ptr<HealthCheckServiceImpl> impl_;
index a0fa2d6..ca0b49a 100644 (file)
@@ -16,9 +16,9 @@
  *
  */
 
-#include <grpcpp/health_check_service_interface.h>
+#include <grpcpp/health_check_service_interface_impl.h>
 
-namespace grpc {
+namespace grpc_impl {
 namespace {
 bool g_grpc_default_health_check_service_enabled = false;
 }  // namespace
@@ -31,4 +31,4 @@ void EnableDefaultHealthCheckService(bool enable) {
   g_grpc_default_health_check_service_enabled = enable;
 }
 
-}  // namespace grpc
+}  // namespace grpc_impl
index 7d749dd..e4623ec 100644 (file)
@@ -21,7 +21,7 @@
 #include <grpc/grpc.h>
 #include <grpc/support/log.h>
 
-namespace grpc {
+namespace grpc_impl {
 namespace {
 class InsecureServerCredentialsImpl final : public ServerCredentials {
  public:
@@ -29,7 +29,7 @@ class InsecureServerCredentialsImpl final : public ServerCredentials {
     return grpc_server_add_insecure_http2_port(server, addr.c_str());
   }
   void SetAuthMetadataProcessor(
-      const std::shared_ptr<AuthMetadataProcessor>& processor) override {
+      const std::shared_ptr<grpc::AuthMetadataProcessor>& processor) override {
     (void)processor;
     GPR_ASSERT(0);  // Should not be called on InsecureServerCredentials.
   }
@@ -41,4 +41,4 @@ std::shared_ptr<ServerCredentials> InsecureServerCredentials() {
       new InsecureServerCredentialsImpl());
 }
 
-}  // namespace grpc
+}  // namespace grpc_impl
index 464063a..422ea62 100644 (file)
@@ -239,7 +239,7 @@ grpc::string LoadReporter::GenerateLbId() {
 
 ::grpc::lb::v1::LoadBalancingFeedback
 LoadReporter::GenerateLoadBalancingFeedback() {
-  std::unique_lock<std::mutex> lock(feedback_mu_);
+  grpc_core::ReleasableMutexLock lock(&feedback_mu_);
   auto now = std::chrono::system_clock::now();
   // Discard records outside the window until there is only one record
   // outside the window, which is used as the base for difference.
@@ -277,7 +277,7 @@ LoadReporter::GenerateLoadBalancingFeedback() {
   double cpu_limit = newest->cpu_limit - oldest->cpu_limit;
   std::chrono::duration<double> duration_seconds =
       newest->end_time - oldest->end_time;
-  lock.unlock();
+  lock.Unlock();
   ::grpc::lb::v1::LoadBalancingFeedback feedback;
   feedback.set_server_utilization(static_cast<float>(cpu_usage / cpu_limit));
   feedback.set_calls_per_second(
@@ -290,7 +290,7 @@ LoadReporter::GenerateLoadBalancingFeedback() {
 ::google::protobuf::RepeatedPtrField<::grpc::lb::v1::Load>
 LoadReporter::GenerateLoads(const grpc::string& hostname,
                             const grpc::string& lb_id) {
-  std::lock_guard<std::mutex> lock(store_mu_);
+  grpc_core::MutexLock lock(&store_mu_);
   auto assigned_stores = load_data_store_.GetAssignedStores(hostname, lb_id);
   GPR_ASSERT(assigned_stores != nullptr);
   GPR_ASSERT(!assigned_stores->empty());
@@ -371,7 +371,7 @@ void LoadReporter::AppendNewFeedbackRecord(uint64_t rpcs, uint64_t errors) {
     // This will make the load balancing feedback generation a no-op.
     cpu_stats = {0, 0};
   }
-  std::unique_lock<std::mutex> lock(feedback_mu_);
+  grpc_core::MutexLock lock(&feedback_mu_);
   feedback_records_.emplace_back(std::chrono::system_clock::now(), rpcs, errors,
                                  cpu_stats.first, cpu_stats.second);
 }
@@ -379,7 +379,7 @@ void LoadReporter::AppendNewFeedbackRecord(uint64_t rpcs, uint64_t errors) {
 void LoadReporter::ReportStreamCreated(const grpc::string& hostname,
                                        const grpc::string& lb_id,
                                        const grpc::string& load_key) {
-  std::lock_guard<std::mutex> lock(store_mu_);
+  grpc_core::MutexLock lock(&store_mu_);
   load_data_store_.ReportStreamCreated(hostname, lb_id, load_key);
   gpr_log(GPR_INFO,
           "[LR %p] Report stream created (host: %s, LB ID: %s, load key: %s).",
@@ -388,7 +388,7 @@ void LoadReporter::ReportStreamCreated(const grpc::string& hostname,
 
 void LoadReporter::ReportStreamClosed(const grpc::string& hostname,
                                       const grpc::string& lb_id) {
-  std::lock_guard<std::mutex> lock(store_mu_);
+  grpc_core::MutexLock lock(&store_mu_);
   load_data_store_.ReportStreamClosed(hostname, lb_id);
   gpr_log(GPR_INFO, "[LR %p] Report stream closed (host: %s, LB ID: %s).", this,
           hostname.c_str(), lb_id.c_str());
@@ -407,7 +407,7 @@ void LoadReporter::ProcessViewDataCallStart(
       LoadRecordKey key(client_ip_and_token, user_id);
       LoadRecordValue value = LoadRecordValue(start_count);
       {
-        std::unique_lock<std::mutex> lock(store_mu_);
+        grpc_core::MutexLock lock(&store_mu_);
         load_data_store_.MergeRow(host, key, value);
       }
     }
@@ -459,7 +459,7 @@ void LoadReporter::ProcessViewDataCallEnd(
       LoadRecordValue value = LoadRecordValue(
           0, ok_count, error_count, bytes_sent, bytes_received, latency_ms);
       {
-        std::unique_lock<std::mutex> lock(store_mu_);
+        grpc_core::MutexLock lock(&store_mu_);
         load_data_store_.MergeRow(host, key, value);
       }
     }
@@ -486,7 +486,7 @@ void LoadReporter::ProcessViewDataOtherCallMetrics(
       LoadRecordValue value = LoadRecordValue(
           metric_name, static_cast<uint64_t>(num_calls), total_metric_value);
       {
-        std::unique_lock<std::mutex> lock(store_mu_);
+        grpc_core::MutexLock lock(&store_mu_);
         load_data_store_.MergeRow(host, key, value);
       }
     }
index b2254d5..766e02a 100644 (file)
@@ -29,6 +29,7 @@
 #include <grpc/support/log.h>
 #include <grpcpp/impl/codegen/config.h>
 
+#include "src/core/lib/gprpp/sync.h"
 #include "src/cpp/server/load_reporter/load_data_store.h"
 #include "src/proto/grpc/lb/v1/load_reporter.grpc.pb.h"
 
@@ -212,11 +213,11 @@ class LoadReporter {
 
   std::atomic<int64_t> next_lb_id_{0};
   const std::chrono::seconds feedback_sample_window_seconds_;
-  std::mutex feedback_mu_;
+  grpc_core::Mutex feedback_mu_;
   std::deque<LoadBalancingFeedbackRecord> feedback_records_;
   // TODO(juanlishen): Lock in finer grain. Locking the whole store may be
   // too expensive.
-  std::mutex store_mu_;
+  grpc_core::Mutex store_mu_;
   LoadDataStore load_data_store_;
   std::unique_ptr<CensusViewProvider> census_view_provider_;
   std::unique_ptr<CpuStatsProvider> cpu_stats_provider_;
index 859ad99..9eaab5d 100644 (file)
@@ -48,7 +48,7 @@ LoadReporterAsyncServiceImpl::~LoadReporterAsyncServiceImpl() {
   // We will reach here after the server starts shutting down.
   shutdown_ = true;
   {
-    std::unique_lock<std::mutex> lock(cq_shutdown_mu_);
+    grpc_core::MutexLock lock(&cq_shutdown_mu_);
     cq_->Shutdown();
   }
   if (next_fetch_and_sample_alarm_ != nullptr)
@@ -62,7 +62,7 @@ void LoadReporterAsyncServiceImpl::ScheduleNextFetchAndSample() {
                    gpr_time_from_millis(kFetchAndSampleIntervalSeconds * 1000,
                                         GPR_TIMESPAN));
   {
-    std::unique_lock<std::mutex> lock(cq_shutdown_mu_);
+    grpc_core::MutexLock lock(&cq_shutdown_mu_);
     if (shutdown_) return;
     // TODO(juanlishen): Improve the Alarm implementation to reuse a single
     // instance for multiple events.
@@ -119,7 +119,7 @@ void LoadReporterAsyncServiceImpl::ReportLoadHandler::CreateAndStart(
       std::make_shared<ReportLoadHandler>(cq, service, load_reporter);
   ReportLoadHandler* p = handler.get();
   {
-    std::unique_lock<std::mutex> lock(service->cq_shutdown_mu_);
+    grpc_core::MutexLock lock(&service->cq_shutdown_mu_);
     if (service->shutdown_) return;
     p->on_done_notified_ =
         CallableTag(std::bind(&ReportLoadHandler::OnDoneNotified, p,
@@ -164,9 +164,9 @@ void LoadReporterAsyncServiceImpl::ReportLoadHandler::OnRequestDelivered(
   // instance will deallocate itself when it's done.
   CreateAndStart(cq_, service_, load_reporter_);
   {
-    std::unique_lock<std::mutex> lock(service_->cq_shutdown_mu_);
+    grpc_core::ReleasableMutexLock lock(&service_->cq_shutdown_mu_);
     if (service_->shutdown_) {
-      lock.release()->unlock();
+      lock.Unlock();
       Shutdown(std::move(self), "OnRequestDelivered");
       return;
     }
@@ -222,9 +222,9 @@ void LoadReporterAsyncServiceImpl::ReportLoadHandler::OnReadDone(
       SendReport(self, true /* ok */);
       // Expect this read to fail.
       {
-        std::unique_lock<std::mutex> lock(service_->cq_shutdown_mu_);
+        grpc_core::ReleasableMutexLock lock(&service_->cq_shutdown_mu_);
         if (service_->shutdown_) {
-          lock.release()->unlock();
+          lock.Unlock();
           Shutdown(std::move(self), "OnReadDone");
           return;
         }
@@ -254,9 +254,9 @@ void LoadReporterAsyncServiceImpl::ReportLoadHandler::ScheduleNextReport(
       gpr_now(GPR_CLOCK_MONOTONIC),
       gpr_time_from_millis(load_report_interval_ms_, GPR_TIMESPAN));
   {
-    std::unique_lock<std::mutex> lock(service_->cq_shutdown_mu_);
+    grpc_core::ReleasableMutexLock lock(&service_->cq_shutdown_mu_);
     if (service_->shutdown_) {
-      lock.release()->unlock();
+      lock.Unlock();
       Shutdown(std::move(self), "ScheduleNextReport");
       return;
     }
@@ -294,9 +294,9 @@ void LoadReporterAsyncServiceImpl::ReportLoadHandler::SendReport(
     call_status_ = INITIAL_RESPONSE_SENT;
   }
   {
-    std::unique_lock<std::mutex> lock(service_->cq_shutdown_mu_);
+    grpc_core::ReleasableMutexLock lock(&service_->cq_shutdown_mu_);
     if (service_->shutdown_) {
-      lock.release()->unlock();
+      lock.Unlock();
       Shutdown(std::move(self), "SendReport");
       return;
     }
@@ -342,7 +342,7 @@ void LoadReporterAsyncServiceImpl::ReportLoadHandler::Shutdown(
   // OnRequestDelivered() may be called after OnDoneNotified(), so we need to
   // try to Finish() every time we are in Shutdown().
   if (call_status_ >= DELIVERED && call_status_ < FINISH_CALLED) {
-    std::unique_lock<std::mutex> lock(service_->cq_shutdown_mu_);
+    grpc_core::MutexLock lock(&service_->cq_shutdown_mu_);
     if (!service_->shutdown_) {
       on_finish_done_ =
           CallableTag(std::bind(&ReportLoadHandler::OnFinishDone, this,
index 6fc577f..3087cbf 100644 (file)
@@ -25,6 +25,7 @@
 #include <grpcpp/alarm.h>
 #include <grpcpp/grpcpp.h>
 
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/gprpp/thd.h"
 #include "src/cpp/server/load_reporter/load_reporter.h"
 
@@ -181,7 +182,7 @@ class LoadReporterAsyncServiceImpl
   std::unique_ptr<ServerCompletionQueue> cq_;
   // To synchronize the operations related to shutdown state of cq_, so that we
   // don't enqueue new tags into cq_ after it is already shut down.
-  std::mutex cq_shutdown_mu_;
+  grpc_core::Mutex cq_shutdown_mu_;
   std::atomic_bool shutdown_{false};
   std::unique_ptr<::grpc_core::Thread> thread_;
   std::unique_ptr<LoadReporter> load_reporter_;
index 81cf6ac..ab63beb 100644 (file)
@@ -22,7 +22,7 @@
 
 #include "src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.h"
 
-namespace grpc {
+namespace grpc_impl {
 namespace load_reporter {
 namespace experimental {
 
@@ -33,9 +33,10 @@ void LoadReportingServiceServerBuilderOption::UpdateArguments(
 
 void LoadReportingServiceServerBuilderOption::UpdatePlugins(
     std::vector<std::unique_ptr<::grpc::ServerBuilderPlugin>>* plugins) {
-  plugins->emplace_back(new LoadReportingServiceServerBuilderPlugin());
+  plugins->emplace_back(
+      new grpc::load_reporter::LoadReportingServiceServerBuilderPlugin());
 }
 
 }  // namespace experimental
 }  // namespace load_reporter
-}  // namespace grpc
+}  // namespace grpc_impl
index 1f09859..c80802b 100644 (file)
@@ -36,13 +36,13 @@ class LoadReportingServiceServerBuilderPlugin : public ServerBuilderPlugin {
   grpc::string name() override { return "load_reporting_service"; }
 
   // Creates a load reporting service.
-  void UpdateServerBuilder(grpc::ServerBuilder* builder) override;
+  void UpdateServerBuilder(ServerBuilder* builder) override;
 
   // Registers the load reporter service.
-  void InitServer(grpc::ServerInitializer* si) override;
+  void InitServer(grpc_impl::ServerInitializer* si) override;
 
   // Starts the load reporter service.
-  void Finish(grpc::ServerInitializer* si) override;
+  void Finish(grpc_impl::ServerInitializer* si) override;
 
   void ChangeArguments(const grpc::string& name, void* value) override {}
   void UpdateChannelArguments(grpc::ChannelArguments* args) override {}
index 89bdf57..b69705a 100644 (file)
@@ -24,7 +24,7 @@
 
 #include <grpc/support/log.h>
 
-namespace grpc {
+namespace grpc_impl {
 namespace load_reporter {
 namespace experimental {
 
@@ -44,4 +44,4 @@ void AddLoadReportingCost(grpc::ServerContext* ctx,
 
 }  // namespace experimental
 }  // namespace load_reporter
-}  // namespace grpc
+}  // namespace grpc_impl
index 453e76e..93dc10f 100644 (file)
@@ -93,21 +93,25 @@ void AuthMetadataProcessorAyncWrapper::InvokeProcessor(
      status.error_message().c_str());
 }
 
+}  // namespace grpc
+
+namespace grpc_impl {
+
 int SecureServerCredentials::AddPortToServer(const grpc::string& addr,
                                              grpc_server* server) {
   return grpc_server_add_secure_http2_port(server, addr.c_str(), creds_);
 }
 
 void SecureServerCredentials::SetAuthMetadataProcessor(
-    const std::shared_ptr<AuthMetadataProcessor>& processor) {
-  auto* wrapper = new AuthMetadataProcessorAyncWrapper(processor);
+    const std::shared_ptr<grpc::AuthMetadataProcessor>& processor) {
+  auto* wrapper = new grpc::AuthMetadataProcessorAyncWrapper(processor);
   grpc_server_credentials_set_auth_metadata_processor(
-      creds_, {AuthMetadataProcessorAyncWrapper::Process,
-               AuthMetadataProcessorAyncWrapper::Destroy, wrapper});
+      creds_, {grpc::AuthMetadataProcessorAyncWrapper::Process,
+               grpc::AuthMetadataProcessorAyncWrapper::Destroy, wrapper});
 }
 
 std::shared_ptr<ServerCredentials> SslServerCredentials(
-    const SslServerCredentialsOptions& options) {
+    const grpc::SslServerCredentialsOptions& options) {
   std::vector<grpc_ssl_pem_key_cert_pair> pem_key_cert_pairs;
   for (auto key_cert_pair = options.pem_key_cert_pairs.begin();
        key_cert_pair != options.pem_key_cert_pairs.end(); key_cert_pair++) {
@@ -147,4 +151,4 @@ std::shared_ptr<ServerCredentials> LocalServerCredentials(
 }
 
 }  // namespace experimental
-}  // namespace grpc
+}  // namespace grpc_impl
index 8a81af2..24b133c 100644 (file)
 
 #include "src/cpp/server/thread_pool_interface.h"
 
+namespace grpc_impl {
+
+class SecureServerCredentials;
+}  // namespace grpc_impl
+
 namespace grpc {
 
+typedef ::grpc_impl::SecureServerCredentials SecureServerCredentials;
+
 class AuthMetadataProcessorAyncWrapper final {
  public:
   static void Destroy(void* wrapper);
@@ -49,6 +56,10 @@ class AuthMetadataProcessorAyncWrapper final {
   std::shared_ptr<AuthMetadataProcessor> processor_;
 };
 
+}  // namespace grpc
+
+namespace grpc_impl {
+
 class SecureServerCredentials final : public ServerCredentials {
  public:
   explicit SecureServerCredentials(grpc_server_credentials* creds)
@@ -60,13 +71,13 @@ class SecureServerCredentials final : public ServerCredentials {
   int AddPortToServer(const grpc::string& addr, grpc_server* server) override;
 
   void SetAuthMetadataProcessor(
-      const std::shared_ptr<AuthMetadataProcessor>& processor) override;
+      const std::shared_ptr<grpc::AuthMetadataProcessor>& processor) override;
 
  private:
   grpc_server_credentials* creds_;
-  std::unique_ptr<AuthMetadataProcessorAyncWrapper> processor_;
+  std::unique_ptr<grpc::AuthMetadataProcessorAyncWrapper> processor_;
 };
 
-}  // namespace grpc
+}  // namespace grpc_impl
 
 #endif  // GRPC_INTERNAL_CPP_SERVER_SECURE_SERVER_CREDENTIALS_H
index cd0e516..af76ffb 100644 (file)
 #include "src/core/lib/gpr/useful.h"
 #include "src/cpp/server/thread_pool_interface.h"
 
-namespace grpc {
+namespace grpc_impl {
 
-static std::vector<std::unique_ptr<ServerBuilderPlugin> (*)()>*
+static std::vector<std::unique_ptr<grpc::ServerBuilderPlugin> (*)()>*
     g_plugin_factory_list;
 static gpr_once once_init_plugin_list = GPR_ONCE_INIT;
 
 static void do_plugin_list_init(void) {
   g_plugin_factory_list =
-      new std::vector<std::unique_ptr<ServerBuilderPlugin> (*)()>();
+      new std::vector<std::unique_ptr<grpc::ServerBuilderPlugin> (*)()>();
 }
 
 ServerBuilder::ServerBuilder()
@@ -67,29 +67,29 @@ ServerBuilder::~ServerBuilder() {
   }
 }
 
-std::unique_ptr<ServerCompletionQueue> ServerBuilder::AddCompletionQueue(
+std::unique_ptr<grpc::ServerCompletionQueue> ServerBuilder::AddCompletionQueue(
     bool is_frequently_polled) {
-  ServerCompletionQueue* cq = new ServerCompletionQueue(
+  grpc::ServerCompletionQueue* cq = new grpc::ServerCompletionQueue(
       GRPC_CQ_NEXT,
       is_frequently_polled ? GRPC_CQ_DEFAULT_POLLING : GRPC_CQ_NON_LISTENING,
       nullptr);
   cqs_.push_back(cq);
-  return std::unique_ptr<ServerCompletionQueue>(cq);
+  return std::unique_ptr<grpc::ServerCompletionQueue>(cq);
 }
 
-ServerBuilder& ServerBuilder::RegisterService(Service* service) {
+ServerBuilder& ServerBuilder::RegisterService(grpc::Service* service) {
   services_.emplace_back(new NamedService(service));
   return *this;
 }
 
 ServerBuilder& ServerBuilder::RegisterService(const grpc::string& addr,
-                                              Service* service) {
+                                              grpc::Service* service) {
   services_.emplace_back(new NamedService(addr, service));
   return *this;
 }
 
 ServerBuilder& ServerBuilder::RegisterAsyncGenericService(
-    AsyncGenericService* service) {
+    grpc::AsyncGenericService* service) {
   if (generic_service_ || callback_generic_service_) {
     gpr_log(GPR_ERROR,
             "Adding multiple generic services is unsupported for now. "
@@ -102,7 +102,7 @@ ServerBuilder& ServerBuilder::RegisterAsyncGenericService(
 }
 
 ServerBuilder& ServerBuilder::experimental_type::RegisterCallbackGenericService(
-    experimental::CallbackGenericService* service) {
+    grpc::experimental::CallbackGenericService* service) {
   if (builder_->generic_service_ || builder_->callback_generic_service_) {
     gpr_log(GPR_ERROR,
             "Adding multiple generic services is unsupported for now. "
@@ -115,7 +115,7 @@ ServerBuilder& ServerBuilder::experimental_type::RegisterCallbackGenericService(
 }
 
 ServerBuilder& ServerBuilder::SetOption(
-    std::unique_ptr<ServerBuilderOption> option) {
+    std::unique_ptr<grpc::ServerBuilderOption> option) {
   options_.push_back(std::move(option));
   return *this;
 }
@@ -164,7 +164,7 @@ ServerBuilder& ServerBuilder::SetDefaultCompressionAlgorithm(
 }
 
 ServerBuilder& ServerBuilder::SetResourceQuota(
-    const grpc::ResourceQuota& resource_quota) {
+    const grpc_impl::ResourceQuota& resource_quota) {
   if (resource_quota_ != nullptr) {
     grpc_resource_quota_unref(resource_quota_);
   }
@@ -174,8 +174,8 @@ ServerBuilder& ServerBuilder::SetResourceQuota(
 }
 
 ServerBuilder& ServerBuilder::AddListeningPort(
-    const grpc::string& addr_uri, std::shared_ptr<ServerCredentials> creds,
-    int* selected_port) {
+    const grpc::string& addr_uri,
+    std::shared_ptr<grpc::ServerCredentials> creds, int* selected_port) {
   const grpc::string uri_scheme = "dns:";
   grpc::string addr = addr_uri;
   if (addr_uri.compare(0, uri_scheme.size(), uri_scheme) == 0) {
@@ -188,8 +188,8 @@ ServerBuilder& ServerBuilder::AddListeningPort(
   return *this;
 }
 
-std::unique_ptr<Server> ServerBuilder::BuildAndStart() {
-  ChannelArguments args;
+std::unique_ptr<grpc::Server> ServerBuilder::BuildAndStart() {
+  grpc::ChannelArguments args;
   for (auto option = options_.begin(); option != options_.end(); ++option) {
     (*option)->UpdateArguments(&args);
     (*option)->UpdatePlugins(&plugins_);
@@ -251,9 +251,10 @@ std::unique_ptr<Server> 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<std::vector<std::unique_ptr<ServerCompletionQueue>>>
-      sync_server_cqs(std::make_shared<
-                      std::vector<std::unique_ptr<ServerCompletionQueue>>>());
+  std::shared_ptr<std::vector<std::unique_ptr<grpc::ServerCompletionQueue>>>
+      sync_server_cqs(
+          std::make_shared<
+              std::vector<std::unique_ptr<grpc::ServerCompletionQueue>>>());
 
   bool has_frequently_polled_cqs = false;
   for (auto it = cqs_.begin(); it != cqs_.end(); ++it) {
@@ -282,7 +283,7 @@ std::unique_ptr<Server> 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 ServerCompletionQueue(GRPC_CQ_NEXT, polling_type, nullptr));
+          new grpc::ServerCompletionQueue(GRPC_CQ_NEXT, polling_type, nullptr));
     }
   }
 
@@ -303,13 +304,13 @@ std::unique_ptr<Server> ServerBuilder::BuildAndStart() {
     gpr_log(GPR_INFO, "Callback server.");
   }
 
-  std::unique_ptr<Server> server(new Server(
+  std::unique_ptr<grpc::Server> server(new grpc::Server(
       max_receive_message_size_, &args, sync_server_cqs,
       sync_server_settings_.min_pollers, sync_server_settings_.max_pollers,
       sync_server_settings_.cq_timeout_msec, resource_quota_,
       std::move(interceptor_creators_)));
 
-  ServerInitializer* initializer = server->initializer();
+  grpc_impl::ServerInitializer* initializer = server->initializer();
 
   // Register all the completion queues with the server. i.e
   //  1. sync_server_cqs: internal completion queues created IF this is a sync
@@ -393,7 +394,7 @@ std::unique_ptr<Server> ServerBuilder::BuildAndStart() {
 }
 
 void ServerBuilder::InternalAddPluginFactory(
-    std::unique_ptr<ServerBuilderPlugin> (*CreatePlugin)()) {
+    std::unique_ptr<grpc::ServerBuilderPlugin> (*CreatePlugin)()) {
   gpr_once_init(&once_init_plugin_list, do_plugin_list_init);
   (*g_plugin_factory_list).push_back(CreatePlugin);
 }
@@ -408,4 +409,4 @@ ServerBuilder& ServerBuilder::EnableWorkaround(grpc_workaround_list id) {
   }
 }
 
-}  // namespace grpc
+}  // namespace grpc_impl
index 26e84f1..9c2b1ee 100644 (file)
@@ -106,15 +106,183 @@ class UnimplementedAsyncRequestContext {
 
 }  // namespace
 
+ServerInterface::BaseAsyncRequest::BaseAsyncRequest(
+    ServerInterface* server, ServerContext* context,
+    internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
+    ServerCompletionQueue* notification_cq, void* tag, bool delete_on_finalize)
+    : server_(server),
+      context_(context),
+      stream_(stream),
+      call_cq_(call_cq),
+      notification_cq_(notification_cq),
+      tag_(tag),
+      delete_on_finalize_(delete_on_finalize),
+      call_(nullptr),
+      done_intercepting_(false) {
+  /* Set up interception state partially for the receive ops. call_wrapper_ is
+   * not filled at this point, but it will be filled before the interceptors are
+   * run. */
+  interceptor_methods_.SetCall(&call_wrapper_);
+  interceptor_methods_.SetReverse();
+  call_cq_->RegisterAvalanching();  // This op will trigger more ops
+}
+
+ServerInterface::BaseAsyncRequest::~BaseAsyncRequest() {
+  call_cq_->CompleteAvalanching();
+}
+
+bool ServerInterface::BaseAsyncRequest::FinalizeResult(void** tag,
+                                                       bool* status) {
+  if (done_intercepting_) {
+    *tag = tag_;
+    if (delete_on_finalize_) {
+      delete this;
+    }
+    return true;
+  }
+  context_->set_call(call_);
+  context_->cq_ = call_cq_;
+  if (call_wrapper_.call() == nullptr) {
+    // Fill it since it is empty.
+    call_wrapper_ = internal::Call(
+        call_, server_, call_cq_, server_->max_receive_message_size(), nullptr);
+  }
+
+  // just the pointers inside call are copied here
+  stream_->BindCall(&call_wrapper_);
+
+  if (*status && call_ && call_wrapper_.server_rpc_info()) {
+    done_intercepting_ = true;
+    // Set interception point for RECV INITIAL METADATA
+    interceptor_methods_.AddInterceptionHookPoint(
+        experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA);
+    interceptor_methods_.SetRecvInitialMetadata(&context_->client_metadata_);
+    if (interceptor_methods_.RunInterceptors(
+            [this]() { ContinueFinalizeResultAfterInterception(); })) {
+      // There are no interceptors to run. Continue
+    } else {
+      // There were interceptors to be run, so
+      // ContinueFinalizeResultAfterInterception will be run when interceptors
+      // are done.
+      return false;
+    }
+  }
+  if (*status && call_) {
+    context_->BeginCompletionOp(&call_wrapper_, nullptr, nullptr);
+  }
+  *tag = tag_;
+  if (delete_on_finalize_) {
+    delete this;
+  }
+  return true;
+}
+
+void ServerInterface::BaseAsyncRequest::
+    ContinueFinalizeResultAfterInterception() {
+  context_->BeginCompletionOp(&call_wrapper_, nullptr, nullptr);
+  // Queue a tag which will be returned immediately
+  grpc_core::ExecCtx exec_ctx;
+  grpc_cq_begin_op(notification_cq_->cq(), this);
+  grpc_cq_end_op(
+      notification_cq_->cq(), this, GRPC_ERROR_NONE,
+      [](void* arg, grpc_cq_completion* completion) { delete completion; },
+      nullptr, new grpc_cq_completion());
+}
+
+ServerInterface::RegisteredAsyncRequest::RegisteredAsyncRequest(
+    ServerInterface* server, ServerContext* context,
+    internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
+    ServerCompletionQueue* notification_cq, void* tag, const char* name,
+    internal::RpcMethod::RpcType type)
+    : BaseAsyncRequest(server, context, stream, call_cq, notification_cq, tag,
+                       true),
+      name_(name),
+      type_(type) {}
+
+void ServerInterface::RegisteredAsyncRequest::IssueRequest(
+    void* registered_method, grpc_byte_buffer** payload,
+    ServerCompletionQueue* notification_cq) {
+  GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_registered_call(
+                                 server_->server(), registered_method, &call_,
+                                 &context_->deadline_,
+                                 context_->client_metadata_.arr(), payload,
+                                 call_cq_->cq(), notification_cq->cq(), this));
+}
+
+ServerInterface::GenericAsyncRequest::GenericAsyncRequest(
+    ServerInterface* server, GenericServerContext* context,
+    internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
+    ServerCompletionQueue* notification_cq, void* tag, bool delete_on_finalize)
+    : BaseAsyncRequest(server, context, stream, call_cq, notification_cq, tag,
+                       delete_on_finalize) {
+  grpc_call_details_init(&call_details_);
+  GPR_ASSERT(notification_cq);
+  GPR_ASSERT(call_cq);
+  GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(
+                                 server->server(), &call_, &call_details_,
+                                 context->client_metadata_.arr(), call_cq->cq(),
+                                 notification_cq->cq(), this));
+}
+
+bool ServerInterface::GenericAsyncRequest::FinalizeResult(void** tag,
+                                                          bool* status) {
+  // If we are done intercepting, there is nothing more for us to do
+  if (done_intercepting_) {
+    return BaseAsyncRequest::FinalizeResult(tag, status);
+  }
+  // TODO(yangg) remove the copy here.
+  if (*status) {
+    static_cast<GenericServerContext*>(context_)->method_ =
+        StringFromCopiedSlice(call_details_.method);
+    static_cast<GenericServerContext*>(context_)->host_ =
+        StringFromCopiedSlice(call_details_.host);
+    context_->deadline_ = call_details_.deadline;
+  }
+  grpc_slice_unref(call_details_.method);
+  grpc_slice_unref(call_details_.host);
+  call_wrapper_ = internal::Call(
+      call_, server_, call_cq_, server_->max_receive_message_size(),
+      context_->set_server_rpc_info(
+          static_cast<GenericServerContext*>(context_)->method_.c_str(),
+          internal::RpcMethod::BIDI_STREAMING,
+          *server_->interceptor_creators()));
+  return BaseAsyncRequest::FinalizeResult(tag, status);
+}
+
+namespace {
+class ShutdownCallback : public grpc_experimental_completion_queue_functor {
+ public:
+  ShutdownCallback() { functor_run = &ShutdownCallback::Run; }
+  // TakeCQ takes ownership of the cq into the shutdown callback
+  // so that the shutdown callback will be responsible for destroying it
+  void TakeCQ(CompletionQueue* cq) { cq_ = cq; }
+
+  // The Run function will get invoked by the completion queue library
+  // when the shutdown is actually complete
+  static void Run(grpc_experimental_completion_queue_functor* cb, int) {
+    auto* callback = static_cast<ShutdownCallback*>(cb);
+    delete callback->cq_;
+    delete callback;
+  }
+
+ private:
+  CompletionQueue* cq_ = nullptr;
+};
+}  // namespace
+
+}  // namespace grpc
+
+namespace grpc_impl {
+
 /// Use private inheritance rather than composition only to establish order
 /// of construction, since the public base class should be constructed after the
 /// elements belonging to the private base class are constructed. This is not
 /// possible using true composition.
 class Server::UnimplementedAsyncRequest final
-    : private UnimplementedAsyncRequestContext,
+    : private grpc::UnimplementedAsyncRequestContext,
       public GenericAsyncRequest {
  public:
-  UnimplementedAsyncRequest(Server* server, ServerCompletionQueue* cq)
+  UnimplementedAsyncRequest(Server* server, grpc::ServerCompletionQueue* cq)
       : GenericAsyncRequest(server, &server_context_, &generic_stream_, cq, cq,
                             nullptr, false),
         server_(server),
@@ -122,27 +290,29 @@ class Server::UnimplementedAsyncRequest final
 
   bool FinalizeResult(void** tag, bool* status) override;
 
-  ServerContext* context() { return &server_context_; }
-  GenericServerAsyncReaderWriter* stream() { return &generic_stream_; }
+  grpc::ServerContext* context() { return &server_context_; }
+  grpc::GenericServerAsyncReaderWriter* stream() { return &generic_stream_; }
 
  private:
   Server* const server_;
-  ServerCompletionQueue* const cq_;
+  grpc::ServerCompletionQueue* const cq_;
 };
 
 /// UnimplementedAsyncResponse should not post user-visible completions to the
 /// C++ completion queue, but is generated as a CQ event by the core
 class Server::UnimplementedAsyncResponse final
-    : public internal::CallOpSet<internal::CallOpSendInitialMetadata,
-                                 internal::CallOpServerSendStatus> {
+    : public grpc::internal::CallOpSet<
+          grpc::internal::CallOpSendInitialMetadata,
+          grpc::internal::CallOpServerSendStatus> {
  public:
   UnimplementedAsyncResponse(UnimplementedAsyncRequest* request);
   ~UnimplementedAsyncResponse() { delete request_; }
 
   bool FinalizeResult(void** tag, bool* status) override {
-    if (internal::CallOpSet<
-            internal::CallOpSendInitialMetadata,
-            internal::CallOpServerSendStatus>::FinalizeResult(tag, status)) {
+    if (grpc::internal::CallOpSet<
+            grpc::internal::CallOpSendInitialMetadata,
+            grpc::internal::CallOpServerSendStatus>::FinalizeResult(tag,
+                                                                    status)) {
       delete this;
     } else {
       // The tag was swallowed due to interception. We will see it again.
@@ -154,15 +324,16 @@ class Server::UnimplementedAsyncResponse final
   UnimplementedAsyncRequest* const request_;
 };
 
-class Server::SyncRequest final : public internal::CompletionQueueTag {
+class Server::SyncRequest final : public grpc::internal::CompletionQueueTag {
  public:
-  SyncRequest(internal::RpcServiceMethod* method, void* method_tag)
+  SyncRequest(grpc::internal::RpcServiceMethod* method, void* method_tag)
       : method_(method),
         method_tag_(method_tag),
         in_flight_(false),
-        has_request_payload_(
-            method->method_type() == internal::RpcMethod::NORMAL_RPC ||
-            method->method_type() == internal::RpcMethod::SERVER_STREAMING),
+        has_request_payload_(method->method_type() ==
+                                 grpc::internal::RpcMethod::NORMAL_RPC ||
+                             method->method_type() ==
+                                 grpc::internal::RpcMethod::SERVER_STREAMING),
         call_details_(nullptr),
         cq_(nullptr) {
     grpc_metadata_array_init(&request_metadata_);
@@ -273,7 +444,8 @@ class Server::SyncRequest final : public internal::CompletionQueueTag {
       interceptor_methods_.SetReverse();
       // Set interception point for RECV INITIAL METADATA
       interceptor_methods_.AddInterceptionHookPoint(
-          experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA);
+          grpc::experimental::InterceptionHookPoints::
+              POST_RECV_INITIAL_METADATA);
       interceptor_methods_.SetRecvInitialMetadata(&ctx_.client_metadata_);
 
       if (has_request_payload_) {
@@ -281,11 +453,11 @@ class Server::SyncRequest final : public internal::CompletionQueueTag {
         auto* handler = resources_ ? method_->handler()
                                    : server_->resource_exhausted_handler_.get();
         request_ = handler->Deserialize(call_.call(), request_payload_,
-                                        &request_status_);
+                                        &request_status_, nullptr);
 
         request_payload_ = nullptr;
         interceptor_methods_.AddInterceptionHookPoint(
-            experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
+            grpc::experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
         interceptor_methods_.SetRecvMessage(request_, nullptr);
       }
 
@@ -304,40 +476,40 @@ class Server::SyncRequest final : public internal::CompletionQueueTag {
         global_callbacks_->PreSynchronousRequest(&ctx_);
         auto* handler = resources_ ? method_->handler()
                                    : server_->resource_exhausted_handler_.get();
-        handler->RunHandler(internal::MethodHandler::HandlerParameter(
-            &call_, &ctx_, request_, request_status_, nullptr));
+        handler->RunHandler(grpc::internal::MethodHandler::HandlerParameter(
+            &call_, &ctx_, request_, request_status_, nullptr, nullptr));
         request_ = nullptr;
         global_callbacks_->PostSynchronousRequest(&ctx_);
 
         cq_.Shutdown();
 
-        internal::CompletionQueueTag* op_tag = ctx_.GetCompletionOpTag();
+        grpc::internal::CompletionQueueTag* op_tag = ctx_.GetCompletionOpTag();
         cq_.TryPluck(op_tag, gpr_inf_future(GPR_CLOCK_REALTIME));
 
         /* Ensure the cq_ is shutdown */
-        DummyTag ignored_tag;
+        grpc::DummyTag ignored_tag;
         GPR_ASSERT(cq_.Pluck(&ignored_tag) == false);
       }
       delete this;
     }
 
    private:
-    CompletionQueue cq_;
-    ServerContext ctx_;
+    grpc::CompletionQueue cq_;
+    grpc::ServerContext ctx_;
     const bool has_request_payload_;
     grpc_byte_buffer* request_payload_;
     void* request_;
-    Status request_status_;
-    internal::RpcServiceMethod* const method_;
-    internal::Call call_;
+    grpc::Status request_status_;
+    grpc::internal::RpcServiceMethod* const method_;
+    grpc::internal::Call call_;
     Server* server_;
     std::shared_ptr<GlobalCallbacks> global_callbacks_;
     bool resources_;
-    internal::InterceptorBatchMethodsImpl interceptor_methods_;
+    grpc::internal::InterceptorBatchMethodsImpl interceptor_methods_;
   };
 
  private:
-  internal::RpcServiceMethod* const method_;
+  grpc::internal::RpcServiceMethod* const method_;
   void* const method_tag_;
   bool in_flight_;
   const bool has_request_payload_;
@@ -349,7 +521,7 @@ class Server::SyncRequest final : public internal::CompletionQueueTag {
   grpc_completion_queue* cq_;
 };
 
-class Server::CallbackRequestBase : public internal::CompletionQueueTag {
+class Server::CallbackRequestBase : public grpc::internal::CompletionQueueTag {
  public:
   virtual ~CallbackRequestBase() {}
   virtual bool Request() = 0;
@@ -358,7 +530,7 @@ class Server::CallbackRequestBase : public internal::CompletionQueueTag {
 template <class ServerContextType>
 class Server::CallbackRequest final : public Server::CallbackRequestBase {
  public:
-  static_assert(std::is_base_of<ServerContext, ServerContextType>::value,
+  static_assert(std::is_base_of<grpc::ServerContext, ServerContextType>::value,
                 "ServerContextType must be derived from ServerContext");
 
   // The constructor needs to know the server for this callback request and its
@@ -368,15 +540,16 @@ class Server::CallbackRequest final : public Server::CallbackRequestBase {
   // requested. For generic services, method and method_tag are nullptr since
   // these services don't have pre-defined methods or method registration tags.
   CallbackRequest(Server* server, size_t method_idx,
-                  internal::RpcServiceMethod* method, void* method_tag)
+                  grpc::internal::RpcServiceMethod* method, void* method_tag)
       : server_(server),
         method_index_(method_idx),
         method_(method),
         method_tag_(method_tag),
         has_request_payload_(
             method_ != nullptr &&
-            (method->method_type() == internal::RpcMethod::NORMAL_RPC ||
-             method->method_type() == internal::RpcMethod::SERVER_STREAMING)),
+            (method->method_type() == grpc::internal::RpcMethod::NORMAL_RPC ||
+             method->method_type() ==
+                 grpc::internal::RpcMethod::SERVER_STREAMING)),
         cq_(server->CallbackCQ()),
         tag_(this) {
     server_->callback_reqs_outstanding_++;
@@ -388,9 +561,9 @@ class Server::CallbackRequest final : public Server::CallbackRequestBase {
 
     // The counter of outstanding requests must be decremented
     // under a lock in case it causes the server shutdown.
-    std::lock_guard<std::mutex> l(server_->callback_reqs_mu_);
+    grpc::internal::MutexLock l(&server_->callback_reqs_mu_);
     if (--server_->callback_reqs_outstanding_ == 0) {
-      server_->callback_reqs_done_cv_.notify_one();
+      server_->callback_reqs_done_cv_.Signal();
     }
   }
 
@@ -440,7 +613,7 @@ class Server::CallbackRequest final : public Server::CallbackRequestBase {
 
    private:
     Server::CallbackRequest<ServerContextType>* req_;
-    internal::Call* call_;
+    grpc::internal::Call* call_;
 
     static void StaticRun(grpc_experimental_completion_queue_functor* cb,
                           int ok) {
@@ -491,31 +664,35 @@ class Server::CallbackRequest final : public Server::CallbackRequestBase {
       req_->request_metadata_.count = 0;
 
       // Create a C++ Call to control the underlying core call
-      call_ = new (grpc_call_arena_alloc(req_->call_, sizeof(internal::Call)))
-          internal::Call(req_->call_, req_->server_, req_->cq_,
-                         req_->server_->max_receive_message_size(),
-                         req_->ctx_.set_server_rpc_info(
-                             req_->method_name(),
-                             (req_->method_ != nullptr)
-                                 ? req_->method_->method_type()
-                                 : internal::RpcMethod::BIDI_STREAMING,
-                             req_->server_->interceptor_creators_));
+      call_ =
+          new (grpc_call_arena_alloc(req_->call_, sizeof(grpc::internal::Call)))
+              grpc::internal::Call(
+                  req_->call_, req_->server_, req_->cq_,
+                  req_->server_->max_receive_message_size(),
+                  req_->ctx_.set_server_rpc_info(
+                      req_->method_name(),
+                      (req_->method_ != nullptr)
+                          ? req_->method_->method_type()
+                          : grpc::internal::RpcMethod::BIDI_STREAMING,
+                      req_->server_->interceptor_creators_));
 
       req_->interceptor_methods_.SetCall(call_);
       req_->interceptor_methods_.SetReverse();
       // Set interception point for RECV INITIAL METADATA
       req_->interceptor_methods_.AddInterceptionHookPoint(
-          experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA);
+          grpc::experimental::InterceptionHookPoints::
+              POST_RECV_INITIAL_METADATA);
       req_->interceptor_methods_.SetRecvInitialMetadata(
           &req_->ctx_.client_metadata_);
 
       if (req_->has_request_payload_) {
         // Set interception point for RECV MESSAGE
         req_->request_ = req_->method_->handler()->Deserialize(
-            req_->call_, req_->request_payload_, &req_->request_status_);
+            req_->call_, req_->request_payload_, &req_->request_status_,
+            &req_->handler_data_);
         req_->request_payload_ = nullptr;
         req_->interceptor_methods_.AddInterceptionHookPoint(
-            experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
+            grpc::experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
         req_->interceptor_methods_.SetRecvMessage(req_->request_, nullptr);
       }
 
@@ -531,8 +708,9 @@ class Server::CallbackRequest final : public Server::CallbackRequestBase {
       auto* handler = (req_->method_ != nullptr)
                           ? req_->method_->handler()
                           : req_->server_->generic_handler_.get();
-      handler->RunHandler(internal::MethodHandler::HandlerParameter(
-          call_, &req_->ctx_, req_->request_, req_->request_status_, [this] {
+      handler->RunHandler(grpc::internal::MethodHandler::HandlerParameter(
+          call_, &req_->ctx_, req_->request_, req_->request_status_,
+          req_->handler_data_, [this] {
             // Recycle this request if there aren't too many outstanding.
             // Note that we don't have to worry about a case where there
             // are no requests waiting to match for this method since that
@@ -577,40 +755,42 @@ class Server::CallbackRequest final : public Server::CallbackRequestBase {
     ctx_.Setup(gpr_inf_future(GPR_CLOCK_REALTIME));
     request_payload_ = nullptr;
     request_ = nullptr;
-    request_status_ = Status();
+    handler_data_ = nullptr;
+    request_status_ = grpc::Status();
   }
 
   Server* const server_;
   const size_t method_index_;
-  internal::RpcServiceMethod* const method_;
+  grpc::internal::RpcServiceMethod* const method_;
   void* const method_tag_;
   const bool has_request_payload_;
   grpc_byte_buffer* request_payload_;
   void* request_;
-  Status request_status_;
+  void* handler_data_;
+  grpc::Status request_status_;
   grpc_call_details* call_details_ = nullptr;
   grpc_call* call_;
   gpr_timespec deadline_;
   grpc_metadata_array request_metadata_;
-  CompletionQueue* cq_;
+  grpc::CompletionQueue* cq_;
   CallbackCallTag tag_;
   ServerContextType ctx_;
-  internal::InterceptorBatchMethodsImpl interceptor_methods_;
+  grpc::internal::InterceptorBatchMethodsImpl interceptor_methods_;
 };
 
 template <>
-bool Server::CallbackRequest<ServerContext>::FinalizeResult(void** tag,
-                                                            bool* status) {
+bool Server::CallbackRequest<grpc::ServerContext>::FinalizeResult(
+    void** tag, bool* status) {
   return false;
 }
 
 template <>
-bool Server::CallbackRequest<GenericServerContext>::FinalizeResult(
+bool Server::CallbackRequest<grpc::GenericServerContext>::FinalizeResult(
     void** tag, bool* status) {
   if (*status) {
     // TODO(yangg) remove the copy here
-    ctx_.method_ = StringFromCopiedSlice(call_details_->method);
-    ctx_.host_ = StringFromCopiedSlice(call_details_->host);
+    ctx_.method_ = grpc::StringFromCopiedSlice(call_details_->method);
+    ctx_.host_ = grpc::StringFromCopiedSlice(call_details_->host);
   }
   grpc_slice_unref(call_details_->method);
   grpc_slice_unref(call_details_->host);
@@ -618,21 +798,22 @@ bool Server::CallbackRequest<GenericServerContext>::FinalizeResult(
 }
 
 template <>
-const char* Server::CallbackRequest<ServerContext>::method_name() const {
+const char* Server::CallbackRequest<grpc::ServerContext>::method_name() const {
   return method_->name();
 }
 
 template <>
-const char* Server::CallbackRequest<GenericServerContext>::method_name() const {
+const char* Server::CallbackRequest<grpc::GenericServerContext>::method_name()
+    const {
   return ctx_.method().c_str();
 }
 
 // Implementation of ThreadManager. Each instance of SyncRequestThreadManager
 // manages a pool of threads that poll for incoming Sync RPCs and call the
 // appropriate RPC handlers
-class Server::SyncRequestThreadManager : public ThreadManager {
+class Server::SyncRequestThreadManager : public grpc::ThreadManager {
  public:
-  SyncRequestThreadManager(Server* server, CompletionQueue* server_cq,
+  SyncRequestThreadManager(Server* server, grpc::CompletionQueue* server_cq,
                            std::shared_ptr<GlobalCallbacks> global_callbacks,
                            grpc_resource_quota* rq, int min_pollers,
                            int max_pollers, int cq_timeout_msec)
@@ -651,11 +832,11 @@ class Server::SyncRequestThreadManager : public ThreadManager {
                      gpr_time_from_millis(cq_timeout_msec_, GPR_TIMESPAN));
 
     switch (server_cq_->AsyncNext(tag, ok, deadline)) {
-      case CompletionQueue::TIMEOUT:
+      case grpc::CompletionQueue::TIMEOUT:
         return TIMEOUT;
-      case CompletionQueue::SHUTDOWN:
+      case grpc::CompletionQueue::SHUTDOWN:
         return SHUTDOWN;
-      case CompletionQueue::GOT_EVENT:
+      case grpc::CompletionQueue::GOT_EVENT:
         return WORK_FOUND;
     }
 
@@ -690,15 +871,15 @@ class Server::SyncRequestThreadManager : public ThreadManager {
     // object
   }
 
-  void AddSyncMethod(internal::RpcServiceMethod* method, void* tag) {
+  void AddSyncMethod(grpc::internal::RpcServiceMethod* method, void* tag) {
     sync_requests_.emplace_back(new SyncRequest(method, tag));
   }
 
   void AddUnknownSyncMethod() {
     if (!sync_requests_.empty()) {
-      unknown_method_.reset(new internal::RpcServiceMethod(
-          "unknown", internal::RpcMethod::BIDI_STREAMING,
-          new internal::UnknownMethodHandler));
+      unknown_method_.reset(new grpc::internal::RpcServiceMethod(
+          "unknown", grpc::internal::RpcMethod::BIDI_STREAMING,
+          new grpc::internal::UnknownMethodHandler));
       sync_requests_.emplace_back(
           new SyncRequest(unknown_method_.get(), nullptr));
     }
@@ -742,22 +923,22 @@ class Server::SyncRequestThreadManager : public ThreadManager {
 
  private:
   Server* server_;
-  CompletionQueue* server_cq_;
+  grpc::CompletionQueue* server_cq_;
   int cq_timeout_msec_;
   std::vector<std::unique_ptr<SyncRequest>> sync_requests_;
-  std::unique_ptr<internal::RpcServiceMethod> unknown_method_;
+  std::unique_ptr<grpc::internal::RpcServiceMethod> unknown_method_;
   std::shared_ptr<Server::GlobalCallbacks> global_callbacks_;
 };
 
-static internal::GrpcLibraryInitializer g_gli_initializer;
+static grpc::internal::GrpcLibraryInitializer g_gli_initializer;
 Server::Server(
-    int max_receive_message_size, ChannelArguments* args,
-    std::shared_ptr<std::vector<std::unique_ptr<ServerCompletionQueue>>>
+    int max_receive_message_size, grpc::ChannelArguments* args,
+    std::shared_ptr<std::vector<std::unique_ptr<grpc::ServerCompletionQueue>>>
         sync_server_cqs,
     int min_pollers, int max_pollers, int sync_cq_timeout_msec,
     grpc_resource_quota* server_rq,
     std::vector<
-        std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+        std::unique_ptr<grpc::experimental::ServerInterceptorFactoryInterface>>
         interceptor_creators)
     : interceptor_creators_(std::move(interceptor_creators)),
       max_receive_message_size_(max_receive_message_size),
@@ -766,11 +947,11 @@ Server::Server(
       shutdown_(false),
       shutdown_notified_(false),
       server_(nullptr),
-      server_initializer_(new ServerInitializer(this)),
+      server_initializer_(new grpc_impl::ServerInitializer(this)),
       health_check_service_disabled_(false) {
   g_gli_initializer.summon();
-  gpr_once_init(&g_once_init_callbacks, InitGlobalCallbacks);
-  global_callbacks_ = g_callbacks;
+  gpr_once_init(&grpc::g_once_init_callbacks, grpc::InitGlobalCallbacks);
+  global_callbacks_ = grpc::g_callbacks;
   global_callbacks_->UpdateArguments(args);
 
   if (sync_server_cqs_ != nullptr) {
@@ -797,13 +978,14 @@ Server::Server(
   args->SetChannelArgs(&channel_args);
 
   for (size_t i = 0; i < channel_args.num_args; i++) {
-    if (0 ==
-        strcmp(channel_args.args[i].key, kHealthCheckServiceInterfaceArg)) {
+    if (0 == strcmp(channel_args.args[i].key,
+                    grpc::kHealthCheckServiceInterfaceArg)) {
       if (channel_args.args[i].value.pointer.p == nullptr) {
         health_check_service_disabled_ = true;
       } else {
-        health_check_service_.reset(static_cast<HealthCheckServiceInterface*>(
-            channel_args.args[i].value.pointer.p));
+        health_check_service_.reset(
+            static_cast<grpc::HealthCheckServiceInterface*>(
+                channel_args.args[i].value.pointer.p));
       }
       break;
     }
@@ -814,12 +996,12 @@ Server::Server(
 
 Server::~Server() {
   {
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc::internal::ReleasableMutexLock lock(&mu_);
     if (callback_cq_ != nullptr) {
       callback_cq_->Shutdown();
     }
     if (started_ && !shutdown_) {
-      lock.unlock();
+      lock.Unlock();
       Shutdown();
     } else if (!started_) {
       // Shutdown the completion queues
@@ -840,49 +1022,49 @@ Server::~Server() {
 }
 
 void Server::SetGlobalCallbacks(GlobalCallbacks* callbacks) {
-  GPR_ASSERT(!g_callbacks);
+  GPR_ASSERT(!grpc::g_callbacks);
   GPR_ASSERT(callbacks);
-  g_callbacks.reset(callbacks);
+  grpc::g_callbacks.reset(callbacks);
 }
 
 grpc_server* Server::c_server() { return server_; }
 
-std::shared_ptr<Channel> Server::InProcessChannel(
-    const ChannelArguments& args) {
+std::shared_ptr<grpc::Channel> Server::InProcessChannel(
+    const grpc::ChannelArguments& args) {
   grpc_channel_args channel_args = args.c_channel_args();
-  return CreateChannelInternal(
+  return grpc::CreateChannelInternal(
       "inproc", grpc_inproc_channel_create(server_, &channel_args, nullptr),
-      std::vector<
-          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
+      std::vector<std::unique_ptr<
+          grpc::experimental::ClientInterceptorFactoryInterface>>());
 }
 
-std::shared_ptr<Channel>
+std::shared_ptr<grpc::Channel>
 Server::experimental_type::InProcessChannelWithInterceptors(
-    const ChannelArguments& args,
+    const grpc::ChannelArguments& args,
     std::vector<
-        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+        std::unique_ptr<grpc::experimental::ClientInterceptorFactoryInterface>>
         interceptor_creators) {
   grpc_channel_args channel_args = args.c_channel_args();
-  return CreateChannelInternal(
+  return grpc::CreateChannelInternal(
       "inproc",
       grpc_inproc_channel_create(server_->server_, &channel_args, nullptr),
       std::move(interceptor_creators));
 }
 
 static grpc_server_register_method_payload_handling PayloadHandlingForMethod(
-    internal::RpcServiceMethod* method) {
+    grpc::internal::RpcServiceMethod* method) {
   switch (method->method_type()) {
-    case internal::RpcMethod::NORMAL_RPC:
-    case internal::RpcMethod::SERVER_STREAMING:
+    case grpc::internal::RpcMethod::NORMAL_RPC:
+    case grpc::internal::RpcMethod::SERVER_STREAMING:
       return GRPC_SRM_PAYLOAD_READ_INITIAL_BYTE_BUFFER;
-    case internal::RpcMethod::CLIENT_STREAMING:
-    case internal::RpcMethod::BIDI_STREAMING:
+    case grpc::internal::RpcMethod::CLIENT_STREAMING:
+    case grpc::internal::RpcMethod::BIDI_STREAMING:
       return GRPC_SRM_PAYLOAD_NONE;
   }
   GPR_UNREACHABLE_CODE(return GRPC_SRM_PAYLOAD_NONE;);
 }
 
-bool Server::RegisterService(const grpc::string* host, Service* service) {
+bool Server::RegisterService(const grpc::string* host, grpc::Service* service) {
   bool has_async_methods = service->has_async_methods();
   if (has_async_methods) {
     GPR_ASSERT(service->server_ == nullptr &&
@@ -898,7 +1080,7 @@ bool Server::RegisterService(const grpc::string* host, Service* service) {
       continue;
     }
 
-    internal::RpcServiceMethod* method = it->get();
+    grpc::internal::RpcServiceMethod* method = it->get();
     void* method_registration_tag = grpc_server_register_method(
         server_, method->name(), host ? host->c_str() : nullptr,
         PayloadHandlingForMethod(method), 0);
@@ -911,7 +1093,7 @@ bool Server::RegisterService(const grpc::string* host, Service* service) {
     if (method->handler() == nullptr) {  // Async method without handler
       method->set_server_tag(method_registration_tag);
     } else if (method->api_type() ==
-               internal::RpcServiceMethod::ApiType::SYNC) {
+               grpc::internal::RpcServiceMethod::ApiType::SYNC) {
       for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) {
         (*it)->AddSyncMethod(method, method_registration_tag);
       }
@@ -921,8 +1103,9 @@ bool Server::RegisterService(const grpc::string* host, Service* service) {
       auto method_index = callback_unmatched_reqs_count_.size() - 1;
       // TODO(vjpai): Register these dynamically based on need
       for (int i = 0; i < DEFAULT_CALLBACK_REQS_PER_METHOD; i++) {
-        callback_reqs_to_start_.push_back(new CallbackRequest<ServerContext>(
-            this, method_index, method, method_registration_tag));
+        callback_reqs_to_start_.push_back(
+            new CallbackRequest<grpc::ServerContext>(this, method_index, method,
+                                                     method_registration_tag));
       }
       // Enqueue it so that it will be Request'ed later after all request
       // matchers are created at core server startup
@@ -943,7 +1126,7 @@ bool Server::RegisterService(const grpc::string* host, Service* service) {
   return true;
 }
 
-void Server::RegisterAsyncGenericService(AsyncGenericService* service) {
+void Server::RegisterAsyncGenericService(grpc::AsyncGenericService* service) {
   GPR_ASSERT(service->server_ == nullptr &&
              "Can only register an async generic service against one server.");
   service->server_ = this;
@@ -951,7 +1134,7 @@ void Server::RegisterAsyncGenericService(AsyncGenericService* service) {
 }
 
 void Server::RegisterCallbackGenericService(
-    experimental::CallbackGenericService* service) {
+    grpc::experimental::CallbackGenericService* service) {
   GPR_ASSERT(
       service->server_ == nullptr &&
       "Can only register a callback generic service against one server.");
@@ -963,44 +1146,45 @@ void Server::RegisterCallbackGenericService(
   auto method_index = callback_unmatched_reqs_count_.size() - 1;
   // TODO(vjpai): Register these dynamically based on need
   for (int i = 0; i < DEFAULT_CALLBACK_REQS_PER_METHOD; i++) {
-    callback_reqs_to_start_.push_back(new CallbackRequest<GenericServerContext>(
-        this, method_index, nullptr, nullptr));
+    callback_reqs_to_start_.push_back(
+        new CallbackRequest<grpc::GenericServerContext>(this, method_index,
+                                                        nullptr, nullptr));
   }
 }
 
 int Server::AddListeningPort(const grpc::string& addr,
-                             ServerCredentials* creds) {
+                             grpc::ServerCredentials* creds) {
   GPR_ASSERT(!started_);
   int port = creds->AddPortToServer(addr, server_);
   global_callbacks_->AddPort(this, addr, creds, port);
   return port;
 }
 
-void Server::Start(ServerCompletionQueue** cqs, size_t num_cqs) {
+void Server::Start(grpc::ServerCompletionQueue** cqs, size_t num_cqs) {
   GPR_ASSERT(!started_);
   global_callbacks_->PreServerStart(this);
   started_ = true;
 
   // Only create default health check service when user did not provide an
   // explicit one.
-  ServerCompletionQueue* health_check_cq = nullptr;
-  DefaultHealthCheckService::HealthCheckServiceImpl*
+  grpc::ServerCompletionQueue* health_check_cq = nullptr;
+  grpc::DefaultHealthCheckService::HealthCheckServiceImpl*
       default_health_check_service_impl = nullptr;
   if (health_check_service_ == nullptr && !health_check_service_disabled_ &&
-      DefaultHealthCheckServiceEnabled()) {
-    auto* default_hc_service = new DefaultHealthCheckService;
+      grpc::DefaultHealthCheckServiceEnabled()) {
+    auto* default_hc_service = new grpc::DefaultHealthCheckService;
     health_check_service_.reset(default_hc_service);
     // We create a non-polling CQ to avoid impacting application
     // performance.  This ensures that we don't introduce thread hops
     // for application requests that wind up on this CQ, which is polled
     // in its own thread.
-    health_check_cq =
-        new ServerCompletionQueue(GRPC_CQ_NEXT, GRPC_CQ_NON_POLLING, nullptr);
+    health_check_cq = new grpc::ServerCompletionQueue(
+        GRPC_CQ_NEXT, GRPC_CQ_NON_POLLING, nullptr);
     grpc_server_register_completion_queue(server_, health_check_cq->cq(),
                                           nullptr);
     default_health_check_service_impl =
         default_hc_service->GetHealthCheckService(
-            std::unique_ptr<ServerCompletionQueue>(health_check_cq));
+            std::unique_ptr<grpc::ServerCompletionQueue>(health_check_cq));
     RegisterService(nullptr, default_health_check_service_impl);
   }
 
@@ -1008,7 +1192,8 @@ void Server::Start(ServerCompletionQueue** cqs, size_t num_cqs) {
   // service to handle any unimplemented methods using the default reactor
   // creator
   if (!callback_reqs_to_start_.empty() && !has_callback_generic_service_) {
-    unimplemented_service_.reset(new experimental::CallbackGenericService);
+    unimplemented_service_.reset(
+        new grpc::experimental::CallbackGenericService);
     RegisterCallbackGenericService(unimplemented_service_.get());
   }
 
@@ -1033,7 +1218,8 @@ void Server::Start(ServerCompletionQueue** cqs, size_t num_cqs) {
   // server CQs), make sure that we have a ResourceExhausted handler
   // to deal with the case of thread exhaustion
   if (sync_server_cqs_ != nullptr && !sync_server_cqs_->empty()) {
-    resource_exhausted_handler_.reset(new internal::ResourceExhaustedHandler);
+    resource_exhausted_handler_.reset(
+        new grpc::internal::ResourceExhaustedHandler);
   }
 
   for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) {
@@ -1051,7 +1237,7 @@ void Server::Start(ServerCompletionQueue** cqs, size_t num_cqs) {
 }
 
 void Server::ShutdownInternal(gpr_timespec deadline) {
-  std::unique_lock<std::mutex> lock(mu_);
+  grpc::internal::MutexLock lock(&mu_);
   if (shutdown_) {
     return;
   }
@@ -1059,20 +1245,20 @@ void Server::ShutdownInternal(gpr_timespec deadline) {
   shutdown_ = true;
 
   /// The completion queue to use for server shutdown completion notification
-  CompletionQueue shutdown_cq;
-  ShutdownTag shutdown_tag;  // Dummy shutdown tag
+  grpc::CompletionQueue shutdown_cq;
+  grpc::ShutdownTag shutdown_tag;  // Dummy shutdown tag
   grpc_server_shutdown_and_notify(server_, shutdown_cq.cq(), &shutdown_tag);
 
   shutdown_cq.Shutdown();
 
   void* tag;
   bool ok;
-  CompletionQueue::NextStatus status =
+  grpc::CompletionQueue::NextStatus status =
       shutdown_cq.AsyncNext(&tag, &ok, deadline);
 
   // If this timed out, it means we are done with the grace period for a clean
   // shutdown. We should force a shutdown now by cancelling all inflight calls
-  if (status == CompletionQueue::NextStatus::TIMEOUT) {
+  if (status == grpc::CompletionQueue::NextStatus::TIMEOUT) {
     grpc_server_cancel_all_calls(server_);
   }
   // Else in case of SHUTDOWN or GOT_EVENT, it means that the server has
@@ -1102,9 +1288,9 @@ void Server::ShutdownInternal(gpr_timespec deadline) {
   // will report a failure, indicating a shutdown and again we won't end
   // up incrementing the counter.
   {
-    std::unique_lock<std::mutex> cblock(callback_reqs_mu_);
-    callback_reqs_done_cv_.wait(
-        cblock, [this] { return callback_reqs_outstanding_ == 0; });
+    grpc::internal::MutexLock cblock(&callback_reqs_mu_);
+    callback_reqs_done_cv_.WaitUntil(
+        &callback_reqs_mu_, [this] { return callback_reqs_outstanding_ == 0; });
   }
 
   // Drain the shutdown queue (if the previous call to AsyncNext() timed out
@@ -1114,164 +1300,21 @@ void Server::ShutdownInternal(gpr_timespec deadline) {
   }
 
   shutdown_notified_ = true;
-  shutdown_cv_.notify_all();
+  shutdown_cv_.Broadcast();
 }
 
 void Server::Wait() {
-  std::unique_lock<std::mutex> lock(mu_);
+  grpc::internal::MutexLock lock(&mu_);
   while (started_ && !shutdown_notified_) {
-    shutdown_cv_.wait(lock);
+    shutdown_cv_.Wait(&mu_);
   }
 }
 
-void Server::PerformOpsOnCall(internal::CallOpSetInterface* ops,
-                              internal::Call* call) {
+void Server::PerformOpsOnCall(grpc::internal::CallOpSetInterface* ops,
+                              grpc::internal::Call* call) {
   ops->FillOps(call);
 }
 
-ServerInterface::BaseAsyncRequest::BaseAsyncRequest(
-    ServerInterface* server, ServerContext* context,
-    internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
-    ServerCompletionQueue* notification_cq, void* tag, bool delete_on_finalize)
-    : server_(server),
-      context_(context),
-      stream_(stream),
-      call_cq_(call_cq),
-      notification_cq_(notification_cq),
-      tag_(tag),
-      delete_on_finalize_(delete_on_finalize),
-      call_(nullptr),
-      done_intercepting_(false) {
-  /* Set up interception state partially for the receive ops. call_wrapper_ is
-   * not filled at this point, but it will be filled before the interceptors are
-   * run. */
-  interceptor_methods_.SetCall(&call_wrapper_);
-  interceptor_methods_.SetReverse();
-  call_cq_->RegisterAvalanching();  // This op will trigger more ops
-}
-
-ServerInterface::BaseAsyncRequest::~BaseAsyncRequest() {
-  call_cq_->CompleteAvalanching();
-}
-
-bool ServerInterface::BaseAsyncRequest::FinalizeResult(void** tag,
-                                                       bool* status) {
-  if (done_intercepting_) {
-    *tag = tag_;
-    if (delete_on_finalize_) {
-      delete this;
-    }
-    return true;
-  }
-  context_->set_call(call_);
-  context_->cq_ = call_cq_;
-  if (call_wrapper_.call() == nullptr) {
-    // Fill it since it is empty.
-    call_wrapper_ = internal::Call(
-        call_, server_, call_cq_, server_->max_receive_message_size(), nullptr);
-  }
-
-  // just the pointers inside call are copied here
-  stream_->BindCall(&call_wrapper_);
-
-  if (*status && call_ && call_wrapper_.server_rpc_info()) {
-    done_intercepting_ = true;
-    // Set interception point for RECV INITIAL METADATA
-    interceptor_methods_.AddInterceptionHookPoint(
-        experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA);
-    interceptor_methods_.SetRecvInitialMetadata(&context_->client_metadata_);
-    if (interceptor_methods_.RunInterceptors(
-            [this]() { ContinueFinalizeResultAfterInterception(); })) {
-      // There are no interceptors to run. Continue
-    } else {
-      // There were interceptors to be run, so
-      // ContinueFinalizeResultAfterInterception will be run when interceptors
-      // are done.
-      return false;
-    }
-  }
-  if (*status && call_) {
-    context_->BeginCompletionOp(&call_wrapper_, nullptr, nullptr);
-  }
-  *tag = tag_;
-  if (delete_on_finalize_) {
-    delete this;
-  }
-  return true;
-}
-
-void ServerInterface::BaseAsyncRequest::
-    ContinueFinalizeResultAfterInterception() {
-  context_->BeginCompletionOp(&call_wrapper_, nullptr, nullptr);
-  // Queue a tag which will be returned immediately
-  grpc_core::ExecCtx exec_ctx;
-  grpc_cq_begin_op(notification_cq_->cq(), this);
-  grpc_cq_end_op(
-      notification_cq_->cq(), this, GRPC_ERROR_NONE,
-      [](void* arg, grpc_cq_completion* completion) { delete completion; },
-      nullptr, new grpc_cq_completion());
-}
-
-ServerInterface::RegisteredAsyncRequest::RegisteredAsyncRequest(
-    ServerInterface* server, ServerContext* context,
-    internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
-    ServerCompletionQueue* notification_cq, void* tag, const char* name,
-    internal::RpcMethod::RpcType type)
-    : BaseAsyncRequest(server, context, stream, call_cq, notification_cq, tag,
-                       true),
-      name_(name),
-      type_(type) {}
-
-void ServerInterface::RegisteredAsyncRequest::IssueRequest(
-    void* registered_method, grpc_byte_buffer** payload,
-    ServerCompletionQueue* notification_cq) {
-  GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_registered_call(
-                                 server_->server(), registered_method, &call_,
-                                 &context_->deadline_,
-                                 context_->client_metadata_.arr(), payload,
-                                 call_cq_->cq(), notification_cq->cq(), this));
-}
-
-ServerInterface::GenericAsyncRequest::GenericAsyncRequest(
-    ServerInterface* server, GenericServerContext* context,
-    internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
-    ServerCompletionQueue* notification_cq, void* tag, bool delete_on_finalize)
-    : BaseAsyncRequest(server, context, stream, call_cq, notification_cq, tag,
-                       delete_on_finalize) {
-  grpc_call_details_init(&call_details_);
-  GPR_ASSERT(notification_cq);
-  GPR_ASSERT(call_cq);
-  GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(
-                                 server->server(), &call_, &call_details_,
-                                 context->client_metadata_.arr(), call_cq->cq(),
-                                 notification_cq->cq(), this));
-}
-
-bool ServerInterface::GenericAsyncRequest::FinalizeResult(void** tag,
-                                                          bool* status) {
-  // If we are done intercepting, there is nothing more for us to do
-  if (done_intercepting_) {
-    return BaseAsyncRequest::FinalizeResult(tag, status);
-  }
-  // TODO(yangg) remove the copy here.
-  if (*status) {
-    static_cast<GenericServerContext*>(context_)->method_ =
-        StringFromCopiedSlice(call_details_.method);
-    static_cast<GenericServerContext*>(context_)->host_ =
-        StringFromCopiedSlice(call_details_.host);
-    context_->deadline_ = call_details_.deadline;
-  }
-  grpc_slice_unref(call_details_.method);
-  grpc_slice_unref(call_details_.host);
-  call_wrapper_ = internal::Call(
-      call_, server_, call_cq_, server_->max_receive_message_size(),
-      context_->set_server_rpc_info(
-          static_cast<GenericServerContext*>(context_)->method_.c_str(),
-          internal::RpcMethod::BIDI_STREAMING,
-          *server_->interceptor_creators()));
-  return BaseAsyncRequest::FinalizeResult(tag, status);
-}
-
 bool Server::UnimplementedAsyncRequest::FinalizeResult(void** tag,
                                                        bool* status) {
   if (GenericAsyncRequest::FinalizeResult(tag, status)) {
@@ -1291,41 +1334,22 @@ bool Server::UnimplementedAsyncRequest::FinalizeResult(void** tag,
 Server::UnimplementedAsyncResponse::UnimplementedAsyncResponse(
     UnimplementedAsyncRequest* request)
     : request_(request) {
-  Status status(StatusCode::UNIMPLEMENTED, "");
-  internal::UnknownMethodHandler::FillOps(request_->context(), this);
+  grpc::Status status(grpc::StatusCode::UNIMPLEMENTED, "");
+  grpc::internal::UnknownMethodHandler::FillOps(request_->context(), this);
   request_->stream()->call_.PerformOps(this);
 }
 
-ServerInitializer* Server::initializer() { return server_initializer_.get(); }
-
-namespace {
-class ShutdownCallback : public grpc_experimental_completion_queue_functor {
- public:
-  ShutdownCallback() { functor_run = &ShutdownCallback::Run; }
-  // TakeCQ takes ownership of the cq into the shutdown callback
-  // so that the shutdown callback will be responsible for destroying it
-  void TakeCQ(CompletionQueue* cq) { cq_ = cq; }
-
-  // The Run function will get invoked by the completion queue library
-  // when the shutdown is actually complete
-  static void Run(grpc_experimental_completion_queue_functor* cb, int) {
-    auto* callback = static_cast<ShutdownCallback*>(cb);
-    delete callback->cq_;
-    delete callback;
-  }
-
- private:
-  CompletionQueue* cq_ = nullptr;
-};
-}  // namespace
+grpc::ServerInitializer* Server::initializer() {
+  return server_initializer_.get();
+}
 
-CompletionQueue* Server::CallbackCQ() {
+grpc::CompletionQueue* Server::CallbackCQ() {
   // TODO(vjpai): Consider using a single global CQ for the default CQ
   // if there is no explicit per-server CQ registered
-  std::lock_guard<std::mutex> l(mu_);
+  grpc::internal::MutexLock l(&mu_);
   if (callback_cq_ == nullptr) {
-    auto* shutdown_callback = new ShutdownCallback;
-    callback_cq_ = new CompletionQueue(grpc_completion_queue_attributes{
+    auto* shutdown_callback = new grpc::ShutdownCallback;
+    callback_cq_ = new grpc::CompletionQueue(grpc_completion_queue_attributes{
         GRPC_CQ_CURRENT_VERSION, GRPC_CQ_CALLBACK, GRPC_CQ_DEFAULT_POLLING,
         shutdown_callback});
 
@@ -1335,4 +1359,4 @@ CompletionQueue* Server::CallbackCQ() {
   return callback_cq_;
 }
 
-}  // namespace grpc
+}  // namespace grpc_impl
index 73fd6a6..eced89d 100644 (file)
@@ -33,6 +33,7 @@
 #include <grpcpp/support/time.h>
 
 #include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/surface/call.h"
 
 namespace grpc {
@@ -96,7 +97,7 @@ class ServerContext::CompletionOp final : public internal::CallOpSetInterface {
   }
 
   void SetCancelCallback(std::function<void()> callback) {
-    std::lock_guard<std::mutex> lock(mu_);
+    grpc_core::MutexLock lock(&mu_);
 
     if (finalized_ && (cancelled_ != 0)) {
       callback();
@@ -107,7 +108,7 @@ class ServerContext::CompletionOp final : public internal::CallOpSetInterface {
   }
 
   void ClearCancelCallback() {
-    std::lock_guard<std::mutex> g(mu_);
+    grpc_core::MutexLock g(&mu_);
     cancel_callback_ = nullptr;
   }
 
@@ -144,7 +145,7 @@ class ServerContext::CompletionOp final : public internal::CallOpSetInterface {
 
  private:
   bool CheckCancelledNoPluck() {
-    std::lock_guard<std::mutex> g(mu_);
+    grpc_core::MutexLock lock(&mu_);
     return finalized_ ? (cancelled_ != 0) : false;
   }
 
@@ -154,7 +155,7 @@ class ServerContext::CompletionOp final : public internal::CallOpSetInterface {
   void* tag_;
   void* core_cq_tag_;
   grpc_core::RefCount refs_;
-  std::mutex mu_;
+  grpc_core::Mutex mu_;
   bool finalized_;
   int cancelled_;  // This is an int (not bool) because it is passed to core
   std::function<void()> cancel_callback_;
@@ -186,7 +187,7 @@ void ServerContext::CompletionOp::FillOps(internal::Call* call) {
 
 bool ServerContext::CompletionOp::FinalizeResult(void** tag, bool* status) {
   bool ret = false;
-  std::unique_lock<std::mutex> lock(mu_);
+  grpc_core::ReleasableMutexLock lock(&mu_);
   if (done_intercepting_) {
     /* We are done intercepting. */
     if (has_tag_) {
@@ -209,19 +210,21 @@ bool ServerContext::CompletionOp::FinalizeResult(void** tag, bool* status) {
   bool call_cancel = (cancelled_ != 0);
 
   // If it's a unary cancel callback, call it under the lock so that it doesn't
-  // race with ClearCancelCallback
+  // race with ClearCancelCallback. Although we don't normally call callbacks
+  // under a lock, this is a special case since the user needs a guarantee that
+  // the callback won't issue or run after ClearCancelCallback has returned.
+  // This requirement imposes certain restrictions on the callback, documented
+  // in the API comments of SetCancelCallback.
   if (cancel_callback_) {
     cancel_callback_();
   }
 
-  // Release the lock since we are going to be calling a callback and
-  // interceptors now
-  lock.unlock();
+  // Release the lock since we may call a callback and interceptors now.
+  lock.Unlock();
 
   if (call_cancel && reactor_ != nullptr) {
-    reactor_->OnCancel();
+    reactor_->MaybeCallOnCancel();
   }
-
   /* Add interception point and run through interceptors */
   interceptor_methods_.AddInterceptionHookPoint(
       experimental::InterceptionHookPoints::POST_RECV_CLOSE);
index c3b3a8b..8b85264 100644 (file)
  *
  */
 
-#include <grpcpp/security/server_credentials.h>
+#include <grpcpp/security/server_credentials_impl.h>
 
-namespace grpc {
+namespace grpc_impl {
 
 ServerCredentials::~ServerCredentials() {}
 
-}  // namespace grpc
+}  // namespace grpc_impl
index 3e8606a..2b65352 100644 (file)
@@ -62,7 +62,7 @@ ThreadManager::ThreadManager(const char* name,
 
 ThreadManager::~ThreadManager() {
   {
-    std::lock_guard<std::mutex> lock(mu_);
+    grpc_core::MutexLock lock(&mu_);
     GPR_ASSERT(num_threads_ == 0);
   }
 
@@ -72,38 +72,38 @@ ThreadManager::~ThreadManager() {
 }
 
 void ThreadManager::Wait() {
-  std::unique_lock<std::mutex> lock(mu_);
+  grpc_core::MutexLock lock(&mu_);
   while (num_threads_ != 0) {
-    shutdown_cv_.wait(lock);
+    shutdown_cv_.Wait(&mu_);
   }
 }
 
 void ThreadManager::Shutdown() {
-  std::lock_guard<std::mutex> lock(mu_);
+  grpc_core::MutexLock lock(&mu_);
   shutdown_ = true;
 }
 
 bool ThreadManager::IsShutdown() {
-  std::lock_guard<std::mutex> lock(mu_);
+  grpc_core::MutexLock lock(&mu_);
   return shutdown_;
 }
 
 int ThreadManager::GetMaxActiveThreadsSoFar() {
-  std::lock_guard<std::mutex> list_lock(list_mu_);
+  grpc_core::MutexLock list_lock(&list_mu_);
   return max_active_threads_sofar_;
 }
 
 void ThreadManager::MarkAsCompleted(WorkerThread* thd) {
   {
-    std::lock_guard<std::mutex> list_lock(list_mu_);
+    grpc_core::MutexLock list_lock(&list_mu_);
     completed_threads_.push_back(thd);
   }
 
   {
-    std::lock_guard<std::mutex> lock(mu_);
+    grpc_core::MutexLock lock(&mu_);
     num_threads_--;
     if (num_threads_ == 0) {
-      shutdown_cv_.notify_one();
+      shutdown_cv_.Signal();
     }
   }
 
@@ -116,7 +116,7 @@ void ThreadManager::CleanupCompletedThreads() {
   {
     // swap out the completed threads list: allows other threads to clean up
     // more quickly
-    std::unique_lock<std::mutex> lock(list_mu_);
+    grpc_core::MutexLock lock(&list_mu_);
     completed_threads.swap(completed_threads_);
   }
   for (auto thd : completed_threads) delete thd;
@@ -132,7 +132,7 @@ void ThreadManager::Initialize() {
   }
 
   {
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc_core::MutexLock lock(&mu_);
     num_pollers_ = min_pollers_;
     num_threads_ = min_pollers_;
     max_active_threads_sofar_ = min_pollers_;
@@ -149,7 +149,7 @@ void ThreadManager::MainWorkLoop() {
     bool ok;
     WorkStatus work_status = PollForWork(&tag, &ok);
 
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc_core::ReleasableMutexLock lock(&mu_);
     // Reduce the number of pollers by 1 and check what happened with the poll
     num_pollers_--;
     bool done = false;
@@ -176,30 +176,30 @@ void ThreadManager::MainWorkLoop() {
               max_active_threads_sofar_ = num_threads_;
             }
             // Drop lock before spawning thread to avoid contention
-            lock.unlock();
+            lock.Unlock();
             new WorkerThread(this);
           } else if (num_pollers_ > 0) {
             // There is still at least some thread polling, so we can go on
             // even though we are below the number of pollers that we would
             // like to have (min_pollers_)
-            lock.unlock();
+            lock.Unlock();
           } else {
             // There are no pollers to spare and we couldn't allocate
             // a new thread, so resources are exhausted!
-            lock.unlock();
+            lock.Unlock();
             resource_exhausted = true;
           }
         } else {
           // There are a sufficient number of pollers available so we can do
           // the work and continue polling with our existing poller threads
-          lock.unlock();
+          lock.Unlock();
         }
         // Lock is always released at this point - do the application work
         // or return resource exhausted if there is new work but we couldn't
         // get a thread in which to do it.
         DoWork(tag, ok, !resource_exhausted);
         // Take the lock again to check post conditions
-        lock.lock();
+        lock.Lock();
         // If we're shutdown, we should finish at this point.
         if (shutdown_) done = true;
         break;
index 6f0bd17..2fbf309 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <grpcpp/support/config.h>
 
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/resource_quota.h"
 
@@ -140,10 +141,10 @@ class ThreadManager {
 
   // Protects shutdown_, num_pollers_, num_threads_ and
   // max_active_threads_sofar_
-  std::mutex mu_;
+  grpc_core::Mutex mu_;
 
   bool shutdown_;
-  std::condition_variable shutdown_cv_;
+  grpc_core::CondVar shutdown_cv_;
 
   // The resource user object to use when requesting quota to create threads
   //
@@ -169,7 +170,7 @@ class ThreadManager {
   // ever set so far
   int max_active_threads_sofar_;
 
-  std::mutex list_mu_;
+  grpc_core::Mutex list_mu_;
   std::list<WorkerThread*> completed_threads_;
 };
 
index 42c887a..a1aafcb 100644 (file)
 
 #include "src/proto/grpc/status/status.pb.h"
 
-namespace grpc {
+namespace grpc_impl {
 
-Status ExtractErrorDetails(const Status& from, ::google::rpc::Status* to) {
+grpc::Status ExtractErrorDetails(const grpc::Status& from,
+                                 ::google::rpc::Status* to) {
   if (to == nullptr) {
-    return Status(StatusCode::FAILED_PRECONDITION, "");
+    return grpc::Status(grpc::StatusCode::FAILED_PRECONDITION, "");
   }
   if (!to->ParseFromString(from.error_details())) {
-    return Status(StatusCode::INVALID_ARGUMENT, "");
+    return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, "");
   }
-  return Status::OK;
+  return grpc::Status::OK;
 }
 
-Status SetErrorDetails(const ::google::rpc::Status& from, Status* to) {
+grpc::Status SetErrorDetails(const ::google::rpc::Status& from,
+                             grpc::Status* to) {
   if (to == nullptr) {
-    return Status(StatusCode::FAILED_PRECONDITION, "");
+    return grpc::Status(grpc::StatusCode::FAILED_PRECONDITION, "");
   }
-  StatusCode code = StatusCode::UNKNOWN;
-  if (from.code() >= StatusCode::OK &&
-      from.code() <= StatusCode::UNAUTHENTICATED) {
-    code = static_cast<StatusCode>(from.code());
+  grpc::StatusCode code = grpc::StatusCode::UNKNOWN;
+  if (from.code() >= grpc::StatusCode::OK &&
+      from.code() <= grpc::StatusCode::UNAUTHENTICATED) {
+    code = static_cast<grpc::StatusCode>(from.code());
   }
-  *to = Status(code, from.message(), from.SerializeAsString());
-  return Status::OK;
+  *to = grpc::Status(code, from.message(), from.SerializeAsString());
+  return grpc::Status::OK;
 }
 
-}  // namespace grpc
+}  // namespace grpc_impl
diff --git a/src/csharp/Grpc.Core.Api/BindServiceMethodAttribute.cs b/src/csharp/Grpc.Core.Api/BindServiceMethodAttribute.cs
new file mode 100644 (file)
index 0000000..2ee0abe
--- /dev/null
@@ -0,0 +1,53 @@
+#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;
+
+namespace Grpc.Core
+{
+    /// <summary>
+    /// Specifies the location of the service bind method for a gRPC service.
+    /// The bind method is typically generated code and is used to register a service's
+    /// methods with the server on startup.
+    /// 
+    /// The bind method signature takes a <see cref="ServiceBinderBase"/> and an optional
+    /// instance of the service base class, e.g. <c>static void BindService(ServiceBinderBase, GreeterService)</c>.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
+    public class BindServiceMethodAttribute : Attribute
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BindServiceMethodAttribute"/> class.
+        /// </summary>
+        /// <param name="bindType">The type the service bind method is defined on.</param>
+        /// <param name="bindMethodName">The name of the service bind method.</param>
+        public BindServiceMethodAttribute(Type bindType, string bindMethodName)
+        {
+            BindType = bindType;
+            BindMethodName = bindMethodName;
+        }
+
+        /// <summary>
+        /// Gets the type the service bind method is defined on.
+        /// </summary>
+        public Type BindType { get; }
+
+        /// <summary>
+        /// Gets the name of the service bind method.
+        /// </summary>
+        public string BindMethodName { get; }
+    }
+}
diff --git a/src/csharp/Grpc.Core.Api/CallCredentials.cs b/src/csharp/Grpc.Core.Api/CallCredentials.cs
new file mode 100644 (file)
index 0000000..6344a88
--- /dev/null
@@ -0,0 +1,90 @@
+#region Copyright notice and license
+
+// 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.
+
+#endregion
+
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+
+namespace Grpc.Core
+{
+    /// <summary>
+    /// Client-side call credentials. Provide authorization with per-call granularity.
+    /// </summary>
+    public abstract class CallCredentials
+    {
+        /// <summary>
+        /// Composes multiple multiple <c>CallCredentials</c> objects into
+        /// a single <c>CallCredentials</c> object.
+        /// </summary>
+        /// <param name="credentials">credentials to compose</param>
+        /// <returns>The new <c>CompositeCallCredentials</c></returns>
+        public static CallCredentials Compose(params CallCredentials[] credentials)
+        {
+            return new CompositeCallCredentials(credentials);
+        }
+
+        /// <summary>
+        /// Creates a new instance of <c>CallCredentials</c> class from an
+        /// interceptor that can attach metadata to outgoing calls.
+        /// </summary>
+        /// <param name="interceptor">authentication interceptor</param>
+        public static CallCredentials FromInterceptor(AsyncAuthInterceptor interceptor)
+        {
+            return new AsyncAuthInterceptorCredentials(interceptor);
+        }
+
+        /// <summary>
+        /// Populates this call credential instances.
+        /// You never need to invoke this, part of internal implementation.
+        /// </summary>
+        public abstract void InternalPopulateConfiguration(CallCredentialsConfiguratorBase configurator, object state);
+
+        private class CompositeCallCredentials : CallCredentials
+        {
+            readonly IReadOnlyList<CallCredentials> credentials;
+
+            public CompositeCallCredentials(CallCredentials[] credentials)
+            {
+                GrpcPreconditions.CheckArgument(credentials.Length >= 2, "Composite credentials object can only be created from 2 or more credentials.");
+                this.credentials = new List<CallCredentials>(credentials).AsReadOnly();
+            }
+
+            public override void InternalPopulateConfiguration(CallCredentialsConfiguratorBase configurator, object state)
+            {
+                configurator.SetCompositeCredentials(state, credentials);
+            }
+        }
+
+        private class AsyncAuthInterceptorCredentials : CallCredentials
+        {
+            readonly AsyncAuthInterceptor interceptor;
+
+            public AsyncAuthInterceptorCredentials(AsyncAuthInterceptor interceptor)
+            {
+                this.interceptor = GrpcPreconditions.CheckNotNull(interceptor);
+            }
+
+            public override void InternalPopulateConfiguration(CallCredentialsConfiguratorBase configurator, object state)
+            {
+                configurator.SetAsyncAuthInterceptorCredentials(state, interceptor);
+            }
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core.Api/CallCredentialsConfiguratorBase.cs b/src/csharp/Grpc.Core.Api/CallCredentialsConfiguratorBase.cs
new file mode 100644 (file)
index 0000000..51f8d72
--- /dev/null
@@ -0,0 +1,39 @@
+#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.Collections.Generic;
+
+namespace Grpc.Core
+{
+    /// <summary>
+    /// Base class for objects that can consume configuration from <c>CallCredentials</c> objects.
+    /// Note: experimental API that can change or be removed without any prior notice.
+    /// </summary>
+    public abstract class CallCredentialsConfiguratorBase
+    {
+        /// <summary>
+        /// Consumes configuration for composite call credentials.
+        /// </summary>
+        public abstract void SetCompositeCredentials(object state, IReadOnlyList<CallCredentials> credentials);
+
+        /// <summary>
+        /// Consumes configuration for call credentials created from <c>AsyncAuthInterceptor</c>
+        /// </summary>
+        public abstract void SetAsyncAuthInterceptorCredentials(object state, AsyncAuthInterceptor interceptor);
+    }
+}
similarity index 98%
rename from src/csharp/Grpc.Core/CallInvoker.cs
rename to src/csharp/Grpc.Core.Api/CallInvoker.cs
index 09fbf0b..8a84a52 100644 (file)
 #endregion
 
 using System.Threading.Tasks;
-using Grpc.Core.Internal;
 
 namespace Grpc.Core
 {
     /// <summary>
     /// Abstraction of client-side RPC invocation.
     /// </summary>
-    /// <seealso cref="Calls"/>
     public abstract class CallInvoker
     {
         /// <summary>
similarity index 84%
rename from src/csharp/Grpc.Core/CallOptions.cs
rename to src/csharp/Grpc.Core.Api/CallOptions.cs
index a92caae..c0b732a 100644 (file)
@@ -20,7 +20,6 @@ using System;
 using System.Threading;
 
 using Grpc.Core.Internal;
-using Grpc.Core.Utils;
 
 namespace Grpc.Core
 {
@@ -227,36 +226,5 @@ namespace Grpc.Core
             newOptions.flags = flags;
             return newOptions;
         }
-
-        /// <summary>
-        /// Returns a new instance of <see cref="CallOptions"/> with 
-        /// all previously unset values set to their defaults and deadline and cancellation
-        /// token propagated when appropriate.
-        /// </summary>
-        internal CallOptions Normalize()
-        {
-            var newOptions = this;
-            // silently ignore the context propagation token if it wasn't produced by "us"
-            var propagationTokenImpl = propagationToken.AsImplOrNull();
-            if (propagationTokenImpl != null)
-            {
-                if (propagationTokenImpl.Options.IsPropagateDeadline)
-                {
-                    GrpcPreconditions.CheckArgument(!newOptions.deadline.HasValue,
-                        "Cannot propagate deadline from parent call. The deadline has already been set explicitly.");
-                    newOptions.deadline = propagationTokenImpl.ParentDeadline;
-                }
-                if (propagationTokenImpl.Options.IsPropagateCancellation)
-                {
-                    GrpcPreconditions.CheckArgument(!newOptions.cancellationToken.CanBeCanceled,
-                        "Cannot propagate cancellation token from parent call. The cancellation token has already been set to a non-default value.");
-                    newOptions.cancellationToken = propagationTokenImpl.ParentCancellationToken;
-                }
-            }
-
-            newOptions.headers = newOptions.headers ?? Metadata.Empty;
-            newOptions.deadline = newOptions.deadline ?? DateTime.MaxValue;
-            return newOptions;
-        }
     }
 }
index d69e0db..966bcfa 100644 (file)
@@ -39,7 +39,7 @@ namespace Grpc.Core
         /// Also, allocating a new buffer each time can put excessive pressure on GC, especially if
         /// the payload is more than 86700 bytes large (which means the newly allocated buffer will be placed in LOH,
         /// and LOH object can only be garbage collected via a full ("stop the world") GC run).
-        /// NOTE: Deserializers are expected not to call this method more than once per received message
+        /// NOTE: Deserializers are expected not to call this method (or other payload accessor methods) more than once per received message
         /// (as there is no practical reason for doing so) and <c>DeserializationContext</c> implementations are free to assume so.
         /// </summary>
         /// <returns>byte array containing the entire payload.</returns>
@@ -47,5 +47,22 @@ namespace Grpc.Core
         {
             throw new NotImplementedException();
         }
+
+#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
+        /// <summary>
+        /// Gets the entire payload as a ReadOnlySequence.
+        /// The ReadOnlySequence is only valid for the duration of the deserializer routine and the caller must not access it after the deserializer returns.
+        /// Using the read only sequence is the most efficient way to access the message payload. Where possible it allows directly
+        /// accessing the received payload without needing to perform any buffer copying or buffer allocations.
+        /// NOTE: This method is only available in the netstandard2.0 build of the library.
+        /// NOTE: Deserializers are expected not to call this method (or other payload accessor methods) more than once per received message
+        /// (as there is no practical reason for doing so) and <c>DeserializationContext</c> implementations are free to assume so.
+        /// </summary>
+        /// <returns>read only sequence containing the entire payload.</returns>
+        public virtual System.Buffers.ReadOnlySequence<byte> PayloadAsReadOnlySequence()
+        {
+            throw new NotImplementedException();
+        }
+#endif        
     }
 }
index 6c29530..8a32bc7 100755 (executable)
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
 
+  <PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
+    <DefineConstants>$(DefineConstants);GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY</DefineConstants>
+  </PropertyGroup>
+
   <Import Project="..\Grpc.Core\SourceLink.csproj.include" />
 
   <ItemGroup>
     <PackageReference Include="System.Interactive.Async" Version="3.2.0" />
   </ItemGroup>
 
+  <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
+    <PackageReference Include="System.Memory" Version="4.5.2" />
+  </ItemGroup>
+
   <ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
     <Reference Include="System" />
     <Reference Include="Microsoft.CSharp" />
index 57fe538..8493f37 100644 (file)
@@ -114,7 +114,8 @@ namespace Grpc.Core
         /// <summary>
         /// The service is currently unavailable.  This is a most likely a
         /// transient condition and may be corrected by retrying with
-        /// a backoff.
+        /// a backoff. Note that it is not always safe to retry
+        /// non-idempotent operations.
         /// </summary>
         Unavailable = 14,
 
index 402b6c3..494ce14 100644 (file)
@@ -33,11 +33,11 @@ namespace Grpc.Core
         /// <summary>
         /// Current <c>AssemblyFileVersion</c> of gRPC C# assemblies
         /// </summary>
-        public const string CurrentAssemblyFileVersion = "1.20.1.0";
+        public const string CurrentAssemblyFileVersion = "1.21.0.0";
 
         /// <summary>
         /// Current version of gRPC C#
         /// </summary>
-        public const string CurrentVersion = "1.20.1";
+        public const string CurrentVersion = "1.21.0";
     }
 }
index f23c9e9..59587b9 100644 (file)
@@ -42,9 +42,9 @@ namespace Grpc.Core.Tests
 
     internal class FakeCallCredentials : CallCredentials
     {
-        internal override CallCredentialsSafeHandle ToNativeCredentials()
+        public override void InternalPopulateConfiguration(CallCredentialsConfiguratorBase configurator, object state)
         {
-            return null;
+            // not invoking the configurator on purpose
         }
     }
 }
index 23e5d7f..7fef2c7 100755 (executable)
@@ -8,6 +8,10 @@
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
 
+  <PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.1' ">
+    <DefineConstants>$(DefineConstants);GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY</DefineConstants>
+  </PropertyGroup>
+
   <ItemGroup>
     <ProjectReference Include="../Grpc.Core/Grpc.Core.csproj" />
   </ItemGroup>
index 5c7d48f..fd22161 100644 (file)
@@ -35,6 +35,7 @@ namespace Grpc.Core.Internal.Tests
         Server server;
         FakeNativeCall fakeCall;
         AsyncCallServer<string, string> asyncCallServer;
+        FakeBufferReaderManager fakeBufferReaderManager;
 
         [SetUp]
         public void Init()
@@ -52,11 +53,13 @@ namespace Grpc.Core.Internal.Tests
                 Marshallers.StringMarshaller.ContextualSerializer, Marshallers.StringMarshaller.ContextualDeserializer,
                 server);
             asyncCallServer.InitializeForTesting(fakeCall);
+            fakeBufferReaderManager = new FakeBufferReaderManager();
         }
 
         [TearDown]
         public void Cleanup()
         {
+            fakeBufferReaderManager.Dispose();
             server.ShutdownAsync().Wait();
         }
 
@@ -77,7 +80,7 @@ namespace Grpc.Core.Internal.Tests
             var moveNextTask = requestStream.MoveNext();
 
             fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true);
-            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null);
+            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
             Assert.IsFalse(moveNextTask.Result);
 
             AssertFinished(asyncCallServer, fakeCall, finishedTask);
@@ -107,7 +110,7 @@ namespace Grpc.Core.Internal.Tests
             // if a read completion's success==false, the request stream will silently finish
             // and we rely on C core cancelling the call.
             var moveNextTask = requestStream.MoveNext();
-            fakeCall.ReceivedMessageCallback.OnReceivedMessage(false, null);
+            fakeCall.ReceivedMessageCallback.OnReceivedMessage(false, CreateNullResponse());
             Assert.IsFalse(moveNextTask.Result);
 
             fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true);
@@ -182,5 +185,10 @@ namespace Grpc.Core.Internal.Tests
             Assert.IsTrue(finishedTask.IsCompleted);
             Assert.DoesNotThrow(() => finishedTask.Wait());
         }
+
+        IBufferReader CreateNullResponse()
+        {
+            return fakeBufferReaderManager.CreateNullPayloadBufferReader();
+        }
     }
 }
index 775849d..78c7f3a 100644 (file)
@@ -21,6 +21,7 @@ using System.Collections.Generic;
 using System.Threading.Tasks;
 
 using Grpc.Core.Internal;
+using Grpc.Core.Utils;
 using NUnit.Framework;
 
 namespace Grpc.Core.Internal.Tests
@@ -33,6 +34,7 @@ namespace Grpc.Core.Internal.Tests
         Channel channel;
         FakeNativeCall fakeCall;
         AsyncCall<string, string> asyncCall;
+        FakeBufferReaderManager fakeBufferReaderManager;
 
         [SetUp]
         public void Init()
@@ -43,12 +45,14 @@ namespace Grpc.Core.Internal.Tests
 
             var callDetails = new CallInvocationDetails<string, string>(channel, "someMethod", null, Marshallers.StringMarshaller, Marshallers.StringMarshaller, new CallOptions());
             asyncCall = new AsyncCall<string, string>(callDetails, fakeCall);
+            fakeBufferReaderManager = new FakeBufferReaderManager();
         }
 
         [TearDown]
         public void Cleanup()
         {
             channel.ShutdownAsync().Wait();
+            fakeBufferReaderManager.Dispose();
         }
 
         [Test]
@@ -87,7 +91,7 @@ namespace Grpc.Core.Internal.Tests
             var resultTask = asyncCall.UnaryCallAsync("request1");
             fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
                 CreateClientSideStatus(StatusCode.InvalidArgument),
-                null,
+                CreateNullResponse(),
                 new Metadata());
 
             AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.InvalidArgument);
@@ -168,7 +172,7 @@ namespace Grpc.Core.Internal.Tests
             var resultTask = asyncCall.ClientStreamingCallAsync();
             fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
                 CreateClientSideStatus(StatusCode.InvalidArgument),
-                null,
+                CreateNullResponse(),
                 new Metadata());
 
             AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.InvalidArgument);
@@ -214,7 +218,7 @@ namespace Grpc.Core.Internal.Tests
 
             fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
                 CreateClientSideStatus(StatusCode.Internal),
-                null,
+                CreateNullResponse(),
                 new Metadata());
 
             var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
@@ -233,7 +237,7 @@ namespace Grpc.Core.Internal.Tests
 
             fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
                 CreateClientSideStatus(StatusCode.Internal),
-                null,
+                CreateNullResponse(),
                 new Metadata());
 
             fakeCall.SendCompletionCallback.OnSendCompletion(false);
@@ -259,7 +263,7 @@ namespace Grpc.Core.Internal.Tests
 
             fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
                 CreateClientSideStatus(StatusCode.Internal),
-                null,
+                CreateNullResponse(),
                 new Metadata());
 
             var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
@@ -357,7 +361,7 @@ namespace Grpc.Core.Internal.Tests
 
             fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
                 CreateClientSideStatus(StatusCode.Cancelled),
-                null,
+                CreateNullResponse(),
                 new Metadata());
 
             AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.Cancelled);
@@ -390,7 +394,7 @@ namespace Grpc.Core.Internal.Tests
             fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
             Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
 
-            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null);
+            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
             fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
 
             AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask);
@@ -405,7 +409,7 @@ namespace Grpc.Core.Internal.Tests
 
             // try alternative order of completions
             fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
-            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null);
+            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
 
             AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask);
         }
@@ -417,7 +421,7 @@ namespace Grpc.Core.Internal.Tests
             var responseStream = new ClientResponseStream<string, string>(asyncCall);
             var readTask = responseStream.MoveNext();
 
-            fakeCall.ReceivedMessageCallback.OnReceivedMessage(false, null);  // after a failed read, we rely on C core to deliver appropriate status code.
+            fakeCall.ReceivedMessageCallback.OnReceivedMessage(false, CreateNullResponse());  // after a failed read, we rely on C core to deliver appropriate status code.
             fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Internal));
 
             AssertStreamingResponseError(asyncCall, fakeCall, readTask, StatusCode.Internal);
@@ -441,7 +445,7 @@ namespace Grpc.Core.Internal.Tests
 
             var readTask3 = responseStream.MoveNext();
             fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
-            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null);
+            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
 
             AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask3);
         }
@@ -479,7 +483,7 @@ namespace Grpc.Core.Internal.Tests
             Assert.DoesNotThrowAsync(async () => await writeTask1);
 
             var readTask = responseStream.MoveNext();
-            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null);
+            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
             fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
 
             AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask);
@@ -493,7 +497,7 @@ namespace Grpc.Core.Internal.Tests
             var responseStream = new ClientResponseStream<string, string>(asyncCall);
 
             var readTask = responseStream.MoveNext();
-            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null);
+            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
             fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
 
             AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask);
@@ -511,7 +515,7 @@ namespace Grpc.Core.Internal.Tests
             var responseStream = new ClientResponseStream<string, string>(asyncCall);
 
             var readTask = responseStream.MoveNext();
-            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null);
+            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
             fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
 
             AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask);
@@ -533,7 +537,7 @@ namespace Grpc.Core.Internal.Tests
             Assert.IsFalse(writeTask.IsCompleted);
 
             var readTask = responseStream.MoveNext();
-            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null);
+            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
             fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.PermissionDenied));
 
             var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
@@ -552,7 +556,7 @@ namespace Grpc.Core.Internal.Tests
             var writeTask = requestStream.WriteAsync("request1");
 
             var readTask = responseStream.MoveNext();
-            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null);
+            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
             fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.PermissionDenied));
             fakeCall.SendCompletionCallback.OnSendCompletion(false);
 
@@ -576,7 +580,7 @@ namespace Grpc.Core.Internal.Tests
             Assert.ThrowsAsync(typeof(TaskCanceledException), async () => await writeTask);
 
             var readTask = responseStream.MoveNext();
-            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null);
+            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
             fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Cancelled));
 
             AssertStreamingResponseError(asyncCall, fakeCall, readTask, StatusCode.Cancelled);
@@ -597,7 +601,7 @@ namespace Grpc.Core.Internal.Tests
             Assert.AreEqual("response1", responseStream.Current);
 
             var readTask2 = responseStream.MoveNext();
-            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null);
+            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
             fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Cancelled));
 
             AssertStreamingResponseError(asyncCall, fakeCall, readTask2, StatusCode.Cancelled);
@@ -618,7 +622,7 @@ namespace Grpc.Core.Internal.Tests
             Assert.AreEqual("response1", responseStream.Current);
 
             var readTask2 = responseStream.MoveNext();
-            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null);
+            fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
             fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Cancelled));
 
             AssertStreamingResponseError(asyncCall, fakeCall, readTask2, StatusCode.Cancelled);
@@ -638,9 +642,14 @@ namespace Grpc.Core.Internal.Tests
             return new ClientSideStatus(new Status(statusCode, ""), new Metadata());
         }
 
-        byte[] CreateResponsePayload()
+        IBufferReader CreateResponsePayload()
+        {
+            return fakeBufferReaderManager.CreateSingleSegmentBufferReader(Marshallers.StringMarshaller.Serializer("response1"));
+        }
+
+        IBufferReader CreateNullResponse()
         {
-            return Marshallers.StringMarshaller.Serializer("response1");
+            return fakeBufferReaderManager.CreateNullPayloadBufferReader();
         }
 
         static void AssertUnaryResponseSuccess(AsyncCall<string, string> asyncCall, FakeNativeCall fakeCall, Task<string> resultTask)
index 7e4e297..a0ea1cc 100644 (file)
@@ -47,7 +47,6 @@ namespace Grpc.Core.Internal.Tests
             GrpcEnvironment.ReleaseAsync().Wait();
             Assert.AreEqual(CompletionQueueEvent.CompletionType.Shutdown, ev.type);
             Assert.AreNotEqual(IntPtr.Zero, ev.success);
-            Assert.AreEqual(IntPtr.Zero, ev.tag);
         }
     }
 }
diff --git a/src/csharp/Grpc.Core.Tests/Internal/DefaultDeserializationContextTest.cs b/src/csharp/Grpc.Core.Tests/Internal/DefaultDeserializationContextTest.cs
new file mode 100644 (file)
index 0000000..63baab3
--- /dev/null
@@ -0,0 +1,240 @@
+#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.Collections.Generic;
+using Grpc.Core;
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+using NUnit.Framework;
+
+using System.Runtime.InteropServices;
+
+#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
+using System.Buffers;
+#endif
+
+namespace Grpc.Core.Internal.Tests
+{
+    public class DefaultDeserializationContextTest
+    {
+        FakeBufferReaderManager fakeBufferReaderManager;
+
+        [SetUp]
+        public void Init()
+        {
+            fakeBufferReaderManager = new FakeBufferReaderManager();
+        }
+
+        [TearDown]
+        public void Cleanup()
+        {
+            fakeBufferReaderManager.Dispose();
+        }
+
+#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
+        [TestCase]
+        public void PayloadAsReadOnlySequence_ZeroSegmentPayload()
+        {
+            var context = new DefaultDeserializationContext();
+            context.Initialize(fakeBufferReaderManager.CreateMultiSegmentBufferReader(new List<byte[]> {}));
+
+            Assert.AreEqual(0, context.PayloadLength);
+
+            var sequence = context.PayloadAsReadOnlySequence();
+
+            Assert.AreEqual(ReadOnlySequence<byte>.Empty, sequence);
+            Assert.IsTrue(sequence.IsEmpty);
+            Assert.IsTrue(sequence.IsSingleSegment);
+        }
+
+        [TestCase(0)]
+        [TestCase(1)]
+        [TestCase(10)]
+        [TestCase(100)]
+        [TestCase(1000)]
+        public void PayloadAsReadOnlySequence_SingleSegmentPayload(int segmentLength)
+        {
+            var origBuffer = GetTestBuffer(segmentLength);
+            var context = new DefaultDeserializationContext();
+            context.Initialize(fakeBufferReaderManager.CreateSingleSegmentBufferReader(origBuffer));
+
+            Assert.AreEqual(origBuffer.Length, context.PayloadLength);
+
+            var sequence = context.PayloadAsReadOnlySequence();
+
+            Assert.AreEqual(origBuffer.Length, sequence.Length);
+            Assert.AreEqual(origBuffer.Length, sequence.First.Length);
+            Assert.IsTrue(sequence.IsSingleSegment);
+            CollectionAssert.AreEqual(origBuffer, sequence.First.ToArray());
+        }
+
+        [TestCase(0, 5, 10)]
+        [TestCase(1, 1, 1)]
+        [TestCase(10, 100, 1000)]
+        [TestCase(100, 100, 10)]
+        [TestCase(1000, 1000, 1000)]
+        public void PayloadAsReadOnlySequence_MultiSegmentPayload(int segmentLen1, int segmentLen2, int segmentLen3)
+        {
+            var origBuffer1 = GetTestBuffer(segmentLen1);
+            var origBuffer2 = GetTestBuffer(segmentLen2);
+            var origBuffer3 = GetTestBuffer(segmentLen3);
+            int totalLen = origBuffer1.Length + origBuffer2.Length + origBuffer3.Length;
+
+            var context = new DefaultDeserializationContext();
+            context.Initialize(fakeBufferReaderManager.CreateMultiSegmentBufferReader(new List<byte[]> { origBuffer1, origBuffer2, origBuffer3 }));
+
+            Assert.AreEqual(totalLen, context.PayloadLength);
+
+            var sequence = context.PayloadAsReadOnlySequence();
+
+            Assert.AreEqual(totalLen, sequence.Length);
+
+            var segmentEnumerator = sequence.GetEnumerator();
+
+            Assert.IsTrue(segmentEnumerator.MoveNext());
+            CollectionAssert.AreEqual(origBuffer1, segmentEnumerator.Current.ToArray());
+
+            Assert.IsTrue(segmentEnumerator.MoveNext());
+            CollectionAssert.AreEqual(origBuffer2, segmentEnumerator.Current.ToArray());
+
+            Assert.IsTrue(segmentEnumerator.MoveNext());
+            CollectionAssert.AreEqual(origBuffer3, segmentEnumerator.Current.ToArray());
+
+            Assert.IsFalse(segmentEnumerator.MoveNext());
+        }
+#endif
+
+        [TestCase]
+        public void NullPayloadNotAllowed()
+        {
+            var context = new DefaultDeserializationContext();
+            Assert.Throws(typeof(InvalidOperationException), () => context.Initialize(fakeBufferReaderManager.CreateNullPayloadBufferReader()));
+        }
+
+        [TestCase]
+        public void PayloadAsNewByteBuffer_ZeroSegmentPayload()
+        {
+            var context = new DefaultDeserializationContext();
+            context.Initialize(fakeBufferReaderManager.CreateMultiSegmentBufferReader(new List<byte[]> {}));
+
+            Assert.AreEqual(0, context.PayloadLength);
+
+            var payload = context.PayloadAsNewBuffer();
+            Assert.AreEqual(0, payload.Length);
+        }
+
+        [TestCase(0)]
+        [TestCase(1)]
+        [TestCase(10)]
+        [TestCase(100)]
+        [TestCase(1000)]
+        public void PayloadAsNewByteBuffer_SingleSegmentPayload(int segmentLength)
+        {
+            var origBuffer = GetTestBuffer(segmentLength);
+            var context = new DefaultDeserializationContext();
+            context.Initialize(fakeBufferReaderManager.CreateSingleSegmentBufferReader(origBuffer));
+
+            Assert.AreEqual(origBuffer.Length, context.PayloadLength);
+
+            var payload = context.PayloadAsNewBuffer();
+            CollectionAssert.AreEqual(origBuffer, payload);
+        }
+
+        [TestCase(0, 5, 10)]
+        [TestCase(1, 1, 1)]
+        [TestCase(10, 100, 1000)]
+        [TestCase(100, 100, 10)]
+        [TestCase(1000, 1000, 1000)]
+        public void PayloadAsNewByteBuffer_MultiSegmentPayload(int segmentLen1, int segmentLen2, int segmentLen3)
+        {
+            var origBuffer1 = GetTestBuffer(segmentLen1);
+            var origBuffer2 = GetTestBuffer(segmentLen2);
+            var origBuffer3 = GetTestBuffer(segmentLen3);
+
+            var context = new DefaultDeserializationContext();
+            context.Initialize(fakeBufferReaderManager.CreateMultiSegmentBufferReader(new List<byte[]> { origBuffer1, origBuffer2, origBuffer3 }));
+
+            var payload = context.PayloadAsNewBuffer();
+
+            var concatenatedOrigBuffers = new List<byte>();
+            concatenatedOrigBuffers.AddRange(origBuffer1);
+            concatenatedOrigBuffers.AddRange(origBuffer2);
+            concatenatedOrigBuffers.AddRange(origBuffer3);
+
+            Assert.AreEqual(concatenatedOrigBuffers.Count, context.PayloadLength);
+            Assert.AreEqual(concatenatedOrigBuffers.Count, payload.Length);
+            CollectionAssert.AreEqual(concatenatedOrigBuffers, payload);
+        }
+
+        [TestCase]
+        public void GetPayloadMultipleTimesIsIllegal()
+        {
+            var origBuffer = GetTestBuffer(100);
+            var context = new DefaultDeserializationContext();
+            context.Initialize(fakeBufferReaderManager.CreateSingleSegmentBufferReader(origBuffer));
+
+            Assert.AreEqual(origBuffer.Length, context.PayloadLength);
+
+            var payload = context.PayloadAsNewBuffer();
+            CollectionAssert.AreEqual(origBuffer, payload);
+
+            // Getting payload multiple times is illegal
+            Assert.Throws(typeof(InvalidOperationException), () => context.PayloadAsNewBuffer());
+#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
+            Assert.Throws(typeof(InvalidOperationException), () => context.PayloadAsReadOnlySequence());
+#endif
+        }
+
+        [TestCase]
+        public void ResetContextAndReinitialize()
+        {
+            var origBuffer = GetTestBuffer(100);
+            var context = new DefaultDeserializationContext();
+            context.Initialize(fakeBufferReaderManager.CreateSingleSegmentBufferReader(origBuffer));
+
+            Assert.AreEqual(origBuffer.Length, context.PayloadLength);
+
+            // Reset invalidates context
+            context.Reset();
+
+            Assert.AreEqual(0, context.PayloadLength);
+            Assert.Throws(typeof(NullReferenceException), () => context.PayloadAsNewBuffer());
+#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
+            Assert.Throws(typeof(NullReferenceException), () => context.PayloadAsReadOnlySequence());
+#endif
+
+            // Previously reset context can be initialized again
+            var origBuffer2 = GetTestBuffer(50);
+            context.Initialize(fakeBufferReaderManager.CreateSingleSegmentBufferReader(origBuffer2));
+
+            Assert.AreEqual(origBuffer2.Length, context.PayloadLength);
+            CollectionAssert.AreEqual(origBuffer2, context.PayloadAsNewBuffer());
+        }
+
+        private byte[] GetTestBuffer(int length)
+        {
+            var testBuffer = new byte[length];
+            for (int i = 0; i < testBuffer.Length; i++)
+            {
+                testBuffer[i] = (byte) i;
+            }
+            return testBuffer;
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core.Tests/Internal/FakeBufferReaderManager.cs b/src/csharp/Grpc.Core.Tests/Internal/FakeBufferReaderManager.cs
new file mode 100644 (file)
index 0000000..d8d0c0a
--- /dev/null
@@ -0,0 +1,118 @@
+#region Copyright notice and license
+
+// Copyright 2018 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.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+
+namespace Grpc.Core.Internal.Tests
+{
+    // Creates instances of fake IBufferReader. All created instances will become invalid once Dispose is called.
+    internal class FakeBufferReaderManager : IDisposable
+    {
+        List<GCHandle> pinnedHandles = new List<GCHandle>();
+        bool disposed = false;
+        public IBufferReader CreateSingleSegmentBufferReader(byte[] data)
+        {
+            return CreateMultiSegmentBufferReader(new List<byte[]> { data });
+        }
+
+        public IBufferReader CreateMultiSegmentBufferReader(IEnumerable<byte[]> dataSegments)
+        {
+            GrpcPreconditions.CheckState(!disposed);
+            GrpcPreconditions.CheckNotNull(dataSegments);
+            var segments = new List<GCHandle>();
+            foreach (var data in dataSegments)
+            {
+                GrpcPreconditions.CheckNotNull(data);
+                segments.Add(GCHandle.Alloc(data, GCHandleType.Pinned));
+            }
+            pinnedHandles.AddRange(segments);  // all the allocated GCHandles will be freed on Dispose()
+            return new FakeBufferReader(segments);
+        }
+
+        public IBufferReader CreateNullPayloadBufferReader()
+        {
+            GrpcPreconditions.CheckState(!disposed);
+            return new FakeBufferReader(null);
+        }
+
+        public void Dispose()
+        {
+            if (!disposed)
+            {
+                disposed = true;
+                for (int i = 0; i < pinnedHandles.Count; i++)
+                {
+                    pinnedHandles[i].Free();
+                }
+            }
+        }
+
+        private class FakeBufferReader : IBufferReader
+        {
+            readonly List<GCHandle> bufferSegments;
+            readonly int? totalLength;
+            readonly IEnumerator<GCHandle> segmentEnumerator;
+
+            public FakeBufferReader(List<GCHandle> bufferSegments)
+            {
+                this.bufferSegments = bufferSegments;
+                this.totalLength = ComputeTotalLength(bufferSegments);
+                this.segmentEnumerator = bufferSegments?.GetEnumerator();
+            }
+
+            public int? TotalLength => totalLength;
+
+            public bool TryGetNextSlice(out Slice slice)
+            {
+                GrpcPreconditions.CheckNotNull(bufferSegments);
+                if (!segmentEnumerator.MoveNext())
+                {
+                    slice = default(Slice);
+                    return false;
+                }
+
+                var segment = segmentEnumerator.Current;
+                int sliceLen = ((byte[]) segment.Target).Length;
+                slice = new Slice(segment.AddrOfPinnedObject(), sliceLen);
+                return true;
+            }
+
+            static int? ComputeTotalLength(List<GCHandle> bufferSegments)
+            {
+                if (bufferSegments == null)
+                {
+                    return null;
+                }
+
+                int sum = 0;
+                foreach (var segment in bufferSegments)
+                {
+                    var data = (byte[]) segment.Target;
+                    sum += data.Length;
+                }
+                return sum;
+            }
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core.Tests/Internal/FakeBufferReaderManagerTest.cs b/src/csharp/Grpc.Core.Tests/Internal/FakeBufferReaderManagerTest.cs
new file mode 100644 (file)
index 0000000..7c4ff65
--- /dev/null
@@ -0,0 +1,121 @@
+#region Copyright notice and license
+
+// Copyright 2018 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.Collections.Generic;
+using Grpc.Core;
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+using NUnit.Framework;
+
+namespace Grpc.Core.Internal.Tests
+{
+    public class FakeBufferReaderManagerTest
+    {
+        FakeBufferReaderManager fakeBufferReaderManager;
+
+        [SetUp]
+        public void Init()
+        {
+            fakeBufferReaderManager = new FakeBufferReaderManager();
+        }
+
+        [TearDown]
+        public void Cleanup()
+        {
+            fakeBufferReaderManager.Dispose();
+        }
+
+        [TestCase]
+        public void NullPayload()
+        {
+            var fakeBufferReader = fakeBufferReaderManager.CreateNullPayloadBufferReader();
+            Assert.IsFalse(fakeBufferReader.TotalLength.HasValue);
+            Assert.Throws(typeof(ArgumentNullException), () => fakeBufferReader.TryGetNextSlice(out Slice slice));
+        }
+        [TestCase]
+        public void ZeroSegmentPayload()
+        {
+            var fakeBufferReader = fakeBufferReaderManager.CreateMultiSegmentBufferReader(new List<byte[]> {});
+            Assert.AreEqual(0, fakeBufferReader.TotalLength.Value);
+            Assert.IsFalse(fakeBufferReader.TryGetNextSlice(out Slice slice));
+        }
+
+        [TestCase(0)]
+        [TestCase(1)]
+        [TestCase(10)]
+        [TestCase(30)]
+        [TestCase(100)]
+        [TestCase(1000)]
+        public void SingleSegmentPayload(int bufferLen)
+        {
+            var origBuffer = GetTestBuffer(bufferLen);
+            var fakeBufferReader = fakeBufferReaderManager.CreateSingleSegmentBufferReader(origBuffer);
+            Assert.AreEqual(origBuffer.Length, fakeBufferReader.TotalLength.Value);
+
+            Assert.IsTrue(fakeBufferReader.TryGetNextSlice(out Slice slice));
+            AssertSliceDataEqual(origBuffer, slice);
+
+            Assert.IsFalse(fakeBufferReader.TryGetNextSlice(out Slice slice2));
+        }
+
+        [TestCase(0, 5, 10)]
+        [TestCase(1, 1, 1)]
+        [TestCase(10, 100, 1000)]
+        [TestCase(100, 100, 10)]
+        [TestCase(1000, 1000, 1000)]
+        public void MultiSegmentPayload(int segmentLen1, int segmentLen2, int segmentLen3)
+        {
+            var origBuffer1 = GetTestBuffer(segmentLen1);
+            var origBuffer2 = GetTestBuffer(segmentLen2);
+            var origBuffer3 = GetTestBuffer(segmentLen3);
+            var fakeBufferReader = fakeBufferReaderManager.CreateMultiSegmentBufferReader(new List<byte[]> { origBuffer1, origBuffer2, origBuffer3 });
+
+            Assert.AreEqual(origBuffer1.Length + origBuffer2.Length + origBuffer3.Length, fakeBufferReader.TotalLength.Value);
+
+            Assert.IsTrue(fakeBufferReader.TryGetNextSlice(out Slice slice1));
+            AssertSliceDataEqual(origBuffer1, slice1);
+
+            Assert.IsTrue(fakeBufferReader.TryGetNextSlice(out Slice slice2));
+            AssertSliceDataEqual(origBuffer2, slice2);
+
+            Assert.IsTrue(fakeBufferReader.TryGetNextSlice(out Slice slice3));
+            AssertSliceDataEqual(origBuffer3, slice3);
+
+            Assert.IsFalse(fakeBufferReader.TryGetNextSlice(out Slice slice4));
+        }
+
+        private void AssertSliceDataEqual(byte[] expected, Slice actual)
+        {
+            var actualSliceData = new byte[actual.Length];
+            actual.CopyTo(new ArraySegment<byte>(actualSliceData));
+            CollectionAssert.AreEqual(expected, actualSliceData);
+        }
+
+        // create a buffer of given size and fill it with some data
+        private byte[] GetTestBuffer(int length)
+        {
+            var testBuffer = new byte[length];
+            for (int i = 0; i < testBuffer.Length; i++)
+            {
+                testBuffer[i] = (byte) i;
+            }
+            return testBuffer;
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core.Tests/Internal/ReusableSliceBufferTest.cs b/src/csharp/Grpc.Core.Tests/Internal/ReusableSliceBufferTest.cs
new file mode 100644 (file)
index 0000000..7630785
--- /dev/null
@@ -0,0 +1,151 @@
+#region Copyright notice and license
+
+// Copyright 2018 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.Collections.Generic;
+using System.Linq;
+using Grpc.Core;
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+using NUnit.Framework;
+
+using System.Runtime.InteropServices;
+
+#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
+using System.Buffers;
+#endif
+
+namespace Grpc.Core.Internal.Tests
+{
+    // Converts IBufferReader into instances of ReadOnlySequence<byte>
+    // Objects representing the sequence segments are cached to decrease GC load.
+    public class ReusableSliceBufferTest
+    {
+        FakeBufferReaderManager fakeBufferReaderManager;
+
+        [SetUp]
+        public void Init()
+        {
+            fakeBufferReaderManager = new FakeBufferReaderManager();
+        }
+
+        [TearDown]
+        public void Cleanup()
+        {
+            fakeBufferReaderManager.Dispose();
+        }
+
+#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
+        [TestCase]
+        public void NullPayload()
+        {
+            var sliceBuffer = new ReusableSliceBuffer();
+            Assert.Throws(typeof(ArgumentNullException), () => sliceBuffer.PopulateFrom(fakeBufferReaderManager.CreateNullPayloadBufferReader()));
+        }
+
+        [TestCase]
+        public void ZeroSegmentPayload()
+        {
+            var sliceBuffer = new ReusableSliceBuffer();
+            var sequence = sliceBuffer.PopulateFrom(fakeBufferReaderManager.CreateMultiSegmentBufferReader(new List<byte[]> {}));
+
+            Assert.AreEqual(ReadOnlySequence<byte>.Empty, sequence);
+            Assert.IsTrue(sequence.IsEmpty);
+            Assert.IsTrue(sequence.IsSingleSegment);
+        }
+
+        [TestCase]
+        public void SegmentsAreCached()
+        {
+            var bufferSegments1 = Enumerable.Range(0, 100).Select((_) => GetTestBuffer(50)).ToList();
+            var bufferSegments2 = Enumerable.Range(0, 100).Select((_) => GetTestBuffer(50)).ToList();
+
+            var sliceBuffer = new ReusableSliceBuffer();
+
+            var sequence1 = sliceBuffer.PopulateFrom(fakeBufferReaderManager.CreateMultiSegmentBufferReader(bufferSegments1));
+            var memoryManagers1 = GetMemoryManagersForSequenceSegments(sequence1);
+
+            sliceBuffer.Invalidate();
+
+            var sequence2 = sliceBuffer.PopulateFrom(fakeBufferReaderManager.CreateMultiSegmentBufferReader(bufferSegments2));
+            var memoryManagers2 = GetMemoryManagersForSequenceSegments(sequence2);
+
+            // check memory managers are identical objects (i.e. they've been reused)
+            CollectionAssert.AreEquivalent(memoryManagers1, memoryManagers2);
+        }
+
+        [TestCase]
+        public void MultiSegmentPayload_LotsOfSegments()
+        {
+            var bufferSegments = Enumerable.Range(0, ReusableSliceBuffer.MaxCachedSegments + 100).Select((_) => GetTestBuffer(10)).ToList();
+
+            var sliceBuffer = new ReusableSliceBuffer();
+            var sequence = sliceBuffer.PopulateFrom(fakeBufferReaderManager.CreateMultiSegmentBufferReader(bufferSegments));
+
+            int index = 0;
+            foreach (var memory in sequence)
+            {
+                CollectionAssert.AreEqual(bufferSegments[index], memory.ToArray());
+                index ++;
+            }
+        }
+
+        [TestCase]
+        public void InvalidateMakesSequenceUnusable()
+        {
+            var origBuffer = GetTestBuffer(100);
+
+            var sliceBuffer = new ReusableSliceBuffer();
+            var sequence = sliceBuffer.PopulateFrom(fakeBufferReaderManager.CreateMultiSegmentBufferReader(new List<byte[]> { origBuffer }));
+
+            Assert.AreEqual(origBuffer.Length, sequence.Length);
+
+            sliceBuffer.Invalidate();
+
+            // Invalidate with make the returned sequence completely unusable and broken, users must not use it beyond the deserializer functions.
+            Assert.Throws(typeof(ArgumentOutOfRangeException), () => { var first = sequence.First; });
+        }
+
+        private List<MemoryManager<byte>> GetMemoryManagersForSequenceSegments(ReadOnlySequence<byte> sequence)
+        {
+            var result = new List<MemoryManager<byte>>();
+            foreach (var memory in sequence)
+            {
+                Assert.IsTrue(MemoryMarshal.TryGetMemoryManager(memory, out MemoryManager<byte> memoryManager));
+                result.Add(memoryManager);
+            }
+            return result;
+        }
+#else
+        [TestCase]
+        public void OnlySupportedOnNetCore()
+        {
+            // Test case needs to exist to make C# sanity test happy.
+        }
+#endif
+        private byte[] GetTestBuffer(int length)
+        {
+            var testBuffer = new byte[length];
+            for (int i = 0; i < testBuffer.Length; i++)
+            {
+                testBuffer[i] = (byte) i;
+            }
+            return testBuffer;
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core.Tests/Internal/SliceTest.cs b/src/csharp/Grpc.Core.Tests/Internal/SliceTest.cs
new file mode 100644 (file)
index 0000000..eb090bb
--- /dev/null
@@ -0,0 +1,83 @@
+#region Copyright notice and license
+
+// Copyright 2018 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 Grpc.Core;
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+using NUnit.Framework;
+
+using System.Runtime.InteropServices;
+
+namespace Grpc.Core.Internal.Tests
+{
+    public class SliceTest
+    {
+        [TestCase(0)]
+        [TestCase(1)]
+        [TestCase(10)]
+        [TestCase(100)]
+        [TestCase(1000)]
+        public void SliceFromNativePtr_CopyToArraySegment(int bufferLength)
+        {
+            var origBuffer = GetTestBuffer(bufferLength);
+            var gcHandle = GCHandle.Alloc(origBuffer, GCHandleType.Pinned);
+            try
+            {
+                var slice = new Slice(gcHandle.AddrOfPinnedObject(), origBuffer.Length);
+                Assert.AreEqual(bufferLength, slice.Length);
+
+                var newBuffer = new byte[bufferLength];
+                slice.CopyTo(new ArraySegment<byte>(newBuffer));
+                CollectionAssert.AreEqual(origBuffer, newBuffer);
+            }
+            finally
+            {
+                gcHandle.Free();
+            }
+        }
+
+        [TestCase]
+        public void SliceFromNativePtr_CopyToArraySegmentTooSmall()
+        {
+            var origBuffer = GetTestBuffer(100);
+            var gcHandle = GCHandle.Alloc(origBuffer, GCHandleType.Pinned);
+            try
+            {
+                var slice = new Slice(gcHandle.AddrOfPinnedObject(), origBuffer.Length);
+                var tooSmall = new byte[origBuffer.Length - 1];
+                Assert.Catch(typeof(ArgumentException), () => slice.CopyTo(new ArraySegment<byte>(tooSmall)));
+            }
+            finally
+            {
+                gcHandle.Free();
+            }
+        }
+
+        // create a buffer of given size and fill it with some data
+        private byte[] GetTestBuffer(int length)
+        {
+            var testBuffer = new byte[length];
+            for (int i = 0; i < testBuffer.Length; i++)
+            {
+                testBuffer[i] = (byte) i;
+            }
+            return testBuffer;
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core/CallCredentials.cs b/src/csharp/Grpc.Core/CallCredentials.cs
deleted file mode 100644 (file)
index c1bd95b..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-#region Copyright notice and license
-
-// 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.
-
-#endregion
-
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
-using Grpc.Core.Internal;
-using Grpc.Core.Utils;
-
-namespace Grpc.Core
-{
-    /// <summary>
-    /// Client-side call credentials. Provide authorization with per-call granularity.
-    /// </summary>
-    public abstract class CallCredentials
-    {
-        /// <summary>
-        /// Composes multiple multiple <c>CallCredentials</c> objects into
-        /// a single <c>CallCredentials</c> object.
-        /// </summary>
-        /// <param name="credentials">credentials to compose</param>
-        /// <returns>The new <c>CompositeCallCredentials</c></returns>
-        public static CallCredentials Compose(params CallCredentials[] credentials)
-        {
-            return new CompositeCallCredentials(credentials);
-        }
-
-        /// <summary>
-        /// Creates a new instance of <c>CallCredentials</c> class from an
-        /// interceptor that can attach metadata to outgoing calls.
-        /// </summary>
-        /// <param name="interceptor">authentication interceptor</param>
-        public static CallCredentials FromInterceptor(AsyncAuthInterceptor interceptor)
-        {
-            return new MetadataCredentials(interceptor);
-        }
-
-        /// <summary>
-        /// Creates native object for the credentials.
-        /// </summary>
-        /// <returns>The native credentials.</returns>
-        internal abstract CallCredentialsSafeHandle ToNativeCredentials();
-    }
-
-    /// <summary>
-    /// Client-side credentials that delegate metadata based auth to an interceptor.
-    /// The interceptor is automatically invoked for each remote call that uses <c>MetadataCredentials.</c>
-    /// </summary>
-    internal sealed class MetadataCredentials : CallCredentials
-    {
-        readonly AsyncAuthInterceptor interceptor;
-
-        /// <summary>
-        /// Initializes a new instance of <c>MetadataCredentials</c> class.
-        /// </summary>
-        /// <param name="interceptor">authentication interceptor</param>
-        public MetadataCredentials(AsyncAuthInterceptor interceptor)
-        {
-            this.interceptor = GrpcPreconditions.CheckNotNull(interceptor);
-        }
-
-        internal override CallCredentialsSafeHandle ToNativeCredentials()
-        {
-            NativeMetadataCredentialsPlugin plugin = new NativeMetadataCredentialsPlugin(interceptor);
-            return plugin.Credentials;
-        }
-    }
-
-    /// <summary>
-    /// Credentials that allow composing multiple credentials objects into one <see cref="CallCredentials"/> object.
-    /// </summary>
-    internal sealed class CompositeCallCredentials : CallCredentials
-    {
-        readonly List<CallCredentials> credentials;
-
-        /// <summary>
-        /// Initializes a new instance of <c>CompositeCallCredentials</c> class.
-        /// The resulting credentials object will be composite of all the credentials specified as parameters.
-        /// </summary>
-        /// <param name="credentials">credentials to compose</param>
-        public CompositeCallCredentials(params CallCredentials[] credentials)
-        {
-            GrpcPreconditions.CheckArgument(credentials.Length >= 2, "Composite credentials object can only be created from 2 or more credentials.");
-            this.credentials = new List<CallCredentials>(credentials);
-        }
-
-        internal override CallCredentialsSafeHandle ToNativeCredentials()
-        {
-            return ToNativeRecursive(0);
-        }
-
-        // Recursive descent makes managing lifetime of intermediate CredentialSafeHandle instances easier.
-        // In practice, we won't usually see composites from more than two credentials anyway.
-        private CallCredentialsSafeHandle ToNativeRecursive(int startIndex)
-        {
-            if (startIndex == credentials.Count - 1)
-            {
-                return credentials[startIndex].ToNativeCredentials();
-            }
-
-            using (var cred1 = credentials[startIndex].ToNativeCredentials())
-            using (var cred2 = ToNativeRecursive(startIndex + 1))
-            {
-                var nativeComposite = CallCredentialsSafeHandle.CreateComposite(cred1, cred2);
-                if (nativeComposite.IsInvalid)
-                {
-                    throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials.");
-                }
-                return nativeComposite;
-            }
-        }
-    }
-}
index 3ce32f3..dcbe9f3 100644 (file)
 
 using System;
 using System.Collections.Generic;
+using System.Runtime.InteropServices;
 using System.Threading.Tasks;
 
 using Grpc.Core.Internal;
+using Grpc.Core.Logging;
 using Grpc.Core.Utils;
 
 namespace Grpc.Core
@@ -105,19 +107,37 @@ namespace Grpc.Core
     }
 
     /// <summary>
+    /// Callback invoked with the expected targetHost and the peer's certificate.
+    /// If false is returned by this callback then it is treated as a
+    /// verification failure and the attempted connection will fail.
+    /// Invocation of the callback is blocking, so any
+    /// implementation should be light-weight.
+    /// Note that the callback can potentially be invoked multiple times,
+    /// concurrently from different threads (e.g. when multiple connections
+    /// are being created for the same credentials).
+    /// </summary>
+    /// <param name="context">The <see cref="T:Grpc.Core.VerifyPeerContext"/> associated with the callback</param>
+    /// <returns>true if verification succeeded, false otherwise.</returns>
+    /// Note: experimental API that can change or be removed without any prior notice.
+    public delegate bool VerifyPeerCallback(VerifyPeerContext context);
+
+    /// <summary>
     /// Client-side SSL credentials.
     /// </summary>
     public sealed class SslCredentials : ChannelCredentials
     {
+        static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<SslCredentials>();
+
         readonly string rootCertificates;
         readonly KeyCertificatePair keyCertificatePair;
+        readonly VerifyPeerCallback verifyPeerCallback;
 
         /// <summary>
         /// Creates client-side SSL credentials loaded from
         /// disk file pointed to by the GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable.
         /// If that fails, gets the roots certificates from a well known place on disk.
         /// </summary>
-        public SslCredentials() : this(null, null)
+        public SslCredentials() : this(null, null, null)
         {
         }
 
@@ -125,19 +145,32 @@ namespace Grpc.Core
         /// Creates client-side SSL credentials from
         /// a string containing PEM encoded root certificates.
         /// </summary>
-        public SslCredentials(string rootCertificates) : this(rootCertificates, null)
+        public SslCredentials(string rootCertificates) : this(rootCertificates, null, null)
+        {
+        }
+
+        /// <summary>
+        /// Creates client-side SSL credentials.
+        /// </summary>
+        /// <param name="rootCertificates">string containing PEM encoded server root certificates.</param>
+        /// <param name="keyCertificatePair">a key certificate pair.</param>
+        public SslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair) :
+            this(rootCertificates, keyCertificatePair, null)
         {
         }
-            
+
         /// <summary>
         /// Creates client-side SSL credentials.
         /// </summary>
         /// <param name="rootCertificates">string containing PEM encoded server root certificates.</param>
         /// <param name="keyCertificatePair">a key certificate pair.</param>
-        public SslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair)
+        /// <param name="verifyPeerCallback">a callback to verify peer's target name and certificate.</param>
+        /// Note: experimental API that can change or be removed without any prior notice.
+        public SslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair, VerifyPeerCallback verifyPeerCallback)
         {
             this.rootCertificates = rootCertificates;
             this.keyCertificatePair = keyCertificatePair;
+            this.verifyPeerCallback = verifyPeerCallback;
         }
 
         /// <summary>
@@ -171,7 +204,54 @@ namespace Grpc.Core
 
         internal override ChannelCredentialsSafeHandle CreateNativeCredentials()
         {
-            return ChannelCredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair);
+            IntPtr verifyPeerCallbackTag = IntPtr.Zero;
+            if (verifyPeerCallback != null)
+            {
+                verifyPeerCallbackTag = new VerifyPeerCallbackRegistration(verifyPeerCallback).CallbackRegistration.Tag;
+            }
+            return ChannelCredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair, verifyPeerCallbackTag);
+        }
+
+        private class VerifyPeerCallbackRegistration
+        {
+            readonly VerifyPeerCallback verifyPeerCallback;
+            readonly NativeCallbackRegistration callbackRegistration;
+
+            public VerifyPeerCallbackRegistration(VerifyPeerCallback verifyPeerCallback)
+            {
+                this.verifyPeerCallback = verifyPeerCallback;
+                this.callbackRegistration = NativeCallbackDispatcher.RegisterCallback(HandleUniversalCallback);
+            }
+
+            public NativeCallbackRegistration CallbackRegistration => callbackRegistration;
+
+            private int HandleUniversalCallback(IntPtr arg0, IntPtr arg1, IntPtr arg2, IntPtr arg3, IntPtr arg4, IntPtr arg5)
+            {
+                return VerifyPeerCallbackHandler(arg0, arg1, arg2 != IntPtr.Zero);
+            }
+
+            private int VerifyPeerCallbackHandler(IntPtr targetName, IntPtr peerPem, bool isDestroy)
+            {
+                if (isDestroy)
+                {
+                    this.callbackRegistration.Dispose();
+                    return 0;
+                }
+
+                try
+                {
+                    var context = new VerifyPeerContext(Marshal.PtrToStringAnsi(targetName), Marshal.PtrToStringAnsi(peerPem));
+
+                    return this.verifyPeerCallback(context) ? 0 : 1;
+                }
+                catch (Exception e)
+                {
+                    // eat the exception, we must not throw when inside callback from native code.
+                    Logger.Error(e, "Exception occurred while invoking verify peer callback handler.");
+                    // Return validation failure in case of exception.
+                    return 1;
+                }
+            }
         }
     }
 
index 3922192..ab5f5a8 100644 (file)
 
 using System.Runtime.CompilerServices;
 using Grpc.Core;
-using Grpc.Core.Logging;
+using Grpc.Core.Interceptors;
+using Grpc.Core.Internal;
 using Grpc.Core.Utils;
 
 // API types that used to be in Grpc.Core package, but were moved to Grpc.Core.Api
 // https://docs.microsoft.com/en-us/dotnet/framework/app-domains/type-forwarding-in-the-common-language-runtime
 
-// TODO(jtattermusch): move types needed for implementing a client
-
 [assembly:TypeForwardedToAttribute(typeof(GrpcPreconditions))]
+[assembly:TypeForwardedToAttribute(typeof(AsyncClientStreamingCall<,>))]
+[assembly:TypeForwardedToAttribute(typeof(AsyncDuplexStreamingCall<,>))]
+[assembly:TypeForwardedToAttribute(typeof(AsyncServerStreamingCall<>))]
+[assembly:TypeForwardedToAttribute(typeof(AsyncUnaryCall<>))]
 [assembly:TypeForwardedToAttribute(typeof(AuthContext))]
+[assembly:TypeForwardedToAttribute(typeof(AsyncAuthInterceptor))]
+[assembly:TypeForwardedToAttribute(typeof(AuthInterceptorContext))]
+[assembly:TypeForwardedToAttribute(typeof(CallCredentials))]
+[assembly:TypeForwardedToAttribute(typeof(CallFlags))]
+[assembly:TypeForwardedToAttribute(typeof(CallInvoker))]
+[assembly:TypeForwardedToAttribute(typeof(CallOptions))]
+[assembly:TypeForwardedToAttribute(typeof(ClientInterceptorContext<,>))]
 [assembly:TypeForwardedToAttribute(typeof(ContextPropagationOptions))]
 [assembly:TypeForwardedToAttribute(typeof(ContextPropagationToken))]
 [assembly:TypeForwardedToAttribute(typeof(DeserializationContext))]
 [assembly:TypeForwardedToAttribute(typeof(IAsyncStreamReader<>))]
 [assembly:TypeForwardedToAttribute(typeof(IAsyncStreamWriter<>))]
+[assembly:TypeForwardedToAttribute(typeof(IClientStreamWriter<>))]
+[assembly:TypeForwardedToAttribute(typeof(Interceptor))]
 [assembly:TypeForwardedToAttribute(typeof(IServerStreamWriter<>))]
 [assembly:TypeForwardedToAttribute(typeof(Marshaller<>))]
 [assembly:TypeForwardedToAttribute(typeof(Marshallers))]
index b7c191e..afd60e7 100755 (executable)
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
 
+  <PropertyGroup>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+
+  <PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
+    <LangVersion>7.2</LangVersion>
+    <DefineConstants>$(DefineConstants);GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY</DefineConstants>
+  </PropertyGroup>
+
   <ItemGroup>
     <Compile Include="..\Grpc.Core.Api\Version.cs" />
   </ItemGroup>
index 785081c..a1c6881 100644 (file)
@@ -111,7 +111,7 @@ namespace Grpc.Core.Internal
                             {
                                 using (profiler.NewScope("AsyncCall.UnaryCall.HandleBatch"))
                                 {
-                                    HandleUnaryResponse(success, ctx.GetReceivedStatusOnClient(), ctx.GetReceivedMessage(), ctx.GetReceivedInitialMetadata());
+                                    HandleUnaryResponse(success, ctx.GetReceivedStatusOnClient(), ctx.GetReceivedMessageReader(), ctx.GetReceivedInitialMetadata());
                                 }
                             }
                             catch (Exception e)
@@ -537,14 +537,14 @@ namespace Grpc.Core.Internal
         /// <summary>
         /// Handler for unary response completion.
         /// </summary>
-        private void HandleUnaryResponse(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders)
+        private void HandleUnaryResponse(bool success, ClientSideStatus receivedStatus, IBufferReader receivedMessageReader, Metadata responseHeaders)
         {
             // NOTE: because this event is a result of batch containing GRPC_OP_RECV_STATUS_ON_CLIENT,
             // success will be always set to true.
 
             TaskCompletionSource<object> delayedStreamingWriteTcs = null;
             TResponse msg = default(TResponse);
-            var deserializeException = TryDeserialize(receivedMessage, out msg);
+            var deserializeException = TryDeserialize(receivedMessageReader, out msg);
 
             bool releasedResources;
             lock (myLock)
@@ -634,9 +634,9 @@ namespace Grpc.Core.Internal
 
         IUnaryResponseClientCallback UnaryResponseClientCallback => this;
 
-        void IUnaryResponseClientCallback.OnUnaryResponseClient(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders)
+        void IUnaryResponseClientCallback.OnUnaryResponseClient(bool success, ClientSideStatus receivedStatus, IBufferReader receivedMessageReader, Metadata responseHeaders)
         {
-            HandleUnaryResponse(success, receivedStatus, receivedMessage, responseHeaders);
+            HandleUnaryResponse(success, receivedStatus, receivedMessageReader, responseHeaders);
         }
 
         IReceivedStatusOnClientCallback ReceivedStatusOnClientCallback => this;
index 39c9f7c..0b99aaf 100644 (file)
@@ -228,12 +228,12 @@ namespace Grpc.Core.Internal
             }
         }
 
-        protected Exception TryDeserialize(byte[] payload, out TRead msg)
+        protected Exception TryDeserialize(IBufferReader reader, out TRead msg)
         {
             DefaultDeserializationContext context = null;
             try
             {
-                context = DefaultDeserializationContext.GetInitializedThreadLocal(payload);
+                context = DefaultDeserializationContext.GetInitializedThreadLocal(reader);
                 msg = deserializer(context);
                 return null;
             }
@@ -245,7 +245,6 @@ namespace Grpc.Core.Internal
             finally
             {
                 context?.Reset();
-
             }
         }
 
@@ -333,21 +332,21 @@ namespace Grpc.Core.Internal
         /// <summary>
         /// Handles streaming read completion.
         /// </summary>
-        protected void HandleReadFinished(bool success, byte[] receivedMessage)
+        protected void HandleReadFinished(bool success, IBufferReader receivedMessageReader)
         {
-            // if success == false, received message will be null. It that case we will
+            // if success == false, the message reader will report null payload. It that case we will
             // treat this completion as the last read an rely on C core to handle the failed
             // read (e.g. deliver approriate statusCode on the clientside).
 
             TRead msg = default(TRead);
-            var deserializeException = (success && receivedMessage != null) ? TryDeserialize(receivedMessage, out msg) : null;
+            var deserializeException = (success && receivedMessageReader.TotalLength.HasValue) ? TryDeserialize(receivedMessageReader, out msg) : null;
 
             TaskCompletionSource<TRead> origTcs = null;
             bool releasedResources;
             lock (myLock)
             {
                 origTcs = streamingReadTcs;
-                if (receivedMessage == null)
+                if (!receivedMessageReader.TotalLength.HasValue)
                 {
                     // This was the last read.
                     readingDone = true;
@@ -391,9 +390,9 @@ namespace Grpc.Core.Internal
 
         IReceivedMessageCallback ReceivedMessageCallback => this;
 
-        void IReceivedMessageCallback.OnReceivedMessage(bool success, byte[] receivedMessage)
+        void IReceivedMessageCallback.OnReceivedMessage(bool success, IBufferReader receivedMessageReader)
         {
-            HandleReadFinished(success, receivedMessage);
+            HandleReadFinished(success, receivedMessageReader);
         }
     }
 }
index 085e7fa..50a6268 100644 (file)
@@ -30,10 +30,17 @@ namespace Grpc.Core.Internal
         void OnComplete(bool success);
     }
 
+    internal interface IBufferReader
+    {
+        int? TotalLength { get; }
+
+        bool TryGetNextSlice(out Slice slice);
+    }
+
     /// <summary>
     /// grpcsharp_batch_context
     /// </summary>
-    internal class BatchContextSafeHandle : SafeHandleZeroIsInvalid, IOpCompletionCallback, IPooledObject<BatchContextSafeHandle>
+    internal class BatchContextSafeHandle : SafeHandleZeroIsInvalid, IOpCompletionCallback, IPooledObject<BatchContextSafeHandle>, IBufferReader
     {
         static readonly NativeMethods Native = NativeMethods.Get();
         static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<BatchContextSafeHandle>();
@@ -93,17 +100,9 @@ namespace Grpc.Core.Internal
             return new ClientSideStatus(status, metadata);
         }
 
-        // Gets data of recv_message completion.
-        public byte[] GetReceivedMessage()
+        public IBufferReader GetReceivedMessageReader()
         {
-            IntPtr len = Native.grpcsharp_batch_context_recv_message_length(this);
-            if (len == new IntPtr(-1))
-            {
-                return null;
-            }
-            byte[] data = new byte[(int)len];
-            Native.grpcsharp_batch_context_recv_message_to_buffer(this, data, new UIntPtr((ulong)data.Length));
-            return data;
+            return this;
         }
 
         // Gets data of receive_close_on_server completion.
@@ -153,6 +152,29 @@ namespace Grpc.Core.Internal
             }
         }
 
+        int? IBufferReader.TotalLength
+        {
+            get
+            {
+                var len = Native.grpcsharp_batch_context_recv_message_length(this);
+                return len != new IntPtr(-1) ? (int?) len : null;
+            }
+        }
+
+        bool IBufferReader.TryGetNextSlice(out Slice slice)
+        {
+            UIntPtr sliceLen;
+            IntPtr sliceDataPtr;
+
+            if (0 == Native.grpcsharp_batch_context_recv_message_next_slice_peek(this, out sliceLen, out sliceDataPtr))
+            {
+                slice = default(Slice);
+                return false;
+            }
+            slice = new Slice(sliceDataPtr, (int) sliceLen);
+            return true;
+        }
+
         struct CompletionCallbackData
         {
             public CompletionCallbackData(BatchCompletionDelegate callback, object state)
diff --git a/src/csharp/Grpc.Core/Internal/CallOptionsExtensions.cs b/src/csharp/Grpc.Core/Internal/CallOptionsExtensions.cs
new file mode 100644 (file)
index 0000000..b58b041
--- /dev/null
@@ -0,0 +1,57 @@
+#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;\r
+using Grpc.Core.Utils;
+
+namespace Grpc.Core.Internal
+{
+    internal static class CallOptionsExtensions
+    {
+        /// <summary>
+        /// Returns a new instance of <see cref="CallOptions"/> with
+        /// all previously unset values set to their defaults and deadline and cancellation
+        /// token propagated when appropriate.
+        /// </summary>
+        internal static CallOptions Normalize(this CallOptions options)
+        {
+            var newOptions = options;
+            // silently ignore the context propagation token if it wasn't produced by "us"
+            var propagationTokenImpl = options.PropagationToken.AsImplOrNull();
+            if (propagationTokenImpl != null)
+            {
+                if (propagationTokenImpl.Options.IsPropagateDeadline)
+                {
+                    GrpcPreconditions.CheckArgument(!newOptions.Deadline.HasValue,
+                        "Cannot propagate deadline from parent call. The deadline has already been set explicitly.");
+                    newOptions = newOptions.WithDeadline(propagationTokenImpl.ParentDeadline);
+                }
+                if (propagationTokenImpl.Options.IsPropagateCancellation)
+                {
+                    GrpcPreconditions.CheckArgument(!newOptions.CancellationToken.CanBeCanceled,
+                        "Cannot propagate cancellation token from parent call. The cancellation token has already been set to a non-default value.");
+                    newOptions = newOptions.WithCancellationToken(propagationTokenImpl.ParentCancellationToken);
+                }
+            }
+
+            newOptions = newOptions.WithHeaders(newOptions.Headers ?? Metadata.Empty);
+            newOptions = newOptions.WithDeadline(newOptions.Deadline ?? DateTime.MaxValue);
+            return newOptions;
+        }
+    }
+}
index a3ef3e6..858d2a6 100644 (file)
@@ -35,11 +35,11 @@ namespace Grpc.Core.Internal
         // Completion handlers are pre-allocated to avoid unneccessary delegate allocations.
         // The "state" field is used to store the actual callback to invoke.
         static readonly BatchCompletionDelegate CompletionHandler_IUnaryResponseClientCallback =
-            (success, context, state) => ((IUnaryResponseClientCallback)state).OnUnaryResponseClient(success, context.GetReceivedStatusOnClient(), context.GetReceivedMessage(), context.GetReceivedInitialMetadata());
+            (success, context, state) => ((IUnaryResponseClientCallback)state).OnUnaryResponseClient(success, context.GetReceivedStatusOnClient(), context.GetReceivedMessageReader(), context.GetReceivedInitialMetadata());
         static readonly BatchCompletionDelegate CompletionHandler_IReceivedStatusOnClientCallback =
             (success, context, state) => ((IReceivedStatusOnClientCallback)state).OnReceivedStatusOnClient(success, context.GetReceivedStatusOnClient());
         static readonly BatchCompletionDelegate CompletionHandler_IReceivedMessageCallback =
-            (success, context, state) => ((IReceivedMessageCallback)state).OnReceivedMessage(success, context.GetReceivedMessage());
+            (success, context, state) => ((IReceivedMessageCallback)state).OnReceivedMessage(success, context.GetReceivedMessageReader());
         static readonly BatchCompletionDelegate CompletionHandler_IReceivedResponseHeadersCallback =
             (success, context, state) => ((IReceivedResponseHeadersCallback)state).OnReceivedResponseHeaders(success, context.GetReceivedInitialMetadata());
         static readonly BatchCompletionDelegate CompletionHandler_ISendCompletionCallback =
index 11b5d2c..d4f5034 100644 (file)
@@ -38,15 +38,15 @@ namespace Grpc.Core.Internal
             return creds;
         }
 
-        public static ChannelCredentialsSafeHandle CreateSslCredentials(string pemRootCerts, KeyCertificatePair keyCertPair)
+        public static ChannelCredentialsSafeHandle CreateSslCredentials(string pemRootCerts, KeyCertificatePair keyCertPair, IntPtr verifyPeerCallbackTag)
         {
             if (keyCertPair != null)
             {
-                return Native.grpcsharp_ssl_credentials_create(pemRootCerts, keyCertPair.CertificateChain, keyCertPair.PrivateKey);
+                return Native.grpcsharp_ssl_credentials_create(pemRootCerts, keyCertPair.CertificateChain, keyCertPair.PrivateKey, verifyPeerCallbackTag);
             }
             else
             {
-                return Native.grpcsharp_ssl_credentials_create(pemRootCerts, null, null);
+                return Native.grpcsharp_ssl_credentials_create(pemRootCerts, null, null, verifyPeerCallbackTag);
             }
         }
 
diff --git a/src/csharp/Grpc.Core/Internal/DefaultCallCredentialsConfigurator.cs b/src/csharp/Grpc.Core/Internal/DefaultCallCredentialsConfigurator.cs
new file mode 100644 (file)
index 0000000..a2c53a1
--- /dev/null
@@ -0,0 +1,85 @@
+#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.Collections.Generic;
+using Grpc.Core.Utils;
+
+namespace Grpc.Core.Internal
+{
+    /// <summary>
+    /// Creates native call credential objects from instances of <c>CallCredentials</c>.
+    /// </summary>
+    internal class DefaultCallCredentialsConfigurator : CallCredentialsConfiguratorBase
+    {
+        CallCredentialsSafeHandle nativeCredentials;
+
+        public CallCredentialsSafeHandle NativeCredentials => nativeCredentials;
+
+        public override void SetAsyncAuthInterceptorCredentials(object state, AsyncAuthInterceptor interceptor)
+        {
+            GrpcPreconditions.CheckState(nativeCredentials == null);
+
+            var plugin = new NativeMetadataCredentialsPlugin(interceptor);
+            nativeCredentials = plugin.Credentials;
+        }
+
+        public override void SetCompositeCredentials(object state, IReadOnlyList<CallCredentials> credentials)
+        {
+            GrpcPreconditions.CheckState(nativeCredentials == null);
+
+            GrpcPreconditions.CheckArgument(credentials.Count >= 2);
+            nativeCredentials = CompositeToNativeRecursive(credentials, 0);
+        }
+
+        // Recursive descent makes managing lifetime of intermediate CredentialSafeHandle instances easier.
+        // In practice, we won't usually see composites from more than two credentials anyway.
+        private CallCredentialsSafeHandle CompositeToNativeRecursive(IReadOnlyList<CallCredentials> credentials, int startIndex)
+        {
+            if (startIndex == credentials.Count - 1)
+            {
+                return credentials[startIndex].ToNativeCredentials();
+            }
+
+            using (var cred1 = credentials[startIndex].ToNativeCredentials())
+            using (var cred2 = CompositeToNativeRecursive(credentials, startIndex + 1))
+            {
+                var nativeComposite = CallCredentialsSafeHandle.CreateComposite(cred1, cred2);
+                if (nativeComposite.IsInvalid)
+                {
+                    throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials.");
+                }
+                return nativeComposite;
+            }
+        }
+    }
+
+    internal static class CallCredentialsExtensions
+    {
+        /// <summary>
+        /// Creates native object for the credentials.
+        /// </summary>
+        /// <returns>The native credentials.</returns>
+        public static CallCredentialsSafeHandle ToNativeCredentials(this CallCredentials credentials)
+        {
+            var configurator = new DefaultCallCredentialsConfigurator();
+            credentials.InternalPopulateConfiguration(configurator, credentials);
+            return configurator.NativeCredentials;
+        }
+    }
+}
index 7ace80e..946c37d 100644 (file)
@@ -20,6 +20,10 @@ using Grpc.Core.Utils;
 using System;
 using System.Threading;
 
+#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
+using System.Buffers;
+#endif
+
 namespace Grpc.Core.Internal
 {
     internal class DefaultDeserializationContext : DeserializationContext
@@ -27,40 +31,71 @@ namespace Grpc.Core.Internal
         static readonly ThreadLocal<DefaultDeserializationContext> threadLocalInstance =
             new ThreadLocal<DefaultDeserializationContext>(() => new DefaultDeserializationContext(), false);
 
-        byte[] payload;
-        bool alreadyCalledPayloadAsNewBuffer;
+        IBufferReader bufferReader;
+        int payloadLength;
+#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
+        ReusableSliceBuffer cachedSliceBuffer = new ReusableSliceBuffer();
+#endif
 
         public DefaultDeserializationContext()
         {
             Reset();
         }
 
-        public override int PayloadLength => payload.Length;
+        public override int PayloadLength => payloadLength;
 
         public override byte[] PayloadAsNewBuffer()
         {
-            GrpcPreconditions.CheckState(!alreadyCalledPayloadAsNewBuffer);
-            alreadyCalledPayloadAsNewBuffer = true;
-            return payload;
+            var buffer = new byte[payloadLength];
+            FillContinguousBuffer(bufferReader, buffer);
+            return buffer;
+        }
+
+#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
+        public override ReadOnlySequence<byte> PayloadAsReadOnlySequence()
+        {
+            var sequence = cachedSliceBuffer.PopulateFrom(bufferReader);
+            GrpcPreconditions.CheckState(sequence.Length == payloadLength);
+            return sequence;
         }
+#endif
 
-        public void Initialize(byte[] payload)
+        public void Initialize(IBufferReader bufferReader)
         {
-            this.payload = GrpcPreconditions.CheckNotNull(payload);
-            this.alreadyCalledPayloadAsNewBuffer = false;
+            this.bufferReader = GrpcPreconditions.CheckNotNull(bufferReader);
+            this.payloadLength = bufferReader.TotalLength.Value;  // payload must not be null
         }
 
         public void Reset()
         {
-            this.payload = null;
-            this.alreadyCalledPayloadAsNewBuffer = true;  // mark payload as read
+            this.bufferReader = null;
+            this.payloadLength = 0;
+#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
+            this.cachedSliceBuffer.Invalidate();
+#endif
         }
 
-        public static DefaultDeserializationContext GetInitializedThreadLocal(byte[] payload)
+        public static DefaultDeserializationContext GetInitializedThreadLocal(IBufferReader bufferReader)
         {
             var instance = threadLocalInstance.Value;
-            instance.Initialize(payload);
+            instance.Initialize(bufferReader);
             return instance;
         }
+
+        private void FillContinguousBuffer(IBufferReader reader, byte[] destination)
+        {
+#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
+            PayloadAsReadOnlySequence().CopyTo(new Span<byte>(destination));
+#else
+            int offset = 0;
+            while (reader.TryGetNextSlice(out Slice slice))
+            {
+                slice.CopyTo(new ArraySegment<byte>(destination, offset, (int)slice.Length));
+                offset += (int)slice.Length;
+            }
+            // check that we filled the entire destination
+            GrpcPreconditions.CheckState(offset == payloadLength);
+#endif
+        }
     }
 }
index 5c35b2b..98117c6 100644 (file)
@@ -22,7 +22,7 @@ namespace Grpc.Core.Internal
 {
     internal interface IUnaryResponseClientCallback
     {
-        void OnUnaryResponseClient(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders);
+        void OnUnaryResponseClient(bool success, ClientSideStatus receivedStatus, IBufferReader receivedMessageReader, Metadata responseHeaders);
     }
 
     // Received status for streaming response calls.
@@ -33,7 +33,7 @@ namespace Grpc.Core.Internal
 
     internal interface IReceivedMessageCallback
     {
-        void OnReceivedMessage(bool success, byte[] receivedMessage);
+        void OnReceivedMessage(bool success, IBufferReader receivedMessageReader);
     }
 
     internal interface IReceivedResponseHeadersCallback
index d5146e8..0777d79 100644 (file)
@@ -63,7 +63,7 @@ namespace Grpc.Core.Internal
             catch (Exception e)
             {
                 // eat the exception, we must not throw when inside callback from native code.
-                Logger.Error(e, "Caught exception inside callback from native callback.");
+                Logger.Error(e, "Caught exception inside callback from native code.");
                 return 0;
             }
         }
index b7b9a12..b8a60b3 100644 (file)
@@ -40,7 +40,7 @@ namespace Grpc.Core.Internal
         public readonly Delegates.grpcsharp_batch_context_create_delegate grpcsharp_batch_context_create;
         public readonly Delegates.grpcsharp_batch_context_recv_initial_metadata_delegate grpcsharp_batch_context_recv_initial_metadata;
         public readonly Delegates.grpcsharp_batch_context_recv_message_length_delegate grpcsharp_batch_context_recv_message_length;
-        public readonly Delegates.grpcsharp_batch_context_recv_message_to_buffer_delegate grpcsharp_batch_context_recv_message_to_buffer;
+        public readonly Delegates.grpcsharp_batch_context_recv_message_next_slice_peek_delegate grpcsharp_batch_context_recv_message_next_slice_peek;
         public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_status_delegate grpcsharp_batch_context_recv_status_on_client_status;
         public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_details_delegate grpcsharp_batch_context_recv_status_on_client_details;
         public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate grpcsharp_batch_context_recv_status_on_client_trailing_metadata;
@@ -141,7 +141,7 @@ namespace Grpc.Core.Internal
             this.grpcsharp_batch_context_create = GetMethodDelegate<Delegates.grpcsharp_batch_context_create_delegate>(library);
             this.grpcsharp_batch_context_recv_initial_metadata = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_initial_metadata_delegate>(library);
             this.grpcsharp_batch_context_recv_message_length = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_message_length_delegate>(library);
-            this.grpcsharp_batch_context_recv_message_to_buffer = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_message_to_buffer_delegate>(library);
+            this.grpcsharp_batch_context_recv_message_next_slice_peek = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_message_next_slice_peek_delegate>(library);
             this.grpcsharp_batch_context_recv_status_on_client_status = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_status_delegate>(library);
             this.grpcsharp_batch_context_recv_status_on_client_details = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_details_delegate>(library);
             this.grpcsharp_batch_context_recv_status_on_client_trailing_metadata = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate>(library);
@@ -241,7 +241,7 @@ namespace Grpc.Core.Internal
             this.grpcsharp_batch_context_create = DllImportsFromStaticLib.grpcsharp_batch_context_create;
             this.grpcsharp_batch_context_recv_initial_metadata = DllImportsFromStaticLib.grpcsharp_batch_context_recv_initial_metadata;
             this.grpcsharp_batch_context_recv_message_length = DllImportsFromStaticLib.grpcsharp_batch_context_recv_message_length;
-            this.grpcsharp_batch_context_recv_message_to_buffer = DllImportsFromStaticLib.grpcsharp_batch_context_recv_message_to_buffer;
+            this.grpcsharp_batch_context_recv_message_next_slice_peek = DllImportsFromStaticLib.grpcsharp_batch_context_recv_message_next_slice_peek;
             this.grpcsharp_batch_context_recv_status_on_client_status = DllImportsFromStaticLib.grpcsharp_batch_context_recv_status_on_client_status;
             this.grpcsharp_batch_context_recv_status_on_client_details = DllImportsFromStaticLib.grpcsharp_batch_context_recv_status_on_client_details;
             this.grpcsharp_batch_context_recv_status_on_client_trailing_metadata = DllImportsFromStaticLib.grpcsharp_batch_context_recv_status_on_client_trailing_metadata;
@@ -341,7 +341,7 @@ namespace Grpc.Core.Internal
             this.grpcsharp_batch_context_create = DllImportsFromSharedLib.grpcsharp_batch_context_create;
             this.grpcsharp_batch_context_recv_initial_metadata = DllImportsFromSharedLib.grpcsharp_batch_context_recv_initial_metadata;
             this.grpcsharp_batch_context_recv_message_length = DllImportsFromSharedLib.grpcsharp_batch_context_recv_message_length;
-            this.grpcsharp_batch_context_recv_message_to_buffer = DllImportsFromSharedLib.grpcsharp_batch_context_recv_message_to_buffer;
+            this.grpcsharp_batch_context_recv_message_next_slice_peek = DllImportsFromSharedLib.grpcsharp_batch_context_recv_message_next_slice_peek;
             this.grpcsharp_batch_context_recv_status_on_client_status = DllImportsFromSharedLib.grpcsharp_batch_context_recv_status_on_client_status;
             this.grpcsharp_batch_context_recv_status_on_client_details = DllImportsFromSharedLib.grpcsharp_batch_context_recv_status_on_client_details;
             this.grpcsharp_batch_context_recv_status_on_client_trailing_metadata = DllImportsFromSharedLib.grpcsharp_batch_context_recv_status_on_client_trailing_metadata;
@@ -444,7 +444,7 @@ namespace Grpc.Core.Internal
             public delegate BatchContextSafeHandle grpcsharp_batch_context_create_delegate();
             public delegate IntPtr grpcsharp_batch_context_recv_initial_metadata_delegate(BatchContextSafeHandle ctx);
             public delegate IntPtr grpcsharp_batch_context_recv_message_length_delegate(BatchContextSafeHandle ctx);
-            public delegate void grpcsharp_batch_context_recv_message_to_buffer_delegate(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen);
+            public delegate int grpcsharp_batch_context_recv_message_next_slice_peek_delegate(BatchContextSafeHandle ctx, out UIntPtr sliceLen, out IntPtr sliceDataPtr);
             public delegate StatusCode grpcsharp_batch_context_recv_status_on_client_status_delegate(BatchContextSafeHandle ctx);
             public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_details_delegate(BatchContextSafeHandle ctx, out UIntPtr detailsLength);
             public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate(BatchContextSafeHandle ctx);
@@ -482,7 +482,7 @@ namespace Grpc.Core.Internal
             public delegate void grpcsharp_channel_args_set_integer_delegate(ChannelArgsSafeHandle args, UIntPtr index, string key, int value);
             public delegate void grpcsharp_channel_args_destroy_delegate(IntPtr args);
             public delegate void grpcsharp_override_default_ssl_roots_delegate(string pemRootCerts);
-            public delegate ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create_delegate(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey);
+            public delegate ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create_delegate(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey, IntPtr verifyPeerCallbackTag);
             public delegate ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create_delegate(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds);
             public delegate void grpcsharp_channel_credentials_release_delegate(IntPtr credentials);
             public delegate ChannelSafeHandle grpcsharp_insecure_channel_create_delegate(string target, ChannelArgsSafeHandle channelArgs);
@@ -562,7 +562,7 @@ namespace Grpc.Core.Internal
             public static extern IntPtr grpcsharp_batch_context_recv_message_length(BatchContextSafeHandle ctx);
             
             [DllImport(ImportName)]
-            public static extern void grpcsharp_batch_context_recv_message_to_buffer(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen);
+            public static extern int grpcsharp_batch_context_recv_message_next_slice_peek(BatchContextSafeHandle ctx, out UIntPtr sliceLen, out IntPtr sliceDataPtr);
             
             [DllImport(ImportName)]
             public static extern StatusCode grpcsharp_batch_context_recv_status_on_client_status(BatchContextSafeHandle ctx);
@@ -676,7 +676,7 @@ namespace Grpc.Core.Internal
             public static extern void grpcsharp_override_default_ssl_roots(string pemRootCerts);
             
             [DllImport(ImportName)]
-            public static extern ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey);
+            public static extern ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey, IntPtr verifyPeerCallbackTag);
             
             [DllImport(ImportName)]
             public static extern ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds);
@@ -858,7 +858,7 @@ namespace Grpc.Core.Internal
             public static extern IntPtr grpcsharp_batch_context_recv_message_length(BatchContextSafeHandle ctx);
             
             [DllImport(ImportName)]
-            public static extern void grpcsharp_batch_context_recv_message_to_buffer(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen);
+            public static extern int grpcsharp_batch_context_recv_message_next_slice_peek(BatchContextSafeHandle ctx, out UIntPtr sliceLen, out IntPtr sliceDataPtr);
             
             [DllImport(ImportName)]
             public static extern StatusCode grpcsharp_batch_context_recv_status_on_client_status(BatchContextSafeHandle ctx);
@@ -972,7 +972,7 @@ namespace Grpc.Core.Internal
             public static extern void grpcsharp_override_default_ssl_roots(string pemRootCerts);
             
             [DllImport(ImportName)]
-            public static extern ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey);
+            public static extern ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey, IntPtr verifyPeerCallbackTag);
             
             [DllImport(ImportName)]
             public static extern ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds);
diff --git a/src/csharp/Grpc.Core/Internal/ReusableSliceBuffer.cs b/src/csharp/Grpc.Core/Internal/ReusableSliceBuffer.cs
new file mode 100644 (file)
index 0000000..2d38509
--- /dev/null
@@ -0,0 +1,148 @@
+#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
+
+#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
+
+using Grpc.Core.Utils;
+using System;
+using System.Threading;
+
+using System.Buffers;
+
+namespace Grpc.Core.Internal
+{
+    internal class ReusableSliceBuffer
+    {
+        public const int MaxCachedSegments = 1024;  // ~4MB payload for 4K slices
+
+        readonly SliceSegment[] cachedSegments = new SliceSegment[MaxCachedSegments];
+        int populatedSegmentCount;
+
+        public ReadOnlySequence<byte> PopulateFrom(IBufferReader bufferReader)
+        {
+            populatedSegmentCount = 0;
+            long offset = 0;
+            SliceSegment prevSegment = null;
+            while (bufferReader.TryGetNextSlice(out Slice slice))
+            {
+                // Initialize cached segment if still null or just allocate a new segment if we already reached MaxCachedSegments
+                var current = populatedSegmentCount < cachedSegments.Length ? cachedSegments[populatedSegmentCount] : new SliceSegment();
+                if (current == null)
+                {
+                    current = cachedSegments[populatedSegmentCount] = new SliceSegment();
+                }
+
+                current.Reset(slice, offset);
+                prevSegment?.SetNext(current);
+
+                populatedSegmentCount ++;
+                offset += slice.Length;
+                prevSegment = current;
+            }
+
+            // Not necessary for ending the ReadOnlySequence, but for making sure we
+            // don't keep more than MaxCachedSegments alive.
+            prevSegment?.SetNext(null);
+
+            if (populatedSegmentCount == 0)
+            {
+                return ReadOnlySequence<byte>.Empty;
+            }
+
+            var firstSegment = cachedSegments[0];
+            var lastSegment = prevSegment;
+            return new ReadOnlySequence<byte>(firstSegment, 0, lastSegment, lastSegment.Memory.Length);
+        }
+
+        public void Invalidate()
+        {
+            if (populatedSegmentCount == 0)
+            {
+                return;
+            }
+            var segment = cachedSegments[0];
+            while (segment != null)
+            {
+                segment.Reset(new Slice(IntPtr.Zero, 0), 0);
+                var nextSegment = (SliceSegment) segment.Next;
+                segment.SetNext(null);
+                segment = nextSegment;
+            }
+            populatedSegmentCount = 0;
+        }
+
+        // Represents a segment in ReadOnlySequence
+        // Segment is backed by Slice and the instances are reusable.
+        private class SliceSegment : ReadOnlySequenceSegment<byte>
+        {
+            readonly SliceMemoryManager pointerMemoryManager = new SliceMemoryManager();
+
+            public void Reset(Slice slice, long runningIndex)
+            {
+                pointerMemoryManager.Reset(slice);
+                Memory = pointerMemoryManager.Memory;  // maybe not always necessary
+                RunningIndex = runningIndex;
+            }
+
+            public void SetNext(ReadOnlySequenceSegment<byte> next)
+            {
+                Next = next;
+            }        
+        }
+
+        // Allow creating instances of Memory<byte> from Slice.
+        // Represents a chunk of native memory, but doesn't manage its lifetime.
+        // Instances of this class are reuseable - they can be reset to point to a different memory chunk.
+        // That is important to make the instances cacheable (rather then creating new instances
+        // the old ones will be reused to reduce GC pressure).
+        private class SliceMemoryManager : MemoryManager<byte>
+        {
+            private Slice slice;
+
+            public void Reset(Slice slice)
+            {
+                this.slice = slice;
+            }
+
+            public void Reset()
+            {
+                Reset(new Slice(IntPtr.Zero, 0));
+            }
+
+            public override Span<byte> GetSpan()
+            {
+                return slice.ToSpanUnsafe();
+            }
+
+            public override MemoryHandle Pin(int elementIndex = 0)
+            {
+                throw new NotSupportedException();
+            }
+
+            public override void Unpin()
+            {
+            }
+
+            protected override void Dispose(bool disposing)
+            {
+                // NOP
+            }
+        }
+    }
+}
+#endif
diff --git a/src/csharp/Grpc.Core/Internal/Slice.cs b/src/csharp/Grpc.Core/Internal/Slice.cs
new file mode 100644 (file)
index 0000000..22eb953
--- /dev/null
@@ -0,0 +1,68 @@
+#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.InteropServices;
+using System.Threading;
+using Grpc.Core.Utils;
+
+namespace Grpc.Core.Internal
+{
+    /// <summary>
+    /// Slice of native memory.
+    /// Rough equivalent of grpc_slice (but doesn't support inlined slices, just a pointer to data and length)
+    /// </summary>
+    internal struct Slice
+    {
+        private readonly IntPtr dataPtr;
+        private readonly int length;
+     
+        public Slice(IntPtr dataPtr, int length)
+        {
+            this.dataPtr = dataPtr;
+            this.length = length;
+        }
+
+        public int Length => length;
+
+        // copies data of the slice to given span.
+        // there needs to be enough space in the destination buffer
+        public void CopyTo(ArraySegment<byte> destination)
+        {
+            Marshal.Copy(dataPtr, destination.Array, destination.Offset, length);
+        }
+
+#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
+        public Span<byte> ToSpanUnsafe()
+        {
+            unsafe
+            {
+                return new Span<byte>((byte*) dataPtr, length);
+            }
+        }
+#endif
+
+        /// <summary>
+        /// Returns a <see cref="System.String"/> that represents the current <see cref="Grpc.Core.Internal.Slice"/>.
+        /// </summary>
+        public override string ToString()
+        {
+            return string.Format("[Slice: dataPtr={0}, length={1}]", dataPtr, length);
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core/VerifyPeerContext.cs b/src/csharp/Grpc.Core/VerifyPeerContext.cs
new file mode 100644 (file)
index 0000000..b1dc60f
--- /dev/null
@@ -0,0 +1,48 @@
+#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
+
+namespace Grpc.Core
+{
+    /// <summary>
+    /// Verification context for VerifyPeerCallback.
+    /// Note: experimental API that can change or be removed without any prior notice.
+    /// </summary>
+    public class VerifyPeerContext
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="T:Grpc.Core.VerifyPeerContext"/> class.
+        /// </summary>
+        /// <param name="targetName">The target name of the peer.</param>
+        /// <param name="peerPem">The PEM encoded certificate of the peer.</param>
+        internal VerifyPeerContext(string targetName, string peerPem)
+        {
+            this.TargetName = targetName;
+            this.PeerPem = peerPem;
+        }
+
+        /// <summary>
+        /// The target name of the peer.
+        /// </summary>
+        public string TargetName { get; }
+
+        /// <summary>
+        /// The PEM encoded certificate of the peer.
+        /// </summary>
+        public string PeerPem { get; }
+    }
+}
index ba6824d..fab6435 100644 (file)
@@ -67,6 +67,7 @@ namespace Math {
     }
 
     /// <summary>Base class for server-side implementations of Math</summary>
+    [grpc::BindServiceMethod(typeof(Math), "BindService")]
     public abstract partial class MathBase
     {
       /// <summary>
index 3edec5a..7137b90 100644 (file)
@@ -54,6 +54,7 @@ namespace Grpc.Health.V1 {
     }
 
     /// <summary>Base class for server-side implementations of Health</summary>
+    [grpc::BindServiceMethod(typeof(Health), "BindService")]
     public abstract partial class HealthBase
     {
       /// <summary>
index 09691d2..5b37b14 100644 (file)
@@ -74,6 +74,7 @@ namespace Grpc.Testing {
     }
 
     /// <summary>Base class for server-side implementations of BenchmarkService</summary>
+    [grpc::BindServiceMethod(typeof(BenchmarkService), "BindService")]
     public abstract partial class BenchmarkServiceBase
     {
       /// <summary>
index bfa3348..50c6e15 100644 (file)
@@ -39,6 +39,7 @@ namespace Grpc.Testing {
     }
 
     /// <summary>Base class for server-side implementations of EmptyService</summary>
+    [grpc::BindServiceMethod(typeof(EmptyService), "BindService")]
     public abstract partial class EmptyServiceBase
     {
     }
index 27746c0..9b11e99 100644 (file)
@@ -58,6 +58,7 @@ namespace Grpc.Testing {
     }
 
     /// <summary>Base class for server-side implementations of MetricsService</summary>
+    [grpc::BindServiceMethod(typeof(MetricsService), "BindService")]
     public abstract partial class MetricsServiceBase
     {
       /// <summary>
index f92ae8e..1a505eb 100644 (file)
@@ -46,6 +46,7 @@ namespace Grpc.Testing {
     }
 
     /// <summary>Base class for server-side implementations of ReportQpsScenarioService</summary>
+    [grpc::BindServiceMethod(typeof(ReportQpsScenarioService), "BindService")]
     public abstract partial class ReportQpsScenarioServiceBase
     {
       /// <summary>
index b3c47c2..4c11893 100644 (file)
@@ -46,7 +46,8 @@ namespace Grpc.IntegrationTesting
         KeyCertificatePair keyCertPair;
 
         public void InitClientAndServer(bool clientAddKeyCertPair,
-                SslClientCertificateRequestType clientCertRequestType)
+                SslClientCertificateRequestType clientCertRequestType,
+                VerifyPeerCallback verifyPeerCallback = null)
         {
             rootCert = File.ReadAllText(TestCredentials.ClientCertAuthorityPath);
             keyCertPair = new KeyCertificatePair(
@@ -54,7 +55,7 @@ namespace Grpc.IntegrationTesting
                 File.ReadAllText(TestCredentials.ServerPrivateKeyPath));
 
             var serverCredentials = new SslServerCredentials(new[] { keyCertPair }, rootCert, clientCertRequestType);
-            var clientCredentials = clientAddKeyCertPair ? new SslCredentials(rootCert, keyCertPair) : new SslCredentials(rootCert);
+            var clientCredentials = new SslCredentials(rootCert, clientAddKeyCertPair ? keyCertPair : null, verifyPeerCallback);
 
             // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755
             server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) })
@@ -188,6 +189,52 @@ namespace Grpc.IntegrationTesting
             Assert.Throws(typeof(ArgumentNullException), () => new SslServerCredentials(keyCertPairs, null, SslClientCertificateRequestType.RequestAndRequireAndVerify));
         }
 
+        [Test]
+        public async Task VerifyPeerCallback_Accepted()
+        {
+            string targetNameFromCallback = null;
+            string peerPemFromCallback = null;
+            InitClientAndServer(
+                clientAddKeyCertPair: false,
+                clientCertRequestType: SslClientCertificateRequestType.DontRequest,
+                verifyPeerCallback: (ctx) =>
+                {
+                    targetNameFromCallback = ctx.TargetName;
+                    peerPemFromCallback = ctx.PeerPem;
+                    return true;
+                });
+            await CheckAccepted(expectPeerAuthenticated: false);
+            Assert.AreEqual(TestCredentials.DefaultHostOverride, targetNameFromCallback);
+            var expectedServerPem = File.ReadAllText(TestCredentials.ServerCertChainPath).Replace("\r", "");
+            Assert.AreEqual(expectedServerPem, peerPemFromCallback);
+        }
+
+        [Test]
+        public void VerifyPeerCallback_CallbackThrows_Rejected()
+        {
+            InitClientAndServer(
+                clientAddKeyCertPair: false,
+                clientCertRequestType: SslClientCertificateRequestType.DontRequest,
+                verifyPeerCallback: (ctx) =>
+                {
+                    throw new Exception("VerifyPeerCallback has thrown on purpose.");
+                });
+            CheckRejected();
+        }
+
+        [Test]
+        public void VerifyPeerCallback_Rejected()
+        {
+            InitClientAndServer(
+                clientAddKeyCertPair: false,
+                clientCertRequestType: SslClientCertificateRequestType.DontRequest,
+                verifyPeerCallback: (ctx) =>
+                {
+                    return false;
+                });
+            CheckRejected();
+        }
+
         private async Task CheckAccepted(bool expectPeerAuthenticated)
         {
             var call = client.UnaryCallAsync(new SimpleRequest { ResponseSize = 10 });
index d47b5fe..e7b9309 100644 (file)
@@ -105,6 +105,7 @@ namespace Grpc.Testing {
     }
 
     /// <summary>Base class for server-side implementations of TestService</summary>
+    [grpc::BindServiceMethod(typeof(TestService), "BindService")]
     public abstract partial class TestServiceBase
     {
       /// <summary>
@@ -580,6 +581,7 @@ namespace Grpc.Testing {
     }
 
     /// <summary>Base class for server-side implementations of UnimplementedService</summary>
+    [grpc::BindServiceMethod(typeof(UnimplementedService), "BindService")]
     public abstract partial class UnimplementedServiceBase
     {
       /// <summary>
@@ -719,6 +721,7 @@ namespace Grpc.Testing {
     }
 
     /// <summary>Base class for server-side implementations of ReconnectService</summary>
+    [grpc::BindServiceMethod(typeof(ReconnectService), "BindService")]
     public abstract partial class ReconnectServiceBase
     {
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.Empty> Start(global::Grpc.Testing.ReconnectParams request, grpc::ServerCallContext context)
index f7dd2ee..14c26f9 100644 (file)
@@ -72,6 +72,7 @@ namespace Grpc.Testing {
     }
 
     /// <summary>Base class for server-side implementations of WorkerService</summary>
+    [grpc::BindServiceMethod(typeof(WorkerService), "BindService")]
     public abstract partial class WorkerServiceBase
     {
       /// <summary>
index 5007382..f97b314 100644 (file)
@@ -46,6 +46,7 @@ namespace Grpc.Reflection.V1Alpha {
     }
 
     /// <summary>Base class for server-side implementations of ServerReflection</summary>
+    [grpc::BindServiceMethod(typeof(ServerReflection), "BindService")]
     public abstract partial class ServerReflectionBase
     {
       /// <summary>
index 1a86233..b1030ba 100644 (file)
@@ -28,6 +28,7 @@
       <ProtoRoot Condition="'%(Protobuf.ProtoRoot)' == '' " />
       <CompileOutputs Condition="'%(Protobuf.CompileOutputs)' == ''">True</CompileOutputs>
       <OutputDir Condition="'%(Protobuf.OutputDir)' == '' ">$(Protobuf_OutputPath)</OutputDir>
+      <Generator Condition="'%(Protobuf.Generator)' == '' and '$(DisableProtobufDesignTimeBuild)' != 'true' " >MSBuild:Compile</Generator>
     </Protobuf>
   </ItemDefinitionGroup>
 
index 291772f..c01cae0 100644 (file)
@@ -40,6 +40,17 @@ See [Experimentally supported platforms](experimental) for instructions.
 
 See [Experimentally supported platforms](experimental) for instructions.
 
+NUGET DEVELOPMENT FEED (NIGHTLY BUILDS)
+--------------
+
+In production, you should use officially released stable packages available on http://nuget.org, but if you want to test the newest upstream bug fixes and features early, you can can use the development nuget feed where new nuget builds are uploaded nightly.
+
+Feed URL (NuGet v2): https://grpc.jfrog.io/grpc/api/nuget/grpc-nuget-dev
+
+Feed URL (NuGet v3): https://grpc.jfrog.io/grpc/api/nuget/v3/grpc-nuget-dev
+
+The same development nuget packages and packages for other languages can also be found at https://packages.grpc.io/
+
 BUILD FROM SOURCE
 -----------------
 
index 17fb31e..fe28059 100644 (file)
@@ -1,7 +1,7 @@
 <!-- This file is generated -->
 <Project>
   <PropertyGroup>
-    <GrpcCsharpVersion>1.20.1</GrpcCsharpVersion>
+    <GrpcCsharpVersion>1.21.0</GrpcCsharpVersion>
     <GoogleProtobufVersion>3.7.0</GoogleProtobufVersion>
   </PropertyGroup>
 </Project>
index cd77d55..6cfe3a1 100644 (file)
@@ -13,7 +13,7 @@
 @rem limitations under the License.
 
 @rem Current package versions
-set VERSION=1.20.1
+set VERSION=1.21.0
 
 @rem Adjust the location of nuget.exe
 set NUGET=C:\nuget\nuget.exe
index dc690a6..51e498b 100644 (file)
@@ -59,12 +59,16 @@ typedef struct grpcsharp_batch_context {
   } send_status_from_server;
   grpc_metadata_array recv_initial_metadata;
   grpc_byte_buffer* recv_message;
+  grpc_byte_buffer_reader* recv_message_reader;
   struct {
     grpc_metadata_array trailing_metadata;
     grpc_status_code status;
     grpc_slice status_details;
   } recv_status_on_client;
   int recv_close_on_server_cancelled;
+
+  /* reserve space for byte_buffer_reader */
+  grpc_byte_buffer_reader reserved_recv_message_reader;
 } grpcsharp_batch_context;
 
 GPR_EXPORT grpcsharp_batch_context* GPR_CALLTYPE
@@ -206,6 +210,9 @@ grpcsharp_batch_context_reset(grpcsharp_batch_context* ctx) {
 
   grpcsharp_metadata_array_destroy_metadata_only(&(ctx->recv_initial_metadata));
 
+  if (ctx->recv_message_reader) {
+    grpc_byte_buffer_reader_destroy(ctx->recv_message_reader);
+  }
   grpc_byte_buffer_destroy(ctx->recv_message);
 
   grpcsharp_metadata_array_destroy_metadata_only(
@@ -264,27 +271,42 @@ GPR_EXPORT intptr_t GPR_CALLTYPE grpcsharp_batch_context_recv_message_length(
 }
 
 /*
- * Copies data from recv_message to a buffer. Fatal error occurs if
- * buffer is too small.
+ * Gets the next slice from recv_message byte buffer.
+ * Returns 1 if a slice was get successfully, 0 if there are no more slices to
+ * read. Set slice_len to the length of the slice and the slice_data_ptr to
+ * point to slice's data. Caller must ensure that the byte buffer being read
+ * from stays alive as long as the data of the slice are being accessed
+ * (grpc_byte_buffer_reader_peek method is used internally)
+ *
+ * Remarks:
+ * Slices can only be iterated once.
+ * Initializes recv_message_buffer_reader if it was not initialized yet.
  */
-GPR_EXPORT void GPR_CALLTYPE grpcsharp_batch_context_recv_message_to_buffer(
-    const grpcsharp_batch_context* ctx, char* buffer, size_t buffer_len) {
-  grpc_byte_buffer_reader reader;
-  grpc_slice slice;
-  size_t offset = 0;
+GPR_EXPORT int GPR_CALLTYPE
+grpcsharp_batch_context_recv_message_next_slice_peek(
+    grpcsharp_batch_context* ctx, size_t* slice_len, uint8_t** slice_data_ptr) {
+  *slice_len = 0;
+  *slice_data_ptr = NULL;
 
-  GPR_ASSERT(grpc_byte_buffer_reader_init(&reader, ctx->recv_message));
+  if (!ctx->recv_message) {
+    return 0;
+  }
 
-  while (grpc_byte_buffer_reader_next(&reader, &slice)) {
-    size_t len = GRPC_SLICE_LENGTH(slice);
-    GPR_ASSERT(offset + len <= buffer_len);
-    memcpy(buffer + offset, GRPC_SLICE_START_PTR(slice),
-           GRPC_SLICE_LENGTH(slice));
-    offset += len;
-    grpc_slice_unref(slice);
+  if (!ctx->recv_message_reader) {
+    ctx->recv_message_reader = &ctx->reserved_recv_message_reader;
+    GPR_ASSERT(grpc_byte_buffer_reader_init(ctx->recv_message_reader,
+                                            ctx->recv_message));
   }
 
-  grpc_byte_buffer_reader_destroy(&reader);
+  grpc_slice* slice_ptr;
+  if (!grpc_byte_buffer_reader_peek(ctx->recv_message_reader, &slice_ptr)) {
+    return 0;
+  }
+
+  /* recv_message buffer must not be deleted before all the data is read */
+  *slice_len = GRPC_SLICE_LENGTH(*slice_ptr);
+  *slice_data_ptr = GRPC_SLICE_START_PTR(*slice_ptr);
+  return 1;
 }
 
 GPR_EXPORT grpc_status_code GPR_CALLTYPE
@@ -901,6 +923,21 @@ grpcsharp_server_request_call(grpc_server* server, grpc_completion_queue* cq,
                                   &(ctx->request_metadata), cq, cq, ctx);
 }
 
+/* Native callback dispatcher */
+
+typedef int(GPR_CALLTYPE* grpcsharp_native_callback_dispatcher_func)(
+    void* tag, void* arg0, void* arg1, void* arg2, void* arg3, void* arg4,
+    void* arg5);
+
+static grpcsharp_native_callback_dispatcher_func native_callback_dispatcher =
+    NULL;
+
+GPR_EXPORT void GPR_CALLTYPE grpcsharp_native_callback_dispatcher_init(
+    grpcsharp_native_callback_dispatcher_func func) {
+  GPR_ASSERT(func);
+  native_callback_dispatcher = func;
+}
+
 /* Security */
 
 static char* default_pem_root_certs = NULL;
@@ -927,21 +964,47 @@ grpcsharp_override_default_ssl_roots(const char* pem_root_certs) {
   grpc_set_ssl_roots_override_callback(override_ssl_roots_handler);
 }
 
+static void grpcsharp_verify_peer_destroy_handler(void* userdata) {
+  native_callback_dispatcher(userdata, NULL, NULL, (void*)1, NULL, NULL, NULL);
+}
+
+static int grpcsharp_verify_peer_handler(const char* target_name,
+                                         const char* peer_pem, void* userdata) {
+  return native_callback_dispatcher(userdata, (void*)target_name,
+                                    (void*)peer_pem, (void*)0, NULL, NULL,
+                                    NULL);
+}
+
 GPR_EXPORT grpc_channel_credentials* GPR_CALLTYPE
 grpcsharp_ssl_credentials_create(const char* pem_root_certs,
                                  const char* key_cert_pair_cert_chain,
-                                 const char* key_cert_pair_private_key) {
+                                 const char* key_cert_pair_private_key,
+                                 void* verify_peer_callback_tag) {
   grpc_ssl_pem_key_cert_pair key_cert_pair;
+  verify_peer_options verify_options;
+  grpc_ssl_pem_key_cert_pair* key_cert_pair_ptr = NULL;
+  verify_peer_options* verify_options_ptr = NULL;
+
   if (key_cert_pair_cert_chain || key_cert_pair_private_key) {
+    memset(&key_cert_pair, 0, sizeof(key_cert_pair));
     key_cert_pair.cert_chain = key_cert_pair_cert_chain;
     key_cert_pair.private_key = key_cert_pair_private_key;
-    return grpc_ssl_credentials_create(pem_root_certs, &key_cert_pair, NULL,
-                                       NULL);
+    key_cert_pair_ptr = &key_cert_pair;
   } else {
     GPR_ASSERT(!key_cert_pair_cert_chain);
     GPR_ASSERT(!key_cert_pair_private_key);
-    return grpc_ssl_credentials_create(pem_root_certs, NULL, NULL, NULL);
   }
+
+  if (verify_peer_callback_tag != NULL) {
+    memset(&verify_options, 0, sizeof(verify_peer_options));
+    verify_options.verify_peer_callback_userdata = verify_peer_callback_tag;
+    verify_options.verify_peer_destruct = grpcsharp_verify_peer_destroy_handler;
+    verify_options.verify_peer_callback = grpcsharp_verify_peer_handler;
+    verify_options_ptr = &verify_options;
+  }
+
+  return grpc_ssl_credentials_create(pem_root_certs, key_cert_pair_ptr,
+                                     verify_options_ptr, NULL);
 }
 
 GPR_EXPORT void GPR_CALLTYPE
@@ -1010,21 +1073,6 @@ grpcsharp_composite_call_credentials_create(grpc_call_credentials* creds1,
   return grpc_composite_call_credentials_create(creds1, creds2, NULL);
 }
 
-/* Native callback dispatcher */
-
-typedef int(GPR_CALLTYPE* grpcsharp_native_callback_dispatcher_func)(
-    void* tag, void* arg0, void* arg1, void* arg2, void* arg3, void* arg4,
-    void* arg5);
-
-static grpcsharp_native_callback_dispatcher_func native_callback_dispatcher =
-    NULL;
-
-GPR_EXPORT void GPR_CALLTYPE grpcsharp_native_callback_dispatcher_init(
-    grpcsharp_native_callback_dispatcher_func func) {
-  GPR_ASSERT(func);
-  native_callback_dispatcher = func;
-}
-
 /* Metadata credentials plugin */
 
 GPR_EXPORT void GPR_CALLTYPE grpcsharp_metadata_credentials_notify_from_plugin(
index c1e7fc1..cacdb30 100644 (file)
@@ -7,8 +7,12 @@
     "Grpc.Core.Internal.Tests.ChannelArgsSafeHandleTest",
     "Grpc.Core.Internal.Tests.CompletionQueueEventTest",
     "Grpc.Core.Internal.Tests.CompletionQueueSafeHandleTest",
+    "Grpc.Core.Internal.Tests.DefaultDeserializationContextTest",
     "Grpc.Core.Internal.Tests.DefaultObjectPoolTest",
+    "Grpc.Core.Internal.Tests.FakeBufferReaderManagerTest",
     "Grpc.Core.Internal.Tests.MetadataArraySafeHandleTest",
+    "Grpc.Core.Internal.Tests.ReusableSliceBufferTest",
+    "Grpc.Core.Internal.Tests.SliceTest",
     "Grpc.Core.Internal.Tests.TimespecTest",
     "Grpc.Core.Tests.AppDomainUnloadTest",
     "Grpc.Core.Tests.AuthContextTest",
index 0e9d56f..c2f9d20 100644 (file)
@@ -46,7 +46,7 @@ void grpcsharp_batch_context_recv_message_length() {
   fprintf(stderr, "Should never reach here");
   abort();
 }
-void grpcsharp_batch_context_recv_message_to_buffer() {
+void grpcsharp_batch_context_recv_message_next_slice_peek() {
   fprintf(stderr, "Should never reach here");
   abort();
 }
index 558a163..77d6d48 100644 (file)
@@ -42,7 +42,7 @@ Pod::Spec.new do |s|
   # exclamation mark ensures that other "regular" pods will be able to find it as it'll be installed
   # before them.
   s.name     = '!ProtoCompiler-gRPCPlugin'
-  v = '1.20.1'
+  v = '1.21.0'
   s.version  = v
   s.summary  = 'The gRPC ProtoC plugin generates Objective-C files from .proto services.'
   s.description = <<-DESC
index 6669067..a430d5c 100644 (file)
@@ -135,7 +135,8 @@ typedef NS_ENUM(NSUInteger, GRPCErrorCode) {
 
   /**
    * The server is currently unavailable. This is most likely a transient condition and may be
-   * corrected by retrying with a backoff.
+   * corrected by retrying with a backoff. Note that it is not always safe to retry
+   * non-idempotent operations.
    */
   GRPCErrorCodeUnavailable = 14,
 
@@ -183,6 +184,12 @@ extern NSString *const kGRPCTrailersKey;
 - (void)didCloseWithTrailingMetadata:(nullable NSDictionary *)trailingMetadata
                                error:(nullable NSError *)error;
 
+/**
+ * Issued when flow control is enabled for the call and a message written with writeData: method of
+ * GRPCCall2 is passed to gRPC core with SEND_MESSAGE operation.
+ */
+- (void)didWriteData;
+
 @end
 
 /**
@@ -264,6 +271,14 @@ extern NSString *const kGRPCTrailersKey;
 - (void)finish;
 
 /**
+ * Tell gRPC to receive the next N gRPC message from gRPC core.
+ *
+ * This method should only be used when flow control is enabled. When flow control is not enabled,
+ * this method is a no-op.
+ */
+- (void)receiveNextMessages:(NSUInteger)numberOfMessages;
+
+/**
  * Get a copy of the original call options.
  */
 @property(readonly, copy) GRPCCallOptions *callOptions;
index 74a1b47..495f942 100644 (file)
@@ -63,6 +63,15 @@ const char *kCFStreamVarName = "grpc_cfstream";
               requestsWriter:(GRXWriter *)requestsWriter
                  callOptions:(GRPCCallOptions *)callOptions;
 
+- (instancetype)initWithHost:(NSString *)host
+                        path:(NSString *)path
+                  callSafety:(GRPCCallSafety)safety
+              requestsWriter:(GRXWriter *)requestsWriter
+                 callOptions:(GRPCCallOptions *)callOptions
+                   writeDone:(void (^)(void))writeDone;
+
+- (void)receiveNextMessages:(NSUInteger)numberOfMessages;
+
 @end
 
 @implementation GRPCRequestOptions
@@ -113,6 +122,8 @@ const char *kCFStreamVarName = "grpc_cfstream";
   BOOL _canceled;
   /** Flags whether call has been finished. */
   BOOL _finished;
+  /** The number of pending messages receiving requests. */
+  NSUInteger _pendingReceiveNextMessages;
 }
 
 - (instancetype)initWithRequestOptions:(GRPCRequestOptions *)requestOptions
@@ -190,10 +201,22 @@ const char *kCFStreamVarName = "grpc_cfstream";
                                       path:_requestOptions.path
                                 callSafety:_requestOptions.safety
                             requestsWriter:_pipe
-                               callOptions:_callOptions];
+                               callOptions:_callOptions
+                                 writeDone:^{
+                                   @synchronized(self) {
+                                     if (self->_handler) {
+                                       [self issueDidWriteData];
+                                     }
+                                   }
+                                 }];
+    [_call setResponseDispatchQueue:_dispatchQueue];
     if (_callOptions.initialMetadata) {
       [_call.requestHeaders addEntriesFromDictionary:_callOptions.initialMetadata];
     }
+    if (_pendingReceiveNextMessages > 0) {
+      [_call receiveNextMessages:_pendingReceiveNextMessages];
+      _pendingReceiveNextMessages = 0;
+    }
     copiedCall = _call;
   }
 
@@ -363,6 +386,33 @@ const char *kCFStreamVarName = "grpc_cfstream";
   }
 }
 
+- (void)issueDidWriteData {
+  @synchronized(self) {
+    if (_callOptions.flowControlEnabled && [_handler respondsToSelector:@selector(didWriteData)]) {
+      dispatch_async(_dispatchQueue, ^{
+        id<GRPCResponseHandler> copiedHandler = nil;
+        @synchronized(self) {
+          copiedHandler = self->_handler;
+        };
+        [copiedHandler didWriteData];
+      });
+    }
+  }
+}
+
+- (void)receiveNextMessages:(NSUInteger)numberOfMessages {
+  // branching based on _callOptions.flowControlEnabled is handled inside _call
+  GRPCCall *copiedCall = nil;
+  @synchronized(self) {
+    copiedCall = _call;
+    if (copiedCall == nil) {
+      _pendingReceiveNextMessages += numberOfMessages;
+      return;
+    }
+  }
+  [copiedCall receiveNextMessages:numberOfMessages];
+}
+
 @end
 
 // The following methods of a C gRPC call object aren't reentrant, and thus
@@ -426,6 +476,15 @@ const char *kCFStreamVarName = "grpc_cfstream";
 
   // The OAuth2 token fetched from a token provider.
   NSString *_fetchedOauth2AccessToken;
+
+  // The callback to be called when a write message op is done.
+  void (^_writeDone)(void);
+
+  // Indicate a read request to core is pending.
+  BOOL _pendingCoreRead;
+
+  // Indicate pending read message request from user.
+  NSUInteger _pendingReceiveNextMessages;
 }
 
 @synthesize state = _state;
@@ -434,6 +493,9 @@ const char *kCFStreamVarName = "grpc_cfstream";
   // Guarantees the code in {} block is invoked only once. See ref at:
   // https://developer.apple.com/documentation/objectivec/nsobject/1418639-initialize?language=objc
   if (self == [GRPCCall self]) {
+    // Enable CFStream by default by do not overwrite if the user explicitly disables CFStream with
+    // environment variable "grpc_cfstream=0"
+    setenv(kCFStreamVarName, "1", 0);
     grpc_init();
     callFlags = [NSMutableDictionary dictionary];
   }
@@ -482,12 +544,26 @@ const char *kCFStreamVarName = "grpc_cfstream";
 - (instancetype)initWithHost:(NSString *)host
                         path:(NSString *)path
                   callSafety:(GRPCCallSafety)safety
-              requestsWriter:(GRXWriter *)requestWriter
+              requestsWriter:(GRXWriter *)requestsWriter
                  callOptions:(GRPCCallOptions *)callOptions {
+  return [self initWithHost:host
+                       path:path
+                 callSafety:safety
+             requestsWriter:requestsWriter
+                callOptions:callOptions
+                  writeDone:nil];
+}
+
+- (instancetype)initWithHost:(NSString *)host
+                        path:(NSString *)path
+                  callSafety:(GRPCCallSafety)safety
+              requestsWriter:(GRXWriter *)requestsWriter
+                 callOptions:(GRPCCallOptions *)callOptions
+                   writeDone:(void (^)(void))writeDone {
   // Purposely using pointer rather than length (host.length == 0) for backwards compatibility.
   NSAssert(host != nil && path != nil, @"Neither host nor path can be nil.");
   NSAssert(safety <= GRPCCallSafetyCacheableRequest, @"Invalid call safety value.");
-  NSAssert(requestWriter.state == GRXWriterStateNotStarted,
+  NSAssert(requestsWriter.state == GRXWriterStateNotStarted,
            @"The requests writer can't be already started.");
   if (!host || !path) {
     return nil;
@@ -495,7 +571,7 @@ const char *kCFStreamVarName = "grpc_cfstream";
   if (safety > GRPCCallSafetyCacheableRequest) {
     return nil;
   }
-  if (requestWriter.state != GRXWriterStateNotStarted) {
+  if (requestsWriter.state != GRXWriterStateNotStarted) {
     return nil;
   }
 
@@ -508,16 +584,20 @@ const char *kCFStreamVarName = "grpc_cfstream";
     // Serial queue to invoke the non-reentrant methods of the grpc_call object.
     _callQueue = dispatch_queue_create("io.grpc.call", DISPATCH_QUEUE_SERIAL);
 
-    _requestWriter = requestWriter;
-
+    _requestWriter = requestsWriter;
     _requestHeaders = [[GRPCRequestHeaders alloc] initWithCall:self];
+    _writeDone = writeDone;
 
-    if ([requestWriter isKindOfClass:[GRXImmediateSingleWriter class]]) {
+    if ([requestsWriter isKindOfClass:[GRXImmediateSingleWriter class]]) {
       _unaryCall = YES;
       _unaryOpBatch = [NSMutableArray arrayWithCapacity:kMaxClientBatch];
     }
 
     _responseQueue = dispatch_get_main_queue();
+
+    // do not start a read until initial metadata is received
+    _pendingReceiveNextMessages = 0;
+    _pendingCoreRead = YES;
   }
   return self;
 }
@@ -589,11 +669,16 @@ const char *kCFStreamVarName = "grpc_cfstream";
 // If the call is currently paused, this is a noop. Restarting the call will invoke this
 // method.
 // TODO(jcanizales): Rename to readResponseIfNotPaused.
-- (void)startNextRead {
+- (void)maybeStartNextRead {
   @synchronized(self) {
     if (_state != GRXWriterStateStarted) {
       return;
     }
+    if (_callOptions.flowControlEnabled && (_pendingCoreRead || _pendingReceiveNextMessages == 0)) {
+      return;
+    }
+    _pendingCoreRead = YES;
+    _pendingReceiveNextMessages--;
   }
 
   dispatch_async(_callQueue, ^{
@@ -616,6 +701,7 @@ const char *kCFStreamVarName = "grpc_cfstream";
         // that's on the hands of any server to have. Instead we finish and ask
         // the server to cancel.
         @synchronized(strongSelf) {
+          strongSelf->_pendingCoreRead = NO;
           [strongSelf
               finishWithError:[NSError errorWithDomain:kGRPCErrorDomain
                                                   code:GRPCErrorCodeResourceExhausted
@@ -631,7 +717,13 @@ const char *kCFStreamVarName = "grpc_cfstream";
         @synchronized(strongSelf) {
           [strongSelf->_responseWriteable enqueueValue:data
                                      completionHandler:^{
-                                       [strongSelf startNextRead];
+                                       __strong GRPCCall *strongSelf = weakSelf;
+                                       if (strongSelf) {
+                                         @synchronized(strongSelf) {
+                                           strongSelf->_pendingCoreRead = NO;
+                                           [strongSelf maybeStartNextRead];
+                                         }
+                                       }
                                      }];
         }
       }
@@ -682,6 +774,20 @@ const char *kCFStreamVarName = "grpc_cfstream";
   });
 }
 
+- (void)receiveNextMessages:(NSUInteger)numberOfMessages {
+  if (numberOfMessages == 0) {
+    return;
+  }
+  @synchronized(self) {
+    _pendingReceiveNextMessages += numberOfMessages;
+
+    if (_state != GRXWriterStateStarted || !_callOptions.flowControlEnabled) {
+      return;
+    }
+    [self maybeStartNextRead];
+  }
+}
+
 #pragma mark GRXWriteable implementation
 
 // Only called from the call queue. The error handler will be called from the
@@ -695,9 +801,11 @@ const char *kCFStreamVarName = "grpc_cfstream";
     GRPCCall *strongSelf = weakSelf;
     if (strongSelf) {
       strongSelf->_requestWriter.state = GRXWriterStateStarted;
+      if (strongSelf->_writeDone) {
+        strongSelf->_writeDone();
+      }
     }
   };
-
   GRPCOpSendMessage *op =
       [[GRPCOpSendMessage alloc] initWithMessage:message handler:resumingHandler];
   if (!_unaryCall) {
@@ -774,8 +882,11 @@ const char *kCFStreamVarName = "grpc_cfstream";
     // Response headers received.
     __strong GRPCCall *strongSelf = weakSelf;
     if (strongSelf) {
-      strongSelf.responseHeaders = headers;
-      [strongSelf startNextRead];
+      @synchronized(strongSelf) {
+        strongSelf.responseHeaders = headers;
+        strongSelf->_pendingCoreRead = NO;
+        [strongSelf maybeStartNextRead];
+      }
     }
   }
       completionHandler:^(NSError *error, NSDictionary *trailers) {
@@ -929,7 +1040,7 @@ const char *kCFStreamVarName = "grpc_cfstream";
       case GRXWriterStateStarted:
         if (_state == GRXWriterStatePaused) {
           _state = newState;
-          [self startNextRead];
+          [self maybeStartNextRead];
         }
         return;
       case GRXWriterStateNotStarted:
index b5bf4c9..9f77652 100644 (file)
@@ -90,6 +90,14 @@ typedef NS_ENUM(NSUInteger, GRPCTransportType) {
  */
 @property(readonly) NSTimeInterval timeout;
 
+/**
+ * Enable flow control of a gRPC call. The option is default to NO. If set to YES, writeData: method
+ * should only be called at most once before a didWriteData callback is issued, and
+ * receiveNextMessage: must be called each time before gRPC call issues a didReceiveMessage
+ * callback.
+ */
+@property(readonly) BOOL flowControlEnabled;
+
 // OAuth2 parameters. Users of gRPC may specify one of the following two parameters.
 
 /**
@@ -232,6 +240,19 @@ typedef NS_ENUM(NSUInteger, GRPCTransportType) {
  */
 @property(readwrite) NSTimeInterval timeout;
 
+/**
+ * Enable flow control of a gRPC call. The option is default to NO. If set to YES, writeData: method
+ * should only be called at most once before a didWriteData callback is issued, and
+ * receiveNextMessage: must be called each time before gRPC call can issue a didReceiveMessage
+ * callback.
+ *
+ * If writeData: method is called more than once before issuance of a didWriteData callback, gRPC
+ * will continue to queue the message and write them to gRPC core in order. However, the user
+ * assumes their own responsibility of flow control by keeping tracking of the pending writes in
+ * the call.
+ */
+@property(readwrite) BOOL flowControlEnabled;
+
 // OAuth2 parameters. Users of gRPC may specify one of the following two parameters.
 
 /**
index e59a812..e576641 100644 (file)
@@ -22,6 +22,7 @@
 // The default values for the call options.
 static NSString *const kDefaultServerAuthority = nil;
 static const NSTimeInterval kDefaultTimeout = 0;
+static const BOOL kDefaultFlowControlEnabled = NO;
 static NSDictionary *const kDefaultInitialMetadata = nil;
 static NSString *const kDefaultUserAgentPrefix = nil;
 static const NSUInteger kDefaultResponseSizeLimit = 0;
@@ -59,6 +60,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
  @protected
   NSString *_serverAuthority;
   NSTimeInterval _timeout;
+  BOOL _flowControlEnabled;
   NSString *_oauth2AccessToken;
   id<GRPCAuthorizationProtocol> _authTokenProvider;
   NSDictionary *_initialMetadata;
@@ -84,6 +86,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
 
 @synthesize serverAuthority = _serverAuthority;
 @synthesize timeout = _timeout;
+@synthesize flowControlEnabled = _flowControlEnabled;
 @synthesize oauth2AccessToken = _oauth2AccessToken;
 @synthesize authTokenProvider = _authTokenProvider;
 @synthesize initialMetadata = _initialMetadata;
@@ -109,6 +112,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
 - (instancetype)init {
   return [self initWithServerAuthority:kDefaultServerAuthority
                                timeout:kDefaultTimeout
+                    flowControlEnabled:kDefaultFlowControlEnabled
                      oauth2AccessToken:kDefaultOauth2AccessToken
                      authTokenProvider:kDefaultAuthTokenProvider
                        initialMetadata:kDefaultInitialMetadata
@@ -134,6 +138,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
 
 - (instancetype)initWithServerAuthority:(NSString *)serverAuthority
                                 timeout:(NSTimeInterval)timeout
+                     flowControlEnabled:(BOOL)flowControlEnabled
                       oauth2AccessToken:(NSString *)oauth2AccessToken
                       authTokenProvider:(id<GRPCAuthorizationProtocol>)authTokenProvider
                         initialMetadata:(NSDictionary *)initialMetadata
@@ -158,6 +163,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
   if ((self = [super init])) {
     _serverAuthority = [serverAuthority copy];
     _timeout = timeout < 0 ? 0 : timeout;
+    _flowControlEnabled = flowControlEnabled;
     _oauth2AccessToken = [oauth2AccessToken copy];
     _authTokenProvider = authTokenProvider;
     _initialMetadata =
@@ -193,6 +199,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
   GRPCCallOptions *newOptions =
       [[GRPCCallOptions allocWithZone:zone] initWithServerAuthority:_serverAuthority
                                                             timeout:_timeout
+                                                 flowControlEnabled:_flowControlEnabled
                                                   oauth2AccessToken:_oauth2AccessToken
                                                   authTokenProvider:_authTokenProvider
                                                     initialMetadata:_initialMetadata
@@ -221,6 +228,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
   GRPCMutableCallOptions *newOptions = [[GRPCMutableCallOptions allocWithZone:zone]
       initWithServerAuthority:[_serverAuthority copy]
                       timeout:_timeout
+           flowControlEnabled:_flowControlEnabled
             oauth2AccessToken:[_oauth2AccessToken copy]
             authTokenProvider:_authTokenProvider
               initialMetadata:[[NSDictionary alloc] initWithDictionary:_initialMetadata
@@ -301,6 +309,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
 
 @dynamic serverAuthority;
 @dynamic timeout;
+@dynamic flowControlEnabled;
 @dynamic oauth2AccessToken;
 @dynamic authTokenProvider;
 @dynamic initialMetadata;
@@ -326,6 +335,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
 - (instancetype)init {
   return [self initWithServerAuthority:kDefaultServerAuthority
                                timeout:kDefaultTimeout
+                    flowControlEnabled:kDefaultFlowControlEnabled
                      oauth2AccessToken:kDefaultOauth2AccessToken
                      authTokenProvider:kDefaultAuthTokenProvider
                        initialMetadata:kDefaultInitialMetadata
@@ -353,6 +363,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
   GRPCCallOptions *newOptions =
       [[GRPCCallOptions allocWithZone:zone] initWithServerAuthority:_serverAuthority
                                                             timeout:_timeout
+                                                 flowControlEnabled:_flowControlEnabled
                                                   oauth2AccessToken:_oauth2AccessToken
                                                   authTokenProvider:_authTokenProvider
                                                     initialMetadata:_initialMetadata
@@ -381,6 +392,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
   GRPCMutableCallOptions *newOptions = [[GRPCMutableCallOptions allocWithZone:zone]
       initWithServerAuthority:_serverAuthority
                       timeout:_timeout
+           flowControlEnabled:_flowControlEnabled
             oauth2AccessToken:_oauth2AccessToken
             authTokenProvider:_authTokenProvider
               initialMetadata:_initialMetadata
@@ -417,6 +429,10 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
   }
 }
 
+- (void)setFlowControlEnabled:(BOOL)flowControlEnabled {
+  _flowControlEnabled = flowControlEnabled;
+}
+
 - (void)setOauth2AccessToken:(NSString *)oauth2AccessToken {
   _oauth2AccessToken = [oauth2AccessToken copy];
 }
index b1a6797..e6522d7 100644 (file)
                                error:(NSError **)errorPtr {
   static dispatch_once_t loading;
   dispatch_once(&loading, ^{
-    NSString *defaultPath = @"gRPCCertificates.bundle/roots";  // .pem
+    NSString *rootsPEM = @"roots";
+    NSString *resourceBundlePath = @"gRPCCertificates.bundle";  // .pem
     // Do not use NSBundle.mainBundle, as it's nil for tests of library projects.
     NSBundle *bundle = [NSBundle bundleForClass:[self class]];
-    NSString *path = [bundle pathForResource:defaultPath ofType:@"pem"];
-    setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR,
-           [path cStringUsingEncoding:NSUTF8StringEncoding], 1);
+    NSBundle *resourceBundle = [NSBundle
+        bundleWithURL:[[bundle resourceURL] URLByAppendingPathComponent:resourceBundlePath]];
+    NSString *path = [resourceBundle pathForResource:rootsPEM ofType:@"pem"];
+    setenv("GRPC_DEFAULT_SSL_ROOTS_FILE_PATH", [path cStringUsingEncoding:NSUTF8StringEncoding], 1);
   });
 
   NSData *rootsASCII = nil;
index 2f62440..4686dc2 100644 (file)
@@ -22,4 +22,4 @@
 // instead. This file can be regenerated from the template by running
 // `tools/buildgen/generate_projects.sh`.
 
-#define GRPC_OBJC_VERSION_STRING @"1.20.1"
+#define GRPC_OBJC_VERSION_STRING @"1.21.0"
index 8ce3421..12db46a 100644 (file)
@@ -57,6 +57,13 @@ NS_ASSUME_NONNULL_BEGIN
 - (void)didCloseWithTrailingMetadata:(nullable NSDictionary *)trailingMetadata
                                error:(nullable NSError *)error;
 
+/**
+ * Issued when flow control is enabled for the call and a message (written with writeMessage: method
+ * of GRPCStreamingProtoCall or the initializer of GRPCUnaryProtoCall) is passed to gRPC core with
+ * SEND_MESSAGE operation.
+ */
+- (void)didWriteMessage;
+
 @end
 
 /** A unary-request RPC call with Protobuf. */
@@ -130,6 +137,26 @@ NS_ASSUME_NONNULL_BEGIN
  */
 - (void)finish;
 
+/**
+ * Tell gRPC to receive another message.
+ *
+ * This method should only be used when flow control is enabled. If flow control is enabled, gRPC
+ * will only receive additional messages after the user indicates so by using either
+ * receiveNextMessage: or receiveNextMessages: methods. If flow control is not enabled, messages
+ * will be automatically received after the previous one is delivered.
+ */
+- (void)receiveNextMessage;
+
+/**
+ * Tell gRPC to receive another N message.
+ *
+ * This method should only be used when flow control is enabled. If flow control is enabled, the
+ * messages received from the server are buffered in gRPC until the user want to receive the next
+ * message. If flow control is not enabled, messages will be automatically received after the
+ * previous one is delivered.
+ */
+- (void)receiveNextMessages:(NSUInteger)numberOfMessages;
+
 @end
 
 NS_ASSUME_NONNULL_END
index 0ab96a5..80988be 100644 (file)
@@ -72,6 +72,7 @@ static NSError *ErrorForBadProto(id proto, Class expectedClass, NSError *parsing
 
 - (void)start {
   [_call start];
+  [_call receiveNextMessage];
   [_call writeMessage:_message];
   [_call finish];
 }
@@ -197,6 +198,17 @@ static NSError *ErrorForBadProto(id proto, Class expectedClass, NSError *parsing
   [copiedCall finish];
 }
 
+- (void)receiveNextMessage {
+  [self receiveNextMessages:1];
+}
+- (void)receiveNextMessages:(NSUInteger)numberOfMessages {
+  GRPCCall2 *copiedCall;
+  @synchronized(self) {
+    copiedCall = _call;
+  }
+  [copiedCall receiveNextMessages:numberOfMessages];
+}
+
 - (void)didReceiveInitialMetadata:(NSDictionary *)initialMetadata {
   @synchronized(self) {
     if (initialMetadata != nil &&
@@ -260,6 +272,20 @@ static NSError *ErrorForBadProto(id proto, Class expectedClass, NSError *parsing
   }
 }
 
+- (void)didWriteData {
+  @synchronized(self) {
+    if ([_handler respondsToSelector:@selector(didWriteMessage)]) {
+      dispatch_async(_dispatchQueue, ^{
+        id<GRPCProtoResponseHandler> copiedHandler = nil;
+        @synchronized(self) {
+          copiedHandler = self->_handler;
+        }
+        [copiedHandler didWriteMessage];
+      });
+    }
+  }
+}
+
 - (dispatch_queue_t)dispatchQueue {
   return _dispatchQueue;
 }
index 0cb25ab..184d567 100644 (file)
@@ -6,24 +6,19 @@ sockets) for networking. Using CFStream resolves a bunch of network connectivity
 (see the [doc](https://github.com/grpc/grpc/blob/master/src/objective-c/NetworkTransitionBehavior.md)
 for more information).
 
-CFStream integration is now in experimental state. You will need explicit opt-in to use it to get
+<s>CFStream integration is now in experimental state. You will need explicit opt-in to use it to get
 the benefits of resolving the issues above. We expect to make CFStream the default networking
-interface that gRPC uses when it is ready for production.
+interface that gRPC uses when it is ready for production.</s>
 
-## Usage
-If you use gRPC following the instructions in
-[README.md](https://github.com/grpc/grpc/blob/master/src/objective-c/README.md):
-- Replace the
-dependency on `gRPC-ProtoRPC` with `gRPC-ProtoRPC/CFStream`.
-- Enable CFStream with environment variable `grpc_cfstream=1`. This can be done either in Xcode
-  console or by your code with `setenv()` before gRPC is initialized.
-
-If your project directly depends on podspecs other than `gRPC-ProtoRPC` (e.g. `gRPC` or
-`gRPC-Core`):
+As of v1.21.0, CFStream integration is now the default networking stack being used by gRPC
+Objective-C on iOS layer. You get to use it automatically without special configuration needed. See
+below on how to disable CFStream in case of problem.
 
-- Make your projects depend on subspecs corresponding to CFStream in each gRPC podspec.
-- Enable CFStream with environment variable `grpc_cfstream=1`. This can be done either in Xcode
-  console or by your code with `setenv()` before gRPC is initialized.
+## Usage
+If you use gRPC Objective-C library on iOS, CFStream is on automatically. If you use it on other
+platforms, you can turn it on with macro `GRPC_CFSTREAM=1` for the pod 'gRPC-Core' and 'gRPC'. In
+case of problem and you want to disable CFStream on iOS, you can set environment variable
+"grpc\_cfstream=0".
 
 ## Notes
 
diff --git a/src/objective-c/manual_tests/GrpcIosTest.xcodeproj/xcshareddata/xcschemes/GrpcIosTest.xcscheme b/src/objective-c/manual_tests/GrpcIosTest.xcodeproj/xcshareddata/xcschemes/GrpcIosTest.xcscheme
new file mode 100644 (file)
index 0000000..b9fb9d4
--- /dev/null
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1010"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "5EDA907A220DF0BC0046D27A"
+               BuildableName = "GrpcIosTest.app"
+               BlueprintName = "GrpcIosTest"
+               ReferencedContainer = "container:GrpcIosTest.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "B0C18CA3222DEF140002B502"
+               BuildableName = "GrpcIosTestUITests.xctest"
+               BlueprintName = "GrpcIosTestUITests"
+               ReferencedContainer = "container:GrpcIosTest.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "5EDA907A220DF0BC0046D27A"
+            BuildableName = "GrpcIosTest.app"
+            BlueprintName = "GrpcIosTest"
+            ReferencedContainer = "container:GrpcIosTest.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "5EDA907A220DF0BC0046D27A"
+            BuildableName = "GrpcIosTest.app"
+            BlueprintName = "GrpcIosTest"
+            ReferencedContainer = "container:GrpcIosTest.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "5EDA907A220DF0BC0046D27A"
+            BuildableName = "GrpcIosTest.app"
+            BlueprintName = "GrpcIosTest"
+            ReferencedContainer = "container:GrpcIosTest.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
index b0a929e..a75b632 100644 (file)
@@ -19,6 +19,7 @@
 #import <XCTest/XCTest.h>
 
 NSTimeInterval const kWaitTime = 30;
+int const kNumIterations = 1;
 
 @interface GrpcIosTestUITests : XCTestCase
 @end
@@ -32,16 +33,27 @@ NSTimeInterval const kWaitTime = 30;
   self.continueAfterFailure = NO;
   [[[XCUIApplication alloc] init] launch];
   testApp = [[XCUIApplication alloc] initWithBundleIdentifier:@"io.grpc.GrpcIosTest"];
+  [testApp activate];
+  // Reset RPC counter
+  [self pressButton:@"Reset counter"];
+
   settingsApp = [[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.Preferences"];
   [settingsApp activate];
+  [NSThread sleepForTimeInterval:1];
   // Go back to the first page of Settings.
   XCUIElement *backButton = settingsApp.navigationBars.buttons.firstMatch;
-  while (backButton.exists) {
+  while (backButton.exists && backButton.isHittable) {
+    NSLog(@"Tapping back button");
     [backButton tap];
   }
   XCTAssert([settingsApp.navigationBars[@"Settings"] waitForExistenceWithTimeout:kWaitTime]);
+  NSLog(@"Turning off airplane mode");
   // Turn off airplane mode
   [self setAirplaneMode:NO];
+
+  // Turn on wifi
+  NSLog(@"Turning on wifi");
+  [self setWifi:YES];
 }
 
 - (void)tearDown {
@@ -49,14 +61,25 @@ NSTimeInterval const kWaitTime = 30;
 
 - (void)doUnaryCall {
   [testApp activate];
-  [testApp.buttons[@"Unary call"] tap];
+  [self pressButton:@"Unary call"];
 }
 
-- (void)doStreamingCall {
+- (void)do10UnaryCalls {
   [testApp activate];
-  [testApp.buttons[@"Start streaming call"] tap];
-  [testApp.buttons[@"Send Message"] tap];
-  [testApp.buttons[@"Stop streaming call"] tap];
+  [self pressButton:@"10 Unary calls"];
+}
+
+- (void)pressButton:(NSString *)name {
+  // Wait for button to be visible
+  while (![testApp.buttons[name] exists] || ![testApp.buttons[name] isHittable]) {
+    [NSThread sleepForTimeInterval:1];
+  }
+  // Wait until all events in run loop have been processed
+  while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true) == kCFRunLoopRunHandledSource)
+    ;
+
+  NSLog(@"Pressing button: %@", name);
+  [testApp.buttons[name] tap];
 }
 
 - (void)expectCallSuccess {
@@ -67,45 +90,87 @@ NSTimeInterval const kWaitTime = 30;
   XCTAssert([testApp.staticTexts[@"Call failed"] waitForExistenceWithTimeout:kWaitTime]);
 }
 
+- (void)expectCallSuccessOrFailed {
+  NSDate *startTime = [NSDate date];
+  while (![testApp.staticTexts[@"Call done"] exists] &&
+         ![testApp.staticTexts[@"Call failed"] exists]) {
+    XCTAssertLessThan([[NSDate date] timeIntervalSinceDate:startTime], kWaitTime);
+    [NSThread sleepForTimeInterval:1];
+  }
+}
+
 - (void)setAirplaneMode:(BOOL)to {
   [settingsApp activate];
   XCUIElement *mySwitch = settingsApp.tables.element.cells.switches[@"Airplane Mode"];
   BOOL from = [(NSString *)mySwitch.value boolValue];
+  NSLog(@"Setting airplane from: %d to: %d", from, to);
   if (from != to) {
     [mySwitch tap];
-    // wait for gRPC to detect the change
-    sleep(10);
+    // wait for network change to finish
+    [NSThread sleepForTimeInterval:5];
   }
   XCTAssert([(NSString *)mySwitch.value boolValue] == to);
 }
+- (void)setWifi:(BOOL)to {
+  [settingsApp activate];
+  [settingsApp.tables.element.cells.staticTexts[@"Wi-Fi"] tap];
+  XCUIElement *wifiSwitch = settingsApp.tables.cells.switches[@"Wi-Fi"];
+  BOOL from = [(NSString *)wifiSwitch.value boolValue];
+  NSLog(@"Setting wifi from: %d to: %d", from, to);
+  if (from != to) {
+    [wifiSwitch tap];
+    // wait for wifi networks to be detected
+    [NSThread sleepForTimeInterval:10];
+  }
+  // Go back to the first page of Settings.
+  XCUIElement *backButton = settingsApp.navigationBars.buttons.firstMatch;
+  [backButton tap];
+}
 
-- (void)testBackgroundBeforeUnaryCall {
+- (int)getRandomNumberBetween:(int)min max:(int)max {
+  return min + arc4random_uniform((max - min + 1));
+}
+
+- (void)testBackgroundBeforeCall {
+  NSLog(@"%s", __func__);
   // Open test app
   [testApp activate];
-
   // Send test app to background
   [XCUIDevice.sharedDevice pressButton:XCUIDeviceButtonHome];
-  sleep(5);
+
+  // Wait a bit
+  int sleepTime = [self getRandomNumberBetween:5 max:10];
+  NSLog(@"Sleeping for %d seconds", sleepTime);
+  [NSThread sleepForTimeInterval:sleepTime];
 
   // Bring test app to foreground and make a unary call. Call should succeed
   [self doUnaryCall];
   [self expectCallSuccess];
 }
 
-- (void)testBackgroundBeforeStreamingCall {
-  // Open test app
+- (void)testBackgroundDuringStreamingCall {
+  NSLog(@"%s", __func__);
+  // Open test app and start a streaming call
   [testApp activate];
+  [self pressButton:@"Start streaming call"];
 
   // Send test app to background
   [XCUIDevice.sharedDevice pressButton:XCUIDeviceButtonHome];
-  sleep(5);
+
+  // Wait a bit
+  int sleepTime = [self getRandomNumberBetween:5 max:10];
+  NSLog(@"Sleeping for %d seconds", sleepTime);
+  [NSThread sleepForTimeInterval:sleepTime];
 
   // Bring test app to foreground and make a streaming call. Call should succeed.
-  [self doStreamingCall];
+  [testApp activate];
+  [self pressButton:@"Send Message"];
+  [self pressButton:@"Stop streaming call"];
   [self expectCallSuccess];
 }
 
-- (void)testUnaryCallAfterNetworkFlap {
+- (void)testCallAfterNetworkFlap {
+  NSLog(@"%s", __func__);
   // Open test app and make a unary call. Channel to server should be open after this.
   [self doUnaryCall];
   [self expectCallSuccess];
@@ -119,56 +184,148 @@ NSTimeInterval const kWaitTime = 30;
   [self expectCallSuccess];
 }
 
-- (void)testStreamingCallAfterNetworkFlap {
+- (void)testCallWhileNetworkDown {
+  NSLog(@"%s", __func__);
   // Open test app and make a unary call. Channel to server should be open after this.
   [self doUnaryCall];
   [self expectCallSuccess];
 
-  // Toggle airplane mode on and off
+  // Turn on airplane mode
   [self setAirplaneMode:YES];
+  // Turn off wifi
+  [self setWifi:NO];
+
+  // Unary call should fail
+  [self doUnaryCall];
+  [self expectCallFailed];
+
+  // Turn off airplane mode
   [self setAirplaneMode:NO];
+  // Turn on wifi
+  [self setWifi:YES];
 
-  [self doStreamingCall];
+  // Unary call should succeed
+  [self doUnaryCall];
   [self expectCallSuccess];
 }
 
-- (void)testUnaryCallWhileNetworkDown {
+- (void)testSwitchApp {
+  NSLog(@"%s", __func__);
   // Open test app and make a unary call. Channel to server should be open after this.
   [self doUnaryCall];
   [self expectCallSuccess];
 
-  // Turn on airplane mode
+  // Send test app to background
+  [XCUIDevice.sharedDevice pressButton:XCUIDeviceButtonHome];
+
+  // Open stocks app
+  XCUIApplication *stocksApp =
+      [[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.stocks"];
+  [stocksApp activate];
+  // Ensure that stocks app is running in the foreground
+  XCTAssert([stocksApp waitForState:XCUIApplicationStateRunningForeground timeout:5]);
+  // Wait a bit
+  int sleepTime = [self getRandomNumberBetween:5 max:10];
+  NSLog(@"Sleeping for %d seconds", sleepTime);
+  [NSThread sleepForTimeInterval:sleepTime];
+  [stocksApp terminate];
+
+  // Make another unary call
+  [self doUnaryCall];
+  [self expectCallSuccess];
+}
+
+- (void)testNetworkFlapDuringStreamingCall {
+  NSLog(@"%s", __func__);
+  // Open test app and make a unary call. Channel to server should be open after this.
+  [self doUnaryCall];
+  [self expectCallSuccess];
+  // Start streaming call and send a message
+  [self pressButton:@"Start streaming call"];
+  [self pressButton:@"Send Message"];
+
+  // Toggle network on and off
   [self setAirplaneMode:YES];
+  [self setWifi:NO];
+  [self setAirplaneMode:NO];
+  [self setWifi:YES];
 
-  // Unary call should fail
+  [testApp activate];
+  [self pressButton:@"Stop streaming call"];
+  // The call will fail if the stream gets a read error, else the call will succeed.
+  [self expectCallSuccessOrFailed];
+
+  // Make another unary call, it should succeed
   [self doUnaryCall];
-  [self expectCallFailed];
+  [self expectCallSuccess];
+}
 
-  // Turn off airplane mode
+- (void)testConcurrentCalls {
+  NSLog(@"%s", __func__);
+
+  // Press button to start 10 unary calls
+  [self do10UnaryCalls];
+
+  // Toggle airplane mode on and off
+  [self setAirplaneMode:YES];
   [self setAirplaneMode:NO];
 
-  // Unary call should succeed
+  // 10 calls should have completed
+  [testApp activate];
+  XCTAssert([testApp.staticTexts[@"Calls completed: 10"] waitForExistenceWithTimeout:kWaitTime]);
+}
+
+- (void)invokeTest {
+  for (int i = 0; i < kNumIterations; i++) {
+    [super invokeTest];
+  }
+}
+
+- (void)testUnaryCallTurnOffWifi {
+  NSLog(@"%s", __func__);
+  // Open test app and make a unary call. Channel to server should be open after this.
+  [self doUnaryCall];
+  [self expectCallSuccess];
+
+  // Turn off wifi
+  [self setWifi:NO];
+
+  // Phone should switch to cellular connection, call should succeed
+  [self doUnaryCall];
+  [self expectCallSuccess];
+
+  // Turn on wifi
+  [self setWifi:YES];
+
+  // Call should succeed after turning wifi back on
   [self doUnaryCall];
   [self expectCallSuccess];
 }
 
-- (void)testStreamingCallWhileNetworkDown {
+- (void)testStreamingCallTurnOffWifi {
+  NSLog(@"%s", __func__);
   // Open test app and make a unary call. Channel to server should be open after this.
   [self doUnaryCall];
   [self expectCallSuccess];
 
-  // Turn on airplane mode
-  [self setAirplaneMode:YES];
+  // Start streaming call and send a message
+  [self pressButton:@"Start streaming call"];
+  [self pressButton:@"Send Message"];
 
-  // Streaming call should fail
-  [self doStreamingCall];
+  // Turn off wifi
+  [self setWifi:NO];
+
+  // Phone should switch to cellular connection, this results in the call failing
+  [testApp activate];
+  [self pressButton:@"Stop streaming call"];
   [self expectCallFailed];
 
-  // Turn off airplane mode
-  [self setAirplaneMode:NO];
+  // Turn on wifi
+  [self setWifi:YES];
 
-  // Unary call should succeed
-  [self doStreamingCall];
+  // Call should succeed after turning wifi back on
+  [self doUnaryCall];
   [self expectCallSuccess];
 }
+
 @end
index e7e0530..4090841 100644 (file)
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
     <device id="retina4_7" orientation="portrait">
         <adaptation id="fullscreen"/>
     </device>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
                                     <action selector="tapStreamingCallStart:" destination="BYZ-38-t0r" eventType="touchUpInside" id="6tF-9u-Gew"/>
                                 </connections>
                             </button>
-                            <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dxG-ib-Xoa">
-                                <rect key="frame" x="197" y="221" width="132" height="30"/>
-                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
-                                <state key="normal" title="Stop streaming call"/>
-                                <connections>
-                                    <action selector="tapStreamingCallStop:" destination="BYZ-38-t0r" eventType="touchUpInside" id="QLQ-yw-wlZ"/>
-                                </connections>
-                            </button>
                             <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ay7-K7-Lez">
-                                <rect key="frame" x="48" y="71" width="69" height="30"/>
+                                <rect key="frame" x="35" y="71" width="69" height="30"/>
                                 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                 <state key="normal" title="Unary call"/>
                                 <connections>
                                 </connections>
                             </button>
                             <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Sq5-O5-fiK">
-                                <rect key="frame" x="213" y="147" width="101" height="30"/>
+                                <rect key="frame" x="196" y="147" width="101" height="30"/>
                                 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                 <state key="normal" title="Send Message"/>
                                 <connections>
                                     <action selector="tapStreamingCallSend:" destination="BYZ-38-t0r" eventType="touchUpInside" id="Qz0-PB-akd"/>
                                 </connections>
                             </button>
+                            <label opaque="NO" userInteractionEnabled="NO" tag="1" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7JI-bR-X8r">
+                                <rect key="frame" x="134" y="323" width="98" height="56"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                <nil key="textColor"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <label opaque="NO" userInteractionEnabled="NO" tag="2" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Calls completed: 0" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wwu-kV-w7R">
+                                <rect key="frame" x="128" y="418" width="180" height="56"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                <nil key="textColor"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="q8d-Mi-SAJ">
+                                <rect key="frame" x="35" y="147" width="96" height="30"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <state key="normal" title="10 Unary calls"/>
+                                <connections>
+                                    <action selector="tap10UnaryCalls:" destination="BYZ-38-t0r" eventType="touchUpInside" id="pY2-Rq-hVn"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Csm-o1-8eh">
+                                <rect key="frame" x="35" y="221" width="96" height="30"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <state key="normal" title="Reset counter"/>
+                                <connections>
+                                    <action selector="resetCounter:" destination="BYZ-38-t0r" eventType="touchUpInside" id="InY-SX-R6H"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dxG-ib-Xoa">
+                                <rect key="frame" x="196" y="221" width="132" height="30"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <state key="normal" title="Stop streaming call"/>
+                                <connections>
+                                    <action selector="tapStreamingCallStop:" destination="BYZ-38-t0r" eventType="touchUpInside" id="QLQ-yw-wlZ"/>
+                                </connections>
+                            </button>
                         </subviews>
                         <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                         <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
@@ -57,6 +87,7 @@
                 </viewController>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
             </objects>
+            <point key="canvasLocation" x="128.80000000000001" y="132.68365817091455"/>
         </scene>
     </scenes>
 </document>
index 813b176..50a729e 100644 (file)
@@ -27,33 +27,36 @@ NSString *const kRemoteHost = @"grpc-test.sandbox.googleapis.com";
 const int32_t kMessageSize = 100;
 
 @interface ViewController : UIViewController<GRPCProtoResponseHandler>
-@property(strong, nonatomic) UILabel *fromLabel;
+@property(strong, nonatomic) UILabel *callStatusLabel;
+@property(strong, nonatomic) UILabel *callCountLabel;
 @end
 
 @implementation ViewController {
   RMTTestService *_service;
   dispatch_queue_t _dispatchQueue;
   GRPCStreamingProtoCall *_call;
+  int _calls_completed;
 }
 - (instancetype)init {
   self = [super init];
+  _calls_completed = 0;
   return self;
 }
 
 - (void)viewDidLoad {
   [super viewDidLoad];
   _dispatchQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
-  _fromLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 500, 200, 20)];
-  _fromLabel.textColor = [UIColor blueColor];
-  _fromLabel.backgroundColor = [UIColor whiteColor];
-  [self.view addSubview:_fromLabel];
+  _callStatusLabel = (UILabel *)[self.view viewWithTag:1];
+  _callCountLabel = (UILabel *)[self.view viewWithTag:2];
 }
 
-- (IBAction)tapUnaryCall:(id)sender {
+- (void)startUnaryCall {
   if (_service == nil) {
     _service = [RMTTestService serviceWithHost:kRemoteHost];
   }
-  self->_fromLabel.text = @"";
+  dispatch_async(dispatch_get_main_queue(), ^{
+    self->_callStatusLabel.text = @"";
+  });
 
   // Set up request proto message
   RMTSimpleRequest *request = [RMTSimpleRequest message];
@@ -63,14 +66,43 @@ const int32_t kMessageSize = 100;
 
   GRPCUnaryProtoCall *call =
       [_service unaryCallWithMessage:request responseHandler:self callOptions:nil];
+
   [call start];
 }
 
+- (IBAction)tapUnaryCall:(id)sender {
+  NSLog(@"In tapUnaryCall");
+  [self startUnaryCall];
+}
+
+- (IBAction)tap10UnaryCalls:(id)sender {
+  NSLog(@"In tap10UnaryCalls");
+  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
+    // Background thread
+    for (int i = 0; i < 10; ++i) {
+      [self startUnaryCall];
+      [NSThread sleepForTimeInterval:0.5];
+    }
+  });
+}
+
+- (IBAction)resetCounter:(id)sender {
+  _calls_completed = 0;
+  dispatch_async(dispatch_get_main_queue(), ^{
+    self->_callCountLabel.text =
+        [NSString stringWithFormat:@"Calls completed: %d", self->_calls_completed];
+    self->_callStatusLabel.text = @"";
+  });
+}
+
 - (IBAction)tapStreamingCallStart:(id)sender {
+  NSLog(@"In tapStreamingCallStart");
   if (_service == nil) {
     _service = [RMTTestService serviceWithHost:kRemoteHost];
   }
-  self->_fromLabel.text = @"";
+  dispatch_async(dispatch_get_main_queue(), ^{
+    self->_callStatusLabel.text = @"";
+  });
 
   // Set up request proto message
   RMTStreamingOutputCallRequest *request = RMTStreamingOutputCallRequest.message;
@@ -83,10 +115,10 @@ const int32_t kMessageSize = 100;
   [call start];
   _call = call;
   // display something to confirm the tester the call is started
-  NSLog(@"Started streaming call");
 }
 
 - (IBAction)tapStreamingCallSend:(id)sender {
+  NSLog(@"In tapStreamingCallSend");
   if (_call == nil) return;
 
   RMTStreamingOutputCallRequest *request = RMTStreamingOutputCallRequest.message;
@@ -99,6 +131,7 @@ const int32_t kMessageSize = 100;
 }
 
 - (IBAction)tapStreamingCallStop:(id)sender {
+  NSLog(@"In tapStreamingCallStop");
   if (_call == nil) return;
 
   [_call finish];
@@ -118,13 +151,18 @@ const int32_t kMessageSize = 100;
   NSLog(@"Recv trailing metadata: %@, error: %@", trailingMetadata, error);
   if (error == nil) {
     dispatch_async(dispatch_get_main_queue(), ^{
-      self->_fromLabel.text = @"Call done";
+      self->_callStatusLabel.text = @"Call done";
     });
   } else {
     dispatch_async(dispatch_get_main_queue(), ^{
-      self->_fromLabel.text = @"Call failed";
+      self->_callStatusLabel.text = @"Call failed";
     });
   }
+  ++_calls_completed;
+  dispatch_async(dispatch_get_main_queue(), ^{
+    self->_callCountLabel.text =
+        [NSString stringWithFormat:@"Calls completed: %d", self->_calls_completed];
+  });
 }
 
 - (dispatch_queue_t)dispatchQueue {
index ca7bf47..db29375 100644 (file)
@@ -22,6 +22,7 @@
 #import <XCTest/XCTest.h>
 
 #include <grpc/grpc.h>
+#include <grpc/support/port_platform.h>
 
 #import "../version.h"
 
@@ -39,11 +40,13 @@ static NSString *const kService = @"TestService";
 static GRPCProtoMethod *kInexistentMethod;
 static GRPCProtoMethod *kEmptyCallMethod;
 static GRPCProtoMethod *kUnaryCallMethod;
+static GRPCProtoMethod *kOutputStreamingCallMethod;
 static GRPCProtoMethod *kFullDuplexCallMethod;
 
 static const int kSimpleDataLength = 100;
 
-static const NSTimeInterval kTestTimeout = 16;
+static const NSTimeInterval kTestTimeout = 8;
+static const NSTimeInterval kInvertedTimeout = 2;
 
 // Reveal the _class ivar for testing access
 @interface GRPCCall2 () {
@@ -58,6 +61,11 @@ static const NSTimeInterval kTestTimeout = 16;
 
 - (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback
                                 messageCallback:(void (^)(id))messageCallback
+                                  closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback
+                              writeDataCallback:(void (^)(void))writeDataCallback;
+
+- (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback
+                                messageCallback:(void (^)(id))messageCallback
                                   closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback;
 
 @end
@@ -66,21 +74,33 @@ static const NSTimeInterval kTestTimeout = 16;
   void (^_initialMetadataCallback)(NSDictionary *);
   void (^_messageCallback)(id);
   void (^_closeCallback)(NSDictionary *, NSError *);
+  void (^_writeDataCallback)(void);
   dispatch_queue_t _dispatchQueue;
 }
 
 - (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback
                                 messageCallback:(void (^)(id))messageCallback
-                                  closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback {
+                                  closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback
+                              writeDataCallback:(void (^)(void))writeDataCallback {
   if ((self = [super init])) {
     _initialMetadataCallback = initialMetadataCallback;
     _messageCallback = messageCallback;
     _closeCallback = closeCallback;
+    _writeDataCallback = writeDataCallback;
     _dispatchQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL);
   }
   return self;
 }
 
+- (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback
+                                messageCallback:(void (^)(id))messageCallback
+                                  closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback {
+  return [self initWithInitialMetadataCallback:initialMetadataCallback
+                               messageCallback:messageCallback
+                                 closeCallback:closeCallback
+                             writeDataCallback:nil];
+}
+
 - (void)didReceiveInitialMetadata:(NSDictionary *)initialMetadata {
   if (self->_initialMetadataCallback) {
     self->_initialMetadataCallback(initialMetadata);
@@ -99,6 +119,12 @@ static const NSTimeInterval kTestTimeout = 16;
   }
 }
 
+- (void)didWriteData {
+  if (self->_writeDataCallback) {
+    self->_writeDataCallback();
+  }
+}
+
 - (dispatch_queue_t)dispatchQueue {
   return _dispatchQueue;
 }
@@ -119,6 +145,9 @@ static const NSTimeInterval kTestTimeout = 16;
       [[GRPCProtoMethod alloc] initWithPackage:kPackage service:kService method:@"EmptyCall"];
   kUnaryCallMethod =
       [[GRPCProtoMethod alloc] initWithPackage:kPackage service:kService method:@"UnaryCall"];
+  kOutputStreamingCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
+                                                                service:kService
+                                                                 method:@"StreamingOutputCall"];
   kFullDuplexCallMethod =
       [[GRPCProtoMethod alloc] initWithPackage:kPackage service:kService method:@"FullDuplexCall"];
 }
@@ -198,7 +227,9 @@ static const NSTimeInterval kTestTimeout = 16;
                expectedUserAgent = [expectedUserAgent stringByAppendingString:@" grpc-c/"];
                expectedUserAgent =
                    [expectedUserAgent stringByAppendingString:GRPC_C_VERSION_STRING];
-               expectedUserAgent = [expectedUserAgent stringByAppendingString:@" (ios; chttp2; "];
+               expectedUserAgent = [expectedUserAgent stringByAppendingString:@" ("];
+               expectedUserAgent = [expectedUserAgent stringByAppendingString:@GPR_PLATFORM_STRING];
+               expectedUserAgent = [expectedUserAgent stringByAppendingString:@"; chttp2; "];
                expectedUserAgent = [expectedUserAgent
                    stringByAppendingString:[NSString stringWithUTF8String:grpc_g_stands_for()]];
                expectedUserAgent = [expectedUserAgent stringByAppendingString:@")"];
@@ -475,4 +506,268 @@ static const NSTimeInterval kTestTimeout = 16;
   [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
 }
 
+- (void)testFlowControlWrite {
+  __weak XCTestExpectation *expectWriteData =
+      [self expectationWithDescription:@"Reported write data"];
+
+  RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
+  RMTResponseParameters *parameters = [RMTResponseParameters message];
+  parameters.size = kSimpleDataLength;
+  [request.responseParametersArray addObject:parameters];
+  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
+
+  GRPCRequestOptions *callRequest =
+      [[GRPCRequestOptions alloc] initWithHost:(NSString *)kHostAddress
+                                          path:kUnaryCallMethod.HTTPPath
+                                        safety:GRPCCallSafetyDefault];
+  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
+  options.transportType = GRPCTransportTypeInsecure;
+  options.flowControlEnabled = YES;
+  GRPCCall2 *call =
+      [[GRPCCall2 alloc] initWithRequestOptions:callRequest
+                                responseHandler:[[ClientTestsBlockCallbacks alloc]
+                                                    initWithInitialMetadataCallback:nil
+                                                                    messageCallback:nil
+                                                                      closeCallback:nil
+                                                                  writeDataCallback:^{
+                                                                    [expectWriteData fulfill];
+                                                                  }]
+                                    callOptions:options];
+
+  [call start];
+  [call receiveNextMessages:1];
+  [call writeData:[request data]];
+
+  // Wait for 3 seconds and make sure we do not receive the response
+  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
+
+  [call finish];
+}
+
+- (void)testFlowControlRead {
+  __weak __block XCTestExpectation *expectBlockedMessage =
+      [self expectationWithDescription:@"Message not delivered without recvNextMessage"];
+  __weak __block XCTestExpectation *expectPassedMessage = nil;
+  __weak __block XCTestExpectation *expectBlockedClose =
+      [self expectationWithDescription:@"Call not closed with pending message"];
+  __weak __block XCTestExpectation *expectPassedClose = nil;
+  expectBlockedMessage.inverted = YES;
+  expectBlockedClose.inverted = YES;
+
+  RMTSimpleRequest *request = [RMTSimpleRequest message];
+  request.responseSize = kSimpleDataLength;
+  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
+
+  GRPCRequestOptions *callRequest =
+      [[GRPCRequestOptions alloc] initWithHost:(NSString *)kHostAddress
+                                          path:kUnaryCallMethod.HTTPPath
+                                        safety:GRPCCallSafetyDefault];
+  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
+  options.transportType = GRPCTransportTypeInsecure;
+  options.flowControlEnabled = YES;
+  __block int unblocked = NO;
+  GRPCCall2 *call = [[GRPCCall2 alloc]
+      initWithRequestOptions:callRequest
+             responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
+                                 messageCallback:^(NSData *message) {
+                                   if (!unblocked) {
+                                     [expectBlockedMessage fulfill];
+                                   } else {
+                                     [expectPassedMessage fulfill];
+                                   }
+                                 }
+                                 closeCallback:^(NSDictionary *trailers, NSError *error) {
+                                   if (!unblocked) {
+                                     [expectBlockedClose fulfill];
+                                   } else {
+                                     [expectPassedClose fulfill];
+                                   }
+                                 }]
+                 callOptions:options];
+
+  [call start];
+  [call writeData:[request data]];
+  [call finish];
+
+  // Wait to make sure we do not receive the response
+  [self waitForExpectationsWithTimeout:kInvertedTimeout handler:nil];
+
+  expectPassedMessage =
+      [self expectationWithDescription:@"Message delivered with receiveNextMessage"];
+  expectPassedClose = [self expectationWithDescription:@"Close delivered after receiveNextMessage"];
+
+  unblocked = YES;
+  [call receiveNextMessages:1];
+
+  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
+}
+
+- (void)testFlowControlMultipleMessages {
+  __weak XCTestExpectation *expectPassedMessage =
+      [self expectationWithDescription:@"two messages delivered with receiveNextMessage"];
+  expectPassedMessage.expectedFulfillmentCount = 2;
+  __weak XCTestExpectation *expectBlockedMessage =
+      [self expectationWithDescription:@"Message 3 not delivered"];
+  expectBlockedMessage.inverted = YES;
+  __weak XCTestExpectation *expectWriteTwice =
+      [self expectationWithDescription:@"Write 2 messages done"];
+  expectWriteTwice.expectedFulfillmentCount = 2;
+
+  RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
+  RMTResponseParameters *parameters = [RMTResponseParameters message];
+  parameters.size = kSimpleDataLength;
+  [request.responseParametersArray addObject:parameters];
+  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
+
+  GRPCRequestOptions *callRequest =
+      [[GRPCRequestOptions alloc] initWithHost:(NSString *)kHostAddress
+                                          path:kFullDuplexCallMethod.HTTPPath
+                                        safety:GRPCCallSafetyDefault];
+  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
+  options.transportType = GRPCTransportTypeInsecure;
+  options.flowControlEnabled = YES;
+  __block NSUInteger messageId = 0;
+  __block GRPCCall2 *call = [[GRPCCall2 alloc]
+      initWithRequestOptions:callRequest
+             responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
+                                 messageCallback:^(NSData *message) {
+                                   if (messageId <= 1) {
+                                     [expectPassedMessage fulfill];
+                                   } else {
+                                     [expectBlockedMessage fulfill];
+                                   }
+                                   messageId++;
+                                 }
+                                 closeCallback:nil
+                                 writeDataCallback:^{
+                                   [expectWriteTwice fulfill];
+                                 }]
+                 callOptions:options];
+
+  [call receiveNextMessages:2];
+  [call start];
+  [call writeData:[request data]];
+  [call writeData:[request data]];
+
+  [self waitForExpectationsWithTimeout:kInvertedTimeout handler:nil];
+}
+
+- (void)testFlowControlReadReadyBeforeStart {
+  __weak XCTestExpectation *expectPassedMessage =
+      [self expectationWithDescription:@"Message delivered with receiveNextMessage"];
+  __weak XCTestExpectation *expectPassedClose =
+      [self expectationWithDescription:@"Close delivered with receiveNextMessage"];
+
+  RMTSimpleRequest *request = [RMTSimpleRequest message];
+  request.responseSize = kSimpleDataLength;
+  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
+
+  GRPCRequestOptions *callRequest =
+      [[GRPCRequestOptions alloc] initWithHost:(NSString *)kHostAddress
+                                          path:kUnaryCallMethod.HTTPPath
+                                        safety:GRPCCallSafetyDefault];
+  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
+  options.transportType = GRPCTransportTypeInsecure;
+  options.flowControlEnabled = YES;
+  __block BOOL closed = NO;
+  GRPCCall2 *call = [[GRPCCall2 alloc]
+      initWithRequestOptions:callRequest
+             responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
+                                 messageCallback:^(NSData *message) {
+                                   [expectPassedMessage fulfill];
+                                   XCTAssertFalse(closed);
+                                 }
+                                 closeCallback:^(NSDictionary *ttrailers, NSError *error) {
+                                   closed = YES;
+                                   [expectPassedClose fulfill];
+                                 }]
+                 callOptions:options];
+
+  [call receiveNextMessages:1];
+  [call start];
+  [call writeData:[request data]];
+  [call finish];
+
+  [self waitForExpectationsWithTimeout:kInvertedTimeout handler:nil];
+}
+
+- (void)testFlowControlReadReadyAfterStart {
+  __weak XCTestExpectation *expectPassedMessage =
+      [self expectationWithDescription:@"Message delivered with receiveNextMessage"];
+  __weak XCTestExpectation *expectPassedClose =
+      [self expectationWithDescription:@"Close delivered with receiveNextMessage"];
+
+  RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
+  RMTResponseParameters *parameters = [RMTResponseParameters message];
+  parameters.size = kSimpleDataLength;
+  [request.responseParametersArray addObject:parameters];
+  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
+
+  GRPCRequestOptions *callRequest =
+      [[GRPCRequestOptions alloc] initWithHost:(NSString *)kHostAddress
+                                          path:kUnaryCallMethod.HTTPPath
+                                        safety:GRPCCallSafetyDefault];
+  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
+  options.transportType = GRPCTransportTypeInsecure;
+  options.flowControlEnabled = YES;
+  __block BOOL closed = NO;
+  GRPCCall2 *call = [[GRPCCall2 alloc]
+      initWithRequestOptions:callRequest
+             responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
+                                 messageCallback:^(NSData *message) {
+                                   [expectPassedMessage fulfill];
+                                   XCTAssertFalse(closed);
+                                 }
+                                 closeCallback:^(NSDictionary *trailers, NSError *error) {
+                                   closed = YES;
+                                   [expectPassedClose fulfill];
+                                 }]
+                 callOptions:options];
+
+  [call start];
+  [call receiveNextMessages:1];
+  [call writeData:[request data]];
+  [call finish];
+
+  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
+}
+
+- (void)testFlowControlReadNonBlockingFailure {
+  __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
+
+  GRPCRequestOptions *requestOptions =
+      [[GRPCRequestOptions alloc] initWithHost:kHostAddress
+                                          path:kUnaryCallMethod.HTTPPath
+                                        safety:GRPCCallSafetyDefault];
+  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
+  options.flowControlEnabled = YES;
+  options.transportType = GRPCTransportTypeInsecure;
+
+  RMTSimpleRequest *request = [RMTSimpleRequest message];
+  request.payload.body = [NSMutableData dataWithLength:options.responseSizeLimit];
+
+  RMTEchoStatus *status = [RMTEchoStatus message];
+  status.code = 2;
+  status.message = @"test";
+  request.responseStatus = status;
+
+  GRPCCall2 *call = [[GRPCCall2 alloc]
+      initWithRequestOptions:requestOptions
+             responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
+                                 messageCallback:^(NSData *data) {
+                                   XCTFail(@"Received unexpected message");
+                                 }
+                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
+                                   XCTAssertNotNil(error, @"Expecting non-nil error");
+                                   XCTAssertEqual(error.code, 2);
+                                   [completion fulfill];
+                                 }]
+                 callOptions:options];
+  [call writeData:[request data]];
+  [call start];
+  [call finish];
+
+  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
+}
+
 @end
index 2fac1be..0d081e4 100644 (file)
 #include <grpc/support/log.h>
 
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gpr/tmpfile.h"
 #include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/security_connector/ssl_utils.h"
 #include "test/core/end2end/data/ssl_test_data.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
@@ -172,7 +172,7 @@ static char *roots_filename;
   GPR_ASSERT(roots_file != NULL);
   GPR_ASSERT(fwrite(test_root_cert, 1, roots_size, roots_file) == roots_size);
   fclose(roots_file);
-  gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_filename);
+  GPR_GLOBAL_CONFIG_SET(grpc_default_ssl_roots_file_path, roots_filename);
 
   grpc_init();
 
index 3ef0468..b361078 100644 (file)
@@ -16,9 +16,9 @@
  *
  */
 
-#import <UIKit/UIKit.h>
 #import <XCTest/XCTest.h>
 #import <grpc/grpc.h>
+#import <grpc/support/port_platform.h>
 
 #import <GRPCClient/GRPCCall+ChannelArg.h>
 #import <GRPCClient/GRPCCall+OAuth2.h>
@@ -297,7 +297,9 @@ static GRPCProtoMethod *kFullDuplexCallMethod;
         expectedUserAgent = [expectedUserAgent stringByAppendingString:GRPC_OBJC_VERSION_STRING];
         expectedUserAgent = [expectedUserAgent stringByAppendingString:@" grpc-c/"];
         expectedUserAgent = [expectedUserAgent stringByAppendingString:GRPC_C_VERSION_STRING];
-        expectedUserAgent = [expectedUserAgent stringByAppendingString:@" (ios; chttp2; "];
+        expectedUserAgent = [expectedUserAgent stringByAppendingString:@" ("];
+        expectedUserAgent = [expectedUserAgent stringByAppendingString:@GPR_PLATFORM_STRING];
+        expectedUserAgent = [expectedUserAgent stringByAppendingString:@"; chttp2; "];
         expectedUserAgent = [expectedUserAgent
             stringByAppendingString:[NSString stringWithUTF8String:grpc_g_stands_for()]];
         expectedUserAgent = [expectedUserAgent stringByAppendingString:@")"];
index 717dfd8..aab38e3 100644 (file)
@@ -20,7 +20,9 @@
 
 #include <grpc/status.h>
 
+#ifdef GRPC_COMPILE_WITH_CRONET
 #import <Cronet/Cronet.h>
+#endif
 #import <GRPCClient/GRPCCall+ChannelArg.h>
 #import <GRPCClient/GRPCCall+Cronet.h>
 #import <GRPCClient/GRPCCall+Tests.h>
@@ -79,6 +81,11 @@ BOOL isRemoteInteropTest(NSString *host) {
 
 - (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback
                                 messageCallback:(void (^)(id))messageCallback
+                                  closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback
+                           writeMessageCallback:(void (^)(void))writeMessageCallback;
+
+- (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback
+                                messageCallback:(void (^)(id))messageCallback
                                   closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback;
 
 @end
@@ -87,21 +94,33 @@ BOOL isRemoteInteropTest(NSString *host) {
   void (^_initialMetadataCallback)(NSDictionary *);
   void (^_messageCallback)(id);
   void (^_closeCallback)(NSDictionary *, NSError *);
+  void (^_writeMessageCallback)(void);
   dispatch_queue_t _dispatchQueue;
 }
 
 - (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback
                                 messageCallback:(void (^)(id))messageCallback
-                                  closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback {
+                                  closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback
+                           writeMessageCallback:(void (^)(void))writeMessageCallback {
   if ((self = [super init])) {
     _initialMetadataCallback = initialMetadataCallback;
     _messageCallback = messageCallback;
     _closeCallback = closeCallback;
+    _writeMessageCallback = writeMessageCallback;
     _dispatchQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL);
   }
   return self;
 }
 
+- (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback
+                                messageCallback:(void (^)(id))messageCallback
+                                  closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback {
+  return [self initWithInitialMetadataCallback:initialMetadataCallback
+                               messageCallback:messageCallback
+                                 closeCallback:closeCallback
+                          writeMessageCallback:nil];
+}
+
 - (void)didReceiveInitialMetadata:(NSDictionary *)initialMetadata {
   if (_initialMetadataCallback) {
     _initialMetadataCallback(initialMetadata);
@@ -120,6 +139,12 @@ BOOL isRemoteInteropTest(NSString *host) {
   }
 }
 
+- (void)didWriteMessage {
+  if (_writeMessageCallback) {
+    _writeMessageCallback();
+  }
+}
+
 - (dispatch_queue_t)dispatchQueue {
   return _dispatchQueue;
 }
@@ -227,6 +252,52 @@ BOOL isRemoteInteropTest(NSString *host) {
   [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
+// Test that responses can be dispatched even if we do not run main run-loop
+- (void)testAsyncDispatchWithV2API {
+  XCTAssertNotNil([[self class] host]);
+
+  GPBEmpty *request = [GPBEmpty message];
+  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
+  options.transportType = [[self class] transportType];
+  options.PEMRootCertificates = [[self class] PEMRootCertificates];
+  options.hostNameOverride = [[self class] hostNameOverride];
+
+  __block BOOL messageReceived = NO;
+  __block BOOL done = NO;
+  NSCondition *cond = [[NSCondition alloc] init];
+  GRPCUnaryProtoCall *call = [_service
+      emptyCallWithMessage:request
+           responseHandler:[[InteropTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
+                               messageCallback:^(id message) {
+                                 if (message) {
+                                   id expectedResponse = [GPBEmpty message];
+                                   XCTAssertEqualObjects(message, expectedResponse);
+                                   [cond lock];
+                                   messageReceived = YES;
+                                   [cond unlock];
+                                 }
+                               }
+                               closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
+                                 XCTAssertNil(error, @"Unexpected error: %@", error);
+                                 [cond lock];
+                                 done = YES;
+                                 [cond signal];
+                                 [cond unlock];
+                               }]
+               callOptions:options];
+
+  NSDate *deadline = [NSDate dateWithTimeIntervalSinceNow:TEST_TIMEOUT];
+  [call start];
+
+  [cond lock];
+  while (!done && [deadline timeIntervalSinceNow] > 0) {
+    [cond waitUntilDate:deadline];
+  }
+  XCTAssertTrue(messageReceived);
+  XCTAssertTrue(done);
+  [cond unlock];
+}
+
 - (void)testLargeUnaryRPC {
   XCTAssertNotNil([[self class] host]);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"LargeUnary"];
@@ -293,6 +364,90 @@ BOOL isRemoteInteropTest(NSString *host) {
   [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
+- (void)testConcurrentRPCsWithErrorsWithV2API {
+  NSMutableArray *completeExpectations = [NSMutableArray array];
+  NSMutableArray *calls = [NSMutableArray array];
+  int num_rpcs = 10;
+  for (int i = 0; i < num_rpcs; ++i) {
+    [completeExpectations
+        addObject:[self expectationWithDescription:
+                            [NSString stringWithFormat:@"Received trailer for RPC %d", i]]];
+
+    RMTSimpleRequest *request = [RMTSimpleRequest message];
+    request.responseType = RMTPayloadType_Compressable;
+    request.responseSize = 314159;
+    request.payload.body = [NSMutableData dataWithLength:271828];
+    if (i % 3 == 0) {
+      request.responseStatus.code = GRPC_STATUS_UNAVAILABLE;
+    } else if (i % 7 == 0) {
+      request.responseStatus.code = GRPC_STATUS_CANCELLED;
+    }
+    GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
+    options.transportType = [[self class] transportType];
+    options.PEMRootCertificates = [[self class] PEMRootCertificates];
+    options.hostNameOverride = [[self class] hostNameOverride];
+
+    GRPCUnaryProtoCall *call = [_service
+        unaryCallWithMessage:request
+             responseHandler:[[InteropTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
+                                 messageCallback:^(id message) {
+                                   if (message) {
+                                     RMTSimpleResponse *expectedResponse =
+                                         [RMTSimpleResponse message];
+                                     expectedResponse.payload.type = RMTPayloadType_Compressable;
+                                     expectedResponse.payload.body =
+                                         [NSMutableData dataWithLength:314159];
+                                     XCTAssertEqualObjects(message, expectedResponse);
+                                   }
+                                 }
+                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
+                                   [completeExpectations[i] fulfill];
+                                 }]
+                 callOptions:options];
+    [calls addObject:call];
+  }
+
+  for (int i = 0; i < num_rpcs; ++i) {
+    GRPCUnaryProtoCall *call = calls[i];
+    [call start];
+  }
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
+}
+
+- (void)testConcurrentRPCsWithErrors {
+  NSMutableArray *completeExpectations = [NSMutableArray array];
+  int num_rpcs = 10;
+  for (int i = 0; i < num_rpcs; ++i) {
+    [completeExpectations
+        addObject:[self expectationWithDescription:
+                            [NSString stringWithFormat:@"Received trailer for RPC %d", i]]];
+
+    RMTSimpleRequest *request = [RMTSimpleRequest message];
+    request.responseType = RMTPayloadType_Compressable;
+    request.responseSize = 314159;
+    request.payload.body = [NSMutableData dataWithLength:271828];
+    if (i % 3 == 0) {
+      request.responseStatus.code = GRPC_STATUS_UNAVAILABLE;
+    } else if (i % 7 == 0) {
+      request.responseStatus.code = GRPC_STATUS_CANCELLED;
+    }
+
+    [_service unaryCallWithRequest:request
+                           handler:^(RMTSimpleResponse *response, NSError *error) {
+                             if (error == nil) {
+                               RMTSimpleResponse *expectedResponse = [RMTSimpleResponse message];
+                               expectedResponse.payload.type = RMTPayloadType_Compressable;
+                               expectedResponse.payload.body =
+                                   [NSMutableData dataWithLength:314159];
+                               XCTAssertEqualObjects(response, expectedResponse);
+                             }
+                             [completeExpectations[i] fulfill];
+                           }];
+  }
+
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
+}
+
 - (void)testPacketCoalescing {
   XCTAssertNotNil([[self class] host]);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"LargeUnary"];
@@ -570,6 +725,67 @@ BOOL isRemoteInteropTest(NSString *host) {
   [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
+- (void)testPingPongRPCWithFlowControl {
+  XCTAssertNotNil([[self class] host]);
+  __weak XCTestExpectation *expectation = [self expectationWithDescription:@"PingPongWithV2API"];
+
+  NSArray *requests = @[ @27182, @8, @1828, @45904 ];
+  NSArray *responses = @[ @31415, @9, @2653, @58979 ];
+
+  __block int index = 0;
+
+  id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index]
+                                               requestedResponseSize:responses[index]];
+  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
+  options.transportType = [[self class] transportType];
+  options.PEMRootCertificates = [[self class] PEMRootCertificates];
+  options.hostNameOverride = [[self class] hostNameOverride];
+  options.flowControlEnabled = YES;
+  __block BOOL canWriteData = NO;
+
+  __block GRPCStreamingProtoCall *call = [_service
+      fullDuplexCallWithResponseHandler:[[InteropTestsBlockCallbacks alloc]
+                                            initWithInitialMetadataCallback:nil
+                                            messageCallback:^(id message) {
+                                              XCTAssertLessThan(index, 4,
+                                                                @"More than 4 responses received.");
+                                              id expected = [RMTStreamingOutputCallResponse
+                                                  messageWithPayloadSize:responses[index]];
+                                              XCTAssertEqualObjects(message, expected);
+                                              index += 1;
+                                              if (index < 4) {
+                                                id request = [RMTStreamingOutputCallRequest
+                                                    messageWithPayloadSize:requests[index]
+                                                     requestedResponseSize:responses[index]];
+                                                XCTAssertTrue(canWriteData);
+                                                canWriteData = NO;
+                                                [call writeMessage:request];
+                                                [call receiveNextMessage];
+                                              } else {
+                                                [call finish];
+                                              }
+                                            }
+                                            closeCallback:^(NSDictionary *trailingMetadata,
+                                                            NSError *error) {
+                                              XCTAssertNil(error,
+                                                           @"Finished with unexpected error: %@",
+                                                           error);
+                                              XCTAssertEqual(index, 4,
+                                                             @"Received %i responses instead of 4.",
+                                                             index);
+                                              [expectation fulfill];
+                                            }
+                                            writeMessageCallback:^{
+                                              canWriteData = YES;
+                                            }]
+                            callOptions:options];
+  [call start];
+  [call receiveNextMessage];
+  [call writeMessage:request];
+
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
+}
+
 - (void)testEmptyStreamRPC {
   XCTAssertNotNil([[self class] host]);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"EmptyStream"];
@@ -753,7 +969,7 @@ BOOL isRemoteInteropTest(NSString *host) {
                      [GRPCCall closeOpenConnections];
 #pragma clang diagnostic pop
 
-                     [_service
+                     [self->_service
                          emptyCallWithRequest:request
                                       handler:^(GPBEmpty *response, NSError *error) {
                                         XCTAssertNil(
diff --git a/src/objective-c/tests/MacTests/Info.plist b/src/objective-c/tests/MacTests/Info.plist
new file mode 100644 (file)
index 0000000..6c40a6c
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>$(DEVELOPMENT_LANGUAGE)</string>
+       <key>CFBundleExecutable</key>
+       <string>$(EXECUTABLE_NAME)</string>
+       <key>CFBundleIdentifier</key>
+       <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundleName</key>
+       <string>$(PRODUCT_NAME)</string>
+       <key>CFBundlePackageType</key>
+       <string>BNDL</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0</string>
+       <key>CFBundleVersion</key>
+       <string>1</string>
+</dict>
+</plist>
diff --git a/src/objective-c/tests/MacTests/StressTests.h b/src/objective-c/tests/MacTests/StressTests.h
new file mode 100644 (file)
index 0000000..8bee0e6
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#import <XCTest/XCTest.h>
+
+#import <GRPCClient/GRPCCallOptions.h>
+
+@interface StressTests : XCTestCase
+/**
+ * Host to send the RPCs to. The base implementation returns nil, which would make all tests to
+ * fail.
+ * Override in a subclass to perform these tests against a specific address.
+ */
++ (NSString *)host;
+
+/**
+ * Bytes of overhead of test proto responses due to encoding. This is used to excercise the behavior
+ * when responses are just above or below the max response size. For some reason, the local and
+ * remote servers enconde responses with different overhead (?), so this is defined per-subclass.
+ */
+- (int32_t)encodingOverhead;
+
+/**
+ * The type of transport to be used. The base implementation returns default. Subclasses should
+ * override to appropriate settings.
+ */
++ (GRPCTransportType)transportType;
+
+/**
+ * The root certificates to be used. The base implementation returns nil. Subclasses should override
+ * to appropriate settings.
+ */
++ (NSString *)PEMRootCertificates;
+
+/**
+ * The root certificates to be used. The base implementation returns nil. Subclasses should override
+ * to appropriate settings.
+ */
++ (NSString *)hostNameOverride;
+
+@end
diff --git a/src/objective-c/tests/MacTests/StressTests.m b/src/objective-c/tests/MacTests/StressTests.m
new file mode 100644 (file)
index 0000000..22174b5
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ *
+ * Copyright 2019 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include "StressTests.h"
+
+#import <GRPCClient/GRPCCall+ChannelArg.h>
+#import <GRPCClient/GRPCCall+Tests.h>
+#import <GRPCClient/internal_testing/GRPCCall+InternalTests.h>
+#import <ProtoRPC/ProtoRPC.h>
+#import <RemoteTest/Messages.pbobjc.h>
+#import <RemoteTest/Test.pbobjc.h>
+#import <RemoteTest/Test.pbrpc.h>
+#import <RxLibrary/GRXBufferedPipe.h>
+#import <RxLibrary/GRXWriter+Immediate.h>
+#import <grpc/grpc.h>
+#import <grpc/support/log.h>
+
+#define TEST_TIMEOUT 32
+
+extern const char *kCFStreamVarName;
+
+// Convenience class to use blocks as callbacks
+@interface MacTestsBlockCallbacks : NSObject<GRPCProtoResponseHandler>
+
+- (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback
+                                messageCallback:(void (^)(id))messageCallback
+                                  closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback;
+
+@end
+
+@implementation MacTestsBlockCallbacks {
+  void (^_initialMetadataCallback)(NSDictionary *);
+  void (^_messageCallback)(id);
+  void (^_closeCallback)(NSDictionary *, NSError *);
+  dispatch_queue_t _dispatchQueue;
+}
+
+- (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback
+                                messageCallback:(void (^)(id))messageCallback
+                                  closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback {
+  if ((self = [super init])) {
+    _initialMetadataCallback = initialMetadataCallback;
+    _messageCallback = messageCallback;
+    _closeCallback = closeCallback;
+    _dispatchQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL);
+  }
+  return self;
+}
+
+- (void)didReceiveInitialMetadata:(NSDictionary *)initialMetadata {
+  if (_initialMetadataCallback) {
+    _initialMetadataCallback(initialMetadata);
+  }
+}
+
+- (void)didReceiveProtoMessage:(GPBMessage *)message {
+  if (_messageCallback) {
+    _messageCallback(message);
+  }
+}
+
+- (void)didCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata error:(NSError *)error {
+  if (_closeCallback) {
+    _closeCallback(trailingMetadata, error);
+  }
+}
+
+- (dispatch_queue_t)dispatchQueue {
+  return _dispatchQueue;
+}
+
+@end
+
+@implementation StressTests {
+  RMTTestService *_service;
+}
+
++ (NSString *)host {
+  return nil;
+}
+
++ (NSString *)hostAddress {
+  return nil;
+}
+
++ (NSString *)PEMRootCertificates {
+  return nil;
+}
+
++ (NSString *)hostNameOverride {
+  return nil;
+}
+
+- (int32_t)encodingOverhead {
+  return 0;
+}
+
++ (void)setUp {
+  setenv(kCFStreamVarName, "1", 1);
+}
+
+- (void)setUp {
+  self.continueAfterFailure = NO;
+
+  [GRPCCall resetHostSettings];
+
+  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
+  options.transportType = [[self class] transportType];
+  options.PEMRootCertificates = [[self class] PEMRootCertificates];
+  options.hostNameOverride = [[self class] hostNameOverride];
+  _service = [RMTTestService serviceWithHost:[[self class] host] callOptions:options];
+  system([[NSString stringWithFormat:@"sudo ifconfig lo0 alias %@", [[self class] hostAddress]]
+      UTF8String]);
+}
+
+- (void)tearDown {
+  system([[NSString stringWithFormat:@"sudo ifconfig lo0 -alias %@", [[self class] hostAddress]]
+      UTF8String]);
+}
+
++ (GRPCTransportType)transportType {
+  return GRPCTransportTypeChttp2BoringSSL;
+}
+
+- (void)testNetworkFlapWithV2API {
+  NSMutableArray *completeExpectations = [NSMutableArray array];
+  NSMutableArray *calls = [NSMutableArray array];
+  int num_rpcs = 100;
+  __block BOOL address_removed = FALSE;
+  __block BOOL address_readded = FALSE;
+  for (int i = 0; i < num_rpcs; ++i) {
+    [completeExpectations
+        addObject:[self expectationWithDescription:
+                            [NSString stringWithFormat:@"Received trailer for RPC %d", i]]];
+
+    RMTSimpleRequest *request = [RMTSimpleRequest message];
+    request.responseType = RMTPayloadType_Compressable;
+    request.responseSize = 314159;
+    request.payload.body = [NSMutableData dataWithLength:271828];
+
+    GRPCUnaryProtoCall *call = [_service
+        unaryCallWithMessage:request
+             responseHandler:[[MacTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
+                                 messageCallback:^(id message) {
+                                   if (message) {
+                                     RMTSimpleResponse *expectedResponse =
+                                         [RMTSimpleResponse message];
+                                     expectedResponse.payload.type = RMTPayloadType_Compressable;
+                                     expectedResponse.payload.body =
+                                         [NSMutableData dataWithLength:314159];
+                                     XCTAssertEqualObjects(message, expectedResponse);
+                                   }
+                                 }
+                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
+
+                                   @synchronized(self) {
+                                     if (error == nil && !address_removed) {
+                                       system([[NSString
+                                           stringWithFormat:@"sudo ifconfig lo0 -alias %@",
+                                                            [[self class] hostAddress]]
+                                           UTF8String]);
+                                       address_removed = YES;
+                                     } else if (error != nil && !address_readded) {
+                                       system([
+                                           [NSString stringWithFormat:@"sudo ifconfig lo0 alias %@",
+                                                                      [[self class] hostAddress]]
+                                           UTF8String]);
+                                       address_readded = YES;
+                                     }
+                                   }
+                                   [completeExpectations[i] fulfill];
+                                 }]
+                 callOptions:nil];
+    [calls addObject:call];
+  }
+
+  for (int i = 0; i < num_rpcs; ++i) {
+    GRPCUnaryProtoCall *call = calls[i];
+    [call start];
+    [NSThread sleepForTimeInterval:0.1f];
+  }
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
+}
+
+- (void)testNetworkFlapWithV1API {
+  NSMutableArray *completeExpectations = [NSMutableArray array];
+  int num_rpcs = 100;
+  __block BOOL address_removed = FALSE;
+  __block BOOL address_readded = FALSE;
+  for (int i = 0; i < num_rpcs; ++i) {
+    [completeExpectations
+        addObject:[self expectationWithDescription:
+                            [NSString stringWithFormat:@"Received response for RPC %d", i]]];
+
+    RMTSimpleRequest *request = [RMTSimpleRequest message];
+    request.responseType = RMTPayloadType_Compressable;
+    request.responseSize = 314159;
+    request.payload.body = [NSMutableData dataWithLength:271828];
+
+    [_service unaryCallWithRequest:request
+                           handler:^(RMTSimpleResponse *response, NSError *error) {
+                             @synchronized(self) {
+                               if (error == nil && !address_removed) {
+                                 system([[NSString stringWithFormat:@"sudo ifconfig lo0 -alias %@",
+                                                                    [[self class] hostAddress]]
+                                     UTF8String]);
+                                 address_removed = YES;
+                               } else if (error != nil && !address_readded) {
+                                 system([[NSString stringWithFormat:@"sudo ifconfig lo0 alias %@",
+                                                                    [[self class] hostAddress]]
+                                     UTF8String]);
+                                 address_readded = YES;
+                               }
+                             }
+
+                             [completeExpectations[i] fulfill];
+                           }];
+
+    [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
+  }
+}
+
+@end
diff --git a/src/objective-c/tests/MacTests/StressTestsCleartext.m b/src/objective-c/tests/MacTests/StressTestsCleartext.m
new file mode 100644 (file)
index 0000000..2b3a861
--- /dev/null
@@ -0,0 +1,68 @@
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#import <GRPCClient/GRPCCall+Tests.h>
+#import <GRPCClient/internal_testing/GRPCCall+InternalTests.h>
+
+#import "StressTests.h"
+
+static NSString *const kHostAddress = @"10.0.0.1";
+
+// The Protocol Buffers encoding overhead of local interop server. Acquired
+// by experiment. Adjust this when server's proto file changes.
+static int32_t kLocalInteropServerOverhead = 10;
+
+/** Tests in InteropTests.m, sending the RPCs to a local cleartext server. */
+@interface StressTestsCleartext : StressTests
+@end
+
+@implementation StressTestsCleartext
+
++ (NSString *)host {
+  return [NSString stringWithFormat:@"%@:5050", kHostAddress];
+}
+
++ (NSString *)hostAddress {
+  return kHostAddress;
+}
+
++ (NSString *)PEMRootCertificates {
+  return nil;
+}
+
++ (NSString *)hostNameOverride {
+  return nil;
+}
+
+- (int32_t)encodingOverhead {
+  return kLocalInteropServerOverhead;  // bytes
+}
+
+- (void)setUp {
+  [super setUp];
+
+  // Register test server as non-SSL.
+  [GRPCCall useInsecureConnectionsForHost:[[self class] host]];
+}
+
++ (GRPCTransportType)transportType {
+  return GRPCTransportTypeInsecure;
+}
+
+@end
diff --git a/src/objective-c/tests/MacTests/StressTestsSSL.m b/src/objective-c/tests/MacTests/StressTestsSSL.m
new file mode 100644 (file)
index 0000000..c5d79fe
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#import <GRPCClient/GRPCCall+Tests.h>
+#import <GRPCClient/internal_testing/GRPCCall+InternalTests.h>
+
+#include "StressTests.h"
+
+static NSString *const kHostAddress = @"10.0.0.1";
+// The Protocol Buffers encoding overhead of local interop server. Acquired
+// by experiment. Adjust this when server's proto file changes.
+static int32_t kLocalInteropServerOverhead = 10;
+
+@interface StressTestsSSL : StressTests
+@end
+
+@implementation StressTestsSSL
+
++ (NSString *)host {
+  return [NSString stringWithFormat:@"%@:5051", kHostAddress];
+}
+
++ (NSString *)hostAddress {
+  return kHostAddress;
+}
+
++ (NSString *)PEMRootCertificates {
+  NSBundle *bundle = [NSBundle bundleForClass:[self class]];
+  NSString *certsPath =
+      [bundle pathForResource:@"TestCertificates.bundle/test-certificates" ofType:@"pem"];
+  NSError *error;
+  return [NSString stringWithContentsOfFile:certsPath encoding:NSUTF8StringEncoding error:&error];
+}
+
++ (NSString *)hostNameOverride {
+  return @"foo.test.google.fr";
+}
+
+- (int32_t)encodingOverhead {
+  return kLocalInteropServerOverhead;  // bytes
+}
+
++ (GRPCTransportType)transportType {
+  return GRPCTransportTypeChttp2BoringSSL;
+}
+
+- (void)setUp {
+  [super setUp];
+
+  // Register test server certificates and name.
+  NSBundle *bundle = [NSBundle bundleForClass:[self class]];
+  NSString *certsPath =
+      [bundle pathForResource:@"TestCertificates.bundle/test-certificates" ofType:@"pem"];
+  [GRPCCall useTestCertsPath:certsPath testName:@"foo.test.google.fr" forHost:[[self class] host]];
+}
+@end
index 8e5f588..34ed668 100644 (file)
@@ -1,5 +1,4 @@
 source 'https://github.com/CocoaPods/Specs.git'
-platform :ios, '8.0'
 
 install! 'cocoapods', :deterministic_uuids => false
 
@@ -17,9 +16,13 @@ GRPC_LOCAL_SRC = '../../..'
   InteropTestsMultipleChannels
   InteropTestsCallOptions
   UnitTests
+  InteropTestsRemoteCFStream
+  InteropTestsLocalSSLCFStream
+  InteropTestsLocalCleartextCFStream
   APIv2Tests
 ).each do |target_name|
   target target_name do
+    platform :ios, '8.0'
     pod 'Protobuf', :path => "#{GRPC_LOCAL_SRC}/third_party/protobuf", :inhibit_warnings => true
 
     pod '!ProtoCompiler',            :path => "#{GRPC_LOCAL_SRC}/src/objective-c"
@@ -40,25 +43,20 @@ GRPC_LOCAL_SRC = '../../..'
   end
 end
 
-%w(
-  InteropTestsRemoteCFStream
-  InteropTestsLocalSSLCFStream
-  InteropTestsLocalCleartextCFStream
-).each do |target_name|
-  target target_name do
-    pod 'Protobuf', :path => "#{GRPC_LOCAL_SRC}/third_party/protobuf", :inhibit_warnings => true
+target 'MacTests' do
+  platform :osx, '10.13'
+  pod 'Protobuf', :path => "#{GRPC_LOCAL_SRC}/third_party/protobuf", :inhibit_warnings => true
 
-    pod '!ProtoCompiler',            :path => "#{GRPC_LOCAL_SRC}/src/objective-c"
-    pod '!ProtoCompiler-gRPCPlugin', :path => "#{GRPC_LOCAL_SRC}/src/objective-c"
+  pod '!ProtoCompiler',            :path => "#{GRPC_LOCAL_SRC}/src/objective-c"
+  pod '!ProtoCompiler-gRPCPlugin', :path => "#{GRPC_LOCAL_SRC}/src/objective-c"
 
-    pod 'BoringSSL-GRPC',                 :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c", :inhibit_warnings => true
+  pod 'BoringSSL-GRPC',       :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c", :inhibit_warnings => true
 
-    pod 'gRPC/CFStream',             :path => GRPC_LOCAL_SRC
-    pod 'gRPC-Core/CFStream-Implementation',       :path => GRPC_LOCAL_SRC
-    pod 'gRPC-RxLibrary', :path => GRPC_LOCAL_SRC
-    pod 'gRPC-ProtoRPC',  :path => GRPC_LOCAL_SRC, :inhibit_warnings => true
-    pod 'RemoteTest', :path => "RemoteTestClient", :inhibit_warnings => true
-  end
+  pod 'gRPC',           :path => GRPC_LOCAL_SRC
+  pod 'gRPC-Core',      :path => GRPC_LOCAL_SRC
+  pod 'gRPC-RxLibrary', :path => GRPC_LOCAL_SRC
+  pod 'gRPC-ProtoRPC',  :path => GRPC_LOCAL_SRC, :inhibit_warnings => true
+  pod 'RemoteTest', :path => "RemoteTestClient", :inhibit_warnings => true
 end
 
 %w(
@@ -66,6 +64,7 @@ end
   CronetUnitTests
 ).each do |target_name|
   target target_name do
+    platform :ios, '8.0'
     pod 'BoringSSL-GRPC', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c", :inhibit_warnings => true
     pod 'CronetFramework', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c"
     pod 'gRPC-Core', :path => GRPC_LOCAL_SRC
@@ -76,6 +75,7 @@ end
 end
 
 target 'ChannelTests' do
+  platform :ios, '8.0'
   pod 'gRPC', :path => GRPC_LOCAL_SRC
   pod 'gRPC-Core', :path => GRPC_LOCAL_SRC
   pod 'BoringSSL-GRPC', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c", :inhibit_warnings => true
@@ -127,11 +127,7 @@ post_install do |installer|
         # GPR_UNREACHABLE_CODE causes "Control may reach end of non-void
         # function" warning
         config.build_settings['GCC_WARN_ABOUT_RETURN_TYPE'] = 'NO'
-        if target.name.include?('CFStream')
-          config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) COCOAPODS=1 GRPC_CFSTREAM=1'
-        else
-          config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) COCOAPODS=1 GRPC_CRONET_WITH_PACKET_COALESCING=1'
-        end
+        config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) COCOAPODS=1 GRPC_CRONET_WITH_PACKET_COALESCING=1 GRPC_CFSTREAM=1'
       end
     end
 
@@ -139,7 +135,7 @@ post_install do |installer|
     # the test target 'InteropTestsRemoteWithCronet'
     # Activate GRPCCall+InternalTests functions for the dedicated build configuration 'Test', which will
     # be used by all test targets using it.
-    if target.name == 'gRPC' || target.name.start_with?('gRPC.')
+    if /gRPC-(mac|i)OS/.match(target.name)
       target.build_configurations.each do |config|
         if config.name == 'Cronet'
           config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) COCOAPODS=1 GRPC_COMPILE_WITH_CRONET=1 GRPC_TEST_OBJC=1'
@@ -150,9 +146,7 @@ post_install do |installer|
     end
 
     # Enable NSAssert on gRPC
-    if target.name == 'gRPC' || target.name.start_with?('gRPC.') ||
-        target.name == 'ProtoRPC' || target.name.start_with?('ProtoRPC.') ||
-        target.name == 'RxLibrary' || target.name.start_with?('RxLibrary.') 
+    if /(gRPC|ProtoRPC|RxLibrary)-(mac|i)OS/.match(target.name)
       target.build_configurations.each do |config|
         if config.name != 'Release'
           config.build_settings['ENABLE_NS_ASSERTIONS'] = 'YES'
index ad83481..93b56d5 100644 (file)
@@ -14,20 +14,34 @@ Pod::Spec.new do |s|
   s.dependency "!ProtoCompiler-gRPCPlugin"
 
   repo_root = '../../../..'
-  bin_dir = "#{repo_root}/bins/$CONFIG"
+  config = ENV['CONFIG'] || 'opt'
+  bin_dir = "#{repo_root}/bins/#{config}"
 
   protoc = "#{bin_dir}/protobuf/protoc"
   well_known_types_dir = "#{repo_root}/third_party/protobuf/src"
   plugin = "#{bin_dir}/grpc_objective_c_plugin"
 
   s.prepare_command = <<-CMD
-    #{protoc} \
-        --plugin=protoc-gen-grpc=#{plugin} \
-        --objc_out=. \
-        --grpc_out=. \
-        -I . \
-        -I #{well_known_types_dir} \
-        *.proto
+    if [ -f #{protoc} ]; then
+      #{protoc} \
+          --plugin=protoc-gen-grpc=#{plugin} \
+          --objc_out=. \
+          --grpc_out=. \
+          -I . \
+          -I #{well_known_types_dir} \
+          *.proto
+    else
+      # protoc was not found bin_dir, use installed version instead
+      (>&2 echo "\nWARNING: Using installed version of protoc. It might be incompatible with gRPC")
+
+      protoc \
+          --plugin=protoc-gen-grpc=#{plugin} \
+          --objc_out=. \
+          --grpc_out=. \
+          -I . \
+          -I #{well_known_types_dir} \
+          *.proto
+    fi
   CMD
 
   s.subspec "Messages" do |ms|
index ecd914c..2faf57e 100644 (file)
@@ -16,7 +16,6 @@
  *
  */
 
-#import <UIKit/UIKit.h>
 #import <XCTest/XCTest.h>
 
 #import <RxLibrary/GRXBufferedPipe.h>
@@ -44,9 +43,9 @@
 
 - (GRXSingleHandler)block {
   return ^(id value, NSError *errorOrNil) {
-    ++_timesCalled;
-    _value = value;
-    _errorOrNil = errorOrNil;
+    ++self->_timesCalled;
+    self->_value = value;
+    self->_errorOrNil = errorOrNil;
   };
 }
 @end
index b6762cc..27a617c 100644 (file)
                6C1A3F81CCF7C998B4813EFD /* libPods-InteropTestsCallOptions.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AF3FC2CFFE7B0961823BC740 /* libPods-InteropTestsCallOptions.a */; };
                886717A79EFF774F356798E6 /* libPods-InteropTestsMultipleChannels.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 355D0E30AD224763BC9519F4 /* libPods-InteropTestsMultipleChannels.a */; };
                91D4B3C85B6D8562F409CB48 /* libPods-InteropTestsLocalSSLCFStream.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F3AB031E0E26AC8EF30A2A2A /* libPods-InteropTestsLocalSSLCFStream.a */; };
+               953CD2942A3A6D6CE695BE87 /* libPods-MacTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 276873A05AC5479B60DF6079 /* libPods-MacTests.a */; };
                98478C9F42329DF769A45B6C /* libPods-APIv2Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B6AD69CACF67505B0F028E92 /* libPods-APIv2Tests.a */; };
+               B071230B22669EED004B64A1 /* StressTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B071230A22669EED004B64A1 /* StressTests.m */; };
+               B0BB3F02225E7A3C008DA580 /* InteropTestsLocalSSL.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */; };
+               B0BB3F03225E7A44008DA580 /* InteropTestsLocalCleartext.m in Sources */ = {isa = PBXBuildFile; fileRef = 63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */; };
+               B0BB3F04225E7A8D008DA580 /* RxLibraryUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */; };
+               B0BB3F05225E7A9F008DA580 /* InteropTestsRemote.m in Sources */ = {isa = PBXBuildFile; fileRef = 6379CC4F1BE16703001BC0A1 /* InteropTestsRemote.m */; };
+               B0BB3F06225E7AAD008DA580 /* GRPCClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */; };
+               B0BB3F07225E7AB5008DA580 /* APIv2Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E3B95A421CAC6C500C0A151 /* APIv2Tests.m */; };
+               B0BB3F08225E7ABA008DA580 /* UnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E0282E8215AA697007AC99D /* UnitTests.m */; };
+               B0BB3F0A225EA511008DA580 /* TestCertificates.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */; };
+               B0BB3F0B225EB110008DA580 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; };
+               B0D39B9A2266F3CB00A4078D /* StressTestsSSL.m in Sources */ = {isa = PBXBuildFile; fileRef = B0D39B992266F3CB00A4078D /* StressTestsSSL.m */; };
+               B0D39B9C2266FF9800A4078D /* StressTestsCleartext.m in Sources */ = {isa = PBXBuildFile; fileRef = B0D39B9B2266FF9800A4078D /* StressTestsCleartext.m */; };
                BC111C80CBF7068B62869352 /* libPods-InteropTestsRemoteCFStream.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F44AC3F44E3491A8C0D890FE /* libPods-InteropTestsRemoteCFStream.a */; };
                C3D6F4270A2FFF634D8849ED /* libPods-InteropTestsLocalCleartextCFStream.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BDA4BA011779D5D25B5618C /* libPods-InteropTestsLocalCleartextCFStream.a */; };
                CCF5C0719EF608276AE16374 /* libPods-UnitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 22A3EBB488699C8CEA19707B /* libPods-UnitTests.a */; };
                12B238CD1702393C2BA5DE80 /* Pods-APIv2Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-APIv2Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-APIv2Tests/Pods-APIv2Tests.release.xcconfig"; sourceTree = "<group>"; };
                14B09A58FEE53A7A6B838920 /* Pods-InteropTestsLocalSSL.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalSSL.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalSSL/Pods-InteropTestsLocalSSL.cronet.xcconfig"; sourceTree = "<group>"; };
                1588C85DEAF7FC0ACDEA4C02 /* Pods-InteropTestsLocalCleartext.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartext.test.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext.test.xcconfig"; sourceTree = "<group>"; };
+               16A2E4C5839C83FBDA63881F /* Pods-MacTests.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MacTests.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-MacTests/Pods-MacTests.cronet.xcconfig"; sourceTree = "<group>"; };
                17F60BF2871F6AF85FB3FA12 /* Pods-InteropTestsRemoteWithCronet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemoteWithCronet.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemoteWithCronet/Pods-InteropTestsRemoteWithCronet.debug.xcconfig"; sourceTree = "<group>"; };
+               1E43EAE443CBB4482B1EB071 /* Pods-MacTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MacTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-MacTests/Pods-MacTests.release.xcconfig"; sourceTree = "<group>"; };
+               1F5E788FBF9A4A06EB9E1ACD /* Pods-MacTests.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MacTests.test.xcconfig"; path = "Pods/Target Support Files/Pods-MacTests/Pods-MacTests.test.xcconfig"; sourceTree = "<group>"; };
                20DFF2F3C97EF098FE5A3171 /* libPods-Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
                22A3EBB488699C8CEA19707B /* libPods-UnitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-UnitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
                2650FEF00956E7924772F9D9 /* Pods-InteropTestsMultipleChannels.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsMultipleChannels.release.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsMultipleChannels/Pods-InteropTestsMultipleChannels.release.xcconfig"; sourceTree = "<group>"; };
+               276873A05AC5479B60DF6079 /* libPods-MacTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MacTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
                2B89F3037963E6EDDD48D8C3 /* Pods-InteropTestsRemoteWithCronet.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemoteWithCronet.test.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemoteWithCronet/Pods-InteropTestsRemoteWithCronet.test.xcconfig"; sourceTree = "<group>"; };
                303F4A17EB1650FC44603D17 /* Pods-InteropTestsRemoteCFStream.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemoteCFStream.release.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemoteCFStream/Pods-InteropTestsRemoteCFStream.release.xcconfig"; sourceTree = "<group>"; };
                32748C4078AEB05F8F954361 /* Pods-InteropTestsRemoteCFStream.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemoteCFStream.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemoteCFStream/Pods-InteropTestsRemoteCFStream.debug.xcconfig"; sourceTree = "<group>"; };
                AA7CB64B4DD9915AE7C03163 /* Pods-InteropTestsLocalCleartext.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartext.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext.cronet.xcconfig"; sourceTree = "<group>"; };
                AC414EF7A6BF76ED02B6E480 /* Pods-InteropTestsRemoteWithCronet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemoteWithCronet.release.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemoteWithCronet/Pods-InteropTestsRemoteWithCronet.release.xcconfig"; sourceTree = "<group>"; };
                AF3FC2CFFE7B0961823BC740 /* libPods-InteropTestsCallOptions.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-InteropTestsCallOptions.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+               B071230A22669EED004B64A1 /* StressTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StressTests.m; sourceTree = "<group>"; };
+               B0BB3EF7225E795F008DA580 /* MacTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MacTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+               B0BB3EFB225E795F008DA580 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               B0C5FC172267C77200F192BE /* StressTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StressTests.h; sourceTree = "<group>"; };
+               B0D39B992266F3CB00A4078D /* StressTestsSSL.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StressTestsSSL.m; sourceTree = "<group>"; };
+               B0D39B9B2266FF9800A4078D /* StressTestsCleartext.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StressTestsCleartext.m; sourceTree = "<group>"; };
                B226619DC4E709E0FFFF94B8 /* Pods-CronetUnitTests.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CronetUnitTests.test.xcconfig"; path = "Pods/Target Support Files/Pods-CronetUnitTests/Pods-CronetUnitTests.test.xcconfig"; sourceTree = "<group>"; };
                B6AD69CACF67505B0F028E92 /* libPods-APIv2Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-APIv2Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
                B94C27C06733CF98CE1B2757 /* Pods-AllTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AllTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AllTests/Pods-AllTests.debug.xcconfig"; sourceTree = "<group>"; };
                DC3CA1D948F068E76957A861 /* Pods-InteropTestsRemote.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemote.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemote/Pods-InteropTestsRemote.debug.xcconfig"; sourceTree = "<group>"; };
                E1486220285AF123EB124008 /* Pods-InteropTestsLocalCleartext.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartext.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext.debug.xcconfig"; sourceTree = "<group>"; };
                E1E7660656D902104F728892 /* Pods-UnitTests.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-UnitTests/Pods-UnitTests.cronet.xcconfig"; sourceTree = "<group>"; };
+               E3ACD4D5902745976D9C2229 /* Pods-MacTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MacTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MacTests/Pods-MacTests.debug.xcconfig"; sourceTree = "<group>"; };
                E4275A759BDBDF143B9B438F /* Pods-InteropTestsRemote.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemote.release.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemote/Pods-InteropTestsRemote.release.xcconfig"; sourceTree = "<group>"; };
                E4FD4606D4AB8D5A314D72F0 /* Pods-InteropTestsLocalCleartextCFStream.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartextCFStream.test.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartextCFStream/Pods-InteropTestsLocalCleartextCFStream.test.xcconfig"; sourceTree = "<group>"; };
                E7E4D3FD76E3B745D992AF5F /* Pods-AllTests.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AllTests.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-AllTests/Pods-AllTests.cronet.xcconfig"; sourceTree = "<group>"; };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               B0BB3EF4225E795F008DA580 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               953CD2942A3A6D6CE695BE87 /* libPods-MacTests.a in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
                                AF3FC2CFFE7B0961823BC740 /* libPods-InteropTestsCallOptions.a */,
                                22A3EBB488699C8CEA19707B /* libPods-UnitTests.a */,
                                B6AD69CACF67505B0F028E92 /* libPods-APIv2Tests.a */,
+                               276873A05AC5479B60DF6079 /* libPods-MacTests.a */,
                        );
                        name = Frameworks;
                        sourceTree = "<group>";
                                51F2A64B7AADBA1B225B132E /* Pods-APIv2Tests.test.xcconfig */,
                                8C233E85C3EB45B3CAE52EDF /* Pods-APIv2Tests.cronet.xcconfig */,
                                12B238CD1702393C2BA5DE80 /* Pods-APIv2Tests.release.xcconfig */,
+                               E3ACD4D5902745976D9C2229 /* Pods-MacTests.debug.xcconfig */,
+                               1F5E788FBF9A4A06EB9E1ACD /* Pods-MacTests.test.xcconfig */,
+                               16A2E4C5839C83FBDA63881F /* Pods-MacTests.cronet.xcconfig */,
+                               1E43EAE443CBB4482B1EB071 /* Pods-MacTests.release.xcconfig */,
                        );
                        name = Pods;
                        sourceTree = "<group>";
                                5E7D71B3210B9EC9001EA6BA /* InteropTestsCallOptions */,
                                5E0282E7215AA697007AC99D /* UnitTests */,
                                5E3B95A321CAC6C500C0A151 /* APIv2Tests */,
+                               B0BB3EF8225E795F008DA580 /* MacTests */,
                                635697C81B14FC11007A7283 /* Products */,
                                51E4650F34F854F41FF053B3 /* Pods */,
                                136D535E19727099B941D7B1 /* Frameworks */,
                                5E7D71B2210B9EC8001EA6BA /* InteropTestsCallOptions.xctest */,
                                5E0282E6215AA697007AC99D /* UnitTests.xctest */,
                                5E3B95A221CAC6C500C0A151 /* APIv2Tests.xctest */,
+                               B0BB3EF7225E795F008DA580 /* MacTests.xctest */,
                        );
                        name = Products;
                        sourceTree = "<group>";
                        name = "Supporting Files";
                        sourceTree = "<group>";
                };
+               B0BB3EF8225E795F008DA580 /* MacTests */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B0BB3EFB225E795F008DA580 /* Info.plist */,
+                               B071230A22669EED004B64A1 /* StressTests.m */,
+                               B0D39B992266F3CB00A4078D /* StressTestsSSL.m */,
+                               B0D39B9B2266FF9800A4078D /* StressTestsCleartext.m */,
+                               B0C5FC172267C77200F192BE /* StressTests.h */,
+                       );
+                       path = MacTests;
+                       sourceTree = "<group>";
+               };
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
                        productReference = 63DC84431BE152B5000708E8 /* InteropTestsLocalCleartext.xctest */;
                        productType = "com.apple.product-type.bundle.unit-test";
                };
+               B0BB3EF6225E795F008DA580 /* MacTests */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = B0BB3EFC225E795F008DA580 /* Build configuration list for PBXNativeTarget "MacTests" */;
+                       buildPhases = (
+                               E5B20F69559C6AE299DFEA7C /* [CP] Check Pods Manifest.lock */,
+                               B0BB3EF3225E795F008DA580 /* Sources */,
+                               B0BB3EF4225E795F008DA580 /* Frameworks */,
+                               B0BB3EF5225E795F008DA580 /* Resources */,
+                               452FDC3918FEC23ECAFD31EC /* [CP] Copy Pods Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = MacTests;
+                       productName = MacTests;
+                       productReference = B0BB3EF7225E795F008DA580 /* MacTests.xctest */;
+                       productType = "com.apple.product-type.bundle.unit-test";
+               };
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
                                        63DC84421BE152B5000708E8 = {
                                                CreatedOnToolsVersion = 7.0.1;
                                        };
+                                       B0BB3EF6225E795F008DA580 = {
+                                               CreatedOnToolsVersion = 10.1;
+                                               ProvisioningStyle = Automatic;
+                                       };
                                };
                        };
                        buildConfigurationList = 635697C21B14FC11007A7283 /* Build configuration list for PBXProject "Tests" */;
                                5E7D71B1210B9EC8001EA6BA /* InteropTestsCallOptions */,
                                5E0282E5215AA697007AC99D /* UnitTests */,
                                5E3B95A121CAC6C500C0A151 /* APIv2Tests */,
+                               B0BB3EF6225E795F008DA580 /* MacTests */,
                        );
                };
 /* End PBXProject section */
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               B0BB3EF5225E795F008DA580 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               B0BB3F0A225EA511008DA580 /* TestCertificates.bundle in Resources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
                        );
                        inputPaths = (
                                "${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsMultipleChannels/Pods-InteropTestsMultipleChannels-resources.sh",
-                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle",
+                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-iOS/gRPCCertificates.bundle",
                        );
                        name = "[CP] Copy Pods Resources";
                        outputPaths = (
                        );
                        inputPaths = (
                                "${SRCROOT}/Pods/Target Support Files/Pods-ChannelTests/Pods-ChannelTests-resources.sh",
-                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle",
+                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-iOS/gRPCCertificates.bundle",
                        );
                        name = "[CP] Copy Pods Resources";
                        outputPaths = (
                        shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsRemoteWithCronet/Pods-InteropTestsRemoteWithCronet-frameworks.sh\"\n";
                        showEnvVarsInLog = 0;
                };
+               452FDC3918FEC23ECAFD31EC /* [CP] Copy Pods Resources */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       inputFileListPaths = (
+                       );
+                       inputPaths = (
+                               "${SRCROOT}/Pods/Target Support Files/Pods-MacTests/Pods-MacTests-resources.sh",
+                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-macOS/gRPCCertificates.bundle",
+                       );
+                       name = "[CP] Copy Pods Resources";
+                       outputFileListPaths = (
+                       );
+                       outputPaths = (
+                               "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle",
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+                       shellPath = /bin/sh;
+                       shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MacTests/Pods-MacTests-resources.sh\"\n";
+                       showEnvVarsInLog = 0;
+               };
                483CDBBAEAEFCB530ADDDDD5 /* [CP] Check Pods Manifest.lock */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 2147483647;
                        );
                        inputPaths = (
                                "${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsLocalSSLCFStream/Pods-InteropTestsLocalSSLCFStream-resources.sh",
-                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC.default-CFStream/gRPCCertificates.bundle",
+                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-iOS/gRPCCertificates.bundle",
                        );
                        name = "[CP] Copy Pods Resources";
                        outputPaths = (
                        );
                        inputPaths = (
                                "${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsLocalSSL/Pods-InteropTestsLocalSSL-resources.sh",
-                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle",
+                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-iOS/gRPCCertificates.bundle",
                        );
                        name = "[CP] Copy Pods Resources";
                        outputPaths = (
                        );
                        inputPaths = (
                                "${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsCallOptions/Pods-InteropTestsCallOptions-resources.sh",
-                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle",
+                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-iOS/gRPCCertificates.bundle",
                        );
                        name = "[CP] Copy Pods Resources";
                        outputPaths = (
                        );
                        inputPaths = (
                                "${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext-resources.sh",
-                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle",
+                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-iOS/gRPCCertificates.bundle",
                        );
                        name = "[CP] Copy Pods Resources";
                        outputPaths = (
                        );
                        inputPaths = (
                                "${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsRemoteCFStream/Pods-InteropTestsRemoteCFStream-resources.sh",
-                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC.default-CFStream/gRPCCertificates.bundle",
+                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-iOS/gRPCCertificates.bundle",
                        );
                        name = "[CP] Copy Pods Resources";
                        outputPaths = (
                        );
                        inputPaths = (
                                "${SRCROOT}/Pods/Target Support Files/Pods-UnitTests/Pods-UnitTests-resources.sh",
-                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle",
+                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-iOS/gRPCCertificates.bundle",
                        );
                        name = "[CP] Copy Pods Resources";
                        outputPaths = (
                        );
                        inputPaths = (
                                "${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsLocalCleartextCFStream/Pods-InteropTestsLocalCleartextCFStream-resources.sh",
-                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC.default-CFStream/gRPCCertificates.bundle",
+                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-iOS/gRPCCertificates.bundle",
                        );
                        name = "[CP] Copy Pods Resources";
                        outputPaths = (
                        );
                        inputPaths = (
                                "${SRCROOT}/Pods/Target Support Files/Pods-AllTests/Pods-AllTests-resources.sh",
-                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle",
+                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-iOS/gRPCCertificates.bundle",
                        );
                        name = "[CP] Copy Pods Resources";
                        outputPaths = (
                        );
                        inputPaths = (
                                "${SRCROOT}/Pods/Target Support Files/Pods-APIv2Tests/Pods-APIv2Tests-resources.sh",
-                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle",
+                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-iOS/gRPCCertificates.bundle",
                        );
                        name = "[CP] Copy Pods Resources";
                        outputFileListPaths = (
                        );
                        inputPaths = (
                                "${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsRemote/Pods-InteropTestsRemote-resources.sh",
-                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle",
+                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-iOS/gRPCCertificates.bundle",
                        );
                        name = "[CP] Copy Pods Resources";
                        outputPaths = (
                        );
                        inputPaths = (
                                "${SRCROOT}/Pods/Target Support Files/Pods-RxLibraryUnitTests/Pods-RxLibraryUnitTests-resources.sh",
-                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle",
+                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-iOS/gRPCCertificates.bundle",
                        );
                        name = "[CP] Copy Pods Resources";
                        outputPaths = (
                        );
                        inputPaths = (
                                "${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsRemoteWithCronet/Pods-InteropTestsRemoteWithCronet-resources.sh",
-                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle",
+                               "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-iOS/gRPCCertificates.bundle",
                        );
                        name = "[CP] Copy Pods Resources";
                        outputPaths = (
                        shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsRemoteWithCronet/Pods-InteropTestsRemoteWithCronet-resources.sh\"\n";
                        showEnvVarsInLog = 0;
                };
+               E5B20F69559C6AE299DFEA7C /* [CP] Check Pods Manifest.lock */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       inputFileListPaths = (
+                       );
+                       inputPaths = (
+                               "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+                               "${PODS_ROOT}/Manifest.lock",
+                       );
+                       name = "[CP] Check Pods Manifest.lock";
+                       outputFileListPaths = (
+                       );
+                       outputPaths = (
+                               "$(DERIVED_FILE_DIR)/Pods-MacTests-checkManifestLockResult.txt",
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+                       shellPath = /bin/sh;
+                       shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+                       showEnvVarsInLog = 0;
+               };
                E63468C760D0724F18861822 /* [CP] Embed Pods Frameworks */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 2147483647;
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               B0BB3EF3225E795F008DA580 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               B0BB3F07225E7AB5008DA580 /* APIv2Tests.m in Sources */,
+                               B0BB3F08225E7ABA008DA580 /* UnitTests.m in Sources */,
+                               B071230B22669EED004B64A1 /* StressTests.m in Sources */,
+                               B0BB3F05225E7A9F008DA580 /* InteropTestsRemote.m in Sources */,
+                               B0D39B9A2266F3CB00A4078D /* StressTestsSSL.m in Sources */,
+                               B0BB3F03225E7A44008DA580 /* InteropTestsLocalCleartext.m in Sources */,
+                               B0BB3F04225E7A8D008DA580 /* RxLibraryUnitTests.m in Sources */,
+                               B0D39B9C2266FF9800A4078D /* StressTestsCleartext.m in Sources */,
+                               B0BB3F0B225EB110008DA580 /* InteropTests.m in Sources */,
+                               B0BB3F02225E7A3C008DA580 /* InteropTestsLocalSSL.m in Sources */,
+                               B0BB3F06225E7AAD008DA580 /* GRPCClientTests.m in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
                        };
                        name = Release;
                };
+               B0BB3EFD225E795F008DA580 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = E3ACD4D5902745976D9C2229 /* Pods-MacTests.debug.xcconfig */;
+                       buildSettings = {
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+                               CLANG_ENABLE_OBJC_WEAK = YES;
+                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+                               CLANG_WARN_COMMA = YES;
+                               CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+                               CLANG_WARN_INFINITE_RECURSION = YES;
+                               CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+                               CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+                               CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+                               CLANG_WARN_STRICT_PROTOTYPES = YES;
+                               CLANG_WARN_SUSPICIOUS_MOVE = YES;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+                               CODE_SIGN_IDENTITY = "-";
+                               CODE_SIGN_STYLE = Automatic;
+                               COMBINE_HIDPI_IMAGES = YES;
+                               DEBUG_INFORMATION_FORMAT = dwarf;
+                               ENABLE_TESTABILITY = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu11;
+                               INFOPLIST_FILE = MacTests/Info.plist;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+                               MACOSX_DEPLOYMENT_TARGET = 10.13;
+                               MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+                               MTL_FAST_MATH = YES;
+                               PRODUCT_BUNDLE_IDENTIFIER = io.grpc.MacTests;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx;
+                       };
+                       name = Debug;
+               };
+               B0BB3EFE225E795F008DA580 /* Test */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 1F5E788FBF9A4A06EB9E1ACD /* Pods-MacTests.test.xcconfig */;
+                       buildSettings = {
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+                               CLANG_ENABLE_OBJC_WEAK = YES;
+                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+                               CLANG_WARN_COMMA = YES;
+                               CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+                               CLANG_WARN_INFINITE_RECURSION = YES;
+                               CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+                               CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+                               CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+                               CLANG_WARN_STRICT_PROTOTYPES = YES;
+                               CLANG_WARN_SUSPICIOUS_MOVE = YES;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+                               CODE_SIGN_IDENTITY = "-";
+                               CODE_SIGN_STYLE = Automatic;
+                               COMBINE_HIDPI_IMAGES = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu11;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "$(inherited)",
+                                       "COCOAPODS=1",
+                                       "GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1",
+                                       "PB_FIELD_32BIT=1",
+                                       "PB_NO_PACKED_STRUCTS=1",
+                                       "PB_ENABLE_MALLOC=1",
+                                       "GRPC_TEST_OBJC=1",
+                               );
+                               INFOPLIST_FILE = MacTests/Info.plist;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+                               MACOSX_DEPLOYMENT_TARGET = 10.13;
+                               MTL_FAST_MATH = YES;
+                               PRODUCT_BUNDLE_IDENTIFIER = io.grpc.MacTests;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx;
+                       };
+                       name = Test;
+               };
+               B0BB3EFF225E795F008DA580 /* Cronet */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 16A2E4C5839C83FBDA63881F /* Pods-MacTests.cronet.xcconfig */;
+                       buildSettings = {
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+                               CLANG_ENABLE_OBJC_WEAK = YES;
+                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+                               CLANG_WARN_COMMA = YES;
+                               CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+                               CLANG_WARN_INFINITE_RECURSION = YES;
+                               CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+                               CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+                               CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+                               CLANG_WARN_STRICT_PROTOTYPES = YES;
+                               CLANG_WARN_SUSPICIOUS_MOVE = YES;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+                               CODE_SIGN_IDENTITY = "-";
+                               CODE_SIGN_STYLE = Automatic;
+                               COMBINE_HIDPI_IMAGES = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu11;
+                               INFOPLIST_FILE = MacTests/Info.plist;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+                               MACOSX_DEPLOYMENT_TARGET = 10.13;
+                               MTL_FAST_MATH = YES;
+                               PRODUCT_BUNDLE_IDENTIFIER = io.grpc.MacTests;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx;
+                       };
+                       name = Cronet;
+               };
+               B0BB3F00225E795F008DA580 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 1E43EAE443CBB4482B1EB071 /* Pods-MacTests.release.xcconfig */;
+                       buildSettings = {
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+                               CLANG_ENABLE_OBJC_WEAK = YES;
+                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+                               CLANG_WARN_COMMA = YES;
+                               CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+                               CLANG_WARN_INFINITE_RECURSION = YES;
+                               CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+                               CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+                               CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+                               CLANG_WARN_STRICT_PROTOTYPES = YES;
+                               CLANG_WARN_SUSPICIOUS_MOVE = YES;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+                               CODE_SIGN_IDENTITY = "-";
+                               CODE_SIGN_STYLE = Automatic;
+                               COMBINE_HIDPI_IMAGES = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu11;
+                               INFOPLIST_FILE = MacTests/Info.plist;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+                               MACOSX_DEPLOYMENT_TARGET = 10.13;
+                               MTL_FAST_MATH = YES;
+                               PRODUCT_BUNDLE_IDENTIFIER = io.grpc.MacTests;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx;
+                       };
+                       name = Release;
+               };
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
+               B0BB3EFC225E795F008DA580 /* Build configuration list for PBXNativeTarget "MacTests" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               B0BB3EFD225E795F008DA580 /* Debug */,
+                               B0BB3EFE225E795F008DA580 /* Test */,
+                               B0BB3EFF225E795F008DA580 /* Cronet */,
+                               B0BB3F00225E795F008DA580 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
 /* End XCConfigurationList section */
        };
        rootObject = 635697BF1B14FC11007A7283 /* Project object */;
index b0bb933..a2560fe 100644 (file)
@@ -26,7 +26,6 @@
       buildConfiguration = "Test"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      language = ""
       shouldUseLaunchSchemeArgsEnv = "YES">
       <Testables>
          <TestableReference
@@ -67,7 +66,6 @@
       buildConfiguration = "Test"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      language = ""
       launchStyle = "0"
       useCustomWorkingDirectory = "NO"
       ignoresPersistentStateOnLaunch = "NO"
diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/MacTests.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/MacTests.xcscheme
new file mode 100644 (file)
index 0000000..f84ac7f
--- /dev/null
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1010"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "NO"
+            buildForArchiving = "NO"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "B0BB3EF6225E795F008DA580"
+               BuildableName = "MacTests.xctest"
+               BlueprintName = "MacTests"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Test"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "B0BB3EF6225E795F008DA580"
+               BuildableName = "MacTests.xctest"
+               BlueprintName = "MacTests"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+            <SkippedTests>
+               <Test
+                  Identifier = "InteropTests">
+               </Test>
+               <Test
+                  Identifier = "StressTests">
+               </Test>
+            </SkippedTests>
+         </TestableReference>
+      </Testables>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "B0BB3EF6225E795F008DA580"
+            BuildableName = "MacTests.xctest"
+            BlueprintName = "MacTests"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "B0BB3EF6225E795F008DA580"
+            BuildableName = "MacTests.xctest"
+            BlueprintName = "MacTests"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme
new file mode 100644 (file)
index 0000000..77f567d
--- /dev/null
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1010"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "635697C61B14FC11007A7283"
+               BuildableName = "libTests.a"
+               BlueprintName = "Tests"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "635697C61B14FC11007A7283"
+            BuildableName = "libTests.a"
+            BlueprintName = "Tests"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "635697C61B14FC11007A7283"
+            BuildableName = "libTests.a"
+            BlueprintName = "Tests"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
index 57e686d..4dcb8c0 100644 (file)
@@ -20,7 +20,7 @@
 
 #import <GRPCClient/GRPCCall.h>
 
-#import "src/objective-c/GRPCClient/private/NSError+GRPC.h"
+#import "../../GRPCClient/private/NSError+GRPC.h"
 
 @interface UnitTests : XCTestCase
 
index f6fea96..8c768cb 100755 (executable)
@@ -195,4 +195,17 @@ xcodebuild \
     | egrep -v '^$' \
     | egrep -v "(GPBDictionary|GPBArray)" -
 
+echo "TIME:  $(date)"
+xcodebuild \
+    -workspace Tests.xcworkspace \
+    -scheme MacTests \
+    -destination platform=macOS \
+    HOST_PORT_LOCALSSL=localhost:5051 \
+    HOST_PORT_LOCAL=localhost:5050 \
+    HOST_PORT_REMOTE=grpc-test.sandbox.googleapis.com \
+    test \
+    | egrep -v "$XCODEBUILD_FILTER" \
+    | egrep -v '^$' \
+    | egrep -v "(GPBDictionary|GPBArray)" -
+
 exit 0
index 39d38a9..30f9ad1 100644 (file)
@@ -22,5 +22,5 @@
 // instead. This file can be regenerated from the template by running
 // `tools/buildgen/generate_projects.sh`.
 
-#define GRPC_OBJC_VERSION_STRING @"1.20.1"
+#define GRPC_OBJC_VERSION_STRING @"1.21.0"
 #define GRPC_C_VERSION_STRING @"7.0.0"
index 3ae0ce5..a9d0aeb 100644 (file)
@@ -2,7 +2,7 @@
   "name": "grpc/grpc-dev",
   "description": "gRPC library for PHP - for Developement use only",
   "license": "Apache-2.0",
-  "version": "1.20.1",
+  "version": "1.21.0",
   "require": {
     "php": ">=5.5.0",
     "google/protobuf": "^v3.3.0"
index 3aaf1d8..33adae4 100644 (file)
@@ -20,6 +20,6 @@
 #ifndef VERSION_H
 #define VERSION_H
 
-#define PHP_GRPC_VERSION "1.20.1"
+#define PHP_GRPC_VERSION "1.21.0"
 
 #endif /* VERSION_H */
index b6b485e..1d80ec2 100644 (file)
@@ -25,6 +25,11 @@ grpc_proto_library(
     well_known_protos = True,
 )
 
+proto_library(
+    name = "channelz_proto_descriptors",
+    srcs = ["channelz.proto"],
+)
+
 filegroup(
     name = "channelz_proto_file",
     srcs = [
index 38a7d99..fc58e8a 100644 (file)
@@ -23,6 +23,11 @@ grpc_proto_library(
     srcs = ["health.proto"],
 )
 
+proto_library(
+    name = "health_proto_descriptor",
+    srcs = ["health.proto"],
+)
+
 filegroup(
     name = "health_proto_file",
     srcs = [
index 4d919d0..5424c0d 100644 (file)
@@ -23,6 +23,11 @@ grpc_proto_library(
     srcs = ["reflection.proto"],
 )
 
+proto_library(
+    name = "reflection_proto_descriptor",
+    srcs = ["reflection.proto"],
+)
+
 filegroup(
     name = "reflection_proto_file",
     srcs = [
index 9876d51..727c99c 100644 (file)
@@ -16,7 +16,7 @@ licenses(["notice"])  # Apache v2
 
 load("//bazel:grpc_build_system.bzl", "grpc_proto_library", "grpc_package")
 load("@grpc_python_dependencies//:requirements.bzl", "requirement")
-load("@org_pubref_rules_protobuf//python:rules.bzl", "py_proto_library")
+load("//bazel:python_rules.bzl", "py_proto_library")
 
 grpc_package(name = "testing", visibility = "public")
 
@@ -61,13 +61,14 @@ grpc_proto_library(
     has_services = False,
 )
 
+proto_library(
+    name = "empty_proto_descriptor",
+    srcs = ["empty.proto"],
+)
+
 py_proto_library(
     name = "py_empty_proto",
-    protos = ["empty.proto",],
-    with_grpc = True,
-    deps = [
-        requirement('protobuf'),
-    ],
+    deps = [":empty_proto_descriptor"],
 )
 
 grpc_proto_library(
@@ -76,13 +77,14 @@ grpc_proto_library(
     has_services = False,
 )
 
+proto_library(
+    name = "messages_proto_descriptor",
+    srcs = ["messages.proto"],
+)
+
 py_proto_library(
     name = "py_messages_proto",
-    protos = ["messages.proto",],
-    with_grpc = True,
-    deps = [
-        requirement('protobuf'),
-    ],
+    deps = [":messages_proto_descriptor"],
 )
 
 grpc_proto_library(
@@ -144,16 +146,50 @@ grpc_proto_library(
     ],
 )
 
+# Test that grpc_proto_library/cc_grpc_library can consume generated files
+genrule(
+    name = "messages_gen_proto_file",
+    srcs = ["messages.proto"],
+    outs = ["messages_gen.proto"],
+    cmd = "cp $< $@",
+)
+
+grpc_proto_library(
+    name = "messages_gen_proto",
+    srcs = ["messages_gen_proto_file"],
+    has_services = False,
+)
+
+genrule(
+    name = "test_gen_proto_file",
+    srcs = ["test.proto"],
+    outs = ["test_gen.proto"],
+    cmd = "sed 's/messages.proto/messages_gen.proto/' $< > $@",
+)
+
+# Consume generated files in srcs and in deps
+grpc_proto_library(
+    name = "test_gen_proto",
+    srcs = ["test_gen_proto_file"],
+    deps = [
+        "empty_proto",
+        "messages_gen_proto",
+    ],
+)
+
+proto_library(
+    name = "test_proto_descriptor",
+    srcs = ["test.proto"],
+    deps = [
+        ":empty_proto_descriptor",
+        ":messages_proto_descriptor",
+    ],
+)
+
 py_proto_library(
     name = "py_test_proto",
-    protos = ["test.proto",],
-    with_grpc = True,
     deps = [
-        requirement('protobuf'),
-    ],
-    proto_deps = [
-        ":py_empty_proto",
-        ":py_messages_proto",
+        ":test_proto_descriptor",
     ]
 )
 
index 2f93530..2c773ea 100644 (file)
@@ -17,6 +17,8 @@ syntax = "proto3";
 
 package grpc.testing;
 
+option cc_enable_arenas = true;
+
 // Message to be echoed back serialized in trailer.
 message DebugInfo {
   repeated string stack_entries = 1;
@@ -47,6 +49,7 @@ message RequestParams {
   ErrorStatus expected_error = 14;
   int32 server_sleep_us = 15; // Amount to sleep when invoking server
   int32 backend_channel_idx = 16; // which backend to send request to
+  bool echo_metadata_initially = 17;
 }
 
 message EchoRequest {
index c4c4f00..e939c52 100644 (file)
@@ -1,30 +1,32 @@
 load("@grpc_python_dependencies//:requirements.bzl", "requirement")
-load("@org_pubref_rules_protobuf//python:rules.bzl", "py_proto_library")
 
 package(default_visibility = ["//visibility:public"])
+load("//bazel:python_rules.bzl", "py_proto_library")
+
+proto_library(
+    name = "empty2_proto_descriptor",
+    srcs = ["empty2.proto"],
+)
 
 py_proto_library(
     name = "empty2_proto",
-    protos = [
-        "empty2.proto",
-    ],
-    with_grpc = True,
     deps = [
-        requirement('protobuf'),
+        ":empty2_proto_descriptor",
     ],
 )
 
+proto_library(
+    name = "empty2_extensions_proto_descriptor",
+    srcs = ["empty2_extensions.proto"],
+    deps = [
+        ":empty2_proto_descriptor",
+    ]
+)
+
 py_proto_library(
     name = "empty2_extensions_proto",
-    protos = [
-        "empty2_extensions.proto",
-    ],
-    proto_deps = [
-        ":empty2_proto",
-    ],
-    with_grpc = True,
     deps = [
-        requirement('protobuf'),
+        ":empty2_extensions_proto_descriptor",
     ],
 )
 
index f047243..44d516e 100644 (file)
@@ -3,6 +3,14 @@ gRPC Python
 
 Package for gRPC Python.
 
+Supported Python Versions
+-------------------------
+Python >= 3.5
+
+Deprecated Python Versions
+--------------------------
+Python == 2.7. Python 2.7 support will be removed on January 1, 2020.
+
 Installation
 ------------
 
index 27d5d2e..a2bedae 100644 (file)
@@ -12,6 +12,7 @@ py_library(
         ":channel",
         ":interceptor",
         ":server",
+        ":compression",
         "//src/python/grpcio/grpc/_cython:cygrpc",
         "//src/python/grpcio/grpc/experimental",
         "//src/python/grpcio/grpc/framework",
@@ -32,11 +33,17 @@ py_library(
 )
 
 py_library(
+    name = "compression",
+    srcs = ["_compression.py"],
+)
+
+py_library(
     name = "channel",
     srcs = ["_channel.py"],
     deps = [
         ":common",
         ":grpcio_metadata",
+        ":compression",
     ],
 )
 
@@ -68,6 +75,7 @@ py_library(
     srcs = ["_server.py"],
     deps = [
         ":common",
+        ":compression",
         ":interceptor",
     ],
 )
index 7631410..6175180 100644 (file)
@@ -21,6 +21,7 @@ import sys
 import six
 
 from grpc._cython import cygrpc as _cygrpc
+from grpc import _compression
 
 logging.getLogger(__name__).addHandler(logging.NullHandler())
 
@@ -413,6 +414,8 @@ class ClientCallDetails(six.with_metaclass(abc.ABCMeta)):
       credentials: An optional CallCredentials for the RPC.
       wait_for_ready: This is an EXPERIMENTAL argument. An optional flag t
         enable wait for ready mechanism.
+      compression: An element of grpc.compression, e.g.
+        grpc.compression.Gzip. This is an EXPERIMENTAL option.
     """
 
 
@@ -669,7 +672,8 @@ class UnaryUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
                  timeout=None,
                  metadata=None,
                  credentials=None,
-                 wait_for_ready=None):
+                 wait_for_ready=None,
+                 compression=None):
         """Synchronously invokes the underlying RPC.
 
         Args:
@@ -681,6 +685,8 @@ class UnaryUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
           credentials: An optional CallCredentials for the RPC.
           wait_for_ready: This is an EXPERIMENTAL argument. An optional
             flag to enable wait for ready mechanism
+          compression: An element of grpc.compression, e.g.
+            grpc.compression.Gzip. This is an EXPERIMENTAL option.
 
         Returns:
           The response value for the RPC.
@@ -698,7 +704,8 @@ class UnaryUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
                   timeout=None,
                   metadata=None,
                   credentials=None,
-                  wait_for_ready=None):
+                  wait_for_ready=None,
+                  compression=None):
         """Synchronously invokes the underlying RPC.
 
         Args:
@@ -710,6 +717,8 @@ class UnaryUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
           credentials: An optional CallCredentials for the RPC.
           wait_for_ready: This is an EXPERIMENTAL argument. An optional
             flag to enable wait for ready mechanism
+          compression: An element of grpc.compression, e.g.
+            grpc.compression.Gzip. This is an EXPERIMENTAL option.
 
         Returns:
           The response value for the RPC and a Call value for the RPC.
@@ -727,7 +736,8 @@ class UnaryUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
                timeout=None,
                metadata=None,
                credentials=None,
-               wait_for_ready=None):
+               wait_for_ready=None,
+               compression=None):
         """Asynchronously invokes the underlying RPC.
 
         Args:
@@ -739,6 +749,8 @@ class UnaryUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
           credentials: An optional CallCredentials for the RPC.
           wait_for_ready: This is an EXPERIMENTAL argument. An optional
             flag to enable wait for ready mechanism
+          compression: An element of grpc.compression, e.g.
+            grpc.compression.Gzip. This is an EXPERIMENTAL option.
 
         Returns:
             An object that is both a Call for the RPC and a Future.
@@ -759,7 +771,8 @@ class UnaryStreamMultiCallable(six.with_metaclass(abc.ABCMeta)):
                  timeout=None,
                  metadata=None,
                  credentials=None,
-                 wait_for_ready=None):
+                 wait_for_ready=None,
+                 compression=None):
         """Invokes the underlying RPC.
 
         Args:
@@ -771,6 +784,8 @@ class UnaryStreamMultiCallable(six.with_metaclass(abc.ABCMeta)):
           credentials: An optional CallCredentials for the RPC.
           wait_for_ready: This is an EXPERIMENTAL argument. An optional
             flag to enable wait for ready mechanism
+          compression: An element of grpc.compression, e.g.
+            grpc.compression.Gzip. This is an EXPERIMENTAL option.
 
         Returns:
             An object that is both a Call for the RPC and an iterator of
@@ -790,7 +805,8 @@ class StreamUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
                  timeout=None,
                  metadata=None,
                  credentials=None,
-                 wait_for_ready=None):
+                 wait_for_ready=None,
+                 compression=None):
         """Synchronously invokes the underlying RPC.
 
         Args:
@@ -803,6 +819,8 @@ class StreamUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
           credentials: An optional CallCredentials for the RPC.
           wait_for_ready: This is an EXPERIMENTAL argument. An optional
             flag to enable wait for ready mechanism
+          compression: An element of grpc.compression, e.g.
+            grpc.compression.Gzip. This is an EXPERIMENTAL option.
 
         Returns:
           The response value for the RPC.
@@ -820,7 +838,8 @@ class StreamUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
                   timeout=None,
                   metadata=None,
                   credentials=None,
-                  wait_for_ready=None):
+                  wait_for_ready=None,
+                  compression=None):
         """Synchronously invokes the underlying RPC on the client.
 
         Args:
@@ -833,6 +852,8 @@ class StreamUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
           credentials: An optional CallCredentials for the RPC.
           wait_for_ready: This is an EXPERIMENTAL argument. An optional
             flag to enable wait for ready mechanism
+          compression: An element of grpc.compression, e.g.
+            grpc.compression.Gzip. This is an EXPERIMENTAL option.
 
         Returns:
           The response value for the RPC and a Call object for the RPC.
@@ -850,7 +871,8 @@ class StreamUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
                timeout=None,
                metadata=None,
                credentials=None,
-               wait_for_ready=None):
+               wait_for_ready=None,
+               compression=None):
         """Asynchronously invokes the underlying RPC on the client.
 
         Args:
@@ -862,6 +884,8 @@ class StreamUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
           credentials: An optional CallCredentials for the RPC.
           wait_for_ready: This is an EXPERIMENTAL argument. An optional
             flag to enable wait for ready mechanism
+          compression: An element of grpc.compression, e.g.
+            grpc.compression.Gzip. This is an EXPERIMENTAL option.
 
         Returns:
             An object that is both a Call for the RPC and a Future.
@@ -882,7 +906,8 @@ class StreamStreamMultiCallable(six.with_metaclass(abc.ABCMeta)):
                  timeout=None,
                  metadata=None,
                  credentials=None,
-                 wait_for_ready=None):
+                 wait_for_ready=None,
+                 compression=None):
         """Invokes the underlying RPC on the client.
 
         Args:
@@ -894,6 +919,8 @@ class StreamStreamMultiCallable(six.with_metaclass(abc.ABCMeta)):
           credentials: An optional CallCredentials for the RPC.
           wait_for_ready: This is an EXPERIMENTAL argument. An optional
             flag to enable wait for ready mechanism
+          compression: An element of grpc.compression, e.g.
+            grpc.compression.Gzip. This is an EXPERIMENTAL option.
 
         Returns:
             An object that is both a Call for the RPC and an iterator of
@@ -1097,6 +1124,17 @@ class ServicerContext(six.with_metaclass(abc.ABCMeta, RpcContext)):
         """
         raise NotImplementedError()
 
+    def set_compression(self, compression):
+        """Set the compression algorithm to be used for the entire call.
+
+        This is an EXPERIMENTAL method.
+
+        Args:
+          compression: An element of grpc.compression, e.g.
+            grpc.compression.Gzip.
+        """
+        raise NotImplementedError()
+
     @abc.abstractmethod
     def send_initial_metadata(self, initial_metadata):
         """Sends the initial metadata value to the client.
@@ -1184,6 +1222,16 @@ class ServicerContext(six.with_metaclass(abc.ABCMeta, RpcContext)):
         """
         raise NotImplementedError()
 
+    def disable_next_message_compression(self):
+        """Disables compression for the next response message.
+
+        This is an EXPERIMENTAL method.
+
+        This method will override any compression configuration set during
+        server creation or set on the call.
+        """
+        raise NotImplementedError()
+
 
 #####################  Service-Side Handler Interfaces  ########################
 
@@ -1682,7 +1730,7 @@ def channel_ready_future(channel):
     return _utilities.channel_ready_future(channel)
 
 
-def insecure_channel(target, options=None):
+def insecure_channel(target, options=None, compression=None):
     """Creates an insecure Channel to a server.
 
     The returned Channel is thread-safe.
@@ -1691,15 +1739,18 @@ def insecure_channel(target, options=None):
       target: The server address
       options: An optional list of key-value pairs (channel args
         in gRPC Core runtime) to configure the channel.
+      compression: An optional value indicating the compression method to be
+        used over the lifetime of the channel. This is an EXPERIMENTAL option.
 
     Returns:
       A Channel.
     """
     from grpc import _channel  # pylint: disable=cyclic-import
-    return _channel.Channel(target, () if options is None else options, None)
+    return _channel.Channel(target, ()
+                            if options is None else options, None, compression)
 
 
-def secure_channel(target, credentials, options=None):
+def secure_channel(target, credentials, options=None, compression=None):
     """Creates a secure Channel to a server.
 
     The returned Channel is thread-safe.
@@ -1709,13 +1760,15 @@ def secure_channel(target, credentials, options=None):
       credentials: A ChannelCredentials instance.
       options: An optional list of key-value pairs (channel args
         in gRPC Core runtime) to configure the channel.
+      compression: An optional value indicating the compression method to be
+        used over the lifetime of the channel. This is an EXPERIMENTAL option.
 
     Returns:
       A Channel.
     """
     from grpc import _channel  # pylint: disable=cyclic-import
     return _channel.Channel(target, () if options is None else options,
-                            credentials._credentials)
+                            credentials._credentials, compression)
 
 
 def intercept_channel(channel, *interceptors):
@@ -1750,7 +1803,8 @@ def server(thread_pool,
            handlers=None,
            interceptors=None,
            options=None,
-           maximum_concurrent_rpcs=None):
+           maximum_concurrent_rpcs=None,
+           compression=None):
     """Creates a Server with which RPCs can be serviced.
 
     Args:
@@ -1768,6 +1822,9 @@ def server(thread_pool,
       maximum_concurrent_rpcs: The maximum number of concurrent RPCs this server
         will service before returning RESOURCE_EXHAUSTED status, or None to
         indicate no limit.
+      compression: An element of grpc.compression, e.g.
+        grpc.compression.Gzip. This compression algorithm will be used for the
+        lifetime of the server unless overridden. This is an EXPERIMENTAL option.
 
     Returns:
       A Server object.
@@ -1777,7 +1834,7 @@ def server(thread_pool,
                                  if handlers is None else handlers, ()
                                  if interceptors is None else interceptors, ()
                                  if options is None else options,
-                                 maximum_concurrent_rpcs)
+                                 maximum_concurrent_rpcs, compression)
 
 
 @contextlib.contextmanager
@@ -1788,6 +1845,16 @@ def _create_servicer_context(rpc_event, state, request_deserializer):
     context._finalize_state()  # pylint: disable=protected-access
 
 
+class Compression(enum.IntEnum):
+    """Indicates the compression method to be used for an RPC.
+
+       This enumeration is part of an EXPERIMENTAL API.
+    """
+    NoCompression = _compression.NoCompression
+    Deflate = _compression.Deflate
+    Gzip = _compression.Gzip
+
+
 ###################################  __all__  #################################
 
 __all__ = (
@@ -1805,6 +1872,7 @@ __all__ = (
     'AuthMetadataContext',
     'AuthMetadataPluginCallback',
     'AuthMetadataPlugin',
+    'Compression',
     'ClientCallDetails',
     'ServerCertificateConfiguration',
     'ServerCredentials',
index ed4c871..1272ee8 100644 (file)
@@ -19,6 +19,7 @@ import threading
 import time
 
 import grpc
+from grpc import _compression
 from grpc import _common
 from grpc import _grpcio_metadata
 from grpc._cython import cygrpc
@@ -512,17 +513,19 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
         self._response_deserializer = response_deserializer
         self._context = cygrpc.build_census_context()
 
-    def _prepare(self, request, timeout, metadata, wait_for_ready):
+    def _prepare(self, request, timeout, metadata, wait_for_ready, compression):
         deadline, serialized_request, rendezvous = _start_unary_request(
             request, timeout, self._request_serializer)
         initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
             wait_for_ready)
+        augmented_metadata = _compression.augment_metadata(
+            metadata, compression)
         if serialized_request is None:
             return None, None, None, rendezvous
         else:
             state = _RPCState(_UNARY_UNARY_INITIAL_DUE, None, None, None, None)
             operations = (
-                cygrpc.SendInitialMetadataOperation(metadata,
+                cygrpc.SendInitialMetadataOperation(augmented_metadata,
                                                     initial_metadata_flags),
                 cygrpc.SendMessageOperation(serialized_request, _EMPTY_FLAGS),
                 cygrpc.SendCloseFromClientOperation(_EMPTY_FLAGS),
@@ -532,18 +535,17 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
             )
             return state, operations, deadline, None
 
-    def _blocking(self, request, timeout, metadata, credentials,
-                  wait_for_ready):
+    def _blocking(self, request, timeout, metadata, credentials, wait_for_ready,
+                  compression):
         state, operations, deadline, rendezvous = self._prepare(
-            request, timeout, metadata, wait_for_ready)
+            request, timeout, metadata, wait_for_ready, compression)
         if state is None:
             raise rendezvous  # pylint: disable-msg=raising-bad-type
         else:
-            deadline_to_propagate = _determine_deadline(deadline)
             call = self._channel.segregated_call(
                 cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS,
-                self._method, None, deadline_to_propagate, metadata, None
-                if credentials is None else credentials._credentials, ((
+                self._method, None, _determine_deadline(deadline), metadata,
+                None if credentials is None else credentials._credentials, ((
                     operations,
                     None,
                 ),), self._context)
@@ -556,9 +558,10 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
                  timeout=None,
                  metadata=None,
                  credentials=None,
-                 wait_for_ready=None):
+                 wait_for_ready=None,
+                 compression=None):
         state, call, = self._blocking(request, timeout, metadata, credentials,
-                                      wait_for_ready)
+                                      wait_for_ready, compression)
         return _end_unary_response_blocking(state, call, False, None)
 
     def with_call(self,
@@ -566,9 +569,10 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
                   timeout=None,
                   metadata=None,
                   credentials=None,
-                  wait_for_ready=None):
+                  wait_for_ready=None,
+                  compression=None):
         state, call, = self._blocking(request, timeout, metadata, credentials,
-                                      wait_for_ready)
+                                      wait_for_ready, compression)
         return _end_unary_response_blocking(state, call, True, None)
 
     def future(self,
@@ -576,9 +580,10 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
                timeout=None,
                metadata=None,
                credentials=None,
-               wait_for_ready=None):
+               wait_for_ready=None,
+               compression=None):
         state, operations, deadline, rendezvous = self._prepare(
-            request, timeout, metadata, wait_for_ready)
+            request, timeout, metadata, wait_for_ready, compression)
         if state is None:
             raise rendezvous  # pylint: disable-msg=raising-bad-type
         else:
@@ -604,12 +609,14 @@ class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
         self._response_deserializer = response_deserializer
         self._context = cygrpc.build_census_context()
 
-    def __call__(self,
-                 request,
-                 timeout=None,
-                 metadata=None,
-                 credentials=None,
-                 wait_for_ready=None):
+    def __call__(  # pylint: disable=too-many-locals
+            self,
+            request,
+            timeout=None,
+            metadata=None,
+            credentials=None,
+            wait_for_ready=None,
+            compression=None):
         deadline, serialized_request, rendezvous = _start_unary_request(
             request, timeout, self._request_serializer)
         initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
@@ -617,10 +624,12 @@ class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
         if serialized_request is None:
             raise rendezvous  # pylint: disable-msg=raising-bad-type
         else:
+            augmented_metadata = _compression.augment_metadata(
+                metadata, compression)
             state = _RPCState(_UNARY_STREAM_INITIAL_DUE, None, None, None, None)
             operationses = (
                 (
-                    cygrpc.SendInitialMetadataOperation(metadata,
+                    cygrpc.SendInitialMetadataOperation(augmented_metadata,
                                                         initial_metadata_flags),
                     cygrpc.SendMessageOperation(serialized_request,
                                                 _EMPTY_FLAGS),
@@ -629,12 +638,13 @@ class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
                 ),
                 (cygrpc.ReceiveInitialMetadataOperation(_EMPTY_FLAGS),),
             )
-            event_handler = _event_handler(state, self._response_deserializer)
             call = self._managed_call(
                 cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS,
                 self._method, None, _determine_deadline(deadline), metadata,
-                None if credentials is None else credentials._credentials,
-                operationses, event_handler, self._context)
+                None if credentials is None else
+                credentials._credentials, operationses,
+                _event_handler(state,
+                               self._response_deserializer), self._context)
             return _Rendezvous(state, call, self._response_deserializer,
                                deadline)
 
@@ -652,18 +662,19 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
         self._context = cygrpc.build_census_context()
 
     def _blocking(self, request_iterator, timeout, metadata, credentials,
-                  wait_for_ready):
+                  wait_for_ready, compression):
         deadline = _deadline(timeout)
         state = _RPCState(_STREAM_UNARY_INITIAL_DUE, None, None, None, None)
         initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
             wait_for_ready)
-        deadline_to_propagate = _determine_deadline(deadline)
+        augmented_metadata = _compression.augment_metadata(
+            metadata, compression)
         call = self._channel.segregated_call(
             cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS, self._method,
-            None, deadline_to_propagate, metadata, None
+            None, _determine_deadline(deadline), augmented_metadata, None
             if credentials is None else credentials._credentials,
             _stream_unary_invocation_operationses_and_tags(
-                metadata, initial_metadata_flags), self._context)
+                augmented_metadata, initial_metadata_flags), self._context)
         _consume_request_iterator(request_iterator, state, call,
                                   self._request_serializer, None)
         while True:
@@ -680,9 +691,10 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
                  timeout=None,
                  metadata=None,
                  credentials=None,
-                 wait_for_ready=None):
+                 wait_for_ready=None,
+                 compression=None):
         state, call, = self._blocking(request_iterator, timeout, metadata,
-                                      credentials, wait_for_ready)
+                                      credentials, wait_for_ready, compression)
         return _end_unary_response_blocking(state, call, False, None)
 
     def with_call(self,
@@ -690,9 +702,10 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
                   timeout=None,
                   metadata=None,
                   credentials=None,
-                  wait_for_ready=None):
+                  wait_for_ready=None,
+                  compression=None):
         state, call, = self._blocking(request_iterator, timeout, metadata,
-                                      credentials, wait_for_ready)
+                                      credentials, wait_for_ready, compression)
         return _end_unary_response_blocking(state, call, True, None)
 
     def future(self,
@@ -700,15 +713,18 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
                timeout=None,
                metadata=None,
                credentials=None,
-               wait_for_ready=None):
+               wait_for_ready=None,
+               compression=None):
         deadline = _deadline(timeout)
         state = _RPCState(_STREAM_UNARY_INITIAL_DUE, None, None, None, None)
         event_handler = _event_handler(state, self._response_deserializer)
         initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
             wait_for_ready)
+        augmented_metadata = _compression.augment_metadata(
+            metadata, compression)
         call = self._managed_call(
             cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS, self._method,
-            None, deadline, metadata, None
+            None, deadline, augmented_metadata, None
             if credentials is None else credentials._credentials,
             _stream_unary_invocation_operationses(
                 metadata, initial_metadata_flags), event_handler, self._context)
@@ -734,24 +750,26 @@ class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable):
                  timeout=None,
                  metadata=None,
                  credentials=None,
-                 wait_for_ready=None):
+                 wait_for_ready=None,
+                 compression=None):
         deadline = _deadline(timeout)
         state = _RPCState(_STREAM_STREAM_INITIAL_DUE, None, None, None, None)
         initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
             wait_for_ready)
+        augmented_metadata = _compression.augment_metadata(
+            metadata, compression)
         operationses = (
             (
-                cygrpc.SendInitialMetadataOperation(metadata,
+                cygrpc.SendInitialMetadataOperation(augmented_metadata,
                                                     initial_metadata_flags),
                 cygrpc.ReceiveStatusOnClientOperation(_EMPTY_FLAGS),
             ),
             (cygrpc.ReceiveInitialMetadataOperation(_EMPTY_FLAGS),),
         )
         event_handler = _event_handler(state, self._response_deserializer)
-        deadline_to_propagate = _determine_deadline(deadline)
         call = self._managed_call(
             cygrpc.PropagationConstants.GRPC_PROPAGATE_DEFAULTS, self._method,
-            None, deadline_to_propagate, metadata, None
+            None, _determine_deadline(deadline), augmented_metadata, None
             if credentials is None else credentials._credentials, operationses,
             event_handler, self._context)
         _consume_request_iterator(request_iterator, state, call,
@@ -982,28 +1000,30 @@ def _unsubscribe(state, callback):
                 break
 
 
-def _options(options):
-    return list(options) + [
-        (
-            cygrpc.ChannelArgKey.primary_user_agent_string,
-            _USER_AGENT,
-        ),
-    ]
+def _augment_options(base_options, compression):
+    compression_option = _compression.create_channel_option(compression)
+    return tuple(base_options) + compression_option + ((
+        cygrpc.ChannelArgKey.primary_user_agent_string,
+        _USER_AGENT,
+    ),)
 
 
 class Channel(grpc.Channel):
     """A cygrpc.Channel-backed implementation of grpc.Channel."""
 
-    def __init__(self, target, options, credentials):
+    def __init__(self, target, options, credentials, compression):
         """Constructor.
 
         Args:
           target: The target to which to connect.
           options: Configuration options for the channel.
           credentials: A cygrpc.ChannelCredentials or None.
+          compression: An optional value indicating the compression method to be
+            used over the lifetime of the channel.
         """
         self._channel = cygrpc.Channel(
-            _common.encode(target), _options(options), credentials)
+            _common.encode(target), _augment_options(options, compression),
+            credentials)
         self._call_state = _ChannelCallState(self._channel)
         self._connectivity_state = _ChannelConnectivityState(self._channel)
         cygrpc.fork_register_channel(self)
diff --git a/src/python/grpcio/grpc/_compression.py b/src/python/grpcio/grpc/_compression.py
new file mode 100644 (file)
index 0000000..45339c3
--- /dev/null
@@ -0,0 +1,55 @@
+# 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.
+
+from grpc._cython import cygrpc
+
+NoCompression = cygrpc.CompressionAlgorithm.none
+Deflate = cygrpc.CompressionAlgorithm.deflate
+Gzip = cygrpc.CompressionAlgorithm.gzip
+
+_METADATA_STRING_MAPPING = {
+    NoCompression: 'identity',
+    Deflate: 'deflate',
+    Gzip: 'gzip',
+}
+
+
+def _compression_algorithm_to_metadata_value(compression):
+    return _METADATA_STRING_MAPPING[compression]
+
+
+def compression_algorithm_to_metadata(compression):
+    return (cygrpc.GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY,
+            _compression_algorithm_to_metadata_value(compression))
+
+
+def create_channel_option(compression):
+    return ((cygrpc.GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM,
+             int(compression)),) if compression else ()
+
+
+def augment_metadata(metadata, compression):
+    if not metadata and not compression:
+        return None
+    base_metadata = tuple(metadata) if metadata else ()
+    compression_metadata = (
+        compression_algorithm_to_metadata(compression),) if compression else ()
+    return base_metadata + compression_metadata
+
+
+__all__ = (
+    "NoCompression",
+    "Deflate",
+    "Gzip",
+)
index 9f06ce0..0307f74 100644 (file)
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 
-cdef grpc_event _next(grpc_completion_queue *c_completion_queue, deadline)
+cdef grpc_event _next(grpc_completion_queue *c_completion_queue, deadline) except *
 
 
 cdef _interpret_event(grpc_event c_event)
index 212d27d..325e72a 100644 (file)
@@ -20,7 +20,7 @@ import time
 cdef int _INTERRUPT_CHECK_PERIOD_MS = 200
 
 
-cdef grpc_event _next(grpc_completion_queue *c_completion_queue, deadline):
+cdef grpc_event _next(grpc_completion_queue *c_completion_queue, deadline) except *:
   cdef gpr_timespec c_increment
   cdef gpr_timespec c_timeout
   cdef gpr_timespec c_deadline
index 0a35002..057d077 100644 (file)
@@ -140,7 +140,8 @@ cdef extern from "grpc/grpc.h":
   const char *GRPC_ARG_SECONDARY_USER_AGENT_STRING
   const char *GRPC_SSL_TARGET_NAME_OVERRIDE_ARG
   const char *GRPC_SSL_SESSION_CACHE_ARG
-  const char *GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM
+  const char *_GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM \
+    "GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM"
   const char *GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL
   const char *GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET
 
@@ -618,3 +619,8 @@ cdef extern from "grpc/compression.h":
   int grpc_compression_options_is_algorithm_enabled(
       const grpc_compression_options *opts,
       grpc_compression_algorithm algorithm) nogil
+
+cdef extern from "grpc/impl/codegen/compression_types.h":
+
+  const char *_GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY \
+    "GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY"
index 02c904b..308d677 100644 (file)
@@ -108,6 +108,11 @@ class OperationType:
   receive_status_on_client = GRPC_OP_RECV_STATUS_ON_CLIENT
   receive_close_on_server = GRPC_OP_RECV_CLOSE_ON_SERVER
 
+GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM= (
+  _GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM)
+
+GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY = (
+  _GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY)
 
 class CompressionAlgorithm:
   none = GRPC_COMPRESS_NONE
index 79e68cf..2b68fd5 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc/_grpcio_metadata.py.template`!!!
 
-__version__ = """1.20.1"""
+__version__ = """1.21.0"""
index 6c4e396..4ec2e6b 100644 (file)
@@ -44,9 +44,9 @@ def service_pipeline(interceptors):
 
 
 class _ClientCallDetails(
-        collections.namedtuple(
-            '_ClientCallDetails',
-            ('method', 'timeout', 'metadata', 'credentials', 'wait_for_ready')),
+        collections.namedtuple('_ClientCallDetails',
+                               ('method', 'timeout', 'metadata', 'credentials',
+                                'wait_for_ready', 'compression')),
         grpc.ClientCallDetails):
     pass
 
@@ -77,7 +77,12 @@ def _unwrap_client_call_details(call_details, default_details):
     except AttributeError:
         wait_for_ready = default_details.wait_for_ready
 
-    return method, timeout, metadata, credentials, wait_for_ready
+    try:
+        compression = call_details.compression
+    except AttributeError:
+        compression = default_details.compression
+
+    return method, timeout, metadata, credentials, wait_for_ready, compression
 
 
 class _FailureOutcome(grpc.RpcError, grpc.Future, grpc.Call):  # pylint: disable=too-many-ancestors
@@ -206,13 +211,15 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
                  timeout=None,
                  metadata=None,
                  credentials=None,
-                 wait_for_ready=None):
+                 wait_for_ready=None,
+                 compression=None):
         response, ignored_call = self._with_call(
             request,
             timeout=timeout,
             metadata=metadata,
             credentials=credentials,
-            wait_for_ready=wait_for_ready)
+            wait_for_ready=wait_for_ready,
+            compression=compression)
         return response
 
     def _with_call(self,
@@ -220,20 +227,25 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
                    timeout=None,
                    metadata=None,
                    credentials=None,
-                   wait_for_ready=None):
-        client_call_details = _ClientCallDetails(
-            self._method, timeout, metadata, credentials, wait_for_ready)
+                   wait_for_ready=None,
+                   compression=None):
+        client_call_details = _ClientCallDetails(self._method, timeout,
+                                                 metadata, credentials,
+                                                 wait_for_ready, compression)
 
         def continuation(new_details, request):
-            new_method, new_timeout, new_metadata, new_credentials, new_wait_for_ready = (
-                _unwrap_client_call_details(new_details, client_call_details))
+            (new_method, new_timeout, new_metadata, new_credentials,
+             new_wait_for_ready,
+             new_compression) = (_unwrap_client_call_details(
+                 new_details, client_call_details))
             try:
                 response, call = self._thunk(new_method).with_call(
                     request,
                     timeout=new_timeout,
                     metadata=new_metadata,
                     credentials=new_credentials,
-                    wait_for_ready=new_wait_for_ready)
+                    wait_for_ready=new_wait_for_ready,
+                    compression=new_compression)
                 return _UnaryOutcome(response, call)
             except grpc.RpcError as rpc_error:
                 return rpc_error
@@ -249,32 +261,39 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
                   timeout=None,
                   metadata=None,
                   credentials=None,
-                  wait_for_ready=None):
+                  wait_for_ready=None,
+                  compression=None):
         return self._with_call(
             request,
             timeout=timeout,
             metadata=metadata,
             credentials=credentials,
-            wait_for_ready=wait_for_ready)
+            wait_for_ready=wait_for_ready,
+            compression=compression)
 
     def future(self,
                request,
                timeout=None,
                metadata=None,
                credentials=None,
-               wait_for_ready=None):
-        client_call_details = _ClientCallDetails(
-            self._method, timeout, metadata, credentials, wait_for_ready)
+               wait_for_ready=None,
+               compression=None):
+        client_call_details = _ClientCallDetails(self._method, timeout,
+                                                 metadata, credentials,
+                                                 wait_for_ready, compression)
 
         def continuation(new_details, request):
-            new_method, new_timeout, new_metadata, new_credentials, new_wait_for_ready = (
-                _unwrap_client_call_details(new_details, client_call_details))
+            (new_method, new_timeout, new_metadata, new_credentials,
+             new_wait_for_ready,
+             new_compression) = (_unwrap_client_call_details(
+                 new_details, client_call_details))
             return self._thunk(new_method).future(
                 request,
                 timeout=new_timeout,
                 metadata=new_metadata,
                 credentials=new_credentials,
-                wait_for_ready=new_wait_for_ready)
+                wait_for_ready=new_wait_for_ready,
+                compression=new_compression)
 
         try:
             return self._interceptor.intercept_unary_unary(
@@ -295,19 +314,24 @@ class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
                  timeout=None,
                  metadata=None,
                  credentials=None,
-                 wait_for_ready=None):
-        client_call_details = _ClientCallDetails(
-            self._method, timeout, metadata, credentials, wait_for_ready)
+                 wait_for_ready=None,
+                 compression=None):
+        client_call_details = _ClientCallDetails(self._method, timeout,
+                                                 metadata, credentials,
+                                                 wait_for_ready, compression)
 
         def continuation(new_details, request):
-            new_method, new_timeout, new_metadata, new_credentials, new_wait_for_ready = (
-                _unwrap_client_call_details(new_details, client_call_details))
+            (new_method, new_timeout, new_metadata, new_credentials,
+             new_wait_for_ready,
+             new_compression) = (_unwrap_client_call_details(
+                 new_details, client_call_details))
             return self._thunk(new_method)(
                 request,
                 timeout=new_timeout,
                 metadata=new_metadata,
                 credentials=new_credentials,
-                wait_for_ready=new_wait_for_ready)
+                wait_for_ready=new_wait_for_ready,
+                compression=new_compression)
 
         try:
             return self._interceptor.intercept_unary_stream(
@@ -328,13 +352,15 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
                  timeout=None,
                  metadata=None,
                  credentials=None,
-                 wait_for_ready=None):
+                 wait_for_ready=None,
+                 compression=None):
         response, ignored_call = self._with_call(
             request_iterator,
             timeout=timeout,
             metadata=metadata,
             credentials=credentials,
-            wait_for_ready=wait_for_ready)
+            wait_for_ready=wait_for_ready,
+            compression=compression)
         return response
 
     def _with_call(self,
@@ -342,20 +368,25 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
                    timeout=None,
                    metadata=None,
                    credentials=None,
-                   wait_for_ready=None):
-        client_call_details = _ClientCallDetails(
-            self._method, timeout, metadata, credentials, wait_for_ready)
+                   wait_for_ready=None,
+                   compression=None):
+        client_call_details = _ClientCallDetails(self._method, timeout,
+                                                 metadata, credentials,
+                                                 wait_for_ready, compression)
 
         def continuation(new_details, request_iterator):
-            new_method, new_timeout, new_metadata, new_credentials, new_wait_for_ready = (
-                _unwrap_client_call_details(new_details, client_call_details))
+            (new_method, new_timeout, new_metadata, new_credentials,
+             new_wait_for_ready,
+             new_compression) = (_unwrap_client_call_details(
+                 new_details, client_call_details))
             try:
                 response, call = self._thunk(new_method).with_call(
                     request_iterator,
                     timeout=new_timeout,
                     metadata=new_metadata,
                     credentials=new_credentials,
-                    wait_for_ready=new_wait_for_ready)
+                    wait_for_ready=new_wait_for_ready,
+                    compression=new_compression)
                 return _UnaryOutcome(response, call)
             except grpc.RpcError as rpc_error:
                 return rpc_error
@@ -371,32 +402,39 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
                   timeout=None,
                   metadata=None,
                   credentials=None,
-                  wait_for_ready=None):
+                  wait_for_ready=None,
+                  compression=None):
         return self._with_call(
             request_iterator,
             timeout=timeout,
             metadata=metadata,
             credentials=credentials,
-            wait_for_ready=wait_for_ready)
+            wait_for_ready=wait_for_ready,
+            compression=compression)
 
     def future(self,
                request_iterator,
                timeout=None,
                metadata=None,
                credentials=None,
-               wait_for_ready=None):
-        client_call_details = _ClientCallDetails(
-            self._method, timeout, metadata, credentials, wait_for_ready)
+               wait_for_ready=None,
+               compression=None):
+        client_call_details = _ClientCallDetails(self._method, timeout,
+                                                 metadata, credentials,
+                                                 wait_for_ready, compression)
 
         def continuation(new_details, request_iterator):
-            new_method, new_timeout, new_metadata, new_credentials, new_wait_for_ready = (
-                _unwrap_client_call_details(new_details, client_call_details))
+            (new_method, new_timeout, new_metadata, new_credentials,
+             new_wait_for_ready,
+             new_compression) = (_unwrap_client_call_details(
+                 new_details, client_call_details))
             return self._thunk(new_method).future(
                 request_iterator,
                 timeout=new_timeout,
                 metadata=new_metadata,
                 credentials=new_credentials,
-                wait_for_ready=new_wait_for_ready)
+                wait_for_ready=new_wait_for_ready,
+                compression=new_compression)
 
         try:
             return self._interceptor.intercept_stream_unary(
@@ -417,19 +455,24 @@ class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable):
                  timeout=None,
                  metadata=None,
                  credentials=None,
-                 wait_for_ready=None):
-        client_call_details = _ClientCallDetails(
-            self._method, timeout, metadata, credentials, wait_for_ready)
+                 wait_for_ready=None,
+                 compression=None):
+        client_call_details = _ClientCallDetails(self._method, timeout,
+                                                 metadata, credentials,
+                                                 wait_for_ready, compression)
 
         def continuation(new_details, request_iterator):
-            new_method, new_timeout, new_metadata, new_credentials, new_wait_for_ready = (
-                _unwrap_client_call_details(new_details, client_call_details))
+            (new_method, new_timeout, new_metadata, new_credentials,
+             new_wait_for_ready,
+             new_compression) = (_unwrap_client_call_details(
+                 new_details, client_call_details))
             return self._thunk(new_method)(
                 request_iterator,
                 timeout=new_timeout,
                 metadata=new_metadata,
                 credentials=new_credentials,
-                wait_for_ready=new_wait_for_ready)
+                wait_for_ready=new_wait_for_ready,
+                compression=new_compression)
 
         try:
             return self._interceptor.intercept_stream_stream(
index 90136ae..370c811 100644 (file)
@@ -24,6 +24,7 @@ import six
 
 import grpc
 from grpc import _common
+from grpc import _compression
 from grpc import _interceptor
 from grpc._cython import cygrpc
 
@@ -94,6 +95,7 @@ class _RPCState(object):
         self.request = None
         self.client = _OPEN
         self.initial_metadata_allowed = True
+        self.compression_algorithm = None
         self.disable_next_compression = False
         self.trailing_metadata = None
         self.code = None
@@ -129,13 +131,33 @@ def _send_status_from_server(state, token):
     return send_status_from_server
 
 
+def _get_initial_metadata(state, metadata):
+    with state.condition:
+        if state.compression_algorithm:
+            compression_metadata = (
+                _compression.compression_algorithm_to_metadata(
+                    state.compression_algorithm),)
+            if metadata is None:
+                return compression_metadata
+            else:
+                return compression_metadata + tuple(metadata)
+        else:
+            return metadata
+
+
+def _get_initial_metadata_operation(state, metadata):
+    operation = cygrpc.SendInitialMetadataOperation(
+        _get_initial_metadata(state, metadata), _EMPTY_FLAGS)
+    return operation
+
+
 def _abort(state, call, code, details):
     if state.client is not _CANCELLED:
         effective_code = _abortion_code(state, code)
         effective_details = details if state.details is None else state.details
         if state.initial_metadata_allowed:
             operations = (
-                cygrpc.SendInitialMetadataOperation(None, _EMPTY_FLAGS),
+                _get_initial_metadata_operation(state, None),
                 cygrpc.SendStatusFromServerOperation(
                     state.trailing_metadata, effective_code, effective_details,
                     _EMPTY_FLAGS),
@@ -259,14 +281,18 @@ class _Context(grpc.ServicerContext):
                 cygrpc.auth_context(self._rpc_event.call))
         }
 
+    def set_compression(self, compression):
+        with self._state.condition:
+            self._state.compression_algorithm = compression
+
     def send_initial_metadata(self, initial_metadata):
         with self._state.condition:
             if self._state.client is _CANCELLED:
                 _raise_rpc_error(self._state)
             else:
                 if self._state.initial_metadata_allowed:
-                    operation = cygrpc.SendInitialMetadataOperation(
-                        initial_metadata, _EMPTY_FLAGS)
+                    operation = _get_initial_metadata_operation(
+                        self._state, initial_metadata)
                     self._rpc_event.call.start_server_batch(
                         (operation,), _send_initial_metadata(self._state))
                     self._state.initial_metadata_allowed = False
@@ -400,10 +426,13 @@ def _call_behavior(rpc_event,
     with _create_servicer_context(rpc_event, state,
                                   request_deserializer) as context:
         try:
+            response_or_iterator = None
             if send_response_callback is not None:
-                return behavior(argument, context, send_response_callback), True
+                response_or_iterator = behavior(argument, context,
+                                                send_response_callback)
             else:
-                return behavior(argument, context), True
+                response_or_iterator = behavior(argument, context)
+            return response_or_iterator, True
         except Exception as exception:  # pylint: disable=broad-except
             with state.condition:
                 if state.aborted:
@@ -447,6 +476,18 @@ def _serialize_response(rpc_event, state, response, response_serializer):
         return serialized_response
 
 
+def _get_send_message_op_flags_from_state(state):
+    if state.disable_next_compression:
+        return cygrpc.WriteFlag.no_compress
+    else:
+        return _EMPTY_FLAGS
+
+
+def _reset_per_message_state(state):
+    with state.condition:
+        state.disable_next_compression = False
+
+
 def _send_response(rpc_event, state, serialized_response):
     with state.condition:
         if not _is_rpc_state_active(state):
@@ -454,19 +495,22 @@ def _send_response(rpc_event, state, serialized_response):
         else:
             if state.initial_metadata_allowed:
                 operations = (
-                    cygrpc.SendInitialMetadataOperation(None, _EMPTY_FLAGS),
-                    cygrpc.SendMessageOperation(serialized_response,
-                                                _EMPTY_FLAGS),
+                    _get_initial_metadata_operation(state, None),
+                    cygrpc.SendMessageOperation(
+                        serialized_response,
+                        _get_send_message_op_flags_from_state(state)),
                 )
                 state.initial_metadata_allowed = False
                 token = _SEND_INITIAL_METADATA_AND_SEND_MESSAGE_TOKEN
             else:
                 operations = (cygrpc.SendMessageOperation(
-                    serialized_response, _EMPTY_FLAGS),)
+                    serialized_response,
+                    _get_send_message_op_flags_from_state(state)),)
                 token = _SEND_MESSAGE_TOKEN
             rpc_event.call.start_server_batch(operations,
                                               _send_message(state, token))
             state.due.add(token)
+            _reset_per_message_state(state)
             while True:
                 state.condition.wait()
                 if token not in state.due:
@@ -483,16 +527,17 @@ def _status(rpc_event, state, serialized_response):
                     state.trailing_metadata, code, details, _EMPTY_FLAGS),
             ]
             if state.initial_metadata_allowed:
-                operations.append(
-                    cygrpc.SendInitialMetadataOperation(None, _EMPTY_FLAGS))
+                operations.append(_get_initial_metadata_operation(state, None))
             if serialized_response is not None:
                 operations.append(
-                    cygrpc.SendMessageOperation(serialized_response,
-                                                _EMPTY_FLAGS))
+                    cygrpc.SendMessageOperation(
+                        serialized_response,
+                        _get_send_message_op_flags_from_state(state)))
             rpc_event.call.start_server_batch(
                 operations,
                 _send_status_from_server(state, _SEND_STATUS_FROM_SERVER_TOKEN))
             state.statused = True
+            _reset_per_message_state(state)
             state.due.add(_SEND_STATUS_FROM_SERVER_TOKEN)
 
 
@@ -639,13 +684,13 @@ def _find_method_handler(rpc_event, generic_handlers, interceptor_pipeline):
 
 
 def _reject_rpc(rpc_event, status, details):
+    rpc_state = _RPCState()
     operations = (
-        cygrpc.SendInitialMetadataOperation(None, _EMPTY_FLAGS),
+        _get_initial_metadata_operation(rpc_state, None),
         cygrpc.ReceiveCloseOnServerOperation(_EMPTY_FLAGS),
         cygrpc.SendStatusFromServerOperation(None, status, details,
                                              _EMPTY_FLAGS),
     )
-    rpc_state = _RPCState()
     rpc_event.call.start_server_batch(operations,
                                       lambda ignored_event: (rpc_state, (),))
     return rpc_state
@@ -883,13 +928,18 @@ def _validate_generic_rpc_handlers(generic_rpc_handlers):
                 'not have "service" method!'.format(generic_rpc_handler))
 
 
+def _augment_options(base_options, compression):
+    compression_option = _compression.create_channel_option(compression)
+    return tuple(base_options) + compression_option
+
+
 class _Server(grpc.Server):
 
     # pylint: disable=too-many-arguments
     def __init__(self, thread_pool, generic_handlers, interceptors, options,
-                 maximum_concurrent_rpcs):
+                 maximum_concurrent_rpcs, compression):
         completion_queue = cygrpc.CompletionQueue()
-        server = cygrpc.Server(options)
+        server = cygrpc.Server(_augment_options(options, compression))
         server.register_completion_queue(completion_queue)
         self._state = _ServerState(completion_queue, server, generic_handlers,
                                    _interceptor.service_pipeline(interceptors),
@@ -920,7 +970,7 @@ class _Server(grpc.Server):
 
 
 def create_server(thread_pool, generic_rpc_handlers, interceptors, options,
-                  maximum_concurrent_rpcs):
+                  maximum_concurrent_rpcs, compression):
     _validate_generic_rpc_handlers(generic_rpc_handlers)
     return _Server(thread_pool, generic_rpc_handlers, interceptors, options,
-                   maximum_concurrent_rpcs)
+                   maximum_concurrent_rpcs, compression)
index 21efd68..2619ccf 100644 (file)
@@ -19,7 +19,6 @@ CORE_SOURCE_FILES = [
     'third_party/address_sorting/address_sorting_posix.c',
     'third_party/address_sorting/address_sorting_windows.c',
     'src/core/lib/gpr/alloc.cc',
-    'src/core/lib/gpr/arena.cc',
     'src/core/lib/gpr/atm.cc',
     'src/core/lib/gpr/cpu_iphone.cc',
     'src/core/lib/gpr/cpu_linux.cc',
@@ -52,7 +51,9 @@ CORE_SOURCE_FILES = [
     'src/core/lib/gpr/tmpfile_posix.cc',
     'src/core/lib/gpr/tmpfile_windows.cc',
     'src/core/lib/gpr/wrap_memcpy.cc',
+    'src/core/lib/gprpp/arena.cc',
     'src/core/lib/gprpp/fork.cc',
+    'src/core/lib/gprpp/global_config_env.cc',
     'src/core/lib/gprpp/thd_posix.cc',
     'src/core/lib/gprpp/thd_windows.cc',
     'src/core/lib/profiling/basic_timers.cc',
@@ -71,6 +72,7 @@ CORE_SOURCE_FILES = [
     'src/core/lib/channel/handshaker_registry.cc',
     'src/core/lib/channel/status_util.cc',
     'src/core/lib/compression/compression.cc',
+    'src/core/lib/compression/compression_args.cc',
     'src/core/lib/compression/compression_internal.cc',
     'src/core/lib/compression/message_compress.cc',
     'src/core/lib/compression/stream_compression.cc',
@@ -83,12 +85,15 @@ CORE_SOURCE_FILES = [
     'src/core/lib/http/parser.cc',
     'src/core/lib/iomgr/buffer_list.cc',
     'src/core/lib/iomgr/call_combiner.cc',
+    'src/core/lib/iomgr/cfstream_handle.cc',
     'src/core/lib/iomgr/combiner.cc',
     'src/core/lib/iomgr/endpoint.cc',
+    'src/core/lib/iomgr/endpoint_cfstream.cc',
     'src/core/lib/iomgr/endpoint_pair_posix.cc',
     'src/core/lib/iomgr/endpoint_pair_uv.cc',
     'src/core/lib/iomgr/endpoint_pair_windows.cc',
     'src/core/lib/iomgr/error.cc',
+    'src/core/lib/iomgr/error_cfstream.cc',
     'src/core/lib/iomgr/ev_epoll1_linux.cc',
     'src/core/lib/iomgr/ev_epollex_linux.cc',
     'src/core/lib/iomgr/ev_poll_posix.cc',
@@ -109,6 +114,7 @@ CORE_SOURCE_FILES = [
     'src/core/lib/iomgr/iomgr_custom.cc',
     'src/core/lib/iomgr/iomgr_internal.cc',
     'src/core/lib/iomgr/iomgr_posix.cc',
+    'src/core/lib/iomgr/iomgr_posix_cfstream.cc',
     'src/core/lib/iomgr/iomgr_uv.cc',
     'src/core/lib/iomgr/iomgr_windows.cc',
     'src/core/lib/iomgr/is_epollexclusive_available.cc',
@@ -137,6 +143,7 @@ CORE_SOURCE_FILES = [
     'src/core/lib/iomgr/socket_utils_windows.cc',
     'src/core/lib/iomgr/socket_windows.cc',
     'src/core/lib/iomgr/tcp_client.cc',
+    'src/core/lib/iomgr/tcp_client_cfstream.cc',
     'src/core/lib/iomgr/tcp_client_custom.cc',
     'src/core/lib/iomgr/tcp_client_posix.cc',
     'src/core/lib/iomgr/tcp_client_windows.cc',
@@ -370,12 +377,15 @@ CORE_SOURCE_FILES = [
     'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc',
     'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc',
     'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc',
+    'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc',
     'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc',
     'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc',
     'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc',
     'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc',
+    'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc',
     'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc',
     'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc',
+    'src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc',
     'src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc',
     'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc',
     'src/core/ext/filters/census/grpc_context.cc',
index 443a2dc..cba5f3c 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc_version.py.template`!!!
 
-VERSION = '1.20.1'
+VERSION = '1.21.0'
index efeaa56..d66d0c4 100644 (file)
@@ -3,6 +3,14 @@ gRPC Python Channelz package
 
 Channelz is a live debug tool in gRPC Python.
 
+Supported Python Versions
+-------------------------
+Python >= 3.5
+
+Deprecated Python Versions
+--------------------------
+Python == 2.7. Python 2.7 support will be removed on January 1, 2020.
+
 Dependencies
 ------------
 
index aae8ced..eccc166 100644 (file)
@@ -1,30 +1,10 @@
-load("@grpc_python_dependencies//:requirements.bzl", "requirement")
-load("@org_pubref_rules_protobuf//python:rules.bzl", "py_proto_library")
-
+load("//bazel:python_rules.bzl", "py_proto_library")
 package(default_visibility = ["//visibility:public"])
 
-genrule(
-    name = "mv_channelz_proto",
-    srcs = [
-        "//src/proto/grpc/channelz:channelz_proto_file",
-    ],
-    outs = ["channelz.proto",],
-    cmd = "cp $< $@",
-)
-
 py_proto_library(
     name = "py_channelz_proto",
-    protos = ["mv_channelz_proto",],
-    imports = [
-        "external/com_google_protobuf/src/",
-    ],
-    inputs = [
-        "@com_google_protobuf//:well_known_protos",
-    ],
-    with_grpc = True,
-    deps = [
-        requirement('protobuf'),
-    ],
+    well_known_protos = True,
+    deps = ["//src/proto/grpc/channelz:channelz_proto_descriptors"],
 )
 
 py_library(
index a10aa78..29a7523 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_channelz/grpc_version.py.template`!!!
 
-VERSION = '1.20.1'
+VERSION = '1.21.0'
index f8c0e93..cc03809 100644 (file)
@@ -18,6 +18,9 @@ import sys
 
 import setuptools
 
+_PACKAGE_PATH = os.path.realpath(os.path.dirname(__file__))
+_README_PATH = os.path.join(_PACKAGE_PATH, 'README.rst')
+
 # Ensure we're in the proper directory whether or not we're being used by pip.
 os.chdir(os.path.dirname(os.path.abspath(__file__)))
 
@@ -85,6 +88,7 @@ setuptools.setup(
     version=grpc_version.VERSION,
     license='Apache License 2.0',
     description='Channel Level Live Debug Information Service for gRPC',
+    long_description=open(_README_PATH, 'r').read(),
     author='The gRPC Authors',
     author_email='grpc-io@googlegroups.com',
     classifiers=CLASSIFIERS,
index 600734e..044377a 100644 (file)
@@ -3,6 +3,14 @@ gRPC Python Health Checking
 
 Reference package for GRPC Python health checking.
 
+Supported Python Versions
+-------------------------
+Python >= 3.5
+
+Deprecated Python Versions
+--------------------------
+Python == 2.7. Python 2.7 support will be removed on January 1, 2020.
+
 Dependencies
 ------------
 
index ce3121f..9e4fff3 100644 (file)
@@ -1,24 +1,9 @@
-load("@grpc_python_dependencies//:requirements.bzl", "requirement")
-load("@org_pubref_rules_protobuf//python:rules.bzl", "py_proto_library")
-
+load("//bazel:python_rules.bzl", "py_proto_library")
 package(default_visibility = ["//visibility:public"])
 
-genrule(
-    name = "mv_health_proto",
-    srcs = [
-        "//src/proto/grpc/health/v1:health_proto_file",
-    ],
-    outs = ["health.proto",],
-    cmd = "cp $< $@",
-)
-
 py_proto_library(
     name = "py_health_proto",
-    protos = [":mv_health_proto",],
-    with_grpc = True,
-    deps = [
-        requirement('protobuf'),
-    ],
+    deps = ["//src/proto/grpc/health/v1:health_proto_descriptor",],
 )
 
 py_library(
index 7501427..80257d7 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_health_checking/grpc_version.py.template`!!!
 
-VERSION = '1.20.1'
+VERSION = '1.21.0'
index 5a09a80..dc2a69c 100644 (file)
@@ -17,6 +17,9 @@ import os
 
 import setuptools
 
+_PACKAGE_PATH = os.path.realpath(os.path.dirname(__file__))
+_README_PATH = os.path.join(_PACKAGE_PATH, 'README.rst')
+
 # Ensure we're in the proper directory whether or not we're being used by pip.
 os.chdir(os.path.dirname(os.path.abspath(__file__)))
 
@@ -83,6 +86,7 @@ setuptools.setup(
     name='grpcio-health-checking',
     version=grpc_version.VERSION,
     description='Standard Health Checking Service for gRPC',
+    long_description=open(_README_PATH, 'r').read(),
     author='The gRPC Authors',
     author_email='grpc-io@googlegroups.com',
     url='https://grpc.io',
index da99a44..56f9953 100644 (file)
@@ -3,6 +3,14 @@ gRPC Python Reflection package
 
 Reference package for reflection in GRPC Python.
 
+Supported Python Versions
+-------------------------
+Python >= 3.5
+
+Deprecated Python Versions
+--------------------------
+Python == 2.7. Python 2.7 support will be removed on January 1, 2020.
+
 Dependencies
 ------------
 
index 3a2ba26..6aaa4dd 100644 (file)
@@ -1,24 +1,11 @@
+load("//bazel:python_rules.bzl", "py_proto_library")
 load("@grpc_python_dependencies//:requirements.bzl", "requirement")
-load("@org_pubref_rules_protobuf//python:rules.bzl", "py_proto_library")
 
 package(default_visibility = ["//visibility:public"])
 
-genrule(
-    name = "mv_reflection_proto",
-    srcs = [
-        "//src/proto/grpc/reflection/v1alpha:reflection_proto_file",
-    ],
-    outs = ["reflection.proto",],
-    cmd = "cp $< $@",
-)
-
 py_proto_library(
     name = "py_reflection_proto",
-    protos = [":mv_reflection_proto",],
-    with_grpc = True,
-    deps = [
-        requirement('protobuf'),
-    ],
+    deps = ["//src/proto/grpc/reflection/v1alpha:reflection_proto_descriptor",],
 )
 
 py_library(
index 64ec549..bf61f45 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_reflection/grpc_version.py.template`!!!
 
-VERSION = '1.20.1'
+VERSION = '1.21.0'
index f205069..3fbcfda 100644 (file)
@@ -18,6 +18,9 @@ import sys
 
 import setuptools
 
+_PACKAGE_PATH = os.path.realpath(os.path.dirname(__file__))
+_README_PATH = os.path.join(_PACKAGE_PATH, 'README.rst')
+
 # Ensure we're in the proper directory whether or not we're being used by pip.
 os.chdir(os.path.dirname(os.path.abspath(__file__)))
 
@@ -85,6 +88,7 @@ setuptools.setup(
     version=grpc_version.VERSION,
     license='Apache License 2.0',
     description='Standard Protobuf Reflection Service for gRPC',
+    long_description=open(_README_PATH, 'r').read(),
     author='The gRPC Authors',
     author_email='grpc-io@googlegroups.com',
     classifiers=CLASSIFIERS,
index dc2f7b1..16c5938 100644 (file)
@@ -3,6 +3,14 @@ gRPC Python Status Proto
 
 Reference package for GRPC Python status proto mapping.
 
+Supported Python Versions
+-------------------------
+Python >= 3.5
+
+Deprecated Python Versions
+--------------------------
+Python == 2.7. Python 2.7 support will be removed on January 1, 2020.
+
 Dependencies
 ------------
 
index 456fcde..850704d 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_status/grpc_version.py.template`!!!
 
-VERSION = '1.20.1'
+VERSION = '1.21.0'
index 983d3ea..06d5dcf 100644 (file)
@@ -17,6 +17,9 @@ import os
 
 import setuptools
 
+_PACKAGE_PATH = os.path.realpath(os.path.dirname(__file__))
+_README_PATH = os.path.join(_PACKAGE_PATH, 'README.rst')
+
 # Ensure we're in the proper directory whether or not we're being used by pip.
 os.chdir(os.path.dirname(os.path.abspath(__file__)))
 
@@ -82,6 +85,7 @@ setuptools.setup(
     name='grpcio-status',
     version=grpc_version.VERSION,
     description='Status proto mapping for gRPC',
+    long_description=open(_README_PATH, 'r').read(),
     author='The gRPC Authors',
     author_email='grpc-io@googlegroups.com',
     url='https://grpc.io',
index c699b80..968dec8 100644 (file)
@@ -3,6 +3,14 @@ gRPC Python Testing Package
 
 Testing utilities for gRPC Python
 
+Supported Python Versions
+-------------------------
+Python >= 3.5
+
+Deprecated Python Versions
+--------------------------
+Python == 2.7. Python 2.7 support will be removed on January 1, 2020.
+
 Dependencies
 ------------
 
index 5b1dfea..63a1b1a 100644 (file)
@@ -56,6 +56,9 @@ class ServicerContext(grpc.ServicerContext):
     def auth_context(self):
         raise NotImplementedError()
 
+    def set_compression(self):
+        raise NotImplementedError()
+
     def send_initial_metadata(self, initial_metadata):
         initial_metadata_sent = self._rpc.send_initial_metadata(
             _common.fuss_with_metadata(initial_metadata))
@@ -63,6 +66,9 @@ class ServicerContext(grpc.ServicerContext):
             raise ValueError(
                 'ServicerContext.send_initial_metadata called too late!')
 
+    def disable_next_message_compression(self):
+        raise NotImplementedError()
+
     def set_trailing_metadata(self, trailing_metadata):
         self._rpc.set_trailing_metadata(
             _common.fuss_with_metadata(trailing_metadata))
index ed767b6..2a6b1c4 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_testing/grpc_version.py.template`!!!
 
-VERSION = '1.20.1'
+VERSION = '1.21.0'
index 18db71e..05d2a13 100644 (file)
@@ -18,6 +18,9 @@ import sys
 
 import setuptools
 
+_PACKAGE_PATH = os.path.realpath(os.path.dirname(__file__))
+_README_PATH = os.path.join(_PACKAGE_PATH, 'README.rst')
+
 # Ensure we're in the proper directory whether or not we're being used by pip.
 os.chdir(os.path.dirname(os.path.abspath(__file__)))
 
@@ -68,6 +71,7 @@ setuptools.setup(
     version=grpc_version.VERSION,
     license='Apache License 2.0',
     description='Testing utilities for gRPC Python',
+    long_description=open(_README_PATH, 'r').read(),
     author='The gRPC Authors',
     author_email='grpc-io@googlegroups.com',
     url='https://grpc.io',
index 8f27ab5..dc0795d 100644 (file)
@@ -117,6 +117,7 @@ class TestGevent(setuptools.Command):
         # eventually succeed, but need to dig into performance issues.
         'unit._cython._no_messages_server_completion_queue_per_call_test.Test.test_rpcs',
         'unit._cython._no_messages_single_server_completion_queue_test.Test.test_rpcs',
+        'unit._compression_test',
         # TODO(https://github.com/grpc/grpc/issues/16890) enable this test
         'unit._cython._channel_test.ChannelTest.test_multiple_channels_lonely_connectivity',
         # I have no idea why this doesn't work in gevent, but it shouldn't even be
index 8c8ee0c..73cbe39 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_tests/grpc_version.py.template`!!!
 
-VERSION = '1.20.1'
+VERSION = '1.21.0'
index 608148d..602786c 100644 (file)
@@ -56,12 +56,12 @@ class ForkInteropTest(unittest.TestCase):
 
             import grpc
             from src.proto.grpc.testing import test_pb2_grpc
-            from tests.interop import methods as interop_methods
+            from tests.interop import service as interop_service
             from tests.unit import test_common
 
             server = test_common.test_server()
             test_pb2_grpc.add_TestServiceServicer_to_server(
-                interop_methods.TestService(), server)
+                interop_service.TestService(), server)
             port = server.add_insecure_port('[::]:0')
             server.start()
             print(port)
index 1098d38..7a332b8 100644 (file)
@@ -246,7 +246,7 @@ class HealthServicerTest(BaseWatchTests.WatchTests):
         resp = self._stub.Check(request)
         self.assertEqual(health_pb2.HealthCheckResponse.SERVING, resp.status)
 
-    def test_check_unknown_serivce(self):
+    def test_check_unknown_service(self):
         request = health_pb2.HealthCheckRequest(service=_UNKNOWN_SERVICE)
         resp = self._stub.Check(request)
         self.assertEqual(health_pb2.HealthCheckResponse.UNKNOWN, resp.status)
index 770b1f7..fd63655 100644 (file)
@@ -5,45 +5,45 @@ package(default_visibility = ["//visibility:public"])
 py_library(
     name = "_intraop_test_case",
     srcs = ["_intraop_test_case.py"],
+    imports = ["../../"],
     deps = [
         ":methods",
     ],
-    imports=["../../",],
 )
 
 py_library(
     name = "client",
     srcs = ["client.py"],
+    imports = ["../../"],
     deps = [
-        "//src/python/grpcio/grpc:grpcio",
         ":methods",
         ":resources",
         "//src/proto/grpc/testing:py_test_proto",
-        requirement('google-auth'),
+        "//src/python/grpcio/grpc:grpcio",
+        requirement("google-auth"),
     ],
-    imports=["../../",],
 )
 
 py_library(
     name = "methods",
     srcs = ["methods.py"],
+    imports = ["../../"],
     deps = [
         "//src/python/grpcio/grpc:grpcio",
         "//src/python/grpcio_tests/tests:bazel_namespace_package_hack",
         "//src/proto/grpc/testing:py_empty_proto",
         "//src/proto/grpc/testing:py_messages_proto",
         "//src/proto/grpc/testing:py_test_proto",
-        requirement('google-auth'),
-        requirement('requests'),
-        requirement('urllib3'),
-        requirement('chardet'),
-        requirement('certifi'),
-        requirement('idna'),
+        requirement("google-auth"),
+        requirement("requests"),
+        requirement("urllib3"),
+        requirement("chardet"),
+        requirement("certifi"),
+        requirement("idna"),
     ] + select({
-        "//conditions:default": [requirement('enum34'),],
+        "//conditions:default": [requirement("enum34")],
         "//:python3": [],
     }),
-    imports=["../../",],
 )
 
 py_library(
@@ -55,50 +55,61 @@ py_library(
 )
 
 py_library(
+    name = "service",
+    srcs = ["service.py"],
+    imports = ["../../"],
+    deps = [
+        "//src/proto/grpc/testing:py_empty_proto",
+        "//src/proto/grpc/testing:py_messages_proto",
+        "//src/proto/grpc/testing:py_test_proto",
+        "//src/python/grpcio/grpc:grpcio",
+    ],
+)
+
+py_library(
     name = "server",
     srcs = ["server.py"],
+    imports = ["../../"],
     deps = [
-        "//src/python/grpcio/grpc:grpcio",
-        ":methods",
         ":resources",
-        "//src/python/grpcio_tests/tests/unit:test_common",
+        ":service",
         "//src/proto/grpc/testing:py_test_proto",
+        "//src/python/grpcio/grpc:grpcio",
+        "//src/python/grpcio_tests/tests/unit:test_common",
     ],
-    imports=["../../",],
 )
 
 py_test(
-    name="_insecure_intraop_test",
-    size="small",
-    srcs=["_insecure_intraop_test.py",],
-    main="_insecure_intraop_test.py",
-    deps=[
-        "//src/python/grpcio/grpc:grpcio",
+    name = "_insecure_intraop_test",
+    size = "small",
+    srcs = ["_insecure_intraop_test.py"],
+    data = [
+        "//src/python/grpcio_tests/tests/unit/credentials",
+    ],
+    imports = ["../../"],
+    main = "_insecure_intraop_test.py",
+    deps = [
         ":_intraop_test_case",
-        ":methods",
         ":server",
-        "//src/python/grpcio_tests/tests/unit:test_common",
+        ":service",
         "//src/proto/grpc/testing:py_test_proto",
-    ],
-    imports=["../../",],
-    data=[
-        "//src/python/grpcio_tests/tests/unit/credentials",
+        "//src/python/grpcio/grpc:grpcio",
+        "//src/python/grpcio_tests/tests/unit:test_common",
     ],
 )
 
 py_test(
-    name="_secure_intraop_test",
-    size="small",
-    srcs=["_secure_intraop_test.py",],
-    main="_secure_intraop_test.py",
-    deps=[
-        "//src/python/grpcio/grpc:grpcio",
+    name = "_secure_intraop_test",
+    size = "small",
+    srcs = ["_secure_intraop_test.py"],
+    imports = ["../../"],
+    main = "_secure_intraop_test.py",
+    deps = [
         ":_intraop_test_case",
-        ":methods",
         ":server",
-        "//src/python/grpcio_tests/tests/unit:test_common",
+        ":service",
         "//src/proto/grpc/testing:py_test_proto",
+        "//src/python/grpcio/grpc:grpcio",
+        "//src/python/grpcio_tests/tests/unit:test_common",
     ],
-    imports=["../../",],
 )
-
index ace15be..fecf317 100644 (file)
@@ -19,7 +19,7 @@ import grpc
 from src.proto.grpc.testing import test_pb2_grpc
 
 from tests.interop import _intraop_test_case
-from tests.interop import methods
+from tests.interop import service
 from tests.interop import server
 from tests.unit import test_common
 
@@ -29,7 +29,7 @@ class InsecureIntraopTest(_intraop_test_case.IntraopTestCase,
 
     def setUp(self):
         self.server = test_common.test_server()
-        test_pb2_grpc.add_TestServiceServicer_to_server(methods.TestService(),
+        test_pb2_grpc.add_TestServiceServicer_to_server(service.TestService(),
                                                         self.server)
         port = self.server.add_insecure_port('[::]:0')
         self.server.start()
index e27e551..1b5e5cf 100644 (file)
@@ -19,7 +19,7 @@ import grpc
 from src.proto.grpc.testing import test_pb2_grpc
 
 from tests.interop import _intraop_test_case
-from tests.interop import methods
+from tests.interop import service
 from tests.interop import resources
 from tests.unit import test_common
 
@@ -30,7 +30,7 @@ class SecureIntraopTest(_intraop_test_case.IntraopTestCase, unittest.TestCase):
 
     def setUp(self):
         self.server = test_common.test_server()
-        test_pb2_grpc.add_TestServiceServicer_to_server(methods.TestService(),
+        test_pb2_grpc.add_TestServiceServicer_to_server(service.TestService(),
                                                         self.server)
         port = self.server.add_secure_port(
             '[::]:0',
index 40341ca..250db8b 100644 (file)
@@ -25,6 +25,7 @@ import enum
 import json
 import os
 import threading
+import time
 
 from google import auth as google_auth
 from google.auth import environment_vars as google_auth_environment_vars
@@ -34,78 +35,11 @@ import grpc
 
 from src.proto.grpc.testing import empty_pb2
 from src.proto.grpc.testing import messages_pb2
-from src.proto.grpc.testing import test_pb2_grpc
 
 _INITIAL_METADATA_KEY = "x-grpc-test-echo-initial"
 _TRAILING_METADATA_KEY = "x-grpc-test-echo-trailing-bin"
 
 
-def _maybe_echo_metadata(servicer_context):
-    """Copies metadata from request to response if it is present."""
-    invocation_metadata = dict(servicer_context.invocation_metadata())
-    if _INITIAL_METADATA_KEY in invocation_metadata:
-        initial_metadatum = (_INITIAL_METADATA_KEY,
-                             invocation_metadata[_INITIAL_METADATA_KEY])
-        servicer_context.send_initial_metadata((initial_metadatum,))
-    if _TRAILING_METADATA_KEY in invocation_metadata:
-        trailing_metadatum = (_TRAILING_METADATA_KEY,
-                              invocation_metadata[_TRAILING_METADATA_KEY])
-        servicer_context.set_trailing_metadata((trailing_metadatum,))
-
-
-def _maybe_echo_status_and_message(request, servicer_context):
-    """Sets the response context code and details if the request asks for them"""
-    if request.HasField('response_status'):
-        servicer_context.set_code(request.response_status.code)
-        servicer_context.set_details(request.response_status.message)
-
-
-class TestService(test_pb2_grpc.TestServiceServicer):
-
-    def EmptyCall(self, request, context):
-        _maybe_echo_metadata(context)
-        return empty_pb2.Empty()
-
-    def UnaryCall(self, request, context):
-        _maybe_echo_metadata(context)
-        _maybe_echo_status_and_message(request, context)
-        return messages_pb2.SimpleResponse(
-            payload=messages_pb2.Payload(
-                type=messages_pb2.COMPRESSABLE,
-                body=b'\x00' * request.response_size))
-
-    def StreamingOutputCall(self, request, context):
-        _maybe_echo_status_and_message(request, context)
-        for response_parameters in request.response_parameters:
-            yield messages_pb2.StreamingOutputCallResponse(
-                payload=messages_pb2.Payload(
-                    type=request.response_type,
-                    body=b'\x00' * response_parameters.size))
-
-    def StreamingInputCall(self, request_iterator, context):
-        aggregate_size = 0
-        for request in request_iterator:
-            if request.payload is not None and request.payload.body:
-                aggregate_size += len(request.payload.body)
-        return messages_pb2.StreamingInputCallResponse(
-            aggregated_payload_size=aggregate_size)
-
-    def FullDuplexCall(self, request_iterator, context):
-        _maybe_echo_metadata(context)
-        for request in request_iterator:
-            _maybe_echo_status_and_message(request, context)
-            for response_parameters in request.response_parameters:
-                yield messages_pb2.StreamingOutputCallResponse(
-                    payload=messages_pb2.Payload(
-                        type=request.payload.type,
-                        body=b'\x00' * response_parameters.size))
-
-    # NOTE(nathaniel): Apparently this is the same as the full-duplex call?
-    # NOTE(atash): It isn't even called in the interop spec (Oct 22 2015)...
-    def HalfDuplexCall(self, request_iterator, context):
-        return self.FullDuplexCall(request_iterator, context)
-
-
 def _expect_status_code(call, expected_code):
     if call.code() != expected_code:
         raise ValueError('expected code %s, got %s' % (expected_code,
index 72f88a1..3611ffd 100644 (file)
@@ -21,7 +21,7 @@ import time
 import grpc
 from src.proto.grpc.testing import test_pb2_grpc
 
-from tests.interop import methods
+from tests.interop import service
 from tests.interop import resources
 from tests.unit import test_common
 
@@ -42,7 +42,7 @@ def serve():
     args = parser.parse_args()
 
     server = test_common.test_server()
-    test_pb2_grpc.add_TestServiceServicer_to_server(methods.TestService(),
+    test_pb2_grpc.add_TestServiceServicer_to_server(service.TestService(),
                                                     server)
     if args.use_tls:
         private_key = resources.private_key()
diff --git a/src/python/grpcio_tests/tests/interop/service.py b/src/python/grpcio_tests/tests/interop/service.py
new file mode 100644 (file)
index 0000000..37e4404
--- /dev/null
@@ -0,0 +1,97 @@
+# 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.
+"""The Python implementation of the TestServicer."""
+
+import time
+
+import grpc
+
+from src.proto.grpc.testing import empty_pb2
+from src.proto.grpc.testing import messages_pb2
+from src.proto.grpc.testing import test_pb2_grpc
+
+_INITIAL_METADATA_KEY = "x-grpc-test-echo-initial"
+_TRAILING_METADATA_KEY = "x-grpc-test-echo-trailing-bin"
+_US_IN_A_SECOND = 1000 * 1000
+
+
+def _maybe_echo_metadata(servicer_context):
+    """Copies metadata from request to response if it is present."""
+    invocation_metadata = dict(servicer_context.invocation_metadata())
+    if _INITIAL_METADATA_KEY in invocation_metadata:
+        initial_metadatum = (_INITIAL_METADATA_KEY,
+                             invocation_metadata[_INITIAL_METADATA_KEY])
+        servicer_context.send_initial_metadata((initial_metadatum,))
+    if _TRAILING_METADATA_KEY in invocation_metadata:
+        trailing_metadatum = (_TRAILING_METADATA_KEY,
+                              invocation_metadata[_TRAILING_METADATA_KEY])
+        servicer_context.set_trailing_metadata((trailing_metadatum,))
+
+
+def _maybe_echo_status_and_message(request, servicer_context):
+    """Sets the response context code and details if the request asks for them"""
+    if request.HasField('response_status'):
+        servicer_context.set_code(request.response_status.code)
+        servicer_context.set_details(request.response_status.message)
+
+
+class TestService(test_pb2_grpc.TestServiceServicer):
+
+    def EmptyCall(self, request, context):
+        _maybe_echo_metadata(context)
+        return empty_pb2.Empty()
+
+    def UnaryCall(self, request, context):
+        _maybe_echo_metadata(context)
+        _maybe_echo_status_and_message(request, context)
+        return messages_pb2.SimpleResponse(
+            payload=messages_pb2.Payload(
+                type=messages_pb2.COMPRESSABLE,
+                body=b'\x00' * request.response_size))
+
+    def StreamingOutputCall(self, request, context):
+        _maybe_echo_status_and_message(request, context)
+        for response_parameters in request.response_parameters:
+            if response_parameters.interval_us != 0:
+                time.sleep(response_parameters.interval_us / _US_IN_A_SECOND)
+            yield messages_pb2.StreamingOutputCallResponse(
+                payload=messages_pb2.Payload(
+                    type=request.response_type,
+                    body=b'\x00' * response_parameters.size))
+
+    def StreamingInputCall(self, request_iterator, context):
+        aggregate_size = 0
+        for request in request_iterator:
+            if request.payload is not None and request.payload.body:
+                aggregate_size += len(request.payload.body)
+        return messages_pb2.StreamingInputCallResponse(
+            aggregated_payload_size=aggregate_size)
+
+    def FullDuplexCall(self, request_iterator, context):
+        _maybe_echo_metadata(context)
+        for request in request_iterator:
+            _maybe_echo_status_and_message(request, context)
+            for response_parameters in request.response_parameters:
+                if response_parameters.interval_us != 0:
+                    time.sleep(
+                        response_parameters.interval_us / _US_IN_A_SECOND)
+                yield messages_pb2.StreamingOutputCallResponse(
+                    payload=messages_pb2.Payload(
+                        type=request.payload.type,
+                        body=b'\x00' * response_parameters.size))
+
+    # NOTE(nathaniel): Apparently this is the same as the full-duplex call?
+    # NOTE(atash): It isn't even called in the interop spec (Oct 22 2015)...
+    def HalfDuplexCall(self, request_iterator, context):
+        return self.FullDuplexCall(request_iterator, context)
index c0efb0b..b635b72 100644 (file)
@@ -14,6 +14,7 @@ py_test(
         "//src/python/grpcio_tests/tests/unit:test_common",
         "//src/proto/grpc/testing:py_empty_proto",
         "//src/proto/grpc/testing/proto2:empty2_extensions_proto",
+        "//src/proto/grpc/testing/proto2:empty2_proto",
         requirement('protobuf'),
     ],
     imports=["../../",],
index 4c35b05..11e46d4 100644 (file)
@@ -34,12 +34,12 @@ def _args():
         description='gRPC Python stress test client')
     parser.add_argument(
         '--server_addresses',
-        help='comma seperated list of hostname:port to run servers on',
+        help='comma separated list of hostname:port to run servers on',
         default='localhost:8080',
         type=str)
     parser.add_argument(
         '--test_cases',
-        help='comma seperated list of testcase:weighting of tests to run',
+        help='comma separated list of testcase:weighting of tests to run',
         default='large_unary:100',
         type=str)
     parser.add_argument(
index 9c9887b..a161794 100644 (file)
@@ -35,6 +35,11 @@ GRPCIO_TESTS_UNIT = [
 ]
 
 py_library(
+    name = "_tcp_proxy",
+    srcs = ["_tcp_proxy.py"],
+)
+
+py_library(
     name = "resources",
     srcs = ["resources.py"],
     data=[
@@ -81,6 +86,7 @@ py_library(
             ":_exit_scenarios",
             ":_server_shutdown_scenarios",
             ":_from_grpc_import_star",
+            ":_tcp_proxy",
             "//src/python/grpcio_tests/tests/unit/framework/common",
             "//src/python/grpcio_tests/tests/testing",
             requirement('six'),
index 0dc6a87..127dab3 100644 (file)
@@ -31,6 +31,7 @@ class AllTest(unittest.TestCase):
             'FutureCancelledError',
             'Future',
             'ChannelConnectivity',
+            'Compression',
             'StatusCode',
             'Status',
             'RpcError',
index 87884a1..f9cf8eb 100644 (file)
 
 import unittest
 
+import contextlib
+from concurrent import futures
+import functools
+import itertools
 import logging
+import os
+
 import grpc
 from grpc import _grpcio_metadata
 
 from tests.unit import test_common
 from tests.unit.framework.common import test_constants
+from tests.unit import _tcp_proxy
 
 _UNARY_UNARY = '/test/UnaryUnary'
+_UNARY_STREAM = '/test/UnaryStream'
+_STREAM_UNARY = '/test/StreamUnary'
 _STREAM_STREAM = '/test/StreamStream'
 
+# Cut down on test time.
+_STREAM_LENGTH = test_constants.STREAM_LENGTH // 16
+
+_HOST = 'localhost'
+
+_REQUEST = b'\x00' * 100
+_COMPRESSION_RATIO_THRESHOLD = 0.05
+_COMPRESSION_METHODS = (
+    None,
+    # Disabled for test tractability.
+    # grpc.Compression.NoCompression,
+    # grpc.Compression.Deflate,
+    grpc.Compression.Gzip,
+)
+_COMPRESSION_NAMES = {
+    None: 'Uncompressed',
+    grpc.Compression.NoCompression: 'NoCompression',
+    grpc.Compression.Deflate: 'DeflateCompression',
+    grpc.Compression.Gzip: 'GzipCompression',
+}
+
+_TEST_OPTIONS = {
+    'client_streaming': (True, False),
+    'server_streaming': (True, False),
+    'channel_compression': _COMPRESSION_METHODS,
+    'multicallable_compression': _COMPRESSION_METHODS,
+    'server_compression': _COMPRESSION_METHODS,
+    'server_call_compression': _COMPRESSION_METHODS,
+}
+
+
+def _make_handle_unary_unary(pre_response_callback):
+
+    def _handle_unary(request, servicer_context):
+        if pre_response_callback:
+            pre_response_callback(request, servicer_context)
+        return request
+
+    return _handle_unary
+
+
+def _make_handle_unary_stream(pre_response_callback):
+
+    def _handle_unary_stream(request, servicer_context):
+        if pre_response_callback:
+            pre_response_callback(request, servicer_context)
+        for _ in range(_STREAM_LENGTH):
+            yield request
+
+    return _handle_unary_stream
+
+
+def _make_handle_stream_unary(pre_response_callback):
+
+    def _handle_stream_unary(request_iterator, servicer_context):
+        if pre_response_callback:
+            pre_response_callback(request_iterator, servicer_context)
+        response = None
+        for request in request_iterator:
+            if not response:
+                response = request
+        return response
+
+    return _handle_stream_unary
 
-def handle_unary(request, servicer_context):
-    servicer_context.send_initial_metadata([('grpc-internal-encoding-request',
-                                             'gzip')])
-    return request
 
+def _make_handle_stream_stream(pre_response_callback):
 
-def handle_stream(request_iterator, servicer_context):
-    # TODO(issue:#6891) We should be able to remove this loop,
-    # and replace with return; yield
-    servicer_context.send_initial_metadata([('grpc-internal-encoding-request',
-                                             'gzip')])
-    for request in request_iterator:
-        yield request
+    def _handle_stream(request_iterator, servicer_context):
+        # TODO(issue:#6891) We should be able to remove this loop,
+        # and replace with return; yield
+        for request in request_iterator:
+            if pre_response_callback:
+                pre_response_callback(request, servicer_context)
+            yield request
+
+    return _handle_stream
+
+
+def set_call_compression(compression_method, request_or_iterator,
+                         servicer_context):
+    del request_or_iterator
+    servicer_context.set_compression(compression_method)
+
+
+def disable_next_compression(request, servicer_context):
+    del request
+    servicer_context.disable_next_message_compression()
+
+
+def disable_first_compression(request, servicer_context):
+    if int(request.decode('ascii')) == 0:
+        servicer_context.disable_next_message_compression()
 
 
 class _MethodHandler(grpc.RpcMethodHandler):
 
-    def __init__(self, request_streaming, response_streaming):
+    def __init__(self, request_streaming, response_streaming,
+                 pre_response_callback):
         self.request_streaming = request_streaming
         self.response_streaming = response_streaming
         self.request_deserializer = None
@@ -52,75 +141,239 @@ class _MethodHandler(grpc.RpcMethodHandler):
         self.unary_stream = None
         self.stream_unary = None
         self.stream_stream = None
+
         if self.request_streaming and self.response_streaming:
-            self.stream_stream = handle_stream
+            self.stream_stream = _make_handle_stream_stream(
+                pre_response_callback)
         elif not self.request_streaming and not self.response_streaming:
-            self.unary_unary = handle_unary
+            self.unary_unary = _make_handle_unary_unary(pre_response_callback)
+        elif not self.request_streaming and self.response_streaming:
+            self.unary_stream = _make_handle_unary_stream(pre_response_callback)
+        else:
+            self.stream_unary = _make_handle_stream_unary(pre_response_callback)
 
 
 class _GenericHandler(grpc.GenericRpcHandler):
 
+    def __init__(self, pre_response_callback):
+        self._pre_response_callback = pre_response_callback
+
     def service(self, handler_call_details):
         if handler_call_details.method == _UNARY_UNARY:
-            return _MethodHandler(False, False)
+            return _MethodHandler(False, False, self._pre_response_callback)
+        elif handler_call_details.method == _UNARY_STREAM:
+            return _MethodHandler(False, True, self._pre_response_callback)
+        elif handler_call_details.method == _STREAM_UNARY:
+            return _MethodHandler(True, False, self._pre_response_callback)
         elif handler_call_details.method == _STREAM_STREAM:
-            return _MethodHandler(True, True)
+            return _MethodHandler(True, True, self._pre_response_callback)
         else:
             return None
 
 
+@contextlib.contextmanager
+def _instrumented_client_server_pair(channel_kwargs, server_kwargs,
+                                     server_handler):
+    server = grpc.server(futures.ThreadPoolExecutor(), **server_kwargs)
+    server.add_generic_rpc_handlers((server_handler,))
+    server_port = server.add_insecure_port('{}:0'.format(_HOST))
+    server.start()
+    with _tcp_proxy.TcpProxy(_HOST, _HOST, server_port) as proxy:
+        proxy_port = proxy.get_port()
+        with grpc.insecure_channel('{}:{}'.format(_HOST, proxy_port),
+                                   **channel_kwargs) as client_channel:
+            try:
+                yield client_channel, proxy, server
+            finally:
+                server.stop(None)
+
+
+def _get_byte_counts(channel_kwargs, multicallable_kwargs, client_function,
+                     server_kwargs, server_handler, message):
+    with _instrumented_client_server_pair(channel_kwargs, server_kwargs,
+                                          server_handler) as pipeline:
+        client_channel, proxy, server = pipeline
+        client_function(client_channel, multicallable_kwargs, message)
+        return proxy.get_byte_count()
+
+
+def _get_compression_ratios(client_function, first_channel_kwargs,
+                            first_multicallable_kwargs, first_server_kwargs,
+                            first_server_handler, second_channel_kwargs,
+                            second_multicallable_kwargs, second_server_kwargs,
+                            second_server_handler, message):
+    try:
+        # This test requires the byte length of each connection to be deterministic. As
+        # it turns out, flow control puts bytes on the wire in a nondeterministic
+        # manner. We disable it here in order to measure compression ratios
+        # deterministically.
+        os.environ['GRPC_EXPERIMENTAL_DISABLE_FLOW_CONTROL'] = 'true'
+        first_bytes_sent, first_bytes_received = _get_byte_counts(
+            first_channel_kwargs, first_multicallable_kwargs, client_function,
+            first_server_kwargs, first_server_handler, message)
+        second_bytes_sent, second_bytes_received = _get_byte_counts(
+            second_channel_kwargs, second_multicallable_kwargs, client_function,
+            second_server_kwargs, second_server_handler, message)
+        return ((
+            second_bytes_sent - first_bytes_sent) / float(first_bytes_sent),
+                (second_bytes_received - first_bytes_received) /
+                float(first_bytes_received))
+    finally:
+        del os.environ['GRPC_EXPERIMENTAL_DISABLE_FLOW_CONTROL']
+
+
+def _unary_unary_client(channel, multicallable_kwargs, message):
+    multi_callable = channel.unary_unary(_UNARY_UNARY)
+    response = multi_callable(message, **multicallable_kwargs)
+    if response != message:
+        raise RuntimeError("Request '{}' != Response '{}'".format(
+            message, response))
+
+
+def _unary_stream_client(channel, multicallable_kwargs, message):
+    multi_callable = channel.unary_stream(_UNARY_STREAM)
+    response_iterator = multi_callable(message, **multicallable_kwargs)
+    for response in response_iterator:
+        if response != message:
+            raise RuntimeError("Request '{}' != Response '{}'".format(
+                message, response))
+
+
+def _stream_unary_client(channel, multicallable_kwargs, message):
+    multi_callable = channel.stream_unary(_STREAM_UNARY)
+    requests = (_REQUEST for _ in range(_STREAM_LENGTH))
+    response = multi_callable(requests, **multicallable_kwargs)
+    if response != message:
+        raise RuntimeError("Request '{}' != Response '{}'".format(
+            message, response))
+
+
+def _stream_stream_client(channel, multicallable_kwargs, message):
+    multi_callable = channel.stream_stream(_STREAM_STREAM)
+    request_prefix = str(0).encode('ascii') * 100
+    requests = (
+        request_prefix + str(i).encode('ascii') for i in range(_STREAM_LENGTH))
+    response_iterator = multi_callable(requests, **multicallable_kwargs)
+    for i, response in enumerate(response_iterator):
+        if int(response.decode('ascii')) != i:
+            raise RuntimeError("Request '{}' != Response '{}'".format(
+                i, response))
+
+
 class CompressionTest(unittest.TestCase):
 
-    def setUp(self):
-        self._server = test_common.test_server()
-        self._server.add_generic_rpc_handlers((_GenericHandler(),))
-        self._port = self._server.add_insecure_port('[::]:0')
-        self._server.start()
-
-    def tearDown(self):
-        self._server.stop(None)
-
-    def testUnary(self):
-        request = b'\x00' * 100
-
-        # Client -> server compressed through default client channel compression
-        # settings. Server -> client compressed via server-side metadata setting.
-        # TODO(https://github.com/grpc/grpc/issues/4078): replace the "1" integer
-        # literal with proper use of the public API.
-        compressed_channel = grpc.insecure_channel(
-            'localhost:%d' % self._port,
-            options=[('grpc.default_compression_algorithm', 1)])
-        multi_callable = compressed_channel.unary_unary(_UNARY_UNARY)
-        response = multi_callable(request)
-        self.assertEqual(request, response)
-
-        # Client -> server compressed through client metadata setting. Server ->
-        # client compressed via server-side metadata setting.
-        # TODO(https://github.com/grpc/grpc/issues/4078): replace the "0" integer
-        # literal with proper use of the public API.
-        uncompressed_channel = grpc.insecure_channel(
-            'localhost:%d' % self._port,
-            options=[('grpc.default_compression_algorithm', 0)])
-        multi_callable = compressed_channel.unary_unary(_UNARY_UNARY)
-        response = multi_callable(
-            request, metadata=[('grpc-internal-encoding-request', 'gzip')])
-        self.assertEqual(request, response)
-        compressed_channel.close()
-
-    def testStreaming(self):
-        request = b'\x00' * 100
-
-        # TODO(https://github.com/grpc/grpc/issues/4078): replace the "1" integer
-        # literal with proper use of the public API.
-        compressed_channel = grpc.insecure_channel(
-            'localhost:%d' % self._port,
-            options=[('grpc.default_compression_algorithm', 1)])
-        multi_callable = compressed_channel.stream_stream(_STREAM_STREAM)
-        call = multi_callable(iter([request] * test_constants.STREAM_LENGTH))
-        for response in call:
-            self.assertEqual(request, response)
-        compressed_channel.close()
+    def assertCompressed(self, compression_ratio):
+        self.assertLess(
+            compression_ratio,
+            -1.0 * _COMPRESSION_RATIO_THRESHOLD,
+            msg='Actual compression ratio: {}'.format(compression_ratio))
+
+    def assertNotCompressed(self, compression_ratio):
+        self.assertGreaterEqual(
+            compression_ratio,
+            -1.0 * _COMPRESSION_RATIO_THRESHOLD,
+            msg='Actual compession ratio: {}'.format(compression_ratio))
+
+    def assertConfigurationCompressed(
+            self, client_streaming, server_streaming, channel_compression,
+            multicallable_compression, server_compression,
+            server_call_compression):
+        client_side_compressed = channel_compression or multicallable_compression
+        server_side_compressed = server_compression or server_call_compression
+        channel_kwargs = {
+            'compression': channel_compression,
+        } if channel_compression else {}
+        multicallable_kwargs = {
+            'compression': multicallable_compression,
+        } if multicallable_compression else {}
+
+        client_function = None
+        if not client_streaming and not server_streaming:
+            client_function = _unary_unary_client
+        elif not client_streaming and server_streaming:
+            client_function = _unary_stream_client
+        elif client_streaming and not server_streaming:
+            client_function = _stream_unary_client
+        else:
+            client_function = _stream_stream_client
+
+        server_kwargs = {
+            'compression': server_compression,
+        } if server_compression else {}
+        server_handler = _GenericHandler(
+            functools.partial(set_call_compression, grpc.Compression.Gzip)
+        ) if server_call_compression else _GenericHandler(None)
+        sent_ratio, received_ratio = _get_compression_ratios(
+            client_function, {}, {}, {}, _GenericHandler(None), channel_kwargs,
+            multicallable_kwargs, server_kwargs, server_handler, _REQUEST)
+
+        if client_side_compressed:
+            self.assertCompressed(sent_ratio)
+        else:
+            self.assertNotCompressed(sent_ratio)
+
+        if server_side_compressed:
+            self.assertCompressed(received_ratio)
+        else:
+            self.assertNotCompressed(received_ratio)
+
+    def testDisableNextCompressionStreaming(self):
+        server_kwargs = {
+            'compression': grpc.Compression.Deflate,
+        }
+        _, received_ratio = _get_compression_ratios(
+            _stream_stream_client, {}, {}, {}, _GenericHandler(None), {}, {},
+            server_kwargs, _GenericHandler(disable_next_compression), _REQUEST)
+        self.assertNotCompressed(received_ratio)
+
+    def testDisableNextCompressionStreamingResets(self):
+        server_kwargs = {
+            'compression': grpc.Compression.Deflate,
+        }
+        _, received_ratio = _get_compression_ratios(
+            _stream_stream_client, {}, {}, {}, _GenericHandler(None), {}, {},
+            server_kwargs, _GenericHandler(disable_first_compression), _REQUEST)
+        self.assertCompressed(received_ratio)
+
+
+def _get_compression_str(name, value):
+    return '{}{}'.format(name, _COMPRESSION_NAMES[value])
+
+
+def _get_compression_test_name(client_streaming, server_streaming,
+                               channel_compression, multicallable_compression,
+                               server_compression, server_call_compression):
+    client_arity = 'Stream' if client_streaming else 'Unary'
+    server_arity = 'Stream' if server_streaming else 'Unary'
+    arity = '{}{}'.format(client_arity, server_arity)
+    channel_compression_str = _get_compression_str('Channel',
+                                                   channel_compression)
+    multicallable_compression_str = _get_compression_str(
+        'Multicallable', multicallable_compression)
+    server_compression_str = _get_compression_str('Server', server_compression)
+    server_call_compression_str = _get_compression_str('ServerCall',
+                                                       server_call_compression)
+    return 'test{}{}{}{}{}'.format(
+        arity, channel_compression_str, multicallable_compression_str,
+        server_compression_str, server_call_compression_str)
+
+
+def _test_options():
+    for test_parameters in itertools.product(*_TEST_OPTIONS.values()):
+        yield dict(zip(_TEST_OPTIONS.keys(), test_parameters))
+
+
+for options in _test_options():
+
+    def test_compression(**kwargs):
+
+        def _test_compression(self):
+            self.assertConfigurationCompressed(**kwargs)
+
+        return _test_compression
 
+    setattr(CompressionTest, _get_compression_test_name(**options),
+            test_compression(**options))
 
 if __name__ == '__main__':
     logging.basicConfig()
diff --git a/src/python/grpcio_tests/tests/unit/_tcp_proxy.py b/src/python/grpcio_tests/tests/unit/_tcp_proxy.py
new file mode 100644 (file)
index 0000000..5ad0bf8
--- /dev/null
@@ -0,0 +1,164 @@
+# 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.
+""" Proxies a TCP connection between a single client-server pair.
+
+This proxy is not suitable for production, but should work well for cases in
+which a test needs to spy on the bytes put on the wire between a server and
+a client.
+"""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import datetime
+import select
+import socket
+import threading
+
+_TCP_PROXY_BUFFER_SIZE = 1024
+_TCP_PROXY_TIMEOUT = datetime.timedelta(milliseconds=500)
+
+
+def _create_socket_ipv6(bind_address):
+    listen_socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
+    listen_socket.bind((bind_address, 0, 0, 0))
+    return listen_socket
+
+
+def _create_socket_ipv4(bind_address):
+    listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    listen_socket.bind((bind_address, 0))
+    return listen_socket
+
+
+def _init_listen_socket(bind_address):
+    listen_socket = None
+    if socket.has_ipv6:
+        try:
+            listen_socket = _create_socket_ipv6(bind_address)
+        except socket.error:
+            listen_socket = _create_socket_ipv4(bind_address)
+    else:
+        listen_socket = _create_socket_ipv4(bind_address)
+    listen_socket.listen(1)
+    return listen_socket, listen_socket.getsockname()[1]
+
+
+def _init_proxy_socket(gateway_address, gateway_port):
+    proxy_socket = socket.create_connection((gateway_address, gateway_port))
+    return proxy_socket
+
+
+class TcpProxy(object):
+    """Proxies a TCP connection between one client and one server."""
+
+    def __init__(self, bind_address, gateway_address, gateway_port):
+        self._bind_address = bind_address
+        self._gateway_address = gateway_address
+        self._gateway_port = gateway_port
+
+        self._byte_count_lock = threading.RLock()
+        self._sent_byte_count = 0
+        self._received_byte_count = 0
+
+        self._stop_event = threading.Event()
+
+        self._port = None
+        self._listen_socket = None
+        self._proxy_socket = None
+
+        # The following three attributes are owned by the serving thread.
+        self._northbound_data = b""
+        self._southbound_data = b""
+        self._client_sockets = []
+
+        self._thread = threading.Thread(target=self._run_proxy)
+
+    def start(self):
+        self._listen_socket, self._port = _init_listen_socket(
+            self._bind_address)
+        self._proxy_socket = _init_proxy_socket(self._gateway_address,
+                                                self._gateway_port)
+        self._thread.start()
+
+    def get_port(self):
+        return self._port
+
+    def _handle_reads(self, sockets_to_read):
+        for socket_to_read in sockets_to_read:
+            if socket_to_read is self._listen_socket:
+                client_socket, client_address = socket_to_read.accept()
+                self._client_sockets.append(client_socket)
+            elif socket_to_read is self._proxy_socket:
+                data = socket_to_read.recv(_TCP_PROXY_BUFFER_SIZE)
+                with self._byte_count_lock:
+                    self._received_byte_count += len(data)
+                self._northbound_data += data
+            elif socket_to_read in self._client_sockets:
+                data = socket_to_read.recv(_TCP_PROXY_BUFFER_SIZE)
+                if data:
+                    with self._byte_count_lock:
+                        self._sent_byte_count += len(data)
+                    self._southbound_data += data
+                else:
+                    self._client_sockets.remove(socket_to_read)
+            else:
+                raise RuntimeError('Unidentified socket appeared in read set.')
+
+    def _handle_writes(self, sockets_to_write):
+        for socket_to_write in sockets_to_write:
+            if socket_to_write is self._proxy_socket:
+                if self._southbound_data:
+                    self._proxy_socket.sendall(self._southbound_data)
+                    self._southbound_data = b""
+            elif socket_to_write in self._client_sockets:
+                if self._northbound_data:
+                    socket_to_write.sendall(self._northbound_data)
+                    self._northbound_data = b""
+
+    def _run_proxy(self):
+        while not self._stop_event.is_set():
+            expected_reads = (self._listen_socket, self._proxy_socket) + tuple(
+                self._client_sockets)
+            expected_writes = expected_reads
+            sockets_to_read, sockets_to_write, _ = select.select(
+                expected_reads, expected_writes, (),
+                _TCP_PROXY_TIMEOUT.total_seconds())
+            self._handle_reads(sockets_to_read)
+            self._handle_writes(sockets_to_write)
+        for client_socket in self._client_sockets:
+            client_socket.close()
+
+    def stop(self):
+        self._stop_event.set()
+        self._thread.join()
+        self._listen_socket.close()
+        self._proxy_socket.close()
+
+    def get_byte_count(self):
+        with self._byte_count_lock:
+            return self._sent_byte_count, self._received_byte_count
+
+    def reset_byte_count(self):
+        with self._byte_count_lock:
+            self._byte_count = 0
+            self._received_byte_count = 0
+
+    def __enter__(self):
+        self.start()
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.stop()
index 4916cee..4aa4121 100644 (file)
@@ -304,7 +304,7 @@ static VALUE bg_thread_init_rb_mu = Qundef;
 static int bg_thread_init_done = 0;
 
 static void grpc_ruby_init_threads() {
-  // Avoid calling calling into ruby library (when creating threads here)
+  // Avoid calling into ruby library (when creating threads here)
   // in gpr_once_init. In general, it appears to be unsafe to call
   // into the ruby library while holding a non-ruby mutex, because a gil yield
   // could end up trying to lock onto that same mutex and deadlocking.
index 0c15c2f..0b27bee 100644 (file)
@@ -42,12 +42,31 @@ module GRPC
       @metadata = metadata
     end
 
-    # Converts the exception to a GRPC::Status for use in the networking
+    # Converts the exception to a {Struct::Status} for use in the networking
     # wrapper layer.
     #
-    # @return [Status] with the same code and details
+    # @return [Struct::Status] with the same code and details
     def to_status
-      Struct::Status.new(code, details, @metadata)
+      Struct::Status.new(code, details, metadata)
+    end
+
+    # Converts the exception to a deserialized {Google::Rpc::Status} object.
+    # Returns `nil` if the `grpc-status-details-bin` trailer could not be
+    # converted to a {Google::Rpc::Status} due to the server not providing
+    # the necessary trailers.
+    #
+    # @return [Google::Rpc::Status, nil]
+    def to_rpc_status
+      # Lazily require google_rpc_status_utils to scope
+      # loading protobuf_c.so to the users of this method.
+      require_relative './google_rpc_status_utils'
+      status = to_status
+      return if status.nil?
+      GoogleRpcStatusUtils.extract_google_rpc_status(status)
+    rescue Google::Protobuf::ParseError => parse_error
+      GRPC.logger.warn('parse error: to_rpc_status failed')
+      GRPC.logger.warn(parse_error)
+      nil
     end
 
     def self.new_status_exception(code, details = 'unknown cause',
index ffb232b..a4d4af6 100644 (file)
@@ -224,7 +224,7 @@ module GRPC
         set_input_stream_done.call
       end
       GRPC.logger.debug('bidi-read-loop: finished')
-      # Make sure that the write loop is done done before finishing the call.
+      # Make sure that the write loop is done before finishing the call.
       # Note that blocking is ok at this point because we've already received
       # a status
       @enq_th.join if is_client
index dfeb47f..3d34419 100644 (file)
@@ -202,7 +202,7 @@ module GRPC
     # forcing an abrupt exit to each thread.
     #
     # * connect_md_proc:
-    # when non-nil is a proc for determining metadata to to send back the client
+    # when non-nil is a proc for determining metadata to send back the client
     # on receiving an invocation req.  The proc signature is:
     #   {key: val, ..} func(method_name, {key: val, ...})
     #
index 620f67f..272a053 100644 (file)
@@ -14,5 +14,5 @@
 
 # GRPC contains the General RPC module.
 module GRPC
-  VERSION = '1.20.1'
+  VERSION = '1.21.0'
 end
diff --git a/src/ruby/spec/errors_spec.rb b/src/ruby/spec/errors_spec.rb
new file mode 100644 (file)
index 0000000..bf27a53
--- /dev/null
@@ -0,0 +1,141 @@
+# 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.
+
+require 'spec_helper'
+require 'google/protobuf/well_known_types'
+require_relative '../pb/src/proto/grpc/testing/messages_pb'
+
+describe GRPC::BadStatus do
+  describe :attributes do
+    it 'has attributes' do
+      code = 1
+      details = 'details'
+      metadata = { 'key' => 'val' }
+
+      exception = GRPC::BadStatus.new(code, details, metadata)
+
+      expect(exception.code).to eq code
+      expect(exception.details).to eq details
+      expect(exception.metadata).to eq metadata
+    end
+  end
+
+  describe :new_status_exception do
+    let(:codes_and_classes) do
+      [
+        [GRPC::Core::StatusCodes::OK, GRPC::Ok],
+        [GRPC::Core::StatusCodes::CANCELLED, GRPC::Cancelled],
+        [GRPC::Core::StatusCodes::UNKNOWN, GRPC::Unknown],
+        [GRPC::Core::StatusCodes::INVALID_ARGUMENT, GRPC::InvalidArgument],
+        [GRPC::Core::StatusCodes::DEADLINE_EXCEEDED, GRPC::DeadlineExceeded],
+        [GRPC::Core::StatusCodes::NOT_FOUND, GRPC::NotFound],
+        [GRPC::Core::StatusCodes::ALREADY_EXISTS, GRPC::AlreadyExists],
+        [GRPC::Core::StatusCodes::PERMISSION_DENIED, GRPC::PermissionDenied],
+        [GRPC::Core::StatusCodes::UNAUTHENTICATED, GRPC::Unauthenticated],
+        [GRPC::Core::StatusCodes::RESOURCE_EXHAUSTED, GRPC::ResourceExhausted],
+        [GRPC::Core::StatusCodes::FAILED_PRECONDITION, GRPC::FailedPrecondition],
+        [GRPC::Core::StatusCodes::ABORTED, GRPC::Aborted],
+        [GRPC::Core::StatusCodes::OUT_OF_RANGE, GRPC::OutOfRange],
+        [GRPC::Core::StatusCodes::UNIMPLEMENTED, GRPC::Unimplemented],
+        [GRPC::Core::StatusCodes::INTERNAL, GRPC::Internal],
+        [GRPC::Core::StatusCodes::UNAVAILABLE, GRPC::Unavailable],
+        [GRPC::Core::StatusCodes::DATA_LOSS, GRPC::DataLoss],
+        [99, GRPC::BadStatus] # Unknown codes default to BadStatus
+      ]
+    end
+
+    it 'maps codes to the correct error class' do
+      codes_and_classes.each do |code, grpc_error_class|
+        exception = GRPC::BadStatus.new_status_exception(code)
+
+        expect(exception).to be_a grpc_error_class
+      end
+    end
+  end
+
+  describe :to_status do
+    it 'gets status' do
+      code = 1
+      details = 'details'
+      metadata = { 'key' => 'val' }
+
+      exception = GRPC::BadStatus.new(code, details, metadata)
+      status = Struct::Status.new(code, details, metadata)
+
+      expect(exception.to_status).to eq status
+    end
+  end
+
+  describe :to_rpc_status do
+    let(:simple_request_any) do
+      Google::Protobuf::Any.new.tap do |any|
+        any.pack(
+          Grpc::Testing::SimpleRequest.new(
+            payload: Grpc::Testing::Payload.new(body: 'request')
+          )
+        )
+      end
+    end
+    let(:simple_response_any) do
+      Google::Protobuf::Any.new.tap do |any|
+        any.pack(
+          Grpc::Testing::SimpleResponse.new(
+            payload: Grpc::Testing::Payload.new(body: 'response')
+          )
+        )
+      end
+    end
+    let(:payload_any) do
+      Google::Protobuf::Any.new.tap do |any|
+        any.pack(Grpc::Testing::Payload.new(body: 'payload'))
+      end
+    end
+
+    it 'decodes proto values' do
+      rpc_status = Google::Rpc::Status.new(
+        code: 1,
+        message: 'matching message',
+        details: [simple_request_any, simple_response_any, payload_any]
+      )
+      rpc_status_proto = Google::Rpc::Status.encode(rpc_status)
+
+      code = 1
+      details = 'details'
+      metadata = { 'grpc-status-details-bin' => rpc_status_proto }
+
+      exception = GRPC::BadStatus.new(code, details, metadata)
+
+      expect(exception.to_rpc_status).to eq rpc_status
+    end
+
+    it 'does not raise when decoding a bad proto' do
+      code = 1
+      details = 'details'
+      metadata = { 'grpc-status-details-bin' => 'notavalidprotostream' }
+
+      exception = GRPC::BadStatus.new(code, details, metadata)
+
+      expect(exception.to_rpc_status).to be nil
+
+      error_msg = 'parse error: to_rpc_status failed'
+      error_desc = '<Google::Protobuf::ParseError> ' \
+        'Error occurred during parsing: Invalid wire type'
+
+      # Check that the parse error was logged correctly
+      log_contents = @log_output.read
+      expect(log_contents).to include "WARN  GRPC : #{error_msg}"
+      expect(log_contents).to include "WARN  GRPC : #{error_desc}"
+    end
+  end
+end
index 5860fbc..3f1c59e 100644 (file)
@@ -14,6 +14,6 @@
 
 module GRPC
   module Tools
-    VERSION = '1.20.1'
+    VERSION = '1.21.0'
   end
 end
index 71391f8..24bea5c 100644 (file)
   ${arg} += $(EXTRA_${arg})
   % endfor
 
-  HOST_CPPFLAGS = $(CPPFLAGS)
-  HOST_CFLAGS = $(CFLAGS)
-  HOST_CXXFLAGS = $(CXXFLAGS)
-  HOST_LDFLAGS = $(LDFLAGS)
-  HOST_LDLIBS = $(LDLIBS)
+  HOST_CPPFLAGS += $(CPPFLAGS)
+  HOST_CFLAGS += $(CFLAGS)
+  HOST_CXXFLAGS += $(CXXFLAGS)
+  HOST_LDFLAGS += $(LDFLAGS)
+  HOST_LDLIBS += $(LDLIBS)
 
   # These are automatically computed variables.
   # There shouldn't be any need to change anything from now on.
index 43cb6db..40368e4 100644 (file)
     s.name     = 'gRPC-C++'
     # TODO (mxyan): use version that match gRPC version when pod is stabilized
     # version = '${settings.version}'
-    version = '${modify_podspec_version_string('0.0.8', settings.version)}'
+    version = '${modify_podspec_version_string('0.0.9', settings.version)}'
     s.version  = version
     s.summary  = 'gRPC C++ library'
     s.homepage = 'https://grpc.io'
     s.default_subspecs = 'Interface', 'Implementation'
 
     # Certificates, to be able to establish TLS connections:
-    s.resource_bundles = { 'gRPCCertificates' => ['etc/roots.pem'] }
+    s.resource_bundles = { 'gRPCCertificates-Cpp' => ['etc/roots.pem'] }
 
     s.header_mappings_dir = 'include/grpcpp'
 
index 93dc735..89a1e91 100644 (file)
     excl = grpc_private_files(libs)
     return [file for file in out if not file in excl]
 
-  def cfstream_private_headers(libs):
-    out = grpc_lib_files(libs, ("grpc_cfstream",), ("own_headers",))
-    return out
-
-  def cfstream_private_files(libs):
-    out = grpc_lib_files(libs, ("grpc_cfstream",), ("own_src", "own_headers"))
-    return out
-
   def ruby_multiline_list(files, indent):
     return (',\n' + indent*' ').join('\'%s\'' % f for f in files)
   %>
       ss.private_header_files = ${ruby_multiline_list(grpc_private_headers(libs), 30)}
     end
 
+    # CFStream is now default. Leaving this subspec only for compatibility purpose.
     s.subspec 'CFStream-Implementation' do |ss|
-      ss.header_mappings_dir = '.'
       ss.dependency "#{s.name}/Implementation", version
-      ss.pod_target_xcconfig = {
-        'GCC_PREPROCESSOR_DEFINITIONS' => 'GRPC_CFSTREAM=1'
-      }
-      ss.source_files = ${ruby_multiline_list(cfstream_private_files(filegroups), 22)}
-      ss.private_header_files = ${ruby_multiline_list(cfstream_private_headers(filegroups), 30)}
     end
 
     s.subspec 'Cronet-Interface' do |ss|
index 9d7e392..e4d5f3f 100644 (file)
 
       ss.source_files = "#{src_dir}/*.{h,m}"
     end
+
+    # CFStream is now default. Leaving this subspec only for compatibility purpose.
     s.subspec 'CFStream' do |ss|
-      ss.dependency 'gRPC/CFStream', version
       ss.dependency "#{s.name}/Main", version
-      ss.pod_target_xcconfig = {
-        'GCC_PREPROCESSOR_DEFINITIONS' => 'GRPC_CFSTREAM=1'
-      }
     end
 
     s.pod_target_xcconfig = {
index 6fe20e6..c972393 100644 (file)
       ss.dependency 'gRPC-Core', version
     end
 
-    # This subspec is mutually exclusive with the `Main` subspec
+    # CFStream is now default. Leaving this subspec only for compatibility purpose.
     s.subspec 'CFStream' do |ss|
-      ss.dependency 'gRPC-Core/CFStream-Implementation', version
       ss.dependency "#{s.name}/Main", version
-
-      ss.pod_target_xcconfig = {
-        'GCC_PREPROCESSOR_DEFINITIONS' => 'GRPC_CFSTREAM=1'
-      }
     end
 
     s.subspec 'GID' do |ss|
index 0e32171..d2b54a9 100644 (file)
@@ -32,7 +32,7 @@
     s.platform      = Gem::Platform::RUBY
 
     s.add_dependency 'google-protobuf', '~> 3.7'
-    s.add_dependency 'googleapis-common-protos-types', '~> 1.0.0'
+    s.add_dependency 'googleapis-common-protos-types', '~> 1.0'
 
     s.add_development_dependency 'bundler',            '~> 1.9'
     s.add_development_dependency 'facter',             '~> 2.4'
index b7a8e28..4a31171 100644 (file)
@@ -6,7 +6,7 @@ native_method_signatures = [
     'BatchContextSafeHandle grpcsharp_batch_context_create()',
     'IntPtr grpcsharp_batch_context_recv_initial_metadata(BatchContextSafeHandle ctx)',
     'IntPtr grpcsharp_batch_context_recv_message_length(BatchContextSafeHandle ctx)',
-    'void grpcsharp_batch_context_recv_message_to_buffer(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen)',
+    'int grpcsharp_batch_context_recv_message_next_slice_peek(BatchContextSafeHandle ctx, out UIntPtr sliceLen, out IntPtr sliceDataPtr)',
     'StatusCode grpcsharp_batch_context_recv_status_on_client_status(BatchContextSafeHandle ctx)',
     'IntPtr grpcsharp_batch_context_recv_status_on_client_details(BatchContextSafeHandle ctx, out UIntPtr detailsLength)',
     'IntPtr grpcsharp_batch_context_recv_status_on_client_trailing_metadata(BatchContextSafeHandle ctx)',
@@ -44,7 +44,7 @@ native_method_signatures = [
     'void grpcsharp_channel_args_set_integer(ChannelArgsSafeHandle args, UIntPtr index, string key, int value)',
     'void grpcsharp_channel_args_destroy(IntPtr args)',
     'void grpcsharp_override_default_ssl_roots(string pemRootCerts)',
-    'ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey)',
+    'ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey, IntPtr verifyPeerCallbackTag)',
     'ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds)',
     'void grpcsharp_channel_credentials_release(IntPtr credentials)',
     'ChannelSafeHandle grpcsharp_insecure_channel_create(string target, ChannelArgsSafeHandle channelArgs)',
index 62d6860..8888e93 100644 (file)
@@ -2,6 +2,6 @@
 # Bazel installation
 
 RUN apt-get update && apt-get install -y wget && apt-get clean
-RUN wget https://github.com/bazelbuild/bazel/releases/download/0.20.0/bazel-0.20.0-installer-linux-x86_64.sh && ${'\\'}
-  bash ./bazel-0.20.0-installer-linux-x86_64.sh && ${'\\'}
-  rm bazel-0.20.0-installer-linux-x86_64.sh
+RUN wget https://github.com/bazelbuild/bazel/releases/download/0.23.2/bazel-0.23.2-installer-linux-x86_64.sh && ${'\\'}
+  bash ./bazel-0.23.2-installer-linux-x86_64.sh && ${'\\'}
+  rm bazel-0.23.2-installer-linux-x86_64.sh
index 11bd5f9..f71f986 100644 (file)
@@ -1,2 +1 @@
 FROM debian:jessie
-RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
index e80b517..7307e29 100644 (file)
 <%include file="../../debian_jessie_header.include"/>
 
 <%include file="java_deps.include"/>
-<%include file="../../python_deps.include"/>
-
-# Trigger download of as many Gradle artifacts as possible.
-RUN git clone --recursive --depth 1 https://github.com/grpc/grpc-java.git && ${'\\'}
-  cd grpc-java && ${'\\'}
-  ./gradlew :grpc-interop-testing:installDist -PskipCodegen=true && ${'\\'}
-  rm -r "$(pwd)"
 
 # Define the default command.
-CMD ["bash"]
\ No newline at end of file
+CMD ["bash"]
index 40d70e0..c05b564 100644 (file)
@@ -1,16 +1,11 @@
-# Install JDK 8 and Git
+# Install JDK 8
 #
 RUN echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections && ${'\\'}
   echo "deb http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main" | tee /etc/apt/sources.list.d/webupd8team-java.list && ${'\\'}
   echo "deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main" | tee -a /etc/apt/sources.list.d/webupd8team-java.list && ${'\\'}
-  apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys EEA14886
-
-RUN apt-get update && apt-get -y install ${'\\'}
-      git ${'\\'}
-      libapr1 ${'\\'}
-      oracle-java8-installer ${'\\'}
-      && ${'\\'}
-    apt-get clean && rm -r /var/cache/oracle-jdk8-installer/
+  apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys EEA14886 && ${'\\'}
+  apt-get update && apt-get -y install oracle-java8-installer && ${'\\'}
+  apt-get clean && rm -r /var/cache/oracle-jdk8-installer/
 
 ENV JAVA_HOME /usr/lib/jvm/java-8-oracle
 ENV PATH $PATH:$JAVA_HOME/bin
index 16d5fb6..e30b53e 100755 (executable)
 # Builds Java interop server and client in a base image.
 set -e
 
-mkdir -p /var/local/git
-git clone --recursive --depth 1 /var/local/jenkins/grpc-java /var/local/git/grpc-java
+cp -r /var/local/jenkins/grpc-java /tmp/grpc-java
 
 # copy service account keys if available
 cp -r /var/local/jenkins/service_account $HOME || true
 
-cd /var/local/git/grpc-java
-
+pushd /tmp/grpc-java
 ./gradlew :grpc-interop-testing:installDist -PskipCodegen=true
 
+mkdir -p /var/local/git/grpc-java/
+cp -r --parents -t /var/local/git/grpc-java/ ${'\\'}
+    interop-testing/build/install/ ${'\\'}
+    run-test-client.sh ${'\\'}
+    run-test-server.sh
+
+popd
+rm -r /tmp/grpc-java
+rm -r "$HOME/.gradle"
+
 # enable extra java logging
 mkdir -p /var/local/grpc_java_logging
 echo "handlers = java.util.logging.ConsoleHandler
index 6b49252..26550a2 100644 (file)
@@ -257,7 +257,7 @@ bool client_connection_preface_validator(grpc_slice_buffer* incoming,
     return false;
   }
   grpc_slice slice = incoming->slices[0];
-  /* There should be atleast a settings frame present */
+  /* There should be at least one settings frame present */
   if (GRPC_SLICE_LENGTH(slice) < MIN_HTTP2_FRAME_SIZE) {
     return false;
   }
index 317526a..78a1a5c 100644 (file)
@@ -39,7 +39,6 @@
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/iomgr/endpoint_pair.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/completion_queue.h"
@@ -328,7 +327,6 @@ static void _test_close_before_server_recv(fd_type fdtype) {
    */
   if (event.type == GRPC_QUEUE_TIMEOUT) {
     GPR_ASSERT(event.success == 0);
-    GPR_ASSERT(event.tag == nullptr);
     /* status is not initialized */
     GPR_ASSERT(status == GRPC_STATUS__DO_NOT_USE);
   } else {
@@ -531,7 +529,6 @@ static void _test_close_before_server_send(fd_type fdtype) {
   } else {
     GPR_ASSERT(event.type == GRPC_QUEUE_TIMEOUT);
     GPR_ASSERT(event.success == 0);
-    GPR_ASSERT(event.tag == nullptr);
     /* status is not initialized */
     GPR_ASSERT(status == GRPC_STATUS__DO_NOT_USE);
   }
@@ -664,7 +661,6 @@ static void _test_close_before_client_send(fd_type fdtype) {
       g_ctx.cq, grpc_timeout_milliseconds_to_deadline(100), nullptr);
   GPR_ASSERT(event.success == 0);
   GPR_ASSERT(event.type == GRPC_QUEUE_TIMEOUT);
-  GPR_ASSERT(event.tag == nullptr);
 
   grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
@@ -720,13 +716,11 @@ static void _test_close_before_call_create(fd_type fdtype) {
       g_ctx.client_cq, grpc_timeout_milliseconds_to_deadline(100), nullptr);
   GPR_ASSERT(event.type == GRPC_QUEUE_TIMEOUT);
   GPR_ASSERT(event.success == 0);
-  GPR_ASSERT(event.tag == nullptr);
 
   event = grpc_completion_queue_next(
       g_ctx.cq, grpc_timeout_milliseconds_to_deadline(100), nullptr);
   GPR_ASSERT(event.type == GRPC_QUEUE_TIMEOUT);
   GPR_ASSERT(event.success == 0);
-  GPR_ASSERT(event.tag == nullptr);
 
   grpc_call_unref(call);
   end_test();
index 73d251e..8dd55f6 100644 (file)
@@ -25,9 +25,9 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
+#include "src/core/lib/security/security_connector/ssl_utils.h"
 #include "test/core/end2end/cq_verifier.h"
 #include "test/core/util/port.h"
 #include "test/core/util/subprocess.h"
@@ -133,7 +133,7 @@ int main(int argc, char** argv) {
     strcpy(root, ".");
   }
   if (argc == 2) {
-    gpr_setenv("GRPC_DEFAULT_SSL_ROOTS_FILE_PATH", argv[1]);
+    GPR_GLOBAL_CONFIG_SET(grpc_default_ssl_roots_file_path, argv[1]);
   }
   /* figure out our test name */
   tmp = lunder - 1;
index 087a767..9c47d5d 100644 (file)
@@ -58,96 +58,6 @@ static void test_create(void) {
   grpc_channel_args_destroy(ch_args);
 }
 
-static void test_set_compression_algorithm(void) {
-  grpc_core::ExecCtx exec_ctx;
-  grpc_channel_args* ch_args;
-
-  ch_args =
-      grpc_channel_args_set_compression_algorithm(nullptr, GRPC_COMPRESS_GZIP);
-  GPR_ASSERT(ch_args->num_args == 1);
-  GPR_ASSERT(strcmp(ch_args->args[0].key,
-                    GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM) == 0);
-  GPR_ASSERT(ch_args->args[0].type == GRPC_ARG_INTEGER);
-
-  grpc_channel_args_destroy(ch_args);
-}
-
-static void test_compression_algorithm_states(void) {
-  grpc_core::ExecCtx exec_ctx;
-  grpc_channel_args *ch_args, *ch_args_wo_gzip, *ch_args_wo_gzip_deflate,
-      *ch_args_wo_gzip_deflate_gzip;
-  unsigned states_bitset;
-  size_t i;
-
-  ch_args = grpc_channel_args_copy_and_add(nullptr, nullptr, 0);
-  /* by default, all enabled */
-  states_bitset = static_cast<unsigned>(
-      grpc_channel_args_compression_algorithm_get_states(ch_args));
-
-  for (i = 0; i < GRPC_COMPRESS_ALGORITHMS_COUNT; i++) {
-    GPR_ASSERT(GPR_BITGET(states_bitset, i));
-  }
-
-  /* disable gzip and deflate and stream/gzip */
-  ch_args_wo_gzip = grpc_channel_args_compression_algorithm_set_state(
-      &ch_args, GRPC_COMPRESS_GZIP, 0);
-  GPR_ASSERT(ch_args == ch_args_wo_gzip);
-  ch_args_wo_gzip_deflate = grpc_channel_args_compression_algorithm_set_state(
-      &ch_args_wo_gzip, GRPC_COMPRESS_DEFLATE, 0);
-  GPR_ASSERT(ch_args_wo_gzip == ch_args_wo_gzip_deflate);
-  ch_args_wo_gzip_deflate_gzip =
-      grpc_channel_args_compression_algorithm_set_state(
-          &ch_args_wo_gzip_deflate, GRPC_COMPRESS_STREAM_GZIP, 0);
-  GPR_ASSERT(ch_args_wo_gzip_deflate == ch_args_wo_gzip_deflate_gzip);
-
-  states_bitset =
-      static_cast<unsigned>(grpc_channel_args_compression_algorithm_get_states(
-          ch_args_wo_gzip_deflate));
-  for (i = 0; i < GRPC_COMPRESS_ALGORITHMS_COUNT; i++) {
-    if (i == GRPC_COMPRESS_GZIP || i == GRPC_COMPRESS_DEFLATE ||
-        i == GRPC_COMPRESS_STREAM_GZIP) {
-      GPR_ASSERT(GPR_BITGET(states_bitset, i) == 0);
-    } else {
-      GPR_ASSERT(GPR_BITGET(states_bitset, i) != 0);
-    }
-  }
-
-  /* re-enabled gzip and stream/gzip only */
-  ch_args_wo_gzip = grpc_channel_args_compression_algorithm_set_state(
-      &ch_args_wo_gzip_deflate_gzip, GRPC_COMPRESS_GZIP, 1);
-  ch_args_wo_gzip = grpc_channel_args_compression_algorithm_set_state(
-      &ch_args_wo_gzip, GRPC_COMPRESS_STREAM_GZIP, 1);
-  GPR_ASSERT(ch_args_wo_gzip == ch_args_wo_gzip_deflate_gzip);
-
-  states_bitset = static_cast<unsigned>(
-      grpc_channel_args_compression_algorithm_get_states(ch_args_wo_gzip));
-  for (i = 0; i < GRPC_COMPRESS_ALGORITHMS_COUNT; i++) {
-    if (i == GRPC_COMPRESS_DEFLATE) {
-      GPR_ASSERT(GPR_BITGET(states_bitset, i) == 0);
-    } else {
-      GPR_ASSERT(GPR_BITGET(states_bitset, i) != 0);
-    }
-  }
-
-  grpc_channel_args_destroy(ch_args);
-}
-
-static void test_set_socket_mutator(void) {
-  grpc_channel_args* ch_args;
-  grpc_socket_mutator mutator;
-  grpc_socket_mutator_init(&mutator, nullptr);
-
-  ch_args = grpc_channel_args_set_socket_mutator(nullptr, &mutator);
-  GPR_ASSERT(ch_args->num_args == 1);
-  GPR_ASSERT(strcmp(ch_args->args[0].key, GRPC_ARG_SOCKET_MUTATOR) == 0);
-  GPR_ASSERT(ch_args->args[0].type == GRPC_ARG_POINTER);
-
-  {
-    grpc_core::ExecCtx exec_ctx;
-    grpc_channel_args_destroy(ch_args);
-  }
-}
-
 struct fake_class {
   int foo;
 };
@@ -234,9 +144,6 @@ int main(int argc, char** argv) {
   grpc::testing::TestEnvironment env(argc, argv);
   grpc_init();
   test_create();
-  test_set_compression_algorithm();
-  test_compression_algorithm_states();
-  test_set_socket_mutator();
   test_channel_create_with_args();
   test_server_create_with_args();
   grpc_shutdown();
index 68a7163..a9cfa96 100644 (file)
@@ -79,3 +79,17 @@ grpc_cc_test(
         "//test/core/util:grpc_test_util",
     ],
 )
+
+grpc_cc_test(
+    name = "service_config_test",
+    srcs = ["service_config_test.cc"],
+    external_deps = [
+        "gtest",
+    ],
+    language = "C++",
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//test/core/util:grpc_test_util",
+    ],
+)
index ed3b4e6..129866b 100644 (file)
@@ -21,8 +21,8 @@
 #include <grpc/support/log.h>
 
 #include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
+#include "src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h"
 #include "src/core/ext/filters/client_channel/resolver_registry.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/iomgr/combiner.h"
 #include "test/core/util/test_config.h"
@@ -78,13 +78,13 @@ int main(int argc, char** argv) {
   test_succeeds(dns, "dns:10.2.1.1:1234");
   test_succeeds(dns, "dns:www.google.com");
   test_succeeds(dns, "dns:///www.google.com");
-  char* resolver_env = gpr_getenv("GRPC_DNS_RESOLVER");
-  if (resolver_env != nullptr && gpr_stricmp(resolver_env, "native") == 0) {
+  grpc_core::UniquePtr<char> resolver =
+      GPR_GLOBAL_CONFIG_GET(grpc_dns_resolver);
+  if (gpr_stricmp(resolver.get(), "native") == 0) {
     test_fails(dns, "dns://8.8.8.8/8.8.8.8:8888");
   } else {
     test_succeeds(dns, "dns://8.8.8.8/8.8.8.8:8888");
   }
-  gpr_free(resolver_env);
   {
     grpc_core::ExecCtx exec_ctx;
     GRPC_COMBINER_UNREF(g_combiner, "test");
diff --git a/test/core/client_channel/service_config_test.cc b/test/core/client_channel/service_config_test.cc
new file mode 100644 (file)
index 0000000..9734304
--- /dev/null
@@ -0,0 +1,1045 @@
+/*
+ *
+ * Copyright 2019 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <regex>
+
+#include <gtest/gtest.h>
+
+#include <grpc/grpc.h>
+#include "src/core/ext/filters/client_channel/resolver_result_parsing.h"
+#include "src/core/ext/filters/client_channel/service_config.h"
+#include "src/core/ext/filters/message_size/message_size_filter.h"
+#include "src/core/lib/gpr/string.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+namespace grpc_core {
+namespace testing {
+
+class TestParsedObject1 : public ServiceConfig::ParsedConfig {
+ public:
+  TestParsedObject1(int value) : value_(value) {}
+
+  int value() const { return value_; }
+
+ private:
+  int value_;
+};
+
+class TestParser1 : public ServiceConfig::Parser {
+ public:
+  UniquePtr<ServiceConfig::ParsedConfig> ParseGlobalParams(
+      const grpc_json* json, grpc_error** error) override {
+    GPR_DEBUG_ASSERT(error != nullptr);
+    for (grpc_json* field = json->child; field != nullptr;
+         field = field->next) {
+      if (strcmp(field->key, "global_param") == 0) {
+        if (field->type != GRPC_JSON_NUMBER) {
+          *error =
+              GRPC_ERROR_CREATE_FROM_STATIC_STRING(InvalidTypeErrorMessage());
+          return nullptr;
+        }
+        int value = gpr_parse_nonnegative_int(field->value);
+        if (value == -1) {
+          *error =
+              GRPC_ERROR_CREATE_FROM_STATIC_STRING(InvalidValueErrorMessage());
+          return nullptr;
+        }
+        return UniquePtr<ServiceConfig::ParsedConfig>(
+            New<TestParsedObject1>(value));
+      }
+    }
+    return nullptr;
+  }
+
+  static const char* InvalidTypeErrorMessage() {
+    return "global_param value type should be a number";
+  }
+
+  static const char* InvalidValueErrorMessage() {
+    return "global_param value type should be non-negative";
+  }
+};
+
+class TestParser2 : public ServiceConfig::Parser {
+ public:
+  UniquePtr<ServiceConfig::ParsedConfig> ParsePerMethodParams(
+      const grpc_json* json, grpc_error** error) override {
+    GPR_DEBUG_ASSERT(error != nullptr);
+    for (grpc_json* field = json->child; field != nullptr;
+         field = field->next) {
+      if (field->key == nullptr || strcmp(field->key, "name") == 0) {
+        continue;
+      }
+      if (strcmp(field->key, "method_param") == 0) {
+        if (field->type != GRPC_JSON_NUMBER) {
+          *error =
+              GRPC_ERROR_CREATE_FROM_STATIC_STRING(InvalidTypeErrorMessage());
+          return nullptr;
+        }
+        int value = gpr_parse_nonnegative_int(field->value);
+        if (value == -1) {
+          *error =
+              GRPC_ERROR_CREATE_FROM_STATIC_STRING(InvalidValueErrorMessage());
+          return nullptr;
+        }
+        return UniquePtr<ServiceConfig::ParsedConfig>(
+            New<TestParsedObject1>(value));
+      }
+    }
+    return nullptr;
+  }
+
+  static const char* InvalidTypeErrorMessage() {
+    return "method_param value type should be a number";
+  }
+
+  static const char* InvalidValueErrorMessage() {
+    return "method_param value type should be non-negative";
+  }
+};
+
+// This parser always adds errors
+class ErrorParser : public ServiceConfig::Parser {
+ public:
+  UniquePtr<ServiceConfig::ParsedConfig> ParsePerMethodParams(
+      const grpc_json* json, grpc_error** error) override {
+    GPR_DEBUG_ASSERT(error != nullptr);
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(MethodError());
+    return nullptr;
+  }
+
+  UniquePtr<ServiceConfig::ParsedConfig> ParseGlobalParams(
+      const grpc_json* json, grpc_error** error) override {
+    GPR_DEBUG_ASSERT(error != nullptr);
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(GlobalError());
+    return nullptr;
+  }
+
+  static const char* MethodError() { return "ErrorParser : methodError"; }
+
+  static const char* GlobalError() { return "ErrorParser : globalError"; }
+};
+
+void VerifyRegexMatch(grpc_error* error, const std::regex& e) {
+  std::smatch match;
+  std::string s(grpc_error_string(error));
+  EXPECT_TRUE(std::regex_search(s, match, e));
+  GRPC_ERROR_UNREF(error);
+}
+
+class ServiceConfigTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ServiceConfig::Shutdown();
+    ServiceConfig::Init();
+    EXPECT_TRUE(ServiceConfig::RegisterParser(
+                    UniquePtr<ServiceConfig::Parser>(New<TestParser1>())) == 0);
+    EXPECT_TRUE(ServiceConfig::RegisterParser(
+                    UniquePtr<ServiceConfig::Parser>(New<TestParser2>())) == 1);
+  }
+};
+
+TEST_F(ServiceConfigTest, ErrorCheck1) {
+  const char* test_json = "";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(std::string("failed to parse JSON for service config"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ServiceConfigTest, BasicTest1) {
+  const char* test_json = "{}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  EXPECT_TRUE(error == GRPC_ERROR_NONE);
+}
+
+TEST_F(ServiceConfigTest, ErrorNoNames) {
+  const char* test_json = "{\"methodConfig\": [{\"blah\":1}]}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(
+      std::string("(Service config parsing "
+                  "error)(.*)(referenced_errors)(.*)(Method "
+                  "Params)(.*)(referenced_errors)(.*)(No names "
+                  "found)(.*)(methodConfig)(.*)(referenced_errors)(.*)(No "
+                  "names specified)"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ServiceConfigTest, ErrorNoNamesWithMultipleMethodConfigs) {
+  const char* test_json =
+      "{\"methodConfig\": [{}, {\"name\":[{\"service\":\"TestServ\"}]}]}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(
+      std::string("(Service config parsing "
+                  "error)(.*)(referenced_errors)(.*)(Method "
+                  "Params)(.*)(referenced_errors)(.*)(No names "
+                  "found)(.*)(methodConfig)(.*)(referenced_errors)(.*)(No "
+                  "names specified)"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ServiceConfigTest, ValidMethodConfig) {
+  const char* test_json =
+      "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}]}]}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  EXPECT_TRUE(error == GRPC_ERROR_NONE);
+}
+
+TEST_F(ServiceConfigTest, Parser1BasicTest1) {
+  const char* test_json = "{\"global_param\":5}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  ASSERT_TRUE(error == GRPC_ERROR_NONE);
+  EXPECT_TRUE((static_cast<TestParsedObject1*>(
+                   svc_cfg->GetParsedGlobalServiceConfigObject(0)))
+                  ->value() == 5);
+  EXPECT_TRUE(svc_cfg->GetMethodServiceConfigObjectsVector(
+                  grpc_slice_from_static_string("/TestServ/TestMethod")) ==
+              nullptr);
+}
+
+TEST_F(ServiceConfigTest, Parser1BasicTest2) {
+  const char* test_json = "{\"global_param\":1000}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  ASSERT_TRUE(error == GRPC_ERROR_NONE);
+  EXPECT_TRUE((static_cast<TestParsedObject1*>(
+                   svc_cfg->GetParsedGlobalServiceConfigObject(0)))
+                  ->value() == 1000);
+}
+
+TEST_F(ServiceConfigTest, Parser1ErrorInvalidType) {
+  const char* test_json = "{\"global_param\":\"5\"}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(std::string("(Service config parsing "
+                           "error)(.*)(referenced_errors)(.*)(Global "
+                           "Params)(.*)(referenced_errors)(.*)") +
+               TestParser1::InvalidTypeErrorMessage());
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ServiceConfigTest, Parser1ErrorInvalidValue) {
+  const char* test_json = "{\"global_param\":-5}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(std::string("(Service config parsing "
+                           "error)(.*)(referenced_errors)(.*)(Global "
+                           "Params)(.*)(referenced_errors)(.*)") +
+               TestParser1::InvalidValueErrorMessage());
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ServiceConfigTest, Parser2BasicTest) {
+  const char* test_json =
+      "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], "
+      "\"method_param\":5}]}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  ASSERT_TRUE(error == GRPC_ERROR_NONE);
+  const auto* vector_ptr = svc_cfg->GetMethodServiceConfigObjectsVector(
+      grpc_slice_from_static_string("/TestServ/TestMethod"));
+  EXPECT_TRUE(vector_ptr != nullptr);
+  auto parsed_object = ((*vector_ptr)[1]).get();
+  EXPECT_TRUE(static_cast<TestParsedObject1*>(parsed_object)->value() == 5);
+}
+
+TEST_F(ServiceConfigTest, Parser2ErrorInvalidType) {
+  const char* test_json =
+      "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], "
+      "\"method_param\":\"5\"}]}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  std::regex e(std::string("(Service config parsing "
+                           "error)(.*)(referenced_errors\":\\[)(.*)(Method "
+                           "Params)(.*)(referenced_errors)(.*)(methodConfig)("
+                           ".*)(referenced_errors)(.*)") +
+               TestParser2::InvalidTypeErrorMessage());
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ServiceConfigTest, Parser2ErrorInvalidValue) {
+  const char* test_json =
+      "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], "
+      "\"method_param\":-5}]}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  std::regex e(std::string("(Service config parsing "
+                           "error)(.*)(referenced_errors\":\\[)(.*)(Method "
+                           "Params)(.*)(referenced_errors)()(.*)(methodConfig)("
+                           ".*)(referenced_errors)(.*)") +
+               TestParser2::InvalidValueErrorMessage());
+  VerifyRegexMatch(error, e);
+}
+
+// Test parsing with ErrorParsers which always add errors
+class ErroredParsersScopingTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ServiceConfig::Shutdown();
+    ServiceConfig::Init();
+    EXPECT_TRUE(ServiceConfig::RegisterParser(
+                    UniquePtr<ServiceConfig::Parser>(New<ErrorParser>())) == 0);
+    EXPECT_TRUE(ServiceConfig::RegisterParser(
+                    UniquePtr<ServiceConfig::Parser>(New<ErrorParser>())) == 1);
+  }
+};
+
+TEST_F(ErroredParsersScopingTest, GlobalParams) {
+  const char* test_json = "{}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  std::regex e(std::string("(Service config parsing "
+                           "error)(.*)(referenced_errors\":\\[)(.*)(Global "
+                           "Params)(.*)(referenced_errors)()(.*)") +
+               ErrorParser::GlobalError() + std::string("(.*)") +
+               ErrorParser::GlobalError());
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ErroredParsersScopingTest, MethodParams) {
+  const char* test_json = "{\"methodConfig\": [{}]}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  std::regex e(
+      std::string("(Service config parsing "
+                  "error)(.*)(referenced_errors\":\\[)(.*)(Global "
+                  "Params)(.*)(referenced_errors)()(.*)") +
+      ErrorParser::GlobalError() + std::string("(.*)") +
+      ErrorParser::GlobalError() +
+      std::string("(.*)(Method "
+                  "Params)(.*)(referenced_errors)(.*)(field:methodConfig "
+                  "error:No names "
+                  "found)(.*)(methodConfig)(.*)(referenced_errors)(.*)") +
+      ErrorParser::MethodError() + std::string("(.*)") +
+      ErrorParser::MethodError() + std::string("(.*)(No names specified)"));
+  VerifyRegexMatch(error, e);
+}
+
+class ClientChannelParserTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ServiceConfig::Shutdown();
+    ServiceConfig::Init();
+    EXPECT_TRUE(
+        ServiceConfig::RegisterParser(UniquePtr<ServiceConfig::Parser>(
+            New<grpc_core::internal::ClientChannelServiceConfigParser>())) ==
+        0);
+  }
+};
+
+TEST_F(ClientChannelParserTest, ValidLoadBalancingConfigPickFirst) {
+  const char* test_json = "{\"loadBalancingConfig\": [{\"pick_first\":{}}]}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  ASSERT_TRUE(error == GRPC_ERROR_NONE);
+  const auto* parsed_object =
+      static_cast<grpc_core::internal::ClientChannelGlobalParsedObject*>(
+          svc_cfg->GetParsedGlobalServiceConfigObject(0));
+  auto lb_config = parsed_object->parsed_lb_config();
+  EXPECT_TRUE(strcmp(lb_config->name(), "pick_first") == 0);
+}
+
+TEST_F(ClientChannelParserTest, ValidLoadBalancingConfigRoundRobin) {
+  const char* test_json =
+      "{\"loadBalancingConfig\": [{\"round_robin\":{}}, {}]}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  ASSERT_TRUE(error == GRPC_ERROR_NONE);
+  auto parsed_object =
+      static_cast<grpc_core::internal::ClientChannelGlobalParsedObject*>(
+          svc_cfg->GetParsedGlobalServiceConfigObject(0));
+  auto lb_config = parsed_object->parsed_lb_config();
+  EXPECT_TRUE(strcmp(lb_config->name(), "round_robin") == 0);
+}
+
+TEST_F(ClientChannelParserTest, ValidLoadBalancingConfigGrpclb) {
+  const char* test_json =
+      "{\"loadBalancingConfig\": "
+      "[{\"grpclb\":{\"childPolicy\":[{\"pick_first\":{}}]}}]}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  ASSERT_TRUE(error == GRPC_ERROR_NONE);
+  const auto* parsed_object =
+      static_cast<grpc_core::internal::ClientChannelGlobalParsedObject*>(
+          svc_cfg->GetParsedGlobalServiceConfigObject(0));
+  auto lb_config = parsed_object->parsed_lb_config();
+  EXPECT_TRUE(strcmp(lb_config->name(), "grpclb") == 0);
+}
+
+TEST_F(ClientChannelParserTest, ValidLoadBalancingConfigXds) {
+  const char* test_json =
+      "{\n"
+      "  \"loadBalancingConfig\":[\n"
+      "    { \"does_not_exist\":{} },\n"
+      "    { \"xds_experimental\":{ \"balancerName\": \"fake:///lb\" } }\n"
+      "  ]\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error == GRPC_ERROR_NONE);
+  const auto* parsed_object =
+      static_cast<grpc_core::internal::ClientChannelGlobalParsedObject*>(
+          svc_cfg->GetParsedGlobalServiceConfigObject(0));
+  auto lb_config = parsed_object->parsed_lb_config();
+  EXPECT_TRUE(strcmp(lb_config->name(), "xds_experimental") == 0);
+}
+
+TEST_F(ClientChannelParserTest, UnknownLoadBalancingConfig) {
+  const char* test_json = "{\"loadBalancingConfig\": [{\"unknown\":{}}]}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(
+      std::string("(Service config parsing "
+                  "error)(.*)(referenced_errors)(.*)(Global "
+                  "Params)(.*)(referenced_errors)(.*)(Client channel global "
+                  "parser)(.*)(referenced_errors)(.*)(field:"
+                  "loadBalancingConfig error:No known policy)"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ClientChannelParserTest, InvalidGrpclbLoadBalancingConfig) {
+  const char* test_json =
+      "{\"loadBalancingConfig\": "
+      "[{\"grpclb\":{\"childPolicy\":[{\"unknown\":{}}]}}]}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(
+      std::string("(Service config parsing "
+                  "error)(.*)(referenced_errors)(.*)(Global "
+                  "Params)(.*)(referenced_errors)(.*)(Client channel global "
+                  "parser)(.*)(referenced_errors)(.*)(GrpcLb "
+                  "Parser)(.*)(referenced_errors)(.*)(field:childPolicy "
+                  "error:No known policy)"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ClientChannelParserTest, InalidLoadBalancingConfigXds) {
+  const char* test_json =
+      "{\n"
+      "  \"loadBalancingConfig\":[\n"
+      "    { \"does_not_exist\":{} },\n"
+      "    { \"xds_experimental\":{} }\n"
+      "  ]\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(
+      std::string("(Service config parsing "
+                  "error)(.*)(referenced_errors)(.*)(Global "
+                  "Params)(.*)(referenced_errors)(.*)(Client channel global "
+                  "parser)(.*)(referenced_errors)(.*)(Xds "
+                  "Parser)(.*)(referenced_errors)(.*)(field:balancerName "
+                  "error:not found)"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ClientChannelParserTest, ValidLoadBalancingPolicy) {
+  const char* test_json = "{\"loadBalancingPolicy\":\"pick_first\"}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  ASSERT_TRUE(error == GRPC_ERROR_NONE);
+  const auto* parsed_object =
+      static_cast<grpc_core::internal::ClientChannelGlobalParsedObject*>(
+          svc_cfg->GetParsedGlobalServiceConfigObject(0));
+  const auto* lb_policy = parsed_object->parsed_deprecated_lb_policy();
+  ASSERT_TRUE(lb_policy != nullptr);
+  EXPECT_TRUE(strcmp(lb_policy, "pick_first") == 0);
+}
+
+TEST_F(ClientChannelParserTest, ValidLoadBalancingPolicyAllCaps) {
+  const char* test_json = "{\"loadBalancingPolicy\":\"PICK_FIRST\"}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error == GRPC_ERROR_NONE);
+  const auto* parsed_object =
+      static_cast<grpc_core::internal::ClientChannelGlobalParsedObject*>(
+          svc_cfg->GetParsedGlobalServiceConfigObject(0));
+  const auto* lb_policy = parsed_object->parsed_deprecated_lb_policy();
+  ASSERT_TRUE(lb_policy != nullptr);
+  EXPECT_TRUE(strcmp(lb_policy, "pick_first") == 0);
+}
+
+TEST_F(ClientChannelParserTest, UnknownLoadBalancingPolicy) {
+  const char* test_json = "{\"loadBalancingPolicy\":\"unknown\"}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(
+      std::string("(Service config parsing "
+                  "error)(.*)(referenced_errors)(.*)(Global "
+                  "Params)(.*)(referenced_errors)(.*)(Client channel global "
+                  "parser)(.*)(referenced_errors)(.*)(field:"
+                  "loadBalancingPolicy error:Unknown lb policy)"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ClientChannelParserTest, LoadBalancingPolicyXdsNotAllowed) {
+  const char* test_json = "{\"loadBalancingPolicy\":\"xds_experimental\"}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(
+      std::string("(Service config parsing "
+                  "error)(.*)(referenced_errors)(.*)(Global "
+                  "Params)(.*)(referenced_errors)(.*)(Client channel global "
+                  "parser)(.*)(referenced_errors)(.*)(field:"
+                  "loadBalancingPolicy error:xds_experimental requires a "
+                  "config. Please use loadBalancingConfig instead.)"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ClientChannelParserTest, ValidRetryThrottling) {
+  const char* test_json =
+      "{\n"
+      "  \"retryThrottling\": {\n"
+      "    \"maxTokens\": 2,\n"
+      "    \"tokenRatio\": 1.0\n"
+      "  }\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error == GRPC_ERROR_NONE);
+  const auto* parsed_object =
+      static_cast<grpc_core::internal::ClientChannelGlobalParsedObject*>(
+          svc_cfg->GetParsedGlobalServiceConfigObject(0));
+  const auto retryThrottling = parsed_object->retry_throttling();
+  ASSERT_TRUE(retryThrottling.has_value());
+  EXPECT_EQ(retryThrottling.value().max_milli_tokens, 2000);
+  EXPECT_EQ(retryThrottling.value().milli_token_ratio, 1000);
+}
+
+TEST_F(ClientChannelParserTest, RetryThrottlingMissingFields) {
+  const char* test_json =
+      "{\n"
+      "  \"retryThrottling\": {\n"
+      "  }\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(
+      std::string("(Service config parsing "
+                  "error)(.*)(referenced_errors)(.*)(Global "
+                  "Params)(.*)(referenced_errors)(.*)(Client channel global "
+                  "parser)(.*)(referenced_errors)(.*)(field:retryThrottling "
+                  "field:maxTokens error:Not found)(.*)(field:retryThrottling "
+                  "field:tokenRatio error:Not found)"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ClientChannelParserTest, InvalidRetryThrottlingNegativeMaxTokens) {
+  const char* test_json =
+      "{\n"
+      "  \"retryThrottling\": {\n"
+      "    \"maxTokens\": -2,\n"
+      "    \"tokenRatio\": 1.0\n"
+      "  }\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(
+      std::string("(Service config parsing "
+                  "error)(.*)(referenced_errors)(.*)(Global "
+                  "Params)(.*)(referenced_errors)(.*)(Client channel global "
+                  "parser)(.*)(referenced_errors)(.*)(field:retryThrottling "
+                  "field:maxTokens error:should be greater than zero)"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ClientChannelParserTest, InvalidRetryThrottlingInvalidTokenRatio) {
+  const char* test_json =
+      "{\n"
+      "  \"retryThrottling\": {\n"
+      "    \"maxTokens\": 2,\n"
+      "    \"tokenRatio\": -1\n"
+      "  }\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(
+      std::string("(Service config parsing "
+                  "error)(.*)(referenced_errors)(.*)(Global "
+                  "Params)(.*)(referenced_errors)(.*)(Client channel global "
+                  "parser)(.*)(referenced_errors)(.*)(field:retryThrottling "
+                  "field:tokenRatio error:Failed parsing)"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ClientChannelParserTest, ValidTimeout) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"timeout\": \"5s\"\n"
+      "  } ]\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  ASSERT_TRUE(error == GRPC_ERROR_NONE);
+  const auto* vector_ptr = svc_cfg->GetMethodServiceConfigObjectsVector(
+      grpc_slice_from_static_string("/TestServ/TestMethod"));
+  EXPECT_TRUE(vector_ptr != nullptr);
+  auto parsed_object = ((*vector_ptr)[0]).get();
+  EXPECT_EQ((static_cast<grpc_core::internal::ClientChannelMethodParsedObject*>(
+                 parsed_object))
+                ->timeout(),
+            5000);
+}
+
+TEST_F(ClientChannelParserTest, InvalidTimeout) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"service\", \"method\": \"method\" }\n"
+      "    ],\n"
+      "    \"timeout\": \"5sec\"\n"
+      "  } ]\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(
+      std::string("(Service config parsing "
+                  "error)(.*)(referenced_errors)(.*)(Method "
+                  "Params)(.*)(referenced_errors)(.*)(methodConfig)(.*)("
+                  "referenced_errors)(.*)(Client channel "
+                  "parser)(.*)(referenced_errors)(.*)(field:timeout "
+                  "error:Failed parsing)"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ClientChannelParserTest, ValidWaitForReady) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"waitForReady\": true\n"
+      "  } ]\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  ASSERT_TRUE(error == GRPC_ERROR_NONE);
+  const auto* vector_ptr = svc_cfg->GetMethodServiceConfigObjectsVector(
+      grpc_slice_from_static_string("/TestServ/TestMethod"));
+  EXPECT_TRUE(vector_ptr != nullptr);
+  auto parsed_object = ((*vector_ptr)[0]).get();
+  EXPECT_TRUE(
+      (static_cast<grpc_core::internal::ClientChannelMethodParsedObject*>(
+           parsed_object))
+          ->wait_for_ready()
+          .has_value());
+  EXPECT_TRUE(
+      (static_cast<grpc_core::internal::ClientChannelMethodParsedObject*>(
+           parsed_object))
+          ->wait_for_ready()
+          .value());
+}
+
+TEST_F(ClientChannelParserTest, InvalidWaitForReady) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"service\", \"method\": \"method\" }\n"
+      "    ],\n"
+      "    \"waitForReady\": \"true\"\n"
+      "  } ]\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(
+      std::string("(Service config parsing "
+                  "error)(.*)(referenced_errors)(.*)(Method "
+                  "Params)(.*)(referenced_errors)(.*)(methodConfig)(.*)("
+                  "referenced_errors)(.*)(Client channel "
+                  "parser)(.*)(referenced_errors)(.*)(field:waitForReady "
+                  "error:Type should be true/false)"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ClientChannelParserTest, ValidRetryPolicy) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 3,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error == GRPC_ERROR_NONE);
+  const auto* vector_ptr = svc_cfg->GetMethodServiceConfigObjectsVector(
+      grpc_slice_from_static_string("/TestServ/TestMethod"));
+  EXPECT_TRUE(vector_ptr != nullptr);
+  const auto* parsed_object =
+      static_cast<grpc_core::internal::ClientChannelMethodParsedObject*>(
+          ((*vector_ptr)[0]).get());
+  EXPECT_TRUE(parsed_object->retry_policy() != nullptr);
+  EXPECT_EQ(parsed_object->retry_policy()->max_attempts, 3);
+  EXPECT_EQ(parsed_object->retry_policy()->initial_backoff, 1000);
+  EXPECT_EQ(parsed_object->retry_policy()->max_backoff, 120000);
+  EXPECT_EQ(parsed_object->retry_policy()->backoff_multiplier, 1.6f);
+  EXPECT_TRUE(parsed_object->retry_policy()->retryable_status_codes.Contains(
+      GRPC_STATUS_ABORTED));
+}
+
+TEST_F(ClientChannelParserTest, InvalidRetryPolicyMaxAttempts) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 1,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(std::string(
+      "(Service config parsing "
+      "error)(.*)(referenced_errors)(.*)(Method "
+      "Params)(.*)(referenced_errors)(.*)(methodConfig)(.*)(referenced_errors)("
+      ".*)(Client channel "
+      "parser)(.*)(referenced_errors)(.*)(retryPolicy)(.*)(referenced_errors)(."
+      "*)(field:maxAttempts error:should be at least 2)"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ClientChannelParserTest, InvalidRetryPolicyInitialBackoff) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 1,\n"
+      "      \"initialBackoff\": \"1sec\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(std::string(
+      "(Service config parsing "
+      "error)(.*)(referenced_errors)(.*)(Method "
+      "Params)(.*)(referenced_errors)(.*)(methodConfig)(.*)(referenced_errors)("
+      ".*)(Client channel "
+      "parser)(.*)(referenced_errors)(.*)(retryPolicy)(.*)(referenced_errors)(."
+      "*)(field:initialBackoff error:Failed to parse)"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ClientChannelParserTest, InvalidRetryPolicyMaxBackoff) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 1,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120sec\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(std::string(
+      "(Service config parsing "
+      "error)(.*)(referenced_errors)(.*)(Method "
+      "Params)(.*)(referenced_errors)(.*)(methodConfig)(.*)(referenced_errors)("
+      ".*)(Client channel "
+      "parser)(.*)(referenced_errors)(.*)(retryPolicy)(.*)(referenced_errors)(."
+      "*)(field:maxBackoff error:failed to parse)"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ClientChannelParserTest, InvalidRetryPolicyBackoffMultiplier) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 1,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": \"1.6\",\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(std::string(
+      "(Service config parsing "
+      "error)(.*)(referenced_errors)(.*)(Method "
+      "Params)(.*)(referenced_errors)(.*)(methodConfig)(.*)(referenced_errors)("
+      ".*)(Client channel "
+      "parser)(.*)(referenced_errors)(.*)(retryPolicy)(.*)(referenced_errors)(."
+      "*)(field:backoffMultiplier error:should be of type number)"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ClientChannelParserTest, InvalidRetryPolicyRetryableStatusCodes) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 1,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": \"1.6\",\n"
+      "      \"retryableStatusCodes\": []\n"
+      "    }\n"
+      "  } ]\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(std::string(
+      "(Service config parsing "
+      "error)(.*)(referenced_errors)(.*)(Method "
+      "Params)(.*)(referenced_errors)(.*)(methodConfig)(.*)(referenced_errors)("
+      ".*)(Client channel "
+      "parser)(.*)(referenced_errors)(.*)(retryPolicy)(.*)(referenced_errors)(."
+      "*)(field:retryableStatusCodes error:should be non-empty)"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(ClientChannelParserTest, ValidHealthCheck) {
+  const char* test_json =
+      "{\n"
+      "  \"healthCheckConfig\": {\n"
+      "    \"serviceName\": \"health_check_service_name\"\n"
+      "    }\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  ASSERT_TRUE(error == GRPC_ERROR_NONE);
+  const auto* parsed_object =
+      static_cast<grpc_core::internal::ClientChannelGlobalParsedObject*>(
+          svc_cfg->GetParsedGlobalServiceConfigObject(0));
+  ASSERT_TRUE(parsed_object != nullptr);
+  EXPECT_EQ(strcmp(parsed_object->health_check_service_name(),
+                   "health_check_service_name"),
+            0);
+}
+
+TEST_F(ClientChannelParserTest, InvalidHealthCheckMultipleEntries) {
+  const char* test_json =
+      "{\n"
+      "  \"healthCheckConfig\": {\n"
+      "    \"serviceName\": \"health_check_service_name\"\n"
+      "    },\n"
+      "  \"healthCheckConfig\": {\n"
+      "    \"serviceName\": \"health_check_service_name1\"\n"
+      "    }\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(
+      std::string("(Service config parsing "
+                  "error)(.*)(referenced_errors)(.*)(Global "
+                  "Params)(.*)(referenced_errors)(.*)(field:healthCheckConfig "
+                  "error:Duplicate entry)"));
+  std::smatch match;
+  std::string s(grpc_error_string(error));
+  EXPECT_TRUE(std::regex_search(s, match, e));
+  GRPC_ERROR_UNREF(error);
+}
+
+class MessageSizeParserTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ServiceConfig::Shutdown();
+    ServiceConfig::Init();
+    EXPECT_TRUE(ServiceConfig::RegisterParser(UniquePtr<ServiceConfig::Parser>(
+                    New<MessageSizeParser>())) == 0);
+  }
+};
+
+TEST_F(MessageSizeParserTest, Valid) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"maxRequestMessageBytes\": 1024,\n"
+      "    \"maxResponseMessageBytes\": 1024\n"
+      "  } ]\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error == GRPC_ERROR_NONE);
+  const auto* vector_ptr = svc_cfg->GetMethodServiceConfigObjectsVector(
+      grpc_slice_from_static_string("/TestServ/TestMethod"));
+  EXPECT_TRUE(vector_ptr != nullptr);
+  auto parsed_object =
+      static_cast<MessageSizeParsedObject*>(((*vector_ptr)[0]).get());
+  ASSERT_TRUE(parsed_object != nullptr);
+  EXPECT_EQ(parsed_object->limits().max_send_size, 1024);
+  EXPECT_EQ(parsed_object->limits().max_recv_size, 1024);
+}
+
+TEST_F(MessageSizeParserTest, InvalidMaxRequestMessageBytes) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"maxRequestMessageBytes\": -1024\n"
+      "  } ]\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(
+      std::string("(Service config parsing "
+                  "error)(.*)(referenced_errors)(.*)(Method "
+                  "Params)(.*)(referenced_errors)(.*)(methodConfig)(.*)("
+                  "referenced_errors)(.*)(Message size "
+                  "parser)(.*)(referenced_errors)(.*)(field:"
+                  "maxRequestMessageBytes error:should be non-negative)"));
+  VerifyRegexMatch(error, e);
+}
+
+TEST_F(MessageSizeParserTest, InvalidMaxResponseMessageBytes) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"maxResponseMessageBytes\": {}\n"
+      "  } ]\n"
+      "}";
+  grpc_error* error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(test_json, &error);
+  gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
+  ASSERT_TRUE(error != GRPC_ERROR_NONE);
+  std::regex e(
+      std::string("(Service config parsing "
+                  "error)(.*)(referenced_errors)(.*)(Method "
+                  "Params)(.*)(referenced_errors)(.*)(methodConfig)(.*)("
+                  "referenced_errors)(.*)(Message size "
+                  "parser)(.*)(referenced_errors)(.*)(field:"
+                  "maxResponseMessageBytes error:should be of type number)"));
+  VerifyRegexMatch(error, e);
+}
+
+}  // namespace testing
+}  // namespace grpc_core
+
+int main(int argc, char** argv) {
+  grpc::testing::TestEnvironment env(argc, argv);
+  grpc_init();
+  ::testing::InitGoogleTest(&argc, argv);
+  int ret = RUN_ALL_TESTS();
+  grpc_shutdown();
+  return ret;
+}
index 6522988..cf6d188 100644 (file)
 #include <grpc/grpc.h>
 #include <grpc/support/log.h>
 
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/compression/compression_args.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
 #include "test/core/util/test_config.h"
 
 static void test_compression_algorithm_parse(void) {
@@ -258,12 +261,88 @@ static void test_compression_enable_disable_algorithm(void) {
   }
 }
 
+static void test_channel_args_set_compression_algorithm(void) {
+  grpc_core::ExecCtx exec_ctx;
+  grpc_channel_args* ch_args;
+
+  ch_args =
+      grpc_channel_args_set_compression_algorithm(nullptr, GRPC_COMPRESS_GZIP);
+  GPR_ASSERT(ch_args->num_args == 1);
+  GPR_ASSERT(strcmp(ch_args->args[0].key,
+                    GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM) == 0);
+  GPR_ASSERT(ch_args->args[0].type == GRPC_ARG_INTEGER);
+
+  grpc_channel_args_destroy(ch_args);
+}
+
+static void test_channel_args_compression_algorithm_states(void) {
+  grpc_core::ExecCtx exec_ctx;
+  grpc_channel_args *ch_args, *ch_args_wo_gzip, *ch_args_wo_gzip_deflate,
+      *ch_args_wo_gzip_deflate_gzip;
+  unsigned states_bitset;
+  size_t i;
+
+  ch_args = grpc_channel_args_copy_and_add(nullptr, nullptr, 0);
+  /* by default, all enabled */
+  states_bitset = static_cast<unsigned>(
+      grpc_channel_args_compression_algorithm_get_states(ch_args));
+
+  for (i = 0; i < GRPC_COMPRESS_ALGORITHMS_COUNT; i++) {
+    GPR_ASSERT(GPR_BITGET(states_bitset, i));
+  }
+
+  /* disable gzip and deflate and stream/gzip */
+  ch_args_wo_gzip = grpc_channel_args_compression_algorithm_set_state(
+      &ch_args, GRPC_COMPRESS_GZIP, 0);
+  GPR_ASSERT(ch_args == ch_args_wo_gzip);
+  ch_args_wo_gzip_deflate = grpc_channel_args_compression_algorithm_set_state(
+      &ch_args_wo_gzip, GRPC_COMPRESS_DEFLATE, 0);
+  GPR_ASSERT(ch_args_wo_gzip == ch_args_wo_gzip_deflate);
+  ch_args_wo_gzip_deflate_gzip =
+      grpc_channel_args_compression_algorithm_set_state(
+          &ch_args_wo_gzip_deflate, GRPC_COMPRESS_STREAM_GZIP, 0);
+  GPR_ASSERT(ch_args_wo_gzip_deflate == ch_args_wo_gzip_deflate_gzip);
+
+  states_bitset =
+      static_cast<unsigned>(grpc_channel_args_compression_algorithm_get_states(
+          ch_args_wo_gzip_deflate));
+  for (i = 0; i < GRPC_COMPRESS_ALGORITHMS_COUNT; i++) {
+    if (i == GRPC_COMPRESS_GZIP || i == GRPC_COMPRESS_DEFLATE ||
+        i == GRPC_COMPRESS_STREAM_GZIP) {
+      GPR_ASSERT(GPR_BITGET(states_bitset, i) == 0);
+    } else {
+      GPR_ASSERT(GPR_BITGET(states_bitset, i) != 0);
+    }
+  }
+
+  /* re-enabled gzip and stream/gzip only */
+  ch_args_wo_gzip = grpc_channel_args_compression_algorithm_set_state(
+      &ch_args_wo_gzip_deflate_gzip, GRPC_COMPRESS_GZIP, 1);
+  ch_args_wo_gzip = grpc_channel_args_compression_algorithm_set_state(
+      &ch_args_wo_gzip, GRPC_COMPRESS_STREAM_GZIP, 1);
+  GPR_ASSERT(ch_args_wo_gzip == ch_args_wo_gzip_deflate_gzip);
+
+  states_bitset = static_cast<unsigned>(
+      grpc_channel_args_compression_algorithm_get_states(ch_args_wo_gzip));
+  for (i = 0; i < GRPC_COMPRESS_ALGORITHMS_COUNT; i++) {
+    if (i == GRPC_COMPRESS_DEFLATE) {
+      GPR_ASSERT(GPR_BITGET(states_bitset, i) == 0);
+    } else {
+      GPR_ASSERT(GPR_BITGET(states_bitset, i) != 0);
+    }
+  }
+
+  grpc_channel_args_destroy(ch_args);
+}
+
 int main(int argc, char** argv) {
   grpc_init();
   test_compression_algorithm_parse();
   test_compression_algorithm_name();
   test_compression_algorithm_for_level();
   test_compression_enable_disable_algorithm();
+  test_channel_args_set_compression_algorithm();
+  test_channel_args_compression_algorithm_states();
   grpc_shutdown();
 
   return 0;
index 3701a93..2d74b6b 100644 (file)
@@ -250,7 +250,7 @@ static void actually_poll_server(void* arg) {
     if (done || gpr_time_cmp(time_left, gpr_time_0(GPR_TIMESPAN)) < 0) {
       break;
     }
-    test_tcp_server_poll(pa->server, 1);
+    test_tcp_server_poll(pa->server, 1000);
   }
   gpr_event_set(pa->signal_when_done, (void*)1);
   gpr_free(pa);
index 04142da..f97192f 100644 (file)
@@ -29,6 +29,7 @@
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/connected_channel.h"
+#include "src/core/lib/compression/compression_args.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/server.h"
index ce8f6bf..b8dbe26 100644 (file)
@@ -33,7 +33,7 @@
 #include "src/core/ext/filters/http/server/http_server_filter.h"
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/channel/connected_channel.h"
-#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/debug/trace.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/server.h"
@@ -105,7 +105,7 @@ int main(int argc, char** argv) {
 
   /* force tracing on, with a value to force many
      code paths in trace.c to be taken */
-  gpr_setenv("GRPC_TRACE", "doesnt-exist,http,all");
+  GPR_GLOBAL_CONFIG_SET(grpc_trace, "doesnt-exist,http,all");
 
 #ifdef GRPC_POSIX_SOCKET
   g_fixture_slowdown_factor = isatty(STDOUT_FILENO) ? 10 : 1;
index 4494d5c..7954bc1 100644 (file)
@@ -35,7 +35,7 @@
 #include "src/core/ext/filters/http/server/http_server_filter.h"
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/channel/connected_channel.h"
-#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/debug/trace.h"
 #include "src/core/lib/iomgr/endpoint_pair.h"
 #include "src/core/lib/iomgr/iomgr.h"
 #include "src/core/lib/surface/channel.h"
@@ -133,7 +133,8 @@ int main(int argc, char** argv) {
 
   /* force tracing on, with a value to force many
      code paths in trace.c to be taken */
-  gpr_setenv("GRPC_TRACE", "doesnt-exist,http,all");
+  GPR_GLOBAL_CONFIG_SET(grpc_trace, "doesnt-exist,http,all");
+
 #ifdef GRPC_POSIX_SOCKET
   g_fixture_slowdown_factor = isatty(STDOUT_FILENO) ? 10 : 1;
 #else
index 9ab796e..cdf091b 100644 (file)
@@ -35,6 +35,7 @@
 #include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/security/credentials/credentials.h"
 #include "src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h"
+#include "src/core/lib/security/security_connector/ssl_utils.h"
 #include "test/core/end2end/data/ssl_test_data.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
@@ -277,7 +278,7 @@ int main(int argc, char** argv) {
   GPR_ASSERT(roots_file != nullptr);
   GPR_ASSERT(fwrite(test_root_cert, 1, roots_size, roots_file) == roots_size);
   fclose(roots_file);
-  gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_filename);
+  GPR_GLOBAL_CONFIG_SET(grpc_default_ssl_roots_file_path, roots_filename);
   grpc_init();
   for (size_t ind = 0; ind < sizeof(configs) / sizeof(*configs); ind++) {
     grpc_end2end_tests(argc, argv, configs[ind]);
index 1fcd785..3fc9bc7 100644 (file)
 #include <grpc/support/log.h>
 
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gpr/tmpfile.h"
 #include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/security_connector/ssl_utils.h"
 #include "test/core/end2end/data/ssl_test_data.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
@@ -167,7 +167,7 @@ int main(int argc, char** argv) {
   GPR_ASSERT(roots_file != nullptr);
   GPR_ASSERT(fwrite(test_root_cert, 1, roots_size, roots_file) == roots_size);
   fclose(roots_file);
-  gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_filename);
+  GPR_GLOBAL_CONFIG_SET(grpc_default_ssl_roots_file_path, roots_filename);
 
   grpc_init();
 
diff --git a/test/core/end2end/fixtures/h2_ssl_cred_reload.cc b/test/core/end2end/fixtures/h2_ssl_cred_reload.cc
new file mode 100644 (file)
index 0000000..1d54a43
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/tmpfile.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/security_connector/ssl_utils.h"
+#include "test/core/end2end/data/ssl_test_data.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+typedef struct fullstack_secure_fixture_data {
+  char* localaddr;
+  bool server_credential_reloaded;
+} fullstack_secure_fixture_data;
+
+static grpc_ssl_certificate_config_reload_status
+ssl_server_certificate_config_callback(
+    void* user_data, grpc_ssl_server_certificate_config** config) {
+  if (config == nullptr) {
+    return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL;
+  }
+  fullstack_secure_fixture_data* ffd =
+      static_cast<fullstack_secure_fixture_data*>(user_data);
+  if (!ffd->server_credential_reloaded) {
+    grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {test_server1_key,
+                                                    test_server1_cert};
+    *config = grpc_ssl_server_certificate_config_create(test_root_cert,
+                                                        &pem_key_cert_pair, 1);
+    ffd->server_credential_reloaded = true;
+    return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW;
+  } else {
+    return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED;
+  }
+}
+
+static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack(
+    grpc_channel_args* client_args, grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  int port = grpc_pick_unused_port_or_die();
+  fullstack_secure_fixture_data* ffd =
+      static_cast<fullstack_secure_fixture_data*>(
+          gpr_malloc(sizeof(fullstack_secure_fixture_data)));
+  memset(&f, 0, sizeof(f));
+  gpr_join_host_port(&ffd->localaddr, "localhost", port);
+
+  f.fixture_data = ffd;
+  f.cq = grpc_completion_queue_create_for_next(nullptr);
+  f.shutdown_cq = grpc_completion_queue_create_for_pluck(nullptr);
+
+  return f;
+}
+
+static void process_auth_failure(void* state, grpc_auth_context* ctx,
+                                 const grpc_metadata* md, size_t md_count,
+                                 grpc_process_auth_metadata_done_cb cb,
+                                 void* user_data) {
+  GPR_ASSERT(state == nullptr);
+  cb(user_data, nullptr, 0, nullptr, 0, GRPC_STATUS_UNAUTHENTICATED, nullptr);
+}
+
+static void chttp2_init_client_secure_fullstack(
+    grpc_end2end_test_fixture* f, grpc_channel_args* client_args,
+    grpc_channel_credentials* creds) {
+  fullstack_secure_fixture_data* ffd =
+      static_cast<fullstack_secure_fixture_data*>(f->fixture_data);
+  f->client =
+      grpc_secure_channel_create(creds, ffd->localaddr, client_args, nullptr);
+  GPR_ASSERT(f->client != nullptr);
+  grpc_channel_credentials_release(creds);
+}
+
+static void chttp2_init_server_secure_fullstack(
+    grpc_end2end_test_fixture* f, grpc_channel_args* server_args,
+    grpc_server_credentials* server_creds) {
+  fullstack_secure_fixture_data* ffd =
+      static_cast<fullstack_secure_fixture_data*>(f->fixture_data);
+  if (f->server) {
+    grpc_server_destroy(f->server);
+  }
+  ffd->server_credential_reloaded = false;
+  f->server = grpc_server_create(server_args, nullptr);
+  grpc_server_register_completion_queue(f->server, f->cq, nullptr);
+  GPR_ASSERT(grpc_server_add_secure_http2_port(f->server, ffd->localaddr,
+                                               server_creds));
+  grpc_server_credentials_release(server_creds);
+  grpc_server_start(f->server);
+}
+
+void chttp2_tear_down_secure_fullstack(grpc_end2end_test_fixture* f) {
+  fullstack_secure_fixture_data* ffd =
+      static_cast<fullstack_secure_fixture_data*>(f->fixture_data);
+  gpr_free(ffd->localaddr);
+  gpr_free(ffd);
+}
+
+static void chttp2_init_client_simple_ssl_secure_fullstack(
+    grpc_end2end_test_fixture* f, grpc_channel_args* client_args) {
+  grpc_channel_credentials* ssl_creds =
+      grpc_ssl_credentials_create(nullptr, nullptr, nullptr, nullptr);
+  grpc_arg ssl_name_override = {
+      GRPC_ARG_STRING,
+      const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG),
+      {const_cast<char*>("foo.test.google.fr")}};
+  grpc_channel_args* new_client_args =
+      grpc_channel_args_copy_and_add(client_args, &ssl_name_override, 1);
+  chttp2_init_client_secure_fullstack(f, new_client_args, ssl_creds);
+  grpc_channel_args_destroy(new_client_args);
+}
+
+static int fail_server_auth_check(grpc_channel_args* server_args) {
+  size_t i;
+  if (server_args == nullptr) return 0;
+  for (i = 0; i < server_args->num_args; i++) {
+    if (strcmp(server_args->args[i].key, FAIL_AUTH_CHECK_SERVER_ARG_NAME) ==
+        0) {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+static void chttp2_init_server_simple_ssl_secure_fullstack(
+    grpc_end2end_test_fixture* f, grpc_channel_args* server_args) {
+  grpc_ssl_server_credentials_options* options =
+      grpc_ssl_server_credentials_create_options_using_config_fetcher(
+          GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
+          ssl_server_certificate_config_callback, f->fixture_data);
+  grpc_server_credentials* ssl_creds =
+      grpc_ssl_server_credentials_create_with_options(options);
+  if (fail_server_auth_check(server_args)) {
+    grpc_auth_metadata_processor processor = {process_auth_failure, nullptr,
+                                              nullptr};
+    grpc_server_credentials_set_auth_metadata_processor(ssl_creds, processor);
+  }
+  chttp2_init_server_secure_fullstack(f, server_args, ssl_creds);
+}
+
+/* All test configurations */
+
+static grpc_end2end_test_config configs[] = {
+    {"chttp2/simple_ssl_fullstack",
+     FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION |
+         FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS |
+         FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL |
+         FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER,
+     "foo.test.google.fr", chttp2_create_fixture_secure_fullstack,
+     chttp2_init_client_simple_ssl_secure_fullstack,
+     chttp2_init_server_simple_ssl_secure_fullstack,
+     chttp2_tear_down_secure_fullstack},
+};
+
+int main(int argc, char** argv) {
+  size_t i;
+  FILE* roots_file;
+  size_t roots_size = strlen(test_root_cert);
+  char* roots_filename;
+
+  grpc::testing::TestEnvironment env(argc, argv);
+  grpc_end2end_tests_pre_init();
+
+  /* Set the SSL roots env var. */
+  roots_file = gpr_tmpfile("chttp2_simple_ssl_fullstack_test", &roots_filename);
+  GPR_ASSERT(roots_filename != nullptr);
+  GPR_ASSERT(roots_file != nullptr);
+  GPR_ASSERT(fwrite(test_root_cert, 1, roots_size, roots_file) == roots_size);
+  fclose(roots_file);
+  GPR_GLOBAL_CONFIG_SET(grpc_default_ssl_roots_file_path, roots_filename);
+
+  grpc_init();
+
+  for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) {
+    grpc_end2end_tests(argc, argv, configs[i]);
+  }
+
+  grpc_shutdown();
+
+  /* Cleanup. */
+  remove(roots_filename);
+  gpr_free(roots_filename);
+
+  return 0;
+}
index f185807..d5f695b 100644 (file)
 #include <grpc/support/log.h>
 
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gpr/tmpfile.h"
 #include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/security_connector/ssl_utils.h"
 #include "test/core/end2end/data/ssl_test_data.h"
 #include "test/core/end2end/fixtures/proxy.h"
 #include "test/core/util/port.h"
@@ -208,7 +208,7 @@ int main(int argc, char** argv) {
   GPR_ASSERT(roots_file != nullptr);
   GPR_ASSERT(fwrite(test_root_cert, 1, roots_size, roots_file) == roots_size);
   fclose(roots_file);
-  gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_filename);
+  GPR_GLOBAL_CONFIG_SET(grpc_default_ssl_roots_file_path, roots_filename);
 
   grpc_init();
 
index 231d226..7bb802a 100755 (executable)
@@ -74,6 +74,7 @@ END2END_FIXTURES = {
     'h2_sockpair+trace': socketpair_unsecure_fixture_options._replace(
         ci_mac=False, tracing=True, large_writes=False, exclude_iomgrs=['uv']),
     'h2_ssl': default_secure_fixture_options,
+    'h2_ssl_cred_reload': default_secure_fixture_options,
     'h2_spiffe': default_secure_fixture_options,
     'h2_local_uds': local_fixture_options,
     'h2_local_ipv4': local_fixture_options,
index b3c58bf..85faca1 100755 (executable)
@@ -87,6 +87,7 @@ END2END_FIXTURES = {
         client_channel = False,
     ),
     "h2_ssl": _fixture_options(secure = True),
+    "h2_ssl_cred_reload": _fixture_options(secure = True),
     "h2_spiffe": _fixture_options(secure = True),
     "h2_local_uds": _fixture_options(secure = True, dns_resolver = False, _platforms = ["linux", "mac", "posix"]),
     "h2_local_ipv4": _fixture_options(secure = True, dns_resolver = False, _platforms = ["linux", "mac", "posix"]),
@@ -150,6 +151,7 @@ END2END_NOSEC_FIXTURES = {
         client_channel = False,
     ),
     "h2_ssl": _fixture_options(secure = False),
+    "h2_ssl_cred_reload": _fixture_options(secure = False),
     "h2_ssl_proxy": _fixture_options(includes_proxy = True, secure = False),
     "h2_uds": _fixture_options(
         dns_resolver = False,
@@ -385,6 +387,7 @@ def grpc_end2end_tests():
             ":proxy",
             ":local_util",
         ],
+        tags = ["no_windows"],
     )
 
     for f, fopt in END2END_FIXTURES.items():
@@ -398,6 +401,7 @@ def grpc_end2end_tests():
                 "//:grpc",
                 "//:gpr",
             ],
+            tags = ["no_windows"],
         )
         for t, topt in END2END_TESTS.items():
             #print(_compatible(fopt, topt), f, t, fopt, topt)
@@ -413,6 +417,7 @@ def grpc_end2end_tests():
                         t,
                         poller,
                     ],
+                    tags = ["no_windows"],
                 )
 
 def grpc_end2end_nosec_tests():
@@ -435,6 +440,7 @@ def grpc_end2end_nosec_tests():
             ":proxy",
             ":local_util",
         ],
+        tags = ["no_windows"],
     )
 
     for f, fopt in END2END_NOSEC_FIXTURES.items():
@@ -450,6 +456,7 @@ def grpc_end2end_nosec_tests():
                 "//:grpc_unsecure",
                 "//:gpr",
             ],
+            tags = ["no_windows"],
         )
         for t, topt in END2END_TESTS.items():
             #print(_compatible(fopt, topt), f, t, fopt, topt)
index cb0800b..e928577 100644 (file)
 #include <grpc/support/log.h>
 
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gpr/tmpfile.h"
 #include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/security_connector/ssl_utils.h"
 #include "test/core/end2end/cq_verifier.h"
 #include "test/core/end2end/data/ssl_test_data.h"
 #include "test/core/util/port.h"
@@ -366,7 +366,7 @@ int main(int argc, char** argv) {
   GPR_ASSERT(roots_file != nullptr);
   GPR_ASSERT(fwrite(test_root_cert, 1, roots_size, roots_file) == roots_size);
   fclose(roots_file);
-  gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_filename);
+  GPR_GLOBAL_CONFIG_SET(grpc_default_ssl_roots_file_path, roots_filename);
 
   grpc_init();
   ::testing::InitGoogleTest(&argc, argv);
index fbcdcc4..b2d0a5e 100644 (file)
 #include <grpc/support/log.h>
 
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gpr/tmpfile.h"
 #include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/security_connector/ssl_utils.h"
 #include "test/core/end2end/cq_verifier.h"
 #include "test/core/end2end/data/ssl_test_data.h"
 #include "test/core/util/port.h"
@@ -265,7 +265,7 @@ int main(int argc, char** argv) {
   GPR_ASSERT(roots_file != nullptr);
   GPR_ASSERT(fwrite(test_root_cert, 1, roots_size, roots_file) == roots_size);
   fclose(roots_file);
-  gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_filename);
+  GPR_GLOBAL_CONFIG_SET(grpc_default_ssl_roots_file_path, roots_filename);
 
   grpc_init();
   ::testing::InitGoogleTest(&argc, argv);
index 178d68c..2b9ab5d 100644 (file)
@@ -30,6 +30,7 @@
 #include <grpc/support/time.h>
 
 #include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/compression/compression_args.h"
 #include "src/core/lib/surface/call.h"
 #include "src/core/lib/surface/call_test_only.h"
 #include "src/core/lib/transport/static_metadata.h"
index 3c33f04..1750f6f 100644 (file)
 
 #include "src/core/ext/transport/chttp2/transport/frame_ping.h"
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/useful.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/iomgr.h"
 #include "test/core/end2end/cq_verifier.h"
 
+#ifdef GRPC_POSIX_SOCKET
+#include "src/core/lib/iomgr/ev_posix.h"
+#endif  // GRPC_POSIX_SOCKET
+
 static void* tag(intptr_t t) { return (void*)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -225,13 +229,13 @@ static void test_keepalive_timeout(grpc_end2end_test_config config) {
  * 200ms. In the success case, each ping ack should reset the keepalive timer so
  * that the keepalive ping is never sent. */
 static void test_read_delays_keepalive(grpc_end2end_test_config config) {
-  char* poller = gpr_getenv("GRPC_POLL_STRATEGY");
+#ifdef GRPC_POSIX_SOCKET
+  grpc_core::UniquePtr<char> poller = GPR_GLOBAL_CONFIG_GET(grpc_poll_strategy);
   /* It is hard to get the timing right for the polling engine poll. */
-  if (poller != nullptr && (0 == strcmp(poller, "poll"))) {
-    gpr_free(poller);
+  if ((0 == strcmp(poller.get(), "poll"))) {
     return;
   }
-  gpr_free(poller);
+#endif  // GRPC_POSIX_SOCKET
   const int kPingIntervalMS = 100;
   grpc_arg keepalive_arg_elems[3];
   keepalive_arg_elems[0].type = GRPC_ARG_INTEGER;
index f5b28de..0e286c3 100644 (file)
@@ -141,7 +141,7 @@ static void test_retry_throttled(grpc_end2end_test_config config) {
       // purposes of this test.)
       "  \"retryThrottling\": {\n"
       "    \"maxTokens\": 2,\n"
-      "    \"tokenRatio\": 1.0,\n"
+      "    \"tokenRatio\": 1.0\n"
       "  }\n"
       "}");
   grpc_channel_args client_args = {1, &arg};
index 9c01896..a8c5789 100644 (file)
@@ -86,6 +86,14 @@ static void end_test(grpc_end2end_test_fixture* f) {
   grpc_completion_queue_destroy(f->shutdown_cq);
 }
 
+static void check_peer(char* peer_name) {
+  // If the peer name is a uds path, then check if it is filled
+  if (strncmp(peer_name, "unix:/", strlen("unix:/")) == 0) {
+    GPR_ASSERT(strncmp(peer_name, "unix:/tmp/grpc_fullstack_test.",
+                       strlen("unix:/tmp/grpc_fullstack_test.")) == 0);
+  }
+}
+
 static void simple_request_body(grpc_end2end_test_config config,
                                 grpc_end2end_test_fixture f) {
   grpc_call* c;
@@ -166,10 +174,12 @@ static void simple_request_body(grpc_end2end_test_config config,
   peer = grpc_call_get_peer(s);
   GPR_ASSERT(peer != nullptr);
   gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  check_peer(peer);
   gpr_free(peer);
   peer = grpc_call_get_peer(c);
   GPR_ASSERT(peer != nullptr);
   gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  check_peer(peer);
   gpr_free(peer);
 
   memset(ops, 0, sizeof(ops));
index 839f091..39f95b8 100644 (file)
@@ -30,6 +30,7 @@
 #include <grpc/support/time.h>
 
 #include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/compression/compression_args.h"
 #include "src/core/lib/surface/call.h"
 #include "src/core/lib/surface/call_test_only.h"
 #include "src/core/lib/transport/static_metadata.h"
index 4c08150..5f6b9a7 100644 (file)
@@ -27,6 +27,7 @@
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
 #include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/compression/compression_args.h"
 #include "src/core/lib/surface/call.h"
 #include "test/core/end2end/cq_verifier.h"
 
index f7af59f..6e96f7d 100644 (file)
@@ -28,6 +28,7 @@
 #include <grpc/support/time.h>
 
 #include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/compression/compression_args.h"
 #include "src/core/lib/surface/call.h"
 #include "test/core/end2end/cq_verifier.h"
 
index f44ddca..d79b2a9 100644 (file)
@@ -30,6 +30,7 @@
 #include <grpc/support/time.h>
 
 #include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/compression/compression_args.h"
 #include "src/core/lib/surface/call.h"
 #include "src/core/lib/surface/call_test_only.h"
 #include "src/core/lib/transport/static_metadata.h"
index de4bd98..1df1052 100644 (file)
@@ -16,7 +16,7 @@
  *
  */
 
-#include "src/core/lib/gpr/arena.h"
+#include "src/core/lib/gprpp/arena.h"
 
 #include <inttypes.h>
 #include <string.h>
@@ -31,7 +31,9 @@
 #include "src/core/lib/gprpp/thd.h"
 #include "test/core/util/test_config.h"
 
-static void test_noop(void) { gpr_arena_destroy(gpr_arena_create(1)); }
+using grpc_core::Arena;
+
+static void test_noop(void) { Arena::Create(1)->Destroy(); }
 
 static void test(const char* name, size_t init_size, const size_t* allocs,
                  size_t nallocs) {
@@ -50,10 +52,10 @@ static void test(const char* name, size_t init_size, const size_t* allocs,
   gpr_log(GPR_INFO, "%s", s);
   gpr_free(s);
 
-  gpr_arena* a = gpr_arena_create(init_size);
+  Arena* a = Arena::Create(init_size);
   void** ps = static_cast<void**>(gpr_zalloc(sizeof(*ps) * nallocs));
   for (size_t i = 0; i < nallocs; i++) {
-    ps[i] = gpr_arena_alloc(a, allocs[i]);
+    ps[i] = a->Alloc(allocs[i]);
     // ensure the returned address is aligned
     GPR_ASSERT(((intptr_t)ps[i] & 0xf) == 0);
     // ensure no duplicate results
@@ -63,7 +65,7 @@ static void test(const char* name, size_t init_size, const size_t* allocs,
     // ensure writable
     memset(ps[i], 1, allocs[i]);
   }
-  gpr_arena_destroy(a);
+  a->Destroy();
   gpr_free(ps);
 }
 
@@ -80,14 +82,14 @@ size_t concurrent_test_iterations() {
 
 typedef struct {
   gpr_event ev_start;
-  gpr_arena* arena;
+  Arena* arena;
 } concurrent_test_args;
 
 static void concurrent_test_body(void* arg) {
   concurrent_test_args* a = static_cast<concurrent_test_args*>(arg);
   gpr_event_wait(&a->ev_start, gpr_inf_future(GPR_CLOCK_REALTIME));
   for (size_t i = 0; i < concurrent_test_iterations(); i++) {
-    *static_cast<char*>(gpr_arena_alloc(a->arena, 1)) = static_cast<char>(i);
+    *static_cast<char*>(a->arena->Alloc(1)) = static_cast<char>(i);
   }
 }
 
@@ -96,7 +98,7 @@ static void concurrent_test(void) {
 
   concurrent_test_args args;
   gpr_event_init(&args.ev_start);
-  args.arena = gpr_arena_create(1024);
+  args.arena = Arena::Create(1024);
 
   grpc_core::Thread thds[CONCURRENT_TEST_THREADS];
 
@@ -112,7 +114,7 @@ static void concurrent_test(void) {
     th.Join();
   }
 
-  gpr_arena_destroy(args.arena);
+  args.arena->Destroy();
 }
 
 int main(int argc, char* argv[]) {
index a8206bd..3883a5d 100644 (file)
@@ -42,8 +42,22 @@ static void test_setenv_getenv(void) {
   gpr_free(retrieved_value);
 }
 
+static void test_unsetenv(void) {
+  const char* name = "FOO";
+  const char* value = "BAR";
+  char* retrieved_value;
+
+  LOG_TEST_NAME("test_unsetenv");
+
+  gpr_setenv(name, value);
+  gpr_unsetenv(name);
+  retrieved_value = gpr_getenv(name);
+  GPR_ASSERT(retrieved_value == nullptr);
+}
+
 int main(int argc, char** argv) {
   grpc::testing::TestEnvironment env(argc, argv);
   test_setenv_getenv();
+  test_unsetenv();
   return 0;
 }
index f962577..e320daa 100644 (file)
 #include <stdbool.h>
 #include <string.h>
 
-#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gprpp/global_config.h"
 #include "test/core/util/test_config.h"
 
+// Config declaration is supposed to be located at log.h but
+// log.h doesn't include global_config headers because it has to
+// be a strict C so declaration statement gets to be here.
+GPR_GLOBAL_CONFIG_DECLARE_STRING(grpc_verbosity);
+
 static bool log_func_reached = false;
 
 static void test_callback(gpr_log_func_args* args) {
@@ -67,7 +72,7 @@ int main(int argc, char** argv) {
 
   /* gpr_log_verbosity_init() will be effective only once, and only before
    * gpr_set_log_verbosity() is called */
-  gpr_setenv("GRPC_VERBOSITY", "ERROR");
+  GPR_GLOBAL_CONFIG_SET(grpc_verbosity, "ERROR");
   gpr_log_verbosity_init();
 
   test_log_function_reached(GPR_ERROR);
@@ -75,7 +80,7 @@ int main(int argc, char** argv) {
   test_log_function_unreached(GPR_DEBUG);
 
   /* gpr_log_verbosity_init() should not be effective */
-  gpr_setenv("GRPC_VERBOSITY", "DEBUG");
+  GPR_GLOBAL_CONFIG_SET(grpc_verbosity, "DEBUG");
   gpr_log_verbosity_init();
   test_log_function_reached(GPR_ERROR);
   test_log_function_unreached(GPR_INFO);
@@ -97,7 +102,7 @@ int main(int argc, char** argv) {
   test_log_function_unreached(GPR_DEBUG);
 
   /* gpr_log_verbosity_init() should not be effective */
-  gpr_setenv("GRPC_VERBOSITY", "DEBUG");
+  GPR_GLOBAL_CONFIG_SET(grpc_verbosity, "DEBUG");
   gpr_log_verbosity_init();
   test_log_function_reached(GPR_ERROR);
   test_log_function_unreached(GPR_INFO);
index 7da7b18..5e3ed9d 100644 (file)
@@ -279,19 +279,20 @@ static void test_memrchr(void) {
   GPR_ASSERT(0 == strcmp((const char*)gpr_memrchr("hello", 'l', 5), "lo"));
 }
 
-static void test_is_true(void) {
-  LOG_TEST_NAME("test_is_true");
-
-  GPR_ASSERT(true == gpr_is_true("True"));
-  GPR_ASSERT(true == gpr_is_true("true"));
-  GPR_ASSERT(true == gpr_is_true("TRUE"));
-  GPR_ASSERT(true == gpr_is_true("Yes"));
-  GPR_ASSERT(true == gpr_is_true("yes"));
-  GPR_ASSERT(true == gpr_is_true("YES"));
-  GPR_ASSERT(true == gpr_is_true("1"));
-  GPR_ASSERT(false == gpr_is_true(nullptr));
-  GPR_ASSERT(false == gpr_is_true(""));
-  GPR_ASSERT(false == gpr_is_true("0"));
+static void test_parse_bool_value(void) {
+  LOG_TEST_NAME("test_parse_bool_value");
+
+  bool ret;
+  GPR_ASSERT(true == gpr_parse_bool_value("truE", &ret) && true == ret);
+  GPR_ASSERT(true == gpr_parse_bool_value("falsE", &ret) && false == ret);
+  GPR_ASSERT(true == gpr_parse_bool_value("1", &ret) && true == ret);
+  GPR_ASSERT(true == gpr_parse_bool_value("0", &ret) && false == ret);
+  GPR_ASSERT(true == gpr_parse_bool_value("Yes", &ret) && true == ret);
+  GPR_ASSERT(true == gpr_parse_bool_value("No", &ret) && false == ret);
+  GPR_ASSERT(true == gpr_parse_bool_value("Y", &ret) && true == ret);
+  GPR_ASSERT(true == gpr_parse_bool_value("N", &ret) && false == ret);
+  GPR_ASSERT(false == gpr_parse_bool_value(nullptr, &ret));
+  GPR_ASSERT(false == gpr_parse_bool_value("", &ret));
 }
 
 int main(int argc, char** argv) {
@@ -307,6 +308,6 @@ int main(int argc, char** argv) {
   test_leftpad();
   test_stricmp();
   test_memrchr();
-  test_is_true();
+  test_parse_bool_value();
   return 0;
 }
index c8d47be..cd3232a 100644 (file)
@@ -29,6 +29,32 @@ grpc_cc_test(
 )
 
 grpc_cc_test(
+    name = "global_config_test",
+    srcs = ["global_config_test.cc"],
+    external_deps = [
+        "gtest",
+    ],
+    language = "C++",
+    deps = [
+        "//:gpr",
+        "//test/core/util:grpc_test_util",
+    ],
+)
+
+grpc_cc_test(
+    name = "global_config_env_test",
+    srcs = ["global_config_env_test.cc"],
+    external_deps = [
+        "gtest",
+    ],
+    language = "C++",
+    deps = [
+        "//:gpr",
+        "//test/core/util:grpc_test_util",
+    ],
+)
+
+grpc_cc_test(
     name = "manual_constructor_test",
     srcs = ["manual_constructor_test.cc"],
     language = "C++",
@@ -39,6 +65,19 @@ grpc_cc_test(
 )
 
 grpc_cc_test(
+    name = "grpc_core_map_test",
+    srcs = ["map_test.cc"],
+    external_deps = [
+        "gtest",
+    ],
+    language = "C++",
+    deps = [
+        "//:gpr_base",
+        "//test/core/util:grpc_test_util",
+    ],
+)
+
+grpc_cc_test(
     name = "memory_test",
     srcs = ["memory_test.cc"],
     external_deps = [
diff --git a/test/core/gprpp/global_config_env_test.cc b/test/core/gprpp/global_config_env_test.cc
new file mode 100644 (file)
index 0000000..74905d3
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ *
+ * Copyright 2019 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <gtest/gtest.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gprpp/global_config_env.h"
+#include "src/core/lib/gprpp/memory.h"
+
+namespace {
+
+bool g_config_error_function_called;
+
+void ClearConfigErrorCalled() { g_config_error_function_called = false; }
+
+bool IsConfigErrorCalled() { return g_config_error_function_called; }
+
+// This function is for preventing the program from invoking
+// an error handler due to configuration error and
+// make test routines know whether there is error.
+void FakeConfigErrorFunction(const char* error_message) {
+  g_config_error_function_called = true;
+}
+
+class GlobalConfigEnvTest : public ::testing::Test {
+ protected:
+  void SetUp() override { ClearConfigErrorCalled(); }
+  void TearDown() override { EXPECT_FALSE(IsConfigErrorCalled()); }
+};
+
+}  // namespace
+
+GPR_GLOBAL_CONFIG_DEFINE_BOOL(bool_var, true, "");
+GPR_GLOBAL_CONFIG_DEFINE_INT32(int32_var, 1234, "");
+GPR_GLOBAL_CONFIG_DEFINE_STRING(string_var, "Apple", "");
+
+TEST_F(GlobalConfigEnvTest, BoolWithEnvTest) {
+  const char* bool_var_name = "BOOL_VAR";
+
+  gpr_unsetenv(bool_var_name);
+  EXPECT_TRUE(GPR_GLOBAL_CONFIG_GET(bool_var));
+
+  gpr_setenv(bool_var_name, "true");
+  EXPECT_TRUE(GPR_GLOBAL_CONFIG_GET(bool_var));
+
+  gpr_setenv(bool_var_name, "false");
+  EXPECT_FALSE(GPR_GLOBAL_CONFIG_GET(bool_var));
+
+  EXPECT_FALSE(IsConfigErrorCalled());
+
+  gpr_setenv(bool_var_name, "");
+  GPR_GLOBAL_CONFIG_GET(bool_var);
+  EXPECT_TRUE(IsConfigErrorCalled());
+  ClearConfigErrorCalled();
+
+  gpr_setenv(bool_var_name, "!");
+  GPR_GLOBAL_CONFIG_GET(bool_var);
+  EXPECT_TRUE(IsConfigErrorCalled());
+  ClearConfigErrorCalled();
+}
+
+TEST_F(GlobalConfigEnvTest, Int32WithEnvTest) {
+  const char* int32_var_name = "INT32_VAR";
+
+  gpr_unsetenv(int32_var_name);
+  EXPECT_EQ(1234, GPR_GLOBAL_CONFIG_GET(int32_var));
+
+  gpr_setenv(int32_var_name, "0");
+  EXPECT_EQ(0, GPR_GLOBAL_CONFIG_GET(int32_var));
+
+  gpr_setenv(int32_var_name, "-123456789");
+  EXPECT_EQ(-123456789, GPR_GLOBAL_CONFIG_GET(int32_var));
+
+  gpr_setenv(int32_var_name, "123456789");
+  EXPECT_EQ(123456789, GPR_GLOBAL_CONFIG_GET(int32_var));
+
+  EXPECT_FALSE(IsConfigErrorCalled());
+
+  gpr_setenv(int32_var_name, "-1AB");
+  GPR_GLOBAL_CONFIG_GET(int32_var);
+  EXPECT_TRUE(IsConfigErrorCalled());
+  ClearConfigErrorCalled();
+}
+
+TEST_F(GlobalConfigEnvTest, StringWithEnvTest) {
+  const char* string_var_name = "STRING_VAR";
+  grpc_core::UniquePtr<char> value;
+
+  gpr_unsetenv(string_var_name);
+  value = GPR_GLOBAL_CONFIG_GET(string_var);
+  EXPECT_EQ(0, strcmp(value.get(), "Apple"));
+
+  gpr_setenv(string_var_name, "Banana");
+  value = GPR_GLOBAL_CONFIG_GET(string_var);
+  EXPECT_EQ(0, strcmp(value.get(), "Banana"));
+
+  gpr_setenv(string_var_name, "");
+  value = GPR_GLOBAL_CONFIG_GET(string_var);
+  EXPECT_EQ(0, strcmp(value.get(), ""));
+}
+
+int main(int argc, char** argv) {
+  // Not to abort the test when parsing error happens.
+  grpc_core::SetGlobalConfigEnvErrorFunction(&FakeConfigErrorFunction);
+
+  ::testing::InitGoogleTest(&argc, argv);
+  int ret = RUN_ALL_TESTS();
+  return ret;
+}
diff --git a/test/core/gprpp/global_config_test.cc b/test/core/gprpp/global_config_test.cc
new file mode 100644 (file)
index 0000000..7da78b6
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *
+ * Copyright 2019 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <gtest/gtest.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gprpp/global_config.h"
+#include "src/core/lib/gprpp/memory.h"
+
+GPR_GLOBAL_CONFIG_DECLARE_BOOL(bool_var);
+
+GPR_GLOBAL_CONFIG_DEFINE_BOOL(bool_var, false, "");
+GPR_GLOBAL_CONFIG_DEFINE_INT32(int32_var, 0, "");
+GPR_GLOBAL_CONFIG_DEFINE_STRING(string_var, "", "");
+
+TEST(GlobalConfigTest, BoolTest) {
+  EXPECT_FALSE(GPR_GLOBAL_CONFIG_GET(bool_var));
+  GPR_GLOBAL_CONFIG_SET(bool_var, true);
+  EXPECT_TRUE(GPR_GLOBAL_CONFIG_GET(bool_var));
+}
+
+TEST(GlobalConfigTest, Int32Test) {
+  EXPECT_EQ(0, GPR_GLOBAL_CONFIG_GET(int32_var));
+  GPR_GLOBAL_CONFIG_SET(int32_var, 1024);
+  EXPECT_EQ(1024, GPR_GLOBAL_CONFIG_GET(int32_var));
+}
+
+TEST(GlobalConfigTest, StringTest) {
+  grpc_core::UniquePtr<char> value;
+
+  value = GPR_GLOBAL_CONFIG_GET(string_var);
+  EXPECT_EQ(0, strcmp(value.get(), ""));
+
+  GPR_GLOBAL_CONFIG_SET(string_var, "Test");
+
+  value = GPR_GLOBAL_CONFIG_GET(string_var);
+  EXPECT_EQ(0, strcmp(value.get(), "Test"));
+}
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  int ret = RUN_ALL_TESTS();
+  return ret;
+}
diff --git a/test/core/gprpp/map_test.cc b/test/core/gprpp/map_test.cc
new file mode 100644 (file)
index 0000000..e70bf38
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "src/core/lib/gprpp/map.h"
+#include <gtest/gtest.h>
+#include "include/grpc/support/string_util.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/orphanable.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "test/core/util/test_config.h"
+
+namespace grpc_core {
+namespace testing {
+class Payload {
+ public:
+  Payload() : data_(-1) {}
+  explicit Payload(int data) : data_(data) {}
+  Payload(const Payload& other) : data_(other.data_) {}
+  Payload& operator=(const Payload& other) {
+    if (this != &other) {
+      data_ = other.data_;
+    }
+    return *this;
+  }
+  int data() { return data_; }
+
+ private:
+  int data_;
+};
+
+inline UniquePtr<char> CopyString(const char* string) {
+  return UniquePtr<char>(gpr_strdup(string));
+}
+
+static constexpr char kKeys[][4] = {"abc", "efg", "hij", "klm", "xyz"};
+
+class MapTest : public ::testing::Test {
+ public:
+  template <class Key, class T, class Compare>
+  typename ::grpc_core::Map<Key, T, Compare>::Entry* Root(
+      typename ::grpc_core::Map<Key, T, Compare>* map) {
+    return map->root_;
+  }
+};
+
+// Test insertion of Payload
+TEST_F(MapTest, EmplaceAndFind) {
+  Map<const char*, Payload, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.emplace(kKeys[i], Payload(i));
+  }
+  for (int i = 0; i < 5; i++) {
+    EXPECT_EQ(i, test_map.find(kKeys[i])->second.data());
+  }
+}
+
+// Test insertion of Payload Unique Ptrs
+TEST_F(MapTest, EmplaceAndFindWithUniquePtrValue) {
+  Map<const char*, UniquePtr<Payload>, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.emplace(kKeys[i], MakeUnique<Payload>(i));
+  }
+  for (int i = 0; i < 5; i++) {
+    EXPECT_EQ(i, test_map.find(kKeys[i])->second->data());
+  }
+}
+
+// Test insertion of Unique Ptr kKeys and Payload
+TEST_F(MapTest, EmplaceAndFindWithUniquePtrKey) {
+  Map<UniquePtr<char>, Payload, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.emplace(CopyString(kKeys[i]), Payload(i));
+  }
+  for (int i = 0; i < 5; i++) {
+    EXPECT_EQ(i, test_map.find(CopyString(kKeys[i]))->second.data());
+  }
+}
+
+// Test insertion of Payload
+TEST_F(MapTest, InsertAndFind) {
+  Map<const char*, Payload, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.insert(MakePair(kKeys[i], Payload(i)));
+  }
+  for (int i = 0; i < 5; i++) {
+    EXPECT_EQ(i, test_map.find(kKeys[i])->second.data());
+  }
+}
+
+// Test insertion of Payload Unique Ptrs
+TEST_F(MapTest, InsertAndFindWithUniquePtrValue) {
+  Map<const char*, UniquePtr<Payload>, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.insert(MakePair(kKeys[i], MakeUnique<Payload>(i)));
+  }
+  for (int i = 0; i < 5; i++) {
+    EXPECT_EQ(i, test_map.find(kKeys[i])->second->data());
+  }
+}
+
+// Test insertion of Unique Ptr kKeys and Payload
+TEST_F(MapTest, InsertAndFindWithUniquePtrKey) {
+  Map<UniquePtr<char>, Payload, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.insert(MakePair(CopyString(kKeys[i]), Payload(i)));
+  }
+  for (int i = 0; i < 5; i++) {
+    EXPECT_EQ(i, test_map.find(CopyString(kKeys[i]))->second.data());
+  }
+}
+
+// Test bracket operators
+TEST_F(MapTest, BracketOperator) {
+  Map<const char*, Payload, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map[kKeys[i]] = Payload(i);
+  }
+  for (int i = 0; i < 5; i++) {
+    EXPECT_EQ(i, test_map[kKeys[i]].data());
+  }
+}
+
+// Test bracket operators with unique pointer to payload
+TEST_F(MapTest, BracketOperatorWithUniquePtrValue) {
+  Map<const char*, UniquePtr<Payload>, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map[kKeys[i]] = MakeUnique<Payload>(i);
+  }
+  for (int i = 0; i < 5; i++) {
+    EXPECT_EQ(i, test_map[kKeys[i]]->data());
+  }
+}
+
+// Test bracket operators with unique pointer to payload
+TEST_F(MapTest, BracketOperatorWithUniquePtrKey) {
+  Map<UniquePtr<char>, Payload, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map[CopyString(kKeys[i])] = Payload(i);
+  }
+  for (int i = 0; i < 5; i++) {
+    EXPECT_EQ(i, test_map[CopyString(kKeys[i])].data());
+  }
+}
+
+// Test removal of a single value
+TEST_F(MapTest, Erase) {
+  Map<const char*, Payload, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.emplace(kKeys[i], Payload(i));
+  }
+  EXPECT_EQ(test_map.size(), 5UL);
+  EXPECT_EQ(test_map.erase(kKeys[3]), 1UL);  // Remove "hij"
+  for (int i = 0; i < 5; i++) {
+    if (i == 3) {  // "hij" should not be present
+      EXPECT_TRUE(test_map.find(kKeys[i]) == test_map.end());
+    } else {
+      EXPECT_EQ(i, test_map.find(kKeys[i])->second.data());
+    }
+  }
+  EXPECT_EQ(test_map.size(), 4UL);
+}
+
+// Test removal of a single value with unique ptr to payload
+TEST_F(MapTest, EraseWithUniquePtrValue) {
+  Map<const char*, UniquePtr<Payload>, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.emplace(kKeys[i], MakeUnique<Payload>(i));
+  }
+  EXPECT_EQ(test_map.size(), 5UL);
+  test_map.erase(kKeys[3]);  // Remove "hij"
+  for (int i = 0; i < 5; i++) {
+    if (i == 3) {  // "hij" should not be present
+      EXPECT_TRUE(test_map.find(kKeys[i]) == test_map.end());
+    } else {
+      EXPECT_EQ(i, test_map.find(kKeys[i])->second->data());
+    }
+  }
+  EXPECT_EQ(test_map.size(), 4UL);
+}
+
+// Test removal of a single value
+TEST_F(MapTest, EraseWithUniquePtrKey) {
+  Map<UniquePtr<char>, Payload, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.emplace(CopyString(kKeys[i]), Payload(i));
+  }
+  EXPECT_EQ(test_map.size(), 5UL);
+  test_map.erase(CopyString(kKeys[3]));  // Remove "hij"
+  for (int i = 0; i < 5; i++) {
+    if (i == 3) {  // "hij" should not be present
+      EXPECT_TRUE(test_map.find(CopyString(kKeys[i])) == test_map.end());
+    } else {
+      EXPECT_EQ(i, test_map.find(CopyString(kKeys[i]))->second.data());
+    }
+  }
+  EXPECT_EQ(test_map.size(), 4UL);
+}
+
+// Test clear
+TEST_F(MapTest, SizeAndClear) {
+  Map<const char*, Payload, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.emplace(kKeys[i], Payload(i));
+  }
+  EXPECT_EQ(test_map.size(), 5UL);
+  EXPECT_FALSE(test_map.empty());
+  test_map.clear();
+  EXPECT_EQ(test_map.size(), 0UL);
+  EXPECT_TRUE(test_map.empty());
+}
+
+// Test clear with unique ptr payload
+TEST_F(MapTest, SizeAndClearWithUniquePtrValue) {
+  Map<const char*, UniquePtr<Payload>, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.emplace(kKeys[i], MakeUnique<Payload>(i));
+  }
+  EXPECT_EQ(test_map.size(), 5UL);
+  EXPECT_FALSE(test_map.empty());
+  test_map.clear();
+  EXPECT_EQ(test_map.size(), 0UL);
+  EXPECT_TRUE(test_map.empty());
+}
+
+// Test clear with unique ptr char key
+TEST_F(MapTest, SizeAndClearWithUniquePtrKey) {
+  Map<UniquePtr<char>, Payload, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.emplace(CopyString(kKeys[i]), Payload(i));
+  }
+  EXPECT_EQ(test_map.size(), 5UL);
+  EXPECT_FALSE(test_map.empty());
+  test_map.clear();
+  EXPECT_EQ(test_map.size(), 0UL);
+  EXPECT_TRUE(test_map.empty());
+}
+
+// Test correction of Left-Left Tree imbalance
+TEST_F(MapTest, MapLL) {
+  Map<const char*, Payload, StringLess> test_map;
+  for (int i = 2; i >= 0; i--) {
+    test_map.emplace(kKeys[i], Payload(i));
+  }
+  EXPECT_EQ(strcmp(Root(&test_map)->pair.first, kKeys[1]), 0);
+  EXPECT_EQ(strcmp(Root(&test_map)->left->pair.first, kKeys[0]), 0);
+  EXPECT_EQ(strcmp(Root(&test_map)->right->pair.first, kKeys[2]), 0);
+}
+
+// Test correction of Left-Right tree imbalance
+TEST_F(MapTest, MapLR) {
+  Map<const char*, Payload, StringLess> test_map;
+  int insertion_key_index[] = {2, 0, 1};
+  for (int i = 0; i < 3; i++) {
+    int key_index = insertion_key_index[i];
+    test_map.emplace(kKeys[key_index], Payload(key_index));
+  }
+  EXPECT_EQ(strcmp(Root(&test_map)->pair.first, kKeys[1]), 0);
+  EXPECT_EQ(strcmp(Root(&test_map)->left->pair.first, kKeys[0]), 0);
+  EXPECT_EQ(strcmp(Root(&test_map)->right->pair.first, kKeys[2]), 0);
+}
+
+// Test correction of Right-Left tree imbalance
+TEST_F(MapTest, MapRL) {
+  Map<const char*, Payload, StringLess> test_map;
+  int insertion_key_index[] = {0, 2, 1};
+  for (int i = 0; i < 3; i++) {
+    int key_index = insertion_key_index[i];
+    test_map.emplace(kKeys[key_index], Payload(key_index));
+  }
+  EXPECT_EQ(strcmp(Root(&test_map)->pair.first, kKeys[1]), 0);
+  EXPECT_EQ(strcmp(Root(&test_map)->left->pair.first, kKeys[0]), 0);
+  EXPECT_EQ(strcmp(Root(&test_map)->right->pair.first, kKeys[2]), 0);
+}
+
+// Test correction of Right-Right tree imbalance
+TEST_F(MapTest, MapRR) {
+  Map<const char*, Payload, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.emplace(kKeys[i], Payload(i));
+  }
+  EXPECT_EQ(strcmp(Root(&test_map)->pair.first, kKeys[1]), 0);
+  EXPECT_EQ(strcmp(Root(&test_map)->left->pair.first, kKeys[0]), 0);
+  EXPECT_EQ(strcmp(Root(&test_map)->right->pair.first, kKeys[3]), 0);
+  EXPECT_EQ(strcmp(Root(&test_map)->right->left->pair.first, kKeys[2]), 0);
+  EXPECT_EQ(strcmp(Root(&test_map)->right->right->pair.first, kKeys[4]), 0);
+}
+
+// Test correction after random insertion
+TEST_F(MapTest, MapRandomInsertions) {
+  Map<const char*, Payload, StringLess> test_map;
+  int insertion_key_index[] = {1, 4, 3, 0, 2};
+  for (int i = 0; i < 5; i++) {
+    int key_index = insertion_key_index[i];
+    test_map.emplace(kKeys[key_index], Payload(key_index));
+  }
+  EXPECT_EQ(strcmp(Root(&test_map)->pair.first, kKeys[3]), 0);
+  EXPECT_EQ(strcmp(Root(&test_map)->left->pair.first, kKeys[1]), 0);
+  EXPECT_EQ(strcmp(Root(&test_map)->right->pair.first, kKeys[4]), 0);
+  EXPECT_EQ(strcmp(Root(&test_map)->left->right->pair.first, kKeys[2]), 0);
+  EXPECT_EQ(strcmp(Root(&test_map)->left->left->pair.first, kKeys[0]), 0);
+}
+
+// Test Map iterator
+TEST_F(MapTest, Iteration) {
+  Map<const char*, Payload, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.emplace(kKeys[i], Payload(i));
+  }
+  int count = 0;
+  for (auto iter = test_map.begin(); iter != test_map.end(); iter++) {
+    EXPECT_EQ(iter->second.data(), count);
+    count++;
+  }
+  EXPECT_EQ(count, 5);
+}
+
+// Test Map iterator with unique ptr payload
+TEST_F(MapTest, IterationWithUniquePtrValue) {
+  Map<const char*, UniquePtr<Payload>, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.emplace(kKeys[i], MakeUnique<Payload>(i));
+  }
+  int count = 0;
+  for (auto iter = test_map.begin(); iter != test_map.end(); iter++) {
+    EXPECT_EQ(iter->second->data(), count);
+    count++;
+  }
+  EXPECT_EQ(count, 5);
+}
+
+// Test Map iterator with unique ptr to char key
+TEST_F(MapTest, IterationWithUniquePtrKey) {
+  Map<UniquePtr<char>, Payload, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.emplace(CopyString(kKeys[i]), Payload(i));
+  }
+  int count = 0;
+  for (auto iter = test_map.begin(); iter != test_map.end(); iter++) {
+    EXPECT_EQ(iter->second.data(), count);
+    count++;
+  }
+  EXPECT_EQ(count, 5);
+}
+
+// Test removing entries while iterating the map
+TEST_F(MapTest, EraseUsingIterator) {
+  Map<const char*, Payload, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.emplace(kKeys[i], Payload(i));
+  }
+  int count = 0;
+  for (auto iter = test_map.begin(); iter != test_map.end();) {
+    EXPECT_EQ(iter->second.data(), count);
+    iter = test_map.erase(iter);
+    count++;
+  }
+  EXPECT_EQ(count, 5);
+  EXPECT_TRUE(test_map.empty());
+}
+
+// Random ops on a Map with Integer key of Payload value,
+// tests default comparator
+TEST_F(MapTest, RandomOpsWithIntKey) {
+  Map<int, Payload> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.emplace(i, Payload(i));
+  }
+  for (int i = 0; i < 5; i++) {
+    EXPECT_EQ(i, test_map.find(i)->second.data());
+  }
+  for (int i = 0; i < 5; i++) {
+    test_map[i] = Payload(i + 10);
+  }
+  for (int i = 0; i < 5; i++) {
+    EXPECT_EQ(i + 10, test_map[i].data());
+  }
+  EXPECT_EQ(test_map.erase(3), 1UL);
+  EXPECT_TRUE(test_map.find(3) == test_map.end());
+  EXPECT_FALSE(test_map.empty());
+  EXPECT_EQ(test_map.size(), 4UL);
+  test_map.clear();
+  EXPECT_EQ(test_map.size(), 0UL);
+  EXPECT_TRUE(test_map.empty());
+}
+
+}  // namespace testing
+}  // namespace grpc_core
+
+int main(int argc, char** argv) {
+  grpc::testing::TestEnvironment env(argc, argv);
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index b9d2f31..0824efb 100644 (file)
@@ -32,6 +32,7 @@ grpc_cc_test(
         "//:grpc",
         "//test/core/util:grpc_test_util",
     ],
+    tags = ["no_windows"],
 )
 
 grpc_cc_library(
@@ -43,6 +44,7 @@ grpc_cc_library(
         "//:grpc",
         "//test/core/util:grpc_test_util",
     ],
+    tags = ["no_windows"],
 )
 
 grpc_cc_test(
@@ -60,6 +62,7 @@ grpc_cc_test(
         "//:grpc",
         "//test/core/util:grpc_test_util",
     ],
+    tags = ["no_windows"],
 )
 
 grpc_cc_test(
@@ -77,6 +80,7 @@ grpc_cc_test(
         "//:grpc",
         "//test/core/util:grpc_test_util",
     ],
+    tags = ["no_windows"],
 )
 
 grpc_cc_test(
index 326b0e9..e7250c2 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "src/core/lib/gpr/env.h"
 #include "src/core/lib/iomgr/iomgr.h"
+#include "src/core/lib/security/security_connector/ssl_utils.h"
 #include "test/core/util/port.h"
 #include "test/core/util/subprocess.h"
 #include "test/core/util/test_config.h"
@@ -184,7 +185,7 @@ int main(int argc, char** argv) {
   /* Set the environment variable for the SSL certificate file */
   char* pem_file;
   gpr_asprintf(&pem_file, "%s/src/core/tsi/test_creds/ca.pem", root);
-  gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, pem_file);
+  GPR_GLOBAL_CONFIG_SET(grpc_default_ssl_roots_file_path, pem_file);
   gpr_free(pem_file);
 
   /* start the server */
index 826c7e1..112d7c2 100644 (file)
@@ -29,6 +29,7 @@
 #include <grpc/support/sync.h>
 #include <grpc/support/time.h>
 
+#include "src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h"
 #include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gpr/useful.h"
@@ -224,15 +225,16 @@ int main(int argc, char** argv) {
   // --resolver will always be the first one, so only parse the first argument
   // (other arguments may be unknown to cl)
   gpr_cmdline_parse(cl, argc > 2 ? 2 : argc, argv);
-  const char* cur_resolver = gpr_getenv("GRPC_DNS_RESOLVER");
-  if (cur_resolver != nullptr && strlen(cur_resolver) != 0) {
+  grpc_core::UniquePtr<char> resolver =
+      GPR_GLOBAL_CONFIG_GET(grpc_dns_resolver);
+  if (strlen(resolver.get()) != 0) {
     gpr_log(GPR_INFO, "Warning: overriding resolver setting of %s",
-            cur_resolver);
+            resolver.get());
   }
   if (gpr_stricmp(resolver_type, "native") == 0) {
-    gpr_setenv("GRPC_DNS_RESOLVER", "native");
+    GPR_GLOBAL_CONFIG_SET(grpc_dns_resolver, "native");
   } else if (gpr_stricmp(resolver_type, "ares") == 0) {
-    gpr_setenv("GRPC_DNS_RESOLVER", "ares");
+    GPR_GLOBAL_CONFIG_SET(grpc_dns_resolver, "ares");
   } else {
     gpr_log(GPR_ERROR, "--resolver_type was not set to ares or native");
     abort();
@@ -246,12 +248,12 @@ int main(int argc, char** argv) {
     // c-ares resolver doesn't support UDS (ability for native DNS resolver
     // to handle this is only expected to be used by servers, which
     // unconditionally use the native DNS resolver).
-    char* resolver_env = gpr_getenv("GRPC_DNS_RESOLVER");
-    if (resolver_env == nullptr || gpr_stricmp(resolver_env, "native") == 0) {
+    grpc_core::UniquePtr<char> resolver =
+        GPR_GLOBAL_CONFIG_GET(grpc_dns_resolver);
+    if (gpr_stricmp(resolver.get(), "native") == 0) {
       test_unix_socket();
       test_unix_socket_path_name_too_long();
     }
-    gpr_free(resolver_env);
   }
   gpr_cmdline_destroy(cl);
 
index f59a992..cbc0348 100644 (file)
@@ -27,7 +27,7 @@
 
 #include <string.h>
 
-#include "src/core/lib/gpr/env.h"
+#include "src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/iomgr/executor.h"
 #include "src/core/lib/iomgr/iomgr.h"
@@ -83,7 +83,9 @@ static grpc_millis n_sec_deadline(int seconds) {
 
 static void poll_pollset_until_request_done(args_struct* args) {
   grpc_core::ExecCtx exec_ctx;
-  grpc_millis deadline = n_sec_deadline(10);
+  // Try to give enough time for c-ares to run through its retries
+  // a few times if needed.
+  grpc_millis deadline = n_sec_deadline(90);
   while (true) {
     bool done = gpr_atm_acq_load(&args->done_atm) != 0;
     if (done) {
@@ -345,16 +347,17 @@ int main(int argc, char** argv) {
   // --resolver will always be the first one, so only parse the first argument
   // (other arguments may be unknown to cl)
   gpr_cmdline_parse(cl, argc > 2 ? 2 : argc, argv);
-  const char* cur_resolver = gpr_getenv("GRPC_DNS_RESOLVER");
-  if (cur_resolver != nullptr && strlen(cur_resolver) != 0) {
+  grpc_core::UniquePtr<char> resolver =
+      GPR_GLOBAL_CONFIG_GET(grpc_dns_resolver);
+  if (strlen(resolver.get()) != 0) {
     gpr_log(GPR_INFO, "Warning: overriding resolver setting of %s",
-            cur_resolver);
+            resolver.get());
   }
   if (gpr_stricmp(resolver_type, "native") == 0) {
-    gpr_setenv("GRPC_DNS_RESOLVER", "native");
+    GPR_GLOBAL_CONFIG_SET(grpc_dns_resolver, "native");
   } else if (gpr_stricmp(resolver_type, "ares") == 0) {
 #ifndef GRPC_UV
-    gpr_setenv("GRPC_DNS_RESOLVER", "ares");
+    GPR_GLOBAL_CONFIG_SET(grpc_dns_resolver, "ares");
 #endif
   } else {
     gpr_log(GPR_ERROR, "--resolver_type was not set to ares or native");
@@ -371,15 +374,10 @@ int main(int argc, char** argv) {
     test_missing_default_port();
     test_ipv6_with_port();
     test_ipv6_without_port();
-    if (gpr_stricmp(resolver_type, "ares") != 0) {
-      // These tests can trigger DNS queries to the nearby nameserver
-      // that need to come back in order for the test to succeed.
-      // c-ares is prone to not using the local system caches that the
-      // native getaddrinfo implementations take advantage of, so running
-      // these unit tests under c-ares risks flakiness.
-      test_invalid_ip_addresses();
-      test_unparseable_hostports();
-    } else {
+    test_invalid_ip_addresses();
+    test_unparseable_hostports();
+    if (gpr_stricmp(resolver_type, "ares") == 0) {
+      // This behavior expectation is specific to c-ares.
       test_localhost_result_has_ipv6_first();
     }
     grpc_core::Executor::ShutdownAll();
index 4208737..f0537e0 100644 (file)
@@ -24,6 +24,7 @@
 #include "src/core/lib/iomgr/socket_utils_posix.h"
 
 #include <errno.h>
+#include <netinet/in.h>
 #include <netinet/ip.h>
 #include <string.h>
 
index fbc611d..1194e4a 100644 (file)
@@ -33,4 +33,5 @@ grpc_cc_binary(
         "//:grpc",
         "//test/core/util:grpc_test_util",
     ],
+    tags = ["no_windows"],
 )
index 11cfc8c..141346b 100644 (file)
@@ -1161,7 +1161,7 @@ static void test_get_well_known_google_credentials_file_path(void) {
   GPR_ASSERT(path != nullptr);
   gpr_free(path);
 #if defined(GPR_POSIX_ENV) || defined(GPR_LINUX_ENV)
-  unsetenv("HOME");
+  gpr_unsetenv("HOME");
   path = grpc_get_well_known_google_credentials_file_path();
   GPR_ASSERT(path == nullptr);
   gpr_setenv("HOME", home);
index 496f064..c888c90 100644 (file)
@@ -24,7 +24,6 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gpr/tmpfile.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
@@ -394,7 +393,7 @@ static void test_default_ssl_roots(void) {
 
   /* First let's get the root through the override: set the env to an invalid
      value. */
-  gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, "");
+  GPR_GLOBAL_CONFIG_SET(grpc_default_ssl_roots_file_path, "");
   grpc_set_ssl_roots_override_callback(override_roots_success);
   grpc_slice roots =
       grpc_core::TestDefaultSslRootStore::ComputePemRootCertsForTesting();
@@ -405,7 +404,8 @@ static void test_default_ssl_roots(void) {
 
   /* Now let's set the env var: We should get the contents pointed value
      instead. */
-  gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_env_var_file_path);
+  GPR_GLOBAL_CONFIG_SET(grpc_default_ssl_roots_file_path,
+                        roots_env_var_file_path);
   roots = grpc_core::TestDefaultSslRootStore::ComputePemRootCertsForTesting();
   roots_contents = grpc_slice_to_c_string(roots);
   grpc_slice_unref(roots);
@@ -414,7 +414,7 @@ static void test_default_ssl_roots(void) {
 
   /* Now reset the env var. We should fall back to the value overridden using
      the api. */
-  gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, "");
+  GPR_GLOBAL_CONFIG_SET(grpc_default_ssl_roots_file_path, "");
   roots = grpc_core::TestDefaultSslRootStore::ComputePemRootCertsForTesting();
   roots_contents = grpc_slice_to_c_string(roots);
   grpc_slice_unref(roots);
@@ -423,7 +423,7 @@ static void test_default_ssl_roots(void) {
 
   /* Now setup a permanent failure for the overridden roots and we should get
      an empty slice. */
-  gpr_setenv("GRPC_NOT_USE_SYSTEM_SSL_ROOTS", "true");
+  GPR_GLOBAL_CONFIG_SET(grpc_not_use_system_ssl_roots, true);
   grpc_set_ssl_roots_override_callback(override_roots_permanent_failure);
   roots = grpc_core::TestDefaultSslRootStore::ComputePemRootCertsForTesting();
   GPR_ASSERT(GRPC_SLICE_IS_EMPTY(roots));
index 1e53a19..6ed0236 100644 (file)
@@ -51,13 +51,6 @@ static void test_slice_malloc_returns_something_sensible(void) {
     }
     /* Returned slice length must be what was requested. */
     GPR_ASSERT(GRPC_SLICE_LENGTH(slice) == length);
-    /* If the slice has a refcount, it must be destroyable. */
-    if (slice.refcount) {
-      GPR_ASSERT(slice.refcount->vtable != nullptr);
-      GPR_ASSERT(slice.refcount->vtable->ref != nullptr);
-      GPR_ASSERT(slice.refcount->vtable->unref != nullptr);
-      GPR_ASSERT(slice.refcount->vtable->hash != nullptr);
-    }
     /* We must be able to write to every byte of the data */
     for (i = 0; i < length; i++) {
       GRPC_SLICE_START_PTR(slice)[i] = static_cast<uint8_t>(i);
index 7c7e308..26c09a7 100644 (file)
@@ -60,13 +60,9 @@ static void test_connectivity_state_name(void) {
 static void test_check(void) {
   grpc_connectivity_state_tracker tracker;
   grpc_core::ExecCtx exec_ctx;
-  grpc_error* error;
   gpr_log(GPR_DEBUG, "test_check");
   grpc_connectivity_state_init(&tracker, GRPC_CHANNEL_IDLE, "xxx");
-  GPR_ASSERT(grpc_connectivity_state_get(&tracker, &error) ==
-             GRPC_CHANNEL_IDLE);
   GPR_ASSERT(grpc_connectivity_state_check(&tracker) == GRPC_CHANNEL_IDLE);
-  GPR_ASSERT(error == GRPC_ERROR_NONE);
   grpc_connectivity_state_destroy(&tracker);
 }
 
index 48a77db..c489b11 100644 (file)
@@ -32,14 +32,11 @@ int main(int argc, char** argv) {
   uint8_t buffer[] = "abc123";
   grpc_stream_refcount r;
   GRPC_STREAM_REF_INIT(&r, 1, do_nothing, nullptr, "test");
-  GPR_ASSERT(r.refs.count == 1);
   grpc_slice slice =
       grpc_slice_from_stream_owned_buffer(&r, buffer, sizeof(buffer));
   GPR_ASSERT(GRPC_SLICE_START_PTR(slice) == buffer);
   GPR_ASSERT(GRPC_SLICE_LENGTH(slice) == sizeof(buffer));
-  GPR_ASSERT(r.refs.count == 2);
   grpc_slice_unref(slice);
-  GPR_ASSERT(r.refs.count == 1);
 
   grpc_shutdown();
   return 0;
index b931a9d..47f814a 100644 (file)
@@ -76,17 +76,17 @@ grpc_cc_library(
         "tracer_util.h",
         "trickle_endpoint.h",
     ],
+    data = [
+        "lsan_suppressions.txt",
+        "tsan_suppressions.txt",
+        "ubsan_suppressions.txt",
+    ],
     language = "C++",
     deps = [
         ":grpc_debugger_macros",
         "//:gpr",
         "//:grpc_common",
     ],
-    data = [
-        "lsan_suppressions.txt",
-        "tsan_suppressions.txt",
-        "ubsan_suppressions.txt",
-    ],
 )
 
 grpc_cc_library(
@@ -132,7 +132,9 @@ grpc_cc_library(
     deps = [
         ":grpc_test_util",
         "//:grpc",
+        "//test/cpp/util:test_config",
     ],
+    tags = ["no_windows"],
 )
 
 grpc_cc_test(
index 6e3785c..864533c 100644 (file)
@@ -29,6 +29,7 @@
 #include "src/core/lib/gpr/env.h"
 #include "src/core/lib/iomgr/load_file.h"
 #include "test/core/util/test_config.h"
+#include "test/cpp/util/test_config.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
 extern bool squelch;
@@ -145,8 +146,8 @@ INSTANTIATE_TEST_CASE_P(
 
 int main(int argc, char** argv) {
   grpc::testing::TestEnvironment env(argc, argv);
-  ParseCommandLineFlags(&argc, &argv, true);
   ::testing::InitGoogleTest(&argc, argv);
+  grpc::testing::InitTest(&argc, &argv, true);
 
   return RUN_ALL_TESTS();
 }
index ad7cf42..144ad64 100644 (file)
@@ -109,7 +109,7 @@ void reconnect_server_start(reconnect_server* server, int port) {
 }
 
 void reconnect_server_poll(reconnect_server* server, int seconds) {
-  test_tcp_server_poll(&server->tcp_server, seconds);
+  test_tcp_server_poll(&server->tcp_server, 1000 * seconds);
 }
 
 void reconnect_server_clear_timestamps(reconnect_server* server) {
index 476e424..5b248a0 100644 (file)
@@ -28,7 +28,6 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gpr/useful.h"
 #include "src/core/lib/surface/init.h"
index 05e25eb..b871f04 100644 (file)
@@ -150,12 +150,11 @@ class InterceptRecvTrailingMetadataLoadBalancingPolicy
       return parent_->channel_control_helper()->CreateChannel(target, args);
     }
 
-    void UpdateState(grpc_connectivity_state state, grpc_error* state_error,
+    void UpdateState(grpc_connectivity_state state,
                      UniquePtr<SubchannelPicker> picker) override {
       parent_->channel_control_helper()->UpdateState(
-          state, state_error,
-          UniquePtr<SubchannelPicker>(
-              New<Picker>(std::move(picker), cb_, user_data_)));
+          state, UniquePtr<SubchannelPicker>(
+                     New<Picker>(std::move(picker), cb_, user_data_)));
     }
 
     void RequestReresolution() override {
index 610a991..80d0634 100644 (file)
@@ -77,11 +77,11 @@ void test_tcp_server_start(test_tcp_server* server, int port) {
   gpr_log(GPR_INFO, "test tcp server listening on 0.0.0.0:%d", port);
 }
 
-void test_tcp_server_poll(test_tcp_server* server, int seconds) {
+void test_tcp_server_poll(test_tcp_server* server, int milliseconds) {
   grpc_pollset_worker* worker = nullptr;
   grpc_core::ExecCtx exec_ctx;
   grpc_millis deadline = grpc_timespec_to_millis_round_up(
-      grpc_timeout_seconds_to_deadline(seconds));
+      grpc_timeout_milliseconds_to_deadline(milliseconds));
   gpr_mu_lock(server->mu);
   GRPC_LOG_IF_ERROR("pollset_work",
                     grpc_pollset_work(server->pollset, &worker, deadline));
@@ -104,7 +104,7 @@ void test_tcp_server_destroy(test_tcp_server* server) {
                                    gpr_time_from_seconds(5, GPR_TIMESPAN));
   while (!server->shutdown &&
          gpr_time_cmp(gpr_now(GPR_CLOCK_MONOTONIC), shutdown_deadline) < 0) {
-    test_tcp_server_poll(server, 1);
+    test_tcp_server_poll(server, 1000);
   }
   grpc_pollset_shutdown(server->pollset,
                         GRPC_CLOSURE_CREATE(finish_pollset, server->pollset,
index 5814038..313a27a 100644 (file)
@@ -35,7 +35,7 @@ typedef struct test_tcp_server {
 void test_tcp_server_init(test_tcp_server* server,
                           grpc_tcp_server_cb on_connect, void* user_data);
 void test_tcp_server_start(test_tcp_server* server, int port);
-void test_tcp_server_poll(test_tcp_server* server, int seconds);
+void test_tcp_server_poll(test_tcp_server* server, int milliseconds);
 void test_tcp_server_destroy(test_tcp_server* server);
 
 #endif /* GRPC_TEST_CORE_UTIL_TEST_TCP_SERVER_H */
index 91419cb..7b4d472 100644 (file)
@@ -31,6 +31,7 @@
 #include <grpcpp/channel.h>
 #include <grpcpp/client_context.h>
 #include <grpcpp/create_channel.h>
+#include <grpcpp/impl/codegen/sync.h>
 #include <grpcpp/server.h>
 #include <grpcpp/server_builder.h>
 
@@ -168,24 +169,24 @@ class ClientChannelStressTest {
     explicit ServerThread(const grpc::string& type,
                           const grpc::string& server_host, T* service)
         : type_(type), service_(service) {
-      std::mutex mu;
+      grpc::internal::Mutex mu;
       // We need to acquire the lock here in order to prevent the notify_one
       // by ServerThread::Start from firing before the wait below is hit.
-      std::unique_lock<std::mutex> lock(mu);
+      grpc::internal::MutexLock lock(&mu);
       port_ = grpc_pick_unused_port_or_die();
       gpr_log(GPR_INFO, "starting %s server on port %d", type_.c_str(), port_);
-      std::condition_variable cond;
+      grpc::internal::CondVar cond;
       thread_.reset(new std::thread(
           std::bind(&ServerThread::Start, this, server_host, &mu, &cond)));
-      cond.wait(lock);
+      cond.Wait(&mu);
       gpr_log(GPR_INFO, "%s server startup complete", type_.c_str());
     }
 
-    void Start(const grpc::string& server_host, std::mutex* mu,
-               std::condition_variable* cond) {
+    void Start(const grpc::string& server_host, grpc::internal::Mutex* mu,
+               grpc::internal::CondVar* cond) {
       // We need to acquire the lock here in order to prevent the notify_one
       // below from firing before its corresponding wait is executed.
-      std::lock_guard<std::mutex> lock(*mu);
+      grpc::internal::MutexLock lock(mu);
       std::ostringstream server_address;
       server_address << server_host << ":" << port_;
       ServerBuilder builder;
@@ -193,7 +194,7 @@ class ClientChannelStressTest {
                                InsecureServerCredentials());
       builder.RegisterService(service_);
       server_ = builder.BuildAndStart();
-      cond->notify_one();
+      cond->Signal();
     }
 
     void Shutdown() {
@@ -267,8 +268,8 @@ class ClientChannelStressTest {
                     response_generator_.get());
     std::ostringstream uri;
     uri << "fake:///servername_not_used";
-    channel_ =
-        CreateCustomChannel(uri.str(), InsecureChannelCredentials(), args);
+    channel_ = ::grpc::CreateCustomChannel(uri.str(),
+                                           InsecureChannelCredentials(), args);
     stub_ = grpc::testing::EchoTestService::NewStub(channel_);
   }
 
index 558e5e7..90af67b 100644 (file)
@@ -66,6 +66,7 @@ grpc_cc_binary(
     deps = [
         "//:grpc++",
         "//test/core/util:grpc_test_util",
+        "//test/cpp/util:test_config",
     ],
 )
 
index 7f9fd29..e571a95 100644 (file)
 #include <grpcpp/impl/codegen/stub_options.h>
 #include <grpcpp/impl/codegen/sync_stream.h>
 
-namespace grpc {
-class CompletionQueue;
+namespace grpc_impl {
 class Channel;
+class CompletionQueue;
 class ServerCompletionQueue;
+}  // namespace grpc_impl
+
+namespace grpc {
+namespace experimental {
+template <typename RequestT, typename ResponseT>
+class MessageAllocator;
+}  // namespace experimental
+}  // namespace grpc_impl
+
+namespace grpc {
 class ServerContext;
 }  // namespace grpc
 
@@ -114,6 +124,8 @@ class ServiceA final {
       // MethodA1 leading comment 1
       virtual void MethodA1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, std::function<void(::grpc::Status)>) = 0;
       virtual void MethodA1(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::grpc::testing::Response* response, std::function<void(::grpc::Status)>) = 0;
+      virtual void MethodA1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0;
+      virtual void MethodA1(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::grpc::testing::Response* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0;
       // MethodA1 trailing comment 1
       // MethodA2 detached leading comment 1
       //
@@ -184,6 +196,8 @@ class ServiceA final {
      public:
       void MethodA1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, std::function<void(::grpc::Status)>) override;
       void MethodA1(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::grpc::testing::Response* response, std::function<void(::grpc::Status)>) override;
+      void MethodA1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, ::grpc::experimental::ClientUnaryReactor* reactor) override;
+      void MethodA1(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::grpc::testing::Response* response, ::grpc::experimental::ClientUnaryReactor* reactor) override;
       void MethodA2(::grpc::ClientContext* context, ::grpc::testing::Response* response, ::grpc::experimental::ClientWriteReactor< ::grpc::testing::Request>* reactor) override;
       void MethodA3(::grpc::ClientContext* context, ::grpc::testing::Request* request, ::grpc::experimental::ClientReadReactor< ::grpc::testing::Response>* reactor) override;
       void MethodA4(::grpc::ClientContext* context, ::grpc::experimental::ClientBidiReactor< ::grpc::testing::Request,::grpc::testing::Response>* reactor) override;
@@ -332,6 +346,12 @@ class ServiceA final {
                    return this->MethodA1(context, request, response, controller);
                  }));
     }
+    void SetMessageAllocatorFor_MethodA1(
+        ::grpc::experimental::MessageAllocator< ::grpc::testing::Request, ::grpc::testing::Response>* allocator) {
+      static_cast<::grpc::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>*>(
+          ::grpc::Service::experimental().GetHandler(0))
+              ->SetMessageAllocator(allocator);
+    }
     ~ExperimentalWithCallbackMethod_MethodA1() override {
       BaseClassMustBeDerivedFromService(this);
     }
@@ -717,6 +737,8 @@ class ServiceB final {
       // MethodB1 leading comment 1
       virtual void MethodB1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, std::function<void(::grpc::Status)>) = 0;
       virtual void MethodB1(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::grpc::testing::Response* response, std::function<void(::grpc::Status)>) = 0;
+      virtual void MethodB1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0;
+      virtual void MethodB1(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::grpc::testing::Response* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0;
       // MethodB1 trailing comment 1
     };
     virtual class experimental_async_interface* experimental_async() { return nullptr; }
@@ -739,6 +761,8 @@ class ServiceB final {
      public:
       void MethodB1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, std::function<void(::grpc::Status)>) override;
       void MethodB1(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::grpc::testing::Response* response, std::function<void(::grpc::Status)>) override;
+      void MethodB1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, ::grpc::experimental::ClientUnaryReactor* reactor) override;
+      void MethodB1(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::grpc::testing::Response* response, ::grpc::experimental::ClientUnaryReactor* reactor) override;
      private:
       friend class Stub;
       explicit experimental_async(Stub* stub): stub_(stub) { }
@@ -800,6 +824,12 @@ class ServiceB final {
                    return this->MethodB1(context, request, response, controller);
                  }));
     }
+    void SetMessageAllocatorFor_MethodB1(
+        ::grpc::experimental::MessageAllocator< ::grpc::testing::Request, ::grpc::testing::Response>* allocator) {
+      static_cast<::grpc::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>*>(
+          ::grpc::Service::experimental().GetHandler(0))
+              ->SetMessageAllocator(allocator);
+    }
     ~ExperimentalWithCallbackMethod_MethodB1() override {
       BaseClassMustBeDerivedFromService(this);
     }
index bfd3649..8951060 100644 (file)
@@ -22,6 +22,8 @@
 #include <gflags/gflags.h>
 #include <gtest/gtest.h>
 
+#include "test/cpp/util/test_config.h"
+
 // In some distros, gflags is in the namespace google, and in some others,
 // in gflags. This hack is enabling us to find both.
 namespace google {}
@@ -31,7 +33,7 @@ using namespace gflags;
 
 DEFINE_string(
     generated_file_path, "",
-    "path to the directory containing generated files compiler_test.grpc.pb.h"
+    "path to the directory containing generated files compiler_test.grpc.pb.h "
     "and compiler_test_mock.grpc.pb.h");
 
 const char kGoldenFilePath[] = "test/cpp/codegen/compiler_test_golden";
@@ -67,7 +69,7 @@ TEST(GoldenMockFileTest, TestGeneratedMockFile) {
 
 int main(int argc, char** argv) {
   ::testing::InitGoogleTest(&argc, argv);
-  ParseCommandLineFlags(&argc, &argv, true);
+  grpc::testing::InitTest(&argc, &argv, true);
   if (FLAGS_generated_file_path.empty()) {
     FLAGS_generated_file_path = "gens/src/proto/grpc/testing/";
   }
index 707a628..519a33f 100644 (file)
@@ -140,6 +140,7 @@ grpc_cc_binary(
         "//src/proto/grpc/testing:echo_proto",
         "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
         "//test/core/util:grpc_test_util",
+        "//test/cpp/util:test_config",
         "//test/cpp/util:test_util",
     ],
 )
@@ -423,6 +424,26 @@ grpc_cc_test(
 )
 
 grpc_cc_test(
+    name = "service_config_end2end_test",
+    srcs = ["service_config_end2end_test.cc"],
+    external_deps = [
+        "gmock",
+        "gtest",
+    ],
+    deps = [
+        ":test_service_impl",
+        "//:gpr",
+        "//:grpc",
+        "//:grpc++",
+        "//src/proto/grpc/testing:echo_messages_proto",
+        "//src/proto/grpc/testing:echo_proto",
+        "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+        "//test/core/util:grpc_test_util",
+        "//test/cpp/util:test_util",
+    ],
+)
+
+grpc_cc_test(
     name = "grpclb_end2end_test",
     srcs = ["grpclb_end2end_test.cc"],
     external_deps = [
@@ -544,6 +565,7 @@ grpc_cc_binary(
         "//src/proto/grpc/testing:echo_proto",
         "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
         "//test/core/util:grpc_test_util",
+        "//test/cpp/util:test_config",
         "//test/cpp/util:test_util",
     ],
 )
@@ -675,3 +697,23 @@ grpc_cc_test(
         "//test/cpp/util:test_util",
     ],
 )
+
+grpc_cc_test(
+    name = "message_allocator_end2end_test",
+    srcs = ["message_allocator_end2end_test.cc"],
+    external_deps = [
+        "gtest",
+    ],
+    deps = [
+        ":test_service_impl",
+        "//:grpc",
+        "//:gpr",
+        "//:grpc++",
+        "//src/proto/grpc/testing:echo_messages_proto",
+        "//src/proto/grpc/testing:echo_proto",
+        "//src/proto/grpc/testing:simple_messages_proto",
+        "//test/core/util:grpc_test_util",
+        "//test/cpp/util:test_util",
+    ],
+)
+
index e09f54d..6ca0edf 100644 (file)
@@ -32,7 +32,7 @@
 #include <grpcpp/server_builder.h>
 #include <grpcpp/server_context.h>
 
-#include "src/core/lib/gpr/env.h"
+#include "src/core/ext/filters/client_channel/backup_poller.h"
 #include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/iomgr/port.h"
 #include "src/proto/grpc/health/v1/health.grpc.pb.h"
 #include "test/cpp/util/string_ref_helper.h"
 #include "test/cpp/util/test_credentials_provider.h"
 
+#ifdef GRPC_POSIX_SOCKET
+#include "src/core/lib/iomgr/ev_posix.h"
+#endif  // GRPC_POSIX_SOCKET
+
 #include <gtest/gtest.h>
 
 using grpc::testing::EchoRequest;
@@ -294,9 +298,9 @@ class AsyncEnd2endTest : public ::testing::TestWithParam<TestScenario> {
     auto channel_creds = GetCredentialsProvider()->GetChannelCredentials(
         GetParam().credentials_type, &args);
     std::shared_ptr<Channel> channel =
-        !(GetParam().inproc)
-            ? CreateCustomChannel(server_address_.str(), channel_creds, args)
-            : server_->InProcessChannel(args);
+        !(GetParam().inproc) ? ::grpc::CreateCustomChannel(
+                                   server_address_.str(), channel_creds, args)
+                             : server_->InProcessChannel(args);
     stub_ = grpc::testing::EchoTestService::NewStub(channel);
   }
 
@@ -358,13 +362,14 @@ TEST_P(AsyncEnd2endTest, ReconnectChannel) {
     return;
   }
   int poller_slowdown_factor = 1;
+#ifdef GRPC_POSIX_SOCKET
   // It needs 2 pollset_works to reconnect the channel with polling engine
   // "poll"
-  char* s = gpr_getenv("GRPC_POLL_STRATEGY");
-  if (s != nullptr && 0 == strcmp(s, "poll")) {
+  grpc_core::UniquePtr<char> poller = GPR_GLOBAL_CONFIG_GET(grpc_poll_strategy);
+  if (0 == strcmp(poller.get(), "poll")) {
     poller_slowdown_factor = 2;
   }
-  gpr_free(s);
+#endif  // GRPC_POSIX_SOCKET
   ResetStub();
   SendRpc(1);
   server_->Shutdown();
@@ -1255,9 +1260,9 @@ TEST_P(AsyncEnd2endTest, UnimplementedRpc) {
   const auto& channel_creds = GetCredentialsProvider()->GetChannelCredentials(
       GetParam().credentials_type, &args);
   std::shared_ptr<Channel> channel =
-      !(GetParam().inproc)
-          ? CreateCustomChannel(server_address_.str(), channel_creds, args)
-          : server_->InProcessChannel(args);
+      !(GetParam().inproc) ? ::grpc::CreateCustomChannel(server_address_.str(),
+                                                         channel_creds, args)
+                           : server_->InProcessChannel(args);
   std::unique_ptr<grpc::testing::UnimplementedEchoService::Stub> stub;
   stub = grpc::testing::UnimplementedEchoService::NewStub(channel);
   EchoRequest send_request;
@@ -1883,7 +1888,7 @@ INSTANTIATE_TEST_CASE_P(AsyncEnd2endServerTryCancel,
 int main(int argc, char** argv) {
   // Change the backup poll interval from 5s to 100ms to speed up the
   // ReconnectChannel test
-  gpr_setenv("GRPC_CLIENT_CHANNEL_BACKUP_POLL_INTERVAL_MS", "100");
+  GPR_GLOBAL_CONFIG_SET(grpc_client_channel_backup_poll_interval_ms, 100);
   grpc::testing::TestEnvironment env(argc, argv);
   ::testing::InitGoogleTest(&argc, argv);
   int ret = RUN_ALL_TESTS();
index a6ed7c6..59cf98f 100644 (file)
@@ -45,6 +45,7 @@
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
 #include "test/cpp/end2end/test_service_impl.h"
+#include "test/cpp/util/test_credentials_provider.h"
 
 #ifdef GRPC_CFSTREAM
 using grpc::ClientAsyncResponseReader;
@@ -57,13 +58,19 @@ namespace grpc {
 namespace testing {
 namespace {
 
-class CFStreamTest : public ::testing::Test {
+struct TestScenario {
+  TestScenario(const grpc::string& creds_type, const grpc::string& content)
+      : credentials_type(creds_type), message_content(content) {}
+  const grpc::string credentials_type;
+  const grpc::string message_content;
+};
+
+class CFStreamTest : public ::testing::TestWithParam<TestScenario> {
  protected:
   CFStreamTest()
       : server_host_("grpctest"),
         interface_("lo0"),
-        ipv4_address_("127.0.0.2"),
-        kRequestMessage_("🖖") {}
+        ipv4_address_("10.0.0.1") {}
 
   void DNSUp() {
     std::ostringstream cmd;
@@ -118,7 +125,7 @@ class CFStreamTest : public ::testing::Test {
 
   void StartServer() {
     port_ = grpc_pick_unused_port_or_die();
-    server_.reset(new ServerData(port_));
+    server_.reset(new ServerData(port_, GetParam().credentials_type));
     server_->Start(server_host_);
   }
   void StopServer() { server_->Shutdown(); }
@@ -131,8 +138,10 @@ class CFStreamTest : public ::testing::Test {
   std::shared_ptr<Channel> BuildChannel() {
     std::ostringstream server_address;
     server_address << server_host_ << ":" << port_;
-    return CreateCustomChannel(
-        server_address.str(), InsecureChannelCredentials(), ChannelArguments());
+    ChannelArguments args;
+    auto channel_creds = GetCredentialsProvider()->GetChannelCredentials(
+        GetParam().credentials_type, &args);
+    return CreateCustomChannel(server_address.str(), channel_creds, args);
   }
 
   void SendRpc(
@@ -140,11 +149,12 @@ class CFStreamTest : public ::testing::Test {
       bool expect_success = false) {
     auto response = std::unique_ptr<EchoResponse>(new EchoResponse());
     EchoRequest request;
-    request.set_message(kRequestMessage_);
+    auto& msg = GetParam().message_content;
+    request.set_message(msg);
     ClientContext context;
     Status status = stub->Echo(&context, request, response.get());
     if (status.ok()) {
-      gpr_log(GPR_DEBUG, "RPC returned %s\n", response->message().c_str());
+      EXPECT_EQ(msg, response->message());
     } else {
       gpr_log(GPR_DEBUG, "RPC failed: %s", status.error_message().c_str());
     }
@@ -156,9 +166,7 @@ class CFStreamTest : public ::testing::Test {
       const std::unique_ptr<grpc::testing::EchoTestService::Stub>& stub,
       RequestParams param = RequestParams()) {
     EchoRequest request;
-    auto msg = std::to_string(ctr.load());
-    request.set_message(msg);
-    ctr++;
+    request.set_message(GetParam().message_content);
     *request.mutable_param() = std::move(param);
     AsyncClientCall* call = new AsyncClientCall;
 
@@ -166,7 +174,6 @@ class CFStreamTest : public ::testing::Test {
         stub->PrepareAsyncEcho(&call->context, request, &cq_);
 
     call->response_reader->StartCall();
-    gpr_log(GPR_DEBUG, "Sending request: %s", msg.c_str());
     call->response_reader->Finish(&call->reply, &call->status, (void*)call);
   }
 
@@ -206,12 +213,14 @@ class CFStreamTest : public ::testing::Test {
  private:
   struct ServerData {
     int port_;
+    const grpc::string creds_;
     std::unique_ptr<Server> server_;
     TestServiceImpl service_;
     std::unique_ptr<std::thread> thread_;
     bool server_ready_ = false;
 
-    explicit ServerData(int port) { port_ = port; }
+    ServerData(int port, const grpc::string& creds)
+        : port_(port), creds_(creds) {}
 
     void Start(const grpc::string& server_host) {
       gpr_log(GPR_INFO, "starting server on port %d", port_);
@@ -230,8 +239,9 @@ class CFStreamTest : public ::testing::Test {
       std::ostringstream server_address;
       server_address << server_host << ":" << port_;
       ServerBuilder builder;
-      builder.AddListeningPort(server_address.str(),
-                               InsecureServerCredentials());
+      auto server_creds =
+          GetCredentialsProvider()->GetServerCredentials(creds_);
+      builder.AddListeningPort(server_address.str(), server_creds);
       builder.RegisterService(&service_);
       server_ = builder.BuildAndStart();
       std::lock_guard<std::mutex> lock(*mu);
@@ -251,13 +261,44 @@ class CFStreamTest : public ::testing::Test {
   const grpc::string ipv4_address_;
   std::unique_ptr<ServerData> server_;
   int port_;
-  const grpc::string kRequestMessage_;
-  std::atomic_int ctr{0};
 };
 
+std::vector<TestScenario> CreateTestScenarios() {
+  std::vector<TestScenario> scenarios;
+  std::vector<grpc::string> credentials_types;
+  std::vector<grpc::string> messages;
+
+  credentials_types.push_back(kInsecureCredentialsType);
+  auto sec_list = GetCredentialsProvider()->GetSecureCredentialsTypeList();
+  for (auto sec = sec_list.begin(); sec != sec_list.end(); sec++) {
+    credentials_types.push_back(*sec);
+  }
+
+  messages.push_back("🖖");
+  for (size_t k = 1; k < GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH / 1024; k *= 32) {
+    grpc::string big_msg;
+    for (size_t i = 0; i < k * 1024; ++i) {
+      char c = 'a' + (i % 26);
+      big_msg += c;
+    }
+    messages.push_back(big_msg);
+  }
+  for (auto cred = credentials_types.begin(); cred != credentials_types.end();
+       ++cred) {
+    for (auto msg = messages.begin(); msg != messages.end(); msg++) {
+      scenarios.emplace_back(*cred, *msg);
+    }
+  }
+
+  return scenarios;
+}
+
+INSTANTIATE_TEST_CASE_P(CFStreamTest, CFStreamTest,
+                        ::testing::ValuesIn(CreateTestScenarios()));
+
 // gRPC should automatically detech network flaps (without enabling keepalives)
 //  when CFStream is enabled
-TEST_F(CFStreamTest, NetworkTransition) {
+TEST_P(CFStreamTest, NetworkTransition) {
   auto channel = BuildChannel();
   auto stub = BuildStub(channel);
   // Channel should be in READY state after we send an RPC
@@ -293,7 +334,7 @@ TEST_F(CFStreamTest, NetworkTransition) {
 }
 
 // Network flaps while RPCs are in flight
-TEST_F(CFStreamTest, NetworkFlapRpcsInFlight) {
+TEST_P(CFStreamTest, NetworkFlapRpcsInFlight) {
   auto channel = BuildChannel();
   auto stub = BuildStub(channel);
   std::atomic_int rpcs_sent{0};
@@ -318,9 +359,7 @@ TEST_F(CFStreamTest, NetworkFlapRpcsInFlight) {
       ++total_completions;
       GPR_ASSERT(ok);
       AsyncClientCall* call = static_cast<AsyncClientCall*>(got_tag);
-      if (call->status.ok()) {
-        gpr_log(GPR_DEBUG, "RPC response: %s", call->reply.message().c_str());
-      } else {
+      if (!call->status.ok()) {
         gpr_log(GPR_DEBUG, "RPC failed with error: %s",
                 call->status.error_message().c_str());
         // Bring network up when RPCs start failing
@@ -347,7 +386,7 @@ TEST_F(CFStreamTest, NetworkFlapRpcsInFlight) {
 
 // Send a bunch of RPCs, some of which are expected to fail.
 // We should get back a response for all RPCs
-TEST_F(CFStreamTest, ConcurrentRpc) {
+TEST_P(CFStreamTest, ConcurrentRpc) {
   auto channel = BuildChannel();
   auto stub = BuildStub(channel);
   std::atomic_int rpcs_sent{0};
@@ -361,9 +400,7 @@ TEST_F(CFStreamTest, ConcurrentRpc) {
       ++total_completions;
       GPR_ASSERT(ok);
       AsyncClientCall* call = static_cast<AsyncClientCall*>(got_tag);
-      if (call->status.ok()) {
-        gpr_log(GPR_DEBUG, "RPC response: %s", call->reply.message().c_str());
-      } else {
+      if (!call->status.ok()) {
         gpr_log(GPR_DEBUG, "RPC failed: %s",
                 call->status.error_message().c_str());
         // Bring network up when RPCs start failing
index fe52a64..26ef59f 100644 (file)
@@ -145,7 +145,7 @@ class ChannelzServerTest : public ::testing::Test {
       ChannelArguments args;
       args.SetInt(GRPC_ARG_ENABLE_CHANNELZ, 1);
       args.SetInt(GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE, 1024);
-      std::shared_ptr<Channel> channel_to_backend = CreateCustomChannel(
+      std::shared_ptr<Channel> channel_to_backend = ::grpc::CreateCustomChannel(
           backend_server_address, InsecureChannelCredentials(), args);
       proxy_service_.AddChannelToBackend(channel_to_backend);
     }
@@ -157,7 +157,7 @@ class ChannelzServerTest : public ::testing::Test {
     // disable channelz. We only want to focus on proxy to backend outbound.
     args.SetInt(GRPC_ARG_ENABLE_CHANNELZ, 0);
     std::shared_ptr<Channel> channel =
-        CreateCustomChannel(target, InsecureChannelCredentials(), args);
+        ::grpc::CreateCustomChannel(target, InsecureChannelCredentials(), args);
     channelz_stub_ = grpc::channelz::v1::Channelz::NewStub(channel);
     echo_stub_ = grpc::testing::EchoTestService::NewStub(channel);
   }
@@ -171,7 +171,7 @@ class ChannelzServerTest : public ::testing::Test {
     // This ensures that gRPC will not do connection sharing.
     args.SetInt("salt", salt++);
     std::shared_ptr<Channel> channel =
-        CreateCustomChannel(target, InsecureChannelCredentials(), args);
+        ::grpc::CreateCustomChannel(target, InsecureChannelCredentials(), args);
     return grpc::testing::EchoTestService::NewStub(channel);
   }
 
index 821fcc2..ede24f3 100644 (file)
@@ -142,8 +142,8 @@ class ClientCallbackEnd2endTest
     switch (GetParam().protocol) {
       case Protocol::TCP:
         if (!GetParam().use_interceptors) {
-          channel_ =
-              CreateCustomChannel(server_address_.str(), channel_creds, args);
+          channel_ = ::grpc::CreateCustomChannel(server_address_.str(),
+                                                 channel_creds, args);
         } else {
           channel_ = CreateCustomChannelWithInterceptors(
               server_address_.str(), channel_creds, args,
@@ -536,9 +536,9 @@ TEST_P(ClientCallbackEnd2endTest, RequestEchoServerCancel) {
 struct ClientCancelInfo {
   bool cancel{false};
   int ops_before_cancel;
+
   ClientCancelInfo() : cancel{false} {}
-  // Allow the single-op version to be non-explicit for ease of use
-  ClientCancelInfo(int ops) : cancel{true}, ops_before_cancel{ops} {}
+  explicit ClientCancelInfo(int ops) : cancel{true}, ops_before_cancel{ops} {}
 };
 
 class WriteClient : public grpc::experimental::ClientWriteReactor<EchoRequest> {
@@ -651,7 +651,7 @@ TEST_P(ClientCallbackEnd2endTest, RequestStream) {
 TEST_P(ClientCallbackEnd2endTest, ClientCancelsRequestStream) {
   MAYBE_SKIP_TEST;
   ResetStub();
-  WriteClient test{stub_.get(), DO_NOT_CANCEL, 3, {2}};
+  WriteClient test{stub_.get(), DO_NOT_CANCEL, 3, ClientCancelInfo{2}};
   test.Await();
   // Make sure that the server interceptors got the cancel
   if (GetParam().use_interceptors) {
@@ -696,6 +696,65 @@ TEST_P(ClientCallbackEnd2endTest, RequestStreamServerCancelAfterReads) {
   }
 }
 
+TEST_P(ClientCallbackEnd2endTest, UnaryReactor) {
+  MAYBE_SKIP_TEST;
+  ResetStub();
+  class UnaryClient : public grpc::experimental::ClientUnaryReactor {
+   public:
+    UnaryClient(grpc::testing::EchoTestService::Stub* stub) {
+      cli_ctx_.AddMetadata("key1", "val1");
+      cli_ctx_.AddMetadata("key2", "val2");
+      request_.mutable_param()->set_echo_metadata_initially(true);
+      request_.set_message("Hello metadata");
+      stub->experimental_async()->Echo(&cli_ctx_, &request_, &response_, this);
+      StartCall();
+    }
+    void OnReadInitialMetadataDone(bool ok) override {
+      EXPECT_TRUE(ok);
+      EXPECT_EQ(1u, cli_ctx_.GetServerInitialMetadata().count("key1"));
+      EXPECT_EQ(
+          "val1",
+          ToString(cli_ctx_.GetServerInitialMetadata().find("key1")->second));
+      EXPECT_EQ(1u, cli_ctx_.GetServerInitialMetadata().count("key2"));
+      EXPECT_EQ(
+          "val2",
+          ToString(cli_ctx_.GetServerInitialMetadata().find("key2")->second));
+      initial_metadata_done_ = true;
+    }
+    void OnDone(const Status& s) override {
+      EXPECT_TRUE(initial_metadata_done_);
+      EXPECT_EQ(0u, cli_ctx_.GetServerTrailingMetadata().size());
+      EXPECT_TRUE(s.ok());
+      EXPECT_EQ(request_.message(), response_.message());
+      std::unique_lock<std::mutex> l(mu_);
+      done_ = true;
+      cv_.notify_one();
+    }
+    void Await() {
+      std::unique_lock<std::mutex> l(mu_);
+      while (!done_) {
+        cv_.wait(l);
+      }
+    }
+
+   private:
+    EchoRequest request_;
+    EchoResponse response_;
+    ClientContext cli_ctx_;
+    std::mutex mu_;
+    std::condition_variable cv_;
+    bool done_{false};
+    bool initial_metadata_done_{false};
+  };
+
+  UnaryClient test{stub_.get()};
+  test.Await();
+  // Make sure that the server interceptors were not notified of a cancel
+  if (GetParam().use_interceptors) {
+    EXPECT_EQ(0, DummyInterceptor::GetNumTimesCancel());
+  }
+}
+
 class ReadClient : public grpc::experimental::ClientReadReactor<EchoResponse> {
  public:
   ReadClient(grpc::testing::EchoTestService::Stub* stub,
@@ -810,7 +869,7 @@ TEST_P(ClientCallbackEnd2endTest, ResponseStream) {
 TEST_P(ClientCallbackEnd2endTest, ClientCancelsResponseStream) {
   MAYBE_SKIP_TEST;
   ResetStub();
-  ReadClient test{stub_.get(), DO_NOT_CANCEL, 2};
+  ReadClient test{stub_.get(), DO_NOT_CANCEL, ClientCancelInfo{2}};
   test.Await();
   // Because cancel in this case races with server finish, we can't be sure that
   // server interceptors even see cancellation
@@ -993,7 +1052,7 @@ TEST_P(ClientCallbackEnd2endTest, ClientCancelsBidiStream) {
   MAYBE_SKIP_TEST;
   ResetStub();
   BidiClient test{stub_.get(), DO_NOT_CANCEL,
-                  kServerDefaultResponseStreamsToSend, 2};
+                  kServerDefaultResponseStreamsToSend, ClientCancelInfo{2}};
   test.Await();
   // Make sure that the server interceptors were notified of a cancel
   if (GetParam().use_interceptors) {
@@ -1094,7 +1153,8 @@ TEST_P(ClientCallbackEnd2endTest, UnimplementedRpc) {
       GetParam().credentials_type, &args);
   std::shared_ptr<Channel> channel =
       (GetParam().protocol == Protocol::TCP)
-          ? CreateCustomChannel(server_address_.str(), channel_creds, args)
+          ? ::grpc::CreateCustomChannel(server_address_.str(), channel_creds,
+                                        args)
           : server_->InProcessChannel(args);
   std::unique_ptr<grpc::testing::UnimplementedEchoService::Stub> stub;
   stub = grpc::testing::UnimplementedEchoService::NewStub(channel);
index 992f3c4..2d5c1b4 100644 (file)
@@ -60,7 +60,7 @@ class CrashTest : public ::testing::Test {
     }));
     GPR_ASSERT(server_);
     return grpc::testing::EchoTestService::NewStub(
-        CreateChannel(addr, InsecureChannelCredentials()));
+        grpc::CreateChannel(addr, InsecureChannelCredentials()));
   }
 
   void KillServer() { server_.reset(); }
index cb4afd7..d92f9c5 100644 (file)
@@ -27,6 +27,7 @@
 #include <grpcpp/server_context.h>
 
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "test/cpp/util/test_config.h"
 
 DEFINE_string(address, "", "Address to bind to");
 
@@ -72,7 +73,7 @@ void RunServer() {
 }  // namespace grpc
 
 int main(int argc, char** argv) {
-  ParseCommandLineFlags(&argc, &argv, true);
+  grpc::testing::InitTest(&argc, &argv, true);
   grpc::testing::RunServer();
 
   return 0;
index 421b31a..f1aed09 100644 (file)
@@ -611,6 +611,18 @@ TEST_F(ClientInterceptorsEnd2endTest, ClientInterceptorLoggingTest) {
   EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
 }
 
+TEST_F(ClientInterceptorsEnd2endTest,
+       LameChannelClientInterceptorHijackingTest) {
+  ChannelArguments args;
+  std::vector<std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+      creators;
+  creators.push_back(std::unique_ptr<HijackingInterceptorFactory>(
+      new HijackingInterceptorFactory()));
+  auto channel = experimental::CreateCustomChannelWithInterceptors(
+      server_address_, nullptr, args, std::move(creators));
+  MakeCall(channel);
+}
+
 TEST_F(ClientInterceptorsEnd2endTest, ClientInterceptorHijackingTest) {
   ChannelArguments args;
   DummyInterceptor::Reset();
index 77f9db9..766c38a 100644 (file)
 #include <grpcpp/client_context.h>
 #include <grpcpp/create_channel.h>
 #include <grpcpp/health_check_service_interface.h>
+#include <grpcpp/impl/codegen/sync.h>
 #include <grpcpp/server.h>
 #include <grpcpp/server_builder.h>
 
+#include "src/core/ext/filters/client_channel/backup_poller.h"
 #include "src/core/ext/filters/client_channel/global_subchannel_pool.h"
 #include "src/core/ext/filters/client_channel/parse_address.h"
 #include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"
 #include "src/core/ext/filters/client_channel/server_address.h"
 #include "src/core/lib/backoff/backoff.h"
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gprpp/debug_location.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
 #include "src/core/lib/iomgr/tcp_client.h"
@@ -98,7 +99,7 @@ class MyTestServiceImpl : public TestServiceImpl {
   Status Echo(ServerContext* context, const EchoRequest* request,
               EchoResponse* response) override {
     {
-      std::unique_lock<std::mutex> lock(mu_);
+      grpc::internal::MutexLock lock(&mu_);
       ++request_count_;
     }
     AddClient(context->peer());
@@ -106,29 +107,29 @@ class MyTestServiceImpl : public TestServiceImpl {
   }
 
   int request_count() {
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     return request_count_;
   }
 
   void ResetCounters() {
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     request_count_ = 0;
   }
 
   std::set<grpc::string> clients() {
-    std::unique_lock<std::mutex> lock(clients_mu_);
+    grpc::internal::MutexLock lock(&clients_mu_);
     return clients_;
   }
 
  private:
   void AddClient(const grpc::string& client) {
-    std::unique_lock<std::mutex> lock(clients_mu_);
+    grpc::internal::MutexLock lock(&clients_mu_);
     clients_.insert(client);
   }
 
-  std::mutex mu_;
+  grpc::internal::Mutex mu_;
   int request_count_;
-  std::mutex clients_mu_;
+  grpc::internal::Mutex clients_mu_;
   std::set<grpc::string> clients_;
 };
 
@@ -141,7 +142,7 @@ class ClientLbEnd2endTest : public ::testing::Test {
             grpc_fake_transport_security_credentials_create())) {
     // Make the backup poller poll very frequently in order to pick up
     // updates from all the subchannels's FDs.
-    gpr_setenv("GRPC_CLIENT_CHANNEL_BACKUP_POLL_INTERVAL_MS", "1");
+    GPR_GLOBAL_CONFIG_SET(grpc_client_channel_backup_poll_interval_ms, 1);
   }
 
   void SetUp() override {
@@ -236,7 +237,7 @@ class ClientLbEnd2endTest : public ::testing::Test {
     }  // else, default to pick first
     args.SetPointer(GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR,
                     response_generator_.get());
-    return CreateCustomChannel("fake:///", creds_, args);
+    return ::grpc::CreateCustomChannel("fake:///", creds_, args);
   }
 
   bool SendRpc(
@@ -293,18 +294,18 @@ class ClientLbEnd2endTest : public ::testing::Test {
     void Start(const grpc::string& server_host) {
       gpr_log(GPR_INFO, "starting server on port %d", port_);
       started_ = true;
-      std::mutex mu;
-      std::unique_lock<std::mutex> lock(mu);
-      std::condition_variable cond;
+      grpc::internal::Mutex mu;
+      grpc::internal::MutexLock lock(&mu);
+      grpc::internal::CondVar cond;
       thread_.reset(new std::thread(
           std::bind(&ServerData::Serve, this, server_host, &mu, &cond)));
-      cond.wait(lock, [this] { return server_ready_; });
+      cond.WaitUntil(&mu, [this] { return server_ready_; });
       server_ready_ = false;
       gpr_log(GPR_INFO, "server startup complete");
     }
 
-    void Serve(const grpc::string& server_host, std::mutex* mu,
-               std::condition_variable* cond) {
+    void Serve(const grpc::string& server_host, grpc::internal::Mutex* mu,
+               grpc::internal::CondVar* cond) {
       std::ostringstream server_address;
       server_address << server_host << ":" << port_;
       ServerBuilder builder;
@@ -313,9 +314,9 @@ class ClientLbEnd2endTest : public ::testing::Test {
       builder.AddListeningPort(server_address.str(), std::move(creds));
       builder.RegisterService(&service_);
       server_ = builder.BuildAndStart();
-      std::lock_guard<std::mutex> lock(*mu);
+      grpc::internal::MutexLock lock(mu);
       server_ready_ = true;
-      cond->notify_one();
+      cond->Signal();
     }
 
     void Shutdown() {
@@ -1374,7 +1375,7 @@ class ClientLbInterceptTrailingMetadataTest : public ClientLbEnd2endTest {
   void TearDown() override { ClientLbEnd2endTest::TearDown(); }
 
   int trailers_intercepted() {
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     return trailers_intercepted_;
   }
 
@@ -1382,11 +1383,11 @@ class ClientLbInterceptTrailingMetadataTest : public ClientLbEnd2endTest {
   static void ReportTrailerIntercepted(void* arg) {
     ClientLbInterceptTrailingMetadataTest* self =
         static_cast<ClientLbInterceptTrailingMetadataTest*>(arg);
-    std::unique_lock<std::mutex> lock(self->mu_);
+    grpc::internal::MutexLock lock(&self->mu_);
     self->trailers_intercepted_++;
   }
 
-  std::mutex mu_;
+  grpc::internal::Mutex mu_;
   int trailers_intercepted_ = 0;
 };
 
index 40023c7..8fae2da 100644 (file)
@@ -34,7 +34,7 @@
 #include <grpcpp/server_builder.h>
 #include <grpcpp/server_context.h>
 
-#include "src/core/lib/gpr/env.h"
+#include "src/core/ext/filters/client_channel/backup_poller.h"
 #include "src/core/lib/iomgr/iomgr.h"
 #include "src/core/lib/security/credentials/credentials.h"
 #include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
 #include "test/cpp/util/string_ref_helper.h"
 #include "test/cpp/util/test_credentials_provider.h"
 
+#ifdef GRPC_POSIX_SOCKET
+#include "src/core/lib/iomgr/ev_posix.h"
+#endif  // GRPC_POSIX_SOCKET
+
 #include <gtest/gtest.h>
 
 using grpc::testing::EchoRequest;
@@ -129,7 +133,7 @@ class TestAuthMetadataProcessor : public AuthMetadataProcessor {
   TestAuthMetadataProcessor(bool is_blocking) : is_blocking_(is_blocking) {}
 
   std::shared_ptr<CallCredentials> GetCompatibleClientCreds() {
-    return MetadataCredentialsFromPlugin(
+    return grpc::MetadataCredentialsFromPlugin(
         std::unique_ptr<MetadataCredentialsPlugin>(
             new TestMetadataCredentialsPlugin(
                 TestMetadataCredentialsPlugin::kGoodMetadataKey, kGoodGuy,
@@ -137,7 +141,7 @@ class TestAuthMetadataProcessor : public AuthMetadataProcessor {
   }
 
   std::shared_ptr<CallCredentials> GetIncompatibleClientCreds() {
-    return MetadataCredentialsFromPlugin(
+    return grpc::MetadataCredentialsFromPlugin(
         std::unique_ptr<MetadataCredentialsPlugin>(
             new TestMetadataCredentialsPlugin(
                 TestMetadataCredentialsPlugin::kGoodMetadataKey, "Mr Hyde",
@@ -340,8 +344,8 @@ class End2endTest : public ::testing::TestWithParam<TestScenario> {
 
     if (!GetParam().inproc) {
       if (!GetParam().use_interceptors) {
-        channel_ =
-            CreateCustomChannel(server_address_.str(), channel_creds, args);
+        channel_ = ::grpc::CreateCustomChannel(server_address_.str(),
+                                               channel_creds, args);
       } else {
         channel_ = CreateCustomChannelWithInterceptors(
             server_address_.str(), channel_creds, args,
@@ -374,7 +378,8 @@ class End2endTest : public ::testing::TestWithParam<TestScenario> {
 
       proxy_server_ = builder.BuildAndStart();
 
-      channel_ = CreateChannel(proxyaddr.str(), InsecureChannelCredentials());
+      channel_ =
+          grpc::CreateChannel(proxyaddr.str(), InsecureChannelCredentials());
     }
 
     stub_ = grpc::testing::EchoTestService::NewStub(channel_);
@@ -807,11 +812,12 @@ TEST_P(End2endTest, ReconnectChannel) {
   int poller_slowdown_factor = 1;
   // It needs 2 pollset_works to reconnect the channel with polling engine
   // "poll"
-  char* s = gpr_getenv("GRPC_POLL_STRATEGY");
-  if (s != nullptr && 0 == strcmp(s, "poll")) {
+#ifdef GRPC_POSIX_SOCKET
+  grpc_core::UniquePtr<char> poller = GPR_GLOBAL_CONFIG_GET(grpc_poll_strategy);
+  if (0 == strcmp(poller.get(), "poll")) {
     poller_slowdown_factor = 2;
   }
-  gpr_free(s);
+#endif  // GRPC_POSIX_SOCKET
   ResetStub();
   SendRpc(stub_.get(), 1, false);
   RestartServer(std::shared_ptr<AuthMetadataProcessor>());
@@ -1277,7 +1283,7 @@ TEST_P(End2endTest, ChannelStateTimeout) {
   server_address << "127.0.0.1:" << port;
   // Channel to non-existing server
   auto channel =
-      CreateChannel(server_address.str(), InsecureChannelCredentials());
+      grpc::CreateChannel(server_address.str(), InsecureChannelCredentials());
   // Start IDLE
   EXPECT_EQ(GRPC_CHANNEL_IDLE, channel->GetState(true));
 
@@ -1420,18 +1426,18 @@ TEST_P(End2endTest, DelayedRpcLateCanceledUsingCancelCallback) {
   EchoResponse response;
   request.set_message("Hello");
   request.mutable_param()->set_skip_cancelled_check(true);
-  // Let server sleep for 80 ms first to give the cancellation a chance.
-  // This is split into 40 ms to start the cancel and 40 ms extra time for
+  // Let server sleep for 200 ms first to give the cancellation a chance.
+  // This is split into 100 ms to start the cancel and 100 ms extra time for
   // it to make it to the server, to make it highly probable that the server
   // RPC would have already started by the time the cancellation is sent
   // and the server-side gets enough time to react to it.
-  request.mutable_param()->set_server_sleep_us(80 * 1000);
+  request.mutable_param()->set_server_sleep_us(200000);
 
   std::thread echo_thread{[this, &context, &request, &response] {
     Status s = stub_->Echo(&context, request, &response);
     EXPECT_EQ(StatusCode::CANCELLED, s.error_code());
   }};
-  std::this_thread::sleep_for(std::chrono::microseconds(40000));
+  std::this_thread::sleep_for(std::chrono::microseconds(100000));
   context.TryCancel();
   echo_thread.join();
 }
@@ -1825,8 +1831,8 @@ TEST_P(SecureEnd2endTest, AuthMetadataPluginKeyFailure) {
   EchoRequest request;
   EchoResponse response;
   ClientContext context;
-  context.set_credentials(
-      MetadataCredentialsFromPlugin(std::unique_ptr<MetadataCredentialsPlugin>(
+  context.set_credentials(grpc::MetadataCredentialsFromPlugin(
+      std::unique_ptr<MetadataCredentialsPlugin>(
           new TestMetadataCredentialsPlugin(
               TestMetadataCredentialsPlugin::kBadMetadataKey,
               "Does not matter, will fail the key is invalid.", false, true))));
@@ -1843,8 +1849,8 @@ TEST_P(SecureEnd2endTest, AuthMetadataPluginValueFailure) {
   EchoRequest request;
   EchoResponse response;
   ClientContext context;
-  context.set_credentials(
-      MetadataCredentialsFromPlugin(std::unique_ptr<MetadataCredentialsPlugin>(
+  context.set_credentials(grpc::MetadataCredentialsFromPlugin(
+      std::unique_ptr<MetadataCredentialsPlugin>(
           new TestMetadataCredentialsPlugin(
               TestMetadataCredentialsPlugin::kGoodMetadataKey,
               "With illegal \n value.", false, true))));
@@ -1861,8 +1867,8 @@ TEST_P(SecureEnd2endTest, NonBlockingAuthMetadataPluginFailure) {
   EchoRequest request;
   EchoResponse response;
   ClientContext context;
-  context.set_credentials(
-      MetadataCredentialsFromPlugin(std::unique_ptr<MetadataCredentialsPlugin>(
+  context.set_credentials(grpc::MetadataCredentialsFromPlugin(
+      std::unique_ptr<MetadataCredentialsPlugin>(
           new TestMetadataCredentialsPlugin(
               TestMetadataCredentialsPlugin::kGoodMetadataKey,
               "Does not matter, will fail anyway (see 3rd param)", false,
@@ -1925,8 +1931,8 @@ TEST_P(SecureEnd2endTest, BlockingAuthMetadataPluginFailure) {
   EchoRequest request;
   EchoResponse response;
   ClientContext context;
-  context.set_credentials(
-      MetadataCredentialsFromPlugin(std::unique_ptr<MetadataCredentialsPlugin>(
+  context.set_credentials(grpc::MetadataCredentialsFromPlugin(
+      std::unique_ptr<MetadataCredentialsPlugin>(
           new TestMetadataCredentialsPlugin(
               TestMetadataCredentialsPlugin::kGoodMetadataKey,
               "Does not matter, will fail anyway (see 3rd param)", true,
@@ -1952,13 +1958,15 @@ TEST_P(SecureEnd2endTest, CompositeCallCreds) {
   const char kMetadataVal1[] = "call-creds-val1";
   const char kMetadataVal2[] = "call-creds-val2";
 
-  context.set_credentials(CompositeCallCredentials(
-      MetadataCredentialsFromPlugin(std::unique_ptr<MetadataCredentialsPlugin>(
-          new TestMetadataCredentialsPlugin(kMetadataKey1, kMetadataVal1, true,
-                                            true))),
-      MetadataCredentialsFromPlugin(std::unique_ptr<MetadataCredentialsPlugin>(
-          new TestMetadataCredentialsPlugin(kMetadataKey2, kMetadataVal2, true,
-                                            true)))));
+  context.set_credentials(grpc::CompositeCallCredentials(
+      grpc::MetadataCredentialsFromPlugin(
+          std::unique_ptr<MetadataCredentialsPlugin>(
+              new TestMetadataCredentialsPlugin(kMetadataKey1, kMetadataVal1,
+                                                true, true))),
+      grpc::MetadataCredentialsFromPlugin(
+          std::unique_ptr<MetadataCredentialsPlugin>(
+              new TestMetadataCredentialsPlugin(kMetadataKey2, kMetadataVal2,
+                                                true, true)))));
   request.set_message("Hello");
   request.mutable_param()->set_echo_metadata(true);
 
@@ -2104,7 +2112,7 @@ INSTANTIATE_TEST_CASE_P(
 }  // namespace grpc
 
 int main(int argc, char** argv) {
-  gpr_setenv("GRPC_CLIENT_CHANNEL_BACKUP_POLL_INTERVAL_MS", "200");
+  GPR_GLOBAL_CONFIG_SET(grpc_client_channel_backup_poll_interval_ms, 200);
   grpc::testing::TestEnvironment env(argc, argv);
   ::testing::InitGoogleTest(&argc, argv);
   return RUN_ALL_TESTS();
index ad67402..a4c981a 100644 (file)
@@ -146,8 +146,8 @@ class FilterEnd2endTest : public ::testing::Test {
   }
 
   void ResetStub() {
-    std::shared_ptr<Channel> channel =
-        CreateChannel(server_address_.str(), InsecureChannelCredentials());
+    std::shared_ptr<Channel> channel = grpc::CreateChannel(
+        server_address_.str(), InsecureChannelCredentials());
     generic_stub_.reset(new GenericStub(channel));
     ResetConnectionCounter();
     ResetCallCounter();
index 8c4b3cf..c280731 100644 (file)
@@ -90,8 +90,8 @@ class GenericEnd2endTest : public ::testing::Test {
   }
 
   void ResetStub() {
-    std::shared_ptr<Channel> channel =
-        CreateChannel(server_address_.str(), InsecureChannelCredentials());
+    std::shared_ptr<Channel> channel = grpc::CreateChannel(
+        server_address_.str(), InsecureChannelCredentials());
     generic_stub_.reset(new GenericStub(channel));
   }
 
index 7c64323..50b1383 100644 (file)
 #include <grpcpp/channel.h>
 #include <grpcpp/client_context.h>
 #include <grpcpp/create_channel.h>
+#include <grpcpp/impl/codegen/sync.h>
 #include <grpcpp/server.h>
 #include <grpcpp/server_builder.h>
 
+#include "src/core/ext/filters/client_channel/backup_poller.h"
 #include "src/core/ext/filters/client_channel/parse_address.h"
 #include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"
 #include "src/core/ext/filters/client_channel/server_address.h"
 #include "src/core/ext/filters/client_channel/service_config.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
 #include "src/core/lib/iomgr/sockaddr.h"
 #include "src/core/lib/security/credentials/fake/fake_credentials.h"
@@ -85,32 +86,32 @@ template <typename ServiceType>
 class CountedService : public ServiceType {
  public:
   size_t request_count() {
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     return request_count_;
   }
 
   size_t response_count() {
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     return response_count_;
   }
 
   void IncreaseResponseCount() {
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     ++response_count_;
   }
   void IncreaseRequestCount() {
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     ++request_count_;
   }
 
   void ResetCounters() {
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     request_count_ = 0;
     response_count_ = 0;
   }
 
  protected:
-  std::mutex mu_;
+  grpc::internal::Mutex mu_;
 
  private:
   size_t request_count_ = 0;
@@ -148,18 +149,18 @@ class BackendServiceImpl : public BackendService {
   void Shutdown() {}
 
   std::set<grpc::string> clients() {
-    std::unique_lock<std::mutex> lock(clients_mu_);
+    grpc::internal::MutexLock lock(&clients_mu_);
     return clients_;
   }
 
  private:
   void AddClient(const grpc::string& client) {
-    std::unique_lock<std::mutex> lock(clients_mu_);
+    grpc::internal::MutexLock lock(&clients_mu_);
     clients_.insert(client);
   }
 
-  std::mutex mu_;
-  std::mutex clients_mu_;
+  grpc::internal::Mutex mu_;
+  grpc::internal::Mutex clients_mu_;
   std::set<grpc::string> clients_;
 };
 
@@ -208,67 +209,74 @@ class BalancerServiceImpl : public BalancerService {
             client_load_reporting_interval_seconds) {}
 
   Status BalanceLoad(ServerContext* context, Stream* stream) override {
-    // Balancer shouldn't receive the call credentials metadata.
-    EXPECT_EQ(context->client_metadata().find(g_kCallCredsMdKey),
-              context->client_metadata().end());
     gpr_log(GPR_INFO, "LB[%p]: BalanceLoad", this);
-    LoadBalanceRequest request;
-    std::vector<ResponseDelayPair> responses_and_delays;
-
-    if (!stream->Read(&request)) {
-      goto done;
-    }
-    IncreaseRequestCount();
-    gpr_log(GPR_INFO, "LB[%p]: received initial message '%s'", this,
-            request.DebugString().c_str());
-
-    // TODO(juanlishen): Initial response should always be the first response.
-    if (client_load_reporting_interval_seconds_ > 0) {
-      LoadBalanceResponse initial_response;
-      initial_response.mutable_initial_response()
-          ->mutable_client_stats_report_interval()
-          ->set_seconds(client_load_reporting_interval_seconds_);
-      stream->Write(initial_response);
-    }
-
     {
-      std::unique_lock<std::mutex> lock(mu_);
-      responses_and_delays = responses_and_delays_;
-    }
-    for (const auto& response_and_delay : responses_and_delays) {
-      SendResponse(stream, response_and_delay.first, response_and_delay.second);
+      grpc::internal::MutexLock lock(&mu_);
+      if (serverlist_done_) goto done;
     }
     {
-      std::unique_lock<std::mutex> lock(mu_);
-      serverlist_cond_.wait(lock, [this] { return serverlist_done_; });
-    }
+      // Balancer shouldn't receive the call credentials metadata.
+      EXPECT_EQ(context->client_metadata().find(g_kCallCredsMdKey),
+                context->client_metadata().end());
+      LoadBalanceRequest request;
+      std::vector<ResponseDelayPair> responses_and_delays;
+
+      if (!stream->Read(&request)) {
+        goto done;
+      }
+      IncreaseRequestCount();
+      gpr_log(GPR_INFO, "LB[%p]: received initial message '%s'", this,
+              request.DebugString().c_str());
+
+      // TODO(juanlishen): Initial response should always be the first response.
+      if (client_load_reporting_interval_seconds_ > 0) {
+        LoadBalanceResponse initial_response;
+        initial_response.mutable_initial_response()
+            ->mutable_client_stats_report_interval()
+            ->set_seconds(client_load_reporting_interval_seconds_);
+        stream->Write(initial_response);
+      }
+
+      {
+        grpc::internal::MutexLock lock(&mu_);
+        responses_and_delays = responses_and_delays_;
+      }
+      for (const auto& response_and_delay : responses_and_delays) {
+        SendResponse(stream, response_and_delay.first,
+                     response_and_delay.second);
+      }
+      {
+        grpc::internal::MutexLock lock(&mu_);
+        serverlist_cond_.WaitUntil(&mu_, [this] { return serverlist_done_; });
+      }
 
-    if (client_load_reporting_interval_seconds_ > 0) {
-      request.Clear();
-      if (stream->Read(&request)) {
-        gpr_log(GPR_INFO, "LB[%p]: received client load report message '%s'",
-                this, request.DebugString().c_str());
-        GPR_ASSERT(request.has_client_stats());
-        // We need to acquire the lock here in order to prevent the notify_one
-        // below from firing before its corresponding wait is executed.
-        std::lock_guard<std::mutex> lock(mu_);
-        client_stats_.num_calls_started +=
-            request.client_stats().num_calls_started();
-        client_stats_.num_calls_finished +=
-            request.client_stats().num_calls_finished();
-        client_stats_.num_calls_finished_with_client_failed_to_send +=
-            request.client_stats()
-                .num_calls_finished_with_client_failed_to_send();
-        client_stats_.num_calls_finished_known_received +=
-            request.client_stats().num_calls_finished_known_received();
-        for (const auto& drop_token_count :
-             request.client_stats().calls_finished_with_drop()) {
-          client_stats_
-              .drop_token_counts[drop_token_count.load_balance_token()] +=
-              drop_token_count.num_calls();
+      if (client_load_reporting_interval_seconds_ > 0) {
+        request.Clear();
+        if (stream->Read(&request)) {
+          gpr_log(GPR_INFO, "LB[%p]: received client load report message '%s'",
+                  this, request.DebugString().c_str());
+          GPR_ASSERT(request.has_client_stats());
+          // We need to acquire the lock here in order to prevent the notify_one
+          // below from firing before its corresponding wait is executed.
+          grpc::internal::MutexLock lock(&mu_);
+          client_stats_.num_calls_started +=
+              request.client_stats().num_calls_started();
+          client_stats_.num_calls_finished +=
+              request.client_stats().num_calls_finished();
+          client_stats_.num_calls_finished_with_client_failed_to_send +=
+              request.client_stats()
+                  .num_calls_finished_with_client_failed_to_send();
+          client_stats_.num_calls_finished_known_received +=
+              request.client_stats().num_calls_finished_known_received();
+          for (const auto& drop_token_count :
+               request.client_stats().calls_finished_with_drop()) {
+            client_stats_
+                .drop_token_counts[drop_token_count.load_balance_token()] +=
+                drop_token_count.num_calls();
+          }
+          load_report_ready_ = true;
+          load_report_cond_.Signal();
         }
-        load_report_ready_ = true;
-        load_report_cond_.notify_one();
       }
     }
   done:
@@ -277,12 +285,12 @@ class BalancerServiceImpl : public BalancerService {
   }
 
   void add_response(const LoadBalanceResponse& response, int send_after_ms) {
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     responses_and_delays_.push_back(std::make_pair(response, send_after_ms));
   }
 
   void Start() {
-    std::lock_guard<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     serverlist_done_ = false;
     load_report_ready_ = false;
     responses_and_delays_.clear();
@@ -319,17 +327,17 @@ class BalancerServiceImpl : public BalancerService {
   }
 
   const ClientStats& WaitForLoadReport() {
-    std::unique_lock<std::mutex> lock(mu_);
-    load_report_cond_.wait(lock, [this] { return load_report_ready_; });
+    grpc::internal::MutexLock lock(&mu_);
+    load_report_cond_.WaitUntil(&mu_, [this] { return load_report_ready_; });
     load_report_ready_ = false;
     return client_stats_;
   }
 
   void NotifyDoneWithServerlists() {
-    std::lock_guard<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     if (!serverlist_done_) {
       serverlist_done_ = true;
-      serverlist_cond_.notify_all();
+      serverlist_cond_.Broadcast();
     }
   }
 
@@ -348,10 +356,10 @@ class BalancerServiceImpl : public BalancerService {
 
   const int client_load_reporting_interval_seconds_;
   std::vector<ResponseDelayPair> responses_and_delays_;
-  std::mutex mu_;
-  std::condition_variable load_report_cond_;
+  grpc::internal::Mutex mu_;
+  grpc::internal::CondVar load_report_cond_;
   bool load_report_ready_ = false;
-  std::condition_variable serverlist_cond_;
+  grpc::internal::CondVar serverlist_cond_;
   bool serverlist_done_ = false;
   ClientStats client_stats_;
 };
@@ -367,7 +375,7 @@ class GrpclbEnd2endTest : public ::testing::Test {
             client_load_reporting_interval_seconds) {
     // Make the backup poller poll very frequently in order to pick up
     // updates from all the subchannels's FDs.
-    gpr_setenv("GRPC_CLIENT_CHANNEL_BACKUP_POLL_INTERVAL_MS", "1");
+    GPR_GLOBAL_CONFIG_SET(grpc_client_channel_backup_poll_interval_ms, 1);
   }
 
   void SetUp() override {
@@ -426,7 +434,7 @@ class GrpclbEnd2endTest : public ::testing::Test {
             channel_creds, call_creds, nullptr)));
     call_creds->Unref();
     channel_creds->Unref();
-    channel_ = CreateCustomChannel(uri.str(), creds, args);
+    channel_ = ::grpc::CreateCustomChannel(uri.str(), creds, args);
     stub_ = grpc::testing::EchoTestService::NewStub(channel_);
   }
 
@@ -542,8 +550,10 @@ class GrpclbEnd2endTest : public ::testing::Test {
     grpc_core::Resolver::Result result;
     result.addresses = CreateLbAddressesFromAddressDataList(address_data);
     if (service_config_json != nullptr) {
+      grpc_error* error = GRPC_ERROR_NONE;
       result.service_config =
-          grpc_core::ServiceConfig::Create(service_config_json);
+          grpc_core::ServiceConfig::Create(service_config_json, &error);
+      GRPC_ERROR_UNREF(error);
     }
     response_generator_->SetResponse(std::move(result));
   }
@@ -615,22 +625,22 @@ class GrpclbEnd2endTest : public ::testing::Test {
       GPR_ASSERT(!running_);
       running_ = true;
       service_.Start();
-      std::mutex mu;
+      grpc::internal::Mutex mu;
       // We need to acquire the lock here in order to prevent the notify_one
       // by ServerThread::Serve from firing before the wait below is hit.
-      std::unique_lock<std::mutex> lock(mu);
-      std::condition_variable cond;
+      grpc::internal::MutexLock lock(&mu);
+      grpc::internal::CondVar cond;
       thread_.reset(new std::thread(
           std::bind(&ServerThread::Serve, this, server_host, &mu, &cond)));
-      cond.wait(lock);
+      cond.Wait(&mu);
       gpr_log(GPR_INFO, "%s server startup complete", type_.c_str());
     }
 
-    void Serve(const grpc::string& server_host, std::mutex* mu,
-               std::condition_variable* cond) {
+    void Serve(const grpc::string& server_host, grpc::internal::Mutex* mu,
+               grpc::internal::CondVar* cond) {
       // We need to acquire the lock here in order to prevent the notify_one
       // below from firing before its corresponding wait is executed.
-      std::lock_guard<std::mutex> lock(*mu);
+      grpc::internal::MutexLock lock(mu);
       std::ostringstream server_address;
       server_address << server_host << ":" << port_;
       ServerBuilder builder;
@@ -639,7 +649,7 @@ class GrpclbEnd2endTest : public ::testing::Test {
       builder.AddListeningPort(server_address.str(), creds);
       builder.RegisterService(&service_);
       server_ = builder.BuildAndStart();
-      cond->notify_one();
+      cond->Signal();
     }
 
     void Shutdown() {
@@ -728,6 +738,39 @@ TEST_F(SingleBalancerTest, SelectGrpclbWithMigrationServiceConfig) {
 }
 
 TEST_F(SingleBalancerTest,
+       DoNotSpecialCaseUseGrpclbWithLoadBalancingConfigTest) {
+  const int kFallbackTimeoutMs = 200 * grpc_test_slowdown_factor();
+  ResetStub(kFallbackTimeoutMs);
+  SetNextResolution({AddressData{backends_[0]->port_, false, ""},
+                     AddressData{balancers_[0]->port_, true, ""}},
+                    "{\n"
+                    " \"loadBalancingConfig\":[\n"
+                    "  {\"pick_first\":{} }\n"
+                    " ]\n"
+                    "}");
+  CheckRpcSendOk();
+  // Check LB policy name for the channel.
+  EXPECT_EQ("pick_first", channel_->GetLoadBalancingPolicyName());
+}
+
+TEST_F(
+    SingleBalancerTest,
+    DoNotSpecialCaseUseGrpclbWithLoadBalancingConfigTestAndNoBackendAddress) {
+  const int kFallbackTimeoutMs = 200 * grpc_test_slowdown_factor();
+  ResetStub(kFallbackTimeoutMs);
+  SetNextResolution({AddressData{balancers_[0]->port_, true, ""}},
+                    "{\n"
+                    " \"loadBalancingConfig\":[\n"
+                    "  {\"pick_first\":{} }\n"
+                    " ]\n"
+                    "}");
+  // This should fail since we do not have a non-balancer backend
+  CheckRpcSendFailure();
+  // Check LB policy name for the channel.
+  EXPECT_EQ("pick_first", channel_->GetLoadBalancingPolicyName());
+}
+
+TEST_F(SingleBalancerTest,
        SelectGrpclbWithMigrationServiceConfigAndNoAddresses) {
   const int kFallbackTimeoutMs = 200 * grpc_test_slowdown_factor();
   ResetStub(kFallbackTimeoutMs);
@@ -1024,12 +1067,12 @@ TEST_F(SingleBalancerTest, Fallback) {
   SetNextResolutionAllBalancers();
   const int kFallbackTimeoutMs = 200 * grpc_test_slowdown_factor();
   const int kServerlistDelayMs = 500 * grpc_test_slowdown_factor();
-  const size_t kNumBackendInResolution = backends_.size() / 2;
+  const size_t kNumBackendsInResolution = backends_.size() / 2;
 
   ResetStub(kFallbackTimeoutMs);
   std::vector<AddressData> addresses;
   addresses.emplace_back(AddressData{balancers_[0]->port_, true, ""});
-  for (size_t i = 0; i < kNumBackendInResolution; ++i) {
+  for (size_t i = 0; i < kNumBackendsInResolution; ++i) {
     addresses.emplace_back(AddressData{backends_[i]->port_, false, ""});
   }
   SetNextResolution(addresses);
@@ -1038,45 +1081,45 @@ TEST_F(SingleBalancerTest, Fallback) {
   ScheduleResponseForBalancer(
       0,
       BalancerServiceImpl::BuildResponseForBackends(
-          GetBackendPorts(kNumBackendInResolution /* start_index */), {}),
+          GetBackendPorts(kNumBackendsInResolution /* start_index */), {}),
       kServerlistDelayMs);
 
   // Wait until all the fallback backends are reachable.
-  for (size_t i = 0; i < kNumBackendInResolution; ++i) {
+  for (size_t i = 0; i < kNumBackendsInResolution; ++i) {
     WaitForBackend(i);
   }
 
   // The first request.
   gpr_log(GPR_INFO, "========= BEFORE FIRST BATCH ==========");
-  CheckRpcSendOk(kNumBackendInResolution);
+  CheckRpcSendOk(kNumBackendsInResolution);
   gpr_log(GPR_INFO, "========= DONE WITH FIRST BATCH ==========");
 
   // Fallback is used: each backend returned by the resolver should have
   // gotten one request.
-  for (size_t i = 0; i < kNumBackendInResolution; ++i) {
+  for (size_t i = 0; i < kNumBackendsInResolution; ++i) {
     EXPECT_EQ(1U, backends_[i]->service_.request_count());
   }
-  for (size_t i = kNumBackendInResolution; i < backends_.size(); ++i) {
+  for (size_t i = kNumBackendsInResolution; i < backends_.size(); ++i) {
     EXPECT_EQ(0U, backends_[i]->service_.request_count());
   }
 
   // Wait until the serverlist reception has been processed and all backends
   // in the serverlist are reachable.
-  for (size_t i = kNumBackendInResolution; i < backends_.size(); ++i) {
+  for (size_t i = kNumBackendsInResolution; i < backends_.size(); ++i) {
     WaitForBackend(i);
   }
 
   // Send out the second request.
   gpr_log(GPR_INFO, "========= BEFORE SECOND BATCH ==========");
-  CheckRpcSendOk(backends_.size() - kNumBackendInResolution);
+  CheckRpcSendOk(backends_.size() - kNumBackendsInResolution);
   gpr_log(GPR_INFO, "========= DONE WITH SECOND BATCH ==========");
 
   // Serverlist is used: each backend returned by the balancer should
   // have gotten one request.
-  for (size_t i = 0; i < kNumBackendInResolution; ++i) {
+  for (size_t i = 0; i < kNumBackendsInResolution; ++i) {
     EXPECT_EQ(0U, backends_[i]->service_.request_count());
   }
-  for (size_t i = kNumBackendInResolution; i < backends_.size(); ++i) {
+  for (size_t i = kNumBackendsInResolution; i < backends_.size(); ++i) {
     EXPECT_EQ(1U, backends_[i]->service_.request_count());
   }
 
@@ -1091,13 +1134,13 @@ TEST_F(SingleBalancerTest, FallbackUpdate) {
   SetNextResolutionAllBalancers();
   const int kFallbackTimeoutMs = 200 * grpc_test_slowdown_factor();
   const int kServerlistDelayMs = 500 * grpc_test_slowdown_factor();
-  const size_t kNumBackendInResolution = backends_.size() / 3;
-  const size_t kNumBackendInResolutionUpdate = backends_.size() / 3;
+  const size_t kNumBackendsInResolution = backends_.size() / 3;
+  const size_t kNumBackendsInResolutionUpdate = backends_.size() / 3;
 
   ResetStub(kFallbackTimeoutMs);
   std::vector<AddressData> addresses;
   addresses.emplace_back(AddressData{balancers_[0]->port_, true, ""});
-  for (size_t i = 0; i < kNumBackendInResolution; ++i) {
+  for (size_t i = 0; i < kNumBackendsInResolution; ++i) {
     addresses.emplace_back(AddressData{backends_[i]->port_, false, ""});
   }
   SetNextResolution(addresses);
@@ -1106,84 +1149,84 @@ TEST_F(SingleBalancerTest, FallbackUpdate) {
   ScheduleResponseForBalancer(
       0,
       BalancerServiceImpl::BuildResponseForBackends(
-          GetBackendPorts(kNumBackendInResolution +
-                          kNumBackendInResolutionUpdate /* start_index */),
+          GetBackendPorts(kNumBackendsInResolution +
+                          kNumBackendsInResolutionUpdate /* start_index */),
           {}),
       kServerlistDelayMs);
 
   // Wait until all the fallback backends are reachable.
-  for (size_t i = 0; i < kNumBackendInResolution; ++i) {
+  for (size_t i = 0; i < kNumBackendsInResolution; ++i) {
     WaitForBackend(i);
   }
 
   // The first request.
   gpr_log(GPR_INFO, "========= BEFORE FIRST BATCH ==========");
-  CheckRpcSendOk(kNumBackendInResolution);
+  CheckRpcSendOk(kNumBackendsInResolution);
   gpr_log(GPR_INFO, "========= DONE WITH FIRST BATCH ==========");
 
   // Fallback is used: each backend returned by the resolver should have
   // gotten one request.
-  for (size_t i = 0; i < kNumBackendInResolution; ++i) {
+  for (size_t i = 0; i < kNumBackendsInResolution; ++i) {
     EXPECT_EQ(1U, backends_[i]->service_.request_count());
   }
-  for (size_t i = kNumBackendInResolution; i < backends_.size(); ++i) {
+  for (size_t i = kNumBackendsInResolution; i < backends_.size(); ++i) {
     EXPECT_EQ(0U, backends_[i]->service_.request_count());
   }
 
   addresses.clear();
   addresses.emplace_back(AddressData{balancers_[0]->port_, true, ""});
-  for (size_t i = kNumBackendInResolution;
-       i < kNumBackendInResolution + kNumBackendInResolutionUpdate; ++i) {
+  for (size_t i = kNumBackendsInResolution;
+       i < kNumBackendsInResolution + kNumBackendsInResolutionUpdate; ++i) {
     addresses.emplace_back(AddressData{backends_[i]->port_, false, ""});
   }
   SetNextResolution(addresses);
 
   // Wait until the resolution update has been processed and all the new
   // fallback backends are reachable.
-  for (size_t i = kNumBackendInResolution;
-       i < kNumBackendInResolution + kNumBackendInResolutionUpdate; ++i) {
+  for (size_t i = kNumBackendsInResolution;
+       i < kNumBackendsInResolution + kNumBackendsInResolutionUpdate; ++i) {
     WaitForBackend(i);
   }
 
   // Send out the second request.
   gpr_log(GPR_INFO, "========= BEFORE SECOND BATCH ==========");
-  CheckRpcSendOk(kNumBackendInResolutionUpdate);
+  CheckRpcSendOk(kNumBackendsInResolutionUpdate);
   gpr_log(GPR_INFO, "========= DONE WITH SECOND BATCH ==========");
 
   // The resolution update is used: each backend in the resolution update should
   // have gotten one request.
-  for (size_t i = 0; i < kNumBackendInResolution; ++i) {
+  for (size_t i = 0; i < kNumBackendsInResolution; ++i) {
     EXPECT_EQ(0U, backends_[i]->service_.request_count());
   }
-  for (size_t i = kNumBackendInResolution;
-       i < kNumBackendInResolution + kNumBackendInResolutionUpdate; ++i) {
+  for (size_t i = kNumBackendsInResolution;
+       i < kNumBackendsInResolution + kNumBackendsInResolutionUpdate; ++i) {
     EXPECT_EQ(1U, backends_[i]->service_.request_count());
   }
-  for (size_t i = kNumBackendInResolution + kNumBackendInResolutionUpdate;
+  for (size_t i = kNumBackendsInResolution + kNumBackendsInResolutionUpdate;
        i < backends_.size(); ++i) {
     EXPECT_EQ(0U, backends_[i]->service_.request_count());
   }
 
   // Wait until the serverlist reception has been processed and all backends
   // in the serverlist are reachable.
-  for (size_t i = kNumBackendInResolution + kNumBackendInResolutionUpdate;
+  for (size_t i = kNumBackendsInResolution + kNumBackendsInResolutionUpdate;
        i < backends_.size(); ++i) {
     WaitForBackend(i);
   }
 
   // Send out the third request.
   gpr_log(GPR_INFO, "========= BEFORE THIRD BATCH ==========");
-  CheckRpcSendOk(backends_.size() - kNumBackendInResolution -
-                 kNumBackendInResolutionUpdate);
+  CheckRpcSendOk(backends_.size() - kNumBackendsInResolution -
+                 kNumBackendsInResolutionUpdate);
   gpr_log(GPR_INFO, "========= DONE WITH THIRD BATCH ==========");
 
   // Serverlist is used: each backend returned by the balancer should
   // have gotten one request.
   for (size_t i = 0;
-       i < kNumBackendInResolution + kNumBackendInResolutionUpdate; ++i) {
+       i < kNumBackendsInResolution + kNumBackendsInResolutionUpdate; ++i) {
     EXPECT_EQ(0U, backends_[i]->service_.request_count());
   }
-  for (size_t i = kNumBackendInResolution + kNumBackendInResolutionUpdate;
+  for (size_t i = kNumBackendsInResolution + kNumBackendsInResolutionUpdate;
        i < backends_.size(); ++i) {
     EXPECT_EQ(1U, backends_[i]->service_.request_count());
   }
@@ -1359,7 +1402,7 @@ class UpdatesTest : public GrpclbEnd2endTest {
   UpdatesTest() : GrpclbEnd2endTest(4, 3, 0) {}
 };
 
-TEST_F(UpdatesTest, UpdateBalancers) {
+TEST_F(UpdatesTest, UpdateBalancersButKeepUsingOriginalBalancer) {
   SetNextResolutionAllBalancers();
   const std::vector<int> first_backend{GetBackendPorts()[0]};
   const std::vector<int> second_backend{GetBackendPorts()[1]};
@@ -1379,9 +1422,6 @@ TEST_F(UpdatesTest, UpdateBalancers) {
   // All 10 requests should have gone to the first backend.
   EXPECT_EQ(10U, backends_[0]->service_.request_count());
 
-  balancers_[0]->service_.NotifyDoneWithServerlists();
-  balancers_[1]->service_.NotifyDoneWithServerlists();
-  balancers_[2]->service_.NotifyDoneWithServerlists();
   // Balancer 0 got a single request.
   EXPECT_EQ(1U, balancers_[0]->service_.request_count());
   // and sent a single response.
@@ -1397,25 +1437,21 @@ TEST_F(UpdatesTest, UpdateBalancers) {
   SetNextResolution(addresses);
   gpr_log(GPR_INFO, "========= UPDATE 1 DONE ==========");
 
-  // Wait until update has been processed, as signaled by the second backend
-  // receiving a request.
   EXPECT_EQ(0U, backends_[1]->service_.request_count());
-  WaitForBackend(1);
-
-  backends_[1]->service_.ResetCounters();
-  gpr_log(GPR_INFO, "========= BEFORE SECOND BATCH ==========");
-  CheckRpcSendOk(10);
-  gpr_log(GPR_INFO, "========= DONE WITH SECOND BATCH ==========");
-  // All 10 requests should have gone to the second backend.
-  EXPECT_EQ(10U, backends_[1]->service_.request_count());
+  gpr_timespec deadline = gpr_time_add(
+      gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(10000, GPR_TIMESPAN));
+  // Send 10 seconds worth of RPCs
+  do {
+    CheckRpcSendOk();
+  } while (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), deadline) < 0);
+  // The current LB call is still working, so grpclb continued using it to the
+  // first balancer, which doesn't assign the second backend.
+  EXPECT_EQ(0U, backends_[1]->service_.request_count());
 
-  balancers_[0]->service_.NotifyDoneWithServerlists();
-  balancers_[1]->service_.NotifyDoneWithServerlists();
-  balancers_[2]->service_.NotifyDoneWithServerlists();
   EXPECT_EQ(1U, balancers_[0]->service_.request_count());
   EXPECT_EQ(1U, balancers_[0]->service_.response_count());
-  EXPECT_EQ(1U, balancers_[1]->service_.request_count());
-  EXPECT_EQ(1U, balancers_[1]->service_.response_count());
+  EXPECT_EQ(0U, balancers_[1]->service_.request_count());
+  EXPECT_EQ(0U, balancers_[1]->service_.response_count());
   EXPECT_EQ(0U, balancers_[2]->service_.request_count());
   EXPECT_EQ(0U, balancers_[2]->service_.response_count());
 }
@@ -1526,9 +1562,6 @@ TEST_F(UpdatesTest, UpdateBalancersDeadUpdate) {
   EXPECT_EQ(20U, backends_[0]->service_.request_count());
   EXPECT_EQ(0U, backends_[1]->service_.request_count());
 
-  balancers_[0]->service_.NotifyDoneWithServerlists();
-  balancers_[1]->service_.NotifyDoneWithServerlists();
-  balancers_[2]->service_.NotifyDoneWithServerlists();
   // Balancer 0 got a single request.
   EXPECT_EQ(1U, balancers_[0]->service_.request_count());
   // and sent a single response.
@@ -1558,9 +1591,6 @@ TEST_F(UpdatesTest, UpdateBalancersDeadUpdate) {
   // All 10 requests should have gone to the second backend.
   EXPECT_EQ(10U, backends_[1]->service_.request_count());
 
-  balancers_[0]->service_.NotifyDoneWithServerlists();
-  balancers_[1]->service_.NotifyDoneWithServerlists();
-  balancers_[2]->service_.NotifyDoneWithServerlists();
   EXPECT_EQ(1U, balancers_[0]->service_.request_count());
   EXPECT_EQ(1U, balancers_[0]->service_.response_count());
   // The second balancer, published as part of the first update, may end up
index b96ff53..13d5ea5 100644 (file)
@@ -124,8 +124,8 @@ class HealthServiceEnd2endTest : public ::testing::Test {
   }
 
   void ResetStubs() {
-    std::shared_ptr<Channel> channel =
-        CreateChannel(server_address_.str(), InsecureChannelCredentials());
+    std::shared_ptr<Channel> channel = grpc::CreateChannel(
+        server_address_.str(), InsecureChannelCredentials());
     hc_stub_ = grpc::health::v1::Health::NewStub(channel);
   }
 
index b0dd901..75001f0 100644 (file)
@@ -296,8 +296,8 @@ class HybridEnd2endTest : public ::testing::TestWithParam<bool> {
   void ResetStub() {
     std::shared_ptr<Channel> channel =
         inproc_ ? server_->InProcessChannel(ChannelArguments())
-                : CreateChannel(server_address_.str(),
-                                InsecureChannelCredentials());
+                : grpc::CreateChannel(server_address_.str(),
+                                      InsecureChannelCredentials());
     stub_ = grpc::testing::EchoTestService::NewStub(channel);
   }
 
@@ -321,8 +321,8 @@ class HybridEnd2endTest : public ::testing::TestWithParam<bool> {
   }
 
   void SendEchoToDupService() {
-    std::shared_ptr<Channel> channel =
-        CreateChannel(server_address_.str(), InsecureChannelCredentials());
+    std::shared_ptr<Channel> channel = grpc::CreateChannel(
+        server_address_.str(), InsecureChannelCredentials());
     auto stub = grpc::testing::duplicate::EchoTestService::NewStub(channel);
     EchoRequest send_request;
     EchoResponse recv_response;
@@ -373,8 +373,8 @@ class HybridEnd2endTest : public ::testing::TestWithParam<bool> {
   }
 
   void SendSimpleServerStreamingToDupService() {
-    std::shared_ptr<Channel> channel =
-        CreateChannel(server_address_.str(), InsecureChannelCredentials());
+    std::shared_ptr<Channel> channel = grpc::CreateChannel(
+        server_address_.str(), InsecureChannelCredentials());
     auto stub = grpc::testing::duplicate::EchoTestService::NewStub(channel);
     EchoRequest request;
     EchoResponse response;
diff --git a/test/cpp/end2end/message_allocator_end2end_test.cc b/test/cpp/end2end/message_allocator_end2end_test.cc
new file mode 100644 (file)
index 0000000..c833a4f
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ *
+ * Copyright 2019 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <sstream>
+#include <thread>
+
+#include <google/protobuf/arena.h>
+
+#include <gtest/gtest.h>
+
+#include <grpcpp/channel.h>
+#include <grpcpp/client_context.h>
+#include <grpcpp/create_channel.h>
+#include <grpcpp/server.h>
+#include <grpcpp/server_builder.h>
+#include <grpcpp/server_context.h>
+#include <grpcpp/support/client_callback.h>
+#include <grpcpp/support/message_allocator.h>
+
+#include "src/core/lib/iomgr/iomgr.h"
+#include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+#include "test/cpp/util/test_credentials_provider.h"
+
+// MAYBE_SKIP_TEST is a macro to determine if this particular test configuration
+// should be skipped based on a decision made at SetUp time. In particular, any
+// callback tests can only be run if the iomgr can run in the background or if
+// the transport is in-process.
+#define MAYBE_SKIP_TEST \
+  do {                  \
+    if (do_not_test_) { \
+      return;           \
+    }                   \
+  } while (0)
+
+namespace grpc {
+namespace testing {
+namespace {
+
+class CallbackTestServiceImpl
+    : public EchoTestService::ExperimentalCallbackService {
+ public:
+  explicit CallbackTestServiceImpl() {}
+
+  void SetFreeRequest() { free_request_ = true; }
+
+  void SetAllocatorMutator(
+      std::function<void(void* allocator_state, const EchoRequest* req,
+                         EchoResponse* resp)>
+          mutator) {
+    allocator_mutator_ = mutator;
+  }
+
+  void Echo(ServerContext* context, const EchoRequest* request,
+            EchoResponse* response,
+            experimental::ServerCallbackRpcController* controller) override {
+    response->set_message(request->message());
+    if (free_request_) {
+      controller->FreeRequest();
+    } else if (allocator_mutator_) {
+      allocator_mutator_(controller->GetAllocatorState(), request, response);
+    }
+    controller->Finish(Status::OK);
+  }
+
+ private:
+  bool free_request_ = false;
+  std::function<void(void* allocator_state, const EchoRequest* req,
+                     EchoResponse* resp)>
+      allocator_mutator_;
+};
+
+enum class Protocol { INPROC, TCP };
+
+class TestScenario {
+ public:
+  TestScenario(Protocol protocol, const grpc::string& creds_type)
+      : protocol(protocol), credentials_type(creds_type) {}
+  void Log() const;
+  Protocol protocol;
+  const grpc::string credentials_type;
+};
+
+static std::ostream& operator<<(std::ostream& out,
+                                const TestScenario& scenario) {
+  return out << "TestScenario{protocol="
+             << (scenario.protocol == Protocol::INPROC ? "INPROC" : "TCP")
+             << "," << scenario.credentials_type << "}";
+}
+
+void TestScenario::Log() const {
+  std::ostringstream out;
+  out << *this;
+  gpr_log(GPR_INFO, "%s", out.str().c_str());
+}
+
+class MessageAllocatorEnd2endTestBase
+    : public ::testing::TestWithParam<TestScenario> {
+ protected:
+  MessageAllocatorEnd2endTestBase() {
+    GetParam().Log();
+    if (GetParam().protocol == Protocol::TCP) {
+      if (!grpc_iomgr_run_in_background()) {
+        do_not_test_ = true;
+        return;
+      }
+    }
+  }
+
+  ~MessageAllocatorEnd2endTestBase() = default;
+
+  void CreateServer(
+      experimental::MessageAllocator<EchoRequest, EchoResponse>* allocator) {
+    ServerBuilder builder;
+
+    auto server_creds = GetCredentialsProvider()->GetServerCredentials(
+        GetParam().credentials_type);
+    if (GetParam().protocol == Protocol::TCP) {
+      picked_port_ = grpc_pick_unused_port_or_die();
+      server_address_ << "localhost:" << picked_port_;
+      builder.AddListeningPort(server_address_.str(), server_creds);
+    }
+    callback_service_.SetMessageAllocatorFor_Echo(allocator);
+    builder.RegisterService(&callback_service_);
+
+    server_ = builder.BuildAndStart();
+    is_server_started_ = true;
+  }
+
+  void ResetStub() {
+    ChannelArguments args;
+    auto channel_creds = GetCredentialsProvider()->GetChannelCredentials(
+        GetParam().credentials_type, &args);
+    switch (GetParam().protocol) {
+      case Protocol::TCP:
+        channel_ = ::grpc::CreateCustomChannel(server_address_.str(),
+                                               channel_creds, args);
+        break;
+      case Protocol::INPROC:
+        channel_ = server_->InProcessChannel(args);
+        break;
+      default:
+        assert(false);
+    }
+    stub_ = EchoTestService::NewStub(channel_);
+  }
+
+  void TearDown() override {
+    if (is_server_started_) {
+      server_->Shutdown();
+    }
+    if (picked_port_ > 0) {
+      grpc_recycle_unused_port(picked_port_);
+    }
+  }
+
+  void SendRpcs(int num_rpcs) {
+    grpc::string test_string("");
+    for (int i = 0; i < num_rpcs; i++) {
+      EchoRequest request;
+      EchoResponse response;
+      ClientContext cli_ctx;
+
+      test_string += grpc::string(1024, 'x');
+      request.set_message(test_string);
+      grpc::string val;
+      cli_ctx.set_compression_algorithm(GRPC_COMPRESS_GZIP);
+
+      std::mutex mu;
+      std::condition_variable cv;
+      bool done = false;
+      stub_->experimental_async()->Echo(
+          &cli_ctx, &request, &response,
+          [&request, &response, &done, &mu, &cv, val](Status s) {
+            GPR_ASSERT(s.ok());
+
+            EXPECT_EQ(request.message(), response.message());
+            std::lock_guard<std::mutex> l(mu);
+            done = true;
+            cv.notify_one();
+          });
+      std::unique_lock<std::mutex> l(mu);
+      while (!done) {
+        cv.wait(l);
+      }
+    }
+  }
+
+  bool do_not_test_{false};
+  bool is_server_started_{false};
+  int picked_port_{0};
+  std::shared_ptr<Channel> channel_;
+  std::unique_ptr<EchoTestService::Stub> stub_;
+  CallbackTestServiceImpl callback_service_;
+  std::unique_ptr<Server> server_;
+  std::ostringstream server_address_;
+};
+
+class NullAllocatorTest : public MessageAllocatorEnd2endTestBase {};
+
+TEST_P(NullAllocatorTest, SimpleRpc) {
+  MAYBE_SKIP_TEST;
+  CreateServer(nullptr);
+  ResetStub();
+  SendRpcs(1);
+}
+
+class SimpleAllocatorTest : public MessageAllocatorEnd2endTestBase {
+ public:
+  class SimpleAllocator
+      : public experimental::MessageAllocator<EchoRequest, EchoResponse> {
+   public:
+    void AllocateMessages(
+        experimental::RpcAllocatorInfo<EchoRequest, EchoResponse>* info) {
+      allocation_count++;
+      info->request = new EchoRequest;
+      info->response = new EchoResponse;
+      info->allocator_state = info;
+    }
+    void DeallocateRequest(
+        experimental::RpcAllocatorInfo<EchoRequest, EchoResponse>* info) {
+      request_deallocation_count++;
+      delete info->request;
+      info->request = nullptr;
+    }
+    void DeallocateMessages(
+        experimental::RpcAllocatorInfo<EchoRequest, EchoResponse>* info) {
+      messages_deallocation_count++;
+      delete info->request;
+      delete info->response;
+    }
+
+    int allocation_count = 0;
+    int request_deallocation_count = 0;
+    int messages_deallocation_count = 0;
+  };
+};
+
+TEST_P(SimpleAllocatorTest, SimpleRpc) {
+  MAYBE_SKIP_TEST;
+  const int kRpcCount = 10;
+  std::unique_ptr<SimpleAllocator> allocator(new SimpleAllocator);
+  CreateServer(allocator.get());
+  ResetStub();
+  SendRpcs(kRpcCount);
+  EXPECT_EQ(kRpcCount, allocator->allocation_count);
+  EXPECT_EQ(kRpcCount, allocator->messages_deallocation_count);
+  EXPECT_EQ(0, allocator->request_deallocation_count);
+}
+
+TEST_P(SimpleAllocatorTest, RpcWithEarlyFreeRequest) {
+  MAYBE_SKIP_TEST;
+  const int kRpcCount = 10;
+  std::unique_ptr<SimpleAllocator> allocator(new SimpleAllocator);
+  callback_service_.SetFreeRequest();
+  CreateServer(allocator.get());
+  ResetStub();
+  SendRpcs(kRpcCount);
+  EXPECT_EQ(kRpcCount, allocator->allocation_count);
+  EXPECT_EQ(kRpcCount, allocator->messages_deallocation_count);
+  EXPECT_EQ(kRpcCount, allocator->request_deallocation_count);
+}
+
+TEST_P(SimpleAllocatorTest, RpcWithReleaseRequest) {
+  MAYBE_SKIP_TEST;
+  const int kRpcCount = 10;
+  std::unique_ptr<SimpleAllocator> allocator(new SimpleAllocator);
+  std::vector<EchoRequest*> released_requests;
+  auto mutator = [&released_requests](void* allocator_state,
+                                      const EchoRequest* req,
+                                      EchoResponse* resp) {
+    auto* info =
+        static_cast<experimental::RpcAllocatorInfo<EchoRequest, EchoResponse>*>(
+            allocator_state);
+    EXPECT_EQ(req, info->request);
+    EXPECT_EQ(resp, info->response);
+    EXPECT_EQ(allocator_state, info->allocator_state);
+    released_requests.push_back(info->request);
+    info->request = nullptr;
+  };
+  callback_service_.SetAllocatorMutator(mutator);
+  CreateServer(allocator.get());
+  ResetStub();
+  SendRpcs(kRpcCount);
+  EXPECT_EQ(kRpcCount, allocator->allocation_count);
+  EXPECT_EQ(kRpcCount, allocator->messages_deallocation_count);
+  EXPECT_EQ(0, allocator->request_deallocation_count);
+  EXPECT_EQ(static_cast<unsigned>(kRpcCount), released_requests.size());
+  for (auto* req : released_requests) {
+    delete req;
+  }
+}
+
+class ArenaAllocatorTest : public MessageAllocatorEnd2endTestBase {
+ public:
+  class ArenaAllocator
+      : public experimental::MessageAllocator<EchoRequest, EchoResponse> {
+   public:
+    void AllocateMessages(
+        experimental::RpcAllocatorInfo<EchoRequest, EchoResponse>* info) {
+      allocation_count++;
+      auto* arena = new google::protobuf::Arena;
+      info->allocator_state = arena;
+      info->request =
+          google::protobuf::Arena::CreateMessage<EchoRequest>(arena);
+      info->response =
+          google::protobuf::Arena::CreateMessage<EchoResponse>(arena);
+    }
+    void DeallocateRequest(
+        experimental::RpcAllocatorInfo<EchoRequest, EchoResponse>* info) {
+      GPR_ASSERT(0);
+    }
+    void DeallocateMessages(
+        experimental::RpcAllocatorInfo<EchoRequest, EchoResponse>* info) {
+      deallocation_count++;
+      auto* arena =
+          static_cast<google::protobuf::Arena*>(info->allocator_state);
+      delete arena;
+    }
+
+    int allocation_count = 0;
+    int deallocation_count = 0;
+  };
+};
+
+TEST_P(ArenaAllocatorTest, SimpleRpc) {
+  MAYBE_SKIP_TEST;
+  const int kRpcCount = 10;
+  std::unique_ptr<ArenaAllocator> allocator(new ArenaAllocator);
+  CreateServer(allocator.get());
+  ResetStub();
+  SendRpcs(kRpcCount);
+  EXPECT_EQ(kRpcCount, allocator->allocation_count);
+  EXPECT_EQ(kRpcCount, allocator->deallocation_count);
+}
+
+std::vector<TestScenario> CreateTestScenarios(bool test_insecure) {
+  std::vector<TestScenario> scenarios;
+  std::vector<grpc::string> credentials_types{
+      GetCredentialsProvider()->GetSecureCredentialsTypeList()};
+  auto insec_ok = [] {
+    // Only allow insecure credentials type when it is registered with the
+    // provider. User may create providers that do not have insecure.
+    return GetCredentialsProvider()->GetChannelCredentials(
+               kInsecureCredentialsType, nullptr) != nullptr;
+  };
+  if (test_insecure && insec_ok()) {
+    credentials_types.push_back(kInsecureCredentialsType);
+  }
+  GPR_ASSERT(!credentials_types.empty());
+
+  Protocol parr[]{Protocol::INPROC, Protocol::TCP};
+  for (Protocol p : parr) {
+    for (const auto& cred : credentials_types) {
+      // TODO(vjpai): Test inproc with secure credentials when feasible
+      if (p == Protocol::INPROC &&
+          (cred != kInsecureCredentialsType || !insec_ok())) {
+        continue;
+      }
+      scenarios.emplace_back(p, cred);
+    }
+  }
+  return scenarios;
+}
+
+INSTANTIATE_TEST_CASE_P(NullAllocatorTest, NullAllocatorTest,
+                        ::testing::ValuesIn(CreateTestScenarios(true)));
+INSTANTIATE_TEST_CASE_P(SimpleAllocatorTest, SimpleAllocatorTest,
+                        ::testing::ValuesIn(CreateTestScenarios(true)));
+INSTANTIATE_TEST_CASE_P(ArenaAllocatorTest, ArenaAllocatorTest,
+                        ::testing::ValuesIn(CreateTestScenarios(true)));
+
+}  // namespace
+}  // namespace testing
+}  // namespace grpc
+
+int main(int argc, char** argv) {
+  grpc::testing::TestEnvironment env(argc, argv);
+  // The grpc_init is to cover the MAYBE_SKIP_TEST.
+  grpc_init();
+  ::testing::InitGoogleTest(&argc, argv);
+  int ret = RUN_ALL_TESTS();
+  grpc_shutdown();
+  return ret;
+}
index 917ca28..0196c9d 100644 (file)
@@ -244,8 +244,8 @@ class MockTest : public ::testing::Test {
   void TearDown() override { server_->Shutdown(); }
 
   void ResetStub() {
-    std::shared_ptr<Channel> channel =
-        CreateChannel(server_address_.str(), InsecureChannelCredentials());
+    std::shared_ptr<Channel> channel = grpc::CreateChannel(
+        server_address_.str(), InsecureChannelCredentials());
     stub_ = grpc::testing::EchoTestService::NewStub(channel);
   }
 
index 36dea1f..eb651df 100644 (file)
@@ -106,7 +106,7 @@ class NonblockingTest : public ::testing::Test {
   }
 
   void ResetStub() {
-    std::shared_ptr<Channel> channel = CreateChannel(
+    std::shared_ptr<Channel> channel = grpc::CreateChannel(
         server_address_.str(), grpc::InsecureChannelCredentials());
     stub_ = grpc::testing::EchoTestService::NewStub(channel);
   }
index ff097aa..d817438 100644 (file)
@@ -55,7 +55,7 @@ class ProtoServerReflectionTest : public ::testing::Test {
   void ResetStub() {
     string target = "dns:localhost:" + to_string(port_);
     std::shared_ptr<Channel> channel =
-        CreateChannel(target, InsecureChannelCredentials());
+        grpc::CreateChannel(target, InsecureChannelCredentials());
     stub_ = grpc::testing::EchoTestService::NewStub(channel);
     desc_db_.reset(new ProtoReflectionDescriptorDatabase(channel));
     desc_pool_.reset(new protobuf::DescriptorPool(desc_db_.get()));
index c8556ba..184dc1e 100644 (file)
@@ -130,7 +130,7 @@ class RawEnd2EndTest : public ::testing::Test {
 
   void ResetStub() {
     ChannelArguments args;
-    std::shared_ptr<Channel> channel = CreateChannel(
+    std::shared_ptr<Channel> channel = grpc::CreateChannel(
         server_address_.str(), grpc::InsecureChannelCredentials());
     stub_ = grpc::testing::EchoTestService::NewStub(channel);
   }
index d744a93..43b00b9 100644 (file)
@@ -185,7 +185,7 @@ class ServerBuilderPluginTest : public ::testing::TestWithParam<bool> {
 
   void ResetStub() {
     string target = "dns:localhost:" + to_string(port_);
-    channel_ = CreateChannel(target, InsecureChannelCredentials());
+    channel_ = grpc::CreateChannel(target, InsecureChannelCredentials());
     stub_ = grpc::testing::EchoTestService::NewStub(channel_);
   }
 
index c05fcfd..b261560 100644 (file)
@@ -28,6 +28,7 @@
 #include <grpcpp/create_channel.h>
 
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "test/cpp/util/test_config.h"
 
 DEFINE_string(address, "", "Address to connect to");
 DEFINE_string(mode, "", "Test mode to use");
@@ -35,15 +36,8 @@ DEFINE_string(mode, "", "Test mode to use");
 using grpc::testing::EchoRequest;
 using grpc::testing::EchoResponse;
 
-// In some distros, gflags is in the namespace google, and in some others,
-// in gflags. This hack is enabling us to find both.
-namespace google {}
-namespace gflags {}
-using namespace google;
-using namespace gflags;
-
 int main(int argc, char** argv) {
-  ParseCommandLineFlags(&argc, &argv, true);
+  grpc::testing::InitTest(&argc, &argv, true);
   auto stub = grpc::testing::EchoTestService::NewStub(
       grpc::CreateChannel(FLAGS_address, grpc::InsecureChannelCredentials()));
 
index c47e250..6f35c3e 100644 (file)
@@ -122,8 +122,8 @@ class ServerEarlyReturnTest : public ::testing::Test {
     builder.RegisterService(&service_);
     server_ = builder.BuildAndStart();
 
-    channel_ =
-        CreateChannel(server_address_.str(), InsecureChannelCredentials());
+    channel_ = grpc::CreateChannel(server_address_.str(),
+                                   InsecureChannelCredentials());
     stub_ = grpc::testing::EchoTestService::NewStub(channel_);
   }
 
index 028191c..68103f7 100644 (file)
@@ -265,7 +265,8 @@ class ServerInterceptorsEnd2endSyncUnaryTest : public ::testing::Test {
 TEST_F(ServerInterceptorsEnd2endSyncUnaryTest, UnaryTest) {
   ChannelArguments args;
   DummyInterceptor::Reset();
-  auto channel = CreateChannel(server_address_, InsecureChannelCredentials());
+  auto channel =
+      grpc::CreateChannel(server_address_, InsecureChannelCredentials());
   MakeCall(channel);
   // Make sure all 20 dummy interceptors were run
   EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
@@ -308,7 +309,8 @@ class ServerInterceptorsEnd2endSyncStreamingTest : public ::testing::Test {
 TEST_F(ServerInterceptorsEnd2endSyncStreamingTest, ClientStreamingTest) {
   ChannelArguments args;
   DummyInterceptor::Reset();
-  auto channel = CreateChannel(server_address_, InsecureChannelCredentials());
+  auto channel =
+      grpc::CreateChannel(server_address_, InsecureChannelCredentials());
   MakeClientStreamingCall(channel);
   // Make sure all 20 dummy interceptors were run
   EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
@@ -317,7 +319,8 @@ TEST_F(ServerInterceptorsEnd2endSyncStreamingTest, ClientStreamingTest) {
 TEST_F(ServerInterceptorsEnd2endSyncStreamingTest, ServerStreamingTest) {
   ChannelArguments args;
   DummyInterceptor::Reset();
-  auto channel = CreateChannel(server_address_, InsecureChannelCredentials());
+  auto channel =
+      grpc::CreateChannel(server_address_, InsecureChannelCredentials());
   MakeServerStreamingCall(channel);
   // Make sure all 20 dummy interceptors were run
   EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
@@ -326,7 +329,8 @@ TEST_F(ServerInterceptorsEnd2endSyncStreamingTest, ServerStreamingTest) {
 TEST_F(ServerInterceptorsEnd2endSyncStreamingTest, BidiStreamingTest) {
   ChannelArguments args;
   DummyInterceptor::Reset();
-  auto channel = CreateChannel(server_address_, InsecureChannelCredentials());
+  auto channel =
+      grpc::CreateChannel(server_address_, InsecureChannelCredentials());
   MakeBidiStreamingCall(channel);
   // Make sure all 20 dummy interceptors were run
   EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
@@ -356,7 +360,8 @@ TEST_F(ServerInterceptorsAsyncEnd2endTest, UnaryTest) {
   auto server = builder.BuildAndStart();
 
   ChannelArguments args;
-  auto channel = CreateChannel(server_address, InsecureChannelCredentials());
+  auto channel =
+      grpc::CreateChannel(server_address, InsecureChannelCredentials());
   auto stub = grpc::testing::EchoTestService::NewStub(channel);
 
   EchoRequest send_request;
@@ -428,7 +433,8 @@ TEST_F(ServerInterceptorsAsyncEnd2endTest, BidiStreamingTest) {
   auto server = builder.BuildAndStart();
 
   ChannelArguments args;
-  auto channel = CreateChannel(server_address, InsecureChannelCredentials());
+  auto channel =
+      grpc::CreateChannel(server_address, InsecureChannelCredentials());
   auto stub = grpc::testing::EchoTestService::NewStub(channel);
 
   EchoRequest send_request;
@@ -509,7 +515,8 @@ TEST_F(ServerInterceptorsAsyncEnd2endTest, GenericRPCTest) {
   auto server = builder.BuildAndStart();
 
   ChannelArguments args;
-  auto channel = CreateChannel(server_address, InsecureChannelCredentials());
+  auto channel =
+      grpc::CreateChannel(server_address, InsecureChannelCredentials());
   GenericStub generic_stub(channel);
 
   const grpc::string kMethodName("/grpc.cpp.test.util.EchoTestService/Echo");
@@ -612,7 +619,7 @@ TEST_F(ServerInterceptorsAsyncEnd2endTest, UnimplementedRpcTest) {
 
   ChannelArguments args;
   std::shared_ptr<Channel> channel =
-      CreateChannel(server_address, InsecureChannelCredentials());
+      grpc::CreateChannel(server_address, InsecureChannelCredentials());
   std::unique_ptr<grpc::testing::UnimplementedEchoService::Stub> stub;
   stub = grpc::testing::UnimplementedEchoService::NewStub(channel);
   EchoRequest send_request;
@@ -665,7 +672,7 @@ TEST_F(ServerInterceptorsSyncUnimplementedEnd2endTest, UnimplementedRpcTest) {
 
   ChannelArguments args;
   std::shared_ptr<Channel> channel =
-      CreateChannel(server_address, InsecureChannelCredentials());
+      grpc::CreateChannel(server_address, InsecureChannelCredentials());
   std::unique_ptr<grpc::testing::UnimplementedEchoService::Stub> stub;
   stub = grpc::testing::UnimplementedEchoService::NewStub(channel);
   EchoRequest send_request;
index 7bc9af2..8eba912 100644 (file)
@@ -94,7 +94,7 @@ class ServerLoadReportingEnd2endTest : public ::testing::Test {
                            const grpc::string& lb_tag,
                            const grpc::string& message, size_t num_requests) {
     auto stub = EchoTestService::NewStub(
-        CreateChannel(server_address_, InsecureChannelCredentials()));
+        grpc::CreateChannel(server_address_, InsecureChannelCredentials()));
     grpc::string lb_token = lb_id + lb_tag;
     for (int i = 0; i < num_requests; ++i) {
       ClientContext ctx;
diff --git a/test/cpp/end2end/service_config_end2end_test.cc b/test/cpp/end2end/service_config_end2end_test.cc
new file mode 100644 (file)
index 0000000..2d05fd4
--- /dev/null
@@ -0,0 +1,561 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <algorithm>
+#include <memory>
+#include <mutex>
+#include <random>
+#include <set>
+#include <thread>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/atm.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+#include <grpcpp/channel.h>
+#include <grpcpp/client_context.h>
+#include <grpcpp/create_channel.h>
+#include <grpcpp/health_check_service_interface.h>
+#include <grpcpp/impl/codegen/sync.h>
+#include <grpcpp/server.h>
+#include <grpcpp/server_builder.h>
+
+#include "src/core/ext/filters/client_channel/backup_poller.h"
+#include "src/core/ext/filters/client_channel/global_subchannel_pool.h"
+#include "src/core/ext/filters/client_channel/parse_address.h"
+#include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"
+#include "src/core/ext/filters/client_channel/server_address.h"
+#include "src/core/lib/backoff/backoff.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gprpp/debug_location.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+#include "src/core/lib/security/credentials/fake/fake_credentials.h"
+#include "src/cpp/client/secure_credentials.h"
+#include "src/cpp/server/secure_server_credentials.h"
+
+#include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+#include "test/cpp/end2end/test_service_impl.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using grpc::testing::EchoRequest;
+using grpc::testing::EchoResponse;
+using std::chrono::system_clock;
+
+namespace grpc {
+namespace testing {
+namespace {
+
+// Subclass of TestServiceImpl that increments a request counter for
+// every call to the Echo RPC.
+class MyTestServiceImpl : public TestServiceImpl {
+ public:
+  MyTestServiceImpl() : request_count_(0) {}
+
+  Status Echo(ServerContext* context, const EchoRequest* request,
+              EchoResponse* response) override {
+    {
+      grpc::internal::MutexLock lock(&mu_);
+      ++request_count_;
+    }
+    AddClient(context->peer());
+    return TestServiceImpl::Echo(context, request, response);
+  }
+
+  int request_count() {
+    grpc::internal::MutexLock lock(&mu_);
+    return request_count_;
+  }
+
+  void ResetCounters() {
+    grpc::internal::MutexLock lock(&mu_);
+    request_count_ = 0;
+  }
+
+  std::set<grpc::string> clients() {
+    grpc::internal::MutexLock lock(&clients_mu_);
+    return clients_;
+  }
+
+ private:
+  void AddClient(const grpc::string& client) {
+    grpc::internal::MutexLock lock(&clients_mu_);
+    clients_.insert(client);
+  }
+
+  grpc::internal::Mutex mu_;
+  int request_count_;
+  grpc::internal::Mutex clients_mu_;
+  std::set<grpc::string> clients_;
+};
+
+class ServiceConfigEnd2endTest : public ::testing::Test {
+ protected:
+  ServiceConfigEnd2endTest()
+      : server_host_("localhost"),
+        kRequestMessage_("Live long and prosper."),
+        creds_(new SecureChannelCredentials(
+            grpc_fake_transport_security_credentials_create())) {
+    // Make the backup poller poll very frequently in order to pick up
+    // updates from all the subchannels's FDs.
+    GPR_GLOBAL_CONFIG_SET(grpc_client_channel_backup_poll_interval_ms, 1);
+  }
+
+  void SetUp() override {
+    grpc_init();
+    response_generator_ =
+        grpc_core::MakeRefCounted<grpc_core::FakeResolverResponseGenerator>();
+  }
+
+  void TearDown() override {
+    for (size_t i = 0; i < servers_.size(); ++i) {
+      servers_[i]->Shutdown();
+    }
+    // Explicitly destroy all the members so that we can make sure grpc_shutdown
+    // has finished by the end of this function, and thus all the registered
+    // LB policy factories are removed.
+    stub_.reset();
+    servers_.clear();
+    creds_.reset();
+    grpc_shutdown_blocking();
+  }
+
+  void CreateServers(size_t num_servers,
+                     std::vector<int> ports = std::vector<int>()) {
+    servers_.clear();
+    for (size_t i = 0; i < num_servers; ++i) {
+      int port = 0;
+      if (ports.size() == num_servers) port = ports[i];
+      servers_.emplace_back(new ServerData(port));
+    }
+  }
+
+  void StartServer(size_t index) { servers_[index]->Start(server_host_); }
+
+  void StartServers(size_t num_servers,
+                    std::vector<int> ports = std::vector<int>()) {
+    CreateServers(num_servers, std::move(ports));
+    for (size_t i = 0; i < num_servers; ++i) {
+      StartServer(i);
+    }
+  }
+
+  grpc_core::Resolver::Result BuildFakeResults(const std::vector<int>& 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 SetNextResolutionNoServiceConfig(const std::vector<int>& ports) {
+    grpc_core::ExecCtx exec_ctx;
+    grpc_core::Resolver::Result result = BuildFakeResults(ports);
+    response_generator_->SetResponse(result);
+  }
+
+  void SetNextResolutionValidServiceConfig(const std::vector<int>& ports) {
+    grpc_core::ExecCtx exec_ctx;
+    grpc_core::Resolver::Result result = BuildFakeResults(ports);
+    result.service_config =
+        grpc_core::ServiceConfig::Create("{}", &result.service_config_error);
+    response_generator_->SetResponse(result);
+  }
+
+  void SetNextResolutionInvalidServiceConfig(const std::vector<int>& ports) {
+    grpc_core::ExecCtx exec_ctx;
+    grpc_core::Resolver::Result result = BuildFakeResults(ports);
+    result.service_config =
+        grpc_core::ServiceConfig::Create("{", &result.service_config_error);
+    response_generator_->SetResponse(result);
+  }
+
+  void SetNextResolutionWithServiceConfig(const std::vector<int>& ports,
+                                          const char* svc_cfg) {
+    grpc_core::ExecCtx exec_ctx;
+    grpc_core::Resolver::Result result = BuildFakeResults(ports);
+    result.service_config =
+        grpc_core::ServiceConfig::Create(svc_cfg, &result.service_config_error);
+    response_generator_->SetResponse(result);
+  }
+
+  std::vector<int> GetServersPorts(size_t start_index = 0) {
+    std::vector<int> ports;
+    for (size_t i = start_index; i < servers_.size(); ++i) {
+      ports.push_back(servers_[i]->port_);
+    }
+    return ports;
+  }
+
+  std::unique_ptr<grpc::testing::EchoTestService::Stub> BuildStub(
+      const std::shared_ptr<Channel>& channel) {
+    return grpc::testing::EchoTestService::NewStub(channel);
+  }
+
+  std::shared_ptr<Channel> BuildChannel() {
+    ChannelArguments args;
+    args.SetPointer(GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR,
+                    response_generator_.get());
+    return ::grpc::CreateCustomChannel("fake:///", creds_, args);
+  }
+
+  std::shared_ptr<Channel> BuildChannelWithDefaultServiceConfig() {
+    ChannelArguments args;
+    args.SetServiceConfigJSON(ValidDefaultServiceConfig());
+    args.SetPointer(GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR,
+                    response_generator_.get());
+    return ::grpc::CreateCustomChannel("fake:///", creds_, args);
+  }
+
+  bool SendRpc(
+      const std::unique_ptr<grpc::testing::EchoTestService::Stub>& stub,
+      EchoResponse* response = nullptr, int timeout_ms = 1000,
+      Status* result = nullptr, bool wait_for_ready = false) {
+    const bool local_response = (response == nullptr);
+    if (local_response) response = new EchoResponse;
+    EchoRequest request;
+    request.set_message(kRequestMessage_);
+    ClientContext context;
+    context.set_deadline(grpc_timeout_milliseconds_to_deadline(timeout_ms));
+    if (wait_for_ready) context.set_wait_for_ready(true);
+    Status status = stub->Echo(&context, request, response);
+    if (result != nullptr) *result = status;
+    if (local_response) delete response;
+    return status.ok();
+  }
+
+  void CheckRpcSendOk(
+      const std::unique_ptr<grpc::testing::EchoTestService::Stub>& stub,
+      const grpc_core::DebugLocation& location, bool wait_for_ready = false) {
+    EchoResponse response;
+    Status status;
+    const bool success =
+        SendRpc(stub, &response, 2000, &status, wait_for_ready);
+    ASSERT_TRUE(success) << "From " << location.file() << ":" << location.line()
+                         << "\n"
+                         << "Error: " << status.error_message() << " "
+                         << status.error_details();
+    ASSERT_EQ(response.message(), kRequestMessage_)
+        << "From " << location.file() << ":" << location.line();
+    if (!success) abort();
+  }
+
+  void CheckRpcSendFailure(
+      const std::unique_ptr<grpc::testing::EchoTestService::Stub>& stub) {
+    const bool success = SendRpc(stub);
+    EXPECT_FALSE(success);
+  }
+
+  struct ServerData {
+    int port_;
+    std::unique_ptr<Server> server_;
+    MyTestServiceImpl service_;
+    std::unique_ptr<std::thread> thread_;
+    bool server_ready_ = false;
+    bool started_ = false;
+
+    explicit ServerData(int port = 0) {
+      port_ = port > 0 ? port : grpc_pick_unused_port_or_die();
+    }
+
+    void Start(const grpc::string& server_host) {
+      gpr_log(GPR_INFO, "starting server on port %d", port_);
+      started_ = true;
+      grpc::internal::Mutex mu;
+      grpc::internal::MutexLock lock(&mu);
+      grpc::internal::CondVar cond;
+      thread_.reset(new std::thread(
+          std::bind(&ServerData::Serve, this, server_host, &mu, &cond)));
+      cond.WaitUntil(&mu, [this] { return server_ready_; });
+      server_ready_ = false;
+      gpr_log(GPR_INFO, "server startup complete");
+    }
+
+    void Serve(const grpc::string& server_host, grpc::internal::Mutex* mu,
+               grpc::internal::CondVar* cond) {
+      std::ostringstream server_address;
+      server_address << server_host << ":" << port_;
+      ServerBuilder builder;
+      std::shared_ptr<ServerCredentials> creds(new SecureServerCredentials(
+          grpc_fake_transport_security_server_credentials_create()));
+      builder.AddListeningPort(server_address.str(), std::move(creds));
+      builder.RegisterService(&service_);
+      server_ = builder.BuildAndStart();
+      grpc::internal::MutexLock lock(mu);
+      server_ready_ = true;
+      cond->Signal();
+    }
+
+    void Shutdown() {
+      if (!started_) return;
+      server_->Shutdown(grpc_timeout_milliseconds_to_deadline(0));
+      thread_->join();
+      started_ = false;
+    }
+
+    void SetServingStatus(const grpc::string& service, bool serving) {
+      server_->GetHealthCheckService()->SetServingStatus(service, serving);
+    }
+  };
+
+  void ResetCounters() {
+    for (const auto& server : servers_) server->service_.ResetCounters();
+  }
+
+  void WaitForServer(
+      const std::unique_ptr<grpc::testing::EchoTestService::Stub>& stub,
+      size_t server_idx, const grpc_core::DebugLocation& location,
+      bool ignore_failure = false) {
+    do {
+      if (ignore_failure) {
+        SendRpc(stub);
+      } else {
+        CheckRpcSendOk(stub, location, true);
+      }
+    } while (servers_[server_idx]->service_.request_count() == 0);
+    ResetCounters();
+  }
+
+  bool WaitForChannelNotReady(Channel* channel, int timeout_seconds = 5) {
+    const gpr_timespec deadline =
+        grpc_timeout_seconds_to_deadline(timeout_seconds);
+    grpc_connectivity_state state;
+    while ((state = channel->GetState(false /* try_to_connect */)) ==
+           GRPC_CHANNEL_READY) {
+      if (!channel->WaitForStateChange(state, deadline)) return false;
+    }
+    return true;
+  }
+
+  bool WaitForChannelReady(Channel* channel, int timeout_seconds = 5) {
+    const gpr_timespec deadline =
+        grpc_timeout_seconds_to_deadline(timeout_seconds);
+    grpc_connectivity_state state;
+    while ((state = channel->GetState(true /* try_to_connect */)) !=
+           GRPC_CHANNEL_READY) {
+      if (!channel->WaitForStateChange(state, deadline)) return false;
+    }
+    return true;
+  }
+
+  bool SeenAllServers() {
+    for (const auto& server : servers_) {
+      if (server->service_.request_count() == 0) return false;
+    }
+    return true;
+  }
+
+  // Updates \a connection_order by appending to it the index of the newly
+  // connected server. Must be called after every single RPC.
+  void UpdateConnectionOrder(
+      const std::vector<std::unique_ptr<ServerData>>& servers,
+      std::vector<int>* connection_order) {
+    for (size_t i = 0; i < servers.size(); ++i) {
+      if (servers[i]->service_.request_count() == 1) {
+        // Was the server index known? If not, update connection_order.
+        const auto it =
+            std::find(connection_order->begin(), connection_order->end(), i);
+        if (it == connection_order->end()) {
+          connection_order->push_back(i);
+          return;
+        }
+      }
+    }
+  }
+
+  const char* ValidServiceConfigV1() { return "{\"version\": \"1\"}"; }
+
+  const char* ValidServiceConfigV2() { return "{\"version\": \"2\"}"; }
+
+  const char* ValidDefaultServiceConfig() {
+    return "{\"version\": \"valid_default\"}";
+  }
+
+  const char* InvalidDefaultServiceConfig() {
+    return "{\"version\": \"invalid_default\"}";
+  }
+
+  const grpc::string server_host_;
+  std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
+  std::vector<std::unique_ptr<ServerData>> servers_;
+  grpc_core::RefCountedPtr<grpc_core::FakeResolverResponseGenerator>
+      response_generator_;
+  const grpc::string kRequestMessage_;
+  std::shared_ptr<ChannelCredentials> creds_;
+};
+
+TEST_F(ServiceConfigEnd2endTest, NoServiceConfigTest) {
+  StartServers(1);
+  auto channel = BuildChannel();
+  auto stub = BuildStub(channel);
+  SetNextResolutionNoServiceConfig(GetServersPorts());
+  CheckRpcSendOk(stub, DEBUG_LOCATION);
+  EXPECT_STREQ("", channel->GetServiceConfigJSON().c_str());
+}
+
+TEST_F(ServiceConfigEnd2endTest, NoServiceConfigWithDefaultConfigTest) {
+  StartServers(1);
+  auto channel = BuildChannelWithDefaultServiceConfig();
+  auto stub = BuildStub(channel);
+  SetNextResolutionNoServiceConfig(GetServersPorts());
+  CheckRpcSendOk(stub, DEBUG_LOCATION);
+  EXPECT_STREQ(ValidDefaultServiceConfig(),
+               channel->GetServiceConfigJSON().c_str());
+}
+
+TEST_F(ServiceConfigEnd2endTest, InvalidServiceConfigTest) {
+  StartServers(1);
+  auto channel = BuildChannel();
+  auto stub = BuildStub(channel);
+  SetNextResolutionInvalidServiceConfig(GetServersPorts());
+  CheckRpcSendFailure(stub);
+}
+
+TEST_F(ServiceConfigEnd2endTest, InvalidServiceConfigWithDefaultConfigTest) {
+  StartServers(1);
+  auto channel = BuildChannelWithDefaultServiceConfig();
+  auto stub = BuildStub(channel);
+  SetNextResolutionInvalidServiceConfig(GetServersPorts());
+  CheckRpcSendOk(stub, DEBUG_LOCATION);
+  EXPECT_STREQ(ValidDefaultServiceConfig(),
+               channel->GetServiceConfigJSON().c_str());
+}
+
+TEST_F(ServiceConfigEnd2endTest, ValidServiceConfigUpdatesTest) {
+  StartServers(1);
+  auto channel = BuildChannel();
+  auto stub = BuildStub(channel);
+  SetNextResolutionWithServiceConfig(GetServersPorts(), ValidServiceConfigV1());
+  CheckRpcSendOk(stub, DEBUG_LOCATION);
+  EXPECT_STREQ(ValidServiceConfigV1(), channel->GetServiceConfigJSON().c_str());
+  SetNextResolutionWithServiceConfig(GetServersPorts(), ValidServiceConfigV2());
+  CheckRpcSendOk(stub, DEBUG_LOCATION);
+  EXPECT_STREQ(ValidServiceConfigV2(), channel->GetServiceConfigJSON().c_str());
+}
+
+TEST_F(ServiceConfigEnd2endTest,
+       NoServiceConfigUpdateAfterValidServiceConfigTest) {
+  StartServers(1);
+  auto channel = BuildChannel();
+  auto stub = BuildStub(channel);
+  SetNextResolutionWithServiceConfig(GetServersPorts(), ValidServiceConfigV1());
+  CheckRpcSendOk(stub, DEBUG_LOCATION);
+  EXPECT_STREQ(ValidServiceConfigV1(), channel->GetServiceConfigJSON().c_str());
+  SetNextResolutionNoServiceConfig(GetServersPorts());
+  CheckRpcSendOk(stub, DEBUG_LOCATION);
+  EXPECT_STREQ("", channel->GetServiceConfigJSON().c_str());
+}
+
+TEST_F(ServiceConfigEnd2endTest,
+       NoServiceConfigUpdateAfterValidServiceConfigWithDefaultConfigTest) {
+  StartServers(1);
+  auto channel = BuildChannelWithDefaultServiceConfig();
+  auto stub = BuildStub(channel);
+  SetNextResolutionWithServiceConfig(GetServersPorts(), ValidServiceConfigV1());
+  CheckRpcSendOk(stub, DEBUG_LOCATION);
+  EXPECT_STREQ(ValidServiceConfigV1(), channel->GetServiceConfigJSON().c_str());
+  SetNextResolutionNoServiceConfig(GetServersPorts());
+  CheckRpcSendOk(stub, DEBUG_LOCATION);
+  EXPECT_STREQ(ValidDefaultServiceConfig(),
+               channel->GetServiceConfigJSON().c_str());
+}
+
+TEST_F(ServiceConfigEnd2endTest,
+       InvalidServiceConfigUpdateAfterValidServiceConfigTest) {
+  StartServers(1);
+  auto channel = BuildChannel();
+  auto stub = BuildStub(channel);
+  SetNextResolutionWithServiceConfig(GetServersPorts(), ValidServiceConfigV1());
+  CheckRpcSendOk(stub, DEBUG_LOCATION);
+  EXPECT_STREQ(ValidServiceConfigV1(), channel->GetServiceConfigJSON().c_str());
+  SetNextResolutionInvalidServiceConfig(GetServersPorts());
+  CheckRpcSendOk(stub, DEBUG_LOCATION);
+  EXPECT_STREQ(ValidServiceConfigV1(), channel->GetServiceConfigJSON().c_str());
+}
+
+TEST_F(ServiceConfigEnd2endTest,
+       InvalidServiceConfigUpdateAfterValidServiceConfigWithDefaultConfigTest) {
+  StartServers(1);
+  auto channel = BuildChannelWithDefaultServiceConfig();
+  auto stub = BuildStub(channel);
+  SetNextResolutionWithServiceConfig(GetServersPorts(), ValidServiceConfigV1());
+  CheckRpcSendOk(stub, DEBUG_LOCATION);
+  EXPECT_STREQ(ValidServiceConfigV1(), channel->GetServiceConfigJSON().c_str());
+  SetNextResolutionInvalidServiceConfig(GetServersPorts());
+  CheckRpcSendOk(stub, DEBUG_LOCATION);
+  EXPECT_STREQ(ValidServiceConfigV1(), channel->GetServiceConfigJSON().c_str());
+}
+
+TEST_F(ServiceConfigEnd2endTest,
+       ValidServiceConfigAfterInvalidServiceConfigTest) {
+  StartServers(1);
+  auto channel = BuildChannel();
+  auto stub = BuildStub(channel);
+  SetNextResolutionInvalidServiceConfig(GetServersPorts());
+  CheckRpcSendFailure(stub);
+  SetNextResolutionValidServiceConfig(GetServersPorts());
+  CheckRpcSendOk(stub, DEBUG_LOCATION);
+}
+
+TEST_F(ServiceConfigEnd2endTest, NoServiceConfigAfterInvalidServiceConfigTest) {
+  StartServers(1);
+  auto channel = BuildChannel();
+  auto stub = BuildStub(channel);
+  SetNextResolutionInvalidServiceConfig(GetServersPorts());
+  CheckRpcSendFailure(stub);
+  SetNextResolutionNoServiceConfig(GetServersPorts());
+  CheckRpcSendOk(stub, DEBUG_LOCATION);
+  EXPECT_STREQ("", channel->GetServiceConfigJSON().c_str());
+}
+
+TEST_F(ServiceConfigEnd2endTest,
+       AnotherInvalidServiceConfigAfterInvalidServiceConfigTest) {
+  StartServers(1);
+  auto channel = BuildChannel();
+  auto stub = BuildStub(channel);
+  SetNextResolutionInvalidServiceConfig(GetServersPorts());
+  CheckRpcSendFailure(stub);
+  SetNextResolutionInvalidServiceConfig(GetServersPorts());
+  CheckRpcSendFailure(stub);
+}
+
+}  // namespace
+}  // namespace testing
+}  // namespace grpc
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  grpc::testing::TestEnvironment env(argc, argv);
+  const auto result = RUN_ALL_TESTS();
+  return result;
+}
index da42178..9e92536 100644 (file)
@@ -86,7 +86,7 @@ class ShutdownTest : public ::testing::TestWithParam<string> {
     ChannelArguments args;
     auto channel_creds =
         GetCredentialsProvider()->GetChannelCredentials(GetParam(), &args);
-    channel_ = CreateCustomChannel(target, channel_creds, args);
+    channel_ = ::grpc::CreateCustomChannel(target, channel_creds, args);
     stub_ = grpc::testing::EchoTestService::NewStub(channel_);
   }
 
index 4406565..0c10957 100644 (file)
@@ -145,8 +145,8 @@ class End2endTest : public ::testing::Test {
   void TearDown() override { server_->Shutdown(); }
 
   void ResetStub() {
-    std::shared_ptr<Channel> channel =
-        CreateChannel(server_address_.str(), InsecureChannelCredentials());
+    std::shared_ptr<Channel> channel = grpc::CreateChannel(
+        server_address_.str(), InsecureChannelCredentials());
     stub_ = grpc::testing::EchoTestService::NewStub(channel);
   }
 
index abbb669..4078cdf 100644 (file)
@@ -143,7 +143,6 @@ void LoopUntilCancelled(Alarm* alarm, ServerContext* context,
 
 Status TestServiceImpl::Echo(ServerContext* context, const EchoRequest* request,
                              EchoResponse* response) {
-  gpr_log(GPR_DEBUG, "Request message was %s", request->message().c_str());
   // A bit of sleep to make sure that short deadline tests fail
   if (request->has_param() && request->param().server_sleep_us() > 0) {
     gpr_sleep_until(
@@ -201,6 +200,17 @@ Status TestServiceImpl::Echo(ServerContext* context, const EchoRequest* request,
     EXPECT_FALSE(context->IsCancelled());
   }
 
+  if (request->has_param() && request->param().echo_metadata_initially()) {
+    const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata =
+        context->client_metadata();
+    for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator
+             iter = client_metadata.begin();
+         iter != client_metadata.end(); ++iter) {
+      context->AddInitialMetadata(ToString(iter->first),
+                                  ToString(iter->second));
+    }
+  }
+
   if (request->has_param() && request->param().echo_metadata()) {
     const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata =
         context->client_metadata();
@@ -380,6 +390,18 @@ void CallbackTestServiceImpl::EchoNonDelayed(
     EXPECT_FALSE(context->IsCancelled());
   }
 
+  if (request->has_param() && request->param().echo_metadata_initially()) {
+    const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata =
+        context->client_metadata();
+    for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator
+             iter = client_metadata.begin();
+         iter != client_metadata.end(); ++iter) {
+      context->AddInitialMetadata(ToString(iter->first),
+                                  ToString(iter->second));
+    }
+    controller->SendInitialMetadata([](bool ok) { EXPECT_TRUE(ok); });
+  }
+
   if (request->has_param() && request->param().echo_metadata()) {
     const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata =
         context->client_metadata();
@@ -590,8 +612,9 @@ CallbackTestServiceImpl::RequestStream() {
    public:
     Reactor() {}
     void OnStarted(ServerContext* context, EchoResponse* response) override {
-      ctx_ = context;
-      response_ = response;
+      // Assign ctx_ and response_ as late as possible to increase likelihood of
+      // catching any races
+
       // If 'server_try_cancel' is set in the metadata, the RPC is cancelled by
       // the server by calling ServerContext::TryCancel() depending on the
       // value:
@@ -603,22 +626,26 @@ CallbackTestServiceImpl::RequestStream() {
       server_try_cancel_ = GetIntValueFromMetadata(
           kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL);
 
-      response_->set_message("");
+      response->set_message("");
 
       if (server_try_cancel_ == CANCEL_BEFORE_PROCESSING) {
-        ServerTryCancelNonblocking(ctx_);
-        return;
-      }
-
-      if (server_try_cancel_ == CANCEL_DURING_PROCESSING) {
-        ctx_->TryCancel();
-        // Don't wait for it here
+        ServerTryCancelNonblocking(context);
+        ctx_ = context;
+      } else {
+        if (server_try_cancel_ == CANCEL_DURING_PROCESSING) {
+          context->TryCancel();
+          // Don't wait for it here
+        }
+        ctx_ = context;
+        response_ = response;
+        StartRead(&request_);
       }
 
-      StartRead(&request_);
+      on_started_done_ = true;
     }
     void OnDone() override { delete this; }
     void OnCancel() override {
+      EXPECT_TRUE(on_started_done_);
       EXPECT_TRUE(ctx_->IsCancelled());
       FinishOnce(Status::CANCELLED);
     }
@@ -658,6 +685,7 @@ CallbackTestServiceImpl::RequestStream() {
     int server_try_cancel_;
     std::mutex finish_mu_;
     bool finished_{false};
+    bool on_started_done_{false};
   };
 
   return new Reactor;
@@ -674,8 +702,9 @@ CallbackTestServiceImpl::ResponseStream() {
     Reactor() {}
     void OnStarted(ServerContext* context,
                    const EchoRequest* request) override {
-      ctx_ = context;
-      request_ = request;
+      // Assign ctx_ and request_ as late as possible to increase likelihood of
+      // catching any races
+
       // If 'server_try_cancel' is set in the metadata, the RPC is cancelled by
       // the server by calling ServerContext::TryCancel() depending on the
       // value:
@@ -692,19 +721,23 @@ CallbackTestServiceImpl::ResponseStream() {
           kServerResponseStreamsToSend, context->client_metadata(),
           kServerDefaultResponseStreamsToSend);
       if (server_try_cancel_ == CANCEL_BEFORE_PROCESSING) {
-        ServerTryCancelNonblocking(ctx_);
-        return;
-      }
-
-      if (server_try_cancel_ == CANCEL_DURING_PROCESSING) {
-        ctx_->TryCancel();
-      }
-      if (num_msgs_sent_ < server_responses_to_send_) {
-        NextWrite();
+        ServerTryCancelNonblocking(context);
+        ctx_ = context;
+      } else {
+        if (server_try_cancel_ == CANCEL_DURING_PROCESSING) {
+          context->TryCancel();
+        }
+        ctx_ = context;
+        request_ = request;
+        if (num_msgs_sent_ < server_responses_to_send_) {
+          NextWrite();
+        }
       }
+      on_started_done_ = true;
     }
     void OnDone() override { delete this; }
     void OnCancel() override {
+      EXPECT_TRUE(on_started_done_);
       EXPECT_TRUE(ctx_->IsCancelled());
       FinishOnce(Status::CANCELLED);
     }
@@ -754,6 +787,7 @@ CallbackTestServiceImpl::ResponseStream() {
     int server_responses_to_send_;
     std::mutex finish_mu_;
     bool finished_{false};
+    bool on_started_done_{false};
   };
   return new Reactor;
 }
@@ -765,7 +799,9 @@ CallbackTestServiceImpl::BidiStream() {
    public:
     Reactor() {}
     void OnStarted(ServerContext* context) override {
-      ctx_ = context;
+      // Assign ctx_ as late as possible to increase likelihood of catching any
+      // races
+
       // If 'server_try_cancel' is set in the metadata, the RPC is cancelled by
       // the server by calling ServerContext::TryCancel() depending on the
       // value:
@@ -779,18 +815,20 @@ CallbackTestServiceImpl::BidiStream() {
       server_write_last_ = GetIntValueFromMetadata(
           kServerFinishAfterNReads, context->client_metadata(), 0);
       if (server_try_cancel_ == CANCEL_BEFORE_PROCESSING) {
-        ServerTryCancelNonblocking(ctx_);
-        return;
-      }
-
-      if (server_try_cancel_ == CANCEL_DURING_PROCESSING) {
-        ctx_->TryCancel();
+        ServerTryCancelNonblocking(context);
+        ctx_ = context;
+      } else {
+        if (server_try_cancel_ == CANCEL_DURING_PROCESSING) {
+          context->TryCancel();
+        }
+        ctx_ = context;
+        StartRead(&request_);
       }
-
-      StartRead(&request_);
+      on_started_done_ = true;
     }
     void OnDone() override { delete this; }
     void OnCancel() override {
+      EXPECT_TRUE(on_started_done_);
       EXPECT_TRUE(ctx_->IsCancelled());
       FinishOnce(Status::CANCELLED);
     }
@@ -840,6 +878,7 @@ CallbackTestServiceImpl::BidiStream() {
     int server_write_last_;
     std::mutex finish_mu_;
     bool finished_{false};
+    bool on_started_done_{false};
   };
 
   return new Reactor;
index e30ce0d..eb8e795 100644 (file)
@@ -25,6 +25,7 @@
 #include <grpcpp/channel.h>
 #include <grpcpp/client_context.h>
 #include <grpcpp/create_channel.h>
+#include <grpcpp/impl/codegen/sync.h>
 #include <grpcpp/resource_quota.h>
 #include <grpcpp/server.h>
 #include <grpcpp/server_builder.h>
@@ -95,8 +96,8 @@ template <class Service>
 class CommonStressTestInsecure : public CommonStressTest<Service> {
  public:
   void ResetStub() override {
-    std::shared_ptr<Channel> channel =
-        CreateChannel(server_address_.str(), InsecureChannelCredentials());
+    std::shared_ptr<Channel> channel = grpc::CreateChannel(
+        server_address_.str(), InsecureChannelCredentials());
     this->stub_ = grpc::testing::EchoTestService::NewStub(channel);
   }
   bool AllowExhaustion() override { return false; }
@@ -188,7 +189,7 @@ class CommonStressTestAsyncServer : public BaseClass {
   }
   void TearDown() override {
     {
-      std::unique_lock<std::mutex> l(mu_);
+      grpc::internal::MutexLock l(&mu_);
       this->TearDownStart();
       shutting_down_ = true;
       cq_->Shutdown();
@@ -229,7 +230,7 @@ class CommonStressTestAsyncServer : public BaseClass {
     }
   }
   void RefreshContext(int i) {
-    std::unique_lock<std::mutex> l(mu_);
+    grpc::internal::MutexLock l(&mu_);
     if (!shutting_down_) {
       contexts_[i].state = Context::READY;
       contexts_[i].srv_ctx.reset(new ServerContext);
@@ -253,7 +254,7 @@ class CommonStressTestAsyncServer : public BaseClass {
   ::grpc::testing::EchoTestService::AsyncService service_;
   std::unique_ptr<ServerCompletionQueue> cq_;
   bool shutting_down_;
-  std::mutex mu_;
+  grpc::internal::Mutex mu_;
   std::vector<std::thread> server_threads_;
 };
 
@@ -341,9 +342,9 @@ class AsyncClientEnd2endTest : public ::testing::Test {
   }
 
   void Wait() {
-    std::unique_lock<std::mutex> l(mu_);
+    grpc::internal::MutexLock l(&mu_);
     while (rpcs_outstanding_ != 0) {
-      cv_.wait(l);
+      cv_.Wait(&mu_);
     }
 
     cq_.Shutdown();
@@ -366,7 +367,7 @@ class AsyncClientEnd2endTest : public ::testing::Test {
       call->response_reader->Finish(&call->response, &call->status,
                                     (void*)call);
 
-      std::unique_lock<std::mutex> l(mu_);
+      grpc::internal::MutexLock l(&mu_);
       rpcs_outstanding_++;
     }
   }
@@ -384,20 +385,20 @@ class AsyncClientEnd2endTest : public ::testing::Test {
 
       bool notify;
       {
-        std::unique_lock<std::mutex> l(mu_);
+        grpc::internal::MutexLock l(&mu_);
         rpcs_outstanding_--;
         notify = (rpcs_outstanding_ == 0);
       }
       if (notify) {
-        cv_.notify_all();
+        cv_.Signal();
       }
     }
   }
 
   Common common_;
   CompletionQueue cq_;
-  std::mutex mu_;
-  std::condition_variable cv_;
+  grpc::internal::Mutex mu_;
+  grpc::internal::CondVar cv_;
   int rpcs_outstanding_;
 };
 
index 7f4e3ca..688549e 100644 (file)
@@ -139,7 +139,7 @@ class TimeChangeTest : public ::testing::Test {
         "--address=" + addr,
     }));
     GPR_ASSERT(server_);
-    channel_ = CreateChannel(addr, InsecureChannelCredentials());
+    channel_ = grpc::CreateChannel(addr, InsecureChannelCredentials());
     GPR_ASSERT(channel_);
     stub_ = grpc::testing::EchoTestService::NewStub(channel_);
   }
index 61e759c..b876e06 100644 (file)
@@ -33,6 +33,7 @@
 #include <grpcpp/server.h>
 #include <grpcpp/server_builder.h>
 
+#include "src/core/ext/filters/client_channel/backup_poller.h"
 #include "src/core/ext/filters/client_channel/parse_address.h"
 #include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"
 #include "src/core/ext/filters/client_channel/server_address.h"
@@ -84,32 +85,32 @@ template <typename ServiceType>
 class CountedService : public ServiceType {
  public:
   size_t request_count() {
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     return request_count_;
   }
 
   size_t response_count() {
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     return response_count_;
   }
 
   void IncreaseResponseCount() {
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     ++response_count_;
   }
   void IncreaseRequestCount() {
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     ++request_count_;
   }
 
   void ResetCounters() {
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     request_count_ = 0;
     response_count_ = 0;
   }
 
  protected:
-  std::mutex mu_;
+  grpc::internal::Mutex mu_;
 
  private:
   size_t request_count_ = 0;
@@ -145,18 +146,18 @@ class BackendServiceImpl : public BackendService {
   void Shutdown() {}
 
   std::set<grpc::string> clients() {
-    std::unique_lock<std::mutex> lock(clients_mu_);
+    grpc::internal::MutexLock lock(&clients_mu_);
     return clients_;
   }
 
  private:
   void AddClient(const grpc::string& client) {
-    std::unique_lock<std::mutex> lock(clients_mu_);
+    grpc::internal::MutexLock lock(&clients_mu_);
     clients_.insert(client);
   }
 
-  std::mutex mu_;
-  std::mutex clients_mu_;
+  grpc::internal::Mutex mu_;
+  grpc::internal::Mutex clients_mu_;
   std::set<grpc::string> clients_;
 };
 
@@ -208,6 +209,10 @@ class BalancerServiceImpl : public BalancerService {
     // TODO(juanlishen): Clean up the scoping.
     gpr_log(GPR_INFO, "LB[%p]: BalanceLoad", this);
     {
+      grpc::internal::MutexLock lock(&mu_);
+      if (serverlist_done_) goto done;
+    }
+    {
       // Balancer shouldn't receive the call credentials metadata.
       EXPECT_EQ(context->client_metadata().find(g_kCallCredsMdKey),
                 context->client_metadata().end());
@@ -230,7 +235,7 @@ class BalancerServiceImpl : public BalancerService {
       }
 
       {
-        std::unique_lock<std::mutex> lock(mu_);
+        grpc::internal::MutexLock lock(&mu_);
         responses_and_delays = responses_and_delays_;
       }
       for (const auto& response_and_delay : responses_and_delays) {
@@ -238,8 +243,8 @@ class BalancerServiceImpl : public BalancerService {
                      response_and_delay.second);
       }
       {
-        std::unique_lock<std::mutex> lock(mu_);
-        serverlist_cond_.wait(lock, [this] { return serverlist_done_; });
+        grpc::internal::MutexLock lock(&mu_);
+        serverlist_cond_.WaitUntil(&mu_, [this] { return serverlist_done_; });
       }
 
       if (client_load_reporting_interval_seconds_ > 0) {
@@ -250,7 +255,7 @@ class BalancerServiceImpl : public BalancerService {
           GPR_ASSERT(request.has_client_stats());
           // We need to acquire the lock here in order to prevent the notify_one
           // below from firing before its corresponding wait is executed.
-          std::lock_guard<std::mutex> lock(mu_);
+          grpc::internal::MutexLock lock(&mu_);
           client_stats_.num_calls_started +=
               request.client_stats().num_calls_started();
           client_stats_.num_calls_finished +=
@@ -267,7 +272,7 @@ class BalancerServiceImpl : public BalancerService {
                 drop_token_count.num_calls();
           }
           load_report_ready_ = true;
-          load_report_cond_.notify_one();
+          load_report_cond_.Signal();
         }
       }
     }
@@ -277,12 +282,12 @@ class BalancerServiceImpl : public BalancerService {
   }
 
   void add_response(const LoadBalanceResponse& response, int send_after_ms) {
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     responses_and_delays_.push_back(std::make_pair(response, send_after_ms));
   }
 
   void Shutdown() {
-    std::unique_lock<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     NotifyDoneWithServerlistsLocked();
     responses_and_delays_.clear();
     client_stats_.Reset();
@@ -314,21 +319,21 @@ class BalancerServiceImpl : public BalancerService {
   }
 
   const ClientStats& WaitForLoadReport() {
-    std::unique_lock<std::mutex> lock(mu_);
-    load_report_cond_.wait(lock, [this] { return load_report_ready_; });
+    grpc::internal::MutexLock lock(&mu_);
+    load_report_cond_.WaitUntil(&mu_, [this] { return load_report_ready_; });
     load_report_ready_ = false;
     return client_stats_;
   }
 
   void NotifyDoneWithServerlists() {
-    std::lock_guard<std::mutex> lock(mu_);
+    grpc::internal::MutexLock lock(&mu_);
     NotifyDoneWithServerlistsLocked();
   }
 
   void NotifyDoneWithServerlistsLocked() {
     if (!serverlist_done_) {
       serverlist_done_ = true;
-      serverlist_cond_.notify_all();
+      serverlist_cond_.Broadcast();
     }
   }
 
@@ -347,10 +352,10 @@ class BalancerServiceImpl : public BalancerService {
 
   const int client_load_reporting_interval_seconds_;
   std::vector<ResponseDelayPair> responses_and_delays_;
-  std::mutex mu_;
-  std::condition_variable load_report_cond_;
+  grpc::internal::Mutex mu_;
+  grpc::internal::CondVar load_report_cond_;
   bool load_report_ready_ = false;
-  std::condition_variable serverlist_cond_;
+  grpc::internal::CondVar serverlist_cond_;
   bool serverlist_done_ = false;
   ClientStats client_stats_;
 };
@@ -366,7 +371,7 @@ class XdsEnd2endTest : public ::testing::Test {
             client_load_reporting_interval_seconds) {
     // Make the backup poller poll very frequently in order to pick up
     // updates from all the subchannels's FDs.
-    gpr_setenv("GRPC_CLIENT_CHANNEL_BACKUP_POLL_INTERVAL_MS", "1");
+    GPR_GLOBAL_CONFIG_SET(grpc_client_channel_backup_poll_interval_ms, 1);
   }
 
   void SetUp() override {
@@ -409,7 +414,9 @@ class XdsEnd2endTest : public ::testing::Test {
                  const grpc::string& expected_targets = "") {
     ChannelArguments args;
     // TODO(juanlishen): Add setter to ChannelArguments.
-    args.SetInt(GRPC_ARG_XDS_FALLBACK_TIMEOUT_MS, fallback_timeout);
+    if (fallback_timeout > 0) {
+      args.SetInt(GRPC_ARG_XDS_FALLBACK_TIMEOUT_MS, fallback_timeout);
+    }
     args.SetPointer(GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR,
                     response_generator_.get());
     if (!expected_targets.empty()) {
@@ -428,7 +435,7 @@ class XdsEnd2endTest : public ::testing::Test {
             channel_creds, call_creds, nullptr)));
     call_creds->Unref();
     channel_creds->Unref();
-    channel_ = CreateCustomChannel(uri.str(), creds, args);
+    channel_ = ::grpc::CreateCustomChannel(uri.str(), creds, args);
     stub_ = grpc::testing::EchoTestService::NewStub(channel_);
   }
 
@@ -524,8 +531,10 @@ class XdsEnd2endTest : public ::testing::Test {
     grpc_core::Resolver::Result result;
     result.addresses = CreateLbAddressesFromPortList(ports);
     if (service_config_json != nullptr) {
+      grpc_error* error = GRPC_ERROR_NONE;
       result.service_config =
-          grpc_core::ServiceConfig::Create(service_config_json);
+          grpc_core::ServiceConfig::Create(service_config_json, &error);
+      GRPC_ERROR_UNREF(error);
     }
     grpc_arg arg = grpc_core::FakeResolverResponseGenerator::MakeChannelArg(
         lb_channel_response_generator == nullptr
@@ -555,8 +564,10 @@ class XdsEnd2endTest : public ::testing::Test {
     grpc_core::Resolver::Result result;
     result.addresses = CreateLbAddressesFromPortList(ports);
     if (service_config_json != nullptr) {
+      grpc_error* error = GRPC_ERROR_NONE;
       result.service_config =
-          grpc_core::ServiceConfig::Create(service_config_json);
+          grpc_core::ServiceConfig::Create(service_config_json, &error);
+      GRPC_ERROR_UNREF(error);
     }
     if (lb_channel_response_generator == nullptr) {
       lb_channel_response_generator = lb_channel_response_generator_.get();
@@ -629,22 +640,22 @@ class XdsEnd2endTest : public ::testing::Test {
       gpr_log(GPR_INFO, "starting %s server on port %d", type_.c_str(), port_);
       GPR_ASSERT(!running_);
       running_ = true;
-      std::mutex mu;
+      grpc::internal::Mutex mu;
       // We need to acquire the lock here in order to prevent the notify_one
       // by ServerThread::Serve from firing before the wait below is hit.
-      std::unique_lock<std::mutex> lock(mu);
-      std::condition_variable cond;
+      grpc::internal::MutexLock lock(&mu);
+      grpc::internal::CondVar cond;
       thread_.reset(new std::thread(
           std::bind(&ServerThread::Serve, this, server_host, &mu, &cond)));
-      cond.wait(lock);
+      cond.Wait(&mu);
       gpr_log(GPR_INFO, "%s server startup complete", type_.c_str());
     }
 
-    void Serve(const grpc::string& server_host, std::mutex* mu,
-               std::condition_variable* cond) {
+    void Serve(const grpc::string& server_host, grpc::internal::Mutex* mu,
+               grpc::internal::CondVar* cond) {
       // We need to acquire the lock here in order to prevent the notify_one
       // below from firing before its corresponding wait is executed.
-      std::lock_guard<std::mutex> lock(*mu);
+      grpc::internal::MutexLock lock(mu);
       std::ostringstream server_address;
       server_address << server_host << ":" << port_;
       ServerBuilder builder;
@@ -653,7 +664,7 @@ class XdsEnd2endTest : public ::testing::Test {
       builder.AddListeningPort(server_address.str(), creds);
       builder.RegisterService(&service_);
       server_ = builder.BuildAndStart();
-      cond->notify_one();
+      cond->Signal();
     }
 
     void Shutdown() {
@@ -847,38 +858,228 @@ TEST_F(SingleBalancerTest, AllServersUnreachableFailFast) {
   EXPECT_EQ(1U, balancers_[0]->service_.response_count());
 }
 
-// The fallback tests are deferred because the fallback mode hasn't been
-// supported yet.
+TEST_F(SingleBalancerTest, Fallback) {
+  const int kFallbackTimeoutMs = 200 * grpc_test_slowdown_factor();
+  const int kServerlistDelayMs = 500 * grpc_test_slowdown_factor();
+  const size_t kNumBackendsInResolution = backends_.size() / 2;
+  ResetStub(kFallbackTimeoutMs);
+  SetNextResolution(GetBackendPorts(0, kNumBackendsInResolution),
+                    kDefaultServiceConfig_.c_str());
+  SetNextResolutionForLbChannelAllBalancers();
+  // Send non-empty serverlist only after kServerlistDelayMs.
+  ScheduleResponseForBalancer(
+      0,
+      BalancerServiceImpl::BuildResponseForBackends(
+          GetBackendPorts(kNumBackendsInResolution /* start_index */), {}),
+      kServerlistDelayMs);
+  // Wait until all the fallback backends are reachable.
+  WaitForAllBackends(1 /* num_requests_multiple_of */, 0 /* start_index */,
+                     kNumBackendsInResolution /* stop_index */);
+  gpr_log(GPR_INFO, "========= BEFORE FIRST BATCH ==========");
+  CheckRpcSendOk(kNumBackendsInResolution);
+  gpr_log(GPR_INFO, "========= DONE WITH FIRST BATCH ==========");
+  // Fallback is used: each backend returned by the resolver should have
+  // gotten one request.
+  for (size_t i = 0; i < kNumBackendsInResolution; ++i) {
+    EXPECT_EQ(1U, backends_[i]->service_.request_count());
+  }
+  for (size_t i = kNumBackendsInResolution; i < backends_.size(); ++i) {
+    EXPECT_EQ(0U, backends_[i]->service_.request_count());
+  }
+  // Wait until the serverlist reception has been processed and all backends
+  // in the serverlist are reachable.
+  WaitForAllBackends(1 /* num_requests_multiple_of */,
+                     kNumBackendsInResolution /* start_index */);
+  gpr_log(GPR_INFO, "========= BEFORE SECOND BATCH ==========");
+  CheckRpcSendOk(backends_.size() - kNumBackendsInResolution);
+  gpr_log(GPR_INFO, "========= DONE WITH SECOND BATCH ==========");
+  // Serverlist is used: each backend returned by the balancer should
+  // have gotten one request.
+  for (size_t i = 0; i < kNumBackendsInResolution; ++i) {
+    EXPECT_EQ(0U, backends_[i]->service_.request_count());
+  }
+  for (size_t i = kNumBackendsInResolution; i < backends_.size(); ++i) {
+    EXPECT_EQ(1U, backends_[i]->service_.request_count());
+  }
+  // The balancer got a single request.
+  EXPECT_EQ(1U, balancers_[0]->service_.request_count());
+  // and sent a single response.
+  EXPECT_EQ(1U, balancers_[0]->service_.response_count());
+}
 
-// TODO(juanlishen): Add TEST_F(SingleBalancerTest, Fallback)
+TEST_F(SingleBalancerTest, FallbackUpdate) {
+  const int kFallbackTimeoutMs = 200 * grpc_test_slowdown_factor();
+  const int kServerlistDelayMs = 500 * grpc_test_slowdown_factor();
+  const size_t kNumBackendsInResolution = backends_.size() / 3;
+  const size_t kNumBackendsInResolutionUpdate = backends_.size() / 3;
+  ResetStub(kFallbackTimeoutMs);
+  SetNextResolution(GetBackendPorts(0, kNumBackendsInResolution),
+                    kDefaultServiceConfig_.c_str());
+  SetNextResolutionForLbChannelAllBalancers();
+  // Send non-empty serverlist only after kServerlistDelayMs.
+  ScheduleResponseForBalancer(
+      0,
+      BalancerServiceImpl::BuildResponseForBackends(
+          GetBackendPorts(kNumBackendsInResolution +
+                          kNumBackendsInResolutionUpdate /* start_index */),
+          {}),
+      kServerlistDelayMs);
+  // Wait until all the fallback backends are reachable.
+  WaitForAllBackends(1 /* num_requests_multiple_of */, 0 /* start_index */,
+                     kNumBackendsInResolution /* stop_index */);
+  gpr_log(GPR_INFO, "========= BEFORE FIRST BATCH ==========");
+  CheckRpcSendOk(kNumBackendsInResolution);
+  gpr_log(GPR_INFO, "========= DONE WITH FIRST BATCH ==========");
+  // Fallback is used: each backend returned by the resolver should have
+  // gotten one request.
+  for (size_t i = 0; i < kNumBackendsInResolution; ++i) {
+    EXPECT_EQ(1U, backends_[i]->service_.request_count());
+  }
+  for (size_t i = kNumBackendsInResolution; i < backends_.size(); ++i) {
+    EXPECT_EQ(0U, backends_[i]->service_.request_count());
+  }
+  SetNextResolution(GetBackendPorts(kNumBackendsInResolution,
+                                    kNumBackendsInResolution +
+                                        kNumBackendsInResolutionUpdate),
+                    kDefaultServiceConfig_.c_str());
+  // Wait until the resolution update has been processed and all the new
+  // fallback backends are reachable.
+  WaitForAllBackends(1 /* num_requests_multiple_of */,
+                     kNumBackendsInResolution /* start_index */,
+                     kNumBackendsInResolution +
+                         kNumBackendsInResolutionUpdate /* stop_index */);
+  gpr_log(GPR_INFO, "========= BEFORE SECOND BATCH ==========");
+  CheckRpcSendOk(kNumBackendsInResolutionUpdate);
+  gpr_log(GPR_INFO, "========= DONE WITH SECOND BATCH ==========");
+  // The resolution update is used: each backend in the resolution update should
+  // have gotten one request.
+  for (size_t i = 0; i < kNumBackendsInResolution; ++i) {
+    EXPECT_EQ(0U, backends_[i]->service_.request_count());
+  }
+  for (size_t i = kNumBackendsInResolution;
+       i < kNumBackendsInResolution + kNumBackendsInResolutionUpdate; ++i) {
+    EXPECT_EQ(1U, backends_[i]->service_.request_count());
+  }
+  for (size_t i = kNumBackendsInResolution + kNumBackendsInResolutionUpdate;
+       i < backends_.size(); ++i) {
+    EXPECT_EQ(0U, backends_[i]->service_.request_count());
+  }
+  // Wait until the serverlist reception has been processed and all backends
+  // in the serverlist are reachable.
+  WaitForAllBackends(1 /* num_requests_multiple_of */,
+                     kNumBackendsInResolution +
+                         kNumBackendsInResolutionUpdate /* start_index */);
+  gpr_log(GPR_INFO, "========= BEFORE THIRD BATCH ==========");
+  CheckRpcSendOk(backends_.size() - kNumBackendsInResolution -
+                 kNumBackendsInResolutionUpdate);
+  gpr_log(GPR_INFO, "========= DONE WITH THIRD BATCH ==========");
+  // Serverlist is used: each backend returned by the balancer should
+  // have gotten one request.
+  for (size_t i = 0;
+       i < kNumBackendsInResolution + kNumBackendsInResolutionUpdate; ++i) {
+    EXPECT_EQ(0U, backends_[i]->service_.request_count());
+  }
+  for (size_t i = kNumBackendsInResolution + kNumBackendsInResolutionUpdate;
+       i < backends_.size(); ++i) {
+    EXPECT_EQ(1U, backends_[i]->service_.request_count());
+  }
+  // The balancer got a single request.
+  EXPECT_EQ(1U, balancers_[0]->service_.request_count());
+  // and sent a single response.
+  EXPECT_EQ(1U, balancers_[0]->service_.response_count());
+}
+
+TEST_F(SingleBalancerTest, FallbackEarlyWhenBalancerChannelFails) {
+  const int kFallbackTimeoutMs = 10000 * grpc_test_slowdown_factor();
+  ResetStub(kFallbackTimeoutMs);
+  // Return an unreachable balancer and one fallback backend.
+  SetNextResolution({backends_[0]->port_}, kDefaultServiceConfig_.c_str());
+  SetNextResolutionForLbChannel({grpc_pick_unused_port_or_die()});
+  // Send RPC with deadline less than the fallback timeout and make sure it
+  // succeeds.
+  CheckRpcSendOk(/* times */ 1, /* timeout_ms */ 1000,
+                 /* wait_for_ready */ false);
+}
 
-// TODO(juanlishen): Add TEST_F(SingleBalancerTest, FallbackUpdate)
+TEST_F(SingleBalancerTest, FallbackEarlyWhenBalancerCallFails) {
+  const int kFallbackTimeoutMs = 10000 * grpc_test_slowdown_factor();
+  ResetStub(kFallbackTimeoutMs);
+  // Return one balancer and one fallback backend.
+  SetNextResolution({backends_[0]->port_}, kDefaultServiceConfig_.c_str());
+  SetNextResolutionForLbChannelAllBalancers();
+  // Balancer drops call without sending a serverlist.
+  balancers_[0]->service_.NotifyDoneWithServerlists();
+  // Send RPC with deadline less than the fallback timeout and make sure it
+  // succeeds.
+  CheckRpcSendOk(/* times */ 1, /* timeout_ms */ 1000,
+                 /* wait_for_ready */ false);
+}
 
-// TODO(juanlishen): Add TEST_F(SingleBalancerTest,
-// FallbackEarlyWhenBalancerChannelFails)
+TEST_F(SingleBalancerTest, FallbackModeIsExitedWhenBalancerSaysToDropAllCalls) {
+  // Return an unreachable balancer and one fallback backend.
+  SetNextResolution({backends_[0]->port_}, kDefaultServiceConfig_.c_str());
+  SetNextResolutionForLbChannel({grpc_pick_unused_port_or_die()});
+  // Enter fallback mode because the LB channel fails to connect.
+  WaitForBackend(0);
+  // Return a new balancer that sends an empty serverlist.
+  ScheduleResponseForBalancer(
+      0, BalancerServiceImpl::BuildResponseForBackends({}, {}), 0);
+  SetNextResolutionForLbChannelAllBalancers();
+  // Send RPCs until failure.
+  gpr_timespec deadline = gpr_time_add(
+      gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(5000, GPR_TIMESPAN));
+  do {
+    auto status = SendRpc();
+    if (!status.ok()) break;
+  } while (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), deadline) < 0);
+  CheckRpcSendFailure();
+}
+
+TEST_F(SingleBalancerTest, FallbackModeIsExitedAfterChildRready) {
+  // Return an unreachable balancer and one fallback backend.
+  SetNextResolution({backends_[0]->port_}, kDefaultServiceConfig_.c_str());
+  SetNextResolutionForLbChannel({grpc_pick_unused_port_or_die()});
+  // Enter fallback mode because the LB channel fails to connect.
+  WaitForBackend(0);
+  // Return a new balancer that sends a dead backend.
+  ShutdownBackend(1);
+  ScheduleResponseForBalancer(
+      0,
+      BalancerServiceImpl::BuildResponseForBackends({backends_[1]->port_}, {}),
+      0);
+  SetNextResolutionForLbChannelAllBalancers();
+  // The state (TRANSIENT_FAILURE) update from the child policy will be ignored
+  // because we are still in fallback mode.
+  gpr_timespec deadline = gpr_time_add(
+      gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(5000, GPR_TIMESPAN));
+  // Send 5 seconds worth of RPCs.
+  do {
+    CheckRpcSendOk();
+  } while (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), deadline) < 0);
+  // After the backend is restarted, the child policy will eventually be READY,
+  // and we will exit fallback mode.
+  StartBackend(1);
+  WaitForBackend(1);
+  // We have exited fallback mode, so calls will go to the child policy
+  // exclusively.
+  CheckRpcSendOk(100);
+  EXPECT_EQ(0U, backends_[0]->service_.request_count());
+  EXPECT_EQ(100U, backends_[1]->service_.request_count());
+}
 
 TEST_F(SingleBalancerTest, BackendsRestart) {
   SetNextResolution({}, kDefaultServiceConfig_.c_str());
   SetNextResolutionForLbChannelAllBalancers();
-  const size_t kNumRpcsPerAddress = 100;
   ScheduleResponseForBalancer(
       0, BalancerServiceImpl::BuildResponseForBackends(GetBackendPorts(), {}),
       0);
-  // Make sure that trying to connect works without a call.
-  channel_->GetState(true /* try_to_connect */);
-  // Send kNumRpcsPerAddress RPCs per server.
-  CheckRpcSendOk(kNumRpcsPerAddress * num_backends_);
-  balancers_[0]->service_.NotifyDoneWithServerlists();
-  // The balancer got a single request.
-  EXPECT_EQ(1U, balancers_[0]->service_.request_count());
-  // and sent a single response.
-  EXPECT_EQ(1U, balancers_[0]->service_.response_count());
+  WaitForAllBackends();
   // Stop backends.  RPCs should fail.
   ShutdownAllBackends();
   CheckRpcSendFailure();
   // Restart all backends.  RPCs should start succeeding again.
   StartAllBackends();
-  CheckRpcSendOk(1 /* times */, 1000 /* timeout_ms */,
+  CheckRpcSendOk(1 /* times */, 2000 /* timeout_ms */,
                  true /* wait_for_ready */);
 }
 
index ae155b6..8e45b87 100644 (file)
  *
  */
 
-#ifndef _POSIX_SOURCE
-#define _POSIX_SOURCE
-#endif
-
 #include <assert.h>
 #include <signal.h>
 #include <stdio.h>
index 64aad64..d95771a 100644 (file)
@@ -30,8 +30,6 @@
 namespace grpc {
 namespace testing {
 
-auto& force_library_initialization = Library::get();
-
 static void BM_Alarm_Tag_Immediate(benchmark::State& state) {
   TrackCounters track_counters;
   CompletionQueue cq;
@@ -57,6 +55,7 @@ void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
 }  // namespace benchmark
 
 int main(int argc, char** argv) {
+  LibraryInitializer libInit;
   ::benchmark::Initialize(&argc, argv);
   ::grpc::testing::InitTest(&argc, &argv, false);
   benchmark::RunTheBenchmarksNamespaced();
index b97c954..c3ded0d 100644 (file)
 /* Benchmark arenas */
 
 #include <benchmark/benchmark.h>
-#include "src/core/lib/gpr/arena.h"
+#include "src/core/lib/gprpp/arena.h"
 #include "test/cpp/microbenchmarks/helpers.h"
 #include "test/cpp/util/test_config.h"
 
+using grpc_core::Arena;
+
 static void BM_Arena_NoOp(benchmark::State& state) {
   while (state.KeepRunning()) {
-    gpr_arena_destroy(gpr_arena_create(state.range(0)));
+    Arena::Create(state.range(0))->Destroy();
   }
 }
 BENCHMARK(BM_Arena_NoOp)->Range(1, 1024 * 1024);
 
 static void BM_Arena_ManyAlloc(benchmark::State& state) {
-  gpr_arena* a = gpr_arena_create(state.range(0));
+  Arena* a = Arena::Create(state.range(0));
   const size_t realloc_after =
       1024 * 1024 * 1024 / ((state.range(1) + 15) & 0xffffff0u);
   while (state.KeepRunning()) {
-    gpr_arena_alloc(a, state.range(1));
+    a->Alloc(state.range(1));
     // periodically recreate arena to avoid OOM
     if (state.iterations() % realloc_after == 0) {
-      gpr_arena_destroy(a);
-      a = gpr_arena_create(state.range(0));
+      a->Destroy();
+      a = Arena::Create(state.range(0));
     }
   }
-  gpr_arena_destroy(a);
+  a->Destroy();
 }
 BENCHMARK(BM_Arena_ManyAlloc)->Ranges({{1, 1024 * 1024}, {1, 32 * 1024}});
 
 static void BM_Arena_Batch(benchmark::State& state) {
   while (state.KeepRunning()) {
-    gpr_arena* a = gpr_arena_create(state.range(0));
+    Arena* a = Arena::Create(state.range(0));
     for (int i = 0; i < state.range(1); i++) {
-      gpr_arena_alloc(a, state.range(2));
+      a->Alloc(state.range(2));
     }
-    gpr_arena_destroy(a);
+    a->Destroy();
   }
 }
 BENCHMARK(BM_Arena_Batch)->Ranges({{1, 64 * 1024}, {1, 64}, {1, 1024}});
index 644c27c..595cc73 100644 (file)
@@ -30,7 +30,6 @@ namespace grpc {
 namespace testing {
 
 static void BM_ByteBuffer_Copy(benchmark::State& state) {
-  Library::get();
   int num_slices = state.range(0);
   size_t slice_size = state.range(1);
   std::vector<grpc::Slice> slices;
@@ -48,7 +47,6 @@ static void BM_ByteBuffer_Copy(benchmark::State& state) {
 BENCHMARK(BM_ByteBuffer_Copy)->Ranges({{1, 64}, {1, 1024 * 1024}});
 
 static void BM_ByteBufferReader_Next(benchmark::State& state) {
-  Library::get();
   const int num_slices = state.range(0);
   constexpr size_t kSliceSize = 16;
   std::vector<grpc_slice> slices;
@@ -82,7 +80,6 @@ static void BM_ByteBufferReader_Next(benchmark::State& state) {
 BENCHMARK(BM_ByteBufferReader_Next)->Ranges({{64 * 1024, 1024 * 1024}});
 
 static void BM_ByteBufferReader_Peek(benchmark::State& state) {
-  Library::get();
   const int num_slices = state.range(0);
   constexpr size_t kSliceSize = 16;
   std::vector<grpc_slice> slices;
@@ -125,6 +122,7 @@ void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
 }  // namespace benchmark
 
 int main(int argc, char** argv) {
+  LibraryInitializer libInit;
   ::benchmark::Initialize(&argc, argv);
   ::grpc::testing::InitTest(&argc, &argv, false);
   benchmark::RunTheBenchmarksNamespaced();
index e84999b..3bd1464 100644 (file)
@@ -47,8 +47,6 @@
 #include "test/cpp/microbenchmarks/helpers.h"
 #include "test/cpp/util/test_config.h"
 
-auto& force_library_initialization = Library::get();
-
 void BM_Zalloc(benchmark::State& state) {
   // speed of light for call creation is zalloc, so benchmark a few interesting
   // sizes
@@ -405,7 +403,7 @@ const char* name;
 /* implementation of grpc_transport_init_stream */
 int InitStream(grpc_transport* self, grpc_stream* stream,
                grpc_stream_refcount* refcount, const void* server_data,
-               gpr_arena* arena) {
+               grpc_core::Arena* arena) {
   return 0;
 }
 
@@ -540,7 +538,7 @@ static void BM_IsolatedFilter(benchmark::State& state) {
                                    method,
                                    start_time,
                                    deadline,
-                                   gpr_arena_create(kArenaSize),
+                                   grpc_core::Arena::Create(kArenaSize),
                                    nullptr};
   while (state.KeepRunning()) {
     GPR_TIMER_SCOPE("BenchmarkCycle", 0);
@@ -552,11 +550,11 @@ static void BM_IsolatedFilter(benchmark::State& state) {
     grpc_core::ExecCtx::Get()->Flush();
     // recreate arena every 64k iterations to avoid oom
     if (0 == (state.iterations() & 0xffff)) {
-      gpr_arena_destroy(call_args.arena);
-      call_args.arena = gpr_arena_create(kArenaSize);
+      call_args.arena->Destroy();
+      call_args.arena = grpc_core::Arena::Create(kArenaSize);
     }
   }
-  gpr_arena_destroy(call_args.arena);
+  call_args.arena->Destroy();
   grpc_channel_stack_destroy(channel_stack);
   grpc_core::ExecCtx::Get()->Flush();
 
@@ -609,7 +607,7 @@ BENCHMARK_TEMPLATE(BM_IsolatedFilter, MessageSizeFilter, SendEmptyMetadata);
 namespace isolated_call_filter {
 
 typedef struct {
-  grpc_call_combiner* call_combiner;
+  grpc_core::CallCombiner* call_combiner;
 } call_data;
 
 static void StartTransportStreamOp(grpc_call_element* elem,
@@ -823,6 +821,7 @@ void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
 }  // namespace benchmark
 
 int main(int argc, char** argv) {
+  LibraryInitializer libInit;
   ::benchmark::Initialize(&argc, argv);
   ::grpc::testing::InitTest(&argc, &argv, false);
   benchmark::RunTheBenchmarksNamespaced();
index 15ac997..88856c3 100644 (file)
@@ -23,8 +23,6 @@
 #include "test/cpp/microbenchmarks/helpers.h"
 #include "test/cpp/util/test_config.h"
 
-auto& force_library_initialization = Library::get();
-
 class ChannelDestroyerFixture {
  public:
   ChannelDestroyerFixture() {}
@@ -83,6 +81,7 @@ void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
 }  // namespace benchmark
 
 int main(int argc, char** argv) {
+  LibraryInitializer libInit;
   ::benchmark::Initialize(&argc, argv);
   ::grpc::testing::InitTest(&argc, &argv, false);
   benchmark::RunTheBenchmarksNamespaced();
index 85d233d..d0ed1da 100644 (file)
@@ -36,8 +36,6 @@
 #include "test/cpp/microbenchmarks/helpers.h"
 #include "test/cpp/util/test_config.h"
 
-auto& force_library_initialization = Library::get();
-
 static grpc_slice MakeSlice(std::vector<uint8_t> bytes) {
   grpc_slice s = grpc_slice_malloc(bytes.size());
   uint8_t* p = GRPC_SLICE_START_PTR(s);
@@ -458,7 +456,7 @@ static void BM_HpackParserParseHeader(benchmark::State& state) {
   grpc_chttp2_hpack_parser p;
   grpc_chttp2_hpack_parser_init(&p);
   const int kArenaSize = 4096 * 4096;
-  p.on_header_user_data = gpr_arena_create(kArenaSize);
+  p.on_header_user_data = grpc_core::Arena::Create(kArenaSize);
   p.on_header = OnHeader;
   for (auto slice : init_slices) {
     GPR_ASSERT(GRPC_ERROR_NONE == grpc_chttp2_hpack_parser_parse(&p, slice));
@@ -470,12 +468,12 @@ static void BM_HpackParserParseHeader(benchmark::State& state) {
     grpc_core::ExecCtx::Get()->Flush();
     // Recreate arena every 4k iterations to avoid oom
     if (0 == (state.iterations() & 0xfff)) {
-      gpr_arena_destroy((gpr_arena*)p.on_header_user_data);
-      p.on_header_user_data = gpr_arena_create(kArenaSize);
+      static_cast<grpc_core::Arena*>(p.on_header_user_data)->Destroy();
+      p.on_header_user_data = grpc_core::Arena::Create(kArenaSize);
     }
   }
   // Clean up
-  gpr_arena_destroy((gpr_arena*)p.on_header_user_data);
+  static_cast<grpc_core::Arena*>(p.on_header_user_data)->Destroy();
   for (auto slice : init_slices) grpc_slice_unref(slice);
   for (auto slice : benchmark_slices) grpc_slice_unref(slice);
   grpc_chttp2_hpack_parser_destroy(&p);
@@ -778,7 +776,8 @@ static void free_timeout(void* p) { gpr_free(p); }
 // Benchmark the current on_initial_header implementation
 static void OnInitialHeader(void* user_data, grpc_mdelem md) {
   // Setup for benchmark. This will bloat the absolute values of this benchmark
-  grpc_chttp2_incoming_metadata_buffer buffer((gpr_arena*)user_data);
+  grpc_chttp2_incoming_metadata_buffer buffer(
+      static_cast<grpc_core::Arena*>(user_data));
   bool seen_error = false;
 
   // Below here is the code we actually care about benchmarking
@@ -927,6 +926,7 @@ void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
 }  // namespace benchmark
 
 int main(int argc, char** argv) {
+  LibraryInitializer libInit;
   ::benchmark::Initialize(&argc, argv);
   ::grpc::testing::InitTest(&argc, &argv, false);
   benchmark::RunTheBenchmarksNamespaced();
index baa6da3..0550458 100644 (file)
@@ -36,8 +36,6 @@
 #include "test/cpp/microbenchmarks/helpers.h"
 #include "test/cpp/util/test_config.h"
 
-auto& force_library_initialization = Library::get();
-
 ////////////////////////////////////////////////////////////////////////////////
 // Helper classes
 //
@@ -57,7 +55,8 @@ class DummyEndpoint : public grpc_endpoint {
                                                    get_fd,
                                                    can_track_err};
     grpc_endpoint::vtable = &my_vtable;
-    ru_ = grpc_resource_user_create(Library::get().rq(), "dummy_endpoint");
+    ru_ = grpc_resource_user_create(LibraryInitializer::get().rq(),
+                                    "dummy_endpoint");
   }
 
   void PushInput(grpc_slice slice) {
@@ -193,13 +192,13 @@ class Stream {
   Stream(Fixture* f) : f_(f) {
     stream_size_ = grpc_transport_stream_size(f->transport());
     stream_ = gpr_malloc(stream_size_);
-    arena_ = gpr_arena_create(4096);
+    arena_ = grpc_core::Arena::Create(4096);
   }
 
   ~Stream() {
     gpr_event_wait(&done_, gpr_inf_future(GPR_CLOCK_REALTIME));
     gpr_free(stream_);
-    gpr_arena_destroy(arena_);
+    arena_->Destroy();
   }
 
   void Init(benchmark::State& state) {
@@ -208,8 +207,8 @@ class Stream {
     gpr_event_init(&done_);
     memset(stream_, 0, stream_size_);
     if ((state.iterations() & 0xffff) == 0) {
-      gpr_arena_destroy(arena_);
-      arena_ = gpr_arena_create(4096);
+      arena_->Destroy();
+      arena_ = grpc_core::Arena::Create(4096);
     }
     grpc_transport_init_stream(f_->transport(),
                                static_cast<grpc_stream*>(stream_), &refcount_,
@@ -245,7 +244,7 @@ class Stream {
 
   Fixture* f_;
   grpc_stream_refcount refcount_;
-  gpr_arena* arena_;
+  grpc_core::Arena* arena_;
   size_t stream_size_;
   void* stream_;
   grpc_closure* destroy_closure_ = nullptr;
@@ -642,6 +641,7 @@ void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
 }  // namespace benchmark
 
 int main(int argc, char** argv) {
+  LibraryInitializer libInit;
   ::benchmark::Initialize(&argc, argv);
   ::grpc::testing::InitTest(&argc, &argv, false);
   benchmark::RunTheBenchmarksNamespaced();
index e1f1e92..84b1c53 100644 (file)
@@ -30,8 +30,6 @@
 #include "test/cpp/microbenchmarks/helpers.h"
 #include "test/cpp/util/test_config.h"
 
-auto& force_library_initialization = Library::get();
-
 static void BM_NoOpExecCtx(benchmark::State& state) {
   TrackCounters track_counters;
   while (state.KeepRunning()) {
@@ -425,6 +423,7 @@ void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
 }  // namespace benchmark
 
 int main(int argc, char** argv) {
+  LibraryInitializer libInit;
   ::benchmark::Initialize(&argc, argv);
   ::grpc::testing::InitTest(&argc, &argv, false);
   benchmark::RunTheBenchmarksNamespaced();
index a7cb939..263314d 100644 (file)
@@ -32,8 +32,6 @@
 namespace grpc {
 namespace testing {
 
-auto& force_library_initialization = Library::get();
-
 static void BM_CreateDestroyCpp(benchmark::State& state) {
   TrackCounters track_counters;
   while (state.KeepRunning()) {
@@ -156,6 +154,7 @@ void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
 }  // namespace benchmark
 
 int main(int argc, char** argv) {
+  LibraryInitializer libInit;
   ::benchmark::Initialize(&argc, argv);
   ::grpc::testing::InitTest(&argc, &argv, false);
   benchmark::RunTheBenchmarksNamespaced();
index 5445535..329eaf2 100644 (file)
@@ -138,7 +138,7 @@ static void teardown() {
  Setup:
   The benchmark framework ensures that none of the threads proceed beyond the
   state.KeepRunning() call unless all the threads have called state.keepRunning
-  atleast once.  So it is safe to do the initialization in one of the threads
+  at least once.  So it is safe to do the initialization in one of the threads
   before state.KeepRunning() is called.
 
  Teardown:
index ae557a5..a71817f 100644 (file)
@@ -27,8 +27,6 @@
 #include "test/cpp/microbenchmarks/helpers.h"
 #include "test/cpp/util/test_config.h"
 
-auto& force_library_initialization = Library::get();
-
 class ErrorDeleter {
  public:
   void operator()(grpc_error* error) { GRPC_ERROR_UNREF(error); }
@@ -318,6 +316,7 @@ void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
 }  // namespace benchmark
 
 int main(int argc, char** argv) {
+  LibraryInitializer libInit;
   ::benchmark::Initialize(&argc, argv);
   ::grpc::testing::InitTest(&argc, &argv, false);
   benchmark::RunTheBenchmarksNamespaced();
index 34df77a..60ca9a0 100644 (file)
@@ -24,9 +24,6 @@
 namespace grpc {
 namespace testing {
 
-// force library initialization
-auto& force_library_initialization = Library::get();
-
 /*******************************************************************************
  * CONFIGURATIONS
  */
@@ -122,6 +119,7 @@ void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
 }  // namespace benchmark
 
 int main(int argc, char** argv) {
+  LibraryInitializer libInit;
   ::benchmark::Initialize(&argc, argv);
   ::grpc::testing::InitTest(&argc, &argv, false);
   benchmark::RunTheBenchmarksNamespaced();
index da98f3c..d4533e6 100644 (file)
@@ -28,9 +28,6 @@ namespace testing {
  * CONFIGURATIONS
  */
 
-// force library initialization
-auto& force_library_initialization = Library::get();
-
 BENCHMARK_TEMPLATE(BM_PumpStreamClientToServer, TCP)
     ->Range(0, 128 * 1024 * 1024);
 BENCHMARK_TEMPLATE(BM_PumpStreamClientToServer, UDS)
@@ -72,6 +69,7 @@ void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
 }  // namespace benchmark
 
 int main(int argc, char** argv) {
+  LibraryInitializer libInit;
   ::benchmark::Initialize(&argc, argv);
   ::grpc::testing::InitTest(&argc, &argv, false);
   benchmark::RunTheBenchmarksNamespaced();
index 1af92d2..2d733bc 100644 (file)
@@ -214,8 +214,8 @@ class TrickledCHTTP2 : public EndpointPairFixture {
   static grpc_endpoint_pair MakeEndpoints(size_t kilobits,
                                           grpc_passthru_endpoint_stats* stats) {
     grpc_endpoint_pair p;
-    grpc_passthru_endpoint_create(&p.client, &p.server, Library::get().rq(),
-                                  stats);
+    grpc_passthru_endpoint_create(&p.client, &p.server,
+                                  LibraryInitializer::get().rq(), stats);
     double bytes_per_second = 125.0 * kilobits;
     p.client = grpc_trickle_endpoint_create(p.client, bytes_per_second);
     p.server = grpc_trickle_endpoint_create(p.server, bytes_per_second);
@@ -235,9 +235,6 @@ class TrickledCHTTP2 : public EndpointPairFixture {
   }
 };
 
-// force library initialization
-auto& force_library_initialization = Library::get();
-
 static void TrickleCQNext(TrickledCHTTP2* fixture, void** t, bool* ok,
                           int64_t iteration) {
   while (true) {
@@ -465,6 +462,7 @@ void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
 }  // namespace benchmark
 
 int main(int argc, char** argv) {
+  LibraryInitializer libInit;
   ::benchmark::Initialize(&argc, argv);
   ::grpc::testing::InitTest(&argc, &argv, false);
   grpc_timer_manager_set_threading(false);
index d4bd58b..3e8f934 100644 (file)
@@ -24,9 +24,6 @@
 namespace grpc {
 namespace testing {
 
-// force library initialization
-auto& force_library_initialization = Library::get();
-
 /*******************************************************************************
  * CONFIGURATIONS
  */
@@ -174,6 +171,7 @@ void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
 }  // namespace benchmark
 
 int main(int argc, char** argv) {
+  LibraryInitializer libInit;
   ::benchmark::Initialize(&argc, argv);
   ::grpc::testing::InitTest(&argc, &argv, false);
   benchmark::RunTheBenchmarksNamespaced();
index 553b33c..ff8fe54 100644 (file)
@@ -27,8 +27,6 @@
 #include "test/cpp/microbenchmarks/helpers.h"
 #include "test/cpp/util/test_config.h"
 
-auto& force_library_initialization = Library::get();
-
 static void BM_SliceFromStatic(benchmark::State& state) {
   TrackCounters track_counters;
   while (state.KeepRunning()) {
@@ -298,6 +296,7 @@ void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
 }  // namespace benchmark
 
 int main(int argc, char** argv) {
+  LibraryInitializer libInit;
   ::benchmark::Initialize(&argc, argv);
   ::grpc::testing::InitTest(&argc, &argv, false);
   benchmark::RunTheBenchmarksNamespaced();
index 050c7f7..f8e36f1 100644 (file)
@@ -40,8 +40,6 @@
 #include <unistd.h>
 #endif
 
-auto& force_library_initialization = Library::get();
-
 static void shutdown_ps(void* ps, grpc_error* error) {
   grpc_pollset_destroy(static_cast<grpc_pollset*>(ps));
 }
@@ -264,6 +262,7 @@ void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
 }  // namespace benchmark
 
 int main(int argc, char** argv) {
+  LibraryInitializer libInit;
   ::benchmark::Initialize(&argc, argv);
   ::grpc::testing::InitTest(&argc, &argv, false);
   benchmark::RunTheBenchmarksNamespaced();
index f5a4112..53aaead 100644 (file)
@@ -32,8 +32,6 @@
 namespace grpc {
 namespace testing {
 
-auto& force_library_initialization = Library::get();
-
 struct TimerClosure {
   grpc_timer timer;
   grpc_closure closure;
@@ -111,6 +109,7 @@ void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
 }  // namespace benchmark
 
 int main(int argc, char** argv) {
+  LibraryInitializer libInit;
   ::benchmark::Initialize(&argc, argv);
   ::grpc::testing::InitTest(&argc, &argv, false);
   benchmark::RunTheBenchmarksNamespaced();
index 6bbf553..4d60e97 100644 (file)
@@ -86,8 +86,8 @@ class FullstackFixture : public BaseFixture {
     ChannelArguments args;
     config.ApplyCommonChannelArguments(&args);
     if (address.length() > 0) {
-      channel_ =
-          CreateCustomChannel(address, InsecureChannelCredentials(), args);
+      channel_ = ::grpc::CreateCustomChannel(
+          address, InsecureChannelCredentials(), args);
     } else {
       channel_ = server_->InProcessChannel(args);
     }
@@ -218,7 +218,7 @@ class EndpointPairFixture : public BaseFixture {
           "target", &c_args, GRPC_CLIENT_DIRECT_CHANNEL, client_transport_);
       grpc_chttp2_transport_start_reading(client_transport_, nullptr, nullptr);
 
-      channel_ = CreateChannelInternal(
+      channel_ = ::grpc::CreateChannelInternal(
           "", channel,
           std::vector<std::unique_ptr<
               experimental::ClientInterceptorFactoryInterface>>());
@@ -299,8 +299,8 @@ class InProcessCHTTP2WithExplicitStats : public EndpointPairFixture {
 
   static grpc_endpoint_pair MakeEndpoints(grpc_passthru_endpoint_stats* stats) {
     grpc_endpoint_pair p;
-    grpc_passthru_endpoint_create(&p.client, &p.server, Library::get().rq(),
-                                  stats);
+    grpc_passthru_endpoint_create(&p.client, &p.server,
+                                  LibraryInitializer::get().rq(), stats);
     return p;
   }
 };
index d4070de..7d78e21 100644 (file)
 #include "test/cpp/microbenchmarks/helpers.h"
 
 static grpc::internal::GrpcLibraryInitializer g_gli_initializer;
+static LibraryInitializer* g_libraryInitializer;
+
+LibraryInitializer::LibraryInitializer() {
+  GPR_ASSERT(g_libraryInitializer == nullptr);
+  g_libraryInitializer = this;
 
-Library::Library() {
   g_gli_initializer.summon();
 #ifdef GPR_LOW_LEVEL_COUNTERS
   grpc_memory_counters_init();
@@ -31,6 +35,17 @@ Library::Library() {
   rq_ = grpc_resource_quota_create("bm");
 }
 
+LibraryInitializer::~LibraryInitializer() {
+  g_libraryInitializer = nullptr;
+  init_lib_.shutdown();
+  grpc_resource_quota_unref(rq_);
+}
+
+LibraryInitializer& LibraryInitializer::get() {
+  GPR_ASSERT(g_libraryInitializer != nullptr);
+  return *g_libraryInitializer;
+}
+
 void TrackCounters::Finish(benchmark::State& state) {
   std::ostringstream out;
   for (const auto& l : labels_) {
index 770966a..b712c85 100644 (file)
 #include <benchmark/benchmark.h>
 #include <grpcpp/impl/grpc_library.h>
 
-class Library {
+class LibraryInitializer {
  public:
-  static Library& get() {
-    static Library lib;
-    return lib;
-  }
+  LibraryInitializer();
+  ~LibraryInitializer();
 
   grpc_resource_quota* rq() { return rq_; }
 
- private:
-  Library();
-
-  ~Library() { init_lib_.shutdown(); }
+  static LibraryInitializer& get();
 
+ private:
   grpc::internal::GrpcLibrary init_lib_;
   grpc_resource_quota* rq_;
 };
index 58e7048..7db435a 100644 (file)
@@ -22,7 +22,7 @@ package(
 
 licenses(["notice"])  # Apache v2
 
-load("//bazel:grpc_build_system.bzl", "grpc_py_binary", "grpc_cc_test")
+load("//bazel:grpc_build_system.bzl", "grpc_py_binary", "grpc_cc_test", "grpc_cc_library")
 load(":generate_resolver_component_tests.bzl", "generate_resolver_component_tests")
 
 # Meant to be invoked only through the top-level shell script driver.
@@ -39,6 +39,7 @@ grpc_cc_test(
     srcs = ["cancel_ares_query_test.cc"],
     external_deps = ["gmock"],
     deps = [
+        ":dns_test_util",
         "//:gpr",
         "//:grpc",
         "//:grpc++",
@@ -49,4 +50,14 @@ grpc_cc_test(
     ],
 )
 
+grpc_cc_library(
+    name = "dns_test_util",
+    hdrs = ["dns_test_util.h"],
+    srcs = ["dns_test_util.cc"],
+    deps = [
+        "//:gpr",
+        "//:grpc",
+    ],
+)
+
 generate_resolver_component_tests()
index 78ad49e..affc75b 100644 (file)
 #include "src/core/ext/filters/client_channel/client_channel.h"
 #include "src/core/ext/filters/client_channel/resolver.h"
 #include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
+#include "src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h"
 #include "src/core/ext/filters/client_channel/resolver_registry.h"
 #include "src/core/ext/filters/client_channel/server_address.h"
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/iomgr/combiner.h"
@@ -52,6 +52,7 @@
 
 #ifndef GPR_WINDOWS
 #include <arpa/inet.h>
+#include <netinet/in.h>
 #include <sys/socket.h>
 #endif
 
@@ -828,13 +829,13 @@ TEST_F(AddressSortingTest, TestSorterKnowsIpv6LoopbackIsAvailable) {
 }  // namespace
 
 int main(int argc, char** argv) {
-  char* resolver = gpr_getenv("GRPC_DNS_RESOLVER");
-  if (resolver == nullptr || strlen(resolver) == 0) {
-    gpr_setenv("GRPC_DNS_RESOLVER", "ares");
-  } else if (strcmp("ares", resolver)) {
-    gpr_log(GPR_INFO, "GRPC_DNS_RESOLVER != ares: %s.", resolver);
+  grpc_core::UniquePtr<char> resolver =
+      GPR_GLOBAL_CONFIG_GET(grpc_dns_resolver);
+  if (strlen(resolver.get()) == 0) {
+    GPR_GLOBAL_CONFIG_SET(grpc_dns_resolver, "ares");
+  } else if (strcmp("ares", resolver.get())) {
+    gpr_log(GPR_INFO, "GRPC_DNS_RESOLVER != ares: %s.", resolver.get());
   }
-  gpr_free(resolver);
   grpc::testing::TestEnvironment env(argc, argv);
   ::testing::InitGoogleTest(&argc, argv);
   auto result = RUN_ALL_TESTS();
index bcf96aa..667011a 100644 (file)
 #include <grpc/support/time.h>
 #include "include/grpc/support/string_util.h"
 #include "src/core/ext/filters/client_channel/resolver.h"
+#include "src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h"
 #include "src/core/ext/filters/client_channel/resolver_registry.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/debug/stats.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gprpp/orphanable.h"
@@ -44,6 +44,7 @@
 #include "test/core/util/cmdline.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
+#include "test/cpp/naming/dns_test_util.h"
 
 #ifdef GPR_WINDOWS
 #include "src/core/lib/iomgr/sockaddr_windows.h"
@@ -76,36 +77,6 @@ void EndTest(grpc_channel* client, grpc_completion_queue* cq) {
   grpc_completion_queue_destroy(cq);
 }
 
-class FakeNonResponsiveDNSServer {
- public:
-  FakeNonResponsiveDNSServer(int port) {
-    socket_ = socket(AF_INET6, SOCK_DGRAM, 0);
-    if (socket_ == BAD_SOCKET_RETURN_VAL) {
-      gpr_log(GPR_DEBUG, "Failed to create UDP ipv6 socket");
-      abort();
-    }
-    sockaddr_in6 addr;
-    memset(&addr, 0, sizeof(addr));
-    addr.sin6_family = AF_INET6;
-    addr.sin6_port = htons(port);
-    ((char*)&addr.sin6_addr)[15] = 1;
-    if (bind(socket_, (const sockaddr*)&addr, sizeof(addr)) != 0) {
-      gpr_log(GPR_DEBUG, "Failed to bind UDP ipv6 socket to [::1]:%d", port);
-      abort();
-    }
-  }
-  ~FakeNonResponsiveDNSServer() {
-#ifdef GPR_WINDOWS
-    closesocket(socket_);
-#else
-    close(socket_);
-#endif
-  }
-
- private:
-  int socket_;
-};
-
 struct ArgsStruct {
   gpr_atm done_atm;
   gpr_mu* mu;
@@ -184,7 +155,7 @@ class AssertFailureResultHandler : public grpc_core::Resolver::ResultHandler {
 
 void TestCancelActiveDNSQuery(ArgsStruct* args) {
   int fake_dns_port = grpc_pick_unused_port_or_die();
-  FakeNonResponsiveDNSServer fake_dns_server(fake_dns_port);
+  grpc::testing::FakeNonResponsiveDNSServer fake_dns_server(fake_dns_port);
   char* client_target;
   GPR_ASSERT(gpr_asprintf(
       &client_target,
@@ -282,7 +253,7 @@ void TestCancelDuringActiveQuery(
     cancellation_test_query_timeout_setting query_timeout_setting) {
   // Start up fake non responsive DNS server
   int fake_dns_port = grpc_pick_unused_port_or_die();
-  FakeNonResponsiveDNSServer fake_dns_server(fake_dns_port);
+  grpc::testing::FakeNonResponsiveDNSServer fake_dns_server(fake_dns_port);
   // Create a call that will try to use the fake DNS server
   char* client_target = nullptr;
   GPR_ASSERT(gpr_asprintf(
@@ -403,7 +374,7 @@ TEST(
 int main(int argc, char** argv) {
   grpc::testing::TestEnvironment env(argc, argv);
   ::testing::InitGoogleTest(&argc, argv);
-  gpr_setenv("GRPC_DNS_RESOLVER", "ares");
+  GPR_GLOBAL_CONFIG_SET(grpc_dns_resolver, "ares");
   // Sanity check the time that it takes to run the test
   // including the teardown time (the teardown
   // part of the test involves cancelling the DNS query,
diff --git a/test/cpp/naming/dns_test_util.cc b/test/cpp/naming/dns_test_util.cc
new file mode 100644 (file)
index 0000000..1380d0a
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "test/cpp/naming/dns_test_util.h"
+
+#ifdef GPR_WINDOWS
+#include "src/core/lib/iomgr/sockaddr_windows.h"
+#include "src/core/lib/iomgr/socket_windows.h"
+#define BAD_SOCKET_RETURN_VAL INVALID_SOCKET
+#else
+#include "src/core/lib/iomgr/sockaddr_posix.h"
+#define BAD_SOCKET_RETURN_VAL -1
+#endif
+
+namespace grpc {
+namespace testing {
+
+FakeNonResponsiveDNSServer::FakeNonResponsiveDNSServer(int port) {
+  udp_socket_ = socket(AF_INET6, SOCK_DGRAM, 0);
+  tcp_socket_ = socket(AF_INET6, SOCK_STREAM, 0);
+  if (udp_socket_ == BAD_SOCKET_RETURN_VAL) {
+    gpr_log(GPR_DEBUG, "Failed to create UDP ipv6 socket");
+    abort();
+  }
+  if (tcp_socket_ == BAD_SOCKET_RETURN_VAL) {
+    gpr_log(GPR_DEBUG, "Failed to create TCP ipv6 socket");
+    abort();
+  }
+  sockaddr_in6 addr;
+  memset(&addr, 0, sizeof(addr));
+  addr.sin6_family = AF_INET6;
+  addr.sin6_port = htons(port);
+  ((char*)&addr.sin6_addr)[15] = 1;
+  if (bind(udp_socket_, (const sockaddr*)&addr, sizeof(addr)) != 0) {
+    gpr_log(GPR_DEBUG, "Failed to bind UDP ipv6 socket to [::1]:%d", port);
+    abort();
+  }
+#ifdef GPR_WINDOWS
+  char val = 1;
+  if (setsockopt(tcp_socket_, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) ==
+      SOCKET_ERROR) {
+    gpr_log(GPR_DEBUG,
+            "Failed to set SO_REUSEADDR on TCP ipv6 socket to [::1]:%d", port);
+    abort();
+  }
+#else
+  int val = 1;
+  if (setsockopt(tcp_socket_, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) !=
+      0) {
+    gpr_log(GPR_DEBUG,
+            "Failed to set SO_REUSEADDR on TCP ipv6 socket to [::1]:%d", port);
+    abort();
+  }
+#endif
+  if (bind(tcp_socket_, (const sockaddr*)&addr, sizeof(addr)) != 0) {
+    gpr_log(GPR_DEBUG, "Failed to bind TCP ipv6 socket to [::1]:%d", port);
+    abort();
+  }
+  if (listen(tcp_socket_, 100)) {
+    gpr_log(GPR_DEBUG, "Failed to listen on TCP ipv6 socket to [::1]:%d", port);
+    abort();
+  }
+}
+
+FakeNonResponsiveDNSServer::~FakeNonResponsiveDNSServer() {
+#ifdef GPR_WINDOWS
+  closesocket(udp_socket_);
+  closesocket(tcp_socket_);
+#else
+  close(udp_socket_);
+  close(tcp_socket_);
+#endif
+}
+
+}  // namespace testing
+}  // namespace grpc
diff --git a/test/cpp/naming/dns_test_util.h b/test/cpp/naming/dns_test_util.h
new file mode 100644 (file)
index 0000000..d5e5099
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *
+ * 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 GRPC_DNS_TEST_UTIL_H
+#define GRPC_DNS_TEST_UTIL_H
+
+namespace grpc {
+namespace testing {
+
+class FakeNonResponsiveDNSServer {
+ public:
+  explicit FakeNonResponsiveDNSServer(int port);
+  virtual ~FakeNonResponsiveDNSServer();
+
+ private:
+  int udp_socket_;
+  int tcp_socket_;
+};
+
+}  // namespace testing
+}  // namespace grpc
+
+#endif /* GRPC_DNS_TEST_UTIL_H */
index 9bf5ae9..7cbf916 100755 (executable)
@@ -47,9 +47,11 @@ def _resolver_test_cases(resolver_component_data):
              _build_expected_addrs_cmd_arg(test_case['expected_addrs'])),
             ('expected_chosen_service_config',
              (test_case['expected_chosen_service_config'] or '')),
+            ('expected_service_config_error', (test_case['expected_service_config_error'] or '')),
             ('expected_lb_policy', (test_case['expected_lb_policy'] or '')),
             ('enable_srv_queries', test_case['enable_srv_queries']),
             ('enable_txt_queries', test_case['enable_txt_queries']),
+            ('inject_broken_nameserver_list', test_case['inject_broken_nameserver_list']),
         ],
     })
   return out
@@ -72,6 +74,7 @@ def main():
               'src': ['test/cpp/naming/resolver_component_test.cc'],
               'platforms': ['linux', 'posix', 'mac', 'windows'],
               'deps': [
+                  'dns_test_util',
                   'grpc++_test_util' + unsecure_build_config_suffix,
                   'grpc_test_util' + unsecure_build_config_suffix,
                   'grpc++' + unsecure_build_config_suffix,
@@ -130,6 +133,7 @@ def main():
           'src': ['test/cpp/naming/cancel_ares_query_test.cc'],
           'platforms': ['linux', 'posix', 'mac', 'windows'],
           'deps': [
+              'dns_test_util',
               'grpc++_test_util',
               'grpc_test_util',
               'grpc++',
index 5891767..bcc62f6 100755 (executable)
@@ -46,6 +46,7 @@ def generate_resolver_component_tests():
             "gmock",
         ],
         deps = [
+            ":dns_test_util",
             "//test/cpp/util:test_util%s" % unsecure_build_config_suffix,
             "//test/core/util:grpc_test_util%s" % unsecure_build_config_suffix,
             "//:grpc++%s" % unsecure_build_config_suffix,
index 398822d..6cea814 100644 (file)
 #include "test/cpp/util/test_config.h"
 
 #include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/ext/filters/client_channel/parse_address.h"
 #include "src/core/ext/filters/client_channel/resolver.h"
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h"
 #include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
 #include "src/core/ext/filters/client_channel/resolver_registry.h"
 #include "src/core/ext/filters/client_channel/server_address.h"
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gprpp/orphanable.h"
 #include "src/core/lib/iomgr/iomgr.h"
 #include "src/core/lib/iomgr/resolve_address.h"
 #include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/socket_utils.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
 
+#include "test/cpp/naming/dns_test_util.h"
+
 // TODO: pull in different headers when enabling this
 // test on windows. Also set BAD_SOCKET_RETURN_VAL
 // to INVALID_SOCKET on windows.
@@ -89,6 +93,8 @@ DEFINE_string(expected_addrs, "",
 DEFINE_string(expected_chosen_service_config, "",
               "Expected service config json string that gets chosen (no "
               "whitespace). Empty for none.");
+DEFINE_string(expected_service_config_error, "",
+              "Expected service config error. Empty for none.");
 DEFINE_string(
     local_dns_server_address, "",
     "Optional. This address is placed as the uri authority if present.");
@@ -106,6 +112,17 @@ DEFINE_string(
     "generate "
     "the python script runner doesn't allow us to pass a gflags bool to this "
     "binary.");
+DEFINE_string(
+    inject_broken_nameserver_list, "",
+    "Whether or not to configure c-ares to use a broken nameserver list, in "
+    "which "
+    "the first nameserver in the list is non-responsive, but the second one "
+    "works, i.e "
+    "serves the expected DNS records; using for testing such a real scenario."
+    "It would be better if this arg could be bool, but the way that we "
+    "generate "
+    "the python script runner doesn't allow us to pass a gflags bool to this "
+    "binary.");
 DEFINE_string(expected_lb_policy, "",
               "Expected lb policy name that appears in resolver result channel "
               "arg. Empty for none.");
@@ -146,8 +163,9 @@ vector<GrpcLBAddress> ParseExpectedAddrs(std::string expected_addrs) {
     expected_addrs = expected_addrs.substr(next_comma + 1, std::string::npos);
     // get the next is_balancer 'bool' associated with this address
     size_t next_semicolon = expected_addrs.find(';');
-    bool is_balancer =
-        gpr_is_true(expected_addrs.substr(0, next_semicolon).c_str());
+    bool is_balancer = false;
+    gpr_parse_bool_value(expected_addrs.substr(0, next_semicolon).c_str(),
+                         &is_balancer);
     out.emplace_back(GrpcLBAddress(next_addr, is_balancer));
     if (next_semicolon == std::string::npos) {
       break;
@@ -178,6 +196,7 @@ struct ArgsStruct {
   grpc_channel_args* channel_args;
   vector<GrpcLBAddress> expected_addrs;
   std::string expected_service_config_string;
+  std::string expected_service_config_error;
   std::string expected_lb_policy;
 };
 
@@ -216,7 +235,10 @@ gpr_timespec NSecondDeadline(int seconds) {
 }
 
 void PollPollsetUntilRequestDone(ArgsStruct* args) {
-  gpr_timespec deadline = NSecondDeadline(10);
+  // Use a 20-second timeout to give room for the tests that involve
+  // a non-responsive name server (c-ares uses a ~5 second query timeout
+  // for that server before succeeding with the healthy one).
+  gpr_timespec deadline = NSecondDeadline(20);
   while (true) {
     bool done = gpr_atm_acq_load(&args->done_atm) != 0;
     if (done) {
@@ -240,13 +262,19 @@ void PollPollsetUntilRequestDone(ArgsStruct* args) {
 }
 
 void CheckServiceConfigResultLocked(const char* service_config_json,
+                                    grpc_error* service_config_error,
                                     ArgsStruct* args) {
   if (args->expected_service_config_string != "") {
     GPR_ASSERT(service_config_json != nullptr);
     EXPECT_EQ(service_config_json, args->expected_service_config_string);
+  }
+  if (args->expected_service_config_error == "") {
+    EXPECT_EQ(service_config_error, GRPC_ERROR_NONE);
   } else {
-    GPR_ASSERT(service_config_json == nullptr);
+    EXPECT_THAT(grpc_error_string(service_config_error),
+                testing::HasSubstr(args->expected_service_config_error));
   }
+  GRPC_ERROR_UNREF(service_config_error);
 }
 
 void CheckLBPolicyResultLocked(const grpc_channel_args* channel_args,
@@ -461,13 +489,58 @@ class CheckingResultHandler : public ResultHandler {
         result.service_config == nullptr
             ? nullptr
             : result.service_config->service_config_json();
-    CheckServiceConfigResultLocked(service_config_json, args);
+    CheckServiceConfigResultLocked(
+        service_config_json, GRPC_ERROR_REF(result.service_config_error), args);
     if (args->expected_service_config_string == "") {
       CheckLBPolicyResultLocked(result.args, args);
     }
   }
 };
 
+int g_fake_non_responsive_dns_server_port = -1;
+
+/* This function will configure any ares_channel created by the c-ares based
+ * resolver. This is useful to effectively mock /etc/resolv.conf settings
+ * (and equivalent on Windows), which unit tests don't have write permissions.
+ */
+void InjectBrokenNameServerList(ares_channel channel) {
+  struct ares_addr_port_node dns_server_addrs[2];
+  memset(dns_server_addrs, 0, sizeof(dns_server_addrs));
+  char* unused_host;
+  char* local_dns_server_port;
+  GPR_ASSERT(gpr_split_host_port(FLAGS_local_dns_server_address.c_str(),
+                                 &unused_host, &local_dns_server_port));
+  gpr_log(GPR_DEBUG,
+          "Injecting broken nameserver list. Bad server address:|[::1]:%d|. "
+          "Good server address:%s",
+          g_fake_non_responsive_dns_server_port,
+          FLAGS_local_dns_server_address.c_str());
+  // Put the non-responsive DNS server at the front of c-ares's nameserver list.
+  dns_server_addrs[0].family = AF_INET6;
+  ((char*)&dns_server_addrs[0].addr.addr6)[15] = 0x1;
+  dns_server_addrs[0].tcp_port = g_fake_non_responsive_dns_server_port;
+  dns_server_addrs[0].udp_port = g_fake_non_responsive_dns_server_port;
+  dns_server_addrs[0].next = &dns_server_addrs[1];
+  // Put the actual healthy DNS server after the first one. The expectation is
+  // that the resolver will timeout the query to the non-responsive DNS server
+  // and will skip over to this healthy DNS server, without causing any DNS
+  // resolution errors.
+  dns_server_addrs[1].family = AF_INET;
+  ((char*)&dns_server_addrs[1].addr.addr4)[0] = 0x7f;
+  ((char*)&dns_server_addrs[1].addr.addr4)[3] = 0x1;
+  dns_server_addrs[1].tcp_port = atoi(local_dns_server_port);
+  dns_server_addrs[1].udp_port = atoi(local_dns_server_port);
+  dns_server_addrs[1].next = nullptr;
+  GPR_ASSERT(ares_set_servers_ports(channel, dns_server_addrs) == ARES_SUCCESS);
+  gpr_free(local_dns_server_port);
+  gpr_free(unused_host);
+}
+
+void StartResolvingLocked(void* arg, grpc_error* unused) {
+  grpc_core::Resolver* r = static_cast<grpc_core::Resolver*>(arg);
+  r->StartLocked();
+}
+
 void RunResolvesRelevantRecordsTest(
     grpc_core::UniquePtr<grpc_core::Resolver::ResultHandler> (
         *CreateResultHandler)(ArgsStruct* args)) {
@@ -476,12 +549,33 @@ void RunResolvesRelevantRecordsTest(
   ArgsInit(&args);
   args.expected_addrs = ParseExpectedAddrs(FLAGS_expected_addrs);
   args.expected_service_config_string = FLAGS_expected_chosen_service_config;
+  args.expected_service_config_error = FLAGS_expected_service_config_error;
   args.expected_lb_policy = FLAGS_expected_lb_policy;
   // maybe build the address with an authority
   char* whole_uri = nullptr;
-  GPR_ASSERT(gpr_asprintf(&whole_uri, "dns://%s/%s",
-                          FLAGS_local_dns_server_address.c_str(),
-                          FLAGS_target_name.c_str()));
+  gpr_log(GPR_DEBUG,
+          "resolver_component_test: --inject_broken_nameserver_list: %s",
+          FLAGS_inject_broken_nameserver_list.c_str());
+  grpc_core::UniquePtr<grpc::testing::FakeNonResponsiveDNSServer>
+      fake_non_responsive_dns_server;
+  if (FLAGS_inject_broken_nameserver_list == "True") {
+    g_fake_non_responsive_dns_server_port = grpc_pick_unused_port_or_die();
+    fake_non_responsive_dns_server.reset(
+        grpc_core::New<grpc::testing::FakeNonResponsiveDNSServer>(
+            g_fake_non_responsive_dns_server_port));
+    grpc_ares_test_only_inject_config = InjectBrokenNameServerList;
+    GPR_ASSERT(
+        gpr_asprintf(&whole_uri, "dns:///%s", FLAGS_target_name.c_str()));
+  } else if (FLAGS_inject_broken_nameserver_list == "False") {
+    gpr_log(GPR_INFO, "Specifying authority in uris to: %s",
+            FLAGS_local_dns_server_address.c_str());
+    GPR_ASSERT(gpr_asprintf(&whole_uri, "dns://%s/%s",
+                            FLAGS_local_dns_server_address.c_str(),
+                            FLAGS_target_name.c_str()));
+  } else {
+    gpr_log(GPR_DEBUG, "Invalid value for --inject_broken_nameserver_list.");
+    abort();
+  }
   gpr_log(GPR_DEBUG, "resolver_component_test: --enable_srv_queries: %s",
           FLAGS_enable_srv_queries.c_str());
   grpc_channel_args* resolver_args = nullptr;
@@ -524,7 +618,9 @@ void RunResolvesRelevantRecordsTest(
                                                   CreateResultHandler(&args));
   grpc_channel_args_destroy(resolver_args);
   gpr_free(whole_uri);
-  resolver->StartLocked();
+  GRPC_CLOSURE_SCHED(GRPC_CLOSURE_CREATE(StartResolvingLocked, resolver.get(),
+                                         grpc_combiner_scheduler(args.lock)),
+                     GRPC_ERROR_NONE);
   grpc_core::ExecCtx::Get()->Flush();
   PollPollsetUntilRequestDone(&args);
   ArgsFinish(&args);
@@ -554,15 +650,11 @@ int main(int argc, char** argv) {
   grpc_init();
   grpc::testing::TestEnvironment env(argc, argv);
   ::testing::InitGoogleTest(&argc, argv);
-  ParseCommandLineFlags(&argc, &argv, true);
+  grpc::testing::InitTest(&argc, &argv, true);
   if (FLAGS_target_name == "") {
     gpr_log(GPR_ERROR, "Missing target_name param.");
     abort();
   }
-  if (FLAGS_local_dns_server_address != "") {
-    gpr_log(GPR_INFO, "Specifying authority in uris to: %s",
-            FLAGS_local_dns_server_address.c_str());
-  }
   auto result = RUN_ALL_TESTS();
   grpc_shutdown();
   return result;
index a0eda79..e767e4c 100755 (executable)
@@ -124,9 +124,11 @@ current_test_subprocess = subprocess.Popen([
   '--target_name', 'no-srv-ipv4-single-target.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '5.5.5.5:443,False',
   '--expected_chosen_service_config', '',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', '',
   '--enable_srv_queries', 'True',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -138,9 +140,11 @@ current_test_subprocess = subprocess.Popen([
   '--target_name', 'srv-ipv4-single-target.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '1.2.3.4:1234,True',
   '--expected_chosen_service_config', '',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', '',
   '--enable_srv_queries', 'True',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -152,9 +156,11 @@ current_test_subprocess = subprocess.Popen([
   '--target_name', 'srv-ipv4-multi-target.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '1.2.3.5:1234,True;1.2.3.6:1234,True;1.2.3.7:1234,True',
   '--expected_chosen_service_config', '',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', '',
   '--enable_srv_queries', 'True',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -166,9 +172,11 @@ current_test_subprocess = subprocess.Popen([
   '--target_name', 'srv-ipv6-single-target.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '[2607:f8b0:400a:801::1001]:1234,True',
   '--expected_chosen_service_config', '',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', '',
   '--enable_srv_queries', 'True',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -180,9 +188,11 @@ current_test_subprocess = subprocess.Popen([
   '--target_name', 'srv-ipv6-multi-target.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '[2607:f8b0:400a:801::1002]:1234,True;[2607:f8b0:400a:801::1003]:1234,True;[2607:f8b0:400a:801::1004]:1234,True',
   '--expected_chosen_service_config', '',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', '',
   '--enable_srv_queries', 'True',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -193,10 +203,12 @@ current_test_subprocess = subprocess.Popen([
   args.test_bin_path,
   '--target_name', 'srv-ipv4-simple-service-config.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '1.2.3.4:1234,True',
-  '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]}]}',
+  '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService"}],"waitForReady":true}]}',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', 'round_robin',
   '--enable_srv_queries', 'True',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -207,10 +219,12 @@ current_test_subprocess = subprocess.Popen([
   args.test_bin_path,
   '--target_name', 'ipv4-no-srv-simple-service-config.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '1.2.3.4:443,False',
-  '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"NoSrvSimpleService","waitForReady":true}]}]}',
+  '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"NoSrvSimpleService"}],"waitForReady":true}]}',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', 'round_robin',
   '--enable_srv_queries', 'True',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -222,9 +236,11 @@ current_test_subprocess = subprocess.Popen([
   '--target_name', 'ipv4-no-config-for-cpp.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_chosen_service_config', '',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', '',
   '--enable_srv_queries', 'True',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -236,9 +252,11 @@ current_test_subprocess = subprocess.Popen([
   '--target_name', 'ipv4-cpp-config-has-zero-percentage.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_chosen_service_config', '',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', '',
   '--enable_srv_queries', 'True',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -249,10 +267,12 @@ current_test_subprocess = subprocess.Popen([
   args.test_bin_path,
   '--target_name', 'ipv4-second-language-is-cpp.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '1.2.3.4:443,False',
-  '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService","waitForReady":true}]}]}',
+  '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService"}],"waitForReady":true}]}',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', 'round_robin',
   '--enable_srv_queries', 'True',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -263,10 +283,12 @@ current_test_subprocess = subprocess.Popen([
   args.test_bin_path,
   '--target_name', 'ipv4-config-with-percentages.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '1.2.3.4:443,False',
-  '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"AlwaysPickedService","waitForReady":true}]}]}',
+  '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"AlwaysPickedService"}],"waitForReady":true}]}',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', 'round_robin',
   '--enable_srv_queries', 'True',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -278,9 +300,11 @@ current_test_subprocess = subprocess.Popen([
   '--target_name', 'srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '1.2.3.4:1234,True;1.2.3.4:443,False',
   '--expected_chosen_service_config', '',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', '',
   '--enable_srv_queries', 'True',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -292,9 +316,11 @@ current_test_subprocess = subprocess.Popen([
   '--target_name', 'srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '[2607:f8b0:400a:801::1002]:1234,True;[2607:f8b0:400a:801::1002]:443,False',
   '--expected_chosen_service_config', '',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', '',
   '--enable_srv_queries', 'True',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -305,10 +331,12 @@ current_test_subprocess = subprocess.Popen([
   args.test_bin_path,
   '--target_name', 'ipv4-config-causing-fallback-to-tcp.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '1.2.3.4:443,False',
-  '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooThree","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFour","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFive","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSix","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSeven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEight","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooNine","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTen","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEleven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]}]}',
+  '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooTwo","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooThree","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooFour","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooFive","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooSix","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooSeven","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooEight","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooNine","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooTen","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooEleven","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooTwelve","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooTwelve","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooTwelve","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooTwelve","service":"SimpleService"}],"waitForReady":true}]}',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', '',
   '--enable_srv_queries', 'True',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -320,9 +348,11 @@ current_test_subprocess = subprocess.Popen([
   '--target_name', 'srv-ipv4-single-target-srv-disabled.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '2.3.4.5:443,False',
   '--expected_chosen_service_config', '',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', '',
   '--enable_srv_queries', 'False',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -334,9 +364,11 @@ current_test_subprocess = subprocess.Popen([
   '--target_name', 'srv-ipv4-multi-target-srv-disabled.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '9.2.3.5:443,False;9.2.3.6:443,False;9.2.3.7:443,False',
   '--expected_chosen_service_config', '',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', '',
   '--enable_srv_queries', 'False',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -348,9 +380,11 @@ current_test_subprocess = subprocess.Popen([
   '--target_name', 'srv-ipv6-single-target-srv-disabled.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '[2600::1001]:443,False',
   '--expected_chosen_service_config', '',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', '',
   '--enable_srv_queries', 'False',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -362,9 +396,11 @@ current_test_subprocess = subprocess.Popen([
   '--target_name', 'srv-ipv6-multi-target-srv-disabled.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '[2600::1002]:443,False;[2600::1003]:443,False;[2600::1004]:443,False',
   '--expected_chosen_service_config', '',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', '',
   '--enable_srv_queries', 'False',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -375,10 +411,12 @@ current_test_subprocess = subprocess.Popen([
   args.test_bin_path,
   '--target_name', 'srv-ipv4-simple-service-config-srv-disabled.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '5.5.3.4:443,False',
-  '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]}]}',
+  '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService"}],"waitForReady":true}]}',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', 'round_robin',
   '--enable_srv_queries', 'False',
   '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -390,9 +428,11 @@ current_test_subprocess = subprocess.Popen([
   '--target_name', 'srv-ipv4-simple-service-config-txt-disabled.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '1.2.3.4:1234,True',
   '--expected_chosen_service_config', '',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', '',
   '--enable_srv_queries', 'True',
   '--enable_txt_queries', 'False',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -404,9 +444,11 @@ current_test_subprocess = subprocess.Popen([
   '--target_name', 'ipv4-cpp-config-has-zero-percentage-txt-disabled.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_chosen_service_config', '',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', '',
   '--enable_srv_queries', 'True',
   '--enable_txt_queries', 'False',
+  '--inject_broken_nameserver_list', 'False',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
@@ -418,9 +460,107 @@ current_test_subprocess = subprocess.Popen([
   '--target_name', 'ipv4-second-language-is-cpp-txt-disabled.resolver-tests-version-4.grpctestingexp.',
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_chosen_service_config', '',
+  '--expected_service_config_error', '',
   '--expected_lb_policy', '',
   '--enable_srv_queries', 'True',
   '--enable_txt_queries', 'False',
+  '--inject_broken_nameserver_list', 'False',
+  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+current_test_subprocess.communicate()
+if current_test_subprocess.returncode != 0:
+  num_test_failures += 1
+
+test_runner_log('Run test with target: %s' % 'ipv4-svc_cfg_bad_json.resolver-tests-version-4.grpctestingexp.')
+current_test_subprocess = subprocess.Popen([
+  args.test_bin_path,
+  '--target_name', 'ipv4-svc_cfg_bad_json.resolver-tests-version-4.grpctestingexp.',
+  '--expected_addrs', '1.2.3.4:443,False',
+  '--expected_chosen_service_config', '',
+  '--expected_service_config_error', 'could not parse',
+  '--expected_lb_policy', '',
+  '--enable_srv_queries', 'True',
+  '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
+  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+current_test_subprocess.communicate()
+if current_test_subprocess.returncode != 0:
+  num_test_failures += 1
+
+test_runner_log('Run test with target: %s' % 'ipv4-svc_cfg_bad_client_language.resolver-tests-version-4.grpctestingexp.')
+current_test_subprocess = subprocess.Popen([
+  args.test_bin_path,
+  '--target_name', 'ipv4-svc_cfg_bad_client_language.resolver-tests-version-4.grpctestingexp.',
+  '--expected_addrs', '1.2.3.4:443,False',
+  '--expected_chosen_service_config', '',
+  '--expected_service_config_error', 'field:clientLanguage error:should be of type array',
+  '--expected_lb_policy', '',
+  '--enable_srv_queries', 'True',
+  '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
+  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+current_test_subprocess.communicate()
+if current_test_subprocess.returncode != 0:
+  num_test_failures += 1
+
+test_runner_log('Run test with target: %s' % 'ipv4-svc_cfg_bad_percentage.resolver-tests-version-4.grpctestingexp.')
+current_test_subprocess = subprocess.Popen([
+  args.test_bin_path,
+  '--target_name', 'ipv4-svc_cfg_bad_percentage.resolver-tests-version-4.grpctestingexp.',
+  '--expected_addrs', '1.2.3.4:443,False',
+  '--expected_chosen_service_config', '',
+  '--expected_service_config_error', 'field:percentage error:should be of type number',
+  '--expected_lb_policy', '',
+  '--enable_srv_queries', 'True',
+  '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
+  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+current_test_subprocess.communicate()
+if current_test_subprocess.returncode != 0:
+  num_test_failures += 1
+
+test_runner_log('Run test with target: %s' % 'ipv4-svc_cfg_bad_wait_for_ready.resolver-tests-version-4.grpctestingexp.')
+current_test_subprocess = subprocess.Popen([
+  args.test_bin_path,
+  '--target_name', 'ipv4-svc_cfg_bad_wait_for_ready.resolver-tests-version-4.grpctestingexp.',
+  '--expected_addrs', '1.2.3.4:443,False',
+  '--expected_chosen_service_config', '',
+  '--expected_service_config_error', 'field:waitForReady error:Type should be true/false',
+  '--expected_lb_policy', '',
+  '--enable_srv_queries', 'True',
+  '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'False',
+  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+current_test_subprocess.communicate()
+if current_test_subprocess.returncode != 0:
+  num_test_failures += 1
+
+test_runner_log('Run test with target: %s' % 'no-srv-ipv4-single-target-inject-broken-nameservers.resolver-tests-version-4.grpctestingexp.')
+current_test_subprocess = subprocess.Popen([
+  args.test_bin_path,
+  '--target_name', 'no-srv-ipv4-single-target-inject-broken-nameservers.resolver-tests-version-4.grpctestingexp.',
+  '--expected_addrs', '5.5.5.5:443,False',
+  '--expected_chosen_service_config', '',
+  '--expected_service_config_error', '',
+  '--expected_lb_policy', '',
+  '--enable_srv_queries', 'True',
+  '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'True',
+  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+current_test_subprocess.communicate()
+if current_test_subprocess.returncode != 0:
+  num_test_failures += 1
+
+test_runner_log('Run test with target: %s' % 'ipv4-config-causing-fallback-to-tcp-inject-broken-nameservers.resolver-tests-version-4.grpctestingexp.')
+current_test_subprocess = subprocess.Popen([
+  args.test_bin_path,
+  '--target_name', 'ipv4-config-causing-fallback-to-tcp-inject-broken-nameservers.resolver-tests-version-4.grpctestingexp.',
+  '--expected_addrs', '1.2.3.4:443,False',
+  '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooThree","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFour","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFive","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSix","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSeven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEight","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooNine","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTen","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEleven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]}]}',
+  '--expected_service_config_error', 'Service config parsing error',
+  '--expected_lb_policy', '',
+  '--enable_srv_queries', 'True',
+  '--enable_txt_queries', 'True',
+  '--inject_broken_nameserver_list', 'True',
   '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
index 68be00a..d8f7100 100644 (file)
 #include <thread>
 #include <vector>
 
+#ifdef __FreeBSD__
+#include <sys/wait.h>
+#endif
+
 #include "test/cpp/util/subprocess.h"
 #include "test/cpp/util/test_config.h"
 
index 738fe65..f72a7ea 100644 (file)
@@ -4,9 +4,11 @@ resolver_component_tests:
 - expected_addrs:
   - {address: '5.5.5.5:443', is_balancer: false}
   expected_chosen_service_config: null
+  expected_service_config_error: null
   expected_lb_policy: null
   enable_srv_queries: true
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: no-srv-ipv4-single-target
   records:
     no-srv-ipv4-single-target:
@@ -14,9 +16,11 @@ resolver_component_tests:
 - expected_addrs:
   - {address: '1.2.3.4:1234', is_balancer: true}
   expected_chosen_service_config: null
+  expected_service_config_error: null
   expected_lb_policy: null
   enable_srv_queries: true
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: srv-ipv4-single-target
   records:
     _grpclb._tcp.srv-ipv4-single-target:
@@ -28,9 +32,11 @@ resolver_component_tests:
   - {address: '1.2.3.6:1234', is_balancer: true}
   - {address: '1.2.3.7:1234', is_balancer: true}
   expected_chosen_service_config: null
+  expected_service_config_error: null
   expected_lb_policy: null
   enable_srv_queries: true
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: srv-ipv4-multi-target
   records:
     _grpclb._tcp.srv-ipv4-multi-target:
@@ -42,9 +48,11 @@ resolver_component_tests:
 - expected_addrs:
   - {address: '[2607:f8b0:400a:801::1001]:1234', is_balancer: true}
   expected_chosen_service_config: null
+  expected_service_config_error: null
   expected_lb_policy: null
   enable_srv_queries: true
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: srv-ipv6-single-target
   records:
     _grpclb._tcp.srv-ipv6-single-target:
@@ -56,9 +64,11 @@ resolver_component_tests:
   - {address: '[2607:f8b0:400a:801::1003]:1234', is_balancer: true}
   - {address: '[2607:f8b0:400a:801::1004]:1234', is_balancer: true}
   expected_chosen_service_config: null
+  expected_service_config_error: null
   expected_lb_policy: null
   enable_srv_queries: true
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: srv-ipv6-multi-target
   records:
     _grpclb._tcp.srv-ipv6-multi-target:
@@ -69,10 +79,12 @@ resolver_component_tests:
     - {TTL: '2100', data: '2607:f8b0:400a:801::1004', type: AAAA}
 - expected_addrs:
   - {address: '1.2.3.4:1234', is_balancer: true}
-  expected_chosen_service_config: '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]}]}'
+  expected_chosen_service_config: '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService"}],"waitForReady":true}]}'
+  expected_service_config_error: null
   expected_lb_policy: round_robin
   enable_srv_queries: true
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: srv-ipv4-simple-service-config
   records:
     _grpclb._tcp.srv-ipv4-simple-service-config:
@@ -80,80 +92,92 @@ resolver_component_tests:
     ipv4-simple-service-config:
     - {TTL: '2100', data: 1.2.3.4, type: A}
     _grpc_config.srv-ipv4-simple-service-config:
-    - {TTL: '2100', data: 'grpc_config=[{"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]}]}}]',
+    - {TTL: '2100', data: 'grpc_config=[{"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService"}],"waitForReady":true}]}}]',
       type: TXT}
 - expected_addrs:
   - {address: '1.2.3.4:443', is_balancer: false}
-  expected_chosen_service_config: '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"NoSrvSimpleService","waitForReady":true}]}]}'
+  expected_chosen_service_config: '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"NoSrvSimpleService"}],"waitForReady":true}]}'
+  expected_service_config_error: null
   expected_lb_policy: round_robin
   enable_srv_queries: true
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: ipv4-no-srv-simple-service-config
   records:
     ipv4-no-srv-simple-service-config:
     - {TTL: '2100', data: 1.2.3.4, type: A}
     _grpc_config.ipv4-no-srv-simple-service-config:
-    - {TTL: '2100', data: 'grpc_config=[{"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"NoSrvSimpleService","waitForReady":true}]}]}}]',
+    - {TTL: '2100', data: 'grpc_config=[{"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"NoSrvSimpleService"}],"waitForReady":true}]}}]',
       type: TXT}
 - expected_addrs:
   - {address: '1.2.3.4:443', is_balancer: false}
   expected_chosen_service_config: null
+  expected_service_config_error: null
   expected_lb_policy: null
   enable_srv_queries: true
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: ipv4-no-config-for-cpp
   records:
     ipv4-no-config-for-cpp:
     - {TTL: '2100', data: 1.2.3.4, type: A}
     _grpc_config.ipv4-no-config-for-cpp:
-    - {TTL: '2100', data: 'grpc_config=[{"clientLanguage":["python"],"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"PythonService","waitForReady":true}]}]}}]',
+    - {TTL: '2100', data: 'grpc_config=[{"clientLanguage":["python"],"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"PythonService"}],"waitForReady":true}]}}]',
       type: TXT}
 - expected_addrs:
   - {address: '1.2.3.4:443', is_balancer: false}
   expected_chosen_service_config: null
+  expected_service_config_error: null
   expected_lb_policy: null
   enable_srv_queries: true
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: ipv4-cpp-config-has-zero-percentage
   records:
     ipv4-cpp-config-has-zero-percentage:
     - {TTL: '2100', data: 1.2.3.4, type: A}
     _grpc_config.ipv4-cpp-config-has-zero-percentage:
-    - {TTL: '2100', data: 'grpc_config=[{"percentage":0,"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService","waitForReady":true}]}]}}]',
+    - {TTL: '2100', data: 'grpc_config=[{"percentage":0,"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService"}],"waitForReady":true}]}}]',
       type: TXT}
 - expected_addrs:
   - {address: '1.2.3.4:443', is_balancer: false}
-  expected_chosen_service_config: '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService","waitForReady":true}]}]}'
+  expected_chosen_service_config: '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService"}],"waitForReady":true}]}'
+  expected_service_config_error: null
   expected_lb_policy: round_robin
   enable_srv_queries: true
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: ipv4-second-language-is-cpp
   records:
     ipv4-second-language-is-cpp:
     - {TTL: '2100', data: 1.2.3.4, type: A}
     _grpc_config.ipv4-second-language-is-cpp:
-    - {TTL: '2100', data: 'grpc_config=[{"clientLanguage":["go"],"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"GoService","waitForReady":true}]}]}},{"clientLanguage":["c++"],"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService","waitForReady":true}]}]}}]',
+    - {TTL: '2100', data: 'grpc_config=[{"clientLanguage":["go"],"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"GoService"}],"waitForReady":true}]}},{"clientLanguage":["c++"],"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService"}],"waitForReady":true}]}}]',
       type: TXT}
 - expected_addrs:
   - {address: '1.2.3.4:443', is_balancer: false}
-  expected_chosen_service_config: '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"AlwaysPickedService","waitForReady":true}]}]}'
+  expected_chosen_service_config: '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"AlwaysPickedService"}],"waitForReady":true}]}'
+  expected_service_config_error: null
   expected_lb_policy: round_robin
   enable_srv_queries: true
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: ipv4-config-with-percentages
   records:
     ipv4-config-with-percentages:
     - {TTL: '2100', data: 1.2.3.4, type: A}
     _grpc_config.ipv4-config-with-percentages:
-    - {TTL: '2100', data: 'grpc_config=[{"percentage":0,"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"NeverPickedService","waitForReady":true}]}]}},{"percentage":100,"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"AlwaysPickedService","waitForReady":true}]}]}}]',
+    - {TTL: '2100', data: 'grpc_config=[{"percentage":0,"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"NeverPickedService"}],"waitForReady":true}]}},{"percentage":100,"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"AlwaysPickedService"}],"waitForReady":true}]}}]',
       type: TXT}
 - expected_addrs:
   - {address: '1.2.3.4:1234', is_balancer: true}
   - {address: '1.2.3.4:443', is_balancer: false}
   expected_chosen_service_config: null
+  expected_service_config_error: null
   expected_lb_policy: null
   enable_srv_queries: true
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: srv-ipv4-target-has-backend-and-balancer
   records:
     _grpclb._tcp.srv-ipv4-target-has-backend-and-balancer:
@@ -166,9 +190,11 @@ resolver_component_tests:
   - {address: '[2607:f8b0:400a:801::1002]:1234', is_balancer: true}
   - {address: '[2607:f8b0:400a:801::1002]:443', is_balancer: false}
   expected_chosen_service_config: null
+  expected_service_config_error: null
   expected_lb_policy: null
   enable_srv_queries: true
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: srv-ipv6-target-has-backend-and-balancer
   records:
     _grpclb._tcp.srv-ipv6-target-has-backend-and-balancer:
@@ -179,24 +205,28 @@ resolver_component_tests:
     - {TTL: '2100', data: '2607:f8b0:400a:801::1002', type: AAAA}
 - expected_addrs:
   - {address: '1.2.3.4:443', is_balancer: false}
-  expected_chosen_service_config: '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooThree","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFour","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFive","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSix","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSeven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEight","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooNine","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTen","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEleven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]}]}'
+  expected_chosen_service_config: '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooTwo","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooThree","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooFour","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooFive","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooSix","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooSeven","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooEight","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooNine","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooTen","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooEleven","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooTwelve","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooTwelve","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooTwelve","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooTwelve","service":"SimpleService"}],"waitForReady":true}]}'
+  expected_service_config_error: null
   expected_lb_policy: null
   enable_srv_queries: true
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: ipv4-config-causing-fallback-to-tcp
   records:
     ipv4-config-causing-fallback-to-tcp:
     - {TTL: '2100', data: 1.2.3.4, type: A}
     _grpc_config.ipv4-config-causing-fallback-to-tcp:
-    - {TTL: '2100', data: 'grpc_config=[{"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooThree","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFour","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFive","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSix","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSeven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEight","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooNine","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTen","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEleven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]}]}}]',
+    - {TTL: '2100', data: 'grpc_config=[{"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooTwo","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooThree","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooFour","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooFive","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooSix","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooSeven","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooEight","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooNine","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooTen","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooEleven","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooTwelve","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooTwelve","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooTwelve","service":"SimpleService"}],"waitForReady":true},{"name":[{"method":"FooTwelve","service":"SimpleService"}],"waitForReady":true}]}}]',
       type: TXT}
 # Tests for which we don't enable SRV queries
 - expected_addrs:
   - {address: '2.3.4.5:443', is_balancer: false}
   expected_chosen_service_config: null
+  expected_service_config_error: null
   expected_lb_policy: null
   enable_srv_queries: false
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: srv-ipv4-single-target-srv-disabled
   records:
     _grpclb._tcp.srv-ipv4-single-target-srv-disabled:
@@ -210,9 +240,11 @@ resolver_component_tests:
   - {address: '9.2.3.6:443', is_balancer: false}
   - {address: '9.2.3.7:443', is_balancer: false}
   expected_chosen_service_config: null
+  expected_service_config_error: null
   expected_lb_policy: null
   enable_srv_queries: false
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: srv-ipv4-multi-target-srv-disabled
   records:
     _grpclb._tcp.srv-ipv4-multi-target-srv-disabled:
@@ -228,9 +260,11 @@ resolver_component_tests:
 - expected_addrs:
   - {address: '[2600::1001]:443', is_balancer: false}
   expected_chosen_service_config: null
+  expected_service_config_error: null
   expected_lb_policy: null
   enable_srv_queries: false
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: srv-ipv6-single-target-srv-disabled
   records:
     _grpclb._tcp.srv-ipv6-single-target-srv-disabled:
@@ -244,9 +278,11 @@ resolver_component_tests:
   - {address: '[2600::1003]:443', is_balancer: false}
   - {address: '[2600::1004]:443', is_balancer: false}
   expected_chosen_service_config: null
+  expected_service_config_error: null
   expected_lb_policy: null
   enable_srv_queries: false
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: srv-ipv6-multi-target-srv-disabled
   records:
     _grpclb._tcp.srv-ipv6-multi-target-srv-disabled:
@@ -261,10 +297,12 @@ resolver_component_tests:
     - {TTL: '2100', data: '2600::1004', type: AAAA}
 - expected_addrs:
   - {address: '5.5.3.4:443', is_balancer: false}
-  expected_chosen_service_config: '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]}]}'
+  expected_chosen_service_config: '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService"}],"waitForReady":true}]}'
+  expected_service_config_error: null
   expected_lb_policy: round_robin
   enable_srv_queries: false
   enable_txt_queries: true
+  inject_broken_nameserver_list: false
   record_to_resolve: srv-ipv4-simple-service-config-srv-disabled
   records:
     _grpclb._tcp.srv-ipv4-simple-service-config-srv-disabled:
@@ -274,14 +312,16 @@ resolver_component_tests:
     srv-ipv4-simple-service-config-srv-disabled:
     - {TTL: '2100', data: 5.5.3.4, type: A}
     _grpc_config.srv-ipv4-simple-service-config-srv-disabled:
-    - {TTL: '2100', data: 'grpc_config=[{"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]}]}}]',
+    - {TTL: '2100', data: 'grpc_config=[{"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService"}],"waitForReady":true}]}}]',
       type: TXT}
 - expected_addrs:
   - {address: '1.2.3.4:1234', is_balancer: true}
   expected_chosen_service_config: null
+  expected_service_config_error: null
   expected_lb_policy: null
   enable_srv_queries: true
   enable_txt_queries: false
+  inject_broken_nameserver_list: false
   record_to_resolve: srv-ipv4-simple-service-config-txt-disabled
   records:
     _grpclb._tcp.srv-ipv4-simple-service-config-txt-disabled:
@@ -289,31 +329,123 @@ resolver_component_tests:
     ipv4-simple-service-config-txt-disabled:
     - {TTL: '2100', data: 1.2.3.4, type: A}
     _grpc_config.srv-ipv4-simple-service-config-txt-disabled:
-    - {TTL: '2100', data: 'grpc_config=[{"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]}]}}]',
+    - {TTL: '2100', data: 'grpc_config=[{"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService"}],"waitForReady":true}]}}]',
       type: TXT}
 - expected_addrs:
   - {address: '1.2.3.4:443', is_balancer: false}
   expected_chosen_service_config: null
+  expected_service_config_error: null
   expected_lb_policy: null
   enable_srv_queries: true
   enable_txt_queries: false
+  inject_broken_nameserver_list: false
   record_to_resolve: ipv4-cpp-config-has-zero-percentage-txt-disabled
   records:
     ipv4-cpp-config-has-zero-percentage-txt-disabled:
     - {TTL: '2100', data: 1.2.3.4, type: A}
     _grpc_config.ipv4-cpp-config-has-zero-percentage-txt-disabled:
-    - {TTL: '2100', data: 'grpc_config=[{"percentage":0,"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService","waitForReady":true}]}]}}]',
+    - {TTL: '2100', data: 'grpc_config=[{"percentage":0,"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService"}],"waitForReady":true}]}}]',
       type: TXT}
 - expected_addrs:
   - {address: '1.2.3.4:443', is_balancer: false}
   expected_chosen_service_config: null
+  expected_service_config_error: null
   expected_lb_policy: null
   enable_srv_queries: true
   enable_txt_queries: false
+  inject_broken_nameserver_list: false
   record_to_resolve: ipv4-second-language-is-cpp-txt-disabled
   records:
     ipv4-second-language-is-cpp-txt-disabled:
     - {TTL: '2100', data: 1.2.3.4, type: A}
     _grpc_config.ipv4-second-language-is-cpp-txt-disabled:
-    - {TTL: '2100', data: 'grpc_config=[{"clientLanguage":["go"],"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"GoService","waitForReady":true}]}]}},{"clientLanguage":["c++"],"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService","waitForReady":true}]}]}}]',
+    - {TTL: '2100', data: 'grpc_config=[{"clientLanguage":["go"],"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"GoService"}],"waitForReady":true}]}},{"clientLanguage":["c++"],"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService"}],"waitForReady":true}]}}]',
+      type: TXT}
+- expected_addrs:
+  - {address: '1.2.3.4:443', is_balancer: false}
+  expected_chosen_service_config: null
+  expected_service_config_error: 'could not parse'
+  expected_lb_policy: null
+  enable_srv_queries: true
+  enable_txt_queries: true
+  inject_broken_nameserver_list: false
+  record_to_resolve: ipv4-svc_cfg_bad_json
+  records:
+    ipv4-svc_cfg_bad_json:
+    - {TTL: '2100', data: 1.2.3.4, type: A}
+    _grpc_config.ipv4-svc_cfg_bad_json:
+    - {TTL: '2100', data: 'grpc_config=[{]',
+      type: TXT}
+- expected_addrs:
+  - {address: '1.2.3.4:443', is_balancer: false}
+  expected_chosen_service_config: null
+  expected_service_config_error: 'field:clientLanguage error:should be of type array'
+  expected_lb_policy: null
+  enable_srv_queries: true
+  enable_txt_queries: true
+  inject_broken_nameserver_list: false
+  record_to_resolve: ipv4-svc_cfg_bad_client_language
+  records:
+    ipv4-svc_cfg_bad_client_language:
+    - {TTL: '2100', data: 1.2.3.4, type: A}
+    _grpc_config.ipv4-svc_cfg_bad_client_language:
+    - {TTL: '2100', data: 'grpc_config=[{"clientLanguage":"go","serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"GoService"}],"waitForReady":true}]}},{"clientLanguage":["c++"],"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService"}],"waitForReady":true}]}}]',
+      type: TXT}
+- expected_addrs:
+  - {address: '1.2.3.4:443', is_balancer: false}
+  expected_chosen_service_config: null
+  expected_service_config_error: 'field:percentage error:should be of type number'
+  expected_lb_policy: null
+  enable_srv_queries: true
+  enable_txt_queries: true
+  inject_broken_nameserver_list: false
+  record_to_resolve: ipv4-svc_cfg_bad_percentage
+  records:
+    ipv4-svc_cfg_bad_percentage:
+    - {TTL: '2100', data: 1.2.3.4, type: A}
+    _grpc_config.ipv4-svc_cfg_bad_percentage:
+    - {TTL: '2100', data: 'grpc_config=[{"percentage":"0","serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"GoService"}],"waitForReady":true}]}},{"clientLanguage":["c++"],"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService"}],"waitForReady":true}]}}]',
+      type: TXT}
+- expected_addrs:
+  - {address: '1.2.3.4:443', is_balancer: false}
+  expected_chosen_service_config: null
+  expected_service_config_error: 'field:waitForReady error:Type should be true/false'
+  expected_lb_policy: null
+  enable_srv_queries: true
+  enable_txt_queries: true
+  inject_broken_nameserver_list: false
+  record_to_resolve: ipv4-svc_cfg_bad_wait_for_ready
+  records:
+    ipv4-svc_cfg_bad_wait_for_ready:
+    - {TTL: '2100', data: 1.2.3.4, type: A}
+    _grpc_config.ipv4-svc_cfg_bad_wait_for_ready:
+    - {TTL: '2100', data: 'grpc_config=[{"serviceConfig":{"methodConfig":[{"name":[{"method":"Foo","service":"CppService"}],"waitForReady":"true"}]}}]',
+      type: TXT}      
+# Tests for which we also exercise the resolver's ability to skip past a broken DNS server in its nameserver list
+- expected_addrs:
+  - {address: '5.5.5.5:443', is_balancer: false}
+  expected_chosen_service_config: null
+  expected_service_config_error: null
+  expected_lb_policy: null
+  enable_srv_queries: true
+  enable_txt_queries: true
+  inject_broken_nameserver_list: true
+  record_to_resolve: no-srv-ipv4-single-target-inject-broken-nameservers
+  records:
+    no-srv-ipv4-single-target-inject-broken-nameservers:
+    - {TTL: '2100', data: 5.5.5.5, type: A}
+- expected_addrs:
+  - {address: '1.2.3.4:443', is_balancer: false}
+  expected_chosen_service_config: '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooThree","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFour","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFive","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSix","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSeven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEight","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooNine","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTen","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEleven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]}]}'
+  expected_service_config_error: 'Service config parsing error'
+  expected_lb_policy: null
+  enable_srv_queries: true
+  enable_txt_queries: true
+  inject_broken_nameserver_list: true
+  record_to_resolve: ipv4-config-causing-fallback-to-tcp-inject-broken-nameservers
+  records:
+    ipv4-config-causing-fallback-to-tcp-inject-broken-nameservers:
+    - {TTL: '2100', data: 1.2.3.4, type: A}
+    _grpc_config.ipv4-config-causing-fallback-to-tcp-inject-broken-nameservers:
+    - {TTL: '2100', data: 'grpc_config=[{"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooThree","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFour","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFive","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSix","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSeven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEight","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooNine","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTen","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEleven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]}]}}]',
       type: TXT}
index 7b22f23..b74f4d0 100644 (file)
@@ -118,7 +118,7 @@ class EndpointPairFixture {
           "target", &c_args, GRPC_CLIENT_DIRECT_CHANNEL, transport);
       grpc_chttp2_transport_start_reading(transport, nullptr, nullptr);
 
-      channel_ = CreateChannelInternal(
+      channel_ = ::grpc::CreateChannelInternal(
           "", channel,
           std::vector<std::unique_ptr<
               experimental::ClientInterceptorFactoryInterface>>());
index 815780e..dcfa2db 100644 (file)
@@ -285,8 +285,18 @@ class CallbackStreamingPingPongReactor final
       }
       return;
     }
-    write_time_ = UsageTimer::Now();
-    StartWrite(client_->request());
+    if (!client_->IsClosedLoop()) {
+      gpr_timespec next_issue_time = client_->NextRPCIssueTime();
+      // Start an alarm callback to run the internal callback after
+      // next_issue_time
+      ctx_->alarm_.experimental().Set(next_issue_time, [this](bool ok) {
+        write_time_ = UsageTimer::Now();
+        StartWrite(client_->request());
+      });
+    } else {
+      write_time_ = UsageTimer::Now();
+      StartWrite(client_->request());
+    }
   }
 
   void OnDone(const Status& s) override {
index 181e11f..7d4d5d9 100644 (file)
@@ -288,7 +288,7 @@ std::unique_ptr<ScenarioResult> RunScenario(
     gpr_log(GPR_INFO, "Starting server on %s (worker #%" PRIuPTR ")",
             workers[i].c_str(), i);
     if (!run_inproc) {
-      servers[i].stub = WorkerService::NewStub(CreateChannel(
+      servers[i].stub = WorkerService::NewStub(grpc::CreateChannel(
           workers[i], GetCredentialsProvider()->GetChannelCredentials(
                           GetCredType(workers[i], per_worker_credential_types,
                                       credential_type),
@@ -349,7 +349,7 @@ std::unique_ptr<ScenarioResult> RunScenario(
     gpr_log(GPR_INFO, "Starting client on %s (worker #%" PRIuPTR ")",
             worker.c_str(), i + num_servers);
     if (!run_inproc) {
-      clients[i].stub = WorkerService::NewStub(CreateChannel(
+      clients[i].stub = WorkerService::NewStub(grpc::CreateChannel(
           worker,
           GetCredentialsProvider()->GetChannelCredentials(
               GetCredType(worker, per_worker_credential_types, credential_type),
@@ -557,7 +557,7 @@ bool RunQuit(
 
   ChannelArguments channel_args;
   for (size_t i = 0; i < workers.size(); i++) {
-    auto stub = WorkerService::NewStub(CreateChannel(
+    auto stub = WorkerService::NewStub(grpc::CreateChannel(
         workers[i], GetCredentialsProvider()->GetChannelCredentials(
                         GetCredType(workers[i], per_worker_credential_types,
                                     credential_type),
index 948c308..42cc9e0 100644 (file)
 #include <sstream>
 #include <string>
 
+#ifdef __FreeBSD__
+#include <sys/wait.h>
+#endif
+
 #include <grpc/support/log.h>
 
 #include "src/core/lib/gpr/env.h"
index 9831c06..14b735c 100644 (file)
@@ -115,7 +115,7 @@ TEST(ServerRequestCallTest, ShortDeadlineDoesNotCauseOkayFalse) {
   });
 
   auto stub = testing::EchoTestService::NewStub(
-      CreateChannel(address, InsecureChannelCredentials()));
+      grpc::CreateChannel(address, InsecureChannelCredentials()));
 
   for (int i = 0; i < 100; i++) {
     gpr_log(GPR_INFO, "Sending %d.", i);
index 1d64153..a91705b 100644 (file)
@@ -74,8 +74,8 @@ class CliCallTest : public ::testing::Test {
   void TearDown() override { server_->Shutdown(); }
 
   void ResetStub() {
-    channel_ =
-        CreateChannel(server_address_.str(), InsecureChannelCredentials());
+    channel_ = grpc::CreateChannel(server_address_.str(),
+                                   InsecureChannelCredentials());
     stub_ = grpc::testing::EchoTestService::NewStub(channel_);
   }
 
index 79a5e13..5c32198 100644 (file)
@@ -34,7 +34,7 @@ class SslCredentialProvider : public testing::CredentialTypeProvider {
  public:
   std::shared_ptr<ChannelCredentials> GetChannelCredentials(
       grpc::ChannelArguments* args) override {
-    return SslCredentials(SslCredentialsOptions());
+    return grpc::SslCredentials(SslCredentialsOptions());
   }
   std::shared_ptr<ServerCredentials> GetServerCredentials() override {
     return nullptr;
@@ -116,9 +116,9 @@ std::shared_ptr<Channel> CreateTestChannel(
                                                                &channel_args);
   GPR_ASSERT(channel_creds != nullptr);
   if (creds.get()) {
-    channel_creds = CompositeChannelCredentials(channel_creds, creds);
+    channel_creds = grpc::CompositeChannelCredentials(channel_creds, creds);
   }
-  return CreateCustomChannel(server, channel_creds, channel_args);
+  return ::grpc::CreateCustomChannel(server, channel_creds, channel_args);
 }
 
 std::shared_ptr<Channel> CreateTestChannel(
@@ -132,7 +132,8 @@ std::shared_ptr<Channel> CreateTestChannel(
   std::shared_ptr<ChannelCredentials> channel_creds;
   if (cred_type.empty()) {
     if (interceptor_creators.empty()) {
-      return CreateCustomChannel(server, InsecureChannelCredentials(), args);
+      return ::grpc::CreateCustomChannel(server, InsecureChannelCredentials(),
+                                         args);
     } else {
       return experimental::CreateCustomChannelWithInterceptors(
           server, InsecureChannelCredentials(), args,
@@ -156,10 +157,11 @@ std::shared_ptr<Channel> CreateTestChannel(
     const grpc::string& connect_to =
         server.empty() ? override_hostname : server;
     if (creds.get()) {
-      channel_creds = CompositeChannelCredentials(channel_creds, creds);
+      channel_creds = grpc::CompositeChannelCredentials(channel_creds, creds);
     }
     if (interceptor_creators.empty()) {
-      return CreateCustomChannel(connect_to, channel_creds, channel_args);
+      return ::grpc::CreateCustomChannel(connect_to, channel_creds,
+                                         channel_args);
     } else {
       return experimental::CreateCustomChannelWithInterceptors(
           connect_to, channel_creds, channel_args,
@@ -171,7 +173,7 @@ std::shared_ptr<Channel> CreateTestChannel(
     GPR_ASSERT(channel_creds != nullptr);
 
     if (interceptor_creators.empty()) {
-      return CreateCustomChannel(server, channel_creds, args);
+      return ::grpc::CreateCustomChannel(server, channel_creds, args);
     } else {
       return experimental::CreateCustomChannelWithInterceptors(
           server, channel_creds, args, std::move(interceptor_creators));
@@ -220,7 +222,7 @@ std::shared_ptr<Channel> CreateTestChannel(
                                                                &channel_args);
   GPR_ASSERT(channel_creds != nullptr);
   if (creds.get()) {
-    channel_creds = CompositeChannelCredentials(channel_creds, creds);
+    channel_creds = grpc::CompositeChannelCredentials(channel_creds, creds);
   }
   return experimental::CreateCustomChannelWithInterceptors(
       server, channel_creds, channel_args, std::move(interceptor_creators));
index b50131b..42564a3 100644 (file)
 #include <grpcpp/impl/codegen/client_interceptor.h>
 #include <grpcpp/security/credentials.h>
 
-namespace grpc {
+namespace grpc_impl {
+
 class Channel;
+}
+
+namespace grpc {
 
 namespace testing {
 
@@ -33,31 +37,31 @@ typedef enum { INSECURE = 0, TLS, ALTS } transport_security;
 
 }  // namespace testing
 
-std::shared_ptr<Channel> CreateTestChannel(
+std::shared_ptr<::grpc_impl::Channel> CreateTestChannel(
     const grpc::string& server, testing::transport_security security_type);
 
-std::shared_ptr<Channel> CreateTestChannel(
+std::shared_ptr<::grpc_impl::Channel> CreateTestChannel(
     const grpc::string& server, const grpc::string& override_hostname,
     testing::transport_security security_type, bool use_prod_roots);
 
-std::shared_ptr<Channel> CreateTestChannel(
+std::shared_ptr<::grpc_impl::Channel> CreateTestChannel(
     const grpc::string& server, const grpc::string& override_hostname,
     testing::transport_security security_type, bool use_prod_roots,
     const std::shared_ptr<CallCredentials>& creds);
 
-std::shared_ptr<Channel> CreateTestChannel(
+std::shared_ptr<::grpc_impl::Channel> CreateTestChannel(
     const grpc::string& server, const grpc::string& override_hostname,
     testing::transport_security security_type, bool use_prod_roots,
     const std::shared_ptr<CallCredentials>& creds,
     const ChannelArguments& args);
 
-std::shared_ptr<Channel> CreateTestChannel(
+std::shared_ptr<::grpc_impl::Channel> CreateTestChannel(
     const grpc::string& server, const grpc::string& cred_type,
     const grpc::string& override_hostname, bool use_prod_roots,
     const std::shared_ptr<CallCredentials>& creds,
     const ChannelArguments& args);
 
-std::shared_ptr<Channel> CreateTestChannel(
+std::shared_ptr<::grpc_impl::Channel> CreateTestChannel(
     const grpc::string& server, const grpc::string& credential_type,
     const std::shared_ptr<CallCredentials>& creds);
 
index 44b14bf..dbac311 100644 (file)
@@ -217,7 +217,8 @@ std::shared_ptr<grpc::Channel> CreateCliChannel(
   if (!cred.GetSslTargetNameOverride().empty()) {
     args.SetSslTargetNameOverride(cred.GetSslTargetNameOverride());
   }
-  return grpc::CreateCustomChannel(server_address, cred.GetCredentials(), args);
+  return ::grpc::CreateCustomChannel(server_address, cred.GetCredentials(),
+                                     args);
 }
 
 struct Command {
index 57cdbeb..e44ada4 100644 (file)
@@ -122,7 +122,7 @@ class TestCliCredentials final : public grpc::testing::CliCredentials {
       return InsecureChannelCredentials();
     }
     SslCredentialsOptions ssl_opts = {test_root_cert, "", ""};
-    return SslCredentials(grpc::SslCredentialsOptions(ssl_opts));
+    return grpc::SslCredentials(grpc::SslCredentialsOptions(ssl_opts));
   }
   const grpc::string GetCredentialUsage() const override { return ""; }
 
index 2d6ddf0..08f6034 100644 (file)
@@ -21,6 +21,8 @@
 #include <map>
 #include <mutex>
 
+#include <grpcpp/server.h>
+
 #include "src/proto/grpc/testing/metrics.grpc.pb.h"
 #include "src/proto/grpc/testing/metrics.pb.h"
 
index 0cf75f1..455f94e 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <grpc/support/log.h>
 #include <grpc/support/sync.h>
+#include <grpcpp/security/server_credentials.h>
 
 #include "test/core/end2end/data/ssl_test_data.h"
 
@@ -62,7 +63,7 @@ class DefaultCredentialsProvider : public CredentialsProvider {
     } else if (type == grpc::testing::kTlsCredentialsType) {
       SslCredentialsOptions ssl_opts = {test_root_cert, "", ""};
       args->SetSslTargetNameOverride("foo.test.google.fr");
-      return SslCredentials(ssl_opts);
+      return grpc::SslCredentials(ssl_opts);
     } else if (type == grpc::testing::kGoogleDefaultCredentialsType) {
       return grpc::GoogleDefaultCredentials();
     } else {
index 66ec174..78a4590 100644 (file)
@@ -144,6 +144,7 @@ cc_library(
         "ares_strdup.h",
         "ares_strsplit.h",
         "ares_version.h",
+        "ares_writev.h",
         "bitncmp.h",
         "config-win32.h",
         "nameser.h",
index 943690a..0ff68f9 100644 (file)
@@ -16,34 +16,45 @@ licenses(["notice"])  # Apache v2
 
 package(default_visibility = ["//visibility:public"])
 
-# Latest RBE Ubuntu16_04 container
-# Update every time when a new container is released.
 alias(
-    name = "rbe_ubuntu1604",
-    actual = ":rbe_ubuntu1604_r346485",
-)
-
-alias(
-    name = "rbe_ubuntu1604_large",
-    actual = ":rbe_ubuntu1604_r346485_large",
+    name = "rbe_windows",
+    actual = ":rbe_windows_1803",
 )
 
-# RBE Ubuntu16_04 r346485
+# RBE Windows
 platform(
-    name = "rbe_ubuntu1604_r346485",
+    name = "rbe_windows_1803",
     constraint_values = [
         "@bazel_tools//platforms:x86_64",
-        "@bazel_tools//platforms:linux",
-        "@bazel_tools//tools/cpp:clang",
-        "@com_github_bazelbuild_bazeltoolchains//constraints:xenial",
-        "@com_github_bazelbuild_bazeltoolchains//constraints/sanitizers:support_msan",
-        "//third_party/toolchains/machine_size:standard",
+        "@bazel_tools//platforms:windows",
+        "@bazel_tools//tools/cpp:msvc",
     ],
     remote_execution_properties = """
         properties: {
           name: "container-image"
-          value:"docker://gcr.io/cloud-marketplace/google/rbe-ubuntu16-04@sha256:f3120a030a19d67626ababdac79cc787e699a1aa924081431285118f87e7b375"
+          value:"docker://gcr.io/grpc-testing/rbe_windows_toolchain@sha256:689b177e4a157c431c7077d19d043de27922c37de835031f29c9093b8d5c6370"
+        }
+        properties: {
+          name: "gceMachineType"  # Small machines for majority of tests.
+          value: "n1-highmem-2"
+        }
+        properties:{
+            name: "OSFamily"
+            value: "Windows"
         }
+
+        """,
+)
+
+# RBE Ubuntu16_04 r346485
+platform(
+    name = "rbe_ubuntu1604",
+    parents = ["@rbe_default//config:platform"],
+    constraint_values = [
+        "//third_party/toolchains/machine_size:standard",
+    ],
+    remote_execution_properties = """
+        {PARENT_REMOTE_EXECUTION_PROPERTIES}
         properties: {
           name: "gceMachineType"  # Small machines for majority of tests.
           value: "n1-highmem-2"
@@ -67,22 +78,14 @@ platform(
         """,
 )
 
-# RBE Ubuntu16_04 r346485 large
 platform(
-    name = "rbe_ubuntu1604_r346485_large",
+    name = "rbe_ubuntu1604_large",
+    parents = ["@rbe_default//config:platform"],
     constraint_values = [
-        "@bazel_tools//platforms:x86_64",
-        "@bazel_tools//platforms:linux",
-        "@bazel_tools//tools/cpp:clang",
-        "@com_github_bazelbuild_bazeltoolchains//constraints:xenial",
-        "@com_github_bazelbuild_bazeltoolchains//constraints/sanitizers:support_msan",
         "//third_party/toolchains/machine_size:large",
     ],
     remote_execution_properties = """
-        properties: {
-          name: "container-image"
-          value:"docker://gcr.io/cloud-marketplace/google/rbe-ubuntu16-04@sha256:f3120a030a19d67626ababdac79cc787e699a1aa924081431285118f87e7b375"
-        }
+        {PARENT_REMOTE_EXECUTION_PROPERTIES}
         properties: {
           name: "gceMachineType"  # Large machines for some resource demanding tests (TSAN).
           value: "n1-standard-8"
@@ -106,12 +109,18 @@ platform(
     """,
 )
 
-toolchain(
-    name = "cc-toolchain-clang-x86_64-default",
-    exec_compatible_with = [
+platform(
+    name = "local",
+    parents = ["@bazel_tools//platforms:target_platform"],
+    constraint_values = [
+        "//third_party/toolchains/machine_size:standard",
     ],
-    target_compatible_with = [
+)
+
+platform(
+    name = "local_large",
+    parents = ["@bazel_tools//platforms:target_platform"],
+    constraint_values = [
+        "//third_party/toolchains/machine_size:large",
     ],
-    toolchain = "@com_github_bazelbuild_bazeltoolchains//configs/ubuntu16_04_clang/1.1/bazel_0.20.0/default:cc-compiler-k8",
-    toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
 )
diff --git a/third_party/toolchains/bazel_0.23.2_rbe_windows/BUILD b/third_party/toolchains/bazel_0.23.2_rbe_windows/BUILD
new file mode 100644 (file)
index 0000000..5ce4e00
--- /dev/null
@@ -0,0 +1,188 @@
+# Copyright 2018 The Bazel Authors. All rights reserved.
+#
+# 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.
+
+# This becomes the BUILD file for @local_config_cc// under Windows.
+licenses(["notice"])  # Apache v2
+
+package(default_visibility = ["//visibility:public"])
+
+load(":cc_toolchain_config.bzl", "cc_toolchain_config")
+
+cc_library(
+    name = "malloc",
+)
+
+filegroup(
+    name = "empty",
+    srcs = [],
+)
+
+# Hardcoded toolchain, legacy behaviour.
+cc_toolchain_suite(
+    name = "toolchain",
+    toolchains = {
+        "armeabi-v7a|compiler": ":cc-compiler-armeabi-v7a",
+        "x64_windows|msvc-cl": ":cc-compiler-x64_windows",
+        "x64_windows|msys-gcc": ":cc-compiler-x64_windows_msys",
+        "x64_windows|mingw-gcc": ":cc-compiler-x64_windows_mingw",
+        "x64_windows_msys": ":cc-compiler-x64_windows_msys",
+        "x64_windows": ":cc-compiler-x64_windows",
+        "armeabi-v7a": ":cc-compiler-armeabi-v7a",
+    },
+)
+
+cc_toolchain(
+    name = "cc-compiler-x64_windows_msys",
+    all_files = ":empty",
+    ar_files = ":empty",
+    as_files = ":empty",
+    compiler_files = ":empty",
+    dwp_files = ":empty",
+    linker_files = ":empty",
+    objcopy_files = ":empty",
+    strip_files = ":empty",
+    supports_param_files = 1,
+    toolchain_config = ":msys_x64",
+    toolchain_identifier = "msys_x64",
+)
+
+cc_toolchain_config(
+    name = "msys_x64",
+    compiler = "msys-gcc",
+    cpu = "x64_windows",
+)
+
+toolchain(
+    name = "cc-toolchain-x64_windows_msys",
+    exec_compatible_with = [
+        "@bazel_tools//platforms:x86_64",
+        "@bazel_tools//platforms:windows",
+        "@bazel_tools//tools/cpp:msys",
+    ],
+    target_compatible_with = [
+        "@bazel_tools//platforms:x86_64",
+        "@bazel_tools//platforms:windows",
+    ],
+    toolchain = ":cc-compiler-x64_windows_msys",
+    toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+)
+
+cc_toolchain(
+    name = "cc-compiler-x64_windows_mingw",
+    all_files = ":empty",
+    ar_files = ":empty",
+    as_files = ":empty",
+    compiler_files = ":empty",
+    dwp_files = ":empty",
+    linker_files = ":empty",
+    objcopy_files = ":empty",
+    strip_files = ":empty",
+    supports_param_files = 0,
+    toolchain_config = ":msys_x64_mingw",
+    toolchain_identifier = "msys_x64_mingw",
+)
+
+cc_toolchain_config(
+    name = "msys_x64_mingw",
+    compiler = "mingw-gcc",
+    cpu = "x64_windows",
+)
+
+toolchain(
+    name = "cc-toolchain-x64_windows_mingw",
+    exec_compatible_with = [
+        "@bazel_tools//platforms:x86_64",
+        "@bazel_tools//platforms:windows",
+        "@bazel_tools//tools/cpp:mingw",
+    ],
+    target_compatible_with = [
+        "@bazel_tools//platforms:x86_64",
+        "@bazel_tools//platforms:windows",
+    ],
+    toolchain = ":cc-compiler-x64_windows_mingw",
+    toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+)
+
+cc_toolchain(
+    name = "cc-compiler-x64_windows",
+    all_files = ":empty",
+    ar_files = ":empty",
+    as_files = ":empty",
+    compiler_files = ":empty",
+    dwp_files = ":empty",
+    linker_files = ":empty",
+    objcopy_files = ":empty",
+    strip_files = ":empty",
+    supports_param_files = 1,
+    toolchain_config = ":msvc_x64",
+    toolchain_identifier = "msvc_x64",
+)
+
+cc_toolchain_config(
+    name = "msvc_x64",
+    compiler = "msvc-cl",
+    cpu = "x64_windows",
+)
+
+toolchain(
+    name = "cc-toolchain-x64_windows",
+    exec_compatible_with = [
+        "@bazel_tools//platforms:x86_64",
+        "@bazel_tools//platforms:windows",
+    ],
+    target_compatible_with = [
+        "@bazel_tools//platforms:x86_64",
+        "@bazel_tools//platforms:windows",
+    ],
+    toolchain = ":cc-compiler-x64_windows",
+    toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+)
+
+cc_toolchain(
+    name = "cc-compiler-armeabi-v7a",
+    all_files = ":empty",
+    ar_files = ":empty",
+    as_files = ":empty",
+    compiler_files = ":empty",
+    dwp_files = ":empty",
+    linker_files = ":empty",
+    objcopy_files = ":empty",
+    strip_files = ":empty",
+    supports_param_files = 1,
+    toolchain_config = ":stub_armeabi-v7a",
+    toolchain_identifier = "stub_armeabi-v7a",
+)
+
+cc_toolchain_config(
+    name = "stub_armeabi-v7a",
+    compiler = "compiler",
+    cpu = "armeabi-v7a",
+)
+
+toolchain(
+    name = "cc-toolchain-armeabi-v7a",
+    exec_compatible_with = [
+    ],
+    target_compatible_with = [
+        "@bazel_tools//platforms:arm",
+        "@bazel_tools//platforms:android",
+    ],
+    toolchain = ":cc-compiler-armeabi-v7a",
+    toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+)
+
+filegroup(
+    name = "link_dynamic_library",
+    srcs = ["link_dynamic_library.sh"],
+)
diff --git a/third_party/toolchains/bazel_0.23.2_rbe_windows/cc_toolchain_config.bzl b/third_party/toolchains/bazel_0.23.2_rbe_windows/cc_toolchain_config.bzl
new file mode 100644 (file)
index 0000000..790ddcb
--- /dev/null
@@ -0,0 +1,1704 @@
+# Copyright 2019 The Bazel Authors. All rights reserved.
+#
+# 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.
+
+"""A Starlark cc_toolchain configuration rule"""
+
+load(
+    "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
+    "action_config",
+    "artifact_name_pattern",
+    "env_entry",
+    "env_set",
+    "feature",
+    "feature_set",
+    "flag_group",
+    "flag_set",
+    "make_variable",
+    "tool",
+    "tool_path",
+    "variable_with_value",
+    "with_feature_set",
+)
+load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
+
+all_compile_actions = [
+    ACTION_NAMES.c_compile,
+    ACTION_NAMES.cpp_compile,
+    ACTION_NAMES.linkstamp_compile,
+    ACTION_NAMES.assemble,
+    ACTION_NAMES.preprocess_assemble,
+    ACTION_NAMES.cpp_header_parsing,
+    ACTION_NAMES.cpp_module_compile,
+    ACTION_NAMES.cpp_module_codegen,
+    ACTION_NAMES.clif_match,
+    ACTION_NAMES.lto_backend,
+]
+
+all_cpp_compile_actions = [
+    ACTION_NAMES.cpp_compile,
+    ACTION_NAMES.linkstamp_compile,
+    ACTION_NAMES.cpp_header_parsing,
+    ACTION_NAMES.cpp_module_compile,
+    ACTION_NAMES.cpp_module_codegen,
+    ACTION_NAMES.clif_match,
+]
+
+preprocessor_compile_actions = [
+    ACTION_NAMES.c_compile,
+    ACTION_NAMES.cpp_compile,
+    ACTION_NAMES.linkstamp_compile,
+    ACTION_NAMES.preprocess_assemble,
+    ACTION_NAMES.cpp_header_parsing,
+    ACTION_NAMES.cpp_module_compile,
+    ACTION_NAMES.clif_match,
+]
+
+codegen_compile_actions = [
+    ACTION_NAMES.c_compile,
+    ACTION_NAMES.cpp_compile,
+    ACTION_NAMES.linkstamp_compile,
+    ACTION_NAMES.assemble,
+    ACTION_NAMES.preprocess_assemble,
+    ACTION_NAMES.cpp_module_codegen,
+    ACTION_NAMES.lto_backend,
+]
+
+all_link_actions = [
+    ACTION_NAMES.cpp_link_executable,
+    ACTION_NAMES.cpp_link_dynamic_library,
+    ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+]
+
+def _windows_msvc_impl(ctx):
+    toolchain_identifier = "msvc_x64"
+    host_system_name = "local"
+    target_system_name = "local"
+    target_cpu = "x64_windows"
+    target_libc = "msvcrt"
+    compiler = "msvc-cl"
+    abi_version = "local"
+    abi_libc_version = "local"
+    cc_target_os = None
+    builtin_sysroot = None
+
+    cxx_builtin_include_directories = [
+        # This is a workaround for https://github.com/bazelbuild/bazel/issues/5087.
+        "C:\\botcode\\w",
+        "c:/tools/msys64/usr/",
+        "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE",
+        "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt",
+        "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\shared",
+        "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\um",
+        "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\winrt",
+    ]
+
+    cpp_link_nodeps_dynamic_library_action = action_config(
+        action_name = ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+        implies = [
+            "nologo",
+            "shared_flag",
+            "linkstamps",
+            "output_execpath_flags",
+            "input_param_flags",
+            "user_link_flags",
+            "default_link_flags",
+            "linker_subsystem_flag",
+            "linker_param_file",
+            "msvc_env",
+            "no_stripping",
+            "has_configured_linker_path",
+            "def_file",
+        ],
+        tools = [tool(path = "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/link.exe")],
+    )
+
+    cpp_link_static_library_action = action_config(
+        action_name = ACTION_NAMES.cpp_link_static_library,
+        implies = [
+            "nologo",
+            "archiver_flags",
+            "input_param_flags",
+            "linker_param_file",
+            "msvc_env",
+        ],
+        tools = [tool(path = "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/lib.exe")],
+    )
+
+    assemble_action = action_config(
+        action_name = ACTION_NAMES.assemble,
+        implies = [
+            "compiler_input_flags",
+            "compiler_output_flags",
+            "nologo",
+            "msvc_env",
+            "sysroot",
+        ],
+        tools = [tool(path = "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/ml64.exe")],
+    )
+
+    preprocess_assemble_action = action_config(
+        action_name = ACTION_NAMES.preprocess_assemble,
+        implies = [
+            "compiler_input_flags",
+            "compiler_output_flags",
+            "nologo",
+            "msvc_env",
+            "sysroot",
+        ],
+        tools = [tool(path = "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/ml64.exe")],
+    )
+
+    c_compile_action = action_config(
+        action_name = ACTION_NAMES.c_compile,
+        implies = [
+            "compiler_input_flags",
+            "compiler_output_flags",
+            "default_compile_flags",
+            "nologo",
+            "msvc_env",
+            "parse_showincludes",
+            "user_compile_flags",
+            "sysroot",
+            "unfiltered_compile_flags",
+        ],
+        tools = [tool(path = "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe")],
+    )
+
+    cpp_compile_action = action_config(
+        action_name = ACTION_NAMES.cpp_compile,
+        implies = [
+            "compiler_input_flags",
+            "compiler_output_flags",
+            "default_compile_flags",
+            "nologo",
+            "msvc_env",
+            "parse_showincludes",
+            "user_compile_flags",
+            "sysroot",
+            "unfiltered_compile_flags",
+        ],
+        tools = [tool(path = "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe")],
+    )
+
+    cpp_link_executable_action = action_config(
+        action_name = ACTION_NAMES.cpp_link_executable,
+        implies = [
+            "nologo",
+            "linkstamps",
+            "output_execpath_flags",
+            "input_param_flags",
+            "user_link_flags",
+            "default_link_flags",
+            "linker_subsystem_flag",
+            "linker_param_file",
+            "msvc_env",
+            "no_stripping",
+        ],
+        tools = [tool(path = "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/link.exe")],
+    )
+
+    cpp_link_dynamic_library_action = action_config(
+        action_name = ACTION_NAMES.cpp_link_dynamic_library,
+        implies = [
+            "nologo",
+            "shared_flag",
+            "linkstamps",
+            "output_execpath_flags",
+            "input_param_flags",
+            "user_link_flags",
+            "default_link_flags",
+            "linker_subsystem_flag",
+            "linker_param_file",
+            "msvc_env",
+            "no_stripping",
+            "has_configured_linker_path",
+            "def_file",
+        ],
+        tools = [tool(path = "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/link.exe")],
+    )
+
+    action_configs = [
+        assemble_action,
+        preprocess_assemble_action,
+        c_compile_action,
+        cpp_compile_action,
+        cpp_link_executable_action,
+        cpp_link_dynamic_library_action,
+        cpp_link_nodeps_dynamic_library_action,
+        cpp_link_static_library_action,
+    ]
+
+    msvc_link_env_feature = feature(
+        name = "msvc_link_env",
+        env_sets = [
+            env_set(
+                actions = all_link_actions +
+                          [ACTION_NAMES.cpp_link_static_library],
+                env_entries = [env_entry(key = "LIB", value = "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\LIB\\amd64;C:\\Program Files (x86)\\Windows Kits\\10\\lib\\10.0.10240.0\\ucrt\\x64;C:\\Program Files (x86)\\Windows Kits\\8.1\\lib\\winv6.3\\um\\x64;")],
+            ),
+        ],
+    )
+
+    shared_flag_feature = feature(
+        name = "shared_flag",
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.cpp_link_dynamic_library,
+                    ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+                ],
+                flag_groups = [flag_group(flags = ["/DLL"])],
+            ),
+        ],
+    )
+
+    determinism_feature = feature(
+        name = "determinism",
+        enabled = True,
+        flag_sets = [
+            flag_set(
+                actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile],
+                flag_groups = [
+                    flag_group(
+                        flags = [
+                            "/wd4117",
+                            "-D__DATE__=\"redacted\"",
+                            "-D__TIMESTAMP__=\"redacted\"",
+                            "-D__TIME__=\"redacted\"",
+                        ],
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    sysroot_feature = feature(
+        name = "sysroot",
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                    ACTION_NAMES.cpp_link_executable,
+                    ACTION_NAMES.cpp_link_dynamic_library,
+                    ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["--sysroot=%{sysroot}"],
+                        iterate_over = "sysroot",
+                        expand_if_available = "sysroot",
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    unfiltered_compile_flags_feature = feature(
+        name = "unfiltered_compile_flags",
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["%{unfiltered_compile_flags}"],
+                        iterate_over = "unfiltered_compile_flags",
+                        expand_if_available = "unfiltered_compile_flags",
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    copy_dynamic_libraries_to_binary_feature = feature(name = "copy_dynamic_libraries_to_binary")
+
+    input_param_flags_feature = feature(
+        name = "input_param_flags",
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.cpp_link_dynamic_library,
+                    ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["/IMPLIB:%{interface_library_output_path}"],
+                        expand_if_available = "interface_library_output_path",
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [
+                    flag_group(
+                        flags = ["%{libopts}"],
+                        iterate_over = "libopts",
+                        expand_if_available = "libopts",
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = all_link_actions +
+                          [ACTION_NAMES.cpp_link_static_library],
+                flag_groups = [
+                    flag_group(
+                        iterate_over = "libraries_to_link",
+                        flag_groups = [
+                            flag_group(
+                                iterate_over = "libraries_to_link.object_files",
+                                flag_groups = [flag_group(flags = ["%{libraries_to_link.object_files}"])],
+                                expand_if_equal = variable_with_value(
+                                    name = "libraries_to_link.type",
+                                    value = "object_file_group",
+                                ),
+                            ),
+                            flag_group(
+                                flag_groups = [flag_group(flags = ["%{libraries_to_link.name}"])],
+                                expand_if_equal = variable_with_value(
+                                    name = "libraries_to_link.type",
+                                    value = "object_file",
+                                ),
+                            ),
+                            flag_group(
+                                flag_groups = [flag_group(flags = ["%{libraries_to_link.name}"])],
+                                expand_if_equal = variable_with_value(
+                                    name = "libraries_to_link.type",
+                                    value = "interface_library",
+                                ),
+                            ),
+                            flag_group(
+                                flag_groups = [
+                                    flag_group(
+                                        flags = ["%{libraries_to_link.name}"],
+                                        expand_if_false = "libraries_to_link.is_whole_archive",
+                                    ),
+                                    flag_group(
+                                        flags = ["/WHOLEARCHIVE:%{libraries_to_link.name}"],
+                                        expand_if_true = "libraries_to_link.is_whole_archive",
+                                    ),
+                                ],
+                                expand_if_equal = variable_with_value(
+                                    name = "libraries_to_link.type",
+                                    value = "static_library",
+                                ),
+                            ),
+                        ],
+                        expand_if_available = "libraries_to_link",
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    fastbuild_feature = feature(
+        name = "fastbuild",
+        flag_sets = [
+            flag_set(
+                actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile],
+                flag_groups = [flag_group(flags = ["/Od", "/Z7"])],
+            ),
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [
+                    flag_group(
+                        flags = ["/DEBUG:FASTLINK", "/INCREMENTAL:NO"],
+                    ),
+                ],
+            ),
+        ],
+        implies = ["generate_pdb_file"],
+    )
+
+    user_compile_flags_feature = feature(
+        name = "user_compile_flags",
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["%{user_compile_flags}"],
+                        iterate_over = "user_compile_flags",
+                        expand_if_available = "user_compile_flags",
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    archiver_flags_feature = feature(
+        name = "archiver_flags",
+        flag_sets = [
+            flag_set(
+                actions = [ACTION_NAMES.cpp_link_static_library],
+                flag_groups = [
+                    flag_group(
+                        flags = ["/OUT:%{output_execpath}"],
+                        expand_if_available = "output_execpath",
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    default_link_flags_feature = feature(
+        name = "default_link_flags",
+        enabled = True,
+        flag_sets = [
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [flag_group(flags = ["/MACHINE:X64"])],
+            ),
+        ],
+    )
+
+    static_link_msvcrt_feature = feature(name = "static_link_msvcrt")
+
+    dynamic_link_msvcrt_debug_feature = feature(
+        name = "dynamic_link_msvcrt_debug",
+        flag_sets = [
+            flag_set(
+                actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile],
+                flag_groups = [flag_group(flags = ["/MDd"])],
+            ),
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [flag_group(flags = ["/DEFAULTLIB:msvcrtd.lib"])],
+            ),
+        ],
+        requires = [feature_set(features = ["dbg"])],
+    )
+
+    dbg_feature = feature(
+        name = "dbg",
+        flag_sets = [
+            flag_set(
+                actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile],
+                flag_groups = [flag_group(flags = ["/Od", "/Z7"])],
+            ),
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [
+                    flag_group(
+                        flags = ["/DEBUG:FULL", "/INCREMENTAL:NO"],
+                    ),
+                ],
+            ),
+        ],
+        implies = ["generate_pdb_file"],
+    )
+
+    opt_feature = feature(
+        name = "opt",
+        flag_sets = [
+            flag_set(
+                actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile],
+                flag_groups = [flag_group(flags = ["/O2"])],
+            ),
+        ],
+        implies = ["frame_pointer"],
+    )
+
+    supports_interface_shared_libraries_feature = feature(
+        name = "supports_interface_shared_libraries",
+        enabled = True,
+    )
+
+    user_link_flags_feature = feature(
+        name = "user_link_flags",
+        flag_sets = [
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [
+                    flag_group(
+                        flags = ["%{user_link_flags}"],
+                        iterate_over = "user_link_flags",
+                        expand_if_available = "user_link_flags",
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    default_compile_flags_feature = feature(
+        name = "default_compile_flags",
+        enabled = True,
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.linkstamp_compile,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                    ACTION_NAMES.lto_backend,
+                    ACTION_NAMES.clif_match,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = [
+                            "/DCOMPILER_MSVC",
+                            "/DNOMINMAX",
+                            "/D_WIN32_WINNT=0x0601",
+                            "/D_CRT_SECURE_NO_DEPRECATE",
+                            "/D_CRT_SECURE_NO_WARNINGS",
+                            "/bigobj",
+                            "/Zm500",
+                            "/EHsc",
+                            "/wd4351",
+                            "/wd4291",
+                            "/wd4250",
+                            "/wd4996",
+                        ],
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    msvc_compile_env_feature = feature(
+        name = "msvc_compile_env",
+        env_sets = [
+            env_set(
+                actions = [
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                ],
+                env_entries = [env_entry(key = "INCLUDE", value = "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE;C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt;C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\shared;C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\um;C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\winrt;")],
+            ),
+        ],
+    )
+
+    preprocessor_defines_feature = feature(
+        name = "preprocessor_defines",
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["/D%{preprocessor_defines}"],
+                        iterate_over = "preprocessor_defines",
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    generate_pdb_file_feature = feature(
+        name = "generate_pdb_file",
+        requires = [
+            feature_set(features = ["dbg"]),
+            feature_set(features = ["fastbuild"]),
+        ],
+    )
+
+    output_execpath_flags_feature = feature(
+        name = "output_execpath_flags",
+        flag_sets = [
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [
+                    flag_group(
+                        flags = ["/OUT:%{output_execpath}"],
+                        expand_if_available = "output_execpath",
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    dynamic_link_msvcrt_no_debug_feature = feature(
+        name = "dynamic_link_msvcrt_no_debug",
+        flag_sets = [
+            flag_set(
+                actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile],
+                flag_groups = [flag_group(flags = ["/MD"])],
+            ),
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [flag_group(flags = ["/DEFAULTLIB:msvcrt.lib"])],
+            ),
+        ],
+        requires = [
+            feature_set(features = ["fastbuild"]),
+            feature_set(features = ["opt"]),
+        ],
+    )
+
+    disable_assertions_feature = feature(
+        name = "disable_assertions",
+        enabled = True,
+        flag_sets = [
+            flag_set(
+                actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile],
+                flag_groups = [flag_group(flags = ["/DNDEBUG"])],
+                with_features = [with_feature_set(features = ["opt"])],
+            ),
+        ],
+    )
+
+    has_configured_linker_path_feature = feature(name = "has_configured_linker_path")
+
+    supports_dynamic_linker_feature = feature(name = "supports_dynamic_linker", enabled = True)
+
+    no_stripping_feature = feature(name = "no_stripping")
+
+    linker_param_file_feature = feature(
+        name = "linker_param_file",
+        flag_sets = [
+            flag_set(
+                actions = all_link_actions +
+                          [ACTION_NAMES.cpp_link_static_library],
+                flag_groups = [
+                    flag_group(
+                        flags = ["@%{linker_param_file}"],
+                        expand_if_available = "linker_param_file",
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    ignore_noisy_warnings_feature = feature(
+        name = "ignore_noisy_warnings",
+        enabled = True,
+        flag_sets = [
+            flag_set(
+                actions = [ACTION_NAMES.cpp_link_static_library],
+                flag_groups = [flag_group(flags = ["/ignore:4221"])],
+            ),
+        ],
+    )
+
+    no_legacy_features_feature = feature(name = "no_legacy_features")
+
+    parse_showincludes_feature = feature(
+        name = "parse_showincludes",
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                ],
+                flag_groups = [flag_group(flags = ["/showIncludes"])],
+            ),
+        ],
+    )
+
+    static_link_msvcrt_no_debug_feature = feature(
+        name = "static_link_msvcrt_no_debug",
+        flag_sets = [
+            flag_set(
+                actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile],
+                flag_groups = [flag_group(flags = ["/MT"])],
+            ),
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [flag_group(flags = ["/DEFAULTLIB:libcmt.lib"])],
+            ),
+        ],
+        requires = [
+            feature_set(features = ["fastbuild"]),
+            feature_set(features = ["opt"]),
+        ],
+    )
+
+    treat_warnings_as_errors_feature = feature(
+        name = "treat_warnings_as_errors",
+        flag_sets = [
+            flag_set(
+                actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile],
+                flag_groups = [flag_group(flags = ["/WX"])],
+            ),
+        ],
+    )
+
+    windows_export_all_symbols_feature = feature(name = "windows_export_all_symbols")
+
+    no_windows_export_all_symbols_feature = feature(name = "no_windows_export_all_symbols")
+
+    include_paths_feature = feature(
+        name = "include_paths",
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["/I%{quote_include_paths}"],
+                        iterate_over = "quote_include_paths",
+                    ),
+                    flag_group(
+                        flags = ["/I%{include_paths}"],
+                        iterate_over = "include_paths",
+                    ),
+                    flag_group(
+                        flags = ["/I%{system_include_paths}"],
+                        iterate_over = "system_include_paths",
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    linkstamps_feature = feature(
+        name = "linkstamps",
+        flag_sets = [
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [
+                    flag_group(
+                        flags = ["%{linkstamp_paths}"],
+                        iterate_over = "linkstamp_paths",
+                        expand_if_available = "linkstamp_paths",
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    targets_windows_feature = feature(
+        name = "targets_windows",
+        enabled = True,
+        implies = ["copy_dynamic_libraries_to_binary"],
+    )
+
+    linker_subsystem_flag_feature = feature(
+        name = "linker_subsystem_flag",
+        flag_sets = [
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [flag_group(flags = ["/SUBSYSTEM:CONSOLE"])],
+            ),
+        ],
+    )
+
+    static_link_msvcrt_debug_feature = feature(
+        name = "static_link_msvcrt_debug",
+        flag_sets = [
+            flag_set(
+                actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile],
+                flag_groups = [flag_group(flags = ["/MTd"])],
+            ),
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [flag_group(flags = ["/DEFAULTLIB:libcmtd.lib"])],
+            ),
+        ],
+        requires = [feature_set(features = ["dbg"])],
+    )
+
+    frame_pointer_feature = feature(
+        name = "frame_pointer",
+        flag_sets = [
+            flag_set(
+                actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile],
+                flag_groups = [flag_group(flags = ["/Oy-"])],
+            ),
+        ],
+    )
+
+    compiler_output_flags_feature = feature(
+        name = "compiler_output_flags",
+        flag_sets = [
+            flag_set(
+                actions = [ACTION_NAMES.assemble],
+                flag_groups = [
+                    flag_group(
+                        flag_groups = [
+                            flag_group(
+                                flags = ["/Fo%{output_file}", "/Zi"],
+                                expand_if_available = "output_file",
+                                expand_if_not_available = "output_assembly_file",
+                            ),
+                        ],
+                        expand_if_not_available = "output_preprocess_file",
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = [
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flag_groups = [
+                            flag_group(
+                                flags = ["/Fo%{output_file}"],
+                                expand_if_not_available = "output_preprocess_file",
+                            ),
+                        ],
+                        expand_if_available = "output_file",
+                        expand_if_not_available = "output_assembly_file",
+                    ),
+                    flag_group(
+                        flag_groups = [
+                            flag_group(
+                                flags = ["/Fa%{output_file}"],
+                                expand_if_available = "output_assembly_file",
+                            ),
+                        ],
+                        expand_if_available = "output_file",
+                    ),
+                    flag_group(
+                        flag_groups = [
+                            flag_group(
+                                flags = ["/P", "/Fi%{output_file}"],
+                                expand_if_available = "output_preprocess_file",
+                            ),
+                        ],
+                        expand_if_available = "output_file",
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    nologo_feature = feature(
+        name = "nologo",
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.cpp_link_executable,
+                    ACTION_NAMES.cpp_link_dynamic_library,
+                    ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+                    ACTION_NAMES.cpp_link_static_library,
+                ],
+                flag_groups = [flag_group(flags = ["/nologo"])],
+            ),
+        ],
+    )
+
+    smaller_binary_feature = feature(
+        name = "smaller_binary",
+        enabled = True,
+        flag_sets = [
+            flag_set(
+                actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile],
+                flag_groups = [flag_group(flags = ["/Gy", "/Gw"])],
+                with_features = [with_feature_set(features = ["opt"])],
+            ),
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [flag_group(flags = ["/OPT:ICF", "/OPT:REF"])],
+                with_features = [with_feature_set(features = ["opt"])],
+            ),
+        ],
+    )
+
+    compiler_input_flags_feature = feature(
+        name = "compiler_input_flags",
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["/c", "%{source_file}"],
+                        expand_if_available = "source_file",
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    def_file_feature = feature(
+        name = "def_file",
+        flag_sets = [
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [
+                    flag_group(
+                        flags = ["/DEF:%{def_file_path}", "/ignore:4070"],
+                        expand_if_available = "def_file_path",
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    msvc_env_feature = feature(
+        name = "msvc_env",
+        env_sets = [
+            env_set(
+                actions = [
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.cpp_link_executable,
+                    ACTION_NAMES.cpp_link_dynamic_library,
+                    ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+                    ACTION_NAMES.cpp_link_static_library,
+                ],
+                env_entries = [
+                    env_entry(key = "PATH", value = "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\BIN\\amd64;C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319;C:\\Windows\\Microsoft.NET\\Framework64\\;C:\\Program Files (x86)\\Windows Kits\\8.1\\bin\\x64;C:\\Program Files (x86)\\Windows Kits\\8.1\\bin\\x86;;C:\\Windows\\system32"),
+                    env_entry(key = "TMP", value = "C:\\Users\\ContainerAdministrator\\AppData\\Local\\Temp"),
+                    env_entry(key = "TEMP", value = "C:\\Users\\ContainerAdministrator\\AppData\\Local\\Temp"),
+                ],
+            ),
+        ],
+        implies = ["msvc_compile_env", "msvc_link_env"],
+    )
+
+    features = [
+        no_legacy_features_feature,
+        nologo_feature,
+        has_configured_linker_path_feature,
+        no_stripping_feature,
+        targets_windows_feature,
+        copy_dynamic_libraries_to_binary_feature,
+        default_compile_flags_feature,
+        msvc_env_feature,
+        msvc_compile_env_feature,
+        msvc_link_env_feature,
+        include_paths_feature,
+        preprocessor_defines_feature,
+        parse_showincludes_feature,
+        generate_pdb_file_feature,
+        shared_flag_feature,
+        linkstamps_feature,
+        output_execpath_flags_feature,
+        archiver_flags_feature,
+        input_param_flags_feature,
+        linker_subsystem_flag_feature,
+        user_link_flags_feature,
+        default_link_flags_feature,
+        linker_param_file_feature,
+        static_link_msvcrt_feature,
+        static_link_msvcrt_no_debug_feature,
+        dynamic_link_msvcrt_no_debug_feature,
+        static_link_msvcrt_debug_feature,
+        dynamic_link_msvcrt_debug_feature,
+        dbg_feature,
+        fastbuild_feature,
+        opt_feature,
+        frame_pointer_feature,
+        disable_assertions_feature,
+        determinism_feature,
+        treat_warnings_as_errors_feature,
+        smaller_binary_feature,
+        ignore_noisy_warnings_feature,
+        user_compile_flags_feature,
+        sysroot_feature,
+        unfiltered_compile_flags_feature,
+        compiler_output_flags_feature,
+        compiler_input_flags_feature,
+        def_file_feature,
+        windows_export_all_symbols_feature,
+        no_windows_export_all_symbols_feature,
+        supports_dynamic_linker_feature,
+        supports_interface_shared_libraries_feature,
+    ]
+
+    artifact_name_patterns = [
+        artifact_name_pattern(
+            category_name = "object_file",
+            prefix = "",
+            extension = ".obj",
+        ),
+        artifact_name_pattern(
+            category_name = "static_library",
+            prefix = "",
+            extension = ".lib",
+        ),
+        artifact_name_pattern(
+            category_name = "alwayslink_static_library",
+            prefix = "",
+            extension = ".lo.lib",
+        ),
+        artifact_name_pattern(
+            category_name = "executable",
+            prefix = "",
+            extension = ".exe",
+        ),
+        artifact_name_pattern(
+            category_name = "dynamic_library",
+            prefix = "",
+            extension = ".dll",
+        ),
+        artifact_name_pattern(
+            category_name = "interface_library",
+            prefix = "",
+            extension = ".if.lib",
+        ),
+    ]
+
+    make_variables = []
+
+    tool_paths = [
+        tool_path(name = "ar", path = "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/lib.exe"),
+        tool_path(name = "ml", path = "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/ml64.exe"),
+        tool_path(name = "cpp", path = "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe"),
+        tool_path(name = "gcc", path = "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe"),
+        tool_path(name = "gcov", path = "wrapper/bin/msvc_nop.bat"),
+        tool_path(name = "ld", path = "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/link.exe"),
+        tool_path(name = "nm", path = "wrapper/bin/msvc_nop.bat"),
+        tool_path(
+            name = "objcopy",
+            path = "wrapper/bin/msvc_nop.bat",
+        ),
+        tool_path(
+            name = "objdump",
+            path = "wrapper/bin/msvc_nop.bat",
+        ),
+        tool_path(
+            name = "strip",
+            path = "wrapper/bin/msvc_nop.bat",
+        ),
+    ]
+
+    return cc_common.create_cc_toolchain_config_info(
+        ctx = ctx,
+        features = features,
+        action_configs = action_configs,
+        artifact_name_patterns = artifact_name_patterns,
+        cxx_builtin_include_directories = cxx_builtin_include_directories,
+        toolchain_identifier = toolchain_identifier,
+        host_system_name = host_system_name,
+        target_system_name = target_system_name,
+        target_cpu = target_cpu,
+        target_libc = target_libc,
+        compiler = compiler,
+        abi_version = abi_version,
+        abi_libc_version = abi_libc_version,
+        tool_paths = tool_paths,
+        make_variables = make_variables,
+        builtin_sysroot = builtin_sysroot,
+        cc_target_os = None,
+    )
+
+def _windows_msys_mingw_impl(ctx):
+    toolchain_identifier = "msys_x64_mingw"
+    host_system_name = "local"
+    target_system_name = "local"
+    target_cpu = "x64_windows"
+    target_libc = "mingw"
+    compiler = "mingw-gcc"
+    abi_version = "local"
+    abi_libc_version = "local"
+    cc_target_os = None
+    builtin_sysroot = None
+    action_configs = []
+
+    targets_windows_feature = feature(
+        name = "targets_windows",
+        implies = ["copy_dynamic_libraries_to_binary"],
+        enabled = True,
+    )
+
+    copy_dynamic_libraries_to_binary_feature = feature(name = "copy_dynamic_libraries_to_binary")
+
+    gcc_env_feature = feature(
+        name = "gcc_env",
+        enabled = True,
+        env_sets = [
+            env_set(
+                actions = [
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.cpp_link_executable,
+                    ACTION_NAMES.cpp_link_dynamic_library,
+                    ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+                    ACTION_NAMES.cpp_link_static_library,
+                ],
+                env_entries = [
+                    env_entry(key = "PATH", value = "c:/tools/msys64/mingw64/bin"),
+                ],
+            ),
+        ],
+    )
+
+    msys_mingw_flags = [
+        "-std=gnu++0x",
+    ]
+    msys_mingw_link_flags = [
+        "-lstdc++",
+    ]
+
+    default_compile_flags_feature = feature(
+        name = "default_compile_flags",
+        enabled = True,
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.linkstamp_compile,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                    ACTION_NAMES.lto_backend,
+                    ACTION_NAMES.clif_match,
+                ],
+            ),
+            flag_set(
+                actions = [
+                    ACTION_NAMES.linkstamp_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                    ACTION_NAMES.lto_backend,
+                    ACTION_NAMES.clif_match,
+                ],
+                flag_groups = ([flag_group(flags = msys_mingw_flags)] if msys_mingw_flags else []),
+            ),
+        ],
+    )
+
+    default_link_flags_feature = feature(
+        name = "default_link_flags",
+        enabled = True,
+        flag_sets = [
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = ([flag_group(flags = msys_mingw_link_flags)] if msys_mingw_link_flags else []),
+            ),
+        ],
+    )
+
+    supports_dynamic_linker_feature = feature(name = "supports_dynamic_linker", enabled = True)
+
+    features = [
+        targets_windows_feature,
+        copy_dynamic_libraries_to_binary_feature,
+        gcc_env_feature,
+        default_compile_flags_feature,
+        default_link_flags_feature,
+        supports_dynamic_linker_feature,
+    ]
+
+    cxx_builtin_include_directories = [
+        # This is a workaround for https://github.com/bazelbuild/bazel/issues/5087.
+        "C:\\botcode\\w",
+        "c:/tools/msys64/mingw64/",
+        "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE",
+        "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt",
+        "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\shared",
+        "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\um",
+        "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\winrt",
+    ]
+
+    artifact_name_patterns = [
+        artifact_name_pattern(
+            category_name = "executable",
+            prefix = "",
+            extension = ".exe",
+        ),
+    ]
+
+    make_variables = []
+    tool_paths = [
+        tool_path(name = "ar", path = "c:/tools/msys64/mingw64/bin/ar"),
+        tool_path(name = "compat-ld", path = "c:/tools/msys64/mingw64/bin/ld"),
+        tool_path(name = "cpp", path = "c:/tools/msys64/mingw64/bin/cpp"),
+        tool_path(name = "dwp", path = "c:/tools/msys64/mingw64/bin/dwp"),
+        tool_path(name = "gcc", path = "c:/tools/msys64/mingw64/bin/gcc"),
+        tool_path(name = "gcov", path = "c:/tools/msys64/mingw64/bin/gcov"),
+        tool_path(name = "ld", path = "c:/tools/msys64/mingw64/bin/ld"),
+        tool_path(name = "nm", path = "c:/tools/msys64/mingw64/bin/nm"),
+        tool_path(name = "objcopy", path = "c:/tools/msys64/mingw64/bin/objcopy"),
+        tool_path(name = "objdump", path = "c:/tools/msys64/mingw64/bin/objdump"),
+        tool_path(name = "strip", path = "c:/tools/msys64/mingw64/bin/strip"),
+    ]
+
+    return cc_common.create_cc_toolchain_config_info(
+        ctx = ctx,
+        features = features,
+        action_configs = action_configs,
+        artifact_name_patterns = artifact_name_patterns,
+        cxx_builtin_include_directories = cxx_builtin_include_directories,
+        toolchain_identifier = toolchain_identifier,
+        host_system_name = host_system_name,
+        target_system_name = target_system_name,
+        target_cpu = target_cpu,
+        target_libc = target_libc,
+        compiler = compiler,
+        abi_version = abi_version,
+        abi_libc_version = abi_libc_version,
+        tool_paths = tool_paths,
+        make_variables = make_variables,
+        builtin_sysroot = builtin_sysroot,
+        cc_target_os = cc_target_os,
+    )
+
+def _armeabi_impl(ctx):
+    toolchain_identifier = "stub_armeabi-v7a"
+    host_system_name = "armeabi-v7a"
+    target_system_name = "armeabi-v7a"
+    target_cpu = "armeabi-v7a"
+    target_libc = "armeabi-v7a"
+    compiler = "compiler"
+    abi_version = "armeabi-v7a"
+    abi_libc_version = "armeabi-v7a"
+    cc_target_os = None
+    builtin_sysroot = None
+    action_configs = []
+
+    supports_pic_feature = feature(name = "supports_pic", enabled = True)
+    supports_dynamic_linker_feature = feature(name = "supports_dynamic_linker", enabled = True)
+    features = [supports_dynamic_linker_feature, supports_pic_feature]
+
+    cxx_builtin_include_directories = [
+        # This is a workaround for https://github.com/bazelbuild/bazel/issues/5087.
+        "C:\\botcode\\w",
+    ]
+    artifact_name_patterns = []
+    make_variables = []
+
+    tool_paths = [
+        tool_path(name = "ar", path = "/bin/false"),
+        tool_path(name = "compat-ld", path = "/bin/false"),
+        tool_path(name = "cpp", path = "/bin/false"),
+        tool_path(name = "dwp", path = "/bin/false"),
+        tool_path(name = "gcc", path = "/bin/false"),
+        tool_path(name = "gcov", path = "/bin/false"),
+        tool_path(name = "ld", path = "/bin/false"),
+        tool_path(name = "nm", path = "/bin/false"),
+        tool_path(name = "objcopy", path = "/bin/false"),
+        tool_path(name = "objdump", path = "/bin/false"),
+        tool_path(name = "strip", path = "/bin/false"),
+    ]
+
+    return cc_common.create_cc_toolchain_config_info(
+        ctx = ctx,
+        features = features,
+        action_configs = action_configs,
+        artifact_name_patterns = artifact_name_patterns,
+        cxx_builtin_include_directories = cxx_builtin_include_directories,
+        toolchain_identifier = toolchain_identifier,
+        host_system_name = host_system_name,
+        target_system_name = target_system_name,
+        target_cpu = target_cpu,
+        target_libc = target_libc,
+        compiler = compiler,
+        abi_version = abi_version,
+        abi_libc_version = abi_libc_version,
+        tool_paths = tool_paths,
+        make_variables = make_variables,
+        builtin_sysroot = builtin_sysroot,
+        cc_target_os = cc_target_os,
+    )
+
+def _impl(ctx):
+    if ctx.attr.cpu == "armeabi-v7a":
+        return _armeabi_impl(ctx)
+    elif ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "msvc-cl":
+        return _windows_msvc_impl(ctx)
+    elif ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "mingw-gcc":
+        return _windows_msys_mingw_impl(ctx)
+
+    tool_paths = [
+        tool_path(name = "ar", path = "c:/tools/msys64/usr/bin/ar"),
+        tool_path(name = "compat-ld", path = "c:/tools/msys64/usr/bin/ld"),
+        tool_path(name = "cpp", path = "c:/tools/msys64/usr/bin/cpp"),
+        tool_path(name = "dwp", path = "c:/tools/msys64/usr/bin/dwp"),
+        tool_path(name = "gcc", path = "c:/tools/msys64/usr/bin/gcc"),
+        tool_path(name = "gcov", path = "c:/tools/msys64/usr/bin/gcov"),
+        tool_path(name = "ld", path = "c:/tools/msys64/usr/bin/ld"),
+        tool_path(name = "nm", path = "c:/tools/msys64/usr/bin/nm"),
+        tool_path(name = "objcopy", path = "c:/tools/msys64/usr/bin/objcopy"),
+        tool_path(name = "objdump", path = "c:/tools/msys64/usr/bin/objdump"),
+        tool_path(name = "strip", path = "c:/tools/msys64/usr/bin/strip"),
+    ]
+
+    cxx_builtin_include_directories = [
+        # This is a workaround for https://github.com/bazelbuild/bazel/issues/5087.
+        "C:\\botcode\\w",
+        "c:/tools/msys64/usr/",
+        "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE",
+        "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt",
+        "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\shared",
+        "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\um",
+        "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\winrt",
+    ]
+
+    action_configs = []
+
+    compile_flags = [
+    ]
+
+    dbg_compile_flags = [
+    ]
+
+    opt_compile_flags = [
+    ]
+
+    cxx_flags = [
+        "-std=gnu++0x",
+    ]
+
+    link_flags = [
+        "-lstdc++",
+    ]
+
+    opt_link_flags = [
+    ]
+
+    unfiltered_compile_flags = [
+    ]
+
+    targets_windows_feature = feature(
+        name = "targets_windows",
+        implies = ["copy_dynamic_libraries_to_binary"],
+        enabled = True,
+    )
+
+    copy_dynamic_libraries_to_binary_feature = feature(name = "copy_dynamic_libraries_to_binary")
+
+    gcc_env_feature = feature(
+        name = "gcc_env",
+        enabled = True,
+        env_sets = [
+            env_set(
+                actions = [
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.cpp_link_executable,
+                    ACTION_NAMES.cpp_link_dynamic_library,
+                    ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+                    ACTION_NAMES.cpp_link_static_library,
+                ],
+                env_entries = [
+                    env_entry(key = "PATH", value = "c:/tools/msys64/usr/bin"),
+                ],
+            ),
+        ],
+    )
+
+    windows_features = [
+        targets_windows_feature,
+        copy_dynamic_libraries_to_binary_feature,
+        gcc_env_feature,
+    ]
+
+    supports_pic_feature = feature(
+        name = "supports_pic",
+        enabled = True,
+    )
+    supports_start_end_lib_feature = feature(
+        name = "supports_start_end_lib",
+        enabled = True,
+    )
+
+    default_compile_flags_feature = feature(
+        name = "default_compile_flags",
+        enabled = True,
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.linkstamp_compile,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                    ACTION_NAMES.lto_backend,
+                    ACTION_NAMES.clif_match,
+                ],
+                flag_groups = ([flag_group(flags = compile_flags)] if compile_flags else []),
+            ),
+            flag_set(
+                actions = [
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.linkstamp_compile,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                    ACTION_NAMES.lto_backend,
+                    ACTION_NAMES.clif_match,
+                ],
+                flag_groups = ([flag_group(flags = dbg_compile_flags)] if dbg_compile_flags else []),
+                with_features = [with_feature_set(features = ["dbg"])],
+            ),
+            flag_set(
+                actions = [
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.linkstamp_compile,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                    ACTION_NAMES.lto_backend,
+                    ACTION_NAMES.clif_match,
+                ],
+                flag_groups = ([flag_group(flags = opt_compile_flags)] if opt_compile_flags else []),
+                with_features = [with_feature_set(features = ["opt"])],
+            ),
+            flag_set(
+                actions = [
+                    ACTION_NAMES.linkstamp_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                    ACTION_NAMES.lto_backend,
+                    ACTION_NAMES.clif_match,
+                ],
+                flag_groups = ([flag_group(flags = cxx_flags)] if cxx_flags else []),
+            ),
+        ],
+    )
+
+    default_link_flags_feature = feature(
+        name = "default_link_flags",
+        enabled = True,
+        flag_sets = [
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = ([flag_group(flags = link_flags)] if link_flags else []),
+            ),
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = ([flag_group(flags = opt_link_flags)] if opt_link_flags else []),
+                with_features = [with_feature_set(features = ["opt"])],
+            ),
+        ],
+    )
+
+    dbg_feature = feature(name = "dbg")
+
+    opt_feature = feature(name = "opt")
+
+    sysroot_feature = feature(
+        name = "sysroot",
+        enabled = True,
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.linkstamp_compile,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                    ACTION_NAMES.lto_backend,
+                    ACTION_NAMES.clif_match,
+                    ACTION_NAMES.cpp_link_executable,
+                    ACTION_NAMES.cpp_link_dynamic_library,
+                    ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["--sysroot=%{sysroot}"],
+                        expand_if_available = "sysroot",
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    fdo_optimize_feature = feature(
+        name = "fdo_optimize",
+        flag_sets = [
+            flag_set(
+                actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile],
+                flag_groups = [
+                    flag_group(
+                        flags = [
+                            "-fprofile-use=%{fdo_profile_path}",
+                            "-fprofile-correction",
+                        ],
+                        expand_if_available = "fdo_profile_path",
+                    ),
+                ],
+            ),
+        ],
+        provides = ["profile"],
+    )
+
+    supports_dynamic_linker_feature = feature(name = "supports_dynamic_linker", enabled = True)
+
+    user_compile_flags_feature = feature(
+        name = "user_compile_flags",
+        enabled = True,
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.linkstamp_compile,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                    ACTION_NAMES.lto_backend,
+                    ACTION_NAMES.clif_match,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["%{user_compile_flags}"],
+                        iterate_over = "user_compile_flags",
+                        expand_if_available = "user_compile_flags",
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    unfiltered_compile_flags_feature = feature(
+        name = "unfiltered_compile_flags",
+        enabled = True,
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.linkstamp_compile,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                    ACTION_NAMES.lto_backend,
+                    ACTION_NAMES.clif_match,
+                ],
+                flag_groups = ([flag_group(flags = unfiltered_compile_flags)] if unfiltered_compile_flags else []),
+            ),
+        ],
+    )
+
+    features = windows_features + [
+        supports_pic_feature,
+        default_compile_flags_feature,
+        default_link_flags_feature,
+        fdo_optimize_feature,
+        supports_dynamic_linker_feature,
+        dbg_feature,
+        opt_feature,
+        user_compile_flags_feature,
+        sysroot_feature,
+        unfiltered_compile_flags_feature,
+    ]
+
+    artifact_name_patterns = [
+        artifact_name_pattern(category_name = "executable", prefix = "", extension = ".exe"),
+    ]
+
+    make_variables = []
+
+    return cc_common.create_cc_toolchain_config_info(
+        ctx = ctx,
+        features = features,
+        action_configs = action_configs,
+        artifact_name_patterns = artifact_name_patterns,
+        cxx_builtin_include_directories = cxx_builtin_include_directories,
+        toolchain_identifier = "msys_x64",
+        host_system_name = "local",
+        target_system_name = "local",
+        target_cpu = "x64_windows",
+        target_libc = "msys",
+        compiler = "msys-gcc",
+        abi_version = "local",
+        abi_libc_version = "local",
+        tool_paths = tool_paths,
+        make_variables = make_variables,
+        builtin_sysroot = "",
+        cc_target_os = None,
+    )
+
+cc_toolchain_config = rule(
+    implementation = _impl,
+    attrs = {
+        "cpu": attr.string(mandatory = True),
+        "compiler": attr.string(),
+    },
+    provides = [CcToolchainConfigInfo],
+)
diff --git a/third_party/toolchains/bazel_0.23.2_rbe_windows/dummy_toolchain.bzl b/third_party/toolchains/bazel_0.23.2_rbe_windows/dummy_toolchain.bzl
new file mode 100644 (file)
index 0000000..45c0285
--- /dev/null
@@ -0,0 +1,23 @@
+# pylint: disable=g-bad-file-header
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# 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.
+
+"""Skylark rule that stubs a toolchain."""
+
+def _dummy_toolchain_impl(ctx):
+    ctx = ctx  # unused argument
+    toolchain = platform_common.ToolchainInfo()
+    return [toolchain]
+
+dummy_toolchain = rule(_dummy_toolchain_impl, attrs = {})
index ab288a0..5545e87 100755 (executable)
@@ -394,7 +394,7 @@ for i, elem in enumerate(all_strs):
 
 def slice_def(i):
     return ('{&grpc_static_metadata_refcounts[%d],'
-            ' {{g_bytes+%d, %d}}}') % (i, id2strofs[i], len(all_strs[i]))
+            ' {{%d, g_bytes+%d}}}') % (i, len(all_strs[i]), id2strofs[i])
 
 
 # validate configuration
@@ -412,29 +412,19 @@ print >> H
 print >> C, 'static uint8_t g_bytes[] = {%s};' % (','.join(
     '%d' % ord(c) for c in ''.join(all_strs)))
 print >> C
-print >> C, 'static void static_ref(void *unused) {}'
-print >> C, 'static void static_unref(void *unused) {}'
-print >> C, ('static const grpc_slice_refcount_vtable static_sub_vtable = '
-             '{static_ref, static_unref, grpc_slice_default_eq_impl, '
-             'grpc_slice_default_hash_impl};')
-print >> H, ('extern const grpc_slice_refcount_vtable '
-             'grpc_static_metadata_vtable;')
-print >> C, ('const grpc_slice_refcount_vtable grpc_static_metadata_vtable = '
-             '{static_ref, static_unref, grpc_static_slice_eq, '
-             'grpc_static_slice_hash};')
-print >> C, ('static grpc_slice_refcount static_sub_refcnt = '
-             '{&static_sub_vtable, &static_sub_refcnt};')
+print >> C, ('static grpc_slice_refcount static_sub_refcnt;')
 print >> H, ('extern grpc_slice_refcount '
              'grpc_static_metadata_refcounts[GRPC_STATIC_MDSTR_COUNT];')
 print >> C, ('grpc_slice_refcount '
              'grpc_static_metadata_refcounts[GRPC_STATIC_MDSTR_COUNT] = {')
 for i, elem in enumerate(all_strs):
-    print >> C, '  {&grpc_static_metadata_vtable, &static_sub_refcnt},'
+    print >> C, ('  grpc_slice_refcount(&static_sub_refcnt, '
+                 'grpc_slice_refcount::Type::STATIC), ')
 print >> C, '};'
 print >> C
 print >> H, '#define GRPC_IS_STATIC_METADATA_STRING(slice) \\'
-print >> H, ('  ((slice).refcount != NULL && (slice).refcount->vtable == '
-             '&grpc_static_metadata_vtable)')
+print >> H, ('  ((slice).refcount != NULL && (slice).refcount->GetType() == '
+             'grpc_slice_refcount::Type::STATIC)')
 print >> H
 print >> C, ('const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT]'
              ' = {')
index 2ca36b7..ff21c49 100755 (executable)
@@ -52,7 +52,9 @@ proto_files=( \
   "envoy/api/v2/cds.proto" \
   "envoy/api/v2/eds.proto" \
   "envoy/api/v2/endpoint/endpoint.proto" \
-  "envoy/service/discovery/v2/ads.proto")
+  "envoy/api/v2/endpoint/load_report.proto" \
+  "envoy/service/discovery/v2/ads.proto" \
+  "envoy/service/load_stats/v2/lrs.proto")
 
 for i in "${proto_files[@]}"
 do
diff --git a/tools/distrib/bazel_style.cfg b/tools/distrib/bazel_style.cfg
new file mode 100644 (file)
index 0000000..a5a1fea
--- /dev/null
@@ -0,0 +1,4 @@
+[style]
+based_on_style = google
+allow_split_before_dict_value = False
+spaces_around_default_or_named_assign = True
diff --git a/tools/distrib/format_bazel.sh b/tools/distrib/format_bazel.sh
new file mode 100755 (executable)
index 0000000..ee23011
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/bash
+# 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.
+
+set=-ex
+
+VIRTUAL_ENV=bazel_format_virtual_environment
+
+CONFIG_PATH="$(dirname ${0})/bazel_style.cfg"
+
+python -m virtualenv ${VIRTUAL_ENV}
+PYTHON=${VIRTUAL_ENV}/bin/python
+"$PYTHON" -m pip install --upgrade pip==10.0.1
+"$PYTHON" -m pip install --upgrade futures
+"$PYTHON" -m pip install yapf==0.20.0
+
+pushd "$(dirname "${0}")/../.."
+FILES=$(find . -path ./third_party -prune -o -name '*.bzl' -print)
+echo "${FILES}" | xargs "$PYTHON" -m yapf -i --style="${CONFIG_PATH}"
+
+if ! which buildifier &>/dev/null; then
+    echo 'buildifer must be installed.' >/dev/stderr
+    exit 1
+fi
+
+echo "${FILES}" | xargs buildifier --type=bzl
+
+popd
index de47c00..67d0054 100755 (executable)
@@ -44,7 +44,7 @@ PROJECT_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, '..', '..', '..'))
 
 CONFIG = args.config
 SETUP_PATH = os.path.join(PROJECT_ROOT, 'setup.py')
-REQUIREMENTS_PATH = os.path.join(PROJECT_ROOT, 'requirements.txt')
+REQUIREMENTS_PATH = os.path.join(PROJECT_ROOT, 'requirements.bazel.txt')
 DOC_PATH = os.path.join(PROJECT_ROOT, 'doc/build')
 INCLUDE_PATH = os.path.join(PROJECT_ROOT, 'include')
 LIBRARY_PATH = os.path.join(PROJECT_ROOT, 'libs/{}'.format(CONFIG))
index 32873b2..cc974ed 100644 (file)
@@ -3,6 +3,14 @@ gRPC Python Tools
 
 Package for gRPC Python tools.
 
+Supported Python Versions
+-------------------------
+Python >= 3.5
+
+Deprecated Python Versions
+--------------------------
+Python == 2.7. Python 2.7 support will be removed on January 1, 2020.
+
 Installation
 ------------
 
index 7d2fc48..5577597 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/tools/distrib/python/grpcio_tools/grpc_version.py.template`!!!
 
-VERSION = '1.20.1'
+VERSION = '1.21.0'
index 64c468c..e83e08b 100644 (file)
@@ -31,6 +31,9 @@ from setuptools.command import build_ext
 
 # TODO(atash) add flag to disable Cython use
 
+_PACKAGE_PATH = os.path.realpath(os.path.dirname(__file__))
+_README_PATH = os.path.join(_PACKAGE_PATH, 'README.rst')
+
 os.chdir(os.path.dirname(os.path.abspath(__file__)))
 sys.path.insert(0, os.path.abspath('.'))
 
@@ -191,6 +194,7 @@ setuptools.setup(
     name='grpcio-tools',
     version=grpc_version.VERSION,
     description='Protobuf code generator for gRPC',
+    long_description=open(_README_PATH, 'r').read(),
     author='The gRPC Authors',
     author_email='grpc-io@googlegroups.com',
     url='https://grpc.io',
index fed1e64..13488b1 100755 (executable)
@@ -29,11 +29,11 @@ if [ "x$1" == 'x--pre-commit' ]; then
     fi
   fi
   CHANGED_FILES=$(eval $DIFF_COMMAND) ./tools/distrib/clang_format_code.sh
-  ./tools/distrib/check_copyright.py --fix --precommit
+  ./tools/distrib/check_copyright.py --precommit
   ./tools/distrib/check_trailing_newlines.sh
 else
   ./tools/buildgen/generate_projects.sh
   ./tools/distrib/clang_format_code.sh
-  ./tools/distrib/check_copyright.py --fix
+  ./tools/distrib/check_copyright.py
   ./tools/distrib/check_trailing_newlines.sh
 fi
index 8e5edf7..876992f 100644 (file)
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 FROM debian:jessie
-RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
 
 
 RUN apt-get update && apt-get -y install wget xz-utils
index 2e0e683..a5abca0 100644 (file)
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 FROM debian:jessie
-RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
 
 
 RUN apt-get update && apt-get -y install wget xz-utils
index 1fe64a4..e9f2a85 100644 (file)
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 FROM debian:jessie
-RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
 
 
 # Install Git and basic packages.
index d168306..979c14d 100644 (file)
 # limitations under the License.
 
 FROM debian:jessie
-RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
 
 
-# Install JDK 8 and Git
+# Install JDK 8
 #
 RUN echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections && \
   echo "deb http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main" | tee /etc/apt/sources.list.d/webupd8team-java.list && \
   echo "deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main" | tee -a /etc/apt/sources.list.d/webupd8team-java.list && \
-  apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys EEA14886
-
-RUN apt-get update && apt-get -y install \
-      git \
-      libapr1 \
-      oracle-java8-installer \
-      && \
-    apt-get clean && rm -r /var/cache/oracle-jdk8-installer/
+  apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys EEA14886 && \
+  apt-get update && apt-get -y install oracle-java8-installer && \
+  apt-get clean && rm -r /var/cache/oracle-jdk8-installer/
 
 ENV JAVA_HOME /usr/lib/jvm/java-8-oracle
 ENV PATH $PATH:$JAVA_HOME/bin
 
 
-#====================
-# Python dependencies
-
-# Install dependencies
-
-RUN apt-get update && apt-get install -y \
-    python-all-dev \
-    python3-all-dev \
-    python-pip
-
-# Install Python packages from PyPI
-RUN pip install --upgrade pip==10.0.1
-RUN pip install virtualenv
-RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0
-
-
-# Trigger download of as many Gradle artifacts as possible.
-RUN git clone --recursive --depth 1 https://github.com/grpc/grpc-java.git && \
-  cd grpc-java && \
-  ./gradlew :grpc-interop-testing:installDist -PskipCodegen=true && \
-  rm -r "$(pwd)"
 
 # Define the default command.
 CMD ["bash"]
+
index 0e36c19..8e11546 100644 (file)
 # Builds Java interop server and client in a base image.
 set -e
 
-mkdir -p /var/local/git
-git clone --recursive --depth 1 /var/local/jenkins/grpc-java /var/local/git/grpc-java
+cp -r /var/local/jenkins/grpc-java /tmp/grpc-java
 
 # copy service account keys if available
 cp -r /var/local/jenkins/service_account $HOME || true
 
-cd /var/local/git/grpc-java
-
+pushd /tmp/grpc-java
 ./gradlew :grpc-interop-testing:installDist -PskipCodegen=true
 
+mkdir -p /var/local/git/grpc-java/
+cp -r --parents -t /var/local/git/grpc-java/ \
+    interop-testing/build/install/ \
+    run-test-client.sh \
+    run-test-server.sh
+
+popd
+rm -r /tmp/grpc-java
+rm -r "$HOME/.gradle"
+
 # enable extra java logging
 mkdir -p /var/local/grpc_java_logging
 echo "handlers = java.util.logging.ConsoleHandler
index d168306..979c14d 100644 (file)
 # limitations under the License.
 
 FROM debian:jessie
-RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
 
 
-# Install JDK 8 and Git
+# Install JDK 8
 #
 RUN echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections && \
   echo "deb http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main" | tee /etc/apt/sources.list.d/webupd8team-java.list && \
   echo "deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main" | tee -a /etc/apt/sources.list.d/webupd8team-java.list && \
-  apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys EEA14886
-
-RUN apt-get update && apt-get -y install \
-      git \
-      libapr1 \
-      oracle-java8-installer \
-      && \
-    apt-get clean && rm -r /var/cache/oracle-jdk8-installer/
+  apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys EEA14886 && \
+  apt-get update && apt-get -y install oracle-java8-installer && \
+  apt-get clean && rm -r /var/cache/oracle-jdk8-installer/
 
 ENV JAVA_HOME /usr/lib/jvm/java-8-oracle
 ENV PATH $PATH:$JAVA_HOME/bin
 
 
-#====================
-# Python dependencies
-
-# Install dependencies
-
-RUN apt-get update && apt-get install -y \
-    python-all-dev \
-    python3-all-dev \
-    python-pip
-
-# Install Python packages from PyPI
-RUN pip install --upgrade pip==10.0.1
-RUN pip install virtualenv
-RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0
-
-
-# Trigger download of as many Gradle artifacts as possible.
-RUN git clone --recursive --depth 1 https://github.com/grpc/grpc-java.git && \
-  cd grpc-java && \
-  ./gradlew :grpc-interop-testing:installDist -PskipCodegen=true && \
-  rm -r "$(pwd)"
 
 # Define the default command.
 CMD ["bash"]
+
index 4c5ba4b..77d3228 100644 (file)
 # Builds Java interop server and client in a base image.
 set -e
 
-mkdir -p /var/local/git
-git clone --recursive --depth 1 /var/local/jenkins/grpc-java /var/local/git/grpc-java
+cp -r /var/local/jenkins/grpc-java /tmp/grpc-java
 
 # copy service account keys if available
 cp -r /var/local/jenkins/service_account $HOME || true
 
-cd /var/local/git/grpc-java
-
+pushd /tmp/grpc-java
 ./gradlew :grpc-interop-testing:installDist -PskipCodegen=true
 
+mkdir -p /var/local/git/grpc-java/
+cp -r --parents -t /var/local/git/grpc-java/ \
+    interop-testing/build/install/ \
+    run-test-client.sh \
+    run-test-server.sh
+
+popd
+rm -r /tmp/grpc-java
+rm -r "$HOME/.gradle"
+
 # enable extra java logging
 mkdir -p /var/local/grpc_java_logging
 echo "handlers = java.util.logging.ConsoleHandler
index 96adf6e..283a99d 100644 (file)
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 FROM debian:jessie
-RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
 
 
 # Install Git and basic packages.
index 88e3cfa..fd089f1 100644 (file)
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 FROM debian:jessie
-RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
 
 
 # Install Git and basic packages.
index c9c141e..04f0ac2 100644 (file)
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 FROM debian:jessie
-RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
 
 
 # Install Git and basic packages.
index 8424139..01de0eb 100644 (file)
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 FROM debian:jessie
-RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
 
 
 #=================
index cce5e86..8e605a8 100644 (file)
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 FROM debian:jessie
-RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
 
 
 # Install Git and basic packages.
index 7dee005..2536fe2 100644 (file)
@@ -52,9 +52,9 @@ RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 t
 # Bazel installation
 
 RUN apt-get update && apt-get install -y wget && apt-get clean
-RUN wget https://github.com/bazelbuild/bazel/releases/download/0.20.0/bazel-0.20.0-installer-linux-x86_64.sh && \
-  bash ./bazel-0.20.0-installer-linux-x86_64.sh && \
-  rm bazel-0.20.0-installer-linux-x86_64.sh
+RUN wget https://github.com/bazelbuild/bazel/releases/download/0.23.2/bazel-0.23.2-installer-linux-x86_64.sh && \
+  bash ./bazel-0.23.2-installer-linux-x86_64.sh && \
+  rm bazel-0.23.2-installer-linux-x86_64.sh
 
 
 RUN mkdir -p /var/local/jenkins
index c46f961..b79faa0 100644 (file)
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 FROM debian:jessie
-RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
 
 
 # Install Git and basic packages.
index 14e4478..f9dd948 100644 (file)
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 FROM debian:jessie
-RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
 
 
 # Install Git and basic packages.
index 0ef3210..7c0b86a 100644 (file)
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 FROM debian:jessie
-RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
 
 
 # Install Git and basic packages.
index a32b764..5cca63d 100644 (file)
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 FROM debian:jessie
-RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
 
 
 #=================
index ebbbcf5..ee54263 100644 (file)
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 FROM debian:jessie
-RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
 
 
 # Install Git and basic packages.
index 5ac3af8..367785f 100644 (file)
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 FROM debian:jessie
-RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
 
 
 # Install Git and basic packages.
index ae46075..7670409 100644 (file)
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 FROM debian:jessie
-RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
 
 
 # Install Git and basic packages.
index 7a6fbfe..765bd72 100644 (file)
@@ -98,9 +98,9 @@ ENV CLANG_TIDY=clang-tidy
 # Bazel installation
 
 RUN apt-get update && apt-get install -y wget && apt-get clean
-RUN wget https://github.com/bazelbuild/bazel/releases/download/0.20.0/bazel-0.20.0-installer-linux-x86_64.sh && \
-  bash ./bazel-0.20.0-installer-linux-x86_64.sh && \
-  rm bazel-0.20.0-installer-linux-x86_64.sh
+RUN wget https://github.com/bazelbuild/bazel/releases/download/0.23.2/bazel-0.23.2-installer-linux-x86_64.sh && \
+  bash ./bazel-0.23.2-installer-linux-x86_64.sh && \
+  rm bazel-0.23.2-installer-linux-x86_64.sh
 
 
 # Define the default command.
index fac989c..ac397ab 100644 (file)
@@ -40,7 +40,7 @@ PROJECT_NAME           = "GRPC C++"
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 1.20.1
+PROJECT_NUMBER         = 1.21.0
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
@@ -927,16 +927,20 @@ include/grpc/support/workaround_list.h \
 include/grpcpp/alarm.h \
 include/grpcpp/alarm_impl.h \
 include/grpcpp/channel.h \
+include/grpcpp/channel_impl.h \
 include/grpcpp/client_context.h \
 include/grpcpp/completion_queue.h \
 include/grpcpp/create_channel.h \
+include/grpcpp/create_channel_impl.h \
 include/grpcpp/create_channel_posix.h \
 include/grpcpp/create_channel_posix_impl.h \
 include/grpcpp/ext/health_check_service_server_builder_option.h \
 include/grpcpp/generic/async_generic_service.h \
 include/grpcpp/generic/generic_stub.h \
+include/grpcpp/generic/generic_stub_impl.h \
 include/grpcpp/grpcpp.h \
 include/grpcpp/health_check_service_interface.h \
+include/grpcpp/health_check_service_interface_impl.h \
 include/grpcpp/impl/call.h \
 include/grpcpp/impl/channel_argument_option.h \
 include/grpcpp/impl/client_unary_call.h \
@@ -955,6 +959,7 @@ include/grpcpp/impl/codegen/client_context.h \
 include/grpcpp/impl/codegen/client_interceptor.h \
 include/grpcpp/impl/codegen/client_unary_call.h \
 include/grpcpp/impl/codegen/completion_queue.h \
+include/grpcpp/impl/codegen/completion_queue_impl.h \
 include/grpcpp/impl/codegen/completion_queue_tag.h \
 include/grpcpp/impl/codegen/config.h \
 include/grpcpp/impl/codegen/config_protobuf.h \
@@ -965,6 +970,7 @@ include/grpcpp/impl/codegen/grpc_library.h \
 include/grpcpp/impl/codegen/intercepted_channel.h \
 include/grpcpp/impl/codegen/interceptor.h \
 include/grpcpp/impl/codegen/interceptor_common.h \
+include/grpcpp/impl/codegen/message_allocator.h \
 include/grpcpp/impl/codegen/metadata_map.h \
 include/grpcpp/impl/codegen/method_handler_impl.h \
 include/grpcpp/impl/codegen/proto_buffer_reader.h \
@@ -984,6 +990,7 @@ include/grpcpp/impl/codegen/status.h \
 include/grpcpp/impl/codegen/status_code_enum.h \
 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/time.h \
 include/grpcpp/impl/grpc_library.h \
@@ -995,25 +1002,34 @@ include/grpcpp/impl/server_builder_option.h \
 include/grpcpp/impl/server_builder_option_impl.h \
 include/grpcpp/impl/server_builder_plugin.h \
 include/grpcpp/impl/server_initializer.h \
+include/grpcpp/impl/server_initializer_impl.h \
 include/grpcpp/impl/service_type.h \
 include/grpcpp/resource_quota.h \
+include/grpcpp/resource_quota_impl.h \
 include/grpcpp/security/auth_context.h \
 include/grpcpp/security/auth_metadata_processor.h \
+include/grpcpp/security/auth_metadata_processor_impl.h \
 include/grpcpp/security/credentials.h \
+include/grpcpp/security/credentials_impl.h \
 include/grpcpp/security/server_credentials.h \
+include/grpcpp/security/server_credentials_impl.h \
 include/grpcpp/server.h \
 include/grpcpp/server_builder.h \
+include/grpcpp/server_builder_impl.h \
 include/grpcpp/server_context.h \
+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_unary_call.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_interceptor.h \
 include/grpcpp/support/config.h \
 include/grpcpp/support/interceptor.h \
+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 \
index 5c48152..e0ffeff 100644 (file)
@@ -40,7 +40,7 @@ PROJECT_NAME           = "GRPC C++"
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 1.20.1
+PROJECT_NUMBER         = 1.21.0
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
@@ -928,16 +928,20 @@ include/grpc/support/workaround_list.h \
 include/grpcpp/alarm.h \
 include/grpcpp/alarm_impl.h \
 include/grpcpp/channel.h \
+include/grpcpp/channel_impl.h \
 include/grpcpp/client_context.h \
 include/grpcpp/completion_queue.h \
 include/grpcpp/create_channel.h \
+include/grpcpp/create_channel_impl.h \
 include/grpcpp/create_channel_posix.h \
 include/grpcpp/create_channel_posix_impl.h \
 include/grpcpp/ext/health_check_service_server_builder_option.h \
 include/grpcpp/generic/async_generic_service.h \
 include/grpcpp/generic/generic_stub.h \
+include/grpcpp/generic/generic_stub_impl.h \
 include/grpcpp/grpcpp.h \
 include/grpcpp/health_check_service_interface.h \
+include/grpcpp/health_check_service_interface_impl.h \
 include/grpcpp/impl/call.h \
 include/grpcpp/impl/channel_argument_option.h \
 include/grpcpp/impl/client_unary_call.h \
@@ -956,6 +960,7 @@ include/grpcpp/impl/codegen/client_context.h \
 include/grpcpp/impl/codegen/client_interceptor.h \
 include/grpcpp/impl/codegen/client_unary_call.h \
 include/grpcpp/impl/codegen/completion_queue.h \
+include/grpcpp/impl/codegen/completion_queue_impl.h \
 include/grpcpp/impl/codegen/completion_queue_tag.h \
 include/grpcpp/impl/codegen/config.h \
 include/grpcpp/impl/codegen/config_protobuf.h \
@@ -967,6 +972,7 @@ include/grpcpp/impl/codegen/grpc_library.h \
 include/grpcpp/impl/codegen/intercepted_channel.h \
 include/grpcpp/impl/codegen/interceptor.h \
 include/grpcpp/impl/codegen/interceptor_common.h \
+include/grpcpp/impl/codegen/message_allocator.h \
 include/grpcpp/impl/codegen/metadata_map.h \
 include/grpcpp/impl/codegen/method_handler_impl.h \
 include/grpcpp/impl/codegen/proto_buffer_reader.h \
@@ -986,6 +992,7 @@ include/grpcpp/impl/codegen/status.h \
 include/grpcpp/impl/codegen/status_code_enum.h \
 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/time.h \
 include/grpcpp/impl/grpc_library.h \
@@ -997,25 +1004,34 @@ include/grpcpp/impl/server_builder_option.h \
 include/grpcpp/impl/server_builder_option_impl.h \
 include/grpcpp/impl/server_builder_plugin.h \
 include/grpcpp/impl/server_initializer.h \
+include/grpcpp/impl/server_initializer_impl.h \
 include/grpcpp/impl/service_type.h \
 include/grpcpp/resource_quota.h \
+include/grpcpp/resource_quota_impl.h \
 include/grpcpp/security/auth_context.h \
 include/grpcpp/security/auth_metadata_processor.h \
+include/grpcpp/security/auth_metadata_processor_impl.h \
 include/grpcpp/security/credentials.h \
+include/grpcpp/security/credentials_impl.h \
 include/grpcpp/security/server_credentials.h \
+include/grpcpp/security/server_credentials_impl.h \
 include/grpcpp/server.h \
 include/grpcpp/server_builder.h \
+include/grpcpp/server_builder_impl.h \
 include/grpcpp/server_context.h \
+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_unary_call.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_interceptor.h \
 include/grpcpp/support/config.h \
 include/grpcpp/support/interceptor.h \
+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 \
@@ -1045,6 +1061,7 @@ src/core/lib/channel/handshaker_factory.h \
 src/core/lib/channel/handshaker_registry.h \
 src/core/lib/channel/status_util.h \
 src/core/lib/compression/algorithm_metadata.h \
+src/core/lib/compression/compression_args.h \
 src/core/lib/compression/compression_internal.h \
 src/core/lib/compression/message_compress.h \
 src/core/lib/compression/stream_compression.h \
@@ -1070,17 +1087,24 @@ src/core/lib/gpr/tls_pthread.h \
 src/core/lib/gpr/tmpfile.h \
 src/core/lib/gpr/useful.h \
 src/core/lib/gprpp/abstract.h \
+src/core/lib/gprpp/arena.h \
 src/core/lib/gprpp/atomic.h \
 src/core/lib/gprpp/debug_location.h \
 src/core/lib/gprpp/fork.h \
+src/core/lib/gprpp/global_config.h \
+src/core/lib/gprpp/global_config_custom.h \
+src/core/lib/gprpp/global_config_env.h \
+src/core/lib/gprpp/global_config_generic.h \
 src/core/lib/gprpp/inlined_vector.h \
 src/core/lib/gprpp/manual_constructor.h \
+src/core/lib/gprpp/map.h \
 src/core/lib/gprpp/memory.h \
-src/core/lib/gprpp/mutex_lock.h \
 src/core/lib/gprpp/optional.h \
 src/core/lib/gprpp/orphanable.h \
+src/core/lib/gprpp/pair.h \
 src/core/lib/gprpp/ref_counted.h \
 src/core/lib/gprpp/ref_counted_ptr.h \
+src/core/lib/gprpp/sync.h \
 src/core/lib/gprpp/thd.h \
 src/core/lib/http/format_request.h \
 src/core/lib/http/httpcli.h \
@@ -1088,12 +1112,15 @@ src/core/lib/http/parser.h \
 src/core/lib/iomgr/block_annotate.h \
 src/core/lib/iomgr/buffer_list.h \
 src/core/lib/iomgr/call_combiner.h \
+src/core/lib/iomgr/cfstream_handle.h \
 src/core/lib/iomgr/closure.h \
 src/core/lib/iomgr/combiner.h \
 src/core/lib/iomgr/dynamic_annotations.h \
 src/core/lib/iomgr/endpoint.h \
+src/core/lib/iomgr/endpoint_cfstream.h \
 src/core/lib/iomgr/endpoint_pair.h \
 src/core/lib/iomgr/error.h \
+src/core/lib/iomgr/error_cfstream.h \
 src/core/lib/iomgr/error_internal.h \
 src/core/lib/iomgr/ev_epoll1_linux.h \
 src/core/lib/iomgr/ev_epollex_linux.h \
index 0fdab21..b34c773 100644 (file)
@@ -942,13 +942,17 @@ src/core/ext/filters/client_channel/resolver/README.md \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h \
+src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc \
+src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc \
+src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc \
+src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h \
 src/core/ext/filters/client_channel/resolver/dns/native/README.md \
 src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc \
 src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc \
@@ -1089,6 +1093,8 @@ src/core/lib/channel/status_util.cc \
 src/core/lib/channel/status_util.h \
 src/core/lib/compression/algorithm_metadata.h \
 src/core/lib/compression/compression.cc \
+src/core/lib/compression/compression_args.cc \
+src/core/lib/compression/compression_args.h \
 src/core/lib/compression/compression_internal.cc \
 src/core/lib/compression/compression_internal.h \
 src/core/lib/compression/message_compress.cc \
@@ -1108,7 +1114,6 @@ src/core/lib/debug/trace.h \
 src/core/lib/gpr/README.md \
 src/core/lib/gpr/alloc.cc \
 src/core/lib/gpr/alloc.h \
-src/core/lib/gpr/arena.cc \
 src/core/lib/gpr/arena.h \
 src/core/lib/gpr/atm.cc \
 src/core/lib/gpr/cpu_iphone.cc \
@@ -1158,18 +1163,27 @@ src/core/lib/gpr/useful.h \
 src/core/lib/gpr/wrap_memcpy.cc \
 src/core/lib/gprpp/README.md \
 src/core/lib/gprpp/abstract.h \
+src/core/lib/gprpp/arena.cc \
+src/core/lib/gprpp/arena.h \
 src/core/lib/gprpp/atomic.h \
 src/core/lib/gprpp/debug_location.h \
 src/core/lib/gprpp/fork.cc \
 src/core/lib/gprpp/fork.h \
+src/core/lib/gprpp/global_config.h \
+src/core/lib/gprpp/global_config_custom.h \
+src/core/lib/gprpp/global_config_env.cc \
+src/core/lib/gprpp/global_config_env.h \
+src/core/lib/gprpp/global_config_generic.h \
 src/core/lib/gprpp/inlined_vector.h \
 src/core/lib/gprpp/manual_constructor.h \
+src/core/lib/gprpp/map.h \
 src/core/lib/gprpp/memory.h \
-src/core/lib/gprpp/mutex_lock.h \
 src/core/lib/gprpp/optional.h \
 src/core/lib/gprpp/orphanable.h \
+src/core/lib/gprpp/pair.h \
 src/core/lib/gprpp/ref_counted.h \
 src/core/lib/gprpp/ref_counted_ptr.h \
+src/core/lib/gprpp/sync.h \
 src/core/lib/gprpp/thd.h \
 src/core/lib/gprpp/thd_posix.cc \
 src/core/lib/gprpp/thd_windows.cc \
@@ -1186,18 +1200,24 @@ src/core/lib/iomgr/buffer_list.cc \
 src/core/lib/iomgr/buffer_list.h \
 src/core/lib/iomgr/call_combiner.cc \
 src/core/lib/iomgr/call_combiner.h \
+src/core/lib/iomgr/cfstream_handle.cc \
+src/core/lib/iomgr/cfstream_handle.h \
 src/core/lib/iomgr/closure.h \
 src/core/lib/iomgr/combiner.cc \
 src/core/lib/iomgr/combiner.h \
 src/core/lib/iomgr/dynamic_annotations.h \
 src/core/lib/iomgr/endpoint.cc \
 src/core/lib/iomgr/endpoint.h \
+src/core/lib/iomgr/endpoint_cfstream.cc \
+src/core/lib/iomgr/endpoint_cfstream.h \
 src/core/lib/iomgr/endpoint_pair.h \
 src/core/lib/iomgr/endpoint_pair_posix.cc \
 src/core/lib/iomgr/endpoint_pair_uv.cc \
 src/core/lib/iomgr/endpoint_pair_windows.cc \
 src/core/lib/iomgr/error.cc \
 src/core/lib/iomgr/error.h \
+src/core/lib/iomgr/error_cfstream.cc \
+src/core/lib/iomgr/error_cfstream.h \
 src/core/lib/iomgr/error_internal.h \
 src/core/lib/iomgr/ev_epoll1_linux.cc \
 src/core/lib/iomgr/ev_epoll1_linux.h \
@@ -1233,6 +1253,7 @@ src/core/lib/iomgr/iomgr_internal.cc \
 src/core/lib/iomgr/iomgr_internal.h \
 src/core/lib/iomgr/iomgr_posix.cc \
 src/core/lib/iomgr/iomgr_posix.h \
+src/core/lib/iomgr/iomgr_posix_cfstream.cc \
 src/core/lib/iomgr/iomgr_uv.cc \
 src/core/lib/iomgr/iomgr_windows.cc \
 src/core/lib/iomgr/is_epollexclusive_available.cc \
@@ -1288,6 +1309,7 @@ src/core/lib/iomgr/socket_windows.h \
 src/core/lib/iomgr/sys_epoll_wrapper.h \
 src/core/lib/iomgr/tcp_client.cc \
 src/core/lib/iomgr/tcp_client.h \
+src/core/lib/iomgr/tcp_client_cfstream.cc \
 src/core/lib/iomgr/tcp_client_custom.cc \
 src/core/lib/iomgr/tcp_client_posix.cc \
 src/core/lib/iomgr/tcp_client_posix.h \
index 9031661..1b014ad 100755 (executable)
@@ -22,7 +22,7 @@ cd "$(dirname "$0")"
 
 CLOUD_PROJECT=grpc-testing
 ZONE=us-central1-b  # this zone allows 32core machines
-LATEST_PERF_WORKER_IMAGE=grpc-performance-kokoro-v4  # update if newer image exists
+LATEST_PERF_WORKER_IMAGE=grpc-performance-kokoro-v5  # update if newer image exists
 
 INSTANCE_NAME="${1:-grpc-kokoro-performance-server}"
 MACHINE_TYPE="${2:-n1-standard-32}"
index 1bf2228..46061a0 100755 (executable)
@@ -219,6 +219,8 @@ touch /tmpfs/READY
 # See https://github.com/grpc/grpc/issues/17794
 sudo sed -i 's/APT::Periodic::Update-Package-Lists "1"/APT::Periodic::Update-Package-Lists "0"/' /etc/apt/apt.conf.d/10periodic
 sudo sed -i 's/APT::Periodic::AutocleanInterval "1"/APT::Periodic::AutocleanInterval "0"/' /etc/apt/apt.conf.d/10periodic
+sudo sed -i 's/APT::Periodic::Update-Package-Lists "1"/APT::Periodic::Update-Package-Lists "0"/' /etc/apt/apt.conf.d/20auto-upgrades
+sudo sed -i 's/APT::Periodic::Unattended-Upgrade "1"/APT::Periodic::Unattended-Upgrade "0"/' /etc/apt/apt.conf.d/20auto-upgrades
 
 # Restart for VM to pick up kernel update
 echo 'Successfully initialized the linux worker, going for reboot in 10 seconds'
diff --git a/tools/internal_ci/linux/grpc_basictests_csharp.cfg b/tools/internal_ci/linux/grpc_basictests_csharp.cfg
new file mode 100644 (file)
index 0000000..017e929
--- /dev/null
@@ -0,0 +1,30 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests linux csharp --inner_jobs 16 -j 2 --internal_ci --bq_result_table aggregate_results"
+}
diff --git a/tools/internal_ci/linux/grpc_basictests_node.cfg b/tools/internal_ci/linux/grpc_basictests_node.cfg
new file mode 100644 (file)
index 0000000..d7b35a0
--- /dev/null
@@ -0,0 +1,30 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests linux grpc-node --inner_jobs 16 -j 2 --internal_ci --bq_result_table aggregate_results"
+}
diff --git a/tools/internal_ci/linux/grpc_basictests_php.cfg b/tools/internal_ci/linux/grpc_basictests_php.cfg
new file mode 100644 (file)
index 0000000..80aa0dc
--- /dev/null
@@ -0,0 +1,30 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests linux php --inner_jobs 16 -j 2 --internal_ci --bq_result_table aggregate_results"
+}
diff --git a/tools/internal_ci/linux/grpc_basictests_python.cfg b/tools/internal_ci/linux/grpc_basictests_python.cfg
new file mode 100644 (file)
index 0000000..444dba9
--- /dev/null
@@ -0,0 +1,30 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests linux python --inner_jobs 16 -j 2 --internal_ci --bq_result_table aggregate_results"
+}
diff --git a/tools/internal_ci/linux/grpc_basictests_ruby.cfg b/tools/internal_ci/linux/grpc_basictests_ruby.cfg
new file mode 100644 (file)
index 0000000..336aa46
--- /dev/null
@@ -0,0 +1,30 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests linux ruby --inner_jobs 16 -j 2 --internal_ci --bq_result_table aggregate_results"
+}
index 863b43a..93399f8 100755 (executable)
 
 set -ex
 
-# A temporary solution to give Kokoro credentials.
-# The file name 4321_grpc-testing-service needs to match auth_credential in
-# the build config.
-mkdir -p ${KOKORO_KEYSTORE_DIR}
-cp ${KOKORO_GFILE_DIR}/GrpcTesting-d0eeee2db331.json ${KOKORO_KEYSTORE_DIR}/4321_grpc-testing-service
-
 # Download bazel
 temp_dir="$(mktemp -d)"
-wget -q https://github.com/bazelbuild/bazel/releases/download/0.20.0/bazel-0.20.0-linux-x86_64 -O "${temp_dir}/bazel"
+wget -q https://github.com/bazelbuild/bazel/releases/download/0.23.2/bazel-0.23.2-linux-x86_64 -O "${temp_dir}/bazel"
 chmod 755 "${temp_dir}/bazel"
 export PATH="${temp_dir}:${PATH}"
 # This should show ${temp_dir}/bazel
@@ -45,6 +39,7 @@ bazel \
   test \
   --invocation_id="${BAZEL_INVOCATION_ID}" \
   --workspace_status_command=tools/remote_build/workspace_status_kokoro.sh \
+  --google_credentials="${KOKORO_GFILE_DIR}/GrpcTesting-d0eeee2db331.json" \
   $@ \
   -- //test/... || FAILED="true"
 
diff --git a/tools/internal_ci/linux/grpc_bazel_rbe_incompatible_changes.sh b/tools/internal_ci/linux/grpc_bazel_rbe_incompatible_changes.sh
new file mode 100644 (file)
index 0000000..9c3712a
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+# Copyright 2017 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.
+
+set -ex
+
+# TODO(jtattermusch): use the latest version of bazel
+
+# Use --all_incompatible_changes to give an early warning about future
+# bazel incompatibilities.
+EXTRA_FLAGS="--config=opt --cache_test_results=no --all_incompatible_changes"
+github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh "${EXTRA_FLAGS}"
index dc9fe7d..54e03a9 100644 (file)
@@ -24,3 +24,5 @@ action {
     regex: "github/grpc/artifacts/**"
   }
 }
+
+gfile_resources: "/bigstore/grpc-testing-secrets/nuget_credentials/artifactory_grpc_nuget_dev_api_key"
index 1449230..8768421 100755 (executable)
@@ -233,3 +233,16 @@ gsutil -m cp -r "$LOCAL_STAGING_TEMPDIR/${BUILD_RELPATH%%/*}" "$GCS_ARCHIVE_ROOT
 )
 # Upload the new /index.xml
 gsutil -h "Content-Type:application/xml" cp "$NEW_INDEX" "$GCS_INDEX"
+
+# Upload C# nugets to the dev nuget feed
+pushd "$UNZIPPED_CSHARP_PACKAGES"
+docker pull mcr.microsoft.com/dotnet/core/sdk:2.1
+for nugetfile in *.nupkg
+do
+  echo "Going to push $nugetfile"
+  # use nuget from a docker container to push the nupkg
+  set +x  # IMPORTANT: avoid revealing the nuget api key by the command echo
+  docker run -v "$(pwd):/nugets:ro" --rm=true mcr.microsoft.com/dotnet/core/sdk:2.1 bash -c "dotnet nuget push /nugets/$nugetfile -k $(cat ${KOKORO_GFILE_DIR}/artifactory_grpc_nuget_dev_api_key) --source https://grpc.jfrog.io/grpc/api/nuget/v3/grpc-nuget-dev"
+  set -ex
+done
+popd
@@ -1,4 +1,4 @@
-# Copyright 2017 gRPC authors.
+# 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.
@@ -16,7 +16,7 @@
 
 # Location of the continuous shell script in repository.
 build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh"
-timeout_mins: 240
+timeout_mins: 60
 action {
   define_artifacts {
     regex: "**/*sponge_log.*"
@@ -26,5 +26,5 @@ action {
 
 env_vars {
   key: "RUN_TESTS_FLAGS"
-  value: "-f basictests linux corelang dbg --inner_jobs 16 -j 1 --internal_ci --max_time=3600"
+  value: "-f basictests linux csharp --inner_jobs 16 -j 2 --internal_ci --max_time=3600"
 }
@@ -1,4 +1,4 @@
-# Copyright 2017 gRPC authors.
+# 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.
@@ -16,7 +16,7 @@
 
 # Location of the continuous shell script in repository.
 build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh"
-timeout_mins: 240
+timeout_mins: 60
 action {
   define_artifacts {
     regex: "**/*sponge_log.*"
@@ -26,5 +26,5 @@ action {
 
 env_vars {
   key: "RUN_TESTS_FLAGS"
-  value: "-f basictests linux corelang opt --inner_jobs 16 -j 1 --internal_ci --max_time=3600"
+  value: "-f basictests linux grpc-node --inner_jobs 16 -j 2 --internal_ci --max_time=3600"
 }
diff --git a/tools/internal_ci/linux/pull_request/grpc_basictests_php.cfg b/tools/internal_ci/linux/pull_request/grpc_basictests_php.cfg
new file mode 100644 (file)
index 0000000..c844fcf
--- /dev/null
@@ -0,0 +1,30 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests linux php --inner_jobs 16 -j 2 --internal_ci --max_time=3600"
+}
diff --git a/tools/internal_ci/linux/pull_request/grpc_basictests_python.cfg b/tools/internal_ci/linux/pull_request/grpc_basictests_python.cfg
new file mode 100644 (file)
index 0000000..b4e91c2
--- /dev/null
@@ -0,0 +1,30 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests linux python --inner_jobs 16 -j 2 --internal_ci --max_time=3600"
+}
diff --git a/tools/internal_ci/linux/pull_request/grpc_basictests_ruby.cfg b/tools/internal_ci/linux/pull_request/grpc_basictests_ruby.cfg
new file mode 100644 (file)
index 0000000..49d3dad
--- /dev/null
@@ -0,0 +1,30 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests linux ruby --inner_jobs 16 -j 2 --internal_ci --max_time=3600"
+}
diff --git a/tools/internal_ci/macos/grpc_basictests_c_cpp.cfg b/tools/internal_ci/macos/grpc_basictests_c_cpp.cfg
new file mode 100644 (file)
index 0000000..f16e3e8
--- /dev/null
@@ -0,0 +1,31 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
+timeout_mins: 120
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests macos corelang --internal_ci -j 1 --inner_jobs 4 --bq_result_table aggregate_results"
+}
diff --git a/tools/internal_ci/macos/grpc_basictests_csharp.cfg b/tools/internal_ci/macos/grpc_basictests_csharp.cfg
new file mode 100644 (file)
index 0000000..d3e04e7
--- /dev/null
@@ -0,0 +1,31 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests macos csharp --internal_ci -j 1 --inner_jobs 4 --bq_result_table aggregate_results"
+}
diff --git a/tools/internal_ci/macos/grpc_basictests_node.cfg b/tools/internal_ci/macos/grpc_basictests_node.cfg
new file mode 100644 (file)
index 0000000..9dfd6a7
--- /dev/null
@@ -0,0 +1,31 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests macos grpc-node --internal_ci -j 1 --inner_jobs 4 --bq_result_table aggregate_results"
+}
diff --git a/tools/internal_ci/macos/grpc_basictests_php.cfg b/tools/internal_ci/macos/grpc_basictests_php.cfg
new file mode 100644 (file)
index 0000000..091f68e
--- /dev/null
@@ -0,0 +1,31 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests macos php --internal_ci -j 1 --inner_jobs 4 --bq_result_table aggregate_results"
+}
diff --git a/tools/internal_ci/macos/grpc_basictests_python.cfg b/tools/internal_ci/macos/grpc_basictests_python.cfg
new file mode 100644 (file)
index 0000000..ae80ac3
--- /dev/null
@@ -0,0 +1,31 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests macos python --internal_ci -j 1 --inner_jobs 4 --bq_result_table aggregate_results"
+}
diff --git a/tools/internal_ci/macos/grpc_basictests_ruby.cfg b/tools/internal_ci/macos/grpc_basictests_ruby.cfg
new file mode 100644 (file)
index 0000000..3a28f97
--- /dev/null
@@ -0,0 +1,31 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests macos ruby --internal_ci -j 1 --inner_jobs 4 --bq_result_table aggregate_results"
+}
index ef02a67..56fae8f 100644 (file)
@@ -18,6 +18,13 @@ set -ex
 # change to grpc repo root
 cd $(dirname $0)/../../..
 
+# Download bazel
+temp_dir="$(mktemp -d)"
+wget -q https://github.com/bazelbuild/bazel/releases/download/0.23.2/bazel-0.23.2-darwin-x86_64 -O "${temp_dir}/bazel"
+chmod 755 "${temp_dir}/bazel"
+export PATH="${temp_dir}:${PATH}"
+# This should show ${temp_dir}/bazel
+which bazel
 
 ./tools/run_tests/start_port_server.py
 
diff --git a/tools/internal_ci/macos/pull_request/grpc_basictests_c_cpp.cfg b/tools/internal_ci/macos/pull_request/grpc_basictests_c_cpp.cfg
new file mode 100644 (file)
index 0000000..00f402d
--- /dev/null
@@ -0,0 +1,31 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
+timeout_mins: 120
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests macos corelang --internal_ci -j 1 --inner_jobs 4 --max_time=3600"
+}
diff --git a/tools/internal_ci/macos/pull_request/grpc_basictests_csharp.cfg b/tools/internal_ci/macos/pull_request/grpc_basictests_csharp.cfg
new file mode 100644 (file)
index 0000000..c9c1403
--- /dev/null
@@ -0,0 +1,31 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests macos csharp --internal_ci -j 1 --inner_jobs 4 --max_time=3600"
+}
diff --git a/tools/internal_ci/macos/pull_request/grpc_basictests_node.cfg b/tools/internal_ci/macos/pull_request/grpc_basictests_node.cfg
new file mode 100644 (file)
index 0000000..ed729ef
--- /dev/null
@@ -0,0 +1,31 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests macos grpc-node --internal_ci -j 1 --inner_jobs 4 --max_time=3600"
+}
diff --git a/tools/internal_ci/macos/pull_request/grpc_basictests_php.cfg b/tools/internal_ci/macos/pull_request/grpc_basictests_php.cfg
new file mode 100644 (file)
index 0000000..11b1787
--- /dev/null
@@ -0,0 +1,31 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests macos php --internal_ci -j 1 --inner_jobs 4 --max_time=3600"
+}
diff --git a/tools/internal_ci/macos/pull_request/grpc_basictests_python.cfg b/tools/internal_ci/macos/pull_request/grpc_basictests_python.cfg
new file mode 100644 (file)
index 0000000..c86871f
--- /dev/null
@@ -0,0 +1,31 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests macos python --internal_ci -j 1 --inner_jobs 4 --max_time=3600"
+}
diff --git a/tools/internal_ci/macos/pull_request/grpc_basictests_ruby.cfg b/tools/internal_ci/macos/pull_request/grpc_basictests_ruby.cfg
new file mode 100644 (file)
index 0000000..5729bf8
--- /dev/null
@@ -0,0 +1,31 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests macos ruby --internal_ci -j 1 --inner_jobs 4 --max_time=3600"
+}
diff --git a/tools/internal_ci/windows/bazel_rbe.bat b/tools/internal_ci/windows/bazel_rbe.bat
new file mode 100644 (file)
index 0000000..8f2c534
--- /dev/null
@@ -0,0 +1,31 @@
+@rem Copyright 2019 gRPC authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem     http://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+
+@rem TODO(jtattermusch): make this generate less output
+choco install bazel -y --version 0.23.2 --limit-output
+
+cd github/grpc
+set PATH=C:\tools\msys64\usr\bin;C:\Python27;%PATH%
+
+@rem Generate a random UUID and store in "bazel_invocation_ids" artifact file
+powershell -Command "[guid]::NewGuid().ToString()" >%KOKORO_ARTIFACTS_DIR%/bazel_invocation_ids
+set /p BAZEL_INVOCATION_ID=<%KOKORO_ARTIFACTS_DIR%/bazel_invocation_ids
+
+@rem TODO(jtattermusch): windows RBE should be able to use the same credentials as Linux RBE.
+bazel --bazelrc=tools/remote_build/windows.bazelrc build --invocation_id="%BAZEL_INVOCATION_ID%" --workspace_status_command=tools/remote_build/workspace_status_kokoro.sh :all --incompatible_disallow_filetype=false --google_credentials=%KOKORO_GFILE_DIR%/rbe-windows-credentials.json
+set BAZEL_EXITCODE=%errorlevel%
+
+@rem TODO(jtattermusch): upload results to bigquery
+
+exit /b %BAZEL_EXITCODE%
diff --git a/tools/internal_ci/windows/grpc_basictests_c.cfg b/tools/internal_ci/windows/grpc_basictests_c.cfg
new file mode 100644 (file)
index 0000000..223cf38
--- /dev/null
@@ -0,0 +1,30 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat"
+timeout_mins: 120
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests windows c -j 1 --inner_jobs 8 --internal_ci --bq_result_table aggregate_results"
+}
diff --git a/tools/internal_ci/windows/grpc_basictests_csharp.cfg b/tools/internal_ci/windows/grpc_basictests_csharp.cfg
new file mode 100644 (file)
index 0000000..17a362f
--- /dev/null
@@ -0,0 +1,30 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests windows csharp -j 1 --inner_jobs 8 --internal_ci --bq_result_table aggregate_results"
+}
diff --git a/tools/internal_ci/windows/grpc_basictests_python.cfg b/tools/internal_ci/windows/grpc_basictests_python.cfg
new file mode 100644 (file)
index 0000000..b0f6f67
--- /dev/null
@@ -0,0 +1,30 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests windows python -j 1 --inner_jobs 8 --internal_ci --bq_result_table aggregate_results"
+}
diff --git a/tools/internal_ci/windows/pull_request/grpc_basictests_c.cfg b/tools/internal_ci/windows/pull_request/grpc_basictests_c.cfg
new file mode 100644 (file)
index 0000000..4cedcae
--- /dev/null
@@ -0,0 +1,30 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat"
+timeout_mins: 120
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests windows c -j 1 --inner_jobs 8 --internal_ci --max_time=3600"
+}
diff --git a/tools/internal_ci/windows/pull_request/grpc_basictests_csharp.cfg b/tools/internal_ci/windows/pull_request/grpc_basictests_csharp.cfg
new file mode 100644 (file)
index 0000000..e1c5af1
--- /dev/null
@@ -0,0 +1,30 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests windows csharp -j 1 --inner_jobs 8 --internal_ci --max_time=3600"
+}
diff --git a/tools/internal_ci/windows/pull_request/grpc_basictests_python.cfg b/tools/internal_ci/windows/pull_request/grpc_basictests_python.cfg
new file mode 100644 (file)
index 0000000..6947e8e
--- /dev/null
@@ -0,0 +1,30 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests windows python -j 1 --inner_jobs 8 --internal_ci --max_time=3600"
+}
diff --git a/tools/internal_ci/windows/pull_request/grpc_bazel_rbe_dbg.cfg b/tools/internal_ci/windows/pull_request/grpc_bazel_rbe_dbg.cfg
new file mode 100644 (file)
index 0000000..f958fed
--- /dev/null
@@ -0,0 +1,32 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/windows/bazel_rbe.bat"
+
+timeout_mins: 60
+
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/resultstore_api_key"
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/rbe-windows-credentials.json"
+
+bazel_setting {
+  # In order for Kokoro to recognize this as a bazel build and publish the bazel resultstore link,
+  # the bazel_setting section needs to be present and "upsalite_frontend_address" needs to be
+  # set. The rest of configuration from bazel_setting is unused (we configure everything when bazel
+  # command is invoked).
+  upsalite_frontend_address: "https://source.cloud.google.com"
+}
index 654dfd6..234c712 100644 (file)
@@ -99,6 +99,8 @@ LANG_RELEASE_MATRIX = {
         ('v1.16.0', ReleaseInfo()),
         ('v1.17.1', ReleaseInfo()),
         ('v1.18.0', ReleaseInfo()),
+        ('v1.19.0', ReleaseInfo()),
+        ('v1.20.0', ReleaseInfo()),
     ]),
     'go':
     OrderedDict([
@@ -120,6 +122,8 @@ LANG_RELEASE_MATRIX = {
         ('v1.16.0', ReleaseInfo(runtime_subset=['go1.8'])),
         ('v1.17.0', ReleaseInfo(runtime_subset=['go1.11'])),
         ('v1.18.0', ReleaseInfo(runtime_subset=['go1.11'])),
+        ('v1.19.0', ReleaseInfo(runtime_subset=['go1.11'])),
+        ('v1.20.0', ReleaseInfo(runtime_subset=['go1.11'])),
     ]),
     'java':
     OrderedDict([
@@ -143,6 +147,7 @@ LANG_RELEASE_MATRIX = {
         ('v1.17.1', ReleaseInfo()),
         ('v1.18.0', ReleaseInfo()),
         ('v1.19.0', ReleaseInfo()),
+        ('v1.20.0', ReleaseInfo()),
     ]),
     'python':
     OrderedDict([
@@ -164,6 +169,8 @@ LANG_RELEASE_MATRIX = {
         ('v1.16.0', ReleaseInfo(testcases_file='python__v1.11.1')),
         ('v1.17.1', ReleaseInfo(testcases_file='python__v1.11.1')),
         ('v1.18.0', ReleaseInfo()),
+        ('v1.19.0', ReleaseInfo()),
+        ('v1.20.0', ReleaseInfo()),
     ]),
     'node':
     OrderedDict([
@@ -210,6 +217,9 @@ LANG_RELEASE_MATRIX = {
          ReleaseInfo(patch=[
              'tools/dockerfile/interoptest/grpc_interop_ruby/build_interop.sh',
          ])),
+        # TODO: https://github.com/grpc/grpc/issues/18262.
+        # If you are not encountering the error in above issue
+        # go ahead and upload the docker image for new releases.
     ]),
     'php':
     OrderedDict([
@@ -231,6 +241,8 @@ LANG_RELEASE_MATRIX = {
         ('v1.16.0', ReleaseInfo()),
         ('v1.17.1', ReleaseInfo()),
         ('v1.18.0', ReleaseInfo()),
+        # TODO:https://github.com/grpc/grpc/issues/18264
+        # Error in above issues needs to be resolved.
     ]),
     'csharp':
     OrderedDict([
@@ -257,6 +269,8 @@ LANG_RELEASE_MATRIX = {
         ('v1.15.0', ReleaseInfo(testcases_file='csharp__v1.3.9')),
         ('v1.16.0', ReleaseInfo(testcases_file='csharp__v1.3.9')),
         ('v1.17.1', ReleaseInfo(testcases_file='csharp__v1.3.9')),
-        ('v1.18.0', ReleaseInfo()),
+        ('v1.18.0', ReleaseInfo(testcases_file='csharp__v1.18.0')),
+        ('v1.19.0', ReleaseInfo(testcases_file='csharp__v1.18.0')),
+        ('v1.20.0', ReleaseInfo()),
     ]),
 }
index 9f1cd05..e526b3c 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/bash
 # DO NOT MODIFY
 # This file is generated by run_interop_tests.py/create_testcases.sh
-echo "Testing ${docker_image:=grpc_interop_csharp:71b05977-476b-4e57-9752-dd211c9e3741}"
+echo "Testing ${docker_image:=grpc_interop_csharp:9296f21a-f657-4bb0-82a7-24fc527abcbd}"
 docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=large_unary --use_tls=true"
 docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=empty_unary --use_tls=true"
 docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=ping_pong --use_tls=true"
diff --git a/tools/interop_matrix/testcases/csharp__v1.18.0 b/tools/interop_matrix/testcases/csharp__v1.18.0
new file mode 100644 (file)
index 0000000..34444c7
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+# DO NOT MODIFY
+# This file is generated by run_interop_tests.py/create_testcases.sh
+echo "Testing ${docker_image:=grpc_interop_csharp:71b05977-476b-4e57-9752-dd211c9e3741}"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=large_unary --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=empty_unary --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=ping_pong --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=empty_stream --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=client_streaming --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=server_streaming --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=cancel_after_begin --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=cancel_after_first_response --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=timeout_on_sleeping_server --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=large_unary --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=empty_unary --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=ping_pong --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=empty_stream --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=client_streaming --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=server_streaming --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=cancel_after_begin --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=cancel_after_first_response --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/net45 --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=timeout_on_sleeping_server --use_tls=true"
+
index 3ca145e..e14ec04 100755 (executable)
@@ -1,22 +1,22 @@
 #!/bin/bash
 # DO NOT MODIFY
 # This file is generated by run_interop_tests.py/create_testcases.sh
-echo "Testing ${docker_image:=grpc_interop_csharpcoreclr:bae17a7e-5450-4781-8982-e82cb89db6dd}"
-docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=large_unary --use_tls=true"
-docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=empty_unary --use_tls=true"
-docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=ping_pong --use_tls=true"
-docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=empty_stream --use_tls=true"
-docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=client_streaming --use_tls=true"
-docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=server_streaming --use_tls=true"
-docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=cancel_after_begin --use_tls=true"
-docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=cancel_after_first_response --use_tls=true"
-docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=timeout_on_sleeping_server --use_tls=true"
-docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=large_unary --use_tls=true"
-docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=empty_unary --use_tls=true"
-docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=ping_pong --use_tls=true"
-docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=empty_stream --use_tls=true"
-docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=client_streaming --use_tls=true"
-docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=server_streaming --use_tls=true"
-docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=cancel_after_begin --use_tls=true"
-docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=cancel_after_first_response --use_tls=true"
-docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=timeout_on_sleeping_server --use_tls=true"
+echo "Testing ${docker_image:=grpc_interop_csharpcoreclr:33395965-11d7-4b12-bcd6-a272d4015207}"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp2.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=large_unary --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp2.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=empty_unary --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp2.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=ping_pong --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp2.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=empty_stream --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp2.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=client_streaming --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp2.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=server_streaming --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp2.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=cancel_after_begin --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp2.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=cancel_after_first_response --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp2.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=timeout_on_sleeping_server --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp2.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=large_unary --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp2.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=empty_unary --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp2.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=ping_pong --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp2.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=empty_stream --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp2.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=client_streaming --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp2.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=server_streaming --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp2.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=cancel_after_begin --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp2.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=cancel_after_first_response --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp2.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=timeout_on_sleeping_server --use_tls=true"
diff --git a/tools/interop_matrix/testcases/csharpcoreclr__v1.18.0 b/tools/interop_matrix/testcases/csharpcoreclr__v1.18.0
new file mode 100755 (executable)
index 0000000..3ca145e
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/bash
+# DO NOT MODIFY
+# This file is generated by run_interop_tests.py/create_testcases.sh
+echo "Testing ${docker_image:=grpc_interop_csharpcoreclr:bae17a7e-5450-4781-8982-e82cb89db6dd}"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=large_unary --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=empty_unary --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=ping_pong --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=empty_stream --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=client_streaming --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=server_streaming --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=cancel_after_begin --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=cancel_after_first_response --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test.sandbox.googleapis.com --server_port=443 --test_case=timeout_on_sleeping_server --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=large_unary --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=empty_unary --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=ping_pong --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=empty_stream --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=client_streaming --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=server_streaming --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=cancel_after_begin --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=cancel_after_first_response --use_tls=true"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.1 --net=host $docker_image bash -c "dotnet exec Grpc.IntegrationTesting.Client.dll --server_host=grpc-test4.sandbox.googleapis.com --server_port=443 --test_case=timeout_on_sleeping_server --use_tls=true"
diff --git a/tools/release/release_notes.py b/tools/release/release_notes.py
new file mode 100644 (file)
index 0000000..46e0184
--- /dev/null
@@ -0,0 +1,369 @@
+#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.
+"""Generate draft and release notes in Markdown from Github PRs.
+
+You'll need a github API token to avoid being rate-limited. See
+https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/
+
+This script collects PRs using "git log X..Y" from local repo where X and Y are
+tags or release branch names of previous and current releases respectively.
+Typically, notes are generated before the release branch is labelled so Y is
+almost always the name of the release branch. X is the previous release branch
+if this is not a patch release. Otherwise, it is the previous release tag.
+For example, for release v1.17.0, X will be origin/v1.16.x and for release v1.17.3,
+X will be v1.17.2. In both cases Y will be origin/v1.17.x.
+
+"""
+
+from collections import defaultdict
+import base64
+import json
+
+content_header = """Draft Release Notes For {version}
+--
+Final release notes will be generated from the PR titles that have *"release notes:yes"* label. If you have any additional notes please add them below. These will be appended to auto generated release notes. Previous releases notes are [here](https://github.com/grpc/grpc/releases).
+
+**Also, look at the PRs listed below against your name.** Please apply the missing labels and make necessary corrections (like fixing the title) to the PR in Github. Final release notes will be generated just before the release on {date}.
+
+Add additional notes not in PRs
+--
+
+Core
+-
+
+
+C++
+-
+
+
+C#
+-
+
+
+Objective-C
+-
+
+
+PHP
+-
+
+
+Python
+-
+
+
+Ruby
+-
+
+
+"""
+
+rl_header = """This is the {version} release ([{name}](https://github.com/grpc/grpc/blob/master/doc/g_stands_for.md)) of gRPC Core.
+
+Please see the notes for the previous releases here: https://github.com/grpc/grpc/releases. Please consult https://grpc.io/ for all information regarding this product.
+
+This release contains refinements, improvements, and bug fixes, with highlights listed below.
+
+
+"""
+
+HTML_URL = "https://github.com/grpc/grpc/pull/"
+API_URL = 'https://api.github.com/repos/grpc/grpc/pulls/'
+
+
+def get_commit_log(prevRelLabel, relBranch):
+    """Return the output of 'git log --pretty=online --merges prevRelLabel..relBranch' """
+
+    import subprocess
+    print("Running git log --pretty=oneline --merges " + prevRelLabel + ".." +
+          relBranch)
+    return subprocess.check_output([
+        "git", "log", "--pretty=oneline", "--merges",
+        "%s..%s" % (prevRelLabel, relBranch)
+    ])
+
+
+def get_pr_data(pr_num):
+    """Get the PR data from github. Return 'error' on exception"""
+
+    try:
+        from urllib2 import Request, urlopen, HTTPError
+    except ImportError:
+        import urllib
+        from urllib.request import Request, urlopen, HTTPError
+    url = API_URL + pr_num
+    req = Request(url)
+    req.add_header('Authorization', 'token %s' % TOKEN)
+    try:
+        f = urlopen(req)
+        response = json.loads(f.read().decode('utf-8'))
+        #print(response)
+    except HTTPError as e:
+        response = json.loads(e.fp.read().decode('utf-8'))
+        if 'message' in response:
+            print(response['message'])
+        response = "error"
+    return response
+
+
+def get_pr_titles(gitLogs):
+    import re
+    error_count = 0
+    match = b"Merge pull request #(\d+)"
+    prlist = re.findall(match, gitLogs, re.MULTILINE)
+    print("\nPRs matching 'Merge pull request #<num>':")
+    print(prlist)
+    print("\n")
+    langs_pr = defaultdict(list)
+    for pr_num in prlist:
+        pr_num = str(pr_num)
+        print("---------- getting data for PR " + pr_num)
+        pr = get_pr_data(pr_num)
+        if pr == "error":
+            print("\n***ERROR*** Error in getting data for PR " + pr_num + "\n")
+            error_count += 1
+            continue
+        rl_no_found = False
+        rl_yes_found = False
+        lang_found = False
+        for label in pr['labels']:
+            if label['name'] == 'release notes: yes':
+                rl_yes_found = True
+            elif label['name'] == 'release notes: no':
+                rl_no_found = True
+            elif label['name'].startswith('lang/'):
+                lang_found = True
+                lang = label['name'].split('/')[1].lower()
+                #lang = lang[0].upper() + lang[1:]
+        body = pr["title"]
+        if not body.endswith("."):
+            body = body + "."
+        if not pr["merged_by"]:
+            print("\n***ERROR***: No merge_by found for PR " + pr_num + "\n")
+            error_count += 1
+            continue
+
+        prline = "-  " + body + " ([#" + pr_num + "](" + HTML_URL + pr_num + "))"
+        detail = "- " + pr["merged_by"]["login"] + "@ " + prline
+        prline = prline.encode('ascii', 'ignore')
+        detail = detail.encode('ascii', 'ignore')
+        print(detail)
+        #if no RL label
+        if not rl_no_found and not rl_yes_found:
+            print("Release notes label missing for " + pr_num)
+            langs_pr["nolabel"].append(detail)
+        elif rl_yes_found and not lang_found:
+            print("Lang label missing for " + pr_num)
+            langs_pr["nolang"].append(detail)
+        elif rl_no_found:
+            print("'Release notes:no' found for " + pr_num)
+            langs_pr["notinrel"].append(detail)
+        elif rl_yes_found:
+            print("'Release notes:yes' found for " + pr_num + " with lang " +
+                  lang)
+            langs_pr["inrel"].append(detail)
+            langs_pr[lang].append(prline)
+
+    return langs_pr, error_count
+
+
+def write_draft(langs_pr, file, version, date):
+    file.write(content_header.format(version=version, date=date))
+    file.write("PRs with missing release notes label - please fix in Github\n")
+    file.write("---\n")
+    file.write("\n")
+    if langs_pr["nolabel"]:
+        langs_pr["nolabel"].sort()
+        file.write("\n".join(langs_pr["nolabel"]))
+    else:
+        file.write("- None")
+    file.write("\n")
+    file.write("\n")
+    file.write("PRs with missing lang label - please fix in Github\n")
+    file.write("---\n")
+    file.write("\n")
+    if langs_pr["nolang"]:
+        langs_pr["nolang"].sort()
+        file.write("\n".join(langs_pr["nolang"]))
+    else:
+        file.write("- None")
+    file.write("\n")
+    file.write("\n")
+    file.write(
+        "PRs going into release notes - please check title and fix in Github. Do not edit here.\n"
+    )
+    file.write("---\n")
+    file.write("\n")
+    if langs_pr["inrel"]:
+        langs_pr["inrel"].sort()
+        file.write("\n".join(langs_pr["inrel"]))
+    else:
+        file.write("- None")
+    file.write("\n")
+    file.write("\n")
+    file.write("PRs not going into release notes\n")
+    file.write("---\n")
+    file.write("\n")
+    if langs_pr["notinrel"]:
+        langs_pr["notinrel"].sort()
+        file.write("\n".join(langs_pr["notinrel"]))
+    else:
+        file.write("- None")
+    file.write("\n")
+    file.write("\n")
+
+
+def write_rel_notes(langs_pr, file, version, name):
+    file.write(rl_header.format(version=version, name=name))
+    if langs_pr["core"]:
+        file.write("Core\n---\n\n")
+        file.write("\n".join(langs_pr["core"]))
+        file.write("\n")
+        file.write("\n")
+    if langs_pr["c++"]:
+        file.write("C++\n---\n\n")
+        file.write("\n".join(langs_pr["c++"]))
+        file.write("\n")
+        file.write("\n")
+    if langs_pr["c#"]:
+        file.write("C#\n---\n\n")
+        file.write("\n".join(langs_pr["c#"]))
+        file.write("\n")
+        file.write("\n")
+    if langs_pr["go"]:
+        file.write("Go\n---\n\n")
+        file.write("\n".join(langs_pr["go"]))
+        file.write("\n")
+        file.write("\n")
+    if langs_pr["Java"]:
+        file.write("Java\n---\n\n")
+        file.write("\n".join(langs_pr["Java"]))
+        file.write("\n")
+        file.write("\n")
+    if langs_pr["node"]:
+        file.write("Node\n---\n\n")
+        file.write("\n".join(langs_pr["node"]))
+        file.write("\n")
+        file.write("\n")
+    if langs_pr["objc"]:
+        file.write("Objective-C\n---\n\n")
+        file.write("\n".join(langs_pr["objc"]))
+        file.write("\n")
+        file.write("\n")
+    if langs_pr["php"]:
+        file.write("PHP\n---\n\n")
+        file.write("\n".join(langs_pr["php"]))
+        file.write("\n")
+        file.write("\n")
+    if langs_pr["python"]:
+        file.write("Python\n---\n\n")
+        file.write("\n".join(langs_pr["python"]))
+        file.write("\n")
+        file.write("\n")
+    if langs_pr["ruby"]:
+        file.write("Ruby\n---\n\n")
+        file.write("\n".join(langs_pr["ruby"]))
+        file.write("\n")
+        file.write("\n")
+    if langs_pr["other"]:
+        file.write("Other\n---\n\n")
+        file.write("\n".join(langs_pr["other"]))
+        file.write("\n")
+        file.write("\n")
+
+
+def build_args_parser():
+    import argparse
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        'release_version', type=str, help='New release version e.g. 1.14.0')
+    parser.add_argument(
+        'release_name', type=str, help='New release name e.g. gladiolus')
+    parser.add_argument(
+        'release_date', type=str, help='Release date e.g. 7/30/18')
+    parser.add_argument(
+        'previous_release_label',
+        type=str,
+        help='Previous release branch/tag e.g. v1.13.x')
+    parser.add_argument(
+        'release_branch',
+        type=str,
+        help='Current release branch e.g. origin/v1.14.x')
+    parser.add_argument(
+        'draft_filename', type=str, help='Name of the draft file e.g. draft.md')
+    parser.add_argument(
+        'release_notes_filename',
+        type=str,
+        help='Name of the release notes file e.g. relnotes.md')
+    parser.add_argument(
+        '--token',
+        type=str,
+        default='',
+        help='GitHub API token to avoid being rate limited')
+    return parser
+
+
+def main():
+    import os
+    global TOKEN
+
+    parser = build_args_parser()
+    args = parser.parse_args()
+    version, name, date = args.release_version, args.release_name, args.release_date
+    start, end = args.previous_release_label, args.release_branch
+
+    TOKEN = args.token
+    if TOKEN == '':
+        try:
+            TOKEN = os.environ["GITHUB_TOKEN"]
+        except:
+            pass
+    if TOKEN == '':
+        print(
+            "Error: Github API token required. Either include param --token=<your github token> or set environment variable GITHUB_TOKEN to your github token"
+        )
+        return
+
+    langs_pr, error_count = get_pr_titles(get_commit_log(start, end))
+
+    draft_file, rel_file = args.draft_filename, args.release_notes_filename
+    filename = os.path.abspath(draft_file)
+    if os.path.exists(filename):
+        file = open(filename, 'r+')
+    else:
+        file = open(filename, 'w')
+
+    file.seek(0)
+    write_draft(langs_pr, file, version, date)
+    file.truncate()
+    file.close()
+    print("\nDraft notes written to " + filename)
+
+    filename = os.path.abspath(rel_file)
+    if os.path.exists(filename):
+        file = open(filename, 'r+')
+    else:
+        file = open(filename, 'w')
+
+    file.seek(0)
+    write_rel_notes(langs_pr, file, version, name)
+    file.truncate()
+    file.close()
+    print("\nRelease notes written to " + filename)
+    if error_count > 0:
+        print("\n\n*** Errors were encountered. See log. *********\n")
+
+
+if __name__ == "__main__":
+    main()
index bb27dc7..2cd5f03 100644 (file)
@@ -31,8 +31,8 @@ bazel --bazelrc=tools/remote_build/manual.bazelrc test --config=asan //test/...
 
 Run on Windows MSVC:
 ```
-# local manual run only for C++ targets (RBE to be supported)
-bazel --bazelrc=tools/remote_build/windows.bazelrc test //test/cpp/...
+# RBE manual run only for c-core (must be run on a Windows host machine)
+bazel --bazelrc=tools/remote_build/windows.bazelrc build :all [--credentials_json=(path to service account credentials)]
 ```
 
 Available command line options can be found in
index 2fbdd3c..064e94b 100644 (file)
@@ -21,9 +21,6 @@ build --remote_executor=remotebuildexecution.googleapis.com
 build --tls_enabled=true
 
 build --auth_enabled=true
-# magic location where kokoro script puts the credentials
-build --auth_credentials=/tmpfs/src/keystore/4321_grpc-testing-service
-build --auth_scope=https://www.googleapis.com/auth/cloud-source-tools
 
 build --bes_backend=buildeventservice.googleapis.com
 build --bes_timeout=600s
index 3438b18..69394fe 100644 (file)
@@ -19,8 +19,8 @@
 
 startup --host_jvm_args=-Dbazel.DigestFunction=SHA256
 
-build --crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/ubuntu16_04_clang/1.1/bazel_0.20.0/default:toolchain
-build --extra_toolchains=//third_party/toolchains:cc-toolchain-clang-x86_64-default
+build --crosstool_top=@rbe_default//cc:toolchain
+build --extra_toolchains=@rbe_default//config:cc-toolchain
 # Use custom execution platforms defined in third_party/toolchains
 build --extra_execution_platforms=//third_party/toolchains:rbe_ubuntu1604,//third_party/toolchains:rbe_ubuntu1604_large
 build --host_platform=//third_party/toolchains:rbe_ubuntu1604
@@ -66,9 +66,9 @@ build:msan --cxxopt=--stdlib=libc++
 # setting LD_LIBRARY_PATH is necessary
 # to avoid "libc++.so.1: cannot open shared object file"
 build:msan --action_env=LD_LIBRARY_PATH=/usr/local/lib
-build:msan --host_crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/ubuntu16_04_clang/1.1/bazel_0.20.0/default:toolchain
+build:msan --host_crosstool_top=@rbe_default//cc:toolchain
 # override the config-agnostic crosstool_top
-build:msan --crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/ubuntu16_04_clang/1.1/bazel_0.20.0/msan:toolchain
+build:msan --crosstool_top=@rbe_msan//cc:toolchain
 
 # thread sanitizer: most settings are already in %workspace%/.bazelrc
 # we only need a few additional ones that are Foundry specific
@@ -84,8 +84,4 @@ build:ubsan --copt=-gmlt
 # TODO(jtattermusch): use more reasonable test timeout
 build:ubsan --test_timeout=3600
 # override the config-agnostic crosstool_top
---crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/experimental/ubuntu16_04_clang/1.1/bazel_0.20.0/ubsan:toolchain
-# TODO(jtattermusch): remove this once Foundry adds the env to the docker image.
-# ubsan needs symbolizer to work properly, otherwise the suppression file doesn't work
-# and we get test failures.
-build:ubsan --action_env=UBSAN_SYMBOLIZER_PATH=/usr/local/bin/llvm-symbolizer
+build:ubsan --crosstool_top=@bazel_toolchains//configs/experimental/ubuntu16_04_clang/1.2/bazel_0.21.0/ubsan:toolchain
index 7057537..492489f 100644 (file)
@@ -1,3 +1,49 @@
-# TODO(yfen): Merge with rbe_common.bazelrc and enable Windows RBE
+startup --host_jvm_args=-Dbazel.DigestFunction=SHA256
+
+build --remote_cache=remotebuildexecution.googleapis.com
+build --remote_executor=remotebuildexecution.googleapis.com
+build --tls_enabled=true
+
+build --host_crosstool_top=//third_party/toolchains/bazel_0.23.2_rbe_windows:toolchain
+build --crosstool_top=//third_party/toolchains/bazel_0.23.2_rbe_windows:toolchain
+build --extra_toolchains=//third_party/toolchains/bazel_0.23.2_rbe_windows:cc-toolchain-x64_windows
+# Use custom execution platforms defined in third_party/toolchains
+build --extra_execution_platforms=//third_party/toolchains:rbe_windows
+build --host_platform=//third_party/toolchains:rbe_windows
+build --platforms=//third_party/toolchains:rbe_windows
+
+build --shell_executable=C:\\tools\\msys64\\usr\\bin\\bash.exe
+build --python_path=C:\\Python27\\python.exe
+
+build --spawn_strategy=remote
+build --strategy=Javac=remote
+build --strategy=Closure=remote
+build --genrule_strategy=remote
+build --remote_timeout=3600
+
+build --remote_instance_name=projects/grpc-testing/instances/grpc-windows-rbe-test
+
+build --verbose_failures=true
+
+build --experimental_strict_action_env=true
+build --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
+
+# don't use port server
+build --define GRPC_PORT_ISOLATED_RUNTIME=1
 build --test_tag_filters=-no_windows
 build --build_tag_filters=-no_windows
+
+# without verbose gRPC logs the test outputs are not very useful
+test --test_env=GRPC_VERBOSITY=debug
+
+# Set flags for uploading to BES in order to view results in the Bazel Build
+# Results UI.
+build --bes_backend="buildeventservice.googleapis.com"
+build --bes_timeout=60s
+build --bes_results_url="https://source.cloud.google.com/results/invocations/"
+build --project_id=grpc-testing
+
+build --jobs=30
+
+# print output for tests that fail (default is "summary")
+build --test_output=errors
index 34d9e31..39ee76b 100644 (file)
       "gpr", 
       "grpc", 
       "grpc++", 
+      "grpc++_test_config", 
       "grpc++_test_util", 
       "grpc_test_util"
     ], 
   {
     "deps": [
       "gpr", 
+      "grpc_test_util_unsecure"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "global_config_env_test", 
+    "src": [
+      "test/core/gprpp/global_config_env_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "grpc_test_util_unsecure"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "global_config_test", 
+    "src": [
+      "test/core/gprpp/global_config_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
       "grpc", 
-      "grpc++"
+      "grpc++", 
+      "grpc++_test_config"
     ], 
     "headers": [
       "src/proto/grpc/testing/compiler_test.grpc.pb.h", 
   }, 
   {
     "deps": [
+      "gpr", 
+      "grpc", 
+      "grpc++", 
+      "grpc++_test", 
+      "grpc_test_util"
+    ], 
+    "headers": [
+      "test/core/gprpp/map_tester.h"
+    ], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "grpc_core_map_test", 
+    "src": [
+      "test/core/gprpp/map_test.cc", 
+      "test/core/gprpp/map_tester.h"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
       "grpc_plugin_support"
     ], 
     "headers": [], 
       "gpr", 
       "grpc", 
       "grpc++", 
+      "grpc++_test_util", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "message_allocator_end2end_test", 
+    "src": [
+      "test/cpp/end2end/message_allocator_end2end_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "grpc", 
+      "grpc++", 
       "grpc++_test_config"
     ], 
     "headers": [
       "gpr", 
       "grpc", 
       "grpc++", 
+      "grpc++_test_config", 
       "grpc++_test_util", 
       "grpc_test_util"
     ], 
     "headers": [], 
     "is_filegroup": false, 
     "language": "c++", 
+    "name": "service_config_end2end_test", 
+    "src": [
+      "test/cpp/end2end/service_config_end2end_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "grpc", 
+      "grpc++", 
+      "grpc++_test", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "service_config_test", 
+    "src": [
+      "test/core/client_channel/service_config_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "grpc", 
+      "grpc++", 
+      "grpc++_test_util", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
     "name": "shutdown_test", 
     "src": [
       "test/cpp/end2end/shutdown_test.cc"
     "headers": [], 
     "is_filegroup": false, 
     "language": "c", 
+    "name": "h2_ssl_cred_reload_test", 
+    "src": [
+      "test/core/end2end/fixtures/h2_ssl_cred_reload.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "end2end_tests", 
+      "gpr", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
     "name": "h2_ssl_proxy_test", 
     "src": [
       "test/core/end2end/fixtures/h2_ssl_proxy.cc"
   }, 
   {
     "deps": [
+      "dns_test_util", 
       "gpr", 
       "grpc++_test_config", 
       "grpc++_test_util_unsecure", 
   }, 
   {
     "deps": [
+      "dns_test_util", 
       "gpr", 
       "grpc", 
       "grpc++", 
   }, 
   {
     "deps": [
+      "dns_test_util", 
       "gpr", 
       "grpc", 
       "grpc++", 
     "type": "lib"
   }, 
   {
+    "deps": [], 
+    "headers": [
+      "test/cpp/naming/dns_test_util.h"
+    ], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "dns_test_util", 
+    "src": [
+      "test/cpp/naming/dns_test_util.cc", 
+      "test/cpp/naming/dns_test_util.h"
+    ], 
+    "third_party": false, 
+    "type": "lib"
+  }, 
+  {
     "deps": [
       "gpr", 
       "grpc", 
     "headers": [
       "include/grpc++/support/error_details.h", 
       "include/grpcpp/support/error_details.h", 
+      "include/grpcpp/support/error_details_impl.h", 
       "src/proto/grpc/status/status.grpc.pb.h", 
       "src/proto/grpc/status/status.pb.h", 
       "src/proto/grpc/status/status_mock.grpc.pb.h"
     "src": [
       "include/grpc++/support/error_details.h", 
       "include/grpcpp/support/error_details.h", 
+      "include/grpcpp/support/error_details_impl.h", 
       "src/cpp/util/error_details.cc"
     ], 
     "third_party": false, 
     "headers": [
       "include/grpc++/ext/proto_server_reflection_plugin.h", 
       "include/grpcpp/ext/proto_server_reflection_plugin.h", 
+      "include/grpcpp/ext/proto_server_reflection_plugin_impl.h", 
       "src/cpp/ext/proto_server_reflection.h"
     ], 
     "is_filegroup": false, 
     "src": [
       "include/grpc++/ext/proto_server_reflection_plugin.h", 
       "include/grpcpp/ext/proto_server_reflection_plugin.h", 
+      "include/grpcpp/ext/proto_server_reflection_plugin_impl.h", 
       "src/cpp/ext/proto_server_reflection.cc", 
       "src/cpp/ext/proto_server_reflection.h", 
       "src/cpp/ext/proto_server_reflection_plugin.cc"
     "name": "gpr_base", 
     "src": [
       "src/core/lib/gpr/alloc.cc", 
-      "src/core/lib/gpr/arena.cc", 
       "src/core/lib/gpr/atm.cc", 
       "src/core/lib/gpr/cpu_iphone.cc", 
       "src/core/lib/gpr/cpu_linux.cc", 
       "src/core/lib/gpr/tmpfile_posix.cc", 
       "src/core/lib/gpr/tmpfile_windows.cc", 
       "src/core/lib/gpr/wrap_memcpy.cc", 
+      "src/core/lib/gprpp/arena.cc", 
       "src/core/lib/gprpp/fork.cc", 
+      "src/core/lib/gprpp/global_config_env.cc", 
       "src/core/lib/gprpp/thd_posix.cc", 
       "src/core/lib/gprpp/thd_windows.cc", 
       "src/core/lib/profiling/basic_timers.cc", 
       "src/core/lib/gpr/tmpfile.h", 
       "src/core/lib/gpr/useful.h", 
       "src/core/lib/gprpp/abstract.h", 
+      "src/core/lib/gprpp/arena.h", 
       "src/core/lib/gprpp/atomic.h", 
       "src/core/lib/gprpp/fork.h", 
+      "src/core/lib/gprpp/global_config.h", 
+      "src/core/lib/gprpp/global_config_custom.h", 
+      "src/core/lib/gprpp/global_config_env.h", 
+      "src/core/lib/gprpp/global_config_generic.h", 
       "src/core/lib/gprpp/manual_constructor.h", 
+      "src/core/lib/gprpp/map.h", 
       "src/core/lib/gprpp/memory.h", 
-      "src/core/lib/gprpp/mutex_lock.h", 
+      "src/core/lib/gprpp/pair.h", 
+      "src/core/lib/gprpp/sync.h", 
       "src/core/lib/gprpp/thd.h", 
       "src/core/lib/profiling/timers.h"
     ], 
       "src/core/lib/gpr/tmpfile.h", 
       "src/core/lib/gpr/useful.h", 
       "src/core/lib/gprpp/abstract.h", 
+      "src/core/lib/gprpp/arena.h", 
       "src/core/lib/gprpp/atomic.h", 
       "src/core/lib/gprpp/fork.h", 
+      "src/core/lib/gprpp/global_config.h", 
+      "src/core/lib/gprpp/global_config_custom.h", 
+      "src/core/lib/gprpp/global_config_env.h", 
+      "src/core/lib/gprpp/global_config_generic.h", 
       "src/core/lib/gprpp/manual_constructor.h", 
+      "src/core/lib/gprpp/map.h", 
       "src/core/lib/gprpp/memory.h", 
-      "src/core/lib/gprpp/mutex_lock.h", 
+      "src/core/lib/gprpp/pair.h", 
+      "src/core/lib/gprpp/sync.h", 
       "src/core/lib/gprpp/thd.h", 
       "src/core/lib/profiling/timers.h"
     ], 
       "src/core/lib/channel/handshaker_registry.cc", 
       "src/core/lib/channel/status_util.cc", 
       "src/core/lib/compression/compression.cc", 
+      "src/core/lib/compression/compression_args.cc", 
       "src/core/lib/compression/compression_internal.cc", 
       "src/core/lib/compression/message_compress.cc", 
       "src/core/lib/compression/stream_compression.cc", 
       "src/core/lib/http/parser.cc", 
       "src/core/lib/iomgr/buffer_list.cc", 
       "src/core/lib/iomgr/call_combiner.cc", 
+      "src/core/lib/iomgr/cfstream_handle.cc", 
       "src/core/lib/iomgr/combiner.cc", 
       "src/core/lib/iomgr/endpoint.cc", 
+      "src/core/lib/iomgr/endpoint_cfstream.cc", 
       "src/core/lib/iomgr/endpoint_pair_posix.cc", 
       "src/core/lib/iomgr/endpoint_pair_uv.cc", 
       "src/core/lib/iomgr/endpoint_pair_windows.cc", 
       "src/core/lib/iomgr/error.cc", 
+      "src/core/lib/iomgr/error_cfstream.cc", 
       "src/core/lib/iomgr/ev_epoll1_linux.cc", 
       "src/core/lib/iomgr/ev_epollex_linux.cc", 
       "src/core/lib/iomgr/ev_poll_posix.cc", 
       "src/core/lib/iomgr/iomgr_custom.cc", 
       "src/core/lib/iomgr/iomgr_internal.cc", 
       "src/core/lib/iomgr/iomgr_posix.cc", 
+      "src/core/lib/iomgr/iomgr_posix_cfstream.cc", 
       "src/core/lib/iomgr/iomgr_uv.cc", 
       "src/core/lib/iomgr/iomgr_windows.cc", 
       "src/core/lib/iomgr/is_epollexclusive_available.cc", 
       "src/core/lib/iomgr/socket_utils_windows.cc", 
       "src/core/lib/iomgr/socket_windows.cc", 
       "src/core/lib/iomgr/tcp_client.cc", 
+      "src/core/lib/iomgr/tcp_client_cfstream.cc", 
       "src/core/lib/iomgr/tcp_client_custom.cc", 
       "src/core/lib/iomgr/tcp_client_posix.cc", 
       "src/core/lib/iomgr/tcp_client_windows.cc", 
       "src/core/lib/channel/handshaker_registry.h", 
       "src/core/lib/channel/status_util.h", 
       "src/core/lib/compression/algorithm_metadata.h", 
+      "src/core/lib/compression/compression_args.h", 
       "src/core/lib/compression/compression_internal.h", 
       "src/core/lib/compression/message_compress.h", 
       "src/core/lib/compression/stream_compression.h", 
       "src/core/lib/iomgr/block_annotate.h", 
       "src/core/lib/iomgr/buffer_list.h", 
       "src/core/lib/iomgr/call_combiner.h", 
+      "src/core/lib/iomgr/cfstream_handle.h", 
       "src/core/lib/iomgr/closure.h", 
       "src/core/lib/iomgr/combiner.h", 
       "src/core/lib/iomgr/dynamic_annotations.h", 
       "src/core/lib/iomgr/endpoint.h", 
+      "src/core/lib/iomgr/endpoint_cfstream.h", 
       "src/core/lib/iomgr/endpoint_pair.h", 
       "src/core/lib/iomgr/error.h", 
+      "src/core/lib/iomgr/error_cfstream.h", 
       "src/core/lib/iomgr/error_internal.h", 
       "src/core/lib/iomgr/ev_epoll1_linux.h", 
       "src/core/lib/iomgr/ev_epollex_linux.h", 
       "src/core/lib/channel/handshaker_registry.h", 
       "src/core/lib/channel/status_util.h", 
       "src/core/lib/compression/algorithm_metadata.h", 
+      "src/core/lib/compression/compression_args.h", 
       "src/core/lib/compression/compression_internal.h", 
       "src/core/lib/compression/message_compress.h", 
       "src/core/lib/compression/stream_compression.h", 
       "src/core/lib/iomgr/block_annotate.h", 
       "src/core/lib/iomgr/buffer_list.h", 
       "src/core/lib/iomgr/call_combiner.h", 
+      "src/core/lib/iomgr/cfstream_handle.h", 
       "src/core/lib/iomgr/closure.h", 
       "src/core/lib/iomgr/combiner.h", 
       "src/core/lib/iomgr/dynamic_annotations.h", 
       "src/core/lib/iomgr/endpoint.h", 
+      "src/core/lib/iomgr/endpoint_cfstream.h", 
       "src/core/lib/iomgr/endpoint_pair.h", 
       "src/core/lib/iomgr/error.h", 
+      "src/core/lib/iomgr/error_cfstream.h", 
       "src/core/lib/iomgr/error_internal.h", 
       "src/core/lib/iomgr/ev_epoll1_linux.h", 
       "src/core/lib/iomgr/ev_epollex_linux.h", 
   {
     "deps": [
       "gpr", 
-      "gpr_base_headers", 
-      "grpc_base_headers"
-    ], 
-    "headers": [
-      "src/core/lib/iomgr/cfstream_handle.h", 
-      "src/core/lib/iomgr/endpoint_cfstream.h", 
-      "src/core/lib/iomgr/error_cfstream.h"
-    ], 
-    "is_filegroup": true, 
-    "language": "c", 
-    "name": "grpc_cfstream", 
-    "src": [
-      "src/core/lib/iomgr/cfstream_handle.cc", 
-      "src/core/lib/iomgr/cfstream_handle.h", 
-      "src/core/lib/iomgr/endpoint_cfstream.cc", 
-      "src/core/lib/iomgr/endpoint_cfstream.h", 
-      "src/core/lib/iomgr/error_cfstream.cc", 
-      "src/core/lib/iomgr/error_cfstream.h", 
-      "src/core/lib/iomgr/iomgr_posix_cfstream.cc", 
-      "src/core/lib/iomgr/tcp_client_cfstream.cc"
-    ], 
-    "third_party": false, 
-    "type": "filegroup"
-  }, 
-  {
-    "deps": [
-      "gpr", 
       "grpc_base"
     ], 
     "headers": [
     "deps": [
       "gpr", 
       "grpc_base", 
-      "grpc_client_channel"
+      "grpc_client_channel", 
+      "grpc_resolver_dns_selection"
     ], 
     "headers": [
       "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h", 
       "src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc", 
       "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc", 
       "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h", 
+      "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc", 
       "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc", 
       "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc", 
       "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc", 
       "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h", 
       "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc", 
+      "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc", 
       "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc", 
       "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc"
     ], 
     "deps": [
       "gpr", 
       "grpc_base", 
-      "grpc_client_channel"
+      "grpc_client_channel", 
+      "grpc_resolver_dns_selection"
     ], 
     "headers": [], 
     "is_filegroup": true, 
   {
     "deps": [
       "gpr", 
+      "grpc_base"
+    ], 
+    "headers": [
+      "src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h"
+    ], 
+    "is_filegroup": true, 
+    "language": "c", 
+    "name": "grpc_resolver_dns_selection", 
+    "src": [
+      "src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc", 
+      "src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h"
+    ], 
+    "third_party": false, 
+    "type": "filegroup"
+  }, 
+  {
+    "deps": [
+      "gpr", 
       "grpc_base", 
       "grpc_client_channel"
     ], 
   }, 
   {
     "deps": [
+      "grpc++_internal_hdrs_only", 
       "grpc_codegen"
     ], 
     "headers": [
       "include/grpcpp/impl/codegen/client_interceptor.h", 
       "include/grpcpp/impl/codegen/client_unary_call.h", 
       "include/grpcpp/impl/codegen/completion_queue.h", 
+      "include/grpcpp/impl/codegen/completion_queue_impl.h", 
       "include/grpcpp/impl/codegen/completion_queue_tag.h", 
       "include/grpcpp/impl/codegen/config.h", 
       "include/grpcpp/impl/codegen/core_codegen_interface.h", 
       "include/grpcpp/impl/codegen/intercepted_channel.h", 
       "include/grpcpp/impl/codegen/interceptor.h", 
       "include/grpcpp/impl/codegen/interceptor_common.h", 
+      "include/grpcpp/impl/codegen/message_allocator.h", 
       "include/grpcpp/impl/codegen/metadata_map.h", 
       "include/grpcpp/impl/codegen/method_handler_impl.h", 
       "include/grpcpp/impl/codegen/rpc_method.h", 
       "include/grpcpp/impl/codegen/client_interceptor.h", 
       "include/grpcpp/impl/codegen/client_unary_call.h", 
       "include/grpcpp/impl/codegen/completion_queue.h", 
+      "include/grpcpp/impl/codegen/completion_queue_impl.h", 
       "include/grpcpp/impl/codegen/completion_queue_tag.h", 
       "include/grpcpp/impl/codegen/config.h", 
       "include/grpcpp/impl/codegen/core_codegen_interface.h", 
       "include/grpcpp/impl/codegen/intercepted_channel.h", 
       "include/grpcpp/impl/codegen/interceptor.h", 
       "include/grpcpp/impl/codegen/interceptor_common.h", 
+      "include/grpcpp/impl/codegen/message_allocator.h", 
       "include/grpcpp/impl/codegen/metadata_map.h", 
       "include/grpcpp/impl/codegen/method_handler_impl.h", 
       "include/grpcpp/impl/codegen/rpc_method.h", 
       "gpr", 
       "gpr_base_headers", 
       "grpc++_codegen_base", 
+      "grpc++_internal_hdrs_only", 
       "grpc_base_headers", 
       "grpc_transport_inproc_headers", 
       "health_proto", 
       "include/grpcpp/alarm.h", 
       "include/grpcpp/alarm_impl.h", 
       "include/grpcpp/channel.h", 
+      "include/grpcpp/channel_impl.h", 
       "include/grpcpp/client_context.h", 
       "include/grpcpp/completion_queue.h", 
       "include/grpcpp/create_channel.h", 
+      "include/grpcpp/create_channel_impl.h", 
       "include/grpcpp/create_channel_posix.h", 
       "include/grpcpp/create_channel_posix_impl.h", 
       "include/grpcpp/ext/health_check_service_server_builder_option.h", 
       "include/grpcpp/generic/async_generic_service.h", 
       "include/grpcpp/generic/generic_stub.h", 
+      "include/grpcpp/generic/generic_stub_impl.h", 
       "include/grpcpp/grpcpp.h", 
       "include/grpcpp/health_check_service_interface.h", 
+      "include/grpcpp/health_check_service_interface_impl.h", 
       "include/grpcpp/impl/call.h", 
       "include/grpcpp/impl/channel_argument_option.h", 
       "include/grpcpp/impl/client_unary_call.h", 
       "include/grpcpp/impl/server_builder_option_impl.h", 
       "include/grpcpp/impl/server_builder_plugin.h", 
       "include/grpcpp/impl/server_initializer.h", 
+      "include/grpcpp/impl/server_initializer_impl.h", 
       "include/grpcpp/impl/service_type.h", 
       "include/grpcpp/resource_quota.h", 
+      "include/grpcpp/resource_quota_impl.h", 
       "include/grpcpp/security/auth_context.h", 
       "include/grpcpp/security/auth_metadata_processor.h", 
+      "include/grpcpp/security/auth_metadata_processor_impl.h", 
       "include/grpcpp/security/credentials.h", 
+      "include/grpcpp/security/credentials_impl.h", 
       "include/grpcpp/security/server_credentials.h", 
+      "include/grpcpp/security/server_credentials_impl.h", 
       "include/grpcpp/server.h", 
       "include/grpcpp/server_builder.h", 
+      "include/grpcpp/server_builder_impl.h", 
       "include/grpcpp/server_context.h", 
+      "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_unary_call.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_interceptor.h", 
       "include/grpcpp/support/config.h", 
       "include/grpcpp/support/interceptor.h", 
+      "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/alarm.h", 
       "include/grpcpp/alarm_impl.h", 
       "include/grpcpp/channel.h", 
+      "include/grpcpp/channel_impl.h", 
       "include/grpcpp/client_context.h", 
       "include/grpcpp/completion_queue.h", 
       "include/grpcpp/create_channel.h", 
+      "include/grpcpp/create_channel_impl.h", 
       "include/grpcpp/create_channel_posix.h", 
       "include/grpcpp/create_channel_posix_impl.h", 
       "include/grpcpp/ext/health_check_service_server_builder_option.h", 
       "include/grpcpp/generic/async_generic_service.h", 
       "include/grpcpp/generic/generic_stub.h", 
+      "include/grpcpp/generic/generic_stub_impl.h", 
       "include/grpcpp/grpcpp.h", 
       "include/grpcpp/health_check_service_interface.h", 
+      "include/grpcpp/health_check_service_interface_impl.h", 
       "include/grpcpp/impl/call.h", 
       "include/grpcpp/impl/channel_argument_option.h", 
       "include/grpcpp/impl/client_unary_call.h", 
       "include/grpcpp/impl/server_builder_option_impl.h", 
       "include/grpcpp/impl/server_builder_plugin.h", 
       "include/grpcpp/impl/server_initializer.h", 
+      "include/grpcpp/impl/server_initializer_impl.h", 
       "include/grpcpp/impl/service_type.h", 
       "include/grpcpp/resource_quota.h", 
+      "include/grpcpp/resource_quota_impl.h", 
       "include/grpcpp/security/auth_context.h", 
       "include/grpcpp/security/auth_metadata_processor.h", 
+      "include/grpcpp/security/auth_metadata_processor_impl.h", 
       "include/grpcpp/security/credentials.h", 
+      "include/grpcpp/security/credentials_impl.h", 
       "include/grpcpp/security/server_credentials.h", 
+      "include/grpcpp/security/server_credentials_impl.h", 
       "include/grpcpp/server.h", 
       "include/grpcpp/server_builder.h", 
+      "include/grpcpp/server_builder_impl.h", 
       "include/grpcpp/server_context.h", 
+      "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_unary_call.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_interceptor.h", 
       "include/grpcpp/support/config.h", 
       "include/grpcpp/support/interceptor.h", 
+      "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", 
   {
     "deps": [], 
     "headers": [
+      "include/grpcpp/impl/codegen/sync.h"
+    ], 
+    "is_filegroup": true, 
+    "language": "c++", 
+    "name": "grpc++_internal_hdrs_only", 
+    "src": [
+      "include/grpcpp/impl/codegen/sync.h"
+    ], 
+    "third_party": false, 
+    "type": "filegroup"
+  }, 
+  {
+    "deps": [], 
+    "headers": [
       "src/proto/grpc/reflection/v1alpha/reflection.grpc.pb.h", 
       "src/proto/grpc/reflection/v1alpha/reflection.pb.h", 
       "src/proto/grpc/reflection/v1alpha/reflection_mock.grpc.pb.h"
index cf0e574..c156c83 100644 (file)
     "uses_polling": true
   }, 
   {
-    "args": [
-      "--generated_file_path=gens/src/proto/grpc/testing/"
-    ], 
+    "args": [], 
     "benchmark": false, 
     "ci_platforms": [
       "linux", 
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
-    "gtest": true, 
+    "gtest": false, 
     "language": "c++", 
-    "name": "golden_file_test", 
+    "name": "global_config_env_test", 
     "platforms": [
       "linux", 
       "mac", 
     "flaky": false, 
     "gtest": false, 
     "language": "c++", 
-    "name": "grpc_alts_credentials_options_test", 
+    "name": "global_config_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "uses_polling": true
+    "uses_polling": false
   }, 
   {
-    "args": [], 
-    "benchmark": false, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "gtest": true, 
-    "language": "c++", 
-    "name": "grpc_linux_system_roots_test", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
+    "args": [
+      "--generated_file_path=gens/src/proto/grpc/testing/"
     ], 
-    "uses_polling": true
-  }, 
-  {
-    "args": [], 
     "benchmark": false, 
     "ci_platforms": [
       "linux", 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "grpc_tool_test", 
+    "name": "golden_file_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "uses_polling": true
+    "uses_polling": false
   }, 
   {
     "args": [], 
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
-    "gtest": true, 
+    "gtest": false, 
     "language": "c++", 
-    "name": "grpclb_api_test", 
+    "name": "grpc_alts_credentials_options_test", 
     "platforms": [
       "linux", 
       "mac", 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "grpclb_end2end_test", 
+    "name": "grpc_core_map_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "uses_polling": true
+    "uses_polling": false
   }, 
   {
     "args": [], 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "h2_ssl_cert_test", 
+    "name": "grpc_linux_system_roots_test", 
     "platforms": [
       "linux", 
       "mac", 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "h2_ssl_session_reuse_test", 
+    "name": "grpc_tool_test", 
     "platforms": [
       "linux", 
       "mac", 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "health_service_end2end_test", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "uses_polling": true
-  }, 
-  {
-    "args": [], 
-    "benchmark": false, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": true, 
-    "gtest": false, 
-    "language": "c++", 
-    "name": "hybrid_end2end_test", 
+    "name": "grpclb_api_test", 
     "platforms": [
       "linux", 
       "mac", 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "inlined_vector_test", 
+    "name": "grpclb_end2end_test", 
     "platforms": [
       "linux", 
       "mac", 
     "ci_platforms": [
       "linux", 
       "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "gtest": false, 
-    "language": "c++", 
-    "name": "inproc_sync_unary_ping_pong_test", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "uses_polling": true
-  }, 
-  {
-    "args": [], 
-    "benchmark": false, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "gtest": false, 
-    "language": "c++", 
-    "name": "interop_test", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "uses_polling": true
-  }, 
-  {
-    "args": [], 
-    "benchmark": false, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
       "posix", 
       "windows"
     ], 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "memory_test", 
+    "name": "h2_ssl_cert_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "uses_polling": false
+    "uses_polling": true
   }, 
   {
     "args": [], 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "mock_test", 
+    "name": "h2_ssl_session_reuse_test", 
     "platforms": [
       "linux", 
       "mac", 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "nonblocking_test", 
+    "name": "health_service_end2end_test", 
     "platforms": [
       "linux", 
       "mac", 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
-    "flaky": false, 
+    "flaky": true, 
     "gtest": false, 
     "language": "c++", 
-    "name": "noop-benchmark", 
+    "name": "hybrid_end2end_test", 
     "platforms": [
       "linux", 
       "mac", 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "optional_test", 
+    "name": "inlined_vector_test", 
     "platforms": [
       "linux", 
       "mac", 
     "ci_platforms": [
       "linux", 
       "mac", 
-      "posix", 
-      "windows"
+      "posix"
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
-    "gtest": true, 
+    "gtest": false, 
     "language": "c++", 
-    "name": "orphanable_test", 
+    "name": "inproc_sync_unary_ping_pong_test", 
     "platforms": [
       "linux", 
       "mac", 
-      "posix", 
-      "windows"
+      "posix"
     ], 
     "uses_polling": true
   }, 
     "ci_platforms": [
       "linux", 
       "mac", 
-      "posix", 
-      "windows"
+      "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
-    "gtest": true, 
+    "gtest": false, 
     "language": "c++", 
-    "name": "proto_server_reflection_test", 
+    "name": "interop_test", 
     "platforms": [
       "linux", 
       "mac", 
-      "posix", 
-      "windows"
+      "posix"
     ], 
     "uses_polling": true
   }, 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "proto_utils_test", 
+    "name": "memory_test", 
     "platforms": [
       "linux", 
       "mac", 
     "ci_platforms": [
       "linux", 
       "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 0.5, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "gtest": false, 
-    "language": "c++", 
-    "name": "qps_openloop_test", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "uses_polling": true
-  }, 
-  {
-    "args": [], 
-    "benchmark": false, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
       "posix", 
       "windows"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.5
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "raw_end2end_test", 
+    "name": "message_allocator_end2end_test", 
     "platforms": [
       "linux", 
       "mac", 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "ref_counted_ptr_test", 
+    "name": "mock_test", 
     "platforms": [
       "linux", 
       "mac", 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "ref_counted_test", 
+    "name": "nonblocking_test", 
     "platforms": [
       "linux", 
       "mac", 
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
-    "gtest": true, 
+    "gtest": false, 
     "language": "c++", 
-    "name": "retry_throttle_test", 
+    "name": "noop-benchmark", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "uses_polling": false
+    "uses_polling": true
   }, 
   {
     "args": [], 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "secure_auth_context_test", 
+    "name": "optional_test", 
     "platforms": [
       "linux", 
       "mac", 
     "ci_platforms": [
       "linux", 
       "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "gtest": false, 
-    "language": "c++", 
-    "name": "secure_sync_unary_ping_pong_test", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "uses_polling": true
-  }, 
-  {
-    "args": [], 
-    "benchmark": false, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
       "posix", 
       "windows"
     ], 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "server_builder_plugin_test", 
+    "name": "orphanable_test", 
     "platforms": [
       "linux", 
       "mac", 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "server_builder_test", 
+    "name": "proto_server_reflection_test", 
     "platforms": [
       "linux", 
       "mac", 
     "args": [], 
     "benchmark": false, 
     "ci_platforms": [
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "gtest": true, 
-    "language": "c++", 
-    "name": "server_builder_with_socket_mutator_test", 
-    "platforms": [
-      "posix"
-    ], 
-    "uses_polling": true
-  }, 
-  {
-    "args": [], 
-    "benchmark": false, 
-    "ci_platforms": [
       "linux", 
       "mac", 
       "posix", 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "server_context_test_spouse_test", 
+    "name": "proto_utils_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "uses_polling": true
+    "uses_polling": false
   }, 
   {
     "args": [], 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 0.5
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
-    "gtest": true, 
+    "gtest": false, 
     "language": "c++", 
-    "name": "server_crash_test", 
+    "name": "qps_openloop_test", 
     "platforms": [
       "linux", 
       "mac", 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "server_early_return_test", 
+    "name": "raw_end2end_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "cpu_cost": 0.5
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "server_interceptors_end2end_test", 
+    "name": "ref_counted_ptr_test", 
     "platforms": [
       "linux", 
       "mac", 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "server_request_call_test", 
+    "name": "ref_counted_test", 
     "platforms": [
       "linux", 
       "mac", 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "shutdown_test", 
+    "name": "retry_throttle_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "uses_polling": true
+    "uses_polling": false
   }, 
   {
     "args": [], 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "slice_hash_table_test", 
+    "name": "secure_auth_context_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "uses_polling": false
+    "uses_polling": true
   }, 
   {
     "args": [], 
     "ci_platforms": [
       "linux", 
       "mac", 
-      "posix", 
-      "windows"
+      "posix"
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
-    "gtest": true, 
+    "gtest": false, 
     "language": "c++", 
-    "name": "slice_weak_hash_table_test", 
+    "name": "secure_sync_unary_ping_pong_test", 
     "platforms": [
       "linux", 
       "mac", 
-      "posix", 
-      "windows"
+      "posix"
     ], 
-    "uses_polling": false
+    "uses_polling": true
   }, 
   {
     "args": [], 
       "windows"
     ], 
     "cpu_cost": 1.0, 
-    "exclude_configs": [
-      "tsan"
-    ], 
+    "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "stats_test", 
+    "name": "server_builder_plugin_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "timeout_seconds": 1200, 
-    "uses_polling": false
+    "uses_polling": true
   }, 
   {
     "args": [], 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "status_metadata_test", 
+    "name": "server_builder_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "uses_polling": false
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "server_builder_with_socket_mutator_test", 
+    "platforms": [
+      "posix"
+    ], 
+    "uses_polling": true
   }, 
   {
     "args": [], 
       "posix", 
       "windows"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "status_util_test", 
+    "name": "server_context_test_spouse_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "uses_polling": false
+    "uses_polling": true
   }, 
   {
     "args": [], 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "streaming_throughput_test", 
+    "name": "server_crash_test", 
     "platforms": [
       "linux", 
       "mac", 
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
-    "gtest": false, 
+    "gtest": true, 
     "language": "c++", 
-    "name": "thread_manager_test", 
+    "name": "server_early_return_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "cpu_cost": 100
+    "cpu_cost": 0.5
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "thread_stress_test", 
+    "name": "server_interceptors_end2end_test", 
     "platforms": [
       "linux", 
       "mac", 
     "ci_platforms": [
       "linux", 
       "mac", 
-      "posix"
+      "posix", 
+      "windows"
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "time_change_test", 
+    "name": "server_request_call_test", 
     "platforms": [
       "linux", 
       "mac", 
-      "posix"
+      "posix", 
+      "windows"
     ], 
     "uses_polling": true
   }, 
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
-    "gtest": false, 
+    "gtest": true, 
     "language": "c++", 
-    "name": "transport_pid_controller_test", 
+    "name": "service_config_end2end_test", 
     "platforms": [
       "linux", 
       "mac", 
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
-    "gtest": false, 
+    "gtest": true, 
     "language": "c++", 
-    "name": "transport_security_common_api_test", 
+    "name": "service_config_test", 
     "platforms": [
       "linux", 
       "mac", 
     "ci_platforms": [
       "linux", 
       "mac", 
-      "posix"
+      "posix", 
+      "windows"
     ], 
-    "cpu_cost": 0.5
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "writes_per_rpc_test", 
+    "name": "shutdown_test", 
     "platforms": [
       "linux", 
       "mac", 
-      "posix"
+      "posix", 
+      "windows"
     ], 
     "uses_polling": true
   }, 
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
-    "name": "xds_end2end_test", 
+    "name": "slice_hash_table_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "uses_polling": true
+    "uses_polling": false
   }, 
   {
     "args": [], 
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
-    "gtest": false, 
-    "language": "c89", 
-    "name": "public_headers_must_be_c89", 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "slice_weak_hash_table_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "uses_polling": true
+    "uses_polling": false
   }, 
   {
     "args": [], 
       "windows"
     ], 
     "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
+    "exclude_configs": [
+      "tsan"
     ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
-    "gtest": false, 
-    "language": "c", 
-    "name": "badreq_bad_client_test", 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "stats_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "uses_polling": true
+    "timeout_seconds": 1200, 
+    "uses_polling": false
   }, 
   {
     "args": [], 
       "posix", 
       "windows"
     ], 
-    "cpu_cost": 0.2
+    "cpu_cost": 1.0
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
-    "gtest": false, 
-    "language": "c", 
-    "name": "connection_prefix_bad_client_test", 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "status_metadata_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "uses_polling": true
+    "uses_polling": false
   }, 
   {
     "args": [], 
       "posix", 
       "windows"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
-    "gtest": false, 
-    "language": "c", 
-    "name": "duplicate_header_bad_client_test", 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "status_util_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "streaming_throughput_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
     "uses_polling": true
   }, 
   {
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
     "gtest": false, 
-    "language": "c", 
-    "name": "head_of_line_blocking_bad_client_test", 
+    "language": "c++", 
+    "name": "thread_manager_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "cpu_cost": 0.2
+    "cpu_cost": 100
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
-    "gtest": false, 
-    "language": "c", 
-    "name": "headers_bad_client_test", 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "thread_stress_test", 
     "platforms": [
       "linux", 
       "mac", 
     "ci_platforms": [
       "linux", 
       "mac", 
-      "posix", 
-      "windows"
+      "posix"
     ], 
-    "cpu_cost": 0.2
+    "cpu_cost": 1.0
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
-    "gtest": false, 
-    "language": "c", 
-    "name": "initial_settings_frame_bad_client_test", 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "time_change_test", 
     "platforms": [
       "linux", 
       "mac", 
-      "posix", 
-      "windows"
+      "posix"
     ], 
     "uses_polling": true
   }, 
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
     "gtest": false, 
-    "language": "c", 
-    "name": "large_metadata_bad_client_test", 
+    "language": "c++", 
+    "name": "transport_pid_controller_test", 
     "platforms": [
       "linux", 
       "mac", 
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
     "gtest": false, 
-    "language": "c", 
-    "name": "server_registered_method_bad_client_test", 
+    "language": "c++", 
+    "name": "transport_security_common_api_test", 
     "platforms": [
       "linux", 
       "mac", 
     "ci_platforms": [
       "linux", 
       "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.5, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "writes_per_rpc_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
       "posix", 
       "windows"
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
-    "gtest": false, 
-    "language": "c", 
-    "name": "simple_request_bad_client_test", 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "xds_end2end_test", 
     "platforms": [
       "linux", 
       "mac", 
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
     "gtest": false, 
-    "language": "c", 
-    "name": "unknown_frame_bad_client_test", 
+    "language": "c89", 
+    "name": "public_headers_must_be_c89", 
     "platforms": [
       "linux", 
       "mac", 
     "flaky": false, 
     "gtest": false, 
     "language": "c", 
-    "name": "window_overflow_bad_client_test", 
+    "name": "badreq_bad_client_test", 
     "platforms": [
       "linux", 
       "mac", 
     "ci_platforms": [
       "linux", 
       "mac", 
-      "posix"
+      "posix", 
+      "windows"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 0.2
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
     "gtest": false, 
     "language": "c", 
-    "name": "bad_ssl_cert_test", 
+    "name": "connection_prefix_bad_client_test", 
     "platforms": [
       "linux", 
       "mac", 
-      "posix"
+      "posix", 
+      "windows"
     ], 
     "uses_polling": true
   }, 
   {
-    "args": [
-      "--test_bin_name=resolver_component_test_unsecure", 
-      "--running_under_bazel=false"
-    ], 
+    "args": [], 
     "benchmark": false, 
     "ci_platforms": [
       "linux", 
       "mac", 
-      "posix"
+      "posix", 
+      "windows"
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
     "gtest": false, 
-    "language": "c++", 
-    "name": "resolver_component_tests_runner_invoker_unsecure", 
+    "language": "c", 
+    "name": "duplicate_header_bad_client_test", 
     "platforms": [
       "linux", 
       "mac", 
-      "posix"
+      "posix", 
+      "windows"
     ], 
     "uses_polling": true
   }, 
   {
-    "args": [
-      "--test_bin_name=resolver_component_test", 
-      "--running_under_bazel=false"
-    ], 
+    "args": [], 
     "benchmark": false, 
     "ci_platforms": [
       "linux", 
       "mac", 
-      "posix"
+      "posix", 
+      "windows"
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
     "gtest": false, 
-    "language": "c++", 
-    "name": "resolver_component_tests_runner_invoker", 
+    "language": "c", 
+    "name": "head_of_line_blocking_bad_client_test", 
     "platforms": [
       "linux", 
       "mac", 
-      "posix"
+      "posix", 
+      "windows"
     ], 
     "uses_polling": true
   }, 
       "posix", 
       "windows"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.2
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
-    "gtest": true, 
-    "language": "c++", 
-    "name": "address_sorting_test_unsecure", 
+    "gtest": false, 
+    "language": "c", 
+    "name": "headers_bad_client_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.2
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
-    "gtest": true, 
-    "language": "c++", 
-    "name": "address_sorting_test", 
+    "gtest": false, 
+    "language": "c", 
+    "name": "initial_settings_frame_bad_client_test", 
     "platforms": [
       "linux", 
       "mac", 
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
-    "gtest": true, 
-    "language": "c++", 
-    "name": "cancel_ares_query_test", 
+    "gtest": false, 
+    "language": "c", 
+    "name": "large_metadata_bad_client_test", 
     "platforms": [
       "linux", 
       "mac", 
   }, 
   {
     "args": [], 
-    "boringssl": true, 
+    "benchmark": false, 
     "ci_platforms": [
       "linux", 
       "mac", 
       "windows"
     ], 
     "cpu_cost": 1.0, 
-    "defaults": "boringssl", 
-    "exclude_configs": [
-      "asan", 
-      "ubsan"
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
     ], 
     "flaky": false, 
-    "gtest": true, 
-    "language": "c++", 
-    "name": "boringssl_ssl_test", 
+    "gtest": false, 
+    "language": "c", 
+    "name": "server_registered_method_bad_client_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
-    ]
+    ], 
+    "uses_polling": true
   }, 
   {
     "args": [], 
-    "boringssl": true, 
+    "benchmark": false, 
     "ci_platforms": [
       "linux", 
       "mac", 
       "windows"
     ], 
     "cpu_cost": 1.0, 
-    "defaults": "boringssl", 
-    "exclude_configs": [
-      "asan", 
-      "ubsan"
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
     ], 
     "flaky": false, 
-    "gtest": true, 
-    "language": "c++", 
-    "name": "boringssl_crypto_test", 
+    "gtest": false, 
+    "language": "c", 
+    "name": "simple_request_bad_client_test", 
     "platforms": [
       "linux", 
       "mac", 
       "posix", 
       "windows"
-    ]
+    ], 
+    "uses_polling": true
   }, 
   {
-    "args": [
-      "authority_not_supported"
-    ], 
+    "args": [], 
+    "benchmark": false, 
     "ci_platforms": [
-      "windows", 
       "linux", 
       "mac", 
-      "posix"
+      "posix", 
+      "windows"
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
+    "gtest": false, 
     "language": "c", 
-    "name": "h2_census_test", 
+    "name": "unknown_frame_bad_client_test", 
     "platforms": [
-      "windows", 
       "linux", 
       "mac", 
-      "posix"
-    ]
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
   }, 
   {
-    "args": [
-      "bad_hostname"
-    ], 
+    "args": [], 
+    "benchmark": false, 
     "ci_platforms": [
-      "windows", 
       "linux", 
       "mac", 
-      "posix"
+      "posix", 
+      "windows"
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
+    "gtest": false, 
     "language": "c", 
-    "name": "h2_census_test", 
+    "name": "window_overflow_bad_client_test", 
     "platforms": [
-      "windows", 
       "linux", 
       "mac", 
-      "posix"
-    ]
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
   }, 
   {
-    "args": [
-      "bad_ping"
-    ], 
+    "args": [], 
+    "benchmark": false, 
     "ci_platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
+    "gtest": false, 
     "language": "c", 
-    "name": "h2_census_test", 
+    "name": "bad_ssl_cert_test", 
     "platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
-    ]
+    ], 
+    "uses_polling": true
   }, 
   {
     "args": [
-      "binary_metadata"
+      "--test_bin_name=resolver_component_test_unsecure", 
+      "--running_under_bazel=false"
     ], 
+    "benchmark": false, 
     "ci_platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
-    "language": "c", 
-    "name": "h2_census_test", 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "resolver_component_tests_runner_invoker_unsecure", 
     "platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
-    ]
+    ], 
+    "uses_polling": true
   }, 
   {
     "args": [
-      "call_creds"
+      "--test_bin_name=resolver_component_test", 
+      "--running_under_bazel=false"
     ], 
+    "benchmark": false, 
     "ci_platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
-    "language": "c", 
-    "name": "h2_census_test", 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "resolver_component_tests_runner_invoker", 
     "platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
-    ]
+    ], 
+    "uses_polling": true
   }, 
   {
-    "args": [
-      "call_host_override"
-    ], 
+    "args": [], 
+    "benchmark": false, 
     "ci_platforms": [
-      "windows", 
       "linux", 
       "mac", 
-      "posix"
+      "posix", 
+      "windows"
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
-    "language": "c", 
-    "name": "h2_census_test", 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "address_sorting_test_unsecure", 
     "platforms": [
-      "windows", 
       "linux", 
       "mac", 
-      "posix"
-    ]
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
   }, 
   {
-    "args": [
-      "cancel_after_accept"
-    ], 
+    "args": [], 
+    "benchmark": false, 
     "ci_platforms": [
-      "windows", 
       "linux", 
       "mac", 
-      "posix"
+      "posix", 
+      "windows"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
-    "language": "c", 
-    "name": "h2_census_test", 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "address_sorting_test", 
     "platforms": [
-      "windows", 
       "linux", 
       "mac", 
-      "posix"
-    ]
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
   }, 
   {
-    "args": [
-      "cancel_after_client_done"
-    ], 
+    "args": [], 
+    "benchmark": false, 
     "ci_platforms": [
-      "windows", 
       "linux", 
       "mac", 
-      "posix"
+      "posix", 
+      "windows"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
-    "language": "c", 
-    "name": "h2_census_test", 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "cancel_ares_query_test", 
     "platforms": [
-      "windows", 
       "linux", 
       "mac", 
-      "posix"
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "asan", 
+      "ubsan"
+    ], 
+    "flaky": false, 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "boringssl_ssl_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ]
+  }, 
+  {
+    "args": [], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "asan", 
+      "ubsan"
+    ], 
+    "flaky": false, 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "boringssl_crypto_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
     ]
   }, 
   {
     "args": [
-      "cancel_after_invoke"
+      "authority_not_supported"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "cancel_after_round_trip"
+      "bad_hostname"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "cancel_before_invoke"
+      "bad_ping"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "cancel_in_a_vacuum"
+      "binary_metadata"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "cancel_with_status"
+      "call_creds"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "channelz"
+      "call_host_override"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "compressed_payload"
+      "cancel_after_accept"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "connectivity"
+      "cancel_after_client_done"
     ], 
     "ci_platforms": [
       "windows", 
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
     "name": "h2_census_test", 
   }, 
   {
     "args": [
-      "default_host"
+      "cancel_after_invoke"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "disappearing_server"
+      "cancel_after_round_trip"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
-    "flaky": true, 
+    "flaky": false, 
     "language": "c", 
     "name": "h2_census_test", 
     "platforms": [
   }, 
   {
     "args": [
-      "empty_batch"
+      "cancel_before_invoke"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "filter_call_init_fails"
+      "cancel_in_a_vacuum"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "filter_causes_close"
+      "cancel_with_status"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "filter_context"
+      "channelz"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "filter_latency"
+      "compressed_payload"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "filter_status_code"
+      "connectivity"
     ], 
     "ci_platforms": [
       "windows", 
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
     "language": "c", 
     "name": "h2_census_test", 
   }, 
   {
     "args": [
-      "graceful_server_shutdown"
+      "default_host"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "high_initial_seqno"
+      "disappearing_server"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
-    "flaky": false, 
+    "flaky": true, 
     "language": "c", 
     "name": "h2_census_test", 
     "platforms": [
   }, 
   {
     "args": [
-      "hpack_size"
+      "empty_batch"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "idempotent_request"
+      "filter_call_init_fails"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "invoke_large_request"
+      "filter_causes_close"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "keepalive_timeout"
+      "filter_context"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "large_metadata"
+      "filter_latency"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "max_concurrent_streams"
+      "filter_status_code"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "max_connection_age"
+      "graceful_server_shutdown"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "max_connection_idle"
+      "high_initial_seqno"
     ], 
     "ci_platforms": [
       "windows", 
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
     "name": "h2_census_test", 
   }, 
   {
     "args": [
-      "max_message_length"
+      "hpack_size"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "negative_deadline"
+      "idempotent_request"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "no_error_on_hotpath"
+      "invoke_large_request"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "no_logging"
+      "keepalive_timeout"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "no_op"
+      "large_metadata"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "payload"
+      "max_concurrent_streams"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "ping"
+      "max_connection_age"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "ping_pong_streaming"
+      "max_connection_idle"
     ], 
     "ci_platforms": [
       "windows", 
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
     "language": "c", 
     "name": "h2_census_test", 
   }, 
   {
     "args": [
-      "registered_call"
+      "max_message_length"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "request_with_flags"
+      "negative_deadline"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "request_with_payload"
+      "no_error_on_hotpath"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "resource_quota_server"
+      "no_logging"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry"
+      "no_op"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "retry_cancellation"
+      "payload"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "retry_disabled"
+      "ping"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_exceeds_buffer_size_in_initial_batch"
+      "ping_pong_streaming"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_exceeds_buffer_size_in_subsequent_batch"
+      "registered_call"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "retry_non_retriable_status"
+      "request_with_flags"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_non_retriable_status_before_recv_trailing_metadata_started"
+      "request_with_payload"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_recv_initial_metadata"
+      "resource_quota_server"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "retry_recv_message"
+      "retry"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_server_pushback_delay"
+      "retry_cancellation"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_server_pushback_disabled"
+      "retry_disabled"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_streaming"
+      "retry_exceeds_buffer_size_in_initial_batch"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_streaming_after_commit"
+      "retry_exceeds_buffer_size_in_subsequent_batch"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_streaming_succeeds_before_replay_finished"
+      "retry_non_retriable_status"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_throttled"
+      "retry_non_retriable_status_before_recv_trailing_metadata_started"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_too_many_attempts"
+      "retry_recv_initial_metadata"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "server_finishes_request"
+      "retry_recv_message"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "shutdown_finishes_calls"
+      "retry_server_pushback_delay"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "shutdown_finishes_tags"
+      "retry_server_pushback_disabled"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "simple_cacheable_request"
+      "retry_streaming"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "simple_delayed_request"
+      "retry_streaming_after_commit"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "simple_metadata"
+      "retry_streaming_succeeds_before_replay_finished"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "simple_request"
+      "retry_throttled"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "stream_compression_compressed_payload"
+      "retry_too_many_attempts"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "stream_compression_payload"
+      "server_finishes_request"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "stream_compression_ping_pong_streaming"
+      "shutdown_finishes_calls"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "streaming_error_response"
+      "shutdown_finishes_tags"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "trailing_metadata"
+      "simple_cacheable_request"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "workaround_cronet_compression"
+      "simple_delayed_request"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "write_buffering"
+      "simple_metadata"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "write_buffering_at_end"
+      "simple_request"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "authority_not_supported"
+      "stream_compression_compressed_payload"
     ], 
     "ci_platforms": [
       "windows", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_compress_test", 
+    "name": "h2_census_test", 
     "platforms": [
       "windows", 
       "linux", 
   }, 
   {
     "args": [
-      "bad_hostname"
+      "stream_compression_payload"
     ], 
     "ci_platforms": [
       "windows", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_compress_test", 
+    "name": "h2_census_test", 
     "platforms": [
       "windows", 
       "linux", 
   }, 
   {
     "args": [
-      "bad_ping"
+      "stream_compression_ping_pong_streaming"
     ], 
     "ci_platforms": [
       "windows", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_compress_test", 
+    "name": "h2_census_test", 
     "platforms": [
       "windows", 
       "linux", 
   }, 
   {
     "args": [
-      "binary_metadata"
+      "streaming_error_response"
     ], 
     "ci_platforms": [
       "windows", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_compress_test", 
+    "name": "h2_census_test", 
     "platforms": [
       "windows", 
       "linux", 
   }, 
   {
     "args": [
-      "call_creds"
+      "trailing_metadata"
     ], 
     "ci_platforms": [
       "windows", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_compress_test", 
+    "name": "h2_census_test", 
     "platforms": [
       "windows", 
       "linux", 
   }, 
   {
     "args": [
-      "call_host_override"
+      "workaround_cronet_compression"
     ], 
     "ci_platforms": [
       "windows", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_compress_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "cancel_after_accept"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_compress_test", 
+    "name": "h2_census_test", 
     "platforms": [
       "windows", 
       "linux", 
   }, 
   {
     "args": [
-      "cancel_after_client_done"
+      "write_buffering"
     ], 
     "ci_platforms": [
       "windows", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_compress_test", 
+    "name": "h2_census_test", 
     "platforms": [
       "windows", 
       "linux", 
   }, 
   {
     "args": [
-      "cancel_after_invoke"
+      "write_buffering_at_end"
     ], 
     "ci_platforms": [
       "windows", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_compress_test", 
+    "name": "h2_census_test", 
     "platforms": [
       "windows", 
       "linux", 
   }, 
   {
     "args": [
-      "cancel_after_round_trip"
+      "authority_not_supported"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "cancel_before_invoke"
+      "bad_hostname"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "cancel_in_a_vacuum"
+      "bad_ping"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "cancel_with_status"
+      "binary_metadata"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "channelz"
+      "call_creds"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "compressed_payload"
+      "call_host_override"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "connectivity"
+      "cancel_after_accept"
     ], 
     "ci_platforms": [
       "windows", 
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_compress_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "default_host"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
   }, 
   {
     "args": [
-      "disappearing_server"
+      "cancel_after_client_done"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
-    "flaky": true, 
+    "flaky": false, 
     "language": "c", 
     "name": "h2_compress_test", 
     "platforms": [
   }, 
   {
     "args": [
-      "empty_batch"
+      "cancel_after_invoke"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "filter_call_init_fails"
+      "cancel_after_round_trip"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "filter_causes_close"
+      "cancel_before_invoke"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "filter_context"
+      "cancel_in_a_vacuum"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "filter_latency"
+      "cancel_with_status"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "filter_status_code"
+      "channelz"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "graceful_server_shutdown"
+      "compressed_payload"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "high_initial_seqno"
+      "connectivity"
     ], 
     "ci_platforms": [
       "windows", 
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
     "language": "c", 
     "name": "h2_compress_test", 
   }, 
   {
     "args": [
-      "hpack_size"
+      "default_host"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "idempotent_request"
+      "disappearing_server"
     ], 
     "ci_platforms": [
       "windows", 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
+    "flaky": true, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "empty_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
     "name": "h2_compress_test", 
   }, 
   {
     "args": [
-      "invoke_large_request"
+      "filter_call_init_fails"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "keepalive_timeout"
+      "filter_causes_close"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "large_metadata"
+      "filter_context"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "max_concurrent_streams"
+      "filter_latency"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "max_connection_age"
+      "filter_status_code"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "max_connection_idle"
+      "graceful_server_shutdown"
     ], 
     "ci_platforms": [
       "windows", 
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "high_initial_seqno"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
     ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
     "name": "h2_compress_test", 
   }, 
   {
     "args": [
-      "max_message_length"
+      "hpack_size"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "negative_deadline"
+      "idempotent_request"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "no_error_on_hotpath"
+      "invoke_large_request"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "no_logging"
+      "keepalive_timeout"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "no_op"
+      "large_metadata"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "payload"
+      "max_concurrent_streams"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "ping"
+      "max_connection_age"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "ping_pong_streaming"
+      "max_connection_idle"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "max_message_length"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "registered_call"
+      "negative_deadline"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "request_with_flags"
+      "no_error_on_hotpath"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "request_with_payload"
+      "no_logging"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "retry"
+      "no_op"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "retry_cancellation"
+      "payload"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "ping"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_disabled"
+      "ping_pong_streaming"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_exceeds_buffer_size_in_initial_batch"
+      "registered_call"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "request_with_flags"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_exceeds_buffer_size_in_subsequent_batch"
+      "request_with_payload"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_non_retriable_status"
+      "retry"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_non_retriable_status_before_recv_trailing_metadata_started"
+      "retry_cancellation"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_recv_initial_metadata"
+      "retry_disabled"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_recv_message"
+      "retry_exceeds_buffer_size_in_initial_batch"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_server_pushback_delay"
+      "retry_exceeds_buffer_size_in_subsequent_batch"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_server_pushback_disabled"
+      "retry_non_retriable_status"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_streaming"
+      "retry_non_retriable_status_before_recv_trailing_metadata_started"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_streaming_after_commit"
+      "retry_recv_initial_metadata"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_streaming_succeeds_before_replay_finished"
+      "retry_recv_message"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_throttled"
+      "retry_server_pushback_delay"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_too_many_attempts"
+      "retry_server_pushback_disabled"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "server_finishes_request"
+      "retry_streaming"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "shutdown_finishes_calls"
+      "retry_streaming_after_commit"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "shutdown_finishes_tags"
+      "retry_streaming_succeeds_before_replay_finished"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "simple_cacheable_request"
+      "retry_throttled"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "simple_delayed_request"
+      "retry_too_many_attempts"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "simple_metadata"
+      "server_finishes_request"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "simple_request"
+      "shutdown_finishes_calls"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "stream_compression_compressed_payload"
+      "shutdown_finishes_tags"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "stream_compression_payload"
+      "simple_cacheable_request"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "stream_compression_ping_pong_streaming"
+      "simple_delayed_request"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "streaming_error_response"
+      "simple_metadata"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "trailing_metadata"
+      "simple_request"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "workaround_cronet_compression"
+      "stream_compression_compressed_payload"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "write_buffering"
+      "stream_compression_payload"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "write_buffering_at_end"
+      "stream_compression_ping_pong_streaming"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "authority_not_supported"
+      "streaming_error_response"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
+      "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_fakesec_test", 
+    "name": "h2_compress_test", 
     "platforms": [
       "windows", 
       "linux", 
   }, 
   {
     "args": [
-      "bad_hostname"
+      "trailing_metadata"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
+      "mac", 
       "posix"
     ], 
     "cpu_cost": 1.0, 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_fakesec_test", 
+    "name": "h2_compress_test", 
     "platforms": [
       "windows", 
       "linux", 
   }, 
   {
     "args": [
-      "bad_ping"
+      "workaround_cronet_compression"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
+      "mac", 
       "posix"
     ], 
     "cpu_cost": 1.0, 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_fakesec_test", 
+    "name": "h2_compress_test", 
     "platforms": [
       "windows", 
       "linux", 
   }, 
   {
     "args": [
-      "binary_metadata"
+      "write_buffering"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
+      "mac", 
       "posix"
     ], 
     "cpu_cost": 0.1, 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_fakesec_test", 
+    "name": "h2_compress_test", 
     "platforms": [
       "windows", 
       "linux", 
   }, 
   {
     "args": [
-      "call_creds"
+      "write_buffering_at_end"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
+      "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_fakesec_test", 
+    "name": "h2_compress_test", 
     "platforms": [
       "windows", 
       "linux", 
   }, 
   {
     "args": [
-      "call_host_override"
+      "authority_not_supported"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "cancel_after_accept"
+      "bad_hostname"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "cancel_after_client_done"
+      "bad_ping"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "cancel_after_invoke"
+      "binary_metadata"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "cancel_after_round_trip"
+      "call_creds"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "cancel_before_invoke"
+      "call_host_override"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "cancel_in_a_vacuum"
+      "cancel_after_accept"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "cancel_with_status"
+      "cancel_after_client_done"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "channelz"
+      "cancel_after_invoke"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "compressed_payload"
+      "cancel_after_round_trip"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "connectivity"
+      "cancel_before_invoke"
     ], 
     "ci_platforms": [
       "windows", 
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_fakesec_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "default_host"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
   }, 
   {
     "args": [
-      "disappearing_server"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": true, 
-    "language": "c", 
-    "name": "h2_fakesec_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "empty_batch"
+      "cancel_in_a_vacuum"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "filter_call_init_fails"
+      "cancel_with_status"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "filter_causes_close"
+      "channelz"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "filter_context"
+      "compressed_payload"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "filter_latency"
+      "connectivity"
     ], 
     "ci_platforms": [
       "windows", 
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
     "language": "c", 
     "name": "h2_fakesec_test", 
   }, 
   {
     "args": [
-      "filter_status_code"
+      "default_host"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "graceful_server_shutdown"
+      "disappearing_server"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
-    "flaky": false, 
+    "flaky": true, 
     "language": "c", 
     "name": "h2_fakesec_test", 
     "platforms": [
   }, 
   {
     "args": [
-      "high_initial_seqno"
+      "empty_batch"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "hpack_size"
+      "filter_call_init_fails"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "idempotent_request"
+      "filter_causes_close"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "invoke_large_request"
+      "filter_context"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "keepalive_timeout"
+      "filter_latency"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "large_metadata"
+      "filter_status_code"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "max_concurrent_streams"
+      "graceful_server_shutdown"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "max_connection_age"
+      "high_initial_seqno"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "max_connection_idle"
+      "hpack_size"
     ], 
     "ci_platforms": [
       "windows", 
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
     "name": "h2_fakesec_test", 
   }, 
   {
     "args": [
-      "max_message_length"
+      "idempotent_request"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "negative_deadline"
+      "invoke_large_request"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "no_error_on_hotpath"
+      "keepalive_timeout"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "no_logging"
+      "large_metadata"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "no_op"
+      "max_concurrent_streams"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "payload"
+      "max_connection_age"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "ping"
+      "max_connection_idle"
     ], 
     "ci_platforms": [
       "windows", 
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
     "language": "c", 
     "name": "h2_fakesec_test", 
   }, 
   {
     "args": [
-      "ping_pong_streaming"
+      "max_message_length"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "registered_call"
+      "negative_deadline"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "request_with_flags"
+      "no_error_on_hotpath"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "request_with_payload"
+      "no_logging"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "resource_quota_server"
+      "no_op"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry"
+      "payload"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "retry_cancellation"
+      "ping"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_disabled"
+      "ping_pong_streaming"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_exceeds_buffer_size_in_initial_batch"
+      "registered_call"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "retry_exceeds_buffer_size_in_subsequent_batch"
+      "request_with_flags"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_non_retriable_status"
+      "request_with_payload"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_non_retriable_status_before_recv_trailing_metadata_started"
+      "resource_quota_server"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "retry_recv_initial_metadata"
+      "retry"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_recv_message"
+      "retry_cancellation"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_server_pushback_delay"
+      "retry_disabled"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_server_pushback_disabled"
+      "retry_exceeds_buffer_size_in_initial_batch"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_streaming"
+      "retry_exceeds_buffer_size_in_subsequent_batch"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_streaming_after_commit"
+      "retry_non_retriable_status"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_streaming_succeeds_before_replay_finished"
+      "retry_non_retriable_status_before_recv_trailing_metadata_started"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_throttled"
+      "retry_recv_initial_metadata"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_too_many_attempts"
+      "retry_recv_message"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "server_finishes_request"
+      "retry_server_pushback_delay"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "shutdown_finishes_calls"
+      "retry_server_pushback_disabled"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "shutdown_finishes_tags"
+      "retry_streaming"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "simple_cacheable_request"
+      "retry_streaming_after_commit"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "simple_delayed_request"
+      "retry_streaming_succeeds_before_replay_finished"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "simple_metadata"
+      "retry_throttled"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "simple_request"
+      "retry_too_many_attempts"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "stream_compression_compressed_payload"
+      "server_finishes_request"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "stream_compression_payload"
+      "shutdown_finishes_calls"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "stream_compression_ping_pong_streaming"
+      "shutdown_finishes_tags"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "streaming_error_response"
+      "simple_cacheable_request"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "trailing_metadata"
+      "simple_delayed_request"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "workaround_cronet_compression"
+      "simple_metadata"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "write_buffering"
+      "simple_request"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "write_buffering_at_end"
+      "stream_compression_compressed_payload"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "authority_not_supported"
+      "stream_compression_payload"
     ], 
     "ci_platforms": [
+      "windows", 
       "linux", 
-      "mac", 
       "posix"
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_fd_test", 
+    "name": "h2_fakesec_test", 
     "platforms": [
+      "windows", 
       "linux", 
       "mac", 
       "posix"
   }, 
   {
     "args": [
-      "bad_hostname"
+      "stream_compression_ping_pong_streaming"
     ], 
     "ci_platforms": [
+      "windows", 
       "linux", 
-      "mac", 
       "posix"
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_fd_test", 
+    "name": "h2_fakesec_test", 
     "platforms": [
+      "windows", 
       "linux", 
       "mac", 
       "posix"
   }, 
   {
     "args": [
-      "binary_metadata"
+      "streaming_error_response"
     ], 
     "ci_platforms": [
+      "windows", 
       "linux", 
-      "mac", 
       "posix"
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_fd_test", 
+    "name": "h2_fakesec_test", 
     "platforms": [
+      "windows", 
       "linux", 
       "mac", 
       "posix"
   }, 
   {
     "args": [
-      "call_creds"
+      "trailing_metadata"
     ], 
     "ci_platforms": [
+      "windows", 
       "linux", 
-      "mac", 
       "posix"
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_fd_test", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "cancel_after_accept"
-    ], 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_fd_test", 
+    "name": "h2_fakesec_test", 
     "platforms": [
+      "windows", 
       "linux", 
       "mac", 
       "posix"
   }, 
   {
     "args": [
-      "cancel_after_client_done"
+      "workaround_cronet_compression"
     ], 
     "ci_platforms": [
+      "windows", 
       "linux", 
-      "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_fd_test", 
+    "name": "h2_fakesec_test", 
     "platforms": [
+      "windows", 
       "linux", 
       "mac", 
       "posix"
   }, 
   {
     "args": [
-      "cancel_after_invoke"
+      "write_buffering"
     ], 
     "ci_platforms": [
+      "windows", 
       "linux", 
-      "mac", 
       "posix"
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_fd_test", 
+    "name": "h2_fakesec_test", 
     "platforms": [
+      "windows", 
       "linux", 
       "mac", 
       "posix"
   }, 
   {
     "args": [
-      "cancel_after_round_trip"
+      "write_buffering_at_end"
     ], 
     "ci_platforms": [
+      "windows", 
       "linux", 
-      "mac", 
       "posix"
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_fd_test", 
+    "name": "h2_fakesec_test", 
     "platforms": [
+      "windows", 
       "linux", 
       "mac", 
       "posix"
   }, 
   {
     "args": [
-      "cancel_before_invoke"
+      "authority_not_supported"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "cancel_in_a_vacuum"
+      "bad_hostname"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "cancel_with_status"
+      "binary_metadata"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "channelz"
-    ], 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_fd_test", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "compressed_payload"
+      "call_creds"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "empty_batch"
+      "cancel_after_accept"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "filter_call_init_fails"
+      "cancel_after_client_done"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "filter_causes_close"
+      "cancel_after_invoke"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "filter_context"
+      "cancel_after_round_trip"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "filter_latency"
+      "cancel_before_invoke"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "filter_status_code"
+      "cancel_in_a_vacuum"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "graceful_server_shutdown"
+      "cancel_with_status"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "high_initial_seqno"
+      "channelz"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "hpack_size"
+      "compressed_payload"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "idempotent_request"
+      "empty_batch"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "invoke_large_request"
+      "filter_call_init_fails"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "keepalive_timeout"
+      "filter_causes_close"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "large_metadata"
+      "filter_context"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "max_concurrent_streams"
+      "filter_latency"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "max_connection_age"
+      "filter_status_code"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "max_message_length"
+      "graceful_server_shutdown"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "negative_deadline"
+      "high_initial_seqno"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "no_error_on_hotpath"
+      "hpack_size"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "no_logging"
+      "idempotent_request"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "no_op"
+      "invoke_large_request"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "payload"
+      "keepalive_timeout"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "ping_pong_streaming"
+      "large_metadata"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "registered_call"
+      "max_concurrent_streams"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "request_with_flags"
+      "max_connection_age"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "request_with_payload"
+      "max_message_length"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "resource_quota_server"
+      "negative_deadline"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "server_finishes_request"
+      "no_error_on_hotpath"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "shutdown_finishes_calls"
+      "no_logging"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "shutdown_finishes_tags"
+      "no_op"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "simple_cacheable_request"
+      "payload"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "simple_metadata"
+      "ping_pong_streaming"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "simple_request"
+      "registered_call"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "stream_compression_compressed_payload"
+      "request_with_flags"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "stream_compression_payload"
+      "request_with_payload"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "stream_compression_ping_pong_streaming"
+      "resource_quota_server"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "streaming_error_response"
+      "server_finishes_request"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "trailing_metadata"
+      "shutdown_finishes_calls"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "workaround_cronet_compression"
+      "shutdown_finishes_tags"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "write_buffering"
+      "simple_cacheable_request"
     ], 
     "ci_platforms": [
       "linux", 
   }, 
   {
     "args": [
-      "write_buffering_at_end"
+      "simple_metadata"
     ], 
     "ci_platforms": [
       "linux", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
   }, 
   {
     "args": [
-      "authority_not_supported"
+      "simple_request"
     ], 
     "ci_platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_full_test", 
+    "name": "h2_fd_test", 
     "platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
   }, 
   {
     "args": [
-      "bad_hostname"
+      "stream_compression_compressed_payload"
     ], 
     "ci_platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_full_test", 
+    "name": "h2_fd_test", 
     "platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
   }, 
   {
     "args": [
-      "bad_ping"
+      "stream_compression_payload"
     ], 
     "ci_platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_full_test", 
+    "name": "h2_fd_test", 
     "platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
   }, 
   {
     "args": [
-      "binary_metadata"
+      "stream_compression_ping_pong_streaming"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fd_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "streaming_error_response"
     ], 
     "ci_platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_full_test", 
+    "name": "h2_fd_test", 
     "platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
   }, 
   {
     "args": [
-      "call_creds"
+      "trailing_metadata"
     ], 
     "ci_platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_full_test", 
+    "name": "h2_fd_test", 
     "platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
   }, 
   {
     "args": [
-      "call_host_override"
+      "workaround_cronet_compression"
     ], 
     "ci_platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_full_test", 
+    "name": "h2_fd_test", 
     "platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
   }, 
   {
     "args": [
-      "cancel_after_accept"
+      "write_buffering"
     ], 
     "ci_platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_full_test", 
+    "name": "h2_fd_test", 
     "platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
   }, 
   {
     "args": [
-      "cancel_after_client_done"
+      "write_buffering_at_end"
     ], 
     "ci_platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_full_test", 
+    "name": "h2_fd_test", 
     "platforms": [
-      "windows", 
       "linux", 
       "mac", 
       "posix"
   }, 
   {
     "args": [
-      "cancel_after_invoke"
+      "authority_not_supported"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "cancel_after_round_trip"
+      "bad_hostname"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "cancel_before_invoke"
+      "bad_ping"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "cancel_in_a_vacuum"
+      "binary_metadata"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "cancel_with_status"
+      "call_creds"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "channelz"
+      "call_host_override"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "compressed_payload"
+      "cancel_after_accept"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "connectivity"
+      "cancel_after_client_done"
     ], 
     "ci_platforms": [
       "windows", 
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
     "name": "h2_full_test", 
   }, 
   {
     "args": [
-      "default_host"
+      "cancel_after_invoke"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "disappearing_server"
+      "cancel_after_round_trip"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
-    "flaky": true, 
+    "flaky": false, 
     "language": "c", 
     "name": "h2_full_test", 
     "platforms": [
   }, 
   {
     "args": [
-      "empty_batch"
+      "cancel_before_invoke"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "filter_call_init_fails"
+      "cancel_in_a_vacuum"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "filter_causes_close"
+      "cancel_with_status"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "filter_context"
+      "channelz"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "filter_latency"
+      "compressed_payload"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "filter_status_code"
+      "connectivity"
     ], 
     "ci_platforms": [
       "windows", 
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
     "language": "c", 
     "name": "h2_full_test", 
   }, 
   {
     "args": [
-      "graceful_server_shutdown"
+      "default_host"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "high_initial_seqno"
+      "disappearing_server"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
-    "flaky": false, 
+    "flaky": true, 
     "language": "c", 
     "name": "h2_full_test", 
     "platforms": [
   }, 
   {
     "args": [
-      "hpack_size"
+      "empty_batch"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "idempotent_request"
+      "filter_call_init_fails"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "invoke_large_request"
+      "filter_causes_close"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "filter_context"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "keepalive_timeout"
+      "filter_latency"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "large_metadata"
+      "filter_status_code"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "max_concurrent_streams"
+      "graceful_server_shutdown"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "max_connection_age"
+      "high_initial_seqno"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "max_connection_idle"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_full_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "max_message_length"
+      "hpack_size"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "negative_deadline"
+      "idempotent_request"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "no_error_on_hotpath"
+      "invoke_large_request"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "no_logging"
+      "keepalive_timeout"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "no_op"
+      "large_metadata"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "payload"
+      "max_concurrent_streams"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "ping"
+      "max_connection_age"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "ping_pong_streaming"
+      "max_connection_idle"
     ], 
     "ci_platforms": [
       "windows", 
     ], 
     "cpu_cost": 0.1, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_full_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "registered_call"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
+    "exclude_iomgrs": [
+      "uv"
     ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
     "name": "h2_full_test", 
   }, 
   {
     "args": [
-      "request_with_flags"
+      "max_message_length"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "request_with_payload"
+      "negative_deadline"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "resource_quota_server"
+      "no_error_on_hotpath"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry"
+      "no_logging"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "retry_cancellation"
+      "no_op"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "retry_disabled"
+      "payload"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "retry_exceeds_buffer_size_in_initial_batch"
+      "ping"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_exceeds_buffer_size_in_subsequent_batch"
+      "ping_pong_streaming"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_non_retriable_status"
+      "registered_call"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "retry_non_retriable_status_before_recv_trailing_metadata_started"
+      "request_with_flags"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_recv_initial_metadata"
+      "request_with_payload"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_recv_message"
+      "resource_quota_server"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 0.1
+    "cpu_cost": 1.0
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "retry_server_pushback_delay"
+      "retry"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_server_pushback_disabled"
+      "retry_cancellation"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_streaming"
+      "retry_disabled"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_streaming_after_commit"
+      "retry_exceeds_buffer_size_in_initial_batch"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_streaming_succeeds_before_replay_finished"
+      "retry_exceeds_buffer_size_in_subsequent_batch"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_throttled"
+      "retry_non_retriable_status"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "retry_too_many_attempts"
+      "retry_non_retriable_status_before_recv_trailing_metadata_started"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "server_finishes_request"
+      "retry_recv_initial_metadata"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "shutdown_finishes_calls"
+      "retry_recv_message"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "shutdown_finishes_tags"
+      "retry_server_pushback_delay"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "simple_cacheable_request"
+      "retry_server_pushback_disabled"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "simple_delayed_request"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_full_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "simple_metadata"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_full_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "simple_request"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_full_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "stream_compression_compressed_payload"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_full_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "stream_compression_payload"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_full_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "stream_compression_ping_pong_streaming"
+      "retry_streaming"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "streaming_error_response"
+      "retry_streaming_after_commit"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "trailing_metadata"
+      "retry_streaming_succeeds_before_replay_finished"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "workaround_cronet_compression"
+      "retry_throttled"
     ], 
     "ci_platforms": [
       "windows", 
       "mac", 
       "posix"
     ], 
-    "cpu_cost": 1.0
+    "cpu_cost": 0.1
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
   }, 
   {
     "args": [
-      "write_buffering"
+      "retry_too_many_attempts"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "write_buffering_at_end"
+      "server_finishes_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "shutdown_finishes_calls"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "shutdown_finishes_tags"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_delayed_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "stream_compression_compressed_payload"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "stream_compression_payload"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "stream_compression_ping_pong_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "streaming_error_response"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "trailing_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "workaround_cronet_compression"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "write_buffering"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "write_buffering_at_end"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "cancel_after_accept"
+      "cancel_after_accept"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_after_client_done"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_after_invoke"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_after_round_trip"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_before_invoke"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_in_a_vacuum"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_with_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "channelz"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "compressed_payload"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "connectivity"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "default_host"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "disappearing_server"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": true, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "empty_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "filter_causes_close"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "filter_context"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "filter_latency"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "filter_status_code"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "graceful_server_shutdown"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "high_initial_seqno"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "hpack_size"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "idempotent_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "invoke_large_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "large_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "max_concurrent_streams"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "max_connection_age"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "max_connection_idle"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "max_message_length"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "negative_deadline"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "no_error_on_hotpath"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "no_op"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "payload"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "ping"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "ping_pong_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "registered_call"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "request_with_flags"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "request_with_payload"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "resource_quota_server"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status_before_recv_trailing_metadata_started"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "server_finishes_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "shutdown_finishes_calls"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "shutdown_finishes_tags"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_delayed_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "stream_compression_compressed_payload"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "stream_compression_payload"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "stream_compression_ping_pong_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "streaming_error_response"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "trailing_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "workaround_cronet_compression"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_spiffe_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "write_buffering"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "cancel_after_client_done"
+      "write_buffering_at_end"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
+      "authority_not_supported"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "bad_hostname"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "bad_ping"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "binary_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "call_creds"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "call_host_override"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_after_accept"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_after_client_done"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "cancel_after_invoke"
     ], 
     "ci_platforms": [
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     ], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": true, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     ], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
+    "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_spiffe_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "stream_compression_compressed_payload"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_spiffe_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "stream_compression_payload"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_spiffe_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "stream_compression_ping_pong_streaming"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_spiffe_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "streaming_error_response"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_spiffe_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "trailing_metadata"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_spiffe_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "workaround_cronet_compression"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_spiffe_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "write_buffering"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_spiffe_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "write_buffering_at_end"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "h2_spiffe_test", 
-    "platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [
-      "authority_not_supported"
-    ], 
-    "ci_platforms": [
-      "windows", 
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "language": "c", 
     "name": "h2_ssl_test", 
     "platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "bad_hostname"
+      "stream_compression_compressed_payload"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "bad_ping"
+      "stream_compression_payload"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "binary_metadata"
+      "stream_compression_ping_pong_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "streaming_error_response"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "call_creds"
+      "trailing_metadata"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "call_host_override"
+      "workaround_cronet_compression"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "cancel_after_accept"
+      "write_buffering"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
-      "cancel_after_client_done"
+      "write_buffering_at_end"
     ], 
     "ci_platforms": [
       "windows", 
   }, 
   {
     "args": [
+      "authority_not_supported"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cred_reload_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "bad_hostname"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cred_reload_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "bad_ping"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cred_reload_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "binary_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cred_reload_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "call_creds"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cred_reload_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "call_host_override"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cred_reload_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_after_accept"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cred_reload_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_after_client_done"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cred_reload_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "cancel_after_invoke"
     ], 
     "ci_platforms": [
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     ], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": true, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     ], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_ssl_test", 
+    "name": "h2_ssl_cred_reload_test", 
     "platforms": [
       "windows", 
       "linux", 
index bda567e..e650479 100755 (executable)
@@ -146,6 +146,8 @@ if __name__ == "__main__":
     invocation_id = args.invocation_id or _get_invocation_id()
     resultstore_actions = _get_resultstore_data(api_key, invocation_id)
 
+    # google.devtools.resultstore.v2.Action schema:
+    # https://github.com/googleapis/googleapis/blob/master/google/devtools/resultstore/v2/action.proto
     bq_rows = []
     for index, action in enumerate(resultstore_actions):
         # Filter out non-test related data, such as build results.
@@ -187,6 +189,8 @@ if __name__ == "__main__":
             }
         elif 'testSuite' not in action['testAction']:
             continue
+        elif 'tests' not in action['testAction']['testSuite']:
+            continue
         else:
             test_cases = action['testAction']['testSuite']['tests'][0][
                 'testSuite']['tests']
index 4e4d05c..a7fde30 100755 (executable)
@@ -96,7 +96,7 @@ def collect_latency(bm_name, args):
                     '--benchmark_filter=^%s$' % line,
                     '--benchmark_min_time=0.05'
                 ],
-                environ={'LATENCY_TRACE': '%s.trace' % fnize(line)},
+                environ={'GRPC_LATENCY_TRACE': '%s.trace' % fnize(line)},
                 shortname='profile-%s' % fnize(line)))
         profile_analysis.append(
             jobset.JobSpec(
index 1c4d20e..a7a9dbd 100755 (executable)
@@ -1064,33 +1064,6 @@ class ObjCLanguage(object):
             self.config.job_spec(
                 ['src/objective-c/tests/build_one_example.sh'],
                 timeout_seconds=10 * 60,
-                shortname='objc-build-example-helloworld',
-                cpu_cost=1e6,
-                environ={
-                    'SCHEME': 'HelloWorld',
-                    'EXAMPLE_PATH': 'examples/objective-c/helloworld'
-                }),
-            self.config.job_spec(
-                ['src/objective-c/tests/build_one_example.sh'],
-                timeout_seconds=10 * 60,
-                shortname='objc-build-example-routeguide',
-                cpu_cost=1e6,
-                environ={
-                    'SCHEME': 'RouteGuideClient',
-                    'EXAMPLE_PATH': 'examples/objective-c/route_guide'
-                }),
-            self.config.job_spec(
-                ['src/objective-c/tests/build_one_example.sh'],
-                timeout_seconds=10 * 60,
-                shortname='objc-build-example-authsample',
-                cpu_cost=1e6,
-                environ={
-                    'SCHEME': 'AuthSample',
-                    'EXAMPLE_PATH': 'examples/objective-c/auth_sample'
-                }),
-            self.config.job_spec(
-                ['src/objective-c/tests/build_one_example.sh'],
-                timeout_seconds=10 * 60,
                 shortname='objc-build-example-sample',
                 cpu_cost=1e6,
                 environ={
index d93add0..785dff3 100755 (executable)
@@ -197,6 +197,7 @@ def _create_test_jobs(extra_args=[], inner_jobs=_DEFAULT_INNER_JOBS):
         inner_jobs=inner_jobs,
         timeout_seconds=_CPP_RUNTESTS_TIMEOUT)
 
+    # C# tests on .NET desktop/mono
     test_jobs += _generate_jobs(
         languages=['csharp'],
         configs=['dbg', 'opt'],
@@ -204,6 +205,16 @@ def _create_test_jobs(extra_args=[], inner_jobs=_DEFAULT_INNER_JOBS):
         labels=['basictests', 'multilang'],
         extra_args=extra_args,
         inner_jobs=inner_jobs)
+    # C# tests on .NET core
+    test_jobs += _generate_jobs(
+        languages=['csharp'],
+        configs=['dbg', 'opt'],
+        platforms=['linux', 'macos', 'windows'],
+        arch='default',
+        compiler='coreclr',
+        labels=['basictests', 'multilang'],
+        extra_args=extra_args,
+        inner_jobs=inner_jobs)
 
     test_jobs += _generate_jobs(
         languages=['python'],
@@ -393,16 +404,6 @@ def _create_portability_test_jobs(extra_args=[],
         inner_jobs=inner_jobs)
 
     test_jobs += _generate_jobs(
-        languages=['csharp'],
-        configs=['dbg'],
-        platforms=['linux'],
-        arch='default',
-        compiler='coreclr',
-        labels=['portability', 'multilang'],
-        extra_args=extra_args,
-        inner_jobs=inner_jobs)
-
-    test_jobs += _generate_jobs(
         languages=['c'],
         configs=['dbg'],
         platforms=['linux'],
index 3601634..2017f58 100755 (executable)
@@ -35,7 +35,7 @@ git_submodule_hashes = {
 }
 
 _BAZEL_SKYLIB_DEP_NAME = 'bazel_skylib'
-_BAZEL_TOOLCHAINS_DEP_NAME = 'com_github_bazelbuild_bazeltoolchains'
+_BAZEL_TOOLCHAINS_DEP_NAME = 'bazel_toolchains'
 _TWISTED_TWISTED_DEP_NAME = 'com_github_twisted_twisted'
 _YAML_PYYAML_DEP_NAME = 'com_github_yaml_pyyaml'
 _TWISTED_INCREMENTAL_DEP_NAME = 'com_github_twisted_incremental'
index 549ae14..ce9ff0d 100755 (executable)
@@ -45,10 +45,6 @@ BANNED_EXCEPT = {
     'grpc_closure_sched(': ['src/core/lib/iomgr/closure.cc'],
     'grpc_closure_run(': ['src/core/lib/iomgr/closure.cc'],
     'grpc_closure_list_sched(': ['src/core/lib/iomgr/closure.cc'],
-    'gpr_getenv_silent(': [
-        'src/core/lib/gpr/log.cc', 'src/core/lib/gpr/env_linux.cc',
-        'src/core/lib/gpr/env_posix.cc', 'src/core/lib/gpr/env_windows.cc'
-    ],
 }
 
 errors = 0