diff options
94 files changed, 4521 insertions, 4726 deletions
@@ -586,8 +586,6 @@ grpc_cc_library( "src/core/lib/iomgr/endpoint_pair_windows.c", "src/core/lib/iomgr/error.c", "src/core/lib/iomgr/ev_epoll1_linux.c", - "src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c", - "src/core/lib/iomgr/ev_epoll_thread_pool_linux.c", "src/core/lib/iomgr/ev_epollex_linux.c", "src/core/lib/iomgr/ev_epollsig_linux.c", "src/core/lib/iomgr/ev_poll_posix.c", @@ -718,8 +716,6 @@ grpc_cc_library( "src/core/lib/iomgr/error.h", "src/core/lib/iomgr/error_internal.h", "src/core/lib/iomgr/ev_epoll1_linux.h", - "src/core/lib/iomgr/ev_epoll_limited_pollers_linux.h", - "src/core/lib/iomgr/ev_epoll_thread_pool_linux.h", "src/core/lib/iomgr/ev_epollex_linux.h", "src/core/lib/iomgr/ev_epollsig_linux.h", "src/core/lib/iomgr/ev_poll_posix.h", diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f374ff947..f77cd5b131 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -421,6 +421,9 @@ add_dependencies(buildtests_c ev_epollsig_linux_test) endif() add_dependencies(buildtests_c fake_resolver_test) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) +add_dependencies(buildtests_c fake_transport_security_test) +endif() +if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_c fd_conservation_posix_test) endif() if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) @@ -530,6 +533,9 @@ add_dependencies(buildtests_c sockaddr_utils_test) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_c socket_utils_test) endif() +if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) +add_dependencies(buildtests_c ssl_transport_security_test) +endif() add_dependencies(buildtests_c status_conversion_test) add_dependencies(buildtests_c stream_compression_test) add_dependencies(buildtests_c stream_owned_slice_test) @@ -976,8 +982,6 @@ add_library(grpc src/core/lib/iomgr/endpoint_pair_windows.c src/core/lib/iomgr/error.c src/core/lib/iomgr/ev_epoll1_linux.c - src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c - src/core/lib/iomgr/ev_epoll_thread_pool_linux.c src/core/lib/iomgr/ev_epollex_linux.c src/core/lib/iomgr/ev_epollsig_linux.c src/core/lib/iomgr/ev_poll_posix.c @@ -1328,8 +1332,6 @@ add_library(grpc_cronet src/core/lib/iomgr/endpoint_pair_windows.c src/core/lib/iomgr/error.c src/core/lib/iomgr/ev_epoll1_linux.c - src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c - src/core/lib/iomgr/ev_epoll_thread_pool_linux.c src/core/lib/iomgr/ev_epollex_linux.c src/core/lib/iomgr/ev_epollsig_linux.c src/core/lib/iomgr/ev_poll_posix.c @@ -1648,8 +1650,6 @@ add_library(grpc_test_util src/core/lib/iomgr/endpoint_pair_windows.c src/core/lib/iomgr/error.c src/core/lib/iomgr/ev_epoll1_linux.c - src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c - src/core/lib/iomgr/ev_epoll_thread_pool_linux.c src/core/lib/iomgr/ev_epollex_linux.c src/core/lib/iomgr/ev_epollsig_linux.c src/core/lib/iomgr/ev_poll_posix.c @@ -1912,8 +1912,6 @@ add_library(grpc_test_util_unsecure src/core/lib/iomgr/endpoint_pair_windows.c src/core/lib/iomgr/error.c src/core/lib/iomgr/ev_epoll1_linux.c - src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c - src/core/lib/iomgr/ev_epoll_thread_pool_linux.c src/core/lib/iomgr/ev_epollex_linux.c src/core/lib/iomgr/ev_epollsig_linux.c src/core/lib/iomgr/ev_poll_posix.c @@ -2162,8 +2160,6 @@ add_library(grpc_unsecure src/core/lib/iomgr/endpoint_pair_windows.c src/core/lib/iomgr/error.c src/core/lib/iomgr/ev_epoll1_linux.c - src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c - src/core/lib/iomgr/ev_epoll_thread_pool_linux.c src/core/lib/iomgr/ev_epollex_linux.c src/core/lib/iomgr/ev_epollsig_linux.c src/core/lib/iomgr/ev_poll_posix.c @@ -2864,8 +2860,6 @@ add_library(grpc++_cronet src/core/lib/iomgr/endpoint_pair_windows.c src/core/lib/iomgr/error.c src/core/lib/iomgr/ev_epoll1_linux.c - src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c - src/core/lib/iomgr/ev_epoll_thread_pool_linux.c src/core/lib/iomgr/ev_epollex_linux.c src/core/lib/iomgr/ev_epollsig_linux.c src/core/lib/iomgr/ev_poll_posix.c @@ -5972,6 +5966,39 @@ endif (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) +add_executable(fake_transport_security_test + test/core/tsi/fake_transport_security_test.c + test/core/tsi/transport_security_test_lib.c +) + + +target_include_directories(fake_transport_security_test + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + PRIVATE ${BORINGSSL_ROOT_DIR}/include + PRIVATE ${PROTOBUF_ROOT_DIR}/src + PRIVATE ${BENCHMARK_ROOT_DIR}/include + PRIVATE ${ZLIB_ROOT_DIR} + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib + PRIVATE ${CARES_BUILD_INCLUDE_DIR} + PRIVATE ${CARES_INCLUDE_DIR} + PRIVATE ${CARES_PLATFORM_INCLUDE_DIR} + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/cares/cares + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include +) + +target_link_libraries(fake_transport_security_test + ${_gRPC_ALLTARGETS_LIBRARIES} + gpr_test_util + gpr + grpc +) + +endif() +endif (gRPC_BUILD_TESTS) +if (gRPC_BUILD_TESTS) +if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) + add_executable(fd_conservation_posix_test test/core/iomgr/fd_conservation_posix_test.c ) @@ -8717,6 +8744,39 @@ target_link_libraries(socket_utils_test endif() endif (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS) +if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) + +add_executable(ssl_transport_security_test + test/core/tsi/ssl_transport_security_test.c + test/core/tsi/transport_security_test_lib.c +) + + +target_include_directories(ssl_transport_security_test + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + PRIVATE ${BORINGSSL_ROOT_DIR}/include + PRIVATE ${PROTOBUF_ROOT_DIR}/src + PRIVATE ${BENCHMARK_ROOT_DIR}/include + PRIVATE ${ZLIB_ROOT_DIR} + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib + PRIVATE ${CARES_BUILD_INCLUDE_DIR} + PRIVATE ${CARES_INCLUDE_DIR} + PRIVATE ${CARES_PLATFORM_INCLUDE_DIR} + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/cares/cares + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include +) + +target_link_libraries(ssl_transport_security_test + ${_gRPC_ALLTARGETS_LIBRARIES} + gpr_test_util + gpr + grpc +) + +endif() +endif (gRPC_BUILD_TESTS) +if (gRPC_BUILD_TESTS) add_executable(status_conversion_test test/core/transport/status_conversion_test.c @@ -976,6 +976,7 @@ endpoint_pair_test: $(BINDIR)/$(CONFIG)/endpoint_pair_test error_test: $(BINDIR)/$(CONFIG)/error_test ev_epollsig_linux_test: $(BINDIR)/$(CONFIG)/ev_epollsig_linux_test fake_resolver_test: $(BINDIR)/$(CONFIG)/fake_resolver_test +fake_transport_security_test: $(BINDIR)/$(CONFIG)/fake_transport_security_test fd_conservation_posix_test: $(BINDIR)/$(CONFIG)/fd_conservation_posix_test fd_posix_test: $(BINDIR)/$(CONFIG)/fd_posix_test fling_client: $(BINDIR)/$(CONFIG)/fling_client @@ -1075,6 +1076,7 @@ sockaddr_resolver_test: $(BINDIR)/$(CONFIG)/sockaddr_resolver_test sockaddr_utils_test: $(BINDIR)/$(CONFIG)/sockaddr_utils_test socket_utils_test: $(BINDIR)/$(CONFIG)/socket_utils_test ssl_server_fuzzer: $(BINDIR)/$(CONFIG)/ssl_server_fuzzer +ssl_transport_security_test: $(BINDIR)/$(CONFIG)/ssl_transport_security_test status_conversion_test: $(BINDIR)/$(CONFIG)/status_conversion_test stream_compression_test: $(BINDIR)/$(CONFIG)/stream_compression_test stream_owned_slice_test: $(BINDIR)/$(CONFIG)/stream_owned_slice_test @@ -1366,6 +1368,7 @@ buildtests_c: privatelibs_c \ $(BINDIR)/$(CONFIG)/error_test \ $(BINDIR)/$(CONFIG)/ev_epollsig_linux_test \ $(BINDIR)/$(CONFIG)/fake_resolver_test \ + $(BINDIR)/$(CONFIG)/fake_transport_security_test \ $(BINDIR)/$(CONFIG)/fd_conservation_posix_test \ $(BINDIR)/$(CONFIG)/fd_posix_test \ $(BINDIR)/$(CONFIG)/fling_client \ @@ -1448,6 +1451,7 @@ buildtests_c: privatelibs_c \ $(BINDIR)/$(CONFIG)/sockaddr_resolver_test \ $(BINDIR)/$(CONFIG)/sockaddr_utils_test \ $(BINDIR)/$(CONFIG)/socket_utils_test \ + $(BINDIR)/$(CONFIG)/ssl_transport_security_test \ $(BINDIR)/$(CONFIG)/status_conversion_test \ $(BINDIR)/$(CONFIG)/stream_compression_test \ $(BINDIR)/$(CONFIG)/stream_owned_slice_test \ @@ -1788,6 +1792,8 @@ test_c: buildtests_c $(Q) $(BINDIR)/$(CONFIG)/ev_epollsig_linux_test || ( echo test ev_epollsig_linux_test failed ; exit 1 ) $(E) "[RUN] Testing fake_resolver_test" $(Q) $(BINDIR)/$(CONFIG)/fake_resolver_test || ( echo test fake_resolver_test failed ; exit 1 ) + $(E) "[RUN] Testing fake_transport_security_test" + $(Q) $(BINDIR)/$(CONFIG)/fake_transport_security_test || ( echo test fake_transport_security_test failed ; exit 1 ) $(E) "[RUN] Testing fd_conservation_posix_test" $(Q) $(BINDIR)/$(CONFIG)/fd_conservation_posix_test || ( echo test fd_conservation_posix_test failed ; exit 1 ) $(E) "[RUN] Testing fd_posix_test" @@ -1936,6 +1942,8 @@ test_c: buildtests_c $(Q) $(BINDIR)/$(CONFIG)/sockaddr_utils_test || ( echo test sockaddr_utils_test failed ; exit 1 ) $(E) "[RUN] Testing socket_utils_test" $(Q) $(BINDIR)/$(CONFIG)/socket_utils_test || ( echo test socket_utils_test failed ; exit 1 ) + $(E) "[RUN] Testing ssl_transport_security_test" + $(Q) $(BINDIR)/$(CONFIG)/ssl_transport_security_test || ( echo test ssl_transport_security_test failed ; exit 1 ) $(E) "[RUN] Testing status_conversion_test" $(Q) $(BINDIR)/$(CONFIG)/status_conversion_test || ( echo test status_conversion_test failed ; exit 1 ) $(E) "[RUN] Testing stream_compression_test" @@ -2921,8 +2929,6 @@ LIBGRPC_SRC = \ src/core/lib/iomgr/endpoint_pair_windows.c \ src/core/lib/iomgr/error.c \ src/core/lib/iomgr/ev_epoll1_linux.c \ - src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c \ - src/core/lib/iomgr/ev_epoll_thread_pool_linux.c \ src/core/lib/iomgr/ev_epollex_linux.c \ src/core/lib/iomgr/ev_epollsig_linux.c \ src/core/lib/iomgr/ev_poll_posix.c \ @@ -3271,8 +3277,6 @@ LIBGRPC_CRONET_SRC = \ src/core/lib/iomgr/endpoint_pair_windows.c \ src/core/lib/iomgr/error.c \ src/core/lib/iomgr/ev_epoll1_linux.c \ - src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c \ - src/core/lib/iomgr/ev_epoll_thread_pool_linux.c \ src/core/lib/iomgr/ev_epollex_linux.c \ src/core/lib/iomgr/ev_epollsig_linux.c \ src/core/lib/iomgr/ev_poll_posix.c \ @@ -3588,8 +3592,6 @@ LIBGRPC_TEST_UTIL_SRC = \ src/core/lib/iomgr/endpoint_pair_windows.c \ src/core/lib/iomgr/error.c \ src/core/lib/iomgr/ev_epoll1_linux.c \ - src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c \ - src/core/lib/iomgr/ev_epoll_thread_pool_linux.c \ src/core/lib/iomgr/ev_epollex_linux.c \ src/core/lib/iomgr/ev_epollsig_linux.c \ src/core/lib/iomgr/ev_poll_posix.c \ @@ -3841,8 +3843,6 @@ LIBGRPC_TEST_UTIL_UNSECURE_SRC = \ src/core/lib/iomgr/endpoint_pair_windows.c \ src/core/lib/iomgr/error.c \ src/core/lib/iomgr/ev_epoll1_linux.c \ - src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c \ - src/core/lib/iomgr/ev_epoll_thread_pool_linux.c \ src/core/lib/iomgr/ev_epollex_linux.c \ src/core/lib/iomgr/ev_epollsig_linux.c \ src/core/lib/iomgr/ev_poll_posix.c \ @@ -4067,8 +4067,6 @@ LIBGRPC_UNSECURE_SRC = \ src/core/lib/iomgr/endpoint_pair_windows.c \ src/core/lib/iomgr/error.c \ src/core/lib/iomgr/ev_epoll1_linux.c \ - src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c \ - src/core/lib/iomgr/ev_epoll_thread_pool_linux.c \ src/core/lib/iomgr/ev_epollex_linux.c \ src/core/lib/iomgr/ev_epollsig_linux.c \ src/core/lib/iomgr/ev_poll_posix.c \ @@ -4752,8 +4750,6 @@ LIBGRPC++_CRONET_SRC = \ src/core/lib/iomgr/endpoint_pair_windows.c \ src/core/lib/iomgr/error.c \ src/core/lib/iomgr/ev_epoll1_linux.c \ - src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c \ - src/core/lib/iomgr/ev_epoll_thread_pool_linux.c \ src/core/lib/iomgr/ev_epollex_linux.c \ src/core/lib/iomgr/ev_epollsig_linux.c \ src/core/lib/iomgr/ev_poll_posix.c \ @@ -9608,6 +9604,41 @@ endif endif +FAKE_TRANSPORT_SECURITY_TEST_SRC = \ + test/core/tsi/fake_transport_security_test.c \ + test/core/tsi/transport_security_test_lib.c \ + +FAKE_TRANSPORT_SECURITY_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(FAKE_TRANSPORT_SECURITY_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/fake_transport_security_test: openssl_dep_error + +else + + + +$(BINDIR)/$(CONFIG)/fake_transport_security_test: $(FAKE_TRANSPORT_SECURITY_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(FAKE_TRANSPORT_SECURITY_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/fake_transport_security_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/tsi/fake_transport_security_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a + +$(OBJDIR)/$(CONFIG)/test/core/tsi/transport_security_test_lib.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a + +deps_fake_transport_security_test: $(FAKE_TRANSPORT_SECURITY_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(FAKE_TRANSPORT_SECURITY_TEST_OBJS:.o=.dep) +endif +endif + + FD_CONSERVATION_POSIX_TEST_SRC = \ test/core/iomgr/fd_conservation_posix_test.c \ @@ -12776,6 +12807,41 @@ endif endif +SSL_TRANSPORT_SECURITY_TEST_SRC = \ + test/core/tsi/ssl_transport_security_test.c \ + test/core/tsi/transport_security_test_lib.c \ + +SSL_TRANSPORT_SECURITY_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SSL_TRANSPORT_SECURITY_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/ssl_transport_security_test: openssl_dep_error + +else + + + +$(BINDIR)/$(CONFIG)/ssl_transport_security_test: $(SSL_TRANSPORT_SECURITY_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(SSL_TRANSPORT_SECURITY_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/ssl_transport_security_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/tsi/ssl_transport_security_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a + +$(OBJDIR)/$(CONFIG)/test/core/tsi/transport_security_test_lib.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a + +deps_ssl_transport_security_test: $(SSL_TRANSPORT_SECURITY_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(SSL_TRANSPORT_SECURITY_TEST_OBJS:.o=.dep) +endif +endif + + STATUS_CONVERSION_TEST_SRC = \ test/core/transport/status_conversion_test.c \ @@ -39,6 +39,11 @@ bind( ) bind( + name = "gmock", + actual = "@submodule_gtest//:gmock", +) + +bind( name = "benchmark", actual = "@submodule_benchmark//:benchmark", ) diff --git a/binding.gyp b/binding.gyp index ef2f3d0223..0110951305 100644 --- a/binding.gyp +++ b/binding.gyp @@ -681,8 +681,6 @@ 'src/core/lib/iomgr/endpoint_pair_windows.c', 'src/core/lib/iomgr/error.c', 'src/core/lib/iomgr/ev_epoll1_linux.c', - 'src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c', - 'src/core/lib/iomgr/ev_epoll_thread_pool_linux.c', 'src/core/lib/iomgr/ev_epollex_linux.c', 'src/core/lib/iomgr/ev_epollsig_linux.c', 'src/core/lib/iomgr/ev_poll_posix.c', diff --git a/build.yaml b/build.yaml index 5264b4adb8..089b8423a8 100644 --- a/build.yaml +++ b/build.yaml @@ -209,8 +209,6 @@ filegroups: - src/core/lib/iomgr/endpoint_pair_windows.c - src/core/lib/iomgr/error.c - src/core/lib/iomgr/ev_epoll1_linux.c - - src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c - - src/core/lib/iomgr/ev_epoll_thread_pool_linux.c - src/core/lib/iomgr/ev_epollex_linux.c - src/core/lib/iomgr/ev_epollsig_linux.c - src/core/lib/iomgr/ev_poll_posix.c @@ -361,8 +359,6 @@ filegroups: - src/core/lib/iomgr/error.h - src/core/lib/iomgr/error_internal.h - src/core/lib/iomgr/ev_epoll1_linux.h - - src/core/lib/iomgr/ev_epoll_limited_pollers_linux.h - - src/core/lib/iomgr/ev_epoll_thread_pool_linux.h - src/core/lib/iomgr/ev_epollex_linux.h - src/core/lib/iomgr/ev_epollsig_linux.h - src/core/lib/iomgr/ev_poll_posix.h @@ -752,6 +748,7 @@ filegroups: - test/core/util/trickle_endpoint.c deps: - gpr_test_util + - gpr uses: - grpc_base - grpc_client_channel @@ -926,6 +923,14 @@ filegroups: - third_party/nanopb/pb_common.h - third_party/nanopb/pb_decode.h - third_party/nanopb/pb_encode.h +- name: transport_security_test_lib + build: test + headers: + - test/core/tsi/transport_security_test_lib.h + src: + - test/core/tsi/transport_security_test_lib.c + deps: + - grpc - name: tsi headers: - src/core/tsi/fake_transport_security.h @@ -2036,6 +2041,21 @@ targets: - grpc - gpr_test_util - gpr +- name: fake_transport_security_test + build: test + language: c + src: + - test/core/tsi/fake_transport_security_test.c + deps: + - gpr_test_util + - gpr + - grpc + filegroups: + - transport_security_test_lib + platforms: + - linux + - posix + - mac - name: fd_conservation_posix_test build: test language: c @@ -3107,6 +3127,21 @@ targets: corpus_dirs: - test/core/security/corpus/ssl_server_corpus maxlen: 2048 +- name: ssl_transport_security_test + build: test + language: c + src: + - test/core/tsi/ssl_transport_security_test.c + deps: + - gpr_test_util + - gpr + - grpc + filegroups: + - transport_security_test_lib + platforms: + - linux + - posix + - mac - name: status_conversion_test build: test language: c @@ -110,8 +110,6 @@ if test "$PHP_GRPC" != "no"; then src/core/lib/iomgr/endpoint_pair_windows.c \ src/core/lib/iomgr/error.c \ src/core/lib/iomgr/ev_epoll1_linux.c \ - src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c \ - src/core/lib/iomgr/ev_epoll_thread_pool_linux.c \ src/core/lib/iomgr/ev_epollex_linux.c \ src/core/lib/iomgr/ev_epollsig_linux.c \ src/core/lib/iomgr/ev_poll_posix.c \ diff --git a/config.w32 b/config.w32 index 1a64d23ac7..1b1a82b1ae 100644 --- a/config.w32 +++ b/config.w32 @@ -87,8 +87,6 @@ if (PHP_GRPC != "no") { "src\\core\\lib\\iomgr\\endpoint_pair_windows.c " + "src\\core\\lib\\iomgr\\error.c " + "src\\core\\lib\\iomgr\\ev_epoll1_linux.c " + - "src\\core\\lib\\iomgr\\ev_epoll_limited_pollers_linux.c " + - "src\\core\\lib\\iomgr\\ev_epoll_thread_pool_linux.c " + "src\\core\\lib\\iomgr\\ev_epollex_linux.c " + "src\\core\\lib\\iomgr\\ev_epollsig_linux.c " + "src\\core\\lib\\iomgr\\ev_poll_posix.c " + diff --git a/doc/environment_variables.md b/doc/environment_variables.md index 0dae0543ff..54c9c8e731 100644 --- a/doc/environment_variables.md +++ b/doc/environment_variables.md @@ -114,3 +114,11 @@ some configuration as environment variables that can be set. - native (default)- a DNS resolver based around getaddrinfo(), creates a new thread to perform name resolution - ares - a DNS resolver based around the c-ares library + +* GRPC_DISABLE_CHANNEL_CONNECTIVITY_WATCHER + The channel connectivity watcher uses one extra thread to check the channel + state every 500 ms on the client side. It can help reconnect disconnected + client channels (mostly due to idleness), so that the next RPC on this channel + won't fail. Set to 1 to turn off this watcher and save a thread. Please note + this is a temporary work-around, it will be removed in the future once we have + support for automatically reestablishing failed connections. diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index c76b520808..fdab4b3f81 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -342,8 +342,6 @@ Pod::Spec.new do |s| 'src/core/lib/iomgr/error.h', 'src/core/lib/iomgr/error_internal.h', 'src/core/lib/iomgr/ev_epoll1_linux.h', - 'src/core/lib/iomgr/ev_epoll_limited_pollers_linux.h', - 'src/core/lib/iomgr/ev_epoll_thread_pool_linux.h', 'src/core/lib/iomgr/ev_epollex_linux.h', 'src/core/lib/iomgr/ev_epollsig_linux.h', 'src/core/lib/iomgr/ev_poll_posix.h', @@ -494,8 +492,6 @@ Pod::Spec.new do |s| 'src/core/lib/iomgr/endpoint_pair_windows.c', 'src/core/lib/iomgr/error.c', 'src/core/lib/iomgr/ev_epoll1_linux.c', - 'src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c', - 'src/core/lib/iomgr/ev_epoll_thread_pool_linux.c', 'src/core/lib/iomgr/ev_epollex_linux.c', 'src/core/lib/iomgr/ev_epollsig_linux.c', 'src/core/lib/iomgr/ev_poll_posix.c', @@ -841,8 +837,6 @@ Pod::Spec.new do |s| 'src/core/lib/iomgr/error.h', 'src/core/lib/iomgr/error_internal.h', 'src/core/lib/iomgr/ev_epoll1_linux.h', - 'src/core/lib/iomgr/ev_epoll_limited_pollers_linux.h', - 'src/core/lib/iomgr/ev_epoll_thread_pool_linux.h', 'src/core/lib/iomgr/ev_epollex_linux.h', 'src/core/lib/iomgr/ev_epollsig_linux.h', 'src/core/lib/iomgr/ev_poll_posix.h', @@ -997,6 +991,7 @@ Pod::Spec.new do |s| 'test/core/end2end/end2end_tests.{c,h}', 'test/core/end2end/end2end_test_utils.c', 'test/core/end2end/tests/*.{c,h}', + 'test/core/end2end/fixtures/*.h', 'test/core/end2end/data/*.{c,h}', 'test/core/util/debugger_macros.{c,h}', 'test/core/util/test_config.{c,h}', @@ -70,6 +70,7 @@ EXPORTS grpc_channel_check_connectivity_state grpc_channel_num_external_connectivity_watchers grpc_channel_watch_connectivity_state + grpc_channel_support_connectivity_watcher grpc_channel_create_call grpc_channel_ping grpc_channel_register_call diff --git a/grpc.gemspec b/grpc.gemspec index 61376b8165..c441107643 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -33,12 +33,12 @@ Gem::Specification.new do |s| s.add_development_dependency 'bundler', '~> 1.9' s.add_development_dependency 'facter', '~> 2.4' s.add_development_dependency 'logging', '~> 2.0' - s.add_development_dependency 'simplecov', '~> 0.9' - s.add_development_dependency 'rake', '~> 10.4' + s.add_development_dependency 'simplecov', '~> 0.14.1' + s.add_development_dependency 'rake', '~> 12.0' s.add_development_dependency 'rake-compiler', '~> 1.0' s.add_development_dependency 'rake-compiler-dock', '~> 0.5.1' - s.add_development_dependency 'rspec', '~> 3.2' - s.add_development_dependency 'rubocop', '~> 0.30.0' + s.add_development_dependency 'rspec', '~> 3.6' + s.add_development_dependency 'rubocop', '~> 0.49.1' s.add_development_dependency 'signet', '~> 0.7.0' s.extensions = %w(src/ruby/ext/grpc/extconf.rb) @@ -274,8 +274,6 @@ Gem::Specification.new do |s| s.files += %w( src/core/lib/iomgr/error.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_epoll_limited_pollers_linux.h ) - s.files += %w( src/core/lib/iomgr/ev_epoll_thread_pool_linux.h ) s.files += %w( src/core/lib/iomgr/ev_epollex_linux.h ) s.files += %w( src/core/lib/iomgr/ev_epollsig_linux.h ) s.files += %w( src/core/lib/iomgr/ev_poll_posix.h ) @@ -430,8 +428,6 @@ Gem::Specification.new do |s| s.files += %w( src/core/lib/iomgr/endpoint_pair_windows.c ) s.files += %w( src/core/lib/iomgr/error.c ) s.files += %w( src/core/lib/iomgr/ev_epoll1_linux.c ) - s.files += %w( src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c ) - s.files += %w( src/core/lib/iomgr/ev_epoll_thread_pool_linux.c ) s.files += %w( src/core/lib/iomgr/ev_epollex_linux.c ) s.files += %w( src/core/lib/iomgr/ev_epollsig_linux.c ) s.files += %w( src/core/lib/iomgr/ev_poll_posix.c ) @@ -247,8 +247,6 @@ 'src/core/lib/iomgr/endpoint_pair_windows.c', 'src/core/lib/iomgr/error.c', 'src/core/lib/iomgr/ev_epoll1_linux.c', - 'src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c', - 'src/core/lib/iomgr/ev_epoll_thread_pool_linux.c', 'src/core/lib/iomgr/ev_epollex_linux.c', 'src/core/lib/iomgr/ev_epollsig_linux.c', 'src/core/lib/iomgr/ev_poll_posix.c', @@ -548,8 +546,6 @@ 'src/core/lib/iomgr/endpoint_pair_windows.c', 'src/core/lib/iomgr/error.c', 'src/core/lib/iomgr/ev_epoll1_linux.c', - 'src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c', - 'src/core/lib/iomgr/ev_epoll_thread_pool_linux.c', 'src/core/lib/iomgr/ev_epollex_linux.c', 'src/core/lib/iomgr/ev_epollsig_linux.c', 'src/core/lib/iomgr/ev_poll_posix.c', @@ -754,8 +750,6 @@ 'src/core/lib/iomgr/endpoint_pair_windows.c', 'src/core/lib/iomgr/error.c', 'src/core/lib/iomgr/ev_epoll1_linux.c', - 'src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c', - 'src/core/lib/iomgr/ev_epoll_thread_pool_linux.c', 'src/core/lib/iomgr/ev_epollex_linux.c', 'src/core/lib/iomgr/ev_epollsig_linux.c', 'src/core/lib/iomgr/ev_poll_posix.c', @@ -945,8 +939,6 @@ 'src/core/lib/iomgr/endpoint_pair_windows.c', 'src/core/lib/iomgr/error.c', 'src/core/lib/iomgr/ev_epoll1_linux.c', - 'src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c', - 'src/core/lib/iomgr/ev_epoll_thread_pool_linux.c', 'src/core/lib/iomgr/ev_epollex_linux.c', 'src/core/lib/iomgr/ev_epollsig_linux.c', 'src/core/lib/iomgr/ev_poll_posix.c', diff --git a/include/grpc++/impl/codegen/sync_stream.h b/include/grpc++/impl/codegen/sync_stream.h index 3fa208963d..c1784f1820 100644 --- a/include/grpc++/impl/codegen/sync_stream.h +++ b/include/grpc++/impl/codegen/sync_stream.h @@ -244,7 +244,7 @@ class ClientWriterInterface : public ClientStreamingInterface, public WriterInterface<W> { public: /// Half close writing from the client. (signal that the stream of messages - /// coming from the clinet is complete). + /// coming from the client is complete). /// Blocks until currently-pending writes are completed. /// Thread safe with respect to \a ReaderInterface::Read operations only /// @@ -375,7 +375,7 @@ class ClientReaderWriterInterface : public ClientStreamingInterface, virtual void WaitForInitialMetadata() = 0; /// Half close writing from the client. (signal that the stream of messages - /// coming from the clinet is complete). + /// coming from the client is complete). /// Blocks until currently-pending writes are completed. /// Thread-safe with respect to \a ReaderInterface::Read /// diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h index 943d6e4891..b562167b5f 100644 --- a/include/grpc/grpc.h +++ b/include/grpc/grpc.h @@ -178,6 +178,9 @@ GRPCAPI void grpc_channel_watch_connectivity_state( grpc_channel *channel, grpc_connectivity_state last_observed_state, gpr_timespec deadline, grpc_completion_queue *cq, void *tag); +/** Check whether a grpc channel supports connectivity watcher */ +GRPCAPI int grpc_channel_support_connectivity_watcher(grpc_channel *channel); + /** Create a call given a grpc_channel, in order to call 'method'. All completions are sent to 'completion_queue'. 'method' and 'host' need only live through the invocation of this function. diff --git a/include/grpc/impl/codegen/slice.h b/include/grpc/impl/codegen/slice.h index a04c683a55..128fa8e121 100644 --- a/include/grpc/impl/codegen/slice.h +++ b/include/grpc/impl/codegen/slice.h @@ -62,7 +62,12 @@ typedef struct grpc_slice_refcount { struct grpc_slice_refcount *sub_refcount; } grpc_slice_refcount; -#define GRPC_SLICE_INLINED_SIZE (sizeof(size_t) + sizeof(uint8_t *) - 1) +/* 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 *) + +#define GRPC_SLICE_INLINED_SIZE \ + (sizeof(size_t) + sizeof(uint8_t *) - 1 + GRPC_SLICE_INLINE_EXTRA_SIZE) /** A grpc_slice s, if initialized, represents the byte range s.bytes[0..s.length-1]. diff --git a/package.xml b/package.xml index f588fc5a20..4b1f42dd02 100644 --- a/package.xml +++ b/package.xml @@ -284,8 +284,6 @@ <file baseinstalldir="/" name="src/core/lib/iomgr/error.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_epoll_limited_pollers_linux.h" role="src" /> - <file baseinstalldir="/" name="src/core/lib/iomgr/ev_epoll_thread_pool_linux.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/ev_epollex_linux.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/ev_epollsig_linux.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/ev_poll_posix.h" role="src" /> @@ -440,8 +438,6 @@ <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_pair_windows.c" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/error.c" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/ev_epoll1_linux.c" role="src" /> - <file baseinstalldir="/" name="src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c" role="src" /> - <file baseinstalldir="/" name="src/core/lib/iomgr/ev_epoll_thread_pool_linux.c" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/ev_epollex_linux.c" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/ev_epollsig_linux.c" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/ev_poll_posix.c" role="src" /> diff --git a/src/core/ext/census/context.c b/src/core/ext/census/context.c index 1019b287d7..9b25a32e36 100644 --- a/src/core/ext/census/context.c +++ b/src/core/ext/census/context.c @@ -141,7 +141,7 @@ static char *decode_tag(struct raw_tag *tag, char *header, int offset) { // Make a copy (in 'to') of an existing tag_set. static void tag_set_copy(struct tag_set *to, const struct tag_set *from) { memcpy(to, from, sizeof(struct tag_set)); - to->kvm = gpr_malloc(to->kvm_size); + to->kvm = (char *)gpr_malloc(to->kvm_size); memcpy(to->kvm, from->kvm, from->kvm_used); } @@ -184,7 +184,7 @@ static bool tag_set_add_tag(struct tag_set *tags, const census_tag *tag, if (tags->kvm_used + tag_size > tags->kvm_size) { // allocate new memory if needed tags->kvm_size += 2 * CENSUS_MAX_TAG_KV_LEN + TAG_HEADER_SIZE; - char *new_kvm = gpr_malloc(tags->kvm_size); + char *new_kvm = (char *)gpr_malloc(tags->kvm_size); if (tags->kvm_used > 0) memcpy(new_kvm, tags->kvm, tags->kvm_used); gpr_free(tags->kvm); tags->kvm = new_kvm; @@ -274,7 +274,8 @@ static void tag_set_flatten(struct tag_set *tags) { census_context *census_context_create(const census_context *base, const census_tag *tags, int ntags, census_context_status const **status) { - census_context *context = gpr_malloc(sizeof(census_context)); + census_context *context = + (census_context *)gpr_malloc(sizeof(census_context)); // If we are given a base, copy it into our new tag set. Otherwise set it // to zero/NULL everything. if (base == NULL) { @@ -459,7 +460,7 @@ static void tag_set_decode(struct tag_set *tags, const char *buffer, } tags->kvm_used = size - header_size; tags->kvm_size = tags->kvm_used + CENSUS_MAX_TAG_KV_LEN; - tags->kvm = gpr_malloc(tags->kvm_size); + tags->kvm = (char *)gpr_malloc(tags->kvm_size); if (tag_header_size != TAG_HEADER_SIZE) { // something new in the tag information. I don't understand it, so // don't copy it over. @@ -481,7 +482,8 @@ static void tag_set_decode(struct tag_set *tags, const char *buffer, } census_context *census_context_decode(const char *buffer, size_t size) { - census_context *context = gpr_malloc(sizeof(census_context)); + census_context *context = + (census_context *)gpr_malloc(sizeof(census_context)); memset(&context->tags[LOCAL_TAGS], 0, sizeof(struct tag_set)); if (buffer == NULL) { memset(&context->tags[PROPAGATED_TAGS], 0, sizeof(struct tag_set)); diff --git a/src/core/ext/census/grpc_filter.c b/src/core/ext/census/grpc_filter.c index 3e10f61154..b37ab90389 100644 --- a/src/core/ext/census/grpc_filter.c +++ b/src/core/ext/census/grpc_filter.c @@ -60,8 +60,8 @@ static void extract_and_annotate_method_tag(grpc_metadata_batch *md, static void client_mutate_op(grpc_call_element *elem, grpc_transport_stream_op_batch *op) { - call_data *calld = elem->call_data; - channel_data *chand = elem->channel_data; + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; if (op->send_initial_metadata) { extract_and_annotate_method_tag( op->payload->send_initial_metadata.send_initial_metadata, calld, chand); @@ -78,9 +78,9 @@ static void client_start_transport_op(grpc_exec_ctx *exec_ctx, static void server_on_done_recv(grpc_exec_ctx *exec_ctx, void *ptr, grpc_error *error) { GPR_TIMER_BEGIN("census-server:server_on_done_recv", 0); - grpc_call_element *elem = ptr; - call_data *calld = elem->call_data; - channel_data *chand = elem->channel_data; + grpc_call_element *elem = (grpc_call_element *)ptr; + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; if (error == GRPC_ERROR_NONE) { extract_and_annotate_method_tag(calld->recv_initial_metadata, calld, chand); } @@ -90,7 +90,7 @@ static void server_on_done_recv(grpc_exec_ctx *exec_ctx, void *ptr, static void server_mutate_op(grpc_call_element *elem, grpc_transport_stream_op_batch *op) { - call_data *calld = elem->call_data; + call_data *calld = (call_data *)elem->call_data; if (op->recv_initial_metadata) { /* substitute our callback for the op callback */ calld->recv_initial_metadata = @@ -117,7 +117,7 @@ static void server_start_transport_op(grpc_exec_ctx *exec_ctx, static grpc_error *client_init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, const grpc_call_element_args *args) { - call_data *d = elem->call_data; + call_data *d = (call_data *)elem->call_data; GPR_ASSERT(d != NULL); memset(d, 0, sizeof(*d)); d->start_ts = args->start_time; @@ -128,7 +128,7 @@ static void client_destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, const grpc_call_final_info *final_info, grpc_closure *ignored) { - call_data *d = elem->call_data; + call_data *d = (call_data *)elem->call_data; GPR_ASSERT(d != NULL); /* TODO(hongyu): record rpc client stats and census_rpc_end_op here */ } @@ -136,7 +136,7 @@ static void client_destroy_call_elem(grpc_exec_ctx *exec_ctx, static grpc_error *server_init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, const grpc_call_element_args *args) { - call_data *d = elem->call_data; + call_data *d = (call_data *)elem->call_data; GPR_ASSERT(d != NULL); memset(d, 0, sizeof(*d)); d->start_ts = args->start_time; @@ -150,7 +150,7 @@ static void server_destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, const grpc_call_final_info *final_info, grpc_closure *ignored) { - call_data *d = elem->call_data; + call_data *d = (call_data *)elem->call_data; GPR_ASSERT(d != NULL); /* TODO(hongyu): record rpc server stats and census_tracing_end_op here */ } @@ -158,14 +158,14 @@ static void server_destroy_call_elem(grpc_exec_ctx *exec_ctx, static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, grpc_channel_element_args *args) { - channel_data *chand = elem->channel_data; + channel_data *chand = (channel_data *)elem->channel_data; GPR_ASSERT(chand != NULL); return GRPC_ERROR_NONE; } static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem) { - channel_data *chand = elem->channel_data; + channel_data *chand = (channel_data *)elem->channel_data; GPR_ASSERT(chand != NULL); } diff --git a/src/core/ext/census/mlog.c b/src/core/ext/census/mlog.c index 937ceb101b..4b8c8466b3 100644 --- a/src/core/ext/census/mlog.c +++ b/src/core/ext/census/mlog.c @@ -467,7 +467,8 @@ void census_log_initialize(size_t size_in_mb, int discard_old_records) { g_log.blocks = (cl_block*)gpr_malloc_aligned( g_log.num_blocks * sizeof(cl_block), GPR_CACHELINE_SIZE_LOG); memset(g_log.blocks, 0, g_log.num_blocks * sizeof(cl_block)); - g_log.buffer = gpr_malloc(g_log.num_blocks * CENSUS_LOG_MAX_RECORD_SIZE); + g_log.buffer = + (char*)gpr_malloc(g_log.num_blocks * CENSUS_LOG_MAX_RECORD_SIZE); memset(g_log.buffer, 0, g_log.num_blocks * CENSUS_LOG_MAX_RECORD_SIZE); cl_block_list_initialize(&g_log.free_block_list); cl_block_list_initialize(&g_log.dirty_block_list); diff --git a/src/core/ext/census/resource.c b/src/core/ext/census/resource.c index 1a676f0e1e..44a887231c 100644 --- a/src/core/ext/census/resource.c +++ b/src/core/ext/census/resource.c @@ -87,7 +87,7 @@ static bool validate_string(pb_istream_t *stream, const pb_field_t *field, gpr_log(GPR_INFO, "Zero-length Resource name."); return false; } - vresource->name = gpr_malloc(stream->bytes_left + 1); + vresource->name = (char *)gpr_malloc(stream->bytes_left + 1); vresource->name[stream->bytes_left] = '\0'; if (!pb_read(stream, (uint8_t *)vresource->name, stream->bytes_left)) { return false; @@ -106,7 +106,7 @@ static bool validate_string(pb_istream_t *stream, const pb_field_t *field, if (stream->bytes_left == 0) { return true; } - vresource->description = gpr_malloc(stream->bytes_left + 1); + vresource->description = (char *)gpr_malloc(stream->bytes_left + 1); vresource->description[stream->bytes_left] = '\0'; if (!pb_read(stream, (uint8_t *)vresource->description, stream->bytes_left)) { @@ -134,7 +134,8 @@ static bool validate_units_helper(pb_istream_t *stream, int *count, // Have to allocate a new array of values. Normal case is 0 or 1, so // this should normally not be an issue. google_census_Resource_BasicUnit *new_bup = - gpr_malloc((size_t)*count * sizeof(google_census_Resource_BasicUnit)); + (google_census_Resource_BasicUnit *)gpr_malloc( + (size_t)*count * sizeof(google_census_Resource_BasicUnit)); if (*count != 1) { memcpy(new_bup, *bup, (size_t)(*count - 1) * sizeof(google_census_Resource_BasicUnit)); @@ -207,7 +208,8 @@ size_t allocate_resource(void) { // Expand resources if needed. if (n_resources == n_defined_resources) { size_t new_n_resources = n_resources ? n_resources * 2 : 2; - resource **new_resources = gpr_malloc(new_n_resources * sizeof(resource *)); + resource **new_resources = + (resource **)gpr_malloc(new_n_resources * sizeof(resource *)); if (n_resources != 0) { memcpy(new_resources, resources, n_resources * sizeof(resource *)); } @@ -226,7 +228,7 @@ size_t allocate_resource(void) { } } GPR_ASSERT(id < n_resources && resources[id] == NULL); - resources[id] = gpr_malloc(sizeof(resource)); + resources[id] = (resource *)gpr_malloc(sizeof(resource)); memset(resources[id], 0, sizeof(resource)); n_defined_resources++; next_id = (id + 1) % n_resources; @@ -276,22 +278,24 @@ int32_t define_resource(const resource *base) { gpr_mu_lock(&resource_lock); size_t id = allocate_resource(); size_t len = strlen(base->name) + 1; - resources[id]->name = gpr_malloc(len); + resources[id]->name = (char *)gpr_malloc(len); memcpy(resources[id]->name, base->name, len); if (base->description) { len = strlen(base->description) + 1; - resources[id]->description = gpr_malloc(len); + resources[id]->description = (char *)gpr_malloc(len); memcpy(resources[id]->description, base->description, len); } resources[id]->prefix = base->prefix; resources[id]->n_numerators = base->n_numerators; len = (size_t)base->n_numerators * sizeof(*base->numerators); - resources[id]->numerators = gpr_malloc(len); + resources[id]->numerators = + (google_census_Resource_BasicUnit *)gpr_malloc(len); memcpy(resources[id]->numerators, base->numerators, len); resources[id]->n_denominators = base->n_denominators; if (base->n_denominators != 0) { len = (size_t)base->n_denominators * sizeof(*base->denominators); - resources[id]->denominators = gpr_malloc(len); + resources[id]->denominators = + (google_census_Resource_BasicUnit *)gpr_malloc(len); memcpy(resources[id]->denominators, base->denominators, len); } gpr_mu_unlock(&resource_lock); diff --git a/src/core/ext/filters/client_channel/channel_connectivity.c b/src/core/ext/filters/client_channel/channel_connectivity.c index b83c95275f..0a9e90d12e 100644 --- a/src/core/ext/filters/client_channel/channel_connectivity.c +++ b/src/core/ext/filters/client_channel/channel_connectivity.c @@ -191,6 +191,12 @@ static void watcher_timer_init(grpc_exec_ctx *exec_ctx, void *arg, gpr_free(wa); } +int grpc_channel_support_connectivity_watcher(grpc_channel *channel) { + grpc_channel_element *client_channel_elem = + grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel)); + return client_channel_elem->filter != &grpc_client_channel_filter ? 0 : 1; +} + void grpc_channel_watch_connectivity_state( grpc_channel *channel, grpc_connectivity_state last_observed_state, gpr_timespec deadline, grpc_completion_queue *cq, void *tag) { diff --git a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c index 56ed4371a9..3ff081a514 100644 --- a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c +++ b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c @@ -32,6 +32,7 @@ #include "src/core/ext/filters/client_channel/parse_address.h" #include "src/core/ext/filters/client_channel/resolver_registry.h" #include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/closure.h" #include "src/core/lib/iomgr/combiner.h" #include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/iomgr/unix_sockets_posix.h" @@ -125,7 +126,6 @@ static const grpc_resolver_vtable fake_resolver_vtable = { struct grpc_fake_resolver_response_generator { fake_resolver* resolver; // Set by the fake_resolver constructor to itself. - grpc_channel_args* next_response; gpr_refcount refcount; }; @@ -151,19 +151,26 @@ void grpc_fake_resolver_response_generator_unref( } } -static void set_response_cb(grpc_exec_ctx* exec_ctx, void* arg, - grpc_error* error) { - grpc_fake_resolver_response_generator* generator = - (grpc_fake_resolver_response_generator*)arg; +typedef struct set_response_closure_arg { + grpc_closure set_response_closure; + grpc_fake_resolver_response_generator* generator; + grpc_channel_args* next_response; +} set_response_closure_arg; + +static void set_response_closure_fn(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { + set_response_closure_arg* closure_arg = arg; + grpc_fake_resolver_response_generator* generator = closure_arg->generator; fake_resolver* r = generator->resolver; if (r->next_results != NULL) { grpc_channel_args_destroy(exec_ctx, r->next_results); } - r->next_results = generator->next_response; + r->next_results = closure_arg->next_response; if (r->results_upon_error != NULL) { grpc_channel_args_destroy(exec_ctx, r->results_upon_error); } - r->results_upon_error = grpc_channel_args_copy(generator->next_response); + r->results_upon_error = grpc_channel_args_copy(closure_arg->next_response); + gpr_free(closure_arg); fake_resolver_maybe_finish_next_locked(exec_ctx, r); } @@ -171,12 +178,15 @@ void grpc_fake_resolver_response_generator_set_response( grpc_exec_ctx* exec_ctx, grpc_fake_resolver_response_generator* generator, grpc_channel_args* next_response) { GPR_ASSERT(generator->resolver != NULL); - generator->next_response = grpc_channel_args_copy(next_response); - GRPC_CLOSURE_SCHED( - exec_ctx, GRPC_CLOSURE_CREATE(set_response_cb, generator, - grpc_combiner_scheduler( - generator->resolver->base.combiner)), - GRPC_ERROR_NONE); + set_response_closure_arg* closure_arg = gpr_zalloc(sizeof(*closure_arg)); + closure_arg->generator = generator; + closure_arg->next_response = grpc_channel_args_copy(next_response); + GRPC_CLOSURE_SCHED(exec_ctx, + GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, + set_response_closure_fn, closure_arg, + grpc_combiner_scheduler( + generator->resolver->base.combiner)), + GRPC_ERROR_NONE); } static void* response_generator_arg_copy(void* p) { diff --git a/src/core/ext/filters/max_age/max_age_filter.c b/src/core/ext/filters/max_age/max_age_filter.c index 16c85a70d0..450f67746f 100644 --- a/src/core/ext/filters/max_age/max_age_filter.c +++ b/src/core/ext/filters/max_age/max_age_filter.c @@ -273,7 +273,7 @@ static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx, static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, const grpc_call_final_info* final_info, grpc_closure* ignored) { - channel_data* chand = elem->channel_data; + channel_data* chand = (channel_data*)elem->channel_data; decrease_call_count(exec_ctx, chand); } diff --git a/src/core/ext/transport/chttp2/transport/writing.c b/src/core/ext/transport/chttp2/transport/writing.c index 80eb51ff0d..711938b278 100644 --- a/src/core/ext/transport/chttp2/transport/writing.c +++ b/src/core/ext/transport/chttp2/transport/writing.c @@ -154,17 +154,8 @@ static uint32_t target_write_size(grpc_chttp2_transport *t) { } // Returns true if initial_metadata contains only default headers. -// -// TODO(roth): The fact that we hard-code these particular headers here -// is fairly ugly. Need some better way to know which headers are -// default, maybe via a bit in the static metadata table? static bool is_default_initial_metadata(grpc_metadata_batch *initial_metadata) { - int num_default_fields = - (initial_metadata->idx.named.status != NULL) + - (initial_metadata->idx.named.content_type != NULL) + - (initial_metadata->idx.named.grpc_encoding != NULL) + - (initial_metadata->idx.named.grpc_accept_encoding != NULL); - return (size_t)num_default_fields == initial_metadata->list.count; + return initial_metadata->list.default_count == initial_metadata->list.count; } grpc_chttp2_begin_write_result grpc_chttp2_begin_write( diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.c b/src/core/ext/transport/cronet/transport/cronet_transport.c index 09420d92e7..587a3b83b5 100644 --- a/src/core/ext/transport/cronet/transport/cronet_transport.c +++ b/src/core/ext/transport/cronet/transport/cronet_transport.c @@ -187,9 +187,34 @@ struct stream_obj { /* Mutex to protect storage */ gpr_mu mu; + + /* Refcount object of the stream */ + grpc_stream_refcount *refcount; }; typedef struct stream_obj stream_obj; +#ifndef NDEBUG +#define GRPC_CRONET_STREAM_REF(stream, reason) \ + grpc_cronet_stream_ref((stream), (reason)) +#define GRPC_CRONET_STREAM_UNREF(exec_ctx, stream, reason) \ + grpc_cronet_stream_unref((exec_ctx), (stream), (reason)) +void grpc_cronet_stream_ref(stream_obj *s, const char *reason) { + grpc_stream_ref(s->refcount, reason); +} +void grpc_cronet_stream_unref(grpc_exec_ctx *exec_ctx, stream_obj *s, + const char *reason) { + grpc_stream_unref(exec_ctx, s->refcount, reason); +} +#else +#define GRPC_CRONET_STREAM_REF(stream, reason) grpc_cronet_stream_ref((stream)) +#define GRPC_CRONET_STREAM_UNREF(exec_ctx, stream, reason) \ + grpc_cronet_stream_unref((exec_ctx), (stream)) +void grpc_cronet_stream_ref(stream_obj *s) { grpc_stream_ref(s->refcount); } +void grpc_cronet_stream_unref(grpc_exec_ctx *exec_ctx, stream_obj *s) { + grpc_stream_unref(exec_ctx, s->refcount); +} +#endif + static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx, struct op_and_state *oas); @@ -346,13 +371,12 @@ static void remove_from_storage(struct stream_obj *s, This can get executed from the Cronet network thread via cronet callback or on the application supplied thread via the perform_stream_op function. */ -static void execute_from_storage(stream_obj *s) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; +static void execute_from_storage(grpc_exec_ctx *exec_ctx, stream_obj *s) { gpr_mu_lock(&s->mu); for (struct op_and_state *curr = s->storage.head; curr != NULL;) { CRONET_LOG(GPR_DEBUG, "calling op at %p. done = %d", curr, curr->done); GPR_ASSERT(curr->done == 0); - enum e_op_result result = execute_stream_op(&exec_ctx, curr); + enum e_op_result result = execute_stream_op(exec_ctx, curr); CRONET_LOG(GPR_DEBUG, "execute_stream_op[%p] returns %s", curr, op_result_string(result)); /* if this op is done, then remove it and free memory */ @@ -369,7 +393,6 @@ static void execute_from_storage(stream_obj *s) { } } gpr_mu_unlock(&s->mu); - grpc_exec_ctx_finish(&exec_ctx); } /* @@ -377,6 +400,8 @@ static void execute_from_storage(stream_obj *s) { */ static void on_failed(bidirectional_stream *stream, int net_error) { CRONET_LOG(GPR_DEBUG, "on_failed(%p, %d)", stream, net_error); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + stream_obj *s = (stream_obj *)stream->annotation; gpr_mu_lock(&s->mu); bidirectional_stream_destroy(s->cbs); @@ -392,7 +417,9 @@ static void on_failed(bidirectional_stream *stream, int net_error) { } null_and_maybe_free_read_buffer(s); gpr_mu_unlock(&s->mu); - execute_from_storage(s); + execute_from_storage(&exec_ctx, s); + GRPC_CRONET_STREAM_UNREF(&exec_ctx, s, "cronet transport"); + grpc_exec_ctx_finish(&exec_ctx); } /* @@ -400,6 +427,8 @@ static void on_failed(bidirectional_stream *stream, int net_error) { */ static void on_canceled(bidirectional_stream *stream) { CRONET_LOG(GPR_DEBUG, "on_canceled(%p)", stream); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + stream_obj *s = (stream_obj *)stream->annotation; gpr_mu_lock(&s->mu); bidirectional_stream_destroy(s->cbs); @@ -415,7 +444,9 @@ static void on_canceled(bidirectional_stream *stream) { } null_and_maybe_free_read_buffer(s); gpr_mu_unlock(&s->mu); - execute_from_storage(s); + execute_from_storage(&exec_ctx, s); + GRPC_CRONET_STREAM_UNREF(&exec_ctx, s, "cronet transport"); + grpc_exec_ctx_finish(&exec_ctx); } /* @@ -423,6 +454,8 @@ static void on_canceled(bidirectional_stream *stream) { */ static void on_succeeded(bidirectional_stream *stream) { CRONET_LOG(GPR_DEBUG, "on_succeeded(%p)", stream); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + stream_obj *s = (stream_obj *)stream->annotation; gpr_mu_lock(&s->mu); bidirectional_stream_destroy(s->cbs); @@ -430,7 +463,9 @@ static void on_succeeded(bidirectional_stream *stream) { s->cbs = NULL; null_and_maybe_free_read_buffer(s); gpr_mu_unlock(&s->mu); - execute_from_storage(s); + execute_from_storage(&exec_ctx, s); + GRPC_CRONET_STREAM_UNREF(&exec_ctx, s, "cronet transport"); + grpc_exec_ctx_finish(&exec_ctx); } /* @@ -438,6 +473,7 @@ static void on_succeeded(bidirectional_stream *stream) { */ static void on_stream_ready(bidirectional_stream *stream) { CRONET_LOG(GPR_DEBUG, "W: on_stream_ready(%p)", stream); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; stream_obj *s = (stream_obj *)stream->annotation; grpc_cronet_transport *t = (grpc_cronet_transport *)s->curr_ct; gpr_mu_lock(&s->mu); @@ -457,7 +493,8 @@ static void on_stream_ready(bidirectional_stream *stream) { } } gpr_mu_unlock(&s->mu); - execute_from_storage(s); + execute_from_storage(&exec_ctx, s); + grpc_exec_ctx_finish(&exec_ctx); } /* @@ -513,14 +550,15 @@ static void on_response_headers_received( s->state.pending_read_from_cronet = true; } gpr_mu_unlock(&s->mu); + execute_from_storage(&exec_ctx, s); grpc_exec_ctx_finish(&exec_ctx); - execute_from_storage(s); } /* Cronet callback */ static void on_write_completed(bidirectional_stream *stream, const char *data) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; stream_obj *s = (stream_obj *)stream->annotation; CRONET_LOG(GPR_DEBUG, "W: on_write_completed(%p, %s)", stream, data); gpr_mu_lock(&s->mu); @@ -530,7 +568,8 @@ static void on_write_completed(bidirectional_stream *stream, const char *data) { } s->state.state_callback_received[OP_SEND_MESSAGE] = true; gpr_mu_unlock(&s->mu); - execute_from_storage(s); + execute_from_storage(&exec_ctx, s); + grpc_exec_ctx_finish(&exec_ctx); } /* @@ -538,6 +577,7 @@ static void on_write_completed(bidirectional_stream *stream, const char *data) { */ static void on_read_completed(bidirectional_stream *stream, char *data, int count) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; stream_obj *s = (stream_obj *)stream->annotation; CRONET_LOG(GPR_DEBUG, "R: on_read_completed(%p, %p, %d)", stream, data, count); @@ -563,14 +603,15 @@ static void on_read_completed(bidirectional_stream *stream, char *data, gpr_mu_unlock(&s->mu); } else { gpr_mu_unlock(&s->mu); - execute_from_storage(s); + execute_from_storage(&exec_ctx, s); } } else { null_and_maybe_free_read_buffer(s); s->state.rs.read_stream_closed = true; gpr_mu_unlock(&s->mu); - execute_from_storage(s); + execute_from_storage(&exec_ctx, s); } + grpc_exec_ctx_finish(&exec_ctx); } /* @@ -625,12 +666,11 @@ static void on_response_trailers_received( s->state.state_op_done[OP_SEND_TRAILING_METADATA] = true; gpr_mu_unlock(&s->mu); - grpc_exec_ctx_finish(&exec_ctx); } else { gpr_mu_unlock(&s->mu); - grpc_exec_ctx_finish(&exec_ctx); - execute_from_storage(s); + execute_from_storage(&exec_ctx, s); } + grpc_exec_ctx_finish(&exec_ctx); } /* @@ -1313,6 +1353,9 @@ static int init_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt, grpc_stream *gs, grpc_stream_refcount *refcount, const void *server_data, gpr_arena *arena) { stream_obj *s = (stream_obj *)gs; + + s->refcount = refcount; + GRPC_CRONET_STREAM_REF(s, "cronet transport"); memset(&s->storage, 0, sizeof(s->storage)); s->storage.head = NULL; memset(&s->state, 0, sizeof(s->state)); @@ -1370,7 +1413,7 @@ static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, } stream_obj *s = (stream_obj *)gs; add_to_storage(s, op); - execute_from_storage(s); + execute_from_storage(exec_ctx, s); } static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt, diff --git a/src/core/lib/iomgr/ev_epoll1_linux.c b/src/core/lib/iomgr/ev_epoll1_linux.c index 7f053fa728..b76eb9e1c9 100644 --- a/src/core/lib/iomgr/ev_epoll1_linux.c +++ b/src/core/lib/iomgr/ev_epoll1_linux.c @@ -698,22 +698,30 @@ static bool begin_worker(grpc_pollset *pollset, grpc_pollset_worker *worker, gpr_mu_unlock(&pollset->mu); goto retry_lock_neighbourhood; } - pollset->seen_inactive = false; - if (neighbourhood->active_root == NULL) { - neighbourhood->active_root = pollset->next = pollset->prev = pollset; - /* TODO: sreek. Why would this worker state be other than UNKICKED - * here ? (since the worker isn't added to the pollset yet, there is no - * way it can be "found" by other threads to get kicked). */ - - /* If there is no designated poller, make this the designated poller */ - if (worker->kick_state == UNKICKED && - gpr_atm_no_barrier_cas(&g_active_poller, 0, (gpr_atm)worker)) { - SET_KICK_STATE(worker, DESIGNATED_POLLER); + + /* In the brief time we released the pollset locks above, the worker MAY + have been kicked. In this case, the worker should get out of this + pollset ASAP and hence this should neither add the pollset to + neighbourhood nor mark the pollset as active. + + On a side note, the only way a worker's kick state could have changed + at this point is if it were "kicked specifically". Since the worker has + not added itself to the pollset yet (by calling worker_insert()), it is + not visible in the "kick any" path yet */ + if (worker->kick_state == UNKICKED) { + pollset->seen_inactive = false; + if (neighbourhood->active_root == NULL) { + neighbourhood->active_root = pollset->next = pollset->prev = pollset; + /* Make this the designated poller if there isn't one already */ + if (worker->kick_state == UNKICKED && + gpr_atm_no_barrier_cas(&g_active_poller, 0, (gpr_atm)worker)) { + SET_KICK_STATE(worker, DESIGNATED_POLLER); + } + } else { + pollset->next = neighbourhood->active_root; + pollset->prev = pollset->next->prev; + pollset->next->prev = pollset->prev->next = pollset; } - } else { - pollset->next = neighbourhood->active_root; - pollset->prev = pollset->next->prev; - pollset->next->prev = pollset->prev->next = pollset; } } if (is_reassigning) { @@ -1001,6 +1009,7 @@ static grpc_error *pollset_kick(grpc_pollset *pollset, gpr_log(GPR_ERROR, "%s", tmp); gpr_free(tmp); } + if (specific_worker == NULL) { if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)pollset) { grpc_pollset_worker *root_worker = pollset->root_worker; @@ -1076,7 +1085,11 @@ static grpc_error *pollset_kick(grpc_pollset *pollset, } goto done; } - } else if (specific_worker->kick_state == KICKED) { + + GPR_UNREACHABLE_CODE(goto done); + } + + if (specific_worker->kick_state == KICKED) { if (GRPC_TRACER_ON(grpc_polling_trace)) { gpr_log(GPR_ERROR, " .. specific worker already kicked"); } diff --git a/src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c b/src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c deleted file mode 100644 index e2e3cd9003..0000000000 --- a/src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c +++ /dev/null @@ -1,1961 +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 "src/core/lib/iomgr/port.h" - -/* This polling engine is only relevant on linux kernels supporting epoll() */ -#ifdef GRPC_LINUX_EPOLL - -#include "src/core/lib/iomgr/ev_epoll_limited_pollers_linux.h" - -#include <assert.h> -#include <errno.h> -#include <limits.h> -#include <poll.h> -#include <pthread.h> -#include <signal.h> -#include <string.h> -#include <sys/epoll.h> -#include <sys/socket.h> -#include <unistd.h> - -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> -#include <grpc/support/string_util.h> -#include <grpc/support/tls.h> -#include <grpc/support/useful.h> - -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/iomgr/ev_posix.h" -#include "src/core/lib/iomgr/iomgr_internal.h" -#include "src/core/lib/iomgr/lockfree_event.h" -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/iomgr/wakeup_fd_posix.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/support/block_annotate.h" -#include "src/core/lib/support/env.h" - -#define GRPC_POLLING_TRACE(fmt, ...) \ - if (GRPC_TRACER_ON(grpc_polling_trace)) { \ - gpr_log(GPR_INFO, (fmt), __VA_ARGS__); \ - } - -#define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker *)1) - -/* The maximum number of polling threads per polling island. By default no - limit */ -static int g_max_pollers_per_pi = INT_MAX; - -static int grpc_wakeup_signal = -1; -static bool is_grpc_wakeup_signal_initialized = false; - -/* Implements the function defined in grpc_posix.h. This function might be - * called before even calling grpc_init() to set either a different signal to - * use. If signum == -1, then the use of signals is disabled */ -static void grpc_use_signal(int signum) { - grpc_wakeup_signal = signum; - is_grpc_wakeup_signal_initialized = true; - - if (grpc_wakeup_signal < 0) { - gpr_log(GPR_INFO, - "Use of signals is disabled. Epoll engine will not be used"); - } else { - gpr_log(GPR_INFO, "epoll engine will be using signal: %d", - grpc_wakeup_signal); - } -} - -struct polling_island; - -typedef enum { - POLL_OBJ_FD, - POLL_OBJ_POLLSET, - POLL_OBJ_POLLSET_SET -} poll_obj_type; - -typedef struct poll_obj { -#ifndef NDEBUG - poll_obj_type obj_type; -#endif - gpr_mu mu; - struct polling_island *pi; -} poll_obj; - -static const char *poll_obj_string(poll_obj_type po_type) { - switch (po_type) { - case POLL_OBJ_FD: - return "fd"; - case POLL_OBJ_POLLSET: - return "pollset"; - case POLL_OBJ_POLLSET_SET: - return "pollset_set"; - } - - GPR_UNREACHABLE_CODE(return "UNKNOWN"); -} - -/******************************************************************************* - * Fd Declarations - */ - -#define FD_FROM_PO(po) ((grpc_fd *)(po)) - -struct grpc_fd { - poll_obj po; - - int fd; - /* refst format: - bit 0 : 1=Active / 0=Orphaned - bits 1-n : refcount - Ref/Unref by two to avoid altering the orphaned bit */ - gpr_atm refst; - - /* The fd is either closed or we relinquished control of it. In either - cases, this indicates that the 'fd' on this structure is no longer - valid */ - bool orphaned; - - gpr_atm read_closure; - gpr_atm write_closure; - - struct grpc_fd *freelist_next; - grpc_closure *on_done_closure; - - /* The pollset that last noticed that the fd is readable. The actual type - * stored in this is (grpc_pollset *) */ - gpr_atm read_notifier_pollset; - - grpc_iomgr_object iomgr_object; -}; - -/* Reference counting for fds */ -#ifndef NDEBUG -static void fd_ref(grpc_fd *fd, const char *reason, const char *file, int line); -static void fd_unref(grpc_fd *fd, const char *reason, const char *file, - int line); -#define GRPC_FD_REF(fd, reason) fd_ref(fd, reason, __FILE__, __LINE__) -#define GRPC_FD_UNREF(fd, reason) fd_unref(fd, reason, __FILE__, __LINE__) -#else -static void fd_ref(grpc_fd *fd); -static void fd_unref(grpc_fd *fd); -#define GRPC_FD_REF(fd, reason) fd_ref(fd) -#define GRPC_FD_UNREF(fd, reason) fd_unref(fd) -#endif - -static void fd_global_init(void); -static void fd_global_shutdown(void); - -/******************************************************************************* - * Polling island Declarations - */ - -#ifndef NDEBUG - -#define PI_ADD_REF(p, r) pi_add_ref_dbg((p), (r), __FILE__, __LINE__) -#define PI_UNREF(exec_ctx, p, r) \ - pi_unref_dbg((exec_ctx), (p), (r), __FILE__, __LINE__) - -#else - -#define PI_ADD_REF(p, r) pi_add_ref((p)) -#define PI_UNREF(exec_ctx, p, r) pi_unref((exec_ctx), (p)) - -#endif - -typedef struct worker_node { - struct worker_node *next; - struct worker_node *prev; -} worker_node; - -/* This is also used as grpc_workqueue (by directly casing it) */ -typedef struct polling_island { - gpr_mu mu; - /* Ref count. Use PI_ADD_REF() and PI_UNREF() macros to increment/decrement - the refcount. - Once the ref count becomes zero, this structure is destroyed which means - we should ensure that there is never a scenario where a PI_ADD_REF() is - racing with a PI_UNREF() that just made the ref_count zero. */ - gpr_atm ref_count; - - /* Pointer to the polling_island this merged into. - * merged_to value is only set once in polling_island's lifetime (and that too - * only if the island is merged with another island). Because of this, we can - * use gpr_atm type here so that we can do atomic access on this and reduce - * lock contention on 'mu' mutex. - * - * Note that if this field is not NULL (i.e not 0), all the remaining fields - * (except mu and ref_count) are invalid and must be ignored. */ - gpr_atm merged_to; - - /* Number of threads currently polling on this island */ - gpr_atm poller_count; - - /* The list of workers waiting to do polling on this polling island */ - gpr_mu worker_list_mu; - worker_node worker_list_head; - - /* The fd of the underlying epoll set */ - int epoll_fd; - - /* The file descriptors in the epoll set */ - size_t fd_cnt; - size_t fd_capacity; - grpc_fd **fds; -} polling_island; - -/******************************************************************************* - * Pollset Declarations - */ -#define WORKER_FROM_WORKER_LIST_NODE(p) \ - (struct grpc_pollset_worker *)(((char *)(p)) - \ - offsetof(grpc_pollset_worker, pi_list_link)) -struct grpc_pollset_worker { - /* Thread id of this worker */ - pthread_t pt_id; - - /* Used to prevent a worker from getting kicked multiple times */ - gpr_atm is_kicked; - - struct grpc_pollset_worker *next; - struct grpc_pollset_worker *prev; - - /* Indicates if it is this worker's turn to do epoll */ - gpr_atm is_polling_turn; - - /* Node in the polling island's worker list. */ - worker_node pi_list_link; -}; - -struct grpc_pollset { - poll_obj po; - - grpc_pollset_worker root_worker; - bool kicked_without_pollers; - - bool shutting_down; /* Is the pollset shutting down ? */ - bool finish_shutdown_called; /* Is the 'finish_shutdown_locked()' called ? */ - grpc_closure *shutdown_done; /* Called after after shutdown is complete */ -}; - -/******************************************************************************* - * Pollset-set Declarations - */ -struct grpc_pollset_set { - poll_obj po; -}; - -/******************************************************************************* - * Common helpers - */ - -static bool append_error(grpc_error **composite, grpc_error *error, - const char *desc) { - if (error == GRPC_ERROR_NONE) return true; - if (*composite == GRPC_ERROR_NONE) { - *composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc); - } - *composite = grpc_error_add_child(*composite, error); - return false; -} - -/******************************************************************************* - * Polling island Definitions - */ - -/* The wakeup fd that is used to wake up all threads in a Polling island. This - is useful in the polling island merge operation where we need to wakeup all - the threads currently polling the smaller polling island (so that they can - start polling the new/merged polling island) - - NOTE: This fd is initialized to be readable and MUST NOT be consumed i.e the - threads that woke up MUST NOT call grpc_wakeup_fd_consume_wakeup() */ -static grpc_wakeup_fd polling_island_wakeup_fd; - -/* The polling island being polled right now. - See comments in workqueue_maybe_wakeup for why this is tracked. */ -static __thread polling_island *g_current_thread_polling_island; - -/* Forward declaration */ -static void polling_island_delete(grpc_exec_ctx *exec_ctx, polling_island *pi); - -#ifdef GRPC_TSAN -/* Currently TSAN may incorrectly flag data races between epoll_ctl and - epoll_wait for any grpc_fd structs that are added to the epoll set via - epoll_ctl and are returned (within a very short window) via epoll_wait(). - - To work-around this race, we establish a happens-before relation between - the code just-before epoll_ctl() and the code after epoll_wait() by using - this atomic */ -gpr_atm g_epoll_sync; -#endif /* defined(GRPC_TSAN) */ - -static void pi_add_ref(polling_island *pi); -static void pi_unref(grpc_exec_ctx *exec_ctx, polling_island *pi); - -#ifndef NDEBUG -static void pi_add_ref_dbg(polling_island *pi, const char *reason, - const char *file, int line) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_atm old_cnt = gpr_atm_acq_load(&pi->ref_count); - gpr_log(GPR_DEBUG, "Add ref pi: %p, old:%" PRIdPTR " -> new:%" PRIdPTR - " (%s) - (%s, %d)", - pi, old_cnt, old_cnt + 1, reason, file, line); - } - pi_add_ref(pi); -} - -static void pi_unref_dbg(grpc_exec_ctx *exec_ctx, polling_island *pi, - const char *reason, const char *file, int line) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_atm old_cnt = gpr_atm_acq_load(&pi->ref_count); - gpr_log(GPR_DEBUG, "Unref pi: %p, old:%" PRIdPTR " -> new:%" PRIdPTR - " (%s) - (%s, %d)", - pi, old_cnt, (old_cnt - 1), reason, file, line); - } - pi_unref(exec_ctx, pi); -} -#endif - -static void pi_add_ref(polling_island *pi) { - gpr_atm_no_barrier_fetch_add(&pi->ref_count, 1); -} - -static void pi_unref(grpc_exec_ctx *exec_ctx, polling_island *pi) { - /* If ref count went to zero, delete the polling island. - Note that this deletion not be done under a lock. Once the ref count goes - to zero, we are guaranteed that no one else holds a reference to the - polling island (and that there is no racing pi_add_ref() call either). - - Also, if we are deleting the polling island and the merged_to field is - non-empty, we should remove a ref to the merged_to polling island - */ - if (1 == gpr_atm_full_fetch_add(&pi->ref_count, -1)) { - polling_island *next = (polling_island *)gpr_atm_acq_load(&pi->merged_to); - polling_island_delete(exec_ctx, pi); - if (next != NULL) { - PI_UNREF(exec_ctx, next, "pi_delete"); /* Recursive call */ - } - } -} - -static void worker_node_init(worker_node *node) { - node->next = node->prev = node; -} - -/* Not thread safe. Do under a list-level lock */ -static void push_back_worker_node(worker_node *head, worker_node *node) { - node->next = head; - node->prev = head->prev; - head->prev->next = node; - head->prev = node; -} - -/* Not thread safe. Do under a list-level lock */ -static void remove_worker_node(worker_node *node) { - node->next->prev = node->prev; - node->prev->next = node->next; - /* If node's next and prev point to itself, the node is considered detached - * from the list*/ - node->next = node->prev = node; -} - -/* Not thread safe. Do under a list-level lock */ -static worker_node *pop_front_worker_node(worker_node *head) { - worker_node *node = head->next; - if (node != head) { - remove_worker_node(node); - } else { - node = NULL; - } - - return node; -} - -/* Returns true if the node's next and prev are pointing to itself (which - indicates that the node is not in the list */ -static bool is_worker_node_detached(worker_node *node) { - return (node->next == node->prev && node->next == node); -} - -/* The caller is expected to hold pi->mu lock before calling this function - */ -static void polling_island_add_fds_locked(polling_island *pi, grpc_fd **fds, - size_t fd_count, bool add_fd_refs, - grpc_error **error) { - int err; - size_t i; - struct epoll_event ev; - char *err_msg; - const char *err_desc = "polling_island_add_fds"; - -#ifdef GRPC_TSAN - /* See the definition of g_epoll_sync for more context */ - gpr_atm_rel_store(&g_epoll_sync, (gpr_atm)0); -#endif /* defined(GRPC_TSAN) */ - - for (i = 0; i < fd_count; i++) { - ev.events = (uint32_t)(EPOLLIN | EPOLLOUT | EPOLLET); - ev.data.ptr = fds[i]; - err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_ADD, fds[i]->fd, &ev); - - if (err < 0) { - if (errno != EEXIST) { - gpr_asprintf( - &err_msg, - "epoll_ctl (epoll_fd: %d) add fd: %d failed with error: %d (%s)", - pi->epoll_fd, fds[i]->fd, errno, strerror(errno)); - append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); - gpr_free(err_msg); - } - - continue; - } - - if (pi->fd_cnt == pi->fd_capacity) { - pi->fd_capacity = GPR_MAX(pi->fd_capacity + 8, pi->fd_cnt * 3 / 2); - pi->fds = gpr_realloc(pi->fds, sizeof(grpc_fd *) * pi->fd_capacity); - } - - pi->fds[pi->fd_cnt++] = fds[i]; - if (add_fd_refs) { - GRPC_FD_REF(fds[i], "polling_island"); - } - } -} - -/* The caller is expected to hold pi->mu before calling this */ -static void polling_island_add_wakeup_fd_locked(polling_island *pi, - grpc_wakeup_fd *wakeup_fd, - grpc_error **error) { - struct epoll_event ev; - int err; - char *err_msg; - const char *err_desc = "polling_island_add_wakeup_fd"; - - ev.events = (uint32_t)(EPOLLIN | EPOLLET); - ev.data.ptr = wakeup_fd; - err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_ADD, - GRPC_WAKEUP_FD_GET_READ_FD(wakeup_fd), &ev); - if (err < 0 && errno != EEXIST) { - gpr_asprintf(&err_msg, - "epoll_ctl (epoll_fd: %d) add wakeup fd: %d failed with " - "error: %d (%s)", - pi->epoll_fd, GRPC_WAKEUP_FD_GET_READ_FD(wakeup_fd), errno, - strerror(errno)); - append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); - gpr_free(err_msg); - } -} - -/* The caller is expected to hold pi->mu lock before calling this function */ -static void polling_island_remove_all_fds_locked(polling_island *pi, - bool remove_fd_refs, - grpc_error **error) { - int err; - size_t i; - char *err_msg; - const char *err_desc = "polling_island_remove_fds"; - - for (i = 0; i < pi->fd_cnt; i++) { - err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_DEL, pi->fds[i]->fd, NULL); - if (err < 0 && errno != ENOENT) { - gpr_asprintf(&err_msg, - "epoll_ctl (epoll_fd: %d) delete fds[%zu]: %d failed with " - "error: %d (%s)", - pi->epoll_fd, i, pi->fds[i]->fd, errno, strerror(errno)); - append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); - gpr_free(err_msg); - } - - if (remove_fd_refs) { - GRPC_FD_UNREF(pi->fds[i], "polling_island"); - } - } - - pi->fd_cnt = 0; -} - -/* The caller is expected to hold pi->mu lock before calling this function */ -static void polling_island_remove_fd_locked(polling_island *pi, grpc_fd *fd, - bool is_fd_closed, - grpc_error **error) { - int err; - size_t i; - char *err_msg; - const char *err_desc = "polling_island_remove_fd"; - - /* If fd is already closed, then it would have been automatically been removed - from the epoll set */ - if (!is_fd_closed) { - err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_DEL, fd->fd, NULL); - if (err < 0 && errno != ENOENT) { - gpr_asprintf( - &err_msg, - "epoll_ctl (epoll_fd: %d) del fd: %d failed with error: %d (%s)", - pi->epoll_fd, fd->fd, errno, strerror(errno)); - append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); - gpr_free(err_msg); - } - } - - for (i = 0; i < pi->fd_cnt; i++) { - if (pi->fds[i] == fd) { - pi->fds[i] = pi->fds[--pi->fd_cnt]; - GRPC_FD_UNREF(fd, "polling_island"); - break; - } - } -} - -/* Might return NULL in case of an error */ -static polling_island *polling_island_create(grpc_exec_ctx *exec_ctx, - grpc_fd *initial_fd, - grpc_error **error) { - polling_island *pi = NULL; - const char *err_desc = "polling_island_create"; - - *error = GRPC_ERROR_NONE; - - pi = gpr_malloc(sizeof(*pi)); - gpr_mu_init(&pi->mu); - pi->fd_cnt = 0; - pi->fd_capacity = 0; - pi->fds = NULL; - pi->epoll_fd = -1; - - gpr_atm_rel_store(&pi->ref_count, 0); - gpr_atm_rel_store(&pi->poller_count, 0); - gpr_atm_rel_store(&pi->merged_to, (gpr_atm)NULL); - - gpr_mu_init(&pi->worker_list_mu); - worker_node_init(&pi->worker_list_head); - - pi->epoll_fd = epoll_create1(EPOLL_CLOEXEC); - - if (pi->epoll_fd < 0) { - append_error(error, GRPC_OS_ERROR(errno, "epoll_create1"), err_desc); - goto done; - } - - if (initial_fd != NULL) { - polling_island_add_fds_locked(pi, &initial_fd, 1, true, error); - } - -done: - if (*error != GRPC_ERROR_NONE) { - polling_island_delete(exec_ctx, pi); - pi = NULL; - } - return pi; -} - -static void polling_island_delete(grpc_exec_ctx *exec_ctx, polling_island *pi) { - GPR_ASSERT(pi->fd_cnt == 0); - - if (pi->epoll_fd >= 0) { - close(pi->epoll_fd); - } - gpr_mu_destroy(&pi->mu); - gpr_mu_destroy(&pi->worker_list_mu); - GPR_ASSERT(is_worker_node_detached(&pi->worker_list_head)); - - gpr_free(pi->fds); - gpr_free(pi); -} - -/* Attempts to gets the last polling island in the linked list (liked by the - * 'merged_to' field). Since this does not lock the polling island, there are no - * guarantees that the island returned is the last island */ -static polling_island *polling_island_maybe_get_latest(polling_island *pi) { - polling_island *next = (polling_island *)gpr_atm_acq_load(&pi->merged_to); - while (next != NULL) { - pi = next; - next = (polling_island *)gpr_atm_acq_load(&pi->merged_to); - } - - return pi; -} - -/* Gets the lock on the *latest* polling island i.e the last polling island in - the linked list (linked by the 'merged_to' field). Call gpr_mu_unlock on the - returned polling island's mu. - Usage: To lock/unlock polling island "pi", do the following: - polling_island *pi_latest = polling_island_lock(pi); - ... - ... critical section .. - ... - gpr_mu_unlock(&pi_latest->mu); // NOTE: use pi_latest->mu. NOT pi->mu */ -static polling_island *polling_island_lock(polling_island *pi) { - polling_island *next = NULL; - - while (true) { - next = (polling_island *)gpr_atm_acq_load(&pi->merged_to); - if (next == NULL) { - /* Looks like 'pi' is the last node in the linked list but unless we check - this by holding the pi->mu lock, we cannot be sure (i.e without the - pi->mu lock, we don't prevent island merges). - To be absolutely sure, check once more by holding the pi->mu lock */ - gpr_mu_lock(&pi->mu); - next = (polling_island *)gpr_atm_acq_load(&pi->merged_to); - if (next == NULL) { - /* pi is infact the last node and we have the pi->mu lock. we're done */ - break; - } - - /* pi->merged_to is not NULL i.e pi isn't the last node anymore. pi->mu - * isn't the lock we are interested in. Continue traversing the list */ - gpr_mu_unlock(&pi->mu); - } - - pi = next; - } - - return pi; -} - -/* Gets the lock on the *latest* polling islands in the linked lists pointed by - *p and *q (and also updates *p and *q to point to the latest polling islands) - - This function is needed because calling the following block of code to obtain - locks on polling islands (*p and *q) is prone to deadlocks. - { - polling_island_lock(*p, true); - polling_island_lock(*q, true); - } - - Usage/example: - polling_island *p1; - polling_island *p2; - .. - polling_island_lock_pair(&p1, &p2); - .. - .. Critical section with both p1 and p2 locked - .. - // Release locks: Always call polling_island_unlock_pair() to release locks - polling_island_unlock_pair(p1, p2); -*/ -static void polling_island_lock_pair(polling_island **p, polling_island **q) { - polling_island *pi_1 = *p; - polling_island *pi_2 = *q; - polling_island *next_1 = NULL; - polling_island *next_2 = NULL; - - /* The algorithm is simple: - - Go to the last polling islands in the linked lists *pi_1 and *pi_2 (and - keep updating pi_1 and pi_2) - - Then obtain locks on the islands by following a lock order rule of - locking polling_island with lower address first - Special case: Before obtaining the locks, check if pi_1 and pi_2 are - pointing to the same island. If that is the case, we can just call - polling_island_lock() - - After obtaining both the locks, double check that the polling islands - are still the last polling islands in their respective linked lists - (this is because there might have been polling island merges before - we got the lock) - - If the polling islands are the last islands, we are done. If not, - release the locks and continue the process from the first step */ - while (true) { - next_1 = (polling_island *)gpr_atm_acq_load(&pi_1->merged_to); - while (next_1 != NULL) { - pi_1 = next_1; - next_1 = (polling_island *)gpr_atm_acq_load(&pi_1->merged_to); - } - - next_2 = (polling_island *)gpr_atm_acq_load(&pi_2->merged_to); - while (next_2 != NULL) { - pi_2 = next_2; - next_2 = (polling_island *)gpr_atm_acq_load(&pi_2->merged_to); - } - - if (pi_1 == pi_2) { - pi_1 = pi_2 = polling_island_lock(pi_1); - break; - } - - if (pi_1 < pi_2) { - gpr_mu_lock(&pi_1->mu); - gpr_mu_lock(&pi_2->mu); - } else { - gpr_mu_lock(&pi_2->mu); - gpr_mu_lock(&pi_1->mu); - } - - next_1 = (polling_island *)gpr_atm_acq_load(&pi_1->merged_to); - next_2 = (polling_island *)gpr_atm_acq_load(&pi_2->merged_to); - if (next_1 == NULL && next_2 == NULL) { - break; - } - - gpr_mu_unlock(&pi_1->mu); - gpr_mu_unlock(&pi_2->mu); - } - - *p = pi_1; - *q = pi_2; -} - -static void polling_island_unlock_pair(polling_island *p, polling_island *q) { - if (p == q) { - gpr_mu_unlock(&p->mu); - } else { - gpr_mu_unlock(&p->mu); - gpr_mu_unlock(&q->mu); - } -} - -static polling_island *polling_island_merge(polling_island *p, - polling_island *q, - grpc_error **error) { - /* Get locks on both the polling islands */ - polling_island_lock_pair(&p, &q); - - if (p != q) { - /* Make sure that p points to the polling island with fewer fds than q */ - if (p->fd_cnt > q->fd_cnt) { - GPR_SWAP(polling_island *, p, q); - } - - /* Merge p with q i.e move all the fds from p (The one with fewer fds) to q - Note that the refcounts on the fds being moved will not change here. - This is why the last param in the following two functions is 'false') */ - polling_island_add_fds_locked(q, p->fds, p->fd_cnt, false, error); - polling_island_remove_all_fds_locked(p, false, error); - - /* Wakeup all the pollers (if any) on p so that they pickup this change */ - polling_island_add_wakeup_fd_locked(p, &polling_island_wakeup_fd, error); - - /* Add the 'merged_to' link from p --> q */ - gpr_atm_rel_store(&p->merged_to, (gpr_atm)q); - PI_ADD_REF(q, "pi_merge"); /* To account for the new incoming ref from p */ - } - /* else if p == q, nothing needs to be done */ - - polling_island_unlock_pair(p, q); - - /* Return the merged polling island (Note that no merge would have happened - if p == q which is ok) */ - return q; -} - -static grpc_error *polling_island_global_init() { - grpc_error *error = GRPC_ERROR_NONE; - - error = grpc_wakeup_fd_init(&polling_island_wakeup_fd); - if (error == GRPC_ERROR_NONE) { - error = grpc_wakeup_fd_wakeup(&polling_island_wakeup_fd); - } - - return error; -} - -static void polling_island_global_shutdown() { - grpc_wakeup_fd_destroy(&polling_island_wakeup_fd); -} - -/******************************************************************************* - * Fd Definitions - */ - -/* We need to keep a freelist not because of any concerns of malloc performance - * but instead so that implementations with multiple threads in (for example) - * epoll_wait deal with the race between pollset removal and incoming poll - * notifications. - * - * The problem is that the poller ultimately holds a reference to this - * object, so it is very difficult to know when is safe to free it, at least - * without some expensive synchronization. - * - * If we keep the object freelisted, in the worst case losing this race just - * becomes a spurious read notification on a reused fd. - */ - -/* The alarm system needs to be able to wakeup 'some poller' sometimes - * (specifically when a new alarm needs to be triggered earlier than the next - * alarm 'epoch'). This wakeup_fd gives us something to alert on when such a - * case occurs. */ - -static grpc_fd *fd_freelist = NULL; -static gpr_mu fd_freelist_mu; - -#ifndef NDEBUG -#define REF_BY(fd, n, reason) ref_by(fd, n, reason, __FILE__, __LINE__) -#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_TRACER_ON(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), - gpr_atm_no_barrier_load(&fd->refst) + n, reason, file, line); - } -#else -#define REF_BY(fd, n, reason) ref_by(fd, n) -#define UNREF_BY(fd, n, reason) unref_by(fd, n) -static void ref_by(grpc_fd *fd, int n) { -#endif - GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&fd->refst, n) > 0); -} - -#ifndef NDEBUG -static void unref_by(grpc_fd *fd, int n, const char *reason, const char *file, - int line) { - if (GRPC_TRACER_ON(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), - gpr_atm_no_barrier_load(&fd->refst) - n, reason, file, line); - } -#else -static void unref_by(grpc_fd *fd, int n) { -#endif - gpr_atm old = gpr_atm_full_fetch_add(&fd->refst, -n); - if (old == n) { - /* Add the fd to the freelist */ - gpr_mu_lock(&fd_freelist_mu); - fd->freelist_next = fd_freelist; - fd_freelist = fd; - grpc_iomgr_unregister_object(&fd->iomgr_object); - - grpc_lfev_destroy(&fd->read_closure); - grpc_lfev_destroy(&fd->write_closure); - - gpr_mu_unlock(&fd_freelist_mu); - } else { - GPR_ASSERT(old > n); - } -} - -/* Increment refcount by two to avoid changing the orphan bit */ -#ifndef NDEBUG -static void fd_ref(grpc_fd *fd, const char *reason, const char *file, - int line) { - ref_by(fd, 2, reason, file, line); -} - -static void fd_unref(grpc_fd *fd, const char *reason, const char *file, - int line) { - unref_by(fd, 2, reason, file, line); -} -#else -static void fd_ref(grpc_fd *fd) { ref_by(fd, 2); } -static void fd_unref(grpc_fd *fd) { unref_by(fd, 2); } -#endif - -static void fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); } - -static void fd_global_shutdown(void) { - gpr_mu_lock(&fd_freelist_mu); - gpr_mu_unlock(&fd_freelist_mu); - while (fd_freelist != NULL) { - grpc_fd *fd = fd_freelist; - fd_freelist = fd_freelist->freelist_next; - gpr_mu_destroy(&fd->po.mu); - gpr_free(fd); - } - gpr_mu_destroy(&fd_freelist_mu); -} - -static grpc_fd *fd_create(int fd, const char *name) { - grpc_fd *new_fd = NULL; - - gpr_mu_lock(&fd_freelist_mu); - if (fd_freelist != NULL) { - new_fd = fd_freelist; - fd_freelist = fd_freelist->freelist_next; - } - gpr_mu_unlock(&fd_freelist_mu); - - if (new_fd == NULL) { - new_fd = gpr_malloc(sizeof(grpc_fd)); - gpr_mu_init(&new_fd->po.mu); - } - - /* Note: It is not really needed to get the new_fd->po.mu lock here. If this - * is a newly created fd (or an fd we got from the freelist), no one else - * would be holding a lock to it anyway. */ - gpr_mu_lock(&new_fd->po.mu); - new_fd->po.pi = NULL; -#ifndef NDEBUG - new_fd->po.obj_type = POLL_OBJ_FD; -#endif - - gpr_atm_rel_store(&new_fd->refst, (gpr_atm)1); - new_fd->fd = fd; - new_fd->orphaned = false; - grpc_lfev_init(&new_fd->read_closure); - grpc_lfev_init(&new_fd->write_closure); - gpr_atm_no_barrier_store(&new_fd->read_notifier_pollset, (gpr_atm)NULL); - - new_fd->freelist_next = NULL; - new_fd->on_done_closure = NULL; - - gpr_mu_unlock(&new_fd->po.mu); - - char *fd_name; - gpr_asprintf(&fd_name, "%s fd=%d", name, fd); - grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name); -#ifndef NDEBUG - if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) { - gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, new_fd, fd_name); - } -#endif - gpr_free(fd_name); - return new_fd; -} - -static int fd_wrapped_fd(grpc_fd *fd) { - int ret_fd = -1; - gpr_mu_lock(&fd->po.mu); - if (!fd->orphaned) { - ret_fd = fd->fd; - } - gpr_mu_unlock(&fd->po.mu); - - return ret_fd; -} - -static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *on_done, int *release_fd, - bool already_closed, const char *reason) { - grpc_error *error = GRPC_ERROR_NONE; - polling_island *unref_pi = NULL; - - gpr_mu_lock(&fd->po.mu); - fd->on_done_closure = on_done; - - /* Remove the active status but keep referenced. We want this grpc_fd struct - to be alive (and not added to freelist) until the end of this function */ - REF_BY(fd, 1, reason); - - /* Remove the fd from the polling island: - - Get a lock on the latest polling island (i.e the last island in the - linked list pointed by fd->po.pi). This is the island that - would actually contain the fd - - Remove the fd from the latest polling island - - Unlock the latest polling island - - Set fd->po.pi to NULL (but remove the ref on the polling island - before doing this.) */ - if (fd->po.pi != NULL) { - polling_island *pi_latest = polling_island_lock(fd->po.pi); - polling_island_remove_fd_locked(pi_latest, fd, already_closed, &error); - gpr_mu_unlock(&pi_latest->mu); - - unref_pi = fd->po.pi; - fd->po.pi = NULL; - } - - /* If release_fd is not NULL, we should be relinquishing control of the file - descriptor fd->fd (but we still own the grpc_fd structure). */ - if (release_fd != NULL) { - *release_fd = fd->fd; - } else { - close(fd->fd); - } - - fd->orphaned = true; - - GRPC_CLOSURE_SCHED(exec_ctx, fd->on_done_closure, GRPC_ERROR_REF(error)); - - gpr_mu_unlock(&fd->po.mu); - UNREF_BY(fd, 2, reason); /* Drop the reference */ - if (unref_pi != NULL) { - /* Unref stale polling island here, outside the fd lock above. - The polling island owns a workqueue which owns an fd, and unreffing - inside the lock can cause an eventual lock loop that makes TSAN very - unhappy. */ - PI_UNREF(exec_ctx, unref_pi, "fd_orphan"); - } - GRPC_LOG_IF_ERROR("fd_orphan", GRPC_ERROR_REF(error)); - GRPC_ERROR_UNREF(error); -} - -static grpc_pollset *fd_get_read_notifier_pollset(grpc_exec_ctx *exec_ctx, - grpc_fd *fd) { - gpr_atm notifier = gpr_atm_acq_load(&fd->read_notifier_pollset); - return (grpc_pollset *)notifier; -} - -static bool fd_is_shutdown(grpc_fd *fd) { - return grpc_lfev_is_shutdown(&fd->read_closure); -} - -/* Might be called multiple times */ -static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_error *why) { - if (grpc_lfev_set_shutdown(exec_ctx, &fd->read_closure, - GRPC_ERROR_REF(why))) { - shutdown(fd->fd, SHUT_RDWR); - grpc_lfev_set_shutdown(exec_ctx, &fd->write_closure, GRPC_ERROR_REF(why)); - } - GRPC_ERROR_UNREF(why); -} - -static void fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *closure) { - grpc_lfev_notify_on(exec_ctx, &fd->read_closure, closure, "read"); -} - -static void fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *closure) { - grpc_lfev_notify_on(exec_ctx, &fd->write_closure, closure, "write"); -} - -/******************************************************************************* - * Pollset Definitions - */ -GPR_TLS_DECL(g_current_thread_pollset); -GPR_TLS_DECL(g_current_thread_worker); -static __thread bool g_initialized_sigmask; -static __thread sigset_t g_orig_sigmask; -static __thread sigset_t g_wakeup_sig_set; - -static void sig_handler(int sig_num) { -#ifdef GRPC_EPOLL_DEBUG - gpr_log(GPR_INFO, "Received signal %d", sig_num); -#endif -} - -static void pollset_worker_init(grpc_pollset_worker *worker) { - worker->pt_id = pthread_self(); - worker->next = worker->prev = NULL; - gpr_atm_no_barrier_store(&worker->is_kicked, (gpr_atm)0); - gpr_atm_no_barrier_store(&worker->is_polling_turn, (gpr_atm)0); - worker_node_init(&worker->pi_list_link); -} - -static void poller_kick_init() { signal(grpc_wakeup_signal, sig_handler); } - -/* Global state management */ -static grpc_error *pollset_global_init(void) { - gpr_tls_init(&g_current_thread_pollset); - gpr_tls_init(&g_current_thread_worker); - poller_kick_init(); - return GRPC_ERROR_NONE; -} - -static void pollset_global_shutdown(void) { - gpr_tls_destroy(&g_current_thread_pollset); - gpr_tls_destroy(&g_current_thread_worker); -} - -static grpc_error *worker_kick(grpc_pollset_worker *worker, - gpr_atm *is_kicked) { - grpc_error *err = GRPC_ERROR_NONE; - - /* Kick the worker only if it was not already kicked */ - if (gpr_atm_no_barrier_cas(is_kicked, (gpr_atm)0, (gpr_atm)1)) { - GRPC_POLLING_TRACE( - "pollset_worker_kick: Kicking worker: %p (thread id: %ld)", - (void *)worker, (long int)worker->pt_id); - int err_num = pthread_kill(worker->pt_id, grpc_wakeup_signal); - if (err_num != 0) { - err = GRPC_OS_ERROR(err_num, "pthread_kill"); - } - } - return err; -} - -static grpc_error *pollset_worker_kick(grpc_pollset_worker *worker) { - return worker_kick(worker, &worker->is_kicked); -} - -static grpc_error *poller_kick(grpc_pollset_worker *worker) { - return worker_kick(worker, &worker->is_polling_turn); -} - -/* Return 1 if the pollset has active threads in pollset_work (pollset must - * be locked) */ -static int pollset_has_workers(grpc_pollset *p) { - return p->root_worker.next != &p->root_worker; -} - -static void remove_worker(grpc_pollset *p, grpc_pollset_worker *worker) { - worker->prev->next = worker->next; - worker->next->prev = worker->prev; -} - -static grpc_pollset_worker *pop_front_worker(grpc_pollset *p) { - if (pollset_has_workers(p)) { - grpc_pollset_worker *w = p->root_worker.next; - remove_worker(p, w); - return w; - } else { - return NULL; - } -} - -static void push_back_worker(grpc_pollset *p, grpc_pollset_worker *worker) { - worker->next = &p->root_worker; - worker->prev = worker->next->prev; - worker->prev->next = worker->next->prev = worker; -} - -static void push_front_worker(grpc_pollset *p, grpc_pollset_worker *worker) { - worker->prev = &p->root_worker; - worker->next = worker->prev->next; - worker->prev->next = worker->next->prev = worker; -} - -/* p->mu must be held before calling this function */ -static grpc_error *pollset_kick(grpc_pollset *p, - grpc_pollset_worker *specific_worker) { - GPR_TIMER_BEGIN("pollset_kick", 0); - grpc_error *error = GRPC_ERROR_NONE; - const char *err_desc = "Kick Failure"; - grpc_pollset_worker *worker = specific_worker; - if (worker != NULL) { - if (worker == GRPC_POLLSET_KICK_BROADCAST) { - if (pollset_has_workers(p)) { - GPR_TIMER_BEGIN("pollset_kick.broadcast", 0); - for (worker = p->root_worker.next; worker != &p->root_worker; - worker = worker->next) { - if (gpr_tls_get(&g_current_thread_worker) != (intptr_t)worker) { - append_error(&error, pollset_worker_kick(worker), err_desc); - } - } - GPR_TIMER_END("pollset_kick.broadcast", 0); - } else { - p->kicked_without_pollers = true; - } - } else { - GPR_TIMER_MARK("kicked_specifically", 0); - if (gpr_tls_get(&g_current_thread_worker) != (intptr_t)worker) { - append_error(&error, pollset_worker_kick(worker), err_desc); - } - } - } else if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)p) { - /* Since worker == NULL, it means that we can kick "any" worker on this - pollset 'p'. If 'p' happens to be the same pollset this thread is - currently polling (i.e in pollset_work() function), then there is no need - to kick any other worker since the current thread can just absorb the - kick. This is the reason why we enter this case only when - g_current_thread_pollset is != p */ - - GPR_TIMER_MARK("kick_anonymous", 0); - worker = pop_front_worker(p); - if (worker != NULL) { - GPR_TIMER_MARK("finally_kick", 0); - push_back_worker(p, worker); - append_error(&error, pollset_worker_kick(worker), err_desc); - } else { - GPR_TIMER_MARK("kicked_no_pollers", 0); - p->kicked_without_pollers = true; - } - } - - GPR_TIMER_END("pollset_kick", 0); - GRPC_LOG_IF_ERROR("pollset_kick", GRPC_ERROR_REF(error)); - return error; -} - -static void pollset_init(grpc_pollset *pollset, gpr_mu **mu) { - gpr_mu_init(&pollset->po.mu); - *mu = &pollset->po.mu; - pollset->po.pi = NULL; -#ifndef NDEBUG - pollset->po.obj_type = POLL_OBJ_POLLSET; -#endif - - pollset->root_worker.next = pollset->root_worker.prev = &pollset->root_worker; - pollset->kicked_without_pollers = false; - - pollset->shutting_down = false; - pollset->finish_shutdown_called = false; - pollset->shutdown_done = NULL; -} - -/* Convert millis to timespec (clock-type is assumed to be GPR_TIMESPAN) */ -static struct timespec millis_to_timespec(int millis) { - struct timespec linux_ts; - gpr_timespec gpr_ts; - - if (millis == -1) { - gpr_ts = gpr_inf_future(GPR_TIMESPAN); - } else { - gpr_ts = gpr_time_from_millis(millis, GPR_TIMESPAN); - } - - linux_ts.tv_sec = (time_t)gpr_ts.tv_sec; - linux_ts.tv_nsec = gpr_ts.tv_nsec; - return linux_ts; -} - -/* Convert a timespec to milliseconds: - - Very small or negative poll times are clamped to zero to do a non-blocking - poll (which becomes spin polling) - - Other small values are rounded up to one millisecond - - Longer than a millisecond polls are rounded up to the next nearest - millisecond to avoid spinning - - Infinite timeouts are converted to -1 */ -static int poll_deadline_to_millis_timeout(gpr_timespec deadline, - gpr_timespec now) { - gpr_timespec timeout; - static const int64_t max_spin_polling_us = 10; - if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) { - return -1; - } - - if (gpr_time_cmp(deadline, gpr_time_add(now, gpr_time_from_micros( - max_spin_polling_us, - GPR_TIMESPAN))) <= 0) { - return 0; - } - timeout = gpr_time_sub(deadline, now); - int millis = gpr_time_to_millis(gpr_time_add( - timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN))); - return millis >= 1 ? millis : 1; -} - -static void fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_pollset *notifier) { - grpc_lfev_set_ready(exec_ctx, &fd->read_closure, "read"); - - /* Note, it is possible that fd_become_readable might be called twice with - different 'notifier's when an fd becomes readable and it is in two epoll - sets (This can happen briefly during polling island merges). In such cases - it does not really matter which notifer is set as the read_notifier_pollset - (They would both point to the same polling island anyway) */ - /* Use release store to match with acquire load in fd_get_read_notifier */ - gpr_atm_rel_store(&fd->read_notifier_pollset, (gpr_atm)notifier); -} - -static void fd_become_writable(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { - grpc_lfev_set_ready(exec_ctx, &fd->write_closure, "write"); -} - -static void pollset_release_polling_island(grpc_exec_ctx *exec_ctx, - grpc_pollset *ps, char *reason) { - if (ps->po.pi != NULL) { - PI_UNREF(exec_ctx, ps->po.pi, reason); - } - ps->po.pi = NULL; -} - -static void finish_shutdown_locked(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset) { - /* The pollset cannot have any workers if we are at this stage */ - GPR_ASSERT(!pollset_has_workers(pollset)); - - pollset->finish_shutdown_called = true; - - /* Release the ref and set pollset->po.pi to NULL */ - pollset_release_polling_island(exec_ctx, pollset, "ps_shutdown"); - GRPC_CLOSURE_SCHED(exec_ctx, pollset->shutdown_done, GRPC_ERROR_NONE); -} - -/* pollset->po.mu lock must be held by the caller before calling this */ -static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_closure *closure) { - GPR_TIMER_BEGIN("pollset_shutdown", 0); - GPR_ASSERT(!pollset->shutting_down); - pollset->shutting_down = true; - pollset->shutdown_done = closure; - pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST); - - /* If the pollset has any workers, we cannot call finish_shutdown_locked() - because it would release the underlying polling island. In such a case, we - let the last worker call finish_shutdown_locked() from pollset_work() */ - if (!pollset_has_workers(pollset)) { - GPR_ASSERT(!pollset->finish_shutdown_called); - GPR_TIMER_MARK("pollset_shutdown.finish_shutdown_locked", 0); - finish_shutdown_locked(exec_ctx, pollset); - } - GPR_TIMER_END("pollset_shutdown", 0); -} - -/* pollset_shutdown is guaranteed to be called before pollset_destroy. So other - * than destroying the mutexes, there is nothing special that needs to be done - * here */ -static void pollset_destroy(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { - GPR_ASSERT(!pollset_has_workers(pollset)); - gpr_mu_destroy(&pollset->po.mu); -} - -/* NOTE: This function may modify 'now' */ -static bool acquire_polling_lease(grpc_exec_ctx *exec_ctx, - grpc_pollset_worker *worker, - polling_island *pi, gpr_timespec deadline, - gpr_timespec *now) { - bool is_lease_acquired = false; - - gpr_mu_lock(&pi->worker_list_mu); // LOCK - long num_pollers = gpr_atm_no_barrier_load(&pi->poller_count); - - if (num_pollers >= g_max_pollers_per_pi) { - push_back_worker_node(&pi->worker_list_head, &worker->pi_list_link); - gpr_mu_unlock(&pi->worker_list_mu); // UNLOCK - - bool is_timeout = false; - int ret; - int timeout_ms = poll_deadline_to_millis_timeout(deadline, *now); - if (timeout_ms == -1) { - ret = sigwaitinfo(&g_wakeup_sig_set, NULL); - } else { - struct timespec sigwait_timeout = millis_to_timespec(timeout_ms); - GRPC_SCHEDULING_START_BLOCKING_REGION; - GRPC_STATS_INC_SYSCALL_WAIT(exec_ctx); - ret = sigtimedwait(&g_wakeup_sig_set, NULL, &sigwait_timeout); - GRPC_SCHEDULING_END_BLOCKING_REGION; - } - - if (ret == -1) { - if (errno == EAGAIN) { - is_timeout = true; - } else { - /* NOTE: This should not happen. If we see these log messages, it means - we are most likely doing something incorrect in the setup * needed - for sigwaitinfo/sigtimedwait */ - gpr_log(GPR_ERROR, - "sigtimedwait failed with retcode: %d (timeout_ms: %d)", errno, - timeout_ms); - } - } - - /* Did the worker come out of sigtimedwait due to a thread that just - exited epoll and kicking it (in release_polling_lease function). */ - bool is_polling_turn = gpr_atm_acq_load(&worker->is_polling_turn); - - /* Did the worker come out of sigtimedwait due to a thread alerting it that - some completion event was (likely) available in the completion queue */ - bool is_kicked = gpr_atm_no_barrier_load(&worker->is_kicked); - - if (is_kicked || is_timeout) { - *now = deadline; /* Essentially make the epoll timeout = 0 */ - } else if (is_polling_turn) { - *now = gpr_now(GPR_CLOCK_MONOTONIC); /* Reduce the epoll timeout */ - } - - gpr_mu_lock(&pi->worker_list_mu); // LOCK - /* The node might have already been removed from the list by the poller - that kicked this. However it is safe to call 'remove_worker_node' on - an already detached node */ - remove_worker_node(&worker->pi_list_link); - /* It is important to read the num_pollers again under the lock so that we - * have the latest num_pollers value that doesn't change while we are doing - * the "(num_pollers < g_max_pollers_per_pi)" a a few lines below */ - num_pollers = gpr_atm_no_barrier_load(&pi->poller_count); - } - - if (num_pollers < g_max_pollers_per_pi) { - gpr_atm_no_barrier_fetch_add(&pi->poller_count, 1); - is_lease_acquired = true; - } - - gpr_mu_unlock(&pi->worker_list_mu); // UNLOCK - return is_lease_acquired; -} - -static void release_polling_lease(polling_island *pi, grpc_error **error) { - gpr_mu_lock(&pi->worker_list_mu); - - gpr_atm_no_barrier_fetch_add(&pi->poller_count, -1); - worker_node *node = pop_front_worker_node(&pi->worker_list_head); - if (node != NULL) { - grpc_pollset_worker *next_worker = WORKER_FROM_WORKER_LIST_NODE(node); - append_error(error, poller_kick(next_worker), "poller kick error"); - } - - gpr_mu_unlock(&pi->worker_list_mu); -} - -#define GRPC_EPOLL_MAX_EVENTS 100 -static void pollset_do_epoll_pwait(grpc_exec_ctx *exec_ctx, int epoll_fd, - grpc_pollset *pollset, polling_island *pi, - grpc_pollset_worker *worker, - gpr_timespec now, gpr_timespec deadline, - sigset_t *sig_mask, grpc_error **error) { - /* Only g_max_pollers_per_pi threads can be doing polling in parallel. - If we cannot get a lease, we cannot continue to do epoll_pwait() */ - if (!acquire_polling_lease(exec_ctx, worker, pi, deadline, &now)) { - return; - } - - struct epoll_event ep_ev[GRPC_EPOLL_MAX_EVENTS]; - int ep_rv; - char *err_msg; - const char *err_desc = "pollset_work_and_unlock"; - - /* timeout_ms is the time between 'now' and 'deadline' */ - int timeout_ms = poll_deadline_to_millis_timeout(deadline, now); - - GRPC_SCHEDULING_START_BLOCKING_REGION; - GRPC_STATS_INC_SYSCALL_POLL(exec_ctx); - ep_rv = - epoll_pwait(epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, timeout_ms, sig_mask); - GRPC_SCHEDULING_END_BLOCKING_REGION; - - /* Give back the lease right away so that some other thread can enter */ - release_polling_lease(pi, error); - - if (ep_rv < 0) { - if (errno != EINTR) { - gpr_asprintf(&err_msg, - "epoll_wait() epoll fd: %d failed with error: %d (%s)", - epoll_fd, errno, strerror(errno)); - append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); - } else { - /* We were interrupted. Save an interation by doing a zero timeout - epoll_wait to see if there are any other events of interest */ - GRPC_POLLING_TRACE("pollset_work: pollset: %p, worker: %p received kick", - (void *)pollset, (void *)worker); - ep_rv = epoll_wait(epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, 0); - } - } - -#ifdef GRPC_TSAN - /* See the definition of g_poll_sync for more details */ - gpr_atm_acq_load(&g_epoll_sync); -#endif /* defined(GRPC_TSAN) */ - - for (int i = 0; i < ep_rv; ++i) { - void *data_ptr = ep_ev[i].data.ptr; - if (data_ptr == &polling_island_wakeup_fd) { - GRPC_POLLING_TRACE( - "pollset_work: pollset: %p, worker: %p polling island (epoll_fd: " - "%d) got merged", - (void *)pollset, (void *)worker, epoll_fd); - /* This means that our polling island is merged with a different - island. We do not have to do anything here since the subsequent call - to the function pollset_work_and_unlock() will pick up the correct - epoll_fd */ - } else { - grpc_fd *fd = data_ptr; - int cancel = ep_ev[i].events & (EPOLLERR | EPOLLHUP); - int read_ev = ep_ev[i].events & (EPOLLIN | EPOLLPRI); - int write_ev = ep_ev[i].events & EPOLLOUT; - if (read_ev || cancel) { - fd_become_readable(exec_ctx, fd, pollset); - } - if (write_ev || cancel) { - fd_become_writable(exec_ctx, fd); - } - } - } -} - -/* Note: sig_mask contains the signal mask to use *during* epoll_wait() */ -static void pollset_work_and_unlock(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset, - grpc_pollset_worker *worker, - gpr_timespec now, gpr_timespec deadline, - sigset_t *sig_mask, grpc_error **error) { - int epoll_fd = -1; - polling_island *pi = NULL; - GPR_TIMER_BEGIN("pollset_work_and_unlock", 0); - - /* We need to get the epoll_fd to wait on. The epoll_fd is in inside the - latest polling island pointed by pollset->po.pi - - Since epoll_fd is immutable, it is safe to read it without a lock on the - polling island. There is however a possibility that the polling island from - which we got the epoll_fd, got merged with another island in the meantime. - This is okay because in such a case, we will wakeup right-away from - epoll_pwait() (because any merge will poison the old polling island's epoll - set 'polling_island_wakeup_fd') and then pick up the latest polling_island - the next time this function - pollset_work_and_unlock()) is called */ - - if (pollset->po.pi == NULL) { - pollset->po.pi = polling_island_create(exec_ctx, NULL, error); - if (pollset->po.pi == NULL) { - GPR_TIMER_END("pollset_work_and_unlock", 0); - return; /* Fatal error. Cannot continue */ - } - - PI_ADD_REF(pollset->po.pi, "ps"); - GRPC_POLLING_TRACE("pollset_work: pollset: %p created new pi: %p", - (void *)pollset, (void *)pollset->po.pi); - } - - pi = polling_island_maybe_get_latest(pollset->po.pi); - epoll_fd = pi->epoll_fd; - - /* Update the pollset->po.pi since the island being pointed by - pollset->po.pi maybe older than the one pointed by pi) */ - if (pollset->po.pi != pi) { - /* Always do PI_ADD_REF before PI_UNREF because PI_UNREF may cause the - polling island to be deleted */ - PI_ADD_REF(pi, "ps"); - PI_UNREF(exec_ctx, pollset->po.pi, "ps"); - pollset->po.pi = pi; - } - - /* Add an extra ref so that the island does not get destroyed (which means - the epoll_fd won't be closed) while we are are doing an epoll_wait() on the - epoll_fd */ - PI_ADD_REF(pi, "ps_work"); - gpr_mu_unlock(&pollset->po.mu); - - g_current_thread_polling_island = pi; - pollset_do_epoll_pwait(exec_ctx, epoll_fd, pollset, pi, worker, now, deadline, - sig_mask, error); - g_current_thread_polling_island = NULL; - - GPR_ASSERT(pi != NULL); - - /* Before leaving, release the extra ref we added to the polling island. It - is important to use "pi" here (i.e our old copy of pollset->po.pi - that we got before releasing the polling island lock). This is because - pollset->po.pi pointer might get udpated in other parts of the - code when there is an island merge while we are doing epoll_wait() above */ - PI_UNREF(exec_ctx, pi, "ps_work"); - - GPR_TIMER_END("pollset_work_and_unlock", 0); -} - -/* pollset->po.mu lock must be held by the caller before calling this. - The function pollset_work() may temporarily release the lock (pollset->po.mu) - during the course of its execution but it will always re-acquire the lock and - ensure that it is held by the time the function returns */ -static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker **worker_hdl, - gpr_timespec now, gpr_timespec deadline) { - GPR_TIMER_BEGIN("pollset_work", 0); - grpc_error *error = GRPC_ERROR_NONE; - - grpc_pollset_worker worker; - pollset_worker_init(&worker); - - if (worker_hdl) *worker_hdl = &worker; - - gpr_tls_set(&g_current_thread_pollset, (intptr_t)pollset); - gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker); - - if (pollset->kicked_without_pollers) { - /* If the pollset was kicked without pollers, pretend that the current - worker got the kick and skip polling. A kick indicates that there is some - work that needs attention like an event on the completion queue or an - alarm */ - GPR_TIMER_MARK("pollset_work.kicked_without_pollers", 0); - pollset->kicked_without_pollers = 0; - } else if (!pollset->shutting_down) { - /* We use the posix-signal with number 'grpc_wakeup_signal' for waking up - (i.e 'kicking') a worker in the pollset. A 'kick' is a way to inform the - worker that there is some pending work that needs immediate attention - (like an event on the completion queue, or a polling island merge that - results in a new epoll-fd to wait on) and that the worker should not - spend time waiting in epoll_pwait(). - - A worker can be kicked anytime from the point it is added to the pollset - via push_front_worker() (or push_back_worker()) to the point it is - removed via remove_worker(). - If the worker is kicked before/during it calls epoll_pwait(), it should - immediately exit from epoll_wait(). If the worker is kicked after it - returns from epoll_wait(), then nothing really needs to be done. - - To accomplish this, we mask 'grpc_wakeup_signal' on this thread at all - times *except* when it is in epoll_pwait(). This way, the worker never - misses acting on a kick */ - - if (!g_initialized_sigmask) { - sigemptyset(&g_wakeup_sig_set); - sigaddset(&g_wakeup_sig_set, grpc_wakeup_signal); - pthread_sigmask(SIG_BLOCK, &g_wakeup_sig_set, &g_orig_sigmask); - sigdelset(&g_orig_sigmask, grpc_wakeup_signal); - g_initialized_sigmask = true; - /* new_mask: The new thread mask which blocks 'grpc_wakeup_signal'. - This is the mask used at all times *except during - epoll_wait()*" - g_orig_sigmask: The thread mask which allows 'grpc_wakeup_signal' and - this is the mask to use *during epoll_wait()* - - The new_mask is set on the worker before it is added to the pollset - (i.e before it can be kicked) */ - } - - push_front_worker(pollset, &worker); /* Add worker to pollset */ - - pollset_work_and_unlock(exec_ctx, pollset, &worker, now, deadline, - &g_orig_sigmask, &error); - grpc_exec_ctx_flush(exec_ctx); - - gpr_mu_lock(&pollset->po.mu); - - /* Note: There is no need to reset worker.is_kicked to 0 since we are no - longer going to use this worker */ - remove_worker(pollset, &worker); - } - - /* If we are the last worker on the pollset (i.e pollset_has_workers() is - false at this point) and the pollset is shutting down, we may have to - finish the shutdown process by calling finish_shutdown_locked(). - See pollset_shutdown() for more details. - - Note: Continuing to access pollset here is safe; it is the caller's - responsibility to not destroy a pollset when it has outstanding calls to - pollset_work() */ - if (pollset->shutting_down && !pollset_has_workers(pollset) && - !pollset->finish_shutdown_called) { - GPR_TIMER_MARK("pollset_work.finish_shutdown_locked", 0); - finish_shutdown_locked(exec_ctx, pollset); - - gpr_mu_unlock(&pollset->po.mu); - grpc_exec_ctx_flush(exec_ctx); - gpr_mu_lock(&pollset->po.mu); - } - - if (worker_hdl) *worker_hdl = NULL; - - gpr_tls_set(&g_current_thread_pollset, (intptr_t)0); - gpr_tls_set(&g_current_thread_worker, (intptr_t)0); - - GPR_TIMER_END("pollset_work", 0); - - GRPC_LOG_IF_ERROR("pollset_work", GRPC_ERROR_REF(error)); - return error; -} - -static void add_poll_object(grpc_exec_ctx *exec_ctx, poll_obj *bag, - poll_obj_type bag_type, poll_obj *item, - poll_obj_type item_type) { - GPR_TIMER_BEGIN("add_poll_object", 0); - -#ifndef NDEBUG - GPR_ASSERT(item->obj_type == item_type); - GPR_ASSERT(bag->obj_type == bag_type); -#endif - - grpc_error *error = GRPC_ERROR_NONE; - polling_island *pi_new = NULL; - - gpr_mu_lock(&bag->mu); - gpr_mu_lock(&item->mu); - -retry: - /* - * 1) If item->pi and bag->pi are both non-NULL and equal, do nothing - * 2) If item->pi and bag->pi are both NULL, create a new polling island (with - * a refcount of 2) and point item->pi and bag->pi to the new island - * 3) If exactly one of item->pi or bag->pi is NULL, update it to point to - * the other's non-NULL pi - * 4) Finally if item->pi and bag-pi are non-NULL and not-equal, merge the - * polling islands and update item->pi and bag->pi to point to the new - * island - */ - - /* Early out if we are trying to add an 'fd' to a 'bag' but the fd is already - * orphaned */ - if (item_type == POLL_OBJ_FD && (FD_FROM_PO(item))->orphaned) { - gpr_mu_unlock(&item->mu); - gpr_mu_unlock(&bag->mu); - return; - } - - if (item->pi == bag->pi) { - pi_new = item->pi; - if (pi_new == NULL) { - /* GPR_ASSERT(item->pi == bag->pi == NULL) */ - - /* If we are adding an fd to a bag (i.e pollset or pollset_set), then - * we need to do some extra work to make TSAN happy */ - if (item_type == POLL_OBJ_FD) { - /* Unlock before creating a new polling island: the polling island will - create a workqueue which creates a file descriptor, and holding an fd - lock here can eventually cause a loop to appear to TSAN (making it - unhappy). We don't think it's a real loop (there's an epoch point - where that loop possibility disappears), but the advantages of - keeping TSAN happy outweigh any performance advantage we might have - by keeping the lock held. */ - gpr_mu_unlock(&item->mu); - pi_new = polling_island_create(exec_ctx, FD_FROM_PO(item), &error); - gpr_mu_lock(&item->mu); - - /* Need to reverify any assumptions made between the initial lock and - getting to this branch: if they've changed, we need to throw away our - work and figure things out again. */ - if (item->pi != NULL) { - GRPC_POLLING_TRACE( - "add_poll_object: Raced creating new polling island. pi_new: %p " - "(fd: %d, %s: %p)", - (void *)pi_new, FD_FROM_PO(item)->fd, poll_obj_string(bag_type), - (void *)bag); - /* No need to lock 'pi_new' here since this is a new polling island - and no one has a reference to it yet */ - polling_island_remove_all_fds_locked(pi_new, true, &error); - - /* Ref and unref so that the polling island gets deleted during unref - */ - PI_ADD_REF(pi_new, "dance_of_destruction"); - PI_UNREF(exec_ctx, pi_new, "dance_of_destruction"); - goto retry; - } - } else { - pi_new = polling_island_create(exec_ctx, NULL, &error); - } - - GRPC_POLLING_TRACE( - "add_poll_object: Created new polling island. pi_new: %p (%s: %p, " - "%s: %p)", - (void *)pi_new, poll_obj_string(item_type), (void *)item, - poll_obj_string(bag_type), (void *)bag); - } else { - GRPC_POLLING_TRACE( - "add_poll_object: Same polling island. pi: %p (%s, %s)", - (void *)pi_new, poll_obj_string(item_type), - poll_obj_string(bag_type)); - } - } else if (item->pi == NULL) { - /* GPR_ASSERT(bag->pi != NULL) */ - /* Make pi_new point to latest pi*/ - pi_new = polling_island_lock(bag->pi); - - if (item_type == POLL_OBJ_FD) { - grpc_fd *fd = FD_FROM_PO(item); - polling_island_add_fds_locked(pi_new, &fd, 1, true, &error); - } - - gpr_mu_unlock(&pi_new->mu); - GRPC_POLLING_TRACE( - "add_poll_obj: item->pi was NULL. pi_new: %p (item(%s): %p, " - "bag(%s): %p)", - (void *)pi_new, poll_obj_string(item_type), (void *)item, - poll_obj_string(bag_type), (void *)bag); - } else if (bag->pi == NULL) { - /* GPR_ASSERT(item->pi != NULL) */ - /* Make pi_new to point to latest pi */ - pi_new = polling_island_lock(item->pi); - gpr_mu_unlock(&pi_new->mu); - GRPC_POLLING_TRACE( - "add_poll_obj: bag->pi was NULL. pi_new: %p (item(%s): %p, " - "bag(%s): %p)", - (void *)pi_new, poll_obj_string(item_type), (void *)item, - poll_obj_string(bag_type), (void *)bag); - } else { - pi_new = polling_island_merge(item->pi, bag->pi, &error); - GRPC_POLLING_TRACE( - "add_poll_obj: polling islands merged. pi_new: %p (item(%s): %p, " - "bag(%s): %p)", - (void *)pi_new, poll_obj_string(item_type), (void *)item, - poll_obj_string(bag_type), (void *)bag); - } - - /* At this point, pi_new is the polling island that both item->pi and bag->pi - MUST be pointing to */ - - if (item->pi != pi_new) { - PI_ADD_REF(pi_new, poll_obj_string(item_type)); - if (item->pi != NULL) { - PI_UNREF(exec_ctx, item->pi, poll_obj_string(item_type)); - } - item->pi = pi_new; - } - - if (bag->pi != pi_new) { - PI_ADD_REF(pi_new, poll_obj_string(bag_type)); - if (bag->pi != NULL) { - PI_UNREF(exec_ctx, bag->pi, poll_obj_string(bag_type)); - } - bag->pi = pi_new; - } - - gpr_mu_unlock(&item->mu); - gpr_mu_unlock(&bag->mu); - - GRPC_LOG_IF_ERROR("add_poll_object", error); - GPR_TIMER_END("add_poll_object", 0); -} - -static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_fd *fd) { - add_poll_object(exec_ctx, &pollset->po, POLL_OBJ_POLLSET, &fd->po, - POLL_OBJ_FD); -} - -/******************************************************************************* - * Pollset-set Definitions - */ - -static grpc_pollset_set *pollset_set_create(void) { - grpc_pollset_set *pss = gpr_malloc(sizeof(*pss)); - gpr_mu_init(&pss->po.mu); - pss->po.pi = NULL; -#ifndef NDEBUG - pss->po.obj_type = POLL_OBJ_POLLSET_SET; -#endif - return pss; -} - -static void pollset_set_destroy(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pss) { - gpr_mu_destroy(&pss->po.mu); - - if (pss->po.pi != NULL) { - PI_UNREF(exec_ctx, pss->po.pi, "pss_destroy"); - } - - gpr_free(pss); -} - -static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss, - grpc_fd *fd) { - add_poll_object(exec_ctx, &pss->po, POLL_OBJ_POLLSET_SET, &fd->po, - POLL_OBJ_FD); -} - -static void pollset_set_del_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss, - grpc_fd *fd) { - /* Nothing to do */ -} - -static void pollset_set_add_pollset(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pss, grpc_pollset *ps) { - add_poll_object(exec_ctx, &pss->po, POLL_OBJ_POLLSET_SET, &ps->po, - POLL_OBJ_POLLSET); -} - -static void pollset_set_del_pollset(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pss, grpc_pollset *ps) { - /* Nothing to do */ -} - -static void pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *bag, - grpc_pollset_set *item) { - add_poll_object(exec_ctx, &bag->po, POLL_OBJ_POLLSET_SET, &item->po, - POLL_OBJ_POLLSET_SET); -} - -static void pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *bag, - grpc_pollset_set *item) { - /* Nothing to do */ -} - -/******************************************************************************* - * Event engine binding - */ - -static void shutdown_engine(void) { - fd_global_shutdown(); - pollset_global_shutdown(); - polling_island_global_shutdown(); -} - -static const grpc_event_engine_vtable vtable = { - .pollset_size = sizeof(grpc_pollset), - - .fd_create = fd_create, - .fd_wrapped_fd = fd_wrapped_fd, - .fd_orphan = fd_orphan, - .fd_shutdown = fd_shutdown, - .fd_is_shutdown = fd_is_shutdown, - .fd_notify_on_read = fd_notify_on_read, - .fd_notify_on_write = fd_notify_on_write, - .fd_get_read_notifier_pollset = fd_get_read_notifier_pollset, - - .pollset_init = pollset_init, - .pollset_shutdown = pollset_shutdown, - .pollset_destroy = pollset_destroy, - .pollset_work = pollset_work, - .pollset_kick = pollset_kick, - .pollset_add_fd = pollset_add_fd, - - .pollset_set_create = pollset_set_create, - .pollset_set_destroy = pollset_set_destroy, - .pollset_set_add_pollset = pollset_set_add_pollset, - .pollset_set_del_pollset = pollset_set_del_pollset, - .pollset_set_add_pollset_set = pollset_set_add_pollset_set, - .pollset_set_del_pollset_set = pollset_set_del_pollset_set, - .pollset_set_add_fd = pollset_set_add_fd, - .pollset_set_del_fd = pollset_set_del_fd, - - .shutdown_engine = shutdown_engine, -}; - -/* It is possible that GLIBC has epoll but the underlying kernel doesn't. - * Create a dummy epoll_fd to make sure epoll support is available */ -static bool is_epoll_available() { - int fd = epoll_create1(EPOLL_CLOEXEC); - if (fd < 0) { - gpr_log( - GPR_ERROR, - "epoll_create1 failed with error: %d. Not using epoll polling engine", - fd); - return false; - } - close(fd); - return true; -} - -/* This is mainly for testing purposes. Checks to see if environment variable - * GRPC_MAX_POLLERS_PER_PI is set and if so, assigns that value to - * g_max_pollers_per_pi (any negative value is considered INT_MAX) */ -static void set_max_pollers_per_island() { - char *s = gpr_getenv("GRPC_MAX_POLLERS_PER_PI"); - if (s) { - g_max_pollers_per_pi = (int)strtol(s, NULL, 10); - if (g_max_pollers_per_pi < 0) { - g_max_pollers_per_pi = INT_MAX; - } - } else { - g_max_pollers_per_pi = INT_MAX; - } - - gpr_log(GPR_INFO, "Max number of pollers per polling island: %d", - g_max_pollers_per_pi); -} - -const grpc_event_engine_vtable *grpc_init_epoll_limited_pollers_linux( - bool explicitly_requested) { - if (!explicitly_requested) { - return NULL; - } - - /* If use of signals is disabled, we cannot use epoll engine*/ - if (is_grpc_wakeup_signal_initialized && grpc_wakeup_signal < 0) { - return NULL; - } - - if (!grpc_has_wakeup_fd()) { - return NULL; - } - - if (!is_epoll_available()) { - return NULL; - } - - if (!is_grpc_wakeup_signal_initialized) { - grpc_use_signal(SIGRTMIN + 6); - } - - set_max_pollers_per_island(); - - fd_global_init(); - - if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) { - return NULL; - } - - if (!GRPC_LOG_IF_ERROR("polling_island_global_init", - polling_island_global_init())) { - return NULL; - } - - return &vtable; -} - -#else /* defined(GRPC_LINUX_EPOLL) */ -#if defined(GRPC_POSIX_SOCKET) -#include "src/core/lib/iomgr/ev_posix.h" -/* If GRPC_LINUX_EPOLL is not defined, it means epoll is not available. Return - * NULL */ -const grpc_event_engine_vtable *grpc_init_epoll_limited_pollers_linux( - bool explicitly_requested) { - return NULL; -} -#endif /* defined(GRPC_POSIX_SOCKET) */ -#endif /* !defined(GRPC_LINUX_EPOLL) */ diff --git a/src/core/lib/iomgr/ev_epoll_limited_pollers_linux.h b/src/core/lib/iomgr/ev_epoll_limited_pollers_linux.h deleted file mode 100644 index 1d6af5f52c..0000000000 --- a/src/core/lib/iomgr/ev_epoll_limited_pollers_linux.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * - * 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_IOMGR_EV_EPOLL_LIMITED_POLLERS_LINUX_H -#define GRPC_CORE_LIB_IOMGR_EV_EPOLL_LIMITED_POLLERS_LINUX_H - -#include "src/core/lib/iomgr/ev_posix.h" -#include "src/core/lib/iomgr/port.h" - -const grpc_event_engine_vtable *grpc_init_epoll_limited_pollers_linux( - bool explicitly_requested); - -#endif /* GRPC_CORE_LIB_IOMGR_EV_EPOLL_LIMITED_POLLERS_LINUX_H */ diff --git a/src/core/lib/iomgr/ev_epoll_thread_pool_linux.c b/src/core/lib/iomgr/ev_epoll_thread_pool_linux.c deleted file mode 100644 index ddb8c74d2d..0000000000 --- a/src/core/lib/iomgr/ev_epoll_thread_pool_linux.c +++ /dev/null @@ -1,1184 +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 "src/core/lib/iomgr/port.h" - -/* This polling engine is only relevant on linux kernels supporting epoll() */ -#ifdef GRPC_LINUX_EPOLL - -#include "src/core/lib/iomgr/ev_epoll_thread_pool_linux.h" - -#include <assert.h> -#include <errno.h> -#include <limits.h> -#include <poll.h> -#include <pthread.h> -#include <string.h> -#include <sys/epoll.h> -#include <sys/socket.h> -#include <unistd.h> - -#include <grpc/support/alloc.h> -#include <grpc/support/cpu.h> -#include <grpc/support/log.h> -#include <grpc/support/string_util.h> -#include <grpc/support/thd.h> -#include <grpc/support/tls.h> -#include <grpc/support/useful.h> - -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/iomgr/ev_posix.h" -#include "src/core/lib/iomgr/iomgr_internal.h" -#include "src/core/lib/iomgr/lockfree_event.h" -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/iomgr/wakeup_fd_posix.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/support/block_annotate.h" - -/* TODO: sreek - Move this to init.c and initialize this like other tracers. */ -#define GRPC_POLLING_TRACE(fmt, ...) \ - if (GRPC_TRACER_ON(grpc_polling_trace)) { \ - gpr_log(GPR_INFO, (fmt), __VA_ARGS__); \ - } - -/* The alarm system needs to be able to wakeup 'some poller' sometimes - * (specifically when a new alarm needs to be triggered earlier than the next - * alarm 'epoch'). This wakeup_fd gives us something to alert on when such a - * case occurs. */ - -struct epoll_set; - -#define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker *)1) - -/******************************************************************************* - * Fd Declarations - */ -struct grpc_fd { - gpr_mu mu; - struct epoll_set *eps; - - int fd; - - /* The fd is either closed or we relinquished control of it. In either cases, - this indicates that the 'fd' on this structure is no longer valid */ - bool orphaned; - - gpr_atm read_closure; - gpr_atm write_closure; - - struct grpc_fd *freelist_next; - grpc_closure *on_done_closure; - - grpc_iomgr_object iomgr_object; -}; - -static void fd_global_init(void); -static void fd_global_shutdown(void); - -/******************************************************************************* - * epoll set Declarations - */ - -#ifndef NDEBUG - -#define EPS_ADD_REF(p, r) eps_add_ref_dbg((p), (r), __FILE__, __LINE__) -#define EPS_UNREF(exec_ctx, p, r) \ - eps_unref_dbg((exec_ctx), (p), (r), __FILE__, __LINE__) - -#else - -#define EPS_ADD_REF(p, r) eps_add_ref((p)) -#define EPS_UNREF(exec_ctx, p, r) eps_unref((exec_ctx), (p)) - -#endif - -typedef struct epoll_set { - /* Mutex poller should acquire to poll this. This enforces that only one - * poller can be polling on epoll_set at any time */ - gpr_mu mu; - - /* Ref count. Use EPS_ADD_REF() and EPS_UNREF() macros to increment/decrement - the refcount. Once the ref count becomes zero, this structure is destroyed - which means we should ensure that there is never a scenario where a - EPS_ADD_REF() is racing with a EPS_UNREF() that just made the ref_count - zero. */ - gpr_atm ref_count; - - /* Number of threads currently polling on this epoll set*/ - gpr_atm poller_count; - - /* Is the epoll set shutdown */ - gpr_atm is_shutdown; - - /* The fd of the underlying epoll set */ - int epoll_fd; -} epoll_set; - -/******************************************************************************* - * Pollset Declarations - */ -struct grpc_pollset_worker { - gpr_cv kick_cv; - - struct grpc_pollset_worker *next; - struct grpc_pollset_worker *prev; -}; - -struct grpc_pollset { - gpr_mu mu; - struct epoll_set *eps; - - grpc_pollset_worker root_worker; - bool kicked_without_pollers; - - bool shutting_down; /* Is the pollset shutting down ? */ - bool finish_shutdown_called; /* Is the 'finish_shutdown_locked()' called ? */ - grpc_closure *shutdown_done; /* Called after after shutdown is complete */ -}; - -/******************************************************************************* - * Pollset-set Declarations - */ -struct grpc_pollset_set { - char unused; -}; - -/***************************************************************************** - * Dedicated polling threads and pollsets - Declarations - */ - -size_t g_num_eps = 1; -struct epoll_set **g_epoll_sets = NULL; -gpr_atm g_next_eps; -size_t g_num_threads_per_eps = 1; -gpr_thd_id *g_poller_threads = NULL; - -/* Used as read-notifier pollsets for fds. We won't be using read notifier - * pollsets with this polling engine. So it does not matter what pollset we - * return */ -grpc_pollset g_read_notifier; - -static void add_fd_to_eps(grpc_fd *fd); -static bool init_epoll_sets(); -static void shutdown_epoll_sets(); -static void poller_thread_loop(void *arg); -static void start_poller_threads(); -static void shutdown_poller_threads(); - -/******************************************************************************* - * Common helpers - */ - -static bool append_error(grpc_error **composite, grpc_error *error, - const char *desc) { - if (error == GRPC_ERROR_NONE) return true; - if (*composite == GRPC_ERROR_NONE) { - *composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc); - } - *composite = grpc_error_add_child(*composite, error); - return false; -} - -/******************************************************************************* - * epoll set Definitions - */ - -/* The wakeup fd that is used to wake up all threads in an epoll_set informing - that the epoll set is shutdown. This wakeup fd initialized to be readable - and MUST NOT be consumed i.e the threads that woke up MUST NOT call - grpc_wakeup_fd_consume_wakeup() */ -static grpc_wakeup_fd epoll_set_wakeup_fd; - -/* The epoll set being polled right now. - See comments in workqueue_maybe_wakeup for why this is tracked. */ -static __thread epoll_set *g_current_thread_epoll_set; - -/* Forward declaration */ -static void epoll_set_delete(epoll_set *eps); - -#ifdef GRPC_TSAN -/* Currently TSAN may incorrectly flag data races between epoll_ctl and - epoll_wait for any grpc_fd structs that are added to the epoll set via - epoll_ctl and are returned (within a very short window) via epoll_wait(). - - To work-around this race, we establish a happens-before relation between - the code just-before epoll_ctl() and the code after epoll_wait() by using - this atomic */ -gpr_atm g_epoll_sync; -#endif /* defined(GRPC_TSAN) */ - -static void eps_add_ref(epoll_set *eps); -static void eps_unref(grpc_exec_ctx *exec_ctx, epoll_set *eps); - -#ifndef NDEBUG -static void eps_add_ref_dbg(epoll_set *eps, const char *reason, - const char *file, int line) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_atm old_cnt = gpr_atm_acq_load(&eps->ref_count); - gpr_log(GPR_DEBUG, "Add ref eps: %p, old:%" PRIdPTR " -> new:%" PRIdPTR - " (%s) - (%s, %d)", - eps, old_cnt, old_cnt + 1, reason, file, line); - } - eps_add_ref(eps); -} - -static void eps_unref_dbg(grpc_exec_ctx *exec_ctx, epoll_set *eps, - const char *reason, const char *file, int line) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_atm old_cnt = gpr_atm_acq_load(&eps->ref_count); - gpr_log(GPR_DEBUG, "Unref eps: %p, old:%" PRIdPTR " -> new:%" PRIdPTR - " (%s) - (%s, %d)", - eps, old_cnt, (old_cnt - 1), reason, file, line); - } - eps_unref(exec_ctx, eps); -} -#endif - -static void eps_add_ref(epoll_set *eps) { - gpr_atm_no_barrier_fetch_add(&eps->ref_count, 1); -} - -static void eps_unref(grpc_exec_ctx *exec_ctx, epoll_set *eps) { - /* If ref count went to zero, delete the epoll set. This deletion is - not done under a lock since once the ref count goes to zero, we are - guaranteed that no one else holds a reference to the epoll set (and - that there is no racing eps_add_ref() call either).*/ - if (1 == gpr_atm_full_fetch_add(&eps->ref_count, -1)) { - epoll_set_delete(eps); - } -} - -static void epoll_set_add_fd_locked(epoll_set *eps, grpc_fd *fd, - grpc_error **error) { - int err; - struct epoll_event ev; - char *err_msg; - const char *err_desc = "epoll_set_add_fd_locked"; - -#ifdef GRPC_TSAN - /* See the definition of g_epoll_sync for more context */ - gpr_atm_rel_store(&g_epoll_sync, (gpr_atm)0); -#endif /* defined(GRPC_TSAN) */ - - ev.events = (uint32_t)(EPOLLIN | EPOLLOUT | EPOLLET); - ev.data.ptr = fd; - err = epoll_ctl(eps->epoll_fd, EPOLL_CTL_ADD, fd->fd, &ev); - if (err < 0 && errno != EEXIST) { - gpr_asprintf( - &err_msg, - "epoll_ctl (epoll_fd: %d) add fd: %d failed with error: %d (%s)", - eps->epoll_fd, fd->fd, errno, strerror(errno)); - append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); - gpr_free(err_msg); - } -} - -static void epoll_set_add_wakeup_fd_locked(epoll_set *eps, - grpc_wakeup_fd *wakeup_fd, - grpc_error **error) { - struct epoll_event ev; - int err; - char *err_msg; - const char *err_desc = "epoll_set_add_wakeup_fd"; - - ev.events = (uint32_t)(EPOLLIN | EPOLLET); - ev.data.ptr = wakeup_fd; - err = epoll_ctl(eps->epoll_fd, EPOLL_CTL_ADD, - GRPC_WAKEUP_FD_GET_READ_FD(wakeup_fd), &ev); - if (err < 0 && errno != EEXIST) { - gpr_asprintf(&err_msg, - "epoll_ctl (epoll_fd: %d) add wakeup fd: %d failed with " - "error: %d (%s)", - eps->epoll_fd, GRPC_WAKEUP_FD_GET_READ_FD(wakeup_fd), errno, - strerror(errno)); - append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); - gpr_free(err_msg); - } -} - -static void epoll_set_remove_fd(epoll_set *eps, grpc_fd *fd, bool is_fd_closed, - grpc_error **error) { - int err; - char *err_msg; - const char *err_desc = "epoll_set_remove_fd"; - - /* If fd is already closed, then it would have been automatically been removed - from the epoll set */ - if (!is_fd_closed) { - err = epoll_ctl(eps->epoll_fd, EPOLL_CTL_DEL, fd->fd, NULL); - if (err < 0 && errno != ENOENT) { - gpr_asprintf( - &err_msg, - "epoll_ctl (epoll_fd: %d) del fd: %d failed with error: %d (%s)", - eps->epoll_fd, fd->fd, errno, strerror(errno)); - append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); - gpr_free(err_msg); - } - } -} - -/* Might return NULL in case of an error */ -static epoll_set *epoll_set_create(grpc_error **error) { - epoll_set *eps = NULL; - const char *err_desc = "epoll_set_create"; - - *error = GRPC_ERROR_NONE; - - eps = gpr_malloc(sizeof(*eps)); - eps->epoll_fd = -1; - - gpr_mu_init(&eps->mu); - - gpr_atm_rel_store(&eps->ref_count, 0); - gpr_atm_rel_store(&eps->poller_count, 0); - - gpr_atm_rel_store(&eps->is_shutdown, false); - - eps->epoll_fd = epoll_create1(EPOLL_CLOEXEC); - - if (eps->epoll_fd < 0) { - append_error(error, GRPC_OS_ERROR(errno, "epoll_create1"), err_desc); - goto done; - } - -done: - if (*error != GRPC_ERROR_NONE) { - epoll_set_delete(eps); - eps = NULL; - } - return eps; -} - -static void epoll_set_delete(epoll_set *eps) { - if (eps->epoll_fd >= 0) { - close(eps->epoll_fd); - } - - gpr_mu_destroy(&eps->mu); - - gpr_free(eps); -} - -static grpc_error *epoll_set_global_init() { - grpc_error *error = GRPC_ERROR_NONE; - - error = grpc_wakeup_fd_init(&epoll_set_wakeup_fd); - if (error == GRPC_ERROR_NONE) { - error = grpc_wakeup_fd_wakeup(&epoll_set_wakeup_fd); - } - - return error; -} - -static void epoll_set_global_shutdown() { - grpc_wakeup_fd_destroy(&epoll_set_wakeup_fd); -} - -/******************************************************************************* - * Fd Definitions - */ - -/* We need to keep a freelist not because of any concerns of malloc performance - * but instead so that implementations with multiple threads in (for example) - * epoll_wait deal with the race between pollset removal and incoming poll - * notifications. - * - * The problem is that the poller ultimately holds a reference to this - * object, so it is very difficult to know when is safe to free it, at least - * without some expensive synchronization. - * - * If we keep the object freelisted, in the worst case losing this race just - * becomes a spurious read notification on a reused fd. - */ - -static grpc_fd *fd_freelist = NULL; -static gpr_mu fd_freelist_mu; - -static grpc_fd *get_fd_from_freelist() { - grpc_fd *new_fd = NULL; - - gpr_mu_lock(&fd_freelist_mu); - if (fd_freelist != NULL) { - new_fd = fd_freelist; - fd_freelist = fd_freelist->freelist_next; - } - gpr_mu_unlock(&fd_freelist_mu); - return new_fd; -} - -static void add_fd_to_freelist(grpc_fd *fd) { - gpr_mu_lock(&fd_freelist_mu); - fd->freelist_next = fd_freelist; - fd_freelist = fd; - grpc_iomgr_unregister_object(&fd->iomgr_object); - - grpc_lfev_destroy(&fd->read_closure); - grpc_lfev_destroy(&fd->write_closure); - - gpr_mu_unlock(&fd_freelist_mu); -} - -static void fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); } - -static void fd_global_shutdown(void) { - gpr_mu_lock(&fd_freelist_mu); - gpr_mu_unlock(&fd_freelist_mu); - while (fd_freelist != NULL) { - grpc_fd *fd = fd_freelist; - fd_freelist = fd_freelist->freelist_next; - gpr_mu_destroy(&fd->mu); - gpr_free(fd); - } - gpr_mu_destroy(&fd_freelist_mu); -} - -static grpc_fd *fd_create(int fd, const char *name) { - grpc_fd *new_fd = get_fd_from_freelist(); - if (new_fd == NULL) { - new_fd = gpr_malloc(sizeof(grpc_fd)); - gpr_mu_init(&new_fd->mu); - } - - /* Note: It is not really needed to get the new_fd->mu lock here. If this - * is a newly created fd (or an fd we got from the freelist), no one else - * would be holding a lock to it anyway. */ - gpr_mu_lock(&new_fd->mu); - new_fd->eps = NULL; - - new_fd->fd = fd; - new_fd->orphaned = false; - grpc_lfev_init(&new_fd->read_closure); - grpc_lfev_init(&new_fd->write_closure); - - new_fd->freelist_next = NULL; - new_fd->on_done_closure = NULL; - - gpr_mu_unlock(&new_fd->mu); - - char *fd_name; - gpr_asprintf(&fd_name, "%s fd=%d", name, fd); - grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name); - gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, (void *)new_fd, fd_name); - gpr_free(fd_name); - - /* Associate the fd with one of the eps */ - add_fd_to_eps(new_fd); - return new_fd; -} - -static int fd_wrapped_fd(grpc_fd *fd) { - int ret_fd = -1; - gpr_mu_lock(&fd->mu); - if (!fd->orphaned) { - ret_fd = fd->fd; - } - gpr_mu_unlock(&fd->mu); - - return ret_fd; -} - -static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *on_done, int *release_fd, - bool already_closed, const char *reason) { - bool is_fd_closed = already_closed; - grpc_error *error = GRPC_ERROR_NONE; - epoll_set *unref_eps = NULL; - - gpr_mu_lock(&fd->mu); - fd->on_done_closure = on_done; - - /* If release_fd is not NULL, we should be relinquishing control of the file - descriptor fd->fd (but we still own the grpc_fd structure). */ - if (release_fd != NULL) { - *release_fd = fd->fd; - } else if (!is_fd_closed) { - close(fd->fd); - is_fd_closed = true; - } - - fd->orphaned = true; - - /* Remove the fd from the epoll set */ - if (fd->eps != NULL) { - epoll_set_remove_fd(fd->eps, fd, is_fd_closed, &error); - unref_eps = fd->eps; - fd->eps = NULL; - } - - GRPC_CLOSURE_SCHED(exec_ctx, fd->on_done_closure, GRPC_ERROR_REF(error)); - - gpr_mu_unlock(&fd->mu); - - /* We are done with this fd. Release it (i.e add back to freelist) */ - add_fd_to_freelist(fd); - - if (unref_eps != NULL) { - /* Unref stale epoll set here, outside the fd lock above. - The epoll set owns a workqueue which owns an fd, and unreffing - inside the lock can cause an eventual lock loop that makes TSAN very - unhappy. */ - EPS_UNREF(exec_ctx, unref_eps, "fd_orphan"); - } - GRPC_LOG_IF_ERROR("fd_orphan", GRPC_ERROR_REF(error)); - GRPC_ERROR_UNREF(error); -} - -/* This polling engine doesn't really need the read notifier functionality. So - * it just returns a dummy read notifier pollset */ -static grpc_pollset *fd_get_read_notifier_pollset(grpc_exec_ctx *exec_ctx, - grpc_fd *fd) { - return &g_read_notifier; -} - -static bool fd_is_shutdown(grpc_fd *fd) { - return grpc_lfev_is_shutdown(&fd->read_closure); -} - -/* Might be called multiple times */ -static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_error *why) { - if (grpc_lfev_set_shutdown(exec_ctx, &fd->read_closure, - GRPC_ERROR_REF(why))) { - shutdown(fd->fd, SHUT_RDWR); - grpc_lfev_set_shutdown(exec_ctx, &fd->write_closure, GRPC_ERROR_REF(why)); - } - GRPC_ERROR_UNREF(why); -} - -static void fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *closure) { - grpc_lfev_notify_on(exec_ctx, &fd->read_closure, closure, "read"); -} - -static void fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *closure) { - grpc_lfev_notify_on(exec_ctx, &fd->write_closure, closure, "write"); -} - -/******************************************************************************* - * Pollset Definitions - */ -/* TODO: sreek - Not needed anymore */ -GPR_TLS_DECL(g_current_thread_pollset); -GPR_TLS_DECL(g_current_thread_worker); - -static void pollset_worker_init(grpc_pollset_worker *worker) { - worker->next = worker->prev = NULL; - gpr_cv_init(&worker->kick_cv); -} - -/* Global state management */ -static grpc_error *pollset_global_init(void) { - gpr_tls_init(&g_current_thread_pollset); - gpr_tls_init(&g_current_thread_worker); - return GRPC_ERROR_NONE; -} - -static void pollset_global_shutdown(void) { - gpr_tls_destroy(&g_current_thread_pollset); - gpr_tls_destroy(&g_current_thread_worker); -} - -static grpc_error *pollset_worker_kick(grpc_pollset_worker *worker) { - gpr_cv_signal(&worker->kick_cv); - return GRPC_ERROR_NONE; -} - -/* Return 1 if the pollset has active threads in pollset_work (pollset must - * be locked) */ -static int pollset_has_workers(grpc_pollset *p) { - return p->root_worker.next != &p->root_worker; -} - -static void remove_worker(grpc_pollset *p, grpc_pollset_worker *worker) { - worker->prev->next = worker->next; - worker->next->prev = worker->prev; -} - -static grpc_pollset_worker *pop_front_worker(grpc_pollset *p) { - if (pollset_has_workers(p)) { - grpc_pollset_worker *w = p->root_worker.next; - remove_worker(p, w); - return w; - } else { - return NULL; - } -} - -static void push_back_worker(grpc_pollset *p, grpc_pollset_worker *worker) { - worker->next = &p->root_worker; - worker->prev = worker->next->prev; - worker->prev->next = worker->next->prev = worker; -} - -static void push_front_worker(grpc_pollset *p, grpc_pollset_worker *worker) { - worker->prev = &p->root_worker; - worker->next = worker->prev->next; - worker->prev->next = worker->next->prev = worker; -} - -/* p->mu must be held before calling this function */ -static grpc_error *pollset_kick(grpc_pollset *p, - grpc_pollset_worker *specific_worker) { - GPR_TIMER_BEGIN("pollset_kick", 0); - grpc_error *error = GRPC_ERROR_NONE; - const char *err_desc = "Kick Failure"; - grpc_pollset_worker *worker = specific_worker; - if (worker != NULL) { - if (worker == GRPC_POLLSET_KICK_BROADCAST) { - if (pollset_has_workers(p)) { - GPR_TIMER_BEGIN("pollset_kick.broadcast", 0); - for (worker = p->root_worker.next; worker != &p->root_worker; - worker = worker->next) { - if (gpr_tls_get(&g_current_thread_worker) != (intptr_t)worker) { - append_error(&error, pollset_worker_kick(worker), err_desc); - } - } - GPR_TIMER_END("pollset_kick.broadcast", 0); - } else { - p->kicked_without_pollers = true; - } - } else { - GPR_TIMER_MARK("kicked_specifically", 0); - if (gpr_tls_get(&g_current_thread_worker) != (intptr_t)worker) { - append_error(&error, pollset_worker_kick(worker), err_desc); - } - } - } else if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)p) { - /* Since worker == NULL, it means that we can kick "any" worker on this - pollset 'p'. If 'p' happens to be the same pollset this thread is - currently polling (i.e in pollset_work() function), then there is no need - to kick any other worker since the current thread can just absorb the - kick. This is the reason why we enter this case only when - g_current_thread_pollset is != p */ - - GPR_TIMER_MARK("kick_anonymous", 0); - worker = pop_front_worker(p); - if (worker != NULL) { - GPR_TIMER_MARK("finally_kick", 0); - push_back_worker(p, worker); - append_error(&error, pollset_worker_kick(worker), err_desc); - } else { - GPR_TIMER_MARK("kicked_no_pollers", 0); - p->kicked_without_pollers = true; - } - } - - GPR_TIMER_END("pollset_kick", 0); - GRPC_LOG_IF_ERROR("pollset_kick", GRPC_ERROR_REF(error)); - return error; -} - -static void pollset_init(grpc_pollset *pollset, gpr_mu **mu) { - gpr_mu_init(&pollset->mu); - *mu = &pollset->mu; - pollset->eps = NULL; - - pollset->root_worker.next = pollset->root_worker.prev = &pollset->root_worker; - pollset->kicked_without_pollers = false; - - pollset->shutting_down = false; - pollset->finish_shutdown_called = false; - pollset->shutdown_done = NULL; -} - -static void fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { - grpc_lfev_set_ready(exec_ctx, &fd->read_closure, "read"); -} - -static void fd_become_writable(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { - grpc_lfev_set_ready(exec_ctx, &fd->write_closure, "write"); -} - -static void pollset_release_epoll_set(grpc_exec_ctx *exec_ctx, grpc_pollset *ps, - char *reason) { - if (ps->eps != NULL) { - EPS_UNREF(exec_ctx, ps->eps, reason); - } - ps->eps = NULL; -} - -static void finish_shutdown_locked(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset) { - /* The pollset cannot have any workers if we are at this stage */ - GPR_ASSERT(!pollset_has_workers(pollset)); - - pollset->finish_shutdown_called = true; - - /* Release the ref and set pollset->eps to NULL */ - pollset_release_epoll_set(exec_ctx, pollset, "ps_shutdown"); - GRPC_CLOSURE_SCHED(exec_ctx, pollset->shutdown_done, GRPC_ERROR_NONE); -} - -/* pollset->mu lock must be held by the caller before calling this */ -static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_closure *closure) { - GPR_TIMER_BEGIN("pollset_shutdown", 0); - GPR_ASSERT(!pollset->shutting_down); - pollset->shutting_down = true; - pollset->shutdown_done = closure; - pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST); - - /* If the pollset has any workers, we cannot call finish_shutdown_locked() - because it would release the underlying epoll set. In such a case, we - let the last worker call finish_shutdown_locked() from pollset_work() */ - if (!pollset_has_workers(pollset)) { - GPR_ASSERT(!pollset->finish_shutdown_called); - GPR_TIMER_MARK("pollset_shutdown.finish_shutdown_locked", 0); - finish_shutdown_locked(exec_ctx, pollset); - } - GPR_TIMER_END("pollset_shutdown", 0); -} - -/* pollset_shutdown is guaranteed to be called before pollset_destroy. So other - * than destroying the mutexes, there is nothing special that needs to be done - * here */ -static void pollset_destroy(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { - GPR_ASSERT(!pollset_has_workers(pollset)); - gpr_mu_destroy(&pollset->mu); -} - -/* Blocking call */ -static void acquire_epoll_lease(epoll_set *eps) { - if (g_num_threads_per_eps > 1) { - gpr_mu_lock(&eps->mu); - } -} - -static void release_epoll_lease(epoll_set *eps) { - if (g_num_threads_per_eps > 1) { - gpr_mu_unlock(&eps->mu); - } -} - -#define GRPC_EPOLL_MAX_EVENTS 100 -static void do_epoll_wait(grpc_exec_ctx *exec_ctx, int epoll_fd, epoll_set *eps, - grpc_error **error) { - struct epoll_event ep_ev[GRPC_EPOLL_MAX_EVENTS]; - int ep_rv; - char *err_msg; - const char *err_desc = "do_epoll_wait"; - - int timeout_ms = -1; - - GRPC_SCHEDULING_START_BLOCKING_REGION; - acquire_epoll_lease(eps); - GRPC_STATS_INC_SYSCALL_POLL(exec_ctx); - ep_rv = epoll_wait(epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, timeout_ms); - release_epoll_lease(eps); - GRPC_SCHEDULING_END_BLOCKING_REGION; - - if (ep_rv < 0) { - gpr_asprintf(&err_msg, - "epoll_wait() epoll fd: %d failed with error: %d (%s)", - epoll_fd, errno, strerror(errno)); - append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); - } - -#ifdef GRPC_TSAN - /* See the definition of g_poll_sync for more details */ - gpr_atm_acq_load(&g_epoll_sync); -#endif /* defined(GRPC_TSAN) */ - - for (int i = 0; i < ep_rv; ++i) { - void *data_ptr = ep_ev[i].data.ptr; - if (data_ptr == &epoll_set_wakeup_fd) { - gpr_atm_rel_store(&eps->is_shutdown, 1); - gpr_log(GPR_INFO, "pollset poller: shutdown set"); - } else { - grpc_fd *fd = data_ptr; - int cancel = ep_ev[i].events & (EPOLLERR | EPOLLHUP); - int read_ev = ep_ev[i].events & (EPOLLIN | EPOLLPRI); - int write_ev = ep_ev[i].events & EPOLLOUT; - if (read_ev || cancel) { - fd_become_readable(exec_ctx, fd); - } - if (write_ev || cancel) { - fd_become_writable(exec_ctx, fd); - } - } - } -} - -static void epoll_set_work(grpc_exec_ctx *exec_ctx, epoll_set *eps, - grpc_error **error) { - int epoll_fd = -1; - GPR_TIMER_BEGIN("epoll_set_work", 0); - - /* Since epoll_fd is immutable, it is safe to read it without a lock on the - epoll set. */ - epoll_fd = eps->epoll_fd; - - gpr_atm_no_barrier_fetch_add(&eps->poller_count, 1); - g_current_thread_epoll_set = eps; - - do_epoll_wait(exec_ctx, epoll_fd, eps, error); - - g_current_thread_epoll_set = NULL; - gpr_atm_no_barrier_fetch_add(&eps->poller_count, -1); - - GPR_TIMER_END("epoll_set_work", 0); -} - -/* pollset->mu lock must be held by the caller before calling this. - The function pollset_work() may temporarily release the lock (pollset->mu) - during the course of its execution but it will always re-acquire the lock and - ensure that it is held by the time the function returns */ -static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker **worker_hdl, - gpr_timespec now, gpr_timespec deadline) { - GPR_TIMER_BEGIN("pollset_work", 0); - grpc_error *error = GRPC_ERROR_NONE; - - grpc_pollset_worker worker; - pollset_worker_init(&worker); - - if (worker_hdl) *worker_hdl = &worker; - - gpr_tls_set(&g_current_thread_pollset, (intptr_t)pollset); - gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker); - - if (pollset->kicked_without_pollers) { - /* If the pollset was kicked without pollers, pretend that the current - worker got the kick and skip polling. A kick indicates that there is some - work that needs attention like an event on the completion queue or an - alarm */ - GPR_TIMER_MARK("pollset_work.kicked_without_pollers", 0); - pollset->kicked_without_pollers = 0; - } else if (!pollset->shutting_down) { - push_front_worker(pollset, &worker); - - gpr_cv_wait(&worker.kick_cv, &pollset->mu, - gpr_convert_clock_type(deadline, GPR_CLOCK_REALTIME)); - /* pollset->mu locked here */ - - remove_worker(pollset, &worker); - } - - /* If we are the last worker on the pollset (i.e pollset_has_workers() is - false at this point) and the pollset is shutting down, we may have to - finish the shutdown process by calling finish_shutdown_locked(). - See pollset_shutdown() for more details. - - Note: Continuing to access pollset here is safe; it is the caller's - responsibility to not destroy a pollset when it has outstanding calls to - pollset_work() */ - if (pollset->shutting_down && !pollset_has_workers(pollset) && - !pollset->finish_shutdown_called) { - GPR_TIMER_MARK("pollset_work.finish_shutdown_locked", 0); - finish_shutdown_locked(exec_ctx, pollset); - - gpr_mu_unlock(&pollset->mu); - grpc_exec_ctx_flush(exec_ctx); - gpr_mu_lock(&pollset->mu); - } - - if (worker_hdl) *worker_hdl = NULL; - - gpr_tls_set(&g_current_thread_pollset, (intptr_t)0); - gpr_tls_set(&g_current_thread_worker, (intptr_t)0); - - GPR_TIMER_END("pollset_work", 0); - - GRPC_LOG_IF_ERROR("pollset_work", GRPC_ERROR_REF(error)); - return error; -} - -static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_fd *fd) { - /* Nothing to do */ -} - -/******************************************************************************* - * Pollset-set Definitions - */ -grpc_pollset_set g_dummy_pollset_set; -static grpc_pollset_set *pollset_set_create(void) { - return &g_dummy_pollset_set; -} - -static void pollset_set_destroy(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pss) { - /* Nothing to do */ -} - -static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss, - grpc_fd *fd) { - /* Nothing to do */ -} - -static void pollset_set_del_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss, - grpc_fd *fd) { - /* Nothing to do */ -} - -static void pollset_set_add_pollset(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pss, grpc_pollset *ps) { - /* Nothing to do */ -} - -static void pollset_set_del_pollset(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pss, grpc_pollset *ps) { - /* Nothing to do */ -} - -static void pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *bag, - grpc_pollset_set *item) { - /* Nothing to do */ -} - -static void pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *bag, - grpc_pollset_set *item) { - /* Nothing to do */ -} - -/******************************************************************************* - * Event engine binding - */ - -static void shutdown_engine(void) { - shutdown_poller_threads(); - shutdown_epoll_sets(); - fd_global_shutdown(); - pollset_global_shutdown(); - epoll_set_global_shutdown(); - gpr_log(GPR_INFO, "ev-epoll-threadpool engine shutdown complete"); -} - -static const grpc_event_engine_vtable vtable = { - .pollset_size = sizeof(grpc_pollset), - - .fd_create = fd_create, - .fd_wrapped_fd = fd_wrapped_fd, - .fd_orphan = fd_orphan, - .fd_shutdown = fd_shutdown, - .fd_is_shutdown = fd_is_shutdown, - .fd_notify_on_read = fd_notify_on_read, - .fd_notify_on_write = fd_notify_on_write, - .fd_get_read_notifier_pollset = fd_get_read_notifier_pollset, - - .pollset_init = pollset_init, - .pollset_shutdown = pollset_shutdown, - .pollset_destroy = pollset_destroy, - .pollset_work = pollset_work, - .pollset_kick = pollset_kick, - .pollset_add_fd = pollset_add_fd, - - .pollset_set_create = pollset_set_create, - .pollset_set_destroy = pollset_set_destroy, - .pollset_set_add_pollset = pollset_set_add_pollset, - .pollset_set_del_pollset = pollset_set_del_pollset, - .pollset_set_add_pollset_set = pollset_set_add_pollset_set, - .pollset_set_del_pollset_set = pollset_set_del_pollset_set, - .pollset_set_add_fd = pollset_set_add_fd, - .pollset_set_del_fd = pollset_set_del_fd, - - .shutdown_engine = shutdown_engine, -}; - -/***************************************************************************** - * Dedicated polling threads and pollsets - Definitions - */ -static void add_fd_to_eps(grpc_fd *fd) { - GPR_ASSERT(fd->eps == NULL); - GPR_TIMER_BEGIN("add_fd_to_eps", 0); - - grpc_error *error = GRPC_ERROR_NONE; - size_t idx = (size_t)gpr_atm_no_barrier_fetch_add(&g_next_eps, 1) % g_num_eps; - epoll_set *eps = g_epoll_sets[idx]; - - gpr_mu_lock(&fd->mu); - - if (fd->orphaned) { - gpr_mu_unlock(&fd->mu); - return; /* Early out */ - } - - epoll_set_add_fd_locked(eps, fd, &error); - EPS_ADD_REF(eps, "fd"); - fd->eps = eps; - - GRPC_POLLING_TRACE("add_fd_to_eps (fd: %d, eps idx = %" PRIdPTR ")", fd->fd, - idx); - gpr_mu_unlock(&fd->mu); - - GRPC_LOG_IF_ERROR("add_fd_to_eps", error); - GPR_TIMER_END("add_fd_to_eps", 0); -} - -static bool init_epoll_sets() { - grpc_error *error = GRPC_ERROR_NONE; - bool is_success = true; - - g_epoll_sets = (epoll_set **)malloc(g_num_eps * sizeof(epoll_set *)); - - for (size_t i = 0; i < g_num_eps; i++) { - g_epoll_sets[i] = epoll_set_create(&error); - if (g_epoll_sets[i] == NULL) { - gpr_log(GPR_ERROR, "Error in creating a epoll set"); - g_num_eps = i; /* Helps cleanup */ - shutdown_epoll_sets(); - is_success = false; - goto done; - } - - EPS_ADD_REF(g_epoll_sets[i], "init_epoll_sets"); - } - - gpr_atm_no_barrier_store(&g_next_eps, 0); - gpr_mu *mu; - pollset_init(&g_read_notifier, &mu); - -done: - GRPC_LOG_IF_ERROR("init_epoll_sets", error); - return is_success; -} - -static void shutdown_epoll_sets() { - if (!g_epoll_sets) { - return; - } - - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - for (size_t i = 0; i < g_num_eps; i++) { - EPS_UNREF(&exec_ctx, g_epoll_sets[i], "shutdown_epoll_sets"); - } - grpc_exec_ctx_flush(&exec_ctx); - - gpr_free(g_epoll_sets); - g_epoll_sets = NULL; - pollset_destroy(&exec_ctx, &g_read_notifier); - grpc_exec_ctx_finish(&exec_ctx); -} - -static void poller_thread_loop(void *arg) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_error *error = GRPC_ERROR_NONE; - epoll_set *eps = (epoll_set *)arg; - - while (!gpr_atm_acq_load(&eps->is_shutdown)) { - epoll_set_work(&exec_ctx, eps, &error); - grpc_exec_ctx_flush(&exec_ctx); - } - - grpc_exec_ctx_finish(&exec_ctx); - GRPC_LOG_IF_ERROR("poller_thread_loop", error); -} - -/* g_epoll_sets MUST be initialized before calling this */ -static void start_poller_threads() { - GPR_ASSERT(g_epoll_sets); - - gpr_log(GPR_INFO, "Starting poller threads"); - - size_t num_threads = g_num_eps * g_num_threads_per_eps; - g_poller_threads = (gpr_thd_id *)malloc(num_threads * sizeof(gpr_thd_id)); - gpr_thd_options options = gpr_thd_options_default(); - gpr_thd_options_set_joinable(&options); - - for (size_t i = 0; i < num_threads; i++) { - gpr_thd_new(&g_poller_threads[i], poller_thread_loop, - (void *)g_epoll_sets[i % g_num_eps], &options); - } -} - -static void shutdown_poller_threads() { - GPR_ASSERT(g_poller_threads); - GPR_ASSERT(g_epoll_sets); - grpc_error *error = GRPC_ERROR_NONE; - - gpr_log(GPR_INFO, "Shutting down pollers"); - - epoll_set *eps = NULL; - size_t num_threads = g_num_eps * g_num_threads_per_eps; - for (size_t i = 0; i < num_threads; i++) { - eps = g_epoll_sets[i]; - epoll_set_add_wakeup_fd_locked(eps, &epoll_set_wakeup_fd, &error); - } - - for (size_t i = 0; i < g_num_eps; i++) { - gpr_thd_join(g_poller_threads[i]); - } - - GRPC_LOG_IF_ERROR("shutdown_poller_threads", error); - gpr_free(g_poller_threads); - g_poller_threads = NULL; -} - -/****************************************************************************/ - -/* It is possible that GLIBC has epoll but the underlying kernel doesn't. - * Create a dummy epoll_fd to make sure epoll support is available */ -static bool is_epoll_available() { - int fd = epoll_create1(EPOLL_CLOEXEC); - if (fd < 0) { - gpr_log( - GPR_ERROR, - "epoll_create1 failed with error: %d. Not using epoll polling engine", - fd); - return false; - } - close(fd); - return true; -} - -const grpc_event_engine_vtable *grpc_init_epoll_thread_pool_linux( - bool requested_explicitly) { - if (!requested_explicitly) return NULL; - - if (!grpc_has_wakeup_fd()) { - return NULL; - } - - if (!is_epoll_available()) { - return NULL; - } - - fd_global_init(); - - if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) { - return NULL; - } - - if (!GRPC_LOG_IF_ERROR("epoll_set_global_init", epoll_set_global_init())) { - return NULL; - } - - if (!init_epoll_sets()) { - return NULL; - } - - /* TODO (sreek): Maynot be a good idea to start threads here (especially if - * this engine doesn't get picked. Consider introducing an engine_init - * function in the vtable */ - start_poller_threads(); - return &vtable; -} - -#else /* defined(GRPC_LINUX_EPOLL) */ -#if defined(GRPC_POSIX_SOCKET) -#include "src/core/lib/iomgr/ev_posix.h" -/* If GRPC_LINUX_EPOLL is not defined, it means epoll is not available. Return - * NULL */ -const grpc_event_engine_vtable *grpc_init_epoll_thread_pool_linux( - bool requested_explicitly) { - return NULL; -} -#endif /* defined(GRPC_POSIX_SOCKET) */ -#endif /* !defined(GRPC_LINUX_EPOLL) */ diff --git a/src/core/lib/iomgr/ev_epoll_thread_pool_linux.h b/src/core/lib/iomgr/ev_epoll_thread_pool_linux.h deleted file mode 100644 index 2ca68cc9d1..0000000000 --- a/src/core/lib/iomgr/ev_epoll_thread_pool_linux.h +++ /dev/null @@ -1,28 +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. - * - */ - -#ifndef GRPC_CORE_LIB_IOMGR_EV_EPOLL_THREAD_POOL_LINUX_H -#define GRPC_CORE_LIB_IOMGR_EV_EPOLL_THREAD_POOL_LINUX_H - -#include "src/core/lib/iomgr/ev_posix.h" -#include "src/core/lib/iomgr/port.h" - -const grpc_event_engine_vtable *grpc_init_epoll_thread_pool_linux( - bool requested_explicitly); - -#endif /* GRPC_CORE_LIB_IOMGR_EV_EPOLL_THREAD_POOL_LINUX_H */ diff --git a/src/core/lib/iomgr/ev_posix.c b/src/core/lib/iomgr/ev_posix.c index 424b40e425..bb43061ff5 100644 --- a/src/core/lib/iomgr/ev_posix.c +++ b/src/core/lib/iomgr/ev_posix.c @@ -31,8 +31,6 @@ #include "src/core/lib/debug/trace.h" #include "src/core/lib/iomgr/ev_epoll1_linux.h" -#include "src/core/lib/iomgr/ev_epoll_limited_pollers_linux.h" -#include "src/core/lib/iomgr/ev_epoll_thread_pool_linux.h" #include "src/core/lib/iomgr/ev_epollex_linux.h" #include "src/core/lib/iomgr/ev_epollsig_linux.h" #include "src/core/lib/iomgr/ev_poll_posix.h" @@ -66,8 +64,6 @@ typedef struct { static const event_engine_factory g_factories[] = { {"epoll1", grpc_init_epoll1_linux}, {"epollsig", grpc_init_epollsig_linux}, - {"epoll-threadpool", grpc_init_epoll_thread_pool_linux}, - {"epoll-limited", grpc_init_epoll_limited_pollers_linux}, {"poll", grpc_init_poll_posix}, {"poll-cv", grpc_init_poll_cv_posix}, {"epollex", grpc_init_epollex_linux}, diff --git a/src/core/lib/iomgr/iomgr.c b/src/core/lib/iomgr/iomgr.c index 3d19953eeb..1feea6d628 100644 --- a/src/core/lib/iomgr/iomgr.c +++ b/src/core/lib/iomgr/iomgr.c @@ -164,13 +164,7 @@ void grpc_iomgr_unregister_object(grpc_iomgr_object *obj) { bool grpc_iomgr_abort_on_leaks(void) { char *env = gpr_getenv("GRPC_ABORT_ON_LEAKS"); - if (env == NULL) return false; - static const char *truthy[] = {"yes", "Yes", "YES", "true", - "True", "TRUE", "1"}; - bool should_we = false; - for (size_t i = 0; i < GPR_ARRAY_SIZE(truthy); i++) { - if (0 == strcmp(env, truthy[i])) should_we = true; - } + bool should_we = gpr_is_true(env); gpr_free(env); return should_we; } diff --git a/src/core/lib/iomgr/tcp_posix.c b/src/core/lib/iomgr/tcp_posix.c index 7e789cc5b5..6c620ca245 100644 --- a/src/core/lib/iomgr/tcp_posix.c +++ b/src/core/lib/iomgr/tcp_posix.c @@ -67,7 +67,6 @@ typedef struct { grpc_fd *em_fd; int fd; bool finished_edge; - msg_iovlen_type iov_size; /* Number of slices to allocate per read attempt */ double target_length; double bytes_read_this_round; gpr_refcount refcount; @@ -240,7 +239,6 @@ static void tcp_do_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { size_t i; GPR_ASSERT(!tcp->finished_edge); - GPR_ASSERT(tcp->iov_size <= MAX_READ_IOVEC); GPR_ASSERT(tcp->incoming_buffer->count <= MAX_READ_IOVEC); GPR_TIMER_BEGIN("tcp_continue_read", 0); @@ -252,7 +250,7 @@ static void tcp_do_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; - msg.msg_iovlen = tcp->iov_size; + msg.msg_iovlen = (msg_iovlen_type)tcp->incoming_buffer->count; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; @@ -621,7 +619,6 @@ grpc_endpoint *grpc_tcp_create(grpc_exec_ctx *exec_ctx, grpc_fd *em_fd, tcp->min_read_chunk_size = tcp_min_read_chunk_size; tcp->max_read_chunk_size = tcp_max_read_chunk_size; tcp->bytes_read_this_round = 0; - tcp->iov_size = 1; tcp->finished_edge = true; /* paired with unref in grpc_tcp_destroy */ gpr_ref_init(&tcp->refcount, 1); diff --git a/src/core/lib/support/string.c b/src/core/lib/support/string.c index b65009754a..ec93303024 100644 --- a/src/core/lib/support/string.c +++ b/src/core/lib/support/string.c @@ -298,3 +298,16 @@ void *gpr_memrchr(const void *s, int c, size_t n) { } return NULL; } + +bool gpr_is_true(const char *s) { + if (s == NULL) { + return false; + } + static const char *truthy[] = {"yes", "true", "1"}; + for (size_t i = 0; i < GPR_ARRAY_SIZE(truthy); i++) { + if (0 == gpr_stricmp(s, truthy[i])) { + return true; + } + } + return false; +} diff --git a/src/core/lib/support/string.h b/src/core/lib/support/string.h index e11df8439d..5a56fa3a0a 100644 --- a/src/core/lib/support/string.h +++ b/src/core/lib/support/string.h @@ -19,6 +19,7 @@ #ifndef GRPC_CORE_LIB_SUPPORT_STRING_H #define GRPC_CORE_LIB_SUPPORT_STRING_H +#include <stdbool.h> #include <stddef.h> #include <grpc/support/port_platform.h> @@ -106,6 +107,8 @@ 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); #ifdef __cplusplus } #endif diff --git a/src/core/lib/surface/call.h b/src/core/lib/surface/call.h index 185bfccb77..d537637cbb 100644 --- a/src/core/lib/surface/call.h +++ b/src/core/lib/surface/call.h @@ -19,6 +19,10 @@ #ifndef GRPC_CORE_LIB_SURFACE_CALL_H #define GRPC_CORE_LIB_SURFACE_CALL_H +#ifdef __cplusplus +extern "C" { +#endif + #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/channel/context.h" #include "src/core/lib/surface/api_trace.h" @@ -26,10 +30,6 @@ #include <grpc/grpc.h> #include <grpc/impl/codegen/compression_types.h> -#ifdef __cplusplus -extern "C" { -#endif - typedef void (*grpc_ioreq_completion_func)(grpc_exec_ctx *exec_ctx, grpc_call *call, int success, void *user_data); @@ -89,7 +89,7 @@ grpc_call_error grpc_call_start_batch_and_execute(grpc_exec_ctx *exec_ctx, /* Given the top call_element, get the call object. */ grpc_call *grpc_call_from_top_element(grpc_call_element *surface_element); -void grpc_call_log_batch(char *file, int line, gpr_log_severity severity, +void grpc_call_log_batch(const char *file, int line, gpr_log_severity severity, grpc_call *call, const grpc_op *ops, size_t nops, void *tag); diff --git a/src/core/lib/surface/call_log_batch.c b/src/core/lib/surface/call_log_batch.c index 4443aba58a..4a1c265817 100644 --- a/src/core/lib/surface/call_log_batch.c +++ b/src/core/lib/surface/call_log_batch.c @@ -103,7 +103,7 @@ char *grpc_op_string(const grpc_op *op) { return out; } -void grpc_call_log_batch(char *file, int line, gpr_log_severity severity, +void grpc_call_log_batch(const char *file, int line, gpr_log_severity severity, grpc_call *call, const grpc_op *ops, size_t nops, void *tag) { char *tmp; diff --git a/src/core/lib/transport/metadata_batch.c b/src/core/lib/transport/metadata_batch.c index 8f24b8527c..a077052561 100644 --- a/src/core/lib/transport/metadata_batch.c +++ b/src/core/lib/transport/metadata_batch.c @@ -105,6 +105,7 @@ static grpc_error *maybe_link_callout(grpc_metadata_batch *batch, return GRPC_ERROR_NONE; } if (batch->idx.array[idx] == NULL) { + if (grpc_static_callout_is_default[idx]) ++batch->list.default_count; batch->idx.array[idx] = storage; return GRPC_ERROR_NONE; } @@ -120,6 +121,7 @@ static void maybe_unlink_callout(grpc_metadata_batch *batch, if (idx == GRPC_BATCH_CALLOUTS_COUNT) { return; } + if (grpc_static_callout_is_default[idx]) --batch->list.default_count; GPR_ASSERT(batch->idx.array[idx] != NULL); batch->idx.array[idx] = NULL; } diff --git a/src/core/lib/transport/metadata_batch.h b/src/core/lib/transport/metadata_batch.h index 1b11a3e252..57d298c75c 100644 --- a/src/core/lib/transport/metadata_batch.h +++ b/src/core/lib/transport/metadata_batch.h @@ -41,6 +41,7 @@ typedef struct grpc_linked_mdelem { typedef struct grpc_mdelem_list { size_t count; + size_t default_count; // Number of default keys. grpc_linked_mdelem *head; grpc_linked_mdelem *tail; } grpc_mdelem_list; diff --git a/src/core/lib/transport/static_metadata.c b/src/core/lib/transport/static_metadata.c index 28f05d5c44..b20d94aeac 100644 --- a/src/core/lib/transport/static_metadata.c +++ b/src/core/lib/transport/static_metadata.c @@ -823,6 +823,31 @@ grpc_mdelem_data grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT] = { {.refcount = &grpc_static_metadata_refcounts[97], .data.refcounted = {g_bytes + 1040, 13}}}, }; +bool grpc_static_callout_is_default[GRPC_BATCH_CALLOUTS_COUNT] = { + true, // :path + true, // :method + true, // :status + true, // :authority + true, // :scheme + true, // te + true, // grpc-message + true, // grpc-status + true, // grpc-payload-bin + true, // grpc-encoding + true, // grpc-accept-encoding + true, // grpc-server-stats-bin + true, // grpc-tags-bin + true, // grpc-trace-bin + true, // content-type + true, // content-encoding + true, // accept-encoding + true, // grpc-internal-encoding-request + true, // grpc-internal-stream-encoding-request + true, // user-agent + true, // host + true, // lb-token +}; + const uint8_t grpc_static_accept_encoding_metadata[8] = {0, 76, 77, 78, 79, 80, 81, 82}; diff --git a/src/core/lib/transport/static_metadata.h b/src/core/lib/transport/static_metadata.h index 93ab90dff8..f03a9d23b1 100644 --- a/src/core/lib/transport/static_metadata.h +++ b/src/core/lib/transport/static_metadata.h @@ -571,6 +571,8 @@ typedef union { GRPC_BATCH_CALLOUTS_COUNT) \ : GRPC_BATCH_CALLOUTS_COUNT) +extern bool grpc_static_callout_is_default[GRPC_BATCH_CALLOUTS_COUNT]; + extern const uint8_t grpc_static_accept_encoding_metadata[8]; #define GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS(algs) \ (GRPC_MAKE_MDELEM( \ diff --git a/src/core/tsi/test_creds/BUILD b/src/core/tsi/test_creds/BUILD index 4b0786d7b8..732f6d91b2 100644 --- a/src/core/tsi/test_creds/BUILD +++ b/src/core/tsi/test_creds/BUILD @@ -15,7 +15,15 @@ licenses(["notice"]) # Apache v2 exports_files([ - "ca.pem", - "server1.key", - "server1.pem", + "ca.pem", + "server1.key", + "server1.pem", + "server0.key", + "server0.pem", + "client.key", + "client.pem", + "badserver.key", + "badserver.pem", + "badclient.key", + "badclient.pem", ]) diff --git a/src/cpp/client/channel_cc.cc b/src/cpp/client/channel_cc.cc index f2d9bb07c9..19a25c838f 100644 --- a/src/cpp/client/channel_cc.cc +++ b/src/cpp/client/channel_cc.cc @@ -18,7 +18,10 @@ #include <grpc++/channel.h> +#include <chrono> +#include <condition_variable> #include <memory> +#include <mutex> #include <grpc++/client_context.h> #include <grpc++/completion_queue.h> @@ -35,17 +38,197 @@ #include <grpc/slice.h> #include <grpc/support/alloc.h> #include <grpc/support/log.h> +#include <grpc/support/sync.h> +#include <grpc/support/thd.h> +#include <grpc/support/time.h> +#include <grpc/support/useful.h> #include "src/core/lib/profiling/timers.h" +#include "src/core/lib/support/env.h" +#include "src/core/lib/support/string.h" namespace grpc { +namespace { +int kConnectivityCheckIntervalMsec = 500; +void WatchStateChange(void* arg); + +class TagSaver final : public CompletionQueueTag { + public: + explicit TagSaver(void* tag) : tag_(tag) {} + ~TagSaver() override {} + bool FinalizeResult(void** tag, bool* status) override { + *tag = tag_; + delete this; + return true; + } + + private: + void* tag_; +}; + +// Constantly watches channel connectivity status to reconnect a transiently +// disconnected channel. This is a temporary work-around before we have retry +// support. +class ChannelConnectivityWatcher : private GrpcLibraryCodegen { + public: + static void StartWatching(grpc_channel* channel) { + if (!IsDisabled()) { + std::unique_lock<std::mutex> lock(g_watcher_mu_); + if (g_watcher_ == nullptr) { + g_watcher_ = new ChannelConnectivityWatcher(); + } + g_watcher_->StartWatchingLocked(channel); + } + } + + static void StopWatching() { + if (!IsDisabled()) { + std::unique_lock<std::mutex> lock(g_watcher_mu_); + if (g_watcher_->StopWatchingLocked()) { + delete g_watcher_; + g_watcher_ = nullptr; + } + } + } + + private: + ChannelConnectivityWatcher() : channel_count_(0), shutdown_(false) { + gpr_ref_init(&ref_, 0); + gpr_thd_options options = gpr_thd_options_default(); + gpr_thd_options_set_joinable(&options); + gpr_thd_new(&thd_id_, &WatchStateChange, this, &options); + } + + static bool IsDisabled() { + char* env = gpr_getenv("GRPC_DISABLE_CHANNEL_CONNECTIVITY_WATCHER"); + bool disabled = gpr_is_true(env); + gpr_free(env); + return disabled; + } + + void WatchStateChangeImpl() { + bool ok = false; + void* tag = NULL; + CompletionQueue::NextStatus status = CompletionQueue::GOT_EVENT; + while (true) { + { + std::unique_lock<std::mutex> lock(shutdown_mu_); + if (shutdown_) { + // Drain cq_ if the watcher is shutting down + status = cq_.AsyncNext(&tag, &ok, gpr_inf_future(GPR_CLOCK_REALTIME)); + } else { + status = cq_.AsyncNext(&tag, &ok, gpr_inf_past(GPR_CLOCK_REALTIME)); + // Make sure we've seen 2 TIMEOUTs before going to sleep + if (status == CompletionQueue::TIMEOUT) { + status = cq_.AsyncNext(&tag, &ok, gpr_inf_past(GPR_CLOCK_REALTIME)); + if (status == CompletionQueue::TIMEOUT) { + shutdown_cv_.wait_for(lock, std::chrono::milliseconds( + kConnectivityCheckIntervalMsec)); + continue; + } + } + } + } + ChannelState* channel_state = static_cast<ChannelState*>(tag); + channel_state->state = + grpc_channel_check_connectivity_state(channel_state->channel, false); + if (channel_state->state == GRPC_CHANNEL_SHUTDOWN) { + void* shutdown_tag = NULL; + channel_state->shutdown_cq.Next(&shutdown_tag, &ok); + delete channel_state; + if (gpr_unref(&ref_)) { + break; + } + } else { + TagSaver* tag_saver = new TagSaver(channel_state); + grpc_channel_watch_connectivity_state( + channel_state->channel, channel_state->state, + gpr_inf_future(GPR_CLOCK_REALTIME), cq_.cq(), tag_saver); + } + } + } + + void StartWatchingLocked(grpc_channel* channel) { + if (thd_id_ != 0) { + gpr_ref(&ref_); + ++channel_count_; + ChannelState* channel_state = new ChannelState(channel); + // The first grpc_channel_watch_connectivity_state() is not used to + // monitor the channel state change, but to hold a reference of the + // c channel. So that WatchStateChangeImpl() can observe state == + // GRPC_CHANNEL_SHUTDOWN before the channel gets destroyed. + grpc_channel_watch_connectivity_state( + channel_state->channel, channel_state->state, + gpr_inf_future(GPR_CLOCK_REALTIME), channel_state->shutdown_cq.cq(), + new TagSaver(nullptr)); + grpc_channel_watch_connectivity_state( + channel_state->channel, channel_state->state, + gpr_inf_future(GPR_CLOCK_REALTIME), cq_.cq(), + new TagSaver(channel_state)); + } + } + + bool StopWatchingLocked() { + if (--channel_count_ == 0) { + { + std::unique_lock<std::mutex> lock(shutdown_mu_); + shutdown_ = true; + shutdown_cv_.notify_one(); + } + gpr_thd_join(thd_id_); + return true; + } + return false; + } + + friend void WatchStateChange(void* arg); + struct ChannelState { + explicit ChannelState(grpc_channel* channel) + : channel(channel), state(GRPC_CHANNEL_IDLE){}; + grpc_channel* channel; + grpc_connectivity_state state; + CompletionQueue shutdown_cq; + }; + gpr_thd_id thd_id_; + CompletionQueue cq_; + gpr_refcount ref_; + int channel_count_; + + std::mutex shutdown_mu_; + std::condition_variable shutdown_cv_; // protected by shutdown_mu_ + bool shutdown_; // protected by shutdown_mu_ + + static std::mutex g_watcher_mu_; + static ChannelConnectivityWatcher* g_watcher_; // protected by g_watcher_mu_ +}; + +std::mutex ChannelConnectivityWatcher::g_watcher_mu_; +ChannelConnectivityWatcher* ChannelConnectivityWatcher::g_watcher_ = nullptr; + +void WatchStateChange(void* arg) { + ChannelConnectivityWatcher* watcher = + static_cast<ChannelConnectivityWatcher*>(arg); + watcher->WatchStateChangeImpl(); +} +} // namespace + static internal::GrpcLibraryInitializer g_gli_initializer; Channel::Channel(const grpc::string& host, grpc_channel* channel) : host_(host), c_channel_(channel) { g_gli_initializer.summon(); + if (grpc_channel_support_connectivity_watcher(channel)) { + ChannelConnectivityWatcher::StartWatching(channel); + } } -Channel::~Channel() { grpc_channel_destroy(c_channel_); } +Channel::~Channel() { + const bool stop_watching = + grpc_channel_support_connectivity_watcher(c_channel_); + grpc_channel_destroy(c_channel_); + if (stop_watching) { + ChannelConnectivityWatcher::StopWatching(); + } +} namespace { @@ -130,23 +313,6 @@ grpc_connectivity_state Channel::GetState(bool try_to_connect) { return grpc_channel_check_connectivity_state(c_channel_, try_to_connect); } -namespace { -class TagSaver final : public CompletionQueueTag { - public: - explicit TagSaver(void* tag) : tag_(tag) {} - ~TagSaver() override {} - bool FinalizeResult(void** tag, bool* status) override { - *tag = tag_; - delete this; - return true; - } - - private: - void* tag_; -}; - -} // namespace - void Channel::NotifyOnStateChangeImpl(grpc_connectivity_state last_observed, gpr_timespec deadline, CompletionQueue* cq, void* tag) { diff --git a/src/cpp/server/server_cc.cc b/src/cpp/server/server_cc.cc index 2483300cb1..6bd3ecda32 100644 --- a/src/cpp/server/server_cc.cc +++ b/src/cpp/server/server_cc.cc @@ -17,6 +17,7 @@ #include <grpc++/server.h> +#include <cstdlib> #include <sstream> #include <utility> @@ -38,6 +39,7 @@ #include "src/core/ext/transport/inproc/inproc_transport.h" #include "src/core/lib/profiling/timers.h" +#include "src/core/lib/surface/call.h" #include "src/cpp/client/create_channel_internal.h" #include "src/cpp/server/health/default_health_check_service.h" #include "src/cpp/thread_manager/thread_manager.h" @@ -607,7 +609,12 @@ void Server::PerformOpsOnCall(CallOpSetInterface* ops, Call* call) { grpc_op cops[MAX_OPS]; ops->FillOps(call->call(), cops, &nops); auto result = grpc_call_start_batch(call->call(), cops, nops, ops, nullptr); - GPR_ASSERT(GRPC_CALL_OK == result); + if (result != GRPC_CALL_OK) { + gpr_log(GPR_ERROR, "Fatal: grpc_call_start_batch returned %d", result); + grpc_call_log_batch(__FILE__, __LINE__, GPR_LOG_SEVERITY_ERROR, + call->call(), cops, nops, ops); + abort(); + } } ServerInterface::BaseAsyncRequest::BaseAsyncRequest( diff --git a/src/objective-c/!ProtoCompiler-gRPCPlugin.podspec b/src/objective-c/!ProtoCompiler-gRPCPlugin.podspec index 3a282b0526..7d073c9a84 100644 --- a/src/objective-c/!ProtoCompiler-gRPCPlugin.podspec +++ b/src/objective-c/!ProtoCompiler-gRPCPlugin.podspec @@ -101,7 +101,7 @@ Pod::Spec.new do |s| s.preserve_paths = plugin # Restrict the protoc version to the one supported by this plugin. - s.dependency '!ProtoCompiler', '3.3.0' + s.dependency '!ProtoCompiler', '3.4.0' # For the Protobuf dependency not to complain: s.ios.deployment_target = '7.0' s.osx.deployment_target = '10.9' diff --git a/src/objective-c/!ProtoCompiler.podspec b/src/objective-c/!ProtoCompiler.podspec index c3f95f9f42..25c437911f 100644 --- a/src/objective-c/!ProtoCompiler.podspec +++ b/src/objective-c/!ProtoCompiler.podspec @@ -36,7 +36,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' - v = '3.3.0' + v = '3.4.0' s.version = v s.summary = 'The Protobuf Compiler (protoc) generates Objective-C files from .proto files' s.description = <<-DESC diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index 39acd5ddbc..2071827b64 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -86,8 +86,6 @@ CORE_SOURCE_FILES = [ 'src/core/lib/iomgr/endpoint_pair_windows.c', 'src/core/lib/iomgr/error.c', 'src/core/lib/iomgr/ev_epoll1_linux.c', - 'src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c', - 'src/core/lib/iomgr/ev_epoll_thread_pool_linux.c', 'src/core/lib/iomgr/ev_epollex_linux.c', 'src/core/lib/iomgr/ev_epollsig_linux.c', 'src/core/lib/iomgr/ev_poll_posix.c', diff --git a/src/python/grpcio_testing/grpc_testing/__init__.py b/src/python/grpcio_testing/grpc_testing/__init__.py index 14e25f09e2..917e11808e 100644 --- a/src/python/grpcio_testing/grpc_testing/__init__.py +++ b/src/python/grpcio_testing/grpc_testing/__init__.py @@ -293,6 +293,278 @@ class Channel(six.with_metaclass(abc.ABCMeta), grpc.Channel): raise NotImplementedError() +class UnaryUnaryServerRpc(six.with_metaclass(abc.ABCMeta)): + """Fixture for a unary-unary RPC serviced by a system under test. + + Enables users to "play client" for the RPC. + """ + + @abc.abstractmethod + def initial_metadata(self): + """Accesses the initial metadata emitted by the system under test. + + This method blocks until the system under test has added initial + metadata to the RPC (or has provided one or more response messages or + has terminated the RPC, either of which will cause gRPC Python to + synthesize initial metadata for the RPC). + + Returns: + The initial metadata for the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def cancel(self): + """Cancels the RPC.""" + raise NotImplementedError() + + @abc.abstractmethod + def termination(self): + """Blocks until the system under test has terminated the RPC. + + Returns: + A (response, trailing_metadata, code, details) sequence with the RPC's + response, trailing metadata, code, and details. + """ + raise NotImplementedError() + + +class UnaryStreamServerRpc(six.with_metaclass(abc.ABCMeta)): + """Fixture for a unary-stream RPC serviced by a system under test. + + Enables users to "play client" for the RPC. + """ + + @abc.abstractmethod + def initial_metadata(self): + """Accesses the initial metadata emitted by the system under test. + + This method blocks until the system under test has added initial + metadata to the RPC (or has provided one or more response messages or + has terminated the RPC, either of which will cause gRPC Python to + synthesize initial metadata for the RPC). + + Returns: + The initial metadata for the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def take_response(self): + """Draws one of the responses added to the RPC by the system under test. + + Successive calls to this method return responses in the same order in + which the system under test added them to the RPC. + + Returns: + A response message added to the RPC by the system under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def cancel(self): + """Cancels the RPC.""" + raise NotImplementedError() + + @abc.abstractmethod + def termination(self): + """Blocks until the system under test has terminated the RPC. + + Returns: + A (trailing_metadata, code, details) sequence with the RPC's trailing + metadata, code, and details. + """ + raise NotImplementedError() + + +class StreamUnaryServerRpc(six.with_metaclass(abc.ABCMeta)): + """Fixture for a stream-unary RPC serviced by a system under test. + + Enables users to "play client" for the RPC. + """ + + @abc.abstractmethod + def initial_metadata(self): + """Accesses the initial metadata emitted by the system under test. + + This method blocks until the system under test has added initial + metadata to the RPC (or has provided one or more response messages or + has terminated the RPC, either of which will cause gRPC Python to + synthesize initial metadata for the RPC). + + Returns: + The initial metadata for the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def send_request(self, request): + """Sends a request to the system under test. + + Args: + request: A request message for the RPC to be "sent" to the system + under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def requests_closed(self): + """Indicates the end of the RPC's request stream.""" + raise NotImplementedError() + + @abc.abstractmethod + def cancel(self): + """Cancels the RPC.""" + raise NotImplementedError() + + @abc.abstractmethod + def termination(self): + """Blocks until the system under test has terminated the RPC. + + Returns: + A (response, trailing_metadata, code, details) sequence with the RPC's + response, trailing metadata, code, and details. + """ + raise NotImplementedError() + + +class StreamStreamServerRpc(six.with_metaclass(abc.ABCMeta)): + """Fixture for a stream-stream RPC serviced by a system under test. + + Enables users to "play client" for the RPC. + """ + + @abc.abstractmethod + def initial_metadata(self): + """Accesses the initial metadata emitted by the system under test. + + This method blocks until the system under test has added initial + metadata to the RPC (or has provided one or more response messages or + has terminated the RPC, either of which will cause gRPC Python to + synthesize initial metadata for the RPC). + + Returns: + The initial metadata for the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def send_request(self, request): + """Sends a request to the system under test. + + Args: + request: A request message for the RPC to be "sent" to the system + under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def requests_closed(self): + """Indicates the end of the RPC's request stream.""" + raise NotImplementedError() + + @abc.abstractmethod + def take_response(self): + """Draws one of the responses added to the RPC by the system under test. + + Successive calls to this method return responses in the same order in + which the system under test added them to the RPC. + + Returns: + A response message added to the RPC by the system under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def cancel(self): + """Cancels the RPC.""" + raise NotImplementedError() + + @abc.abstractmethod + def termination(self): + """Blocks until the system under test has terminated the RPC. + + Returns: + A (trailing_metadata, code, details) sequence with the RPC's trailing + metadata, code, and details. + """ + raise NotImplementedError() + + +class Server(six.with_metaclass(abc.ABCMeta)): + """A server with which to test a system that services RPCs.""" + + @abc.abstractmethod + def invoke_unary_unary( + self, method_descriptor, invocation_metadata, request, timeout): + """Invokes an RPC to be serviced by the system under test. + + Args: + method_descriptor: A descriptor.MethodDescriptor describing a unary-unary + RPC method. + invocation_metadata: The RPC's invocation metadata. + request: The RPC's request. + timeout: A duration of time in seconds for the RPC or None to + indicate that the RPC has no time limit. + + Returns: + A UnaryUnaryServerRpc with which to "play client" for the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def invoke_unary_stream( + self, method_descriptor, invocation_metadata, request, timeout): + """Invokes an RPC to be serviced by the system under test. + + Args: + method_descriptor: A descriptor.MethodDescriptor describing a unary-stream + RPC method. + invocation_metadata: The RPC's invocation metadata. + request: The RPC's request. + timeout: A duration of time in seconds for the RPC or None to + indicate that the RPC has no time limit. + + Returns: + A UnaryStreamServerRpc with which to "play client" for the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def invoke_stream_unary( + self, method_descriptor, invocation_metadata, timeout): + """Invokes an RPC to be serviced by the system under test. + + Args: + method_descriptor: A descriptor.MethodDescriptor describing a stream-unary + RPC method. + invocation_metadata: The RPC's invocation metadata. + timeout: A duration of time in seconds for the RPC or None to + indicate that the RPC has no time limit. + + Returns: + A StreamUnaryServerRpc with which to "play client" for the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def invoke_stream_stream( + self, method_descriptor, invocation_metadata, timeout): + """Invokes an RPC to be serviced by the system under test. + + Args: + method_descriptor: A descriptor.MethodDescriptor describing a stream-stream + RPC method. + invocation_metadata: The RPC's invocation metadata. + timeout: A duration of time in seconds for the RPC or None to + indicate that the RPC has no time limit. + + Returns: + A StreamStreamServerRpc with which to "play client" for the RPC. + """ + raise NotImplementedError() + + class Time(six.with_metaclass(abc.ABCMeta)): """A simulation of time. @@ -406,3 +678,20 @@ def channel(service_descriptors, time): """ from grpc_testing import _channel return _channel.testing_channel(service_descriptors, time) + + +def server_from_dictionary(descriptors_to_servicers, time): + """Creates a Server for use in tests of a gRPC Python-using system. + + Args: + descriptors_to_servicers: A dictionary from descriptor.ServiceDescriptors + defining RPC services to servicer objects (usually instances of classes + that implement "Servicer" interfaces defined in generated "_pb2_grpc" + modules) implementing those services. + time: A Time to be used for tests. + + Returns: + A Server for use in tests. + """ + from grpc_testing import _server + return _server.server_from_dictionary(descriptors_to_servicers, time) diff --git a/src/python/grpcio_testing/grpc_testing/_common.py b/src/python/grpcio_testing/grpc_testing/_common.py index cb4a7f5fa2..1517434ca7 100644 --- a/src/python/grpcio_testing/grpc_testing/_common.py +++ b/src/python/grpcio_testing/grpc_testing/_common.py @@ -37,6 +37,16 @@ def fuss_with_metadata(metadata): return _fuss(tuple(metadata)) +def rpc_names(service_descriptors): + rpc_names_to_descriptors = {} + for service_descriptor in service_descriptors: + for method_descriptor in service_descriptor.methods_by_name.values(): + rpc_name = '/{}/{}'.format( + service_descriptor.full_name, method_descriptor.name) + rpc_names_to_descriptors[rpc_name] = method_descriptor + return rpc_names_to_descriptors + + class ChannelRpcRead( collections.namedtuple( 'ChannelRpcRead', @@ -90,3 +100,61 @@ class ChannelHandler(six.with_metaclass(abc.ABCMeta)): self, method_full_rpc_name, invocation_metadata, requests, requests_closed, timeout): raise NotImplementedError() + + +class ServerRpcRead( + collections.namedtuple('ServerRpcRead', + ('request', 'requests_closed', 'terminated',))): + pass + + +REQUESTS_CLOSED = ServerRpcRead(None, True, False) +TERMINATED = ServerRpcRead(None, False, True) + + +class ServerRpcHandler(six.with_metaclass(abc.ABCMeta)): + + @abc.abstractmethod + def send_initial_metadata(self, initial_metadata): + raise NotImplementedError() + + @abc.abstractmethod + def take_request(self): + raise NotImplementedError() + + @abc.abstractmethod + def add_response(self, response): + raise NotImplementedError() + + @abc.abstractmethod + def send_termination(self, trailing_metadata, code, details): + raise NotImplementedError() + + @abc.abstractmethod + def add_termination_callback(self, callback): + raise NotImplementedError() + + +class Serverish(six.with_metaclass(abc.ABCMeta)): + + @abc.abstractmethod + def invoke_unary_unary( + self, method_descriptor, handler, invocation_metadata, request, + deadline): + raise NotImplementedError() + + @abc.abstractmethod + def invoke_unary_stream( + self, method_descriptor, handler, invocation_metadata, request, + deadline): + raise NotImplementedError() + + @abc.abstractmethod + def invoke_stream_unary( + self, method_descriptor, handler, invocation_metadata, deadline): + raise NotImplementedError() + + @abc.abstractmethod + def invoke_stream_stream( + self, method_descriptor, handler, invocation_metadata, deadline): + raise NotImplementedError() diff --git a/src/python/grpcio_testing/grpc_testing/_server/__init__.py b/src/python/grpcio_testing/grpc_testing/_server/__init__.py new file mode 100644 index 0000000000..759512949a --- /dev/null +++ b/src/python/grpcio_testing/grpc_testing/_server/__init__.py @@ -0,0 +1,20 @@ +# 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. + +from grpc_testing._server import _server + + +def server_from_dictionary(descriptors_to_servicers, time): + return _server.server_from_descriptor_to_servicers( + descriptors_to_servicers, time) diff --git a/src/python/grpcio_testing/grpc_testing/_server/_handler.py b/src/python/grpcio_testing/grpc_testing/_server/_handler.py new file mode 100644 index 0000000000..b47e04c718 --- /dev/null +++ b/src/python/grpcio_testing/grpc_testing/_server/_handler.py @@ -0,0 +1,215 @@ +# 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. + +import abc +import threading + +import grpc +from grpc_testing import _common + +_CLIENT_INACTIVE = object() + + +class Handler(_common.ServerRpcHandler): + + @abc.abstractmethod + def initial_metadata(self): + raise NotImplementedError() + + @abc.abstractmethod + def add_request(self, request): + raise NotImplementedError() + + @abc.abstractmethod + def take_response(self): + raise NotImplementedError() + + @abc.abstractmethod + def requests_closed(self): + raise NotImplementedError() + + @abc.abstractmethod + def cancel(self): + raise NotImplementedError() + + @abc.abstractmethod + def unary_response_termination(self): + raise NotImplementedError() + + @abc.abstractmethod + def stream_response_termination(self): + raise NotImplementedError() + + +class _Handler(Handler): + + def __init__(self, requests_closed): + self._condition = threading.Condition() + self._requests = [] + self._requests_closed = requests_closed + self._initial_metadata = None + self._responses = [] + self._trailing_metadata = None + self._code = None + self._details = None + self._unary_response = None + self._expiration_future = None + self._termination_callbacks = [] + + def send_initial_metadata(self, initial_metadata): + with self._condition: + self._initial_metadata = initial_metadata + self._condition.notify_all() + + def take_request(self): + with self._condition: + while True: + if self._code is None: + if self._requests: + request = self._requests.pop(0) + self._condition.notify_all() + return _common.ServerRpcRead(request, False, False) + elif self._requests_closed: + return _common.REQUESTS_CLOSED + else: + self._condition.wait() + else: + return _common.TERMINATED + + def is_active(self): + with self._condition: + return self._code is None + + def add_response(self, response): + with self._condition: + self._responses.append(response) + self._condition.notify_all() + + def send_termination(self, trailing_metadata, code, details): + with self._condition: + self._trailing_metadata = trailing_metadata + self._code = code + self._details = details + if self._expiration_future is not None: + self._expiration_future.cancel() + self._condition.notify_all() + + def add_termination_callback(self, termination_callback): + with self._condition: + if self._code is None: + self._termination_callbacks.append(termination_callback) + return True + else: + return False + + def initial_metadata(self): + with self._condition: + while True: + if self._initial_metadata is None: + if self._code is None: + self._condition.wait() + else: + raise ValueError( + 'No initial metadata despite status code!') + else: + return self._initial_metadata + + def add_request(self, request): + with self._condition: + self._requests.append(request) + self._condition.notify_all() + + def take_response(self): + with self._condition: + while True: + if self._responses: + response = self._responses.pop(0) + self._condition.notify_all() + return response + elif self._code is None: + self._condition.wait() + else: + raise ValueError('No more responses!') + + def requests_closed(self): + with self._condition: + self._requests_closed = True + self._condition.notify_all() + + def cancel(self): + with self._condition: + if self._code is None: + self._code = _CLIENT_INACTIVE + termination_callbacks = self._termination_callbacks + self._termination_callbacks = None + if self._expiration_future is not None: + self._expiration_future.cancel() + self._condition.notify_all() + for termination_callback in termination_callbacks: + termination_callback() + + def unary_response_termination(self): + with self._condition: + while True: + if self._code is _CLIENT_INACTIVE: + raise ValueError('Huh? Cancelled but wanting status?') + elif self._code is None: + self._condition.wait() + else: + if self._unary_response is None: + if self._responses: + self._unary_response = self._responses.pop(0) + return ( + self._unary_response, self._trailing_metadata, + self._code, self._details,) + + + def stream_response_termination(self): + with self._condition: + while True: + if self._code is _CLIENT_INACTIVE: + raise ValueError('Huh? Cancelled but wanting status?') + elif self._code is None: + self._condition.wait() + else: + return self._trailing_metadata, self._code, self._details, + + def expire(self): + with self._condition: + if self._code is None: + if self._initial_metadata is None: + self._initial_metadata = _common.FUSSED_EMPTY_METADATA + self._trailing_metadata = _common.FUSSED_EMPTY_METADATA + self._code = grpc.StatusCode.DEADLINE_EXCEEDED + self._details = 'Took too much time!' + termination_callbacks = self._termination_callbacks + self._termination_callbacks = None + self._condition.notify_all() + for termination_callback in termination_callbacks: + termination_callback() + + def set_expiration_future(self, expiration_future): + with self._condition: + self._expiration_future = expiration_future + + +def handler_without_deadline(requests_closed): + return _Handler(requests_closed) + + +def handler_with_deadline(requests_closed, time, deadline): + handler = _Handler(requests_closed) + expiration_future = time.call_at(handler.expire, deadline) + handler.set_expiration_future(expiration_future) + return handler diff --git a/src/python/grpcio_testing/grpc_testing/_server/_rpc.py b/src/python/grpcio_testing/grpc_testing/_server/_rpc.py new file mode 100644 index 0000000000..f81876f4b2 --- /dev/null +++ b/src/python/grpcio_testing/grpc_testing/_server/_rpc.py @@ -0,0 +1,153 @@ +# 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. + +import logging +import threading + +import grpc +from grpc_testing import _common + + +class Rpc(object): + + def __init__(self, handler, invocation_metadata): + self._condition = threading.Condition() + self._handler = handler + self._invocation_metadata = invocation_metadata + self._initial_metadata_sent = False + self._pending_trailing_metadata = None + self._pending_code = None + self._pending_details = None + self._callbacks = [] + self._active = True + self._rpc_errors = [] + + def _ensure_initial_metadata_sent(self): + if not self._initial_metadata_sent: + self._handler.send_initial_metadata(_common.FUSSED_EMPTY_METADATA) + self._initial_metadata_sent = True + + def _call_back(self): + callbacks = tuple(self._callbacks) + self._callbacks = None + + def call_back(): + for callback in callbacks: + try: + callback() + except Exception: # pylint: disable=broad-except + logging.exception('Exception calling server-side callback!') + + callback_calling_thread = threading.Thread(target=call_back) + callback_calling_thread.start() + + def _terminate(self, trailing_metadata, code, details): + if self._active: + self._active = False + self._handler.send_termination(trailing_metadata, code, details) + self._call_back() + self._condition.notify_all() + + def _complete(self): + if self._pending_trailing_metadata is None: + trailing_metadata = _common.FUSSED_EMPTY_METADATA + else: + trailing_metadata = self._pending_trailing_metadata + if self._pending_code is None: + code = grpc.StatusCode.OK + else: + code = self._pending_code + details = '' if self._pending_details is None else self._pending_details + self._terminate(trailing_metadata, code, details) + + def _abort(self, code, details): + self._terminate(_common.FUSSED_EMPTY_METADATA, code, details) + + def add_rpc_error(self, rpc_error): + with self._condition: + self._rpc_errors.append(rpc_error) + + def application_cancel(self): + with self._condition: + self._abort( + grpc.StatusCode.CANCELLED, + 'Cancelled by server-side application!') + + def application_exception_abort(self, exception): + with self._condition: + if exception not in self._rpc_errors: + logging.exception('Exception calling application!') + self._abort( + grpc.StatusCode.UNKNOWN, + 'Exception calling application: {}'.format(exception)) + + def extrinsic_abort(self): + with self._condition: + if self._active: + self._active = False + self._call_back() + self._condition.notify_all() + + def unary_response_complete(self, response): + with self._condition: + self._ensure_initial_metadata_sent() + self._handler.add_response(response) + self._complete() + + def stream_response(self, response): + with self._condition: + self._ensure_initial_metadata_sent() + self._handler.add_response(response) + + def stream_response_complete(self): + with self._condition: + self._ensure_initial_metadata_sent() + self._complete() + + def send_initial_metadata(self, initial_metadata): + with self._condition: + if self._initial_metadata_sent: + return False + else: + self._handler.send_initial_metadata(initial_metadata) + self._initial_metadata_sent = True + return True + + def is_active(self): + with self._condition: + return self._active + + def add_callback(self, callback): + with self._condition: + if self._callbacks is None: + return False + else: + self._callbacks.append(callback) + return True + + def invocation_metadata(self): + with self._condition: + return self._invocation_metadata + + def set_trailing_metadata(self, trailing_metadata): + with self._condition: + self._pending_trailing_metadata = trailing_metadata + + def set_code(self, code): + with self._condition: + self._pending_code = code + + def set_details(self, details): + with self._condition: + self._pending_details = details diff --git a/src/python/grpcio_testing/grpc_testing/_server/_server.py b/src/python/grpcio_testing/grpc_testing/_server/_server.py new file mode 100644 index 0000000000..66bcfc13c0 --- /dev/null +++ b/src/python/grpcio_testing/grpc_testing/_server/_server.py @@ -0,0 +1,149 @@ +# 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. + +import threading + +import grpc_testing +from grpc_testing import _common +from grpc_testing._server import _handler +from grpc_testing._server import _rpc +from grpc_testing._server import _server_rpc +from grpc_testing._server import _service +from grpc_testing._server import _servicer_context + + +def _implementation(descriptors_to_servicers, method_descriptor): + servicer = descriptors_to_servicers[method_descriptor.containing_service] + return getattr(servicer, method_descriptor.name) + + +def _unary_unary_service(request): + def service(implementation, rpc, servicer_context): + _service.unary_unary( + implementation, rpc, request, servicer_context) + return service + + +def _unary_stream_service(request): + def service(implementation, rpc, servicer_context): + _service.unary_stream( + implementation, rpc, request, servicer_context) + return service + + +def _stream_unary_service(handler): + def service(implementation, rpc, servicer_context): + _service.stream_unary(implementation, rpc, handler, servicer_context) + return service + + +def _stream_stream_service(handler): + def service(implementation, rpc, servicer_context): + _service.stream_stream(implementation, rpc, handler, servicer_context) + return service + + +class _Serverish(_common.Serverish): + + def __init__(self, descriptors_to_servicers, time): + self._descriptors_to_servicers = descriptors_to_servicers + self._time = time + + def _invoke( + self, service_behavior, method_descriptor, handler, + invocation_metadata, deadline): + implementation = _implementation( + self._descriptors_to_servicers, method_descriptor) + rpc = _rpc.Rpc(handler, invocation_metadata) + if handler.add_termination_callback(rpc.extrinsic_abort): + servicer_context = _servicer_context.ServicerContext( + rpc, self._time, deadline) + service_thread = threading.Thread( + target=service_behavior, + args=(implementation, rpc, servicer_context,)) + service_thread.start() + + def invoke_unary_unary( + self, method_descriptor, handler, invocation_metadata, request, + deadline): + self._invoke( + _unary_unary_service(request), method_descriptor, handler, + invocation_metadata, deadline) + + def invoke_unary_stream( + self, method_descriptor, handler, invocation_metadata, request, + deadline): + self._invoke( + _unary_stream_service(request), method_descriptor, handler, + invocation_metadata, deadline) + + def invoke_stream_unary( + self, method_descriptor, handler, invocation_metadata, deadline): + self._invoke( + _stream_unary_service(handler), method_descriptor, handler, + invocation_metadata, deadline) + + def invoke_stream_stream( + self, method_descriptor, handler, invocation_metadata, deadline): + self._invoke( + _stream_stream_service(handler), method_descriptor, handler, + invocation_metadata, deadline) + + +def _deadline_and_handler(requests_closed, time, timeout): + if timeout is None: + return None, _handler.handler_without_deadline(requests_closed) + else: + deadline = time.time() + timeout + handler = _handler.handler_with_deadline(requests_closed, time, deadline) + return deadline, handler + + +class _Server(grpc_testing.Server): + + def __init__(self, serverish, time): + self._serverish = serverish + self._time = time + + def invoke_unary_unary( + self, method_descriptor, invocation_metadata, request, timeout): + deadline, handler = _deadline_and_handler(True, self._time, timeout) + self._serverish.invoke_unary_unary( + method_descriptor, handler, invocation_metadata, request, deadline) + return _server_rpc.UnaryUnaryServerRpc(handler) + + def invoke_unary_stream( + self, method_descriptor, invocation_metadata, request, timeout): + deadline, handler = _deadline_and_handler(True, self._time, timeout) + self._serverish.invoke_unary_stream( + method_descriptor, handler, invocation_metadata, request, deadline) + return _server_rpc.UnaryStreamServerRpc(handler) + + def invoke_stream_unary( + self, method_descriptor, invocation_metadata, timeout): + deadline, handler = _deadline_and_handler(False, self._time, timeout) + self._serverish.invoke_stream_unary( + method_descriptor, handler, invocation_metadata, deadline) + return _server_rpc.StreamUnaryServerRpc(handler) + + def invoke_stream_stream( + self, method_descriptor, invocation_metadata, timeout): + deadline, handler = _deadline_and_handler(False, self._time, timeout) + self._serverish.invoke_stream_stream( + method_descriptor, handler, invocation_metadata, deadline) + return _server_rpc.StreamStreamServerRpc(handler) + + +def server_from_descriptor_to_servicers(descriptors_to_servicers, time): + return _Server(_Serverish(descriptors_to_servicers, time), time) diff --git a/src/python/grpcio_testing/grpc_testing/_server/_server_rpc.py b/src/python/grpcio_testing/grpc_testing/_server/_server_rpc.py new file mode 100644 index 0000000000..30de8ff0e2 --- /dev/null +++ b/src/python/grpcio_testing/grpc_testing/_server/_server_rpc.py @@ -0,0 +1,93 @@ +# 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. + +import grpc_testing + + +class UnaryUnaryServerRpc(grpc_testing.UnaryUnaryServerRpc): + + def __init__(self, handler): + self._handler = handler + + def initial_metadata(self): + return self._handler.initial_metadata() + + def cancel(self): + self._handler.cancel() + + def termination(self): + return self._handler.unary_response_termination() + + +class UnaryStreamServerRpc(grpc_testing.UnaryStreamServerRpc): + + def __init__(self, handler): + self._handler = handler + + def initial_metadata(self): + return self._handler.initial_metadata() + + def take_response(self): + return self._handler.take_response() + + def cancel(self): + self._handler.cancel() + + def termination(self): + return self._handler.stream_response_termination() + + +class StreamUnaryServerRpc(grpc_testing.StreamUnaryServerRpc): + + def __init__(self, handler): + self._handler = handler + + def initial_metadata(self): + return self._handler.initial_metadata() + + def send_request(self, request): + self._handler.add_request(request) + + def requests_closed(self): + self._handler.requests_closed() + + def cancel(self): + self._handler.cancel() + + def termination(self): + return self._handler.unary_response_termination() + + +class StreamStreamServerRpc(grpc_testing.StreamStreamServerRpc): + + def __init__(self, handler): + self._handler = handler + + def initial_metadata(self): + return self._handler.initial_metadata() + + def send_request(self, request): + self._handler.add_request(request) + + def requests_closed(self): + self._handler.requests_closed() + + def take_response(self): + return self._handler.take_response() + + def cancel(self): + self._handler.cancel() + + def termination(self): + return self._handler.stream_response_termination() diff --git a/src/python/grpcio_testing/grpc_testing/_server/_service.py b/src/python/grpcio_testing/grpc_testing/_server/_service.py new file mode 100644 index 0000000000..36b0a2f7ff --- /dev/null +++ b/src/python/grpcio_testing/grpc_testing/_server/_service.py @@ -0,0 +1,88 @@ +# 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. + +import grpc + + +class _RequestIterator(object): + + def __init__(self, rpc, handler): + self._rpc = rpc + self._handler = handler + + def _next(self): + read = self._handler.take_request() + if read.requests_closed: + raise StopIteration() + elif read.terminated: + rpc_error = grpc.RpcError() + self._rpc.add_rpc_error(rpc_error) + raise rpc_error + else: + return read.request + + def __iter__(self): + return self + + def __next__(self): + return self._next() + + def next(self): + return self._next() + + +def _unary_response(argument, implementation, rpc, servicer_context): + try: + response = implementation(argument, servicer_context) + except Exception as exception: # pylint: disable=broad-except + rpc.application_exception_abort(exception) + else: + rpc.unary_response_complete(response) + + +def _stream_response(argument, implementation, rpc, servicer_context): + try: + response_iterator = implementation(argument, servicer_context) + except Exception as exception: # pylint: disable=broad-except + rpc.application_exception_abort(exception) + else: + while True: + try: + response = next(response_iterator) + except StopIteration: + rpc.stream_response_complete() + break + except Exception as exception: # pylint: disable=broad-except + rpc.application_exception_abort(exception) + break + else: + rpc.stream_response(response) + + +def unary_unary(implementation, rpc, request, servicer_context): + _unary_response(request, implementation, rpc, servicer_context) + + +def unary_stream(implementation, rpc, request, servicer_context): + _stream_response(request, implementation, rpc, servicer_context) + + +def stream_unary(implementation, rpc, handler, servicer_context): + _unary_response( + _RequestIterator(rpc, handler), implementation, rpc, servicer_context) + + +def stream_stream(implementation, rpc, handler, servicer_context): + _stream_response( + _RequestIterator(rpc, handler), implementation, rpc, servicer_context) diff --git a/src/python/grpcio_testing/grpc_testing/_server/_servicer_context.py b/src/python/grpcio_testing/grpc_testing/_server/_servicer_context.py new file mode 100644 index 0000000000..496689ded0 --- /dev/null +++ b/src/python/grpcio_testing/grpc_testing/_server/_servicer_context.py @@ -0,0 +1,74 @@ +# 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. + +import grpc +from grpc_testing import _common + + +class ServicerContext(grpc.ServicerContext): + + def __init__(self, rpc, time, deadline): + self._rpc = rpc + self._time = time + self._deadline = deadline + + def is_active(self): + return self._rpc.is_active() + + def time_remaining(self): + if self._rpc.is_active(): + if self._deadline is None: + return None + else: + return max(0.0, self._deadline - self._time.time()) + else: + return 0.0 + + def cancel(self): + self._rpc.application_cancel() + + def add_callback(self, callback): + return self._rpc.add_callback(callback) + + def invocation_metadata(self): + return self._rpc.invocation_metadata() + + def peer(self): + raise NotImplementedError() + + def peer_identities(self): + raise NotImplementedError() + + def peer_identity_key(self): + raise NotImplementedError() + + def auth_context(self): + raise NotImplementedError() + + def send_initial_metadata(self, initial_metadata): + initial_metadata_sent = self._rpc.send_initial_metadata( + _common.fuss_with_metadata(initial_metadata)) + if not initial_metadata_sent: + raise ValueError( + 'ServicerContext.send_initial_metadata called too late!') + + def set_trailing_metadata(self, trailing_metadata): + self._rpc.set_trailing_metadata( + _common.fuss_with_metadata(trailing_metadata)) + + def set_code(self, code): + self._rpc.set_code(code) + + def set_details(self, details): + self._rpc.set_details(details) diff --git a/src/python/grpcio_tests/tests/testing/_server_application.py b/src/python/grpcio_tests/tests/testing/_server_application.py new file mode 100644 index 0000000000..06f09c8cb4 --- /dev/null +++ b/src/python/grpcio_tests/tests/testing/_server_application.py @@ -0,0 +1,66 @@ +# 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. +"""An example gRPC Python-using server-side application.""" + +import grpc + +# requests_pb2 is a semantic dependency of this module. +from tests.testing import _application_common +from tests.testing.proto import requests_pb2 # pylint: disable=unused-import +from tests.testing.proto import services_pb2 +from tests.testing.proto import services_pb2_grpc + + +class FirstServiceServicer(services_pb2_grpc.FirstServiceServicer): + """Services RPCs.""" + + def UnUn(self, request, context): + if _application_common.UNARY_UNARY_REQUEST == request: + return _application_common.UNARY_UNARY_RESPONSE + else: + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) + context.set_details('Something is wrong with your request!') + return services_pb2.Down() + + def UnStre(self, request, context): + if _application_common.UNARY_STREAM_REQUEST != request: + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) + context.set_details('Something is wrong with your request!') + return + yield services_pb2.Strange() + + def StreUn(self, request_iterator, context): + context.send_initial_metadata(( + ('server_application_metadata_key', 'Hi there!',),)) + for request in request_iterator: + if request != _application_common.STREAM_UNARY_REQUEST: + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) + context.set_details('Something is wrong with your request!') + return services_pb2.Strange() + elif not context.is_active(): + return services_pb2.Strange() + else: + return _application_common.STREAM_UNARY_RESPONSE + + def StreStre(self, request_iterator, context): + for request in request_iterator: + if request != _application_common.STREAM_STREAM_REQUEST: + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) + context.set_details('Something is wrong with your request!') + return + elif not context.is_active(): + return + else: + yield _application_common.STREAM_STREAM_RESPONSE + yield _application_common.STREAM_STREAM_RESPONSE diff --git a/src/python/grpcio_tests/tests/testing/_server_test.py b/src/python/grpcio_tests/tests/testing/_server_test.py new file mode 100644 index 0000000000..7897bcce01 --- /dev/null +++ b/src/python/grpcio_tests/tests/testing/_server_test.py @@ -0,0 +1,169 @@ +# 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. + +import time +import unittest + +import grpc +import grpc_testing + +from tests.testing import _application_common +from tests.testing import _application_testing_common +from tests.testing import _server_application +from tests.testing.proto import services_pb2 + + +# TODO(https://github.com/google/protobuf/issues/3452): Drop this skip. +@unittest.skipIf( + services_pb2.DESCRIPTOR.services_by_name.get('FirstService') is None, + 'Fix protobuf issue 3452!') +class FirstServiceServicerTest(unittest.TestCase): + + def setUp(self): + self._real_time = grpc_testing.strict_real_time() + self._fake_time = grpc_testing.strict_fake_time(time.time()) + servicer = _server_application.FirstServiceServicer() + descriptors_to_servicers = { + _application_testing_common.FIRST_SERVICE: servicer + } + self._real_time_server = grpc_testing.server_from_dictionary( + descriptors_to_servicers, self._real_time) + self._fake_time_server = grpc_testing.server_from_dictionary( + descriptors_to_servicers, self._fake_time) + + def test_successful_unary_unary(self): + rpc = self._real_time_server.invoke_unary_unary( + _application_testing_common.FIRST_SERVICE_UNUN, (), + _application_common.UNARY_UNARY_REQUEST, None) + initial_metadata = rpc.initial_metadata() + response, trailing_metadata, code, details = rpc.termination() + + self.assertEqual(_application_common.UNARY_UNARY_RESPONSE, response) + self.assertIs(code, grpc.StatusCode.OK) + + def test_successful_unary_stream(self): + rpc = self._real_time_server.invoke_unary_stream( + _application_testing_common.FIRST_SERVICE_UNSTRE, (), + _application_common.UNARY_STREAM_REQUEST, None) + initial_metadata = rpc.initial_metadata() + trailing_metadata, code, details = rpc.termination() + + self.assertIs(code, grpc.StatusCode.OK) + + def test_successful_stream_unary(self): + rpc = self._real_time_server.invoke_stream_unary( + _application_testing_common.FIRST_SERVICE_STREUN, (), None) + rpc.send_request(_application_common.STREAM_UNARY_REQUEST) + rpc.send_request(_application_common.STREAM_UNARY_REQUEST) + rpc.send_request(_application_common.STREAM_UNARY_REQUEST) + rpc.requests_closed() + initial_metadata = rpc.initial_metadata() + response, trailing_metadata, code, details = rpc.termination() + + self.assertEqual(_application_common.STREAM_UNARY_RESPONSE, response) + self.assertIs(code, grpc.StatusCode.OK) + + def test_successful_stream_stream(self): + rpc = self._real_time_server.invoke_stream_stream( + _application_testing_common.FIRST_SERVICE_STRESTRE, (), None) + rpc.send_request(_application_common.STREAM_STREAM_REQUEST) + initial_metadata = rpc.initial_metadata() + responses = [ + rpc.take_response(), + rpc.take_response(), + ] + rpc.send_request(_application_common.STREAM_STREAM_REQUEST) + rpc.send_request(_application_common.STREAM_STREAM_REQUEST) + responses.extend([ + rpc.take_response(), + rpc.take_response(), + rpc.take_response(), + rpc.take_response(), + ]) + rpc.requests_closed() + trailing_metadata, code, details = rpc.termination() + + for response in responses: + self.assertEqual(_application_common.STREAM_STREAM_RESPONSE, + response) + self.assertIs(code, grpc.StatusCode.OK) + + def test_server_rpc_idempotence(self): + rpc = self._real_time_server.invoke_unary_unary( + _application_testing_common.FIRST_SERVICE_UNUN, (), + _application_common.UNARY_UNARY_REQUEST, None) + first_initial_metadata = rpc.initial_metadata() + second_initial_metadata = rpc.initial_metadata() + third_initial_metadata = rpc.initial_metadata() + first_termination = rpc.termination() + second_termination = rpc.termination() + third_termination = rpc.termination() + + for later_initial_metadata in (second_initial_metadata, + third_initial_metadata,): + self.assertEqual(first_initial_metadata, later_initial_metadata) + response = first_termination[0] + terminal_metadata = first_termination[1] + code = first_termination[2] + details = first_termination[3] + for later_termination in (second_termination, third_termination,): + self.assertEqual(response, later_termination[0]) + self.assertEqual(terminal_metadata, later_termination[1]) + self.assertIs(code, later_termination[2]) + self.assertEqual(details, later_termination[3]) + self.assertEqual(_application_common.UNARY_UNARY_RESPONSE, response) + self.assertIs(code, grpc.StatusCode.OK) + + def test_misbehaving_client_unary_unary(self): + rpc = self._real_time_server.invoke_unary_unary( + _application_testing_common.FIRST_SERVICE_UNUN, (), + _application_common.ERRONEOUS_UNARY_UNARY_REQUEST, None) + initial_metadata = rpc.initial_metadata() + response, trailing_metadata, code, details = rpc.termination() + + self.assertIsNot(code, grpc.StatusCode.OK) + + def test_infinite_request_stream_real_time(self): + rpc = self._real_time_server.invoke_stream_unary( + _application_testing_common.FIRST_SERVICE_STREUN, (), + _application_common.INFINITE_REQUEST_STREAM_TIMEOUT) + rpc.send_request(_application_common.STREAM_UNARY_REQUEST) + rpc.send_request(_application_common.STREAM_UNARY_REQUEST) + rpc.send_request(_application_common.STREAM_UNARY_REQUEST) + initial_metadata = rpc.initial_metadata() + self._real_time.sleep_for( + _application_common.INFINITE_REQUEST_STREAM_TIMEOUT * 2) + rpc.send_request(_application_common.STREAM_UNARY_REQUEST) + response, trailing_metadata, code, details = rpc.termination() + + self.assertIs(code, grpc.StatusCode.DEADLINE_EXCEEDED) + + def test_infinite_request_stream_fake_time(self): + rpc = self._fake_time_server.invoke_stream_unary( + _application_testing_common.FIRST_SERVICE_STREUN, (), + _application_common.INFINITE_REQUEST_STREAM_TIMEOUT) + rpc.send_request(_application_common.STREAM_UNARY_REQUEST) + rpc.send_request(_application_common.STREAM_UNARY_REQUEST) + rpc.send_request(_application_common.STREAM_UNARY_REQUEST) + initial_metadata = rpc.initial_metadata() + self._fake_time.sleep_for( + _application_common.INFINITE_REQUEST_STREAM_TIMEOUT * 2) + rpc.send_request(_application_common.STREAM_UNARY_REQUEST) + response, trailing_metadata, code, details = rpc.termination() + + self.assertIs(code, grpc.StatusCode.DEADLINE_EXCEEDED) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_tests/tests/tests.json b/src/python/grpcio_tests/tests/tests.json index c10719b86f..d61297b918 100644 --- a/src/python/grpcio_tests/tests/tests.json +++ b/src/python/grpcio_tests/tests/tests.json @@ -10,6 +10,7 @@ "protoc_plugin.beta_python_plugin_test.PythonPluginTest", "reflection._reflection_servicer_test.ReflectionServicerTest", "testing._client_test.ClientTest", + "testing._server_test.FirstServiceServicerTest", "testing._time_test.StrictFakeTimeTest", "testing._time_test.StrictRealTimeTest", "unit._api_test.AllTest", diff --git a/src/ruby/.rubocop_todo.yml b/src/ruby/.rubocop_todo.yml index 05db404582..32b84b8de0 100644 --- a/src/ruby/.rubocop_todo.yml +++ b/src/ruby/.rubocop_todo.yml @@ -1,44 +1,569 @@ -# This configuration was generated by `rubocop --auto-gen-config` -# on 2015-05-22 13:23:34 -0700 using RuboCop version 0.30.1. +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2017-09-04 17:00:36 +0200 using RuboCop version 0.49.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 30 -Metrics/AbcSize: - Max: 38 +# Offense count: 3 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, IndentOneStep, IndentationWidth. +# SupportedStyles: case, end +Layout/CaseIndentation: + Exclude: + - 'tools/platform_check.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Layout/CommentIndentation: + Exclude: + - 'qps/client.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Layout/EmptyLineAfterMagicComment: + Exclude: + - 'tools/grpc-tools.gemspec' + +# Offense count: 33 +# Cop supports --auto-correct. +# Configuration parameters: AllowAdjacentOneLineDefs, NumberOfEmptyLines. +Layout/EmptyLineBetweenDefs: + Exclude: + - 'qps/client.rb' + - 'qps/histogram.rb' + - 'qps/proxy-worker.rb' + - 'qps/server.rb' + - 'qps/worker.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Layout/EmptyLines: + Exclude: + - 'qps/qps-common.rb' + +# Offense count: 8 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines +Layout/EmptyLinesAroundClassBody: + Exclude: + - 'pb/grpc/testing/duplicate/echo_duplicate_services_pb.rb' + - 'pb/grpc/testing/metrics_services_pb.rb' + - 'pb/src/proto/grpc/testing/test_services_pb.rb' + - 'qps/src/proto/grpc/testing/proxy-service_services_pb.rb' + - 'qps/src/proto/grpc/testing/services_services_pb.rb' + +# Offense count: 28 +# Cop supports --auto-correct. +# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment. +Layout/ExtraSpacing: + Enabled: false + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: normal, rails +Layout/IndentationConsistency: + Exclude: + - 'pb/grpc/health/checker.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: Width, IgnoredPatterns. +Layout/IndentationWidth: + Exclude: + - 'pb/grpc/health/checker.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: symmetrical, new_line, same_line +Layout/MultilineHashBraceLayout: + Exclude: + - 'spec/generic/active_call_spec.rb' + +# Offense count: 70 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: symmetrical, new_line, same_line +Layout/MultilineMethodCallBraceLayout: + Enabled: false + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. +# SupportedStyles: aligned, indented, indented_relative_to_receiver +Layout/MultilineMethodCallIndentation: + Exclude: + - 'spec/generic/rpc_desc_spec.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: symmetrical, new_line, same_line +Layout/MultilineMethodDefinitionBraceLayout: + Exclude: + - 'spec/generic/client_stub_spec.rb' + +# Offense count: 5 +# Cop supports --auto-correct. +Layout/SpaceAfterColon: + Exclude: + - 'lib/grpc/generic/rpc_server.rb' + +# Offense count: 7 +# Cop supports --auto-correct. +Layout/SpaceAfterComma: + Exclude: + - 'qps/client.rb' + +# Offense count: 27 +# Cop supports --auto-correct. +# Configuration parameters: AllowForAlignment. +Layout/SpaceAroundOperators: + Exclude: + - 'qps/client.rb' + - 'qps/histogram.rb' + - 'qps/proxy-worker.rb' + - 'qps/server.rb' + - 'spec/generic/active_call_spec.rb' + - 'spec/generic/rpc_server_spec.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters. +# SupportedStyles: space, no_space +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceInsideBlockBraces: + Exclude: + - 'stress/stress_client.rb' + +# Offense count: 4 +# Cop supports --auto-correct. +Layout/SpaceInsideBrackets: + Exclude: + - 'tools/bin/grpc_tools_ruby_protoc' + - 'tools/bin/grpc_tools_ruby_protoc_plugin' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces. +# SupportedStyles: space, no_space, compact +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceInsideHashLiteralBraces: + Exclude: + - 'qps/server.rb' + +# Offense count: 6 +# Cop supports --auto-correct. +Layout/SpaceInsidePercentLiteralDelimiters: + Exclude: + - 'spec/generic/client_stub_spec.rb' + - 'tools/grpc-tools.gemspec' # Offense count: 3 -# Configuration parameters: CountComments. -Metrics/ClassLength: - Max: 200 +# Cop supports --auto-correct. +Layout/Tab: + Exclude: + - 'pb/grpc/health/checker.rb' + - 'qps/client.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Layout/TrailingWhitespace: + Exclude: + - 'qps/worker.rb' + +# Offense count: 1 +Lint/IneffectiveAccessModifier: + Exclude: + - 'lib/grpc/generic/active_call.rb' + +# Offense count: 4 +# Cop supports --auto-correct. +Lint/PercentStringArray: + Exclude: + - 'spec/client_server_spec.rb' + - 'spec/generic/active_call_spec.rb' + - 'spec/generic/client_stub_spec.rb' + +# Offense count: 4 +Lint/ScriptPermission: + Exclude: + - 'qps/client.rb' + - 'qps/histogram.rb' + - 'qps/qps-common.rb' + - 'qps/server.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. +Lint/UnusedBlockArgument: + Exclude: + - 'qps/client.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods. +Lint/UnusedMethodArgument: + Exclude: + - 'qps/client.rb' + +# Offense count: 1 +# Configuration parameters: ContextCreatingMethods, MethodCreatingMethods. +Lint/UselessAccessModifier: + Exclude: + - 'lib/grpc/logconfig.rb' + +# Offense count: 1 +Lint/UselessAssignment: + Exclude: + - 'qps/client.rb' -# Offense count: 35 +# Offense count: 4 +Lint/Void: + Exclude: + - 'stress/metrics_server.rb' + - 'stress/stress_client.rb' + +# Offense count: 53 +Metrics/AbcSize: + Max: 57 + +# Offense count: 81 +# Configuration parameters: CountComments, ExcludedMethods. +Metrics/BlockLength: + Max: 715 + +# Offense count: 82 +# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. +# URISchemes: http, https +Metrics/LineLength: + Max: 141 + +# Offense count: 82 # Configuration parameters: CountComments. Metrics/MethodLength: - Max: 36 + Max: 54 -# Offense count: 7 +# Offense count: 5 # Configuration parameters: CountKeywordArgs. Metrics/ParameterLists: - Max: 8 + Max: 7 + +# Offense count: 1 +# Cop supports --auto-correct. +Performance/RedundantBlockCall: + Exclude: + - 'spec/generic/client_stub_spec.rb' + +# Offense count: 5 +# Cop supports --auto-correct. +# Configuration parameters: MaxKeyValuePairs. +Performance/RedundantMerge: + Exclude: + - 'spec/generic/active_call_spec.rb' + - 'spec/generic/client_stub_spec.rb' + +# Offense count: 8 +# Cop supports --auto-correct. +Performance/TimesMap: + Exclude: + - 'spec/channel_spec.rb' + - 'spec/client_server_spec.rb' + - 'spec/server_spec.rb' + +# Offense count: 7 +Style/AccessorMethodName: + Exclude: + - 'qps/server.rb' + - 'stress/metrics_server.rb' + - 'stress/stress_client.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: prefer_alias, prefer_alias_method +Style/Alias: + Exclude: + - 'lib/grpc/generic/rpc_server.rb' + - 'lib/grpc/notifier.rb' + +# Offense count: 7 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, ProceduralMethods, FunctionalMethods, IgnoredMethods. +# SupportedStyles: line_count_based, semantic, braces_for_chaining +# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object +# FunctionalMethods: let, let!, subject, watch +# IgnoredMethods: lambda, proc, it +Style/BlockDelimiters: + Exclude: + - 'qps/client.rb' + - 'qps/proxy-worker.rb' + - 'qps/server.rb' + - 'qps/worker.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +Style/ClassMethods: + Exclude: + - 'tools/platform_check.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, SingleLineConditionsOnly, IncludeTernaryExpressions. +# SupportedStyles: assign_to_condition, assign_inside_condition +Style/ConditionalAssignment: + Exclude: + - 'lib/grpc/generic/rpc_server.rb' + - 'lib/grpc/generic/service.rb' + +# Offense count: 19 +Style/Documentation: + Exclude: + - 'spec/**/*' + - 'test/**/*' + - 'pb/grpc/testing/duplicate/echo_duplicate_services_pb.rb' + - 'pb/grpc/testing/metrics_services_pb.rb' + - 'pb/src/proto/grpc/testing/test_pb.rb' + - 'qps/client.rb' + - 'qps/histogram.rb' + - 'qps/proxy-worker.rb' + - 'qps/server.rb' + - 'qps/src/proto/grpc/testing/proxy-service_services_pb.rb' + - 'qps/src/proto/grpc/testing/services_pb.rb' + - 'qps/src/proto/grpc/testing/services_services_pb.rb' + - 'qps/worker.rb' + - 'stress/metrics_server.rb' + - 'stress/stress_client.rb' + - 'tools/platform_check.rb' -# Offense count: 9 +# Offense count: 8 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: compact, expanded +Style/EmptyMethod: + Exclude: + - 'bin/noproto_server.rb' + - 'lib/grpc/logconfig.rb' + - 'spec/generic/rpc_desc_spec.rb' + +# Offense count: 2 +# Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts, AllowedAcronyms. +# AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS +Style/FileName: + Exclude: + - 'qps/src/proto/grpc/testing/proxy-service_pb.rb' + - 'qps/src/proto/grpc/testing/proxy-service_services_pb.rb' + +# Offense count: 12 # Configuration parameters: AllowedVariables. Style/GlobalVars: - Enabled: false + Exclude: + - 'ext/grpc/extconf.rb' + +# Offense count: 3 +# Configuration parameters: MinBodyLength. +Style/GuardClause: + Exclude: + - 'lib/grpc/generic/bidi_call.rb' + - 'lib/grpc/generic/rpc_server.rb' + - 'qps/client.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols. +# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys +Style/HashSyntax: + Exclude: + - 'stress/metrics_server.rb' + +# Offense count: 1 +Style/IfInsideElse: + Exclude: + - 'lib/grpc/generic/rpc_desc.rb' + +# Offense count: 4 +# Cop supports --auto-correct. +# Configuration parameters: MaxLineLength. +Style/IfUnlessModifier: + Exclude: + - 'ext/grpc/extconf.rb' + - 'qps/histogram.rb' + - 'stress/stress_client.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Style/MethodCallWithoutArgsParentheses: + Exclude: + - 'qps/client.rb' + +# Offense count: 3 +# Cop supports --auto-correct. +Style/MultilineIfModifier: + Exclude: + - 'lib/grpc/generic/bidi_call.rb' + - 'lib/grpc/generic/client_stub.rb' + - 'spec/spec_helper.rb' + +# Offense count: 7 +# Cop supports --auto-correct. +Style/MutableConstant: + Exclude: + - 'ext/grpc/extconf.rb' + - 'lib/grpc/version.rb' + - 'spec/compression_options_spec.rb' + - 'spec/generic/active_call_spec.rb' + - 'tools/version.rb' # Offense count: 1 -# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles. -Style/Next: +# Cop supports --auto-correct. +Style/NegatedWhile: + Exclude: + - 'qps/client.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles. +# SupportedStyles: predicate, comparison +Style/NumericPredicate: + Exclude: + - 'spec/**/*' + - 'ext/grpc/extconf.rb' + +# Offense count: 7 +# Cop supports --auto-correct. +Style/ParallelAssignment: + Exclude: + - 'bin/math_server.rb' + - 'lib/grpc/generic/rpc_server.rb' + - 'spec/generic/client_stub_spec.rb' + - 'spec/generic/rpc_desc_spec.rb' + - 'spec/generic/rpc_server_pool_spec.rb' + - 'spec/generic/rpc_server_spec.rb' + +# Offense count: 8 +# Cop supports --auto-correct. +# Configuration parameters: PreferredDelimiters. +Style/PercentLiteralDelimiters: + Exclude: + - 'end2end/grpc_class_init_driver.rb' + - 'spec/client_server_spec.rb' + - 'spec/generic/active_call_spec.rb' + - 'spec/generic/client_stub_spec.rb' + - 'tools/grpc-tools.gemspec' + +# Offense count: 3 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: compact, exploded +Style/RaiseArgs: + Exclude: + - 'stress/metrics_server.rb' + +# Offense count: 4 +# Cop supports --auto-correct. +Style/RedundantParentheses: + Exclude: + - 'lib/grpc/generic/rpc_server.rb' + - 'qps/client.rb' + - 'qps/proxy-worker.rb' + - 'spec/generic/rpc_desc_spec.rb' + +# Offense count: 5 +# Cop supports --auto-correct. +# Configuration parameters: AllowMultipleReturnValues. +Style/RedundantReturn: + Exclude: + - 'end2end/grpc_class_init_client.rb' + +# Offense count: 77 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: only_raise, only_fail, semantic +Style/SignalException: Enabled: false # Offense count: 2 -# Configuration parameters: Methods. -Style/SingleLineBlockParams: - Enabled: false +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: use_perl_names, use_english_names +Style/SpecialGlobalVars: + Exclude: + - 'ext/grpc/extconf.rb' + - 'stress/stress_client.rb' + +# Offense count: 189 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, ConsistentQuotesInMultiline. +# SupportedStyles: single_quotes, double_quotes +Style/StringLiterals: + Exclude: + - 'pb/grpc/testing/metrics_pb.rb' + - 'pb/src/proto/grpc/testing/empty_pb.rb' + - 'pb/src/proto/grpc/testing/messages_pb.rb' + - 'qps/proxy-worker.rb' + - 'qps/server.rb' + - 'qps/src/proto/grpc/testing/control_pb.rb' + - 'qps/src/proto/grpc/testing/messages_pb.rb' + - 'qps/src/proto/grpc/testing/payloads_pb.rb' + - 'qps/src/proto/grpc/testing/proxy-service_pb.rb' + - 'qps/src/proto/grpc/testing/stats_pb.rb' + - 'qps/worker.rb' # Offense count: 1 Style/StructInheritance: - Enabled: false + Exclude: + - 'lib/grpc/generic/rpc_desc.rb' + +# Offense count: 10 +# Cop supports --auto-correct. +# Configuration parameters: MinSize, SupportedStyles. +# SupportedStyles: percent, brackets +Style/SymbolArray: + EnforcedStyle: brackets + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: IgnoredMethods. +# IgnoredMethods: respond_to, define_method +Style/SymbolProc: + Exclude: + - 'qps/client.rb' + - 'stress/stress_client.rb' + +# Offense count: 6 +# Cop supports --auto-correct. +# Configuration parameters: AllowNamedUnderscoreVariables. +Style/TrailingUnderscoreVariable: + Exclude: + - 'spec/channel_credentials_spec.rb' + - 'spec/server_credentials_spec.rb' + +# Offense count: 3 +# Cop supports --auto-correct. +# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, Whitelist. +# Whitelist: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym +Style/TrivialAccessors: + Exclude: + - 'qps/histogram.rb' + +# Offense count: 3 +# Cop supports --auto-correct. +Style/UnneededInterpolation: + Exclude: + - 'pb/grpc/health/checker.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Style/YodaCondition: + Exclude: + - 'stress/stress_client.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +Style/ZeroLengthPredicate: + Exclude: + - 'lib/grpc/generic/rpc_server.rb' diff --git a/src/ruby/end2end/grpc_class_init_client.rb b/src/ruby/end2end/grpc_class_init_client.rb index b9e3d6054f..c35719a71f 100755 --- a/src/ruby/end2end/grpc_class_init_client.rb +++ b/src/ruby/end2end/grpc_class_init_client.rb @@ -41,7 +41,7 @@ def run_gc_stress_test(test_proc) GC.enable construct_many(test_proc) - GC.start(full_mark: true, immediate_sweep: true) + GC.start construct_many(test_proc) end diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.c b/src/ruby/ext/grpc/rb_grpc_imports.generated.c index 9671d794c5..0402ce34fb 100644 --- a/src/ruby/ext/grpc/rb_grpc_imports.generated.c +++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.c @@ -93,6 +93,7 @@ grpc_alarm_destroy_type grpc_alarm_destroy_import; grpc_channel_check_connectivity_state_type grpc_channel_check_connectivity_state_import; grpc_channel_num_external_connectivity_watchers_type grpc_channel_num_external_connectivity_watchers_import; grpc_channel_watch_connectivity_state_type grpc_channel_watch_connectivity_state_import; +grpc_channel_support_connectivity_watcher_type grpc_channel_support_connectivity_watcher_import; grpc_channel_create_call_type grpc_channel_create_call_import; grpc_channel_ping_type grpc_channel_ping_import; grpc_channel_register_call_type grpc_channel_register_call_import; @@ -399,6 +400,7 @@ void grpc_rb_load_imports(HMODULE library) { grpc_channel_check_connectivity_state_import = (grpc_channel_check_connectivity_state_type) GetProcAddress(library, "grpc_channel_check_connectivity_state"); grpc_channel_num_external_connectivity_watchers_import = (grpc_channel_num_external_connectivity_watchers_type) GetProcAddress(library, "grpc_channel_num_external_connectivity_watchers"); grpc_channel_watch_connectivity_state_import = (grpc_channel_watch_connectivity_state_type) GetProcAddress(library, "grpc_channel_watch_connectivity_state"); + grpc_channel_support_connectivity_watcher_import = (grpc_channel_support_connectivity_watcher_type) GetProcAddress(library, "grpc_channel_support_connectivity_watcher"); grpc_channel_create_call_import = (grpc_channel_create_call_type) GetProcAddress(library, "grpc_channel_create_call"); grpc_channel_ping_import = (grpc_channel_ping_type) GetProcAddress(library, "grpc_channel_ping"); grpc_channel_register_call_import = (grpc_channel_register_call_type) GetProcAddress(library, "grpc_channel_register_call"); diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h index b64199be8e..e3704e592b 100644 --- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h +++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h @@ -260,6 +260,9 @@ extern grpc_channel_num_external_connectivity_watchers_type grpc_channel_num_ext typedef void(*grpc_channel_watch_connectivity_state_type)(grpc_channel *channel, grpc_connectivity_state last_observed_state, gpr_timespec deadline, grpc_completion_queue *cq, void *tag); extern grpc_channel_watch_connectivity_state_type grpc_channel_watch_connectivity_state_import; #define grpc_channel_watch_connectivity_state grpc_channel_watch_connectivity_state_import +typedef int(*grpc_channel_support_connectivity_watcher_type)(grpc_channel *channel); +extern grpc_channel_support_connectivity_watcher_type grpc_channel_support_connectivity_watcher_import; +#define grpc_channel_support_connectivity_watcher grpc_channel_support_connectivity_watcher_import typedef grpc_call *(*grpc_channel_create_call_type)(grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask, grpc_completion_queue *completion_queue, grpc_slice method, const grpc_slice *host, gpr_timespec deadline, void *reserved); extern grpc_channel_create_call_type grpc_channel_create_call_import; #define grpc_channel_create_call grpc_channel_create_call_import diff --git a/templates/gRPC-Core.podspec.template b/templates/gRPC-Core.podspec.template index 616f2cbbff..6077f8098d 100644 --- a/templates/gRPC-Core.podspec.template +++ b/templates/gRPC-Core.podspec.template @@ -171,6 +171,7 @@ 'test/core/end2end/end2end_tests.{c,h}', 'test/core/end2end/end2end_test_utils.c', 'test/core/end2end/tests/*.{c,h}', + 'test/core/end2end/fixtures/*.h', 'test/core/end2end/data/*.{c,h}', 'test/core/util/debugger_macros.{c,h}', 'test/core/util/test_config.{c,h}', diff --git a/templates/grpc.gemspec.template b/templates/grpc.gemspec.template index 8b5799306a..e62e5b2721 100644 --- a/templates/grpc.gemspec.template +++ b/templates/grpc.gemspec.template @@ -35,12 +35,12 @@ s.add_development_dependency 'bundler', '~> 1.9' s.add_development_dependency 'facter', '~> 2.4' s.add_development_dependency 'logging', '~> 2.0' - s.add_development_dependency 'simplecov', '~> 0.9' - s.add_development_dependency 'rake', '~> 10.4' + s.add_development_dependency 'simplecov', '~> 0.14.1' + s.add_development_dependency 'rake', '~> 12.0' s.add_development_dependency 'rake-compiler', '~> 1.0' s.add_development_dependency 'rake-compiler-dock', '~> 0.5.1' - s.add_development_dependency 'rspec', '~> 3.2' - s.add_development_dependency 'rubocop', '~> 0.30.0' + s.add_development_dependency 'rspec', '~> 3.6' + s.add_development_dependency 'rubocop', '~> 0.49.1' s.add_development_dependency 'signet', '~> 0.7.0' s.extensions = %w(src/ruby/ext/grpc/extconf.rb) diff --git a/templates/src/objective-c/!ProtoCompiler-gRPCPlugin.podspec.template b/templates/src/objective-c/!ProtoCompiler-gRPCPlugin.podspec.template index b8223417b7..196c405468 100644 --- a/templates/src/objective-c/!ProtoCompiler-gRPCPlugin.podspec.template +++ b/templates/src/objective-c/!ProtoCompiler-gRPCPlugin.podspec.template @@ -103,7 +103,7 @@ s.preserve_paths = plugin # Restrict the protoc version to the one supported by this plugin. - s.dependency '!ProtoCompiler', '3.3.0' + s.dependency '!ProtoCompiler', '3.4.0' # For the Protobuf dependency not to complain: s.ios.deployment_target = '7.0' s.osx.deployment_target = '10.9' diff --git a/test/core/support/string_test.c b/test/core/support/string_test.c index a3c33c3fa4..bee2139477 100644 --- a/test/core/support/string_test.c +++ b/test/core/support/string_test.c @@ -279,6 +279,21 @@ 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(NULL)); + GPR_ASSERT(false == gpr_is_true("")); + GPR_ASSERT(false == gpr_is_true("0")); +} + int main(int argc, char **argv) { grpc_test_init(argc, argv); test_strdup(); @@ -292,5 +307,6 @@ int main(int argc, char **argv) { test_leftpad(); test_stricmp(); test_memrchr(); + test_is_true(); return 0; } diff --git a/test/core/tsi/BUILD b/test/core/tsi/BUILD index 82e0e5fb80..0c5509dda6 100644 --- a/test/core/tsi/BUILD +++ b/test/core/tsi/BUILD @@ -18,13 +18,63 @@ licenses(["notice"]) # Apache v2 grpc_package(name = "test/core/tsi") +grpc_cc_library( + name = "transport_security_test_lib", + srcs = ["transport_security_test_lib.c"], + hdrs = ["transport_security_test_lib.h"], + deps = [ + "//:grpc", + "//:tsi", + ], +) + +grpc_cc_test( + name = "fake_transport_security_test", + srcs = ["fake_transport_security_test.c"], + language = "C", + deps = [ + ":transport_security_test_lib", + "//:grpc", + "//:gpr", + "//:tsi", + "//test/core/util:gpr_test_util", + ], +) + + +grpc_cc_test( + name = "ssl_transport_security_test", + srcs = ["ssl_transport_security_test.c"], + data = [ + "//src/core/tsi/test_creds:badclient.key", + "//src/core/tsi/test_creds:badclient.pem", + "//src/core/tsi/test_creds:badserver.key", + "//src/core/tsi/test_creds:badserver.pem", + "//src/core/tsi/test_creds:ca.pem", + "//src/core/tsi/test_creds:client.key", + "//src/core/tsi/test_creds:client.pem", + "//src/core/tsi/test_creds:server0.key", + "//src/core/tsi/test_creds:server0.pem", + "//src/core/tsi/test_creds:server1.key", + "//src/core/tsi/test_creds:server1.pem", + ], + language = "C", + deps = [ + ":transport_security_test_lib", + "//:grpc", + "//:gpr", + "//:tsi", + "//test/core/util:gpr_test_util", + ], +) + grpc_cc_test( name = "transport_security_test", srcs = ["transport_security_test.c"], language = "C", deps = [ - "//:gpr", "//:grpc", + "//:gpr", "//test/core/util:gpr_test_util", "//test/core/util:grpc_test_util", ], diff --git a/test/core/tsi/fake_transport_security_test.c b/test/core/tsi/fake_transport_security_test.c new file mode 100644 index 0000000000..11be8802b7 --- /dev/null +++ b/test/core/tsi/fake_transport_security_test.c @@ -0,0 +1,148 @@ +/* + * + * 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 <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include "src/core/lib/security/transport/security_connector.h" +#include "src/core/tsi/fake_transport_security.h" +#include "test/core/tsi/transport_security_test_lib.h" +#include "test/core/util/test_config.h" + +#include <grpc/grpc.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +typedef struct fake_tsi_test_fixture { + tsi_test_fixture base; +} fake_tsi_test_fixture; + +static void fake_test_setup_handshakers(tsi_test_fixture *fixture) { + fixture->client_handshaker = + tsi_create_fake_handshaker(true /* is_client. */); + fixture->server_handshaker = + tsi_create_fake_handshaker(false /* is_client. */); +} + +static void validate_handshaker_peers(tsi_handshaker_result *result) { + GPR_ASSERT(result != NULL); + tsi_peer peer; + GPR_ASSERT(tsi_handshaker_result_extract_peer(result, &peer) == TSI_OK); + const tsi_peer_property *property = + tsi_peer_get_property_by_name(&peer, TSI_CERTIFICATE_TYPE_PEER_PROPERTY); + GPR_ASSERT(property != NULL); + GPR_ASSERT(memcmp(property->value.data, TSI_FAKE_CERTIFICATE_TYPE, + property->value.length) == 0); + tsi_peer_destruct(&peer); +} + +static void fake_test_check_handshaker_peers(tsi_test_fixture *fixture) { + validate_handshaker_peers(fixture->client_result); + validate_handshaker_peers(fixture->server_result); +} + +static void fake_test_destruct(tsi_test_fixture *fixture) {} + +static const struct tsi_test_fixture_vtable vtable = { + fake_test_setup_handshakers, fake_test_check_handshaker_peers, + fake_test_destruct}; + +static tsi_test_fixture *fake_tsi_test_fixture_create() { + fake_tsi_test_fixture *fake_fixture = gpr_zalloc(sizeof(*fake_fixture)); + tsi_test_fixture_init(&fake_fixture->base); + fake_fixture->base.vtable = &vtable; + return &fake_fixture->base; +} + +void fake_tsi_test_do_handshake_tiny_handshake_buffer() { + tsi_test_fixture *fixture = fake_tsi_test_fixture_create(); + fixture->handshake_buffer_size = TSI_TEST_TINY_HANDSHAKE_BUFFER_SIZE; + tsi_test_do_handshake(fixture); + tsi_test_fixture_destroy(fixture); +} + +void fake_tsi_test_do_handshake_small_handshake_buffer() { + tsi_test_fixture *fixture = fake_tsi_test_fixture_create(); + fixture->handshake_buffer_size = TSI_TEST_SMALL_HANDSHAKE_BUFFER_SIZE; + tsi_test_do_handshake(fixture); + tsi_test_fixture_destroy(fixture); +} + +void fake_tsi_test_do_handshake() { + tsi_test_fixture *fixture = fake_tsi_test_fixture_create(); + tsi_test_do_handshake(fixture); + tsi_test_fixture_destroy(fixture); +} + +void fake_tsi_test_do_round_trip_for_all_configs() { + unsigned int *bit_array = + gpr_zalloc(sizeof(unsigned int) * TSI_TEST_NUM_OF_ARGUMENTS); + const unsigned int mask = 1U << (TSI_TEST_NUM_OF_ARGUMENTS - 1); + for (unsigned int val = 0; val < TSI_TEST_NUM_OF_COMBINATIONS; val++) { + unsigned int v = val; + for (unsigned int ind = 0; ind < TSI_TEST_NUM_OF_ARGUMENTS; ind++) { + bit_array[ind] = (v & mask) ? 1 : 0; + v <<= 1; + } + tsi_test_fixture *fixture = fake_tsi_test_fixture_create(); + fake_tsi_test_fixture *fake_fixture = (fake_tsi_test_fixture *)fixture; + tsi_test_frame_protector_config_destroy(fake_fixture->base.config); + fake_fixture->base.config = tsi_test_frame_protector_config_create( + bit_array[0], bit_array[1], bit_array[2], bit_array[3], bit_array[4], + bit_array[5], bit_array[6], bit_array[7]); + tsi_test_do_round_trip(&fake_fixture->base); + tsi_test_fixture_destroy(fixture); + } + gpr_free(bit_array); +} + +void fake_tsi_test_do_round_trip_odd_buffer_size() { + const size_t odd_sizes[] = {1025, 2051, 4103, 8207, 16409}; + const size_t size = sizeof(odd_sizes) / sizeof(size_t); + for (size_t ind1 = 0; ind1 < size; ind1++) { + for (size_t ind2 = 0; ind2 < size; ind2++) { + for (size_t ind3 = 0; ind3 < size; ind3++) { + for (size_t ind4 = 0; ind4 < size; ind4++) { + for (size_t ind5 = 0; ind5 < size; ind5++) { + tsi_test_fixture *fixture = fake_tsi_test_fixture_create(); + fake_tsi_test_fixture *fake_fixture = + (fake_tsi_test_fixture *)fixture; + tsi_test_frame_protector_config_set_buffer_size( + fake_fixture->base.config, odd_sizes[ind1], odd_sizes[ind2], + odd_sizes[ind3], odd_sizes[ind4], odd_sizes[ind5]); + tsi_test_do_round_trip(&fake_fixture->base); + tsi_test_fixture_destroy(fixture); + } + } + } + } + } +} + +int main(int argc, char **argv) { + grpc_test_init(argc, argv); + grpc_init(); + fake_tsi_test_do_handshake_tiny_handshake_buffer(); + fake_tsi_test_do_handshake_small_handshake_buffer(); + fake_tsi_test_do_handshake(); + fake_tsi_test_do_round_trip_for_all_configs(); + fake_tsi_test_do_round_trip_odd_buffer_size(); + grpc_shutdown(); + return 0; +} diff --git a/test/core/tsi/ssl_transport_security_test.c b/test/core/tsi/ssl_transport_security_test.c new file mode 100644 index 0000000000..364dfa1b73 --- /dev/null +++ b/test/core/tsi/ssl_transport_security_test.c @@ -0,0 +1,558 @@ +/* + * + * 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 <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include "src/core/lib/iomgr/load_file.h" +#include "src/core/lib/security/transport/security_connector.h" +#include "src/core/tsi/ssl_transport_security.h" +#include "src/core/tsi/transport_security_adapter.h" +#include "test/core/tsi/transport_security_test_lib.h" +#include "test/core/util/test_config.h" + +#include <grpc/grpc.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> + +#define SSL_TSI_TEST_ALPN1 "foo" +#define SSL_TSI_TEST_ALPN2 "toto" +#define SSL_TSI_TEST_ALPN3 "baz" +#define SSL_TSI_TEST_ALPN_NUM 2 +#define SSL_TSI_TEST_SERVER_KEY_CERT_PAIRS_NUM 2 +#define SSL_TSI_TEST_BAD_SERVER_KEY_CERT_PAIRS_NUM 1 +#define SSL_TSI_TEST_CREDENTIALS_DIR "src/core/tsi/test_creds/" + +typedef enum AlpnMode { + NO_ALPN, + ALPN_CLIENT_NO_SERVER, + ALPN_SERVER_NO_CLIENT, + ALPN_CLIENT_SERVER_OK, + ALPN_CLIENT_SERVER_MISMATCH +} AlpnMode; + +typedef struct ssl_alpn_lib { + AlpnMode alpn_mode; + char **server_alpn_protocols; + char **client_alpn_protocols; + uint16_t num_server_alpn_protocols; + uint16_t num_client_alpn_protocols; +} ssl_alpn_lib; + +typedef struct ssl_key_cert_lib { + bool use_bad_server_cert; + bool use_bad_client_cert; + char *root_cert; + tsi_ssl_pem_key_cert_pair *server_pem_key_cert_pairs; + tsi_ssl_pem_key_cert_pair *bad_server_pem_key_cert_pairs; + tsi_ssl_pem_key_cert_pair client_pem_key_cert_pair; + tsi_ssl_pem_key_cert_pair bad_client_pem_key_cert_pair; + uint16_t server_num_key_cert_pairs; + uint16_t bad_server_num_key_cert_pairs; +} ssl_key_cert_lib; + +typedef struct ssl_tsi_test_fixture { + tsi_test_fixture base; + ssl_key_cert_lib *key_cert_lib; + ssl_alpn_lib *alpn_lib; + bool force_client_auth; + char *server_name_indication; + tsi_ssl_server_handshaker_factory *server_handshaker_factory; + tsi_ssl_client_handshaker_factory *client_handshaker_factory; +} ssl_tsi_test_fixture; + +static void ssl_test_setup_handshakers(tsi_test_fixture *fixture) { + ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture; + GPR_ASSERT(ssl_fixture != NULL); + GPR_ASSERT(ssl_fixture->key_cert_lib != NULL); + GPR_ASSERT(ssl_fixture->alpn_lib != NULL); + ssl_key_cert_lib *key_cert_lib = ssl_fixture->key_cert_lib; + ssl_alpn_lib *alpn_lib = ssl_fixture->alpn_lib; + /* Create client handshaker factory. */ + tsi_ssl_pem_key_cert_pair *client_key_cert_pair = NULL; + if (ssl_fixture->force_client_auth) { + client_key_cert_pair = key_cert_lib->use_bad_client_cert + ? &key_cert_lib->bad_client_pem_key_cert_pair + : &key_cert_lib->client_pem_key_cert_pair; + } + char **client_alpn_protocols = NULL; + uint16_t num_client_alpn_protocols = 0; + if (alpn_lib->alpn_mode == ALPN_CLIENT_NO_SERVER || + alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_OK || + alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_MISMATCH) { + client_alpn_protocols = alpn_lib->client_alpn_protocols; + num_client_alpn_protocols = alpn_lib->num_client_alpn_protocols; + } + GPR_ASSERT(tsi_create_ssl_client_handshaker_factory( + client_key_cert_pair, key_cert_lib->root_cert, NULL, + (const char **)client_alpn_protocols, + num_client_alpn_protocols, + &ssl_fixture->client_handshaker_factory) == TSI_OK); + /* Create server handshaker factory. */ + char **server_alpn_protocols = NULL; + uint16_t num_server_alpn_protocols = 0; + if (alpn_lib->alpn_mode == ALPN_SERVER_NO_CLIENT || + alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_OK || + alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_MISMATCH) { + server_alpn_protocols = alpn_lib->server_alpn_protocols; + num_server_alpn_protocols = alpn_lib->num_server_alpn_protocols; + if (alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_MISMATCH) { + num_server_alpn_protocols--; + } + } + GPR_ASSERT(tsi_create_ssl_server_handshaker_factory( + key_cert_lib->use_bad_server_cert + ? key_cert_lib->bad_server_pem_key_cert_pairs + : key_cert_lib->server_pem_key_cert_pairs, + key_cert_lib->use_bad_server_cert + ? key_cert_lib->bad_server_num_key_cert_pairs + : key_cert_lib->server_num_key_cert_pairs, + key_cert_lib->root_cert, ssl_fixture->force_client_auth, NULL, + (const char **)server_alpn_protocols, + num_server_alpn_protocols, + &ssl_fixture->server_handshaker_factory) == TSI_OK); + /* Create server and client handshakers. */ + tsi_handshaker *client_handshaker = NULL; + GPR_ASSERT(tsi_ssl_client_handshaker_factory_create_handshaker( + ssl_fixture->client_handshaker_factory, + ssl_fixture->server_name_indication, + &client_handshaker) == TSI_OK); + ssl_fixture->base.client_handshaker = + tsi_create_adapter_handshaker(client_handshaker); + tsi_handshaker *server_handshaker = NULL; + GPR_ASSERT(tsi_ssl_server_handshaker_factory_create_handshaker( + ssl_fixture->server_handshaker_factory, &server_handshaker) == + TSI_OK); + ssl_fixture->base.server_handshaker = + tsi_create_adapter_handshaker(server_handshaker); +} + +static void check_alpn(ssl_tsi_test_fixture *ssl_fixture, + const tsi_peer *peer) { + GPR_ASSERT(ssl_fixture != NULL); + GPR_ASSERT(ssl_fixture->alpn_lib != NULL); + ssl_alpn_lib *alpn_lib = ssl_fixture->alpn_lib; + const tsi_peer_property *alpn_property = + tsi_peer_get_property_by_name(peer, TSI_SSL_ALPN_SELECTED_PROTOCOL); + if (alpn_lib->alpn_mode != ALPN_CLIENT_SERVER_OK) { + GPR_ASSERT(alpn_property == NULL); + } else { + GPR_ASSERT(alpn_property != NULL); + const char *expected_match = "baz"; + GPR_ASSERT(memcmp(alpn_property->value.data, expected_match, + alpn_property->value.length) == 0); + } +} + +static const tsi_peer_property * +check_basic_authenticated_peer_and_get_common_name(const tsi_peer *peer) { + const tsi_peer_property *cert_type_property = + tsi_peer_get_property_by_name(peer, TSI_CERTIFICATE_TYPE_PEER_PROPERTY); + GPR_ASSERT(cert_type_property != NULL); + GPR_ASSERT(memcmp(cert_type_property->value.data, TSI_X509_CERTIFICATE_TYPE, + cert_type_property->value.length) == 0); + const tsi_peer_property *property = tsi_peer_get_property_by_name( + peer, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY); + GPR_ASSERT(property != NULL); + return property; +} + +void check_server0_peer(tsi_peer *peer) { + const tsi_peer_property *property = + check_basic_authenticated_peer_and_get_common_name(peer); + const char *expected_match = "*.test.google.com.au"; + GPR_ASSERT(memcmp(property->value.data, expected_match, + property->value.length) == 0); + GPR_ASSERT(tsi_peer_get_property_by_name( + peer, TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) == + NULL); + GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "foo.test.google.com.au") == 1); + GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "bar.test.google.com.au") == 1); + GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "bar.test.google.blah") == 0); + GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "foo.bar.test.google.com.au") == + 0); + GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "test.google.com.au") == 0); + tsi_peer_destruct(peer); +} + +static bool check_subject_alt_name(tsi_peer *peer, const char *name) { + for (size_t i = 0; i < peer->property_count; i++) { + const tsi_peer_property *prop = &peer->properties[i]; + if (strcmp(prop->name, TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) == + 0) { + if (memcmp(prop->value.data, name, prop->value.length) == 0) { + return true; + } + } + } + return false; +} + +void check_server1_peer(tsi_peer *peer) { + const tsi_peer_property *property = + check_basic_authenticated_peer_and_get_common_name(peer); + const char *expected_match = "*.test.google.com"; + GPR_ASSERT(memcmp(property->value.data, expected_match, + property->value.length) == 0); + GPR_ASSERT(check_subject_alt_name(peer, "*.test.google.fr") == 1); + GPR_ASSERT(check_subject_alt_name(peer, "waterzooi.test.google.be") == 1); + GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "foo.test.google.fr") == 1); + GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "bar.test.google.fr") == 1); + GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "waterzooi.test.google.be") == 1); + GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "foo.test.youtube.com") == 1); + GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "bar.foo.test.google.com") == 0); + GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "test.google.fr") == 0); + GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "tartines.test.google.be") == 0); + GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "tartines.youtube.com") == 0); + tsi_peer_destruct(peer); +} + +static void check_client_peer(ssl_tsi_test_fixture *ssl_fixture, + tsi_peer *peer) { + GPR_ASSERT(ssl_fixture != NULL); + GPR_ASSERT(ssl_fixture->alpn_lib != NULL); + ssl_alpn_lib *alpn_lib = ssl_fixture->alpn_lib; + if (!ssl_fixture->force_client_auth) { + GPR_ASSERT(peer->property_count == + (alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_OK ? 1 : 0)); + } else { + const tsi_peer_property *property = + check_basic_authenticated_peer_and_get_common_name(peer); + const char *expected_match = "testclient"; + GPR_ASSERT(memcmp(property->value.data, expected_match, + property->value.length) == 0); + } + tsi_peer_destruct(peer); +} + +static void ssl_test_check_handshaker_peers(tsi_test_fixture *fixture) { + ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture; + GPR_ASSERT(ssl_fixture != NULL); + GPR_ASSERT(ssl_fixture->key_cert_lib != NULL); + ssl_key_cert_lib *key_cert_lib = ssl_fixture->key_cert_lib; + tsi_peer peer; + bool expect_success = + !(key_cert_lib->use_bad_server_cert || + (key_cert_lib->use_bad_client_cert && ssl_fixture->force_client_auth)); + if (expect_success) { + GPR_ASSERT(tsi_handshaker_result_extract_peer( + ssl_fixture->base.client_result, &peer) == TSI_OK); + check_alpn(ssl_fixture, &peer); + + if (ssl_fixture->server_name_indication != NULL) { + check_server1_peer(&peer); + } else { + check_server0_peer(&peer); + } + } else { + GPR_ASSERT(ssl_fixture->base.client_result == NULL); + } + if (expect_success) { + GPR_ASSERT(tsi_handshaker_result_extract_peer( + ssl_fixture->base.server_result, &peer) == TSI_OK); + check_alpn(ssl_fixture, &peer); + check_client_peer(ssl_fixture, &peer); + } else { + GPR_ASSERT(ssl_fixture->base.server_result == NULL); + } +} + +static void ssl_test_pem_key_cert_pair_destroy(tsi_ssl_pem_key_cert_pair kp) { + gpr_free((void *)kp.private_key); + gpr_free((void *)kp.cert_chain); +} + +static void ssl_test_destruct(tsi_test_fixture *fixture) { + ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture; + if (ssl_fixture == NULL) { + return; + } + /* Destroy ssl_alpn_lib. */ + ssl_alpn_lib *alpn_lib = ssl_fixture->alpn_lib; + for (size_t i = 0; i < alpn_lib->num_server_alpn_protocols; i++) { + gpr_free(alpn_lib->server_alpn_protocols[i]); + } + gpr_free(alpn_lib->server_alpn_protocols); + for (size_t i = 0; i < alpn_lib->num_client_alpn_protocols; i++) { + gpr_free(alpn_lib->client_alpn_protocols[i]); + } + gpr_free(alpn_lib->client_alpn_protocols); + gpr_free(alpn_lib); + /* Destroy ssl_key_cert_lib. */ + ssl_key_cert_lib *key_cert_lib = ssl_fixture->key_cert_lib; + for (size_t i = 0; i < key_cert_lib->server_num_key_cert_pairs; i++) { + ssl_test_pem_key_cert_pair_destroy( + key_cert_lib->server_pem_key_cert_pairs[i]); + } + gpr_free(key_cert_lib->server_pem_key_cert_pairs); + for (size_t i = 0; i < key_cert_lib->bad_server_num_key_cert_pairs; i++) { + ssl_test_pem_key_cert_pair_destroy( + key_cert_lib->bad_server_pem_key_cert_pairs[i]); + } + gpr_free(key_cert_lib->bad_server_pem_key_cert_pairs); + ssl_test_pem_key_cert_pair_destroy(key_cert_lib->client_pem_key_cert_pair); + ssl_test_pem_key_cert_pair_destroy( + key_cert_lib->bad_client_pem_key_cert_pair); + gpr_free(key_cert_lib->root_cert); + gpr_free(key_cert_lib); + /* Destroy others. */ + tsi_ssl_server_handshaker_factory_destroy( + ssl_fixture->server_handshaker_factory); + tsi_ssl_client_handshaker_factory_destroy( + ssl_fixture->client_handshaker_factory); +} + +static const struct tsi_test_fixture_vtable vtable = { + ssl_test_setup_handshakers, ssl_test_check_handshaker_peers, + ssl_test_destruct}; + +static char *load_file(const char *dir_path, const char *file_name) { + char *file_path = + gpr_zalloc(sizeof(char) * (strlen(dir_path) + strlen(file_name) + 1)); + memcpy(file_path, dir_path, strlen(dir_path)); + memcpy(file_path + strlen(dir_path), file_name, strlen(file_name)); + grpc_slice slice; + GPR_ASSERT(grpc_load_file(file_path, 1, &slice) == GRPC_ERROR_NONE); + char *data = grpc_slice_to_c_string(slice); + grpc_slice_unref(slice); + gpr_free(file_path); + return data; +} + +static tsi_test_fixture *ssl_tsi_test_fixture_create() { + ssl_tsi_test_fixture *ssl_fixture = gpr_zalloc(sizeof(*ssl_fixture)); + tsi_test_fixture_init(&ssl_fixture->base); + ssl_fixture->base.test_unused_bytes = false; + ssl_fixture->base.vtable = &vtable; + /* Create ssl_key_cert_lib. */ + ssl_key_cert_lib *key_cert_lib = gpr_zalloc(sizeof(*key_cert_lib)); + key_cert_lib->use_bad_server_cert = false; + key_cert_lib->use_bad_client_cert = false; + key_cert_lib->server_num_key_cert_pairs = + SSL_TSI_TEST_SERVER_KEY_CERT_PAIRS_NUM; + key_cert_lib->bad_server_num_key_cert_pairs = + SSL_TSI_TEST_BAD_SERVER_KEY_CERT_PAIRS_NUM; + key_cert_lib->server_pem_key_cert_pairs = + gpr_malloc(sizeof(tsi_ssl_pem_key_cert_pair) * + key_cert_lib->server_num_key_cert_pairs); + key_cert_lib->bad_server_pem_key_cert_pairs = + gpr_malloc(sizeof(tsi_ssl_pem_key_cert_pair) * + key_cert_lib->bad_server_num_key_cert_pairs); + key_cert_lib->server_pem_key_cert_pairs[0].private_key = + load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "server0.key"); + key_cert_lib->server_pem_key_cert_pairs[0].cert_chain = + load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "server0.pem"); + key_cert_lib->server_pem_key_cert_pairs[1].private_key = + load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "server1.key"); + key_cert_lib->server_pem_key_cert_pairs[1].cert_chain = + load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "server1.pem"); + key_cert_lib->bad_server_pem_key_cert_pairs[0].private_key = + load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "badserver.key"); + key_cert_lib->bad_server_pem_key_cert_pairs[0].cert_chain = + load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "badserver.pem"); + key_cert_lib->client_pem_key_cert_pair.private_key = + load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "client.key"); + key_cert_lib->client_pem_key_cert_pair.cert_chain = + load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "client.pem"); + key_cert_lib->bad_client_pem_key_cert_pair.private_key = + load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "badclient.key"); + key_cert_lib->bad_client_pem_key_cert_pair.cert_chain = + load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "badclient.pem"); + key_cert_lib->root_cert = load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "ca.pem"); + ssl_fixture->key_cert_lib = key_cert_lib; + /* Create ssl_alpn_lib. */ + ssl_alpn_lib *alpn_lib = gpr_zalloc(sizeof(*alpn_lib)); + alpn_lib->server_alpn_protocols = + gpr_zalloc(sizeof(char *) * SSL_TSI_TEST_ALPN_NUM); + alpn_lib->client_alpn_protocols = + gpr_zalloc(sizeof(char *) * SSL_TSI_TEST_ALPN_NUM); + alpn_lib->server_alpn_protocols[0] = gpr_strdup(SSL_TSI_TEST_ALPN1); + alpn_lib->server_alpn_protocols[1] = gpr_strdup(SSL_TSI_TEST_ALPN3); + alpn_lib->client_alpn_protocols[0] = gpr_strdup(SSL_TSI_TEST_ALPN2); + alpn_lib->client_alpn_protocols[1] = gpr_strdup(SSL_TSI_TEST_ALPN3); + alpn_lib->num_server_alpn_protocols = SSL_TSI_TEST_ALPN_NUM; + alpn_lib->num_client_alpn_protocols = SSL_TSI_TEST_ALPN_NUM; + alpn_lib->alpn_mode = NO_ALPN; + ssl_fixture->alpn_lib = alpn_lib; + ssl_fixture->base.vtable = &vtable; + ssl_fixture->server_name_indication = NULL; + ssl_fixture->force_client_auth = false; + return &ssl_fixture->base; +} + +void ssl_tsi_test_do_handshake_tiny_handshake_buffer() { + tsi_test_fixture *fixture = ssl_tsi_test_fixture_create(); + fixture->handshake_buffer_size = TSI_TEST_TINY_HANDSHAKE_BUFFER_SIZE; + tsi_test_do_handshake(fixture); + tsi_test_fixture_destroy(fixture); +} + +void ssl_tsi_test_do_handshake_small_handshake_buffer() { + tsi_test_fixture *fixture = ssl_tsi_test_fixture_create(); + fixture->handshake_buffer_size = TSI_TEST_SMALL_HANDSHAKE_BUFFER_SIZE; + tsi_test_do_handshake(fixture); + tsi_test_fixture_destroy(fixture); +} + +void ssl_tsi_test_do_handshake() { + tsi_test_fixture *fixture = ssl_tsi_test_fixture_create(); + tsi_test_do_handshake(fixture); + tsi_test_fixture_destroy(fixture); +} + +void ssl_tsi_test_do_handshake_with_client_authentication() { + tsi_test_fixture *fixture = ssl_tsi_test_fixture_create(); + ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture; + ssl_fixture->force_client_auth = true; + tsi_test_do_handshake(fixture); + tsi_test_fixture_destroy(fixture); +} + +void ssl_tsi_test_do_handshake_with_server_name_indication_exact_domain() { + /* server1 cert contains "waterzooi.test.google.be" in SAN. */ + tsi_test_fixture *fixture = ssl_tsi_test_fixture_create(); + ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture; + ssl_fixture->server_name_indication = "waterzooi.test.google.be"; + tsi_test_do_handshake(fixture); + tsi_test_fixture_destroy(fixture); +} + +void ssl_tsi_test_do_handshake_with_server_name_indication_wild_star_domain() { + /* server1 cert contains "*.test.google.fr" in SAN. */ + tsi_test_fixture *fixture = ssl_tsi_test_fixture_create(); + ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture; + ssl_fixture->server_name_indication = "juju.test.google.fr"; + tsi_test_do_handshake(fixture); + tsi_test_fixture_destroy(fixture); +} + +void ssl_tsi_test_do_handshake_with_bad_server_cert() { + tsi_test_fixture *fixture = ssl_tsi_test_fixture_create(); + ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture; + ssl_fixture->key_cert_lib->use_bad_server_cert = true; + tsi_test_do_handshake(fixture); + tsi_test_fixture_destroy(fixture); +} + +void ssl_tsi_test_do_handshake_with_bad_client_cert() { + tsi_test_fixture *fixture = ssl_tsi_test_fixture_create(); + ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture; + ssl_fixture->key_cert_lib->use_bad_client_cert = true; + ssl_fixture->force_client_auth = true; + tsi_test_do_handshake(fixture); + tsi_test_fixture_destroy(fixture); +} + +void ssl_tsi_test_do_handshake_alpn_client_no_server() { + tsi_test_fixture *fixture = ssl_tsi_test_fixture_create(); + ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture; + ssl_fixture->alpn_lib->alpn_mode = ALPN_CLIENT_NO_SERVER; + tsi_test_do_handshake(fixture); + tsi_test_fixture_destroy(fixture); +} + +void ssl_tsi_test_do_handshake_alpn_server_no_client() { + tsi_test_fixture *fixture = ssl_tsi_test_fixture_create(); + ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture; + ssl_fixture->alpn_lib->alpn_mode = ALPN_SERVER_NO_CLIENT; + tsi_test_do_handshake(fixture); + tsi_test_fixture_destroy(fixture); +} + +void ssl_tsi_test_do_handshake_alpn_client_server_mismatch() { + tsi_test_fixture *fixture = ssl_tsi_test_fixture_create(); + ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture; + ssl_fixture->alpn_lib->alpn_mode = ALPN_CLIENT_SERVER_MISMATCH; + tsi_test_do_handshake(fixture); + tsi_test_fixture_destroy(fixture); +} + +void ssl_tsi_test_do_handshake_alpn_client_server_ok() { + tsi_test_fixture *fixture = ssl_tsi_test_fixture_create(); + ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture; + ssl_fixture->alpn_lib->alpn_mode = ALPN_CLIENT_SERVER_OK; + tsi_test_do_handshake(fixture); + tsi_test_fixture_destroy(fixture); +} + +void ssl_tsi_test_do_round_trip_for_all_configs() { + unsigned int *bit_array = + gpr_zalloc(sizeof(unsigned int) * TSI_TEST_NUM_OF_ARGUMENTS); + const unsigned int mask = 1U << (TSI_TEST_NUM_OF_ARGUMENTS - 1); + for (unsigned int val = 0; val < TSI_TEST_NUM_OF_COMBINATIONS; val++) { + unsigned int v = val; + for (unsigned int ind = 0; ind < TSI_TEST_NUM_OF_ARGUMENTS; ind++) { + bit_array[ind] = (v & mask) ? 1 : 0; + v <<= 1; + } + tsi_test_fixture *fixture = ssl_tsi_test_fixture_create(); + ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture; + tsi_test_frame_protector_config_destroy(ssl_fixture->base.config); + ssl_fixture->base.config = tsi_test_frame_protector_config_create( + bit_array[0], bit_array[1], bit_array[2], bit_array[3], bit_array[4], + bit_array[5], bit_array[6], bit_array[7]); + tsi_test_do_round_trip(&ssl_fixture->base); + tsi_test_fixture_destroy(fixture); + } + gpr_free(bit_array); +} + +void ssl_tsi_test_do_round_trip_odd_buffer_size() { + const size_t odd_sizes[] = {1025, 2051, 4103, 8207, 16409}; + const size_t size = sizeof(odd_sizes) / sizeof(size_t); + for (size_t ind1 = 0; ind1 < size; ind1++) { + for (size_t ind2 = 0; ind2 < size; ind2++) { + for (size_t ind3 = 0; ind3 < size; ind3++) { + for (size_t ind4 = 0; ind4 < size; ind4++) { + for (size_t ind5 = 0; ind5 < size; ind5++) { + tsi_test_fixture *fixture = ssl_tsi_test_fixture_create(); + ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture; + tsi_test_frame_protector_config_set_buffer_size( + ssl_fixture->base.config, odd_sizes[ind1], odd_sizes[ind2], + odd_sizes[ind3], odd_sizes[ind4], odd_sizes[ind5]); + tsi_test_do_round_trip(&ssl_fixture->base); + tsi_test_fixture_destroy(fixture); + } + } + } + } + } +} + +int main(int argc, char **argv) { + grpc_test_init(argc, argv); + grpc_init(); + ssl_tsi_test_do_handshake_tiny_handshake_buffer(); + ssl_tsi_test_do_handshake_small_handshake_buffer(); + ssl_tsi_test_do_handshake(); + ssl_tsi_test_do_handshake_with_client_authentication(); + ssl_tsi_test_do_handshake_with_server_name_indication_exact_domain(); + ssl_tsi_test_do_handshake_with_server_name_indication_wild_star_domain(); + ssl_tsi_test_do_handshake_with_bad_server_cert(); + ssl_tsi_test_do_handshake_with_bad_client_cert(); + ssl_tsi_test_do_handshake_alpn_client_no_server(); + ssl_tsi_test_do_handshake_alpn_server_no_client(); + ssl_tsi_test_do_handshake_alpn_client_server_mismatch(); + ssl_tsi_test_do_handshake_alpn_client_server_ok(); + ssl_tsi_test_do_round_trip_for_all_configs(); + ssl_tsi_test_do_round_trip_odd_buffer_size(); + grpc_shutdown(); + return 0; +} diff --git a/test/core/tsi/transport_security_test_lib.c b/test/core/tsi/transport_security_test_lib.c new file mode 100644 index 0000000000..7d66e110f2 --- /dev/null +++ b/test/core/tsi/transport_security_test_lib.c @@ -0,0 +1,550 @@ +/* + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <grpc/grpc.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include "src/core/lib/security/transport/tsi_error.h" +#include "test/core/tsi/transport_security_test_lib.h" + +typedef struct handshaker_args { + tsi_test_fixture *fixture; + unsigned char *handshake_buffer; + size_t handshake_buffer_size; + bool is_client; + bool transferred_data; + bool appended_unused_bytes; + grpc_error *error; +} handshaker_args; + +static handshaker_args *handshaker_args_create(tsi_test_fixture *fixture, + bool is_client) { + GPR_ASSERT(fixture != NULL); + GPR_ASSERT(fixture->config != NULL); + handshaker_args *args = gpr_zalloc(sizeof(*args)); + args->fixture = fixture; + args->handshake_buffer_size = fixture->handshake_buffer_size; + args->handshake_buffer = gpr_zalloc(args->handshake_buffer_size); + args->is_client = is_client; + args->error = GRPC_ERROR_NONE; + return args; +} + +static void handshaker_args_destroy(handshaker_args *args) { + gpr_free(args->handshake_buffer); + GRPC_ERROR_UNREF(args->error); + gpr_free(args); +} + +static void do_handshaker_next(handshaker_args *args); + +static void setup_handshakers(tsi_test_fixture *fixture) { + GPR_ASSERT(fixture != NULL); + GPR_ASSERT(fixture->vtable != NULL); + GPR_ASSERT(fixture->vtable->setup_handshakers != NULL); + fixture->vtable->setup_handshakers(fixture); +} + +static void check_unused_bytes(tsi_test_fixture *fixture) { + tsi_handshaker_result *result_with_unused_bytes = + fixture->has_client_finished_first ? fixture->server_result + : fixture->client_result; + tsi_handshaker_result *result_without_unused_bytes = + fixture->has_client_finished_first ? fixture->client_result + : fixture->server_result; + const unsigned char *bytes = NULL; + size_t bytes_size = 0; + GPR_ASSERT(tsi_handshaker_result_get_unused_bytes( + result_with_unused_bytes, &bytes, &bytes_size) == TSI_OK); + GPR_ASSERT(bytes_size == strlen(TSI_TEST_UNUSED_BYTES)); + GPR_ASSERT(memcmp(bytes, TSI_TEST_UNUSED_BYTES, bytes_size) == 0); + GPR_ASSERT(tsi_handshaker_result_get_unused_bytes( + result_without_unused_bytes, &bytes, &bytes_size) == TSI_OK); + GPR_ASSERT(bytes_size == 0); + GPR_ASSERT(bytes == NULL); +} + +static void check_handshake_results(tsi_test_fixture *fixture) { + GPR_ASSERT(fixture != NULL); + GPR_ASSERT(fixture->vtable != NULL); + GPR_ASSERT(fixture->vtable->check_handshaker_peers != NULL); + /* Check handshaker peers. */ + fixture->vtable->check_handshaker_peers(fixture); + /* Check unused bytes. */ + if (fixture->test_unused_bytes) { + if (fixture->server_result != NULL && fixture->client_result != NULL) { + check_unused_bytes(fixture); + } + fixture->bytes_written_to_server_channel = 0; + fixture->bytes_written_to_client_channel = 0; + fixture->bytes_read_from_client_channel = 0; + fixture->bytes_read_from_server_channel = 0; + } +} + +static void send_bytes_to_peer(tsi_test_fixture *fixture, + const unsigned char *buf, size_t buf_size, + bool is_client) { + GPR_ASSERT(fixture != NULL); + GPR_ASSERT(buf != NULL); + uint8_t *channel = + is_client ? fixture->server_channel : fixture->client_channel; + GPR_ASSERT(channel != NULL); + size_t *bytes_written = is_client ? &fixture->bytes_written_to_server_channel + : &fixture->bytes_written_to_client_channel; + GPR_ASSERT(bytes_written != NULL); + GPR_ASSERT(*bytes_written + buf_size <= TSI_TEST_DEFAULT_CHANNEL_SIZE); + /* Write data to channel. */ + memcpy(channel + *bytes_written, buf, buf_size); + *bytes_written += buf_size; +} + +static void maybe_append_unused_bytes(handshaker_args *args) { + GPR_ASSERT(args != NULL); + GPR_ASSERT(args->fixture != NULL); + tsi_test_fixture *fixture = args->fixture; + if (fixture->test_unused_bytes && !args->appended_unused_bytes) { + args->appended_unused_bytes = true; + send_bytes_to_peer(fixture, (const unsigned char *)TSI_TEST_UNUSED_BYTES, + strlen(TSI_TEST_UNUSED_BYTES), args->is_client); + if (fixture->client_result != NULL && fixture->server_result == NULL) { + fixture->has_client_finished_first = true; + } + } +} + +static void receive_bytes_from_peer(tsi_test_fixture *fixture, + unsigned char **buf, size_t *buf_size, + bool is_client) { + GPR_ASSERT(fixture != NULL); + GPR_ASSERT(*buf != NULL); + GPR_ASSERT(buf_size != NULL); + uint8_t *channel = + is_client ? fixture->client_channel : fixture->server_channel; + GPR_ASSERT(channel != NULL); + size_t *bytes_read = is_client ? &fixture->bytes_read_from_client_channel + : &fixture->bytes_read_from_server_channel; + size_t *bytes_written = is_client ? &fixture->bytes_written_to_client_channel + : &fixture->bytes_written_to_server_channel; + GPR_ASSERT(bytes_read != NULL); + GPR_ASSERT(bytes_written != NULL); + size_t to_read = *buf_size < *bytes_written - *bytes_read + ? *buf_size + : *bytes_written - *bytes_read; + /* Read data from channel. */ + memcpy(*buf, channel + *bytes_read, to_read); + *buf_size = to_read; + *bytes_read += to_read; +} + +static void send_message_to_peer(tsi_test_fixture *fixture, + tsi_frame_protector *protector, + bool is_client) { + /* Initialization. */ + GPR_ASSERT(fixture != NULL); + GPR_ASSERT(fixture->config != NULL); + GPR_ASSERT(protector != NULL); + tsi_test_frame_protector_config *config = fixture->config; + unsigned char *protected_buffer = gpr_zalloc(config->protected_buffer_size); + size_t message_size = + is_client ? config->client_message_size : config->server_message_size; + uint8_t *message = + is_client ? config->client_message : config->server_message; + GPR_ASSERT(message != NULL); + const unsigned char *message_bytes = (const unsigned char *)message; + tsi_result result = TSI_OK; + /* Do protect and send protected data to peer. */ + while (message_size > 0 && result == TSI_OK) { + size_t protected_buffer_size_to_send = config->protected_buffer_size; + size_t processed_message_size = message_size; + /* Do protect. */ + result = tsi_frame_protector_protect( + protector, message_bytes, &processed_message_size, protected_buffer, + &protected_buffer_size_to_send); + GPR_ASSERT(result == TSI_OK); + /* Send protected data to peer. */ + send_bytes_to_peer(fixture, protected_buffer, protected_buffer_size_to_send, + is_client); + message_bytes += processed_message_size; + message_size -= processed_message_size; + /* Flush if we're done. */ + if (message_size == 0) { + size_t still_pending_size; + do { + protected_buffer_size_to_send = config->protected_buffer_size; + result = tsi_frame_protector_protect_flush( + protector, protected_buffer, &protected_buffer_size_to_send, + &still_pending_size); + GPR_ASSERT(result == TSI_OK); + send_bytes_to_peer(fixture, protected_buffer, + protected_buffer_size_to_send, is_client); + } while (still_pending_size > 0 && result == TSI_OK); + GPR_ASSERT(result == TSI_OK); + } + } + GPR_ASSERT(result == TSI_OK); + gpr_free(protected_buffer); +} + +static void receive_message_from_peer(tsi_test_fixture *fixture, + tsi_frame_protector *protector, + unsigned char *message, + size_t *bytes_received, bool is_client) { + /* Initialization. */ + GPR_ASSERT(fixture != NULL); + GPR_ASSERT(protector != NULL); + GPR_ASSERT(message != NULL); + GPR_ASSERT(bytes_received != NULL); + GPR_ASSERT(fixture->config != NULL); + tsi_test_frame_protector_config *config = fixture->config; + size_t read_offset = 0; + size_t message_offset = 0; + size_t read_from_peer_size = 0; + tsi_result result = TSI_OK; + bool done = false; + unsigned char *read_buffer = gpr_zalloc(config->read_buffer_allocated_size); + unsigned char *message_buffer = + gpr_zalloc(config->message_buffer_allocated_size); + /* Do unprotect on data received from peer. */ + while (!done && result == TSI_OK) { + /* Receive data from peer. */ + if (read_from_peer_size == 0) { + read_from_peer_size = config->read_buffer_allocated_size; + receive_bytes_from_peer(fixture, &read_buffer, &read_from_peer_size, + is_client); + read_offset = 0; + } + if (read_from_peer_size == 0) { + done = true; + } + /* Do unprotect. */ + size_t message_buffer_size; + do { + message_buffer_size = config->message_buffer_allocated_size; + size_t processed_size = read_from_peer_size; + result = tsi_frame_protector_unprotect( + protector, read_buffer + read_offset, &processed_size, message_buffer, + &message_buffer_size); + GPR_ASSERT(result == TSI_OK); + if (message_buffer_size > 0) { + memcpy(message + message_offset, message_buffer, message_buffer_size); + message_offset += message_buffer_size; + } + read_offset += processed_size; + read_from_peer_size -= processed_size; + } while ((read_from_peer_size > 0 || message_buffer_size > 0) && + result == TSI_OK); + GPR_ASSERT(result == TSI_OK); + } + GPR_ASSERT(result == TSI_OK); + *bytes_received = message_offset; + gpr_free(read_buffer); + gpr_free(message_buffer); +} + +grpc_error *on_handshake_next_done(tsi_result result, void *user_data, + const unsigned char *bytes_to_send, + size_t bytes_to_send_size, + tsi_handshaker_result *handshaker_result) { + handshaker_args *args = (handshaker_args *)user_data; + GPR_ASSERT(args != NULL); + GPR_ASSERT(args->fixture != NULL); + tsi_test_fixture *fixture = args->fixture; + grpc_error *error = GRPC_ERROR_NONE; + /* Read more data if we need to. */ + if (result == TSI_INCOMPLETE_DATA) { + GPR_ASSERT(bytes_to_send_size == 0); + return error; + } + if (result != TSI_OK) { + return grpc_set_tsi_error_result( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshake failed"), result); + } + /* Update handshaker result. */ + if (handshaker_result != NULL) { + tsi_handshaker_result **result_to_write = + args->is_client ? &fixture->client_result : &fixture->server_result; + GPR_ASSERT(*result_to_write == NULL); + *result_to_write = handshaker_result; + } + /* Send data to peer, if needed. */ + if (bytes_to_send_size > 0) { + send_bytes_to_peer(args->fixture, bytes_to_send, bytes_to_send_size, + args->is_client); + args->transferred_data = true; + } + if (handshaker_result != NULL) { + maybe_append_unused_bytes(args); + } + return error; +} + +static void on_handshake_next_done_wrapper( + tsi_result result, void *user_data, const unsigned char *bytes_to_send, + size_t bytes_to_send_size, tsi_handshaker_result *handshaker_result) { + handshaker_args *args = (handshaker_args *)user_data; + args->error = on_handshake_next_done(result, user_data, bytes_to_send, + bytes_to_send_size, handshaker_result); +} + +static bool is_handshake_finished_properly(handshaker_args *args) { + GPR_ASSERT(args != NULL); + GPR_ASSERT(args->fixture != NULL); + tsi_test_fixture *fixture = args->fixture; + if ((args->is_client && fixture->client_result != NULL) || + (!args->is_client && fixture->server_result != NULL)) { + return true; + } + return false; +} + +static void do_handshaker_next(handshaker_args *args) { + /* Initialization. */ + GPR_ASSERT(args != NULL); + GPR_ASSERT(args->fixture != NULL); + tsi_test_fixture *fixture = args->fixture; + tsi_handshaker *handshaker = + args->is_client ? fixture->client_handshaker : fixture->server_handshaker; + if (is_handshake_finished_properly(args)) { + return; + } + tsi_handshaker_result *handshaker_result = NULL; + unsigned char *bytes_to_send = NULL; + size_t bytes_to_send_size = 0; + /* Receive data from peer, if available. */ + size_t buf_size = args->handshake_buffer_size; + receive_bytes_from_peer(args->fixture, &args->handshake_buffer, &buf_size, + args->is_client); + if (buf_size > 0) { + args->transferred_data = true; + } + /* Peform handshaker next. */ + tsi_result result = tsi_handshaker_next( + handshaker, args->handshake_buffer, buf_size, + (const unsigned char **)&bytes_to_send, &bytes_to_send_size, + &handshaker_result, &on_handshake_next_done_wrapper, args); + if (result != TSI_ASYNC) { + args->error = on_handshake_next_done(result, args, bytes_to_send, + bytes_to_send_size, handshaker_result); + } +} + +void tsi_test_do_handshake(tsi_test_fixture *fixture) { + /* Initializaiton. */ + setup_handshakers(fixture); + handshaker_args *client_args = + handshaker_args_create(fixture, true /* is_client */); + handshaker_args *server_args = + handshaker_args_create(fixture, false /* is_client */); + /* Do handshake. */ + do { + client_args->transferred_data = false; + server_args->transferred_data = false; + do_handshaker_next(client_args); + if (client_args->error != GRPC_ERROR_NONE) { + break; + } + do_handshaker_next(server_args); + if (server_args->error != GRPC_ERROR_NONE) { + break; + } + GPR_ASSERT(client_args->transferred_data || server_args->transferred_data); + } while (fixture->client_result == NULL || fixture->server_result == NULL); + /* Verify handshake results. */ + check_handshake_results(fixture); + /* Cleanup. */ + handshaker_args_destroy(client_args); + handshaker_args_destroy(server_args); +} + +void tsi_test_do_round_trip(tsi_test_fixture *fixture) { + /* Initialization. */ + GPR_ASSERT(fixture != NULL); + GPR_ASSERT(fixture->config != NULL); + tsi_test_frame_protector_config *config = fixture->config; + tsi_frame_protector *client_frame_protector = NULL; + tsi_frame_protector *server_frame_protector = NULL; + /* Perform handshake. */ + tsi_test_do_handshake(fixture); + /* Create frame protectors.*/ + size_t client_max_output_protected_frame_size = + config->client_max_output_protected_frame_size; + GPR_ASSERT(tsi_handshaker_result_create_frame_protector( + fixture->client_result, + client_max_output_protected_frame_size == 0 + ? NULL + : &client_max_output_protected_frame_size, + &client_frame_protector) == TSI_OK); + size_t server_max_output_protected_frame_size = + config->server_max_output_protected_frame_size; + GPR_ASSERT(tsi_handshaker_result_create_frame_protector( + fixture->server_result, + server_max_output_protected_frame_size == 0 + ? NULL + : &server_max_output_protected_frame_size, + &server_frame_protector) == TSI_OK); + /* Client sends a message to server. */ + send_message_to_peer(fixture, client_frame_protector, true /* is_client */); + unsigned char *server_received_message = + gpr_zalloc(TSI_TEST_DEFAULT_CHANNEL_SIZE); + size_t server_received_message_size = 0; + receive_message_from_peer( + fixture, server_frame_protector, server_received_message, + &server_received_message_size, false /* is_client */); + GPR_ASSERT(config->client_message_size == server_received_message_size); + GPR_ASSERT(memcmp(config->client_message, server_received_message, + server_received_message_size) == 0); + /* Server sends a message to client. */ + send_message_to_peer(fixture, server_frame_protector, false /* is_client */); + unsigned char *client_received_message = + gpr_zalloc(TSI_TEST_DEFAULT_CHANNEL_SIZE); + size_t client_received_message_size = 0; + receive_message_from_peer( + fixture, client_frame_protector, client_received_message, + &client_received_message_size, true /* is_client */); + GPR_ASSERT(config->server_message_size == client_received_message_size); + GPR_ASSERT(memcmp(config->server_message, client_received_message, + client_received_message_size) == 0); + /* Destroy server and client frame protectors. */ + tsi_frame_protector_destroy(client_frame_protector); + tsi_frame_protector_destroy(server_frame_protector); + gpr_free(server_received_message); + gpr_free(client_received_message); +} + +static unsigned char *generate_random_message(size_t size) { + size_t i; + unsigned char chars[] = "abcdefghijklmnopqrstuvwxyz1234567890"; + unsigned char *output = gpr_zalloc(sizeof(unsigned char) * size); + for (i = 0; i < size - 1; ++i) { + output[i] = chars[rand() % (int)(sizeof(chars) - 1)]; + } + return output; +} + +tsi_test_frame_protector_config *tsi_test_frame_protector_config_create( + bool use_default_read_buffer_allocated_size, + bool use_default_message_buffer_allocated_size, + bool use_default_protected_buffer_size, bool use_default_client_message, + bool use_default_server_message, + bool use_default_client_max_output_protected_frame_size, + bool use_default_server_max_output_protected_frame_size, + bool use_default_handshake_buffer_size) { + tsi_test_frame_protector_config *config = gpr_zalloc(sizeof(*config)); + /* Set the value for read_buffer_allocated_size. */ + config->read_buffer_allocated_size = + use_default_read_buffer_allocated_size + ? TSI_TEST_DEFAULT_BUFFER_SIZE + : TSI_TEST_SMALL_READ_BUFFER_ALLOCATED_SIZE; + /* Set the value for message_buffer_allocated_size. */ + config->message_buffer_allocated_size = + use_default_message_buffer_allocated_size + ? TSI_TEST_DEFAULT_BUFFER_SIZE + : TSI_TEST_SMALL_MESSAGE_BUFFER_ALLOCATED_SIZE; + /* Set the value for protected_buffer_size. */ + config->protected_buffer_size = use_default_protected_buffer_size + ? TSI_TEST_DEFAULT_PROTECTED_BUFFER_SIZE + : TSI_TEST_SMALL_PROTECTED_BUFFER_SIZE; + /* Set the value for client message. */ + config->client_message_size = use_default_client_message + ? TSI_TEST_BIG_MESSAGE_SIZE + : TSI_TEST_SMALL_MESSAGE_SIZE; + config->client_message = + use_default_client_message + ? generate_random_message(TSI_TEST_BIG_MESSAGE_SIZE) + : generate_random_message(TSI_TEST_SMALL_MESSAGE_SIZE); + /* Set the value for server message. */ + config->server_message_size = use_default_server_message + ? TSI_TEST_BIG_MESSAGE_SIZE + : TSI_TEST_SMALL_MESSAGE_SIZE; + config->server_message = + use_default_server_message + ? generate_random_message(TSI_TEST_BIG_MESSAGE_SIZE) + : generate_random_message(TSI_TEST_SMALL_MESSAGE_SIZE); + /* Set the value for client max_output_protected_frame_size. + If it is 0, we pass NULL to tsi_handshaker_result_create_frame_protector(), + which then uses default protected frame size for it. */ + config->client_max_output_protected_frame_size = + use_default_client_max_output_protected_frame_size + ? 0 + : TSI_TEST_SMALL_CLIENT_MAX_OUTPUT_PROTECTED_FRAME_SIZE; + /* Set the value for server max_output_protected_frame_size. + If it is 0, we pass NULL to tsi_handshaker_result_create_frame_protector(), + which then uses default protected frame size for it. */ + config->server_max_output_protected_frame_size = + use_default_server_max_output_protected_frame_size + ? 0 + : TSI_TEST_SMALL_SERVER_MAX_OUTPUT_PROTECTED_FRAME_SIZE; + return config; +} + +void tsi_test_frame_protector_config_set_buffer_size( + tsi_test_frame_protector_config *config, size_t read_buffer_allocated_size, + size_t message_buffer_allocated_size, size_t protected_buffer_size, + size_t client_max_output_protected_frame_size, + size_t server_max_output_protected_frame_size) { + GPR_ASSERT(config != NULL); + config->read_buffer_allocated_size = read_buffer_allocated_size; + config->message_buffer_allocated_size = message_buffer_allocated_size; + config->protected_buffer_size = protected_buffer_size; + config->client_max_output_protected_frame_size = + client_max_output_protected_frame_size; + config->server_max_output_protected_frame_size = + server_max_output_protected_frame_size; +} + +void tsi_test_frame_protector_config_destroy( + tsi_test_frame_protector_config *config) { + GPR_ASSERT(config != NULL); + gpr_free(config->client_message); + gpr_free(config->server_message); + gpr_free(config); +} + +void tsi_test_fixture_init(tsi_test_fixture *fixture) { + fixture->config = tsi_test_frame_protector_config_create( + true, true, true, true, true, true, true, true); + fixture->handshake_buffer_size = TSI_TEST_DEFAULT_BUFFER_SIZE; + fixture->client_channel = gpr_zalloc(TSI_TEST_DEFAULT_CHANNEL_SIZE); + fixture->server_channel = gpr_zalloc(TSI_TEST_DEFAULT_CHANNEL_SIZE); + fixture->bytes_written_to_client_channel = 0; + fixture->bytes_written_to_server_channel = 0; + fixture->bytes_read_from_client_channel = 0; + fixture->bytes_read_from_server_channel = 0; + fixture->test_unused_bytes = true; + fixture->has_client_finished_first = false; +} + +void tsi_test_fixture_destroy(tsi_test_fixture *fixture) { + GPR_ASSERT(fixture != NULL); + tsi_test_frame_protector_config_destroy(fixture->config); + tsi_handshaker_destroy(fixture->client_handshaker); + tsi_handshaker_destroy(fixture->server_handshaker); + tsi_handshaker_result_destroy(fixture->client_result); + tsi_handshaker_result_destroy(fixture->server_result); + gpr_free(fixture->client_channel); + gpr_free(fixture->server_channel); + GPR_ASSERT(fixture->vtable != NULL); + GPR_ASSERT(fixture->vtable->destruct != NULL); + fixture->vtable->destruct(fixture); + gpr_free(fixture); +} diff --git a/test/core/tsi/transport_security_test_lib.h b/test/core/tsi/transport_security_test_lib.h new file mode 100644 index 0000000000..8ae2024ee4 --- /dev/null +++ b/test/core/tsi/transport_security_test_lib.h @@ -0,0 +1,165 @@ +/* + * + * 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_TEST_CORE_TSI_TRANSPORT_SECURITY_TEST_LIB_H_ +#define GRPC_TEST_CORE_TSI_TRANSPORT_SECURITY_TEST_LIB_H_ + +#include "src/core/tsi/transport_security_interface.h" + +#define TSI_TEST_TINY_HANDSHAKE_BUFFER_SIZE 32 +#define TSI_TEST_SMALL_HANDSHAKE_BUFFER_SIZE 128 +#define TSI_TEST_SMALL_READ_BUFFER_ALLOCATED_SIZE 41 +#define TSI_TEST_SMALL_PROTECTED_BUFFER_SIZE 37 +#define TSI_TEST_SMALL_MESSAGE_BUFFER_ALLOCATED_SIZE 42 +#define TSI_TEST_SMALL_CLIENT_MAX_OUTPUT_PROTECTED_FRAME_SIZE 39 +#define TSI_TEST_SMALL_SERVER_MAX_OUTPUT_PROTECTED_FRAME_SIZE 43 +#define TSI_TEST_DEFAULT_BUFFER_SIZE 4096 +#define TSI_TEST_DEFAULT_PROTECTED_BUFFER_SIZE 16384 +#define TSI_TEST_DEFAULT_CHANNEL_SIZE 32768 +#define TSI_TEST_BIG_MESSAGE_SIZE 17000 +#define TSI_TEST_SMALL_MESSAGE_SIZE 10 +#define TSI_TEST_NUM_OF_ARGUMENTS 8 +#define TSI_TEST_NUM_OF_COMBINATIONS 256 +#define TSI_TEST_UNUSED_BYTES "HELLO GOOGLE" + +/* --- tsi_test_fixture object --- + The tests for specific TSI implementations should create their own + custom "subclass" of this fixture, which wraps all information + that will be used to test correctness of TSI handshakes and frame + protect/unprotect operations with respect to TSI implementations. */ +typedef struct tsi_test_fixture tsi_test_fixture; + +/* --- tsi_test_frame_protector_config object --- + + This object is used to configure different parameters of TSI frame protector + APIs. */ +typedef struct tsi_test_frame_protector_config tsi_test_frame_protector_config; + +/* V-table for tsi_test_fixture operations that are implemented differently in + different TSI implementations. */ +typedef struct tsi_test_fixture_vtable { + void (*setup_handshakers)(tsi_test_fixture *fixture); + void (*check_handshaker_peers)(tsi_test_fixture *fixture); + void (*destruct)(tsi_test_fixture *fixture); +} tranport_security_test_vtable; + +struct tsi_test_fixture { + const struct tsi_test_fixture_vtable *vtable; + /* client/server TSI handshaker used to perform TSI handshakes, and will get + instantiated during the call to setup_handshakers. */ + tsi_handshaker *client_handshaker; + tsi_handshaker *server_handshaker; + /* client/server TSI handshaker results used to store the result of TSI + handshake. If the handshake fails, the result will store NULL upon + finishing the handshake. */ + tsi_handshaker_result *client_result; + tsi_handshaker_result *server_result; + /* size of buffer used to store data received from the peer. */ + size_t handshake_buffer_size; + /* simulated channels between client and server. If the server (client) + wants to send data to the client (server), he will write data to + client_channel (server_channel), which will be read by client (server). */ + uint8_t *client_channel; + uint8_t *server_channel; + /* size of data written to the client/server channel. */ + size_t bytes_written_to_client_channel; + size_t bytes_written_to_server_channel; + /* size of data read from the client/server channel */ + size_t bytes_read_from_client_channel; + size_t bytes_read_from_server_channel; + /* tsi_test_frame_protector_config instance */ + tsi_test_frame_protector_config *config; + /* a flag indicating if client has finished TSI handshake first (i.e., before + server). + The flag should be referred if and only if TSI handshake finishes + successfully. */ + bool has_client_finished_first; + /* a flag indicating whether to test tsi_handshaker_result_get_unused_bytes() + for TSI implementation. This field is true by default, and false + for SSL TSI implementation due to grpc issue #12164 + (https://github.com/grpc/grpc/issues/12164). + */ + bool test_unused_bytes; +}; + +struct tsi_test_frame_protector_config { + /* size of buffer used to store protected frames to be unprotected. */ + size_t read_buffer_allocated_size; + /* size of buffer used to store bytes resulted from unprotect operations. */ + size_t message_buffer_allocated_size; + /* size of buffer used to store frames resulted from protect operations. */ + size_t protected_buffer_size; + /* size of client/server maximum frame size. */ + size_t client_max_output_protected_frame_size; + size_t server_max_output_protected_frame_size; + /* pointer that points to client/server message to be protected. */ + uint8_t *client_message; + uint8_t *server_message; + /* size of client/server message. */ + size_t client_message_size; + size_t server_message_size; +}; + +/* This method creates a tsi_test_frame_protector_config instance. Each + parameter of this function is a boolean value indicating whether to set the + corresponding parameter with a default value or not. If it's false, it will + be set with a specific value which is usually much smaller than the default. + Both values are defined with #define directive. */ +tsi_test_frame_protector_config *tsi_test_frame_protector_config_create( + bool use_default_read_buffer_allocated_size, + bool use_default_message_buffer_allocated_size, + bool use_default_protected_buffer_size, bool use_default_client_message, + bool use_default_server_message, + bool use_default_client_max_output_protected_frame_size, + bool use_default_server_max_output_protected_frame_size, + bool use_default_handshake_buffer_size); + +/* This method sets different buffer and frame sizes of a + tsi_test_frame_protector_config instance with user provided values. */ +void tsi_test_frame_protector_config_set_buffer_size( + tsi_test_frame_protector_config *config, size_t read_buffer_allocated_size, + size_t message_buffer_allocated_size, size_t protected_buffer_size, + size_t client_max_output_protected_frame_size, + size_t server_max_output_protected_frame_size); + +/* This method destroys a tsi_test_frame_protector_config instance. */ +void tsi_test_frame_protector_config_destroy( + tsi_test_frame_protector_config *config); + +/* This method initializes members of tsi_test_fixture instance. + Note that the struct instance should be allocated before making + this call. */ +void tsi_test_fixture_init(tsi_test_fixture *fixture); + +/* This method destroys a tsi_test_fixture instance. Note that the + fixture intance must be dynamically allocated and will be freed by + this function. */ +void tsi_test_fixture_destroy(tsi_test_fixture *fixture); + +/* This method performs a full TSI handshake between a client and a server. + Note that the test library will implement the new TSI handshaker API to + perform handshakes. */ +void tsi_test_do_handshake(tsi_test_fixture *fixture); + +/* This method performs a round trip test between the client and the server. + That is, the client sends a protected message to a server who receives the + message, and unprotects it. The same operation is triggered again with + the client and server switching its role. */ +void tsi_test_do_round_trip(tsi_test_fixture *fixture); + +#endif // GRPC_TEST_CORE_TSI_TRANSPORT_SECURITY_TEST_LIB_H_ diff --git a/test/cpp/end2end/BUILD b/test/cpp/end2end/BUILD index b8505c1ae7..b29a13d4fb 100644 --- a/test/cpp/end2end/BUILD +++ b/test/cpp/end2end/BUILD @@ -193,6 +193,7 @@ grpc_cc_test( "//test/cpp/util:test_util", ], external_deps = [ + "gmock", "gtest", ], ) @@ -235,6 +236,7 @@ grpc_cc_test( "//test/cpp/util:test_util", ], external_deps = [ + "gmock", "gtest", ], ) diff --git a/test/cpp/end2end/async_end2end_test.cc b/test/cpp/end2end/async_end2end_test.cc index 7cb7b262de..e841a702d4 100644 --- a/test/cpp/end2end/async_end2end_test.cc +++ b/test/cpp/end2end/async_end2end_test.cc @@ -260,11 +260,31 @@ class AsyncEnd2endTest : public ::testing::TestWithParam<TestScenario> { server_address_ << "localhost:" << port_; // Setup server + BuildAndStartServer(); + + gpr_tls_set(&g_is_async_end2end_test, 1); + } + + void TearDown() override { + server_->Shutdown(); + void* ignored_tag; + bool ignored_ok; + cq_->Shutdown(); + while (cq_->Next(&ignored_tag, &ignored_ok)) + ; + stub_.reset(); + poll_overrider_.reset(); + gpr_tls_set(&g_is_async_end2end_test, 0); + grpc_recycle_unused_port(port_); + } + + void BuildAndStartServer() { ServerBuilder builder; auto server_creds = GetCredentialsProvider()->GetServerCredentials( GetParam().credentials_type); builder.AddListeningPort(server_address_.str(), server_creds); - builder.RegisterService(&service_); + service_.reset(new grpc::testing::EchoTestService::AsyncService()); + builder.RegisterService(service_.get()); if (GetParam().health_check_service) { builder.RegisterService(&health_check_); } @@ -276,20 +296,6 @@ class AsyncEnd2endTest : public ::testing::TestWithParam<TestScenario> { new ServerBuilderSyncPluginDisabler()); builder.SetOption(move(sync_plugin_disabler)); server_ = builder.BuildAndStart(); - - gpr_tls_set(&g_is_async_end2end_test, 1); - } - - void TearDown() override { - server_->Shutdown(); - void* ignored_tag; - bool ignored_ok; - cq_->Shutdown(); - while (cq_->Next(&ignored_tag, &ignored_ok)) - ; - poll_overrider_.reset(); - gpr_tls_set(&g_is_async_end2end_test, 0); - grpc_recycle_unused_port(port_); } void ResetStub() { @@ -319,8 +325,8 @@ class AsyncEnd2endTest : public ::testing::TestWithParam<TestScenario> { std::unique_ptr<ClientAsyncResponseReader<EchoResponse>> response_reader( stub_->AsyncEcho(&cli_ctx, send_request, cq_.get())); - service_.RequestEcho(&srv_ctx, &recv_request, &response_writer, cq_.get(), - cq_.get(), tag(2)); + service_->RequestEcho(&srv_ctx, &recv_request, &response_writer, + cq_.get(), cq_.get(), tag(2)); Verifier(GetParam().disable_blocking).Expect(2, true).Verify(cq_.get()); EXPECT_EQ(send_request.message(), recv_request.message()); @@ -341,7 +347,7 @@ class AsyncEnd2endTest : public ::testing::TestWithParam<TestScenario> { std::unique_ptr<ServerCompletionQueue> cq_; std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_; std::unique_ptr<Server> server_; - grpc::testing::EchoTestService::AsyncService service_; + std::unique_ptr<grpc::testing::EchoTestService::AsyncService> service_; HealthCheck health_check_; std::ostringstream server_address_; int port_; @@ -359,6 +365,26 @@ TEST_P(AsyncEnd2endTest, SequentialRpcs) { SendRpc(10); } +TEST_P(AsyncEnd2endTest, ReconnectChannel) { + if (GetParam().inproc) { + return; + } + ResetStub(); + SendRpc(1); + server_->Shutdown(); + void* ignored_tag; + bool ignored_ok; + cq_->Shutdown(); + while (cq_->Next(&ignored_tag, &ignored_ok)) + ; + BuildAndStartServer(); + // It needs more than kConnectivityCheckIntervalMsec time to reconnect the + // channel. + gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), + gpr_time_from_millis(1600, GPR_TIMESPAN))); + SendRpc(1); +} + // We do not need to protect notify because the use is synchronized. void ServerWait(Server* server, int* notify) { server->Wait(); @@ -407,8 +433,8 @@ TEST_P(AsyncEnd2endTest, AsyncNextRpc) { Verifier(GetParam().disable_blocking).Verify(cq_.get(), time_now); Verifier(GetParam().disable_blocking).Verify(cq_.get(), time_now); - service_.RequestEcho(&srv_ctx, &recv_request, &response_writer, cq_.get(), - cq_.get(), tag(2)); + service_->RequestEcho(&srv_ctx, &recv_request, &response_writer, cq_.get(), + cq_.get(), tag(2)); Verifier(GetParam().disable_blocking) .Expect(2, true) @@ -444,8 +470,8 @@ TEST_P(AsyncEnd2endTest, SimpleClientStreaming) { std::unique_ptr<ClientAsyncWriter<EchoRequest>> cli_stream( stub_->AsyncRequestStream(&cli_ctx, &recv_response, cq_.get(), tag(1))); - service_.RequestRequestStream(&srv_ctx, &srv_stream, cq_.get(), cq_.get(), - tag(2)); + service_->RequestRequestStream(&srv_ctx, &srv_stream, cq_.get(), cq_.get(), + tag(2)); Verifier(GetParam().disable_blocking) .Expect(2, true) @@ -506,8 +532,8 @@ TEST_P(AsyncEnd2endTest, SimpleClientStreamingWithCoalescingApi) { std::unique_ptr<ClientAsyncWriter<EchoRequest>> cli_stream( stub_->AsyncRequestStream(&cli_ctx, &recv_response, cq_.get(), tag(1))); - service_.RequestRequestStream(&srv_ctx, &srv_stream, cq_.get(), cq_.get(), - tag(2)); + service_->RequestRequestStream(&srv_ctx, &srv_stream, cq_.get(), cq_.get(), + tag(2)); cli_stream->Write(send_request, tag(3)); @@ -579,8 +605,8 @@ TEST_P(AsyncEnd2endTest, SimpleServerStreaming) { std::unique_ptr<ClientAsyncReader<EchoResponse>> cli_stream( stub_->AsyncResponseStream(&cli_ctx, send_request, cq_.get(), tag(1))); - service_.RequestResponseStream(&srv_ctx, &recv_request, &srv_stream, - cq_.get(), cq_.get(), tag(2)); + service_->RequestResponseStream(&srv_ctx, &recv_request, &srv_stream, + cq_.get(), cq_.get(), tag(2)); Verifier(GetParam().disable_blocking) .Expect(1, true) @@ -635,8 +661,8 @@ TEST_P(AsyncEnd2endTest, SimpleServerStreamingWithCoalescingApiWAF) { std::unique_ptr<ClientAsyncReader<EchoResponse>> cli_stream( stub_->AsyncResponseStream(&cli_ctx, send_request, cq_.get(), tag(1))); - service_.RequestResponseStream(&srv_ctx, &recv_request, &srv_stream, - cq_.get(), cq_.get(), tag(2)); + service_->RequestResponseStream(&srv_ctx, &recv_request, &srv_stream, + cq_.get(), cq_.get(), tag(2)); Verifier(GetParam().disable_blocking) .Expect(1, true) @@ -687,8 +713,8 @@ TEST_P(AsyncEnd2endTest, SimpleServerStreamingWithCoalescingApiWL) { std::unique_ptr<ClientAsyncReader<EchoResponse>> cli_stream( stub_->AsyncResponseStream(&cli_ctx, send_request, cq_.get(), tag(1))); - service_.RequestResponseStream(&srv_ctx, &recv_request, &srv_stream, - cq_.get(), cq_.get(), tag(2)); + service_->RequestResponseStream(&srv_ctx, &recv_request, &srv_stream, + cq_.get(), cq_.get(), tag(2)); Verifier(GetParam().disable_blocking) .Expect(1, true) @@ -741,8 +767,8 @@ TEST_P(AsyncEnd2endTest, SimpleBidiStreaming) { std::unique_ptr<ClientAsyncReaderWriter<EchoRequest, EchoResponse>> cli_stream(stub_->AsyncBidiStream(&cli_ctx, cq_.get(), tag(1))); - service_.RequestBidiStream(&srv_ctx, &srv_stream, cq_.get(), cq_.get(), - tag(2)); + service_->RequestBidiStream(&srv_ctx, &srv_stream, cq_.get(), cq_.get(), + tag(2)); Verifier(GetParam().disable_blocking) .Expect(1, true) @@ -801,8 +827,8 @@ TEST_P(AsyncEnd2endTest, SimpleBidiStreamingWithCoalescingApiWAF) { std::unique_ptr<ClientAsyncReaderWriter<EchoRequest, EchoResponse>> cli_stream(stub_->AsyncBidiStream(&cli_ctx, cq_.get(), tag(1))); - service_.RequestBidiStream(&srv_ctx, &srv_stream, cq_.get(), cq_.get(), - tag(2)); + service_->RequestBidiStream(&srv_ctx, &srv_stream, cq_.get(), cq_.get(), + tag(2)); cli_stream->WriteLast(send_request, WriteOptions(), tag(3)); @@ -869,8 +895,8 @@ TEST_P(AsyncEnd2endTest, SimpleBidiStreamingWithCoalescingApiWL) { std::unique_ptr<ClientAsyncReaderWriter<EchoRequest, EchoResponse>> cli_stream(stub_->AsyncBidiStream(&cli_ctx, cq_.get(), tag(1))); - service_.RequestBidiStream(&srv_ctx, &srv_stream, cq_.get(), cq_.get(), - tag(2)); + service_->RequestBidiStream(&srv_ctx, &srv_stream, cq_.get(), cq_.get(), + tag(2)); cli_stream->WriteLast(send_request, WriteOptions(), tag(3)); @@ -946,8 +972,8 @@ TEST_P(AsyncEnd2endTest, ClientInitialMetadataRpc) { std::unique_ptr<ClientAsyncResponseReader<EchoResponse>> response_reader( stub_->AsyncEcho(&cli_ctx, send_request, cq_.get())); - service_.RequestEcho(&srv_ctx, &recv_request, &response_writer, cq_.get(), - cq_.get(), tag(2)); + service_->RequestEcho(&srv_ctx, &recv_request, &response_writer, cq_.get(), + cq_.get(), tag(2)); Verifier(GetParam().disable_blocking).Expect(2, true).Verify(cq_.get()); EXPECT_EQ(send_request.message(), recv_request.message()); auto client_initial_metadata = srv_ctx.client_metadata(); @@ -991,8 +1017,8 @@ TEST_P(AsyncEnd2endTest, ServerInitialMetadataRpc) { std::unique_ptr<ClientAsyncResponseReader<EchoResponse>> response_reader( stub_->AsyncEcho(&cli_ctx, send_request, cq_.get())); - service_.RequestEcho(&srv_ctx, &recv_request, &response_writer, cq_.get(), - cq_.get(), tag(2)); + service_->RequestEcho(&srv_ctx, &recv_request, &response_writer, cq_.get(), + cq_.get(), tag(2)); Verifier(GetParam().disable_blocking).Expect(2, true).Verify(cq_.get()); EXPECT_EQ(send_request.message(), recv_request.message()); srv_ctx.AddInitialMetadata(meta1.first, meta1.second); @@ -1041,8 +1067,8 @@ TEST_P(AsyncEnd2endTest, ServerTrailingMetadataRpc) { std::unique_ptr<ClientAsyncResponseReader<EchoResponse>> response_reader( stub_->AsyncEcho(&cli_ctx, send_request, cq_.get())); - service_.RequestEcho(&srv_ctx, &recv_request, &response_writer, cq_.get(), - cq_.get(), tag(2)); + service_->RequestEcho(&srv_ctx, &recv_request, &response_writer, cq_.get(), + cq_.get(), tag(2)); Verifier(GetParam().disable_blocking).Expect(2, true).Verify(cq_.get()); EXPECT_EQ(send_request.message(), recv_request.message()); response_writer.SendInitialMetadata(tag(3)); @@ -1104,8 +1130,8 @@ TEST_P(AsyncEnd2endTest, MetadataRpc) { std::unique_ptr<ClientAsyncResponseReader<EchoResponse>> response_reader( stub_->AsyncEcho(&cli_ctx, send_request, cq_.get())); - service_.RequestEcho(&srv_ctx, &recv_request, &response_writer, cq_.get(), - cq_.get(), tag(2)); + service_->RequestEcho(&srv_ctx, &recv_request, &response_writer, cq_.get(), + cq_.get(), tag(2)); Verifier(GetParam().disable_blocking).Expect(2, true).Verify(cq_.get()); EXPECT_EQ(send_request.message(), recv_request.message()); auto client_initial_metadata = srv_ctx.client_metadata(); @@ -1168,8 +1194,8 @@ TEST_P(AsyncEnd2endTest, ServerCheckCancellation) { stub_->AsyncEcho(&cli_ctx, send_request, cq_.get())); srv_ctx.AsyncNotifyWhenDone(tag(5)); - service_.RequestEcho(&srv_ctx, &recv_request, &response_writer, cq_.get(), - cq_.get(), tag(2)); + service_->RequestEcho(&srv_ctx, &recv_request, &response_writer, cq_.get(), + cq_.get(), tag(2)); Verifier(GetParam().disable_blocking).Expect(2, true).Verify(cq_.get()); EXPECT_EQ(send_request.message(), recv_request.message()); @@ -1203,8 +1229,8 @@ TEST_P(AsyncEnd2endTest, ServerCheckDone) { stub_->AsyncEcho(&cli_ctx, send_request, cq_.get())); srv_ctx.AsyncNotifyWhenDone(tag(5)); - service_.RequestEcho(&srv_ctx, &recv_request, &response_writer, cq_.get(), - cq_.get(), tag(2)); + service_->RequestEcho(&srv_ctx, &recv_request, &response_writer, cq_.get(), + cq_.get(), tag(2)); Verifier(GetParam().disable_blocking).Expect(2, true).Verify(cq_.get()); EXPECT_EQ(send_request.message(), recv_request.message()); @@ -1295,8 +1321,8 @@ class AsyncEnd2endServerTryCancelTest : public AsyncEnd2endTest { // On the server, request to be notified of 'RequestStream' calls // and receive the 'RequestStream' call just made by the client srv_ctx.AsyncNotifyWhenDone(tag(11)); - service_.RequestRequestStream(&srv_ctx, &srv_stream, cq_.get(), cq_.get(), - tag(2)); + service_->RequestRequestStream(&srv_ctx, &srv_stream, cq_.get(), cq_.get(), + tag(2)); Verifier(GetParam().disable_blocking).Expect(2, true).Verify(cq_.get()); // Client sends 3 messages (tags 3, 4 and 5) @@ -1426,8 +1452,8 @@ class AsyncEnd2endServerTryCancelTest : public AsyncEnd2endTest { // On the server, request to be notified of 'ResponseStream' calls and // receive the call just made by the client srv_ctx.AsyncNotifyWhenDone(tag(11)); - service_.RequestResponseStream(&srv_ctx, &recv_request, &srv_stream, - cq_.get(), cq_.get(), tag(2)); + service_->RequestResponseStream(&srv_ctx, &recv_request, &srv_stream, + cq_.get(), cq_.get(), tag(2)); Verifier(GetParam().disable_blocking).Expect(2, true).Verify(cq_.get()); EXPECT_EQ(send_request.message(), recv_request.message()); @@ -1562,8 +1588,8 @@ class AsyncEnd2endServerTryCancelTest : public AsyncEnd2endTest { // On the server, request to be notified of the 'BidiStream' call and // receive the call just made by the client srv_ctx.AsyncNotifyWhenDone(tag(11)); - service_.RequestBidiStream(&srv_ctx, &srv_stream, cq_.get(), cq_.get(), - tag(2)); + service_->RequestBidiStream(&srv_ctx, &srv_stream, cq_.get(), cq_.get(), + tag(2)); Verifier(GetParam().disable_blocking).Expect(2, true).Verify(cq_.get()); // Client sends the first and the only message diff --git a/test/cpp/end2end/client_lb_end2end_test.cc b/test/cpp/end2end/client_lb_end2end_test.cc index b588eda84f..54408db600 100644 --- a/test/cpp/end2end/client_lb_end2end_test.cc +++ b/test/cpp/end2end/client_lb_end2end_test.cc @@ -180,16 +180,18 @@ class ClientLbEnd2endTest : public ::testing::Test { std::unique_ptr<Server> server_; MyTestServiceImpl service_; std::unique_ptr<std::thread> thread_; + bool server_ready_ = false; explicit ServerData(const grpc::string& server_host, int port = 0) { port_ = port > 0 ? port : grpc_pick_unused_port_or_die(); gpr_log(GPR_INFO, "starting server on port %d", port_); std::mutex mu; + std::unique_lock<std::mutex> lock(mu); std::condition_variable cond; thread_.reset(new std::thread( std::bind(&ServerData::Start, this, server_host, &mu, &cond))); - std::unique_lock<std::mutex> lock(mu); - cond.wait(lock); + cond.wait(lock, [this] { return server_ready_; }); + server_ready_ = false; gpr_log(GPR_INFO, "server startup complete"); } @@ -203,6 +205,7 @@ class ClientLbEnd2endTest : public ::testing::Test { builder.RegisterService(&service_); server_ = builder.BuildAndStart(); std::lock_guard<std::mutex> lock(*mu); + server_ready_ = true; cond->notify_one(); } diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc index 8bada48a2b..1f4861a7e6 100644 --- a/test/cpp/end2end/end2end_test.cc +++ b/test/cpp/end2end/end2end_test.cc @@ -238,6 +238,18 @@ class End2endTest : public ::testing::TestWithParam<TestScenario> { int port = grpc_pick_unused_port_or_die(); server_address_ << "127.0.0.1:" << port; // Setup server + BuildAndStartServer(processor); + } + + void RestartServer(const std::shared_ptr<AuthMetadataProcessor>& processor) { + if (is_server_started_) { + server_->Shutdown(); + BuildAndStartServer(processor); + } + } + + void BuildAndStartServer( + const std::shared_ptr<AuthMetadataProcessor>& processor) { ServerBuilder builder; ConfigureServerBuilder(&builder); auto server_creds = GetCredentialsProvider()->GetServerCredentials( @@ -685,6 +697,20 @@ TEST_P(End2endTest, MultipleRpcs) { } } +TEST_P(End2endTest, ReconnectChannel) { + if (GetParam().inproc) { + return; + } + ResetStub(); + SendRpc(stub_.get(), 1, false); + RestartServer(std::shared_ptr<AuthMetadataProcessor>()); + // It needs more than kConnectivityCheckIntervalMsec time to reconnect the + // channel. + gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), + gpr_time_from_millis(1600, GPR_TIMESPAN))); + SendRpc(stub_.get(), 1, false); +} + TEST_P(End2endTest, RequestStreamOneRequest) { ResetStub(); EchoRequest request; diff --git a/third_party/gtest.BUILD b/third_party/gtest.BUILD index b70f2c51bc..f38bfd8d45 100644 --- a/third_party/gtest.BUILD +++ b/third_party/gtest.BUILD @@ -2,14 +2,38 @@ cc_library( name = "gtest", srcs = [ "googletest/src/gtest-all.cc", - "googlemock/src/gmock-all.cc" ], - hdrs = glob(["googletest/include/**/*.h", "googletest/src/*.cc", "googletest/src/*.h", "googlemock/include/**/*.h", "googlemock/src/*.cc", "googlemock/src/*.h"]), + hdrs = glob([ + "googletest/include/**/*.h", + "googletest/src/*.cc", + "googletest/src/*.h", + ]), includes = [ "googletest", "googletest/include", - "googlemock", - "googlemock/include", + ], + linkstatic = 1, + visibility = [ + "//visibility:public", + ], +) + +cc_library( + name = "gmock", + srcs = [ + "googlemock/src/gmock-all.cc" + ], + hdrs = glob([ + "googlemock/include/**/*.h", + "googlemock/src/*.cc", + "googlemock/src/*.h" + ]), + includes = [ + "googlemock", + "googlemock/include", + ], + deps = [ + ":gtest", ], linkstatic = 1, visibility = [ diff --git a/tools/codegen/core/gen_hpack_tables.c b/tools/codegen/core/gen_hpack_tables.c index 858ae20c2d..73dfa9fbd6 100644 --- a/tools/codegen/core/gen_hpack_tables.c +++ b/tools/codegen/core/gen_hpack_tables.c @@ -189,7 +189,10 @@ static unsigned state_index(unsigned bitofs, symset syms, unsigned *isnew) { return i; } GPR_ASSERT(nhuffstates != MAXHUFFSTATES); - i = nhuffstates++; + + i = nhuffstates; + nhuffstates++; + huffstates[i].bitofs = bitofs; huffstates[i].syms = syms; huffstates[i].next = nibblelut_empty(); diff --git a/tools/codegen/core/gen_static_metadata.py b/tools/codegen/core/gen_static_metadata.py index e56c627721..6ee8a7cace 100755 --- a/tools/codegen/core/gen_static_metadata.py +++ b/tools/codegen/core/gen_static_metadata.py @@ -132,29 +132,33 @@ CONFIG = [ ('www-authenticate', ''), ] +# Entries marked with is_default=True are ignored when counting +# non-default initial metadata that prevents the chttp2 server from +# sending a Trailers-Only response. METADATA_BATCH_CALLOUTS = [ - ':path', - ':method', - ':status', - ':authority', - ':scheme', - 'te', - 'grpc-message', - 'grpc-status', - 'grpc-payload-bin', - 'grpc-encoding', - 'grpc-accept-encoding', - 'grpc-server-stats-bin', - 'grpc-tags-bin', - 'grpc-trace-bin', - 'content-type', - 'content-encoding', - 'accept-encoding', - 'grpc-internal-encoding-request', - 'grpc-internal-stream-encoding-request', - 'user-agent', - 'host', - 'lb-token', + # (name, is_default) + (':path', True), + (':method', True), + (':status', True), + (':authority', True), + (':scheme', True), + ('te', True), + ('grpc-message', True), + ('grpc-status', True), + ('grpc-payload-bin', True), + ('grpc-encoding', True), + ('grpc-accept-encoding', True), + ('grpc-server-stats-bin', True), + ('grpc-tags-bin', True), + ('grpc-trace-bin', True), + ('content-type', True), + ('content-encoding', True), + ('accept-encoding', True), + ('grpc-internal-encoding-request', True), + ('grpc-internal-stream-encoding-request', True), + ('user-agent', True), + ('host', True), + ('lb-token', True), ] COMPRESSION_ALGORITHMS = [ @@ -235,7 +239,7 @@ all_elems = list() static_userdata = {} # put metadata batch callouts first, to make the check of if a static metadata # string is a callout trivial -for elem in METADATA_BATCH_CALLOUTS: +for elem, _ in METADATA_BATCH_CALLOUTS: if elem not in all_strs: all_strs.append(elem) for elem in CONFIG: @@ -372,7 +376,7 @@ def slice_def(i): # validate configuration -for elem in METADATA_BATCH_CALLOUTS: +for elem, _ in METADATA_BATCH_CALLOUTS: assert elem in all_strs print >> H, '#define GRPC_STATIC_MDSTR_COUNT %d' % len(all_strs) @@ -540,7 +544,7 @@ for a, b in all_elems: print >> C, '};' print >> H, 'typedef enum {' -for elem in METADATA_BATCH_CALLOUTS: +for elem, _ in METADATA_BATCH_CALLOUTS: print >> H, ' %s,' % mangle(elem, 'batch').upper() print >> H, ' GRPC_BATCH_CALLOUTS_COUNT' print >> H, '} grpc_metadata_batch_callouts_index;' @@ -548,7 +552,7 @@ print >> H print >> H, 'typedef union {' print >> H, ' struct grpc_linked_mdelem *array[GRPC_BATCH_CALLOUTS_COUNT];' print >> H, ' struct {' -for elem in METADATA_BATCH_CALLOUTS: +for elem, _ in METADATA_BATCH_CALLOUTS: print >> H, ' struct grpc_linked_mdelem *%s;' % mangle(elem, '').lower() print >> H, ' } named;' print >> H, '} grpc_metadata_batch_callouts;' @@ -556,6 +560,14 @@ print >> H print >> H, '#define GRPC_BATCH_INDEX_OF(slice) \\' print >> H, ' (GRPC_IS_STATIC_METADATA_STRING((slice)) ? (grpc_metadata_batch_callouts_index)GPR_CLAMP(GRPC_STATIC_METADATA_INDEX((slice)), 0, GRPC_BATCH_CALLOUTS_COUNT) : GRPC_BATCH_CALLOUTS_COUNT)' print >> H +print >> H, ('extern bool grpc_static_callout_is_default[' + 'GRPC_BATCH_CALLOUTS_COUNT];') +print >> H +print >> C, 'bool grpc_static_callout_is_default[GRPC_BATCH_CALLOUTS_COUNT] = {' +for elem, is_default in METADATA_BATCH_CALLOUTS: + print >> C, ' %s, // %s' % (str(is_default).lower(), elem) +print >> C, '};' +print >> C print >> H, 'extern const uint8_t grpc_static_accept_encoding_metadata[%d];' % ( 1 << len(COMPRESSION_ALGORITHMS)) diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index c2e369afe5..49919415bd 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -956,8 +956,6 @@ src/core/lib/iomgr/endpoint_pair.h \ src/core/lib/iomgr/error.h \ src/core/lib/iomgr/error_internal.h \ src/core/lib/iomgr/ev_epoll1_linux.h \ -src/core/lib/iomgr/ev_epoll_limited_pollers_linux.h \ -src/core/lib/iomgr/ev_epoll_thread_pool_linux.h \ src/core/lib/iomgr/ev_epollex_linux.h \ src/core/lib/iomgr/ev_epollsig_linux.h \ src/core/lib/iomgr/ev_poll_posix.h \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index 94bbe3185a..c2daf900fb 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -1108,10 +1108,6 @@ src/core/lib/iomgr/error.h \ src/core/lib/iomgr/error_internal.h \ src/core/lib/iomgr/ev_epoll1_linux.c \ src/core/lib/iomgr/ev_epoll1_linux.h \ -src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c \ -src/core/lib/iomgr/ev_epoll_limited_pollers_linux.h \ -src/core/lib/iomgr/ev_epoll_thread_pool_linux.c \ -src/core/lib/iomgr/ev_epoll_thread_pool_linux.h \ src/core/lib/iomgr/ev_epollex_linux.c \ src/core/lib/iomgr/ev_epollex_linux.h \ src/core/lib/iomgr/ev_epollsig_linux.c \ diff --git a/tools/gce/create_linux_performance_worker.sh b/tools/gce/create_linux_performance_worker.sh index 9b85f7b437..7f53732c05 100755 --- a/tools/gce/create_linux_performance_worker.sh +++ b/tools/gce/create_linux_performance_worker.sh @@ -34,7 +34,7 @@ gcloud compute instances create $INSTANCE_NAME \ --zone "$ZONE" \ --machine-type $MACHINE_TYPE \ --image-project ubuntu-os-cloud \ - --image-family ubuntu-1610 \ + --image-family ubuntu-1704 \ --boot-disk-size 300 \ --scopes https://www.googleapis.com/auth/bigquery diff --git a/tools/internal_ci/helper_scripts/prepare_build_macos_rc b/tools/internal_ci/helper_scripts/prepare_build_macos_rc index 6da0e1eeff..f7fbec93ff 100644 --- a/tools/internal_ci/helper_scripts/prepare_build_macos_rc +++ b/tools/internal_ci/helper_scripts/prepare_build_macos_rc @@ -54,8 +54,7 @@ pod repo update # needed by python brew install coreutils # we need grealpath pip install virtualenv --user python pip install -U six tox setuptools --user python -export PYTHONPATH=/Library/Python/2.7/site-packages -source ~/.bashrc +export PYTHONPATH=/Library/Python/3.4/site-packages # python 3.4 wget -q https://www.python.org/ftp/python/3.4.4/python-3.4.4-macosx10.6.pkg diff --git a/tools/internal_ci/macos/grpc_basictests_dbg.cfg b/tools/internal_ci/macos/grpc_basictests_dbg.cfg index 53bda1ff0a..f058f0c7e4 100644 --- a/tools/internal_ci/macos/grpc_basictests_dbg.cfg +++ b/tools/internal_ci/macos/grpc_basictests_dbg.cfg @@ -27,5 +27,5 @@ action { env_vars { key: "RUN_TESTS_FLAGS" - value: "-f basictests macos dbg --internal_ci -j 1 --inner_jobs 4 --bq_result_table aggregate_results" + value: "-f basictests macos dbg --internal_ci -j 1 --inner_jobs 4" } diff --git a/tools/internal_ci/macos/grpc_basictests_opt.cfg b/tools/internal_ci/macos/grpc_basictests_opt.cfg index d359eb601a..5048baaf48 100644 --- a/tools/internal_ci/macos/grpc_basictests_opt.cfg +++ b/tools/internal_ci/macos/grpc_basictests_opt.cfg @@ -27,5 +27,5 @@ action { env_vars { key: "RUN_TESTS_FLAGS" - value: "-f basictests macos opt --internal_ci -j 1 --inner_jobs 4 --bq_result_table aggregate_results" + value: "-f basictests macos opt --internal_ci -j 1 --inner_jobs 4" } diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index 7b8cc27a60..eefd80e46f 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -541,6 +541,23 @@ "gpr", "gpr_test_util", "grpc", + "transport_security_test_lib" + ], + "headers": [], + "is_filegroup": false, + "language": "c", + "name": "fake_transport_security_test", + "src": [ + "test/core/tsi/fake_transport_security_test.c" + ], + "third_party": false, + "type": "target" + }, + { + "deps": [ + "gpr", + "gpr_test_util", + "grpc", "grpc_test_util" ], "headers": [], @@ -2168,6 +2185,23 @@ "gpr", "gpr_test_util", "grpc", + "transport_security_test_lib" + ], + "headers": [], + "is_filegroup": false, + "language": "c", + "name": "ssl_transport_security_test", + "src": [ + "test/core/tsi/ssl_transport_security_test.c" + ], + "third_party": false, + "type": "target" + }, + { + "deps": [ + "gpr", + "gpr_test_util", + "grpc", "grpc_test_util" ], "headers": [], @@ -7836,8 +7870,6 @@ "src/core/lib/iomgr/endpoint_pair_windows.c", "src/core/lib/iomgr/error.c", "src/core/lib/iomgr/ev_epoll1_linux.c", - "src/core/lib/iomgr/ev_epoll_limited_pollers_linux.c", - "src/core/lib/iomgr/ev_epoll_thread_pool_linux.c", "src/core/lib/iomgr/ev_epollex_linux.c", "src/core/lib/iomgr/ev_epollsig_linux.c", "src/core/lib/iomgr/ev_poll_posix.c", @@ -7989,8 +8021,6 @@ "src/core/lib/iomgr/error.h", "src/core/lib/iomgr/error_internal.h", "src/core/lib/iomgr/ev_epoll1_linux.h", - "src/core/lib/iomgr/ev_epoll_limited_pollers_linux.h", - "src/core/lib/iomgr/ev_epoll_thread_pool_linux.h", "src/core/lib/iomgr/ev_epollex_linux.h", "src/core/lib/iomgr/ev_epollsig_linux.h", "src/core/lib/iomgr/ev_poll_posix.h", @@ -8122,8 +8152,6 @@ "src/core/lib/iomgr/error.h", "src/core/lib/iomgr/error_internal.h", "src/core/lib/iomgr/ev_epoll1_linux.h", - "src/core/lib/iomgr/ev_epoll_limited_pollers_linux.h", - "src/core/lib/iomgr/ev_epoll_thread_pool_linux.h", "src/core/lib/iomgr/ev_epollex_linux.h", "src/core/lib/iomgr/ev_epollsig_linux.h", "src/core/lib/iomgr/ev_poll_posix.h", @@ -9111,6 +9139,23 @@ }, { "deps": [ + "grpc" + ], + "headers": [ + "test/core/tsi/transport_security_test_lib.h" + ], + "is_filegroup": true, + "language": "c", + "name": "transport_security_test_lib", + "src": [ + "test/core/tsi/transport_security_test_lib.c", + "test/core/tsi/transport_security_test_lib.h" + ], + "third_party": false, + "type": "filegroup" + }, + { + "deps": [ "gpr", "grpc_base", "grpc_trace", diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index 4368a5768e..7c90861372 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -652,6 +652,26 @@ ], "cpu_cost": 1.0, "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": false, + "language": "c", + "name": "fake_transport_security_test", + "platforms": [ + "linux", + "mac", + "posix" + ] + }, + { + "args": [], + "ci_platforms": [ + "linux", + "mac", + "posix" + ], + "cpu_cost": 1.0, + "exclude_configs": [], "exclude_iomgrs": [ "uv" ], @@ -2278,6 +2298,26 @@ "ci_platforms": [ "linux", "mac", + "posix" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": false, + "language": "c", + "name": "ssl_transport_security_test", + "platforms": [ + "linux", + "mac", + "posix" + ] + }, + { + "args": [], + "ci_platforms": [ + "linux", + "mac", "posix", "windows" ], diff --git a/tools/run_tests/run_performance_tests.py b/tools/run_tests/run_performance_tests.py index 0db5e4ef83..9b20fae78f 100755 --- a/tools/run_tests/run_performance_tests.py +++ b/tools/run_tests/run_performance_tests.py @@ -281,18 +281,18 @@ def create_qpsworkers(languages, worker_hosts, perf_cmd=None): for worker_idx, worker in enumerate(workers)] -def perf_report_processor_job(worker_host, perf_base_name, output_filename): +def perf_report_processor_job(worker_host, perf_base_name, output_filename, flame_graph_reports): print('Creating perf report collection job for %s' % worker_host) cmd = '' if worker_host != 'localhost': user_at_host = "%s@%s" % (_REMOTE_HOST_USERNAME, worker_host) cmd = "USER_AT_HOST=%s OUTPUT_FILENAME=%s OUTPUT_DIR=%s PERF_BASE_NAME=%s\ tools/run_tests/performance/process_remote_perf_flamegraphs.sh" \ - % (user_at_host, output_filename, args.flame_graph_reports, perf_base_name) + % (user_at_host, output_filename, flame_graph_reports, perf_base_name) else: cmd = "OUTPUT_FILENAME=%s OUTPUT_DIR=%s PERF_BASE_NAME=%s\ tools/run_tests/performance/process_local_perf_flamegraphs.sh" \ - % (output_filename, args.flame_graph_reports, perf_base_name) + % (output_filename, flame_graph_reports, perf_base_name) return jobset.JobSpec(cmdline=cmd, timeout_seconds=3*60, @@ -332,7 +332,7 @@ def create_scenarios(languages, workers_by_lang, remote_host=None, regex='.*', for language in languages: for scenario_json in language.scenarios(): - if re.search(args.regex, scenario_json['name']): + if re.search(regex, scenario_json['name']): categories = scenario_json.get('CATEGORIES', ['scalable', 'smoketest']) if category in categories or category == 'all': workers = workers_by_lang[str(language)][:] @@ -376,7 +376,7 @@ def create_scenarios(languages, workers_by_lang, remote_host=None, regex='.*', return scenarios -def finish_qps_workers(jobs): +def finish_qps_workers(jobs, qpsworker_jobs): """Waits for given jobs to finish and eventually kills them.""" retries = 0 num_killed = 0 @@ -399,10 +399,10 @@ profile_output_files = [] # Collect perf text reports and flamegraphs if perf_cmd was used # Note the base names of perf text reports are used when creating and processing # perf data. The scenario name uniqifies the output name in the final -# perf reports directory. +# perf reports directory. # Alos, the perf profiles need to be fetched and processed after each scenario # in order to avoid clobbering the output files. -def run_collect_perf_profile_jobs(hosts_and_base_names, scenario_name): +def run_collect_perf_profile_jobs(hosts_and_base_names, scenario_name, flame_graph_reports): perf_report_jobs = [] global profile_output_files for host_and_port in hosts_and_base_names: @@ -411,181 +411,184 @@ def run_collect_perf_profile_jobs(hosts_and_base_names, scenario_name): # from the base filename, create .svg output filename host = host_and_port.split(':')[0] profile_output_files.append('%s.svg' % output_filename) - perf_report_jobs.append(perf_report_processor_job(host, perf_base_name, output_filename)) + perf_report_jobs.append(perf_report_processor_job(host, perf_base_name, output_filename, flame_graph_reports)) jobset.message('START', 'Collecting perf reports from qps workers', do_newline=True) failures, _ = jobset.run(perf_report_jobs, newline_on_success=True, maxjobs=1, clear_alarms=False) jobset.message('END', 'Collecting perf reports from qps workers', do_newline=True) return failures +def main(): + argp = argparse.ArgumentParser(description='Run performance tests.') + argp.add_argument('-l', '--language', + choices=['all'] + sorted(scenario_config.LANGUAGES.keys()), + nargs='+', + required=True, + help='Languages to benchmark.') + argp.add_argument('--remote_driver_host', + default=None, + help='Run QPS driver on given host. By default, QPS driver is run locally.') + argp.add_argument('--remote_worker_host', + nargs='+', + default=[], + help='Worker hosts where to start QPS workers.') + argp.add_argument('--dry_run', + default=False, + action='store_const', + const=True, + help='Just list scenarios to be run, but don\'t run them.') + argp.add_argument('-r', '--regex', default='.*', type=str, + help='Regex to select scenarios to run.') + argp.add_argument('--bq_result_table', default=None, type=str, + help='Bigquery "dataset.table" to upload results to.') + argp.add_argument('--category', + choices=['smoketest','all','scalable','sweep'], + default='all', + help='Select a category of tests to run.') + argp.add_argument('--netperf', + default=False, + action='store_const', + const=True, + help='Run netperf benchmark as one of the scenarios.') + argp.add_argument('--server_cpu_load', + default=0, type=int, + help='Select a targeted server cpu load to run. 0 means ignore this flag') + argp.add_argument('-x', '--xml_report', default='report.xml', type=str, + help='Name of XML report file to generate.') + argp.add_argument('--perf_args', + help=('Example usage: "--perf_args=record -F 99 -g". ' + 'Wrap QPS workers in a perf command ' + 'with the arguments to perf specified here. ' + '".svg" flame graph profiles will be ' + 'created for each Qps Worker on each scenario. ' + 'Files will output to "<repo_root>/<args.flame_graph_reports>" ' + 'directory. Output files from running the worker ' + 'under perf are saved in the repo root where its ran. ' + 'Note that the perf "-g" flag is necessary for ' + 'flame graphs generation to work (assuming the binary ' + 'being profiled uses frame pointers, check out ' + '"--call-graph dwarf" option using libunwind otherwise.) ' + 'Also note that the entire "--perf_args=<arg(s)>" must ' + 'be wrapped in quotes as in the example usage. ' + 'If the "--perg_args" is unspecified, "perf" will ' + 'not be used at all. ' + 'See http://www.brendangregg.com/perf.html ' + 'for more general perf examples.')) + argp.add_argument('--skip_generate_flamegraphs', + default=False, + action='store_const', + const=True, + help=('Turn flame graph generation off. ' + 'May be useful if "perf_args" arguments do not make sense for ' + 'generating flamegraphs (e.g., "--perf_args=stat ...")')) + argp.add_argument('-f', '--flame_graph_reports', default='perf_reports', type=str, + help='Name of directory to output flame graph profiles to, if any are created.') + + args = argp.parse_args() + + languages = set(scenario_config.LANGUAGES[l] + for l in itertools.chain.from_iterable( + six.iterkeys(scenario_config.LANGUAGES) if x == 'all' + else [x] for x in args.language)) + + + # Put together set of remote hosts where to run and build + remote_hosts = set() + if args.remote_worker_host: + for host in args.remote_worker_host: + remote_hosts.add(host) + if args.remote_driver_host: + remote_hosts.add(args.remote_driver_host) + + if not args.dry_run: + if remote_hosts: + archive_repo(languages=[str(l) for l in languages]) + prepare_remote_hosts(remote_hosts, prepare_local=True) + else: + prepare_remote_hosts([], prepare_local=True) + + build_local = False + if not args.remote_driver_host: + build_local = True + if not args.dry_run: + build_on_remote_hosts(remote_hosts, languages=[str(l) for l in languages], build_local=build_local) + + perf_cmd = None + if args.perf_args: + print('Running workers under perf profiler') + # Expect /usr/bin/perf to be installed here, as is usual + perf_cmd = ['/usr/bin/perf'] + perf_cmd.extend(re.split('\s+', args.perf_args)) + + qpsworker_jobs = create_qpsworkers(languages, args.remote_worker_host, perf_cmd=perf_cmd) + + # get list of worker addresses for each language. + workers_by_lang = dict([(str(language), []) for language in languages]) + for job in qpsworker_jobs: + workers_by_lang[str(job.language)].append(job) + + scenarios = create_scenarios(languages, + workers_by_lang=workers_by_lang, + remote_host=args.remote_driver_host, + regex=args.regex, + category=args.category, + bq_result_table=args.bq_result_table, + netperf=args.netperf, + netperf_hosts=args.remote_worker_host, + server_cpu_load=args.server_cpu_load) + + if not scenarios: + raise Exception('No scenarios to run') + + total_scenario_failures = 0 + qps_workers_killed = 0 + merged_resultset = {} + perf_report_failures = 0 + + for scenario in scenarios: + if args.dry_run: + print(scenario.name) + else: + scenario_failures = 0 + try: + for worker in scenario.workers: + worker.start() + jobs = [scenario.jobspec] + if scenario.workers: + jobs.append(create_quit_jobspec(scenario.workers, remote_host=args.remote_driver_host)) + scenario_failures, resultset = jobset.run(jobs, newline_on_success=True, maxjobs=1, clear_alarms=False) + total_scenario_failures += scenario_failures + merged_resultset = dict(itertools.chain(six.iteritems(merged_resultset), + six.iteritems(resultset))) + finally: + # Consider qps workers that need to be killed as failures + qps_workers_killed += finish_qps_workers(scenario.workers, qpsworker_jobs) + + if perf_cmd and scenario_failures == 0 and not args.skip_generate_flamegraphs: + workers_and_base_names = {} + for worker in scenario.workers: + if not worker.perf_file_base_name: + raise Exception('using perf buf perf report filename is unspecified') + workers_and_base_names[worker.host_and_port] = worker.perf_file_base_name + perf_report_failures += run_collect_perf_profile_jobs(workers_and_base_names, scenario.name, args.flame_graph_reports) + + + # Still write the index.html even if some scenarios failed. + # 'profile_output_files' will only have names for scenarios that passed + if perf_cmd and not args.skip_generate_flamegraphs: + # write the index fil to the output dir, with all profiles from all scenarios/workers + report_utils.render_perf_profiling_results('%s/index.html' % args.flame_graph_reports, profile_output_files) + + report_utils.render_junit_xml_report(merged_resultset, args.xml_report, + suite_name='benchmarks') + + if total_scenario_failures > 0 or qps_workers_killed > 0: + print('%s scenarios failed and %s qps worker jobs killed' % (total_scenario_failures, qps_workers_killed)) + sys.exit(1) -argp = argparse.ArgumentParser(description='Run performance tests.') -argp.add_argument('-l', '--language', - choices=['all'] + sorted(scenario_config.LANGUAGES.keys()), - nargs='+', - required=True, - help='Languages to benchmark.') -argp.add_argument('--remote_driver_host', - default=None, - help='Run QPS driver on given host. By default, QPS driver is run locally.') -argp.add_argument('--remote_worker_host', - nargs='+', - default=[], - help='Worker hosts where to start QPS workers.') -argp.add_argument('--dry_run', - default=False, - action='store_const', - const=True, - help='Just list scenarios to be run, but don\'t run them.') -argp.add_argument('-r', '--regex', default='.*', type=str, - help='Regex to select scenarios to run.') -argp.add_argument('--bq_result_table', default=None, type=str, - help='Bigquery "dataset.table" to upload results to.') -argp.add_argument('--category', - choices=['smoketest','all','scalable','sweep'], - default='all', - help='Select a category of tests to run.') -argp.add_argument('--netperf', - default=False, - action='store_const', - const=True, - help='Run netperf benchmark as one of the scenarios.') -argp.add_argument('--server_cpu_load', - default=0, type=int, - help='Select a targeted server cpu load to run. 0 means ignore this flag') -argp.add_argument('-x', '--xml_report', default='report.xml', type=str, - help='Name of XML report file to generate.') -argp.add_argument('--perf_args', - help=('Example usage: "--perf_args=record -F 99 -g". ' - 'Wrap QPS workers in a perf command ' - 'with the arguments to perf specified here. ' - '".svg" flame graph profiles will be ' - 'created for each Qps Worker on each scenario. ' - 'Files will output to "<repo_root>/<args.flame_graph_reports>" ' - 'directory. Output files from running the worker ' - 'under perf are saved in the repo root where its ran. ' - 'Note that the perf "-g" flag is necessary for ' - 'flame graphs generation to work (assuming the binary ' - 'being profiled uses frame pointers, check out ' - '"--call-graph dwarf" option using libunwind otherwise.) ' - 'Also note that the entire "--perf_args=<arg(s)>" must ' - 'be wrapped in quotes as in the example usage. ' - 'If the "--perg_args" is unspecified, "perf" will ' - 'not be used at all. ' - 'See http://www.brendangregg.com/perf.html ' - 'for more general perf examples.')) -argp.add_argument('--skip_generate_flamegraphs', - default=False, - action='store_const', - const=True, - help=('Turn flame graph generation off. ' - 'May be useful if "perf_args" arguments do not make sense for ' - 'generating flamegraphs (e.g., "--perf_args=stat ...")')) -argp.add_argument('-f', '--flame_graph_reports', default='perf_reports', type=str, - help='Name of directory to output flame graph profiles to, if any are created.') - -args = argp.parse_args() - -languages = set(scenario_config.LANGUAGES[l] - for l in itertools.chain.from_iterable( - six.iterkeys(scenario_config.LANGUAGES) if x == 'all' - else [x] for x in args.language)) - - -# Put together set of remote hosts where to run and build -remote_hosts = set() -if args.remote_worker_host: - for host in args.remote_worker_host: - remote_hosts.add(host) -if args.remote_driver_host: - remote_hosts.add(args.remote_driver_host) - -if not args.dry_run: - if remote_hosts: - archive_repo(languages=[str(l) for l in languages]) - prepare_remote_hosts(remote_hosts, prepare_local=True) - else: - prepare_remote_hosts([], prepare_local=True) - -build_local = False -if not args.remote_driver_host: - build_local = True -if not args.dry_run: - build_on_remote_hosts(remote_hosts, languages=[str(l) for l in languages], build_local=build_local) - -perf_cmd = None -if args.perf_args: - print('Running workers under perf profiler') - # Expect /usr/bin/perf to be installed here, as is usual - perf_cmd = ['/usr/bin/perf'] - perf_cmd.extend(re.split('\s+', args.perf_args)) - -qpsworker_jobs = create_qpsworkers(languages, args.remote_worker_host, perf_cmd=perf_cmd) - -# get list of worker addresses for each language. -workers_by_lang = dict([(str(language), []) for language in languages]) -for job in qpsworker_jobs: - workers_by_lang[str(job.language)].append(job) - -scenarios = create_scenarios(languages, - workers_by_lang=workers_by_lang, - remote_host=args.remote_driver_host, - regex=args.regex, - category=args.category, - bq_result_table=args.bq_result_table, - netperf=args.netperf, - netperf_hosts=args.remote_worker_host, - server_cpu_load=args.server_cpu_load) - -if not scenarios: - raise Exception('No scenarios to run') - -total_scenario_failures = 0 -qps_workers_killed = 0 -merged_resultset = {} -perf_report_failures = 0 - -for scenario in scenarios: - if args.dry_run: - print(scenario.name) - else: - scenario_failures = 0 - try: - for worker in scenario.workers: - worker.start() - jobs = [scenario.jobspec] - if scenario.workers: - jobs.append(create_quit_jobspec(scenario.workers, remote_host=args.remote_driver_host)) - scenario_failures, resultset = jobset.run(jobs, newline_on_success=True, maxjobs=1, clear_alarms=False) - total_scenario_failures += scenario_failures - merged_resultset = dict(itertools.chain(six.iteritems(merged_resultset), - six.iteritems(resultset))) - finally: - # Consider qps workers that need to be killed as failures - qps_workers_killed += finish_qps_workers(scenario.workers) - - if perf_cmd and scenario_failures == 0 and not args.skip_generate_flamegraphs: - workers_and_base_names = {} - for worker in scenario.workers: - if not worker.perf_file_base_name: - raise Exception('using perf buf perf report filename is unspecified') - workers_and_base_names[worker.host_and_port] = worker.perf_file_base_name - perf_report_failures += run_collect_perf_profile_jobs(workers_and_base_names, scenario.name) - - -# Still write the index.html even if some scenarios failed. -# 'profile_output_files' will only have names for scenarios that passed -if perf_cmd and not args.skip_generate_flamegraphs: - # write the index fil to the output dir, with all profiles from all scenarios/workers - report_utils.render_perf_profiling_results('%s/index.html' % args.flame_graph_reports, profile_output_files) - -report_utils.render_junit_xml_report(merged_resultset, args.xml_report, - suite_name='benchmarks') - -if total_scenario_failures > 0 or qps_workers_killed > 0: - print('%s scenarios failed and %s qps worker jobs killed' % (total_scenario_failures, qps_workers_killed)) - sys.exit(1) - -if perf_report_failures > 0: - print('%s perf profile collection jobs failed' % perf_report_failures) - sys.exit(1) + if perf_report_failures > 0: + print('%s perf profile collection jobs failed' % perf_report_failures) + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/tools/ubsan_suppressions.txt b/tools/ubsan_suppressions.txt index 2dcfeea9af..6ccc306cf0 100644 --- a/tools/ubsan_suppressions.txt +++ b/tools/ubsan_suppressions.txt @@ -5,6 +5,7 @@ nonnull-attribute:rsa_blinding_get nonnull-attribute:ssl_copy_key_material alignment:CRYPTO_cbc128_encrypt alignment:CRYPTO_gcm128_encrypt +alignment:poly1305_block_copy nonnull-attribute:google::protobuf::* alignment:google::protobuf::* nonnull-attribute:_tr_stored_block diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj b/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj deleted file mode 100644 index 01eb47335c..0000000000 --- a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj +++ /dev/null @@ -1,298 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{47C2CB41-4E9F-58B6-F606-F6FAED5D00ED}</ProjectGuid> - <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected> - <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration"> - <PlatformToolset>v100</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration"> - <PlatformToolset>v110</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration"> - <PlatformToolset>v120</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration"> - <PlatformToolset>v140</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration"> - <ConfigurationType>StaticLibrary</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration"> - <ConfigurationType>StaticLibrary</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(SolutionDir)\..\vsprojects\global.props" /> - <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup Condition="'$(Configuration)'=='Debug'"> - <TargetName>end2end_nosec_tests</TargetName> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)'=='Release'"> - <TargetName>end2end_nosec_tests</TargetName> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <PrecompiledHeader>NotUsing</PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <SDLCheck>true</SDLCheck> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <TreatWarningAsError>true</TreatWarningAsError> - <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> - <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> - <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> - </Link> - </ItemDefinitionGroup> - - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <ClCompile> - <PrecompiledHeader>NotUsing</PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <SDLCheck>true</SDLCheck> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <TreatWarningAsError>true</TreatWarningAsError> - <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> - <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> - <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> - </Link> - </ItemDefinitionGroup> - - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <PrecompiledHeader>NotUsing</PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <Optimization>MaxSpeed</Optimization> - <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <SDLCheck>true</SDLCheck> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <TreatWarningAsError>true</TreatWarningAsError> - <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> - <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> - <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - </Link> - </ItemDefinitionGroup> - - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <ClCompile> - <PrecompiledHeader>NotUsing</PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <Optimization>MaxSpeed</Optimization> - <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <SDLCheck>true</SDLCheck> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <TreatWarningAsError>true</TreatWarningAsError> - <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> - <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> - <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - </Link> - </ItemDefinitionGroup> - - <ItemGroup> - <ClInclude Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_test_helpers.h" /> - <ClInclude Include="$(SolutionDir)\..\test\core\end2end\end2end_tests.h" /> - </ItemGroup> - <ItemGroup> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\end2end_nosec_tests.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\end2end_test_utils.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\authority_not_supported.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\bad_hostname.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\bad_ping.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\binary_metadata.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_accept.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_client_done.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_invoke.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_round_trip.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_before_invoke.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_in_a_vacuum.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_with_status.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\compressed_payload.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\connectivity.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\default_host.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\disappearing_server.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\empty_batch.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_call_init_fails.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_causes_close.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_latency.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\graceful_server_shutdown.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\high_initial_seqno.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\hpack_size.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\idempotent_request.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\invoke_large_request.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\keepalive_timeout.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\load_reporting_hook.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_concurrent_streams.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_connection_age.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_connection_idle.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_message_length.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\negative_deadline.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\network_status_change.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_logging.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_op.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\payload.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\ping.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\ping_pong_streaming.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\proxy_auth.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\registered_call.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\request_with_flags.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\request_with_payload.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\resource_quota_server.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\server_finishes_request.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_calls.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_tags.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_cacheable_request.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_delayed_request.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_metadata.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_request.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_compressed_payload.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_payload.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_ping_pong_streaming.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\streaming_error_response.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\trailing_metadata.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\workaround_cronet_compression.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\write_buffering.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\write_buffering_at_end.c"> - </ClCompile> - </ItemGroup> - <ItemGroup> - <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util_unsecure\grpc_test_util_unsecure.vcxproj"> - <Project>{0A7E7F92-FDEA-40F1-A9EC-3BA484F98BBF}</Project> - </ProjectReference> - <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_unsecure\grpc_unsecure.vcxproj"> - <Project>{46CEDFFF-9692-456A-AA24-38B5D6BCF4C5}</Project> - </ProjectReference> - <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj"> - <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project> - </ProjectReference> - <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj"> - <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project> - </ProjectReference> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> - <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> - <PropertyGroup> - <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> - </PropertyGroup> - </Target> -</Project> - diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj.filters b/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj.filters deleted file mode 100644 index fc066c56c4..0000000000 --- a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj.filters +++ /dev/null @@ -1,209 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\end2end_nosec_tests.c"> - <Filter>test\core\end2end</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\end2end_test_utils.c"> - <Filter>test\core\end2end</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\authority_not_supported.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\bad_hostname.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\bad_ping.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\binary_metadata.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_accept.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_client_done.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_invoke.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_round_trip.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_before_invoke.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_in_a_vacuum.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_with_status.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\compressed_payload.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\connectivity.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\default_host.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\disappearing_server.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\empty_batch.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_call_init_fails.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_causes_close.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_latency.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\graceful_server_shutdown.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\high_initial_seqno.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\hpack_size.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\idempotent_request.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\invoke_large_request.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\keepalive_timeout.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\load_reporting_hook.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_concurrent_streams.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_connection_age.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_connection_idle.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_message_length.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\negative_deadline.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\network_status_change.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_logging.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_op.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\payload.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\ping.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\ping_pong_streaming.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\proxy_auth.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\registered_call.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\request_with_flags.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\request_with_payload.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\resource_quota_server.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\server_finishes_request.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_calls.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_tags.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_cacheable_request.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_delayed_request.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_metadata.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_request.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_compressed_payload.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_payload.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_ping_pong_streaming.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\streaming_error_response.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\trailing_metadata.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\workaround_cronet_compression.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\write_buffering.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\write_buffering_at_end.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - </ItemGroup> - <ItemGroup> - <ClInclude Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_test_helpers.h"> - <Filter>test\core\end2end\tests</Filter> - </ClInclude> - <ClInclude Include="$(SolutionDir)\..\test\core\end2end\end2end_tests.h"> - <Filter>test\core\end2end</Filter> - </ClInclude> - </ItemGroup> - - <ItemGroup> - <Filter Include="test"> - <UniqueIdentifier>{95f38e16-d400-0c90-0b5f-7670d392e71f}</UniqueIdentifier> - </Filter> - <Filter Include="test\core"> - <UniqueIdentifier>{d58c2741-0940-80bc-ac98-9fe98ce69c36}</UniqueIdentifier> - </Filter> - <Filter Include="test\core\end2end"> - <UniqueIdentifier>{f4baa425-3c27-3025-ecbc-a83c877ac226}</UniqueIdentifier> - </Filter> - <Filter Include="test\core\end2end\tests"> - <UniqueIdentifier>{a828bc7b-f20c-0caa-e8ef-ca62e0bed371}</UniqueIdentifier> - </Filter> - </ItemGroup> -</Project> - diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj b/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj deleted file mode 100644 index 6ea05a496e..0000000000 --- a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj +++ /dev/null @@ -1,300 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{1F1F9084-2A93-B80E-364F-5754894AFAB4}</ProjectGuid> - <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected> - <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration"> - <PlatformToolset>v100</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration"> - <PlatformToolset>v110</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration"> - <PlatformToolset>v120</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration"> - <PlatformToolset>v140</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration"> - <ConfigurationType>StaticLibrary</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration"> - <ConfigurationType>StaticLibrary</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(SolutionDir)\..\vsprojects\global.props" /> - <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup Condition="'$(Configuration)'=='Debug'"> - <TargetName>end2end_tests</TargetName> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)'=='Release'"> - <TargetName>end2end_tests</TargetName> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <PrecompiledHeader>NotUsing</PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <SDLCheck>true</SDLCheck> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <TreatWarningAsError>true</TreatWarningAsError> - <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> - <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> - <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> - </Link> - </ItemDefinitionGroup> - - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <ClCompile> - <PrecompiledHeader>NotUsing</PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <SDLCheck>true</SDLCheck> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <TreatWarningAsError>true</TreatWarningAsError> - <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> - <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> - <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> - </Link> - </ItemDefinitionGroup> - - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <PrecompiledHeader>NotUsing</PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <Optimization>MaxSpeed</Optimization> - <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <SDLCheck>true</SDLCheck> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <TreatWarningAsError>true</TreatWarningAsError> - <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> - <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> - <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - </Link> - </ItemDefinitionGroup> - - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <ClCompile> - <PrecompiledHeader>NotUsing</PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <Optimization>MaxSpeed</Optimization> - <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <SDLCheck>true</SDLCheck> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <TreatWarningAsError>true</TreatWarningAsError> - <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> - <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> - <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - </Link> - </ItemDefinitionGroup> - - <ItemGroup> - <ClInclude Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_test_helpers.h" /> - <ClInclude Include="$(SolutionDir)\..\test\core\end2end\end2end_tests.h" /> - </ItemGroup> - <ItemGroup> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\end2end_tests.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\end2end_test_utils.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\authority_not_supported.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\bad_hostname.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\bad_ping.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\binary_metadata.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\call_creds.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_accept.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_client_done.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_invoke.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_round_trip.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_before_invoke.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_in_a_vacuum.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_with_status.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\compressed_payload.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\connectivity.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\default_host.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\disappearing_server.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\empty_batch.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_call_init_fails.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_causes_close.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_latency.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\graceful_server_shutdown.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\high_initial_seqno.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\hpack_size.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\idempotent_request.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\invoke_large_request.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\keepalive_timeout.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\load_reporting_hook.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_concurrent_streams.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_connection_age.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_connection_idle.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_message_length.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\negative_deadline.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\network_status_change.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_logging.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_op.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\payload.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\ping.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\ping_pong_streaming.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\proxy_auth.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\registered_call.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\request_with_flags.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\request_with_payload.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\resource_quota_server.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\server_finishes_request.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_calls.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_tags.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_cacheable_request.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_delayed_request.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_metadata.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_request.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_compressed_payload.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_payload.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_ping_pong_streaming.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\streaming_error_response.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\trailing_metadata.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\workaround_cronet_compression.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\write_buffering.c"> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\write_buffering_at_end.c"> - </ClCompile> - </ItemGroup> - <ItemGroup> - <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj"> - <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project> - </ProjectReference> - <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj"> - <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project> - </ProjectReference> - <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj"> - <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project> - </ProjectReference> - <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj"> - <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project> - </ProjectReference> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> - <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> - <PropertyGroup> - <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> - </PropertyGroup> - </Target> -</Project> - diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj.filters b/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj.filters deleted file mode 100644 index 6ee37c19b7..0000000000 --- a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj.filters +++ /dev/null @@ -1,212 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\end2end_tests.c"> - <Filter>test\core\end2end</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\end2end_test_utils.c"> - <Filter>test\core\end2end</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\authority_not_supported.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\bad_hostname.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\bad_ping.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\binary_metadata.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\call_creds.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_accept.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_client_done.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_invoke.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_round_trip.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_before_invoke.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_in_a_vacuum.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_with_status.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\compressed_payload.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\connectivity.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\default_host.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\disappearing_server.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\empty_batch.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_call_init_fails.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_causes_close.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_latency.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\graceful_server_shutdown.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\high_initial_seqno.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\hpack_size.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\idempotent_request.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\invoke_large_request.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\keepalive_timeout.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\load_reporting_hook.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_concurrent_streams.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_connection_age.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_connection_idle.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_message_length.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\negative_deadline.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\network_status_change.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_logging.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_op.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\payload.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\ping.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\ping_pong_streaming.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\proxy_auth.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\registered_call.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\request_with_flags.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\request_with_payload.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\resource_quota_server.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\server_finishes_request.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_calls.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_tags.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_cacheable_request.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_delayed_request.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_metadata.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_request.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_compressed_payload.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_payload.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_ping_pong_streaming.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\streaming_error_response.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\trailing_metadata.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\workaround_cronet_compression.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\write_buffering.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\write_buffering_at_end.c"> - <Filter>test\core\end2end\tests</Filter> - </ClCompile> - </ItemGroup> - <ItemGroup> - <ClInclude Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_test_helpers.h"> - <Filter>test\core\end2end\tests</Filter> - </ClInclude> - <ClInclude Include="$(SolutionDir)\..\test\core\end2end\end2end_tests.h"> - <Filter>test\core\end2end</Filter> - </ClInclude> - </ItemGroup> - - <ItemGroup> - <Filter Include="test"> - <UniqueIdentifier>{a8b0b6cf-6810-ce42-aabe-361c2a8fad86}</UniqueIdentifier> - </Filter> - <Filter Include="test\core"> - <UniqueIdentifier>{8f9b8c49-9e5d-f954-5bae-c5cfc1e4494d}</UniqueIdentifier> - </Filter> - <Filter Include="test\core\end2end"> - <UniqueIdentifier>{4b2ad304-b30c-df58-bbcd-352f6bee54bd}</UniqueIdentifier> - </Filter> - <Filter Include="test\core\end2end\tests"> - <UniqueIdentifier>{594b8114-e3bd-e69b-3213-cc80cb3e8662}</UniqueIdentifier> - </Filter> - </ItemGroup> -</Project> - |