diff --git a/AUTHORS b/AUTHORS index e491a9e7f78..3e130afda2d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1 +1,2 @@ Google Inc. +WeWork Companies Inc. diff --git a/BUILD b/BUILD index 925e277cf77..6348c4e6ff2 100644 --- a/BUILD +++ b/BUILD @@ -243,6 +243,7 @@ GRPCXX_PUBLIC_HDRS = [ "include/grpcpp/support/async_unary_call.h", "include/grpcpp/support/byte_buffer.h", "include/grpcpp/support/channel_arguments.h", + "include/grpcpp/support/client_callback.h", "include/grpcpp/support/config.h", "include/grpcpp/support/proto_buffer_reader.h", "include/grpcpp/support/proto_buffer_writer.h", @@ -706,7 +707,6 @@ grpc_cc_library( "src/core/lib/iomgr/error.cc", "src/core/lib/iomgr/ev_epoll1_linux.cc", "src/core/lib/iomgr/ev_epollex_linux.cc", - "src/core/lib/iomgr/ev_epollsig_linux.cc", "src/core/lib/iomgr/ev_poll_posix.cc", "src/core/lib/iomgr/ev_posix.cc", "src/core/lib/iomgr/ev_windows.cc", @@ -858,7 +858,6 @@ grpc_cc_library( "src/core/lib/iomgr/error_internal.h", "src/core/lib/iomgr/ev_epoll1_linux.h", "src/core/lib/iomgr/ev_epollex_linux.h", - "src/core/lib/iomgr/ev_epollsig_linux.h", "src/core/lib/iomgr/ev_poll_posix.h", "src/core/lib/iomgr/ev_posix.h", "src/core/lib/iomgr/exec_ctx.h", @@ -1573,6 +1572,7 @@ grpc_cc_library( "src/core/ext/transport/chttp2/transport/frame_window_update.cc", "src/core/ext/transport/chttp2/transport/hpack_encoder.cc", "src/core/ext/transport/chttp2/transport/hpack_parser.cc", + "src/core/ext/transport/chttp2/transport/hpack_mapping.cc", "src/core/ext/transport/chttp2/transport/hpack_table.cc", "src/core/ext/transport/chttp2/transport/http2_settings.cc", "src/core/ext/transport/chttp2/transport/huffsyms.cc", @@ -1597,6 +1597,7 @@ grpc_cc_library( "src/core/ext/transport/chttp2/transport/frame_window_update.h", "src/core/ext/transport/chttp2/transport/hpack_encoder.h", "src/core/ext/transport/chttp2/transport/hpack_parser.h", + "src/core/ext/transport/chttp2/transport/hpack_mapping.h", "src/core/ext/transport/chttp2/transport/hpack_table.h", "src/core/ext/transport/chttp2/transport/http2_settings.h", "src/core/ext/transport/chttp2/transport/huffsyms.h", @@ -1979,7 +1980,9 @@ grpc_cc_library( "include/grpcpp/impl/codegen/byte_buffer.h", "include/grpcpp/impl/codegen/call.h", "include/grpcpp/impl/codegen/call_hook.h", + "include/grpcpp/impl/codegen/callback_common.h", "include/grpcpp/impl/codegen/channel_interface.h", + "include/grpcpp/impl/codegen/client_callback.h", "include/grpcpp/impl/codegen/client_context.h", "include/grpcpp/impl/codegen/client_unary_call.h", "include/grpcpp/impl/codegen/completion_queue.h", diff --git a/CMakeLists.txt b/CMakeLists.txt index a21bb8b5fae..51788be1086 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -253,9 +253,6 @@ add_dependencies(buildtests_c error_test) if(_gRPC_PLATFORM_LINUX) add_dependencies(buildtests_c ev_epollex_linux_test) endif() -if(_gRPC_PLATFORM_LINUX) -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) @@ -355,13 +352,11 @@ add_dependencies(buildtests_c no_server_test) add_dependencies(buildtests_c num_external_connectivity_watchers_test) add_dependencies(buildtests_c parse_address_test) add_dependencies(buildtests_c percent_encoding_test) -if(_gRPC_PLATFORM_LINUX) -add_dependencies(buildtests_c pollset_set_test) -endif() if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_c resolve_address_posix_test) endif() -add_dependencies(buildtests_c resolve_address_test) +add_dependencies(buildtests_c resolve_address_using_ares_resolver_test) +add_dependencies(buildtests_c resolve_address_using_native_resolver_test) add_dependencies(buildtests_c resource_quota_test) add_dependencies(buildtests_c secure_channel_create_test) add_dependencies(buildtests_c secure_endpoint_test) @@ -564,6 +559,7 @@ add_dependencies(buildtests_cxx check_gcp_environment_linux_test) add_dependencies(buildtests_cxx check_gcp_environment_windows_test) add_dependencies(buildtests_cxx chttp2_settings_timeout_test) add_dependencies(buildtests_cxx cli_call_test) +add_dependencies(buildtests_cxx client_callback_end2end_test) add_dependencies(buildtests_cxx client_channel_stress_test) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_cxx client_crash_test) @@ -972,7 +968,6 @@ add_library(grpc src/core/lib/iomgr/error.cc src/core/lib/iomgr/ev_epoll1_linux.cc src/core/lib/iomgr/ev_epollex_linux.cc - src/core/lib/iomgr/ev_epollsig_linux.cc src/core/lib/iomgr/ev_poll_posix.cc src/core/lib/iomgr/ev_posix.cc src/core/lib/iomgr/ev_windows.cc @@ -1103,6 +1098,7 @@ add_library(grpc src/core/ext/transport/chttp2/transport/frame_settings.cc src/core/ext/transport/chttp2/transport/frame_window_update.cc src/core/ext/transport/chttp2/transport/hpack_encoder.cc + src/core/ext/transport/chttp2/transport/hpack_mapping.cc src/core/ext/transport/chttp2/transport/hpack_parser.cc src/core/ext/transport/chttp2/transport/hpack_table.cc src/core/ext/transport/chttp2/transport/http2_settings.cc @@ -1380,7 +1376,6 @@ add_library(grpc_cronet src/core/lib/iomgr/error.cc src/core/lib/iomgr/ev_epoll1_linux.cc src/core/lib/iomgr/ev_epollex_linux.cc - src/core/lib/iomgr/ev_epollsig_linux.cc src/core/lib/iomgr/ev_poll_posix.cc src/core/lib/iomgr/ev_posix.cc src/core/lib/iomgr/ev_windows.cc @@ -1514,6 +1509,7 @@ add_library(grpc_cronet src/core/ext/transport/chttp2/transport/frame_settings.cc src/core/ext/transport/chttp2/transport/frame_window_update.cc src/core/ext/transport/chttp2/transport/hpack_encoder.cc + src/core/ext/transport/chttp2/transport/hpack_mapping.cc src/core/ext/transport/chttp2/transport/hpack_parser.cc src/core/ext/transport/chttp2/transport/hpack_table.cc src/core/ext/transport/chttp2/transport/http2_settings.cc @@ -1774,7 +1770,6 @@ add_library(grpc_test_util src/core/lib/iomgr/error.cc src/core/lib/iomgr/ev_epoll1_linux.cc src/core/lib/iomgr/ev_epollex_linux.cc - src/core/lib/iomgr/ev_epollsig_linux.cc src/core/lib/iomgr/ev_poll_posix.cc src/core/lib/iomgr/ev_posix.cc src/core/lib/iomgr/ev_windows.cc @@ -1927,6 +1922,7 @@ add_library(grpc_test_util src/core/ext/transport/chttp2/transport/frame_settings.cc src/core/ext/transport/chttp2/transport/frame_window_update.cc src/core/ext/transport/chttp2/transport/hpack_encoder.cc + src/core/ext/transport/chttp2/transport/hpack_mapping.cc src/core/ext/transport/chttp2/transport/hpack_parser.cc src/core/ext/transport/chttp2/transport/hpack_table.cc src/core/ext/transport/chttp2/transport/http2_settings.cc @@ -2084,7 +2080,6 @@ add_library(grpc_test_util_unsecure src/core/lib/iomgr/error.cc src/core/lib/iomgr/ev_epoll1_linux.cc src/core/lib/iomgr/ev_epollex_linux.cc - src/core/lib/iomgr/ev_epollsig_linux.cc src/core/lib/iomgr/ev_poll_posix.cc src/core/lib/iomgr/ev_posix.cc src/core/lib/iomgr/ev_windows.cc @@ -2237,6 +2232,7 @@ add_library(grpc_test_util_unsecure src/core/ext/transport/chttp2/transport/frame_settings.cc src/core/ext/transport/chttp2/transport/frame_window_update.cc src/core/ext/transport/chttp2/transport/hpack_encoder.cc + src/core/ext/transport/chttp2/transport/hpack_mapping.cc src/core/ext/transport/chttp2/transport/hpack_parser.cc src/core/ext/transport/chttp2/transport/hpack_table.cc src/core/ext/transport/chttp2/transport/http2_settings.cc @@ -2373,7 +2369,6 @@ add_library(grpc_unsecure src/core/lib/iomgr/error.cc src/core/lib/iomgr/ev_epoll1_linux.cc src/core/lib/iomgr/ev_epollex_linux.cc - src/core/lib/iomgr/ev_epollsig_linux.cc src/core/lib/iomgr/ev_poll_posix.cc src/core/lib/iomgr/ev_posix.cc src/core/lib/iomgr/ev_windows.cc @@ -2505,6 +2500,7 @@ add_library(grpc_unsecure src/core/ext/transport/chttp2/transport/frame_settings.cc src/core/ext/transport/chttp2/transport/frame_window_update.cc src/core/ext/transport/chttp2/transport/hpack_encoder.cc + src/core/ext/transport/chttp2/transport/hpack_mapping.cc src/core/ext/transport/chttp2/transport/hpack_parser.cc src/core/ext/transport/chttp2/transport/hpack_table.cc src/core/ext/transport/chttp2/transport/http2_settings.cc @@ -2917,6 +2913,7 @@ foreach(_hdr include/grpcpp/support/async_unary_call.h include/grpcpp/support/byte_buffer.h include/grpcpp/support/channel_arguments.h + include/grpcpp/support/client_callback.h include/grpcpp/support/config.h include/grpcpp/support/proto_buffer_reader.h include/grpcpp/support/proto_buffer_writer.h @@ -3014,7 +3011,9 @@ foreach(_hdr include/grpcpp/impl/codegen/byte_buffer.h include/grpcpp/impl/codegen/call.h include/grpcpp/impl/codegen/call_hook.h + include/grpcpp/impl/codegen/callback_common.h include/grpcpp/impl/codegen/channel_interface.h + include/grpcpp/impl/codegen/client_callback.h include/grpcpp/impl/codegen/client_context.h include/grpcpp/impl/codegen/client_unary_call.h include/grpcpp/impl/codegen/completion_queue.h @@ -3171,6 +3170,7 @@ add_library(grpc++_cronet src/core/ext/transport/chttp2/transport/frame_settings.cc src/core/ext/transport/chttp2/transport/frame_window_update.cc src/core/ext/transport/chttp2/transport/hpack_encoder.cc + src/core/ext/transport/chttp2/transport/hpack_mapping.cc src/core/ext/transport/chttp2/transport/hpack_parser.cc src/core/ext/transport/chttp2/transport/hpack_table.cc src/core/ext/transport/chttp2/transport/http2_settings.cc @@ -3215,7 +3215,6 @@ add_library(grpc++_cronet src/core/lib/iomgr/error.cc src/core/lib/iomgr/ev_epoll1_linux.cc src/core/lib/iomgr/ev_epollex_linux.cc - src/core/lib/iomgr/ev_epollsig_linux.cc src/core/lib/iomgr/ev_poll_posix.cc src/core/lib/iomgr/ev_posix.cc src/core/lib/iomgr/ev_windows.cc @@ -3486,6 +3485,7 @@ foreach(_hdr include/grpcpp/support/async_unary_call.h include/grpcpp/support/byte_buffer.h include/grpcpp/support/channel_arguments.h + include/grpcpp/support/client_callback.h include/grpcpp/support/config.h include/grpcpp/support/proto_buffer_reader.h include/grpcpp/support/proto_buffer_writer.h @@ -3583,7 +3583,9 @@ foreach(_hdr include/grpcpp/impl/codegen/byte_buffer.h include/grpcpp/impl/codegen/call.h include/grpcpp/impl/codegen/call_hook.h + include/grpcpp/impl/codegen/callback_common.h include/grpcpp/impl/codegen/channel_interface.h + include/grpcpp/impl/codegen/client_callback.h include/grpcpp/impl/codegen/client_context.h include/grpcpp/impl/codegen/client_unary_call.h include/grpcpp/impl/codegen/completion_queue.h @@ -3993,7 +3995,9 @@ foreach(_hdr include/grpcpp/impl/codegen/byte_buffer.h include/grpcpp/impl/codegen/call.h include/grpcpp/impl/codegen/call_hook.h + include/grpcpp/impl/codegen/callback_common.h include/grpcpp/impl/codegen/channel_interface.h + include/grpcpp/impl/codegen/client_callback.h include/grpcpp/impl/codegen/client_context.h include/grpcpp/impl/codegen/client_unary_call.h include/grpcpp/impl/codegen/completion_queue.h @@ -4171,7 +4175,9 @@ foreach(_hdr include/grpcpp/impl/codegen/byte_buffer.h include/grpcpp/impl/codegen/call.h include/grpcpp/impl/codegen/call_hook.h + include/grpcpp/impl/codegen/callback_common.h include/grpcpp/impl/codegen/channel_interface.h + include/grpcpp/impl/codegen/client_callback.h include/grpcpp/impl/codegen/client_context.h include/grpcpp/impl/codegen/client_unary_call.h include/grpcpp/impl/codegen/completion_queue.h @@ -4392,6 +4398,7 @@ foreach(_hdr include/grpcpp/support/async_unary_call.h include/grpcpp/support/byte_buffer.h include/grpcpp/support/channel_arguments.h + include/grpcpp/support/client_callback.h include/grpcpp/support/config.h include/grpcpp/support/proto_buffer_reader.h include/grpcpp/support/proto_buffer_writer.h @@ -4489,7 +4496,9 @@ foreach(_hdr include/grpcpp/impl/codegen/byte_buffer.h include/grpcpp/impl/codegen/call.h include/grpcpp/impl/codegen/call_hook.h + include/grpcpp/impl/codegen/callback_common.h include/grpcpp/impl/codegen/channel_interface.h + include/grpcpp/impl/codegen/client_callback.h include/grpcpp/impl/codegen/client_context.h include/grpcpp/impl/codegen/client_unary_call.h include/grpcpp/impl/codegen/completion_queue.h @@ -6372,37 +6381,6 @@ target_link_libraries(ev_epollex_linux_test gpr ) -endif() -endif (gRPC_BUILD_TESTS) -if (gRPC_BUILD_TESTS) -if(_gRPC_PLATFORM_LINUX) - -add_executable(ev_epollsig_linux_test - test/core/iomgr/ev_epollsig_linux_test.cc -) - - -target_include_directories(ev_epollsig_linux_test - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include - PRIVATE ${_gRPC_SSL_INCLUDE_DIR} - PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR} - PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR} - PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR} - PRIVATE ${_gRPC_CARES_INCLUDE_DIR} - PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR} - PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR} - PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR} -) - -target_link_libraries(ev_epollsig_linux_test - ${_gRPC_ALLTARGETS_LIBRARIES} - grpc_test_util - grpc - gpr_test_util - gpr -) - endif() endif (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS) @@ -8490,14 +8468,14 @@ target_link_libraries(percent_encoding_test endif (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS) -if(_gRPC_PLATFORM_LINUX) +if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) -add_executable(pollset_set_test - test/core/iomgr/pollset_set_test.cc +add_executable(resolve_address_posix_test + test/core/iomgr/resolve_address_posix_test.cc ) -target_include_directories(pollset_set_test +target_include_directories(resolve_address_posix_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include PRIVATE ${_gRPC_SSL_INCLUDE_DIR} @@ -8510,7 +8488,7 @@ target_include_directories(pollset_set_test PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR} ) -target_link_libraries(pollset_set_test +target_link_libraries(resolve_address_posix_test ${_gRPC_ALLTARGETS_LIBRARIES} grpc_test_util grpc @@ -8521,14 +8499,13 @@ target_link_libraries(pollset_set_test endif() endif (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS) -if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) -add_executable(resolve_address_posix_test - test/core/iomgr/resolve_address_posix_test.cc +add_executable(resolve_address_using_ares_resolver_test + test/core/iomgr/resolve_address_test.cc ) -target_include_directories(resolve_address_posix_test +target_include_directories(resolve_address_using_ares_resolver_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include PRIVATE ${_gRPC_SSL_INCLUDE_DIR} @@ -8541,7 +8518,7 @@ target_include_directories(resolve_address_posix_test PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR} ) -target_link_libraries(resolve_address_posix_test +target_link_libraries(resolve_address_using_ares_resolver_test ${_gRPC_ALLTARGETS_LIBRARIES} grpc_test_util grpc @@ -8549,16 +8526,15 @@ target_link_libraries(resolve_address_posix_test gpr ) -endif() endif (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS) -add_executable(resolve_address_test +add_executable(resolve_address_using_native_resolver_test test/core/iomgr/resolve_address_test.cc ) -target_include_directories(resolve_address_test +target_include_directories(resolve_address_using_native_resolver_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include PRIVATE ${_gRPC_SSL_INCLUDE_DIR} @@ -8571,7 +8547,7 @@ target_include_directories(resolve_address_test PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR} ) -target_link_libraries(resolve_address_test +target_link_libraries(resolve_address_using_native_resolver_test ${_gRPC_ALLTARGETS_LIBRARIES} grpc_test_util grpc @@ -11297,6 +11273,46 @@ target_link_libraries(cli_call_test endif (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS) +add_executable(client_callback_end2end_test + test/cpp/end2end/client_callback_end2end_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) + + +target_include_directories(client_callback_end2end_test + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + PRIVATE ${_gRPC_SSL_INCLUDE_DIR} + PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR} + PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR} + PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR} + PRIVATE ${_gRPC_CARES_INCLUDE_DIR} + PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR} + PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR} + PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR} + PRIVATE third_party/googletest/googletest/include + PRIVATE third_party/googletest/googletest + PRIVATE third_party/googletest/googlemock/include + PRIVATE third_party/googletest/googlemock + PRIVATE ${_gRPC_PROTO_GENS_DIR} +) + +target_link_libraries(client_callback_end2end_test + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc++_test_util + grpc_test_util + grpc++ + grpc + gpr_test_util + gpr + ${_gRPC_GFLAGS_LIBRARIES} +) + +endif (gRPC_BUILD_TESTS) +if (gRPC_BUILD_TESTS) + add_executable(client_channel_stress_test ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/lb/v1/load_balancer.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc diff --git a/Makefile b/Makefile index 96ea890bcb8..baaebd1803c 100644 --- a/Makefile +++ b/Makefile @@ -352,6 +352,7 @@ CXXFLAGS += -std=c++11 ifeq ($(SYSTEM),Darwin) CXXFLAGS += -stdlib=libc++ endif +CXXFLAGS += -Wnon-virtual-dtor CPPFLAGS += -g -Wall -Wextra -Werror -Wno-long-long -Wno-unused-parameter -DOSATOMIC_USE_INLINED=1 -Wno-deprecated-declarations -Ithird_party/nanopb -DPB_FIELD_32BIT COREFLAGS += -fno-rtti -fno-exceptions LDFLAGS += -g @@ -997,7 +998,6 @@ dualstack_socket_test: $(BINDIR)/$(CONFIG)/dualstack_socket_test endpoint_pair_test: $(BINDIR)/$(CONFIG)/endpoint_pair_test error_test: $(BINDIR)/$(CONFIG)/error_test ev_epollex_linux_test: $(BINDIR)/$(CONFIG)/ev_epollex_linux_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 @@ -1079,9 +1079,9 @@ parse_address_test: $(BINDIR)/$(CONFIG)/parse_address_test percent_decode_fuzzer: $(BINDIR)/$(CONFIG)/percent_decode_fuzzer percent_encode_fuzzer: $(BINDIR)/$(CONFIG)/percent_encode_fuzzer percent_encoding_test: $(BINDIR)/$(CONFIG)/percent_encoding_test -pollset_set_test: $(BINDIR)/$(CONFIG)/pollset_set_test resolve_address_posix_test: $(BINDIR)/$(CONFIG)/resolve_address_posix_test -resolve_address_test: $(BINDIR)/$(CONFIG)/resolve_address_test +resolve_address_using_ares_resolver_test: $(BINDIR)/$(CONFIG)/resolve_address_using_ares_resolver_test +resolve_address_using_native_resolver_test: $(BINDIR)/$(CONFIG)/resolve_address_using_native_resolver_test resource_quota_test: $(BINDIR)/$(CONFIG)/resource_quota_test secure_channel_create_test: $(BINDIR)/$(CONFIG)/secure_channel_create_test secure_endpoint_test: $(BINDIR)/$(CONFIG)/secure_endpoint_test @@ -1160,6 +1160,7 @@ check_gcp_environment_linux_test: $(BINDIR)/$(CONFIG)/check_gcp_environment_linu check_gcp_environment_windows_test: $(BINDIR)/$(CONFIG)/check_gcp_environment_windows_test chttp2_settings_timeout_test: $(BINDIR)/$(CONFIG)/chttp2_settings_timeout_test cli_call_test: $(BINDIR)/$(CONFIG)/cli_call_test +client_callback_end2end_test: $(BINDIR)/$(CONFIG)/client_callback_end2end_test client_channel_stress_test: $(BINDIR)/$(CONFIG)/client_channel_stress_test client_crash_test: $(BINDIR)/$(CONFIG)/client_crash_test client_crash_test_server: $(BINDIR)/$(CONFIG)/client_crash_test_server @@ -1452,7 +1453,6 @@ buildtests_c: privatelibs_c \ $(BINDIR)/$(CONFIG)/endpoint_pair_test \ $(BINDIR)/$(CONFIG)/error_test \ $(BINDIR)/$(CONFIG)/ev_epollex_linux_test \ - $(BINDIR)/$(CONFIG)/ev_epollsig_linux_test \ $(BINDIR)/$(CONFIG)/fake_resolver_test \ $(BINDIR)/$(CONFIG)/fake_transport_security_test \ $(BINDIR)/$(CONFIG)/fd_conservation_posix_test \ @@ -1522,9 +1522,9 @@ buildtests_c: privatelibs_c \ $(BINDIR)/$(CONFIG)/num_external_connectivity_watchers_test \ $(BINDIR)/$(CONFIG)/parse_address_test \ $(BINDIR)/$(CONFIG)/percent_encoding_test \ - $(BINDIR)/$(CONFIG)/pollset_set_test \ $(BINDIR)/$(CONFIG)/resolve_address_posix_test \ - $(BINDIR)/$(CONFIG)/resolve_address_test \ + $(BINDIR)/$(CONFIG)/resolve_address_using_ares_resolver_test \ + $(BINDIR)/$(CONFIG)/resolve_address_using_native_resolver_test \ $(BINDIR)/$(CONFIG)/resource_quota_test \ $(BINDIR)/$(CONFIG)/secure_channel_create_test \ $(BINDIR)/$(CONFIG)/secure_endpoint_test \ @@ -1665,6 +1665,7 @@ buildtests_cxx: privatelibs_cxx \ $(BINDIR)/$(CONFIG)/check_gcp_environment_windows_test \ $(BINDIR)/$(CONFIG)/chttp2_settings_timeout_test \ $(BINDIR)/$(CONFIG)/cli_call_test \ + $(BINDIR)/$(CONFIG)/client_callback_end2end_test \ $(BINDIR)/$(CONFIG)/client_channel_stress_test \ $(BINDIR)/$(CONFIG)/client_crash_test \ $(BINDIR)/$(CONFIG)/client_crash_test_server \ @@ -1845,6 +1846,7 @@ buildtests_cxx: privatelibs_cxx \ $(BINDIR)/$(CONFIG)/check_gcp_environment_windows_test \ $(BINDIR)/$(CONFIG)/chttp2_settings_timeout_test \ $(BINDIR)/$(CONFIG)/cli_call_test \ + $(BINDIR)/$(CONFIG)/client_callback_end2end_test \ $(BINDIR)/$(CONFIG)/client_channel_stress_test \ $(BINDIR)/$(CONFIG)/client_crash_test \ $(BINDIR)/$(CONFIG)/client_crash_test_server \ @@ -1986,8 +1988,6 @@ test_c: buildtests_c $(Q) $(BINDIR)/$(CONFIG)/error_test || ( echo test error_test failed ; exit 1 ) $(E) "[RUN] Testing ev_epollex_linux_test" $(Q) $(BINDIR)/$(CONFIG)/ev_epollex_linux_test || ( echo test ev_epollex_linux_test failed ; exit 1 ) - $(E) "[RUN] Testing ev_epollsig_linux_test" - $(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" @@ -2114,12 +2114,12 @@ test_c: buildtests_c $(Q) $(BINDIR)/$(CONFIG)/parse_address_test || ( echo test parse_address_test failed ; exit 1 ) $(E) "[RUN] Testing percent_encoding_test" $(Q) $(BINDIR)/$(CONFIG)/percent_encoding_test || ( echo test percent_encoding_test failed ; exit 1 ) - $(E) "[RUN] Testing pollset_set_test" - $(Q) $(BINDIR)/$(CONFIG)/pollset_set_test || ( echo test pollset_set_test failed ; exit 1 ) $(E) "[RUN] Testing resolve_address_posix_test" $(Q) $(BINDIR)/$(CONFIG)/resolve_address_posix_test || ( echo test resolve_address_posix_test failed ; exit 1 ) - $(E) "[RUN] Testing resolve_address_test" - $(Q) $(BINDIR)/$(CONFIG)/resolve_address_test || ( echo test resolve_address_test failed ; exit 1 ) + $(E) "[RUN] Testing resolve_address_using_ares_resolver_test" + $(Q) $(BINDIR)/$(CONFIG)/resolve_address_using_ares_resolver_test || ( echo test resolve_address_using_ares_resolver_test failed ; exit 1 ) + $(E) "[RUN] Testing resolve_address_using_native_resolver_test" + $(Q) $(BINDIR)/$(CONFIG)/resolve_address_using_native_resolver_test || ( echo test resolve_address_using_native_resolver_test failed ; exit 1 ) $(E) "[RUN] Testing resource_quota_test" $(Q) $(BINDIR)/$(CONFIG)/resource_quota_test || ( echo test resource_quota_test failed ; exit 1 ) $(E) "[RUN] Testing secure_channel_create_test" @@ -2302,6 +2302,8 @@ test_cxx: buildtests_cxx $(Q) $(BINDIR)/$(CONFIG)/chttp2_settings_timeout_test || ( echo test chttp2_settings_timeout_test failed ; exit 1 ) $(E) "[RUN] Testing cli_call_test" $(Q) $(BINDIR)/$(CONFIG)/cli_call_test || ( echo test cli_call_test failed ; exit 1 ) + $(E) "[RUN] Testing client_callback_end2end_test" + $(Q) $(BINDIR)/$(CONFIG)/client_callback_end2end_test || ( echo test client_callback_end2end_test failed ; exit 1 ) $(E) "[RUN] Testing client_channel_stress_test" $(Q) $(BINDIR)/$(CONFIG)/client_channel_stress_test || ( echo test client_channel_stress_test failed ; exit 1 ) $(E) "[RUN] Testing client_crash_test" @@ -3474,7 +3476,6 @@ LIBGRPC_SRC = \ src/core/lib/iomgr/error.cc \ src/core/lib/iomgr/ev_epoll1_linux.cc \ src/core/lib/iomgr/ev_epollex_linux.cc \ - src/core/lib/iomgr/ev_epollsig_linux.cc \ src/core/lib/iomgr/ev_poll_posix.cc \ src/core/lib/iomgr/ev_posix.cc \ src/core/lib/iomgr/ev_windows.cc \ @@ -3605,6 +3606,7 @@ LIBGRPC_SRC = \ src/core/ext/transport/chttp2/transport/frame_settings.cc \ src/core/ext/transport/chttp2/transport/frame_window_update.cc \ src/core/ext/transport/chttp2/transport/hpack_encoder.cc \ + src/core/ext/transport/chttp2/transport/hpack_mapping.cc \ src/core/ext/transport/chttp2/transport/hpack_parser.cc \ src/core/ext/transport/chttp2/transport/hpack_table.cc \ src/core/ext/transport/chttp2/transport/http2_settings.cc \ @@ -3881,7 +3883,6 @@ LIBGRPC_CRONET_SRC = \ src/core/lib/iomgr/error.cc \ src/core/lib/iomgr/ev_epoll1_linux.cc \ src/core/lib/iomgr/ev_epollex_linux.cc \ - src/core/lib/iomgr/ev_epollsig_linux.cc \ src/core/lib/iomgr/ev_poll_posix.cc \ src/core/lib/iomgr/ev_posix.cc \ src/core/lib/iomgr/ev_windows.cc \ @@ -4015,6 +4016,7 @@ LIBGRPC_CRONET_SRC = \ src/core/ext/transport/chttp2/transport/frame_settings.cc \ src/core/ext/transport/chttp2/transport/frame_window_update.cc \ src/core/ext/transport/chttp2/transport/hpack_encoder.cc \ + src/core/ext/transport/chttp2/transport/hpack_mapping.cc \ src/core/ext/transport/chttp2/transport/hpack_parser.cc \ src/core/ext/transport/chttp2/transport/hpack_table.cc \ src/core/ext/transport/chttp2/transport/http2_settings.cc \ @@ -4273,7 +4275,6 @@ LIBGRPC_TEST_UTIL_SRC = \ src/core/lib/iomgr/error.cc \ src/core/lib/iomgr/ev_epoll1_linux.cc \ src/core/lib/iomgr/ev_epollex_linux.cc \ - src/core/lib/iomgr/ev_epollsig_linux.cc \ src/core/lib/iomgr/ev_poll_posix.cc \ src/core/lib/iomgr/ev_posix.cc \ src/core/lib/iomgr/ev_windows.cc \ @@ -4426,6 +4427,7 @@ LIBGRPC_TEST_UTIL_SRC = \ src/core/ext/transport/chttp2/transport/frame_settings.cc \ src/core/ext/transport/chttp2/transport/frame_window_update.cc \ src/core/ext/transport/chttp2/transport/hpack_encoder.cc \ + src/core/ext/transport/chttp2/transport/hpack_mapping.cc \ src/core/ext/transport/chttp2/transport/hpack_parser.cc \ src/core/ext/transport/chttp2/transport/hpack_table.cc \ src/core/ext/transport/chttp2/transport/http2_settings.cc \ @@ -4574,7 +4576,6 @@ LIBGRPC_TEST_UTIL_UNSECURE_SRC = \ src/core/lib/iomgr/error.cc \ src/core/lib/iomgr/ev_epoll1_linux.cc \ src/core/lib/iomgr/ev_epollex_linux.cc \ - src/core/lib/iomgr/ev_epollsig_linux.cc \ src/core/lib/iomgr/ev_poll_posix.cc \ src/core/lib/iomgr/ev_posix.cc \ src/core/lib/iomgr/ev_windows.cc \ @@ -4727,6 +4728,7 @@ LIBGRPC_TEST_UTIL_UNSECURE_SRC = \ src/core/ext/transport/chttp2/transport/frame_settings.cc \ src/core/ext/transport/chttp2/transport/frame_window_update.cc \ src/core/ext/transport/chttp2/transport/hpack_encoder.cc \ + src/core/ext/transport/chttp2/transport/hpack_mapping.cc \ src/core/ext/transport/chttp2/transport/hpack_parser.cc \ src/core/ext/transport/chttp2/transport/hpack_table.cc \ src/core/ext/transport/chttp2/transport/http2_settings.cc \ @@ -4841,7 +4843,6 @@ LIBGRPC_UNSECURE_SRC = \ src/core/lib/iomgr/error.cc \ src/core/lib/iomgr/ev_epoll1_linux.cc \ src/core/lib/iomgr/ev_epollex_linux.cc \ - src/core/lib/iomgr/ev_epollsig_linux.cc \ src/core/lib/iomgr/ev_poll_posix.cc \ src/core/lib/iomgr/ev_posix.cc \ src/core/lib/iomgr/ev_windows.cc \ @@ -4973,6 +4974,7 @@ LIBGRPC_UNSECURE_SRC = \ src/core/ext/transport/chttp2/transport/frame_settings.cc \ src/core/ext/transport/chttp2/transport/frame_window_update.cc \ src/core/ext/transport/chttp2/transport/hpack_encoder.cc \ + src/core/ext/transport/chttp2/transport/hpack_mapping.cc \ src/core/ext/transport/chttp2/transport/hpack_parser.cc \ src/core/ext/transport/chttp2/transport/hpack_table.cc \ src/core/ext/transport/chttp2/transport/http2_settings.cc \ @@ -5329,6 +5331,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/support/async_unary_call.h \ include/grpcpp/support/byte_buffer.h \ include/grpcpp/support/channel_arguments.h \ + include/grpcpp/support/client_callback.h \ include/grpcpp/support/config.h \ include/grpcpp/support/proto_buffer_reader.h \ include/grpcpp/support/proto_buffer_writer.h \ @@ -5426,7 +5429,9 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/byte_buffer.h \ include/grpcpp/impl/codegen/call.h \ include/grpcpp/impl/codegen/call_hook.h \ + include/grpcpp/impl/codegen/callback_common.h \ include/grpcpp/impl/codegen/channel_interface.h \ + include/grpcpp/impl/codegen/client_callback.h \ include/grpcpp/impl/codegen/client_context.h \ include/grpcpp/impl/codegen/client_unary_call.h \ include/grpcpp/impl/codegen/completion_queue.h \ @@ -5627,6 +5632,7 @@ LIBGRPC++_CRONET_SRC = \ src/core/ext/transport/chttp2/transport/frame_settings.cc \ src/core/ext/transport/chttp2/transport/frame_window_update.cc \ src/core/ext/transport/chttp2/transport/hpack_encoder.cc \ + src/core/ext/transport/chttp2/transport/hpack_mapping.cc \ src/core/ext/transport/chttp2/transport/hpack_parser.cc \ src/core/ext/transport/chttp2/transport/hpack_table.cc \ src/core/ext/transport/chttp2/transport/http2_settings.cc \ @@ -5671,7 +5677,6 @@ LIBGRPC++_CRONET_SRC = \ src/core/lib/iomgr/error.cc \ src/core/lib/iomgr/ev_epoll1_linux.cc \ src/core/lib/iomgr/ev_epollex_linux.cc \ - src/core/lib/iomgr/ev_epollsig_linux.cc \ src/core/lib/iomgr/ev_poll_posix.cc \ src/core/lib/iomgr/ev_posix.cc \ src/core/lib/iomgr/ev_windows.cc \ @@ -5905,6 +5910,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/support/async_unary_call.h \ include/grpcpp/support/byte_buffer.h \ include/grpcpp/support/channel_arguments.h \ + include/grpcpp/support/client_callback.h \ include/grpcpp/support/config.h \ include/grpcpp/support/proto_buffer_reader.h \ include/grpcpp/support/proto_buffer_writer.h \ @@ -6002,7 +6008,9 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/byte_buffer.h \ include/grpcpp/impl/codegen/call.h \ include/grpcpp/impl/codegen/call_hook.h \ + include/grpcpp/impl/codegen/callback_common.h \ include/grpcpp/impl/codegen/channel_interface.h \ + include/grpcpp/impl/codegen/client_callback.h \ include/grpcpp/impl/codegen/client_context.h \ include/grpcpp/impl/codegen/client_unary_call.h \ include/grpcpp/impl/codegen/completion_queue.h \ @@ -6392,7 +6400,9 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/byte_buffer.h \ include/grpcpp/impl/codegen/call.h \ include/grpcpp/impl/codegen/call_hook.h \ + include/grpcpp/impl/codegen/callback_common.h \ include/grpcpp/impl/codegen/channel_interface.h \ + include/grpcpp/impl/codegen/client_callback.h \ include/grpcpp/impl/codegen/client_context.h \ include/grpcpp/impl/codegen/client_unary_call.h \ include/grpcpp/impl/codegen/completion_queue.h \ @@ -6546,7 +6556,9 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/byte_buffer.h \ include/grpcpp/impl/codegen/call.h \ include/grpcpp/impl/codegen/call_hook.h \ + include/grpcpp/impl/codegen/callback_common.h \ include/grpcpp/impl/codegen/channel_interface.h \ + include/grpcpp/impl/codegen/client_callback.h \ include/grpcpp/impl/codegen/client_context.h \ include/grpcpp/impl/codegen/client_unary_call.h \ include/grpcpp/impl/codegen/completion_queue.h \ @@ -6771,6 +6783,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/support/async_unary_call.h \ include/grpcpp/support/byte_buffer.h \ include/grpcpp/support/channel_arguments.h \ + include/grpcpp/support/client_callback.h \ include/grpcpp/support/config.h \ include/grpcpp/support/proto_buffer_reader.h \ include/grpcpp/support/proto_buffer_writer.h \ @@ -6868,7 +6881,9 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/byte_buffer.h \ include/grpcpp/impl/codegen/call.h \ include/grpcpp/impl/codegen/call_hook.h \ + include/grpcpp/impl/codegen/callback_common.h \ include/grpcpp/impl/codegen/channel_interface.h \ + include/grpcpp/impl/codegen/client_callback.h \ include/grpcpp/impl/codegen/client_context.h \ include/grpcpp/impl/codegen/client_unary_call.h \ include/grpcpp/impl/codegen/completion_queue.h \ @@ -11325,38 +11340,6 @@ endif endif -EV_EPOLLSIG_LINUX_TEST_SRC = \ - test/core/iomgr/ev_epollsig_linux_test.cc \ - -EV_EPOLLSIG_LINUX_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(EV_EPOLLSIG_LINUX_TEST_SRC)))) -ifeq ($(NO_SECURE),true) - -# You can't build secure targets if you don't have OpenSSL. - -$(BINDIR)/$(CONFIG)/ev_epollsig_linux_test: openssl_dep_error - -else - - - -$(BINDIR)/$(CONFIG)/ev_epollsig_linux_test: $(EV_EPOLLSIG_LINUX_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a - $(E) "[LD] Linking $@" - $(Q) mkdir -p `dirname $@` - $(Q) $(LD) $(LDFLAGS) $(EV_EPOLLSIG_LINUX_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/ev_epollsig_linux_test - -endif - -$(OBJDIR)/$(CONFIG)/test/core/iomgr/ev_epollsig_linux_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a - -deps_ev_epollsig_linux_test: $(EV_EPOLLSIG_LINUX_TEST_OBJS:.o=.dep) - -ifneq ($(NO_SECURE),true) -ifneq ($(NO_DEPS),true) --include $(EV_EPOLLSIG_LINUX_TEST_OBJS:.o=.dep) -endif -endif - - FAKE_RESOLVER_TEST_SRC = \ test/core/client_channel/resolvers/fake_resolver_test.cc \ @@ -13967,98 +13950,98 @@ endif endif -POLLSET_SET_TEST_SRC = \ - test/core/iomgr/pollset_set_test.cc \ +RESOLVE_ADDRESS_POSIX_TEST_SRC = \ + test/core/iomgr/resolve_address_posix_test.cc \ -POLLSET_SET_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(POLLSET_SET_TEST_SRC)))) +RESOLVE_ADDRESS_POSIX_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(RESOLVE_ADDRESS_POSIX_TEST_SRC)))) ifeq ($(NO_SECURE),true) # You can't build secure targets if you don't have OpenSSL. -$(BINDIR)/$(CONFIG)/pollset_set_test: openssl_dep_error +$(BINDIR)/$(CONFIG)/resolve_address_posix_test: openssl_dep_error else -$(BINDIR)/$(CONFIG)/pollset_set_test: $(POLLSET_SET_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +$(BINDIR)/$(CONFIG)/resolve_address_posix_test: $(RESOLVE_ADDRESS_POSIX_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(E) "[LD] Linking $@" $(Q) mkdir -p `dirname $@` - $(Q) $(LD) $(LDFLAGS) $(POLLSET_SET_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/pollset_set_test + $(Q) $(LD) $(LDFLAGS) $(RESOLVE_ADDRESS_POSIX_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/resolve_address_posix_test endif -$(OBJDIR)/$(CONFIG)/test/core/iomgr/pollset_set_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +$(OBJDIR)/$(CONFIG)/test/core/iomgr/resolve_address_posix_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a -deps_pollset_set_test: $(POLLSET_SET_TEST_OBJS:.o=.dep) +deps_resolve_address_posix_test: $(RESOLVE_ADDRESS_POSIX_TEST_OBJS:.o=.dep) ifneq ($(NO_SECURE),true) ifneq ($(NO_DEPS),true) --include $(POLLSET_SET_TEST_OBJS:.o=.dep) +-include $(RESOLVE_ADDRESS_POSIX_TEST_OBJS:.o=.dep) endif endif -RESOLVE_ADDRESS_POSIX_TEST_SRC = \ - test/core/iomgr/resolve_address_posix_test.cc \ +RESOLVE_ADDRESS_USING_ARES_RESOLVER_TEST_SRC = \ + test/core/iomgr/resolve_address_test.cc \ -RESOLVE_ADDRESS_POSIX_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(RESOLVE_ADDRESS_POSIX_TEST_SRC)))) +RESOLVE_ADDRESS_USING_ARES_RESOLVER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(RESOLVE_ADDRESS_USING_ARES_RESOLVER_TEST_SRC)))) ifeq ($(NO_SECURE),true) # You can't build secure targets if you don't have OpenSSL. -$(BINDIR)/$(CONFIG)/resolve_address_posix_test: openssl_dep_error +$(BINDIR)/$(CONFIG)/resolve_address_using_ares_resolver_test: openssl_dep_error else -$(BINDIR)/$(CONFIG)/resolve_address_posix_test: $(RESOLVE_ADDRESS_POSIX_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +$(BINDIR)/$(CONFIG)/resolve_address_using_ares_resolver_test: $(RESOLVE_ADDRESS_USING_ARES_RESOLVER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(E) "[LD] Linking $@" $(Q) mkdir -p `dirname $@` - $(Q) $(LD) $(LDFLAGS) $(RESOLVE_ADDRESS_POSIX_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/resolve_address_posix_test + $(Q) $(LD) $(LDFLAGS) $(RESOLVE_ADDRESS_USING_ARES_RESOLVER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/resolve_address_using_ares_resolver_test endif -$(OBJDIR)/$(CONFIG)/test/core/iomgr/resolve_address_posix_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +$(OBJDIR)/$(CONFIG)/test/core/iomgr/resolve_address_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a -deps_resolve_address_posix_test: $(RESOLVE_ADDRESS_POSIX_TEST_OBJS:.o=.dep) +deps_resolve_address_using_ares_resolver_test: $(RESOLVE_ADDRESS_USING_ARES_RESOLVER_TEST_OBJS:.o=.dep) ifneq ($(NO_SECURE),true) ifneq ($(NO_DEPS),true) --include $(RESOLVE_ADDRESS_POSIX_TEST_OBJS:.o=.dep) +-include $(RESOLVE_ADDRESS_USING_ARES_RESOLVER_TEST_OBJS:.o=.dep) endif endif -RESOLVE_ADDRESS_TEST_SRC = \ +RESOLVE_ADDRESS_USING_NATIVE_RESOLVER_TEST_SRC = \ test/core/iomgr/resolve_address_test.cc \ -RESOLVE_ADDRESS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(RESOLVE_ADDRESS_TEST_SRC)))) +RESOLVE_ADDRESS_USING_NATIVE_RESOLVER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(RESOLVE_ADDRESS_USING_NATIVE_RESOLVER_TEST_SRC)))) ifeq ($(NO_SECURE),true) # You can't build secure targets if you don't have OpenSSL. -$(BINDIR)/$(CONFIG)/resolve_address_test: openssl_dep_error +$(BINDIR)/$(CONFIG)/resolve_address_using_native_resolver_test: openssl_dep_error else -$(BINDIR)/$(CONFIG)/resolve_address_test: $(RESOLVE_ADDRESS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +$(BINDIR)/$(CONFIG)/resolve_address_using_native_resolver_test: $(RESOLVE_ADDRESS_USING_NATIVE_RESOLVER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(E) "[LD] Linking $@" $(Q) mkdir -p `dirname $@` - $(Q) $(LD) $(LDFLAGS) $(RESOLVE_ADDRESS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/resolve_address_test + $(Q) $(LD) $(LDFLAGS) $(RESOLVE_ADDRESS_USING_NATIVE_RESOLVER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/resolve_address_using_native_resolver_test endif $(OBJDIR)/$(CONFIG)/test/core/iomgr/resolve_address_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a -deps_resolve_address_test: $(RESOLVE_ADDRESS_TEST_OBJS:.o=.dep) +deps_resolve_address_using_native_resolver_test: $(RESOLVE_ADDRESS_USING_NATIVE_RESOLVER_TEST_OBJS:.o=.dep) ifneq ($(NO_SECURE),true) ifneq ($(NO_DEPS),true) --include $(RESOLVE_ADDRESS_TEST_OBJS:.o=.dep) +-include $(RESOLVE_ADDRESS_USING_NATIVE_RESOLVER_TEST_OBJS:.o=.dep) endif endif @@ -17076,6 +17059,49 @@ endif endif +CLIENT_CALLBACK_END2END_TEST_SRC = \ + test/cpp/end2end/client_callback_end2end_test.cc \ + +CLIENT_CALLBACK_END2END_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CLIENT_CALLBACK_END2END_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/client_callback_end2end_test: openssl_dep_error + +else + + + + +ifeq ($(NO_PROTOBUF),true) + +# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+. + +$(BINDIR)/$(CONFIG)/client_callback_end2end_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/client_callback_end2end_test: $(PROTOBUF_DEP) $(CLIENT_CALLBACK_END2END_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(CLIENT_CALLBACK_END2END_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/client_callback_end2end_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/end2end/client_callback_end2end_test.o: $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + +deps_client_callback_end2end_test: $(CLIENT_CALLBACK_END2END_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(CLIENT_CALLBACK_END2END_TEST_OBJS:.o=.dep) +endif +endif + + CLIENT_CHANNEL_STRESS_TEST_SRC = \ $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.pb.cc $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc \ test/cpp/client/client_channel_stress_test.cc \ diff --git a/bazel/grpc_build_system.bzl b/bazel/grpc_build_system.bzl index b3f97650e8c..159ebd5d1fe 100644 --- a/bazel/grpc_build_system.bzl +++ b/bazel/grpc_build_system.bzl @@ -24,7 +24,7 @@ # # The set of pollers to test against if a test exercises polling -POLLERS = ["epollex", "epollsig", "epoll1", "poll", "poll-cv"] +POLLERS = ["epollex", "epoll1", "poll", "poll-cv"] def if_not_windows(a): return select({ diff --git a/build.yaml b/build.yaml index a50a0a4ab65..f3895bb7026 100644 --- a/build.yaml +++ b/build.yaml @@ -266,7 +266,6 @@ filegroups: - src/core/lib/iomgr/error.cc - src/core/lib/iomgr/ev_epoll1_linux.cc - src/core/lib/iomgr/ev_epollex_linux.cc - - src/core/lib/iomgr/ev_epollsig_linux.cc - src/core/lib/iomgr/ev_poll_posix.cc - src/core/lib/iomgr/ev_posix.cc - src/core/lib/iomgr/ev_windows.cc @@ -446,7 +445,6 @@ filegroups: - src/core/lib/iomgr/error_internal.h - src/core/lib/iomgr/ev_epoll1_linux.h - src/core/lib/iomgr/ev_epollex_linux.h - - src/core/lib/iomgr/ev_epollsig_linux.h - src/core/lib/iomgr/ev_poll_posix.h - src/core/lib/iomgr/ev_posix.h - src/core/lib/iomgr/exec_ctx.h @@ -936,6 +934,7 @@ filegroups: - src/core/ext/transport/chttp2/transport/frame_settings.h - src/core/ext/transport/chttp2/transport/frame_window_update.h - src/core/ext/transport/chttp2/transport/hpack_encoder.h + - src/core/ext/transport/chttp2/transport/hpack_mapping.h - src/core/ext/transport/chttp2/transport/hpack_parser.h - src/core/ext/transport/chttp2/transport/hpack_table.h - src/core/ext/transport/chttp2/transport/http2_settings.h @@ -957,6 +956,7 @@ filegroups: - src/core/ext/transport/chttp2/transport/frame_settings.cc - src/core/ext/transport/chttp2/transport/frame_window_update.cc - src/core/ext/transport/chttp2/transport/hpack_encoder.cc + - src/core/ext/transport/chttp2/transport/hpack_mapping.cc - src/core/ext/transport/chttp2/transport/hpack_parser.cc - src/core/ext/transport/chttp2/transport/hpack_table.cc - src/core/ext/transport/chttp2/transport/http2_settings.cc @@ -1169,7 +1169,9 @@ filegroups: - include/grpcpp/impl/codegen/byte_buffer.h - include/grpcpp/impl/codegen/call.h - include/grpcpp/impl/codegen/call_hook.h + - include/grpcpp/impl/codegen/callback_common.h - include/grpcpp/impl/codegen/channel_interface.h + - include/grpcpp/impl/codegen/client_callback.h - include/grpcpp/impl/codegen/client_context.h - include/grpcpp/impl/codegen/client_unary_call.h - include/grpcpp/impl/codegen/completion_queue.h @@ -1297,6 +1299,7 @@ filegroups: - include/grpcpp/support/async_unary_call.h - include/grpcpp/support/byte_buffer.h - include/grpcpp/support/channel_arguments.h + - include/grpcpp/support/client_callback.h - include/grpcpp/support/config.h - include/grpcpp/support/proto_buffer_reader.h - include/grpcpp/support/proto_buffer_writer.h @@ -2367,21 +2370,6 @@ targets: - uv platforms: - linux -- name: ev_epollsig_linux_test - cpu_cost: 3 - build: test - language: c - src: - - test/core/iomgr/ev_epollsig_linux_test.cc - deps: - - grpc_test_util - - grpc - - gpr_test_util - - gpr - exclude_iomgrs: - - uv - platforms: - - linux - name: fake_resolver_test build: test language: c @@ -3333,11 +3321,11 @@ targets: - gpr_test_util - gpr uses_polling: false -- name: pollset_set_test +- name: resolve_address_posix_test build: test language: c src: - - test/core/iomgr/pollset_set_test.cc + - test/core/iomgr/resolve_address_posix_test.cc deps: - grpc_test_util - grpc @@ -3346,24 +3334,22 @@ targets: exclude_iomgrs: - uv platforms: + - mac - linux -- name: resolve_address_posix_test + - posix +- name: resolve_address_using_ares_resolver_test build: test language: c src: - - test/core/iomgr/resolve_address_posix_test.cc + - test/core/iomgr/resolve_address_test.cc deps: - grpc_test_util - grpc - gpr_test_util - gpr - exclude_iomgrs: - - uv - platforms: - - mac - - linux - - posix -- name: resolve_address_test + args: + - --resolver=ares +- name: resolve_address_using_native_resolver_test build: test language: c src: @@ -3373,6 +3359,8 @@ targets: - grpc - gpr_test_util - gpr + args: + - --resolver=native - name: resource_quota_test cpu_cost: 30 build: test @@ -4465,6 +4453,20 @@ targets: - grpc - gpr_test_util - gpr +- name: client_callback_end2end_test + gtest: true + cpu_cost: 0.5 + build: test + language: c++ + src: + - test/cpp/end2end/client_callback_end2end_test.cc + deps: + - grpc++_test_util + - grpc_test_util + - grpc++ + - grpc + - gpr_test_util + - gpr - name: client_channel_stress_test gtest: false build: test @@ -5827,6 +5829,7 @@ defaults: COREFLAGS: -fno-rtti -fno-exceptions CPPFLAGS: -g -Wall -Wextra -Werror -Wno-long-long -Wno-unused-parameter -DOSATOMIC_USE_INLINED=1 -Wno-deprecated-declarations -Ithird_party/nanopb -DPB_FIELD_32BIT + CXXFLAGS: -Wnon-virtual-dtor LDFLAGS: -g zlib: CFLAGS: -Wno-sign-conversion -Wno-conversion -Wno-unused-value -Wno-implicit-function-declaration diff --git a/config.m4 b/config.m4 index af3624cdd1b..1796c8c8e21 100644 --- a/config.m4 +++ b/config.m4 @@ -118,7 +118,6 @@ if test "$PHP_GRPC" != "no"; then src/core/lib/iomgr/error.cc \ src/core/lib/iomgr/ev_epoll1_linux.cc \ src/core/lib/iomgr/ev_epollex_linux.cc \ - src/core/lib/iomgr/ev_epollsig_linux.cc \ src/core/lib/iomgr/ev_poll_posix.cc \ src/core/lib/iomgr/ev_posix.cc \ src/core/lib/iomgr/ev_windows.cc \ @@ -249,6 +248,7 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/transport/chttp2/transport/frame_settings.cc \ src/core/ext/transport/chttp2/transport/frame_window_update.cc \ src/core/ext/transport/chttp2/transport/hpack_encoder.cc \ + src/core/ext/transport/chttp2/transport/hpack_mapping.cc \ src/core/ext/transport/chttp2/transport/hpack_parser.cc \ src/core/ext/transport/chttp2/transport/hpack_table.cc \ src/core/ext/transport/chttp2/transport/http2_settings.cc \ diff --git a/config.w32 b/config.w32 index ad91ee40bd7..6cdd2dac8b0 100644 --- a/config.w32 +++ b/config.w32 @@ -93,7 +93,6 @@ if (PHP_GRPC != "no") { "src\\core\\lib\\iomgr\\error.cc " + "src\\core\\lib\\iomgr\\ev_epoll1_linux.cc " + "src\\core\\lib\\iomgr\\ev_epollex_linux.cc " + - "src\\core\\lib\\iomgr\\ev_epollsig_linux.cc " + "src\\core\\lib\\iomgr\\ev_poll_posix.cc " + "src\\core\\lib\\iomgr\\ev_posix.cc " + "src\\core\\lib\\iomgr\\ev_windows.cc " + @@ -224,6 +223,7 @@ if (PHP_GRPC != "no") { "src\\core\\ext\\transport\\chttp2\\transport\\frame_settings.cc " + "src\\core\\ext\\transport\\chttp2\\transport\\frame_window_update.cc " + "src\\core\\ext\\transport\\chttp2\\transport\\hpack_encoder.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\hpack_mapping.cc " + "src\\core\\ext\\transport\\chttp2\\transport\\hpack_parser.cc " + "src\\core\\ext\\transport\\chttp2\\transport\\hpack_table.cc " + "src\\core\\ext\\transport\\chttp2\\transport\\http2_settings.cc " + diff --git a/doc/interop-test-descriptions.md b/doc/interop-test-descriptions.md index 3c33189196e..1d6535d7ea0 100644 --- a/doc/interop-test-descriptions.md +++ b/doc/interop-test-descriptions.md @@ -944,6 +944,11 @@ the experimental flag, `soak_iterations`. This tests puts stress on several gRPC components; the resolver, the load balancer, and the RPC hotpath. +#### long_lived_channel + +The client performs a number of large_unary RPCs over a single long-lived +channel with a fixed but configurable interval between each RPC. + ### TODO Tests #### High priority: diff --git a/etc/roots.pem b/etc/roots.pem index c22dfe69f87..3e6bbcd76ea 100644 --- a/etc/roots.pem +++ b/etc/roots.pem @@ -4317,3 +4317,26 @@ JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R 8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4 5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= -----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GC CA" +# Serial: 44084345621038548146064804565436152554 +# MD5 Fingerprint: a9:d6:b9:2d:2f:93:64:f8:a5:69:ca:91:e9:68:07:23 +# SHA1 Fingerprint: e0:11:84:5e:34:de:be:88:81:b9:9c:f6:16:26:d1:96:1f:c3:b9:31 +# SHA256 Fingerprint: 85:60:f9:1c:36:24:da:ba:95:70:b5:fe:a0:db:e3:6f:f1:1a:83:23:be:94:86:85:4f:b3:f3:4a:55:71:19:8d +-----BEGIN CERTIFICATE----- +MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw +CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91 +bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg +Um9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJ +BgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3Vu +ZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2JhbCBS +b290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4ni +eUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4W +p2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7T +rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV +57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg +Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 +-----END CERTIFICATE----- diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec index 581b9246bc5..3ca132f7c38 100644 --- a/gRPC-C++.podspec +++ b/gRPC-C++.podspec @@ -111,6 +111,7 @@ Pod::Spec.new do |s| 'include/grpcpp/support/async_unary_call.h', 'include/grpcpp/support/byte_buffer.h', 'include/grpcpp/support/channel_arguments.h', + 'include/grpcpp/support/client_callback.h', 'include/grpcpp/support/config.h', 'include/grpcpp/support/proto_buffer_reader.h', 'include/grpcpp/support/proto_buffer_writer.h', @@ -127,7 +128,9 @@ Pod::Spec.new do |s| 'include/grpcpp/impl/codegen/byte_buffer.h', 'include/grpcpp/impl/codegen/call.h', 'include/grpcpp/impl/codegen/call_hook.h', + 'include/grpcpp/impl/codegen/callback_common.h', 'include/grpcpp/impl/codegen/channel_interface.h', + 'include/grpcpp/impl/codegen/client_callback.h', 'include/grpcpp/impl/codegen/client_context.h', 'include/grpcpp/impl/codegen/client_unary_call.h', 'include/grpcpp/impl/codegen/completion_queue.h', @@ -251,6 +254,7 @@ Pod::Spec.new do |s| 'src/core/ext/transport/chttp2/transport/frame_settings.h', 'src/core/ext/transport/chttp2/transport/frame_window_update.h', 'src/core/ext/transport/chttp2/transport/hpack_encoder.h', + 'src/core/ext/transport/chttp2/transport/hpack_mapping.h', 'src/core/ext/transport/chttp2/transport/hpack_parser.h', 'src/core/ext/transport/chttp2/transport/hpack_table.h', 'src/core/ext/transport/chttp2/transport/http2_settings.h', @@ -392,7 +396,6 @@ Pod::Spec.new do |s| 'src/core/lib/iomgr/error_internal.h', 'src/core/lib/iomgr/ev_epoll1_linux.h', 'src/core/lib/iomgr/ev_epollex_linux.h', - 'src/core/lib/iomgr/ev_epollsig_linux.h', 'src/core/lib/iomgr/ev_poll_posix.h', 'src/core/lib/iomgr/ev_posix.h', 'src/core/lib/iomgr/exec_ctx.h', @@ -582,7 +585,6 @@ Pod::Spec.new do |s| 'src/core/lib/iomgr/error_internal.h', 'src/core/lib/iomgr/ev_epoll1_linux.h', 'src/core/lib/iomgr/ev_epollex_linux.h', - 'src/core/lib/iomgr/ev_epollsig_linux.h', 'src/core/lib/iomgr/ev_poll_posix.h', 'src/core/lib/iomgr/ev_posix.h', 'src/core/lib/iomgr/exec_ctx.h', diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 5a82a4200a2..0c3fbe0a254 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -181,7 +181,7 @@ Pod::Spec.new do |s| ss.header_mappings_dir = '.' ss.libraries = 'z' ss.dependency "#{s.name}/Interface", version - ss.dependency 'BoringSSL-GRPC', '0.0.1' + ss.dependency 'BoringSSL-GRPC', '0.0.2' ss.dependency 'nanopb', '~> 0.3' ss.compiler_flags = '-DGRPC_SHADOW_BORINGSSL_SYMBOLS' @@ -263,6 +263,7 @@ Pod::Spec.new do |s| 'src/core/ext/transport/chttp2/transport/frame_settings.h', 'src/core/ext/transport/chttp2/transport/frame_window_update.h', 'src/core/ext/transport/chttp2/transport/hpack_encoder.h', + 'src/core/ext/transport/chttp2/transport/hpack_mapping.h', 'src/core/ext/transport/chttp2/transport/hpack_parser.h', 'src/core/ext/transport/chttp2/transport/hpack_table.h', 'src/core/ext/transport/chttp2/transport/http2_settings.h', @@ -404,7 +405,6 @@ Pod::Spec.new do |s| 'src/core/lib/iomgr/error_internal.h', 'src/core/lib/iomgr/ev_epoll1_linux.h', 'src/core/lib/iomgr/ev_epollex_linux.h', - 'src/core/lib/iomgr/ev_epollsig_linux.h', 'src/core/lib/iomgr/ev_poll_posix.h', 'src/core/lib/iomgr/ev_posix.h', 'src/core/lib/iomgr/exec_ctx.h', @@ -550,7 +550,6 @@ Pod::Spec.new do |s| 'src/core/lib/iomgr/error.cc', 'src/core/lib/iomgr/ev_epoll1_linux.cc', 'src/core/lib/iomgr/ev_epollex_linux.cc', - 'src/core/lib/iomgr/ev_epollsig_linux.cc', 'src/core/lib/iomgr/ev_poll_posix.cc', 'src/core/lib/iomgr/ev_posix.cc', 'src/core/lib/iomgr/ev_windows.cc', @@ -681,6 +680,7 @@ Pod::Spec.new do |s| 'src/core/ext/transport/chttp2/transport/frame_settings.cc', 'src/core/ext/transport/chttp2/transport/frame_window_update.cc', 'src/core/ext/transport/chttp2/transport/hpack_encoder.cc', + 'src/core/ext/transport/chttp2/transport/hpack_mapping.cc', 'src/core/ext/transport/chttp2/transport/hpack_parser.cc', 'src/core/ext/transport/chttp2/transport/hpack_table.cc', 'src/core/ext/transport/chttp2/transport/http2_settings.cc', @@ -866,6 +866,7 @@ Pod::Spec.new do |s| 'src/core/ext/transport/chttp2/transport/frame_settings.h', 'src/core/ext/transport/chttp2/transport/frame_window_update.h', 'src/core/ext/transport/chttp2/transport/hpack_encoder.h', + 'src/core/ext/transport/chttp2/transport/hpack_mapping.h', 'src/core/ext/transport/chttp2/transport/hpack_parser.h', 'src/core/ext/transport/chttp2/transport/hpack_table.h', 'src/core/ext/transport/chttp2/transport/http2_settings.h', @@ -1007,7 +1008,6 @@ Pod::Spec.new do |s| 'src/core/lib/iomgr/error_internal.h', 'src/core/lib/iomgr/ev_epoll1_linux.h', 'src/core/lib/iomgr/ev_epollex_linux.h', - 'src/core/lib/iomgr/ev_epollsig_linux.h', 'src/core/lib/iomgr/ev_poll_posix.h', 'src/core/lib/iomgr/ev_posix.h', 'src/core/lib/iomgr/exec_ctx.h', @@ -1307,5 +1307,6 @@ Pod::Spec.new do |s| s.prepare_command = <<-END_OF_COMMAND find src/core/ -type f ! -path '*.grpc_back' -print0 | xargs -0 -L1 sed -E -i'.grpc_back' 's;#include "(pb(_.*)?\\.h)";#include ;g' find src/core/ -type f -path '*.grpc_back' -print0 | xargs -0 rm + find src/core/ -type f \\( -path '*.h' -or -path '*.cc' \\) -print0 | xargs -0 -L1 sed -E -i'.grpc_back' 's;#include #else /* musl libc */ #define GPR_MUSL_LIBC_COMPAT 1 #endif diff --git a/include/grpcpp/alarm.h b/include/grpcpp/alarm.h index f484610a6ed..365feb4eb95 100644 --- a/include/grpcpp/alarm.h +++ b/include/grpcpp/alarm.h @@ -21,6 +21,8 @@ #ifndef GRPCPP_ALARM_H #define GRPCPP_ALARM_H +#include + #include #include #include @@ -76,8 +78,33 @@ class Alarm : private GrpcLibraryCodegen { /// has already fired has no effect. void Cancel(); + /// NOTE: class experimental_type is not part of the public API of this class + /// TODO(vjpai): Move these contents to the public API of Alarm when + /// they are no longer experimental + class experimental_type { + public: + explicit experimental_type(Alarm* alarm) : alarm_(alarm) {} + + /// Set an alarm to invoke callback \a f. The argument to the callback + /// states whether the alarm expired at \a deadline (true) or was cancelled + /// (false) + template + void Set(const T& deadline, std::function f) { + alarm_->SetInternal(TimePoint(deadline).raw_time(), std::move(f)); + } + + private: + Alarm* alarm_; + }; + + /// NOTE: The function experimental() is not stable public API. It is a view + /// to the experimental components of this class. It may be changed or removed + /// at any time. + experimental_type experimental() { return experimental_type(this); } + private: void SetInternal(CompletionQueue* cq, gpr_timespec deadline, void* tag); + void SetInternal(gpr_timespec deadline, std::function f); internal::CompletionQueueTag* alarm_; }; diff --git a/include/grpcpp/channel.h b/include/grpcpp/channel.h index fed02bf7bc7..f1dba5b8ad1 100644 --- a/include/grpcpp/channel.h +++ b/include/grpcpp/channel.h @@ -78,8 +78,19 @@ class Channel final : public ChannelInterface, bool WaitForStateChangeImpl(grpc_connectivity_state last_observed, gpr_timespec deadline) override; + CompletionQueue* CallbackCQ() override; + const grpc::string host_; grpc_channel* const c_channel_; // owned + + // mu_ protects callback_cq_ (the per-channel callbackable completion queue) + std::mutex mu_; + + // callback_cq_ references the callbackable completion queue associated + // with this channel (if any). It is set on the first call to CallbackCQ(). + // It is _not owned_ by the channel; ownership belongs with its internal + // shutdown callback tag (invoked when the CQ is fully shutdown). + CompletionQueue* callback_cq_ = nullptr; }; } // namespace grpc diff --git a/include/grpcpp/generic/generic_stub.h b/include/grpcpp/generic/generic_stub.h index 92405a43fae..d509d9a5206 100644 --- a/include/grpcpp/generic/generic_stub.h +++ b/include/grpcpp/generic/generic_stub.h @@ -19,9 +19,12 @@ #ifndef GRPCPP_GENERIC_GENERIC_STUB_H #define GRPCPP_GENERIC_GENERIC_STUB_H +#include + #include #include #include +#include namespace grpc { @@ -62,6 +65,26 @@ class GenericStub final { ClientContext* context, const grpc::string& method, CompletionQueue* cq, void* tag); + /// NOTE: class experimental_type is not part of the public API of this class + /// TODO(vjpai): Move these contents to the public API of GenericStub when + /// they are no longer experimental + class experimental_type { + public: + explicit experimental_type(GenericStub* stub) : stub_(stub) {} + + void UnaryCall(ClientContext* context, const grpc::string& method, + const ByteBuffer* request, ByteBuffer* response, + std::function on_completion); + + private: + GenericStub* stub_; + }; + + /// NOTE: The function experimental() is not stable public API. It is a view + /// to the experimental components of this class. It may be changed or removed + /// at any time. + experimental_type experimental() { return experimental_type(this); } + private: std::shared_ptr channel_; }; diff --git a/include/grpcpp/impl/codegen/async_stream.h b/include/grpcpp/impl/codegen/async_stream.h index b306cd3604f..6e58fd0eefd 100644 --- a/include/grpcpp/impl/codegen/async_stream.h +++ b/include/grpcpp/impl/codegen/async_stream.h @@ -64,7 +64,7 @@ class ClientAsyncStreamingInterface { /// earlier call to \a AsyncReaderInterface::Read that yielded a failed /// result, e.g. cq->Next(&read_tag, &ok) filled in 'ok' with 'false'). /// - /// This function will return when either: + /// The tag will be returned when either: /// - all incoming messages have been read and the server has returned /// a status. /// - the server has returned a non-OK status. @@ -114,6 +114,9 @@ class AsyncWriterInterface { /// queue BEFORE calling Write again. /// This is thread-safe with respect to \a AsyncReaderInterface::Read /// + /// gRPC doesn't take ownership or a reference to \a msg, so it is safe to + /// to deallocate once Write returns. + /// /// \param[in] msg The message to be written. /// \param[in] tag The tag identifying the operation. virtual void Write(const W& msg, void* tag) = 0; @@ -127,6 +130,9 @@ class AsyncWriterInterface { /// WriteOptions \a options is used to set the write options of this message. /// This is thread-safe with respect to \a AsyncReaderInterface::Read /// + /// gRPC doesn't take ownership or a reference to \a msg, so it is safe to + /// to deallocate once Write returns. + /// /// \param[in] msg The message to be written. /// \param[in] options The WriteOptions to be used to write this message. /// \param[in] tag The tag identifying the operation. @@ -144,6 +150,9 @@ class AsyncWriterInterface { /// the flow control window size. If \a msg size is larger than the window /// size, it will be sent on wire without buffering. /// + /// gRPC doesn't take ownership or a reference to \a msg, so it is safe to + /// to deallocate once Write returns. + /// /// \param[in] msg The message to be written. /// \param[in] options The WriteOptions to be used to write this message. /// \param[in] tag The tag identifying the operation. @@ -651,6 +660,9 @@ class ServerAsyncReaderInterface /// metadata (if not sent already), response message, and status, or if /// some failure occurred when trying to do so. /// + /// gRPC doesn't take ownership or a reference to \a msg or \a status, so it + /// is safe to to deallocate once Finish returns. + /// /// \param[in] tag Tag identifying this request. /// \param[in] status To be sent to the client as the result of this call. /// \param[in] msg To be sent to the client as the response for this call. @@ -671,6 +683,9 @@ class ServerAsyncReaderInterface /// metadata (if not sent already), and status, or if some failure occurred /// when trying to do so. /// + /// gRPC doesn't take ownership or a reference to \a status, so it is safe to + /// to deallocate once FinishWithError returns. + /// /// \param[in] tag Tag identifying this request. /// \param[in] status To be sent to the client as the result of this call. /// - Note: \a status must have a non-OK code. @@ -718,6 +733,9 @@ class ServerAsyncReader final : public ServerAsyncReaderInterface { /// initial and trailing metadata. /// /// Note: \a msg is not sent if \a status has a non-OK code. + /// + /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it + /// is safe to to deallocate once Finish returns. void Finish(const W& msg, const Status& status, void* tag) override { finish_ops_.set_output_tag(tag); if (!ctx_->sent_initial_metadata_) { @@ -744,6 +762,9 @@ class ServerAsyncReader final : public ServerAsyncReaderInterface { /// - also sends initial metadata if not alreay sent. /// - uses the \a ServerContext associated with this call to send possible /// initial and trailing metadata. + /// + /// gRPC doesn't take ownership or a reference to \a status, so it is safe to + /// to deallocate once FinishWithError returns. void FinishWithError(const Status& status, void* tag) override { GPR_CODEGEN_ASSERT(!status.ok()); finish_ops_.set_output_tag(tag); @@ -794,6 +815,9 @@ class ServerAsyncWriterInterface /// metadata (if not sent already), response message, and status, or if /// some failure occurred when trying to do so. /// + /// gRPC doesn't take ownership or a reference to \a status, so it is safe to + /// to deallocate once Finish returns. + /// /// \param[in] tag Tag identifying this request. /// \param[in] status To be sent to the client as the result of this call. virtual void Finish(const Status& status, void* tag) = 0; @@ -805,6 +829,9 @@ class ServerAsyncWriterInterface /// WriteAndFinish is equivalent of performing WriteLast and Finish /// in a single step. /// + /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it + /// is safe to to deallocate once WriteAndFinish returns. + /// /// \param[in] msg The message to be written. /// \param[in] options The WriteOptions to be used to write this message. /// \param[in] status The Status that server returns to client. @@ -868,6 +895,9 @@ class ServerAsyncWriter final : public ServerAsyncWriterInterface { /// for sending trailing (and initial) metadata to the client. /// /// Note: \a status must have an OK code. + /// + /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it + /// is safe to to deallocate once WriteAndFinish returns. void WriteAndFinish(const W& msg, WriteOptions options, const Status& status, void* tag) override { write_ops_.set_output_tag(tag); @@ -886,6 +916,9 @@ class ServerAsyncWriter final : public ServerAsyncWriterInterface { /// /// Note: there are no restrictions are the code of /// \a status,it may be non-OK + /// + /// gRPC doesn't take ownership or a reference to \a status, so it is safe to + /// to deallocate once Finish returns. void Finish(const Status& status, void* tag) override { finish_ops_.set_output_tag(tag); EnsureInitialMetadataSent(&finish_ops_); @@ -945,6 +978,9 @@ class ServerAsyncReaderWriterInterface /// metadata (if not sent already), response message, and status, or if some /// failure occurred when trying to do so. /// + /// gRPC doesn't take ownership or a reference to \a status, so it is safe to + /// to deallocate once Finish returns. + /// /// \param[in] tag Tag identifying this request. /// \param[in] status To be sent to the client as the result of this call. virtual void Finish(const Status& status, void* tag) = 0; @@ -956,6 +992,9 @@ class ServerAsyncReaderWriterInterface /// WriteAndFinish is equivalent of performing WriteLast and Finish in a /// single step. /// + /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it + /// is safe to to deallocate once WriteAndFinish returns. + /// /// \param[in] msg The message to be written. /// \param[in] options The WriteOptions to be used to write this message. /// \param[in] status The Status that server returns to client. @@ -1027,6 +1066,9 @@ class ServerAsyncReaderWriter final /// for sending trailing (and initial) metadata to the client. /// /// Note: \a status must have an OK code. + // + /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it + /// is safe to to deallocate once WriteAndFinish returns. void WriteAndFinish(const W& msg, WriteOptions options, const Status& status, void* tag) override { write_ops_.set_output_tag(tag); @@ -1045,6 +1087,9 @@ class ServerAsyncReaderWriter final /// /// Note: there are no restrictions are the code of \a status, /// it may be non-OK + // + /// gRPC doesn't take ownership or a reference to \a status, so it is safe to + /// to deallocate once Finish returns. void Finish(const Status& status, void* tag) override { finish_ops_.set_output_tag(tag); EnsureInitialMetadataSent(&finish_ops_); diff --git a/include/grpcpp/impl/codegen/call.h b/include/grpcpp/impl/codegen/call.h index e94adada940..7cadea00555 100644 --- a/include/grpcpp/impl/codegen/call.h +++ b/include/grpcpp/impl/codegen/call.h @@ -599,6 +599,11 @@ class CallOpSetInterface : public CompletionQueueTag { /// Fills in grpc_op, starting from ops[*nops] and moving /// upwards. virtual void FillOps(grpc_call* call, grpc_op* ops, size_t* nops) = 0; + + /// Get the tag to be used at the core completion queue. Generally, the + /// value of cq_tag will be "this". However, it can be overridden if we + /// want core to process the tag differently (e.g., as a core callback) + virtual void* cq_tag() = 0; }; /// Primary implementation of CallOpSetInterface. @@ -618,7 +623,7 @@ class CallOpSet : public CallOpSetInterface, public Op5, public Op6 { public: - CallOpSet() : return_tag_(this), call_(nullptr) {} + CallOpSet() : cq_tag_(this), return_tag_(this), call_(nullptr) {} void FillOps(grpc_call* call, grpc_op* ops, size_t* nops) override { this->Op1::AddOp(ops, nops); this->Op2::AddOp(ops, nops); @@ -645,7 +650,16 @@ class CallOpSet : public CallOpSetInterface, void set_output_tag(void* return_tag) { return_tag_ = return_tag; } + void* cq_tag() override { return cq_tag_; } + + /// set_cq_tag is used to provide a different core CQ tag than "this". + /// This is used for callback-based tags, where the core tag is the core + /// callback function. It does not change the use or behavior of any other + /// function (such as FinalizeResult) + void set_cq_tag(void* cq_tag) { cq_tag_ = cq_tag; } + private: + void* cq_tag_; void* return_tag_; grpc_call* call_; }; diff --git a/include/grpcpp/impl/codegen/callback_common.h b/include/grpcpp/impl/codegen/callback_common.h new file mode 100644 index 00000000000..ca2f867d04d --- /dev/null +++ b/include/grpcpp/impl/codegen/callback_common.h @@ -0,0 +1,164 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_CALLBACK_COMMON_H +#define GRPCPP_IMPL_CODEGEN_CALLBACK_COMMON_H + +#include + +#include +#include +#include +#include +#include +#include + +namespace grpc { +namespace internal { + +/// An exception-safe way of invoking a user-specified callback function +template +void CatchingCallback(Func&& func, Arg&& arg) { +#if GRPC_ALLOW_EXCEPTIONS + try { + func(arg); + } catch (...) { + // nothing to return or change here, just don't crash the library + } +#else // GRPC_ALLOW_EXCEPTIONS + func(arg); +#endif // GRPC_ALLOW_EXCEPTIONS +} + +// The contract on these tags is that they are single-shot. They must be +// constructed and then fired at exactly one point. There is no expectation +// that they can be reused without reconstruction. + +class CallbackWithStatusTag + : public grpc_experimental_completion_queue_functor { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(CallbackWithStatusTag)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + CallbackWithStatusTag(grpc_call* call, std::function f, + CompletionQueueTag* ops) + : call_(call), func_(std::move(f)), ops_(ops) { + g_core_codegen_interface->grpc_call_ref(call); + functor_run = &CallbackWithStatusTag::StaticRun; + } + ~CallbackWithStatusTag() {} + Status* status_ptr() { return &status_; } + + // force_run can not be performed on a tag if operations using this tag + // have been sent to PerformOpsOnCall. It is intended for error conditions + // that are detected before the operations are internally processed. + void force_run(Status s) { + status_ = std::move(s); + Run(true); + } + + private: + grpc_call* call_; + std::function func_; + CompletionQueueTag* ops_; + Status status_; + + static void StaticRun(grpc_experimental_completion_queue_functor* cb, + int ok) { + static_cast(cb)->Run(static_cast(ok)); + } + void Run(bool ok) { + void* ignored = ops_; + + GPR_CODEGEN_ASSERT(ops_->FinalizeResult(&ignored, &ok)); + GPR_CODEGEN_ASSERT(ignored == ops_); + + // Last use of func_ or status_, so ok to move them out + CatchingCallback(std::move(func_), std::move(status_)); + + func_ = nullptr; // reset to clear this out for sure + status_ = Status(); // reset to clear this out for sure + g_core_codegen_interface->grpc_call_unref(call_); + } +}; + +class CallbackWithSuccessTag + : public grpc_experimental_completion_queue_functor { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(CallbackWithSuccessTag)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + CallbackWithSuccessTag(grpc_call* call, std::function f, + CompletionQueueTag* ops) + : call_(call), func_(std::move(f)), ops_(ops) { + g_core_codegen_interface->grpc_call_ref(call); + functor_run = &CallbackWithSuccessTag::StaticRun; + } + + CompletionQueueTag* ops() { return ops_; } + + // force_run can not be performed on a tag if operations using this tag + // have been sent to PerformOpsOnCall. It is intended for error conditions + // that are detected before the operations are internally processed. + void force_run(bool ok) { Run(ok); } + + private: + grpc_call* call_; + std::function func_; + CompletionQueueTag* ops_; + + static void StaticRun(grpc_experimental_completion_queue_functor* cb, + int ok) { + static_cast(cb)->Run(static_cast(ok)); + } + void Run(bool ok) { + void* ignored = ops_; + bool new_ok = ok; + GPR_CODEGEN_ASSERT(ops_->FinalizeResult(&ignored, &new_ok)); + GPR_CODEGEN_ASSERT(ignored == ops_); + + // Last use of func_, so ok to move it out for rvalue call above + CatchingCallback(std::move(func_), ok); + + func_ = nullptr; // reset to clear this out for sure + g_core_codegen_interface->grpc_call_unref(call_); + } +}; + +} // namespace internal +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_CALLBACK_COMMON_H diff --git a/include/grpcpp/impl/codegen/channel_interface.h b/include/grpcpp/impl/codegen/channel_interface.h index ec1c6c25d87..b257acc1abd 100644 --- a/include/grpcpp/impl/codegen/channel_interface.h +++ b/include/grpcpp/impl/codegen/channel_interface.h @@ -41,6 +41,8 @@ class CallOpSetInterface; class RpcMethod; template class BlockingUnaryCallImpl; +template +class CallbackUnaryCallImpl; template class ClientAsyncReaderFactory; template @@ -103,6 +105,8 @@ class ChannelInterface { friend class ::grpc::internal::ClientAsyncResponseReaderFactory; template friend class ::grpc::internal::BlockingUnaryCallImpl; + template + friend class ::grpc::internal::CallbackUnaryCallImpl; friend class ::grpc::internal::RpcMethod; virtual internal::Call CreateCall(const internal::RpcMethod& method, ClientContext* context, @@ -115,6 +119,16 @@ class ChannelInterface { CompletionQueue* cq, void* tag) = 0; virtual bool WaitForStateChangeImpl(grpc_connectivity_state last_observed, gpr_timespec deadline) = 0; + + // EXPERIMENTAL + // A method to get the callbackable completion queue associated with this + // channel. If the return value is nullptr, this channel doesn't support + // callback operations. + // TODO(vjpai): Consider a better default like using a global CQ + // Returns nullptr (rather than being pure) since this is a new method + // and adding a new pure method to an interface would be a breaking change + // (even though this is private and non-API) + virtual CompletionQueue* CallbackCQ() { return nullptr; } }; } // namespace grpc diff --git a/include/grpcpp/impl/codegen/client_callback.h b/include/grpcpp/impl/codegen/client_callback.h new file mode 100644 index 00000000000..4d4faea0635 --- /dev/null +++ b/include/grpcpp/impl/codegen/client_callback.h @@ -0,0 +1,95 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_CLIENT_CALLBACK_H +#define GRPCPP_IMPL_CODEGEN_CLIENT_CALLBACK_H + +#include + +#include +#include +#include +#include +#include +#include + +namespace grpc { + +class Channel; +class ClientContext; +class CompletionQueue; + +namespace internal { +class RpcMethod; + +/// Perform a callback-based unary call +/// TODO(vjpai): Combine as much as possible with the blocking unary call code +template +void CallbackUnaryCall(ChannelInterface* channel, const RpcMethod& method, + ClientContext* context, const InputMessage* request, + OutputMessage* result, + std::function on_completion) { + CallbackUnaryCallImpl x( + channel, method, context, request, result, on_completion); +} + +template +class CallbackUnaryCallImpl { + public: + CallbackUnaryCallImpl(ChannelInterface* channel, const RpcMethod& method, + ClientContext* context, const InputMessage* request, + OutputMessage* result, + std::function on_completion) { + CompletionQueue* cq = channel->CallbackCQ(); + GPR_CODEGEN_ASSERT(cq != nullptr); + Call call(channel->CreateCall(method, context, cq)); + + using FullCallOpSet = + CallOpSet, + CallOpClientSendClose, CallOpClientRecvStatus>; + + auto* ops = new (g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(FullCallOpSet))) FullCallOpSet; + + auto* tag = new (g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(CallbackWithStatusTag))) + CallbackWithStatusTag(call.call(), on_completion, ops); + + // TODO(vjpai): Unify code with sync API as much as possible + Status s = ops->SendMessage(*request); + if (!s.ok()) { + tag->force_run(s); + return; + } + ops->SendInitialMetadata(context->send_initial_metadata_, + context->initial_metadata_flags()); + ops->RecvInitialMetadata(context); + ops->RecvMessage(result); + ops->AllowNoMessage(); + ops->ClientSendClose(); + ops->ClientRecvStatus(context, tag->status_ptr()); + ops->set_cq_tag(tag); + call.PerformOps(ops); + } +}; + +} // namespace internal +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_CLIENT_CALLBACK_H diff --git a/include/grpcpp/impl/codegen/client_context.h b/include/grpcpp/impl/codegen/client_context.h index c6c95409504..46635a541a7 100644 --- a/include/grpcpp/impl/codegen/client_context.h +++ b/include/grpcpp/impl/codegen/client_context.h @@ -68,6 +68,8 @@ class CallOpClientRecvStatus; class CallOpRecvInitialMetadata; template class BlockingUnaryCallImpl; +template +class CallbackUnaryCallImpl; } // namespace internal template @@ -389,6 +391,8 @@ class ClientContext { friend class ::grpc::ClientAsyncResponseReader; template friend class ::grpc::internal::BlockingUnaryCallImpl; + template + friend class ::grpc::internal::CallbackUnaryCallImpl; // Used by friend class CallOpClientRecvStatus void set_debug_error_string(const grpc::string& debug_error_string) { diff --git a/include/grpcpp/impl/codegen/completion_queue.h b/include/grpcpp/impl/codegen/completion_queue.h index 3f7d4fb765f..f52f9a53bea 100644 --- a/include/grpcpp/impl/codegen/completion_queue.h +++ b/include/grpcpp/impl/codegen/completion_queue.h @@ -274,6 +274,9 @@ class CompletionQueue : private GrpcLibraryCodegen { template friend class ::grpc::internal::BlockingUnaryCallImpl; + // Friends that need access to constructor for callback CQ + friend class ::grpc::Channel; + /// EXPERIMENTAL /// Creates a Thread Local cache to store the first event /// On this completion queue queued from this thread. Once diff --git a/include/grpcpp/impl/codegen/completion_queue_tag.h b/include/grpcpp/impl/codegen/completion_queue_tag.h index ffb642c56b5..304386a9ecc 100644 --- a/include/grpcpp/impl/codegen/completion_queue_tag.h +++ b/include/grpcpp/impl/codegen/completion_queue_tag.h @@ -26,10 +26,25 @@ namespace internal { class CompletionQueueTag { public: virtual ~CompletionQueueTag() {} - /// Called prior to returning from Next(), return value is the status of the - /// operation (return status is the default thing to do). If this function - /// returns false, the tag is dropped and not returned from the completion - /// queue + + /// FinalizeResult must be called before informing user code that the + /// operation bound to the underlying core completion queue tag has + /// completed. In practice, this means: + /// + /// 1. For the sync API - before returning from Pluck + /// 2. For the CQ-based async API - before returning from Next + /// 3. For the callback-based API - before invoking the user callback + /// + /// This is the method that translates from core-side tag/status to + /// C++ API-observable tag/status. + /// + /// The return value is the status of the operation (returning status is the + /// general behavior of this function). If this function returns false, the + /// tag is dropped and not returned from the completion queue: this concept is + /// for events that are observed at core but not requested by the user + /// application (e.g., server shutdown, for server unimplemented method + /// responses, or for cases where a server-side RPC doesn't have a completion + /// notification registered using AsyncNotifyWhenDone) virtual bool FinalizeResult(void** tag, bool* status) = 0; }; } // namespace internal diff --git a/include/grpcpp/support/client_callback.h b/include/grpcpp/support/client_callback.h new file mode 100644 index 00000000000..063fdc4f859 --- /dev/null +++ b/include/grpcpp/support/client_callback.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_CLIENT_CALLBACK_H +#define GRPCPP_SUPPORT_CLIENT_CALLBACK_H + +#include + +#endif // GRPCPP_SUPPORT_CLIENT_CALLBACK_H diff --git a/package.xml b/package.xml index 9c0e078e92c..fddc676d512 100644 --- a/package.xml +++ b/package.xml @@ -200,6 +200,7 @@ + @@ -345,7 +346,6 @@ - @@ -491,7 +491,6 @@ - @@ -622,6 +621,7 @@ + diff --git a/src/compiler/cpp_generator.cc b/src/compiler/cpp_generator.cc index 1e0c36451bb..56716493dc3 100644 --- a/src/compiler/cpp_generator.cc +++ b/src/compiler/cpp_generator.cc @@ -128,6 +128,7 @@ grpc::string GetHeaderIncludes(grpc_generator::File* file, ""); } static const char* headers_strs[] = { + "functional", "grpcpp/impl/codegen/async_generic_service.h", "grpcpp/impl/codegen/async_stream.h", "grpcpp/impl/codegen/async_unary_call.h", @@ -547,6 +548,116 @@ void PrintHeaderClientMethod(grpc_generator::Printer* printer, } } +void PrintHeaderClientMethodCallbackInterfacesStart( + grpc_generator::Printer* printer, + std::map* vars) { + // This declares the interface for the callback-based API. The components + // are pure; even though this is new (post-1.0) API, it can be pure because + // it is an entirely new interface that happens to be scoped within + // StubInterface, not new additions to StubInterface itself + printer->Print("class experimental_async_interface {\n"); + // All methods in this new interface are public. There is no need for private + // "Raw" methods since the callback-based API returns unowned raw pointers + printer->Print(" public:\n"); + printer->Indent(); + printer->Print("virtual ~experimental_async_interface() {}\n"); +} + +void PrintHeaderClientMethodCallbackInterfaces( + grpc_generator::Printer* printer, const grpc_generator::Method* method, + std::map* vars, bool is_public) { + // Reserve is_public for future expansion + assert(is_public); + + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + + if (method->NoStreaming()) { + printer->Print(*vars, + "virtual void $Method$(::grpc::ClientContext* context, " + "const $Request$* request, $Response$* response, " + "std::function) = 0;\n"); + } else if (ClientOnlyStreaming(method)) { + // TODO(vjpai): Add support for client-side streaming + } else if (ServerOnlyStreaming(method)) { + // TODO(vjpai): Add support for server-side streaming + } else if (method->BidiStreaming()) { + // TODO(vjpai): Add support for bidi streaming + } +} + +void PrintHeaderClientMethodCallbackInterfacesEnd( + grpc_generator::Printer* printer, + std::map* vars) { + printer->Outdent(); + printer->Print("};\n"); + + // Declare a function to give the async stub contents. It can't be pure + // since this is a new API in StubInterface, but it is meaningless by default + // (since any stub that wants to use it must have its own implementation of + // the callback functions therein), so make the default return value nullptr. + // Intentionally include the word "class" to avoid possible shadowing. + printer->Print( + "virtual class experimental_async_interface* experimental_async() { " + "return nullptr; }\n"); +} + +void PrintHeaderClientMethodCallbackStart( + grpc_generator::Printer* printer, + std::map* vars) { + // This declares the stub entry for the callback-based API. + printer->Print("class experimental_async final :\n"); + printer->Print(" public StubInterface::experimental_async_interface {\n"); + printer->Print(" public:\n"); + printer->Indent(); +} + +void PrintHeaderClientMethodCallback(grpc_generator::Printer* printer, + const grpc_generator::Method* method, + std::map* vars, + bool is_public) { + // Reserve is_public for future expansion + assert(is_public); + + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + + if (method->NoStreaming()) { + printer->Print(*vars, + "void $Method$(::grpc::ClientContext* context, " + "const $Request$* request, $Response$* response, " + "std::function) override;\n"); + } else if (ClientOnlyStreaming(method)) { + // TODO(vjpai): Add support for client-side streaming + } else if (ServerOnlyStreaming(method)) { + // TODO(vjpai): Add support for server-side streaming + } else if (method->BidiStreaming()) { + // TODO(vjpai): Add support for bidi streaming + } +} + +void PrintHeaderClientMethodCallbackEnd( + grpc_generator::Printer* printer, + std::map* vars) { + printer->Outdent(); + printer->Print(" private:\n"); + printer->Indent(); + printer->Print("friend class Stub;\n"); + printer->Print("explicit experimental_async(Stub* stub): stub_(stub) { }\n"); + // include a function with a dummy use of stub_ to avoid an unused + // private member warning for service with no methods + printer->Print("Stub* stub() { return stub_; }\n"); + printer->Print("Stub* stub_;\n"); + printer->Outdent(); + printer->Print("};\n"); + + printer->Print( + "class experimental_async_interface* experimental_async() override { " + "return &async_stub_; }\n"); +} + void PrintHeaderClientMethodData(grpc_generator::Printer* printer, const grpc_generator::Method* method, std::map* vars) { @@ -951,6 +1062,14 @@ void PrintHeaderService(grpc_generator::Printer* printer, true); printer->Print(service->method(i)->GetTrailingComments("//").c_str()); } + PrintHeaderClientMethodCallbackInterfacesStart(printer, vars); + for (int i = 0; i < service->method_count(); ++i) { + printer->Print(service->method(i)->GetLeadingComments("//").c_str()); + PrintHeaderClientMethodCallbackInterfaces(printer, service->method(i).get(), + vars, true); + printer->Print(service->method(i)->GetTrailingComments("//").c_str()); + } + PrintHeaderClientMethodCallbackInterfacesEnd(printer, vars); printer->Outdent(); printer->Print("private:\n"); printer->Indent(); @@ -970,10 +1089,17 @@ void PrintHeaderService(grpc_generator::Printer* printer, for (int i = 0; i < service->method_count(); ++i) { PrintHeaderClientMethod(printer, service->method(i).get(), vars, true); } + PrintHeaderClientMethodCallbackStart(printer, vars); + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderClientMethodCallback(printer, service->method(i).get(), vars, + true); + } + PrintHeaderClientMethodCallbackEnd(printer, vars); printer->Outdent(); printer->Print("\n private:\n"); printer->Indent(); printer->Print("std::shared_ptr< ::grpc::ChannelInterface> channel_;\n"); + printer->Print("class experimental_async async_stub_{this};\n"); for (int i = 0; i < service->method_count(); ++i) { PrintHeaderClientMethod(printer, service->method(i).get(), vars, false); } @@ -1199,10 +1325,12 @@ grpc::string GetSourceIncludes(grpc_generator::File* file, std::map vars; static const char* headers_strs[] = { + "functional", "grpcpp/impl/codegen/async_stream.h", "grpcpp/impl/codegen/async_unary_call.h", "grpcpp/impl/codegen/channel_interface.h", "grpcpp/impl/codegen/client_unary_call.h", + "grpcpp/impl/codegen/client_callback.h", "grpcpp/impl/codegen/method_handler_impl.h", "grpcpp/impl/codegen/rpc_service_method.h", "grpcpp/impl/codegen/service_type.h", @@ -1247,6 +1375,17 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer, " return ::grpc::internal::BlockingUnaryCall" "(channel_.get(), rpcmethod_$Method$_, " "context, request, response);\n}\n\n"); + + printer->Print(*vars, + "void $ns$$Service$::Stub::experimental_async::$Method$(" + "::grpc::ClientContext* context, " + "const $Request$* request, $Response$* response, " + "std::function f) {\n"); + printer->Print(*vars, + " return ::grpc::internal::CallbackUnaryCall" + "(stub_->channel_.get(), stub_->rpcmethod_$Method$_, " + "context, request, response, std::move(f));\n}\n\n"); + for (auto async_prefix : async_prefixes) { (*vars)["AsyncPrefix"] = async_prefix.prefix; (*vars)["AsyncStart"] = async_prefix.start; @@ -1277,6 +1416,9 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer, "rpcmethod_$Method$_, " "context, response);\n" "}\n\n"); + + // TODO(vjpai): Add callback version + for (auto async_prefix : async_prefixes) { (*vars)["AsyncPrefix"] = async_prefix.prefix; (*vars)["AsyncStart"] = async_prefix.start; @@ -1308,6 +1450,9 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer, "rpcmethod_$Method$_, " "context, request);\n" "}\n\n"); + + // TODO(vjpai): Add callback version + for (auto async_prefix : async_prefixes) { (*vars)["AsyncPrefix"] = async_prefix.prefix; (*vars)["AsyncStart"] = async_prefix.start; @@ -1339,6 +1484,9 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer, "rpcmethod_$Method$_, " "context);\n" "}\n\n"); + + // TODO(vjpai): Add callback version + for (auto async_prefix : async_prefixes) { (*vars)["AsyncPrefix"] = async_prefix.prefix; (*vars)["AsyncStart"] = async_prefix.start; diff --git a/src/compiler/ruby_generator.cc b/src/compiler/ruby_generator.cc index c7af9c38fad..e39d8be5d41 100644 --- a/src/compiler/ruby_generator.cc +++ b/src/compiler/ruby_generator.cc @@ -160,12 +160,20 @@ grpc::string GetServices(const FileDescriptor* file) { return output; } + std::string package_name; + + if (file->options().has_ruby_package()) { + package_name = file->options().ruby_package(); + } else { + package_name = file->package(); + } + // Write out a file header. std::map header_comment_vars = ListToDict({ "file.name", file->name(), "file.package", - file->package(), + package_name, }); out.Print("# Generated by the protocol buffer compiler. DO NOT EDIT!\n"); out.Print(header_comment_vars, @@ -190,7 +198,7 @@ grpc::string GetServices(const FileDescriptor* file) { // Write out services within the modules out.Print("\n"); - std::vector modules = Split(file->package(), '.'); + std::vector modules = Split(package_name, '.'); for (size_t i = 0; i < modules.size(); ++i) { std::map module_vars = ListToDict({ "module.name", diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc index 1ee1925a259..00611900169 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc @@ -852,10 +852,12 @@ void GrpcLb::BalancerCallState::OnBalancerMessageReceivedLocked( } } else { // No valid initial response or serverlist found. + char* response_slice_str = + grpc_dump_slice(response_slice, GPR_DUMP_ASCII | GPR_DUMP_HEX); gpr_log(GPR_ERROR, "[grpclb %p] Invalid LB response received: '%s'. Ignoring.", - grpclb_policy, - grpc_dump_slice(response_slice, GPR_DUMP_ASCII | GPR_DUMP_HEX)); + grpclb_policy, response_slice_str); + gpr_free(response_slice_str); } grpc_slice_unref_internal(response_slice); if (!grpclb_policy->shutting_down_) { diff --git a/src/core/ext/filters/client_channel/parse_address.cc b/src/core/ext/filters/client_channel/parse_address.cc index 7e726dd5624..707beb88769 100644 --- a/src/core/ext/filters/client_channel/parse_address.cc +++ b/src/core/ext/filters/client_channel/parse_address.cc @@ -129,30 +129,37 @@ bool grpc_parse_ipv6_hostport(const char* hostport, grpc_resolved_address* addr, size_t host_without_scope_len = static_cast(host_end - host); uint32_t sin6_scope_id = 0; if (host_without_scope_len > GRPC_INET6_ADDRSTRLEN) { - gpr_log(GPR_ERROR, - "invalid ipv6 address length %zu. Length cannot be greater than " - "GRPC_INET6_ADDRSTRLEN i.e %d)", - host_without_scope_len, GRPC_INET6_ADDRSTRLEN); + if (log_errors) { + gpr_log( + GPR_ERROR, + "invalid ipv6 address length %zu. Length cannot be greater than " + "GRPC_INET6_ADDRSTRLEN i.e %d)", + host_without_scope_len, GRPC_INET6_ADDRSTRLEN); + } goto done; } strncpy(host_without_scope, host, host_without_scope_len); host_without_scope[host_without_scope_len] = '\0'; if (grpc_inet_pton(GRPC_AF_INET6, host_without_scope, &in6->sin6_addr) == 0) { - gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host_without_scope); + if (log_errors) { + gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host_without_scope); + } goto done; } if (gpr_parse_bytes_to_uint32(host_end + 1, strlen(host) - host_without_scope_len - 1, &sin6_scope_id) == 0) { - gpr_log(GPR_ERROR, "invalid ipv6 scope id: '%s'", host_end + 1); + if (log_errors) { + gpr_log(GPR_ERROR, "invalid ipv6 scope id: '%s'", host_end + 1); + } goto done; } // Handle "sin6_scope_id" being type "u_long". See grpc issue #10027. in6->sin6_scope_id = sin6_scope_id; } else { if (grpc_inet_pton(GRPC_AF_INET6, host, &in6->sin6_addr) == 0) { - gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host); + if (log_errors) gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host); goto done; } } @@ -197,3 +204,12 @@ bool grpc_parse_uri(const grpc_uri* uri, grpc_resolved_address* resolved_addr) { gpr_log(GPR_ERROR, "Can't parse scheme '%s'", uri->scheme); return false; } + +uint16_t grpc_strhtons(const char* port) { + if (strcmp(port, "http") == 0) { + return htons(80); + } else if (strcmp(port, "https") == 0) { + return htons(443); + } + return htons(static_cast(atoi(port))); +} diff --git a/src/core/ext/filters/client_channel/parse_address.h b/src/core/ext/filters/client_channel/parse_address.h index 9a88b66edcf..c2af0e6c498 100644 --- a/src/core/ext/filters/client_channel/parse_address.h +++ b/src/core/ext/filters/client_channel/parse_address.h @@ -47,4 +47,7 @@ bool grpc_parse_ipv4_hostport(const char* hostport, grpc_resolved_address* addr, bool grpc_parse_ipv6_hostport(const char* hostport, grpc_resolved_address* addr, bool log_errors); +/* Converts named or numeric port to a uint16 suitable for use in a sockaddr. */ +uint16_t grpc_strhtons(const char* port); + #endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PARSE_ADDRESS_H */ diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc index 485998f5e44..4c795c34c8e 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc @@ -87,15 +87,6 @@ typedef struct grpc_ares_hostbyname_request { static void do_basic_init(void) { gpr_mu_init(&g_init_mu); } -static uint16_t strhtons(const char* port) { - if (strcmp(port, "http") == 0) { - return htons(80); - } else if (strcmp(port, "https") == 0) { - return htons(443); - } - return htons(static_cast(atoi(port))); -} - static void log_address_sorting_list(grpc_lb_addresses* lb_addrs, const char* input_output_str) { for (size_t i = 0; i < lb_addrs->num_addresses; i++) { @@ -139,12 +130,6 @@ void grpc_cares_wrapper_address_sorting_sort(grpc_lb_addresses* lb_addrs) { } } -/* Allow tests to access grpc_ares_wrapper_address_sorting_sort */ -void grpc_cares_wrapper_test_only_address_sorting_sort( - grpc_lb_addresses* lb_addrs) { - grpc_cares_wrapper_address_sorting_sort(lb_addrs); -} - static void grpc_ares_request_ref_locked(grpc_ares_request* r) { r->pending_queries++; } @@ -371,7 +356,8 @@ done: grpc_ares_request_unref_locked(r); } -static grpc_ares_request* grpc_dns_lookup_ares_locked_impl( +static grpc_ares_request* +grpc_dns_lookup_ares_continue_after_check_localhost_and_ip_literals_locked( const char* dns_server, const char* name, const char* default_port, grpc_pollset_set* interested_parties, grpc_closure* on_done, grpc_lb_addresses** addrs, bool check_grpclb, char** service_config_json, @@ -454,12 +440,12 @@ static grpc_ares_request* grpc_dns_lookup_ares_locked_impl( } r->pending_queries = 1; if (grpc_ares_query_ipv6()) { - hr = create_hostbyname_request_locked(r, host, strhtons(port), + hr = create_hostbyname_request_locked(r, host, grpc_strhtons(port), false /* is_balancer */); ares_gethostbyname(*channel, hr->host, AF_INET6, on_hostbyname_done_locked, hr); } - hr = create_hostbyname_request_locked(r, host, strhtons(port), + hr = create_hostbyname_request_locked(r, host, grpc_strhtons(port), false /* is_balancer */); ares_gethostbyname(*channel, hr->host, AF_INET, on_hostbyname_done_locked, hr); @@ -494,6 +480,79 @@ error_cleanup: return nullptr; } +static bool inner_resolve_as_ip_literal_locked(const char* name, + const char* default_port, + grpc_lb_addresses** addrs, + char** host, char** port, + char** hostport) { + gpr_split_host_port(name, host, port); + if (*host == nullptr) { + gpr_log(GPR_ERROR, + "Failed to parse %s to host:port while attempting to resolve as ip " + "literal.", + name); + return false; + } + if (*port == nullptr) { + if (default_port == nullptr) { + gpr_log(GPR_ERROR, + "No port or default port for %s while attempting to resolve as " + "ip literal.", + name); + return false; + } + *port = gpr_strdup(default_port); + } + grpc_resolved_address addr; + GPR_ASSERT(gpr_join_host_port(hostport, *host, atoi(*port))); + if (grpc_parse_ipv4_hostport(*hostport, &addr, false /* log errors */) || + grpc_parse_ipv6_hostport(*hostport, &addr, false /* log errors */)) { + GPR_ASSERT(*addrs == nullptr); + *addrs = grpc_lb_addresses_create(1, nullptr); + grpc_lb_addresses_set_address( + *addrs, 0, addr.addr, addr.len, false /* is_balancer */, + nullptr /* balancer_name */, nullptr /* user_data */); + return true; + } + return false; +} + +static bool resolve_as_ip_literal_locked(const char* name, + const char* default_port, + grpc_lb_addresses** addrs) { + char* host = nullptr; + char* port = nullptr; + char* hostport = nullptr; + bool out = inner_resolve_as_ip_literal_locked(name, default_port, addrs, + &host, &port, &hostport); + gpr_free(host); + gpr_free(port); + gpr_free(hostport); + return out; +} + +static grpc_ares_request* grpc_dns_lookup_ares_locked_impl( + const char* dns_server, const char* name, const char* default_port, + grpc_pollset_set* interested_parties, grpc_closure* on_done, + grpc_lb_addresses** addrs, bool check_grpclb, char** service_config_json, + grpc_combiner* combiner) { + // Early out if the target is an ipv4 or ipv6 literal. + if (resolve_as_ip_literal_locked(name, default_port, addrs)) { + GRPC_CLOSURE_SCHED(on_done, GRPC_ERROR_NONE); + return nullptr; + } + // Early out if the target is localhost and we're on Windows. + if (grpc_ares_maybe_resolve_localhost_manually_locked(name, default_port, + addrs)) { + GRPC_CLOSURE_SCHED(on_done, GRPC_ERROR_NONE); + return nullptr; + } + // Look up name using c-ares lib. + return grpc_dns_lookup_ares_continue_after_check_localhost_and_ip_literals_locked( + dns_server, name, default_port, interested_parties, on_done, addrs, + check_grpclb, service_config_json, combiner); +} + grpc_ares_request* (*grpc_dns_lookup_ares_locked)( const char* dns_server, const char* name, const char* default_port, grpc_pollset_set* interested_parties, grpc_closure* on_done, @@ -502,7 +561,9 @@ grpc_ares_request* (*grpc_dns_lookup_ares_locked)( void grpc_cancel_ares_request(grpc_ares_request* r) { if (grpc_dns_lookup_ares_locked == grpc_dns_lookup_ares_locked_impl) { - grpc_ares_ev_driver_shutdown_locked(r->ev_driver); + if (r != nullptr) { + grpc_ares_ev_driver_shutdown_locked(r->ev_driver); + } } } diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h index ca5779e1d79..1bc457d4cfe 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h @@ -81,9 +81,15 @@ void grpc_ares_complete_request_locked(grpc_ares_request* request); /* E.g., return false if ipv6 is known to not be available. */ bool grpc_ares_query_ipv6(); -/* Exposed only for testing */ -void grpc_cares_wrapper_test_only_address_sorting_sort( - grpc_lb_addresses* lb_addrs); +/* Maybe (depending on the current platform) checks if "name" matches + * "localhost" and if so fills in addrs with the correct sockaddr structures. + * Returns a bool indicating whether or not such an action was performed. + * See https://github.com/grpc/grpc/issues/15158. */ +bool grpc_ares_maybe_resolve_localhost_manually_locked( + const char* name, const char* default_port, grpc_lb_addresses** addrs); + +/* Sorts destinations in lb_addrs according to RFC 6724. */ +void grpc_cares_wrapper_address_sorting_sort(grpc_lb_addresses* lb_addrs); #endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H \ */ diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc index 23c0fec74f3..639eec2323f 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc @@ -26,4 +26,9 @@ bool grpc_ares_query_ipv6() { return grpc_ipv6_loopback_available(); } +bool grpc_ares_maybe_resolve_localhost_manually_locked( + const char* name, const char* default_port, grpc_lb_addresses** addrs) { + return false; +} + #endif /* GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER) */ diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc index ee827e284e2..7e34784691f 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc @@ -21,9 +21,79 @@ #include "src/core/lib/iomgr/port.h" #if GRPC_ARES == 1 && defined(GPR_WINDOWS) +#include + +#include "src/core/ext/filters/client_channel/lb_policy_factory.h" +#include "src/core/ext/filters/client_channel/parse_address.h" #include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" #include "src/core/lib/iomgr/socket_windows.h" bool grpc_ares_query_ipv6() { return grpc_ipv6_loopback_available(); } +static bool inner_maybe_resolve_localhost_manually_locked( + const char* name, const char* default_port, grpc_lb_addresses** addrs, + char** host, char** port) { + gpr_split_host_port(name, host, port); + if (*host == nullptr) { + gpr_log(GPR_ERROR, + "Failed to parse %s into host:port during Windows localhost " + "resolution check.", + name); + return false; + } + if (*port == nullptr) { + if (default_port == nullptr) { + gpr_log(GPR_ERROR, + "No port or default port for %s during Windows localhost " + "resolution check.", + name); + return false; + } + *port = gpr_strdup(default_port); + } + if (gpr_stricmp(*host, "localhost") == 0) { + GPR_ASSERT(*addrs == nullptr); + *addrs = grpc_lb_addresses_create(2, nullptr); + uint16_t numeric_port = grpc_strhtons(*port); + // Append the ipv6 loopback address. + struct sockaddr_in6 ipv6_loopback_addr; + memset(&ipv6_loopback_addr, 0, sizeof(ipv6_loopback_addr)); + ((char*)&ipv6_loopback_addr.sin6_addr)[15] = 1; + ipv6_loopback_addr.sin6_family = AF_INET6; + ipv6_loopback_addr.sin6_port = numeric_port; + grpc_lb_addresses_set_address( + *addrs, 0, &ipv6_loopback_addr, sizeof(ipv6_loopback_addr), + false /* is_balancer */, nullptr /* balancer_name */, + nullptr /* user_data */); + // Append the ipv4 loopback address. + struct sockaddr_in ipv4_loopback_addr; + memset(&ipv4_loopback_addr, 0, sizeof(ipv4_loopback_addr)); + ((char*)&ipv4_loopback_addr.sin_addr)[0] = 0x7f; + ((char*)&ipv4_loopback_addr.sin_addr)[3] = 0x01; + ipv4_loopback_addr.sin_family = AF_INET; + ipv4_loopback_addr.sin_port = numeric_port; + grpc_lb_addresses_set_address( + *addrs, 1, &ipv4_loopback_addr, sizeof(ipv4_loopback_addr), + false /* is_balancer */, nullptr /* balancer_name */, + nullptr /* user_data */); + // Let the address sorter figure out which one should be tried first. + grpc_cares_wrapper_address_sorting_sort(*addrs); + return true; + } + return false; +} + +bool grpc_ares_maybe_resolve_localhost_manually_locked( + const char* name, const char* default_port, grpc_lb_addresses** addrs) { + char* host = nullptr; + char* port = nullptr; + bool out = inner_maybe_resolve_localhost_manually_locked(name, default_port, + addrs, &host, &port); + gpr_free(host); + gpr_free(port); + return out; +} + #endif /* GRPC_ARES == 1 && defined(GPR_WINDOWS) */ diff --git a/src/core/ext/filters/client_channel/subchannel_index.cc b/src/core/ext/filters/client_channel/subchannel_index.cc index f2b6c24e8ee..1c23a6c4be4 100644 --- a/src/core/ext/filters/client_channel/subchannel_index.cc +++ b/src/core/ext/filters/client_channel/subchannel_index.cc @@ -42,7 +42,7 @@ struct grpc_subchannel_key { grpc_subchannel_args args; }; -static gpr_atm g_force_creation = false; +static bool g_force_creation = false; static grpc_subchannel_key* create_key( const grpc_subchannel_args* args, @@ -74,7 +74,7 @@ static grpc_subchannel_key* subchannel_key_copy(grpc_subchannel_key* k) { int grpc_subchannel_key_compare(const grpc_subchannel_key* a, const grpc_subchannel_key* b) { // To pretend the keys are different, return a non-zero value. - if (GPR_UNLIKELY(gpr_atm_no_barrier_load(&g_force_creation))) return 1; + if (GPR_UNLIKELY(g_force_creation)) return 1; int c = GPR_ICMP(a->args.filter_count, b->args.filter_count); if (c != 0) return c; if (a->args.filter_count > 0) { @@ -251,5 +251,5 @@ void grpc_subchannel_index_unregister(grpc_subchannel_key* key, } void grpc_subchannel_index_test_only_set_force_creation(bool force_creation) { - gpr_atm_no_barrier_store(&g_force_creation, force_creation); + g_force_creation = force_creation; } diff --git a/src/core/ext/filters/http/client/http_client_filter.cc b/src/core/ext/filters/http/client/http_client_filter.cc index 91fa163fecb..cd459e47cd2 100644 --- a/src/core/ext/filters/http/client/http_client_filter.cc +++ b/src/core/ext/filters/http/client/http_client_filter.cc @@ -58,6 +58,8 @@ struct call_data { grpc_metadata_batch* recv_trailing_metadata; grpc_closure* original_recv_trailing_metadata_ready; grpc_closure recv_trailing_metadata_ready; + grpc_error* recv_trailing_metadata_error; + bool seen_recv_trailing_metadata_ready; // State for handling send_message ops. grpc_transport_stream_op_batch* send_message_batch; size_t send_message_bytes_read; @@ -157,12 +159,27 @@ static void recv_initial_metadata_ready(void* user_data, grpc_error* error) { } else { GRPC_ERROR_REF(error); } - GRPC_CLOSURE_RUN(calld->original_recv_initial_metadata_ready, error); + grpc_closure* closure = calld->original_recv_initial_metadata_ready; + calld->original_recv_initial_metadata_ready = nullptr; + if (calld->seen_recv_trailing_metadata_ready) { + GRPC_CALL_COMBINER_START( + calld->call_combiner, &calld->recv_trailing_metadata_ready, + calld->recv_trailing_metadata_error, "continue recv_trailing_metadata"); + } + GRPC_CLOSURE_RUN(closure, error); } static void recv_trailing_metadata_ready(void* user_data, grpc_error* error) { grpc_call_element* elem = static_cast(user_data); call_data* calld = static_cast(elem->call_data); + if (calld->original_recv_initial_metadata_ready != nullptr) { + calld->recv_trailing_metadata_error = GRPC_ERROR_REF(error); + calld->seen_recv_trailing_metadata_ready = true; + GRPC_CALL_COMBINER_STOP(calld->call_combiner, + "deferring recv_trailing_metadata_ready until " + "after recv_initial_metadata_ready"); + return; + } if (error == GRPC_ERROR_NONE) { error = client_filter_incoming_metadata(elem, calld->recv_trailing_metadata); diff --git a/src/core/ext/filters/http/server/http_server_filter.cc b/src/core/ext/filters/http/server/http_server_filter.cc index 1b3426b1200..436ea09d942 100644 --- a/src/core/ext/filters/http/server/http_server_filter.cc +++ b/src/core/ext/filters/http/server/http_server_filter.cc @@ -63,8 +63,11 @@ struct call_data { grpc_core::OrphanablePtr* recv_message; bool seen_recv_message_ready; + // State for intercepting recv_trailing_metadata grpc_closure recv_trailing_metadata_ready; grpc_closure* original_recv_trailing_metadata_ready; + grpc_error* recv_trailing_metadata_ready_error; + bool seen_recv_trailing_metadata_ready; }; struct channel_data { @@ -301,6 +304,13 @@ static void hs_recv_initial_metadata_ready(void* user_data, grpc_error* err) { } else { GRPC_ERROR_REF(err); } + if (calld->seen_recv_trailing_metadata_ready) { + GRPC_CALL_COMBINER_START(calld->call_combiner, + &calld->recv_trailing_metadata_ready, + calld->recv_trailing_metadata_ready_error, + "resuming hs_recv_trailing_metadata_ready from " + "hs_recv_initial_metadata_ready"); + } GRPC_CLOSURE_RUN(calld->original_recv_initial_metadata_ready, err); } @@ -331,6 +341,14 @@ static void hs_recv_message_ready(void* user_data, grpc_error* err) { static void hs_recv_trailing_metadata_ready(void* user_data, grpc_error* err) { grpc_call_element* elem = static_cast(user_data); call_data* calld = static_cast(elem->call_data); + if (!calld->seen_recv_initial_metadata_ready) { + calld->recv_trailing_metadata_ready_error = GRPC_ERROR_REF(err); + calld->seen_recv_trailing_metadata_ready = true; + GRPC_CALL_COMBINER_STOP(calld->call_combiner, + "deferring hs_recv_trailing_metadata_ready until " + "ater hs_recv_initial_metadata_ready"); + return; + } err = grpc_error_add_child( GRPC_ERROR_REF(err), GRPC_ERROR_REF(calld->recv_initial_metadata_ready_error)); diff --git a/src/core/ext/filters/message_size/message_size_filter.cc b/src/core/ext/filters/message_size/message_size_filter.cc index c17df86f3da..2d3b16d992c 100644 --- a/src/core/ext/filters/message_size/message_size_filter.cc +++ b/src/core/ext/filters/message_size/message_size_filter.cc @@ -108,6 +108,8 @@ struct call_data { grpc_closure* next_recv_message_ready; // Original recv_trailing_metadata callback, invoked after our own. grpc_closure* original_recv_trailing_metadata_ready; + bool seen_recv_trailing_metadata; + grpc_error* recv_trailing_metadata_error; }; struct channel_data { @@ -147,7 +149,21 @@ static void recv_message_ready(void* user_data, grpc_error* error) { GRPC_ERROR_REF(error); } // Invoke the next callback. - GRPC_CLOSURE_RUN(calld->next_recv_message_ready, error); + grpc_closure* closure = calld->next_recv_message_ready; + calld->next_recv_message_ready = nullptr; + if (calld->seen_recv_trailing_metadata) { + /* We might potentially see another RECV_MESSAGE op. In that case, we do not + * want to run the recv_trailing_metadata_ready closure again. The newer + * RECV_MESSAGE op cannot cause any errors since the transport has already + * invoked the recv_trailing_metadata_ready closure and all further + * RECV_MESSAGE ops will get null payloads. */ + calld->seen_recv_trailing_metadata = false; + GRPC_CALL_COMBINER_START(calld->call_combiner, + &calld->recv_trailing_metadata_ready, + calld->recv_trailing_metadata_error, + "continue recv_trailing_metadata_ready"); + } + GRPC_CLOSURE_RUN(closure, error); } // Callback invoked on completion of recv_trailing_metadata @@ -155,6 +171,14 @@ static void recv_message_ready(void* user_data, grpc_error* error) { static void recv_trailing_metadata_ready(void* user_data, grpc_error* error) { grpc_call_element* elem = static_cast(user_data); call_data* calld = static_cast(elem->call_data); + if (calld->next_recv_message_ready != nullptr) { + calld->seen_recv_trailing_metadata = true; + calld->recv_trailing_metadata_error = GRPC_ERROR_REF(error); + GRPC_CALL_COMBINER_STOP(calld->call_combiner, + "deferring recv_trailing_metadata_ready until " + "after recv_message_ready"); + return; + } error = grpc_error_add_child(GRPC_ERROR_REF(error), GRPC_ERROR_REF(calld->error)); // Invoke the next callback. diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.cc b/src/core/ext/transport/chttp2/transport/hpack_encoder.cc index 0eaf63f133b..920d52770fc 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_encoder.cc +++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.cc @@ -525,7 +525,7 @@ static void hpack_enc(grpc_chttp2_hpack_compressor* c, grpc_mdelem elem, /* should this elem be in the table? */ size_t decoder_space_usage = - grpc_mdelem_get_size_in_hpack_table(elem, st->use_true_binary_metadata); + grpc_chttp2_get_size_in_hpack_table(elem, st->use_true_binary_metadata); bool should_add_elem = elem_interned && decoder_space_usage < MAX_DECODER_SPACE_USAGE && c->filter_elems[HASH_FRAGMENT_1(elem_hash)] >= @@ -688,11 +688,22 @@ void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor* c, emit_advertise_table_size_change(c, &st); } for (size_t i = 0; i < extra_headers_size; ++i) { - hpack_enc(c, *extra_headers[i], &st); + grpc_mdelem md = *extra_headers[i]; + uint8_t static_index = grpc_chttp2_get_static_hpack_table_index(md); + if (static_index) { + emit_indexed(c, static_index, &st); + } else { + hpack_enc(c, md, &st); + } } grpc_metadata_batch_assert_ok(metadata); for (grpc_linked_mdelem* l = metadata->list.head; l; l = l->next) { - hpack_enc(c, l->md, &st); + uint8_t static_index = grpc_chttp2_get_static_hpack_table_index(l->md); + if (static_index) { + emit_indexed(c, static_index, &st); + } else { + hpack_enc(c, l->md, &st); + } } grpc_millis deadline = metadata->deadline; if (deadline != GRPC_MILLIS_INF_FUTURE) { diff --git a/src/core/ext/transport/chttp2/transport/hpack_mapping.cc b/src/core/ext/transport/chttp2/transport/hpack_mapping.cc new file mode 100644 index 00000000000..fd529f0fd4d --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/hpack_mapping.cc @@ -0,0 +1,39 @@ +/* + * 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. + */ + +/* + * WARNING: Auto-generated code. + * + * To make changes to this file, change + * tools/codegen/core/gen_static_metadata.py, and then re-run it. + * + * This file contains the mapping from the index of each metadata element in the + * grpc static metadata table to the index of that element in the hpack static + * metadata table. If the element is not contained in the static hpack table, + * then the returned index is 0. + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/hpack_mapping.h" + +const uint8_t grpc_hpack_static_mdelem_indices[GRPC_STATIC_MDELEM_COUNT] = { + 0, 0, 0, 0, 0, 0, 0, 0, 3, 8, 13, 6, 7, 0, 1, 2, 0, 4, + 5, 9, 10, 11, 12, 14, 15, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 0, 0, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; diff --git a/src/core/ext/transport/chttp2/transport/hpack_mapping.h b/src/core/ext/transport/chttp2/transport/hpack_mapping.h new file mode 100644 index 00000000000..ebcd65bd9fe --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/hpack_mapping.h @@ -0,0 +1,38 @@ +/* + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * WARNING: Auto-generated code. + * + * To make changes to this file, change + * tools/codegen/core/gen_static_metadata.py, and then re-run it. + * + * This file contains the mapping from the index of each metadata element in the + * grpc static metadata table to the index of that element in the hpack static + * metadata table. If the element is not contained in the static hpack table, + * then the returned index is 0. + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_MAPPING_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_MAPPING_H + +#include + +#include "src/core/lib/transport/static_metadata.h" + +extern const uint8_t grpc_hpack_static_mdelem_indices[GRPC_STATIC_MDELEM_COUNT]; + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_MAPPING_H */ diff --git a/src/core/ext/transport/chttp2/transport/hpack_table.cc b/src/core/ext/transport/chttp2/transport/hpack_table.cc index 79292583567..117679ab751 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_table.cc +++ b/src/core/ext/transport/chttp2/transport/hpack_table.cc @@ -27,8 +27,10 @@ #include #include +#include "src/core/ext/transport/chttp2/transport/hpack_mapping.h" #include "src/core/lib/debug/trace.h" #include "src/core/lib/gpr/murmur_hash.h" +#include "src/core/lib/transport/static_metadata.h" extern grpc_core::TraceFlag grpc_http_trace; @@ -366,3 +368,30 @@ grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find( return r; } + +static size_t get_base64_encoded_size(size_t raw_length) { + static const uint8_t tail_xtra[3] = {0, 2, 3}; + return raw_length / 3 * 4 + tail_xtra[raw_length % 3]; +} + +size_t grpc_chttp2_get_size_in_hpack_table(grpc_mdelem elem, + bool use_true_binary_metadata) { + size_t overhead_and_key = 32 + GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)); + size_t value_len = GRPC_SLICE_LENGTH(GRPC_MDVALUE(elem)); + if (grpc_is_binary_header(GRPC_MDKEY(elem))) { + return overhead_and_key + (use_true_binary_metadata + ? value_len + 1 + : get_base64_encoded_size(value_len)); + } else { + return overhead_and_key + value_len; + } +} + +uint8_t grpc_chttp2_get_static_hpack_table_index(grpc_mdelem md) { + if (GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_STATIC) { + return grpc_hpack_static_mdelem_indices[GRPC_MDELEM_DATA(md) - + grpc_static_mdelem_table]; + } else { + return 0; + } +} diff --git a/src/core/ext/transport/chttp2/transport/hpack_table.h b/src/core/ext/transport/chttp2/transport/hpack_table.h index 98026a4ba4a..a0ffc6fab77 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_table.h +++ b/src/core/ext/transport/chttp2/transport/hpack_table.h @@ -83,6 +83,15 @@ grpc_mdelem grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl* tbl, /* add a table entry to the index */ grpc_error* grpc_chttp2_hptbl_add(grpc_chttp2_hptbl* tbl, grpc_mdelem md) GRPC_MUST_USE_RESULT; + +size_t grpc_chttp2_get_size_in_hpack_table(grpc_mdelem elem, + bool use_true_binary_metadata); + +/* Returns the static hpack table index that corresponds to /a elem. Returns 0 + if /a elem is not statically stored or if it is not in the static hpack + table */ +uint8_t grpc_chttp2_get_static_hpack_table_index(grpc_mdelem md); + /* Find a key/value pair in the table... returns the index in the table of the most similar entry, or 0 if the value was not found */ typedef struct { diff --git a/src/core/ext/transport/chttp2/transport/parsing.cc b/src/core/ext/transport/chttp2/transport/parsing.cc index 0694ad743bb..f532b084c93 100644 --- a/src/core/ext/transport/chttp2/transport/parsing.cc +++ b/src/core/ext/transport/chttp2/transport/parsing.cc @@ -409,67 +409,81 @@ static void on_initial_header(void* tp, grpc_mdelem md) { gpr_free(value); } - if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_STATUS) && - !grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) { - /* TODO(ctiller): check for a status like " 0" */ - s->seen_error = true; - } + if (GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_STATIC) { + // We don't use grpc_mdelem_eq here to avoid executing additional + // instructions. The reasoning is if the payload is not equal, we already + // know that the metadata elements are not equal because the md is + // confirmed to be static. If we had used grpc_mdelem_eq here, then if the + // payloads are not equal, grpc_mdelem_eq executes more instructions to + // determine if they're equal or not. + if (md.payload == GRPC_MDELEM_GRPC_STATUS_1.payload || + md.payload == GRPC_MDELEM_GRPC_STATUS_2.payload) { + s->seen_error = true; + } + } else { + if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_STATUS) && + !grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) { + /* TODO(ctiller): check for a status like " 0" */ + s->seen_error = true; + } - if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_TIMEOUT)) { - grpc_millis* cached_timeout = - static_cast(grpc_mdelem_get_user_data(md, free_timeout)); - grpc_millis timeout; - if (cached_timeout != nullptr) { - timeout = *cached_timeout; - } else { - if (GPR_UNLIKELY( - !grpc_http2_decode_timeout(GRPC_MDVALUE(md), &timeout))) { - char* val = grpc_slice_to_c_string(GRPC_MDVALUE(md)); - gpr_log(GPR_ERROR, "Ignoring bad timeout value '%s'", val); - gpr_free(val); - timeout = GRPC_MILLIS_INF_FUTURE; + if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_TIMEOUT)) { + grpc_millis* cached_timeout = static_cast( + grpc_mdelem_get_user_data(md, free_timeout)); + grpc_millis timeout; + if (cached_timeout != nullptr) { + timeout = *cached_timeout; + } else { + if (GPR_UNLIKELY( + !grpc_http2_decode_timeout(GRPC_MDVALUE(md), &timeout))) { + char* val = grpc_slice_to_c_string(GRPC_MDVALUE(md)); + gpr_log(GPR_ERROR, "Ignoring bad timeout value '%s'", val); + gpr_free(val); + timeout = GRPC_MILLIS_INF_FUTURE; + } + if (GRPC_MDELEM_IS_INTERNED(md)) { + /* store the result */ + cached_timeout = + static_cast(gpr_malloc(sizeof(grpc_millis))); + *cached_timeout = timeout; + grpc_mdelem_set_user_data(md, free_timeout, cached_timeout); + } } - if (GRPC_MDELEM_IS_INTERNED(md)) { - /* store the result */ - cached_timeout = - static_cast(gpr_malloc(sizeof(grpc_millis))); - *cached_timeout = timeout; - grpc_mdelem_set_user_data(md, free_timeout, cached_timeout); + if (timeout != GRPC_MILLIS_INF_FUTURE) { + grpc_chttp2_incoming_metadata_buffer_set_deadline( + &s->metadata_buffer[0], grpc_core::ExecCtx::Get()->Now() + timeout); } + GRPC_MDELEM_UNREF(md); + return; } - if (timeout != GRPC_MILLIS_INF_FUTURE) { - grpc_chttp2_incoming_metadata_buffer_set_deadline( - &s->metadata_buffer[0], grpc_core::ExecCtx::Get()->Now() + timeout); - } + } + + const size_t new_size = s->metadata_buffer[0].size + GRPC_MDELEM_LENGTH(md); + const size_t metadata_size_limit = + t->settings[GRPC_ACKED_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE]; + if (new_size > metadata_size_limit) { + gpr_log(GPR_DEBUG, + "received initial metadata size exceeds limit (%" PRIuPTR + " vs. %" PRIuPTR ")", + new_size, metadata_size_limit); + grpc_chttp2_cancel_stream( + t, s, + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "received initial metadata size exceeds limit"), + GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_RESOURCE_EXHAUSTED)); + grpc_chttp2_parsing_become_skip_parser(t); + s->seen_error = true; GRPC_MDELEM_UNREF(md); } else { - const size_t new_size = s->metadata_buffer[0].size + GRPC_MDELEM_LENGTH(md); - const size_t metadata_size_limit = - t->settings[GRPC_ACKED_SETTINGS] - [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE]; - if (new_size > metadata_size_limit) { - gpr_log(GPR_DEBUG, - "received initial metadata size exceeds limit (%" PRIuPTR - " vs. %" PRIuPTR ")", - new_size, metadata_size_limit); - grpc_chttp2_cancel_stream( - t, s, - grpc_error_set_int( - GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "received initial metadata size exceeds limit"), - GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED)); + grpc_error* error = + grpc_chttp2_incoming_metadata_buffer_add(&s->metadata_buffer[0], md); + if (error != GRPC_ERROR_NONE) { + grpc_chttp2_cancel_stream(t, s, error); grpc_chttp2_parsing_become_skip_parser(t); s->seen_error = true; GRPC_MDELEM_UNREF(md); - } else { - grpc_error* error = - grpc_chttp2_incoming_metadata_buffer_add(&s->metadata_buffer[0], md); - if (error != GRPC_ERROR_NONE) { - grpc_chttp2_cancel_stream(t, s, error); - grpc_chttp2_parsing_become_skip_parser(t); - s->seen_error = true; - GRPC_MDELEM_UNREF(md); - } } } } @@ -491,8 +505,19 @@ static void on_trailing_header(void* tp, grpc_mdelem md) { gpr_free(value); } - if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_STATUS) && - !grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) { + if (GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_STATIC) { + // We don't use grpc_mdelem_eq here to avoid executing additional + // instructions. The reasoning is if the payload is not equal, we already + // know that the metadata elements are not equal because the md is + // confirmed to be static. If we had used grpc_mdelem_eq here, then if the + // payloads are not equal, grpc_mdelem_eq executes more instructions to + // determine if they're equal or not. + if (md.payload == GRPC_MDELEM_GRPC_STATUS_1.payload || + md.payload == GRPC_MDELEM_GRPC_STATUS_2.payload) { + s->seen_error = true; + } + } else if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_STATUS) && + !grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) { /* TODO(ctiller): check for a status like " 0" */ s->seen_error = true; } diff --git a/src/core/lib/channel/channel_trace.h b/src/core/lib/channel/channel_trace.h index 230faa483e4..94fea20b451 100644 --- a/src/core/lib/channel/channel_trace.h +++ b/src/core/lib/channel/channel_trace.h @@ -63,7 +63,7 @@ class ChannelTrace { // stack, determine if it makes more sense to accept a char* instead of a // slice. void AddTraceEventWithReference(Severity severity, grpc_slice data, - RefCountedPtr referenced_channel); + RefCountedPtr referenced_entity); // Creates and returns the raw grpc_json object, so a parent channelz // object may incorporate the json before rendering. diff --git a/src/core/lib/iomgr/ev_epollsig_linux.cc b/src/core/lib/iomgr/ev_epollsig_linux.cc deleted file mode 100644 index 5695ac795d7..00000000000 --- a/src/core/lib/iomgr/ev_epollsig_linux.cc +++ /dev/null @@ -1,1743 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include "src/core/lib/iomgr/port.h" - -#include -#include - -/* This polling engine is only relevant on linux kernels supporting epoll() */ -#ifdef GRPC_LINUX_EPOLL_CREATE1 - -#include "src/core/lib/iomgr/ev_epollsig_linux.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/gpr/tls.h" -#include "src/core/lib/gpr/useful.h" -#include "src/core/lib/gprpp/manual_constructor.h" -#include "src/core/lib/iomgr/block_annotate.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" - -#define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker*)1) - -#define GRPC_POLLING_TRACE(...) \ - if (grpc_polling_trace.enabled()) { \ - gpr_log(GPR_INFO, __VA_ARGS__); \ - } - -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 */ -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; - -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; - - grpc_core::ManualConstructor read_closure; - grpc_core::ManualConstructor write_closure; - grpc_core::ManualConstructor error_closure; - - struct grpc_fd* freelist_next; - grpc_closure* on_done_closure; - - grpc_iomgr_object iomgr_object; - - /* Do we need to track EPOLLERR events separately? */ - bool track_err; -}; - -/* 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(p, r) pi_unref_dbg((p), (r), __FILE__, __LINE__) - -#else - -#define PI_ADD_REF(p, r) pi_add_ref((p)) -#define PI_UNREF(p, r) pi_unref((p)) - -#endif - -/* 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 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 - */ -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; -}; - -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(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(polling_island* pi); - -#ifndef NDEBUG -static void pi_add_ref_dbg(polling_island* pi, const char* reason, - const char* file, int line) { - if (grpc_polling_trace.enabled()) { - gpr_atm old_cnt = gpr_atm_acq_load(&pi->ref_count); - gpr_log(GPR_INFO, - "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(polling_island* pi, const char* reason, - const char* file, int line) { - if (grpc_polling_trace.enabled()) { - gpr_atm old_cnt = gpr_atm_acq_load(&pi->ref_count); - gpr_log(GPR_INFO, - "Unref pi: %p, old:%" PRIdPTR " -> new:%" PRIdPTR - " (%s) - (%s, %d)", - pi, old_cnt, (old_cnt - 1), reason, file, line); - } - pi_unref(pi); -} -#endif - -static void pi_add_ref(polling_island* pi) { - gpr_atm_no_barrier_fetch_add(&pi->ref_count, 1); -} - -static void pi_unref(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(pi); - if (next != nullptr) { - PI_UNREF(next, "pi_delete"); /* Recursive call */ - } - } -} - -/* 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 = static_cast(EPOLLIN | EPOLLOUT | EPOLLET); - /* Use the least significant bit of ev.data.ptr to store track_err to avoid - * synchronization issues when accessing it after receiving an event */ - ev.data.ptr = reinterpret_cast(reinterpret_cast(fds[i]) | - (fds[i]->track_err ? 1 : 0)); - 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 = static_cast( - 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 = static_cast(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, nullptr); - 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, - 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 */ - err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_DEL, fd->fd, nullptr); - 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_fd* initial_fd, - grpc_error** error) { - polling_island* pi = nullptr; - const char* err_desc = "polling_island_create"; - - *error = GRPC_ERROR_NONE; - - pi = static_cast(gpr_malloc(sizeof(*pi))); - gpr_mu_init(&pi->mu); - pi->fd_cnt = 0; - pi->fd_capacity = 0; - pi->fds = nullptr; - 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) nullptr); - - 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 != nullptr) { - polling_island_add_fds_locked(pi, &initial_fd, 1, true, error); - } - -done: - if (*error != GRPC_ERROR_NONE) { - polling_island_delete(pi); - pi = nullptr; - } - return pi; -} - -static void polling_island_delete(polling_island* pi) { - GPR_ASSERT(pi->fd_cnt == 0); - - if (pi->epoll_fd >= 0) { - close(pi->epoll_fd); - } - gpr_mu_destroy(&pi->mu); - 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 != nullptr) { - 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 = nullptr; - - while (true) { - next = (polling_island*)gpr_atm_acq_load(&pi->merged_to); - if (next == nullptr) { - /* 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 == nullptr) { - /* 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 = nullptr; - polling_island* next_2 = nullptr; - - /* 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 != nullptr) { - 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 != nullptr) { - 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 == nullptr && next_2 == nullptr) { - 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 = nullptr; -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_trace_fd_refcount.enabled()) { - 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_trace_fd_refcount.enabled()) { - 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); - - fd->read_closure->DestroyEvent(); - fd->write_closure->DestroyEvent(); - fd->error_closure->DestroyEvent(); - - 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 != nullptr) { - 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, bool track_err) { - grpc_fd* new_fd = nullptr; - - gpr_mu_lock(&fd_freelist_mu); - if (fd_freelist != nullptr) { - new_fd = fd_freelist; - fd_freelist = fd_freelist->freelist_next; - } - gpr_mu_unlock(&fd_freelist_mu); - - if (new_fd == nullptr) { - new_fd = static_cast(gpr_malloc(sizeof(grpc_fd))); - gpr_mu_init(&new_fd->po.mu); - new_fd->read_closure.Init(); - new_fd->write_closure.Init(); - new_fd->error_closure.Init(); - } - - /* 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 = nullptr; -#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; - new_fd->read_closure->InitEvent(); - new_fd->write_closure->InitEvent(); - new_fd->error_closure->InitEvent(); - new_fd->track_err = track_err; - - new_fd->freelist_next = nullptr; - new_fd->on_done_closure = nullptr; - - 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); - 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_fd* fd, grpc_closure* on_done, int* release_fd, - const char* reason) { - grpc_error* error = GRPC_ERROR_NONE; - polling_island* unref_pi = nullptr; - - 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 != nullptr) { - polling_island* pi_latest = polling_island_lock(fd->po.pi); - polling_island_remove_fd_locked(pi_latest, fd, &error); - gpr_mu_unlock(&pi_latest->mu); - - unref_pi = fd->po.pi; - fd->po.pi = nullptr; - } - - /* 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 != nullptr) { - *release_fd = fd->fd; - } else { - close(fd->fd); - } - - fd->orphaned = true; - - GRPC_CLOSURE_SCHED(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 != nullptr) { - /* 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(unref_pi, "fd_orphan"); - } - if (error != GRPC_ERROR_NONE) { - const char* msg = grpc_error_string(error); - gpr_log(GPR_DEBUG, "fd_orphan: %s", msg); - } - GRPC_ERROR_UNREF(error); -} - -static bool fd_is_shutdown(grpc_fd* fd) { - return fd->read_closure->IsShutdown(); -} - -/* Might be called multiple times */ -static void fd_shutdown(grpc_fd* fd, grpc_error* why) { - if (fd->read_closure->SetShutdown(GRPC_ERROR_REF(why))) { - shutdown(fd->fd, SHUT_RDWR); - fd->write_closure->SetShutdown(GRPC_ERROR_REF(why)); - fd->error_closure->SetShutdown(GRPC_ERROR_REF(why)); - } - GRPC_ERROR_UNREF(why); -} - -static void fd_notify_on_read(grpc_fd* fd, grpc_closure* closure) { - fd->read_closure->NotifyOn(closure); -} - -static void fd_notify_on_write(grpc_fd* fd, grpc_closure* closure) { - fd->write_closure->NotifyOn(closure); -} - -static void fd_notify_on_error(grpc_fd* fd, grpc_closure* closure) { - fd->error_closure->NotifyOn(closure); -} - -static void fd_become_readable(grpc_fd* fd) { fd->read_closure->SetReady(); } - -static void fd_become_writable(grpc_fd* fd) { fd->write_closure->SetReady(); } - -static void fd_has_errors(grpc_fd* fd) { fd->error_closure->SetReady(); } - -/******************************************************************************* - * 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 void sig_handler(int sig_num) { -#ifdef GRPC_EPOLL_DEBUG - gpr_log(GPR_INFO, "Received signal %d", sig_num); -#endif -} - -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* pollset_worker_kick(grpc_pollset_worker* worker) { - grpc_error* err = GRPC_ERROR_NONE; - - /* Kick the worker only if it was not already kicked */ - if (gpr_atm_no_barrier_cas(&worker->is_kicked, static_cast(0), - static_cast(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; -} - -/* 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 nullptr; - } -} - -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_SCOPE("pollset_kick", 0); - grpc_error* error = GRPC_ERROR_NONE; - GRPC_STATS_INC_POLLSET_KICK(); - const char* err_desc = "Kick Failure"; - grpc_pollset_worker* worker = specific_worker; - if (worker != nullptr) { - if (worker == GRPC_POLLSET_KICK_BROADCAST) { - if (pollset_has_workers(p)) { - GPR_TIMER_SCOPE("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); - } - } - } 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 != nullptr) { - 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; - } - } - - 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 = nullptr; -#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 = nullptr; -} - -static int poll_deadline_to_millis_timeout(grpc_millis millis) { - if (millis == GRPC_MILLIS_INF_FUTURE) return -1; - grpc_millis delta = millis - grpc_core::ExecCtx::Get()->Now(); - if (delta > INT_MAX) - return INT_MAX; - else if (delta < 0) - return 0; - else - return static_cast(delta); -} - -static void pollset_release_polling_island(grpc_pollset* ps, - const char* reason) { - if (ps->po.pi != nullptr) { - PI_UNREF(ps->po.pi, reason); - } - ps->po.pi = nullptr; -} - -static void finish_shutdown_locked(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(pollset, "ps_shutdown"); - GRPC_CLOSURE_SCHED(pollset->shutdown_done, GRPC_ERROR_NONE); -} - -/* pollset->po.mu lock must be held by the caller before calling this */ -static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { - GPR_TIMER_SCOPE("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(pollset); - } -} - -/* 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_pollset* pollset) { - GPR_ASSERT(!pollset_has_workers(pollset)); - gpr_mu_destroy(&pollset->po.mu); -} - -#define GRPC_EPOLL_MAX_EVENTS 100 -/* Note: sig_mask contains the signal mask to use *during* epoll_wait() */ -static void pollset_work_and_unlock(grpc_pollset* pollset, - grpc_pollset_worker* worker, int timeout_ms, - sigset_t* sig_mask, grpc_error** error) { - GPR_TIMER_SCOPE("pollset_work_and_unlock", 0); - struct epoll_event ep_ev[GRPC_EPOLL_MAX_EVENTS]; - int epoll_fd = -1; - int ep_rv; - polling_island* pi = nullptr; - char* err_msg; - const char* err_desc = "pollset_work_and_unlock"; - - /* 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, we can read it without obtaining the polling - island lock. There is however a possibility that the polling island (from - which we got the epoll_fd) got merged with another island while we are - in this function. This is still okay because in such a case, we will wakeup - right-away from epoll_wait() and pick up the latest polling_island the next - this function (i.e pollset_work_and_unlock()) is called */ - - if (pollset->po.pi == nullptr) { - pollset->po.pi = polling_island_create(nullptr, error); - if (pollset->po.pi == nullptr) { - return; /* Fatal error. We 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(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); - - gpr_atm_no_barrier_fetch_add(&pi->poller_count, 1); - g_current_thread_polling_island = pi; - - GRPC_SCHEDULING_START_BLOCKING_REGION; - GRPC_STATS_INC_SYSCALL_POLL(); - ep_rv = - epoll_pwait(epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, timeout_ms, sig_mask); - GRPC_SCHEDULING_END_BLOCKING_REGION; - 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 = reinterpret_cast( - reinterpret_cast(data_ptr) & ~static_cast(1)); - bool track_err = - reinterpret_cast(data_ptr) & ~static_cast(1); - bool cancel = (ep_ev[i].events & EPOLLHUP) != 0; - bool error = (ep_ev[i].events & EPOLLERR) != 0; - bool read_ev = (ep_ev[i].events & (EPOLLIN | EPOLLPRI)) != 0; - bool write_ev = (ep_ev[i].events & EPOLLOUT) != 0; - bool err_fallback = error && !track_err; - - if (error && !err_fallback) { - fd_has_errors(fd); - } - if (read_ev || cancel || err_fallback) { - fd_become_readable(fd); - } - if (write_ev || cancel || err_fallback) { - fd_become_writable(fd); - } - } - } - - g_current_thread_polling_island = nullptr; - gpr_atm_no_barrier_fetch_add(&pi->poller_count, -1); - - GPR_ASSERT(pi != nullptr); - - /* 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(pi, "ps_work"); -} - -/* 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_pollset* pollset, - grpc_pollset_worker** worker_hdl, - grpc_millis deadline) { - GPR_TIMER_SCOPE("pollset_work", 0); - grpc_error* error = GRPC_ERROR_NONE; - int timeout_ms = poll_deadline_to_millis_timeout(deadline); - - sigset_t new_mask; - - grpc_pollset_worker worker; - worker.next = worker.prev = nullptr; - worker.pt_id = pthread_self(); - gpr_atm_no_barrier_store(&worker.is_kicked, (gpr_atm)0); - - 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(&new_mask); - sigaddset(&new_mask, grpc_wakeup_signal); - pthread_sigmask(SIG_BLOCK, &new_mask, &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(pollset, &worker, timeout_ms, &g_orig_sigmask, - &error); - grpc_core::ExecCtx::Get()->Flush(); - - 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(pollset); - - gpr_mu_unlock(&pollset->po.mu); - grpc_core::ExecCtx::Get()->Flush(); - gpr_mu_lock(&pollset->po.mu); - } - - if (worker_hdl) *worker_hdl = nullptr; - - gpr_tls_set(&g_current_thread_pollset, (intptr_t)0); - gpr_tls_set(&g_current_thread_worker, (intptr_t)0); - - GRPC_LOG_IF_ERROR("pollset_work", GRPC_ERROR_REF(error)); - return error; -} - -static void add_poll_object(poll_obj* bag, poll_obj_type bag_type, - poll_obj* item, poll_obj_type item_type) { - GPR_TIMER_SCOPE("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 = nullptr; - - 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 == nullptr) { - /* 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(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 != nullptr) { - 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(pi_new, "dance_of_destruction"); - goto retry; - } - } else { - pi_new = polling_island_create(nullptr, &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 == nullptr) { - /* 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 == nullptr) { - /* 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 != nullptr) { - PI_UNREF(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 != nullptr) { - PI_UNREF(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); -} - -static void pollset_add_fd(grpc_pollset* pollset, grpc_fd* fd) { - add_poll_object(&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 = - static_cast(gpr_malloc(sizeof(*pss))); - gpr_mu_init(&pss->po.mu); - pss->po.pi = nullptr; -#ifndef NDEBUG - pss->po.obj_type = POLL_OBJ_POLLSET_SET; -#endif - return pss; -} - -static void pollset_set_destroy(grpc_pollset_set* pss) { - gpr_mu_destroy(&pss->po.mu); - - if (pss->po.pi != nullptr) { - PI_UNREF(pss->po.pi, "pss_destroy"); - } - - gpr_free(pss); -} - -static void pollset_set_add_fd(grpc_pollset_set* pss, grpc_fd* fd) { - add_poll_object(&pss->po, POLL_OBJ_POLLSET_SET, &fd->po, POLL_OBJ_FD); -} - -static void pollset_set_del_fd(grpc_pollset_set* pss, grpc_fd* fd) { - /* Nothing to do */ -} - -static void pollset_set_add_pollset(grpc_pollset_set* pss, grpc_pollset* ps) { - add_poll_object(&pss->po, POLL_OBJ_POLLSET_SET, &ps->po, POLL_OBJ_POLLSET); -} - -static void pollset_set_del_pollset(grpc_pollset_set* pss, grpc_pollset* ps) { - /* Nothing to do */ -} - -static void pollset_set_add_pollset_set(grpc_pollset_set* bag, - grpc_pollset_set* item) { - add_poll_object(&bag->po, POLL_OBJ_POLLSET_SET, &item->po, - POLL_OBJ_POLLSET_SET); -} - -static void pollset_set_del_pollset_set(grpc_pollset_set* bag, - grpc_pollset_set* item) { - /* Nothing to do */ -} - -/* Test helper functions - * */ -void* grpc_fd_get_polling_island(grpc_fd* fd) { - polling_island* pi; - - gpr_mu_lock(&fd->po.mu); - pi = fd->po.pi; - gpr_mu_unlock(&fd->po.mu); - - return pi; -} - -void* grpc_pollset_get_polling_island(grpc_pollset* ps) { - polling_island* pi; - - gpr_mu_lock(&ps->po.mu); - pi = ps->po.pi; - gpr_mu_unlock(&ps->po.mu); - - return pi; -} - -bool grpc_are_polling_islands_equal(void* p, void* q) { - polling_island* p1 = static_cast(p); - polling_island* p2 = static_cast(q); - - /* Note: polling_island_lock_pair() may change p1 and p2 to point to the - latest polling islands in their respective linked lists */ - polling_island_lock_pair(&p1, &p2); - polling_island_unlock_pair(p1, p2); - - return p1 == p2; -} - -/******************************************************************************* - * 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 = { - sizeof(grpc_pollset), - true, - - fd_create, - fd_wrapped_fd, - fd_orphan, - fd_shutdown, - fd_notify_on_read, - fd_notify_on_write, - fd_notify_on_error, - fd_become_readable, - fd_become_writable, - fd_has_errors, - fd_is_shutdown, - - pollset_init, - pollset_shutdown, - pollset_destroy, - pollset_work, - pollset_kick, - pollset_add_fd, - - pollset_set_create, - pollset_set_destroy, - pollset_set_add_pollset, - pollset_set_del_pollset, - pollset_set_add_pollset_set, - pollset_set_del_pollset_set, - pollset_set_add_fd, - pollset_set_del_fd, - - 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; -} - -const grpc_event_engine_vtable* grpc_init_epollsig_linux( - bool explicit_request) { - /* If use of signals is disabled, we cannot use epoll engine*/ - if (is_grpc_wakeup_signal_initialized && grpc_wakeup_signal < 0) { - gpr_log(GPR_ERROR, "Skipping epollsig because use of signals is disabled."); - return nullptr; - } - - if (!grpc_has_wakeup_fd()) { - gpr_log(GPR_ERROR, "Skipping epollsig because of no wakeup fd."); - return nullptr; - } - - if (!is_epoll_available()) { - gpr_log(GPR_ERROR, "Skipping epollsig because epoll is unavailable."); - return nullptr; - } - - if (!is_grpc_wakeup_signal_initialized) { - if (explicit_request) { - grpc_use_signal(SIGRTMIN + 6); - } else { - gpr_log(GPR_ERROR, - "Skipping epollsig because uninitialized wakeup signal."); - return nullptr; - } - } - - fd_global_init(); - - if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) { - return nullptr; - } - - if (!GRPC_LOG_IF_ERROR("polling_island_global_init", - polling_island_global_init())) { - return nullptr; - } - - return &vtable; -} - -#else /* defined(GRPC_LINUX_EPOLL_CREATE1) */ -#if defined(GRPC_POSIX_SOCKET_EV_EPOLLSIG) -#include "src/core/lib/iomgr/ev_epollsig_linux.h" -/* If GRPC_LINUX_EPOLL_CREATE1 is not defined, it means - epoll_create1 is not available. Return NULL */ -const grpc_event_engine_vtable* grpc_init_epollsig_linux( - bool explicit_request) { - return nullptr; -} -#endif /* defined(GRPC_POSIX_SOCKET) */ - -void grpc_use_signal(int signum) {} -#endif /* !defined(GRPC_LINUX_EPOLL_CREATE1) */ diff --git a/src/core/lib/iomgr/ev_epollsig_linux.h b/src/core/lib/iomgr/ev_epollsig_linux.h deleted file mode 100644 index 2ba2f0a63b4..00000000000 --- a/src/core/lib/iomgr/ev_epollsig_linux.h +++ /dev/null @@ -1,35 +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_EPOLLSIG_LINUX_H -#define GRPC_CORE_LIB_IOMGR_EV_EPOLLSIG_LINUX_H - -#include - -#include "src/core/lib/iomgr/ev_posix.h" -#include "src/core/lib/iomgr/port.h" - -const grpc_event_engine_vtable* grpc_init_epollsig_linux(bool explicit_request); - -#ifdef GRPC_LINUX_EPOLL_CREATE1 -void* grpc_fd_get_polling_island(grpc_fd* fd); -void* grpc_pollset_get_polling_island(grpc_pollset* ps); -bool grpc_are_polling_islands_equal(void* p, void* q); -#endif /* defined(GRPC_LINUX_EPOLL_CREATE1) */ - -#endif /* GRPC_CORE_LIB_IOMGR_EV_EPOLLSIG_LINUX_H */ diff --git a/src/core/lib/iomgr/ev_posix.cc b/src/core/lib/iomgr/ev_posix.cc index d4377e2d504..8a7dc7b004a 100644 --- a/src/core/lib/iomgr/ev_posix.cc +++ b/src/core/lib/iomgr/ev_posix.cc @@ -35,7 +35,6 @@ #include "src/core/lib/gpr/useful.h" #include "src/core/lib/iomgr/ev_epoll1_linux.h" #include "src/core/lib/iomgr/ev_epollex_linux.h" -#include "src/core/lib/iomgr/ev_epollsig_linux.h" #include "src/core/lib/iomgr/ev_poll_posix.h" grpc_core::TraceFlag grpc_polling_trace(false, @@ -123,13 +122,13 @@ const grpc_event_engine_vtable* init_non_polling(bool explicit_request) { // environment variable if that variable is set (which should be a // comma-separated list of one or more event engine names) static event_engine_factory g_factories[] = { - {ENGINE_HEAD_CUSTOM, nullptr}, {ENGINE_HEAD_CUSTOM, nullptr}, - {ENGINE_HEAD_CUSTOM, nullptr}, {ENGINE_HEAD_CUSTOM, nullptr}, - {"epollex", grpc_init_epollex_linux}, {"epoll1", grpc_init_epoll1_linux}, - {"epollsig", grpc_init_epollsig_linux}, {"poll", grpc_init_poll_posix}, - {"poll-cv", grpc_init_poll_cv_posix}, {"none", init_non_polling}, - {ENGINE_TAIL_CUSTOM, nullptr}, {ENGINE_TAIL_CUSTOM, nullptr}, - {ENGINE_TAIL_CUSTOM, nullptr}, {ENGINE_TAIL_CUSTOM, nullptr}, + {ENGINE_HEAD_CUSTOM, nullptr}, {ENGINE_HEAD_CUSTOM, nullptr}, + {ENGINE_HEAD_CUSTOM, nullptr}, {ENGINE_HEAD_CUSTOM, nullptr}, + {"epollex", grpc_init_epollex_linux}, {"epoll1", grpc_init_epoll1_linux}, + {"poll", grpc_init_poll_posix}, {"poll-cv", grpc_init_poll_cv_posix}, + {"none", init_non_polling}, {ENGINE_TAIL_CUSTOM, nullptr}, + {ENGINE_TAIL_CUSTOM, nullptr}, {ENGINE_TAIL_CUSTOM, nullptr}, + {ENGINE_TAIL_CUSTOM, nullptr}, }; static void add(const char* beg, const char* end, char*** ss, size_t* ns) { diff --git a/src/core/lib/iomgr/port.h b/src/core/lib/iomgr/port.h index abf96662f51..c8046b21dcd 100644 --- a/src/core/lib/iomgr/port.h +++ b/src/core/lib/iomgr/port.h @@ -82,6 +82,11 @@ #define GRPC_LINUX_SOCKETUTILS 1 #endif #endif +#ifdef LINUX_VERSION_CODE +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) +#define GRPC_HAVE_TCP_USER_TIMEOUT +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) */ +#endif /* LINUX_VERSION_CODE */ #ifndef __GLIBC__ #define GRPC_LINUX_EPOLL 1 #define GRPC_LINUX_EPOLL_CREATE1 1 @@ -110,7 +115,6 @@ #define GRPC_POSIX_SOCKET_EV 1 #define GRPC_POSIX_SOCKET_EV_EPOLL1 1 #define GRPC_POSIX_SOCKET_EV_EPOLLEX 1 -#define GRPC_POSIX_SOCKET_EV_EPOLLSIG 1 #define GRPC_POSIX_SOCKET_EV_POLL 1 #define GRPC_POSIX_SOCKET_RESOLVE_ADDRESS 1 #define GRPC_POSIX_SOCKET_SOCKADDR 1 @@ -178,7 +182,6 @@ #define GRPC_POSIX_SOCKET_ARES_EV_DRIVER 1 #define GRPC_POSIX_SOCKET_EV 1 #define GRPC_POSIX_SOCKET_EV_EPOLLEX 1 -#define GRPC_POSIX_SOCKET_EV_EPOLLSIG 1 #define GRPC_POSIX_SOCKET_EV_POLL 1 #define GRPC_POSIX_SOCKET_EV_EPOLL1 1 #define GRPC_POSIX_SOCKET_IOMGR 1 diff --git a/src/core/lib/iomgr/socket_utils_common_posix.cc b/src/core/lib/iomgr/socket_utils_common_posix.cc index c4b991c94d8..50674b08455 100644 --- a/src/core/lib/iomgr/socket_utils_common_posix.cc +++ b/src/core/lib/iomgr/socket_utils_common_posix.cc @@ -41,6 +41,7 @@ #include #include +#include "src/core/lib/channel/channel_args.h" #include "src/core/lib/gpr/host_port.h" #include "src/core/lib/gpr/string.h" #include "src/core/lib/iomgr/sockaddr.h" @@ -222,6 +223,95 @@ grpc_error* grpc_set_socket_low_latency(int fd, int low_latency) { return GRPC_ERROR_NONE; } +/* The default values for TCP_USER_TIMEOUT are currently configured to be in + * line with the default values of KEEPALIVE_TIMEOUT as proposed in + * https://github.com/grpc/proposal/blob/master/A18-tcp-user-timeout.md */ +#define DEFAULT_CLIENT_TCP_USER_TIMEOUT_MS 20000 /* 20 seconds */ +#define DEFAULT_SERVER_TCP_USER_TIMEOUT_MS 20000 /* 20 seconds */ + +static int g_default_client_tcp_user_timeout_ms = + DEFAULT_CLIENT_TCP_USER_TIMEOUT_MS; +static int g_default_server_tcp_user_timeout_ms = + DEFAULT_SERVER_TCP_USER_TIMEOUT_MS; +static bool g_default_client_tcp_user_timeout_enabled = false; +static bool g_default_server_tcp_user_timeout_enabled = true; + +void config_default_tcp_user_timeout(bool enable, int timeout, bool is_client) { + if (is_client) { + g_default_client_tcp_user_timeout_enabled = enable; + if (timeout > 0) { + g_default_client_tcp_user_timeout_ms = timeout; + } + } else { + g_default_server_tcp_user_timeout_enabled = enable; + if (timeout > 0) { + g_default_server_tcp_user_timeout_ms = timeout; + } + } +} + +/* Set TCP_USER_TIMEOUT */ +grpc_error* grpc_set_socket_tcp_user_timeout( + int fd, const grpc_channel_args* channel_args, bool is_client) { +#ifdef GRPC_HAVE_TCP_USER_TIMEOUT + bool enable; + int timeout; + if (is_client) { + enable = g_default_client_tcp_user_timeout_enabled; + timeout = g_default_client_tcp_user_timeout_ms; + } else { + enable = g_default_server_tcp_user_timeout_enabled; + timeout = g_default_server_tcp_user_timeout_ms; + } + if (channel_args) { + for (unsigned int i = 0; i < channel_args->num_args; i++) { + if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_KEEPALIVE_TIME_MS)) { + const int value = grpc_channel_arg_get_integer( + &channel_args->args[i], grpc_integer_options{0, 1, INT_MAX}); + /* Continue using default if value is 0 */ + if (value == 0) { + continue; + } + /* Disable if value is INT_MAX */ + enable = value != INT_MAX; + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_KEEPALIVE_TIMEOUT_MS)) { + const int value = grpc_channel_arg_get_integer( + &channel_args->args[i], grpc_integer_options{0, 1, INT_MAX}); + /* Continue using default if value is 0 */ + if (value == 0) { + continue; + } + timeout = value; + } + } + } + if (enable) { + extern grpc_core::TraceFlag grpc_tcp_trace; + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "Enabling TCP_USER_TIMEOUT with a timeout of %d ms", + timeout); + } + int newval; + socklen_t len = sizeof(newval); + if (0 != setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &timeout, + sizeof(timeout))) { + return GRPC_OS_ERROR(errno, "setsockopt(TCP_USER_TIMEOUT)"); + } + if (0 != getsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &newval, &len)) { + return GRPC_OS_ERROR(errno, "getsockopt(TCP_USER_TIMEOUT)"); + } + if (newval != timeout) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Failed to set TCP_USER_TIMEOUT"); + } + } +#else + gpr_log(GPR_INFO, "TCP_USER_TIMEOUT not supported for this platform"); +#endif /* GRPC_HAVE_TCP_USER_TIMEOUT */ + return GRPC_ERROR_NONE; +} + /* set a socket using a grpc_socket_mutator */ grpc_error* grpc_set_socket_with_mutator(int fd, grpc_socket_mutator* mutator) { GPR_ASSERT(mutator); diff --git a/src/core/lib/iomgr/socket_utils_posix.h b/src/core/lib/iomgr/socket_utils_posix.h index b3fd58a5302..71a304dc4ef 100644 --- a/src/core/lib/iomgr/socket_utils_posix.h +++ b/src/core/lib/iomgr/socket_utils_posix.h @@ -53,6 +53,13 @@ grpc_error* grpc_set_socket_low_latency(int fd, int low_latency); /* set SO_REUSEPORT */ grpc_error* grpc_set_socket_reuse_port(int fd, int reuse); +/* Configure the default values for TCP_USER_TIMEOUT */ +void config_default_tcp_user_timeout(bool enable, int timeout, bool is_client); + +/* Set TCP_USER_TIMEOUT */ +grpc_error* grpc_set_socket_tcp_user_timeout( + int fd, const grpc_channel_args* channel_args, bool is_client); + /* Returns true if this system can create AF_INET6 sockets bound to ::1. The value is probed once, and cached for the life of the process. diff --git a/src/core/lib/iomgr/tcp_client_posix.cc b/src/core/lib/iomgr/tcp_client_posix.cc index 9c989b7dfee..8553ed0db4e 100644 --- a/src/core/lib/iomgr/tcp_client_posix.cc +++ b/src/core/lib/iomgr/tcp_client_posix.cc @@ -76,6 +76,9 @@ static grpc_error* prepare_socket(const grpc_resolved_address* addr, int fd, if (!grpc_is_unix_socket(addr)) { err = grpc_set_socket_low_latency(fd, 1); if (err != GRPC_ERROR_NONE) goto error; + err = grpc_set_socket_tcp_user_timeout(fd, channel_args, + true /* is_client */); + if (err != GRPC_ERROR_NONE) goto error; } err = grpc_set_socket_no_sigpipe_if_possible(fd); if (err != GRPC_ERROR_NONE) goto error; diff --git a/src/core/lib/iomgr/tcp_server_utils_posix_common.cc b/src/core/lib/iomgr/tcp_server_utils_posix_common.cc index 9595c028ce0..8d8d3f4273e 100644 --- a/src/core/lib/iomgr/tcp_server_utils_posix_common.cc +++ b/src/core/lib/iomgr/tcp_server_utils_posix_common.cc @@ -166,6 +166,9 @@ grpc_error* grpc_tcp_server_prepare_socket(grpc_tcp_server* s, int fd, if (err != GRPC_ERROR_NONE) goto error; err = grpc_set_socket_reuse_addr(fd, 1); if (err != GRPC_ERROR_NONE) goto error; + err = grpc_set_socket_tcp_user_timeout(fd, s->channel_args, + false /* is_client */); + if (err != GRPC_ERROR_NONE) goto error; } err = grpc_set_socket_no_sigpipe_if_possible(fd); if (err != GRPC_ERROR_NONE) goto error; diff --git a/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc b/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc index 0a39c6c4858..118d18d1191 100644 --- a/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc +++ b/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc @@ -72,7 +72,8 @@ static void target_service_account_destroy( static const grpc_alts_credentials_options_vtable vtable = { alts_client_options_copy, alts_client_options_destroy}; -grpc_alts_credentials_options* grpc_alts_credentials_client_options_create() { +grpc_alts_credentials_options* grpc_alts_credentials_client_options_create( + void) { auto client_options = static_cast( gpr_zalloc(sizeof(grpc_alts_credentials_client_options))); client_options->base.vtable = &vtable; diff --git a/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc b/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc index 62aa7a620a0..1a59c456758 100644 --- a/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc +++ b/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc @@ -36,7 +36,8 @@ static void alts_server_options_destroy( static const grpc_alts_credentials_options_vtable vtable = { alts_server_options_copy, alts_server_options_destroy}; -grpc_alts_credentials_options* grpc_alts_credentials_server_options_create() { +grpc_alts_credentials_options* grpc_alts_credentials_server_options_create( + void) { grpc_alts_credentials_server_options* server_options = static_cast( gpr_zalloc(sizeof(*server_options))); diff --git a/src/core/lib/security/transport/server_auth_filter.cc b/src/core/lib/security/transport/server_auth_filter.cc index 552e70130ad..b99fc5e1783 100644 --- a/src/core/lib/security/transport/server_auth_filter.cc +++ b/src/core/lib/security/transport/server_auth_filter.cc @@ -41,9 +41,11 @@ struct call_data { grpc_transport_stream_op_batch* recv_initial_metadata_batch; grpc_closure* original_recv_initial_metadata_ready; grpc_closure recv_initial_metadata_ready; - grpc_error* error; + grpc_error* recv_initial_metadata_error; grpc_closure recv_trailing_metadata_ready; grpc_closure* original_recv_trailing_metadata_ready; + grpc_error* recv_trailing_metadata_error; + bool seen_recv_trailing_metadata_ready; grpc_metadata_array md; const grpc_metadata* consumed_md; size_t num_consumed_md; @@ -114,8 +116,16 @@ static void on_md_processing_done_inner(grpc_call_element* elem, batch->payload->recv_initial_metadata.recv_initial_metadata, remove_consumed_md, elem, "Response metadata filtering error"); } - calld->error = GRPC_ERROR_REF(error); - GRPC_CLOSURE_SCHED(calld->original_recv_initial_metadata_ready, error); + calld->recv_initial_metadata_error = GRPC_ERROR_REF(error); + grpc_closure* closure = calld->original_recv_initial_metadata_ready; + calld->original_recv_initial_metadata_ready = nullptr; + if (calld->seen_recv_trailing_metadata_ready) { + GRPC_CALL_COMBINER_START(calld->call_combiner, + &calld->recv_trailing_metadata_ready, + calld->recv_trailing_metadata_error, + "continue recv_trailing_metadata_ready"); + } + GRPC_CLOSURE_SCHED(closure, error); } // Called from application code. @@ -184,14 +194,30 @@ static void recv_initial_metadata_ready(void* arg, grpc_error* error) { return; } } - GRPC_CLOSURE_RUN(calld->original_recv_initial_metadata_ready, - GRPC_ERROR_REF(error)); + grpc_closure* closure = calld->original_recv_initial_metadata_ready; + calld->original_recv_initial_metadata_ready = nullptr; + if (calld->seen_recv_trailing_metadata_ready) { + GRPC_CALL_COMBINER_START(calld->call_combiner, + &calld->recv_trailing_metadata_ready, + calld->recv_trailing_metadata_error, + "continue recv_trailing_metadata_ready"); + } + GRPC_CLOSURE_RUN(closure, GRPC_ERROR_REF(error)); } static void recv_trailing_metadata_ready(void* user_data, grpc_error* err) { grpc_call_element* elem = static_cast(user_data); call_data* calld = static_cast(elem->call_data); - err = grpc_error_add_child(GRPC_ERROR_REF(err), GRPC_ERROR_REF(calld->error)); + if (calld->original_recv_initial_metadata_ready != nullptr) { + calld->recv_trailing_metadata_error = GRPC_ERROR_REF(err); + calld->seen_recv_trailing_metadata_ready = true; + GRPC_CALL_COMBINER_STOP(calld->call_combiner, + "deferring recv_trailing_metadata_ready until " + "after recv_initial_metadata_ready"); + return; + } + err = grpc_error_add_child( + GRPC_ERROR_REF(err), GRPC_ERROR_REF(calld->recv_initial_metadata_error)); GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_ready, err); } @@ -249,7 +275,7 @@ static void destroy_call_elem(grpc_call_element* elem, const grpc_call_final_info* final_info, grpc_closure* ignored) { call_data* calld = static_cast(elem->call_data); - GRPC_ERROR_UNREF(calld->error); + GRPC_ERROR_UNREF(calld->recv_initial_metadata_error); } /* Constructor for channel_data */ diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc index 11b438f5dc8..a9349afa68a 100644 --- a/src/core/lib/surface/call.cc +++ b/src/core/lib/surface/call.cc @@ -207,7 +207,7 @@ struct grpc_call { grpc_server* server; } server; } final_op; - grpc_error* status_error; + gpr_atm status_error; /* recv_state can contain one of the following values: RECV_NONE : : no initial metadata and messages received @@ -519,10 +519,12 @@ static void destroy_call(void* call, grpc_error* error) { GRPC_CQ_INTERNAL_UNREF(c->cq, "bind"); } - grpc_error_get_status(c->status_error, c->send_deadline, + grpc_error* status_error = + reinterpret_cast(gpr_atm_acq_load(&c->status_error)); + grpc_error_get_status(status_error, c->send_deadline, &c->final_info.final_status, nullptr, nullptr, &(c->final_info.error_string)); - GRPC_ERROR_UNREF(c->status_error); + GRPC_ERROR_UNREF(status_error); c->final_info.stats.latency = gpr_time_sub(gpr_now(GPR_CLOCK_MONOTONIC), c->start_time); @@ -705,7 +707,7 @@ static void set_final_status(grpc_call* call, grpc_error* error) { call->final_op.client.error_string); // explicitly take a ref grpc_slice_ref_internal(*call->final_op.client.status_details); - call->status_error = error; + gpr_atm_rel_store(&call->status_error, reinterpret_cast(error)); grpc_core::channelz::ChannelNode* channelz_channel = grpc_channel_get_channelz_node(call->channel); if (channelz_channel != nullptr) { @@ -717,7 +719,9 @@ static void set_final_status(grpc_call* call, grpc_error* error) { } } else { *call->final_op.server.cancelled = - error != GRPC_ERROR_NONE || call->status_error != GRPC_ERROR_NONE; + error != GRPC_ERROR_NONE || + reinterpret_cast(gpr_atm_acq_load(&call->status_error)) != + GRPC_ERROR_NONE; grpc_core::channelz::ServerNode* channelz_server = grpc_server_get_channelz_node(call->final_op.server.server); if (channelz_server != nullptr) { @@ -1686,7 +1690,8 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops, } } - call->status_error = status_error; + gpr_atm_rel_store(&call->status_error, + reinterpret_cast(status_error)); if (!prepare_application_metadata( call, static_cast( diff --git a/src/core/lib/surface/completion_queue.cc b/src/core/lib/surface/completion_queue.cc index 0769d9e4f63..5dc9991f703 100644 --- a/src/core/lib/surface/completion_queue.cc +++ b/src/core/lib/surface/completion_queue.cc @@ -184,7 +184,8 @@ static const cq_poller_vtable g_poller_vtable_by_poller_type[] = { typedef struct cq_vtable { grpc_cq_completion_type cq_completion_type; size_t data_size; - void (*init)(void* data, grpc_core::CQCallbackInterface* shutdown_callback); + void (*init)(void* data, + grpc_experimental_completion_queue_functor* shutdown_callback); void (*shutdown)(grpc_completion_queue* cq); void (*destroy)(void* data); bool (*begin_op)(grpc_completion_queue* cq, void* tag); @@ -267,7 +268,7 @@ typedef struct cq_callback_data { bool shutdown_called; /** A callback that gets invoked when the CQ completes shutdown */ - grpc_core::CQCallbackInterface* shutdown_callback; + grpc_experimental_completion_queue_functor* shutdown_callback; } cq_callback_data; /* Completion queue structure */ @@ -333,12 +334,12 @@ static grpc_event cq_pluck(grpc_completion_queue* cq, void* tag, gpr_timespec deadline, void* reserved); // Note that cq_init_next and cq_init_pluck do not use the shutdown_callback -static void cq_init_next(void* data, - grpc_core::CQCallbackInterface* shutdown_callback); -static void cq_init_pluck(void* data, - grpc_core::CQCallbackInterface* shutdown_callback); -static void cq_init_callback(void* data, - grpc_core::CQCallbackInterface* shutdown_callback); +static void cq_init_next( + void* data, grpc_experimental_completion_queue_functor* shutdown_callback); +static void cq_init_pluck( + void* data, grpc_experimental_completion_queue_functor* shutdown_callback); +static void cq_init_callback( + void* data, grpc_experimental_completion_queue_functor* shutdown_callback); static void cq_destroy_next(void* data); static void cq_destroy_pluck(void* data); static void cq_destroy_callback(void* data); @@ -462,7 +463,7 @@ static long cq_event_queue_num_items(grpc_cq_event_queue* q) { grpc_completion_queue* grpc_completion_queue_create_internal( grpc_cq_completion_type completion_type, grpc_cq_polling_type polling_type, - grpc_core::CQCallbackInterface* shutdown_callback) { + grpc_experimental_completion_queue_functor* shutdown_callback) { GPR_TIMER_SCOPE("grpc_completion_queue_create_internal", 0); grpc_completion_queue* cq; @@ -497,8 +498,8 @@ grpc_completion_queue* grpc_completion_queue_create_internal( return cq; } -static void cq_init_next(void* data, - grpc_core::CQCallbackInterface* shutdown_callback) { +static void cq_init_next( + void* data, grpc_experimental_completion_queue_functor* shutdown_callback) { cq_next_data* cqd = static_cast(data); /* Initial count is dropped by grpc_completion_queue_shutdown */ gpr_atm_no_barrier_store(&cqd->pending_events, 1); @@ -513,8 +514,8 @@ static void cq_destroy_next(void* data) { cq_event_queue_destroy(&cqd->queue); } -static void cq_init_pluck(void* data, - grpc_core::CQCallbackInterface* shutdown_callback) { +static void cq_init_pluck( + void* data, grpc_experimental_completion_queue_functor* shutdown_callback) { cq_pluck_data* cqd = static_cast(data); /* Initial count is dropped by grpc_completion_queue_shutdown */ gpr_atm_no_barrier_store(&cqd->pending_events, 1); @@ -532,7 +533,7 @@ static void cq_destroy_pluck(void* data) { } static void cq_init_callback( - void* data, grpc_core::CQCallbackInterface* shutdown_callback) { + void* data, grpc_experimental_completion_queue_functor* shutdown_callback) { cq_callback_data* cqd = static_cast(data); /* Initial count is dropped by grpc_completion_queue_shutdown */ gpr_atm_no_barrier_store(&cqd->pending_events, 1); @@ -859,7 +860,8 @@ static void cq_end_op_for_callback( GRPC_ERROR_UNREF(error); - (static_cast(tag))->Run(is_success); + auto* functor = static_cast(tag); + (*functor->functor_run)(functor, is_success); } void grpc_cq_end_op(grpc_completion_queue* cq, void* tag, grpc_error* error, @@ -1343,7 +1345,7 @@ static void cq_finish_shutdown_callback(grpc_completion_queue* cq) { GPR_ASSERT(cqd->shutdown_called); cq->poller_vtable->shutdown(POLLSET_FROM_CQ(cq), &cq->pollset_shutdown_done); - callback->Run(true); + (*callback->functor_run)(callback, true); } static void cq_shutdown_callback(grpc_completion_queue* cq) { @@ -1364,9 +1366,11 @@ static void cq_shutdown_callback(grpc_completion_queue* cq) { } cqd->shutdown_called = true; if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) { + gpr_mu_unlock(cq->mu); cq_finish_shutdown_callback(cq); + } else { + gpr_mu_unlock(cq->mu); } - gpr_mu_unlock(cq->mu); GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down (callback cq)"); } diff --git a/src/core/lib/surface/completion_queue.h b/src/core/lib/surface/completion_queue.h index a7c524d8e8f..d60fe6d6efe 100644 --- a/src/core/lib/surface/completion_queue.h +++ b/src/core/lib/surface/completion_queue.h @@ -48,23 +48,6 @@ typedef struct grpc_cq_completion { uintptr_t next; } grpc_cq_completion; -/// For callback CQs, the tag that is passed in for an operation must -/// actually be a pointer to an implementation of the following class. -/// When the operation completes, the tag will be typecasted from void* -/// to grpc_core::CQCallbackInterface* and then the Run method will be -/// invoked on it. In practice, the language binding (e.g., C++ API -/// implementation) is responsible for providing and using an implementation -/// of this abstract base class. -namespace grpc_core { -class CQCallbackInterface { - public: - virtual ~CQCallbackInterface() {} - virtual void Run(bool) GRPC_ABSTRACT; - - GRPC_ABSTRACT_BASE_CLASS -}; -} // namespace grpc_core - #ifndef NDEBUG void grpc_cq_internal_ref(grpc_completion_queue* cc, const char* reason, const char* file, int line); @@ -106,6 +89,6 @@ int grpc_get_cq_poll_num(grpc_completion_queue* cc); grpc_completion_queue* grpc_completion_queue_create_internal( grpc_cq_completion_type completion_type, grpc_cq_polling_type polling_type, - grpc_core::CQCallbackInterface* shutdown_callback); + grpc_experimental_completion_queue_functor* shutdown_callback); #endif /* GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_H */ diff --git a/src/core/lib/surface/completion_queue_factory.cc b/src/core/lib/surface/completion_queue_factory.cc index ed92dd7eba6..2616c156e45 100644 --- a/src/core/lib/surface/completion_queue_factory.cc +++ b/src/core/lib/surface/completion_queue_factory.cc @@ -31,8 +31,7 @@ static grpc_completion_queue* default_create( const grpc_completion_queue_factory* factory, const grpc_completion_queue_attributes* attr) { return grpc_completion_queue_create_internal( - attr->cq_completion_type, attr->cq_polling_type, - static_cast(attr->cq_shutdown_cb)); + attr->cq_completion_type, attr->cq_polling_type, attr->cq_shutdown_cb); } static grpc_completion_queue_factory_vtable default_vtable = {default_create}; @@ -73,7 +72,8 @@ grpc_completion_queue* grpc_completion_queue_create_for_pluck(void* reserved) { } grpc_completion_queue* grpc_completion_queue_create_for_callback( - void* shutdown_callback, void* reserved) { + grpc_experimental_completion_queue_functor* shutdown_callback, + void* reserved) { GPR_ASSERT(!reserved); grpc_completion_queue_attributes attr = { 2, GRPC_CQ_CALLBACK, GRPC_CQ_DEFAULT_POLLING, shutdown_callback}; diff --git a/src/core/lib/surface/server.cc b/src/core/lib/surface/server.cc index 5fa58ffdec2..72ddc2648d9 100644 --- a/src/core/lib/surface/server.cc +++ b/src/core/lib/surface/server.cc @@ -150,12 +150,15 @@ struct call_data { grpc_closure kill_zombie_closure; grpc_closure* on_done_recv_initial_metadata; grpc_closure recv_trailing_metadata_ready; - grpc_error* error; + grpc_error* recv_initial_metadata_error; grpc_closure* original_recv_trailing_metadata_ready; + grpc_error* recv_trailing_metadata_error; + bool seen_recv_trailing_metadata_ready; grpc_closure publish; call_data* pending_next; + grpc_call_combiner* call_combiner; }; struct request_matcher { @@ -727,21 +730,43 @@ static void server_on_recv_initial_metadata(void* ptr, grpc_error* error) { if (calld->host_set && calld->path_set) { /* do nothing */ } else { + /* Pass the error reference to calld->recv_initial_metadata_error */ grpc_error* src_error = error; error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Missing :authority or :path", &error, 1); + "Missing :authority or :path", &src_error, 1); GRPC_ERROR_UNREF(src_error); + calld->recv_initial_metadata_error = GRPC_ERROR_REF(error); } - - GRPC_CLOSURE_RUN(calld->on_done_recv_initial_metadata, error); + grpc_closure* closure = calld->on_done_recv_initial_metadata; + calld->on_done_recv_initial_metadata = nullptr; + if (calld->seen_recv_trailing_metadata_ready) { + GRPC_CALL_COMBINER_START(calld->call_combiner, + &calld->recv_trailing_metadata_ready, + calld->recv_trailing_metadata_error, + "continue server_recv_trailing_metadata_ready"); + } + GRPC_CLOSURE_RUN(closure, error); } static void server_recv_trailing_metadata_ready(void* user_data, - grpc_error* err) { + grpc_error* error) { grpc_call_element* elem = static_cast(user_data); call_data* calld = static_cast(elem->call_data); - err = grpc_error_add_child(GRPC_ERROR_REF(err), GRPC_ERROR_REF(calld->error)); - GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_ready, err); + if (calld->on_done_recv_initial_metadata != nullptr) { + calld->recv_trailing_metadata_error = GRPC_ERROR_REF(error); + calld->seen_recv_trailing_metadata_ready = true; + GRPC_CLOSURE_INIT(&calld->recv_trailing_metadata_ready, + server_recv_trailing_metadata_ready, elem, + grpc_schedule_on_exec_ctx); + GRPC_CALL_COMBINER_STOP(calld->call_combiner, + "deferring server_recv_trailing_metadata_ready " + "until after server_on_recv_initial_metadata"); + return; + } + error = + grpc_error_add_child(GRPC_ERROR_REF(error), + GRPC_ERROR_REF(calld->recv_initial_metadata_error)); + GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_ready, error); } static void server_mutate_op(grpc_call_element* elem, @@ -845,6 +870,7 @@ static grpc_error* init_call_elem(grpc_call_element* elem, memset(calld, 0, sizeof(call_data)); calld->deadline = GRPC_MILLIS_INF_FUTURE; calld->call = grpc_call_from_top_element(elem); + calld->call_combiner = args->call_combiner; GRPC_CLOSURE_INIT(&calld->server_on_recv_initial_metadata, server_on_recv_initial_metadata, elem, @@ -863,7 +889,7 @@ static void destroy_call_elem(grpc_call_element* elem, call_data* calld = static_cast(elem->call_data); GPR_ASSERT(calld->state != PENDING); - GRPC_ERROR_UNREF(calld->error); + GRPC_ERROR_UNREF(calld->recv_initial_metadata_error); if (calld->host_set) { grpc_slice_unref_internal(calld->host); } diff --git a/src/core/lib/transport/metadata.cc b/src/core/lib/transport/metadata.cc index d10194a2fe7..d164502280a 100644 --- a/src/core/lib/transport/metadata.cc +++ b/src/core/lib/transport/metadata.cc @@ -342,24 +342,6 @@ grpc_mdelem grpc_mdelem_from_grpc_metadata(grpc_metadata* metadata) { changed ? nullptr : reinterpret_cast(metadata)); } -static size_t get_base64_encoded_size(size_t raw_length) { - static const uint8_t tail_xtra[3] = {0, 2, 3}; - return raw_length / 3 * 4 + tail_xtra[raw_length % 3]; -} - -size_t grpc_mdelem_get_size_in_hpack_table(grpc_mdelem elem, - bool use_true_binary_metadata) { - size_t overhead_and_key = 32 + GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)); - size_t value_len = GRPC_SLICE_LENGTH(GRPC_MDVALUE(elem)); - if (grpc_is_binary_header(GRPC_MDKEY(elem))) { - return overhead_and_key + (use_true_binary_metadata - ? value_len + 1 - : get_base64_encoded_size(value_len)); - } else { - return overhead_and_key + value_len; - } -} - grpc_mdelem grpc_mdelem_ref(grpc_mdelem gmd DEBUG_ARGS) { switch (GRPC_MDELEM_STORAGE(gmd)) { case GRPC_MDELEM_STORAGE_EXTERNAL: diff --git a/src/core/lib/transport/metadata.h b/src/core/lib/transport/metadata.h index 78df4bc3a31..338082276cc 100644 --- a/src/core/lib/transport/metadata.h +++ b/src/core/lib/transport/metadata.h @@ -125,9 +125,6 @@ grpc_mdelem grpc_mdelem_create( bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b); -size_t grpc_mdelem_get_size_in_hpack_table(grpc_mdelem elem, - bool use_true_binary_metadata); - /* Mutator and accessor for grpc_mdelem user data. The destructor function is used as a type tag and is checked during user_data fetch. */ void* grpc_mdelem_get_user_data(grpc_mdelem md, void (*if_destroy_func)(void*)); diff --git a/src/core/lib/transport/metadata_batch.cc b/src/core/lib/transport/metadata_batch.cc index 49740fcd1e3..761aad19c45 100644 --- a/src/core/lib/transport/metadata_batch.cc +++ b/src/core/lib/transport/metadata_batch.cc @@ -105,7 +105,7 @@ static grpc_error* maybe_link_callout(grpc_metadata_batch* batch, return GRPC_ERROR_NONE; } if (batch->idx.array[idx] == nullptr) { - if (grpc_static_callout_is_default[idx]) ++batch->list.default_count; + ++batch->list.default_count; batch->idx.array[idx] = storage; return GRPC_ERROR_NONE; } @@ -121,7 +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; + --batch->list.default_count; GPR_ASSERT(batch->idx.array[idx] != nullptr); batch->idx.array[idx] = nullptr; } diff --git a/src/core/lib/transport/metadata_batch.h b/src/core/lib/transport/metadata_batch.h index 7068750b6f2..0bcbb32d1fe 100644 --- a/src/core/lib/transport/metadata_batch.h +++ b/src/core/lib/transport/metadata_batch.h @@ -82,6 +82,7 @@ void grpc_metadata_batch_set_value(grpc_linked_mdelem* storage, grpc_error* grpc_metadata_batch_link_head(grpc_metadata_batch* batch, grpc_linked_mdelem* storage) GRPC_MUST_USE_RESULT; + /** Add \a storage to the end of \a batch. storage->md is assumed to be valid. \a storage is owned by the caller and must survive for the @@ -100,6 +101,7 @@ grpc_error* grpc_metadata_batch_link_tail(grpc_metadata_batch* batch, grpc_error* grpc_metadata_batch_add_head( grpc_metadata_batch* batch, grpc_linked_mdelem* storage, grpc_mdelem elem_to_add) GRPC_MUST_USE_RESULT; + /** Add \a elem_to_add as the last element in \a batch, using \a storage as backing storage for the linked list element. \a storage is owned by the caller and must survive for the diff --git a/src/core/lib/transport/static_metadata.cc b/src/core/lib/transport/static_metadata.cc index 6a5144f21a2..ed88aa3f289 100644 --- a/src/core/lib/transport/static_metadata.cc +++ b/src/core/lib/transport/static_metadata.cc @@ -568,33 +568,6 @@ grpc_mdelem_data grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT] = { {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, {&grpc_static_metadata_refcounts[102], {{g_bytes + 1101, 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 - true, // grpc-previous-rpc-attempts - true, // grpc-retry-pushback-ms -}; - 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 b3a10f58730..1e95f4e5bb7 100644 --- a/src/core/lib/transport/static_metadata.h +++ b/src/core/lib/transport/static_metadata.h @@ -587,8 +587,6 @@ 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/cpp/client/channel_cc.cc b/src/cpp/client/channel_cc.cc index 39b891c2e1f..c59059f0450 100644 --- a/src/cpp/client/channel_cc.cc +++ b/src/cpp/client/channel_cc.cc @@ -42,8 +42,10 @@ #include #include "src/core/lib/gpr/env.h" #include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/memory.h" #include "src/core/lib/gprpp/thd.h" #include "src/core/lib/profiling/timers.h" +#include "src/core/lib/surface/completion_queue.h" namespace grpc { @@ -53,7 +55,12 @@ Channel::Channel(const grpc::string& host, grpc_channel* channel) g_gli_initializer.summon(); } -Channel::~Channel() { grpc_channel_destroy(c_channel_); } +Channel::~Channel() { + grpc_channel_destroy(c_channel_); + if (callback_cq_ != nullptr) { + callback_cq_->Shutdown(); + } +} namespace { @@ -135,8 +142,8 @@ void Channel::PerformOpsOnCall(internal::CallOpSetInterface* ops, size_t nops = 0; grpc_op cops[MAX_OPS]; ops->FillOps(call->call(), cops, &nops); - GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_batch(call->call(), cops, nops, ops, nullptr)); + GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(call->call(), cops, nops, + ops->cq_tag(), nullptr)); } void* Channel::RegisterMethod(const char* method) { @@ -185,4 +192,41 @@ bool Channel::WaitForStateChangeImpl(grpc_connectivity_state last_observed, return ok; } +namespace { +class ShutdownCallback : public grpc_experimental_completion_queue_functor { + public: + ShutdownCallback() { functor_run = &ShutdownCallback::Run; } + // TakeCQ takes ownership of the cq into the shutdown callback + // so that the shutdown callback will be responsible for destroying it + void TakeCQ(CompletionQueue* cq) { cq_ = cq; } + + // The Run function will get invoked by the completion queue library + // when the shutdown is actually complete + static void Run(grpc_experimental_completion_queue_functor* cb, int) { + auto* callback = static_cast(cb); + delete callback->cq_; + grpc_core::Delete(callback); + } + + private: + CompletionQueue* cq_ = nullptr; +}; +} // namespace + +CompletionQueue* Channel::CallbackCQ() { + // TODO(vjpai): Consider using a single global CQ for the default CQ + // if there is no explicit per-channel CQ registered + std::lock_guard l(mu_); + if (callback_cq_ == nullptr) { + auto* shutdown_callback = grpc_core::New(); + callback_cq_ = new CompletionQueue(grpc_completion_queue_attributes{ + GRPC_CQ_CURRENT_VERSION, GRPC_CQ_CALLBACK, GRPC_CQ_DEFAULT_POLLING, + shutdown_callback}); + + // Transfer ownership of the new cq to its own shutdown callback + shutdown_callback->TakeCQ(callback_cq_); + } + return callback_cq_; +} + } // namespace grpc diff --git a/src/cpp/client/generic_stub.cc b/src/cpp/client/generic_stub.cc index 67ef46bebea..87902b26f0e 100644 --- a/src/cpp/client/generic_stub.cc +++ b/src/cpp/client/generic_stub.cc @@ -16,9 +16,11 @@ * */ -#include +#include +#include #include +#include namespace grpc { @@ -60,4 +62,14 @@ std::unique_ptr GenericStub::PrepareUnaryCall( context, request, false)); } +void GenericStub::experimental_type::UnaryCall( + ClientContext* context, const grpc::string& method, + const ByteBuffer* request, ByteBuffer* response, + std::function on_completion) { + internal::CallbackUnaryCall( + stub_->channel_.get(), + internal::RpcMethod(method.c_str(), internal::RpcMethod::NORMAL_RPC), + context, request, response, std::move(on_completion)); +} + } // namespace grpc diff --git a/src/cpp/common/alarm.cc b/src/cpp/common/alarm.cc index 15a373d8a54..5819a4210bd 100644 --- a/src/cpp/common/alarm.cc +++ b/src/cpp/common/alarm.cc @@ -39,17 +39,6 @@ class AlarmImpl : public CompletionQueueTag { AlarmImpl() : cq_(nullptr), tag_(nullptr) { gpr_ref_init(&refs_, 1); grpc_timer_init_unset(&timer_); - GRPC_CLOSURE_INIT(&on_alarm_, - [](void* arg, grpc_error* error) { - // queue the op on the completion queue - AlarmImpl* alarm = static_cast(arg); - alarm->Ref(); - grpc_cq_end_op( - alarm->cq_, alarm, error, - [](void* arg, grpc_cq_completion* completion) {}, - arg, &alarm->completion_); - }, - this, grpc_schedule_on_exec_ctx); } ~AlarmImpl() { grpc_core::ExecCtx exec_ctx; @@ -68,6 +57,32 @@ class AlarmImpl : public CompletionQueueTag { cq_ = cq->cq(); tag_ = tag; GPR_ASSERT(grpc_cq_begin_op(cq_, this)); + GRPC_CLOSURE_INIT(&on_alarm_, + [](void* arg, grpc_error* error) { + // queue the op on the completion queue + AlarmImpl* alarm = static_cast(arg); + alarm->Ref(); + grpc_cq_end_op( + alarm->cq_, alarm, error, + [](void* arg, grpc_cq_completion* completion) {}, + arg, &alarm->completion_); + }, + this, grpc_schedule_on_exec_ctx); + grpc_timer_init(&timer_, grpc_timespec_to_millis_round_up(deadline), + &on_alarm_); + } + void Set(gpr_timespec deadline, std::function f) { + grpc_core::ExecCtx exec_ctx; + // Don't use any CQ at all. Instead just use the timer to fire the function + callback_ = std::move(f); + Ref(); + GRPC_CLOSURE_INIT(&on_alarm_, + [](void* arg, grpc_error* error) { + AlarmImpl* alarm = static_cast(arg); + alarm->callback_(error == GRPC_ERROR_NONE); + alarm->Unref(); + }, + this, grpc_schedule_on_exec_ctx); grpc_timer_init(&timer_, grpc_timespec_to_millis_round_up(deadline), &on_alarm_); } @@ -95,6 +110,7 @@ class AlarmImpl : public CompletionQueueTag { // completion queue where events about this alarm will be posted grpc_completion_queue* cq_; void* tag_; + std::function callback_; }; } // namespace internal @@ -113,6 +129,15 @@ void Alarm::SetInternal(CompletionQueue* cq, gpr_timespec deadline, void* tag) { static_cast(alarm_)->Set(cq, deadline, tag); } +void Alarm::SetInternal(gpr_timespec deadline, std::function f) { + // Note that we know that alarm_ is actually an internal::AlarmImpl + // but we declared it as the base pointer to avoid a forward declaration + // or exposing core data structures in the C++ public headers. + // Thus it is safe to use a static_cast to the subclass here, and the + // C++ style guide allows us to do so in this case + static_cast(alarm_)->Set(deadline, std::move(f)); +} + Alarm::~Alarm() { if (alarm_ != nullptr) { static_cast(alarm_)->Destroy(); diff --git a/src/cpp/ext/filters/census/server_filter.cc b/src/cpp/ext/filters/census/server_filter.cc index c7c62eefe51..b5f3d5a13a7 100644 --- a/src/cpp/ext/filters/census/server_filter.cc +++ b/src/cpp/ext/filters/census/server_filter.cc @@ -93,7 +93,7 @@ void CensusServerCallData::OnDoneRecvInitialMetadataCb(void* user_data, FilterInitialMetadata(initial_metadata, &sml); calld->path_ = grpc_slice_ref_internal(sml.path); calld->method_ = GetMethod(&calld->path_); - calld->qualified_method_ = StrCat("Recv.", calld->method_); + calld->qualified_method_ = absl::StrCat("Recv.", calld->method_); const char* tracing_str = GRPC_SLICE_IS_EMPTY(sml.tracing_slice) ? "" diff --git a/src/cpp/server/server_cc.cc b/src/cpp/server/server_cc.cc index b8ba7042d90..36f7eb81f96 100644 --- a/src/cpp/server/server_cc.cc +++ b/src/cpp/server/server_cc.cc @@ -657,6 +657,7 @@ void Server::PerformOpsOnCall(internal::CallOpSetInterface* ops, size_t nops = 0; grpc_op cops[MAX_OPS]; ops->FillOps(call->call(), cops, &nops); + // TODO(vjpai): Use ops->cq_tag once this case supports callbacks auto result = grpc_call_start_batch(call->call(), cops, nops, ops, nullptr); if (result != GRPC_CALL_OK) { gpr_log(GPR_ERROR, "Fatal: grpc_call_start_batch returned %d", result); diff --git a/src/cpp/server/server_context.cc b/src/cpp/server/server_context.cc index bf0c027cda0..b7254b6bb9c 100644 --- a/src/cpp/server/server_context.cc +++ b/src/cpp/server/server_context.cc @@ -61,6 +61,9 @@ class ServerContext::CompletionOp final : public internal::CallOpSetInterface { tag_ = tag; } + /// TODO(vjpai): Allow override of cq_tag if appropriate for callback API + void* cq_tag() override { return this; } + void Unref(); private: diff --git a/src/objective-c/BoringSSL-GRPC.podspec b/src/objective-c/BoringSSL-GRPC.podspec index 704b35a29e8..04e4d5768f2 100644 --- a/src/objective-c/BoringSSL-GRPC.podspec +++ b/src/objective-c/BoringSSL-GRPC.podspec @@ -38,7 +38,7 @@ Pod::Spec.new do |s| s.name = 'BoringSSL-GRPC' - version = '0.0.1' + version = '0.0.2' s.version = version s.summary = 'BoringSSL is a fork of OpenSSL that is designed to meet Google\'s needs.' # Adapted from the homepage: @@ -81,7 +81,7 @@ Pod::Spec.new do |s| s.ios.deployment_target = '5.0' s.osx.deployment_target = '10.7' - name = 'openssl' + name = 'openssl_grpc' # When creating a dynamic framework, name it openssl.framework instead of BoringSSL.framework. # This lets users write their includes like `#include ` as opposed to `#include @@ -1546,6 +1546,7 @@ Pod::Spec.new do |s| sed -i'.back' '/^#define \\([A-Za-z0-9_]*\\) \\1/d' include/openssl/ssl.h sed -i'.back' 'N;/^#define \\([A-Za-z0-9_]*\\) *\\\\\\n *\\1/d' include/openssl/ssl.h sed -i'.back' 's/#ifndef md5_block_data_order/#ifndef GRPC_SHADOW_md5_block_data_order/g' crypto/fipsmodule/md5/md5.c + find . -type f \\( -path '*.h' -or -path '*.cc' -or -path '*.c' \\) -print0 | xargs -0 -L1 sed -E -i'.grpc_back' 's;#include +#import static void drain_cq(grpc_completion_queue *cq) { grpc_event ev; diff --git a/src/php/tests/unit_tests/ChannelTest.php b/src/php/tests/unit_tests/ChannelTest.php index 49b0e350f72..583c618d32a 100644 --- a/src/php/tests/unit_tests/ChannelTest.php +++ b/src/php/tests/unit_tests/ChannelTest.php @@ -599,10 +599,10 @@ class ChannelTest extends PHPUnit_Framework_TestCase public function testPersistentChannelForceNewOldChannelIdle2() { - $this->channel1 = new Grpc\Channel('localhost:50029', [ + $this->channel1 = new Grpc\Channel('localhost:50032', [ "grpc_target_persist_bound" => 2, ]); - $this->channel2 = new Grpc\Channel('localhost:50029', []); + $this->channel2 = new Grpc\Channel('localhost:50032', []); // try to connect on channel2 $state = $this->channel1->getConnectivityState(true); diff --git a/src/proto/grpc/testing/package_options.proto b/src/proto/grpc/testing/package_options.proto new file mode 100644 index 00000000000..e7ecf8c196a --- /dev/null +++ b/src/proto/grpc/testing/package_options.proto @@ -0,0 +1,28 @@ +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package grpc.testing; + +// For sanity checking package definitions +option ruby_package = "Grpc.Testing.Package.Options"; + +message TestRequest { } + +message TestResponse { } + +service TestService { + rpc GetTest(TestRequest) returns (TestResponse) { } +} diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi index 38fd9e78b2d..63048e8da0d 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi @@ -144,8 +144,14 @@ cdef class SSLChannelCredentials(ChannelCredentials): return grpc_ssl_credentials_create( c_pem_root_certificates, NULL, NULL, NULL) else: - c_pem_key_certificate_pair.private_key = self._private_key - c_pem_key_certificate_pair.certificate_chain = self._certificate_chain + if self._private_key: + c_pem_key_certificate_pair.private_key = self._private_key + else: + c_pem_key_certificate_pair.private_key = NULL + if self._certificate_chain: + c_pem_key_certificate_pair.certificate_chain = self._certificate_chain + else: + c_pem_key_certificate_pair.certificate_chain = NULL return grpc_ssl_credentials_create( c_pem_root_certificates, &c_pem_key_certificate_pair, NULL, NULL) diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index 0f68e823d79..3ad82e9cadc 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -92,7 +92,6 @@ CORE_SOURCE_FILES = [ 'src/core/lib/iomgr/error.cc', 'src/core/lib/iomgr/ev_epoll1_linux.cc', 'src/core/lib/iomgr/ev_epollex_linux.cc', - 'src/core/lib/iomgr/ev_epollsig_linux.cc', 'src/core/lib/iomgr/ev_poll_posix.cc', 'src/core/lib/iomgr/ev_posix.cc', 'src/core/lib/iomgr/ev_windows.cc', @@ -223,6 +222,7 @@ CORE_SOURCE_FILES = [ 'src/core/ext/transport/chttp2/transport/frame_settings.cc', 'src/core/ext/transport/chttp2/transport/frame_window_update.cc', 'src/core/ext/transport/chttp2/transport/hpack_encoder.cc', + 'src/core/ext/transport/chttp2/transport/hpack_mapping.cc', 'src/core/ext/transport/chttp2/transport/hpack_parser.cc', 'src/core/ext/transport/chttp2/transport/hpack_table.cc', 'src/core/ext/transport/chttp2/transport/http2_settings.cc', diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c index b6c07914693..fc641dae801 100644 --- a/src/ruby/ext/grpc/rb_call.c +++ b/src/ruby/ext/grpc/rb_call.c @@ -819,6 +819,7 @@ static VALUE grpc_rb_call_run_batch(VALUE self, VALUE ops_hash) { unsigned write_flag = 0; void* tag = (void*)&st; + grpc_ruby_fork_guard(); if (RTYPEDDATA_DATA(self) == NULL) { rb_raise(grpc_rb_eCallError, "Cannot run batch on closed call"); return Qnil; diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c index 3f0dc530cfe..6d4b2293a24 100644 --- a/src/ruby/ext/grpc/rb_channel.c +++ b/src/ruby/ext/grpc/rb_channel.c @@ -217,6 +217,7 @@ static VALUE grpc_rb_channel_init(int argc, VALUE* argv, VALUE self) { MEMZERO(&args, grpc_channel_args, 1); grpc_ruby_once_init(); + grpc_ruby_fork_guard(); rb_thread_call_without_gvl( wait_until_channel_polling_thread_started_no_gil, &stop_waiting_for_thread_start, @@ -374,6 +375,7 @@ static VALUE grpc_rb_channel_watch_connectivity_state(VALUE self, watch_state_stack stack; void* op_success = 0; + grpc_ruby_fork_guard(); TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper); if (wrapper->bg_wrapped == NULL) { @@ -415,6 +417,7 @@ static VALUE grpc_rb_channel_create_call(VALUE self, VALUE parent, VALUE mask, grpc_slice* host_slice_ptr = NULL; char* tmp_str = NULL; + grpc_ruby_fork_guard(); if (host != Qnil) { host_slice = grpc_slice_from_copied_buffer(RSTRING_PTR(host), RSTRING_LEN(host)); diff --git a/src/ruby/ext/grpc/rb_grpc.c b/src/ruby/ext/grpc/rb_grpc.c index f065a857db8..872aed0cfce 100644 --- a/src/ruby/ext/grpc/rb_grpc.c +++ b/src/ruby/ext/grpc/rb_grpc.c @@ -23,9 +23,13 @@ #include #include +#include #include +#include +#include #include +#include #include #include "rb_call.h" #include "rb_call_credentials.h" @@ -255,7 +259,26 @@ static void Init_grpc_time_consts() { id_tv_nsec = rb_intern("tv_nsec"); } -static void grpc_rb_shutdown(void) { grpc_shutdown(); } +#if GPR_WINDOWS +static void grpc_ruby_set_init_pid(void) {} +static bool grpc_ruby_forked_after_init(void) { return false; } +#else +static pid_t grpc_init_pid; + +static void grpc_ruby_set_init_pid(void) { + GPR_ASSERT(grpc_init_pid == 0); + grpc_init_pid = getpid(); +} + +static bool grpc_ruby_forked_after_init(void) { + GPR_ASSERT(grpc_init_pid != 0); + return grpc_init_pid != getpid(); +} +#endif + +static void grpc_rb_shutdown(void) { + if (!grpc_ruby_forked_after_init()) grpc_shutdown(); +} /* Initialize the GRPC module structs */ @@ -276,10 +299,17 @@ VALUE sym_metadata = Qundef; static gpr_once g_once_init = GPR_ONCE_INIT; static void grpc_ruby_once_init_internal() { + grpc_ruby_set_init_pid(); grpc_init(); atexit(grpc_rb_shutdown); } +void grpc_ruby_fork_guard() { + if (grpc_ruby_forked_after_init()) { + rb_raise(rb_eRuntimeError, "grpc cannot be used before and after forking"); + } +} + static VALUE bg_thread_init_rb_mu = Qundef; static int bg_thread_init_done = 0; diff --git a/src/ruby/ext/grpc/rb_grpc.h b/src/ruby/ext/grpc/rb_grpc.h index 577902319ed..4118435ecf7 100644 --- a/src/ruby/ext/grpc/rb_grpc.h +++ b/src/ruby/ext/grpc/rb_grpc.h @@ -69,4 +69,6 @@ gpr_timespec grpc_rb_time_timeval(VALUE time, int interval); void grpc_ruby_once_init(); +void grpc_ruby_fork_guard(); + #endif /* GRPC_RB_H_ */ diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h index 1375f68bbfb..ed4b6264b08 100644 --- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h +++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h @@ -107,7 +107,7 @@ extern grpc_completion_queue_create_for_next_type grpc_completion_queue_create_f typedef grpc_completion_queue*(*grpc_completion_queue_create_for_pluck_type)(void* reserved); extern grpc_completion_queue_create_for_pluck_type grpc_completion_queue_create_for_pluck_import; #define grpc_completion_queue_create_for_pluck grpc_completion_queue_create_for_pluck_import -typedef grpc_completion_queue*(*grpc_completion_queue_create_for_callback_type)(void* shutdown_callback, void* reserved); +typedef grpc_completion_queue*(*grpc_completion_queue_create_for_callback_type)(grpc_experimental_completion_queue_functor* shutdown_callback, void* reserved); extern grpc_completion_queue_create_for_callback_type grpc_completion_queue_create_for_callback_import; #define grpc_completion_queue_create_for_callback grpc_completion_queue_create_for_callback_import typedef grpc_completion_queue*(*grpc_completion_queue_create_type)(const grpc_completion_queue_factory* factory, const grpc_completion_queue_attributes* attributes, void* reserved); @@ -266,7 +266,7 @@ extern grpc_resource_quota_arg_vtable_type grpc_resource_quota_arg_vtable_import typedef char*(*grpc_channelz_get_top_channels_type)(intptr_t start_channel_id); extern grpc_channelz_get_top_channels_type grpc_channelz_get_top_channels_import; #define grpc_channelz_get_top_channels grpc_channelz_get_top_channels_import -typedef char*(*grpc_channelz_get_servers_type)(intptr_t start_channel_id); +typedef char*(*grpc_channelz_get_servers_type)(intptr_t start_server_id); extern grpc_channelz_get_servers_type grpc_channelz_get_servers_import; #define grpc_channelz_get_servers grpc_channelz_get_servers_import typedef char*(*grpc_channelz_get_channel_type)(intptr_t channel_id); @@ -410,10 +410,10 @@ extern grpc_call_set_credentials_type grpc_call_set_credentials_import; typedef void(*grpc_server_credentials_set_auth_metadata_processor_type)(grpc_server_credentials* creds, grpc_auth_metadata_processor processor); extern grpc_server_credentials_set_auth_metadata_processor_type grpc_server_credentials_set_auth_metadata_processor_import; #define grpc_server_credentials_set_auth_metadata_processor grpc_server_credentials_set_auth_metadata_processor_import -typedef grpc_alts_credentials_options*(*grpc_alts_credentials_client_options_create_type)(); +typedef grpc_alts_credentials_options*(*grpc_alts_credentials_client_options_create_type)(void); extern grpc_alts_credentials_client_options_create_type grpc_alts_credentials_client_options_create_import; #define grpc_alts_credentials_client_options_create grpc_alts_credentials_client_options_create_import -typedef grpc_alts_credentials_options*(*grpc_alts_credentials_server_options_create_type)(); +typedef grpc_alts_credentials_options*(*grpc_alts_credentials_server_options_create_type)(void); extern grpc_alts_credentials_server_options_create_type grpc_alts_credentials_server_options_create_import; #define grpc_alts_credentials_server_options_create grpc_alts_credentials_server_options_create_import typedef void(*grpc_alts_credentials_client_options_add_target_service_account_type)(grpc_alts_credentials_options* options, const char* service_account); diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c index 88e6a0cfd56..2931f344092 100644 --- a/src/ruby/ext/grpc/rb_server.c +++ b/src/ruby/ext/grpc/rb_server.c @@ -243,6 +243,8 @@ static VALUE grpc_rb_server_request_call(VALUE self) { static VALUE grpc_rb_server_start(VALUE self) { grpc_rb_server* s = NULL; TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s); + + grpc_ruby_fork_guard(); if (s->wrapped == NULL) { rb_raise(rb_eRuntimeError, "destroyed!"); } else { diff --git a/src/ruby/lib/grpc/generic/rpc_desc.rb b/src/ruby/lib/grpc/generic/rpc_desc.rb index 5fd1805aabf..efb0e4233d7 100644 --- a/src/ruby/lib/grpc/generic/rpc_desc.rb +++ b/src/ruby/lib/grpc/generic/rpc_desc.rb @@ -32,7 +32,7 @@ module GRPC # @return [Proc] { |instance| marshalled(instance) } def marshal_proc - proc { |o| o.class.method(marshal_method).call(o).to_s } + proc { |o| o.class.send(marshal_method, o).to_s } end # @param [:input, :output] target determines whether to produce the an @@ -42,9 +42,9 @@ module GRPC # @return [Proc] An unmarshal proc { |marshalled(instance)| instance } def unmarshal_proc(target) fail ArgumentError unless [:input, :output].include?(target) - unmarshal_class = method(target).call + unmarshal_class = send(target) unmarshal_class = unmarshal_class.type if unmarshal_class.is_a? Stream - proc { |o| unmarshal_class.method(unmarshal_method).call(o) } + proc { |o| unmarshal_class.send(unmarshal_method, o) } end def handle_request_response(active_call, mth, inter_ctx) diff --git a/src/ruby/lib/grpc/generic/rpc_server.rb b/src/ruby/lib/grpc/generic/rpc_server.rb index 838ac45927b..3b5a0ce27f3 100644 --- a/src/ruby/lib/grpc/generic/rpc_server.rb +++ b/src/ruby/lib/grpc/generic/rpc_server.rb @@ -136,7 +136,7 @@ module GRPC begin blk, args = worker_queue.pop blk.call(*args) - rescue StandardError => e + rescue StandardError, GRPC::Core::CallError => e GRPC.logger.warn('Error in worker thread') GRPC.logger.warn(e) end diff --git a/src/ruby/spec/channel_spec.rb b/src/ruby/spec/channel_spec.rb index 3c9eca47ecc..adba6db99cf 100644 --- a/src/ruby/spec/channel_spec.rb +++ b/src/ruby/spec/channel_spec.rb @@ -13,6 +13,7 @@ # limitations under the License. require 'spec_helper' +require 'English' def load_test_certs test_root = File.join(File.dirname(__FILE__), 'testdata') @@ -27,6 +28,28 @@ describe GRPC::Core::Channel do GRPC::Core::ChannelCredentials.new(load_test_certs[0]) end + def fork_with_propagated_error_message + pipe_read, pipe_write = IO.pipe + pid = fork do + pipe_read.close + begin + yield + rescue => exc + pipe_write.syswrite(exc.message) + end + pipe_write.close + end + pipe_write.close + + exc_message = pipe_read.read + Process.wait(pid) + + unless $CHILD_STATUS.success? + raise "forked process failed with #{$CHILD_STATUS}" + end + raise exc_message unless exc_message.empty? + end + shared_examples '#new' do it 'take a host name without channel args' do blk = proc do @@ -79,6 +102,14 @@ describe GRPC::Core::Channel do blk = construct_with_args(args) expect(&blk).to_not raise_error end + + it 'raises if grpc was initialized in another process' do + blk = construct_with_args({}) + expect(&blk).not_to raise_error + expect do + fork_with_propagated_error_message(&blk) + end.to raise_error(RuntimeError, 'grpc cannot be used before and after forking') + end end describe '#new for secure channels' do @@ -121,6 +152,19 @@ describe GRPC::Core::Channel do end expect(&blk).to raise_error(RuntimeError) end + + it 'raises if grpc was initialized in another process' do + ch = GRPC::Core::Channel.new(fake_host, nil, :this_channel_is_insecure) + + deadline = Time.now + 5 + + blk = proc do + fork_with_propagated_error_message do + ch.create_call(nil, nil, 'dummy_method', nil, deadline) + end + end + expect(&blk).to raise_error(RuntimeError, 'grpc cannot be used before and after forking') + end end describe '#destroy' do diff --git a/src/ruby/spec/client_auth_spec.rb b/src/ruby/spec/client_auth_spec.rb index c563ed32277..5d2e6d24ec8 100644 --- a/src/ruby/spec/client_auth_spec.rb +++ b/src/ruby/spec/client_auth_spec.rb @@ -30,7 +30,7 @@ end def create_server_creds test_root = File.join(File.dirname(__FILE__), 'testdata') - p "test root: #{test_root}" + GRPC.logger.info("test root: #{test_root}") files = ['ca.pem', 'server1.key', 'server1.pem'] creds = files.map { |f| File.open(File.join(test_root, f)).read } GRPC::Core::ServerCredentials.new( @@ -59,7 +59,7 @@ class SslTestService def a_client_streaming_rpc(call) check_peer_cert(call) - call.each_remote_read.each { |r| p r } + call.each_remote_read.each { |r| GRPC.logger.info(r) } EchoMsg.new end @@ -70,7 +70,7 @@ class SslTestService def a_bidi_rpc(requests, call) check_peer_cert(call) - requests.each { |r| p r } + requests.each { |r| GRPC.logger.info(r) } [EchoMsg.new, EchoMsg.new] end end @@ -116,11 +116,11 @@ describe 'client-server auth' do it 'client-server auth with server streaming RPCs' do responses = @stub.a_server_streaming_rpc(EchoMsg.new) - responses.each { |r| p r } + responses.each { |r| GRPC.logger.info(r) } end it 'client-server auth with bidi RPCs' do responses = @stub.a_bidi_rpc([EchoMsg.new, EchoMsg.new]) - responses.each { |r| p r } + responses.each { |r| GRPC.logger.info(r) } end end diff --git a/src/ruby/spec/generic/client_stub_spec.rb b/src/ruby/spec/generic/client_stub_spec.rb index fc50362ca5f..200139d8493 100644 --- a/src/ruby/spec/generic/client_stub_spec.rb +++ b/src/ruby/spec/generic/client_stub_spec.rb @@ -293,7 +293,7 @@ describe 'ClientStub' do # rubocop:disable Metrics/BlockLength describe 'without a call operation' do def get_response(stub, credentials: nil) - puts credentials.inspect + GRPC.logger.info(credentials.inspect) stub.request_response(@method, @sent_msg, noop, noop, metadata: @metadata, credentials: credentials) @@ -342,13 +342,15 @@ describe 'ClientStub' do # rubocop:disable Metrics/BlockLength it 'sends metadata to the server ok when running start_call first' do run_op_view_metadata_test(true) check_op_view_of_finished_client_call( - @op, @server_initial_md, @server_trailing_md) { |r| p r } + @op, @server_initial_md, @server_trailing_md + ) { |r| GRPC.logger.info(r) } end it 'does not crash when used after the call has been finished' do run_op_view_metadata_test(false) check_op_view_of_finished_client_call( - @op, @server_initial_md, @server_trailing_md) { |r| p r } + @op, @server_initial_md, @server_trailing_md + ) { |r| GRPC.logger.info(r) } end end end @@ -435,13 +437,15 @@ describe 'ClientStub' do # rubocop:disable Metrics/BlockLength it 'sends metadata to the server ok when running start_call first' do run_op_view_metadata_test(true) check_op_view_of_finished_client_call( - @op, @server_initial_md, @server_trailing_md) { |r| p r } + @op, @server_initial_md, @server_trailing_md + ) { |r| GRPC.logger.info(r) } end it 'does not crash when used after the call has been finished' do run_op_view_metadata_test(false) check_op_view_of_finished_client_call( - @op, @server_initial_md, @server_trailing_md) { |r| p r } + @op, @server_initial_md, @server_trailing_md + ) { |r| GRPC.logger.info(r) } end end end @@ -578,7 +582,7 @@ describe 'ClientStub' do # rubocop:disable Metrics/BlockLength run_op_view_metadata_test(true) check_op_view_of_finished_client_call( @op, @server_initial_md, @server_trailing_md) do |responses| - responses.each { |r| p r } + responses.each { |r| GRPC.logger.info(r) } end end @@ -586,7 +590,7 @@ describe 'ClientStub' do # rubocop:disable Metrics/BlockLength run_op_view_metadata_test(false) check_op_view_of_finished_client_call( @op, @server_initial_md, @server_trailing_md) do |responses| - responses.each { |r| p r } + responses.each { |r| GRPC.logger.info(r) } end end @@ -895,7 +899,7 @@ describe 'ClientStub' do # rubocop:disable Metrics/BlockLength run_op_view_metadata_test(true) check_op_view_of_finished_client_call( @op, @server_initial_md, @server_trailing_md) do |responses| - responses.each { |r| p r } + responses.each { |r| GRPC.logger.info(r) } end end @@ -903,7 +907,7 @@ describe 'ClientStub' do # rubocop:disable Metrics/BlockLength run_op_view_metadata_test(false) check_op_view_of_finished_client_call( @op, @server_initial_md, @server_trailing_md) do |responses| - responses.each { |r| p r } + responses.each { |r| GRPC.logger.info(r) } end end diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb index e072d0c45f1..44a61340869 100644 --- a/src/ruby/spec/generic/rpc_server_spec.rb +++ b/src/ruby/spec/generic/rpc_server_spec.rb @@ -125,7 +125,7 @@ class CheckCallAfterFinishedService fail 'shouldnt reuse service' unless @server_side_call.nil? @server_side_call = call # iterate through requests so call can complete - call.each_remote_read.each { |r| p r } + call.each_remote_read.each { |r| GRPC.logger.info(r) } EchoMsg.new end @@ -138,7 +138,7 @@ class CheckCallAfterFinishedService def a_bidi_rpc(requests, call) fail 'shouldnt reuse service' unless @server_side_call.nil? @server_side_call = call - requests.each { |r| p r } + requests.each { |r| GRPC.logger.info(r) } [EchoMsg.new, EchoMsg.new] end end @@ -560,7 +560,7 @@ describe GRPC::RpcServer do 'connect_k1' => 'connect_v1' } wanted_md.each do |key, value| - puts "key: #{key}" + GRPC.logger.info("key: #{key}") expect(op.metadata[key]).to eq(value) end @srv.stop diff --git a/src/ruby/spec/pb/codegen/package_option_spec.rb b/src/ruby/spec/pb/codegen/package_option_spec.rb new file mode 100644 index 00000000000..46d23cd6518 --- /dev/null +++ b/src/ruby/spec/pb/codegen/package_option_spec.rb @@ -0,0 +1,53 @@ +# Copyright 2018 gRPC authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'open3' +require 'tmpdir' + +describe 'Code Generation Options' do + it 'should generate and respect package options' do + fail 'CONFIG env variable unexpectedly unset' unless ENV['CONFIG'] + bins_sub_dir = ENV['CONFIG'] + + src_dir = File.join(File.dirname(__FILE__), '..', '..', '..', '..') + pb_dir = File.join(src_dir, 'proto') + bins_dir = File.join(src_dir, '..', 'bins', bins_sub_dir) + + plugin = File.join(bins_dir, 'grpc_ruby_plugin') + protoc = File.join(bins_dir, 'protobuf', 'protoc') + + # Generate the service from the proto + Dir.mktmpdir(nil, File.dirname(__FILE__)) do |tmp_dir| + gen_file = system(protoc, + '-I.', + 'grpc/testing/package_options.proto', + "--grpc_out=#{tmp_dir}", # generate the service + "--ruby_out=#{tmp_dir}", # generate the definitions + "--plugin=protoc-gen-grpc=#{plugin}", + chdir: pb_dir, + out: File::NULL) + + expect(gen_file).to be_truthy + begin + $LOAD_PATH.push(tmp_dir) + expect { Grpc::Testing::Package::Options::TestService::Service }.to raise_error(NameError) + expect(require('grpc/testing/package_options_services_pb')).to be_truthy + expect { Grpc::Testing::Package::Options::TestService::Service }.to_not raise_error + ensure + $LOAD_PATH.delete(tmp_dir) + end + end + end +end diff --git a/src/ruby/spec/support/services.rb b/src/ruby/spec/support/services.rb index 27239cd66c3..6e693f1cdec 100644 --- a/src/ruby/spec/support/services.rb +++ b/src/ruby/spec/support/services.rb @@ -50,7 +50,9 @@ class EchoService def a_client_streaming_rpc(call) # iterate through requests so call can complete call.output_metadata.update(@trailing_metadata) - call.each_remote_read.each { |r| p r } + call.each_remote_read.each do |r| + GRPC.logger.info(r) + end EchoMsg.new end @@ -61,7 +63,9 @@ class EchoService def a_bidi_rpc(requests, call) call.output_metadata.update(@trailing_metadata) - requests.each { |r| p r } + requests.each do |r| + GRPC.logger.info(r) + end [EchoMsg.new, EchoMsg.new] end end @@ -71,35 +75,37 @@ EchoStub = EchoService.rpc_stub_class # For testing server interceptors class TestServerInterceptor < GRPC::ServerInterceptor def request_response(request:, call:, method:) - p "Received request/response call at method #{method}" \ - " with request #{request} for call #{call}" + GRPC.logger.info("Received request/response call at method #{method}" \ + " with request #{request} for call #{call}") call.output_metadata[:interc] = 'from_request_response' - p "[GRPC::Ok] (#{method.owner.name}.#{method.name})" + GRPC.logger.info("[GRPC::Ok] (#{method.owner.name}.#{method.name})") yield end def client_streamer(call:, method:) call.output_metadata[:interc] = 'from_client_streamer' call.each_remote_read.each do |r| - p "In interceptor: #{r}" + GRPC.logger.info("In interceptor: #{r}") end - p "Received client streamer call at method #{method} for call #{call}" + GRPC.logger.info( + "Received client streamer call at method #{method} for call #{call}" + ) yield end def server_streamer(request:, call:, method:) - p "Received server streamer call at method #{method} with request" \ - " #{request} for call #{call}" + GRPC.logger.info("Received server streamer call at method #{method} with request" \ + " #{request} for call #{call}") call.output_metadata[:interc] = 'from_server_streamer' yield end def bidi_streamer(requests:, call:, method:) requests.each do |r| - p "Bidi request: #{r}" + GRPC.logger.info("Bidi request: #{r}") end - p "Received bidi streamer call at method #{method} with requests" \ - " #{requests} for call #{call}" + GRPC.logger.info("Received bidi streamer call at method #{method} with requests" \ + " #{requests} for call #{call}") call.output_metadata[:interc] = 'from_bidi_streamer' yield end @@ -108,38 +114,38 @@ end # For testing client interceptors class TestClientInterceptor < GRPC::ClientInterceptor def request_response(request:, call:, method:, metadata: {}) - p "Intercepted request/response call at method #{method}" \ + GRPC.logger.info("Intercepted request/response call at method #{method}" \ " with request #{request} for call #{call}" \ - " and metadata: #{metadata}" + " and metadata: #{metadata}") metadata['foo'] = 'bar_from_request_response' yield end def client_streamer(requests:, call:, method:, metadata: {}) - p "Received client streamer call at method #{method}" \ + GRPC.logger.info("Received client streamer call at method #{method}" \ " with requests #{requests} for call #{call}" \ - " and metadata: #{metadata}" + " and metadata: #{metadata}") requests.each do |r| - p "In client interceptor: #{r}" + GRPC.logger.info("In client interceptor: #{r}") end metadata['foo'] = 'bar_from_client_streamer' yield end def server_streamer(request:, call:, method:, metadata: {}) - p "Received server streamer call at method #{method}" \ + GRPC.logger.info("Received server streamer call at method #{method}" \ " with request #{request} for call #{call}" \ - " and metadata: #{metadata}" + " and metadata: #{metadata}") metadata['foo'] = 'bar_from_server_streamer' yield end def bidi_streamer(requests:, call:, method:, metadata: {}) - p "Received bidi streamer call at method #{method}" \ + GRPC.logger.info("Received bidi streamer call at method #{method}" \ "with requests #{requests} for call #{call}" \ - " and metadata: #{metadata}" + " and metadata: #{metadata}") requests.each do |r| - p "In client interceptor: #{r}" + GRPC.logger.info("In client interceptor: #{r}") end metadata['foo'] = 'bar_from_bidi_streamer' yield diff --git a/templates/gRPC-Core.podspec.template b/templates/gRPC-Core.podspec.template index f912154301f..98b6344a4bf 100644 --- a/templates/gRPC-Core.podspec.template +++ b/templates/gRPC-Core.podspec.template @@ -174,7 +174,7 @@ ss.header_mappings_dir = '.' ss.libraries = 'z' ss.dependency "#{s.name}/Interface", version - ss.dependency 'BoringSSL-GRPC', '0.0.1' + ss.dependency 'BoringSSL-GRPC', '0.0.2' ss.dependency 'nanopb', '~> 0.3' ss.compiler_flags = '-DGRPC_SHADOW_BORINGSSL_SYMBOLS' @@ -223,5 +223,6 @@ s.prepare_command = <<-END_OF_COMMAND find src/core/ -type f ! -path '*.grpc_back' -print0 | xargs -0 -L1 sed -E -i'.grpc_back' 's;#include "(pb(_.*)?\\.h)";#include ;g' find src/core/ -type f -path '*.grpc_back' -print0 | xargs -0 rm + find src/core/ -type f \\( -path '*.h' -or -path '*.cc' \\) -print0 | xargs -0 -L1 sed -E -i'.grpc_back' 's;#include ` as opposed to `#include @@ -1551,6 +1551,7 @@ sed -i'.back' '/^#define \\([A-Za-z0-9_]*\\) \\1/d' include/openssl/ssl.h sed -i'.back' 'N;/^#define \\([A-Za-z0-9_]*\\) *\\\\\\n *\\1/d' include/openssl/ssl.h sed -i'.back' 's/#ifndef md5_block_data_order/#ifndef GRPC_SHADOW_md5_block_data_order/g' crypto/fipsmodule/md5/md5.c + find . -type f \\( -path '*.h' -or -path '*.cc' -or -path '*.c' \\) -print0 | xargs -0 -L1 sed -E -i'.grpc_back' 's;#include -class CQDeletingCallback : public grpc_core::CQCallbackInterface { +class CQDeletingCallback : public grpc_experimental_completion_queue_functor { public: - explicit CQDeletingCallback(F f) : func_(f) {} - ~CQDeletingCallback() override {} - void Run(bool ok) override { - func_(ok); - grpc_core::Delete(this); + explicit CQDeletingCallback(F f) : func_(f) { + functor_run = &CQDeletingCallback::Run; + } + ~CQDeletingCallback() {} + static void Run(grpc_experimental_completion_queue_functor* cb, int ok) { + auto* callback = static_cast(cb); + callback->func_(static_cast(ok)); + grpc_core::Delete(callback); } private: @@ -51,18 +54,24 @@ class CQDeletingCallback : public grpc_core::CQCallbackInterface { }; template -grpc_core::CQCallbackInterface* NewDeletingCallback(F f) { +grpc_experimental_completion_queue_functor* NewDeletingCallback(F f) { return grpc_core::New>(f); } -class ShutdownCallback : public grpc_core::CQCallbackInterface { +class ShutdownCallback : public grpc_experimental_completion_queue_functor { public: ShutdownCallback() : done_(false) { + functor_run = &ShutdownCallback::StaticRun; gpr_mu_init(&mu_); gpr_cv_init(&cv_); } - ~ShutdownCallback() override {} - void Run(bool ok) override { + ~ShutdownCallback() {} + static void StaticRun(grpc_experimental_completion_queue_functor* cb, + int ok) { + auto* callback = static_cast(cb); + callback->Run(static_cast(ok)); + } + void Run(bool ok) { gpr_log(GPR_DEBUG, "CQ shutdown notification invoked"); gpr_mu_lock(&mu_); done_ = true; @@ -170,7 +179,7 @@ static void verify_tags(gpr_timespec deadline) { // This function creates a callback functor that emits the // desired tag into the global tag set -static grpc_core::CQCallbackInterface* tag(intptr_t t) { +static grpc_experimental_completion_queue_functor* tag(intptr_t t) { auto func = [t](bool ok) { gpr_mu_lock(&tags_mu); gpr_log(GPR_DEBUG, "Completing operation %" PRIdPTR, t); diff --git a/test/core/iomgr/BUILD b/test/core/iomgr/BUILD index 675d9e6278c..70ee83acd23 100644 --- a/test/core/iomgr/BUILD +++ b/test/core/iomgr/BUILD @@ -40,6 +40,7 @@ grpc_cc_library( grpc_cc_test( name = "combiner_test", srcs = ["combiner_test.cc"], + data = ["//third_party/toolchains:RBE_USE_MACHINE_TYPE_LARGE"], language = "C++", deps = [ "//:gpr", @@ -47,7 +48,6 @@ grpc_cc_test( "//test/core/util:gpr_test_util", "//test/core/util:grpc_test_util", ], - data = ["//third_party/toolchains:RBE_USE_MACHINE_TYPE_LARGE"], ) grpc_cc_test( @@ -88,18 +88,6 @@ grpc_cc_test( ], ) -grpc_cc_test( - name = "ev_epollsig_linux_test", - srcs = ["ev_epollsig_linux_test.cc"], - language = "C++", - deps = [ - "//:gpr", - "//:grpc", - "//test/core/util:gpr_test_util", - "//test/core/util:grpc_test_util", - ], -) - grpc_cc_test( name = "fd_conservation_posix_test", srcs = ["fd_conservation_posix_test.cc"], @@ -136,7 +124,6 @@ grpc_cc_test( ], ) - grpc_cc_test( name = "load_file_test", srcs = ["load_file_test.cc"], @@ -150,8 +137,8 @@ grpc_cc_test( ) grpc_cc_test( - name = "pollset_set_test", - srcs = ["pollset_set_test.cc"], + name = "resolve_address_posix_test", + srcs = ["resolve_address_posix_test.cc"], language = "C++", deps = [ "//:gpr", @@ -162,8 +149,11 @@ grpc_cc_test( ) grpc_cc_test( - name = "resolve_address_posix_test", - srcs = ["resolve_address_posix_test.cc"], + name = "resolve_address_using_ares_resolver_test", + srcs = ["resolve_address_test.cc"], + args = [ + "--resolver=ares", + ], language = "C++", deps = [ "//:gpr", @@ -174,8 +164,11 @@ grpc_cc_test( ) grpc_cc_test( - name = "resolve_address_test", + name = "resolve_address_using_native_resolver_test", srcs = ["resolve_address_test.cc"], + args = [ + "--resolver=native", + ], language = "C++", deps = [ "//:gpr", @@ -258,7 +251,6 @@ grpc_cc_test( ], ) - grpc_cc_test( name = "tcp_server_posix_test", srcs = ["tcp_server_posix_test.cc"], diff --git a/test/core/iomgr/ev_epollsig_linux_test.cc b/test/core/iomgr/ev_epollsig_linux_test.cc deleted file mode 100644 index 28c9dd408ce..00000000000 --- a/test/core/iomgr/ev_epollsig_linux_test.cc +++ /dev/null @@ -1,321 +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. - * - */ -#include "src/core/lib/iomgr/port.h" - -/* This test only relevant on linux systems where epoll() is available */ -#ifdef GRPC_LINUX_EPOLL_CREATE1 -#include "src/core/lib/iomgr/ev_epollsig_linux.h" -#include "src/core/lib/iomgr/ev_posix.h" - -#include -#include -#include - -#include -#include -#include - -#include "src/core/lib/gpr/useful.h" -#include "src/core/lib/gprpp/thd.h" -#include "src/core/lib/iomgr/iomgr.h" -#include "test/core/util/test_config.h" - -typedef struct test_pollset { - grpc_pollset* pollset; - gpr_mu* mu; -} test_pollset; - -typedef struct test_fd { - int inner_fd; - grpc_fd* fd; -} test_fd; - -/* num_fds should be an even number */ -static void test_fd_init(test_fd* tfds, int* fds, int num_fds) { - int i; - int r; - - /* Create some dummy file descriptors. Currently using pipe file descriptors - * for this test but we could use any other type of file descriptors. Also, - * since pipe() used in this test creates two fds in each call, num_fds should - * be an even number */ - GPR_ASSERT((num_fds % 2) == 0); - for (i = 0; i < num_fds; i = i + 2) { - r = pipe(fds + i); - if (r != 0) { - gpr_log(GPR_ERROR, "Error in creating pipe. %d (%s)", errno, - strerror(errno)); - return; - } - } - - for (i = 0; i < num_fds; i++) { - tfds[i].inner_fd = fds[i]; - tfds[i].fd = grpc_fd_create(fds[i], "test_fd", false); - } -} - -static void test_fd_cleanup(test_fd* tfds, int num_fds) { - int release_fd; - int i; - - for (i = 0; i < num_fds; i++) { - grpc_fd_shutdown(tfds[i].fd, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("test_fd_cleanup")); - grpc_core::ExecCtx::Get()->Flush(); - - grpc_fd_orphan(tfds[i].fd, nullptr, &release_fd, "test_fd_cleanup"); - grpc_core::ExecCtx::Get()->Flush(); - - GPR_ASSERT(release_fd == tfds[i].inner_fd); - close(tfds[i].inner_fd); - } -} - -static void test_pollset_init(test_pollset* pollsets, int num_pollsets) { - int i; - for (i = 0; i < num_pollsets; i++) { - pollsets[i].pollset = - static_cast(gpr_zalloc(grpc_pollset_size())); - grpc_pollset_init(pollsets[i].pollset, &pollsets[i].mu); - } -} - -static void destroy_pollset(void* p, grpc_error* error) { - grpc_pollset_destroy(static_cast(p)); -} - -static void test_pollset_cleanup(test_pollset* pollsets, int num_pollsets) { - grpc_closure destroyed; - int i; - - for (i = 0; i < num_pollsets; i++) { - GRPC_CLOSURE_INIT(&destroyed, destroy_pollset, pollsets[i].pollset, - grpc_schedule_on_exec_ctx); - grpc_pollset_shutdown(pollsets[i].pollset, &destroyed); - - grpc_core::ExecCtx::Get()->Flush(); - gpr_free(pollsets[i].pollset); - } -} - - /* - * Cases to test: - * case 1) Polling islands of both fd and pollset are NULL - * case 2) Polling island of fd is NULL but that of pollset is not-NULL - * case 3) Polling island of fd is not-NULL but that of pollset is NULL - * case 4) Polling islands of both fd and pollset are not-NULL and: - * case 4.1) Polling islands of fd and pollset are equal - * case 4.2) Polling islands of fd and pollset are NOT-equal (This results - * in a merge) - * */ - -#define NUM_FDS 8 -#define NUM_POLLSETS 4 - -static void test_add_fd_to_pollset() { - grpc_core::ExecCtx exec_ctx; - test_fd tfds[NUM_FDS]; - int fds[NUM_FDS]; - test_pollset pollsets[NUM_POLLSETS]; - void* expected_pi = nullptr; - int i; - - test_fd_init(tfds, fds, NUM_FDS); - test_pollset_init(pollsets, NUM_POLLSETS); - - /*Step 1. - * Create three polling islands (This will exercise test case 1 and 2) with - * the following configuration: - * polling island 0 = { fds:0,1,2, pollsets:0} - * polling island 1 = { fds:3,4, pollsets:1} - * polling island 2 = { fds:5,6,7 pollsets:2} - * - *Step 2. - * Add pollset 3 to polling island 0 (by adding fds 0 and 1 to pollset 3) - * (This will exercise test cases 3 and 4.1). The configuration becomes: - * polling island 0 = { fds:0,1,2, pollsets:0,3} <<< pollset 3 added here - * polling island 1 = { fds:3,4, pollsets:1} - * polling island 2 = { fds:5,6,7 pollsets:2} - * - *Step 3. - * Merge polling islands 0 and 1 by adding fd 0 to pollset 1 (This will - * exercise test case 4.2). The configuration becomes: - * polling island (merged) = {fds: 0,1,2,3,4, pollsets: 0,1,3} - * polling island 2 = {fds: 5,6,7 pollsets: 2} - * - *Step 4. - * Finally do one more merge by adding fd 3 to pollset 2. - * polling island (merged) = {fds: 0,1,2,3,4,5,6,7, pollsets: 0,1,2,3} - */ - - /* == Step 1 == */ - for (i = 0; i <= 2; i++) { - grpc_pollset_add_fd(pollsets[0].pollset, tfds[i].fd); - grpc_core::ExecCtx::Get()->Flush(); - } - - for (i = 3; i <= 4; i++) { - grpc_pollset_add_fd(pollsets[1].pollset, tfds[i].fd); - grpc_core::ExecCtx::Get()->Flush(); - } - - for (i = 5; i <= 7; i++) { - grpc_pollset_add_fd(pollsets[2].pollset, tfds[i].fd); - grpc_core::ExecCtx::Get()->Flush(); - } - - /* == Step 2 == */ - for (i = 0; i <= 1; i++) { - grpc_pollset_add_fd(pollsets[3].pollset, tfds[i].fd); - grpc_core::ExecCtx::Get()->Flush(); - } - - /* == Step 3 == */ - grpc_pollset_add_fd(pollsets[1].pollset, tfds[0].fd); - grpc_core::ExecCtx::Get()->Flush(); - - /* == Step 4 == */ - grpc_pollset_add_fd(pollsets[2].pollset, tfds[3].fd); - grpc_core::ExecCtx::Get()->Flush(); - - /* All polling islands are merged at this point */ - - /* Compare Fd:0's polling island with that of all other Fds */ - expected_pi = grpc_fd_get_polling_island(tfds[0].fd); - for (i = 1; i < NUM_FDS; i++) { - GPR_ASSERT(grpc_are_polling_islands_equal( - expected_pi, grpc_fd_get_polling_island(tfds[i].fd))); - } - - /* Compare Fd:0's polling island with that of all other pollsets */ - for (i = 0; i < NUM_POLLSETS; i++) { - GPR_ASSERT(grpc_are_polling_islands_equal( - expected_pi, grpc_pollset_get_polling_island(pollsets[i].pollset))); - } - - test_fd_cleanup(tfds, NUM_FDS); - test_pollset_cleanup(pollsets, NUM_POLLSETS); -} - -#undef NUM_FDS -#undef NUM_POLLSETS - -typedef struct threading_shared { - gpr_mu* mu; - grpc_pollset* pollset; - grpc_wakeup_fd* wakeup_fd; - grpc_fd* wakeup_desc; - grpc_closure on_wakeup; - int wakeups; -} threading_shared; - -static __thread int thread_wakeups = 0; - -static void test_threading_loop(void* arg) { - threading_shared* shared = static_cast(arg); - while (thread_wakeups < 1000000) { - grpc_core::ExecCtx exec_ctx; - grpc_pollset_worker* worker; - gpr_mu_lock(shared->mu); - GPR_ASSERT(GRPC_LOG_IF_ERROR( - "pollset_work", - grpc_pollset_work(shared->pollset, &worker, GRPC_MILLIS_INF_FUTURE))); - gpr_mu_unlock(shared->mu); - } -} - -static void test_threading_wakeup(void* arg, grpc_error* error) { - threading_shared* shared = static_cast(arg); - ++shared->wakeups; - ++thread_wakeups; - if (error == GRPC_ERROR_NONE) { - GPR_ASSERT(GRPC_LOG_IF_ERROR( - "consume_wakeup", grpc_wakeup_fd_consume_wakeup(shared->wakeup_fd))); - grpc_fd_notify_on_read(shared->wakeup_desc, &shared->on_wakeup); - GPR_ASSERT(GRPC_LOG_IF_ERROR("wakeup_next", - grpc_wakeup_fd_wakeup(shared->wakeup_fd))); - } -} - -static void test_threading(void) { - threading_shared shared; - shared.pollset = static_cast(gpr_zalloc(grpc_pollset_size())); - grpc_pollset_init(shared.pollset, &shared.mu); - - grpc_core::Thread thds[10]; - for (auto& th : thds) { - th = grpc_core::Thread("test_thread", test_threading_loop, &shared); - th.Start(); - } - grpc_wakeup_fd fd; - GPR_ASSERT(GRPC_LOG_IF_ERROR("wakeup_fd_init", grpc_wakeup_fd_init(&fd))); - shared.wakeup_fd = &fd; - shared.wakeup_desc = grpc_fd_create(fd.read_fd, "wakeup", false); - shared.wakeups = 0; - { - grpc_core::ExecCtx exec_ctx; - grpc_pollset_add_fd(shared.pollset, shared.wakeup_desc); - grpc_fd_notify_on_read( - shared.wakeup_desc, - GRPC_CLOSURE_INIT(&shared.on_wakeup, test_threading_wakeup, &shared, - grpc_schedule_on_exec_ctx)); - } - GPR_ASSERT(GRPC_LOG_IF_ERROR("wakeup_first", - grpc_wakeup_fd_wakeup(shared.wakeup_fd))); - for (auto& th : thds) { - th.Join(); - } - fd.read_fd = 0; - grpc_wakeup_fd_destroy(&fd); - { - grpc_core::ExecCtx exec_ctx; - grpc_fd_shutdown(shared.wakeup_desc, GRPC_ERROR_CANCELLED); - grpc_fd_orphan(shared.wakeup_desc, nullptr, nullptr, "done"); - grpc_pollset_shutdown(shared.pollset, - GRPC_CLOSURE_CREATE(destroy_pollset, shared.pollset, - grpc_schedule_on_exec_ctx)); - } - gpr_free(shared.pollset); -} - -int main(int argc, char** argv) { - const char* poll_strategy = nullptr; - grpc_test_init(argc, argv); - grpc_init(); - { - grpc_core::ExecCtx exec_ctx; - - poll_strategy = grpc_get_poll_strategy_name(); - if (poll_strategy != nullptr && strcmp(poll_strategy, "epollsig") == 0) { - test_add_fd_to_pollset(); - test_threading(); - } else { - gpr_log(GPR_INFO, - "Skipping the test. The test is only relevant for 'epollsig' " - "strategy. and the current strategy is: '%s'", - poll_strategy); - } - } - - grpc_shutdown(); - return 0; -} -#else /* defined(GRPC_LINUX_EPOLL_CREATE1) */ -int main(int argc, char** argv) { return 0; } -#endif /* !defined(GRPC_LINUX_EPOLL_CREATE1) */ diff --git a/test/core/iomgr/pollset_set_test.cc b/test/core/iomgr/pollset_set_test.cc deleted file mode 100644 index 1aae1daa026..00000000000 --- a/test/core/iomgr/pollset_set_test.cc +++ /dev/null @@ -1,447 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -#include "src/core/lib/iomgr/port.h" - -/* This test only relevant on linux systems where epoll is available */ -#ifdef GRPC_LINUX_EPOLL_CREATE1 - -#include -#include -#include - -#include -#include -#include - -#include "src/core/lib/gpr/useful.h" -#include "src/core/lib/iomgr/ev_posix.h" -#include "src/core/lib/iomgr/iomgr.h" -#include "test/core/util/test_config.h" - -/******************************************************************************* - * test_pollset_set - */ - -typedef struct test_pollset_set { - grpc_pollset_set* pss; -} test_pollset_set; - -void init_test_pollset_sets(test_pollset_set* pollset_sets, const int num_pss) { - for (int i = 0; i < num_pss; i++) { - pollset_sets[i].pss = grpc_pollset_set_create(); - } -} - -void cleanup_test_pollset_sets(test_pollset_set* pollset_sets, - const int num_pss) { - for (int i = 0; i < num_pss; i++) { - grpc_pollset_set_destroy(pollset_sets[i].pss); - pollset_sets[i].pss = nullptr; - } -} - -/******************************************************************************* - * test_pollset - */ - -typedef struct test_pollset { - grpc_pollset* ps; - gpr_mu* mu; -} test_pollset; - -static void init_test_pollsets(test_pollset* pollsets, const int num_pollsets) { - for (int i = 0; i < num_pollsets; i++) { - pollsets[i].ps = - static_cast(gpr_zalloc(grpc_pollset_size())); - grpc_pollset_init(pollsets[i].ps, &pollsets[i].mu); - } -} - -static void destroy_pollset(void* p, grpc_error* error) { - grpc_pollset_destroy(static_cast(p)); -} - -static void cleanup_test_pollsets(test_pollset* pollsets, - const int num_pollsets) { - grpc_closure destroyed; - for (int i = 0; i < num_pollsets; i++) { - GRPC_CLOSURE_INIT(&destroyed, destroy_pollset, pollsets[i].ps, - grpc_schedule_on_exec_ctx); - grpc_pollset_shutdown(pollsets[i].ps, &destroyed); - - grpc_core::ExecCtx::Get()->Flush(); - gpr_free(pollsets[i].ps); - pollsets[i].ps = nullptr; - } -} - -/******************************************************************************* - * test_fd - */ - -typedef struct test_fd { - grpc_fd* fd; - grpc_wakeup_fd wakeup_fd; - - bool is_on_readable_called; /* Is on_readable closure is called ? */ - grpc_closure on_readable; /* Closure to call when this fd is readable */ -} test_fd; - -void on_readable(void* tfd, grpc_error* error) { - (static_cast(tfd))->is_on_readable_called = true; -} - -static void reset_test_fd(test_fd* tfd) { - tfd->is_on_readable_called = false; - - GRPC_CLOSURE_INIT(&tfd->on_readable, on_readable, tfd, - grpc_schedule_on_exec_ctx); - grpc_fd_notify_on_read(tfd->fd, &tfd->on_readable); -} - -static void init_test_fds(test_fd* tfds, const int num_fds) { - for (int i = 0; i < num_fds; i++) { - GPR_ASSERT(GRPC_ERROR_NONE == grpc_wakeup_fd_init(&tfds[i].wakeup_fd)); - tfds[i].fd = grpc_fd_create(GRPC_WAKEUP_FD_GET_READ_FD(&tfds[i].wakeup_fd), - "test_fd", false); - reset_test_fd(&tfds[i]); - } -} - -static void cleanup_test_fds(test_fd* tfds, const int num_fds) { - int release_fd; - - for (int i = 0; i < num_fds; i++) { - grpc_fd_shutdown(tfds[i].fd, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("fd cleanup")); - grpc_core::ExecCtx::Get()->Flush(); - - /* grpc_fd_orphan frees the memory allocated for grpc_fd. Normally it also - * calls close() on the underlying fd. In our case, we are using - * grpc_wakeup_fd and we would like to destroy it ourselves (by calling - * grpc_wakeup_fd_destroy). To prevent grpc_fd from calling close() on the - * underlying fd, call it with a non-NULL 'release_fd' parameter */ - grpc_fd_orphan(tfds[i].fd, nullptr, &release_fd, "test_fd_cleanup"); - grpc_core::ExecCtx::Get()->Flush(); - - grpc_wakeup_fd_destroy(&tfds[i].wakeup_fd); - } -} - -static void make_test_fds_readable(test_fd* tfds, const int num_fds) { - for (int i = 0; i < num_fds; i++) { - GPR_ASSERT(GRPC_ERROR_NONE == grpc_wakeup_fd_wakeup(&tfds[i].wakeup_fd)); - } -} - -static void verify_readable_and_reset(test_fd* tfds, const int num_fds) { - for (int i = 0; i < num_fds; i++) { - /* Verify that the on_readable callback was called */ - GPR_ASSERT(tfds[i].is_on_readable_called); - - /* Reset the tfd[i] structure */ - GPR_ASSERT(GRPC_ERROR_NONE == - grpc_wakeup_fd_consume_wakeup(&tfds[i].wakeup_fd)); - reset_test_fd(&tfds[i]); - } -} - -/******************************************************************************* - * Main tests - */ - -/* Test some typical scenarios in pollset_set */ -static void pollset_set_test_basic() { - /* We construct the following structure for this test: - * - * +---> FD0 (Added before PSS1, PS1 and PS2 are added to PSS0) - * | - * +---> FD5 (Added after PSS1, PS1 and PS2 are added to PSS0) - * | - * | - * | +---> FD1 (Added before PSS1 is added to PSS0) - * | | - * | +---> FD6 (Added after PSS1 is added to PSS0) - * | | - * +---> PSS1--+ +--> FD2 (Added before PS0 is added to PSS1) - * | | | - * | +---> PS0---+ - * | | - * PSS0---+ +--> FD7 (Added after PS0 is added to PSS1) - * | - * | - * | +---> FD3 (Added before PS1 is added to PSS0) - * | | - * +---> PS1---+ - * | | - * | +---> FD8 (Added after PS1 added to PSS0) - * | - * | - * | +---> FD4 (Added before PS2 is added to PSS0) - * | | - * +---> PS2---+ - * | - * +---> FD9 (Added after PS2 is added to PSS0) - */ - grpc_core::ExecCtx exec_ctx; - grpc_pollset_worker* worker; - grpc_millis deadline; - - test_fd tfds[10]; - test_pollset pollsets[3]; - test_pollset_set pollset_sets[2]; - const int num_fds = GPR_ARRAY_SIZE(tfds); - const int num_ps = GPR_ARRAY_SIZE(pollsets); - const int num_pss = GPR_ARRAY_SIZE(pollset_sets); - - init_test_fds(tfds, num_fds); - init_test_pollsets(pollsets, num_ps); - init_test_pollset_sets(pollset_sets, num_pss); - - /* Construct the pollset_set/pollset/fd tree (see diagram above) */ - - grpc_pollset_set_add_fd(pollset_sets[0].pss, tfds[0].fd); - grpc_pollset_set_add_fd(pollset_sets[1].pss, tfds[1].fd); - - grpc_pollset_add_fd(pollsets[0].ps, tfds[2].fd); - grpc_pollset_add_fd(pollsets[1].ps, tfds[3].fd); - grpc_pollset_add_fd(pollsets[2].ps, tfds[4].fd); - - grpc_pollset_set_add_pollset_set(pollset_sets[0].pss, pollset_sets[1].pss); - - grpc_pollset_set_add_pollset(pollset_sets[1].pss, pollsets[0].ps); - grpc_pollset_set_add_pollset(pollset_sets[0].pss, pollsets[1].ps); - grpc_pollset_set_add_pollset(pollset_sets[0].pss, pollsets[2].ps); - - grpc_pollset_set_add_fd(pollset_sets[0].pss, tfds[5].fd); - grpc_pollset_set_add_fd(pollset_sets[1].pss, tfds[6].fd); - - grpc_pollset_add_fd(pollsets[0].ps, tfds[7].fd); - grpc_pollset_add_fd(pollsets[1].ps, tfds[8].fd); - grpc_pollset_add_fd(pollsets[2].ps, tfds[9].fd); - - grpc_core::ExecCtx::Get()->Flush(); - - /* Test that if any FD in the above structure is readable, it is observable by - * doing grpc_pollset_work on any pollset - * - * For every pollset, do the following: - * - (Ensure that all FDs are in reset state) - * - Make all FDs readable - * - Call grpc_pollset_work() on the pollset - * - Flush the exec_ctx - * - Verify that on_readable call back was called for all FDs (and - * reset the FDs) - * */ - for (int i = 0; i < num_ps; i++) { - make_test_fds_readable(tfds, num_fds); - - gpr_mu_lock(pollsets[i].mu); - deadline = grpc_timespec_to_millis_round_up( - grpc_timeout_milliseconds_to_deadline(2)); - GPR_ASSERT(GRPC_ERROR_NONE == - grpc_pollset_work(pollsets[i].ps, &worker, deadline)); - gpr_mu_unlock(pollsets[i].mu); - - grpc_core::ExecCtx::Get()->Flush(); - - verify_readable_and_reset(tfds, num_fds); - grpc_core::ExecCtx::Get()->Flush(); - } - - /* Test tear down */ - grpc_pollset_set_del_fd(pollset_sets[0].pss, tfds[0].fd); - grpc_pollset_set_del_fd(pollset_sets[0].pss, tfds[5].fd); - grpc_pollset_set_del_fd(pollset_sets[1].pss, tfds[1].fd); - grpc_pollset_set_del_fd(pollset_sets[1].pss, tfds[6].fd); - grpc_core::ExecCtx::Get()->Flush(); - - grpc_pollset_set_del_pollset(pollset_sets[1].pss, pollsets[0].ps); - grpc_pollset_set_del_pollset(pollset_sets[0].pss, pollsets[1].ps); - grpc_pollset_set_del_pollset(pollset_sets[0].pss, pollsets[2].ps); - - grpc_pollset_set_del_pollset_set(pollset_sets[0].pss, pollset_sets[1].pss); - grpc_core::ExecCtx::Get()->Flush(); - - cleanup_test_fds(tfds, num_fds); - cleanup_test_pollsets(pollsets, num_ps); - cleanup_test_pollset_sets(pollset_sets, num_pss); -} - -/* Same FD added multiple times to the pollset_set tree */ -void pollset_set_test_dup_fds() { - /* We construct the following structure for this test: - * - * +---> FD0 - * | - * | - * PSS0---+ - * | +---> FD0 (also under PSS0) - * | | - * +---> PSS1--+ +--> FD1 (also under PSS1) - * | | - * +---> PS ---+ - * | | - * | +--> FD2 - * +---> FD1 - */ - grpc_core::ExecCtx exec_ctx; - grpc_pollset_worker* worker; - grpc_millis deadline; - - test_fd tfds[3]; - test_pollset pollset; - test_pollset_set pollset_sets[2]; - const int num_fds = GPR_ARRAY_SIZE(tfds); - const int num_ps = 1; - const int num_pss = GPR_ARRAY_SIZE(pollset_sets); - - init_test_fds(tfds, num_fds); - init_test_pollsets(&pollset, num_ps); - init_test_pollset_sets(pollset_sets, num_pss); - - /* Construct the structure */ - grpc_pollset_set_add_fd(pollset_sets[0].pss, tfds[0].fd); - grpc_pollset_set_add_fd(pollset_sets[1].pss, tfds[0].fd); - grpc_pollset_set_add_fd(pollset_sets[1].pss, tfds[1].fd); - - grpc_pollset_add_fd(pollset.ps, tfds[1].fd); - grpc_pollset_add_fd(pollset.ps, tfds[2].fd); - - grpc_pollset_set_add_pollset(pollset_sets[1].pss, pollset.ps); - grpc_pollset_set_add_pollset_set(pollset_sets[0].pss, pollset_sets[1].pss); - - /* Test. Make all FDs readable and make sure that can be observed by doing a - * grpc_pollset_work on the pollset 'PS' */ - make_test_fds_readable(tfds, num_fds); - - gpr_mu_lock(pollset.mu); - deadline = grpc_timespec_to_millis_round_up( - grpc_timeout_milliseconds_to_deadline(2)); - GPR_ASSERT(GRPC_ERROR_NONE == - grpc_pollset_work(pollset.ps, &worker, deadline)); - gpr_mu_unlock(pollset.mu); - grpc_core::ExecCtx::Get()->Flush(); - - verify_readable_and_reset(tfds, num_fds); - grpc_core::ExecCtx::Get()->Flush(); - - /* Tear down */ - grpc_pollset_set_del_fd(pollset_sets[0].pss, tfds[0].fd); - grpc_pollset_set_del_fd(pollset_sets[1].pss, tfds[0].fd); - grpc_pollset_set_del_fd(pollset_sets[1].pss, tfds[1].fd); - - grpc_pollset_set_del_pollset(pollset_sets[1].pss, pollset.ps); - grpc_pollset_set_del_pollset_set(pollset_sets[0].pss, pollset_sets[1].pss); - grpc_core::ExecCtx::Get()->Flush(); - - cleanup_test_fds(tfds, num_fds); - cleanup_test_pollsets(&pollset, num_ps); - cleanup_test_pollset_sets(pollset_sets, num_pss); -} - -/* Pollset_set with an empty pollset */ -void pollset_set_test_empty_pollset() { - /* We construct the following structure for this test: - * - * +---> PS0 (EMPTY) - * | - * +---> FD0 - * | - * PSS0---+ - * | +---> FD1 - * | | - * +---> PS1--+ - * | - * +---> FD2 - */ - grpc_core::ExecCtx exec_ctx; - grpc_pollset_worker* worker; - grpc_millis deadline; - - test_fd tfds[3]; - test_pollset pollsets[2]; - test_pollset_set pollset_set; - const int num_fds = GPR_ARRAY_SIZE(tfds); - const int num_ps = GPR_ARRAY_SIZE(pollsets); - const int num_pss = 1; - - init_test_fds(tfds, num_fds); - init_test_pollsets(pollsets, num_ps); - init_test_pollset_sets(&pollset_set, num_pss); - - /* Construct the structure */ - grpc_pollset_set_add_fd(pollset_set.pss, tfds[0].fd); - grpc_pollset_add_fd(pollsets[1].ps, tfds[1].fd); - grpc_pollset_add_fd(pollsets[1].ps, tfds[2].fd); - - grpc_pollset_set_add_pollset(pollset_set.pss, pollsets[0].ps); - grpc_pollset_set_add_pollset(pollset_set.pss, pollsets[1].ps); - - /* Test. Make all FDs readable and make sure that can be observed by doing - * grpc_pollset_work on the empty pollset 'PS0' */ - make_test_fds_readable(tfds, num_fds); - - gpr_mu_lock(pollsets[0].mu); - deadline = grpc_timespec_to_millis_round_up( - grpc_timeout_milliseconds_to_deadline(2)); - GPR_ASSERT(GRPC_ERROR_NONE == - grpc_pollset_work(pollsets[0].ps, &worker, deadline)); - gpr_mu_unlock(pollsets[0].mu); - grpc_core::ExecCtx::Get()->Flush(); - - verify_readable_and_reset(tfds, num_fds); - grpc_core::ExecCtx::Get()->Flush(); - - /* Tear down */ - grpc_pollset_set_del_fd(pollset_set.pss, tfds[0].fd); - grpc_pollset_set_del_pollset(pollset_set.pss, pollsets[0].ps); - grpc_pollset_set_del_pollset(pollset_set.pss, pollsets[1].ps); - grpc_core::ExecCtx::Get()->Flush(); - - cleanup_test_fds(tfds, num_fds); - cleanup_test_pollsets(pollsets, num_ps); - cleanup_test_pollset_sets(&pollset_set, num_pss); -} - -int main(int argc, char** argv) { - grpc_test_init(argc, argv); - grpc_init(); - { - grpc_core::ExecCtx exec_ctx; - const char* poll_strategy = grpc_get_poll_strategy_name(); - - if (poll_strategy != nullptr && - (strcmp(poll_strategy, "epollsig") == 0 || - strcmp(poll_strategy, "epoll-threadpool") == 0)) { - pollset_set_test_basic(); - pollset_set_test_dup_fds(); - pollset_set_test_empty_pollset(); - } else { - gpr_log(GPR_INFO, - "Skipping the test. The test is only relevant for 'epoll' " - "strategy. and the current strategy is: '%s'", - poll_strategy); - } - } - grpc_shutdown(); - return 0; -} -#else /* defined(GRPC_LINUX_EPOLL_CREATE1) */ -int main(int argc, char** argv) { return 0; } -#endif /* !defined(GRPC_LINUX_EPOLL_CREATE1) */ diff --git a/test/core/iomgr/resolve_address_test.cc b/test/core/iomgr/resolve_address_test.cc index 2fb831a6a46..52e4840c7c6 100644 --- a/test/core/iomgr/resolve_address_test.cc +++ b/test/core/iomgr/resolve_address_test.cc @@ -22,8 +22,14 @@ #include #include #include + +#include + +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/string.h" #include "src/core/lib/iomgr/executor.h" #include "src/core/lib/iomgr/iomgr.h" +#include "test/core/util/cmdline.h" #include "test/core/util/test_config.h" static gpr_timespec test_deadline(void) { @@ -240,6 +246,28 @@ static void test_unparseable_hostports(void) { } int main(int argc, char** argv) { + // First set the resolver type based off of --resolver + const char* resolver_type = nullptr; + gpr_cmdline* cl = gpr_cmdline_create("resolve address test"); + gpr_cmdline_add_string(cl, "resolver", "Resolver type (ares or native)", + &resolver_type); + gpr_cmdline_parse(cl, argc, argv); + const char* cur_resolver = gpr_getenv("GRPC_DNS_RESOLVER"); + if (cur_resolver != nullptr && strlen(cur_resolver) != 0) { + gpr_log(GPR_INFO, "Warning: overriding resolver setting of %s", + cur_resolver); + } + if (gpr_stricmp(resolver_type, "native") == 0) { + gpr_setenv("GRPC_DNS_RESOLVER", "native"); + } else if (gpr_stricmp(resolver_type, "ares") == 0) { +#ifndef GRPC_UV + gpr_setenv("GRPC_DNS_RESOLVER", "ares"); +#endif + } else { + gpr_log(GPR_ERROR, "--resolver_type was not set to ares or native"); + abort(); + } + // Run the test. grpc_test_init(argc, argv); grpc_init(); { @@ -250,10 +278,18 @@ int main(int argc, char** argv) { test_missing_default_port(); test_ipv6_with_port(); test_ipv6_without_port(); - test_invalid_ip_addresses(); - test_unparseable_hostports(); + if (gpr_stricmp(resolver_type, "ares") != 0) { + // These tests can trigger DNS queries to the nearby nameserver + // that need to come back in order for the test to succeed. + // c-ares is prone to not using the local system caches that the + // native getaddrinfo implementations take advantage of, so running + // these unit tests under c-ares risks flakiness. + test_invalid_ip_addresses(); + test_unparseable_hostports(); + } grpc_executor_shutdown(); } + gpr_cmdline_destroy(cl); grpc_shutdown(); return 0; diff --git a/test/core/surface/completion_queue_test.cc b/test/core/surface/completion_queue_test.cc index b889fd0fc66..f7ce8a7042e 100644 --- a/test/core/surface/completion_queue_test.cc +++ b/test/core/surface/completion_queue_test.cc @@ -369,11 +369,15 @@ static void test_callback(void) { LOG_TEST("test_callback"); bool got_shutdown = false; - class ShutdownCallback : public grpc_core::CQCallbackInterface { + class ShutdownCallback : public grpc_experimental_completion_queue_functor { public: - ShutdownCallback(bool* done) : done_(done) {} + ShutdownCallback(bool* done) : done_(done) { + functor_run = &ShutdownCallback::Run; + } ~ShutdownCallback() {} - void Run(bool ok) override { *done_ = ok; } + static void Run(grpc_experimental_completion_queue_functor* cb, int ok) { + *static_cast(cb)->done_ = static_cast(ok); + } private: bool* done_; @@ -391,14 +395,17 @@ static void test_callback(void) { grpc_completion_queue_factory_lookup(&attr), &attr, nullptr); int counter = 0; - class TagCallback : public grpc_core::CQCallbackInterface { + class TagCallback : public grpc_experimental_completion_queue_functor { public: - TagCallback(int* counter, int tag) : counter_(counter), tag_(tag) {} + TagCallback(int* counter, int tag) : counter_(counter), tag_(tag) { + functor_run = &TagCallback::Run; + } ~TagCallback() {} - void Run(bool ok) override { - GPR_ASSERT(ok); - *counter_ += tag_; - grpc_core::Delete(this); + static void Run(grpc_experimental_completion_queue_functor* cb, int ok) { + GPR_ASSERT(static_cast(ok)); + auto* callback = static_cast(cb); + *callback->counter_ += callback->tag_; + grpc_core::Delete(callback); }; private: diff --git a/test/core/transport/chttp2/hpack_encoder_test.cc b/test/core/transport/chttp2/hpack_encoder_test.cc index 2a57198ab64..ab819f90923 100644 --- a/test/core/transport/chttp2/hpack_encoder_test.cc +++ b/test/core/transport/chttp2/hpack_encoder_test.cc @@ -202,7 +202,7 @@ static void verify_table_size_change_match_elem_size(const char* key, grpc_mdelem elem = grpc_mdelem_from_slices( grpc_slice_intern(grpc_slice_from_static_string(key)), grpc_slice_intern(grpc_slice_from_static_string(value))); - size_t elem_size = grpc_mdelem_get_size_in_hpack_table(elem, use_true_binary); + size_t elem_size = grpc_chttp2_get_size_in_hpack_table(elem, use_true_binary); size_t initial_table_size = g_compressor.table_size; grpc_linked_mdelem* e = static_cast(gpr_malloc(sizeof(*e))); diff --git a/test/core/transport/metadata_test.cc b/test/core/transport/metadata_test.cc index 4be34f72d9b..8ab9639dfa2 100644 --- a/test/core/transport/metadata_test.cc +++ b/test/core/transport/metadata_test.cc @@ -27,6 +27,7 @@ #include #include "src/core/ext/transport/chttp2/transport/bin_encoder.h" +#include "src/core/ext/transport/chttp2/transport/hpack_table.h" #include "src/core/lib/gpr/string.h" #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/slice/slice_internal.h" @@ -293,7 +294,7 @@ static void verify_ascii_header_size(const char* key, const char* value, grpc_mdelem elem = grpc_mdelem_from_slices( maybe_intern(grpc_slice_from_static_string(key), intern_key), maybe_intern(grpc_slice_from_static_string(value), intern_value)); - size_t elem_size = grpc_mdelem_get_size_in_hpack_table(elem, false); + size_t elem_size = grpc_chttp2_get_size_in_hpack_table(elem, false); size_t expected_size = 32 + strlen(key) + strlen(value); GPR_ASSERT(expected_size == elem_size); GRPC_MDELEM_UNREF(elem); @@ -307,7 +308,7 @@ static void verify_binary_header_size(const char* key, const uint8_t* value, maybe_intern(grpc_slice_from_static_buffer(value, value_len), intern_value)); GPR_ASSERT(grpc_is_binary_header(GRPC_MDKEY(elem))); - size_t elem_size = grpc_mdelem_get_size_in_hpack_table(elem, false); + size_t elem_size = grpc_chttp2_get_size_in_hpack_table(elem, false); grpc_slice value_slice = grpc_slice_from_copied_buffer( reinterpret_cast(value), value_len); grpc_slice base64_encoded = grpc_chttp2_base64_encode(value_slice); diff --git a/test/core/tsi/alts/fake_handshaker/BUILD b/test/core/tsi/alts/fake_handshaker/BUILD index a09a046d27b..98cd628a7d2 100644 --- a/test/core/tsi/alts/fake_handshaker/BUILD +++ b/test/core/tsi/alts/fake_handshaker/BUILD @@ -37,21 +37,22 @@ grpc_cc_library( name = "fake_handshaker_lib", testonly = True, srcs = ["fake_handshaker_server.cc"], + hdrs = ["fake_handshaker_server.h"], language = "C++", deps = [ "handshaker_proto", "transport_security_common_proto", "//:grpc++", - "//test/cpp/util:test_config", ], ) grpc_cc_binary( name = "fake_handshaker_server", testonly = True, - srcs = ["fake_handshaker_server.cc"], + srcs = ["fake_handshaker_server_main.cc"], language = "C++", deps = [ + "//test/cpp/util:test_config", "fake_handshaker_lib", ], ) diff --git a/test/core/tsi/alts/fake_handshaker/fake_handshaker_server.cc b/test/core/tsi/alts/fake_handshaker/fake_handshaker_server.cc index f6a4791b492..ba246b07eb1 100644 --- a/test/core/tsi/alts/fake_handshaker/fake_handshaker_server.cc +++ b/test/core/tsi/alts/fake_handshaker/fake_handshaker_server.cc @@ -15,12 +15,12 @@ * limitations under the License. * */ +#include "test/core/tsi/alts/fake_handshaker/fake_handshaker_server.h" #include #include #include -#include #include #include #include @@ -32,10 +32,6 @@ #include "test/core/tsi/alts/fake_handshaker/handshaker.grpc.pb.h" #include "test/core/tsi/alts/fake_handshaker/handshaker.pb.h" #include "test/core/tsi/alts/fake_handshaker/transport_security_common.pb.h" -#include "test/cpp/util/test_config.h" - -DEFINE_int32(handshaker_port, 55056, - "TCP port on which the fake handshaker server listens to."); // Fake handshake messages. constexpr char kClientInitFrame[] = "ClientInit"; @@ -243,26 +239,9 @@ class FakeHandshakerService : public HandshakerService::Service { } }; -} // namespace gcp -} // namespace grpc - -void RunServer() { - GPR_ASSERT(FLAGS_handshaker_port != 0); - std::ostringstream server_address; - server_address << "[::1]:" << FLAGS_handshaker_port; - grpc::gcp::FakeHandshakerService service; - grpc::ServerBuilder builder; - builder.AddListeningPort(server_address.str(), - grpc::InsecureServerCredentials()); - builder.RegisterService(&service); - std::unique_ptr server(builder.BuildAndStart()); - gpr_log(GPR_INFO, "Fake handshaker server listening on %s", - server_address.str().c_str()); - server->Wait(); +std::unique_ptr CreateFakeHandshakerService() { + return std::unique_ptr{new grpc::gcp::FakeHandshakerService}; } -int main(int argc, char** argv) { - grpc::testing::InitTest(&argc, &argv, true); - RunServer(); - return 0; -} +} // namespace gcp +} // namespace grpc diff --git a/test/core/tsi/alts/fake_handshaker/fake_handshaker_server.h b/test/core/tsi/alts/fake_handshaker/fake_handshaker_server.h new file mode 100644 index 00000000000..eb4bfdffa12 --- /dev/null +++ b/test/core/tsi/alts/fake_handshaker/fake_handshaker_server.h @@ -0,0 +1,29 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include +#include + +#include + +namespace grpc { +namespace gcp { + +std::unique_ptr CreateFakeHandshakerService(); + +} // namespace gcp +} // namespace grpc diff --git a/test/core/tsi/alts/fake_handshaker/fake_handshaker_server_main.cc b/test/core/tsi/alts/fake_handshaker/fake_handshaker_server_main.cc new file mode 100644 index 00000000000..60351533d94 --- /dev/null +++ b/test/core/tsi/alts/fake_handshaker/fake_handshaker_server_main.cc @@ -0,0 +1,53 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include "test/core/tsi/alts/fake_handshaker/fake_handshaker_server.h" + +#include + +#include +#include +#include +#include + +#include "test/cpp/util/test_config.h" + +DEFINE_int32(handshaker_port, 55056, + "TCP port on which the fake handshaker server listens to."); + +static void RunFakeHandshakerServer(const std::string& server_address) { + std::unique_ptr service = + grpc::gcp::CreateFakeHandshakerService(); + grpc::ServerBuilder builder; + builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); + builder.RegisterService(service.get()); + gpr_log(GPR_INFO, "Fake handshaker server listening on %s", + server_address.c_str()); + std::unique_ptr server = builder.BuildAndStart(); + server->Wait(); +} + +int main(int argc, char** argv) { + grpc::testing::InitTest(&argc, &argv, true); + + GPR_ASSERT(FLAGS_handshaker_port != 0); + std::ostringstream server_address; + server_address << "[::1]:" << FLAGS_handshaker_port; + + RunFakeHandshakerServer(server_address.str()); + return 0; +} diff --git a/test/core/util/port_isolated_runtime_environment.cc b/test/core/util/port_isolated_runtime_environment.cc index c1ca185a670..1f678c08e57 100644 --- a/test/core/util/port_isolated_runtime_environment.cc +++ b/test/core/util/port_isolated_runtime_environment.cc @@ -30,8 +30,8 @@ #include "test/core/util/port.h" -#define MIN_PORT 49152 -#define MAX_PORT 65535 +#define MIN_PORT 1025 +#define MAX_PORT 32766 static int get_random_port_offset() { srand(gpr_now(GPR_CLOCK_REALTIME).tv_nsec); diff --git a/test/cpp/codegen/compiler_test_golden b/test/cpp/codegen/compiler_test_golden index 756f6a0224c..93e1e686546 100644 --- a/test/cpp/codegen/compiler_test_golden +++ b/test/cpp/codegen/compiler_test_golden @@ -26,6 +26,7 @@ #include "src/proto/grpc/testing/compiler_test.pb.h" +#include #include #include #include @@ -105,6 +106,23 @@ class ServiceA final { return std::unique_ptr< ::grpc::ClientAsyncReaderWriterInterface< ::grpc::testing::Request, ::grpc::testing::Response>>(PrepareAsyncMethodA4Raw(context, cq)); } // Method A4 trailing comment 1 + class experimental_async_interface { + public: + virtual ~experimental_async_interface() {} + // MethodA1 leading comment 1 + virtual void MethodA1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, std::function) = 0; + // MethodA1 trailing comment 1 + // MethodA2 detached leading comment 1 + // + // Method A2 leading comment 1 + // Method A2 leading comment 2 + // MethodA2 trailing comment 1 + // Method A3 leading comment 1 + // Method A3 trailing comment 1 + // Method A4 leading comment 1 + // Method A4 trailing comment 1 + }; + virtual class experimental_async_interface* experimental_async() { return nullptr; } private: virtual ::grpc::ClientAsyncResponseReaderInterface< ::grpc::testing::Response>* AsyncMethodA1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) = 0; virtual ::grpc::ClientAsyncResponseReaderInterface< ::grpc::testing::Response>* PrepareAsyncMethodA1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) = 0; @@ -155,9 +173,21 @@ class ServiceA final { std::unique_ptr< ::grpc::ClientAsyncReaderWriter< ::grpc::testing::Request, ::grpc::testing::Response>> PrepareAsyncMethodA4(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq) { return std::unique_ptr< ::grpc::ClientAsyncReaderWriter< ::grpc::testing::Request, ::grpc::testing::Response>>(PrepareAsyncMethodA4Raw(context, cq)); } + class experimental_async final : + public StubInterface::experimental_async_interface { + public: + void MethodA1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, std::function) override; + private: + friend class Stub; + explicit experimental_async(Stub* stub): stub_(stub) { } + Stub* stub() { return stub_; } + Stub* stub_; + }; + class experimental_async_interface* experimental_async() override { return &async_stub_; } private: std::shared_ptr< ::grpc::ChannelInterface> channel_; + class experimental_async async_stub_{this}; ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>* AsyncMethodA1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) override; ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>* PrepareAsyncMethodA1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) override; ::grpc::ClientWriter< ::grpc::testing::Request>* MethodA2Raw(::grpc::ClientContext* context, ::grpc::testing::Response* response) override; @@ -488,6 +518,14 @@ class ServiceB final { return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::grpc::testing::Response>>(PrepareAsyncMethodB1Raw(context, request, cq)); } // MethodB1 trailing comment 1 + class experimental_async_interface { + public: + virtual ~experimental_async_interface() {} + // MethodB1 leading comment 1 + virtual void MethodB1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, std::function) = 0; + // MethodB1 trailing comment 1 + }; + virtual class experimental_async_interface* experimental_async() { return nullptr; } private: virtual ::grpc::ClientAsyncResponseReaderInterface< ::grpc::testing::Response>* AsyncMethodB1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) = 0; virtual ::grpc::ClientAsyncResponseReaderInterface< ::grpc::testing::Response>* PrepareAsyncMethodB1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) = 0; @@ -502,9 +540,21 @@ class ServiceB final { std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>> PrepareAsyncMethodB1(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) { return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>>(PrepareAsyncMethodB1Raw(context, request, cq)); } + class experimental_async final : + public StubInterface::experimental_async_interface { + public: + void MethodB1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, std::function) override; + private: + friend class Stub; + explicit experimental_async(Stub* stub): stub_(stub) { } + Stub* stub() { return stub_; } + Stub* stub_; + }; + class experimental_async_interface* experimental_async() override { return &async_stub_; } private: std::shared_ptr< ::grpc::ChannelInterface> channel_; + class experimental_async async_stub_{this}; ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>* AsyncMethodB1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) override; ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>* PrepareAsyncMethodB1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) override; const ::grpc::internal::RpcMethod rpcmethod_MethodB1_; diff --git a/test/cpp/common/alarm_test.cc b/test/cpp/common/alarm_test.cc index 57d958349ec..e909d03658c 100644 --- a/test/cpp/common/alarm_test.cc +++ b/test/cpp/common/alarm_test.cc @@ -16,9 +16,13 @@ * */ +#include +#include +#include +#include + #include #include -#include #include @@ -43,6 +47,66 @@ TEST(AlarmTest, RegularExpiry) { EXPECT_EQ(junk, output_tag); } +struct Completion { + bool completed = false; + std::mutex mu; + std::condition_variable cv; +}; + +TEST(AlarmTest, CallbackRegularExpiry) { + Alarm alarm; + + auto c = std::make_shared(); + alarm.experimental().Set( + std::chrono::system_clock::now() + std::chrono::seconds(1), [c](bool ok) { + EXPECT_TRUE(ok); + std::lock_guard l(c->mu); + c->completed = true; + c->cv.notify_one(); + }); + + std::unique_lock l(c->mu); + EXPECT_TRUE(c->cv.wait_until( + l, std::chrono::system_clock::now() + std::chrono::seconds(10), + [c] { return c->completed; })); +} + +TEST(AlarmTest, CallbackZeroExpiry) { + Alarm alarm; + + auto c = std::make_shared(); + alarm.experimental().Set(grpc_timeout_seconds_to_deadline(0), [c](bool ok) { + EXPECT_TRUE(ok); + std::lock_guard l(c->mu); + c->completed = true; + c->cv.notify_one(); + }); + + std::unique_lock l(c->mu); + EXPECT_TRUE(c->cv.wait_until( + l, std::chrono::system_clock::now() + std::chrono::seconds(10), + [c] { return c->completed; })); +} + +TEST(AlarmTest, CallbackNegativeExpiry) { + Alarm alarm; + + auto c = std::make_shared(); + alarm.experimental().Set( + std::chrono::system_clock::now() + std::chrono::seconds(-1), + [c](bool ok) { + EXPECT_TRUE(ok); + std::lock_guard l(c->mu); + c->completed = true; + c->cv.notify_one(); + }); + + std::unique_lock l(c->mu); + EXPECT_TRUE(c->cv.wait_until( + l, std::chrono::system_clock::now() + std::chrono::seconds(10), + [c] { return c->completed; })); +} + TEST(AlarmTest, MultithreadedRegularExpiry) { CompletionQueue cq; void* junk = reinterpret_cast(1618033); @@ -182,6 +246,26 @@ TEST(AlarmTest, Cancellation) { EXPECT_EQ(junk, output_tag); } +TEST(AlarmTest, CallbackCancellation) { + Alarm alarm; + + auto c = std::make_shared(); + alarm.experimental().Set( + std::chrono::system_clock::now() + std::chrono::seconds(10), + [c](bool ok) { + EXPECT_FALSE(ok); + std::lock_guard l(c->mu); + c->completed = true; + c->cv.notify_one(); + }); + alarm.Cancel(); + + std::unique_lock l(c->mu); + EXPECT_TRUE(c->cv.wait_until( + l, std::chrono::system_clock::now() + std::chrono::seconds(1), + [c] { return c->completed; })); +} + TEST(AlarmTest, SetDestruction) { CompletionQueue cq; void* junk = reinterpret_cast(1618033); @@ -200,6 +284,26 @@ TEST(AlarmTest, SetDestruction) { EXPECT_EQ(junk, output_tag); } +TEST(AlarmTest, CallbackSetDestruction) { + auto c = std::make_shared(); + { + Alarm alarm; + alarm.experimental().Set( + std::chrono::system_clock::now() + std::chrono::seconds(10), + [c](bool ok) { + EXPECT_FALSE(ok); + std::lock_guard l(c->mu); + c->completed = true; + c->cv.notify_one(); + }); + } + + std::unique_lock l(c->mu); + EXPECT_TRUE(c->cv.wait_until( + l, std::chrono::system_clock::now() + std::chrono::seconds(1), + [c] { return c->completed; })); +} + TEST(AlarmTest, UnsetDestruction) { CompletionQueue cq; Alarm alarm; diff --git a/test/cpp/end2end/BUILD b/test/cpp/end2end/BUILD index 75dec56a602..0415efc1ef9 100644 --- a/test/cpp/end2end/BUILD +++ b/test/cpp/end2end/BUILD @@ -98,6 +98,25 @@ grpc_cc_binary( ], ) +grpc_cc_test( + name = "client_callback_end2end_test", + srcs = ["client_callback_end2end_test.cc"], + external_deps = [ + "gtest", + ], + deps = [ + ":test_service_impl", + "//:gpr", + "//:grpc", + "//:grpc++", + "//src/proto/grpc/testing:echo_messages_proto", + "//src/proto/grpc/testing:echo_proto", + "//test/core/util:gpr_test_util", + "//test/core/util:grpc_test_util", + "//test/cpp/util:test_util", + ], +) + grpc_cc_library( name = "end2end_test_lib", testonly = True, diff --git a/test/cpp/end2end/async_end2end_test.cc b/test/cpp/end2end/async_end2end_test.cc index c9246f08068..d97ea071d15 100644 --- a/test/cpp/end2end/async_end2end_test.cc +++ b/test/cpp/end2end/async_end2end_test.cc @@ -178,7 +178,7 @@ class Verifier { EXPECT_EQ(it2->second.ok, ok); } } else { - gpr_log(GPR_ERROR, "Unexpected tag: %p", tag); + gpr_log(GPR_ERROR, "Unexpected tag: %p", got_tag); abort(); } } diff --git a/test/cpp/end2end/client_callback_end2end_test.cc b/test/cpp/end2end/client_callback_end2end_test.cc new file mode 100644 index 00000000000..62a85641c7f --- /dev/null +++ b/test/cpp/end2end/client_callback_end2end_test.cc @@ -0,0 +1,241 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "src/proto/grpc/testing/echo.grpc.pb.h" +#include "test/core/util/test_config.h" +#include "test/cpp/end2end/test_service_impl.h" +#include "test/cpp/util/byte_buffer_proto_helper.h" + +#include + +namespace grpc { +namespace testing { +namespace { + +class ClientCallbackEnd2endTest : public ::testing::Test { + protected: + ClientCallbackEnd2endTest() {} + + void SetUp() override { + ServerBuilder builder; + builder.RegisterService(&service_); + + server_ = builder.BuildAndStart(); + is_server_started_ = true; + } + + void ResetStub() { + ChannelArguments args; + channel_ = server_->InProcessChannel(args); + stub_ = grpc::testing::EchoTestService::NewStub(channel_); + generic_stub_.reset(new GenericStub(channel_)); + } + + void TearDown() override { + if (is_server_started_) { + server_->Shutdown(); + } + } + + void SendRpcs(int num_rpcs, bool with_binary_metadata) { + grpc::string test_string(""); + for (int i = 0; i < num_rpcs; i++) { + EchoRequest request; + EchoResponse response; + ClientContext cli_ctx; + + test_string += "Hello world. "; + request.set_message(test_string); + + if (with_binary_metadata) { + char bytes[8] = {'\0', '\1', '\2', '\3', + '\4', '\5', '\6', static_cast(i)}; + cli_ctx.AddMetadata("custom-bin", grpc::string(bytes, 8)); + } + + cli_ctx.set_compression_algorithm(GRPC_COMPRESS_GZIP); + + std::mutex mu; + std::condition_variable cv; + bool done = false; + stub_->experimental_async()->Echo( + &cli_ctx, &request, &response, + [&request, &response, &done, &mu, &cv](Status s) { + GPR_ASSERT(s.ok()); + + EXPECT_EQ(request.message(), response.message()); + std::lock_guard l(mu); + done = true; + cv.notify_one(); + }); + std::unique_lock l(mu); + while (!done) { + cv.wait(l); + } + } + } + + void SendRpcsGeneric(int num_rpcs, bool maybe_except) { + const grpc::string kMethodName("/grpc.testing.EchoTestService/Echo"); + grpc::string test_string(""); + for (int i = 0; i < num_rpcs; i++) { + EchoRequest request; + std::unique_ptr send_buf; + ByteBuffer recv_buf; + ClientContext cli_ctx; + + test_string += "Hello world. "; + request.set_message(test_string); + send_buf = SerializeToByteBuffer(&request); + + std::mutex mu; + std::condition_variable cv; + bool done = false; + generic_stub_->experimental().UnaryCall( + &cli_ctx, kMethodName, send_buf.get(), &recv_buf, + [&request, &recv_buf, &done, &mu, &cv, maybe_except](Status s) { + GPR_ASSERT(s.ok()); + + EchoResponse response; + EXPECT_TRUE(ParseFromByteBuffer(&recv_buf, &response)); + EXPECT_EQ(request.message(), response.message()); + std::lock_guard l(mu); + done = true; + cv.notify_one(); +#if GRPC_ALLOW_EXCEPTIONS + if (maybe_except) { + throw - 1; + } +#else + GPR_ASSERT(!maybe_except); +#endif + }); + std::unique_lock l(mu); + while (!done) { + cv.wait(l); + } + } + } + + bool is_server_started_; + std::shared_ptr channel_; + std::unique_ptr stub_; + std::unique_ptr generic_stub_; + TestServiceImpl service_; + std::unique_ptr server_; +}; + +TEST_F(ClientCallbackEnd2endTest, SimpleRpc) { + ResetStub(); + SendRpcs(1, false); +} + +TEST_F(ClientCallbackEnd2endTest, SequentialRpcs) { + ResetStub(); + SendRpcs(10, false); +} + +TEST_F(ClientCallbackEnd2endTest, SequentialRpcsWithVariedBinaryMetadataValue) { + ResetStub(); + SendRpcs(10, true); +} + +TEST_F(ClientCallbackEnd2endTest, SequentialGenericRpcs) { + ResetStub(); + SendRpcsGeneric(10, false); +} + +#if GRPC_ALLOW_EXCEPTIONS +TEST_F(ClientCallbackEnd2endTest, ExceptingRpc) { + ResetStub(); + SendRpcsGeneric(10, true); +} +#endif + +TEST_F(ClientCallbackEnd2endTest, MultipleRpcsWithVariedBinaryMetadataValue) { + ResetStub(); + std::vector threads; + threads.reserve(10); + for (int i = 0; i < 10; ++i) { + threads.emplace_back([this] { SendRpcs(10, true); }); + } + for (int i = 0; i < 10; ++i) { + threads[i].join(); + } +} + +TEST_F(ClientCallbackEnd2endTest, MultipleRpcs) { + ResetStub(); + std::vector threads; + threads.reserve(10); + for (int i = 0; i < 10; ++i) { + threads.emplace_back([this] { SendRpcs(10, false); }); + } + for (int i = 0; i < 10; ++i) { + threads[i].join(); + } +} + +TEST_F(ClientCallbackEnd2endTest, CancelRpcBeforeStart) { + ResetStub(); + EchoRequest request; + EchoResponse response; + ClientContext context; + request.set_message("hello"); + context.TryCancel(); + + std::mutex mu; + std::condition_variable cv; + bool done = false; + stub_->experimental_async()->Echo( + &context, &request, &response, [&response, &done, &mu, &cv](Status s) { + EXPECT_EQ("", response.message()); + EXPECT_EQ(grpc::StatusCode::CANCELLED, s.error_code()); + std::lock_guard l(mu); + done = true; + cv.notify_one(); + }); + std::unique_lock l(mu); + while (!done) { + cv.wait(l); + } +} + +} // namespace +} // namespace testing +} // namespace grpc + +int main(int argc, char** argv) { + grpc_test_init(argc, argv); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/cpp/end2end/client_lb_end2end_test.cc b/test/cpp/end2end/client_lb_end2end_test.cc index e5d6132012a..a9d68ab0582 100644 --- a/test/cpp/end2end/client_lb_end2end_test.cc +++ b/test/cpp/end2end/client_lb_end2end_test.cc @@ -119,6 +119,7 @@ class ClientLbEnd2endTest : public ::testing::Test { } void SetUp() override { + grpc_init(); response_generator_ = grpc_core::MakeRefCounted(); } @@ -127,6 +128,7 @@ class ClientLbEnd2endTest : public ::testing::Test { for (size_t i = 0; i < servers_.size(); ++i) { servers_[i]->Shutdown(); } + grpc_shutdown(); } void CreateServers(size_t num_servers, @@ -560,7 +562,23 @@ TEST_F(ClientLbEnd2endTest, PickFirstUpdateSuperset) { EXPECT_EQ("pick_first", channel->GetLoadBalancingPolicyName()); } -TEST_F(ClientLbEnd2endTest, PickFirstManyUpdates) { +class ClientLbEnd2endWithParamTest + : public ClientLbEnd2endTest, + public ::testing::WithParamInterface { + protected: + void SetUp() override { + grpc_subchannel_index_test_only_set_force_creation(GetParam()); + ClientLbEnd2endTest::SetUp(); + } + + void TearDown() override { + ClientLbEnd2endTest::TearDown(); + grpc_subchannel_index_test_only_set_force_creation(false); + } +}; + +TEST_P(ClientLbEnd2endWithParamTest, PickFirstManyUpdates) { + gpr_log(GPR_INFO, "subchannel force creation: %d", GetParam()); // Start servers and send one RPC per server. const int kNumServers = 3; StartServers(kNumServers); @@ -570,20 +588,21 @@ TEST_F(ClientLbEnd2endTest, PickFirstManyUpdates) { for (size_t i = 0; i < servers_.size(); ++i) { ports.emplace_back(servers_[i]->port_); } - for (const bool force_creation : {true, false}) { - grpc_subchannel_index_test_only_set_force_creation(force_creation); - gpr_log(GPR_INFO, "Force subchannel creation: %d", force_creation); - for (size_t i = 0; i < 1000; ++i) { - std::shuffle(ports.begin(), ports.end(), - std::mt19937(std::random_device()())); - SetNextResolution(ports); - if (i % 10 == 0) CheckRpcSendOk(stub, DEBUG_LOCATION); - } + for (size_t i = 0; i < 1000; ++i) { + std::shuffle(ports.begin(), ports.end(), + std::mt19937(std::random_device()())); + SetNextResolution(ports); + // We should re-enter core at the end of the loop to give the resolution + // setting closure a chance to run. + if ((i + 1) % 10 == 0) CheckRpcSendOk(stub, DEBUG_LOCATION); } // Check LB policy name for the channel. EXPECT_EQ("pick_first", channel->GetLoadBalancingPolicyName()); } +INSTANTIATE_TEST_CASE_P(SubchannelForceCreation, ClientLbEnd2endWithParamTest, + ::testing::Bool()); + TEST_F(ClientLbEnd2endTest, PickFirstReresolutionNoSelected) { // Prepare the ports for up servers and down servers. const int kNumServers = 3; @@ -984,8 +1003,6 @@ TEST_F(ClientLbEnd2endTest, RoundRobinSingleReconnect) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); grpc_test_init(argc, argv); - grpc_init(); const auto result = RUN_ALL_TESTS(); - grpc_shutdown(); return result; } diff --git a/test/cpp/interop/client.cc b/test/cpp/interop/client.cc index 7bcf23c0eb6..a4b1a85f856 100644 --- a/test/cpp/interop/client.cc +++ b/test/cpp/interop/client.cc @@ -57,6 +57,7 @@ DEFINE_string( "half_duplex : half-duplex streaming;\n" "jwt_token_creds: large_unary with JWT token auth;\n" "large_unary : single request and (large) response;\n" + "long_lived_channel: sends large_unary rpcs over a long-lived channel;\n" "oauth2_auth_token: raw oauth2 access token auth;\n" "per_rpc_creds: raw oauth2 access token on a single rpc;\n" "ping_pong : full-duplex streaming;\n" @@ -84,10 +85,12 @@ DEFINE_bool(do_not_abort_on_transient_failures, false, "whether abort() is called or not. It does not control whether the " "test is retried in case of transient failures (and currently the " "interop tests are not retried even if this flag is set to true)"); - DEFINE_int32(soak_iterations, 1000, "number of iterations to use for the two soak tests; rpc_soak and " "channel_soak"); +DEFINE_int32(iteration_interval, 10, + "The interval in seconds between rpcs. This is used by " + "long_connection test"); using grpc::testing::CreateChannelForTestCase; using grpc::testing::GetServiceAccountJsonKey; @@ -163,6 +166,9 @@ int main(int argc, char** argv) { FLAGS_soak_iterations); actions["rpc_soak"] = std::bind(&grpc::testing::InteropClient::DoRpcSoakTest, &client, FLAGS_soak_iterations); + actions["long_lived_channel"] = + std::bind(&grpc::testing::InteropClient::DoLongLivedChannelTest, &client, + FLAGS_soak_iterations, FLAGS_iteration_interval); UpdateActions(&actions); diff --git a/test/cpp/interop/interop_client.cc b/test/cpp/interop/interop_client.cc index b7ce90803ba..a99cf8122f8 100644 --- a/test/cpp/interop/interop_client.cc +++ b/test/cpp/interop/interop_client.cc @@ -1052,6 +1052,34 @@ bool InteropClient::DoChannelSoakTest(int32_t soak_iterations) { return true; } +bool InteropClient::DoLongLivedChannelTest(int32_t soak_iterations, + int32_t iteration_interval) { + gpr_log(GPR_DEBUG, "Sending %d RPCs...", soak_iterations); + GPR_ASSERT(soak_iterations > 0); + GPR_ASSERT(iteration_interval > 0); + SimpleRequest request; + SimpleResponse response; + int num_failures = 0; + for (int i = 0; i < soak_iterations; ++i) { + gpr_log(GPR_DEBUG, "Sending RPC number %d...", i); + if (!PerformLargeUnary(&request, &response)) { + gpr_log(GPR_ERROR, "Iteration %d failed.", i); + num_failures++; + } + gpr_sleep_until( + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), + gpr_time_from_seconds(iteration_interval, GPR_TIMESPAN))); + } + if (num_failures == 0) { + gpr_log(GPR_DEBUG, "long_lived_channel test done."); + return true; + } else { + gpr_log(GPR_DEBUG, "long_lived_channel test failed with %d rpc failures.", + num_failures); + return false; + } +} + bool InteropClient::DoUnimplementedService() { gpr_log(GPR_DEBUG, "Sending a request for an unimplemented service..."); diff --git a/test/cpp/interop/interop_client.h b/test/cpp/interop/interop_client.h index e5be44d1d47..0ceff55c5c2 100644 --- a/test/cpp/interop/interop_client.h +++ b/test/cpp/interop/interop_client.h @@ -76,6 +76,8 @@ class InteropClient { // languages bool DoChannelSoakTest(int32_t soak_iterations); bool DoRpcSoakTest(int32_t soak_iterations); + bool DoLongLivedChannelTest(int32_t soak_iterations, + int32_t iteration_interval); // Auth tests. // username is a string containing the user email diff --git a/test/cpp/microbenchmarks/bm_chttp2_hpack.cc b/test/cpp/microbenchmarks/bm_chttp2_hpack.cc index 6fcf048bf38..ba4b57ad228 100644 --- a/test/cpp/microbenchmarks/bm_chttp2_hpack.cc +++ b/test/cpp/microbenchmarks/bm_chttp2_hpack.cc @@ -27,6 +27,7 @@ #include "src/core/ext/transport/chttp2/transport/hpack_encoder.h" #include "src/core/ext/transport/chttp2/transport/hpack_parser.h" +#include "src/core/ext/transport/chttp2/transport/incoming_metadata.h" #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/slice/slice_string_helpers.h" #include "src/core/lib/transport/static_metadata.h" @@ -456,8 +457,9 @@ static void BM_HpackParserParseHeader(benchmark::State& state) { std::vector benchmark_slices = Fixture::GetBenchmarkSlices(); grpc_chttp2_hpack_parser p; grpc_chttp2_hpack_parser_init(&p); + const int kArenaSize = 4096 * 4096; + p.on_header_user_data = gpr_arena_create(kArenaSize); p.on_header = OnHeader; - p.on_header_user_data = nullptr; for (auto slice : init_slices) { GPR_ASSERT(GRPC_ERROR_NONE == grpc_chttp2_hpack_parser_parse(&p, slice)); } @@ -466,7 +468,14 @@ static void BM_HpackParserParseHeader(benchmark::State& state) { GPR_ASSERT(GRPC_ERROR_NONE == grpc_chttp2_hpack_parser_parse(&p, slice)); } grpc_core::ExecCtx::Get()->Flush(); + // Recreate arena every 4k iterations to avoid oom + if (0 == (state.iterations() & 0xfff)) { + gpr_arena_destroy((gpr_arena*)p.on_header_user_data); + p.on_header_user_data = gpr_arena_create(kArenaSize); + } } + // Clean up + gpr_arena_destroy((gpr_arena*)p.on_header_user_data); for (auto slice : init_slices) grpc_slice_unref(slice); for (auto slice : benchmark_slices) grpc_slice_unref(slice); grpc_chttp2_hpack_parser_destroy(&p); @@ -766,8 +775,58 @@ class RepresentativeServerTrailingMetadata { static void free_timeout(void* p) { gpr_free(p); } -// New implementation. -static void OnHeaderNew(void* user_data, grpc_mdelem md) { +// Benchmark the current on_initial_header implementation +static void OnInitialHeader(void* user_data, grpc_mdelem md) { + // Setup for benchmark. This will bloat the absolute values of this benchmark + grpc_chttp2_incoming_metadata_buffer buffer; + grpc_chttp2_incoming_metadata_buffer_init(&buffer, (gpr_arena*)user_data); + bool seen_error = false; + + // Below here is the code we actually care about benchmarking + if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_STATUS) && + !grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) { + seen_error = true; + } + if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_TIMEOUT)) { + grpc_millis* cached_timeout = + static_cast(grpc_mdelem_get_user_data(md, free_timeout)); + grpc_millis timeout; + if (cached_timeout != nullptr) { + timeout = *cached_timeout; + } else { + if (GPR_UNLIKELY( + !grpc_http2_decode_timeout(GRPC_MDVALUE(md), &timeout))) { + char* val = grpc_slice_to_c_string(GRPC_MDVALUE(md)); + gpr_log(GPR_ERROR, "Ignoring bad timeout value '%s'", val); + gpr_free(val); + timeout = GRPC_MILLIS_INF_FUTURE; + } + if (GRPC_MDELEM_IS_INTERNED(md)) { + /* not already parsed: parse it now, and store the + * result away */ + cached_timeout = + static_cast(gpr_malloc(sizeof(grpc_millis))); + *cached_timeout = timeout; + grpc_mdelem_set_user_data(md, free_timeout, cached_timeout); + } + } + benchmark::DoNotOptimize(timeout); + GRPC_MDELEM_UNREF(md); + } else { + const size_t new_size = buffer.size + GRPC_MDELEM_LENGTH(md); + if (!seen_error) { + buffer.size = new_size; + } + grpc_error* error = grpc_chttp2_incoming_metadata_buffer_add(&buffer, md); + if (error != GRPC_ERROR_NONE) { + GPR_ASSERT(0); + } + } + grpc_chttp2_incoming_metadata_buffer_destroy(&buffer); +} + +// Benchmark timeout handling +static void OnHeaderTimeout(void* user_data, grpc_mdelem md) { if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_TIMEOUT)) { grpc_millis* cached_timeout = static_cast(grpc_mdelem_get_user_data(md, free_timeout)); @@ -853,8 +912,13 @@ BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, RepresentativeServerInitialMetadata, UnrefHeader); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, RepresentativeServerTrailingMetadata, UnrefHeader); - -BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, SameDeadline, OnHeaderNew); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, + RepresentativeClientInitialMetadata, OnInitialHeader); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, + MoreRepresentativeClientInitialMetadata, OnInitialHeader); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, + RepresentativeServerInitialMetadata, OnInitialHeader); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, SameDeadline, OnHeaderTimeout); } // namespace hpack_parser_fixtures diff --git a/test/cpp/microbenchmarks/fullstack_fixtures.h b/test/cpp/microbenchmarks/fullstack_fixtures.h index 9d70277fbcd..d390ae08f61 100644 --- a/test/cpp/microbenchmarks/fullstack_fixtures.h +++ b/test/cpp/microbenchmarks/fullstack_fixtures.h @@ -48,6 +48,7 @@ namespace testing { class FixtureConfiguration { public: + virtual ~FixtureConfiguration() {} virtual void ApplyCommonChannelArguments(ChannelArguments* c) const { c->SetInt(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, INT_MAX); c->SetInt(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH, INT_MAX); diff --git a/test/cpp/microbenchmarks/helpers.cc b/test/cpp/microbenchmarks/helpers.cc index e4ba37e2d6a..bce72985dc2 100644 --- a/test/cpp/microbenchmarks/helpers.cc +++ b/test/cpp/microbenchmarks/helpers.cc @@ -38,6 +38,7 @@ void TrackCounters::AddLabel(const grpc::string& label) { } void TrackCounters::AddToLabel(std::ostream& out, benchmark::State& state) { +#ifdef GRPC_COLLECT_STATS grpc_stats_data stats_end; grpc_stats_collect(&stats_end); grpc_stats_data stats; @@ -53,6 +54,7 @@ void TrackCounters::AddToLabel(std::ostream& out, benchmark::State& state) { << " " << grpc_stats_histogram_name[i] << "-99p:" << grpc_stats_histo_percentile(&stats, (grpc_stats_histograms)i, 99.0); } +#endif #ifdef GPR_LOW_LEVEL_COUNTERS grpc_memory_counters counters_at_end = grpc_memory_counters_snapshot(); out << " locks/iter:" diff --git a/test/cpp/microbenchmarks/helpers.h b/test/cpp/microbenchmarks/helpers.h index 4aabd4094a8..25d34b5f871 100644 --- a/test/cpp/microbenchmarks/helpers.h +++ b/test/cpp/microbenchmarks/helpers.h @@ -63,6 +63,7 @@ extern gpr_atm gpr_now_call_count; class TrackCounters { public: TrackCounters() { grpc_stats_collect(&stats_begin_); } + virtual ~TrackCounters() {} virtual void Finish(benchmark::State& state); virtual void AddLabel(const grpc::string& label); virtual void AddToLabel(std::ostream& out, benchmark::State& state); diff --git a/test/cpp/naming/address_sorting_test.cc b/test/cpp/naming/address_sorting_test.cc index 04c300876cc..fc6721d0ba8 100644 --- a/test/cpp/naming/address_sorting_test.cc +++ b/test/cpp/naming/address_sorting_test.cc @@ -216,7 +216,7 @@ TEST_F(AddressSortingTest, TestDepriotizesUnreachableAddresses) { {"1.2.3.4:443", AF_INET}, {"5.6.7.8:443", AF_INET}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "1.2.3.4:443", "5.6.7.8:443", @@ -235,7 +235,7 @@ TEST_F(AddressSortingTest, TestDepriotizesUnsupportedDomainIpv6) { {"[2607:f8b0:400a:801::1002]:443", AF_INET6}, {"1.2.3.4:443", AF_INET}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "1.2.3.4:443", "[2607:f8b0:400a:801::1002]:443", @@ -255,7 +255,7 @@ TEST_F(AddressSortingTest, TestDepriotizesUnsupportedDomainIpv4) { {"[2607:f8b0:400a:801::1002]:443", AF_INET6}, {"1.2.3.4:443", AF_INET}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[2607:f8b0:400a:801::1002]:443", "1.2.3.4:443", @@ -279,7 +279,7 @@ TEST_F(AddressSortingTest, TestDepriotizesNonMatchingScope) { {"[2000:f8b0:400a:801::1002]:443", AF_INET6}, {"[fec0::5000]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[fec0::5000]:443", "[2000:f8b0:400a:801::1002]:443", @@ -302,7 +302,7 @@ TEST_F(AddressSortingTest, TestUsesLabelFromDefaultTable) { {"[2002::5001]:443", AF_INET6}, {"[2001::5001]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[2001::5001]:443", "[2002::5001]:443", @@ -325,7 +325,7 @@ TEST_F(AddressSortingTest, TestUsesLabelFromDefaultTableInputFlipped) { {"[2001::5001]:443", AF_INET6}, {"[2002::5001]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[2001::5001]:443", "[2002::5001]:443", @@ -348,7 +348,7 @@ TEST_F(AddressSortingTest, {"[3ffe::5001]:443", AF_INET6}, {"1.2.3.4:443", AF_INET}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs( lb_addrs, { // The AF_INET address should be IPv4-mapped by the sort, @@ -381,7 +381,7 @@ TEST_F(AddressSortingTest, {v4_compat_dest, AF_INET6}, {"[::1]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[::1]:443", v4_compat_dest, @@ -404,7 +404,7 @@ TEST_F(AddressSortingTest, {"[1234::2]:443", AF_INET6}, {"[::1]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs( lb_addrs, { @@ -428,7 +428,7 @@ TEST_F(AddressSortingTest, {"[2001::1234]:443", AF_INET6}, {"[2000::5001]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs( lb_addrs, { // The 2000::/16 address should match the ::/0 prefix rule @@ -452,7 +452,7 @@ TEST_F( {"[2001::1231]:443", AF_INET6}, {"[2000::5001]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[2000::5001]:443", "[2001::1231]:443", @@ -473,7 +473,7 @@ TEST_F(AddressSortingTest, {"[fec0::1234]:443", AF_INET6}, {"[fc00::5001]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[fc00::5001]:443", "[fec0::1234]:443", @@ -498,7 +498,7 @@ TEST_F( {"[::ffff:1.1.1.2]:443", AF_INET6}, {"[1234::2]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { // ::ffff:0:2 should match the v4-mapped // precedence entry and be deprioritized. @@ -525,7 +525,7 @@ TEST_F(AddressSortingTest, TestPrefersSmallerScope) { {"[3ffe::5001]:443", AF_INET6}, {"[fec0::1234]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[fec0::1234]:443", "[3ffe::5001]:443", @@ -550,7 +550,7 @@ TEST_F(AddressSortingTest, TestPrefersLongestMatchingSrcDstPrefix) { {"[3ffe:5001::]:443", AF_INET6}, {"[3ffe:1234::]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[3ffe:1234::]:443", "[3ffe:5001::]:443", @@ -571,7 +571,7 @@ TEST_F(AddressSortingTest, {"[3ffe::5001]:443", AF_INET6}, {"[3ffe::1234]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[3ffe::1234]:443", "[3ffe::5001]:443", @@ -591,7 +591,7 @@ TEST_F(AddressSortingTest, TestPrefersLongestPrefixStressInnerBytePrefix) { {"[3ffe:8000::]:443", AF_INET6}, {"[3ffe:2000::]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[3ffe:2000::]:443", "[3ffe:8000::]:443", @@ -611,7 +611,7 @@ TEST_F(AddressSortingTest, TestPrefersLongestPrefixDiffersOnHighestBitOfByte) { {"[3ffe:6::]:443", AF_INET6}, {"[3ffe:c::]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[3ffe:c::]:443", "[3ffe:6::]:443", @@ -633,7 +633,7 @@ TEST_F(AddressSortingTest, TestPrefersLongestPrefixDiffersByLastBit) { {"[3ffe:1111:1111:1110::]:443", AF_INET6}, {"[3ffe:1111:1111:1111::]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[3ffe:1111:1111:1111::]:443", "[3ffe:1111:1111:1110::]:443", @@ -655,7 +655,7 @@ TEST_F(AddressSortingTest, TestStableSort) { {"[3ffe::1234]:443", AF_INET6}, {"[3ffe::1235]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[3ffe::1234]:443", "[3ffe::1235]:443", @@ -681,7 +681,7 @@ TEST_F(AddressSortingTest, TestStableSortFiveElements) { {"[3ffe::1234]:443", AF_INET6}, {"[3ffe::1235]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[3ffe::1231]:443", "[3ffe::1232]:443", @@ -702,7 +702,7 @@ TEST_F(AddressSortingTest, TestStableSortNoSrcAddrsExist) { {"[3ffe::1234]:443", AF_INET6}, {"[3ffe::1235]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[3ffe::1231]:443", "[3ffe::1232]:443", @@ -720,7 +720,7 @@ TEST_F(AddressSortingTest, TestStableSortNoSrcAddrsExistWithIpv4) { {"[::ffff:5.6.7.8]:443", AF_INET6}, {"1.2.3.4:443", AF_INET}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[::ffff:5.6.7.8]:443", "1.2.3.4:443", @@ -748,7 +748,7 @@ TEST_F(AddressSortingTest, TestStableSortV4CompatAndSiteLocalAddresses) { {"[fec0::2000]:443", AF_INET6}, {v4_compat_dest, AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { // The sort should be stable since @@ -769,7 +769,7 @@ TEST_F(AddressSortingTest, TestPrefersIpv6Loopback) { {"[::1]:443", AF_INET6}, {"127.0.0.1:443", AF_INET}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[::1]:443", "127.0.0.1:443", @@ -783,7 +783,7 @@ TEST_F(AddressSortingTest, TestPrefersIpv6LoopbackInputsFlipped) { {"127.0.0.1:443", AF_INET}, {"[::1]:443", AF_INET6}, }); - grpc_cares_wrapper_test_only_address_sorting_sort(lb_addrs); + grpc_cares_wrapper_address_sorting_sort(lb_addrs); VerifyLbAddrOutputs(lb_addrs, { "[::1]:443", "127.0.0.1:443", diff --git a/test/cpp/util/cli_credentials.cc b/test/cpp/util/cli_credentials.cc index 0a922617bb6..1125b2d945e 100644 --- a/test/cpp/util/cli_credentials.cc +++ b/test/cpp/util/cli_credentials.cc @@ -19,6 +19,11 @@ #include "test/cpp/util/cli_credentials.h" #include +#include +#include +#include + +#include "src/core/lib/iomgr/load_file.h" DEFINE_bool( enable_ssl, false, @@ -34,6 +39,14 @@ DEFINE_string( ssl_target, "", "If not empty, treat the server host name as this for ssl/tls certificate " "validation."); +DEFINE_string( + ssl_client_cert, "", + "If not empty, load this PEM formated client certificate file. Requires " + "use of --ssl_client_key."); +DEFINE_string( + ssl_client_key, "", + "If not empty, load this PEM formated private key. Requires use of " + "--ssl_client_cert"); DEFINE_string( channel_creds_type, "", "The channel creds type: insecure, ssl, gdc (Google Default Credentials) " @@ -99,7 +112,27 @@ CliCredentials::GetChannelCredentials() const { if (FLAGS_channel_creds_type.compare("insecure") == 0) { return grpc::InsecureChannelCredentials(); } else if (FLAGS_channel_creds_type.compare("ssl") == 0) { - return grpc::SslCredentials(grpc::SslCredentialsOptions()); + grpc::SslCredentialsOptions ssl_creds_options; + // TODO(@Capstan): This won't affect Google Default Credentials using SSL. + if (!FLAGS_ssl_client_cert.empty()) { + grpc_slice cert_slice = grpc_empty_slice(); + GRPC_LOG_IF_ERROR( + "load_file", + grpc_load_file(FLAGS_ssl_client_cert.c_str(), 1, &cert_slice)); + ssl_creds_options.pem_cert_chain = + grpc::StringFromCopiedSlice(cert_slice); + grpc_slice_unref(cert_slice); + } + if (!FLAGS_ssl_client_key.empty()) { + grpc_slice key_slice = grpc_empty_slice(); + GRPC_LOG_IF_ERROR( + "load_file", + grpc_load_file(FLAGS_ssl_client_key.c_str(), 1, &key_slice)); + ssl_creds_options.pem_private_key = + grpc::StringFromCopiedSlice(key_slice); + grpc_slice_unref(key_slice); + } + return grpc::SslCredentials(ssl_creds_options); } else if (FLAGS_channel_creds_type.compare("gdc") == 0) { return grpc::GoogleDefaultCredentials(); } else if (FLAGS_channel_creds_type.compare("alts") == 0) { @@ -178,6 +211,8 @@ const grpc::string CliCredentials::GetCredentialUsage() const { " overrides --use_auth\n" " ; (deprecated)\n" " --ssl_target ; Set server host for ssl validation\n" + " --ssl_client_cert ; Client cert for ssl\n" + " --ssl_client_key ; Client private key for ssl\n" " --channel_creds_type ; Set to insecure, ssl, gdc, or alts\n" " --call_creds ; Set to none, or" " access_token=\n"; diff --git a/test/distrib/csharp/run_distrib_test.sh b/test/distrib/csharp/run_distrib_test.sh index f9371410e7b..61924b91df8 100755 --- a/test/distrib/csharp/run_distrib_test.sh +++ b/test/distrib/csharp/run_distrib_test.sh @@ -21,7 +21,8 @@ unzip -o "$EXTERNAL_GIT_ROOT/input_artifacts/csharp_nugets_windows_dotnetcli.zip ./update_version.sh auto -nuget restore +# Retry "nuget restore" to work around https://github.com/grpc/grpc/issues/16312 +nuget restore || nuget restore || nuget restore xbuild DistribTest.sln diff --git a/tools/codegen/core/gen_static_metadata.py b/tools/codegen/core/gen_static_metadata.py index 25da3fdd5f2..ab2e5a671a3 100755 --- a/tools/codegen/core/gen_static_metadata.py +++ b/tools/codegen/core/gen_static_metadata.py @@ -23,10 +23,14 @@ import subprocess import re import perfection -# configuration: a list of either strings or 2-tuples of strings -# a single string represents a static grpc_mdstr -# a 2-tuple represents a static grpc_mdelem (and appropriate grpc_mdstrs will -# also be created) +# Configuration: a list of either strings or 2-tuples of strings or 3-tuples of +# strings. +# A single string represents a static grpc_mdstr. +# A 2-tuple represents a static grpc_mdelem (and appropriate grpc_mdstrs will +# also be created). +# A 3-tuple represents a static grpc_mdelem (and appropriate grpc_mdstrs will +# also be created), with the last value equivalent to the mdelem's static hpack +# table index as defined by RFC 7541 CONFIG = [ # metadata strings @@ -72,105 +76,104 @@ CONFIG = [ ('grpc-encoding', 'deflate'), ('te', 'trailers'), ('content-type', 'application/grpc'), - (':method', 'POST'), - (':status', '200'), - (':status', '404'), - (':scheme', 'http'), - (':scheme', 'https'), - (':scheme', 'grpc'), - (':authority', ''), - (':method', 'GET'), + (':method', 'POST', 3), + (':status', '200', 8), + (':status', '404', 13), + (':scheme', 'http', 6), + (':scheme', 'https', 7), + (':scheme', 'grpc', 0), + (':authority', '', 1), + (':method', 'GET', 2), (':method', 'PUT'), - (':path', '/'), - (':path', '/index.html'), - (':status', '204'), - (':status', '206'), - (':status', '304'), - (':status', '400'), - (':status', '500'), - ('accept-charset', ''), + (':path', '/', 4), + (':path', '/index.html', 5), + (':status', '204', 9), + (':status', '206', 10), + (':status', '304', 11), + (':status', '400', 12), + (':status', '500', 14), + ('accept-charset', '', 15), ('accept-encoding', ''), - ('accept-encoding', 'gzip, deflate'), - ('accept-language', ''), - ('accept-ranges', ''), - ('accept', ''), - ('access-control-allow-origin', ''), - ('age', ''), - ('allow', ''), - ('authorization', ''), - ('cache-control', ''), - ('content-disposition', ''), + ('accept-encoding', 'gzip, deflate', 16), + ('accept-language', '', 17), + ('accept-ranges', '', 18), + ('accept', '', 19), + ('access-control-allow-origin', '', 20), + ('age', '', 21), + ('allow', '', 22), + ('authorization', '', 23), + ('cache-control', '', 24), + ('content-disposition', '', 25), ('content-encoding', 'identity'), ('content-encoding', 'gzip'), - ('content-encoding', ''), - ('content-language', ''), - ('content-length', ''), - ('content-location', ''), - ('content-range', ''), - ('content-type', ''), - ('cookie', ''), - ('date', ''), - ('etag', ''), - ('expect', ''), - ('expires', ''), - ('from', ''), - ('host', ''), - ('if-match', ''), - ('if-modified-since', ''), - ('if-none-match', ''), - ('if-range', ''), - ('if-unmodified-since', ''), - ('last-modified', ''), + ('content-encoding', '', 26), + ('content-language', '', 27), + ('content-length', '', 28), + ('content-location', '', 29), + ('content-range', '', 30), + ('content-type', '', 31), + ('cookie', '', 32), + ('date', '', 33), + ('etag', '', 34), + ('expect', '', 35), + ('expires', '', 36), + ('from', '', 37), + ('host', '', 38), + ('if-match', '', 39), + ('if-modified-since', '', 40), + ('if-none-match', '', 41), + ('if-range', '', 42), + ('if-unmodified-since', '', 43), + ('last-modified', '', 44), ('lb-token', ''), ('lb-cost-bin', ''), - ('link', ''), - ('location', ''), - ('max-forwards', ''), - ('proxy-authenticate', ''), - ('proxy-authorization', ''), - ('range', ''), - ('referer', ''), - ('refresh', ''), - ('retry-after', ''), - ('server', ''), - ('set-cookie', ''), - ('strict-transport-security', ''), - ('transfer-encoding', ''), - ('user-agent', ''), - ('vary', ''), - ('via', ''), - ('www-authenticate', ''), + ('link', '', 45), + ('location', '', 46), + ('max-forwards', '', 47), + ('proxy-authenticate', '', 48), + ('proxy-authorization', '', 49), + ('range', '', 50), + ('referer', '', 51), + ('refresh', '', 52), + ('retry-after', '', 53), + ('server', '', 54), + ('set-cookie', '', 55), + ('strict-transport-security', '', 56), + ('transfer-encoding', '', 57), + ('user-agent', '', 58), + ('vary', '', 59), + ('via', '', 60), + ('www-authenticate', '', 61), ] -# 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. +# All entries here are ignored when counting non-default initial metadata that +# prevents the chttp2 server from sending a Trailers-Only response. METADATA_BATCH_CALLOUTS = [ - # (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), - ('grpc-previous-rpc-attempts', True), - ('grpc-retry-pushback-ms', True), + # (name) + (':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'), + ('grpc-previous-rpc-attempts'), + ('grpc-retry-pushback-ms'), ] COMPRESSION_ALGORITHMS = [ @@ -252,7 +255,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: @@ -323,6 +326,17 @@ else: os.path.dirname(sys.argv[0]), '../../../test/core/end2end/fuzzers/hpack.dictionary'), 'w') +HPACK_H = open( + os.path.join( + os.path.dirname(sys.argv[0]), + '../../../src/core/ext/transport/chttp2/transport/hpack_mapping.h'), + 'w') +HPACK_C = open( + os.path.join( + os.path.dirname(sys.argv[0]), + '../../../src/core/ext/transport/chttp2/transport/hpack_mapping.cc'), + 'w') + # copy-paste copyright notice from this file with open(sys.argv[0]) as my_source: copyright = [] @@ -337,7 +351,8 @@ with open(sys.argv[0]) as my_source: if line[0] != '#': break copyright.append(line) - put_banner([H, C], [line[2:].rstrip() for line in copyright]) + put_banner([H, C, HPACK_H, HPACK_C], + [line[2:].rstrip() for line in copyright]) hex_bytes = [ord(c) for c in 'abcdefABCDEF0123456789'] @@ -364,16 +379,44 @@ See metadata.h for an explanation of the interface here, and metadata.cc for an explanation of what's going on. """.splitlines()) +put_banner([HPACK_H, HPACK_C], """WARNING: Auto-generated code. + +To make changes to this file, change +tools/codegen/core/gen_static_metadata.py, and then re-run it. + +This file contains the mapping from the index of each metadata element in the +grpc static metadata table to the index of that element in the hpack static +metadata table. If the element is not contained in the static hpack table, then +the returned index is 0. +""".splitlines()) + print >> H, '#ifndef GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H' print >> H, '#define GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H' print >> H +print >> H, '#include ' +print >> H print >> H, '#include "src/core/lib/transport/metadata.h"' print >> H - +print >> C, '#include ' +print >> C print >> C, '#include "src/core/lib/transport/static_metadata.h"' print >> C print >> C, '#include "src/core/lib/slice/slice_internal.h"' print >> C +print >> HPACK_H, ('#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_' + 'MAPPING_H') +print >> HPACK_H, ('#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_' + 'MAPPING_H') +print >> HPACK_H +print >> HPACK_H, '#include ' +print >> HPACK_H +print >> HPACK_H, '#include "src/core/lib/transport/static_metadata.h"' +print >> HPACK_H +print >> HPACK_C, '#include ' +print >> HPACK_C +print >> HPACK_C, ('#include ' + '"src/core/ext/transport/chttp2/transport/hpack_mapping.h"') +print >> HPACK_C str_ofs = 0 id2strofs = {} @@ -388,7 +431,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) @@ -450,10 +493,28 @@ print >> H, ('extern grpc_mdelem_data ' print >> H, ('extern uintptr_t ' 'grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT];') for i, elem in enumerate(all_elems): - print >> H, '/* "%s": "%s" */' % elem + print >> H, '/* "%s": "%s" */' % (elem[0], elem[1]) print >> H, ('#define %s (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[%d], ' 'GRPC_MDELEM_STORAGE_STATIC))') % (mangle(elem).upper(), i) print >> H + +# Print out the chttp2 mapping between static mdelem index and the hpack static +# table index +print >> HPACK_H, ('extern const uint8_t grpc_hpack_static_mdelem_indices[' + 'GRPC_STATIC_MDELEM_COUNT];') +print >> HPACK_H +print >> HPACK_C, ('const uint8_t grpc_hpack_static_mdelem_indices[' + 'GRPC_STATIC_MDELEM_COUNT] = {') +indices = '' +for elem in all_elems: + index = 0 + if len(elem) == 3: + index = elem[2] + indices += '%d,' % index +print >> HPACK_C, ' %s' % indices +print >> HPACK_C, '};' +print >> HPACK_C + print >> C, ('uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] ' '= {') print >> C, ' %s' % ','.join( @@ -546,12 +607,13 @@ print >> C, '}' print >> C print >> C, 'grpc_mdelem_data grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT] = {' -for a, b in all_elems: - print >> C, '{%s,%s},' % (slice_def(str_idx(a)), slice_def(str_idx(b))) +for elem in all_elems: + print >> C, '{%s,%s},' % (slice_def(str_idx(elem[0])), + slice_def(str_idx(elem[1]))) 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;' @@ -559,7 +621,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;' @@ -567,14 +629,6 @@ 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)) @@ -599,5 +653,8 @@ print >> H, '#define GRPC_MDELEM_ACCEPT_STREAM_ENCODING_FOR_ALGORITHMS(algs) (GR print >> H, '#endif /* GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H */' +print >> HPACK_H, ('#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_' + 'MAPPING_H */') + H.close() C.close() diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++ index 9a97ee84f2c..3b7fd1fa8e0 100644 --- a/tools/doxygen/Doxyfile.c++ +++ b/tools/doxygen/Doxyfile.c++ @@ -945,7 +945,9 @@ include/grpcpp/impl/codegen/async_unary_call.h \ include/grpcpp/impl/codegen/byte_buffer.h \ include/grpcpp/impl/codegen/call.h \ include/grpcpp/impl/codegen/call_hook.h \ +include/grpcpp/impl/codegen/callback_common.h \ include/grpcpp/impl/codegen/channel_interface.h \ +include/grpcpp/impl/codegen/client_callback.h \ include/grpcpp/impl/codegen/client_context.h \ include/grpcpp/impl/codegen/client_unary_call.h \ include/grpcpp/impl/codegen/completion_queue.h \ @@ -997,6 +999,7 @@ include/grpcpp/support/async_stream.h \ include/grpcpp/support/async_unary_call.h \ include/grpcpp/support/byte_buffer.h \ include/grpcpp/support/channel_arguments.h \ +include/grpcpp/support/client_callback.h \ include/grpcpp/support/config.h \ include/grpcpp/support/proto_buffer_reader.h \ include/grpcpp/support/proto_buffer_writer.h \ diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index 0cd4cfd6474..c1bcdfd3d09 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -946,7 +946,9 @@ include/grpcpp/impl/codegen/async_unary_call.h \ include/grpcpp/impl/codegen/byte_buffer.h \ include/grpcpp/impl/codegen/call.h \ include/grpcpp/impl/codegen/call_hook.h \ +include/grpcpp/impl/codegen/callback_common.h \ include/grpcpp/impl/codegen/channel_interface.h \ +include/grpcpp/impl/codegen/client_callback.h \ include/grpcpp/impl/codegen/client_context.h \ include/grpcpp/impl/codegen/client_unary_call.h \ include/grpcpp/impl/codegen/completion_queue.h \ @@ -999,6 +1001,7 @@ include/grpcpp/support/async_stream.h \ include/grpcpp/support/async_unary_call.h \ include/grpcpp/support/byte_buffer.h \ include/grpcpp/support/channel_arguments.h \ +include/grpcpp/support/client_callback.h \ include/grpcpp/support/config.h \ include/grpcpp/support/proto_buffer_reader.h \ include/grpcpp/support/proto_buffer_writer.h \ @@ -1077,7 +1080,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_epollex_linux.h \ -src/core/lib/iomgr/ev_epollsig_linux.h \ src/core/lib/iomgr/ev_poll_posix.h \ src/core/lib/iomgr/ev_posix.h \ src/core/lib/iomgr/exec_ctx.h \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index 7cd1dc7bf37..91860567339 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -1013,6 +1013,8 @@ src/core/ext/transport/chttp2/transport/frame_window_update.cc \ src/core/ext/transport/chttp2/transport/frame_window_update.h \ src/core/ext/transport/chttp2/transport/hpack_encoder.cc \ src/core/ext/transport/chttp2/transport/hpack_encoder.h \ +src/core/ext/transport/chttp2/transport/hpack_mapping.cc \ +src/core/ext/transport/chttp2/transport/hpack_mapping.h \ src/core/ext/transport/chttp2/transport/hpack_parser.cc \ src/core/ext/transport/chttp2/transport/hpack_parser.h \ src/core/ext/transport/chttp2/transport/hpack_table.cc \ @@ -1179,8 +1181,6 @@ src/core/lib/iomgr/ev_epoll1_linux.cc \ src/core/lib/iomgr/ev_epoll1_linux.h \ src/core/lib/iomgr/ev_epollex_linux.cc \ src/core/lib/iomgr/ev_epollex_linux.h \ -src/core/lib/iomgr/ev_epollsig_linux.cc \ -src/core/lib/iomgr/ev_epollsig_linux.h \ src/core/lib/iomgr/ev_poll_posix.cc \ src/core/lib/iomgr/ev_poll_posix.h \ src/core/lib/iomgr/ev_posix.cc \ diff --git a/tools/interop_matrix/client_matrix.py b/tools/interop_matrix/client_matrix.py index c55789f27ee..15b53d1716b 100644 --- a/tools/interop_matrix/client_matrix.py +++ b/tools/interop_matrix/client_matrix.py @@ -99,6 +99,9 @@ LANG_RELEASE_MATRIX = { { 'v1.14.1': None }, + { + 'v1.15.0': None + }, ], 'go': [ { @@ -231,6 +234,9 @@ LANG_RELEASE_MATRIX = { { 'v1.14.1': None }, + { + 'v1.15.0': None + }, ], 'node': [ { @@ -319,6 +325,9 @@ LANG_RELEASE_MATRIX = { { 'v1.14.1': None }, + { + 'v1.15.0': None + }, ], 'php': [ { @@ -363,6 +372,9 @@ LANG_RELEASE_MATRIX = { { 'v1.14.1': None }, + { + 'v1.15.0': None + }, ], 'csharp': [ { @@ -412,6 +424,9 @@ LANG_RELEASE_MATRIX = { { 'v1.14.1': None }, + { + 'v1.15.0': None + }, ], } diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index 8ea5126fdef..29b72dca43f 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -483,23 +483,6 @@ "third_party": false, "type": "target" }, - { - "deps": [ - "gpr", - "gpr_test_util", - "grpc", - "grpc_test_util" - ], - "headers": [], - "is_filegroup": false, - "language": "c", - "name": "ev_epollsig_linux_test", - "src": [ - "test/core/iomgr/ev_epollsig_linux_test.cc" - ], - "third_party": false, - "type": "target" - }, { "deps": [ "gpr", @@ -1863,9 +1846,9 @@ "headers": [], "is_filegroup": false, "language": "c", - "name": "pollset_set_test", + "name": "resolve_address_posix_test", "src": [ - "test/core/iomgr/pollset_set_test.cc" + "test/core/iomgr/resolve_address_posix_test.cc" ], "third_party": false, "type": "target" @@ -1880,9 +1863,9 @@ "headers": [], "is_filegroup": false, "language": "c", - "name": "resolve_address_posix_test", + "name": "resolve_address_using_ares_resolver_test", "src": [ - "test/core/iomgr/resolve_address_posix_test.cc" + "test/core/iomgr/resolve_address_test.cc" ], "third_party": false, "type": "target" @@ -1897,7 +1880,7 @@ "headers": [], "is_filegroup": false, "language": "c", - "name": "resolve_address_test", + "name": "resolve_address_using_native_resolver_test", "src": [ "test/core/iomgr/resolve_address_test.cc" ], @@ -3322,6 +3305,25 @@ "third_party": false, "type": "target" }, + { + "deps": [ + "gpr", + "gpr_test_util", + "grpc", + "grpc++", + "grpc++_test_util", + "grpc_test_util" + ], + "headers": [], + "is_filegroup": false, + "language": "c++", + "name": "client_callback_end2end_test", + "src": [ + "test/cpp/end2end/client_callback_end2end_test.cc" + ], + "third_party": false, + "type": "target" + }, { "deps": [ "gpr", @@ -9515,7 +9517,6 @@ "src/core/lib/iomgr/error.cc", "src/core/lib/iomgr/ev_epoll1_linux.cc", "src/core/lib/iomgr/ev_epollex_linux.cc", - "src/core/lib/iomgr/ev_epollsig_linux.cc", "src/core/lib/iomgr/ev_poll_posix.cc", "src/core/lib/iomgr/ev_posix.cc", "src/core/lib/iomgr/ev_windows.cc", @@ -9696,7 +9697,6 @@ "src/core/lib/iomgr/error_internal.h", "src/core/lib/iomgr/ev_epoll1_linux.h", "src/core/lib/iomgr/ev_epollex_linux.h", - "src/core/lib/iomgr/ev_epollsig_linux.h", "src/core/lib/iomgr/ev_poll_posix.h", "src/core/lib/iomgr/ev_posix.h", "src/core/lib/iomgr/exec_ctx.h", @@ -9848,7 +9848,6 @@ "src/core/lib/iomgr/error_internal.h", "src/core/lib/iomgr/ev_epoll1_linux.h", "src/core/lib/iomgr/ev_epollex_linux.h", - "src/core/lib/iomgr/ev_epollsig_linux.h", "src/core/lib/iomgr/ev_poll_posix.h", "src/core/lib/iomgr/ev_posix.h", "src/core/lib/iomgr/exec_ctx.h", @@ -10661,6 +10660,7 @@ "src/core/ext/transport/chttp2/transport/frame_settings.h", "src/core/ext/transport/chttp2/transport/frame_window_update.h", "src/core/ext/transport/chttp2/transport/hpack_encoder.h", + "src/core/ext/transport/chttp2/transport/hpack_mapping.h", "src/core/ext/transport/chttp2/transport/hpack_parser.h", "src/core/ext/transport/chttp2/transport/hpack_table.h", "src/core/ext/transport/chttp2/transport/http2_settings.h", @@ -10698,6 +10698,8 @@ "src/core/ext/transport/chttp2/transport/frame_window_update.h", "src/core/ext/transport/chttp2/transport/hpack_encoder.cc", "src/core/ext/transport/chttp2/transport/hpack_encoder.h", + "src/core/ext/transport/chttp2/transport/hpack_mapping.cc", + "src/core/ext/transport/chttp2/transport/hpack_mapping.h", "src/core/ext/transport/chttp2/transport/hpack_parser.cc", "src/core/ext/transport/chttp2/transport/hpack_parser.h", "src/core/ext/transport/chttp2/transport/hpack_table.cc", @@ -11081,7 +11083,9 @@ "include/grpcpp/impl/codegen/byte_buffer.h", "include/grpcpp/impl/codegen/call.h", "include/grpcpp/impl/codegen/call_hook.h", + "include/grpcpp/impl/codegen/callback_common.h", "include/grpcpp/impl/codegen/channel_interface.h", + "include/grpcpp/impl/codegen/client_callback.h", "include/grpcpp/impl/codegen/client_context.h", "include/grpcpp/impl/codegen/client_unary_call.h", "include/grpcpp/impl/codegen/completion_queue.h", @@ -11147,7 +11151,9 @@ "include/grpcpp/impl/codegen/byte_buffer.h", "include/grpcpp/impl/codegen/call.h", "include/grpcpp/impl/codegen/call_hook.h", + "include/grpcpp/impl/codegen/callback_common.h", "include/grpcpp/impl/codegen/channel_interface.h", + "include/grpcpp/impl/codegen/client_callback.h", "include/grpcpp/impl/codegen/client_context.h", "include/grpcpp/impl/codegen/client_unary_call.h", "include/grpcpp/impl/codegen/completion_queue.h", @@ -11305,6 +11311,7 @@ "include/grpcpp/support/async_unary_call.h", "include/grpcpp/support/byte_buffer.h", "include/grpcpp/support/channel_arguments.h", + "include/grpcpp/support/client_callback.h", "include/grpcpp/support/config.h", "include/grpcpp/support/proto_buffer_reader.h", "include/grpcpp/support/proto_buffer_writer.h", @@ -11409,6 +11416,7 @@ "include/grpcpp/support/async_unary_call.h", "include/grpcpp/support/byte_buffer.h", "include/grpcpp/support/channel_arguments.h", + "include/grpcpp/support/client_callback.h", "include/grpcpp/support/config.h", "include/grpcpp/support/proto_buffer_reader.h", "include/grpcpp/support/proto_buffer_writer.h", diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index fba76d69d1e..4fdd26efa48 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -601,26 +601,6 @@ ], "uses_polling": true }, - { - "args": [], - "benchmark": false, - "ci_platforms": [ - "linux" - ], - "cpu_cost": 3, - "exclude_configs": [], - "exclude_iomgrs": [ - "uv" - ], - "flaky": false, - "gtest": false, - "language": "c", - "name": "ev_epollsig_linux_test", - "platforms": [ - "linux" - ], - "uses_polling": true - }, { "args": [], "benchmark": false, @@ -2105,7 +2085,9 @@ "args": [], "benchmark": false, "ci_platforms": [ - "linux" + "linux", + "mac", + "posix" ], "cpu_cost": 1.0, "exclude_configs": [], @@ -2115,38 +2097,44 @@ "flaky": false, "gtest": false, "language": "c", - "name": "pollset_set_test", + "name": "resolve_address_posix_test", "platforms": [ - "linux" + "linux", + "mac", + "posix" ], "uses_polling": true }, { - "args": [], + "args": [ + "--resolver=ares" + ], "benchmark": false, "ci_platforms": [ "linux", "mac", - "posix" + "posix", + "windows" ], "cpu_cost": 1.0, "exclude_configs": [], - "exclude_iomgrs": [ - "uv" - ], + "exclude_iomgrs": [], "flaky": false, "gtest": false, "language": "c", - "name": "resolve_address_posix_test", + "name": "resolve_address_using_ares_resolver_test", "platforms": [ "linux", "mac", - "posix" + "posix", + "windows" ], "uses_polling": true }, { - "args": [], + "args": [ + "--resolver=native" + ], "benchmark": false, "ci_platforms": [ "linux", @@ -2160,7 +2148,7 @@ "flaky": false, "gtest": false, "language": "c", - "name": "resolve_address_test", + "name": "resolve_address_using_native_resolver_test", "platforms": [ "linux", "mac", @@ -3969,6 +3957,30 @@ ], "uses_polling": true }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 0.5, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", + "name": "client_callback_end2end_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, { "args": [], "benchmark": false, diff --git a/tools/run_tests/python_utils/upload_rbe_results.py b/tools/run_tests/python_utils/upload_rbe_results.py index d29ebc6219a..74f329048fc 100644 --- a/tools/run_tests/python_utils/upload_rbe_results.py +++ b/tools/run_tests/python_utils/upload_rbe_results.py @@ -40,13 +40,14 @@ _RESULTS_SCHEMA = [ ('test_case', 'STRING', 'Name of test case'), ('result', 'STRING', 'Test or build result'), ('timestamp', 'TIMESTAMP', 'Timestamp of test run'), + ('duration', 'FLOAT', 'Duration of the test run'), ] _TABLE_ID = 'rbe_test_results' def _get_api_key(): """Returns string with API key to access ResultStore. - Intended to be used in Kokoro envrionment.""" + Intended to be used in Kokoro environment.""" api_key_directory = os.getenv('KOKORO_GFILE_DIR') api_key_file = os.path.join(api_key_directory, 'resultstore_api_key') assert os.path.isfile(api_key_file), 'Must add --api_key arg if not on ' \ @@ -57,7 +58,7 @@ def _get_api_key(): def _get_invocation_id(): """Returns String of Bazel invocation ID. Intended to be used in - Kokoro envirionment.""" + Kokoro environment.""" bazel_id_directory = os.getenv('KOKORO_ARTIFACTS_DIR') bazel_id_file = os.path.join(bazel_id_directory, 'bazel_invocation_ids') assert os.path.isfile(bazel_id_file), 'bazel_invocation_ids file, written ' \ @@ -66,6 +67,16 @@ def _get_invocation_id(): return f.read().replace('\n', '') +def _parse_test_duration(duration_str): + """Parse test duration string in '123.567s' format""" + try: + if duration_str.endswith('s'): + duration_str = duration_str[:-1] + return float(duration_str) + except: + return None + + def _upload_results_to_bq(rows): """Upload test results to a BQ table. @@ -205,6 +216,8 @@ if __name__ == "__main__": result, 'timestamp': action['timing']['startTime'], + 'duration': + _parse_test_duration(action['timing']['duration']), } }) except Exception as e: diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index ecb5e1d899e..3d73f9ec0eb 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -61,7 +61,7 @@ _FORCE_ENVIRON_FOR_WRAPPERS = { } _POLLING_STRATEGIES = { - 'linux': ['epollex', 'epollsig', 'epoll1', 'poll', 'poll-cv'], + 'linux': ['epollex', 'epoll1', 'poll', 'poll-cv'], 'mac': ['poll'], } @@ -1430,7 +1430,7 @@ argp.add_argument( default=None, type=str, help='Only use the specified comma-delimited list of polling engines. ' - 'Example: --force_use_pollers epollsig,poll ' + 'Example: --force_use_pollers epoll1,poll ' ' (This flag has no effect if --force_default_poller flag is also used)') argp.add_argument( '--max_time', default=-1, type=int, help='Maximum test runtime in seconds') diff --git a/tools/run_tests/run_tests_matrix.py b/tools/run_tests/run_tests_matrix.py index 0af9e0cbbe1..1bbec94ee12 100755 --- a/tools/run_tests/run_tests_matrix.py +++ b/tools/run_tests/run_tests_matrix.py @@ -53,12 +53,7 @@ def _safe_report_name(name): def _report_filename(name): - """Generates report file name""" - return 'report_%s_%s' % (_safe_report_name(name), _REPORT_SUFFIX) - - -def _report_filename_internal_ci(name): - """Generates report file name that leads to better presentation by internal CI""" + """Generates report file name with directory structure that leads to better presentation by internal CI""" return '%s/%s' % (_safe_report_name(name), _REPORT_SUFFIX) @@ -507,8 +502,9 @@ if __name__ == "__main__": default=False, action='store_const', const=True, - help='Put reports into subdirectories to improve presentation of ' - 'results by Internal CI.') + help= + '(Deprecated, has no effect) Put reports into subdirectories to improve presentation of ' + 'results by Kokoro.') argp.add_argument( '--bq_result_table', default='', @@ -517,9 +513,6 @@ if __name__ == "__main__": help='Upload test results to a specified BQ table.') args = argp.parse_args() - if args.internal_ci: - _report_filename = _report_filename_internal_ci # override the function - extra_args = [] if args.build_only: extra_args.append('--build_only')