diff --git a/BUILD b/BUILD index 7f3073a6ef4..9de86e8c5d3 100644 --- a/BUILD +++ b/BUILD @@ -2259,12 +2259,16 @@ grpc_cc_library( grpc_cc_library( name = "grpc++_test", + srcs = [ + "src/cpp/client/channel_test_peer.cc", + ], public_hdrs = [ "include/grpc++/test/mock_stream.h", "include/grpc++/test/server_context_test_spouse.h", + "include/grpcpp/test/channel_test_peer.h", + "include/grpcpp/test/default_reactor_test_peer.h", "include/grpcpp/test/mock_stream.h", "include/grpcpp/test/server_context_test_spouse.h", - "include/grpcpp/test/default_reactor_test_peer.h", ], deps = [ ":grpc++", diff --git a/CMakeLists.txt b/CMakeLists.txt index dfac08bdb89..d0973cffe4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10217,6 +10217,7 @@ add_executable(end2end_test ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/simple_messages.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/simple_messages.pb.h ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/simple_messages.grpc.pb.h + src/cpp/client/channel_test_peer.cc test/cpp/end2end/end2end_test.cc test/cpp/end2end/interceptors_util.cc test/cpp/end2end/test_service_impl.cc @@ -11903,6 +11904,7 @@ add_executable(mock_test ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/simple_messages.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/simple_messages.pb.h ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/simple_messages.grpc.pb.h + src/cpp/client/channel_test_peer.cc test/cpp/end2end/mock_test.cc third_party/googletest/googletest/src/gtest-all.cc third_party/googletest/googlemock/src/gmock-all.cc @@ -12879,6 +12881,7 @@ endif() if(gRPC_BUILD_TESTS) add_executable(server_context_test_spouse_test + src/cpp/client/channel_test_peer.cc test/cpp/test/server_context_test_spouse_test.cc third_party/googletest/googletest/src/gtest-all.cc third_party/googletest/googlemock/src/gmock-all.cc diff --git a/Makefile b/Makefile index cb51b9aa63c..2b6f2efe939 100644 --- a/Makefile +++ b/Makefile @@ -13870,6 +13870,7 @@ END2END_TEST_SRC = \ $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc \ $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc \ $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc \ + src/cpp/client/channel_test_peer.cc \ test/cpp/end2end/end2end_test.cc \ test/cpp/end2end/interceptors_util.cc \ test/cpp/end2end/test_service_impl.cc \ @@ -13911,6 +13912,8 @@ $(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/echo_messages.o: $(LIBDIR)/$(CONFIG) $(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/simple_messages.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)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a +$(OBJDIR)/$(CONFIG)/src/cpp/client/channel_test_peer.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)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a + $(OBJDIR)/$(CONFIG)/test/cpp/end2end/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 $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a $(OBJDIR)/$(CONFIG)/test/cpp/end2end/interceptors_util.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)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a @@ -13924,6 +13927,7 @@ ifneq ($(NO_DEPS),true) -include $(END2END_TEST_OBJS:.o=.dep) endif endif +$(OBJDIR)/$(CONFIG)/src/cpp/client/channel_test_peer.o: $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc $(OBJDIR)/$(CONFIG)/test/cpp/end2end/end2end_test.o: $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc $(OBJDIR)/$(CONFIG)/test/cpp/end2end/interceptors_util.o: $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc $(OBJDIR)/$(CONFIG)/test/cpp/end2end/test_service_impl.o: $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc @@ -15866,6 +15870,7 @@ MOCK_TEST_SRC = \ $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc \ $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc \ $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc \ + src/cpp/client/channel_test_peer.cc \ test/cpp/end2end/mock_test.cc \ MOCK_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(MOCK_TEST_SRC)))) @@ -15905,6 +15910,8 @@ $(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/echo_messages.o: $(LIBDIR)/$(CONFIG) $(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/simple_messages.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)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a +$(OBJDIR)/$(CONFIG)/src/cpp/client/channel_test_peer.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)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a + $(OBJDIR)/$(CONFIG)/test/cpp/end2end/mock_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)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a deps_mock_test: $(MOCK_TEST_OBJS:.o=.dep) @@ -15914,6 +15921,7 @@ ifneq ($(NO_DEPS),true) -include $(MOCK_TEST_OBJS:.o=.dep) endif endif +$(OBJDIR)/$(CONFIG)/src/cpp/client/channel_test_peer.o: $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc $(OBJDIR)/$(CONFIG)/test/cpp/end2end/mock_test.o: $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc @@ -17168,6 +17176,7 @@ $(OBJDIR)/$(CONFIG)/test/cpp/server/server_builder_with_socket_mutator_test.o: $ SERVER_CONTEXT_TEST_SPOUSE_TEST_SRC = \ + src/cpp/client/channel_test_peer.cc \ test/cpp/test/server_context_test_spouse_test.cc \ SERVER_CONTEXT_TEST_SPOUSE_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SERVER_CONTEXT_TEST_SPOUSE_TEST_SRC)))) @@ -17199,6 +17208,8 @@ endif endif +$(OBJDIR)/$(CONFIG)/src/cpp/client/channel_test_peer.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)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a + $(OBJDIR)/$(CONFIG)/test/cpp/test/server_context_test_spouse_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)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a deps_server_context_test_spouse_test: $(SERVER_CONTEXT_TEST_SPOUSE_TEST_OBJS:.o=.dep) diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index 6897600f027..b0d6e8a2ef6 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -5638,6 +5638,7 @@ targets: - src/proto/grpc/testing/echo.proto - src/proto/grpc/testing/echo_messages.proto - src/proto/grpc/testing/simple_messages.proto + - src/cpp/client/channel_test_peer.cc - test/cpp/end2end/end2end_test.cc - test/cpp/end2end/interceptors_util.cc - test/cpp/end2end/test_service_impl.cc @@ -6344,6 +6345,7 @@ targets: - src/proto/grpc/testing/echo.proto - src/proto/grpc/testing/echo_messages.proto - src/proto/grpc/testing/simple_messages.proto + - src/cpp/client/channel_test_peer.cc - test/cpp/end2end/mock_test.cc deps: - grpc++_test_util @@ -6807,6 +6809,7 @@ targets: language: c++ headers: [] src: + - src/cpp/client/channel_test_peer.cc - test/cpp/test/server_context_test_spouse_test.cc deps: - grpc++_test_util diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h index cb46477c19d..80c7f796d68 100644 --- a/include/grpc/grpc.h +++ b/include/grpc/grpc.h @@ -225,8 +225,8 @@ GRPCAPI void grpc_channel_ping(grpc_channel* channel, grpc_completion_queue* cq, void* tag, void* reserved); /** Pre-register a method/host pair on a channel. - method and host are not owned and must remain alive while the server is - running. */ + method and host are not owned and must remain alive while the channel is + alive. */ GRPCAPI void* grpc_channel_register_call(grpc_channel* channel, const char* method, const char* host, void* reserved); diff --git a/include/grpcpp/channel_impl.h b/include/grpcpp/channel_impl.h index 4ec913ae523..6cc63ba09aa 100644 --- a/include/grpcpp/channel_impl.h +++ b/include/grpcpp/channel_impl.h @@ -33,6 +33,9 @@ struct grpc_channel; namespace grpc { +namespace testing { +class ChannelTestPeer; +} // namespace testing std::shared_ptr<::grpc_impl::Channel> CreateChannelInternal( const grpc::string& host, grpc_channel* c_channel, @@ -71,6 +74,7 @@ class Channel final : public ::grpc::ChannelInterface, private: template friend class ::grpc::internal::BlockingUnaryCallImpl; + friend class ::grpc::testing::ChannelTestPeer; friend void experimental::ChannelResetConnectionBackoff(Channel* channel); friend std::shared_ptr grpc::CreateChannelInternal( const grpc::string& host, grpc_channel* c_channel, diff --git a/include/grpcpp/test/channel_test_peer.h b/include/grpcpp/test/channel_test_peer.h new file mode 100644 index 00000000000..e41bbfa4604 --- /dev/null +++ b/include/grpcpp/test/channel_test_peer.h @@ -0,0 +1,44 @@ +/* + * + * Copyright 2020 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_TEST_CHANNEL_TEST_PEER_H +#define GRPCPP_TEST_CHANNEL_TEST_PEER_H + +#include + +namespace grpc { +namespace testing { + +/// A test-only class to access private members of Channel. +class ChannelTestPeer { + public: + explicit ChannelTestPeer(Channel* channel) : channel_(channel) {} + + /// Provide the gRPC Core channel + grpc_channel* channel() const { return channel_->c_channel_; } + int registered_calls() const; + int registration_attempts() const; + + private: + Channel* channel_; // not owned +}; + +} // namespace testing +} // namespace grpc + +#endif // GRPCPP_TEST_CHANNEL_TEST_PEER_H diff --git a/src/core/lib/surface/channel.cc b/src/core/lib/surface/channel.cc index 9f5abca9c47..b9296ba02cf 100644 --- a/src/core/lib/surface/channel.cc +++ b/src/core/lib/surface/channel.cc @@ -54,12 +54,6 @@ * (OK, Cancelled, Unknown). */ #define NUM_CACHED_STATUS_ELEMS 3 -typedef struct registered_call { - grpc_mdelem path; - grpc_mdelem authority; - struct registered_call* next; -} registered_call; - static void destroy_channel(void* arg, grpc_error* error); grpc_channel* grpc_channel_create_with_builder( @@ -90,8 +84,7 @@ grpc_channel* grpc_channel_create_with_builder( channel->target = target; channel->resource_user = resource_user; channel->is_client = grpc_channel_stack_type_is_client(channel_stack_type); - gpr_mu_init(&channel->registered_call_mu); - channel->registered_calls = nullptr; + channel->registration_table.Init(); gpr_atm_no_barrier_store( &channel->call_size_estimate, @@ -417,35 +410,65 @@ grpc_call* grpc_channel_create_pollset_set_call( deadline); } +namespace grpc_core { + +RegisteredCall::RegisteredCall(const char* method, const char* host) { + path = grpc_mdelem_from_slices(GRPC_MDSTR_PATH, + grpc_core::ExternallyManagedSlice(method)); + authority = + host ? grpc_mdelem_from_slices(GRPC_MDSTR_AUTHORITY, + grpc_core::ExternallyManagedSlice(host)) + : GRPC_MDNULL; +} + +// TODO(vjpai): Delete copy-constructor when allowed by all supported compilers. +RegisteredCall::RegisteredCall(const RegisteredCall& other) { + path = other.path; + authority = other.authority; + GRPC_MDELEM_REF(path); + GRPC_MDELEM_REF(authority); +} + +RegisteredCall::RegisteredCall(RegisteredCall&& other) { + path = other.path; + authority = other.authority; + other.path = GRPC_MDNULL; + other.authority = GRPC_MDNULL; +} + +RegisteredCall::~RegisteredCall() { + GRPC_MDELEM_UNREF(path); + GRPC_MDELEM_UNREF(authority); +} + +} // namespace grpc_core + void* grpc_channel_register_call(grpc_channel* channel, const char* method, const char* host, void* reserved) { - registered_call* rc = - static_cast(gpr_malloc(sizeof(registered_call))); GRPC_API_TRACE( "grpc_channel_register_call(channel=%p, method=%s, host=%s, reserved=%p)", 4, (channel, method, host, reserved)); GPR_ASSERT(!reserved); grpc_core::ExecCtx exec_ctx; - rc->path = grpc_mdelem_from_slices(GRPC_MDSTR_PATH, - grpc_core::ExternallyManagedSlice(method)); - rc->authority = - host ? grpc_mdelem_from_slices(GRPC_MDSTR_AUTHORITY, - grpc_core::ExternallyManagedSlice(host)) - : GRPC_MDNULL; - gpr_mu_lock(&channel->registered_call_mu); - rc->next = channel->registered_calls; - channel->registered_calls = rc; - gpr_mu_unlock(&channel->registered_call_mu); - - return rc; + grpc_core::MutexLock lock(&channel->registration_table->mu); + channel->registration_table->method_registration_attempts++; + auto key = std::make_pair(host, method); + auto rc_posn = channel->registration_table->map.find(key); + if (rc_posn != channel->registration_table->map.end()) { + return &rc_posn->second; + } + auto insertion_result = channel->registration_table->map.insert( + {key, grpc_core::RegisteredCall(method, host)}); + return &insertion_result.first->second; } grpc_call* grpc_channel_create_registered_call( grpc_channel* channel, grpc_call* parent_call, uint32_t propagation_mask, grpc_completion_queue* completion_queue, void* registered_call_handle, gpr_timespec deadline, void* reserved) { - registered_call* rc = static_cast(registered_call_handle); + grpc_core::RegisteredCall* rc = + static_cast(registered_call_handle); GRPC_API_TRACE( "grpc_channel_create_registered_call(" "channel=%p, parent_call=%p, propagation_mask=%x, completion_queue=%p, " @@ -486,18 +509,11 @@ static void destroy_channel(void* arg, grpc_error* /*error*/) { channel->channelz_node.reset(); } grpc_channel_stack_destroy(CHANNEL_STACK_FROM_CHANNEL(channel)); - while (channel->registered_calls) { - registered_call* rc = channel->registered_calls; - channel->registered_calls = rc->next; - GRPC_MDELEM_UNREF(rc->path); - GRPC_MDELEM_UNREF(rc->authority); - gpr_free(rc); - } + channel->registration_table.Destroy(); if (channel->resource_user != nullptr) { grpc_resource_user_free(channel->resource_user, GRPC_RESOURCE_QUOTA_CHANNEL_SIZE); } - gpr_mu_destroy(&channel->registered_call_mu); gpr_free(channel->target); gpr_free(channel); // See comment in grpc_channel_create() for why we do this. diff --git a/src/core/lib/surface/channel.h b/src/core/lib/surface/channel.h index 280fc96a0bc..39c9b55f8d8 100644 --- a/src/core/lib/surface/channel.h +++ b/src/core/lib/surface/channel.h @@ -21,10 +21,14 @@ #include +#include + #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/channel/channel_stack_builder.h" #include "src/core/lib/channel/channelz.h" +#include "src/core/lib/gprpp/manual_constructor.h" #include "src/core/lib/surface/channel_stack_type.h" +#include "src/core/lib/transport/metadata.h" grpc_channel* grpc_channel_create(const char* target, const grpc_channel_args* args, @@ -62,7 +66,30 @@ grpc_core::channelz::ChannelNode* grpc_channel_get_channelz_node( size_t grpc_channel_get_call_size_estimate(grpc_channel* channel); void grpc_channel_update_call_size_estimate(grpc_channel* channel, size_t size); -struct registered_call; +namespace grpc_core { + +struct RegisteredCall { + grpc_mdelem path; + grpc_mdelem authority; + + explicit RegisteredCall(const char* method, const char* host); + // TODO(vjpai): delete copy constructor once all supported compilers allow + // std::map value_type to be MoveConstructible. + RegisteredCall(const RegisteredCall& other); + RegisteredCall(RegisteredCall&& other); + + ~RegisteredCall(); +}; + +struct CallRegistrationTable { + grpc_core::Mutex mu; + std::map, RegisteredCall> + map /* GUARDED_BY(mu) */; + int method_registration_attempts /* GUARDED_BY(mu) */ = 0; +}; + +} // namespace grpc_core + struct grpc_channel { int is_client; grpc_compression_options compression_options; @@ -70,9 +97,13 @@ struct grpc_channel { gpr_atm call_size_estimate; grpc_resource_user* resource_user; - gpr_mu registered_call_mu; - registered_call* registered_calls; - + // TODO(vjpai): Once the grpc_channel is allocated via new rather than malloc, + // expand the members of the CallRegistrationTable directly into + // the grpc_channel. For now it is kept separate so that all the + // manual constructing can be done with a single call rather than + // a separate manual construction for each field. + grpc_core::ManualConstructor + registration_table; grpc_core::RefCountedPtr channelz_node; char* target; diff --git a/src/cpp/client/channel_test_peer.cc b/src/cpp/client/channel_test_peer.cc new file mode 100644 index 00000000000..f921352a24a --- /dev/null +++ b/src/cpp/client/channel_test_peer.cc @@ -0,0 +1,40 @@ +/* + * + * Copyright 2020 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include "src/core/lib/surface/channel.h" + +namespace grpc { +namespace testing { + +int ChannelTestPeer::registered_calls() const { + grpc_core::MutexLock lock(&channel_->c_channel_->registration_table->mu); + return static_cast(channel_->c_channel_->registration_table->map.size()); +} + +int ChannelTestPeer::registration_attempts() const { + grpc_core::MutexLock lock(&channel_->c_channel_->registration_table->mu); + return channel_->c_channel_->registration_table->method_registration_attempts; +} + +} // namespace testing +} // namespace grpc diff --git a/test/cpp/end2end/BUILD b/test/cpp/end2end/BUILD index 5288c5622be..6116e9e9a05 100644 --- a/test/cpp/end2end/BUILD +++ b/test/cpp/end2end/BUILD @@ -225,6 +225,7 @@ grpc_cc_library( "//:gpr", "//:grpc", "//:grpc++", + "//:grpc++_test", "//src/proto/grpc/testing:echo_messages_proto", "//src/proto/grpc/testing:echo_proto", "//src/proto/grpc/testing/duplicate:echo_duplicate_proto", diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc index 33113942151..94d895a06c4 100644 --- a/test/cpp/end2end/end2end_test.cc +++ b/test/cpp/end2end/end2end_test.cc @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -812,6 +813,19 @@ TEST_P(End2endTest, MultipleRpcs) { } } +TEST_P(End2endTest, ManyStubs) { + MAYBE_SKIP_TEST; + ResetStub(); + ChannelTestPeer peer(channel_.get()); + int registered_calls_pre = peer.registered_calls(); + int registration_attempts_pre = peer.registration_attempts(); + for (int i = 0; i < 1000; ++i) { + grpc::testing::EchoTestService::NewStub(channel_); + } + EXPECT_EQ(peer.registered_calls(), registered_calls_pre); + EXPECT_GT(peer.registration_attempts(), registration_attempts_pre); +} + TEST_P(End2endTest, EmptyBinaryMetadata) { MAYBE_SKIP_TEST; ResetStub();