diff --git a/.gitignore b/.gitignore index a6b34fd4f34..89bd1003f71 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ coverage # vim temp files .*.swp +# Makefile's cache +cache.mk diff --git a/BUILD b/BUILD index 13a8784ed19..d462a148bf1 100644 --- a/BUILD +++ b/BUILD @@ -47,6 +47,7 @@ cc_library( "src/core/support/env.h", "src/core/support/file.h", "src/core/support/murmur_hash.h", + "src/core/support/stack_lockfree.h", "src/core/support/string.h", "src/core/support/string_win32.h", "src/core/support/thd_internal.h", @@ -73,6 +74,7 @@ cc_library( "src/core/support/murmur_hash.c", "src/core/support/slice.c", "src/core/support/slice_buffer.c", + "src/core/support/stack_lockfree.c", "src/core/support/string.c", "src/core/support/string_posix.c", "src/core/support/string_win32.c", @@ -348,6 +350,7 @@ cc_library( "src/core/surface/server_chttp2.c", "src/core/surface/server_create.c", "src/core/surface/surface_trace.c", + "src/core/surface/version.c", "src/core/transport/chttp2/alpn.c", "src/core/transport/chttp2/bin_encoder.c", "src/core/transport/chttp2/frame_data.c", @@ -578,6 +581,7 @@ cc_library( "src/core/surface/server_chttp2.c", "src/core/surface/server_create.c", "src/core/surface/surface_trace.c", + "src/core/surface/version.c", "src/core/transport/chttp2/alpn.c", "src/core/transport/chttp2/bin_encoder.c", "src/core/transport/chttp2/frame_data.c", @@ -633,9 +637,9 @@ cc_library( "src/cpp/server/secure_server_credentials.h", "src/cpp/client/channel.h", "src/cpp/common/create_auth_context.h", - "src/cpp/server/thread_pool.h", "src/cpp/client/secure_channel_arguments.cc", "src/cpp/client/secure_credentials.cc", + "src/cpp/common/auth_property_iterator.cc", "src/cpp/common/secure_auth_context.cc", "src/cpp/common/secure_create_auth_context.cc", "src/cpp/server/secure_server_credentials.cc", @@ -653,12 +657,12 @@ cc_library( "src/cpp/proto/proto_utils.cc", "src/cpp/server/async_generic_service.cc", "src/cpp/server/create_default_thread_pool.cc", + "src/cpp/server/fixed_size_thread_pool.cc", "src/cpp/server/insecure_server_credentials.cc", "src/cpp/server/server.cc", "src/cpp/server/server_builder.cc", "src/cpp/server/server_context.cc", "src/cpp/server/server_credentials.cc", - "src/cpp/server/thread_pool.cc", "src/cpp/util/byte_buffer.cc", "src/cpp/util/slice.cc", "src/cpp/util/status.cc", @@ -668,6 +672,7 @@ cc_library( "include/grpc++/async_generic_service.h", "include/grpc++/async_unary_call.h", "include/grpc++/auth_context.h", + "include/grpc++/auth_property_iterator.h", "include/grpc++/byte_buffer.h", "include/grpc++/channel_arguments.h", "include/grpc++/channel_interface.h", @@ -677,6 +682,7 @@ cc_library( "include/grpc++/config_protobuf.h", "include/grpc++/create_channel.h", "include/grpc++/credentials.h", + "include/grpc++/fixed_size_thread_pool.h", "include/grpc++/generic_stub.h", "include/grpc++/impl/call.h", "include/grpc++/impl/client_unary_call.h", @@ -721,7 +727,6 @@ cc_library( srcs = [ "src/cpp/client/channel.h", "src/cpp/common/create_auth_context.h", - "src/cpp/server/thread_pool.h", "src/cpp/common/insecure_create_auth_context.cc", "src/cpp/client/channel.cc", "src/cpp/client/channel_arguments.cc", @@ -737,12 +742,12 @@ cc_library( "src/cpp/proto/proto_utils.cc", "src/cpp/server/async_generic_service.cc", "src/cpp/server/create_default_thread_pool.cc", + "src/cpp/server/fixed_size_thread_pool.cc", "src/cpp/server/insecure_server_credentials.cc", "src/cpp/server/server.cc", "src/cpp/server/server_builder.cc", "src/cpp/server/server_context.cc", "src/cpp/server/server_credentials.cc", - "src/cpp/server/thread_pool.cc", "src/cpp/util/byte_buffer.cc", "src/cpp/util/slice.cc", "src/cpp/util/status.cc", @@ -752,6 +757,7 @@ cc_library( "include/grpc++/async_generic_service.h", "include/grpc++/async_unary_call.h", "include/grpc++/auth_context.h", + "include/grpc++/auth_property_iterator.h", "include/grpc++/byte_buffer.h", "include/grpc++/channel_arguments.h", "include/grpc++/channel_interface.h", @@ -761,6 +767,7 @@ cc_library( "include/grpc++/config_protobuf.h", "include/grpc++/create_channel.h", "include/grpc++/credentials.h", + "include/grpc++/fixed_size_thread_pool.h", "include/grpc++/generic_stub.h", "include/grpc++/impl/call.h", "include/grpc++/impl/client_unary_call.h", @@ -881,6 +888,7 @@ objc_library( "src/core/support/murmur_hash.c", "src/core/support/slice.c", "src/core/support/slice_buffer.c", + "src/core/support/stack_lockfree.c", "src/core/support/string.c", "src/core/support/string_posix.c", "src/core/support/string_win32.c", @@ -928,6 +936,7 @@ objc_library( "src/core/support/env.h", "src/core/support/file.h", "src/core/support/murmur_hash.h", + "src/core/support/stack_lockfree.h", "src/core/support/string.h", "src/core/support/string_win32.h", "src/core/support/thd_internal.h", @@ -1049,6 +1058,7 @@ objc_library( "src/core/surface/server_chttp2.c", "src/core/surface/server_create.c", "src/core/surface/surface_trace.c", + "src/core/surface/version.c", "src/core/transport/chttp2/alpn.c", "src/core/transport/chttp2/bin_encoder.c", "src/core/transport/chttp2/frame_data.c", diff --git a/Makefile b/Makefile index 6bb5c61c658..96c813ddda1 100644 --- a/Makefile +++ b/Makefile @@ -332,9 +332,17 @@ HOST_LDLIBS = $(LDLIBS) # These are automatically computed variables. # There shouldn't be any need to change anything from now on. -HAS_PKG_CONFIG = $(shell command -v $(PKG_CONFIG) >/dev/null 2>&1 && echo true || echo false) +-include cache.mk -PC_TEMPLATE = prefix=$(prefix)\nexec_prefix=\$${prefix}\nincludedir=\$${prefix}/include\nlibdir=\$${exec_prefix}/lib\n\nName: $(PC_NAME)\nDescription: $(PC_DESCRIPTION)\nVersion: $(VERSION)\nCflags: -I\$${includedir} $(PC_CFLAGS)\nRequires.private: $(PC_REQUIRES_PRIVATE)\nLibs: -L\$${libdir} $(PC_LIB)\nLibs.private: $(PC_LIBS_PRIVATE) +CACHE_MK = + +HAS_PKG_CONFIG ?= $(shell command -v $(PKG_CONFIG) >/dev/null 2>&1 && echo true || echo false) + +ifeq ($(HAS_PKG_CONFIG), true) +CACHE_MK += HAS_PKG_CONFIG = true, +endif + +PC_TEMPLATE = prefix=$(prefix),exec_prefix=\$${prefix},includedir=\$${prefix}/include,libdir=\$${exec_prefix}/lib,,Name: $(PC_NAME),Description: $(PC_DESCRIPTION),Version: $(VERSION),Cflags: -I\$${includedir} $(PC_CFLAGS),Requires.private: $(PC_REQUIRES_PRIVATE),Libs: -L\$${libdir} $(PC_LIB),Libs.private: $(PC_LIBS_PRIVATE) # gpr .pc file PC_NAME = gRPC Portable Runtime @@ -405,23 +413,34 @@ DTRACE_CHECK_CMD = which dtrace > /dev/null SYSTEMTAP_HEADERS_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/systemtap.c $(LDFLAGS) ifndef REQUIRE_CUSTOM_LIBRARIES_$(CONFIG) -HAS_SYSTEM_PERFTOOLS = $(shell $(PERFTOOLS_CHECK_CMD) 2> /dev/null && echo true || echo false) +HAS_SYSTEM_PERFTOOLS ?= $(shell $(PERFTOOLS_CHECK_CMD) 2> /dev/null && echo true || echo false) ifeq ($(HAS_SYSTEM_PERFTOOLS),true) DEFINES += GRPC_HAVE_PERFTOOLS LIBS += profiler +CACHE_MK += HAS_SYSTEM_PERFTOOLS = true, endif endif HAS_SYSTEM_PROTOBUF_VERIFY = $(shell $(PROTOBUF_CHECK_CMD) 2> /dev/null && echo true || echo false) ifndef REQUIRE_CUSTOM_LIBRARIES_$(CONFIG) -HAS_SYSTEM_OPENSSL_ALPN = $(shell $(OPENSSL_ALPN_CHECK_CMD) 2> /dev/null && echo true || echo false) +HAS_SYSTEM_OPENSSL_ALPN ?= $(shell $(OPENSSL_ALPN_CHECK_CMD) 2> /dev/null && echo true || echo false) ifeq ($(HAS_SYSTEM_OPENSSL_ALPN),true) HAS_SYSTEM_OPENSSL_NPN = true +CACHE_MK += HAS_SYSTEM_OPENSSL_ALPN = true, else -HAS_SYSTEM_OPENSSL_NPN = $(shell $(OPENSSL_NPN_CHECK_CMD) 2> /dev/null && echo true || echo false) +HAS_SYSTEM_OPENSSL_NPN ?= $(shell $(OPENSSL_NPN_CHECK_CMD) 2> /dev/null && echo true || echo false) +endif +ifeq ($(HAS_SYSTEM_OPENSSL_NPN),true) +CACHE_MK += HAS_SYSTEM_OPENSSL_NPN = true, +endif +HAS_SYSTEM_ZLIB ?= $(shell $(ZLIB_CHECK_CMD) 2> /dev/null && echo true || echo false) +ifeq ($(HAS_SYSTEM_ZLIB),true) +CACHE_MK += HAS_SYSTEM_ZLIB = true, +endif +HAS_SYSTEM_PROTOBUF ?= $(HAS_SYSTEM_PROTOBUF_VERIFY) +ifeq ($(HAS_SYSTEM_PROTOBUF),true) +CACHE_MK += HAS_SYSTEM_PROTOBUF = true, endif -HAS_SYSTEM_ZLIB = $(shell $(ZLIB_CHECK_CMD) 2> /dev/null && echo true || echo false) -HAS_SYSTEM_PROTOBUF = $(HAS_SYSTEM_PROTOBUF_VERIFY) else # override system libraries if the config requires a custom compiled library HAS_SYSTEM_OPENSSL_ALPN = false @@ -430,9 +449,13 @@ HAS_SYSTEM_ZLIB = false HAS_SYSTEM_PROTOBUF = false endif -HAS_PROTOC = $(shell $(PROTOC_CHECK_CMD) 2> /dev/null && echo true || echo false) +HAS_PROTOC ?= $(shell $(PROTOC_CHECK_CMD) 2> /dev/null && echo true || echo false) ifeq ($(HAS_PROTOC),true) -HAS_VALID_PROTOC = $(shell $(PROTOC_CHECK_VERSION_CMD) 2> /dev/null && echo true || echo false) +CACHE_MK += HAS_PROTOC = true, +HAS_VALID_PROTOC ?= $(shell $(PROTOC_CHECK_VERSION_CMD) 2> /dev/null && echo true || echo false) +ifeq ($(HAS_VALID_PROTOC),true) +CACHE_MK += HAS_VALID_PROTOC = true, +endif else HAS_VALID_PROTOC = false endif @@ -440,6 +463,7 @@ endif # Check for Systemtap (https://sourceware.org/systemtap/), first by making sure is present # in the system and secondly by checking for the "dtrace" binary (on Linux, this is part of the Systemtap # distribution. It's part of the base system on BSD/Solaris machines). +ifndef HAS_SYSTEMTAP HAS_SYSTEMTAP_HEADERS = $(shell $(SYSTEMTAP_HEADERS_CHECK_CMD) 2> /dev/null && echo true || echo false) HAS_DTRACE = $(shell $(DTRACE_CHECK_CMD) 2> /dev/null && echo true || echo false) HAS_SYSTEMTAP = false @@ -448,6 +472,11 @@ ifeq ($(HAS_DTRACE),true) HAS_SYSTEMTAP = true endif endif +endif + +ifeq ($(HAS_SYSTEMTAP),true) +CACHE_MK += HAS_SYSTEMTAP = true, +endif # Note that for testing purposes, one can do: # make HAS_EMBEDDED_OPENSSL_ALPN=false @@ -761,6 +790,7 @@ gpr_host_port_test: $(BINDIR)/$(CONFIG)/gpr_host_port_test gpr_log_test: $(BINDIR)/$(CONFIG)/gpr_log_test gpr_slice_buffer_test: $(BINDIR)/$(CONFIG)/gpr_slice_buffer_test gpr_slice_test: $(BINDIR)/$(CONFIG)/gpr_slice_test +gpr_stack_lockfree_test: $(BINDIR)/$(CONFIG)/gpr_stack_lockfree_test gpr_string_test: $(BINDIR)/$(CONFIG)/gpr_string_test gpr_sync_test: $(BINDIR)/$(CONFIG)/gpr_sync_test gpr_thd_test: $(BINDIR)/$(CONFIG)/gpr_thd_test @@ -804,7 +834,6 @@ tcp_client_posix_test: $(BINDIR)/$(CONFIG)/tcp_client_posix_test tcp_posix_test: $(BINDIR)/$(CONFIG)/tcp_posix_test tcp_server_posix_test: $(BINDIR)/$(CONFIG)/tcp_server_posix_test time_averaged_stats_test: $(BINDIR)/$(CONFIG)/time_averaged_stats_test -time_test: $(BINDIR)/$(CONFIG)/time_test timeout_encoding_test: $(BINDIR)/$(CONFIG)/timeout_encoding_test timers_test: $(BINDIR)/$(CONFIG)/timers_test transport_metadata_test: $(BINDIR)/$(CONFIG)/transport_metadata_test @@ -813,6 +842,7 @@ uri_parser_test: $(BINDIR)/$(CONFIG)/uri_parser_test async_end2end_test: $(BINDIR)/$(CONFIG)/async_end2end_test async_streaming_ping_pong_test: $(BINDIR)/$(CONFIG)/async_streaming_ping_pong_test async_unary_ping_pong_test: $(BINDIR)/$(CONFIG)/async_unary_ping_pong_test +auth_property_iterator_test: $(BINDIR)/$(CONFIG)/auth_property_iterator_test channel_arguments_test: $(BINDIR)/$(CONFIG)/channel_arguments_test cli_call_test: $(BINDIR)/$(CONFIG)/cli_call_test client_crash_test: $(BINDIR)/$(CONFIG)/client_crash_test @@ -822,6 +852,7 @@ cxx_byte_buffer_test: $(BINDIR)/$(CONFIG)/cxx_byte_buffer_test cxx_slice_test: $(BINDIR)/$(CONFIG)/cxx_slice_test cxx_time_test: $(BINDIR)/$(CONFIG)/cxx_time_test end2end_test: $(BINDIR)/$(CONFIG)/end2end_test +fixed_size_thread_pool_test: $(BINDIR)/$(CONFIG)/fixed_size_thread_pool_test generic_end2end_test: $(BINDIR)/$(CONFIG)/generic_end2end_test grpc_cli: $(BINDIR)/$(CONFIG)/grpc_cli grpc_cpp_plugin: $(BINDIR)/$(CONFIG)/grpc_cpp_plugin @@ -838,8 +869,8 @@ pubsub_publisher_test: $(BINDIR)/$(CONFIG)/pubsub_publisher_test pubsub_subscriber_test: $(BINDIR)/$(CONFIG)/pubsub_subscriber_test qps_driver: $(BINDIR)/$(CONFIG)/qps_driver qps_interarrival_test: $(BINDIR)/$(CONFIG)/qps_interarrival_test +qps_openloop_test: $(BINDIR)/$(CONFIG)/qps_openloop_test qps_test: $(BINDIR)/$(CONFIG)/qps_test -qps_test_openloop: $(BINDIR)/$(CONFIG)/qps_test_openloop qps_worker: $(BINDIR)/$(CONFIG)/qps_worker secure_auth_context_test: $(BINDIR)/$(CONFIG)/secure_auth_context_test server_crash_test: $(BINDIR)/$(CONFIG)/server_crash_test @@ -847,7 +878,6 @@ server_crash_test_client: $(BINDIR)/$(CONFIG)/server_crash_test_client status_test: $(BINDIR)/$(CONFIG)/status_test sync_streaming_ping_pong_test: $(BINDIR)/$(CONFIG)/sync_streaming_ping_pong_test sync_unary_ping_pong_test: $(BINDIR)/$(CONFIG)/sync_unary_ping_pong_test -thread_pool_test: $(BINDIR)/$(CONFIG)/thread_pool_test thread_stress_test: $(BINDIR)/$(CONFIG)/thread_stress_test chttp2_fake_security_bad_hostname_test: $(BINDIR)/$(CONFIG)/chttp2_fake_security_bad_hostname_test chttp2_fake_security_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_test @@ -1391,15 +1421,15 @@ $(LIBDIR)/$(CONFIG)/protobuf/libprotobuf.a: third_party/protobuf/configure static: static_c static_cxx -static_c: pc_c pc_c_unsecure $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a +static_c: pc_c pc_c_unsecure cache.mk $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a -static_cxx: pc_cxx pc_cxx_unsecure pc_gpr $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a +static_cxx: pc_cxx pc_cxx_unsecure pc_gpr cache.mk $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a shared: shared_c shared_cxx -shared_c: pc_c pc_c_unsecure pc_gpr $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT) +shared_c: pc_c pc_c_unsecure pc_gpr cache.mk $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT) -shared_cxx: pc_cxx pc_cxx_unsecure $(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.$(SHARED_EXT) +shared_cxx: pc_cxx pc_cxx_unsecure cache.mk $(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.$(SHARED_EXT) shared_csharp: shared_c $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT) grpc_csharp_ext: shared_csharp @@ -1423,9 +1453,9 @@ privatelibs_cxx: $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG buildtests: buildtests_c buildtests_cxx -buildtests_c: privatelibs_c $(BINDIR)/$(CONFIG)/alarm_heap_test $(BINDIR)/$(CONFIG)/alarm_list_test $(BINDIR)/$(CONFIG)/alarm_test $(BINDIR)/$(CONFIG)/alpn_test $(BINDIR)/$(CONFIG)/bin_encoder_test $(BINDIR)/$(CONFIG)/chttp2_status_conversion_test $(BINDIR)/$(CONFIG)/chttp2_stream_encoder_test $(BINDIR)/$(CONFIG)/chttp2_stream_map_test $(BINDIR)/$(CONFIG)/dualstack_socket_test $(BINDIR)/$(CONFIG)/fd_conservation_posix_test $(BINDIR)/$(CONFIG)/fd_posix_test $(BINDIR)/$(CONFIG)/fling_client $(BINDIR)/$(CONFIG)/fling_server $(BINDIR)/$(CONFIG)/fling_stream_test $(BINDIR)/$(CONFIG)/fling_test $(BINDIR)/$(CONFIG)/gpr_cancellable_test $(BINDIR)/$(CONFIG)/gpr_cmdline_test $(BINDIR)/$(CONFIG)/gpr_env_test $(BINDIR)/$(CONFIG)/gpr_file_test $(BINDIR)/$(CONFIG)/gpr_histogram_test $(BINDIR)/$(CONFIG)/gpr_host_port_test $(BINDIR)/$(CONFIG)/gpr_log_test $(BINDIR)/$(CONFIG)/gpr_slice_buffer_test $(BINDIR)/$(CONFIG)/gpr_slice_test $(BINDIR)/$(CONFIG)/gpr_string_test $(BINDIR)/$(CONFIG)/gpr_sync_test $(BINDIR)/$(CONFIG)/gpr_thd_test $(BINDIR)/$(CONFIG)/gpr_time_test $(BINDIR)/$(CONFIG)/gpr_tls_test $(BINDIR)/$(CONFIG)/gpr_useful_test $(BINDIR)/$(CONFIG)/grpc_auth_context_test $(BINDIR)/$(CONFIG)/grpc_base64_test $(BINDIR)/$(CONFIG)/grpc_byte_buffer_reader_test $(BINDIR)/$(CONFIG)/grpc_channel_stack_test $(BINDIR)/$(CONFIG)/grpc_completion_queue_test $(BINDIR)/$(CONFIG)/grpc_credentials_test $(BINDIR)/$(CONFIG)/grpc_json_token_test $(BINDIR)/$(CONFIG)/grpc_jwt_verifier_test $(BINDIR)/$(CONFIG)/grpc_security_connector_test $(BINDIR)/$(CONFIG)/grpc_stream_op_test $(BINDIR)/$(CONFIG)/hpack_parser_test $(BINDIR)/$(CONFIG)/hpack_table_test $(BINDIR)/$(CONFIG)/httpcli_format_request_test $(BINDIR)/$(CONFIG)/httpcli_parser_test $(BINDIR)/$(CONFIG)/httpcli_test $(BINDIR)/$(CONFIG)/json_rewrite $(BINDIR)/$(CONFIG)/json_rewrite_test $(BINDIR)/$(CONFIG)/json_test $(BINDIR)/$(CONFIG)/lame_client_test $(BINDIR)/$(CONFIG)/message_compress_test $(BINDIR)/$(CONFIG)/multi_init_test $(BINDIR)/$(CONFIG)/multiple_server_queues_test $(BINDIR)/$(CONFIG)/murmur_hash_test $(BINDIR)/$(CONFIG)/no_server_test $(BINDIR)/$(CONFIG)/poll_kick_posix_test $(BINDIR)/$(CONFIG)/resolve_address_test $(BINDIR)/$(CONFIG)/secure_endpoint_test $(BINDIR)/$(CONFIG)/sockaddr_utils_test $(BINDIR)/$(CONFIG)/tcp_client_posix_test $(BINDIR)/$(CONFIG)/tcp_posix_test $(BINDIR)/$(CONFIG)/tcp_server_posix_test $(BINDIR)/$(CONFIG)/time_averaged_stats_test $(BINDIR)/$(CONFIG)/time_test $(BINDIR)/$(CONFIG)/timeout_encoding_test $(BINDIR)/$(CONFIG)/timers_test $(BINDIR)/$(CONFIG)/transport_metadata_test $(BINDIR)/$(CONFIG)/transport_security_test $(BINDIR)/$(CONFIG)/uri_parser_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_no_op_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_no_op_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_no_op_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_no_op_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_no_op_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_no_op_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_bad_hostname_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_and_writes_closed_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_before_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_in_a_vacuum_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_census_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_disappearing_server_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_tags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_empty_batch_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_graceful_server_shutdown_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_invoke_large_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_max_concurrent_streams_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_max_message_length_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_no_op_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_registered_call_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_binary_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_trailing_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_flags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_large_metadata_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_server_finishes_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_delayed_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_request_with_high_initial_sequence_number_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_bad_hostname_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_after_accept_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_after_accept_and_writes_closed_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_after_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_before_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_in_a_vacuum_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_census_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_disappearing_server_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_early_server_shutdown_finishes_inflight_calls_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_early_server_shutdown_finishes_tags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_empty_batch_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_graceful_server_shutdown_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_invoke_large_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_max_concurrent_streams_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_max_message_length_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_no_op_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_ping_pong_streaming_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_registered_call_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_response_with_binary_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_response_with_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_response_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_response_with_trailing_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_with_flags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_with_large_metadata_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_server_finishes_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_simple_delayed_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_simple_request_with_high_initial_sequence_number_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_bad_hostname_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_after_accept_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_after_accept_and_writes_closed_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_after_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_before_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_in_a_vacuum_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_census_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_disappearing_server_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_early_server_shutdown_finishes_inflight_calls_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_early_server_shutdown_finishes_tags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_empty_batch_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_graceful_server_shutdown_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_invoke_large_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_max_concurrent_streams_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_max_message_length_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_no_op_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_ping_pong_streaming_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_registered_call_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_response_with_binary_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_response_with_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_response_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_response_with_trailing_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_with_flags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_with_large_metadata_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_server_finishes_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_simple_delayed_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_simple_request_with_high_initial_sequence_number_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_bad_hostname_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_and_writes_closed_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_before_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_in_a_vacuum_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_census_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_disappearing_server_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_tags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_empty_batch_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_graceful_server_shutdown_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_invoke_large_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_max_concurrent_streams_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_max_message_length_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_no_op_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_registered_call_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_binary_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_trailing_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_flags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_large_metadata_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_server_finishes_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_request_with_high_initial_sequence_number_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_bad_hostname_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_census_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_disappearing_server_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_empty_batch_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_graceful_server_shutdown_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_message_length_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_no_op_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_registered_call_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_binary_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_trailing_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_flags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_large_metadata_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_server_finishes_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_with_high_initial_sequence_number_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_bad_hostname_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_after_accept_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_after_accept_and_writes_closed_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_after_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_before_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_in_a_vacuum_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_census_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_disappearing_server_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_early_server_shutdown_finishes_inflight_calls_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_early_server_shutdown_finishes_tags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_empty_batch_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_graceful_server_shutdown_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_invoke_large_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_max_concurrent_streams_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_max_message_length_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_no_op_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_ping_pong_streaming_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_registered_call_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_response_with_binary_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_response_with_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_response_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_response_with_trailing_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_with_flags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_with_large_metadata_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_server_finishes_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_simple_delayed_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_simple_request_with_high_initial_sequence_number_unsecure_test $(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test $(BINDIR)/$(CONFIG)/initial_settings_frame_bad_client_test +buildtests_c: privatelibs_c $(BINDIR)/$(CONFIG)/alarm_heap_test $(BINDIR)/$(CONFIG)/alarm_list_test $(BINDIR)/$(CONFIG)/alarm_test $(BINDIR)/$(CONFIG)/alpn_test $(BINDIR)/$(CONFIG)/bin_encoder_test $(BINDIR)/$(CONFIG)/chttp2_status_conversion_test $(BINDIR)/$(CONFIG)/chttp2_stream_encoder_test $(BINDIR)/$(CONFIG)/chttp2_stream_map_test $(BINDIR)/$(CONFIG)/dualstack_socket_test $(BINDIR)/$(CONFIG)/fd_conservation_posix_test $(BINDIR)/$(CONFIG)/fd_posix_test $(BINDIR)/$(CONFIG)/fling_client $(BINDIR)/$(CONFIG)/fling_server $(BINDIR)/$(CONFIG)/fling_stream_test $(BINDIR)/$(CONFIG)/fling_test $(BINDIR)/$(CONFIG)/gpr_cancellable_test $(BINDIR)/$(CONFIG)/gpr_cmdline_test $(BINDIR)/$(CONFIG)/gpr_env_test $(BINDIR)/$(CONFIG)/gpr_file_test $(BINDIR)/$(CONFIG)/gpr_histogram_test $(BINDIR)/$(CONFIG)/gpr_host_port_test $(BINDIR)/$(CONFIG)/gpr_log_test $(BINDIR)/$(CONFIG)/gpr_slice_buffer_test $(BINDIR)/$(CONFIG)/gpr_slice_test $(BINDIR)/$(CONFIG)/gpr_stack_lockfree_test $(BINDIR)/$(CONFIG)/gpr_string_test $(BINDIR)/$(CONFIG)/gpr_sync_test $(BINDIR)/$(CONFIG)/gpr_thd_test $(BINDIR)/$(CONFIG)/gpr_time_test $(BINDIR)/$(CONFIG)/gpr_tls_test $(BINDIR)/$(CONFIG)/gpr_useful_test $(BINDIR)/$(CONFIG)/grpc_auth_context_test $(BINDIR)/$(CONFIG)/grpc_base64_test $(BINDIR)/$(CONFIG)/grpc_byte_buffer_reader_test $(BINDIR)/$(CONFIG)/grpc_channel_stack_test $(BINDIR)/$(CONFIG)/grpc_completion_queue_test $(BINDIR)/$(CONFIG)/grpc_credentials_test $(BINDIR)/$(CONFIG)/grpc_json_token_test $(BINDIR)/$(CONFIG)/grpc_jwt_verifier_test $(BINDIR)/$(CONFIG)/grpc_security_connector_test $(BINDIR)/$(CONFIG)/grpc_stream_op_test $(BINDIR)/$(CONFIG)/hpack_parser_test $(BINDIR)/$(CONFIG)/hpack_table_test $(BINDIR)/$(CONFIG)/httpcli_format_request_test $(BINDIR)/$(CONFIG)/httpcli_parser_test $(BINDIR)/$(CONFIG)/httpcli_test $(BINDIR)/$(CONFIG)/json_rewrite $(BINDIR)/$(CONFIG)/json_rewrite_test $(BINDIR)/$(CONFIG)/json_test $(BINDIR)/$(CONFIG)/lame_client_test $(BINDIR)/$(CONFIG)/message_compress_test $(BINDIR)/$(CONFIG)/multi_init_test $(BINDIR)/$(CONFIG)/multiple_server_queues_test $(BINDIR)/$(CONFIG)/murmur_hash_test $(BINDIR)/$(CONFIG)/no_server_test $(BINDIR)/$(CONFIG)/poll_kick_posix_test $(BINDIR)/$(CONFIG)/resolve_address_test $(BINDIR)/$(CONFIG)/secure_endpoint_test $(BINDIR)/$(CONFIG)/sockaddr_utils_test $(BINDIR)/$(CONFIG)/tcp_client_posix_test $(BINDIR)/$(CONFIG)/tcp_posix_test $(BINDIR)/$(CONFIG)/tcp_server_posix_test $(BINDIR)/$(CONFIG)/time_averaged_stats_test $(BINDIR)/$(CONFIG)/timeout_encoding_test $(BINDIR)/$(CONFIG)/timers_test $(BINDIR)/$(CONFIG)/transport_metadata_test $(BINDIR)/$(CONFIG)/transport_security_test $(BINDIR)/$(CONFIG)/uri_parser_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_no_op_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_no_op_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_with_poll_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_no_op_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_no_op_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_no_op_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_max_message_length_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_no_op_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_registered_call_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_response_with_payload_and_call_creds_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_response_with_trailing_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_with_flags_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_server_finishes_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_simple_request_with_high_initial_sequence_number_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_bad_hostname_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_and_writes_closed_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_before_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_in_a_vacuum_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_census_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_disappearing_server_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_tags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_empty_batch_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_graceful_server_shutdown_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_invoke_large_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_max_concurrent_streams_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_max_message_length_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_no_op_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_registered_call_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_binary_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_trailing_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_flags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_large_metadata_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_server_finishes_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_delayed_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_request_with_high_initial_sequence_number_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_bad_hostname_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_after_accept_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_after_accept_and_writes_closed_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_after_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_before_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_cancel_in_a_vacuum_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_census_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_disappearing_server_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_early_server_shutdown_finishes_inflight_calls_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_early_server_shutdown_finishes_tags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_empty_batch_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_graceful_server_shutdown_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_invoke_large_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_max_concurrent_streams_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_max_message_length_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_no_op_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_ping_pong_streaming_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_registered_call_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_response_with_binary_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_response_with_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_response_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_response_with_trailing_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_with_flags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_with_large_metadata_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_request_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_server_finishes_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_simple_delayed_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_posix_simple_request_with_high_initial_sequence_number_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_bad_hostname_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_after_accept_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_after_accept_and_writes_closed_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_after_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_before_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_cancel_in_a_vacuum_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_census_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_disappearing_server_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_early_server_shutdown_finishes_inflight_calls_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_early_server_shutdown_finishes_tags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_empty_batch_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_graceful_server_shutdown_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_invoke_large_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_max_concurrent_streams_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_max_message_length_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_no_op_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_ping_pong_streaming_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_registered_call_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_response_with_binary_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_response_with_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_response_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_response_with_trailing_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_with_flags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_with_large_metadata_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_request_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_server_finishes_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_simple_delayed_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_with_poll_simple_request_with_high_initial_sequence_number_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_bad_hostname_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_and_writes_closed_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_before_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_in_a_vacuum_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_census_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_disappearing_server_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_tags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_empty_batch_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_graceful_server_shutdown_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_invoke_large_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_max_concurrent_streams_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_max_message_length_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_no_op_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_registered_call_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_binary_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_trailing_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_flags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_large_metadata_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_server_finishes_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_request_with_high_initial_sequence_number_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_bad_hostname_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_census_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_disappearing_server_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_empty_batch_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_graceful_server_shutdown_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_message_length_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_no_op_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_registered_call_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_binary_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_trailing_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_flags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_large_metadata_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_server_finishes_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_with_high_initial_sequence_number_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_bad_hostname_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_after_accept_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_after_accept_and_writes_closed_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_after_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_before_invoke_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_cancel_in_a_vacuum_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_census_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_disappearing_server_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_early_server_shutdown_finishes_inflight_calls_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_early_server_shutdown_finishes_tags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_empty_batch_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_graceful_server_shutdown_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_invoke_large_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_max_concurrent_streams_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_max_message_length_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_no_op_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_ping_pong_streaming_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_registered_call_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_response_with_binary_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_response_with_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_response_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_response_with_trailing_metadata_and_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_with_flags_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_with_large_metadata_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_request_with_payload_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_server_finishes_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_simple_delayed_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_simple_request_unsecure_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_with_grpc_trace_simple_request_with_high_initial_sequence_number_unsecure_test $(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test $(BINDIR)/$(CONFIG)/initial_settings_frame_bad_client_test -buildtests_cxx: privatelibs_cxx $(BINDIR)/$(CONFIG)/async_end2end_test $(BINDIR)/$(CONFIG)/async_streaming_ping_pong_test $(BINDIR)/$(CONFIG)/async_unary_ping_pong_test $(BINDIR)/$(CONFIG)/channel_arguments_test $(BINDIR)/$(CONFIG)/cli_call_test $(BINDIR)/$(CONFIG)/client_crash_test $(BINDIR)/$(CONFIG)/client_crash_test_server $(BINDIR)/$(CONFIG)/credentials_test $(BINDIR)/$(CONFIG)/cxx_byte_buffer_test $(BINDIR)/$(CONFIG)/cxx_slice_test $(BINDIR)/$(CONFIG)/cxx_time_test $(BINDIR)/$(CONFIG)/end2end_test $(BINDIR)/$(CONFIG)/generic_end2end_test $(BINDIR)/$(CONFIG)/grpc_cli $(BINDIR)/$(CONFIG)/interop_client $(BINDIR)/$(CONFIG)/interop_server $(BINDIR)/$(CONFIG)/interop_test $(BINDIR)/$(CONFIG)/mock_test $(BINDIR)/$(CONFIG)/qps_interarrival_test $(BINDIR)/$(CONFIG)/qps_test $(BINDIR)/$(CONFIG)/qps_test_openloop $(BINDIR)/$(CONFIG)/secure_auth_context_test $(BINDIR)/$(CONFIG)/server_crash_test $(BINDIR)/$(CONFIG)/server_crash_test_client $(BINDIR)/$(CONFIG)/status_test $(BINDIR)/$(CONFIG)/sync_streaming_ping_pong_test $(BINDIR)/$(CONFIG)/sync_unary_ping_pong_test $(BINDIR)/$(CONFIG)/thread_pool_test $(BINDIR)/$(CONFIG)/thread_stress_test +buildtests_cxx: privatelibs_cxx $(BINDIR)/$(CONFIG)/async_end2end_test $(BINDIR)/$(CONFIG)/async_streaming_ping_pong_test $(BINDIR)/$(CONFIG)/async_unary_ping_pong_test $(BINDIR)/$(CONFIG)/auth_property_iterator_test $(BINDIR)/$(CONFIG)/channel_arguments_test $(BINDIR)/$(CONFIG)/cli_call_test $(BINDIR)/$(CONFIG)/client_crash_test $(BINDIR)/$(CONFIG)/client_crash_test_server $(BINDIR)/$(CONFIG)/credentials_test $(BINDIR)/$(CONFIG)/cxx_byte_buffer_test $(BINDIR)/$(CONFIG)/cxx_slice_test $(BINDIR)/$(CONFIG)/cxx_time_test $(BINDIR)/$(CONFIG)/end2end_test $(BINDIR)/$(CONFIG)/fixed_size_thread_pool_test $(BINDIR)/$(CONFIG)/generic_end2end_test $(BINDIR)/$(CONFIG)/grpc_cli $(BINDIR)/$(CONFIG)/interop_client $(BINDIR)/$(CONFIG)/interop_server $(BINDIR)/$(CONFIG)/interop_test $(BINDIR)/$(CONFIG)/mock_test $(BINDIR)/$(CONFIG)/qps_interarrival_test $(BINDIR)/$(CONFIG)/qps_openloop_test $(BINDIR)/$(CONFIG)/qps_test $(BINDIR)/$(CONFIG)/secure_auth_context_test $(BINDIR)/$(CONFIG)/server_crash_test $(BINDIR)/$(CONFIG)/server_crash_test_client $(BINDIR)/$(CONFIG)/status_test $(BINDIR)/$(CONFIG)/sync_streaming_ping_pong_test $(BINDIR)/$(CONFIG)/sync_unary_ping_pong_test $(BINDIR)/$(CONFIG)/thread_stress_test test: test_c test_cxx @@ -1476,6 +1506,8 @@ test_c: buildtests_c $(Q) $(BINDIR)/$(CONFIG)/gpr_slice_buffer_test || ( echo test gpr_slice_buffer_test failed ; exit 1 ) $(E) "[RUN] Testing gpr_slice_test" $(Q) $(BINDIR)/$(CONFIG)/gpr_slice_test || ( echo test gpr_slice_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_stack_lockfree_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_stack_lockfree_test || ( echo test gpr_stack_lockfree_test failed ; exit 1 ) $(E) "[RUN] Testing gpr_string_test" $(Q) $(BINDIR)/$(CONFIG)/gpr_string_test || ( echo test gpr_string_test failed ; exit 1 ) $(E) "[RUN] Testing gpr_sync_test" @@ -1550,8 +1582,6 @@ test_c: buildtests_c $(Q) $(BINDIR)/$(CONFIG)/tcp_server_posix_test || ( echo test tcp_server_posix_test failed ; exit 1 ) $(E) "[RUN] Testing time_averaged_stats_test" $(Q) $(BINDIR)/$(CONFIG)/time_averaged_stats_test || ( echo test time_averaged_stats_test failed ; exit 1 ) - $(E) "[RUN] Testing time_test" - $(Q) $(BINDIR)/$(CONFIG)/time_test || ( echo test time_test failed ; exit 1 ) $(E) "[RUN] Testing timeout_encoding_test" $(Q) $(BINDIR)/$(CONFIG)/timeout_encoding_test || ( echo test timeout_encoding_test failed ; exit 1 ) $(E) "[RUN] Testing timers_test" @@ -2526,6 +2556,8 @@ test_cxx: buildtests_cxx $(Q) $(BINDIR)/$(CONFIG)/async_streaming_ping_pong_test || ( echo test async_streaming_ping_pong_test failed ; exit 1 ) $(E) "[RUN] Testing async_unary_ping_pong_test" $(Q) $(BINDIR)/$(CONFIG)/async_unary_ping_pong_test || ( echo test async_unary_ping_pong_test failed ; exit 1 ) + $(E) "[RUN] Testing auth_property_iterator_test" + $(Q) $(BINDIR)/$(CONFIG)/auth_property_iterator_test || ( echo test auth_property_iterator_test failed ; exit 1 ) $(E) "[RUN] Testing channel_arguments_test" $(Q) $(BINDIR)/$(CONFIG)/channel_arguments_test || ( echo test channel_arguments_test failed ; exit 1 ) $(E) "[RUN] Testing cli_call_test" @@ -2542,16 +2574,18 @@ test_cxx: buildtests_cxx $(Q) $(BINDIR)/$(CONFIG)/cxx_time_test || ( echo test cxx_time_test failed ; exit 1 ) $(E) "[RUN] Testing end2end_test" $(Q) $(BINDIR)/$(CONFIG)/end2end_test || ( echo test end2end_test failed ; exit 1 ) + $(E) "[RUN] Testing fixed_size_thread_pool_test" + $(Q) $(BINDIR)/$(CONFIG)/fixed_size_thread_pool_test || ( echo test fixed_size_thread_pool_test failed ; exit 1 ) $(E) "[RUN] Testing generic_end2end_test" $(Q) $(BINDIR)/$(CONFIG)/generic_end2end_test || ( echo test generic_end2end_test failed ; exit 1 ) $(E) "[RUN] Testing interop_test" $(Q) $(BINDIR)/$(CONFIG)/interop_test || ( echo test interop_test failed ; exit 1 ) $(E) "[RUN] Testing mock_test" $(Q) $(BINDIR)/$(CONFIG)/mock_test || ( echo test mock_test failed ; exit 1 ) + $(E) "[RUN] Testing qps_openloop_test" + $(Q) $(BINDIR)/$(CONFIG)/qps_openloop_test || ( echo test qps_openloop_test failed ; exit 1 ) $(E) "[RUN] Testing qps_test" $(Q) $(BINDIR)/$(CONFIG)/qps_test || ( echo test qps_test failed ; exit 1 ) - $(E) "[RUN] Testing qps_test_openloop" - $(Q) $(BINDIR)/$(CONFIG)/qps_test_openloop || ( echo test qps_test_openloop failed ; exit 1 ) $(E) "[RUN] Testing secure_auth_context_test" $(Q) $(BINDIR)/$(CONFIG)/secure_auth_context_test || ( echo test secure_auth_context_test failed ; exit 1 ) $(E) "[RUN] Testing server_crash_test" @@ -2562,8 +2596,6 @@ test_cxx: buildtests_cxx $(Q) $(BINDIR)/$(CONFIG)/sync_streaming_ping_pong_test || ( echo test sync_streaming_ping_pong_test failed ; exit 1 ) $(E) "[RUN] Testing sync_unary_ping_pong_test" $(Q) $(BINDIR)/$(CONFIG)/sync_unary_ping_pong_test || ( echo test sync_unary_ping_pong_test failed ; exit 1 ) - $(E) "[RUN] Testing thread_pool_test" - $(Q) $(BINDIR)/$(CONFIG)/thread_pool_test || ( echo test thread_pool_test failed ; exit 1 ) $(E) "[RUN] Testing thread_stress_test" $(Q) $(BINDIR)/$(CONFIG)/thread_stress_test || ( echo test thread_stress_test failed ; exit 1 ) @@ -2640,30 +2672,34 @@ ifeq ($(CONFIG),opt) $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT) endif +cache.mk:: + $(E) "[MAKE] Generating $@" + $(Q) echo "$(CACHE_MK)" | tr , '\n' >$@ + $(LIBDIR)/$(CONFIG)/pkgconfig/gpr.pc: $(E) "[MAKE] Generating $@" $(Q) mkdir -p $(@D) - $(Q) echo -e "$(GPR_PC_FILE)" >$@ + $(Q) echo "$(GPR_PC_FILE)" | tr , '\n' >$@ $(LIBDIR)/$(CONFIG)/pkgconfig/grpc.pc: $(E) "[MAKE] Generating $@" $(Q) mkdir -p $(@D) - $(Q) echo -e "$(GRPC_PC_FILE)" >$@ + $(Q) echo "$(GRPC_PC_FILE)" | tr , '\n' >$@ $(LIBDIR)/$(CONFIG)/pkgconfig/grpc_unsecure.pc: $(E) "[MAKE] Generating $@" $(Q) mkdir -p $(@D) - $(Q) echo -e "$(GRPC_UNSECURE_PC_FILE)" >$@ + $(Q) echo "$(GRPC_UNSECURE_PC_FILE)" | tr , '\n' >$@ $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++.pc: $(E) "[MAKE] Generating $@" $(Q) mkdir -p $(@D) - $(Q) echo -e "$(GRPCXX_PC_FILE)" >$@ + $(Q) echo "$(GRPCXX_PC_FILE)" | tr , '\n' >$@ $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++_unsecure.pc: $(E) "[MAKE] Generating $@" $(Q) mkdir -p $(@D) - $(Q) echo -e "$(GRPCXX_UNSECURE_PC_FILE)" >$@ + $(Q) echo "$(GRPCXX_UNSECURE_PC_FILE)" | tr , '\n' >$@ ifeq ($(NO_PROTOC),true) $(GENDIR)/examples/pubsub/empty.pb.cc: protoc_dep_error @@ -3073,7 +3109,7 @@ endif clean: $(E) "[CLEAN] Cleaning build directories." - $(Q) $(RM) -rf $(OBJDIR) $(LIBDIR) $(BINDIR) $(GENDIR) + $(Q) $(RM) -rf $(OBJDIR) $(LIBDIR) $(BINDIR) $(GENDIR) cache.mk # The various libraries @@ -3103,6 +3139,7 @@ LIBGPR_SRC = \ src/core/support/murmur_hash.c \ src/core/support/slice.c \ src/core/support/slice_buffer.c \ + src/core/support/stack_lockfree.c \ src/core/support/string.c \ src/core/support/string_posix.c \ src/core/support/string_win32.c \ @@ -3313,6 +3350,7 @@ LIBGRPC_SRC = \ src/core/surface/server_chttp2.c \ src/core/surface/server_create.c \ src/core/surface/surface_trace.c \ + src/core/surface/version.c \ src/core/transport/chttp2/alpn.c \ src/core/transport/chttp2/bin_encoder.c \ src/core/transport/chttp2/frame_data.c \ @@ -3575,6 +3613,7 @@ LIBGRPC_UNSECURE_SRC = \ src/core/surface/server_chttp2.c \ src/core/surface/server_create.c \ src/core/surface/surface_trace.c \ + src/core/surface/version.c \ src/core/transport/chttp2/alpn.c \ src/core/transport/chttp2/bin_encoder.c \ src/core/transport/chttp2/frame_data.c \ @@ -3651,6 +3690,7 @@ endif LIBGRPC++_SRC = \ src/cpp/client/secure_channel_arguments.cc \ src/cpp/client/secure_credentials.cc \ + src/cpp/common/auth_property_iterator.cc \ src/cpp/common/secure_auth_context.cc \ src/cpp/common/secure_create_auth_context.cc \ src/cpp/server/secure_server_credentials.cc \ @@ -3668,12 +3708,12 @@ LIBGRPC++_SRC = \ src/cpp/proto/proto_utils.cc \ src/cpp/server/async_generic_service.cc \ src/cpp/server/create_default_thread_pool.cc \ + src/cpp/server/fixed_size_thread_pool.cc \ src/cpp/server/insecure_server_credentials.cc \ src/cpp/server/server.cc \ src/cpp/server/server_builder.cc \ src/cpp/server/server_context.cc \ src/cpp/server/server_credentials.cc \ - src/cpp/server/thread_pool.cc \ src/cpp/util/byte_buffer.cc \ src/cpp/util/slice.cc \ src/cpp/util/status.cc \ @@ -3683,6 +3723,7 @@ PUBLIC_HEADERS_CXX += \ include/grpc++/async_generic_service.h \ include/grpc++/async_unary_call.h \ include/grpc++/auth_context.h \ + include/grpc++/auth_property_iterator.h \ include/grpc++/byte_buffer.h \ include/grpc++/channel_arguments.h \ include/grpc++/channel_interface.h \ @@ -3692,6 +3733,7 @@ PUBLIC_HEADERS_CXX += \ include/grpc++/config_protobuf.h \ include/grpc++/create_channel.h \ include/grpc++/credentials.h \ + include/grpc++/fixed_size_thread_pool.h \ include/grpc++/generic_stub.h \ include/grpc++/impl/call.h \ include/grpc++/impl/client_unary_call.h \ @@ -3909,12 +3951,12 @@ LIBGRPC++_UNSECURE_SRC = \ src/cpp/proto/proto_utils.cc \ src/cpp/server/async_generic_service.cc \ src/cpp/server/create_default_thread_pool.cc \ + src/cpp/server/fixed_size_thread_pool.cc \ src/cpp/server/insecure_server_credentials.cc \ src/cpp/server/server.cc \ src/cpp/server/server_builder.cc \ src/cpp/server/server_context.cc \ src/cpp/server/server_credentials.cc \ - src/cpp/server/thread_pool.cc \ src/cpp/util/byte_buffer.cc \ src/cpp/util/slice.cc \ src/cpp/util/status.cc \ @@ -3924,6 +3966,7 @@ PUBLIC_HEADERS_CXX += \ include/grpc++/async_generic_service.h \ include/grpc++/async_unary_call.h \ include/grpc++/auth_context.h \ + include/grpc++/auth_property_iterator.h \ include/grpc++/byte_buffer.h \ include/grpc++/channel_arguments.h \ include/grpc++/channel_interface.h \ @@ -3933,6 +3976,7 @@ PUBLIC_HEADERS_CXX += \ include/grpc++/config_protobuf.h \ include/grpc++/create_channel.h \ include/grpc++/credentials.h \ + include/grpc++/fixed_size_thread_pool.h \ include/grpc++/generic_stub.h \ include/grpc++/impl/call.h \ include/grpc++/impl/client_unary_call.h \ @@ -6223,6 +6267,35 @@ endif endif +GPR_STACK_LOCKFREE_TEST_SRC = \ + test/core/support/stack_lockfree_test.c \ + +GPR_STACK_LOCKFREE_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_STACK_LOCKFREE_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gpr_stack_lockfree_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gpr_stack_lockfree_test: $(GPR_STACK_LOCKFREE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_STACK_LOCKFREE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_stack_lockfree_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/stack_lockfree_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_gpr_stack_lockfree_test: $(GPR_STACK_LOCKFREE_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_STACK_LOCKFREE_TEST_OBJS:.o=.dep) +endif +endif + + GPR_STRING_TEST_SRC = \ test/core/support/string_test.c \ @@ -7470,35 +7543,6 @@ endif endif -TIME_TEST_SRC = \ - test/core/support/time_test.c \ - -TIME_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(TIME_TEST_SRC)))) -ifeq ($(NO_SECURE),true) - -# You can't build secure targets if you don't have OpenSSL. - -$(BINDIR)/$(CONFIG)/time_test: openssl_dep_error - -else - -$(BINDIR)/$(CONFIG)/time_test: $(TIME_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) $(TIME_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)/time_test - -endif - -$(OBJDIR)/$(CONFIG)/test/core/support/time_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a -deps_time_test: $(TIME_TEST_OBJS:.o=.dep) - -ifneq ($(NO_SECURE),true) -ifneq ($(NO_DEPS),true) --include $(TIME_TEST_OBJS:.o=.dep) -endif -endif - - TIMEOUT_ENCODING_TEST_SRC = \ test/core/transport/chttp2/timeout_encoding_test.c \ @@ -7764,6 +7808,46 @@ endif endif +AUTH_PROPERTY_ITERATOR_TEST_SRC = \ + test/cpp/common/auth_property_iterator_test.cc \ + +AUTH_PROPERTY_ITERATOR_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(AUTH_PROPERTY_ITERATOR_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/auth_property_iterator_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.0.0+. + +$(BINDIR)/$(CONFIG)/auth_property_iterator_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/auth_property_iterator_test: $(PROTOBUF_DEP) $(AUTH_PROPERTY_ITERATOR_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(AUTH_PROPERTY_ITERATOR_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/auth_property_iterator_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/common/auth_property_iterator_test.o: $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_auth_property_iterator_test: $(AUTH_PROPERTY_ITERATOR_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(AUTH_PROPERTY_ITERATOR_TEST_OBJS:.o=.dep) +endif +endif + + CHANNEL_ARGUMENTS_TEST_SRC = \ test/cpp/client/channel_arguments_test.cc \ @@ -8124,6 +8208,46 @@ endif endif +FIXED_SIZE_THREAD_POOL_TEST_SRC = \ + test/cpp/server/fixed_size_thread_pool_test.cc \ + +FIXED_SIZE_THREAD_POOL_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(FIXED_SIZE_THREAD_POOL_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/fixed_size_thread_pool_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.0.0+. + +$(BINDIR)/$(CONFIG)/fixed_size_thread_pool_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/fixed_size_thread_pool_test: $(PROTOBUF_DEP) $(FIXED_SIZE_THREAD_POOL_TEST_OBJS) $(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) $(FIXED_SIZE_THREAD_POOL_TEST_OBJS) $(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)/fixed_size_thread_pool_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/server/fixed_size_thread_pool_test.o: $(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_fixed_size_thread_pool_test: $(FIXED_SIZE_THREAD_POOL_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(FIXED_SIZE_THREAD_POOL_TEST_OBJS:.o=.dep) +endif +endif + + GENERIC_END2END_TEST_SRC = \ test/cpp/end2end/generic_end2end_test.cc \ @@ -8682,15 +8806,15 @@ endif endif -QPS_TEST_SRC = \ - test/cpp/qps/qps_test.cc \ +QPS_OPENLOOP_TEST_SRC = \ + test/cpp/qps/qps_openloop_test.cc \ -QPS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(QPS_TEST_SRC)))) +QPS_OPENLOOP_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(QPS_OPENLOOP_TEST_SRC)))) ifeq ($(NO_SECURE),true) # You can't build secure targets if you don't have OpenSSL. -$(BINDIR)/$(CONFIG)/qps_test: openssl_dep_error +$(BINDIR)/$(CONFIG)/qps_openloop_test: openssl_dep_error else @@ -8699,38 +8823,38 @@ ifeq ($(NO_PROTOBUF),true) # You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+. -$(BINDIR)/$(CONFIG)/qps_test: protobuf_dep_error +$(BINDIR)/$(CONFIG)/qps_openloop_test: protobuf_dep_error else -$(BINDIR)/$(CONFIG)/qps_test: $(PROTOBUF_DEP) $(QPS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a +$(BINDIR)/$(CONFIG)/qps_openloop_test: $(PROTOBUF_DEP) $(QPS_OPENLOOP_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(E) "[LD] Linking $@" $(Q) mkdir -p `dirname $@` - $(Q) $(LDXX) $(LDFLAGS) $(QPS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/qps_test + $(Q) $(LDXX) $(LDFLAGS) $(QPS_OPENLOOP_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/qps_openloop_test endif endif -$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_test.o: $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a -deps_qps_test: $(QPS_TEST_OBJS:.o=.dep) +$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_openloop_test.o: $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a +deps_qps_openloop_test: $(QPS_OPENLOOP_TEST_OBJS:.o=.dep) ifneq ($(NO_SECURE),true) ifneq ($(NO_DEPS),true) --include $(QPS_TEST_OBJS:.o=.dep) +-include $(QPS_OPENLOOP_TEST_OBJS:.o=.dep) endif endif -QPS_TEST_OPENLOOP_SRC = \ - test/cpp/qps/qps_test_openloop.cc \ +QPS_TEST_SRC = \ + test/cpp/qps/qps_test.cc \ -QPS_TEST_OPENLOOP_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(QPS_TEST_OPENLOOP_SRC)))) +QPS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(QPS_TEST_SRC)))) ifeq ($(NO_SECURE),true) # You can't build secure targets if you don't have OpenSSL. -$(BINDIR)/$(CONFIG)/qps_test_openloop: openssl_dep_error +$(BINDIR)/$(CONFIG)/qps_test: openssl_dep_error else @@ -8739,25 +8863,25 @@ ifeq ($(NO_PROTOBUF),true) # You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+. -$(BINDIR)/$(CONFIG)/qps_test_openloop: protobuf_dep_error +$(BINDIR)/$(CONFIG)/qps_test: protobuf_dep_error else -$(BINDIR)/$(CONFIG)/qps_test_openloop: $(PROTOBUF_DEP) $(QPS_TEST_OPENLOOP_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a +$(BINDIR)/$(CONFIG)/qps_test: $(PROTOBUF_DEP) $(QPS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(E) "[LD] Linking $@" $(Q) mkdir -p `dirname $@` - $(Q) $(LDXX) $(LDFLAGS) $(QPS_TEST_OPENLOOP_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/qps_test_openloop + $(Q) $(LDXX) $(LDFLAGS) $(QPS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/qps_test endif endif -$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_test_openloop.o: $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a -deps_qps_test_openloop: $(QPS_TEST_OPENLOOP_OBJS:.o=.dep) +$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_test.o: $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a +deps_qps_test: $(QPS_TEST_OBJS:.o=.dep) ifneq ($(NO_SECURE),true) ifneq ($(NO_DEPS),true) --include $(QPS_TEST_OPENLOOP_OBJS:.o=.dep) +-include $(QPS_TEST_OBJS:.o=.dep) endif endif @@ -9042,46 +9166,6 @@ endif endif -THREAD_POOL_TEST_SRC = \ - test/cpp/server/thread_pool_test.cc \ - -THREAD_POOL_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(THREAD_POOL_TEST_SRC)))) -ifeq ($(NO_SECURE),true) - -# You can't build secure targets if you don't have OpenSSL. - -$(BINDIR)/$(CONFIG)/thread_pool_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.0.0+. - -$(BINDIR)/$(CONFIG)/thread_pool_test: protobuf_dep_error - -else - -$(BINDIR)/$(CONFIG)/thread_pool_test: $(PROTOBUF_DEP) $(THREAD_POOL_TEST_OBJS) $(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) $(THREAD_POOL_TEST_OBJS) $(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)/thread_pool_test - -endif - -endif - -$(OBJDIR)/$(CONFIG)/test/cpp/server/thread_pool_test.o: $(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_thread_pool_test: $(THREAD_POOL_TEST_OBJS:.o=.dep) - -ifneq ($(NO_SECURE),true) -ifneq ($(NO_DEPS),true) --include $(THREAD_POOL_TEST_OBJS:.o=.dep) -endif -endif - - THREAD_STRESS_TEST_SRC = \ test/cpp/end2end/thread_stress_test.cc \ diff --git a/README.md b/README.md index f71ee6a6c17..36d9fa07caf 100644 --- a/README.md +++ b/README.md @@ -39,9 +39,9 @@ Libraries in different languages are in different state of development. We are s * Ruby Library: [src/ruby] (src/ruby) : Early adopter ready - Alpha. * NodeJS Library: [src/node] (src/node) : Early adopter ready - Alpha. * Python Library: [src/python] (src/python) : Early adopter ready - Alpha. - * C# Library: [src/csharp] (src/csharp) : Early adopter ready - Alpha. + * C# Library: [src/csharp] (src/csharp) : Early adopter ready - Alpha. + * Objective-C Library: [src/objective-c] (src/objective-c): Early adopter ready - Alpha. * PHP Library: [src/php] (src/php) : Pre-Alpha. - * Objective-C Library: [src/objective-c] (src/objective-c): Pre-Alpha. #Overview diff --git a/build.json b/build.json index 4caa094b4f0..af8bd54460e 100644 --- a/build.json +++ b/build.json @@ -31,6 +31,7 @@ "include/grpc++/async_generic_service.h", "include/grpc++/async_unary_call.h", "include/grpc++/auth_context.h", + "include/grpc++/auth_property_iterator.h", "include/grpc++/byte_buffer.h", "include/grpc++/channel_arguments.h", "include/grpc++/channel_interface.h", @@ -40,6 +41,7 @@ "include/grpc++/config_protobuf.h", "include/grpc++/create_channel.h", "include/grpc++/credentials.h", + "include/grpc++/fixed_size_thread_pool.h", "include/grpc++/generic_stub.h", "include/grpc++/impl/call.h", "include/grpc++/impl/client_unary_call.h", @@ -69,8 +71,7 @@ ], "headers": [ "src/cpp/client/channel.h", - "src/cpp/common/create_auth_context.h", - "src/cpp/server/thread_pool.h" + "src/cpp/common/create_auth_context.h" ], "src": [ "src/cpp/client/channel.cc", @@ -87,12 +88,12 @@ "src/cpp/proto/proto_utils.cc", "src/cpp/server/async_generic_service.cc", "src/cpp/server/create_default_thread_pool.cc", + "src/cpp/server/fixed_size_thread_pool.cc", "src/cpp/server/insecure_server_credentials.cc", "src/cpp/server/server.cc", "src/cpp/server/server_builder.cc", "src/cpp/server/server_context.cc", "src/cpp/server/server_credentials.cc", - "src/cpp/server/thread_pool.cc", "src/cpp/util/byte_buffer.cc", "src/cpp/util/slice.cc", "src/cpp/util/status.cc", @@ -287,6 +288,7 @@ "src/core/surface/server_chttp2.c", "src/core/surface/server_create.c", "src/core/surface/surface_trace.c", + "src/core/surface/version.c", "src/core/transport/chttp2/alpn.c", "src/core/transport/chttp2/bin_encoder.c", "src/core/transport/chttp2/frame_data.c", @@ -377,6 +379,7 @@ "src/core/support/env.h", "src/core/support/file.h", "src/core/support/murmur_hash.h", + "src/core/support/stack_lockfree.h", "src/core/support/string.h", "src/core/support/string_win32.h", "src/core/support/thd_internal.h" @@ -405,6 +408,7 @@ "src/core/support/murmur_hash.c", "src/core/support/slice.c", "src/core/support/slice_buffer.c", + "src/core/support/stack_lockfree.c", "src/core/support/string.c", "src/core/support/string_posix.c", "src/core/support/string_win32.c", @@ -569,6 +573,7 @@ "src": [ "src/cpp/client/secure_channel_arguments.cc", "src/cpp/client/secure_credentials.cc", + "src/cpp/common/auth_property_iterator.cc", "src/cpp/common/secure_auth_context.cc", "src/cpp/common/secure_create_auth_context.cc", "src/cpp/server/secure_server_credentials.cc" @@ -1170,6 +1175,18 @@ "gpr" ] }, + { + "name": "gpr_stack_lockfree_test", + "build": "test", + "language": "c", + "src": [ + "test/core/support/stack_lockfree_test.c" + ], + "deps": [ + "gpr_test_util", + "gpr" + ] + }, { "name": "gpr_string_test", "build": "test", @@ -1772,20 +1789,6 @@ "gpr" ] }, - { - "name": "time_test", - "build": "test", - "language": "c", - "src": [ - "test/core/support/time_test.c" - ], - "deps": [ - "grpc_test_util", - "grpc", - "gpr_test_util", - "gpr" - ] - }, { "name": "timeout_encoding_test", "build": "test", @@ -1906,6 +1909,19 @@ "gpr" ] }, + { + "name": "auth_property_iterator_test", + "build": "test", + "language": "c++", + "src": [ + "test/cpp/common/auth_property_iterator_test.cc" + ], + "deps": [ + "grpc++", + "grpc", + "gpr" + ] + }, { "name": "channel_arguments_test", "build": "test", @@ -2042,6 +2058,21 @@ "gpr" ] }, + { + "name": "fixed_size_thread_pool_test", + "build": "test", + "language": "c++", + "src": [ + "test/cpp/server/fixed_size_thread_pool_test.cc" + ], + "deps": [ + "grpc_test_util", + "grpc++", + "grpc", + "gpr_test_util", + "gpr" + ] + }, { "name": "generic_end2end_test", "build": "test", @@ -2296,11 +2327,11 @@ ] }, { - "name": "qps_test", + "name": "qps_openloop_test", "build": "test", "language": "c++", "src": [ - "test/cpp/qps/qps_test.cc" + "test/cpp/qps/qps_openloop_test.cc" ], "deps": [ "qps", @@ -2314,11 +2345,11 @@ ] }, { - "name": "qps_test_openloop", + "name": "qps_test", "build": "test", "language": "c++", "src": [ - "test/cpp/qps/qps_test_openloop.cc" + "test/cpp/qps/qps_test.cc" ], "deps": [ "qps", @@ -2448,21 +2479,6 @@ "gpr" ] }, - { - "name": "thread_pool_test", - "build": "test", - "language": "c++", - "src": [ - "test/cpp/server/thread_pool_test.cc" - ], - "deps": [ - "grpc_test_util", - "grpc++", - "grpc", - "gpr_test_util", - "gpr" - ] - }, { "name": "thread_stress_test", "build": "test", diff --git a/doc/connection-backoff.md b/doc/connection-backoff.md index 47b71f927bf..7094e737c51 100644 --- a/doc/connection-backoff.md +++ b/doc/connection-backoff.md @@ -8,58 +8,39 @@ requests) and instead do some form of exponential backoff. We have several parameters: 1. INITIAL_BACKOFF (how long to wait after the first failure before retrying) 2. MULTIPLIER (factor with which to multiply backoff after a failed retry) - 3. MAX_BACKOFF (Upper bound on backoff) - 4. MIN_CONNECTION_TIMEOUT + 3. MAX_BACKOFF (upper bound on backoff) + 4. MIN_CONNECT_TIMEOUT (minimum time we're willing to give a connection to + complete) ## Proposed Backoff Algorithm Exponentially back off the start time of connection attempts up to a limit of -MAX_BACKOFF. +MAX_BACKOFF, with jitter. ``` ConnectWithBackoff() current_backoff = INITIAL_BACKOFF current_deadline = now() + INITIAL_BACKOFF - while (TryConnect(Max(current_deadline, MIN_CONNECT_TIMEOUT)) + while (TryConnect(Max(current_deadline, now() + MIN_CONNECT_TIMEOUT)) != SUCCESS) SleepUntil(current_deadline) current_backoff = Min(current_backoff * MULTIPLIER, MAX_BACKOFF) - current_deadline = now() + current_backoff -``` - -## Historical Algorithm in Stubby - -Exponentially increase up to a limit of MAX_BACKOFF the intervals between -connection attempts. This is what stubby 2 uses, and is equivalent if -TryConnect() fails instantly. + current_deadline = now() + current_backoff + + UniformRandom(-JITTER * current_backoff, JITTER * current_backoff) ``` -LegacyConnectWithBackoff() - current_backoff = INITIAL_BACKOFF - while (TryConnect(MIN_CONNECT_TIMEOUT) != SUCCESS) - SleepFor(current_backoff) - current_backoff = Min(current_backoff * MULTIPLIER, MAX_BACKOFF) -``` - -The grpc C implementation currently uses this approach with an initial backoff -of 1 second, multiplier of 2, and maximum backoff of 120 seconds. (This will -change) -Stubby, or at least rpc2, uses exactly this algorithm with an initial backoff -of 1 second, multiplier of 1.2, and a maximum backoff of 120 seconds. +With specific parameters of +MIN_CONNECT_TIMEOUT = 20 seconds +INITIAL_BACKOFF = 1 second +MULTIPLIER = 1.6 +MAX_BACKOFF = 120 seconds +JITTER = 0.2 -## Use Cases to Consider +Implementations with pressing concerns (such as minimizing the number of wakeups +on a mobile phone) may wish to use a different algorithm, and in particular +different jitter logic. -* Client tries to connect to a server which is down for multiple hours, eg for - maintenance -* Client tries to connect to a server which is overloaded -* User is bringing up both a client and a server at the same time - * In particular, we would like to avoid a large unnecessary delay if the - client connects to a server which is about to come up -* Client/server are misconfigured such that connection attempts always fail - * We want to make sure these don’t put too much load on the server by - default. -* Server is overloaded and wants to transiently make clients back off -* Application has out of band reason to believe a server is back - * We should consider an out of band mechanism for the client to hint that - we should short circuit the backoff. +Alternate implementations must ensure that connection backoffs started at the +same time disperse, and must not attempt connections substantially more often +than the above algorithm. diff --git a/doc/interop-test-descriptions.md b/doc/interop-test-descriptions.md index c1b3394596b..3ee5d0f0320 100644 --- a/doc/interop-test-descriptions.md +++ b/doc/interop-test-descriptions.md @@ -396,14 +396,23 @@ Asserts: Similar to the other auth tests, this test is only for cloud-to-prod path. -This test verifies unary calls succeed in sending messages using an OAuth2 token that is obtained OOB. For the purpose of the test, the OAuth2 token is actually obtained from the service account credentials via the language-specific authorization library. +This test verifies unary calls succeed in sending messages using an OAuth2 token +that is obtained out of band. For the purpose of the test, the OAuth2 token is +actually obtained from the service account credentials via the +language-specific authorization library. -The difference between this test and the other auth tests is that rather than configuring the test client with ServiceAccountCredentials directly, the test first uses the authorization library to obtain an authorization token. +The difference between this test and the other auth tests is that rather than +configuring the test client with ServiceAccountCredentials directly, the test +first uses the authorization library to obtain an authorization token. The test -- uses the flag`--service_account_key_file` with the path to a json key file -downloaded from https://console.developers.google.com. Alternately, if using a usable auth implementation, it may specify the file location in the environment variable GOOGLE_APPLICATION_CREDENTIALS -- uses the flag `--oauth_scope` for the oauth scope. For testing against grpc-test.sandbox.google.com, "https://www.googleapis.com/auth/xapi.zoo" should be passed as the `--oauth_scope`. +- uses the flag `--service_account_key_file` with the path to a json key file +downloaded from https://console.developers.google.com. Alternately, if using a +usable auth implementation, it may specify the file location in the environment +variable GOOGLE_APPLICATION_CREDENTIALS +- uses the flag `--oauth_scope` for the oauth scope. For testing against +grpc-test.sandbox.google.com, "https://www.googleapis.com/auth/xapi.zoo" should +be passed as the `--oauth_scope`. Server features: * [UnaryCall][] @@ -412,16 +421,12 @@ Server features: * [Echo OAuth Scope][] Procedure: - 1. Client use the auth library to obtain an authorization token - 2. Client calls UnaryCall, attaching the authorization token obtained in step1, with the following message + 1. Client uses the auth library to obtain an authorization token + 2. Client configures the channel to use AccessTokenCredentials with the access token obtained in step 1. + 3. Client calls UnaryCall with the following message ``` { - response_type: COMPRESSABLE - response_size: 314159 - payload:{ - body: 271828 bytes of zeros - } fill_username: true fill_oauth_scope: true } @@ -429,11 +434,53 @@ Procedure: Asserts: * call was successful -* received SimpleResponse.username is in the json key file used by the auth library to obtain the authorization token +* received SimpleResponse.username is in the json key file used by the auth +library to obtain the authorization token +* received SimpleResponse.oauth_scope is in `--oauth_scope` + +### per_rpc_creds + +Similar to the other auth tests, this test is only for cloud-to-prod path. + +This test verifies unary calls succeed in sending messages using an OAuth2 token +that is obtained out of band. For the purpose of the test, the OAuth2 token is +actually obtained from the service account credentials via the +language-specific authorization library. + +The test +- uses the flag `--service_account_key_file` with the path to a json key file +downloaded from https://console.developers.google.com. Alternately, if using a +usable auth implementation, it may specify the file location in the environment +variable GOOGLE_APPLICATION_CREDENTIALS +- uses the flag `--oauth_scope` for the oauth scope. For testing against +grpc-test.sandbox.google.com, "https://www.googleapis.com/auth/xapi.zoo" should +be passed as the `--oauth_scope`. + +Server features: +* [UnaryCall][] +* [Compressable Payload][] +* [Echo Authenticated Username][] +* [Echo OAuth Scope][] + +Procedure: + 1. Client uses the auth library to obtain an authorization token + 2. Client configures the channel with just SSL credentials. + 3. Client calls UnaryCall, setting per-call credentials to + AccessTokenCredentials with the access token obtained in step 1. The request is + the following message + + ``` + { + fill_username: true + fill_oauth_scope: true + } + ``` + +Asserts: +* call was successful +* received SimpleResponse.username is in the json key file used by the auth +library to obtain the authorization token * received SimpleResponse.oauth_scope is in `--oauth_scope` -* response payload body is 314159 bytes in size -* clients are free to assert that the response payload body contents are zero - and comparing the entire response message against a golden response ### Metadata (TODO: fix name) diff --git a/gRPC.podspec b/gRPC.podspec index 921a4ab096f..34fbccf4e7b 100644 --- a/gRPC.podspec +++ b/gRPC.podspec @@ -64,6 +64,7 @@ Pod::Spec.new do |s| ss.source_files = 'src/core/support/env.h', 'src/core/support/file.h', 'src/core/support/murmur_hash.h', + 'src/core/support/stack_lockfree.h', 'src/core/support/grpc_string.h', 'src/core/support/string_win32.h', 'src/core/support/thd_internal.h', @@ -118,6 +119,7 @@ Pod::Spec.new do |s| 'src/core/support/murmur_hash.c', 'src/core/support/slice.c', 'src/core/support/slice_buffer.c', + 'src/core/support/stack_lockfree.c', 'src/core/support/string.c', 'src/core/support/string_posix.c', 'src/core/support/string_win32.c', @@ -357,6 +359,7 @@ Pod::Spec.new do |s| 'src/core/surface/server_chttp2.c', 'src/core/surface/server_create.c', 'src/core/surface/surface_trace.c', + 'src/core/surface/version.c', 'src/core/transport/chttp2/alpn.c', 'src/core/transport/chttp2/bin_encoder.c', 'src/core/transport/chttp2/frame_data.c', @@ -389,6 +392,7 @@ Pod::Spec.new do |s| ss.private_header_files = 'src/core/support/env.h', 'src/core/support/file.h', 'src/core/support/murmur_hash.h', + 'src/core/support/stack_lockfree.h', 'src/core/support/string.h', 'src/core/support/string_win32.h', 'src/core/support/thd_internal.h', diff --git a/include/grpc++/auth_context.h b/include/grpc++/auth_context.h index 158f8e3f078..c42105b927c 100644 --- a/include/grpc++/auth_context.h +++ b/include/grpc++/auth_context.h @@ -36,14 +36,13 @@ #include +#include #include namespace grpc { class AuthContext { public: - typedef std::pair Property; - virtual ~AuthContext() {} // A peer identity, in general is one or more properties (in which case they @@ -54,6 +53,10 @@ class AuthContext { // Returns all the property values with the given name. virtual std::vector FindPropertyValues( const grpc::string& name) const = 0; + + // Iteration over all the properties. + virtual AuthPropertyIterator begin() const = 0; + virtual AuthPropertyIterator end() const = 0; }; } // namespace grpc diff --git a/include/grpc++/auth_property_iterator.h b/include/grpc++/auth_property_iterator.h new file mode 100644 index 00000000000..c7870c46be1 --- /dev/null +++ b/include/grpc++/auth_property_iterator.h @@ -0,0 +1,77 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPCXX_AUTH_PROPERTY_ITERATOR_H +#define GRPCXX_AUTH_PROPERTY_ITERATOR_H + +#include +#include + +#include + +struct grpc_auth_context; +struct grpc_auth_property; +struct grpc_auth_property_iterator; + +namespace grpc { +class SecureAuthContext; + +typedef std::pair AuthProperty; + +class AuthPropertyIterator + : public std::iterator { + public: + ~AuthPropertyIterator(); + AuthPropertyIterator& operator++(); + AuthPropertyIterator operator++(int); + bool operator==(const AuthPropertyIterator& rhs) const; + bool operator!=(const AuthPropertyIterator& rhs) const; + const AuthProperty operator*(); + + protected: + AuthPropertyIterator(); + AuthPropertyIterator(const grpc_auth_property* property, + const grpc_auth_property_iterator* iter); + private: + friend class SecureAuthContext; + const grpc_auth_property* property_; + // The following items form a grpc_auth_property_iterator. + const grpc_auth_context* ctx_; + size_t index_; + const char* name_; +}; + +} // namespace grpc + + #endif // GRPCXX_AUTH_PROPERTY_ITERATOR_H + diff --git a/include/grpc++/completion_queue.h b/include/grpc++/completion_queue.h index f32cbff06cc..0523ab6a0e3 100644 --- a/include/grpc++/completion_queue.h +++ b/include/grpc++/completion_queue.h @@ -105,7 +105,8 @@ class CompletionQueue : public GrpcLibrary { // Returns false if the queue is ready for destruction, true if event bool Next(void** tag, bool* ok) { - return (AsyncNextInternal(tag, ok, gpr_inf_future) != SHUTDOWN); + return (AsyncNextInternal(tag, ok, gpr_inf_future(GPR_CLOCK_REALTIME)) != + SHUTDOWN); } // Shutdown has to be called, and the CompletionQueue can only be diff --git a/src/cpp/server/thread_pool.h b/include/grpc++/fixed_size_thread_pool.h similarity index 83% rename from src/cpp/server/thread_pool.h rename to include/grpc++/fixed_size_thread_pool.h index 3b70249bf9a..307e1661427 100644 --- a/src/cpp/server/thread_pool.h +++ b/include/grpc++/fixed_size_thread_pool.h @@ -31,8 +31,8 @@ * */ -#ifndef GRPC_INTERNAL_CPP_SERVER_THREAD_POOL_H -#define GRPC_INTERNAL_CPP_SERVER_THREAD_POOL_H +#ifndef GRPCXX_FIXED_SIZE_THREAD_POOL_H +#define GRPCXX_FIXED_SIZE_THREAD_POOL_H #include @@ -45,12 +45,12 @@ namespace grpc { -class ThreadPool GRPC_FINAL : public ThreadPoolInterface { +class FixedSizeThreadPool GRPC_FINAL : public ThreadPoolInterface { public: - explicit ThreadPool(int num_threads); - ~ThreadPool(); + explicit FixedSizeThreadPool(int num_threads); + ~FixedSizeThreadPool(); - void ScheduleCallback(const std::function& callback) GRPC_OVERRIDE; + void Add(const std::function& callback) GRPC_OVERRIDE; private: grpc::mutex mu_; @@ -62,8 +62,6 @@ class ThreadPool GRPC_FINAL : public ThreadPoolInterface { void ThreadFunc(); }; -ThreadPoolInterface* CreateDefaultThreadPool(); - } // namespace grpc -#endif // GRPC_INTERNAL_CPP_SERVER_THREAD_POOL_H +#endif // GRPCXX_FIXED_SIZE_THREAD_POOL_H diff --git a/include/grpc++/impl/call.h b/include/grpc++/impl/call.h index 64fa5d6efbe..1fa44907797 100644 --- a/include/grpc++/impl/call.h +++ b/include/grpc++/impl/call.h @@ -60,6 +60,93 @@ void FillMetadataMap(grpc_metadata_array* arr, grpc_metadata* FillMetadataArray( const std::multimap& metadata); +/// Per-message write options. +class WriteOptions { + public: + WriteOptions() : flags_(0) {} + WriteOptions(const WriteOptions& other) : flags_(other.flags_) {} + + /// Clear all flags. + inline void Clear() { + flags_ = 0; + } + + /// Returns raw flags bitset. + inline gpr_uint32 flags() const { + return flags_; + } + + /// Sets flag for the disabling of compression for the next message write. + /// + /// \sa GRPC_WRITE_NO_COMPRESS + inline WriteOptions& set_no_compression() { + SetBit(GRPC_WRITE_NO_COMPRESS); + return *this; + } + + /// Clears flag for the disabling of compression for the next message write. + /// + /// \sa GRPC_WRITE_NO_COMPRESS + inline WriteOptions& clear_no_compression() { + ClearBit(GRPC_WRITE_NO_COMPRESS); + return *this; + } + + /// Get value for the flag indicating whether compression for the next + /// message write is forcefully disabled. + /// + /// \sa GRPC_WRITE_NO_COMPRESS + inline bool get_no_compression() const { + return GetBit(GRPC_WRITE_NO_COMPRESS); + } + + /// Sets flag indicating that the write may be buffered and need not go out on + /// the wire immediately. + /// + /// \sa GRPC_WRITE_BUFFER_HINT + inline WriteOptions& set_buffer_hint() { + SetBit(GRPC_WRITE_BUFFER_HINT); + return *this; + } + + /// Clears flag indicating that the write may be buffered and need not go out + /// on the wire immediately. + /// + /// \sa GRPC_WRITE_BUFFER_HINT + inline WriteOptions& clear_buffer_hint() { + ClearBit(GRPC_WRITE_BUFFER_HINT); + return *this; + } + + /// Get value for the flag indicating that the write may be buffered and need + /// not go out on the wire immediately. + /// + /// \sa GRPC_WRITE_BUFFER_HINT + inline bool get_buffer_hint() const { + return GetBit(GRPC_WRITE_BUFFER_HINT); + } + + WriteOptions& operator=(const WriteOptions& rhs) { + flags_ = rhs.flags_; + return *this; + } + + private: + void SetBit(const gpr_int32 mask) { + flags_ |= mask; + } + + void ClearBit(const gpr_int32 mask) { + flags_ &= ~mask; + } + + bool GetBit(const gpr_int32 mask) const { + return flags_ & mask; + } + + gpr_uint32 flags_; +}; + /// Default argument for CallOpSet. I is unused by the class, but can be /// used for generating multiple names for the same thing. template @@ -104,6 +191,12 @@ class CallOpSendMessage { public: CallOpSendMessage() : send_buf_(nullptr), own_buf_(false) {} + /// Send \a message using \a options for the write. The \a options are cleared + /// after use. + template + Status SendMessage(const M& message, + const WriteOptions& options) GRPC_MUST_USE_RESULT; + template Status SendMessage(const M& message) GRPC_MUST_USE_RESULT; @@ -112,8 +205,10 @@ class CallOpSendMessage { if (send_buf_ == nullptr) return; grpc_op* op = &ops[(*nops)++]; op->op = GRPC_OP_SEND_MESSAGE; - op->flags = 0; + op->flags = write_options_.flags(); op->data.send_message = send_buf_; + // Flags are per-message: clear them after use. + write_options_.Clear(); } void FinishOp(bool* status, int max_message_size) { if (own_buf_) grpc_byte_buffer_destroy(send_buf_); @@ -122,14 +217,22 @@ class CallOpSendMessage { private: grpc_byte_buffer* send_buf_; + WriteOptions write_options_; bool own_buf_; }; template -Status CallOpSendMessage::SendMessage(const M& message) { +Status CallOpSendMessage::SendMessage(const M& message, + const WriteOptions& options) { + write_options_ = options; return SerializationTraits::Serialize(message, &send_buf_, &own_buf_); } +template +Status CallOpSendMessage::SendMessage(const M& message) { + return SendMessage(message, WriteOptions()); +} + template class CallOpRecvMessage { public: @@ -172,17 +275,34 @@ class CallOpRecvMessage { grpc_byte_buffer* recv_buf_; }; +namespace CallOpGenericRecvMessageHelper { +class DeserializeFunc { + public: + virtual Status Deserialize(grpc_byte_buffer* buf, int max_message_size) = 0; +}; + +template +class DeserializeFuncType GRPC_FINAL : public DeserializeFunc { + public: + DeserializeFuncType(R* message) : message_(message) {} + Status Deserialize(grpc_byte_buffer* buf, + int max_message_size) GRPC_OVERRIDE { + return SerializationTraits::Deserialize(buf, message_, max_message_size); + } + + private: + R* message_; // Not a managed pointer because management is external to this +}; +} // namespace CallOpGenericRecvMessageHelper + class CallOpGenericRecvMessage { public: CallOpGenericRecvMessage() : got_message(false) {} template void RecvMessage(R* message) { - deserialize_ = [message](grpc_byte_buffer* buf, - int max_message_size) -> Status { - return SerializationTraits::Deserialize(buf, message, - max_message_size); - }; + deserialize_.reset( + new CallOpGenericRecvMessageHelper::DeserializeFuncType(message)); } bool got_message; @@ -201,7 +321,7 @@ class CallOpGenericRecvMessage { if (recv_buf_) { if (*status) { got_message = true; - *status = deserialize_(recv_buf_, max_message_size).ok(); + *status = deserialize_->Deserialize(recv_buf_, max_message_size).ok(); } else { got_message = false; grpc_byte_buffer_destroy(recv_buf_); @@ -210,12 +330,11 @@ class CallOpGenericRecvMessage { got_message = false; *status = false; } - deserialize_ = DeserializeFunc(); + deserialize_.reset(); } private: - typedef std::function DeserializeFunc; - DeserializeFunc deserialize_; + std::unique_ptr deserialize_; grpc_byte_buffer* recv_buf_; }; diff --git a/include/grpc++/server_context.h b/include/grpc++/server_context.h index a4ee986df17..6f094eda3d3 100644 --- a/include/grpc++/server_context.h +++ b/include/grpc++/server_context.h @@ -76,6 +76,10 @@ class CallOpBuffer; class CompletionQueue; class Server; +namespace testing { +class InteropContextInspector; +} // namespace testing + // Interface of server side rpc context. class ServerContext { public: @@ -93,17 +97,16 @@ class ServerContext { void AddInitialMetadata(const grpc::string& key, const grpc::string& value); void AddTrailingMetadata(const grpc::string& key, const grpc::string& value); - bool IsCancelled(); + bool IsCancelled() const; const std::multimap& client_metadata() { return client_metadata_; } - std::shared_ptr auth_context() const { - return auth_context_; - } + std::shared_ptr auth_context() const; private: + friend class ::grpc::testing::InteropContextInspector; friend class ::grpc::Server; template friend class ::grpc::ServerAsyncReader; @@ -147,7 +150,7 @@ class ServerContext { grpc_call* call_; CompletionQueue* cq_; bool sent_initial_metadata_; - std::shared_ptr auth_context_; + mutable std::shared_ptr auth_context_; std::multimap client_metadata_; std::multimap initial_metadata_; std::multimap trailing_metadata_; diff --git a/include/grpc++/stream.h b/include/grpc++/stream.h index dd5e52d6d3d..3903f2ec06c 100644 --- a/include/grpc++/stream.h +++ b/include/grpc++/stream.h @@ -79,7 +79,11 @@ class WriterInterface { // Blocking write msg to the stream. Returns true on success. // Returns false when the stream has been closed. - virtual bool Write(const W& msg) = 0; + virtual bool Write(const W& msg, const WriteOptions& options) = 0; + + inline bool Write(const W& msg) { + return Write(msg, WriteOptions()); + } }; template @@ -168,9 +172,10 @@ class ClientWriter : public ClientWriterInterface { cq_.Pluck(&ops); } - bool Write(const W& msg) GRPC_OVERRIDE { + using WriterInterface::Write; + bool Write(const W& msg, const WriteOptions& options) GRPC_OVERRIDE { CallOpSet ops; - if (!ops.SendMessage(msg).ok()) { + if (!ops.SendMessage(msg, options).ok()) { return false; } call_.PerformOps(&ops); @@ -246,9 +251,10 @@ class ClientReaderWriter GRPC_FINAL : public ClientReaderWriterInterface { return cq_.Pluck(&ops) && ops.got_message; } - bool Write(const W& msg) GRPC_OVERRIDE { + using WriterInterface::Write; + bool Write(const W& msg, const WriteOptions& options) GRPC_OVERRIDE { CallOpSet ops; - if (!ops.SendMessage(msg).ok()) return false; + if (!ops.SendMessage(msg, options).ok()) return false; call_.PerformOps(&ops); return cq_.Pluck(&ops); } @@ -317,9 +323,10 @@ class ServerWriter GRPC_FINAL : public WriterInterface { call_->cq()->Pluck(&ops); } - bool Write(const W& msg) GRPC_OVERRIDE { + using WriterInterface::Write; + bool Write(const W& msg, const WriteOptions& options) GRPC_OVERRIDE { CallOpSet ops; - if (!ops.SendMessage(msg).ok()) { + if (!ops.SendMessage(msg, options).ok()) { return false; } if (!ctx_->sent_initial_metadata_) { @@ -359,9 +366,10 @@ class ServerReaderWriter GRPC_FINAL : public WriterInterface, return call_->cq()->Pluck(&ops) && ops.got_message; } - bool Write(const W& msg) GRPC_OVERRIDE { + using WriterInterface::Write; + bool Write(const W& msg, const WriteOptions& options) GRPC_OVERRIDE { CallOpSet ops; - if (!ops.SendMessage(msg).ok()) { + if (!ops.SendMessage(msg, options).ok()) { return false; } if (!ctx_->sent_initial_metadata_) { diff --git a/include/grpc++/thread_pool_interface.h b/include/grpc++/thread_pool_interface.h index ead307f6a2f..d080b31dcc5 100644 --- a/include/grpc++/thread_pool_interface.h +++ b/include/grpc++/thread_pool_interface.h @@ -44,9 +44,11 @@ class ThreadPoolInterface { virtual ~ThreadPoolInterface() {} // Schedule the given callback for execution. - virtual void ScheduleCallback(const std::function& callback) = 0; + virtual void Add(const std::function& callback) = 0; }; +ThreadPoolInterface* CreateDefaultThreadPool(); + } // namespace grpc #endif // GRPCXX_THREAD_POOL_INTERFACE_H diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h index 782923d599e..3c72c1db27a 100644 --- a/include/grpc/grpc.h +++ b/include/grpc/grpc.h @@ -351,6 +351,9 @@ void grpc_init(void); destroyed. */ void grpc_shutdown(void); +/** Return a string representing the current version of grpc */ +const char *grpc_version_string(void); + /** Create a completion queue */ grpc_completion_queue *grpc_completion_queue_create(void); diff --git a/include/grpc/support/port_platform.h b/include/grpc/support/port_platform.h index d3cfee113d1..a5d1b627028 100644 --- a/include/grpc/support/port_platform.h +++ b/include/grpc/support/port_platform.h @@ -82,6 +82,7 @@ #define GPR_WIN32_ATOMIC 1 #define GPR_MSVC_TLS 1 #endif +#define GPR_WINDOWS_CRASH_HANDLER 1 #elif defined(_WIN32) || defined(WIN32) #define GPR_ARCH_32 1 #define GPR_WIN32 1 @@ -94,6 +95,7 @@ #define GPR_WIN32_ATOMIC 1 #define GPR_MSVC_TLS 1 #endif +#define GPR_WINDOWS_CRASH_HANDLER 1 #elif defined(ANDROID) || defined(__ANDROID__) #define GPR_ANDROID 1 #define GPR_ARCH_32 1 diff --git a/include/grpc/support/time.h b/include/grpc/support/time.h index 1fd3181859f..3f375f6ecd5 100644 --- a/include/grpc/support/time.h +++ b/include/grpc/support/time.h @@ -45,15 +45,30 @@ extern "C" { #endif +/* The clocks we support. */ +typedef enum { + /* Monotonic clock. Epoch undefined. Always moves forwards. */ + GPR_CLOCK_MONOTONIC = 0, + /* Realtime clock. May jump forwards or backwards. Settable by + the system administrator. Has its epoch at 0:00:00 UTC 1 Jan 1970. */ + GPR_CLOCK_REALTIME, + /* Unmeasurable clock type: no base, created by taking the difference + between two times */ + GPR_TIMESPAN +} gpr_clock_type; + typedef struct gpr_timespec { - time_t tv_sec; - int tv_nsec; + time_t tv_sec; + int tv_nsec; + /** Against which clock was this time measured? (or GPR_TIMESPAN if + this is a relative time meaure) */ + gpr_clock_type clock_type; } gpr_timespec; /* Time constants. */ -extern const gpr_timespec gpr_time_0; /* The zero time interval. */ -extern const gpr_timespec gpr_inf_future; /* The far future */ -extern const gpr_timespec gpr_inf_past; /* The far past. */ +gpr_timespec gpr_time_0(gpr_clock_type type); /* The zero time interval. */ +gpr_timespec gpr_inf_future(gpr_clock_type type); /* The far future */ +gpr_timespec gpr_inf_past(gpr_clock_type type); /* The far past. */ #define GPR_MS_PER_SEC 1000 #define GPR_US_PER_SEC 1000000 @@ -62,8 +77,11 @@ extern const gpr_timespec gpr_inf_past; /* The far past. */ #define GPR_NS_PER_US 1000 #define GPR_US_PER_MS 1000 -/* Return the current time measured from the system's default epoch. */ -gpr_timespec gpr_now(void); +/* initialize time subsystem */ +void gpr_time_init(void); + +/* Return the current time measured from the given clocks epoch. */ +gpr_timespec gpr_now(gpr_clock_type clock); /* Return -ve, 0, or +ve according to whether a < b, a == b, or a > b respectively. */ @@ -78,12 +96,12 @@ gpr_timespec gpr_time_sub(gpr_timespec a, gpr_timespec b); /* Return a timespec representing a given number of time units. LONG_MIN is interpreted as gpr_inf_past, and LONG_MAX as gpr_inf_future. */ -gpr_timespec gpr_time_from_micros(long x); -gpr_timespec gpr_time_from_nanos(long x); -gpr_timespec gpr_time_from_millis(long x); -gpr_timespec gpr_time_from_seconds(long x); -gpr_timespec gpr_time_from_minutes(long x); -gpr_timespec gpr_time_from_hours(long x); +gpr_timespec gpr_time_from_micros(long x, gpr_clock_type clock_type); +gpr_timespec gpr_time_from_nanos(long x, gpr_clock_type clock_type); +gpr_timespec gpr_time_from_millis(long x, gpr_clock_type clock_type); +gpr_timespec gpr_time_from_seconds(long x, gpr_clock_type clock_type); +gpr_timespec gpr_time_from_minutes(long x, gpr_clock_type clock_type); +gpr_timespec gpr_time_from_hours(long x, gpr_clock_type clock_type); gpr_int32 gpr_time_to_millis(gpr_timespec timespec); @@ -100,4 +118,4 @@ double gpr_timespec_to_micros(gpr_timespec t); } #endif -#endif /* GRPC_SUPPORT_TIME_H */ +#endif /* GRPC_SUPPORT_TIME_H */ diff --git a/include/grpc/support/useful.h b/include/grpc/support/useful.h index e1ce0455c6a..38426115904 100644 --- a/include/grpc/support/useful.h +++ b/include/grpc/support/useful.h @@ -52,4 +52,24 @@ b = x; \ } while (0) +/** Set the \a n-th bit of \a i (a mutable pointer). */ +#define GPR_BITSET(i, n) ((*(i)) |= (1u << (n))) + +/** Clear the \a n-th bit of \a i (a mutable pointer). */ +#define GPR_BITCLEAR(i, n) ((*(i)) &= ~(1u << (n))) + +/** Get the \a n-th bit of \a i */ +#define GPR_BITGET(i, n) (((i) & (1u << (n))) != 0) + +#define GPR_INTERNAL_HEXDIGIT_BITCOUNT(x) \ + ((x) - (((x) >> 1) & 0x77777777) - (((x) >> 2) & 0x33333333) - \ + (((x) >> 3) & 0x11111111)) + +/** Returns number of bits set in bitset \a i */ +#define GPR_BITCOUNT(i) \ + (((GPR_INTERNAL_HEXDIGIT_BITCOUNT(i) + \ + (GPR_INTERNAL_HEXDIGIT_BITCOUNT(i) >> 4)) & \ + 0x0f0f0f0f) % \ + 255) + #endif /* GRPC_SUPPORT_USEFUL_H */ diff --git a/src/compiler/csharp_generator.cc b/src/compiler/csharp_generator.cc index ccb0b688b6b..1910e9bd2de 100644 --- a/src/compiler/csharp_generator.cc +++ b/src/compiler/csharp_generator.cc @@ -257,7 +257,7 @@ void GenerateStaticMethodField(Printer* out, const MethodDescriptor *method) { } void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) { - out->Print("// client-side stub interface\n"); + out->Print("// client interface\n"); out->Print("public interface $name$\n", "name", GetClientInterfaceName(service)); out->Print("{\n"); @@ -269,7 +269,7 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) { if (method_type == METHODTYPE_NO_STREAMING) { // unary calls have an extra synchronous stub method out->Print( - "$response$ $methodname$($request$ request, CancellationToken token = default(CancellationToken));\n", + "$response$ $methodname$($request$ request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken));\n", "methodname", method->name(), "request", GetClassName(method->input_type()), "response", GetClassName(method->output_type())); @@ -280,7 +280,7 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) { method_name += "Async"; // prevent name clash with synchronous method. } out->Print( - "$returntype$ $methodname$($request_maybe$CancellationToken token = default(CancellationToken));\n", + "$returntype$ $methodname$($request_maybe$Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken));\n", "methodname", method_name, "request_maybe", GetMethodRequestParamMaybe(method), "returntype", GetMethodReturnTypeClient(method)); @@ -312,7 +312,7 @@ void GenerateServerInterface(Printer* out, const ServiceDescriptor *service) { void GenerateClientStub(Printer* out, const ServiceDescriptor *service) { out->Print("// client stub\n"); out->Print( - "public class $name$ : AbstractStub<$name$, StubConfiguration>, $interface$\n", + "public class $name$ : ClientBase, $interface$\n", "name", GetClientClassName(service), "interface", GetClientInterfaceName(service)); out->Print("{\n"); @@ -320,12 +320,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) { // constructors out->Print( - "public $name$(Channel channel) : this(channel, StubConfiguration.Default)\n", - "name", GetClientClassName(service)); - out->Print("{\n"); - out->Print("}\n"); - out->Print( - "public $name$(Channel channel, StubConfiguration config) : base(channel, config)\n", + "public $name$(Channel channel) : base(channel)\n", "name", GetClientClassName(service)); out->Print("{\n"); out->Print("}\n"); @@ -337,16 +332,16 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) { if (method_type == METHODTYPE_NO_STREAMING) { // unary calls have an extra synchronous stub method out->Print( - "public $response$ $methodname$($request$ request, CancellationToken token = default(CancellationToken))\n", + "public $response$ $methodname$($request$ request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken))\n", "methodname", method->name(), "request", GetClassName(method->input_type()), "response", GetClassName(method->output_type())); out->Print("{\n"); out->Indent(); - out->Print("var call = CreateCall($servicenamefield$, $methodfield$);\n", + out->Print("var call = CreateCall($servicenamefield$, $methodfield$, headers);\n", "servicenamefield", GetServiceNameFieldName(), "methodfield", GetMethodFieldName(method)); - out->Print("return Calls.BlockingUnaryCall(call, request, token);\n"); + out->Print("return Calls.BlockingUnaryCall(call, request, cancellationToken);\n"); out->Outdent(); out->Print("}\n"); } @@ -356,28 +351,28 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) { method_name += "Async"; // prevent name clash with synchronous method. } out->Print( - "public $returntype$ $methodname$($request_maybe$CancellationToken token = default(CancellationToken))\n", + "public $returntype$ $methodname$($request_maybe$Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken))\n", "methodname", method_name, "request_maybe", GetMethodRequestParamMaybe(method), "returntype", GetMethodReturnTypeClient(method)); out->Print("{\n"); out->Indent(); - out->Print("var call = CreateCall($servicenamefield$, $methodfield$);\n", + out->Print("var call = CreateCall($servicenamefield$, $methodfield$, headers);\n", "servicenamefield", GetServiceNameFieldName(), "methodfield", GetMethodFieldName(method)); switch (GetMethodType(method)) { case METHODTYPE_NO_STREAMING: - out->Print("return Calls.AsyncUnaryCall(call, request, token);\n"); + out->Print("return Calls.AsyncUnaryCall(call, request, cancellationToken);\n"); break; case METHODTYPE_CLIENT_STREAMING: - out->Print("return Calls.AsyncClientStreamingCall(call, token);\n"); + out->Print("return Calls.AsyncClientStreamingCall(call, cancellationToken);\n"); break; case METHODTYPE_SERVER_STREAMING: out->Print( - "return Calls.AsyncServerStreamingCall(call, request, token);\n"); + "return Calls.AsyncServerStreamingCall(call, request, cancellationToken);\n"); break; case METHODTYPE_BIDI_STREAMING: - out->Print("return Calls.AsyncDuplexStreamingCall(call, token);\n"); + out->Print("return Calls.AsyncDuplexStreamingCall(call, cancellationToken);\n"); break; default: GOOGLE_LOG(FATAL)<< "Can't get here."; @@ -423,9 +418,9 @@ void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor *service) { } void GenerateNewStubMethods(Printer* out, const ServiceDescriptor *service) { - out->Print("// creates a new client stub\n"); - out->Print("public static $interface$ NewStub(Channel channel)\n", - "interface", GetClientInterfaceName(service)); + out->Print("// creates a new client\n"); + out->Print("public static $classname$ NewClient(Channel channel)\n", + "classname", GetClientClassName(service)); out->Print("{\n"); out->Indent(); out->Print("return new $classname$(channel);\n", "classname", @@ -433,17 +428,6 @@ void GenerateNewStubMethods(Printer* out, const ServiceDescriptor *service) { out->Outdent(); out->Print("}\n"); out->Print("\n"); - - out->Print("// creates a new client stub\n"); - out->Print( - "public static $interface$ NewStub(Channel channel, StubConfiguration config)\n", - "interface", GetClientInterfaceName(service)); - out->Print("{\n"); - out->Indent(); - out->Print("return new $classname$(channel, config);\n", "classname", - GetClientClassName(service)); - out->Outdent(); - out->Print("}\n"); } void GenerateService(Printer* out, const ServiceDescriptor *service) { diff --git a/src/compiler/objective_c_generator.cc b/src/compiler/objective_c_generator.cc index 79a84b4a7a5..711d0d58709 100644 --- a/src/compiler/objective_c_generator.cc +++ b/src/compiler/objective_c_generator.cc @@ -57,34 +57,34 @@ void PrintProtoRpcDeclarationAsPragma(Printer *printer, vars["server_stream"] = method->server_streaming() ? "stream " : ""; printer->Print(vars, - "#pragma mark $method_name$($client_stream$$request_type$)" - " returns ($server_stream$$response_type$)\n\n"); + "#pragma mark $method_name$($client_stream$$request_type$)" + " returns ($server_stream$$response_type$)\n\n"); } -void PrintMethodSignature(Printer *printer, - const MethodDescriptor *method, - const map& vars) { +void PrintMethodSignature(Printer *printer, const MethodDescriptor *method, + const map &vars) { // TODO(jcanizales): Print method comments. printer->Print(vars, "- ($return_type$)$method_name$With"); if (method->client_streaming()) { - printer->Print("RequestsWriter:(id)requestWriter"); + printer->Print("RequestsWriter:(GRXWriter *)requestWriter"); } else { printer->Print(vars, "Request:($request_class$ *)request"); } // TODO(jcanizales): Put this on a new line and align colons. if (method->server_streaming()) { - printer->Print(vars, " eventHandler:(void(^)(BOOL done, " - "$response_class$ *response, NSError *error))eventHandler"); + printer->Print(vars, + " eventHandler:(void(^)(BOOL done, " + "$response_class$ *response, NSError *error))eventHandler"); } else { - printer->Print(vars, " handler:(void(^)($response_class$ *response, " - "NSError *error))handler"); + printer->Print(vars, + " handler:(void(^)($response_class$ *response, " + "NSError *error))handler"); } } -void PrintSimpleSignature(Printer *printer, - const MethodDescriptor *method, +void PrintSimpleSignature(Printer *printer, const MethodDescriptor *method, map vars) { vars["method_name"] = grpc_generator::LowercaseFirstLetter(vars["method_name"]); @@ -92,8 +92,7 @@ void PrintSimpleSignature(Printer *printer, PrintMethodSignature(printer, method, vars); } -void PrintAdvancedSignature(Printer *printer, - const MethodDescriptor *method, +void PrintAdvancedSignature(Printer *printer, const MethodDescriptor *method, map vars) { vars["method_name"] = "RPCTo" + vars["method_name"]; vars["return_type"] = "ProtoRPC *"; @@ -101,15 +100,16 @@ void PrintAdvancedSignature(Printer *printer, } inline map GetMethodVars(const MethodDescriptor *method) { - return {{ "method_name", method->name() }, - { "request_type", method->input_type()->name() }, - { "response_type", method->output_type()->name() }, - { "request_class", ClassName(method->input_type()) }, - { "response_class", ClassName(method->output_type()) }}; + map res; + res["method_name"] = method->name(); + res["request_type"] = method->input_type()->name(); + res["response_type"] = method->output_type()->name(); + res["request_class"] = ClassName(method->input_type()); + res["response_class"] = ClassName(method->output_type()); + return res; } -void PrintMethodDeclarations(Printer *printer, - const MethodDescriptor *method) { +void PrintMethodDeclarations(Printer *printer, const MethodDescriptor *method) { map vars = GetMethodVars(method); PrintProtoRpcDeclarationAsPragma(printer, method, vars); @@ -120,8 +120,7 @@ void PrintMethodDeclarations(Printer *printer, printer->Print(";\n\n\n"); } -void PrintSimpleImplementation(Printer *printer, - const MethodDescriptor *method, +void PrintSimpleImplementation(Printer *printer, const MethodDescriptor *method, map vars) { printer->Print("{\n"); printer->Print(vars, " [[self RPCTo$method_name$With"); @@ -178,7 +177,7 @@ void PrintMethodImplementations(Printer *printer, PrintAdvancedImplementation(printer, method, vars); } -} // namespace +} // namespace string GetHeader(const ServiceDescriptor *service) { string output; @@ -186,7 +185,7 @@ string GetHeader(const ServiceDescriptor *service) { // Scope the output stream so it closes and finalizes output to the string. grpc::protobuf::io::StringOutputStream output_stream(&output); Printer printer(&output_stream, '$'); - + printer.Print("@protocol GRXWriteable;\n"); printer.Print("@protocol GRXWriter;\n\n"); @@ -199,12 +198,15 @@ string GetHeader(const ServiceDescriptor *service) { } printer.Print("@end\n\n"); - printer.Print("// Basic service implementation, over gRPC, that only does" + printer.Print( + "// Basic service implementation, over gRPC, that only does" " marshalling and parsing.\n"); - printer.Print(vars, "@interface $service_class$ :" - " ProtoService<$service_class$>\n"); - printer.Print("- (instancetype)initWithHost:(NSString *)host" - " NS_DESIGNATED_INITIALIZER;\n"); + printer.Print(vars, + "@interface $service_class$ :" + " ProtoService<$service_class$>\n"); + printer.Print( + "- (instancetype)initWithHost:(NSString *)host" + " NS_DESIGNATED_INITIALIZER;\n"); printer.Print("@end\n"); } return output; @@ -222,18 +224,20 @@ string GetSource(const ServiceDescriptor *service) { {"package", service->file()->package()}}; printer.Print(vars, - "static NSString *const kPackageName = @\"$package$\";\n"); - printer.Print(vars, - "static NSString *const kServiceName = @\"$service_name$\";\n\n"); + "static NSString *const kPackageName = @\"$package$\";\n"); + printer.Print( + vars, "static NSString *const kServiceName = @\"$service_name$\";\n\n"); printer.Print(vars, "@implementation $service_class$\n\n"); - + printer.Print("// Designated initializer\n"); printer.Print("- (instancetype)initWithHost:(NSString *)host {\n"); - printer.Print(" return (self = [super initWithHost:host" + printer.Print( + " return (self = [super initWithHost:host" " packageName:kPackageName serviceName:kServiceName]);\n"); printer.Print("}\n\n"); - printer.Print("// Override superclass initializer to disallow different" + printer.Print( + "// Override superclass initializer to disallow different" " package and service names.\n"); printer.Print("- (instancetype)initWithHost:(NSString *)host\n"); printer.Print(" packageName:(NSString *)packageName\n"); @@ -250,4 +254,4 @@ string GetSource(const ServiceDescriptor *service) { return output; } -} // namespace grpc_objective_c_generator +} // namespace grpc_objective_c_generator diff --git a/src/core/channel/census_filter.c b/src/core/channel/census_filter.c index 43ef5fb3ffb..d996c3475e4 100644 --- a/src/core/channel/census_filter.c +++ b/src/core/channel/census_filter.c @@ -151,7 +151,7 @@ static void client_init_call_elem(grpc_call_element* elem, call_data* d = elem->call_data; GPR_ASSERT(d != NULL); init_rpc_stats(&d->stats); - d->start_ts = gpr_now(); + d->start_ts = gpr_now(GPR_CLOCK_REALTIME); d->op_id = census_tracing_start_op(); if (initial_op) client_mutate_op(elem, initial_op); } @@ -169,7 +169,7 @@ static void server_init_call_elem(grpc_call_element* elem, call_data* d = elem->call_data; GPR_ASSERT(d != NULL); init_rpc_stats(&d->stats); - d->start_ts = gpr_now(); + d->start_ts = gpr_now(GPR_CLOCK_REALTIME); d->op_id = census_tracing_start_op(); if (initial_op) server_mutate_op(elem, initial_op); } @@ -177,8 +177,8 @@ static void server_init_call_elem(grpc_call_element* elem, static void server_destroy_call_elem(grpc_call_element* elem) { call_data* d = elem->call_data; GPR_ASSERT(d != NULL); - d->stats.elapsed_time_ms = - gpr_timespec_to_micros(gpr_time_sub(gpr_now(), d->start_ts)); + d->stats.elapsed_time_ms = gpr_timespec_to_micros( + gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), d->start_ts)); census_record_rpc_server_stats(d->op_id, &d->stats); census_tracing_end_op(d->op_id); } diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c index f890f99237e..10e01ebbb4f 100644 --- a/src/core/channel/client_channel.c +++ b/src/core/channel/client_channel.c @@ -132,7 +132,7 @@ static void handle_op_after_cancellation(grpc_call_element *elem, mdb.list.head = &calld->status; mdb.list.tail = &calld->details; mdb.garbage.head = mdb.garbage.tail = NULL; - mdb.deadline = gpr_inf_future; + mdb.deadline = gpr_inf_future(GPR_CLOCK_REALTIME); grpc_sopb_add_metadata(op->recv_ops, mdb); *op->recv_state = GRPC_STREAM_CLOSED; op->on_done_recv->cb(op->on_done_recv->cb_arg, 1); @@ -518,7 +518,7 @@ static void init_call_elem(grpc_call_element *elem, gpr_mu_init(&calld->mu_state); calld->elem = elem; calld->state = CALL_CREATED; - calld->deadline = gpr_inf_future; + calld->deadline = gpr_inf_future(GPR_CLOCK_REALTIME); } /* Destructor for call_data */ diff --git a/src/core/client_config/lb_policies/pick_first.h b/src/core/client_config/lb_policies/pick_first.h index 94c2a9f0c75..31394985e5e 100644 --- a/src/core/client_config/lb_policies/pick_first.h +++ b/src/core/client_config/lb_policies/pick_first.h @@ -36,6 +36,8 @@ #include "src/core/client_config/lb_policy.h" +/** Returns a load balancing policy instance that picks up the first subchannel + * from \a subchannels to succesfully connect */ grpc_lb_policy *grpc_create_pick_first_lb_policy(grpc_subchannel **subchannels, size_t num_subchannels); diff --git a/src/core/client_config/subchannel.c b/src/core/client_config/subchannel.c index 6cf9062ab0c..35f172683a4 100644 --- a/src/core/client_config/subchannel.c +++ b/src/core/client_config/subchannel.c @@ -300,9 +300,9 @@ static void continue_connect(grpc_subchannel *c) { } static void start_connect(grpc_subchannel *c) { - gpr_timespec now = gpr_now(); + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); c->next_attempt = now; - c->backoff_delta = gpr_time_from_seconds(1); + c->backoff_delta = gpr_time_from_seconds(1, GPR_TIMESPAN); continue_connect(c); } @@ -585,7 +585,7 @@ static void subchannel_connected(void *arg, int iomgr_success) { c->have_alarm = 1; c->next_attempt = gpr_time_add(c->next_attempt, c->backoff_delta); c->backoff_delta = gpr_time_add(c->backoff_delta, c->backoff_delta); - grpc_alarm_init(&c->alarm, c->next_attempt, on_alarm, c, gpr_now()); + grpc_alarm_init(&c->alarm, c->next_attempt, on_alarm, c, gpr_now(GPR_CLOCK_REALTIME)); gpr_mu_unlock(&c->mu); } } diff --git a/src/core/client_config/uri_parser.c b/src/core/client_config/uri_parser.c index 776a2559231..410a61c8cf3 100644 --- a/src/core/client_config/uri_parser.c +++ b/src/core/client_config/uri_parser.c @@ -98,7 +98,7 @@ grpc_uri *grpc_uri_parse(const char *uri_text, int suppress_errors) { if (uri_text[scheme_end + 1] == '/' && uri_text[scheme_end + 2] == '/') { authority_begin = scheme_end + 3; - for (i = authority_begin; uri_text[i] != 0; i++) { + for (i = authority_begin; uri_text[i] != 0 && authority_end == -1; i++) { if (uri_text[i] == '/') { authority_end = i; } diff --git a/src/core/httpcli/httpcli.c b/src/core/httpcli/httpcli.c index 3f5557e08ee..65997d5f44b 100644 --- a/src/core/httpcli/httpcli.c +++ b/src/core/httpcli/httpcli.c @@ -165,6 +165,7 @@ static void start_write(internal_request *req) { static void on_secure_transport_setup_done(void *rp, grpc_security_status status, + grpc_endpoint *wrapped_endpoint, grpc_endpoint *secure_endpoint) { internal_request *req = rp; if (status != GRPC_SECURITY_OK) { diff --git a/src/core/iomgr/alarm.c b/src/core/iomgr/alarm.c index 5860834de3f..5b9a37e5ddc 100644 --- a/src/core/iomgr/alarm.c +++ b/src/core/iomgr/alarm.c @@ -102,7 +102,8 @@ void grpc_alarm_list_init(gpr_timespec now) { void grpc_alarm_list_shutdown(void) { int i; - while (run_some_expired_alarms(NULL, gpr_inf_future, NULL, 0)) + while (run_some_expired_alarms(NULL, gpr_inf_future(GPR_CLOCK_REALTIME), NULL, + 0)) ; for (i = 0; i < NUM_SHARDS; i++) { shard_type *shard = &g_shards[i]; @@ -127,6 +128,7 @@ static gpr_timespec dbl_to_ts(double d) { gpr_timespec ts; ts.tv_sec = d; ts.tv_nsec = 1e9 * (d - ts.tv_sec); + ts.clock_type = GPR_TIMESPAN; return ts; } diff --git a/src/core/iomgr/iocp_windows.c b/src/core/iomgr/iocp_windows.c index 0c62bfccd51..8741241fb8e 100644 --- a/src/core/iomgr/iocp_windows.c +++ b/src/core/iomgr/iocp_windows.c @@ -127,7 +127,6 @@ static void iocp_loop(void *p) { grpc_maybe_call_delayed_callbacks(NULL, 1); do_iocp_work(); } - gpr_log(GPR_DEBUG, "iocp_loop is done"); gpr_event_set(&g_iocp_done, (void *)1); } @@ -158,7 +157,7 @@ void grpc_iocp_shutdown(void) { BOOL success; gpr_event_set(&g_shutdown_iocp, (void *)1); grpc_iocp_kick(); - gpr_event_wait(&g_iocp_done, gpr_inf_future); + gpr_event_wait(&g_iocp_done, gpr_inf_future(GPR_CLOCK_REALTIME)); success = CloseHandle(g_iocp); GPR_ASSERT(success); } diff --git a/src/core/iomgr/iomgr.c b/src/core/iomgr/iomgr.c index 4a2c45a023d..0244f689b1a 100644 --- a/src/core/iomgr/iomgr.c +++ b/src/core/iomgr/iomgr.c @@ -57,9 +57,9 @@ static grpc_iomgr_object g_root_object; static void background_callback_executor(void *ignored) { gpr_mu_lock(&g_mu); while (!g_shutdown) { - gpr_timespec deadline = gpr_inf_future; - gpr_timespec short_deadline = - gpr_time_add(gpr_now(), gpr_time_from_millis(100)); + gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_REALTIME); + gpr_timespec short_deadline = gpr_time_add( + gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(100, GPR_TIMESPAN)); if (g_cbs_head) { grpc_iomgr_closure *closure = g_cbs_head; g_cbs_head = closure->next; @@ -67,7 +67,8 @@ static void background_callback_executor(void *ignored) { gpr_mu_unlock(&g_mu); closure->cb(closure->cb_arg, closure->success); gpr_mu_lock(&g_mu); - } else if (grpc_alarm_check(&g_mu, gpr_now(), &deadline)) { + } else if (grpc_alarm_check(&g_mu, gpr_now(GPR_CLOCK_REALTIME), + &deadline)) { } else { gpr_mu_unlock(&g_mu); gpr_sleep_until(gpr_time_min(short_deadline, deadline)); @@ -89,7 +90,7 @@ void grpc_iomgr_init(void) { gpr_thd_id id; gpr_mu_init(&g_mu); gpr_cv_init(&g_rcv); - grpc_alarm_list_init(gpr_now()); + grpc_alarm_list_init(gpr_now(GPR_CLOCK_REALTIME)); g_root_object.next = g_root_object.prev = &g_root_object; g_root_object.name = "root"; grpc_iomgr_platform_init(); @@ -109,27 +110,28 @@ static size_t count_objects(void) { void grpc_iomgr_shutdown(void) { grpc_iomgr_object *obj; grpc_iomgr_closure *closure; - gpr_timespec shutdown_deadline = - gpr_time_add(gpr_now(), gpr_time_from_seconds(10)); - gpr_timespec last_warning_time = gpr_now(); + gpr_timespec shutdown_deadline = gpr_time_add( + gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_seconds(10, GPR_TIMESPAN)); + gpr_timespec last_warning_time = gpr_now(GPR_CLOCK_REALTIME); gpr_mu_lock(&g_mu); g_shutdown = 1; while (g_cbs_head != NULL || g_root_object.next != &g_root_object) { - if (gpr_time_cmp(gpr_time_sub(gpr_now(), last_warning_time), - gpr_time_from_seconds(1)) >= 0) { - if (g_cbs_head != NULL && g_root_object.next != &g_root_object) { - gpr_log(GPR_DEBUG, - "Waiting for %d iomgr objects to be destroyed and executing " - "final callbacks", - count_objects()); - } else if (g_cbs_head != NULL) { - gpr_log(GPR_DEBUG, "Executing final iomgr callbacks"); - } else { - gpr_log(GPR_DEBUG, "Waiting for %d iomgr objects to be destroyed", - count_objects()); - } - last_warning_time = gpr_now(); + if (gpr_time_cmp( + gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), last_warning_time), + gpr_time_from_seconds(1, GPR_TIMESPAN)) >= 0) { + if (g_cbs_head != NULL && g_root_object.next != &g_root_object) { + gpr_log(GPR_DEBUG, + "Waiting for %d iomgr objects to be destroyed and executing " + "final callbacks", + count_objects()); + } else if (g_cbs_head != NULL) { + gpr_log(GPR_DEBUG, "Executing final iomgr callbacks"); + } else { + gpr_log(GPR_DEBUG, "Waiting for %d iomgr objects to be destroyed", + count_objects()); + } + last_warning_time = gpr_now(GPR_CLOCK_REALTIME); } if (g_cbs_head) { do { @@ -143,16 +145,16 @@ void grpc_iomgr_shutdown(void) { } while (g_cbs_head); continue; } - if (grpc_alarm_check(&g_mu, gpr_inf_future, NULL)) { + if (grpc_alarm_check(&g_mu, gpr_inf_future(GPR_CLOCK_REALTIME), NULL)) { gpr_log(GPR_DEBUG, "got late alarm"); continue; } if (g_root_object.next != &g_root_object) { int timeout = 0; - gpr_timespec short_deadline = - gpr_time_add(gpr_now(), gpr_time_from_millis(100)); + gpr_timespec short_deadline = gpr_time_add( + gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(100, GPR_TIMESPAN)); while (gpr_cv_wait(&g_rcv, &g_mu, short_deadline) && g_cbs_head == NULL) { - if (gpr_time_cmp(gpr_now(), shutdown_deadline) > 0) { + if (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), shutdown_deadline) > 0) { timeout = 1; break; } @@ -172,7 +174,8 @@ void grpc_iomgr_shutdown(void) { gpr_mu_unlock(&g_mu); grpc_kick_poller(); - gpr_event_wait(&g_background_callback_executor_done, gpr_inf_future); + gpr_event_wait(&g_background_callback_executor_done, + gpr_inf_future(GPR_CLOCK_REALTIME)); grpc_alarm_list_shutdown(); diff --git a/src/core/iomgr/pollset_multipoller_with_epoll.c b/src/core/iomgr/pollset_multipoller_with_epoll.c index 1900bbf9e14..d697b59e4c4 100644 --- a/src/core/iomgr/pollset_multipoller_with_epoll.c +++ b/src/core/iomgr/pollset_multipoller_with_epoll.c @@ -50,12 +50,17 @@ typedef struct { } pollset_hdr; static void multipoll_with_epoll_pollset_add_fd(grpc_pollset *pollset, - grpc_fd *fd) { + grpc_fd *fd, + int and_unlock_pollset) { pollset_hdr *h = pollset->data.ptr; struct epoll_event ev; int err; grpc_fd_watcher watcher; + if (and_unlock_pollset) { + gpr_mu_unlock(&pollset->mu); + } + /* We pretend to be polling whilst adding an fd to keep the fd from being closed during the add. This may result in a spurious wakeup being assigned to this pollset whilst adding, but that should be benign. */ @@ -76,9 +81,15 @@ static void multipoll_with_epoll_pollset_add_fd(grpc_pollset *pollset, } static void multipoll_with_epoll_pollset_del_fd(grpc_pollset *pollset, - grpc_fd *fd) { + grpc_fd *fd, + int and_unlock_pollset) { pollset_hdr *h = pollset->data.ptr; int err; + + if (and_unlock_pollset) { + gpr_mu_unlock(&pollset->mu); + } + /* Note that this can race with concurrent poll, but that should be fine since * at worst it creates a spurious read event on a reused grpc_fd object. */ err = epoll_ctl(h->epoll_fd, EPOLL_CTL_DEL, fd->fd, NULL); @@ -105,10 +116,11 @@ static void multipoll_with_epoll_pollset_maybe_work( * here. */ - timeout_ms = grpc_poll_deadline_to_millis_timeout(deadline, now); pollset->counter += 1; gpr_mu_unlock(&pollset->mu); + timeout_ms = grpc_poll_deadline_to_millis_timeout(deadline, now); + do { ep_rv = epoll_wait(h->epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, timeout_ms); if (ep_rv < 0) { @@ -182,7 +194,7 @@ static void epoll_become_multipoller(grpc_pollset *pollset, grpc_fd **fds, abort(); } for (i = 0; i < nfds; i++) { - multipoll_with_epoll_pollset_add_fd(pollset, fds[i]); + multipoll_with_epoll_pollset_add_fd(pollset, fds[i], 0); } grpc_wakeup_fd_create(&h->wakeup_fd); diff --git a/src/core/iomgr/pollset_multipoller_with_poll_posix.c b/src/core/iomgr/pollset_multipoller_with_poll_posix.c index 7b717bd1593..0084e839530 100644 --- a/src/core/iomgr/pollset_multipoller_with_poll_posix.c +++ b/src/core/iomgr/pollset_multipoller_with_poll_posix.c @@ -66,12 +66,13 @@ typedef struct { } pollset_hdr; static void multipoll_with_poll_pollset_add_fd(grpc_pollset *pollset, - grpc_fd *fd) { + grpc_fd *fd, + int and_unlock_pollset) { size_t i; pollset_hdr *h = pollset->data.ptr; /* TODO(ctiller): this is O(num_fds^2); maybe switch to a hash set here */ for (i = 0; i < h->fd_count; i++) { - if (h->fds[i] == fd) return; + if (h->fds[i] == fd) goto exit; } if (h->fd_count == h->fd_capacity) { h->fd_capacity = GPR_MAX(h->fd_capacity + 8, h->fd_count * 3 / 2); @@ -79,10 +80,15 @@ static void multipoll_with_poll_pollset_add_fd(grpc_pollset *pollset, } h->fds[h->fd_count++] = fd; GRPC_FD_REF(fd, "multipoller"); +exit: + if (and_unlock_pollset) { + gpr_mu_unlock(&pollset->mu); + } } static void multipoll_with_poll_pollset_del_fd(grpc_pollset *pollset, - grpc_fd *fd) { + grpc_fd *fd, + int and_unlock_pollset) { /* will get removed next poll cycle */ pollset_hdr *h = pollset->data.ptr; if (h->del_count == h->del_capacity) { @@ -91,6 +97,9 @@ static void multipoll_with_poll_pollset_del_fd(grpc_pollset *pollset, } h->dels[h->del_count++] = fd; GRPC_FD_REF(fd, "multipoller_del"); + if (and_unlock_pollset) { + gpr_mu_unlock(&pollset->mu); + } } static void end_polling(grpc_pollset *pollset) { diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c index 12496440ded..efb301d81cb 100644 --- a/src/core/iomgr/pollset_posix.c +++ b/src/core/iomgr/pollset_posix.c @@ -105,14 +105,28 @@ void grpc_pollset_init(grpc_pollset *pollset) { void grpc_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) { gpr_mu_lock(&pollset->mu); - pollset->vtable->add_fd(pollset, fd); + pollset->vtable->add_fd(pollset, fd, 1); + /* the following (enabled only in debug) will reacquire and then release + our lock - meaning that if the unlocking flag passed to del_fd above is + not respected, the code will deadlock (in a way that we have a chance of + debugging) */ +#ifndef NDEBUG + gpr_mu_lock(&pollset->mu); gpr_mu_unlock(&pollset->mu); +#endif } void grpc_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd) { gpr_mu_lock(&pollset->mu); - pollset->vtable->del_fd(pollset, fd); + pollset->vtable->del_fd(pollset, fd, 1); + /* the following (enabled only in debug) will reacquire and then release + our lock - meaning that if the unlocking flag passed to del_fd above is + not respected, the code will deadlock (in a way that we have a chance of + debugging) */ +#ifndef NDEBUG + gpr_mu_lock(&pollset->mu); gpr_mu_unlock(&pollset->mu); +#endif } static void finish_shutdown(grpc_pollset *pollset) { @@ -122,7 +136,7 @@ static void finish_shutdown(grpc_pollset *pollset) { int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) { /* pollset->mu already held */ - gpr_timespec now = gpr_now(); + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); if (gpr_time_cmp(now, deadline) > 0) { return 0; } @@ -187,20 +201,21 @@ void grpc_pollset_destroy(grpc_pollset *pollset) { gpr_mu_destroy(&pollset->mu); } -int grpc_poll_deadline_to_millis_timeout(gpr_timespec deadline, gpr_timespec now) { +int grpc_poll_deadline_to_millis_timeout(gpr_timespec deadline, + gpr_timespec now) { gpr_timespec timeout; static const int max_spin_polling_us = 10; - if (gpr_time_cmp(deadline, gpr_inf_future) == 0) { + if (gpr_time_cmp(deadline, gpr_inf_future(GPR_CLOCK_REALTIME)) == 0) { return -1; } - if (gpr_time_cmp( - deadline, - gpr_time_add(now, gpr_time_from_micros(max_spin_polling_us))) <= 0) { + if (gpr_time_cmp(deadline, gpr_time_add(now, gpr_time_from_micros( + max_spin_polling_us, + GPR_TIMESPAN))) <= 0) { return 0; } timeout = gpr_time_sub(deadline, now); - return gpr_time_to_millis( - gpr_time_add(timeout, gpr_time_from_nanos(GPR_NS_PER_SEC - 1))); + return gpr_time_to_millis(gpr_time_add( + timeout, gpr_time_from_nanos(GPR_NS_PER_SEC - 1, GPR_TIMESPAN))); } /* @@ -256,7 +271,7 @@ static void basic_do_promote(void *args, int success) { } else if (grpc_fd_is_orphaned(fd)) { /* Don't try to add it to anything, we'll drop our ref on it below */ } else if (pollset->vtable != original_vtable) { - pollset->vtable->add_fd(pollset, fd); + pollset->vtable->add_fd(pollset, fd, 0); } else if (fd != pollset->data.ptr) { grpc_fd *fds[2]; fds[0] = pollset->data.ptr; @@ -286,10 +301,11 @@ static void basic_do_promote(void *args, int success) { GRPC_FD_UNREF(fd, "basicpoll_add"); } -static void basic_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) { +static void basic_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd, + int and_unlock_pollset) { grpc_unary_promote_args *up_args; GPR_ASSERT(fd); - if (fd == pollset->data.ptr) return; + if (fd == pollset->data.ptr) goto exit; if (!pollset->counter) { /* Fast path -- no in flight cbs */ @@ -312,7 +328,7 @@ static void basic_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) { pollset->data.ptr = fd; GRPC_FD_REF(fd, "basicpoll"); } - return; + goto exit; } /* Now we need to promote. This needs to happen when we're not polling. Since @@ -328,14 +344,24 @@ static void basic_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) { grpc_iomgr_add_callback(&up_args->promotion_closure); grpc_pollset_kick(pollset); + +exit: + if (and_unlock_pollset) { + gpr_mu_unlock(&pollset->mu); + } } -static void basic_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd) { +static void basic_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd, + int and_unlock_pollset) { GPR_ASSERT(fd); if (fd == pollset->data.ptr) { GRPC_FD_UNREF(pollset->data.ptr, "basicpoll"); pollset->data.ptr = NULL; } + + if (and_unlock_pollset) { + gpr_mu_unlock(&pollset->mu); + } } static void basic_pollset_maybe_work(grpc_pollset *pollset, diff --git a/src/core/iomgr/pollset_posix.h b/src/core/iomgr/pollset_posix.h index 53585a28865..37de1276d1c 100644 --- a/src/core/iomgr/pollset_posix.h +++ b/src/core/iomgr/pollset_posix.h @@ -66,8 +66,10 @@ typedef struct grpc_pollset { } grpc_pollset; struct grpc_pollset_vtable { - void (*add_fd)(grpc_pollset *pollset, struct grpc_fd *fd); - void (*del_fd)(grpc_pollset *pollset, struct grpc_fd *fd); + void (*add_fd)(grpc_pollset *pollset, struct grpc_fd *fd, + int and_unlock_pollset); + void (*del_fd)(grpc_pollset *pollset, struct grpc_fd *fd, + int and_unlock_pollset); void (*maybe_work)(grpc_pollset *pollset, gpr_timespec deadline, gpr_timespec now, int allow_synchronous_callback); void (*kick)(grpc_pollset *pollset); diff --git a/src/core/iomgr/pollset_set.h b/src/core/iomgr/pollset_set.h index 98e3b552a7a..6d73951c703 100644 --- a/src/core/iomgr/pollset_set.h +++ b/src/core/iomgr/pollset_set.h @@ -38,7 +38,7 @@ /* A grpc_pollset_set is a set of pollsets that are interested in an action. Adding a pollset to a pollset_set automatically adds any - fd's (etc) that have been registered with the set_set with that pollset. + fd's (etc) that have been registered with the set_set to that pollset. Registering fd's automatically adds them to all current pollsets. */ #ifdef GPR_POSIX_SOCKET diff --git a/src/core/iomgr/pollset_windows.c b/src/core/iomgr/pollset_windows.c index 8d6bc79c96c..24226cc9801 100644 --- a/src/core/iomgr/pollset_windows.c +++ b/src/core/iomgr/pollset_windows.c @@ -70,7 +70,7 @@ void grpc_pollset_destroy(grpc_pollset *pollset) { int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) { gpr_timespec now; - now = gpr_now(); + now = gpr_now(GPR_CLOCK_REALTIME); if (gpr_time_cmp(now, deadline) > 0) { return 0 /* GPR_FALSE */; } @@ -86,8 +86,6 @@ int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) { return 1 /* GPR_TRUE */; } -void grpc_pollset_kick(grpc_pollset *p) { - gpr_cv_signal(&p->cv); -} +void grpc_pollset_kick(grpc_pollset *p) { gpr_cv_signal(&p->cv); } #endif /* GPR_WINSOCK_SOCKET */ diff --git a/src/core/iomgr/socket_windows.c b/src/core/iomgr/socket_windows.c index 897408ded29..f6ddfff0ad9 100644 --- a/src/core/iomgr/socket_windows.c +++ b/src/core/iomgr/socket_windows.c @@ -37,6 +37,7 @@ #include #include +#include #include "src/core/iomgr/iocp_windows.h" #include "src/core/iomgr/iomgr_internal.h" @@ -61,22 +62,27 @@ grpc_winsocket *grpc_winsocket_create(SOCKET socket, const char *name) { operations to abort them. We need to do that this way because of the various callsites of that function, which happens to be in various mutex hold states, and that'd be unsafe to call them directly. */ -int grpc_winsocket_shutdown(grpc_winsocket *socket) { +int grpc_winsocket_shutdown(grpc_winsocket *winsocket) { int callbacks_set = 0; - gpr_mu_lock(&socket->state_mu); - if (socket->read_info.cb) { + SOCKET socket; + gpr_mu_lock(&winsocket->state_mu); + socket = winsocket->socket; + if (winsocket->read_info.cb) { callbacks_set++; - grpc_iomgr_closure_init(&socket->shutdown_closure, socket->read_info.cb, - socket->read_info.opaque); - grpc_iomgr_add_delayed_callback(&socket->shutdown_closure, 0); + grpc_iomgr_closure_init(&winsocket->shutdown_closure, + winsocket->read_info.cb, + winsocket->read_info.opaque); + grpc_iomgr_add_delayed_callback(&winsocket->shutdown_closure, 0); } - if (socket->write_info.cb) { + if (winsocket->write_info.cb) { callbacks_set++; - grpc_iomgr_closure_init(&socket->shutdown_closure, socket->write_info.cb, - socket->write_info.opaque); - grpc_iomgr_add_delayed_callback(&socket->shutdown_closure, 0); + grpc_iomgr_closure_init(&winsocket->shutdown_closure, + winsocket->write_info.cb, + winsocket->write_info.opaque); + grpc_iomgr_add_delayed_callback(&winsocket->shutdown_closure, 0); } - gpr_mu_unlock(&socket->state_mu); + gpr_mu_unlock(&winsocket->state_mu); + closesocket(socket); return callbacks_set; } @@ -87,14 +93,12 @@ int grpc_winsocket_shutdown(grpc_winsocket *socket) { an "idle" socket which is neither trying to read or write, we'd start leaking both memory and sockets. */ void grpc_winsocket_orphan(grpc_winsocket *winsocket) { - SOCKET socket = winsocket->socket; grpc_iomgr_unregister_object(&winsocket->iomgr_object); if (winsocket->read_info.outstanding || winsocket->write_info.outstanding) { grpc_iocp_socket_orphan(winsocket); } else { grpc_winsocket_destroy(winsocket); } - closesocket(socket); } void grpc_winsocket_destroy(grpc_winsocket *winsocket) { diff --git a/src/core/iomgr/tcp_client_posix.c b/src/core/iomgr/tcp_client_posix.c index 2c6ff4e9c43..dc0489e64f4 100644 --- a/src/core/iomgr/tcp_client_posix.c +++ b/src/core/iomgr/tcp_client_posix.c @@ -253,7 +253,8 @@ void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *ep), ac->write_closure.cb_arg = ac; gpr_mu_lock(&ac->mu); - grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac, gpr_now()); + grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac, + gpr_now(GPR_CLOCK_REALTIME)); grpc_fd_notify_on_write(ac->fd, &ac->write_closure); gpr_mu_unlock(&ac->mu); diff --git a/src/core/iomgr/tcp_client_windows.c b/src/core/iomgr/tcp_client_windows.c index b1a169b5192..16741452b91 100644 --- a/src/core/iomgr/tcp_client_windows.c +++ b/src/core/iomgr/tcp_client_windows.c @@ -215,7 +215,8 @@ void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *tcp), ac->refs = 2; ac->aborted = 0; - grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac, gpr_now()); + grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac, + gpr_now(GPR_CLOCK_REALTIME)); socket->write_info.outstanding = 1; grpc_socket_notify_on_write(socket, on_connect, ac); return; diff --git a/src/core/iomgr/tcp_server_windows.c b/src/core/iomgr/tcp_server_windows.c index d70968de885..187009b2c86 100644 --- a/src/core/iomgr/tcp_server_windows.c +++ b/src/core/iomgr/tcp_server_windows.c @@ -108,14 +108,15 @@ void grpc_tcp_server_destroy(grpc_tcp_server *s, size_t i; gpr_mu_lock(&s->mu); /* First, shutdown all fd's. This will queue abortion calls for all - of the pending accepts. */ + of the pending accepts due to the normal operation mechanism. */ for (i = 0; i < s->nports; i++) { server_port *sp = &s->ports[i]; + sp->shutting_down = 1; grpc_winsocket_shutdown(sp->socket); } /* This happens asynchronously. Wait while that happens. */ while (s->active_ports) { - gpr_cv_wait(&s->cv, &s->mu, gpr_inf_future); + gpr_cv_wait(&s->cv, &s->mu, gpr_inf_future(GPR_CLOCK_REALTIME)); } gpr_mu_unlock(&s->mu); @@ -242,63 +243,52 @@ static void on_accept(void *arg, int from_iocp) { SOCKET sock = sp->new_socket; grpc_winsocket_callback_info *info = &sp->socket->read_info; grpc_endpoint *ep = NULL; - - /* The shutdown sequence is done in two parts. This is the second - part here, acknowledging the IOCP notification, and doing nothing - else, especially not queuing a new accept. */ - if (sp->shutting_down) { - GPR_ASSERT(from_iocp); - sp->shutting_down = 0; - sp->socket->read_info.outstanding = 0; - gpr_mu_lock(&sp->server->mu); - if (0 == --sp->server->active_ports) { - gpr_cv_broadcast(&sp->server->cv); - } - gpr_mu_unlock(&sp->server->mu); - return; - } - - if (from_iocp) { - /* The IOCP notified us of a completed operation. Let's grab the results, - and act accordingly. */ - DWORD transfered_bytes = 0; - DWORD flags; - BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped, - &transfered_bytes, FALSE, &flags); - if (!wsa_success) { + DWORD transfered_bytes; + DWORD flags; + BOOL wsa_success; + + /* The general mechanism for shutting down is to queue abortion calls. While + this is necessary in the read/write case, it's useless for the accept + case. Let's do nothing. */ + if (!from_iocp) return; + + /* The IOCP notified us of a completed operation. Let's grab the results, + and act accordingly. */ + transfered_bytes = 0; + wsa_success = WSAGetOverlappedResult(sock, &info->overlapped, + &transfered_bytes, FALSE, &flags); + if (!wsa_success) { + if (sp->shutting_down) { + /* During the shutdown case, we ARE expecting an error. So that's swell, + and we can wake up the shutdown thread. */ + sp->shutting_down = 0; + sp->socket->read_info.outstanding = 0; + gpr_mu_lock(&sp->server->mu); + if (0 == --sp->server->active_ports) { + gpr_cv_broadcast(&sp->server->cv); + } + gpr_mu_unlock(&sp->server->mu); + return; + } else { char *utf8_message = gpr_format_message(WSAGetLastError()); gpr_log(GPR_ERROR, "on_accept error: %s", utf8_message); gpr_free(utf8_message); closesocket(sock); - } else { - /* TODO(ctiller): add sockaddr address to label */ - ep = grpc_tcp_create(grpc_winsocket_create(sock, "server")); } } else { - /* If we're not notified from the IOCP, it means we are asked to shutdown. - This will initiate that shutdown. Calling closesocket will trigger an - IOCP notification, that will call this function a second time, from - the IOCP thread. Of course, this only works if the socket was, in fact, - listening. If that's not the case, we'd wait indefinitely. That's a bit - of a degenerate case, but it can happen if you create a server, but - don't start it. So let's support that by recursing once. */ - sp->shutting_down = 1; - sp->new_socket = INVALID_SOCKET; - if (sock != INVALID_SOCKET) { - closesocket(sock); - } else { - on_accept(sp, 1); + if (!sp->shutting_down) { + /* TODO(ctiller): add sockaddr address to label */ + ep = grpc_tcp_create(grpc_winsocket_create(sock, "server")); } - return; } /* The only time we should call our callback, is where we successfully managed to accept a connection, and created an endpoint. */ if (ep) sp->server->cb(sp->server->cb_arg, ep); /* As we were notified from the IOCP of one and exactly one accept, - the former socked we created has now either been destroy or assigned - to the new connection. We need to create a new one for the next - connection. */ + the former socked we created has now either been destroy or assigned + to the new connection. We need to create a new one for the next + connection. */ start_accept(sp); } diff --git a/src/core/iomgr/tcp_windows.c b/src/core/iomgr/tcp_windows.c index 15759c398a1..1bf81a73e0c 100644 --- a/src/core/iomgr/tcp_windows.c +++ b/src/core/iomgr/tcp_windows.c @@ -148,9 +148,11 @@ static void on_read(void *tcpp, int from_iocp) { GPR_ASSERT(tcp->socket->read_info.outstanding); if (socket->read_info.wsa_error != 0) { - char *utf8_message = gpr_format_message(info->wsa_error); - gpr_log(GPR_ERROR, "ReadFile overlapped error: %s", utf8_message); - gpr_free(utf8_message); + if (socket->read_info.wsa_error != WSAECONNRESET) { + char *utf8_message = gpr_format_message(info->wsa_error); + gpr_log(GPR_ERROR, "ReadFile overlapped error: %s", utf8_message); + gpr_free(utf8_message); + } status = GRPC_ENDPOINT_CB_ERROR; } else { if (info->bytes_transfered != 0) { @@ -259,9 +261,11 @@ static void on_write(void *tcpp, int from_iocp) { GPR_ASSERT(tcp->socket->write_info.outstanding); if (info->wsa_error != 0) { - char *utf8_message = gpr_format_message(info->wsa_error); - gpr_log(GPR_ERROR, "WSASend overlapped error: %s", utf8_message); - gpr_free(utf8_message); + if (info->wsa_error != WSAECONNRESET) { + char *utf8_message = gpr_format_message(info->wsa_error); + gpr_log(GPR_ERROR, "WSASend overlapped error: %s", utf8_message); + gpr_free(utf8_message); + } status = GRPC_ENDPOINT_CB_ERROR; } else { GPR_ASSERT(info->bytes_transfered == tcp->write_slices.length); @@ -325,9 +329,11 @@ static grpc_endpoint_write_status win_write(grpc_endpoint *ep, ret = GRPC_ENDPOINT_WRITE_DONE; GPR_ASSERT(bytes_sent == tcp->write_slices.length); } else { - char *utf8_message = gpr_format_message(info->wsa_error); - gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message); - gpr_free(utf8_message); + if (socket->read_info.wsa_error != WSAECONNRESET) { + char *utf8_message = gpr_format_message(info->wsa_error); + gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message); + gpr_free(utf8_message); + } } if (allocated) gpr_free(allocated); gpr_slice_buffer_reset_and_unref(&tcp->write_slices); diff --git a/src/core/profiling/timers_preciseclock.h b/src/core/profiling/timers_preciseclock.h index 163d52b7974..5c251b47e6f 100644 --- a/src/core/profiling/timers_preciseclock.h +++ b/src/core/profiling/timers_preciseclock.h @@ -82,7 +82,7 @@ struct grpc_precise_clock { gpr_timespec clock; }; static void grpc_precise_clock_now(grpc_precise_clock* clk) { - clk->clock = gpr_now(); + clk->clock = gpr_now(GPR_CLOCK_REALTIME); } #define GRPC_PRECISE_CLOCK_FORMAT "%ld.%09d" #define GRPC_PRECISE_CLOCK_PRINTF_ARGS(clk) \ diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c index 52fd5a25b31..fb59fa4b0e5 100644 --- a/src/core/security/credentials.c +++ b/src/core/security/credentials.c @@ -324,7 +324,7 @@ static void jwt_reset_cache(grpc_jwt_credentials *c) { gpr_free(c->cached.service_url); c->cached.service_url = NULL; } - c->cached.jwt_expiration = gpr_inf_past; + c->cached.jwt_expiration = gpr_inf_past(GPR_CLOCK_REALTIME); } static void jwt_destroy(grpc_credentials *creds) { @@ -347,8 +347,8 @@ static void jwt_get_request_metadata(grpc_credentials *creds, grpc_credentials_metadata_cb cb, void *user_data) { grpc_jwt_credentials *c = (grpc_jwt_credentials *)creds; - gpr_timespec refresh_threshold = {GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, - 0}; + gpr_timespec refresh_threshold = gpr_time_from_seconds( + GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN); /* See if we can return a cached jwt. */ grpc_credentials_md_store *jwt_md = NULL; @@ -357,7 +357,8 @@ static void jwt_get_request_metadata(grpc_credentials *creds, if (c->cached.service_url != NULL && strcmp(c->cached.service_url, service_url) == 0 && c->cached.jwt_md != NULL && - (gpr_time_cmp(gpr_time_sub(c->cached.jwt_expiration, gpr_now()), + (gpr_time_cmp(gpr_time_sub(c->cached.jwt_expiration, + gpr_now(GPR_CLOCK_REALTIME)), refresh_threshold) > 0)) { jwt_md = grpc_credentials_md_store_ref(c->cached.jwt_md); } @@ -374,7 +375,8 @@ static void jwt_get_request_metadata(grpc_credentials *creds, char *md_value; gpr_asprintf(&md_value, "Bearer %s", jwt); gpr_free(jwt); - c->cached.jwt_expiration = gpr_time_add(gpr_now(), c->jwt_lifetime); + c->cached.jwt_expiration = + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), c->jwt_lifetime); c->cached.service_url = gpr_strdup(service_url); c->cached.jwt_md = grpc_credentials_md_store_create(1); grpc_credentials_md_store_add_cstrings( @@ -514,6 +516,7 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response( access_token->value); token_lifetime->tv_sec = strtol(expires_in->value, NULL, 10); token_lifetime->tv_nsec = 0; + token_lifetime->clock_type = GPR_TIMESPAN; if (*token_md != NULL) grpc_credentials_md_store_unref(*token_md); *token_md = grpc_credentials_md_store_create(1); grpc_credentials_md_store_add_cstrings( @@ -545,11 +548,12 @@ static void on_oauth2_token_fetcher_http_response( status = grpc_oauth2_token_fetcher_credentials_parse_server_response( response, &c->access_token_md, &token_lifetime); if (status == GRPC_CREDENTIALS_OK) { - c->token_expiration = gpr_time_add(gpr_now(), token_lifetime); + c->token_expiration = + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), token_lifetime); r->cb(r->user_data, c->access_token_md->entries, c->access_token_md->num_entries, status); } else { - c->token_expiration = gpr_inf_past; + c->token_expiration = gpr_inf_past(GPR_CLOCK_REALTIME); r->cb(r->user_data, NULL, 0, status); } gpr_mu_unlock(&c->mu); @@ -561,14 +565,15 @@ static void oauth2_token_fetcher_get_request_metadata( grpc_credentials_metadata_cb cb, void *user_data) { grpc_oauth2_token_fetcher_credentials *c = (grpc_oauth2_token_fetcher_credentials *)creds; - gpr_timespec refresh_threshold = {GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, - 0}; + gpr_timespec refresh_threshold = gpr_time_from_seconds( + GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN); grpc_credentials_md_store *cached_access_token_md = NULL; { gpr_mu_lock(&c->mu); if (c->access_token_md != NULL && - (gpr_time_cmp(gpr_time_sub(c->token_expiration, gpr_now()), - refresh_threshold) > 0)) { + (gpr_time_cmp( + gpr_time_sub(c->token_expiration, gpr_now(GPR_CLOCK_REALTIME)), + refresh_threshold) > 0)) { cached_access_token_md = grpc_credentials_md_store_ref(c->access_token_md); } @@ -582,7 +587,7 @@ static void oauth2_token_fetcher_get_request_metadata( c->fetch_func( grpc_credentials_metadata_request_create(creds, cb, user_data), &c->httpcli_context, pollset, on_oauth2_token_fetcher_http_response, - gpr_time_add(gpr_now(), refresh_threshold)); + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), refresh_threshold)); } } @@ -592,7 +597,7 @@ static void init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials *c, c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2; gpr_ref_init(&c->base.refcount, 1); gpr_mu_init(&c->mu); - c->token_expiration = gpr_inf_past; + c->token_expiration = gpr_inf_past(GPR_CLOCK_REALTIME); c->fetch_func = fetch_func; grpc_httpcli_context_init(&c->httpcli_context); } diff --git a/src/core/security/google_default_credentials.c b/src/core/security/google_default_credentials.c index cd92f9dc30f..833484310f2 100644 --- a/src/core/security/google_default_credentials.c +++ b/src/core/security/google_default_credentials.c @@ -91,7 +91,7 @@ static int is_stack_running_on_compute_engine(void) { /* The http call is local. If it takes more than one sec, it is for sure not on compute engine. */ - gpr_timespec max_detection_delay = {1, 0}; + gpr_timespec max_detection_delay = gpr_time_from_seconds(1, GPR_TIMESPAN); grpc_pollset_init(&detector.pollset); detector.is_done = 0; @@ -103,15 +103,16 @@ static int is_stack_running_on_compute_engine(void) { grpc_httpcli_context_init(&context); - grpc_httpcli_get(&context, &detector.pollset, &request, - gpr_time_add(gpr_now(), max_detection_delay), - on_compute_engine_detection_http_response, &detector); + grpc_httpcli_get( + &context, &detector.pollset, &request, + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), max_detection_delay), + on_compute_engine_detection_http_response, &detector); /* Block until we get the response. This is not ideal but this should only be called once for the lifetime of the process by the default credentials. */ gpr_mu_lock(GRPC_POLLSET_MU(&detector.pollset)); while (!detector.is_done) { - grpc_pollset_work(&detector.pollset, gpr_inf_future); + grpc_pollset_work(&detector.pollset, gpr_inf_future(GPR_CLOCK_REALTIME)); } gpr_mu_unlock(GRPC_POLLSET_MU(&detector.pollset)); diff --git a/src/core/security/json_token.c b/src/core/security/json_token.c index 2e2b9801293..021912f3337 100644 --- a/src/core/security/json_token.c +++ b/src/core/security/json_token.c @@ -49,7 +49,7 @@ /* --- Constants. --- */ /* 1 hour max. */ -const gpr_timespec grpc_max_auth_token_lifetime = {3600, 0}; +const gpr_timespec grpc_max_auth_token_lifetime = {3600, 0, GPR_TIMESPAN}; #define GRPC_JWT_RSA_SHA256_ALGORITHM "RS256" #define GRPC_JWT_TYPE "JWT" @@ -207,7 +207,7 @@ static char *encoded_jwt_claim(const grpc_auth_json_key *json_key, grpc_json *child = NULL; char *json_str = NULL; char *result = NULL; - gpr_timespec now = gpr_now(); + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); gpr_timespec expiration = gpr_time_add(now, token_lifetime); char now_str[GPR_LTOA_MIN_BUFSIZE]; char expiration_str[GPR_LTOA_MIN_BUFSIZE]; @@ -218,8 +218,8 @@ static char *encoded_jwt_claim(const grpc_auth_json_key *json_key, gpr_ltoa(now.tv_sec, now_str); gpr_ltoa(expiration.tv_sec, expiration_str); - child = create_child(NULL, json, "iss", json_key->client_email, - GRPC_JSON_STRING); + child = + create_child(NULL, json, "iss", json_key->client_email, GRPC_JSON_STRING); if (scope != NULL) { child = create_child(child, json, "scope", scope, GRPC_JSON_STRING); } else { @@ -403,4 +403,3 @@ void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token *refresh_token) { refresh_token->refresh_token = NULL; } } - diff --git a/src/core/security/jwt_verifier.c b/src/core/security/jwt_verifier.c index 01007a1a84d..1276693da7f 100644 --- a/src/core/security/jwt_verifier.c +++ b/src/core/security/jwt_verifier.c @@ -109,7 +109,7 @@ static const char *validate_string_field(const grpc_json *json, static gpr_timespec validate_time_field(const grpc_json *json, const char *key) { - gpr_timespec result = gpr_time_0; + gpr_timespec result = gpr_time_0(GPR_CLOCK_REALTIME); if (json->type != GRPC_JSON_NUMBER) { gpr_log(GPR_ERROR, "Invalid %s field [%s]", key, json->value); return result; @@ -189,7 +189,6 @@ struct grpc_jwt_claims { gpr_slice buffer; }; - void grpc_jwt_claims_destroy(grpc_jwt_claims *claims) { grpc_json_destroy(claims->json); gpr_slice_unref(claims->buffer); @@ -222,17 +221,17 @@ const char *grpc_jwt_claims_audience(const grpc_jwt_claims *claims) { } gpr_timespec grpc_jwt_claims_issued_at(const grpc_jwt_claims *claims) { - if (claims == NULL) return gpr_inf_past; + if (claims == NULL) return gpr_inf_past(GPR_CLOCK_REALTIME); return claims->iat; } gpr_timespec grpc_jwt_claims_expires_at(const grpc_jwt_claims *claims) { - if (claims == NULL) return gpr_inf_future; + if (claims == NULL) return gpr_inf_future(GPR_CLOCK_REALTIME); return claims->exp; } gpr_timespec grpc_jwt_claims_not_before(const grpc_jwt_claims *claims) { - if (claims == NULL) return gpr_inf_past; + if (claims == NULL) return gpr_inf_past(GPR_CLOCK_REALTIME); return claims->nbf; } @@ -243,9 +242,9 @@ grpc_jwt_claims *grpc_jwt_claims_from_json(grpc_json *json, gpr_slice buffer) { memset(claims, 0, sizeof(grpc_jwt_claims)); claims->json = json; claims->buffer = buffer; - claims->iat = gpr_inf_past; - claims->nbf = gpr_inf_past; - claims->exp = gpr_inf_future; + claims->iat = gpr_inf_past(GPR_CLOCK_REALTIME); + claims->nbf = gpr_inf_past(GPR_CLOCK_REALTIME); + claims->exp = gpr_inf_future(GPR_CLOCK_REALTIME); /* Per the spec, all fields are optional. */ for (cur = json->child; cur != NULL; cur = cur->next) { @@ -263,13 +262,16 @@ grpc_jwt_claims *grpc_jwt_claims_from_json(grpc_json *json, gpr_slice buffer) { if (claims->jti == NULL) goto error; } else if (strcmp(cur->key, "iat") == 0) { claims->iat = validate_time_field(cur, "iat"); - if (gpr_time_cmp(claims->iat, gpr_time_0) == 0) goto error; + if (gpr_time_cmp(claims->iat, gpr_time_0(GPR_CLOCK_REALTIME)) == 0) + goto error; } else if (strcmp(cur->key, "exp") == 0) { claims->exp = validate_time_field(cur, "exp"); - if (gpr_time_cmp(claims->exp, gpr_time_0) == 0) goto error; + if (gpr_time_cmp(claims->exp, gpr_time_0(GPR_CLOCK_REALTIME)) == 0) + goto error; } else if (strcmp(cur->key, "nbf") == 0) { claims->nbf = validate_time_field(cur, "nbf"); - if (gpr_time_cmp(claims->nbf, gpr_time_0) == 0) goto error; + if (gpr_time_cmp(claims->nbf, gpr_time_0(GPR_CLOCK_REALTIME)) == 0) + goto error; } } return claims; @@ -286,12 +288,14 @@ grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims *claims, GPR_ASSERT(claims != NULL); - skewed_now = gpr_time_add(gpr_now(), grpc_jwt_verifier_clock_skew); + skewed_now = + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_clock_skew); if (gpr_time_cmp(skewed_now, claims->nbf) < 0) { gpr_log(GPR_ERROR, "JWT is not valid yet."); return GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE; } - skewed_now = gpr_time_sub(gpr_now(), grpc_jwt_verifier_clock_skew); + skewed_now = + gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_clock_skew); if (gpr_time_cmp(skewed_now, claims->exp) > 0) { gpr_log(GPR_ERROR, "JWT is expired."); return GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE; @@ -327,10 +331,10 @@ typedef struct { /* Takes ownership of the header, claims and signature. */ static verifier_cb_ctx *verifier_cb_ctx_create( - grpc_jwt_verifier *verifier, grpc_pollset *pollset, - jose_header * header, grpc_jwt_claims *claims, const char *audience, - gpr_slice signature, const char *signed_jwt, size_t signed_jwt_len, - void *user_data, grpc_jwt_verification_done_cb cb) { + grpc_jwt_verifier *verifier, grpc_pollset *pollset, jose_header *header, + grpc_jwt_claims *claims, const char *audience, gpr_slice signature, + const char *signed_jwt, size_t signed_jwt_len, void *user_data, + grpc_jwt_verification_done_cb cb) { verifier_cb_ctx *ctx = gpr_malloc(sizeof(verifier_cb_ctx)); memset(ctx, 0, sizeof(verifier_cb_ctx)); ctx->verifier = verifier; @@ -358,10 +362,10 @@ void verifier_cb_ctx_destroy(verifier_cb_ctx *ctx) { /* --- grpc_jwt_verifier object. --- */ /* Clock skew defaults to one minute. */ -gpr_timespec grpc_jwt_verifier_clock_skew = {60, 0}; +gpr_timespec grpc_jwt_verifier_clock_skew = {60, 0, GPR_TIMESPAN}; /* Max delay defaults to one minute. */ -gpr_timespec grpc_jwt_verifier_max_delay = {60, 0}; +gpr_timespec grpc_jwt_verifier_max_delay = {60, 0, GPR_TIMESPAN}; typedef struct { char *email_domain; @@ -604,7 +608,7 @@ end: static void on_openid_config_retrieved(void *user_data, const grpc_httpcli_response *response) { - const grpc_json* cur; + const grpc_json *cur; grpc_json *json = json_from_http(response); verifier_cb_ctx *ctx = (verifier_cb_ctx *)user_data; grpc_httpcli_request req; @@ -632,9 +636,10 @@ static void on_openid_config_retrieved(void *user_data, } else { *(req.host + (req.path - jwks_uri)) = '\0'; } - grpc_httpcli_get(&ctx->verifier->http_ctx, ctx->pollset, &req, - gpr_time_add(gpr_now(), grpc_jwt_verifier_max_delay), - on_keys_retrieved, ctx); + grpc_httpcli_get( + &ctx->verifier->http_ctx, ctx->pollset, &req, + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay), + on_keys_retrieved, ctx); grpc_json_destroy(json); gpr_free(req.host); return; @@ -645,8 +650,8 @@ error: verifier_cb_ctx_destroy(ctx); } -static email_key_mapping *verifier_get_mapping( - grpc_jwt_verifier *v, const char *email_domain) { +static email_key_mapping *verifier_get_mapping(grpc_jwt_verifier *v, + const char *email_domain) { size_t i; if (v->mappings == NULL) return NULL; for (i = 0; i < v->num_mappings; i++) { @@ -733,9 +738,10 @@ static void retrieve_key_and_verify(verifier_cb_ctx *ctx) { http_cb = on_openid_config_retrieved; } - grpc_httpcli_get(&ctx->verifier->http_ctx, ctx->pollset, &req, - gpr_time_add(gpr_now(), grpc_jwt_verifier_max_delay), - http_cb, ctx); + grpc_httpcli_get( + &ctx->verifier->http_ctx, ctx->pollset, &req, + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay), + http_cb, ctx); gpr_free(req.host); gpr_free(req.path); return; @@ -764,7 +770,7 @@ void grpc_jwt_verifier_verify(grpc_jwt_verifier *verifier, dot = strchr(cur, '.'); if (dot == NULL) goto error; json = parse_json_part_from_jwt(cur, dot - cur, &header_buffer); - if (json == NULL) goto error; + if (json == NULL) goto error; header = jose_header_from_json(json, header_buffer); if (header == NULL) goto error; @@ -772,7 +778,7 @@ void grpc_jwt_verifier_verify(grpc_jwt_verifier *verifier, dot = strchr(cur, '.'); if (dot == NULL) goto error; json = parse_json_part_from_jwt(cur, dot - cur, &claims_buffer); - if (json == NULL) goto error; + if (json == NULL) goto error; claims = grpc_jwt_claims_from_json(json, claims_buffer); if (claims == NULL) goto error; @@ -827,4 +833,3 @@ void grpc_jwt_verifier_destroy(grpc_jwt_verifier *v) { } gpr_free(v); } - diff --git a/src/core/security/secure_transport_setup.c b/src/core/security/secure_transport_setup.c index 731b382f091..0c3572b53c4 100644 --- a/src/core/security/secure_transport_setup.c +++ b/src/core/security/secure_transport_setup.c @@ -47,7 +47,8 @@ typedef struct { tsi_handshaker *handshaker; unsigned char *handshake_buffer; size_t handshake_buffer_size; - grpc_endpoint *endpoint; + grpc_endpoint *wrapped_endpoint; + grpc_endpoint *secure_endpoint; gpr_slice_buffer left_overs; grpc_secure_transport_setup_done_cb cb; void *user_data; @@ -63,13 +64,16 @@ static void on_handshake_data_sent_to_peer(void *setup, static void secure_transport_setup_done(grpc_secure_transport_setup *s, int is_success) { if (is_success) { - s->cb(s->user_data, GRPC_SECURITY_OK, s->endpoint); + s->cb(s->user_data, GRPC_SECURITY_OK, s->wrapped_endpoint, + s->secure_endpoint); } else { - if (s->endpoint != NULL) { - grpc_endpoint_shutdown(s->endpoint); - grpc_endpoint_destroy(s->endpoint); + if (s->secure_endpoint != NULL) { + grpc_endpoint_shutdown(s->secure_endpoint); + grpc_endpoint_destroy(s->secure_endpoint); + } else { + grpc_endpoint_destroy(s->wrapped_endpoint); } - s->cb(s->user_data, GRPC_SECURITY_ERROR, NULL); + s->cb(s->user_data, GRPC_SECURITY_ERROR, s->wrapped_endpoint, NULL); } if (s->handshaker != NULL) tsi_handshaker_destroy(s->handshaker); if (s->handshake_buffer != NULL) gpr_free(s->handshake_buffer); @@ -95,8 +99,9 @@ static void on_peer_checked(void *user_data, grpc_security_status status) { secure_transport_setup_done(s, 0); return; } - s->endpoint = grpc_secure_endpoint_create( - protector, s->endpoint, s->left_overs.slices, s->left_overs.count); + s->secure_endpoint = + grpc_secure_endpoint_create(protector, s->wrapped_endpoint, + s->left_overs.slices, s->left_overs.count); secure_transport_setup_done(s, 1); return; } @@ -152,7 +157,7 @@ static void send_handshake_bytes_to_peer(grpc_secure_transport_setup *s) { gpr_slice_from_copied_buffer((const char *)s->handshake_buffer, offset); /* TODO(klempner,jboeuf): This should probably use the client setup deadline */ - write_status = grpc_endpoint_write(s->endpoint, &to_send, 1, + write_status = grpc_endpoint_write(s->wrapped_endpoint, &to_send, 1, on_handshake_data_sent_to_peer, s); if (write_status == GRPC_ENDPOINT_WRITE_ERROR) { gpr_log(GPR_ERROR, "Could not send handshake data to peer."); @@ -198,7 +203,7 @@ static void on_handshake_data_received_from_peer( if (result == TSI_INCOMPLETE_DATA) { /* TODO(klempner,jboeuf): This should probably use the client setup deadline */ - grpc_endpoint_notify_on_read(s->endpoint, + grpc_endpoint_notify_on_read(s->wrapped_endpoint, on_handshake_data_received_from_peer, setup); cleanup_slices(slices, nslices); return; @@ -256,7 +261,7 @@ static void on_handshake_data_sent_to_peer(void *setup, if (tsi_handshaker_is_in_progress(s->handshaker)) { /* TODO(klempner,jboeuf): This should probably use the client setup deadline */ - grpc_endpoint_notify_on_read(s->endpoint, + grpc_endpoint_notify_on_read(s->wrapped_endpoint, on_handshake_data_received_from_peer, setup); } else { check_peer(s); @@ -280,7 +285,7 @@ void grpc_setup_secure_transport(grpc_security_connector *connector, GRPC_SECURITY_CONNECTOR_REF(connector, "secure_transport_setup"); s->handshake_buffer_size = GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE; s->handshake_buffer = gpr_malloc(s->handshake_buffer_size); - s->endpoint = nonsecure_endpoint; + s->wrapped_endpoint = nonsecure_endpoint; s->user_data = user_data; s->cb = cb; gpr_slice_buffer_init(&s->left_overs); diff --git a/src/core/security/secure_transport_setup.h b/src/core/security/secure_transport_setup.h index 58701c461d9..29025f5236b 100644 --- a/src/core/security/secure_transport_setup.h +++ b/src/core/security/secure_transport_setup.h @@ -42,7 +42,7 @@ /* Ownership of the secure_endpoint is transfered. */ typedef void (*grpc_secure_transport_setup_done_cb)( void *user_data, grpc_security_status status, - grpc_endpoint *secure_endpoint); + grpc_endpoint *wrapped_endpoint, grpc_endpoint *secure_endpoint); /* Calls the callback upon completion. */ void grpc_setup_secure_transport(grpc_security_connector *connector, diff --git a/src/core/security/server_secure_chttp2.c b/src/core/security/server_secure_chttp2.c index 8a7ada07af9..3717b8989f4 100644 --- a/src/core/security/server_secure_chttp2.c +++ b/src/core/security/server_secure_chttp2.c @@ -51,10 +51,16 @@ #include #include +typedef struct tcp_endpoint_list { + grpc_endpoint *tcp_endpoint; + struct tcp_endpoint_list *next; +} tcp_endpoint_list; + typedef struct grpc_server_secure_state { grpc_server *server; grpc_tcp_server *tcp; grpc_security_connector *sc; + tcp_endpoint_list *handshaking_tcp_endpoints; int is_shutdown; gpr_mu mu; gpr_refcount refcount; @@ -88,14 +94,37 @@ static void setup_transport(void *statep, grpc_transport *transport, grpc_channel_args_destroy(args_copy); } +static int remove_tcp_from_list_locked(grpc_server_secure_state *state, + grpc_endpoint *tcp) { + tcp_endpoint_list *node = state->handshaking_tcp_endpoints; + tcp_endpoint_list *tmp = NULL; + if (node && node->tcp_endpoint == tcp) { + state->handshaking_tcp_endpoints = state->handshaking_tcp_endpoints->next; + gpr_free(node); + return 0; + } + while (node) { + if (node->next->tcp_endpoint == tcp) { + tmp = node->next; + node->next = node->next->next; + gpr_free(tmp); + return 0; + } + node = node->next; + } + return -1; +} + static void on_secure_transport_setup_done(void *statep, grpc_security_status status, + grpc_endpoint *wrapped_endpoint, grpc_endpoint *secure_endpoint) { grpc_server_secure_state *state = statep; grpc_transport *transport; grpc_mdctx *mdctx; if (status == GRPC_SECURITY_OK) { gpr_mu_lock(&state->mu); + remove_tcp_from_list_locked(state, wrapped_endpoint); if (!state->is_shutdown) { mdctx = grpc_mdctx_create(); transport = grpc_create_chttp2_transport( @@ -110,6 +139,9 @@ static void on_secure_transport_setup_done(void *statep, } gpr_mu_unlock(&state->mu); } else { + gpr_mu_lock(&state->mu); + remove_tcp_from_list_locked(state, wrapped_endpoint); + gpr_mu_unlock(&state->mu); gpr_log(GPR_ERROR, "Secure transport failed with error %d", status); } state_unref(state); @@ -117,7 +149,14 @@ static void on_secure_transport_setup_done(void *statep, static void on_accept(void *statep, grpc_endpoint *tcp) { grpc_server_secure_state *state = statep; + tcp_endpoint_list *node; state_ref(state); + node = gpr_malloc(sizeof(tcp_endpoint_list)); + node->tcp_endpoint = tcp; + gpr_mu_lock(&state->mu); + node->next = state->handshaking_tcp_endpoints; + state->handshaking_tcp_endpoints = node; + gpr_mu_unlock(&state->mu); grpc_setup_secure_transport(state->sc, tcp, on_secure_transport_setup_done, state); } @@ -132,6 +171,13 @@ static void start(grpc_server *server, void *statep, grpc_pollset **pollsets, static void destroy_done(void *statep) { grpc_server_secure_state *state = statep; grpc_server_listener_destroy_done(state->server); + gpr_mu_lock(&state->mu); + while (state->handshaking_tcp_endpoints != NULL) { + grpc_endpoint_shutdown(state->handshaking_tcp_endpoints->tcp_endpoint); + remove_tcp_from_list_locked(state, + state->handshaking_tcp_endpoints->tcp_endpoint); + } + gpr_mu_unlock(&state->mu); state_unref(state); } @@ -209,6 +255,7 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, state->server = server; state->tcp = tcp; state->sc = sc; + state->handshaking_tcp_endpoints = NULL; state->is_shutdown = 0; gpr_mu_init(&state->mu); gpr_ref_init(&state->refcount, 1); diff --git a/src/core/statistics/census_rpc_stats.c b/src/core/statistics/census_rpc_stats.c index 0491c919479..3e571b1143e 100644 --- a/src/core/statistics/census_rpc_stats.c +++ b/src/core/statistics/census_rpc_stats.c @@ -157,7 +157,7 @@ static void record_stats(census_ht* store, census_op_id op_id, key.ptr = gpr_strdup(key.ptr); census_ht_insert(store, key, (void*)window_stats); } - census_window_stats_add(window_stats, gpr_now(), stats); + census_window_stats_add(window_stats, gpr_now(GPR_CLOCK_REALTIME), stats); } else { census_internal_unlock_trace_store(); } @@ -185,7 +185,7 @@ static void get_stats(census_ht* store, census_aggregated_rpc_stats* data) { if (store != NULL) { size_t n; unsigned i, j; - gpr_timespec now = gpr_now(); + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); census_ht_kv* kv = census_ht_get_all_elements(store, &n); if (kv != NULL) { data->num_entries = n; diff --git a/src/core/statistics/census_tracing.c b/src/core/statistics/census_tracing.c index 05e72b99c0b..3036ba5407f 100644 --- a/src/core/statistics/census_tracing.c +++ b/src/core/statistics/census_tracing.c @@ -94,7 +94,7 @@ census_op_id census_tracing_start_op(void) { g_id++; memcpy(&ret->id, &g_id, sizeof(census_op_id)); ret->rpc_stats.cnt = 1; - ret->ts = gpr_now(); + ret->ts = gpr_now(GPR_CLOCK_REALTIME); census_ht_insert(g_trace_store, op_id_as_key(&ret->id), (void*)ret); gpr_log(GPR_DEBUG, "Start tracing for id %lu", g_id); gpr_mu_unlock(&g_mu); @@ -122,7 +122,7 @@ void census_tracing_print(census_op_id op_id, const char* anno_txt) { trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); if (trace != NULL) { census_trace_annotation* anno = gpr_malloc(sizeof(census_trace_annotation)); - anno->ts = gpr_now(); + anno->ts = gpr_now(GPR_CLOCK_REALTIME); { char* d = anno->txt; const char* s = anno_txt; @@ -143,8 +143,8 @@ void census_tracing_end_op(census_op_id op_id) { gpr_mu_lock(&g_mu); trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); if (trace != NULL) { - trace->rpc_stats.elapsed_time_ms = - gpr_timespec_to_micros(gpr_time_sub(gpr_now(), trace->ts)); + trace->rpc_stats.elapsed_time_ms = gpr_timespec_to_micros( + gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), trace->ts)); gpr_log(GPR_DEBUG, "End tracing for id %lu, method %s, latency %f us", op_id_2_uint64(&op_id), trace->method, trace->rpc_stats.elapsed_time_ms); @@ -194,8 +194,8 @@ const char* census_get_trace_method_name(const census_trace_obj* trace) { static census_trace_annotation* dup_annotation_chain( census_trace_annotation* from) { - census_trace_annotation *ret = NULL; - census_trace_annotation **to = &ret; + census_trace_annotation* ret = NULL; + census_trace_annotation** to = &ret; for (; from != NULL; from = from->next) { *to = gpr_malloc(sizeof(census_trace_annotation)); memcpy(*to, from, sizeof(census_trace_annotation)); @@ -223,9 +223,9 @@ census_trace_obj** census_get_active_ops(int* num_active_ops) { size_t n = 0; census_ht_kv* all_kvs = census_ht_get_all_elements(g_trace_store, &n); *num_active_ops = (int)n; - if (n != 0 ) { + if (n != 0) { size_t i = 0; - ret = gpr_malloc(sizeof(census_trace_obj *) * n); + ret = gpr_malloc(sizeof(census_trace_obj*) * n); for (i = 0; i < n; i++) { ret[i] = trace_obj_dup((census_trace_obj*)all_kvs[i].v); } diff --git a/src/core/statistics/window_stats.h b/src/core/statistics/window_stats.h index d733d8d247a..0020f6b44ca 100644 --- a/src/core/statistics/window_stats.h +++ b/src/core/statistics/window_stats.h @@ -90,11 +90,11 @@ // Record a new event, taking 15.3ms, transferring 1784 bytes. stat.latency = 0.153; stat.bytes = 1784; - census_window_stats_add(stats, gpr_now(), &stat); + census_window_stats_add(stats, gpr_now(GPR_CLOCK_REALTIME), &stat); // Get sums and print them out result[kMinInterval].statistic = &sums[kMinInterval]; result[kHourInterval].statistic = &sums[kHourInterval]; - census_window_stats_get_sums(stats, gpr_now(), result); + census_window_stats_get_sums(stats, gpr_now(GPR_CLOCK_REALTIME), result); printf("%d events/min, average time %gs, average bytes %g\n", result[kMinInterval].count, (my_stat*)result[kMinInterval].statistic->latency / @@ -170,4 +170,4 @@ void census_window_stats_get_sums(const struct census_window_stats* wstats, assertion failure). This function is thread-compatible. */ void census_window_stats_destroy(struct census_window_stats* wstats); -#endif /* GRPC_INTERNAL_CORE_STATISTICS_WINDOW_STATS_H */ +#endif /* GRPC_INTERNAL_CORE_STATISTICS_WINDOW_STATS_H */ diff --git a/src/core/support/cancellable.c b/src/core/support/cancellable.c index 5a4d488dd3c..4756f1e1252 100644 --- a/src/core/support/cancellable.c +++ b/src/core/support/cancellable.c @@ -121,8 +121,9 @@ void gpr_cancellable_cancel(gpr_cancellable *c) { } else { gpr_event ev; gpr_event_init(&ev); - gpr_event_wait(&ev, - gpr_time_add(gpr_now(), gpr_time_from_micros(1000))); + gpr_event_wait( + &ev, gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), + gpr_time_from_micros(1000, GPR_TIMESPAN))); } } } while (failures != 0); diff --git a/src/core/support/log_linux.c b/src/core/support/log_linux.c index 7937466b79a..5ac36e7b950 100644 --- a/src/core/support/log_linux.c +++ b/src/core/support/log_linux.c @@ -76,7 +76,7 @@ void gpr_default_log(gpr_log_func_args *args) { char *prefix; const char *display_file; char time_buffer[64]; - gpr_timespec now = gpr_now(); + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); struct tm tm; final_slash = strrchr(args->file, '/'); diff --git a/src/core/support/log_posix.c b/src/core/support/log_posix.c index afca792c404..940ee20f151 100644 --- a/src/core/support/log_posix.c +++ b/src/core/support/log_posix.c @@ -75,7 +75,7 @@ void gpr_default_log(gpr_log_func_args *args) { char *final_slash; const char *display_file; char time_buffer[64]; - gpr_timespec now = gpr_now(); + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); struct tm tm; final_slash = strrchr(args->file, '/'); diff --git a/src/core/support/log_win32.c b/src/core/support/log_win32.c index d249be7d2ec..629781da8a2 100644 --- a/src/core/support/log_win32.c +++ b/src/core/support/log_win32.c @@ -82,7 +82,7 @@ void gpr_log(const char *file, int line, gpr_log_severity severity, /* Simple starter implementation */ void gpr_default_log(gpr_log_func_args *args) { char time_buffer[64]; - gpr_timespec now = gpr_now(); + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); struct tm tm; if (localtime_s(&tm, &now.tv_sec)) { diff --git a/src/core/support/stack_lockfree.c b/src/core/support/stack_lockfree.c new file mode 100644 index 00000000000..28443303797 --- /dev/null +++ b/src/core/support/stack_lockfree.c @@ -0,0 +1,132 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/support/stack_lockfree.h" + +#include +#include + +#include +#include +#include +#include + +/* The lockfree node structure is a single architecture-level + word that allows for an atomic CAS to set it up. */ +struct lockfree_node_contents { + /* next thing to look at. Actual index for head, next index otherwise */ + gpr_uint16 index; +#ifdef GPR_ARCH_64 + gpr_uint16 pad; + gpr_uint32 aba_ctr; +#else +#ifdef GPR_ARCH_32 + gpr_uint16 aba_ctr; +#else +#error Unsupported bit width architecture +#endif +#endif +}; + +/* Use a union to make sure that these are in the same bits as an atm word */ +typedef union lockfree_node { + gpr_atm atm; + struct lockfree_node_contents contents; +} lockfree_node; + +#define ENTRY_ALIGNMENT_BITS 3 /* make sure that entries aligned to 8-bytes */ +#define INVALID_ENTRY_INDEX \ + ((1 << 16) - 1) /* reserve this entry as invalid \ + */ + +struct gpr_stack_lockfree { + lockfree_node *entries; + lockfree_node head; /* An atomic entry describing curr head */ +}; + +gpr_stack_lockfree *gpr_stack_lockfree_create(int entries) { + gpr_stack_lockfree *stack; + stack = gpr_malloc(sizeof(*stack)); + /* Since we only allocate 16 bits to represent an entry number, + * make sure that we are within the desired range */ + /* Reserve the highest entry number as a dummy */ + GPR_ASSERT(entries < INVALID_ENTRY_INDEX); + stack->entries = gpr_malloc_aligned(entries * sizeof(stack->entries[0]), + ENTRY_ALIGNMENT_BITS); + /* Clear out all entries */ + memset(stack->entries, 0, entries * sizeof(stack->entries[0])); + memset(&stack->head, 0, sizeof(stack->head)); + + /* Point the head at reserved dummy entry */ + stack->head.contents.index = INVALID_ENTRY_INDEX; + return stack; +} + +void gpr_stack_lockfree_destroy(gpr_stack_lockfree *stack) { + gpr_free_aligned(stack->entries); + gpr_free(stack); +} + +int gpr_stack_lockfree_push(gpr_stack_lockfree *stack, int entry) { + lockfree_node head; + lockfree_node newhead; + + /* First fill in the entry's index and aba ctr for new head */ + newhead.contents.index = (gpr_uint16)entry; + /* Also post-increment the aba_ctr */ + newhead.contents.aba_ctr = stack->entries[entry].contents.aba_ctr++; + + do { + /* Atomically get the existing head value for use */ + head.atm = gpr_atm_no_barrier_load(&(stack->head.atm)); + /* Point to it */ + stack->entries[entry].contents.index = head.contents.index; + } while (!gpr_atm_rel_cas(&(stack->head.atm), head.atm, newhead.atm)); + /* Use rel_cas above to make sure that entry index is set properly */ + return head.contents.index == INVALID_ENTRY_INDEX; +} + +int gpr_stack_lockfree_pop(gpr_stack_lockfree *stack) { + lockfree_node head; + lockfree_node newhead; + do { + head.atm = gpr_atm_acq_load(&(stack->head.atm)); + if (head.contents.index == INVALID_ENTRY_INDEX) { + return -1; + } + newhead.atm = + gpr_atm_no_barrier_load(&(stack->entries[head.contents.index].atm)); + + } while (!gpr_atm_no_barrier_cas(&(stack->head.atm), head.atm, newhead.atm)); + return head.contents.index; +} diff --git a/src/core/support/stack_lockfree.h b/src/core/support/stack_lockfree.h new file mode 100644 index 00000000000..eec960fbb01 --- /dev/null +++ b/src/core/support/stack_lockfree.h @@ -0,0 +1,51 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_INTERNAL_CORE_SUPPORT_STACK_LOCKFREE_H +#define GRPC_INTERNAL_CORE_SUPPORT_STACK_LOCKFREE_H + +typedef struct gpr_stack_lockfree gpr_stack_lockfree; + +/* This stack must specify the maximum number of entries to track. + The current implementation only allows up to 65534 entries */ +gpr_stack_lockfree* gpr_stack_lockfree_create(int entries); +void gpr_stack_lockfree_destroy(gpr_stack_lockfree* stack); + +/* Pass in a valid entry number for the next stack entry */ +/* Returns 1 if this is the first element on the stack, 0 otherwise */ +int gpr_stack_lockfree_push(gpr_stack_lockfree*, int entry); + +/* Returns -1 on empty or the actual entry number */ +int gpr_stack_lockfree_pop(gpr_stack_lockfree* stack); + +#endif /* GRPC_INTERNAL_CORE_SUPPORT_STACK_LOCKFREE_H */ diff --git a/src/core/support/sync_posix.c b/src/core/support/sync_posix.c index 0ccbd4923f8..41af8ceb0a5 100644 --- a/src/core/support/sync_posix.c +++ b/src/core/support/sync_posix.c @@ -63,7 +63,7 @@ void gpr_cv_destroy(gpr_cv *cv) { GPR_ASSERT(pthread_cond_destroy(cv) == 0); } int gpr_cv_wait(gpr_cv *cv, gpr_mu *mu, gpr_timespec abs_deadline) { int err = 0; - if (gpr_time_cmp(abs_deadline, gpr_inf_future) == 0) { + if (gpr_time_cmp(abs_deadline, gpr_inf_future(GPR_CLOCK_REALTIME)) == 0) { err = pthread_cond_wait(cv, mu); } else { struct timespec abs_deadline_ts; diff --git a/src/core/support/sync_win32.c b/src/core/support/sync_win32.c index 72f39f8d464..63196d10d3c 100644 --- a/src/core/support/sync_win32.c +++ b/src/core/support/sync_win32.c @@ -83,10 +83,10 @@ int gpr_cv_wait(gpr_cv *cv, gpr_mu *mu, gpr_timespec abs_deadline) { int timeout = 0; DWORD timeout_max_ms; mu->locked = 0; - if (gpr_time_cmp(abs_deadline, gpr_inf_future) == 0) { + if (gpr_time_cmp(abs_deadline, gpr_inf_future(GPR_CLOCK_REALTIME)) == 0) { SleepConditionVariableCS(cv, &mu->cs, INFINITE); } else { - gpr_timespec now = gpr_now(); + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); gpr_int64 now_ms = now.tv_sec * 1000 + now.tv_nsec / 1000000; gpr_int64 deadline_ms = abs_deadline.tv_sec * 1000 + abs_deadline.tv_nsec / 1000000; diff --git a/src/core/support/time.c b/src/core/support/time.c index d47b08b2664..570f195bd11 100644 --- a/src/core/support/time.c +++ b/src/core/support/time.c @@ -41,6 +41,7 @@ int gpr_time_cmp(gpr_timespec a, gpr_timespec b) { int cmp = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec); + GPR_ASSERT(a.clock_type == b.clock_type); if (cmp == 0) { cmp = (a.tv_nsec > b.tv_nsec) - (a.tv_nsec < b.tv_nsec); } @@ -71,19 +72,40 @@ gpr_timespec gpr_time_max(gpr_timespec a, gpr_timespec b) { ((t)(TYPE_IS_SIGNED(t) ? (TOP_BIT_OF_TYPE(t) - 1) \ : ((TOP_BIT_OF_TYPE(t) - 1) << 1) + 1)) -const gpr_timespec gpr_time_0 = {0, 0}; -const gpr_timespec gpr_inf_future = {TYPE_MAX(time_t), 0}; -const gpr_timespec gpr_inf_past = {TYPE_MIN(time_t), 0}; +gpr_timespec gpr_time_0(gpr_clock_type type) { + gpr_timespec out; + out.tv_sec = 0; + out.tv_nsec = 0; + out.clock_type = type; + return out; +} + +gpr_timespec gpr_inf_future(gpr_clock_type type) { + gpr_timespec out; + out.tv_sec = TYPE_MAX(time_t); + out.tv_nsec = 0; + out.clock_type = type; + return out; +} + +gpr_timespec gpr_inf_past(gpr_clock_type type) { + gpr_timespec out; + out.tv_sec = TYPE_MIN(time_t); + out.tv_nsec = 0; + out.clock_type = type; + return out; +} /* TODO(ctiller): consider merging _nanos, _micros, _millis into a single function for maintainability. Similarly for _seconds, _minutes, and _hours */ -gpr_timespec gpr_time_from_nanos(long ns) { +gpr_timespec gpr_time_from_nanos(long ns, gpr_clock_type type) { gpr_timespec result; + result.clock_type = type; if (ns == LONG_MAX) { - result = gpr_inf_future; + result = gpr_inf_future(type); } else if (ns == LONG_MIN) { - result = gpr_inf_past; + result = gpr_inf_past(type); } else if (ns >= 0) { result.tv_sec = ns / GPR_NS_PER_SEC; result.tv_nsec = (int)(ns - result.tv_sec * GPR_NS_PER_SEC); @@ -95,12 +117,13 @@ gpr_timespec gpr_time_from_nanos(long ns) { return result; } -gpr_timespec gpr_time_from_micros(long us) { +gpr_timespec gpr_time_from_micros(long us, gpr_clock_type type) { gpr_timespec result; + result.clock_type = type; if (us == LONG_MAX) { - result = gpr_inf_future; + result = gpr_inf_future(type); } else if (us == LONG_MIN) { - result = gpr_inf_past; + result = gpr_inf_past(type); } else if (us >= 0) { result.tv_sec = us / 1000000; result.tv_nsec = (int)((us - result.tv_sec * 1000000) * 1000); @@ -112,12 +135,13 @@ gpr_timespec gpr_time_from_micros(long us) { return result; } -gpr_timespec gpr_time_from_millis(long ms) { +gpr_timespec gpr_time_from_millis(long ms, gpr_clock_type type) { gpr_timespec result; + result.clock_type = type; if (ms == LONG_MAX) { - result = gpr_inf_future; + result = gpr_inf_future(type); } else if (ms == LONG_MIN) { - result = gpr_inf_past; + result = gpr_inf_past(type); } else if (ms >= 0) { result.tv_sec = ms / 1000; result.tv_nsec = (int)((ms - result.tv_sec * 1000) * 1000000); @@ -129,12 +153,13 @@ gpr_timespec gpr_time_from_millis(long ms) { return result; } -gpr_timespec gpr_time_from_seconds(long s) { +gpr_timespec gpr_time_from_seconds(long s, gpr_clock_type type) { gpr_timespec result; + result.clock_type = type; if (s == LONG_MAX) { - result = gpr_inf_future; + result = gpr_inf_future(type); } else if (s == LONG_MIN) { - result = gpr_inf_past; + result = gpr_inf_past(type); } else { result.tv_sec = s; result.tv_nsec = 0; @@ -142,12 +167,13 @@ gpr_timespec gpr_time_from_seconds(long s) { return result; } -gpr_timespec gpr_time_from_minutes(long m) { +gpr_timespec gpr_time_from_minutes(long m, gpr_clock_type type) { gpr_timespec result; + result.clock_type = type; if (m >= LONG_MAX / 60) { - result = gpr_inf_future; + result = gpr_inf_future(type); } else if (m <= LONG_MIN / 60) { - result = gpr_inf_past; + result = gpr_inf_past(type); } else { result.tv_sec = m * 60; result.tv_nsec = 0; @@ -155,12 +181,13 @@ gpr_timespec gpr_time_from_minutes(long m) { return result; } -gpr_timespec gpr_time_from_hours(long h) { +gpr_timespec gpr_time_from_hours(long h, gpr_clock_type type) { gpr_timespec result; + result.clock_type = type; if (h >= LONG_MAX / 3600) { - result = gpr_inf_future; + result = gpr_inf_future(type); } else if (h <= LONG_MIN / 3600) { - result = gpr_inf_past; + result = gpr_inf_past(type); } else { result.tv_sec = h * 3600; result.tv_nsec = 0; @@ -171,6 +198,8 @@ gpr_timespec gpr_time_from_hours(long h) { gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b) { gpr_timespec sum; int inc = 0; + GPR_ASSERT(b.clock_type == GPR_TIMESPAN); + sum.clock_type = a.clock_type; sum.tv_nsec = a.tv_nsec + b.tv_nsec; if (sum.tv_nsec >= GPR_NS_PER_SEC) { sum.tv_nsec -= GPR_NS_PER_SEC; @@ -180,14 +209,14 @@ gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b) { sum = a; } else if (b.tv_sec == TYPE_MAX(time_t) || (b.tv_sec >= 0 && a.tv_sec >= TYPE_MAX(time_t) - b.tv_sec)) { - sum = gpr_inf_future; + sum = gpr_inf_future(sum.clock_type); } else if (b.tv_sec == TYPE_MIN(time_t) || (b.tv_sec <= 0 && a.tv_sec <= TYPE_MIN(time_t) - b.tv_sec)) { - sum = gpr_inf_past; + sum = gpr_inf_past(sum.clock_type); } else { sum.tv_sec = a.tv_sec + b.tv_sec; if (inc != 0 && sum.tv_sec == TYPE_MAX(time_t) - 1) { - sum = gpr_inf_future; + sum = gpr_inf_future(sum.clock_type); } else { sum.tv_sec += inc; } @@ -198,6 +227,12 @@ gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b) { gpr_timespec gpr_time_sub(gpr_timespec a, gpr_timespec b) { gpr_timespec diff; int dec = 0; + if (b.clock_type == GPR_TIMESPAN) { + diff.clock_type = a.clock_type; + } else { + GPR_ASSERT(a.clock_type == b.clock_type); + diff.clock_type = GPR_TIMESPAN; + } diff.tv_nsec = a.tv_nsec - b.tv_nsec; if (diff.tv_nsec < 0) { diff.tv_nsec += GPR_NS_PER_SEC; @@ -207,14 +242,14 @@ gpr_timespec gpr_time_sub(gpr_timespec a, gpr_timespec b) { diff = a; } else if (b.tv_sec == TYPE_MIN(time_t) || (b.tv_sec <= 0 && a.tv_sec >= TYPE_MAX(time_t) + b.tv_sec)) { - diff = gpr_inf_future; + diff = gpr_inf_future(GPR_CLOCK_REALTIME); } else if (b.tv_sec == TYPE_MAX(time_t) || (b.tv_sec >= 0 && a.tv_sec <= TYPE_MIN(time_t) + b.tv_sec)) { - diff = gpr_inf_past; + diff = gpr_inf_past(GPR_CLOCK_REALTIME); } else { diff.tv_sec = a.tv_sec - b.tv_sec; if (dec != 0 && diff.tv_sec == TYPE_MIN(time_t) + 1) { - diff = gpr_inf_past; + diff = gpr_inf_past(GPR_CLOCK_REALTIME); } else { diff.tv_sec -= dec; } @@ -225,6 +260,9 @@ gpr_timespec gpr_time_sub(gpr_timespec a, gpr_timespec b) { int gpr_time_similar(gpr_timespec a, gpr_timespec b, gpr_timespec threshold) { int cmp_ab; + GPR_ASSERT(a.clock_type == b.clock_type); + GPR_ASSERT(threshold.clock_type == GPR_TIMESPAN); + cmp_ab = gpr_time_cmp(a, b); if (cmp_ab == 0) return 1; if (cmp_ab < 0) { diff --git a/src/core/support/time_posix.c b/src/core/support/time_posix.c index afb58ef2313..258b2e640e9 100644 --- a/src/core/support/time_posix.c +++ b/src/core/support/time_posix.c @@ -38,6 +38,7 @@ #include #include #include +#include #include static struct timespec timespec_from_gpr(gpr_timespec gts) { @@ -48,29 +49,65 @@ static struct timespec timespec_from_gpr(gpr_timespec gts) { } #if _POSIX_TIMERS > 0 -static gpr_timespec gpr_from_timespec(struct timespec ts) { +static gpr_timespec gpr_from_timespec(struct timespec ts, + gpr_clock_type clock) { gpr_timespec rv; rv.tv_sec = ts.tv_sec; rv.tv_nsec = (int)ts.tv_nsec; + rv.clock_type = clock; return rv; } -gpr_timespec gpr_now(void) { +/** maps gpr_clock_type --> clockid_t for clock_gettime */ +static clockid_t clockid_for_gpr_clock[] = {CLOCK_MONOTONIC, CLOCK_REALTIME}; + +void gpr_time_init(void) {} + +gpr_timespec gpr_now(gpr_clock_type clock) { struct timespec now; - clock_gettime(CLOCK_REALTIME, &now); - return gpr_from_timespec(now); + GPR_ASSERT(clock != GPR_TIMESPAN); + clock_gettime(clockid_for_gpr_clock[clock], &now); + return gpr_from_timespec(now, clock); } #else /* For some reason Apple's OSes haven't implemented clock_gettime. */ #include +#include +#include + +static double g_time_scale; +static uint64_t g_time_start; + +void gpr_time_init(void) { + mach_timebase_info_data_t tb = {0, 1}; + mach_timebase_info(&tb); + g_time_scale = tb.numer; + g_time_scale /= tb.denom; + g_time_start = mach_absolute_time(); +} -gpr_timespec gpr_now(void) { +gpr_timespec gpr_now(gpr_clock_type clock) { gpr_timespec now; struct timeval now_tv; - gettimeofday(&now_tv, NULL); - now.tv_sec = now_tv.tv_sec; - now.tv_nsec = now_tv.tv_usec * 1000; + double now_dbl; + + now.clock_type = clock; + switch (clock) { + case GPR_CLOCK_REALTIME: + gettimeofday(&now_tv, NULL); + now.tv_sec = now_tv.tv_sec; + now.tv_nsec = now_tv.tv_usec * 1000; + break; + case GPR_CLOCK_MONOTONIC: + now_dbl = (mach_absolute_time() - g_time_start) * g_time_scale; + now.tv_sec = now_dbl * 1e-9; + now.tv_nsec = now_dbl - now.tv_sec * 1e9; + break; + case GPR_TIMESPAN: + abort(); + } + return now; } #endif @@ -83,7 +120,7 @@ void gpr_sleep_until(gpr_timespec until) { for (;;) { /* We could simplify by using clock_nanosleep instead, but it might be * slightly less portable. */ - now = gpr_now(); + now = gpr_now(GPR_CLOCK_REALTIME); if (gpr_time_cmp(until, now) <= 0) { return; } diff --git a/src/core/support/time_win32.c b/src/core/support/time_win32.c index 9db267c01b0..238cd07ebc7 100644 --- a/src/core/support/time_win32.c +++ b/src/core/support/time_win32.c @@ -40,12 +40,35 @@ #include #include -gpr_timespec gpr_now(void) { +static LARGE_INTEGER g_start_time; +static double g_time_scale; + +void gpr_time_init(void) { + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + QueryPerformanceCounter(&g_start_time); + g_time_scale = 1.0 / frequency.QuadPart; +} + +gpr_timespec gpr_now(gpr_clock_type clock) { gpr_timespec now_tv; struct _timeb now_tb; - _ftime_s(&now_tb); - now_tv.tv_sec = now_tb.time; - now_tv.tv_nsec = now_tb.millitm * 1000000; + LARGE_INTEGER timestamp; + double now_dbl; + now_tv.clock_type = clock; + switch (clock) { + case GPR_CLOCK_REALTIME: + _ftime_s(&now_tb); + now_tv.tv_sec = now_tb.time; + now_tv.tv_nsec = now_tb.millitm * 1000000; + break; + case GPR_CLOCK_MONOTONIC: + QueryPerformanceCounter(×tamp); + now_dbl = (timestamp.QuadPart - g_start_time.QuadPart) * g_time_scale; + now_tv.tv_sec = (time_t)now_dbl; + now_tv.tv_nsec = (int)((now_dbl - (double)now_tv.tv_sec) * 1e9); + break; + } return now_tv; } @@ -57,13 +80,14 @@ void gpr_sleep_until(gpr_timespec until) { for (;;) { /* We could simplify by using clock_nanosleep instead, but it might be * slightly less portable. */ - now = gpr_now(); + now = gpr_now(GPR_CLOCK_REALTIME); if (gpr_time_cmp(until, now) <= 0) { return; } delta = gpr_time_sub(until, now); - sleep_millis = (DWORD)delta.tv_sec * GPR_MS_PER_SEC + delta.tv_nsec / GPR_NS_PER_MS; + sleep_millis = + (DWORD)delta.tv_sec * GPR_MS_PER_SEC + delta.tv_nsec / GPR_NS_PER_MS; Sleep(sleep_millis); } } diff --git a/src/core/surface/byte_buffer_queue.c b/src/core/surface/byte_buffer_queue.c index 7c31bfe5da2..e47dc4f4ce6 100644 --- a/src/core/surface/byte_buffer_queue.c +++ b/src/core/surface/byte_buffer_queue.c @@ -62,6 +62,7 @@ int grpc_bbq_empty(grpc_byte_buffer_queue *q) { } void grpc_bbq_push(grpc_byte_buffer_queue *q, grpc_byte_buffer *buffer) { + q->bytes += grpc_byte_buffer_length(buffer); bba_push(&q->filling, buffer); } @@ -72,8 +73,11 @@ void grpc_bbq_flush(grpc_byte_buffer_queue *q) { } } +size_t grpc_bbq_bytes(grpc_byte_buffer_queue *q) { return q->bytes; } + grpc_byte_buffer *grpc_bbq_pop(grpc_byte_buffer_queue *q) { grpc_bbq_array temp_array; + grpc_byte_buffer *out; if (q->drain_pos == q->draining.count) { if (q->filling.count == 0) { @@ -87,5 +91,7 @@ grpc_byte_buffer *grpc_bbq_pop(grpc_byte_buffer_queue *q) { q->draining = temp_array; } - return q->draining.data[q->drain_pos++]; + out = q->draining.data[q->drain_pos++]; + q->bytes -= grpc_byte_buffer_length(out); + return out; } diff --git a/src/core/surface/byte_buffer_queue.h b/src/core/surface/byte_buffer_queue.h index 32c57f87563..f01958984f9 100644 --- a/src/core/surface/byte_buffer_queue.h +++ b/src/core/surface/byte_buffer_queue.h @@ -49,6 +49,7 @@ typedef struct { size_t drain_pos; grpc_bbq_array filling; grpc_bbq_array draining; + size_t bytes; } grpc_byte_buffer_queue; void grpc_bbq_destroy(grpc_byte_buffer_queue *q); @@ -56,5 +57,6 @@ grpc_byte_buffer *grpc_bbq_pop(grpc_byte_buffer_queue *q); void grpc_bbq_flush(grpc_byte_buffer_queue *q); int grpc_bbq_empty(grpc_byte_buffer_queue *q); void grpc_bbq_push(grpc_byte_buffer_queue *q, grpc_byte_buffer *bb); +size_t grpc_bbq_bytes(grpc_byte_buffer_queue *q); #endif /* GRPC_INTERNAL_CORE_SURFACE_BYTE_BUFFER_QUEUE_H */ diff --git a/src/core/surface/call.c b/src/core/surface/call.c index fb3b0b19189..1146d839821 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -49,6 +49,17 @@ #include #include +/** The maximum number of completions possible. + Based upon the maximum number of individually queueable ops in the batch + api: + - initial metadata send + - message send + - status/close send (depending on client/server) + - initial metadata recv + - message recv + - status/close recv (depending on client/server) */ +#define MAX_CONCURRENT_COMPLETIONS 6 + typedef enum { REQ_INITIAL = 0, REQ_READY, REQ_DONE } req_state; typedef enum { @@ -135,6 +146,7 @@ struct grpc_call { grpc_mdctx *metadata_context; /* TODO(ctiller): share with cq if possible? */ gpr_mu mu; + gpr_mu completion_mu; /* how far through the stream have we read? */ read_state read_state; @@ -162,6 +174,8 @@ struct grpc_call { gpr_uint8 error_status_set; /** should the alarm be cancelled */ gpr_uint8 cancel_alarm; + /** bitmask of allocated completion events in completions */ + gpr_uint8 allocated_completions; /* flags with bits corresponding to write states allowing us to determine what was sent */ @@ -250,6 +264,9 @@ struct grpc_call { grpc_iomgr_closure on_done_recv; grpc_iomgr_closure on_done_send; grpc_iomgr_closure on_done_bind; + + /** completion events - for completion queue use */ + grpc_cq_completion completions[MAX_CONCURRENT_COMPLETIONS]; }; #define CALL_STACK_FROM_CALL(call) ((grpc_call_stack *)((call) + 1)) @@ -286,6 +303,7 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_completion_queue *cq, gpr_malloc(sizeof(grpc_call) + channel_stack->call_stack_size); memset(call, 0, sizeof(grpc_call)); gpr_mu_init(&call->mu); + gpr_mu_init(&call->completion_mu); call->channel = channel; call->cq = cq; if (cq) { @@ -329,7 +347,7 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_completion_queue *cq, } grpc_call_stack_init(channel_stack, server_transport_data, initial_op_ptr, CALL_STACK_FROM_CALL(call)); - if (gpr_time_cmp(send_deadline, gpr_inf_future) != 0) { + if (gpr_time_cmp(send_deadline, gpr_inf_future(GPR_CLOCK_REALTIME)) != 0) { set_deadline_alarm(call, send_deadline); } return call; @@ -349,6 +367,29 @@ grpc_completion_queue *grpc_call_get_completion_queue(grpc_call *call) { return call->cq; } +static grpc_cq_completion *allocate_completion(grpc_call *call) { + gpr_uint8 i; + gpr_mu_lock(&call->completion_mu); + for (i = 0; i < GPR_ARRAY_SIZE(call->completions); i++) { + if (call->allocated_completions & (1u << i)) { + continue; + } + call->allocated_completions |= 1u << i; + gpr_mu_unlock(&call->completion_mu); + return &call->completions[i]; + } + gpr_log(GPR_ERROR, "should never reach here"); + abort(); +} + +static void done_completion(void *call, grpc_cq_completion *completion) { + grpc_call *c = call; + gpr_mu_lock(&c->completion_mu); + c->allocated_completions &= ~(1u << (completion - c->completions)); + gpr_mu_unlock(&c->completion_mu); + GRPC_CALL_INTERNAL_UNREF(c, "completion", 1); +} + #ifdef GRPC_CALL_REF_COUNT_DEBUG void grpc_call_internal_ref(grpc_call *c, const char *reason) { gpr_log(GPR_DEBUG, "CALL: ref %p %d -> %d [%s]", c, @@ -365,6 +406,7 @@ static void destroy_call(void *call, int ignored_success) { grpc_call_stack_destroy(CALL_STACK_FROM_CALL(c)); GRPC_CHANNEL_INTERNAL_UNREF(c->channel, "call"); gpr_mu_destroy(&c->mu); + gpr_mu_destroy(&c->completion_mu); for (i = 0; i < STATUS_SOURCE_COUNT; i++) { if (c->status[i].details) { GRPC_MDSTR_UNREF(c->status[i].details); @@ -471,6 +513,8 @@ static void unlock(grpc_call *call) { int completing_requests = 0; int start_op = 0; int i; + const gpr_uint32 MAX_RECV_PEEK_AHEAD = 65536; + size_t buffered_bytes; int cancel_alarm = 0; memset(&op, 0, sizeof(op)); @@ -486,6 +530,17 @@ static void unlock(grpc_call *call) { op.recv_ops = &call->recv_ops; op.recv_state = &call->recv_state; op.on_done_recv = &call->on_done_recv; + if (grpc_bbq_empty(&call->incoming_queue) && call->reading_message) { + op.max_recv_bytes = call->incoming_message_length - + call->incoming_message.length + MAX_RECV_PEEK_AHEAD; + } else { + buffered_bytes = grpc_bbq_bytes(&call->incoming_queue); + if (buffered_bytes > MAX_RECV_PEEK_AHEAD) { + op.max_recv_bytes = 0; + } else { + op.max_recv_bytes = MAX_RECV_PEEK_AHEAD - buffered_bytes; + } + } call->receiving = 1; GRPC_CALL_INTERNAL_REF(call, "receiving"); start_op = 1; @@ -930,7 +985,7 @@ static int fill_send_ops(grpc_call *call, grpc_transport_stream_op *op) { mdb.list = chain_metadata_from_app(call, data.send_metadata.count, data.send_metadata.metadata); mdb.garbage.head = mdb.garbage.tail = NULL; - mdb.deadline = gpr_inf_future; + mdb.deadline = gpr_inf_future(GPR_CLOCK_REALTIME); /* send status */ /* TODO(ctiller): cache common status values */ data = call->request_data[GRPC_IOREQ_SEND_STATUS]; @@ -1188,7 +1243,8 @@ static void set_deadline_alarm(grpc_call *call, gpr_timespec deadline) { } GRPC_CALL_INTERNAL_REF(call, "alarm"); call->have_alarm = 1; - grpc_alarm_init(&call->alarm, deadline, call_alarm, call, gpr_now()); + grpc_alarm_init(&call->alarm, deadline, call_alarm, call, + gpr_now(GPR_CLOCK_REALTIME)); } /* we offset status by a small amount when storing it into transport metadata @@ -1282,7 +1338,7 @@ static void recv_metadata(grpc_call *call, grpc_metadata_batch *md) { l->md = 0; } } - if (gpr_time_cmp(md->deadline, gpr_inf_future) != 0) { + if (gpr_time_cmp(md->deadline, gpr_inf_future(GPR_CLOCK_REALTIME)) != 0) { set_deadline_alarm(call, md->deadline); } if (!is_trailing) { @@ -1316,11 +1372,13 @@ static void set_cancelled_value(grpc_status_code status, void *dest) { } static void finish_batch(grpc_call *call, int success, void *tag) { - grpc_cq_end_op(call->cq, tag, call, success); + grpc_cq_end_op(call->cq, tag, success, done_completion, call, + allocate_completion(call)); } static void finish_batch_with_close(grpc_call *call, int success, void *tag) { - grpc_cq_end_op(call->cq, tag, call, 1); + grpc_cq_end_op(call->cq, tag, 1, done_completion, call, + allocate_completion(call)); } static int are_write_flags_valid(gpr_uint32 flags) { @@ -1343,8 +1401,10 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, GRPC_CALL_LOG_BATCH(GPR_INFO, call, ops, nops, tag); if (nops == 0) { - grpc_cq_begin_op(call->cq, call); - grpc_cq_end_op(call->cq, tag, call, 1); + grpc_cq_begin_op(call->cq); + GRPC_CALL_INTERNAL_REF(call, "completion"); + grpc_cq_end_op(call->cq, tag, 1, done_completion, call, + allocate_completion(call)); return GRPC_CALL_OK; } @@ -1466,7 +1526,8 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, } } - grpc_cq_begin_op(call->cq, call); + GRPC_CALL_INTERNAL_REF(call, "completion"); + grpc_cq_begin_op(call->cq); return grpc_call_start_ioreq_and_call_back(call, reqs, out, finish_func, tag); } diff --git a/src/core/surface/channel.c b/src/core/surface/channel.c index eeae3b507cc..b7826d4dfc9 100644 --- a/src/core/surface/channel.c +++ b/src/core/surface/channel.c @@ -91,6 +91,7 @@ grpc_channel *grpc_channel_create_from_filters( size_t size = sizeof(grpc_channel) + grpc_channel_stack_size(filters, num_filters); grpc_channel *channel = gpr_malloc(size); + memset(channel, 0, sizeof(*channel)); GPR_ASSERT(grpc_is_initialized() && "call grpc_init()"); channel->is_client = is_client; /* decremented by grpc_channel_destroy */ diff --git a/src/core/surface/completion_queue.c b/src/core/surface/completion_queue.c index 030a8b4e6fc..84844182477 100644 --- a/src/core/surface/completion_queue.c +++ b/src/core/surface/completion_queue.c @@ -45,34 +45,20 @@ #include #include -#define NUM_TAG_BUCKETS 31 - -/* A single event: extends grpc_event to form a linked list with a destruction - function (on_finish) that is hidden from outside this module */ -typedef struct event { - grpc_event base; - struct event *queue_next; - struct event *queue_prev; - struct event *bucket_next; - struct event *bucket_prev; -} event; - /* Completion queue structure */ struct grpc_completion_queue { - /* When refs drops to zero, we are in shutdown mode, and will be destroyable - once all queued events are drained */ - gpr_refcount refs; - /* Once owning_refs drops to zero, we will destroy the cq */ + /** completed events */ + grpc_cq_completion completed_head; + grpc_cq_completion *completed_tail; + /** Number of pending events (+1 if we're not shutdown) */ + gpr_refcount pending_events; + /** Once owning_refs drops to zero, we will destroy the cq */ gpr_refcount owning_refs; - /* the set of low level i/o things that concern this cq */ + /** the set of low level i/o things that concern this cq */ grpc_pollset pollset; - /* 0 initially, 1 once we've begun shutting down */ + /** 0 initially, 1 once we've begun shutting down */ int shutdown; int shutdown_called; - /* Head of a linked list of queued events (prev points to the last element) */ - event *queue; - /* Fixed size chained hash table of events for pluck() */ - event *buckets[NUM_TAG_BUCKETS]; int is_server_cq; }; @@ -80,19 +66,20 @@ grpc_completion_queue *grpc_completion_queue_create(void) { grpc_completion_queue *cc = gpr_malloc(sizeof(grpc_completion_queue)); memset(cc, 0, sizeof(*cc)); /* Initial ref is dropped by grpc_completion_queue_shutdown */ - gpr_ref_init(&cc->refs, 1); + gpr_ref_init(&cc->pending_events, 1); /* One for destroy(), one for pollset_shutdown */ gpr_ref_init(&cc->owning_refs, 2); grpc_pollset_init(&cc->pollset); + cc->completed_tail = &cc->completed_head; + cc->completed_head.next = (gpr_uintptr)cc->completed_tail; return cc; } #ifdef GRPC_CQ_REF_COUNT_DEBUG void grpc_cq_internal_ref(grpc_completion_queue *cc, const char *reason, const char *file, int line) { - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "CQ:%p ref %d -> %d %s", - cc, (int)cc->owning_refs.count, (int)cc->owning_refs.count + 1, - reason); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "CQ:%p ref %d -> %d %s", cc, + (int)cc->owning_refs.count, (int)cc->owning_refs.count + 1, reason); #else void grpc_cq_internal_ref(grpc_completion_queue *cc) { #endif @@ -107,186 +94,135 @@ static void on_pollset_destroy_done(void *arg) { #ifdef GRPC_CQ_REF_COUNT_DEBUG void grpc_cq_internal_unref(grpc_completion_queue *cc, const char *reason, const char *file, int line) { - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "CQ:%p unref %d -> %d %s", - cc, (int)cc->owning_refs.count, (int)cc->owning_refs.count - 1, - reason); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "CQ:%p unref %d -> %d %s", cc, + (int)cc->owning_refs.count, (int)cc->owning_refs.count - 1, reason); #else void grpc_cq_internal_unref(grpc_completion_queue *cc) { #endif if (gpr_unref(&cc->owning_refs)) { - GPR_ASSERT(cc->queue == NULL); + GPR_ASSERT(cc->completed_head.next == (gpr_uintptr)&cc->completed_head); grpc_pollset_destroy(&cc->pollset); gpr_free(cc); } } -/* Create and append an event to the queue. Returns the event so that its data - members can be filled in. - Requires GRPC_POLLSET_MU(&cc->pollset) locked. */ -static event *add_locked(grpc_completion_queue *cc, grpc_completion_type type, - void *tag, grpc_call *call) { - event *ev = gpr_malloc(sizeof(event)); - gpr_uintptr bucket = ((gpr_uintptr)tag) % NUM_TAG_BUCKETS; - ev->base.type = type; - ev->base.tag = tag; - if (cc->queue == NULL) { - cc->queue = ev->queue_next = ev->queue_prev = ev; - } else { - ev->queue_next = cc->queue; - ev->queue_prev = cc->queue->queue_prev; - ev->queue_next->queue_prev = ev->queue_prev->queue_next = ev; - } - if (cc->buckets[bucket] == NULL) { - cc->buckets[bucket] = ev->bucket_next = ev->bucket_prev = ev; - } else { - ev->bucket_next = cc->buckets[bucket]; - ev->bucket_prev = cc->buckets[bucket]->bucket_prev; - ev->bucket_next->bucket_prev = ev->bucket_prev->bucket_next = ev; - } - grpc_pollset_kick(&cc->pollset); - return ev; -} - -void grpc_cq_begin_op(grpc_completion_queue *cc, grpc_call *call) { - gpr_ref(&cc->refs); - if (call) GRPC_CALL_INTERNAL_REF(call, "cq"); +void grpc_cq_begin_op(grpc_completion_queue *cc) { + gpr_ref(&cc->pending_events); } /* Signal the end of an operation - if this is the last waiting-to-be-queued event, then enter shutdown mode */ -void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, grpc_call *call, - int success) { - event *ev; - int shutdown = 0; +/* Queue a GRPC_OP_COMPLETED operation */ +void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, int success, + void (*done)(void *done_arg, grpc_cq_completion *storage), + void *done_arg, grpc_cq_completion *storage) { + int shutdown; + + storage->tag = tag; + storage->done = done; + storage->done_arg = done_arg; + storage->next = + ((gpr_uintptr)&cc->completed_head) | ((gpr_uintptr)(success != 0)); + gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); - ev = add_locked(cc, GRPC_OP_COMPLETE, tag, call); - ev->base.success = success; - if (gpr_unref(&cc->refs)) { + shutdown = gpr_unref(&cc->pending_events); + if (!shutdown) { + cc->completed_tail->next = + ((gpr_uintptr)storage) | (1u & (gpr_uintptr)cc->completed_tail->next); + cc->completed_tail = storage; + grpc_pollset_kick(&cc->pollset); + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); + } else { + cc->completed_tail->next = + ((gpr_uintptr)storage) | (1u & (gpr_uintptr)cc->completed_tail->next); + cc->completed_tail = storage; GPR_ASSERT(!cc->shutdown); GPR_ASSERT(cc->shutdown_called); cc->shutdown = 1; - shutdown = 1; - } - gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); - if (call) GRPC_CALL_INTERNAL_UNREF(call, "cq", 0); - if (shutdown) { + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); grpc_pollset_shutdown(&cc->pollset, on_pollset_destroy_done, cc); } } -/* Create a GRPC_QUEUE_SHUTDOWN event without queuing it anywhere */ -static event *create_shutdown_event(void) { - event *ev = gpr_malloc(sizeof(event)); - ev->base.type = GRPC_QUEUE_SHUTDOWN; - ev->base.tag = NULL; - return ev; -} - grpc_event grpc_completion_queue_next(grpc_completion_queue *cc, gpr_timespec deadline) { - event *ev = NULL; grpc_event ret; GRPC_CQ_INTERNAL_REF(cc, "next"); gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); for (;;) { - if (cc->queue != NULL) { - gpr_uintptr bucket; - ev = cc->queue; - bucket = ((gpr_uintptr)ev->base.tag) % NUM_TAG_BUCKETS; - cc->queue = ev->queue_next; - ev->queue_next->queue_prev = ev->queue_prev; - ev->queue_prev->queue_next = ev->queue_next; - ev->bucket_next->bucket_prev = ev->bucket_prev; - ev->bucket_prev->bucket_next = ev->bucket_next; - if (ev == cc->buckets[bucket]) { - cc->buckets[bucket] = ev->bucket_next; - if (ev == cc->buckets[bucket]) { - cc->buckets[bucket] = NULL; - } - } - if (cc->queue == ev) { - cc->queue = NULL; + if (cc->completed_tail != &cc->completed_head) { + grpc_cq_completion *c = (grpc_cq_completion *)cc->completed_head.next; + cc->completed_head.next = c->next & ~(gpr_uintptr)1; + if (c == cc->completed_tail) { + cc->completed_tail = &cc->completed_head; } + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); + ret.type = GRPC_OP_COMPLETE; + ret.success = c->next & 1u; + ret.tag = c->tag; + c->done(c->done_arg, c); break; } if (cc->shutdown) { - ev = create_shutdown_event(); + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); + memset(&ret, 0, sizeof(ret)); + ret.type = GRPC_QUEUE_SHUTDOWN; break; } if (!grpc_pollset_work(&cc->pollset, deadline)) { gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); memset(&ret, 0, sizeof(ret)); ret.type = GRPC_QUEUE_TIMEOUT; - GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); - GRPC_CQ_INTERNAL_UNREF(cc, "next"); - return ret; + break; } } - gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); - ret = ev->base; - gpr_free(ev); GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); GRPC_CQ_INTERNAL_UNREF(cc, "next"); return ret; } -static event *pluck_event(grpc_completion_queue *cc, void *tag) { - gpr_uintptr bucket = ((gpr_uintptr)tag) % NUM_TAG_BUCKETS; - event *ev = cc->buckets[bucket]; - if (ev == NULL) return NULL; - do { - if (ev->base.tag == tag) { - ev->queue_next->queue_prev = ev->queue_prev; - ev->queue_prev->queue_next = ev->queue_next; - ev->bucket_next->bucket_prev = ev->bucket_prev; - ev->bucket_prev->bucket_next = ev->bucket_next; - if (ev == cc->buckets[bucket]) { - cc->buckets[bucket] = ev->bucket_next; - if (ev == cc->buckets[bucket]) { - cc->buckets[bucket] = NULL; - } - } - if (cc->queue == ev) { - cc->queue = ev->queue_next; - if (cc->queue == ev) { - cc->queue = NULL; - } - } - return ev; - } - ev = ev->bucket_next; - } while (ev != cc->buckets[bucket]); - return NULL; -} - grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag, gpr_timespec deadline) { - event *ev = NULL; grpc_event ret; + grpc_cq_completion *c; + grpc_cq_completion *prev; GRPC_CQ_INTERNAL_REF(cc, "pluck"); gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); for (;;) { - if ((ev = pluck_event(cc, tag))) { - break; + prev = &cc->completed_head; + while ((c = (grpc_cq_completion *)(prev->next & ~(gpr_uintptr)1)) != + &cc->completed_head) { + if (c->tag == tag) { + prev->next = + (prev->next & (gpr_uintptr)1) | (c->next & ~(gpr_uintptr)1); + if (c == cc->completed_tail) { + cc->completed_tail = prev; + } + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); + ret.type = GRPC_OP_COMPLETE; + ret.success = c->next & 1u; + ret.tag = c->tag; + c->done(c->done_arg, c); + goto done; + } + prev = c; } if (cc->shutdown) { - ev = create_shutdown_event(); + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); + memset(&ret, 0, sizeof(ret)); + ret.type = GRPC_QUEUE_SHUTDOWN; break; } if (!grpc_pollset_work(&cc->pollset, deadline)) { gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); memset(&ret, 0, sizeof(ret)); ret.type = GRPC_QUEUE_TIMEOUT; - GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); - GRPC_CQ_INTERNAL_UNREF(cc, "pluck"); - return ret; + break; } } - gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); - ret = ev->base; - gpr_free(ev); +done: GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); GRPC_CQ_INTERNAL_UNREF(cc, "pluck"); return ret; @@ -303,7 +239,7 @@ void grpc_completion_queue_shutdown(grpc_completion_queue *cc) { cc->shutdown_called = 1; gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); - if (gpr_unref(&cc->refs)) { + if (gpr_unref(&cc->pending_events)) { gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); GPR_ASSERT(!cc->shutdown); cc->shutdown = 1; @@ -325,7 +261,8 @@ void grpc_cq_hack_spin_pollset(grpc_completion_queue *cc) { gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); grpc_pollset_kick(&cc->pollset); grpc_pollset_work(&cc->pollset, - gpr_time_add(gpr_now(), gpr_time_from_millis(100))); + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), + gpr_time_from_millis(100, GPR_TIMESPAN))); gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); } diff --git a/src/core/surface/completion_queue.h b/src/core/surface/completion_queue.h index 1b9010f4623..f944f48d8e4 100644 --- a/src/core/surface/completion_queue.h +++ b/src/core/surface/completion_queue.h @@ -39,6 +39,17 @@ #include "src/core/iomgr/pollset.h" #include +typedef struct grpc_cq_completion { + /** user supplied tag */ + void *tag; + /** done callback - called when this queue element is no longer + needed by the completion queue */ + void (*done)(void *done_arg, struct grpc_cq_completion *c); + void *done_arg; + /** next pointer; low bit is used to indicate success or not */ + gpr_uintptr next; +} grpc_cq_completion; + #ifdef GRPC_CQ_REF_COUNT_DEBUG void grpc_cq_internal_ref(grpc_completion_queue *cc, const char *reason, const char *file, int line); @@ -57,11 +68,12 @@ void grpc_cq_internal_unref(grpc_completion_queue *cc); /* Flag that an operation is beginning: the completion channel will not finish shutdown until a corrensponding grpc_cq_end_* call is made */ -void grpc_cq_begin_op(grpc_completion_queue *cc, grpc_call *call); +void grpc_cq_begin_op(grpc_completion_queue *cc); /* Queue a GRPC_OP_COMPLETED operation */ -void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, grpc_call *call, - int success); +void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, int success, + void (*done)(void *done_arg, grpc_cq_completion *storage), + void *done_arg, grpc_cq_completion *storage); grpc_pollset *grpc_cq_pollset(grpc_completion_queue *cc); diff --git a/src/core/surface/init.c b/src/core/surface/init.c index 3847ded28c8..04e27d30ac7 100644 --- a/src/core/surface/init.c +++ b/src/core/surface/init.c @@ -35,6 +35,7 @@ #include #include +#include #include "src/core/channel/channel_stack.h" #include "src/core/client_config/resolver_registry.h" #include "src/core/client_config/resolvers/dns_resolver.h" @@ -64,6 +65,7 @@ void grpc_init(void) { gpr_mu_lock(&g_init_mu); if (++g_initializations == 1) { + gpr_time_init(); grpc_resolver_registry_init("dns:///"); grpc_register_resolver_type("dns", grpc_dns_resolver_factory_create()); #ifdef GPR_POSIX_SOCKET diff --git a/src/core/surface/lame_client.c b/src/core/surface/lame_client.c index 3dd56fe5a96..3f2bb5c8a98 100644 --- a/src/core/surface/lame_client.c +++ b/src/core/surface/lame_client.c @@ -72,7 +72,7 @@ static void lame_start_transport_stream_op(grpc_call_element *elem, mdb.list.head = &calld->status; mdb.list.tail = &calld->details; mdb.garbage.head = mdb.garbage.tail = NULL; - mdb.deadline = gpr_inf_future; + mdb.deadline = gpr_inf_future(GPR_CLOCK_REALTIME); grpc_sopb_add_metadata(op->recv_ops, mdb); *op->recv_state = GRPC_STREAM_CLOSED; op->on_done_recv->cb(op->on_done_recv->cb_arg, 1); diff --git a/src/core/surface/secure_channel_create.c b/src/core/surface/secure_channel_create.c index 34ee3f8400f..f3c7d8397b7 100644 --- a/src/core/surface/secure_channel_create.c +++ b/src/core/surface/secure_channel_create.c @@ -75,6 +75,7 @@ static void connector_unref(grpc_connector *con) { static void on_secure_transport_setup_done(void *arg, grpc_security_status status, + grpc_endpoint *wrapped_endpoint, grpc_endpoint *secure_endpoint) { connector *c = arg; grpc_iomgr_closure *notify; diff --git a/src/core/surface/server.c b/src/core/surface/server.c index adc7ef8efe9..fa120088e1f 100644 --- a/src/core/surface/server.c +++ b/src/core/surface/server.c @@ -36,22 +36,22 @@ #include #include +#include +#include +#include +#include + #include "src/core/channel/census_filter.h" #include "src/core/channel/channel_args.h" #include "src/core/channel/connected_channel.h" #include "src/core/iomgr/iomgr.h" +#include "src/core/support/stack_lockfree.h" #include "src/core/support/string.h" #include "src/core/surface/call.h" #include "src/core/surface/channel.h" #include "src/core/surface/completion_queue.h" #include "src/core/surface/init.h" #include "src/core/transport/metadata.h" -#include -#include -#include -#include - -typedef enum { PENDING_START, CALL_LIST_COUNT } call_list; typedef struct listener { void *arg; @@ -72,12 +72,14 @@ typedef struct { typedef enum { BATCH_CALL, REGISTERED_CALL } requested_call_type; -typedef struct { +typedef struct requested_call { requested_call_type type; void *tag; + grpc_server *server; grpc_completion_queue *cq_bound_to_call; grpc_completion_queue *cq_for_notification; grpc_call **call; + grpc_cq_completion completion; union { struct { grpc_call_details *details; @@ -92,20 +94,6 @@ typedef struct { } data; } requested_call; -typedef struct { - requested_call *calls; - size_t count; - size_t capacity; -} requested_call_array; - -struct registered_method { - char *method; - char *host; - call_data *pending; - requested_call_array requested; - registered_method *next; -}; - typedef struct channel_registered_method { registered_method *server_registered_method; grpc_mdstr *method; @@ -131,46 +119,9 @@ struct channel_data { typedef struct shutdown_tag { void *tag; grpc_completion_queue *cq; + grpc_cq_completion completion; } shutdown_tag; -struct grpc_server { - size_t channel_filter_count; - const grpc_channel_filter **channel_filters; - grpc_channel_args *channel_args; - - grpc_completion_queue **cqs; - grpc_pollset **pollsets; - size_t cq_count; - - /* The two following mutexes control access to server-state - mu_global controls access to non-call-related state (e.g., channel state) - mu_call controls access to call-related state (e.g., the call lists) - - If they are ever required to be nested, you must lock mu_global - before mu_call. This is currently used in shutdown processing - (grpc_server_shutdown_and_notify and maybe_finish_shutdown) */ - gpr_mu mu_global; /* mutex for server and channel state */ - gpr_mu mu_call; /* mutex for call-specific state */ - - registered_method *registered_methods; - requested_call_array requested_calls; - - gpr_uint8 shutdown; - gpr_uint8 shutdown_published; - size_t num_shutdown_tags; - shutdown_tag *shutdown_tags; - - call_data *lists[CALL_LIST_COUNT]; - channel_data root_channel_data; - - listener *listeners; - int listeners_destroyed; - gpr_refcount internal_refcount; - - /** when did we print the last shutdown progress message */ - gpr_timespec last_shutdown_message_time; -}; - typedef enum { /* waiting for metadata */ NOT_STARTED, @@ -182,6 +133,8 @@ typedef enum { ZOMBIED } call_state; +typedef struct request_matcher request_matcher; + struct call_data { grpc_call *call; @@ -204,8 +157,20 @@ struct call_data { grpc_iomgr_closure server_on_recv; grpc_iomgr_closure kill_zombie_closure; - call_data **root[CALL_LIST_COUNT]; - call_link links[CALL_LIST_COUNT]; + call_data *pending_next; +}; + +struct request_matcher { + call_data *pending_head; + call_data *pending_tail; + gpr_stack_lockfree *requests; +}; + +struct registered_method { + char *method; + char *host; + request_matcher request_matcher; + registered_method *next; }; typedef struct { @@ -213,6 +178,48 @@ typedef struct { size_t num_channels; } channel_broadcaster; +struct grpc_server { + size_t channel_filter_count; + const grpc_channel_filter **channel_filters; + grpc_channel_args *channel_args; + + grpc_completion_queue **cqs; + grpc_pollset **pollsets; + size_t cq_count; + + /* The two following mutexes control access to server-state + mu_global controls access to non-call-related state (e.g., channel state) + mu_call controls access to call-related state (e.g., the call lists) + + If they are ever required to be nested, you must lock mu_global + before mu_call. This is currently used in shutdown processing + (grpc_server_shutdown_and_notify and maybe_finish_shutdown) */ + gpr_mu mu_global; /* mutex for server and channel state */ + gpr_mu mu_call; /* mutex for call-specific state */ + + registered_method *registered_methods; + request_matcher unregistered_request_matcher; + /** free list of available requested_calls indices */ + gpr_stack_lockfree *request_freelist; + /** requested call backing data */ + requested_call *requested_calls; + int max_requested_calls; + + gpr_atm shutdown_flag; + gpr_uint8 shutdown_published; + size_t num_shutdown_tags; + shutdown_tag *shutdown_tags; + + channel_data root_channel_data; + + listener *listeners; + int listeners_destroyed; + gpr_refcount internal_refcount; + + /** when did we print the last shutdown progress message */ + gpr_timespec last_shutdown_message_time; +}; + #define SERVER_FROM_CALL_ELEM(elem) \ (((channel_data *)(elem)->channel_data)->server) @@ -223,7 +230,9 @@ static void fail_call(grpc_server *server, requested_call *rc); hold mu_call */ static void maybe_finish_shutdown(grpc_server *server); -/* channel broadcaster */ +/* + * channel broadcaster + */ /* assumes server locked */ static void channel_broadcaster_init(grpc_server *s, channel_broadcaster *cb) { @@ -273,7 +282,8 @@ static void send_shutdown(grpc_channel *channel, int send_goaway, } static void channel_broadcaster_shutdown(channel_broadcaster *cb, - int send_goaway, int force_disconnect) { + int send_goaway, + int force_disconnect) { size_t i; for (i = 0; i < cb->num_channels; i++) { @@ -283,71 +293,44 @@ static void channel_broadcaster_shutdown(channel_broadcaster *cb, gpr_free(cb->channels); } -/* call list */ - -static int call_list_join(call_data **root, call_data *call, call_list list) { - GPR_ASSERT(!call->root[list]); - call->root[list] = root; - if (!*root) { - *root = call; - call->links[list].next = call->links[list].prev = call; - } else { - call->links[list].next = *root; - call->links[list].prev = (*root)->links[list].prev; - call->links[list].next->links[list].prev = - call->links[list].prev->links[list].next = call; - } - return 1; -} +/* + * request_matcher + */ -static call_data *call_list_remove_head(call_data **root, call_list list) { - call_data *out = *root; - if (out) { - out->root[list] = NULL; - if (out->links[list].next == out) { - *root = NULL; - } else { - *root = out->links[list].next; - out->links[list].next->links[list].prev = out->links[list].prev; - out->links[list].prev->links[list].next = out->links[list].next; - } - } - return out; +static void request_matcher_init(request_matcher *request_matcher, + int entries) { + memset(request_matcher, 0, sizeof(*request_matcher)); + request_matcher->requests = gpr_stack_lockfree_create(entries); } -static int call_list_remove(call_data *call, call_list list) { - call_data **root = call->root[list]; - if (root == NULL) return 0; - call->root[list] = NULL; - if (*root == call) { - *root = call->links[list].next; - if (*root == call) { - *root = NULL; - return 1; - } - } - GPR_ASSERT(*root != call); - call->links[list].next->links[list].prev = call->links[list].prev; - call->links[list].prev->links[list].next = call->links[list].next; - return 1; +static void request_matcher_destroy(request_matcher *request_matcher) { + GPR_ASSERT(gpr_stack_lockfree_pop(request_matcher->requests) == -1); + gpr_stack_lockfree_destroy(request_matcher->requests); } -static void requested_call_array_destroy(requested_call_array *array) { - gpr_free(array->calls); +static void kill_zombie(void *elem, int success) { + grpc_call_destroy(grpc_call_from_top_element(elem)); } -static requested_call *requested_call_array_add(requested_call_array *array) { - requested_call *rc; - if (array->count == array->capacity) { - array->capacity = GPR_MAX(array->capacity + 8, array->capacity * 2); - array->calls = - gpr_realloc(array->calls, sizeof(requested_call) * array->capacity); +static void request_matcher_zombify_all_pending_calls( + request_matcher *request_matcher) { + while (request_matcher->pending_head) { + call_data *calld = request_matcher->pending_head; + request_matcher->pending_head = calld->pending_next; + gpr_mu_lock(&calld->mu_state); + calld->state = ZOMBIED; + gpr_mu_unlock(&calld->mu_state); + grpc_iomgr_closure_init( + &calld->kill_zombie_closure, kill_zombie, + grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0)); + grpc_iomgr_add_callback(&calld->kill_zombie_closure); } - rc = &array->calls[array->count++]; - memset(rc, 0, sizeof(*rc)); - return rc; } +/* + * server proper + */ + static void server_ref(grpc_server *server) { gpr_ref(&server->internal_refcount); } @@ -359,20 +342,22 @@ static void server_delete(grpc_server *server) { gpr_mu_destroy(&server->mu_global); gpr_mu_destroy(&server->mu_call); gpr_free(server->channel_filters); - requested_call_array_destroy(&server->requested_calls); while ((rm = server->registered_methods) != NULL) { server->registered_methods = rm->next; + request_matcher_destroy(&rm->request_matcher); gpr_free(rm->method); gpr_free(rm->host); - requested_call_array_destroy(&rm->requested); gpr_free(rm); } for (i = 0; i < server->cq_count; i++) { GRPC_CQ_INTERNAL_UNREF(server->cqs[i], "server"); } + request_matcher_destroy(&server->unregistered_request_matcher); + gpr_stack_lockfree_destroy(server->request_freelist); gpr_free(server->cqs); gpr_free(server->pollsets); gpr_free(server->shutdown_tags); + gpr_free(server->requested_calls); gpr_free(server); } @@ -411,24 +396,29 @@ static void destroy_channel(channel_data *chand) { } static void finish_start_new_rpc(grpc_server *server, grpc_call_element *elem, - call_data **pending_root, - requested_call_array *array) { - requested_call rc; + request_matcher *request_matcher) { call_data *calld = elem->call_data; - gpr_mu_lock(&server->mu_call); - if (array->count == 0) { + int request_id; + + request_id = gpr_stack_lockfree_pop(request_matcher->requests); + if (request_id == -1) { + gpr_mu_lock(&server->mu_call); gpr_mu_lock(&calld->mu_state); calld->state = PENDING; gpr_mu_unlock(&calld->mu_state); - call_list_join(pending_root, calld, PENDING_START); + if (request_matcher->pending_head == NULL) { + request_matcher->pending_tail = request_matcher->pending_head = calld; + } else { + request_matcher->pending_tail->pending_next = calld; + request_matcher->pending_tail = calld; + } + calld->pending_next = NULL; gpr_mu_unlock(&server->mu_call); } else { - rc = array->calls[--array->count]; gpr_mu_lock(&calld->mu_state); calld->state = ACTIVATED; gpr_mu_unlock(&calld->mu_state); - gpr_mu_unlock(&server->mu_call); - begin_call(server, calld, &rc); + begin_call(server, calld, &server->requested_calls[request_id]); } } @@ -450,8 +440,8 @@ static void start_new_rpc(grpc_call_element *elem) { if (!rm) break; if (rm->host != calld->host) continue; if (rm->method != calld->path) continue; - finish_start_new_rpc(server, elem, &rm->server_registered_method->pending, - &rm->server_registered_method->requested); + finish_start_new_rpc(server, elem, + &rm->server_registered_method->request_matcher); return; } /* check for a wildcard method definition (no host set) */ @@ -462,17 +452,12 @@ static void start_new_rpc(grpc_call_element *elem) { if (!rm) break; if (rm->host != NULL) continue; if (rm->method != calld->path) continue; - finish_start_new_rpc(server, elem, &rm->server_registered_method->pending, - &rm->server_registered_method->requested); + finish_start_new_rpc(server, elem, + &rm->server_registered_method->request_matcher); return; } } - finish_start_new_rpc(server, elem, &server->lists[PENDING_START], - &server->requested_calls); -} - -static void kill_zombie(void *elem, int success) { - grpc_call_destroy(grpc_call_from_top_element(elem)); + finish_start_new_rpc(server, elem, &server->unregistered_request_matcher); } static int num_listeners(grpc_server *server) { @@ -484,6 +469,10 @@ static int num_listeners(grpc_server *server) { return n; } +static void done_shutdown_event(void *server, grpc_cq_completion *completion) { + server_unref(server); +} + static int num_channels(grpc_server *server) { channel_data *chand; int n = 0; @@ -496,16 +485,16 @@ static int num_channels(grpc_server *server) { static void maybe_finish_shutdown(grpc_server *server) { size_t i; - if (!server->shutdown || server->shutdown_published) { + if (!gpr_atm_acq_load(&server->shutdown_flag) || server->shutdown_published) { return; } if (server->root_channel_data.next != &server->root_channel_data || server->listeners_destroyed < num_listeners(server)) { - if (gpr_time_cmp( - gpr_time_sub(gpr_now(), server->last_shutdown_message_time), - gpr_time_from_seconds(1)) >= 0) { - server->last_shutdown_message_time = gpr_now(); + if (gpr_time_cmp(gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), + server->last_shutdown_message_time), + gpr_time_from_seconds(1, GPR_TIMESPAN)) >= 0) { + server->last_shutdown_message_time = gpr_now(GPR_CLOCK_REALTIME); gpr_log(GPR_DEBUG, "Waiting for %d channels and %d/%d listeners to be destroyed" " before shutting down server", @@ -517,8 +506,10 @@ static void maybe_finish_shutdown(grpc_server *server) { } server->shutdown_published = 1; for (i = 0; i < server->num_shutdown_tags; i++) { - grpc_cq_end_op(server->shutdown_tags[i].cq, server->shutdown_tags[i].tag, - NULL, 1); + server_ref(server); + grpc_cq_end_op(server->shutdown_tags[i].cq, server->shutdown_tags[i].tag, 1, + done_shutdown_event, server, + &server->shutdown_tags[i].completion); } } @@ -539,7 +530,6 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) { static void server_on_recv(void *ptr, int success) { grpc_call_element *elem = ptr; call_data *calld = elem->call_data; - channel_data *chand = elem->channel_data; if (success && !calld->got_initial_metadata) { size_t i; @@ -549,7 +539,8 @@ static void server_on_recv(void *ptr, int success) { grpc_stream_op *op = &ops[i]; if (op->type != GRPC_OP_METADATA) continue; grpc_metadata_batch_filter(&op->data.metadata, server_filter, elem); - if (0 != gpr_time_cmp(op->data.metadata.deadline, gpr_inf_future)) { + if (0 != gpr_time_cmp(op->data.metadata.deadline, + gpr_inf_future(GPR_CLOCK_REALTIME))) { calld->deadline = op->data.metadata.deadline; } calld->got_initial_metadata = 1; @@ -584,11 +575,8 @@ static void server_on_recv(void *ptr, int success) { } else if (calld->state == PENDING) { calld->state = ZOMBIED; gpr_mu_unlock(&calld->mu_state); - gpr_mu_lock(&chand->server->mu_call); - call_list_remove(calld, PENDING_START); - gpr_mu_unlock(&chand->server->mu_call); - grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem); - grpc_iomgr_add_callback(&calld->kill_zombie_closure); + /* zombied call will be destroyed when it's removed from the pending + queue... later */ } else { gpr_mu_unlock(&calld->mu_state); } @@ -623,7 +611,7 @@ static void accept_stream(void *cd, grpc_transport *transport, channel_data *chand = cd; /* create a call */ grpc_call_create(chand->channel, NULL, transport_server_data, NULL, 0, - gpr_inf_future); + gpr_inf_future(GPR_CLOCK_REALTIME)); } static void channel_connectivity_changed(void *cd, int iomgr_status_ignored) { @@ -651,7 +639,7 @@ static void init_call_elem(grpc_call_element *elem, call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; memset(calld, 0, sizeof(call_data)); - calld->deadline = gpr_inf_future; + calld->deadline = gpr_inf_future(GPR_CLOCK_REALTIME); calld->call = grpc_call_from_top_element(elem); gpr_mu_init(&calld->mu_state); @@ -666,11 +654,7 @@ static void destroy_call_elem(grpc_call_element *elem) { channel_data *chand = elem->channel_data; call_data *calld = elem->call_data; - if (calld->state == PENDING) { - gpr_mu_lock(&chand->server->mu_call); - call_list_remove(elem->call_data, PENDING_START); - gpr_mu_unlock(&chand->server->mu_call); - } + GPR_ASSERT(calld->state != PENDING); if (calld->host) { GRPC_MDSTR_UNREF(calld->host); @@ -777,6 +761,18 @@ grpc_server *grpc_server_create_from_filters(grpc_channel_filter **filters, server->root_channel_data.next = server->root_channel_data.prev = &server->root_channel_data; + /* TODO(ctiller): expose a channel_arg for this */ + server->max_requested_calls = 32768; + server->request_freelist = + gpr_stack_lockfree_create(server->max_requested_calls); + for (i = 0; i < (size_t)server->max_requested_calls; i++) { + gpr_stack_lockfree_push(server->request_freelist, i); + } + request_matcher_init(&server->unregistered_request_matcher, + server->max_requested_calls); + server->requested_calls = gpr_malloc(server->max_requested_calls * + sizeof(*server->requested_calls)); + /* Server filter stack is: server_surface_filter - for making surface API calls @@ -824,6 +820,7 @@ void *grpc_server_register_method(grpc_server *server, const char *method, } m = gpr_malloc(sizeof(registered_method)); memset(m, 0, sizeof(*m)); + request_matcher_init(&m->request_matcher, server->max_requested_calls); m->method = gpr_strdup(method); m->host = gpr_strdup(host); m->next = server->registered_methods; @@ -939,64 +936,87 @@ void grpc_server_setup_transport(grpc_server *s, grpc_transport *transport, grpc_transport_perform_op(transport, &op); } +typedef struct { + requested_call **requests; + size_t count; + size_t capacity; +} request_killer; + +static void request_killer_init(request_killer *rk) { + memset(rk, 0, sizeof(*rk)); +} + +static void request_killer_add(request_killer *rk, requested_call *rc) { + if (rk->capacity == rk->count) { + rk->capacity = GPR_MAX(8, rk->capacity * 2); + rk->requests = + gpr_realloc(rk->requests, rk->capacity * sizeof(*rk->requests)); + } + rk->requests[rk->count++] = rc; +} + +static void request_killer_add_request_matcher(request_killer *rk, + grpc_server *server, + request_matcher *rm) { + int request_id; + while ((request_id = gpr_stack_lockfree_pop(rm->requests)) != -1) { + request_killer_add(rk, &server->requested_calls[request_id]); + } +} + +static void request_killer_run(request_killer *rk, grpc_server *server) { + size_t i; + for (i = 0; i < rk->count; i++) { + fail_call(server, rk->requests[i]); + } + gpr_free(rk->requests); +} + void grpc_server_shutdown_and_notify(grpc_server *server, grpc_completion_queue *cq, void *tag) { listener *l; - requested_call_array requested_calls; - size_t i; registered_method *rm; shutdown_tag *sdt; channel_broadcaster broadcaster; + request_killer reqkill; /* lock, and gather up some stuff to do */ gpr_mu_lock(&server->mu_global); - grpc_cq_begin_op(cq, NULL); + grpc_cq_begin_op(cq); server->shutdown_tags = gpr_realloc(server->shutdown_tags, sizeof(shutdown_tag) * (server->num_shutdown_tags + 1)); sdt = &server->shutdown_tags[server->num_shutdown_tags++]; sdt->tag = tag; sdt->cq = cq; - if (server->shutdown) { + if (gpr_atm_acq_load(&server->shutdown_flag)) { gpr_mu_unlock(&server->mu_global); return; } - server->last_shutdown_message_time = gpr_now(); + server->last_shutdown_message_time = gpr_now(GPR_CLOCK_REALTIME); channel_broadcaster_init(server, &broadcaster); + request_killer_init(&reqkill); /* collect all unregistered then registered calls */ gpr_mu_lock(&server->mu_call); - requested_calls = server->requested_calls; - memset(&server->requested_calls, 0, sizeof(server->requested_calls)); + request_killer_add_request_matcher(&reqkill, server, + &server->unregistered_request_matcher); + request_matcher_zombify_all_pending_calls( + &server->unregistered_request_matcher); for (rm = server->registered_methods; rm; rm = rm->next) { - if (requested_calls.count + rm->requested.count > - requested_calls.capacity) { - requested_calls.capacity = - GPR_MAX(requested_calls.count + rm->requested.count, - 2 * requested_calls.capacity); - requested_calls.calls = - gpr_realloc(requested_calls.calls, sizeof(*requested_calls.calls) * - requested_calls.capacity); - } - memcpy(requested_calls.calls + requested_calls.count, rm->requested.calls, - sizeof(*requested_calls.calls) * rm->requested.count); - requested_calls.count += rm->requested.count; - gpr_free(rm->requested.calls); - memset(&rm->requested, 0, sizeof(rm->requested)); + request_killer_add_request_matcher(&reqkill, server, &rm->request_matcher); + request_matcher_zombify_all_pending_calls(&rm->request_matcher); } gpr_mu_unlock(&server->mu_call); - server->shutdown = 1; + gpr_atm_rel_store(&server->shutdown_flag, 1); maybe_finish_shutdown(server); gpr_mu_unlock(&server->mu_global); /* terminate all the requested calls */ - for (i = 0; i < requested_calls.count; i++) { - fail_call(server, &requested_calls.calls[i]); - } - gpr_free(requested_calls.calls); + request_killer_run(&reqkill, server); /* Shutdown listeners */ for (l = server->listeners; l; l = l->next) { @@ -1028,7 +1048,7 @@ void grpc_server_destroy(grpc_server *server) { listener *l; gpr_mu_lock(&server->mu_global); - GPR_ASSERT(server->shutdown || !server->listeners); + GPR_ASSERT(gpr_atm_acq_load(&server->shutdown_flag) || !server->listeners); GPR_ASSERT(server->listeners_destroyed == num_listeners(server)); while (server->listeners) { @@ -1058,38 +1078,55 @@ void grpc_server_add_listener(grpc_server *server, void *arg, static grpc_call_error queue_call_request(grpc_server *server, requested_call *rc) { call_data *calld = NULL; - requested_call_array *requested_calls = NULL; - gpr_mu_lock(&server->mu_call); - if (server->shutdown) { - gpr_mu_unlock(&server->mu_call); + request_matcher *request_matcher = NULL; + int request_id; + if (gpr_atm_acq_load(&server->shutdown_flag)) { + fail_call(server, rc); + return GRPC_CALL_OK; + } + request_id = gpr_stack_lockfree_pop(server->request_freelist); + if (request_id == -1) { + /* out of request ids: just fail this one */ fail_call(server, rc); return GRPC_CALL_OK; } switch (rc->type) { case BATCH_CALL: - calld = - call_list_remove_head(&server->lists[PENDING_START], PENDING_START); - requested_calls = &server->requested_calls; + request_matcher = &server->unregistered_request_matcher; break; case REGISTERED_CALL: - calld = call_list_remove_head( - &rc->data.registered.registered_method->pending, PENDING_START); - requested_calls = &rc->data.registered.registered_method->requested; + request_matcher = &rc->data.registered.registered_method->request_matcher; break; } - if (calld != NULL) { - gpr_mu_unlock(&server->mu_call); - gpr_mu_lock(&calld->mu_state); - GPR_ASSERT(calld->state == PENDING); - calld->state = ACTIVATED; - gpr_mu_unlock(&calld->mu_state); - begin_call(server, calld, rc); - return GRPC_CALL_OK; - } else { - *requested_call_array_add(requested_calls) = *rc; + server->requested_calls[request_id] = *rc; + gpr_free(rc); + if (gpr_stack_lockfree_push(request_matcher->requests, request_id)) { + /* this was the first queued request: we need to lock and start + matching calls */ + gpr_mu_lock(&server->mu_call); + while ((calld = request_matcher->pending_head) != NULL) { + request_id = gpr_stack_lockfree_pop(request_matcher->requests); + if (request_id == -1) break; + request_matcher->pending_head = calld->pending_next; + gpr_mu_unlock(&server->mu_call); + gpr_mu_lock(&calld->mu_state); + if (calld->state == ZOMBIED) { + gpr_mu_unlock(&calld->mu_state); + grpc_iomgr_closure_init( + &calld->kill_zombie_closure, kill_zombie, + grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0)); + grpc_iomgr_add_callback(&calld->kill_zombie_closure); + } else { + GPR_ASSERT(calld->state == PENDING); + calld->state = ACTIVATED; + gpr_mu_unlock(&calld->mu_state); + begin_call(server, calld, &server->requested_calls[request_id]); + } + gpr_mu_lock(&server->mu_call); + } gpr_mu_unlock(&server->mu_call); - return GRPC_CALL_OK; } + return GRPC_CALL_OK; } grpc_call_error grpc_server_request_call( @@ -1097,22 +1134,24 @@ grpc_call_error grpc_server_request_call( grpc_metadata_array *initial_metadata, grpc_completion_queue *cq_bound_to_call, grpc_completion_queue *cq_for_notification, void *tag) { - requested_call rc; + requested_call *rc = gpr_malloc(sizeof(*rc)); GRPC_SERVER_LOG_REQUEST_CALL(GPR_INFO, server, call, details, initial_metadata, cq_bound_to_call, cq_for_notification, tag); if (!grpc_cq_is_server_cq(cq_for_notification)) { + gpr_free(rc); return GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; } - grpc_cq_begin_op(cq_for_notification, NULL); - rc.type = BATCH_CALL; - rc.tag = tag; - rc.cq_bound_to_call = cq_bound_to_call; - rc.cq_for_notification = cq_for_notification; - rc.call = call; - rc.data.batch.details = details; - rc.data.batch.initial_metadata = initial_metadata; - return queue_call_request(server, &rc); + grpc_cq_begin_op(cq_for_notification); + rc->type = BATCH_CALL; + rc->server = server; + rc->tag = tag; + rc->cq_bound_to_call = cq_bound_to_call; + rc->cq_for_notification = cq_for_notification; + rc->call = call; + rc->data.batch.details = details; + rc->data.batch.initial_metadata = initial_metadata; + return queue_call_request(server, rc); } grpc_call_error grpc_server_request_registered_call( @@ -1120,22 +1159,24 @@ grpc_call_error grpc_server_request_registered_call( grpc_metadata_array *initial_metadata, grpc_byte_buffer **optional_payload, grpc_completion_queue *cq_bound_to_call, grpc_completion_queue *cq_for_notification, void *tag) { - requested_call rc; + requested_call *rc = gpr_malloc(sizeof(*rc)); registered_method *registered_method = rm; if (!grpc_cq_is_server_cq(cq_for_notification)) { + gpr_free(rc); return GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; } - grpc_cq_begin_op(cq_for_notification, NULL); - rc.type = REGISTERED_CALL; - rc.tag = tag; - rc.cq_bound_to_call = cq_bound_to_call; - rc.cq_for_notification = cq_for_notification; - rc.call = call; - rc.data.registered.registered_method = registered_method; - rc.data.registered.deadline = deadline; - rc.data.registered.initial_metadata = initial_metadata; - rc.data.registered.optional_payload = optional_payload; - return queue_call_request(server, &rc); + grpc_cq_begin_op(cq_for_notification); + rc->type = REGISTERED_CALL; + rc->server = server; + rc->tag = tag; + rc->cq_bound_to_call = cq_bound_to_call; + rc->cq_for_notification = cq_for_notification; + rc->call = call; + rc->data.registered.registered_method = registered_method; + rc->data.registered.deadline = deadline; + rc->data.registered.initial_metadata = initial_metadata; + rc->data.registered.optional_payload = optional_payload; + return queue_call_request(server, rc); } static void publish_registered_or_batch(grpc_call *call, int success, @@ -1202,8 +1243,20 @@ static void begin_call(grpc_server *server, call_data *calld, } GRPC_CALL_INTERNAL_REF(calld->call, "server"); - grpc_call_start_ioreq_and_call_back(calld->call, req, r - req, publish, - rc->tag); + grpc_call_start_ioreq_and_call_back(calld->call, req, r - req, publish, rc); +} + +static void done_request_event(void *req, grpc_cq_completion *c) { + requested_call *rc = req; + grpc_server *server = rc->server; + + if (rc >= server->requested_calls && + rc < server->requested_calls + server->max_requested_calls) { + gpr_stack_lockfree_push(server->request_freelist, + rc - server->requested_calls); + } else { + gpr_free(req); + } } static void fail_call(grpc_server *server, requested_call *rc) { @@ -1216,15 +1269,19 @@ static void fail_call(grpc_server *server, requested_call *rc) { rc->data.registered.initial_metadata->count = 0; break; } - grpc_cq_end_op(rc->cq_for_notification, rc->tag, NULL, 0); + grpc_cq_end_op(rc->cq_for_notification, rc->tag, 0, done_request_event, rc, + &rc->completion); } static void publish_registered_or_batch(grpc_call *call, int success, - void *tag) { + void *prc) { grpc_call_element *elem = grpc_call_stack_element(grpc_call_get_call_stack(call), 0); + requested_call *rc = prc; call_data *calld = elem->call_data; - grpc_cq_end_op(calld->cq_new, tag, call, success); + grpc_cq_end_op(calld->cq_new, rc->tag, success, done_request_event, rc, + &rc->completion); + GRPC_CALL_INTERNAL_UNREF(call, "server", 0); } const grpc_channel_args *grpc_server_get_channel_args(grpc_server *server) { diff --git a/src/core/surface/version.c b/src/core/surface/version.c new file mode 100644 index 00000000000..4f5d6483710 --- /dev/null +++ b/src/core/surface/version.c @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* This file is autogenerated from: + templates/src/core/surface/version.c.template */ + +#include + +const char *grpc_version_string(void) { + return "0.10.0.0"; +} diff --git a/src/core/transport/chttp2/frame_data.c b/src/core/transport/chttp2/frame_data.c index 0ad62a9999b..7e3980159e2 100644 --- a/src/core/transport/chttp2/frame_data.c +++ b/src/core/transport/chttp2/frame_data.c @@ -89,12 +89,9 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse( fh_0: case GRPC_CHTTP2_DATA_FH_0: p->frame_type = *cur; - if (++cur == end) { - p->state = GRPC_CHTTP2_DATA_FH_1; - return GRPC_CHTTP2_PARSE_OK; - } switch (p->frame_type) { case 0: + /* noop */ break; case 1: gpr_log(GPR_ERROR, "Compressed GRPC frames not yet supported"); @@ -103,6 +100,10 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse( gpr_log(GPR_ERROR, "Bad GRPC frame type 0x%02x", p->frame_type); return GRPC_CHTTP2_STREAM_ERROR; } + if (++cur == end) { + p->state = GRPC_CHTTP2_DATA_FH_1; + return GRPC_CHTTP2_PARSE_OK; + } /* fallthrough */ case GRPC_CHTTP2_DATA_FH_1: p->frame_size = ((gpr_uint32)*cur) << 24; diff --git a/src/core/transport/chttp2/frame_window_update.c b/src/core/transport/chttp2/frame_window_update.c index b817df77459..d624298ad2a 100644 --- a/src/core/transport/chttp2/frame_window_update.c +++ b/src/core/transport/chttp2/frame_window_update.c @@ -94,8 +94,8 @@ grpc_chttp2_parse_error grpc_chttp2_window_update_parser_parse( } GPR_ASSERT(is_last); - if (transport_parsing->incoming_stream_id) { - if (stream_parsing) { + if (transport_parsing->incoming_stream_id != 0) { + if (stream_parsing != NULL) { GRPC_CHTTP2_FLOWCTL_TRACE_STREAM("update", transport_parsing, stream_parsing, outgoing_window_update, p->amount); diff --git a/src/core/transport/chttp2/incoming_metadata.c b/src/core/transport/chttp2/incoming_metadata.c index 77162a68644..974b864ffb4 100644 --- a/src/core/transport/chttp2/incoming_metadata.c +++ b/src/core/transport/chttp2/incoming_metadata.c @@ -42,7 +42,7 @@ void grpc_chttp2_incoming_metadata_buffer_init( grpc_chttp2_incoming_metadata_buffer *buffer) { - buffer->deadline = gpr_inf_future; + buffer->deadline = gpr_inf_future(GPR_CLOCK_REALTIME); } void grpc_chttp2_incoming_metadata_buffer_destroy( @@ -87,7 +87,7 @@ void grpc_chttp2_incoming_metadata_buffer_place_metadata_batch_into( b.list.tail = (void *)(gpr_intptr)buffer->count; b.garbage.head = b.garbage.tail = NULL; b.deadline = buffer->deadline; - buffer->deadline = gpr_inf_future; + buffer->deadline = gpr_inf_future(GPR_CLOCK_REALTIME); grpc_sopb_add_metadata(sopb, b); } diff --git a/src/core/transport/chttp2/internal.h b/src/core/transport/chttp2/internal.h index bdd4b432ebd..e5e6f445b70 100644 --- a/src/core/transport/chttp2/internal.h +++ b/src/core/transport/chttp2/internal.h @@ -353,7 +353,19 @@ typedef struct { /** window available for us to send to peer */ gpr_int64 outgoing_window; - /** window available for peer to send to us - updated after parse */ + /** The number of bytes the upper layers have offered to receive. + As the upper layer offers more bytes, this value increases. + As bytes are read, this value decreases. */ + gpr_uint32 max_recv_bytes; + /** The number of bytes the upper layer has offered to read but we have + not yet announced to HTTP2 flow control. + As the upper layers offer to read more bytes, this value increases. + As we advertise incoming flow control window, this value decreases. */ + gpr_uint32 unannounced_incoming_window; + /** The number of bytes of HTTP2 flow control we have advertised. + As we advertise incoming flow control window, this value increases. + As bytes are read, this value decreases. + Updated after parse. */ gpr_uint32 incoming_window; /** stream ops the transport user would like to send */ grpc_stream_op_buffer *outgoing_sopb; @@ -391,6 +403,8 @@ typedef struct { grpc_stream_op_buffer sopb; /** how strongly should we indicate closure with the next write */ grpc_chttp2_send_closed send_closed; + /** how much window should we announce? */ + gpr_uint32 announce_window; } grpc_chttp2_stream_writing; struct grpc_chttp2_stream_parsing { @@ -501,7 +515,9 @@ void grpc_chttp2_list_add_writable_window_update_stream( grpc_chttp2_stream_global *stream_global); int grpc_chttp2_list_pop_writable_window_update_stream( grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global **stream_global); + grpc_chttp2_transport_writing *transport_writing, + grpc_chttp2_stream_global **stream_global, + grpc_chttp2_stream_writing **stream_writing); void grpc_chttp2_list_remove_writable_window_update_stream( grpc_chttp2_transport_global *transport_global, grpc_chttp2_stream_global *stream_global); diff --git a/src/core/transport/chttp2/parsing.c b/src/core/transport/chttp2/parsing.c index 130167f8302..aa32f2e44a1 100644 --- a/src/core/transport/chttp2/parsing.c +++ b/src/core/transport/chttp2/parsing.c @@ -173,7 +173,14 @@ void grpc_chttp2_publish_reads( GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( "parsed", transport_parsing, stream_parsing, incoming_window_delta, -(gpr_int64)stream_parsing->incoming_window_delta); + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( + "parsed", transport_parsing, stream_global, max_recv_bytes, + -(gpr_int64)stream_parsing->incoming_window_delta); stream_global->incoming_window -= stream_parsing->incoming_window_delta; + GPR_ASSERT(stream_global->max_recv_bytes >= + stream_parsing->incoming_window_delta); + stream_global->max_recv_bytes -= + stream_parsing->incoming_window_delta; stream_parsing->incoming_window_delta = 0; grpc_chttp2_list_add_writable_window_update_stream(transport_global, stream_global); @@ -205,7 +212,8 @@ void grpc_chttp2_publish_reads( } if (stream_parsing->saw_rst_stream) { stream_global->cancelled = 1; - stream_global->cancelled_status = grpc_chttp2_http2_error_to_grpc_status(stream_parsing->rst_stream_reason); + stream_global->cancelled_status = grpc_chttp2_http2_error_to_grpc_status( + stream_parsing->rst_stream_reason); if (stream_parsing->rst_stream_reason == GRPC_CHTTP2_NO_ERROR) { stream_global->published_cancelled = 1; } @@ -593,13 +601,13 @@ static void on_header(void *tp, grpc_mdelem *md) { cached_timeout)) { gpr_log(GPR_ERROR, "Ignoring bad timeout value '%s'", grpc_mdstr_as_c_string(md->value)); - *cached_timeout = gpr_inf_future; + *cached_timeout = gpr_inf_future(GPR_CLOCK_REALTIME); } grpc_mdelem_set_user_data(md, free_timeout, cached_timeout); } grpc_chttp2_incoming_metadata_buffer_set_deadline( &stream_parsing->incoming_metadata, - gpr_time_add(gpr_now(), *cached_timeout)); + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), *cached_timeout)); GRPC_MDELEM_UNREF(md); } else { grpc_chttp2_incoming_metadata_buffer_add(&stream_parsing->incoming_metadata, diff --git a/src/core/transport/chttp2/stream_encoder.c b/src/core/transport/chttp2/stream_encoder.c index 56ab82006ac..d7fc2da5d3b 100644 --- a/src/core/transport/chttp2/stream_encoder.c +++ b/src/core/transport/chttp2/stream_encoder.c @@ -437,7 +437,8 @@ static void deadline_enc(grpc_chttp2_hpack_compressor *c, gpr_timespec deadline, framer_state *st) { char timeout_str[GRPC_CHTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE]; grpc_mdelem *mdelem; - grpc_chttp2_encode_timeout(gpr_time_sub(deadline, gpr_now()), timeout_str); + grpc_chttp2_encode_timeout( + gpr_time_sub(deadline, gpr_now(GPR_CLOCK_REALTIME)), timeout_str); mdelem = grpc_mdelem_from_metadata_strings( c->mdctx, GRPC_MDSTR_REF(c->timeout_key_str), grpc_mdstr_from_string(c->mdctx, timeout_str)); @@ -584,7 +585,8 @@ void grpc_chttp2_encode(grpc_stream_op *ops, size_t ops_count, int eof, l->md = hpack_enc(compressor, l->md, &st); need_unref |= l->md != NULL; } - if (gpr_time_cmp(op->data.metadata.deadline, gpr_inf_future) != 0) { + if (gpr_time_cmp(op->data.metadata.deadline, + gpr_inf_future(GPR_CLOCK_REALTIME)) != 0) { deadline_enc(compressor, op->data.metadata.deadline, &st); } curop++; diff --git a/src/core/transport/chttp2/stream_lists.c b/src/core/transport/chttp2/stream_lists.c index 4fea058c193..590f6abfbc3 100644 --- a/src/core/transport/chttp2/stream_lists.c +++ b/src/core/transport/chttp2/stream_lists.c @@ -139,6 +139,7 @@ static void stream_list_add(grpc_chttp2_transport *t, grpc_chttp2_stream *s, void grpc_chttp2_list_add_writable_stream( grpc_chttp2_transport_global *transport_global, grpc_chttp2_stream_global *stream_global) { + GPR_ASSERT(stream_global->id != 0); stream_list_add(TRANSPORT_FROM_GLOBAL(transport_global), STREAM_FROM_GLOBAL(stream_global), GRPC_CHTTP2_LIST_WRITABLE); } @@ -204,6 +205,7 @@ int grpc_chttp2_list_pop_written_stream( void grpc_chttp2_list_add_writable_window_update_stream( grpc_chttp2_transport_global *transport_global, grpc_chttp2_stream_global *stream_global) { + GPR_ASSERT(stream_global->id != 0); stream_list_add(TRANSPORT_FROM_GLOBAL(transport_global), STREAM_FROM_GLOBAL(stream_global), GRPC_CHTTP2_LIST_WRITABLE_WINDOW_UPDATE); @@ -211,11 +213,14 @@ void grpc_chttp2_list_add_writable_window_update_stream( int grpc_chttp2_list_pop_writable_window_update_stream( grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global **stream_global) { + grpc_chttp2_transport_writing *transport_writing, + grpc_chttp2_stream_global **stream_global, + grpc_chttp2_stream_writing **stream_writing) { grpc_chttp2_stream *stream; int r = stream_list_pop(TRANSPORT_FROM_GLOBAL(transport_global), &stream, GRPC_CHTTP2_LIST_WRITABLE_WINDOW_UPDATE); *stream_global = &stream->global; + *stream_writing = &stream->writing; return r; } diff --git a/src/core/transport/chttp2/timeout_encoding.c b/src/core/transport/chttp2/timeout_encoding.c index 33915c4039f..1dd768ada40 100644 --- a/src/core/transport/chttp2/timeout_encoding.c +++ b/src/core/transport/chttp2/timeout_encoding.c @@ -147,7 +147,7 @@ int grpc_chttp2_decode_timeout(const char *buffer, gpr_timespec *timeout) { gpr_uint32 xp = x * 10 + *p - '0'; have_digit = 1; if (xp < x) { - *timeout = gpr_inf_future; + *timeout = gpr_inf_future(GPR_CLOCK_REALTIME); return 1; } x = xp; @@ -159,22 +159,22 @@ int grpc_chttp2_decode_timeout(const char *buffer, gpr_timespec *timeout) { /* decode unit specifier */ switch (*p) { case 'n': - *timeout = gpr_time_from_nanos(x); + *timeout = gpr_time_from_nanos(x, GPR_TIMESPAN); break; case 'u': - *timeout = gpr_time_from_micros(x); + *timeout = gpr_time_from_micros(x, GPR_TIMESPAN); break; case 'm': - *timeout = gpr_time_from_millis(x); + *timeout = gpr_time_from_millis(x, GPR_TIMESPAN); break; case 'S': - *timeout = gpr_time_from_seconds(x); + *timeout = gpr_time_from_seconds(x, GPR_TIMESPAN); break; case 'M': - *timeout = gpr_time_from_minutes(x); + *timeout = gpr_time_from_minutes(x, GPR_TIMESPAN); break; case 'H': - *timeout = gpr_time_from_hours(x); + *timeout = gpr_time_from_hours(x, GPR_TIMESPAN); break; default: return 0; diff --git a/src/core/transport/chttp2/writing.c b/src/core/transport/chttp2/writing.c index a78654334e1..d8ec117aa5d 100644 --- a/src/core/transport/chttp2/writing.c +++ b/src/core/transport/chttp2/writing.c @@ -66,11 +66,9 @@ int grpc_chttp2_unlocking_check_writes( /* for each grpc_chttp2_stream that's become writable, frame it's data (according to available window sizes) and add to the output buffer */ - while (transport_global->outgoing_window && - grpc_chttp2_list_pop_writable_stream(transport_global, + while (grpc_chttp2_list_pop_writable_stream(transport_global, transport_writing, &stream_global, - &stream_writing) && - stream_global->outgoing_window > 0) { + &stream_writing)) { stream_writing->id = stream_global->id; window_delta = grpc_chttp2_preencode( stream_global->outgoing_sopb->ops, &stream_global->outgoing_sopb->nops, @@ -106,20 +104,21 @@ int grpc_chttp2_unlocking_check_writes( /* for each grpc_chttp2_stream that wants to update its window, add that * window here */ while (grpc_chttp2_list_pop_writable_window_update_stream(transport_global, - &stream_global)) { - window_delta = - transport_global->settings[GRPC_LOCAL_SETTINGS] - [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE] - - stream_global->incoming_window; - if (!stream_global->read_closed && window_delta > 0) { - gpr_slice_buffer_add( - &transport_writing->outbuf, - grpc_chttp2_window_update_create(stream_global->id, window_delta)); + transport_writing, + &stream_global, + &stream_writing)) { + stream_writing->id = stream_global->id; + if (!stream_global->read_closed && stream_global->unannounced_incoming_window > 0) { + stream_writing->announce_window = stream_global->unannounced_incoming_window; GRPC_CHTTP2_FLOWCTL_TRACE_STREAM("write", transport_global, stream_global, - incoming_window, window_delta); - stream_global->incoming_window += window_delta; + incoming_window, stream_global->unannounced_incoming_window); + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM("write", transport_global, stream_global, + unannounced_incoming_window, -(gpr_int64)stream_global->unannounced_incoming_window); + stream_global->incoming_window += stream_global->unannounced_incoming_window; + stream_global->unannounced_incoming_window = 0; grpc_chttp2_list_add_incoming_window_updated(transport_global, stream_global); + grpc_chttp2_list_add_writing_stream(transport_writing, stream_writing); } } @@ -169,10 +168,19 @@ static void finalize_outbuf(grpc_chttp2_transport_writing *transport_writing) { while ( grpc_chttp2_list_pop_writing_stream(transport_writing, &stream_writing)) { - grpc_chttp2_encode(stream_writing->sopb.ops, stream_writing->sopb.nops, - stream_writing->send_closed != GRPC_DONT_SEND_CLOSED, - stream_writing->id, &transport_writing->hpack_compressor, - &transport_writing->outbuf); + if (stream_writing->sopb.nops > 0 || stream_writing->send_closed != GRPC_DONT_SEND_CLOSED) { + grpc_chttp2_encode(stream_writing->sopb.ops, stream_writing->sopb.nops, + stream_writing->send_closed != GRPC_DONT_SEND_CLOSED, + stream_writing->id, &transport_writing->hpack_compressor, + &transport_writing->outbuf); + } + if (stream_writing->announce_window > 0) { + gpr_slice_buffer_add( + &transport_writing->outbuf, + grpc_chttp2_window_update_create( + stream_writing->id, stream_writing->announce_window)); + stream_writing->announce_window = 0; + } stream_writing->sopb.nops = 0; if (stream_writing->send_closed == GRPC_SEND_CLOSED_WITH_RST_STREAM) { gpr_slice_buffer_add(&transport_writing->outbuf, @@ -197,7 +205,8 @@ void grpc_chttp2_cleanup_writing( while (grpc_chttp2_list_pop_written_stream( transport_global, transport_writing, &stream_global, &stream_writing)) { - if (stream_global->outgoing_sopb->nops == 0) { + if (stream_global->outgoing_sopb != NULL && + stream_global->outgoing_sopb->nops == 0) { stream_global->outgoing_sopb = NULL; grpc_chttp2_schedule_closure(transport_global, stream_global->send_done_closure, 1); diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c index ac399e4a1d0..c923d5e42fb 100644 --- a/src/core/transport/chttp2_transport.c +++ b/src/core/transport/chttp2_transport.c @@ -358,7 +358,9 @@ static int init_stream(grpc_transport *gt, grpc_stream *gs, s->global.outgoing_window = t->global.settings[GRPC_PEER_SETTINGS] [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; - s->parsing.incoming_window = s->global.incoming_window = + s->global.max_recv_bytes = + s->parsing.incoming_window = + s->global.incoming_window = t->global.settings[GRPC_SENT_SETTINGS] [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; *t->accepting_stream = s; @@ -562,6 +564,8 @@ static void maybe_start_some_streams( stream_global->incoming_window = transport_global->settings[GRPC_SENT_SETTINGS] [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; + stream_global->max_recv_bytes = + GPR_MAX(stream_global->incoming_window, stream_global->max_recv_bytes); grpc_chttp2_stream_map_add( &TRANSPORT_FROM_GLOBAL(transport_global)->new_stream_map, stream_global->id, STREAM_FROM_GLOBAL(stream_global)); @@ -570,6 +574,9 @@ static void maybe_start_some_streams( grpc_chttp2_list_add_incoming_window_updated(transport_global, stream_global); grpc_chttp2_list_add_writable_stream(transport_global, stream_global); + grpc_chttp2_list_add_writable_window_update_stream(transport_global, + stream_global); + } /* cancel out streams that will never be started */ while (transport_global->next_stream_id >= MAX_CLIENT_STREAM_ID && @@ -620,12 +627,23 @@ static void perform_stream_op_locked( stream_global->publish_sopb = op->recv_ops; stream_global->publish_sopb->nops = 0; stream_global->publish_state = op->recv_state; + if (stream_global->max_recv_bytes < op->max_recv_bytes) { + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM("op", transport_global, stream_global, + max_recv_bytes, op->max_recv_bytes - stream_global->max_recv_bytes); + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( + "op", transport_global, stream_global, unannounced_incoming_window, + op->max_recv_bytes - stream_global->max_recv_bytes); + stream_global->unannounced_incoming_window += op->max_recv_bytes - stream_global->max_recv_bytes; + stream_global->max_recv_bytes = op->max_recv_bytes; + } grpc_chttp2_incoming_metadata_live_op_buffer_end( &stream_global->outstanding_metadata); - grpc_chttp2_list_add_read_write_state_changed(transport_global, - stream_global); - grpc_chttp2_list_add_writable_window_update_stream(transport_global, - stream_global); + if (stream_global->id != 0) { + grpc_chttp2_list_add_read_write_state_changed(transport_global, + stream_global); + grpc_chttp2_list_add_writable_window_update_stream(transport_global, + stream_global); + } } if (op->bind_pollset) { @@ -1038,7 +1056,7 @@ void grpc_chttp2_flowctl_trace(const char *file, int line, const char *reason, identifier = gpr_strdup(context_scope); } gpr_log(GPR_INFO, - "FLOWCTL: %s %-10s %8s %-23s %8lld %c %8lld = %8lld %-10s [%s:%d]", + "FLOWCTL: %s %-10s %8s %-27s %8lld %c %8lld = %8lld %-10s [%s:%d]", is_client ? "client" : "server", identifier, context_thread, var, current_value, delta < 0 ? '-' : '+', delta < 0 ? -delta : delta, current_value + delta, reason, file, line); diff --git a/src/core/transport/metadata.c b/src/core/transport/metadata.c index 9cbb0952d03..e95b7a21f9a 100644 --- a/src/core/transport/metadata.c +++ b/src/core/transport/metadata.c @@ -87,6 +87,7 @@ typedef struct internal_metadata { gpr_atm refcnt; /* private only data */ + gpr_mu mu_user_data; void *user_data; void (*destroy_user_data)(void *user_data); @@ -183,7 +184,7 @@ grpc_mdctx *grpc_mdctx_create(void) { /* This seed is used to prevent remote connections from controlling hash table * collisions. It needs to be somewhat unpredictable to a remote connection. */ - return grpc_mdctx_create_with_seed(gpr_now().tv_nsec); + return grpc_mdctx_create_with_seed(gpr_now(GPR_CLOCK_REALTIME).tv_nsec); } static void discard_metadata(grpc_mdctx *ctx) { @@ -200,6 +201,7 @@ static void discard_metadata(grpc_mdctx *ctx) { if (cur->user_data) { cur->destroy_user_data(cur->user_data); } + gpr_mu_destroy(&cur->mu_user_data); gpr_free(cur); cur = next; ctx->mdtab_free--; @@ -467,6 +469,7 @@ grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx, md->user_data = NULL; md->destroy_user_data = NULL; md->bucket_next = ctx->mdtab[hash % ctx->mdtab_capacity]; + gpr_mu_init(&md->mu_user_data); #ifdef GRPC_METADATA_REFCOUNT_DEBUG gpr_log(GPR_DEBUG, "ELM NEW:%p:%d: '%s' = '%s'", md, gpr_atm_no_barrier_load(&md->refcnt), @@ -581,18 +584,29 @@ size_t grpc_mdctx_get_mdtab_free_test_only(grpc_mdctx *ctx) { void *grpc_mdelem_get_user_data(grpc_mdelem *md, void (*if_destroy_func)(void *)) { internal_metadata *im = (internal_metadata *)md; - return im->destroy_user_data == if_destroy_func ? im->user_data : NULL; + void *result; + gpr_mu_lock(&im->mu_user_data); + result = im->destroy_user_data == if_destroy_func ? im->user_data : NULL; + gpr_mu_unlock(&im->mu_user_data); + return result; } void grpc_mdelem_set_user_data(grpc_mdelem *md, void (*destroy_func)(void *), void *user_data) { internal_metadata *im = (internal_metadata *)md; GPR_ASSERT((user_data == NULL) == (destroy_func == NULL)); + gpr_mu_lock(&im->mu_user_data); if (im->destroy_user_data) { - im->destroy_user_data(im->user_data); + /* user data can only be set once */ + gpr_mu_unlock(&im->mu_user_data); + if (destroy_func != NULL) { + destroy_func(user_data); + } + return; } im->destroy_user_data = destroy_func; im->user_data = user_data; + gpr_mu_unlock(&im->mu_user_data); } gpr_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *gs) { diff --git a/src/core/transport/stream_op.c b/src/core/transport/stream_op.c index fdb50c6b719..71061fe0c78 100644 --- a/src/core/transport/stream_op.c +++ b/src/core/transport/stream_op.c @@ -205,7 +205,7 @@ void grpc_metadata_batch_assert_ok(grpc_metadata_batch *batch) { void grpc_metadata_batch_init(grpc_metadata_batch *batch) { batch->list.head = batch->list.tail = batch->garbage.head = batch->garbage.tail = NULL; - batch->deadline = gpr_inf_future; + batch->deadline = gpr_inf_future(GPR_CLOCK_REALTIME); } void grpc_metadata_batch_destroy(grpc_metadata_batch *batch) { diff --git a/src/core/transport/transport.h b/src/core/transport/transport.h index 1429737721c..64503604ee1 100644 --- a/src/core/transport/transport.h +++ b/src/core/transport/transport.h @@ -72,6 +72,10 @@ typedef struct grpc_transport_stream_op { grpc_stream_op_buffer *recv_ops; grpc_stream_state *recv_state; + /** The number of bytes this peer is currently prepared to receive. + These bytes will be eventually used to replenish per-stream flow control + windows. */ + gpr_uint32 max_recv_bytes; grpc_iomgr_closure *on_done_recv; grpc_pollset *bind_pollset; diff --git a/src/core/transport/transport_op_string.c b/src/core/transport/transport_op_string.c index 0da396a3201..9d127c5472d 100644 --- a/src/core/transport/transport_op_string.c +++ b/src/core/transport/transport_op_string.c @@ -61,7 +61,7 @@ static void put_metadata_list(gpr_strvec *b, grpc_metadata_batch md) { if (m != md.list.head) gpr_strvec_add(b, gpr_strdup(", ")); put_metadata(b, m->md); } - if (gpr_time_cmp(md.deadline, gpr_inf_future) != 0) { + if (gpr_time_cmp(md.deadline, gpr_inf_future(GPR_CLOCK_REALTIME)) != 0) { char *tmp; gpr_asprintf(&tmp, " deadline=%d.%09d", md.deadline.tv_sec, md.deadline.tv_nsec); @@ -128,7 +128,8 @@ char *grpc_transport_stream_op_string(grpc_transport_stream_op *op) { if (op->recv_ops) { if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); first = 0; - gpr_strvec_add(&b, gpr_strdup("RECV")); + gpr_asprintf(&tmp, "RECV:max_recv_bytes=%d", op->max_recv_bytes); + gpr_strvec_add(&b, tmp); } if (op->bind_pollset) { diff --git a/src/cpp/client/client_context.cc b/src/cpp/client/client_context.cc index c68f6dd9f8d..372f3e27ecc 100644 --- a/src/cpp/client/client_context.cc +++ b/src/cpp/client/client_context.cc @@ -44,7 +44,7 @@ ClientContext::ClientContext() : initial_metadata_received_(false), call_(nullptr), cq_(nullptr), - deadline_(gpr_inf_future) {} + deadline_(gpr_inf_future(GPR_CLOCK_REALTIME)) {} ClientContext::~ClientContext() { if (call_) { @@ -53,8 +53,8 @@ ClientContext::~ClientContext() { if (cq_) { // Drain cq_. grpc_completion_queue_shutdown(cq_); - while (grpc_completion_queue_next(cq_, gpr_inf_future).type != - GRPC_QUEUE_SHUTDOWN) + while (grpc_completion_queue_next(cq_, gpr_inf_future(GPR_CLOCK_REALTIME)) + .type != GRPC_QUEUE_SHUTDOWN) ; grpc_completion_queue_destroy(cq_); } diff --git a/src/cpp/client/secure_credentials.cc b/src/cpp/client/secure_credentials.cc index 4d200908fb2..01c7f14f1a0 100644 --- a/src/cpp/client/secure_credentials.cc +++ b/src/cpp/client/secure_credentials.cc @@ -92,7 +92,8 @@ std::shared_ptr ServiceAccountCredentials( "with non-positive lifetime"); return WrapCredentials(nullptr); } - gpr_timespec lifetime = gpr_time_from_seconds(token_lifetime_seconds); + gpr_timespec lifetime = + gpr_time_from_seconds(token_lifetime_seconds, GPR_TIMESPAN); return WrapCredentials(grpc_service_account_credentials_create( json_key.c_str(), scope.c_str(), lifetime)); } @@ -105,7 +106,8 @@ std::shared_ptr JWTCredentials(const grpc::string& json_key, "Trying to create JWTCredentials with non-positive lifetime"); return WrapCredentials(nullptr); } - gpr_timespec lifetime = gpr_time_from_seconds(token_lifetime_seconds); + gpr_timespec lifetime = + gpr_time_from_seconds(token_lifetime_seconds, GPR_TIMESPAN); return WrapCredentials( grpc_jwt_credentials_create(json_key.c_str(), lifetime)); } diff --git a/src/cpp/common/auth_property_iterator.cc b/src/cpp/common/auth_property_iterator.cc new file mode 100644 index 00000000000..e706c6c921d --- /dev/null +++ b/src/cpp/common/auth_property_iterator.cc @@ -0,0 +1,87 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include + +namespace grpc { + +AuthPropertyIterator::AuthPropertyIterator() + : property_(nullptr), ctx_(nullptr), index_(0), name_(nullptr) {} + +AuthPropertyIterator::AuthPropertyIterator( + const grpc_auth_property* property, const grpc_auth_property_iterator* iter) + : property_(property), + ctx_(iter->ctx), + index_(iter->index), + name_(iter->name) {} + +AuthPropertyIterator::~AuthPropertyIterator() {} + +AuthPropertyIterator& AuthPropertyIterator::operator++() { + grpc_auth_property_iterator iter = {ctx_, index_, name_}; + property_ = grpc_auth_property_iterator_next(&iter); + ctx_ = iter.ctx; + index_ = iter.index; + name_ = iter.name; + return *this; +} + +AuthPropertyIterator AuthPropertyIterator::operator++(int) { + AuthPropertyIterator tmp(*this); + operator++(); + return tmp; +} + +bool AuthPropertyIterator::operator==( + const AuthPropertyIterator& rhs) const { + if (property_ == nullptr || rhs.property_ == nullptr) { + return property_ == rhs.property_; + } else { + return index_ == rhs.index_; + } +} + +bool AuthPropertyIterator::operator!=( + const AuthPropertyIterator& rhs) const { + return !operator==(rhs); +} + +const AuthProperty AuthPropertyIterator::operator*() { + return std::make_pair( + grpc::string(property_->name), + grpc::string(property_->value, property_->value_length)); +} + +} // namespace grpc diff --git a/src/cpp/common/completion_queue.cc b/src/cpp/common/completion_queue.cc index b2dd1acdca2..593963f672f 100644 --- a/src/cpp/common/completion_queue.cc +++ b/src/cpp/common/completion_queue.cc @@ -70,7 +70,8 @@ CompletionQueue::NextStatus CompletionQueue::AsyncNextInternal( } bool CompletionQueue::Pluck(CompletionQueueTag* tag) { - auto ev = grpc_completion_queue_pluck(cq_, tag, gpr_inf_future); + auto ev = + grpc_completion_queue_pluck(cq_, tag, gpr_inf_future(GPR_CLOCK_REALTIME)); bool ok = ev.success != 0; void* ignored = tag; GPR_ASSERT(tag->FinalizeResult(&ignored, &ok)); @@ -80,7 +81,8 @@ bool CompletionQueue::Pluck(CompletionQueueTag* tag) { } void CompletionQueue::TryPluck(CompletionQueueTag* tag) { - auto ev = grpc_completion_queue_pluck(cq_, tag, gpr_time_0); + auto ev = + grpc_completion_queue_pluck(cq_, tag, gpr_time_0(GPR_CLOCK_REALTIME)); if (ev.type == GRPC_QUEUE_TIMEOUT) return; bool ok = ev.success != 0; void* ignored = tag; diff --git a/src/cpp/common/secure_auth_context.cc b/src/cpp/common/secure_auth_context.cc index 45137236536..87d7bab75c6 100644 --- a/src/cpp/common/secure_auth_context.cc +++ b/src/cpp/common/secure_auth_context.cc @@ -77,4 +77,20 @@ std::vector SecureAuthContext::FindPropertyValues( return values; } +AuthPropertyIterator SecureAuthContext::begin() const { + if (ctx_) { + grpc_auth_property_iterator iter = + grpc_auth_context_property_iterator(ctx_); + const grpc_auth_property* property = + grpc_auth_property_iterator_next(&iter); + return AuthPropertyIterator(property, &iter); + } else { + return end(); + } +} + +AuthPropertyIterator SecureAuthContext::end() const { + return AuthPropertyIterator(); +} + } // namespace grpc diff --git a/src/cpp/common/secure_auth_context.h b/src/cpp/common/secure_auth_context.h index bba46803cd6..264ed620a30 100644 --- a/src/cpp/common/secure_auth_context.h +++ b/src/cpp/common/secure_auth_context.h @@ -53,6 +53,10 @@ class SecureAuthContext GRPC_FINAL : public AuthContext { std::vector FindPropertyValues(const grpc::string& name) const GRPC_OVERRIDE; + AuthPropertyIterator begin() const GRPC_OVERRIDE; + + AuthPropertyIterator end() const GRPC_OVERRIDE; + private: grpc_auth_context* ctx_; }; diff --git a/src/cpp/server/async_server_context.cc b/src/cpp/server/async_server_context.cc deleted file mode 100644 index e1f29452a44..00000000000 --- a/src/cpp/server/async_server_context.cc +++ /dev/null @@ -1,99 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#include -#include -#include "src/cpp/proto/proto_utils.h" -#include -#include - -namespace grpc { - -AsyncServerContext::AsyncServerContext( - grpc_call* call, const grpc::string& method, const grpc::string& host, - system_clock::time_point absolute_deadline) - : method_(method), - host_(host), - absolute_deadline_(absolute_deadline), - request_(nullptr), - call_(call) {} - -AsyncServerContext::~AsyncServerContext() { grpc_call_destroy(call_); } - -void AsyncServerContext::Accept(grpc_completion_queue* cq) { - GPR_ASSERT(grpc_call_server_accept_old(call_, cq, this) == GRPC_CALL_OK); - GPR_ASSERT(grpc_call_server_end_initial_metadata_old( - call_, GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK); -} - -bool AsyncServerContext::StartRead(grpc::protobuf::Message* request) { - GPR_ASSERT(request); - request_ = request; - grpc_call_error err = grpc_call_start_read_old(call_, this); - return err == GRPC_CALL_OK; -} - -bool AsyncServerContext::StartWrite(const grpc::protobuf::Message& response, - int flags) { - grpc_byte_buffer* buffer = nullptr; - GRPC_TIMER_MARK(SER_PROTO_BEGIN, call_->call()); - if (!SerializeProto(response, &buffer)) { - return false; - } - GRPC_TIMER_MARK(SER_PROTO_END, call_->call()); - grpc_call_error err = grpc_call_start_write_old(call_, buffer, this, flags); - grpc_byte_buffer_destroy(buffer); - return err == GRPC_CALL_OK; -} - -bool AsyncServerContext::StartWriteStatus(const Status& status) { - grpc_call_error err = grpc_call_start_write_status_old( - call_, static_cast(status.code()), - status.details().empty() ? nullptr - : const_cast(status.details().c_str()), - this); - return err == GRPC_CALL_OK; -} - -bool AsyncServerContext::ParseRead(grpc_byte_buffer* read_buffer) { - GPR_ASSERT(request_); - GRPC_TIMER_MARK(DESER_PROTO_BEGIN, call_->call()); - bool success = DeserializeProto(read_buffer, request_); - GRPC_TIMER_MARK(DESER_PROTO_END, call_->call()); - request_ = nullptr; - return success; -} - -} // namespace grpc diff --git a/src/cpp/server/create_default_thread_pool.cc b/src/cpp/server/create_default_thread_pool.cc index 89c1d7e9295..cc182f59f4e 100644 --- a/src/cpp/server/create_default_thread_pool.cc +++ b/src/cpp/server/create_default_thread_pool.cc @@ -32,7 +32,7 @@ */ #include -#include "src/cpp/server/thread_pool.h" +#include #ifndef GRPC_CUSTOM_DEFAULT_THREAD_POOL @@ -41,7 +41,7 @@ namespace grpc { ThreadPoolInterface* CreateDefaultThreadPool() { int cores = gpr_cpu_num_cores(); if (!cores) cores = 4; - return new ThreadPool(cores); + return new FixedSizeThreadPool(cores); } } // namespace grpc diff --git a/src/cpp/server/thread_pool.cc b/src/cpp/server/fixed_size_thread_pool.cc similarity index 86% rename from src/cpp/server/thread_pool.cc rename to src/cpp/server/fixed_size_thread_pool.cc index 118cabcb612..bafbc5802a4 100644 --- a/src/cpp/server/thread_pool.cc +++ b/src/cpp/server/fixed_size_thread_pool.cc @@ -33,12 +33,11 @@ #include #include - -#include "src/cpp/server/thread_pool.h" +#include namespace grpc { -void ThreadPool::ThreadFunc() { +void FixedSizeThreadPool::ThreadFunc() { for (;;) { // Wait until work is available or we are shutting down. grpc::unique_lock lock(mu_); @@ -58,13 +57,14 @@ void ThreadPool::ThreadFunc() { } } -ThreadPool::ThreadPool(int num_threads) : shutdown_(false) { +FixedSizeThreadPool::FixedSizeThreadPool(int num_threads) : shutdown_(false) { for (int i = 0; i < num_threads; i++) { - threads_.push_back(new grpc::thread(&ThreadPool::ThreadFunc, this)); + threads_.push_back( + new grpc::thread(&FixedSizeThreadPool::ThreadFunc, this)); } } -ThreadPool::~ThreadPool() { +FixedSizeThreadPool::~FixedSizeThreadPool() { { grpc::lock_guard lock(mu_); shutdown_ = true; @@ -76,7 +76,7 @@ ThreadPool::~ThreadPool() { } } -void ThreadPool::ScheduleCallback(const std::function& callback) { +void FixedSizeThreadPool::Add(const std::function& callback) { grpc::lock_guard lock(mu_); callbacks_.push(callback); cv_.notify_one(); diff --git a/src/cpp/server/server.cc b/src/cpp/server/server.cc index e6761d62442..ab87b22f5fb 100644 --- a/src/cpp/server/server.cc +++ b/src/cpp/server/server.cc @@ -383,7 +383,7 @@ void Server::ScheduleCallback() { grpc::unique_lock lock(mu_); num_running_cb_++; } - thread_pool_->ScheduleCallback(std::bind(&Server::RunRpc, this)); + thread_pool_->Add(std::bind(&Server::RunRpc, this)); } void Server::RunRpc() { diff --git a/src/cpp/server/server_builder.cc b/src/cpp/server/server_builder.cc index 86c78f05ff9..f723d4611ae 100644 --- a/src/cpp/server/server_builder.cc +++ b/src/cpp/server/server_builder.cc @@ -37,7 +37,7 @@ #include #include #include -#include "src/cpp/server/thread_pool.h" +#include namespace grpc { diff --git a/src/cpp/server/server_context.cc b/src/cpp/server/server_context.cc index 1bb3a8bcc4c..3b8a0269964 100644 --- a/src/cpp/server/server_context.cc +++ b/src/cpp/server/server_context.cc @@ -144,7 +144,7 @@ void ServerContext::AddTrailingMetadata(const grpc::string& key, trailing_metadata_.insert(std::make_pair(key, value)); } -bool ServerContext::IsCancelled() { +bool ServerContext::IsCancelled() const { return completion_op_ && completion_op_->CheckCancelled(cq_); } @@ -153,4 +153,11 @@ void ServerContext::set_call(grpc_call* call) { auth_context_ = CreateAuthContext(call); } +std::shared_ptr ServerContext::auth_context() const { + if (auth_context_.get() == nullptr) { + auth_context_ = CreateAuthContext(call_); + } + return auth_context_; +} + } // namespace grpc diff --git a/src/cpp/util/time.cc b/src/cpp/util/time.cc index fd94d00b329..a814cad452e 100644 --- a/src/cpp/util/time.cc +++ b/src/cpp/util/time.cc @@ -51,13 +51,15 @@ void Timepoint2Timespec(const system_clock::time_point& from, system_clock::duration deadline = from.time_since_epoch(); seconds secs = duration_cast(deadline); if (from == system_clock::time_point::max() || - secs.count() >= gpr_inf_future.tv_sec || secs.count() < 0) { - *to = gpr_inf_future; + secs.count() >= gpr_inf_future(GPR_CLOCK_REALTIME).tv_sec || + secs.count() < 0) { + *to = gpr_inf_future(GPR_CLOCK_REALTIME); return; } nanoseconds nsecs = duration_cast(deadline - secs); to->tv_sec = secs.count(); to->tv_nsec = nsecs.count(); + to->clock_type = GPR_CLOCK_REALTIME; } void TimepointHR2Timespec(const high_resolution_clock::time_point& from, @@ -65,17 +67,19 @@ void TimepointHR2Timespec(const high_resolution_clock::time_point& from, high_resolution_clock::duration deadline = from.time_since_epoch(); seconds secs = duration_cast(deadline); if (from == high_resolution_clock::time_point::max() || - secs.count() >= gpr_inf_future.tv_sec || secs.count() < 0) { - *to = gpr_inf_future; + secs.count() >= gpr_inf_future(GPR_CLOCK_REALTIME).tv_sec || + secs.count() < 0) { + *to = gpr_inf_future(GPR_CLOCK_REALTIME); return; } nanoseconds nsecs = duration_cast(deadline - secs); to->tv_sec = secs.count(); to->tv_nsec = nsecs.count(); + to->clock_type = GPR_CLOCK_REALTIME; } system_clock::time_point Timespec2Timepoint(gpr_timespec t) { - if (gpr_time_cmp(t, gpr_inf_future) == 0) { + if (gpr_time_cmp(t, gpr_inf_future(GPR_CLOCK_REALTIME)) == 0) { return system_clock::time_point::max(); } system_clock::time_point tp; diff --git a/src/csharp/Grpc.Auth/Grpc.Auth.csproj b/src/csharp/Grpc.Auth/Grpc.Auth.csproj index e6abbbfdf01..fdec2e7bd7f 100644 --- a/src/csharp/Grpc.Auth/Grpc.Auth.csproj +++ b/src/csharp/Grpc.Auth/Grpc.Auth.csproj @@ -68,6 +68,9 @@ + + Version.cs + @@ -81,6 +84,7 @@ + diff --git a/src/csharp/Grpc.Auth/Grpc.Auth.nuspec b/src/csharp/Grpc.Auth/Grpc.Auth.nuspec index 978b04d70b3..1262bdbdab8 100644 --- a/src/csharp/Grpc.Auth/Grpc.Auth.nuspec +++ b/src/csharp/Grpc.Auth/Grpc.Auth.nuspec @@ -5,19 +5,19 @@ gRPC C# Auth Auth library for C# implementation of gRPC - an RPC library and framework Auth library for C# implementation of gRPC - an RPC library and framework. See project site for more info. - 0.6.0 + $version$ Google Inc. grpc-packages https://github.com/grpc/grpc/blob/master/LICENSE https://github.com/grpc/grpc false - Release 0.6.0 of gRPC C# + Release $version$ of gRPC C# Copyright 2015, Google Inc. gRPC RPC Protocol HTTP/2 Auth OAuth2 - + diff --git a/src/csharp/Grpc.Auth/OAuth2InterceptorFactory.cs b/src/csharp/Grpc.Auth/OAuth2InterceptorFactory.cs index ca384d1a6e4..420c4cb5371 100644 --- a/src/csharp/Grpc.Auth/OAuth2InterceptorFactory.cs +++ b/src/csharp/Grpc.Auth/OAuth2InterceptorFactory.cs @@ -52,10 +52,10 @@ namespace Grpc.Auth /// /// Creates OAuth2 interceptor. /// - public static HeaderInterceptorDelegate Create(GoogleCredential googleCredential) + public static MetadataInterceptorDelegate Create(GoogleCredential googleCredential) { var interceptor = new OAuth2Interceptor(googleCredential.InternalCredential, SystemClock.Default); - return new HeaderInterceptorDelegate(interceptor.InterceptHeaders); + return new MetadataInterceptorDelegate(interceptor.InterceptHeaders); } /// @@ -94,10 +94,10 @@ namespace Grpc.Auth return credential.Token.AccessToken; } - public void InterceptHeaders(Metadata.Builder headerBuilder) + public void InterceptHeaders(Metadata metadata) { var accessToken = GetAccessToken(CancellationToken.None); - headerBuilder.Add(new Metadata.MetadataEntry(AuthorizationHeader, Schema + " " + accessToken)); + metadata.Add(new Metadata.Entry(AuthorizationHeader, Schema + " " + accessToken)); } } } diff --git a/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs index 2cdf6435971..70cb32d5b2b 100644 --- a/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs @@ -9,6 +9,5 @@ using System.Runtime.CompilerServices; [assembly: AssemblyCopyright("Google Inc. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("0.6.*")] [assembly: InternalsVisibleTo("Grpc.Auth.Tests")] diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index 21f94d3cf55..e797dd82f24 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -73,12 +73,6 @@ namespace Grpc.Core.Tests Server server; Channel channel; - [TestFixtureSetUp] - public void InitClass() - { - GrpcEnvironment.Initialize(); - } - [SetUp] public void Init() { diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj index 92e28b7d74c..927954c448a 100644 --- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj +++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj @@ -37,6 +37,9 @@ + + Version.cs + @@ -63,4 +66,4 @@ - + \ No newline at end of file diff --git a/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs b/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs index 6a132a5b224..9ae12776f3c 100644 --- a/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs +++ b/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs @@ -43,16 +43,17 @@ namespace Grpc.Core.Tests [Test] public void InitializeAndShutdownGrpcEnvironment() { - GrpcEnvironment.Initialize(); - Assert.IsNotNull(GrpcEnvironment.ThreadPool.CompletionQueue); + var env = GrpcEnvironment.GetInstance(); + Assert.IsNotNull(env.CompletionQueue); GrpcEnvironment.Shutdown(); } [Test] public void SubsequentInvocations() { - GrpcEnvironment.Initialize(); - GrpcEnvironment.Initialize(); + var env1 = GrpcEnvironment.GetInstance(); + var env2 = GrpcEnvironment.GetInstance(); + Assert.IsTrue(object.ReferenceEquals(env1, env2)); GrpcEnvironment.Shutdown(); GrpcEnvironment.Shutdown(); } @@ -60,15 +61,13 @@ namespace Grpc.Core.Tests [Test] public void InitializeAfterShutdown() { - GrpcEnvironment.Initialize(); - var tp1 = GrpcEnvironment.ThreadPool; + var env1 = GrpcEnvironment.GetInstance(); GrpcEnvironment.Shutdown(); - GrpcEnvironment.Initialize(); - var tp2 = GrpcEnvironment.ThreadPool; + var env2 = GrpcEnvironment.GetInstance(); GrpcEnvironment.Shutdown(); - Assert.IsFalse(object.ReferenceEquals(tp1, tp2)); + Assert.IsFalse(object.ReferenceEquals(env1, env2)); } } } diff --git a/src/csharp/Grpc.Core.Tests/Internal/MetadataArraySafeHandleTest.cs b/src/csharp/Grpc.Core.Tests/Internal/MetadataArraySafeHandleTest.cs index 2f6013483d9..320423b245d 100644 --- a/src/csharp/Grpc.Core.Tests/Internal/MetadataArraySafeHandleTest.cs +++ b/src/csharp/Grpc.Core.Tests/Internal/MetadataArraySafeHandleTest.cs @@ -44,17 +44,17 @@ namespace Grpc.Core.Internal.Tests [Test] public void CreateEmptyAndDestroy() { - var metadata = Metadata.CreateBuilder().Build(); - var nativeMetadata = MetadataArraySafeHandle.Create(metadata); + var nativeMetadata = MetadataArraySafeHandle.Create(new Metadata()); nativeMetadata.Dispose(); } [Test] public void CreateAndDestroy() { - var metadata = Metadata.CreateBuilder() - .Add(new Metadata.MetadataEntry("host", "somehost")) - .Add(new Metadata.MetadataEntry("header2", "header value")).Build(); + var metadata = new Metadata { + new Metadata.Entry("host", "somehost"), + new Metadata.Entry("header2", "header value"), + }; var nativeMetadata = MetadataArraySafeHandle.Create(metadata); nativeMetadata.Dispose(); } diff --git a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs index 8b3c9102513..714c2f7494c 100644 --- a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs +++ b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs @@ -53,18 +53,6 @@ namespace Grpc.Core.Tests [DllImport("grpc_csharp_ext.dll")] static extern IntPtr grpcsharp_test_nop(IntPtr ptr); - [TestFixtureSetUp] - public void Init() - { - GrpcEnvironment.Initialize(); - } - - [TestFixtureTearDown] - public void Cleanup() - { - GrpcEnvironment.Shutdown(); - } - /// /// (~1.26us .NET Windows) /// diff --git a/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs index d5fffb8b183..c2e5e81e911 100644 --- a/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs @@ -9,4 +9,3 @@ using System.Runtime.CompilerServices; [assembly: AssemblyCopyright("Google Inc. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("0.6.*")] diff --git a/src/csharp/Grpc.Core.Tests/ServerTest.cs b/src/csharp/Grpc.Core.Tests/ServerTest.cs index 02c773c9ccc..1119aa370e5 100644 --- a/src/csharp/Grpc.Core.Tests/ServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ServerTest.cs @@ -44,13 +44,10 @@ namespace Grpc.Core.Tests [Test] public void StartAndShutdownServer() { - GrpcEnvironment.Initialize(); - Server server = new Server(); server.AddListeningPort("localhost", Server.PickUnusedPort); server.Start(); server.ShutdownAsync().Wait(); - GrpcEnvironment.Shutdown(); } } diff --git a/src/csharp/Grpc.Core.Tests/TimespecTest.cs b/src/csharp/Grpc.Core.Tests/TimespecTest.cs index f5bae6d9355..5831121add3 100644 --- a/src/csharp/Grpc.Core.Tests/TimespecTest.cs +++ b/src/csharp/Grpc.Core.Tests/TimespecTest.cs @@ -61,28 +61,28 @@ namespace Grpc.Core.Internal.Tests [Test] public void Add() { - var t = new Timespec { tv_sec = new IntPtr(12345), tv_nsec = new IntPtr(123456789) }; + var t = new Timespec { tv_sec = new IntPtr(12345), tv_nsec = 123456789 }; var result = t.Add(TimeSpan.FromTicks(TimeSpan.TicksPerSecond * 10)); Assert.AreEqual(result.tv_sec, new IntPtr(12355)); - Assert.AreEqual(result.tv_nsec, new IntPtr(123456789)); + Assert.AreEqual(result.tv_nsec, 123456789); } [Test] public void Add_Nanos() { - var t = new Timespec { tv_sec = new IntPtr(12345), tv_nsec = new IntPtr(123456789) }; + var t = new Timespec { tv_sec = new IntPtr(12345), tv_nsec = 123456789 }; var result = t.Add(TimeSpan.FromTicks(10)); Assert.AreEqual(result.tv_sec, new IntPtr(12345)); - Assert.AreEqual(result.tv_nsec, new IntPtr(123456789 + 1000)); + Assert.AreEqual(result.tv_nsec, 123456789 + 1000); } [Test] public void Add_NanosOverflow() { - var t = new Timespec { tv_sec = new IntPtr(12345), tv_nsec = new IntPtr(999999999) }; + var t = new Timespec { tv_sec = new IntPtr(12345), tv_nsec = 999999999 }; var result = t.Add(TimeSpan.FromTicks(TimeSpan.TicksPerSecond * 10 + 10)); Assert.AreEqual(result.tv_sec, new IntPtr(12356)); - Assert.AreEqual(result.tv_nsec, new IntPtr(999)); + Assert.AreEqual(result.tv_nsec, 999); } } } diff --git a/src/csharp/Grpc.Core/Calls.cs b/src/csharp/Grpc.Core/Calls.cs index 9f8baac6841..9e95182c720 100644 --- a/src/csharp/Grpc.Core/Calls.cs +++ b/src/csharp/Grpc.Core/Calls.cs @@ -39,7 +39,7 @@ using Grpc.Core.Internal; namespace Grpc.Core { /// - /// Helper methods for generated client stubs to make RPC calls. + /// Helper methods for generated clients to make RPC calls. /// public static class Calls { @@ -58,7 +58,7 @@ namespace Grpc.Core where TResponse : class { var asyncCall = new AsyncCall(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); - asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.Name); + asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name); var asyncResult = asyncCall.UnaryCallAsync(req, call.Headers); RegisterCancellationCallback(asyncCall, token); return await asyncResult; @@ -69,7 +69,7 @@ namespace Grpc.Core where TResponse : class { var asyncCall = new AsyncCall(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); - asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.Name); + asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name); asyncCall.StartServerStreamingCall(req, call.Headers); RegisterCancellationCallback(asyncCall, token); var responseStream = new ClientResponseStream(asyncCall); @@ -81,7 +81,7 @@ namespace Grpc.Core where TResponse : class { var asyncCall = new AsyncCall(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); - asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.Name); + asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name); var resultTask = asyncCall.ClientStreamingCallAsync(call.Headers); RegisterCancellationCallback(asyncCall, token); var requestStream = new ClientRequestStream(asyncCall); @@ -93,7 +93,7 @@ namespace Grpc.Core where TResponse : class { var asyncCall = new AsyncCall(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); - asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.Name); + asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name); asyncCall.StartDuplexStreamingCall(call.Headers); RegisterCancellationCallback(asyncCall, token); var requestStream = new ClientRequestStream(asyncCall); @@ -108,13 +108,5 @@ namespace Grpc.Core token.Register(() => asyncCall.Cancel()); } } - - /// - /// Gets shared completion queue used for async calls. - /// - private static CompletionQueueSafeHandle GetCompletionQueue() - { - return GrpcEnvironment.ThreadPool.CompletionQueue; - } } } diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs index d6bfbb7bc45..5baf2600031 100644 --- a/src/csharp/Grpc.Core/Channel.cs +++ b/src/csharp/Grpc.Core/Channel.cs @@ -42,8 +42,10 @@ namespace Grpc.Core /// public class Channel : IDisposable { + readonly GrpcEnvironment environment; readonly ChannelSafeHandle handle; readonly string target; + bool disposed; /// /// Creates a channel that connects to a specific host. @@ -54,6 +56,7 @@ namespace Grpc.Core /// Channel options. public Channel(string host, Credentials credentials = null, IEnumerable options = null) { + this.environment = GrpcEnvironment.GetInstance(); using (ChannelArgsSafeHandle nativeChannelArgs = ChannelOptions.CreateChannelArgs(options)) { if (credentials != null) @@ -105,10 +108,35 @@ namespace Grpc.Core } } + internal CompletionQueueSafeHandle CompletionQueue + { + get + { + return this.environment.CompletionQueue; + } + } + + internal CompletionRegistry CompletionRegistry + { + get + { + return this.environment.CompletionRegistry; + } + } + + internal GrpcEnvironment Environment + { + get + { + return this.environment; + } + } + protected virtual void Dispose(bool disposing) { - if (handle != null && !handle.IsInvalid) + if (disposing && handle != null && !disposed) { + disposed = true; handle.Dispose(); } } diff --git a/src/csharp/Grpc.Core/Stub/AbstractStub.cs b/src/csharp/Grpc.Core/ClientBase.cs similarity index 70% rename from src/csharp/Grpc.Core/Stub/AbstractStub.cs rename to src/csharp/Grpc.Core/ClientBase.cs index 4a8b2543579..a099f96aeab 100644 --- a/src/csharp/Grpc.Core/Stub/AbstractStub.cs +++ b/src/csharp/Grpc.Core/ClientBase.cs @@ -32,26 +32,39 @@ #endregion using System; +using System.Collections.Generic; + using Grpc.Core.Internal; namespace Grpc.Core { - // TODO: support adding timeout to methods. + public delegate void MetadataInterceptorDelegate(Metadata metadata); + /// - /// Base for client-side stubs. + /// Base class for client-side stubs. /// - public abstract class AbstractStub - where TConfig : StubConfiguration + public abstract class ClientBase { readonly Channel channel; - readonly TConfig config; - public AbstractStub(Channel channel, TConfig config) + public ClientBase(Channel channel) { this.channel = channel; - this.config = config; } + /// + /// Can be used to register a custom header (initial metadata) interceptor. + /// The delegate each time before a new call on this client is started. + /// + public MetadataInterceptorDelegate HeaderInterceptor + { + get; + set; + } + + /// + /// Channel associated with this client. + /// public Channel Channel { get @@ -63,13 +76,19 @@ namespace Grpc.Core /// /// Creates a new call to given method. /// - protected Call CreateCall(string serviceName, Method method) + protected Call CreateCall(string serviceName, Method method, Metadata metadata) where TRequest : class where TResponse : class { - var headerBuilder = Metadata.CreateBuilder(); - config.HeaderInterceptor(headerBuilder); - return new Call(serviceName, method, channel, headerBuilder.Build()); + var interceptor = HeaderInterceptor; + if (interceptor != null) + { + metadata = metadata ?? new Metadata(); + interceptor(metadata); + metadata.Freeze(); + } + metadata = metadata ?? Metadata.Empty; + return new Call(serviceName, method, channel, metadata); } } } diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj index 0e67da32453..a227fe54778 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.csproj +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -49,6 +49,7 @@ + @@ -87,8 +88,7 @@ - - + @@ -104,6 +104,7 @@ + diff --git a/src/csharp/Grpc.Core/Grpc.Core.nuspec b/src/csharp/Grpc.Core/Grpc.Core.nuspec index 457983532a0..5ace6dcf894 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.nuspec +++ b/src/csharp/Grpc.Core/Grpc.Core.nuspec @@ -5,19 +5,19 @@ gRPC C# Core Core C# implementation of gRPC - an RPC library and framework Core C# implementation of gRPC - an RPC library and framework. See project site for more info. - 0.6.0 + $version$ Google Inc. grpc-packages https://github.com/grpc/grpc/blob/master/LICENSE https://github.com/grpc/grpc false - Release 0.6.0 of gRPC C# + Release $version$ of gRPC C# Copyright 2015, Google Inc. gRPC RPC Protocol HTTP/2 - + diff --git a/src/csharp/Grpc.Core/GrpcEnvironment.cs b/src/csharp/Grpc.Core/GrpcEnvironment.cs index 30ff2897145..47d1651aab8 100644 --- a/src/csharp/Grpc.Core/GrpcEnvironment.cs +++ b/src/csharp/Grpc.Core/GrpcEnvironment.cs @@ -33,7 +33,9 @@ using System; using System.Runtime.InteropServices; +using System.Threading.Tasks; using Grpc.Core.Internal; +using Grpc.Core.Utils; namespace Grpc.Core { @@ -51,20 +53,18 @@ namespace Grpc.Core static extern void grpcsharp_shutdown(); static object staticLock = new object(); - static volatile GrpcEnvironment instance; + static GrpcEnvironment instance; readonly GrpcThreadPool threadPool; readonly CompletionRegistry completionRegistry; + readonly DebugStats debugStats = new DebugStats(); bool isClosed; /// - /// Makes sure GRPC environment is initialized. Subsequent invocations don't have any - /// effect unless you call Shutdown first. - /// Although normal use cases assume you will call this just once in your application's - /// lifetime (and call Shutdown once you're done), for the sake of easier testing it's - /// allowed to initialize the environment again after it has been successfully shutdown. + /// Returns an instance of initialized gRPC environment. + /// Subsequent invocations return the same instance unless Shutdown has been called first. /// - public static void Initialize() + internal static GrpcEnvironment GetInstance() { lock (staticLock) { @@ -72,12 +72,13 @@ namespace Grpc.Core { instance = new GrpcEnvironment(); } + return instance; } } /// - /// Shuts down the GRPC environment if it was initialized before. - /// Repeated invocations have no effect. + /// Shuts down the gRPC environment if it was initialized before. + /// Blocks until the environment has been fully shutdown. /// public static void Shutdown() { @@ -87,50 +88,55 @@ namespace Grpc.Core { instance.Close(); instance = null; - - CheckDebugStats(); } } } - internal static GrpcThreadPool ThreadPool + /// + /// Creates gRPC environment. + /// + private GrpcEnvironment() + { + GrpcLog.RedirectNativeLogs(Console.Error); + grpcsharp_init(); + completionRegistry = new CompletionRegistry(this); + threadPool = new GrpcThreadPool(this, THREAD_POOL_SIZE); + threadPool.Start(); + // TODO: use proper logging here + Console.WriteLine("GRPC initialized."); + } + + /// + /// Gets the completion registry used by this gRPC environment. + /// + internal CompletionRegistry CompletionRegistry { get { - var inst = instance; - if (inst == null) - { - throw new InvalidOperationException("GRPC environment not initialized"); - } - return inst.threadPool; + return this.completionRegistry; } } - internal static CompletionRegistry CompletionRegistry + /// + /// Gets the completion queue used by this gRPC environment. + /// + internal CompletionQueueSafeHandle CompletionQueue { get { - var inst = instance; - if (inst == null) - { - throw new InvalidOperationException("GRPC environment not initialized"); - } - return inst.completionRegistry; + return this.threadPool.CompletionQueue; } } /// - /// Creates gRPC environment. + /// Gets the completion queue used by this gRPC environment. /// - private GrpcEnvironment() + internal DebugStats DebugStats { - GrpcLog.RedirectNativeLogs(Console.Error); - grpcsharp_init(); - completionRegistry = new CompletionRegistry(); - threadPool = new GrpcThreadPool(THREAD_POOL_SIZE); - threadPool.Start(); - // TODO: use proper logging here - Console.WriteLine("GRPC initialized."); + get + { + return this.debugStats; + } } /// @@ -146,32 +152,28 @@ namespace Grpc.Core grpcsharp_shutdown(); isClosed = true; + debugStats.CheckOK(); + // TODO: use proper logging here Console.WriteLine("GRPC shutdown."); } - private static void CheckDebugStats() + /// + /// Shuts down this environment asynchronously. + /// + private Task CloseAsync() { - var remainingClientCalls = DebugStats.ActiveClientCalls.Count; - if (remainingClientCalls != 0) - { - DebugWarning(string.Format("Detected {0} client calls that weren't disposed properly.", remainingClientCalls)); - } - var remainingServerCalls = DebugStats.ActiveServerCalls.Count; - if (remainingServerCalls != 0) - { - DebugWarning(string.Format("Detected {0} server calls that weren't disposed properly.", remainingServerCalls)); - } - var pendingBatchCompletions = DebugStats.PendingBatchCompletions.Count; - if (pendingBatchCompletions != 0) + return Task.Run(() => { - DebugWarning(string.Format("Detected {0} pending batch completions.", pendingBatchCompletions)); - } - } - - private static void DebugWarning(string message) - { - throw new Exception("Shutdown check: " + message); + try + { + Close(); + } + catch (Exception e) + { + Console.WriteLine("Error occured while shutting down GrpcEnvironment: " + e); + } + }); } } } diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs index d350f45da6b..24b75d16686 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs @@ -47,6 +47,8 @@ namespace Grpc.Core.Internal /// internal class AsyncCall : AsyncCallBase { + Channel channel; + // Completion of a pending unary response if not null. TaskCompletionSource unaryResponseTcs; @@ -61,8 +63,9 @@ namespace Grpc.Core.Internal public void Initialize(Channel channel, CompletionQueueSafeHandle cq, string methodName) { - var call = CallSafeHandle.Create(channel.Handle, cq, methodName, channel.Target, Timespec.InfFuture); - DebugStats.ActiveClientCalls.Increment(); + this.channel = channel; + var call = CallSafeHandle.Create(channel.Handle, channel.CompletionRegistry, cq, methodName, channel.Target, Timespec.InfFuture); + channel.Environment.DebugStats.ActiveClientCalls.Increment(); InitializeInternal(call); } @@ -277,7 +280,7 @@ namespace Grpc.Core.Internal protected override void OnReleaseResources() { - DebugStats.ActiveClientCalls.Decrement(); + channel.Environment.DebugStats.ActiveClientCalls.Decrement(); } /// diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs index 4f510ba40ac..309067ea9de 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs @@ -48,14 +48,17 @@ namespace Grpc.Core.Internal internal class AsyncCallServer : AsyncCallBase { readonly TaskCompletionSource finishedServersideTcs = new TaskCompletionSource(); + readonly GrpcEnvironment environment; - public AsyncCallServer(Func serializer, Func deserializer) : base(serializer, deserializer) + public AsyncCallServer(Func serializer, Func deserializer, GrpcEnvironment environment) : base(serializer, deserializer) { + this.environment = Preconditions.CheckNotNull(environment); } public void Initialize(CallSafeHandle call) { - DebugStats.ActiveServerCalls.Increment(); + call.SetCompletionRegistry(environment.CompletionRegistry); + environment.DebugStats.ActiveServerCalls.Increment(); InitializeInternal(call); } @@ -114,7 +117,7 @@ namespace Grpc.Core.Internal protected override void OnReleaseResources() { - DebugStats.ActiveServerCalls.Decrement(); + environment.DebugStats.ActiveServerCalls.Decrement(); } /// diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs index ef92b44402b..3b246ac01bb 100644 --- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs @@ -43,6 +43,7 @@ namespace Grpc.Core.Internal internal class CallSafeHandle : SafeHandleZeroIsInvalid { const uint GRPC_WRITE_BUFFER_HINT = 1; + CompletionRegistry completionRegistry; [DllImport("grpc_csharp_ext.dll")] static extern CallSafeHandle grpcsharp_channel_create_call(ChannelSafeHandle channel, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline); @@ -97,15 +98,22 @@ namespace Grpc.Core.Internal { } - public static CallSafeHandle Create(ChannelSafeHandle channel, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline) + public static CallSafeHandle Create(ChannelSafeHandle channel, CompletionRegistry registry, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline) { - return grpcsharp_channel_create_call(channel, cq, method, host, deadline); + var result = grpcsharp_channel_create_call(channel, cq, method, host, deadline); + result.SetCompletionRegistry(registry); + return result; + } + + public void SetCompletionRegistry(CompletionRegistry completionRegistry) + { + this.completionRegistry = completionRegistry; } public void StartUnary(byte[] payload, BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray) { var ctx = BatchContextSafeHandle.Create(); - GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback); + completionRegistry.RegisterBatchCompletion(ctx, callback); grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray) .CheckOk(); } @@ -119,56 +127,56 @@ namespace Grpc.Core.Internal public void StartClientStreaming(BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray) { var ctx = BatchContextSafeHandle.Create(); - GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback); + completionRegistry.RegisterBatchCompletion(ctx, callback); grpcsharp_call_start_client_streaming(this, ctx, metadataArray).CheckOk(); } public void StartServerStreaming(byte[] payload, BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray) { var ctx = BatchContextSafeHandle.Create(); - GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback); + completionRegistry.RegisterBatchCompletion(ctx, callback); grpcsharp_call_start_server_streaming(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray).CheckOk(); } public void StartDuplexStreaming(BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray) { var ctx = BatchContextSafeHandle.Create(); - GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback); + completionRegistry.RegisterBatchCompletion(ctx, callback); grpcsharp_call_start_duplex_streaming(this, ctx, metadataArray).CheckOk(); } public void StartSendMessage(byte[] payload, BatchCompletionDelegate callback) { var ctx = BatchContextSafeHandle.Create(); - GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback); + completionRegistry.RegisterBatchCompletion(ctx, callback); grpcsharp_call_send_message(this, ctx, payload, new UIntPtr((ulong)payload.Length)).CheckOk(); } public void StartSendCloseFromClient(BatchCompletionDelegate callback) { var ctx = BatchContextSafeHandle.Create(); - GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback); + completionRegistry.RegisterBatchCompletion(ctx, callback); grpcsharp_call_send_close_from_client(this, ctx).CheckOk(); } public void StartSendStatusFromServer(Status status, BatchCompletionDelegate callback) { var ctx = BatchContextSafeHandle.Create(); - GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback); + completionRegistry.RegisterBatchCompletion(ctx, callback); grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, status.Detail).CheckOk(); } public void StartReceiveMessage(BatchCompletionDelegate callback) { var ctx = BatchContextSafeHandle.Create(); - GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback); + completionRegistry.RegisterBatchCompletion(ctx, callback); grpcsharp_call_recv_message(this, ctx).CheckOk(); } public void StartServerSide(BatchCompletionDelegate callback) { var ctx = BatchContextSafeHandle.Create(); - GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback); + completionRegistry.RegisterBatchCompletion(ctx, callback); grpcsharp_call_start_serverside(this, ctx).CheckOk(); } diff --git a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs index 80f006ae50d..f6d8aa0600f 100644 --- a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs +++ b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs @@ -45,11 +45,17 @@ namespace Grpc.Core.Internal internal class CompletionRegistry { - readonly ConcurrentDictionary dict = new ConcurrentDictionary(); + readonly GrpcEnvironment environment; + readonly ConcurrentDictionary dict = new ConcurrentDictionary(); + + public CompletionRegistry(GrpcEnvironment environment) + { + this.environment = environment; + } public void Register(IntPtr key, OpCompletionDelegate callback) { - DebugStats.PendingBatchCompletions.Increment(); + environment.DebugStats.PendingBatchCompletions.Increment(); Preconditions.CheckState(dict.TryAdd(key, callback)); } @@ -63,7 +69,7 @@ namespace Grpc.Core.Internal { OpCompletionDelegate value; Preconditions.CheckState(dict.TryRemove(key, out value)); - DebugStats.PendingBatchCompletions.Decrement(); + environment.DebugStats.PendingBatchCompletions.Decrement(); return value; } diff --git a/src/csharp/Grpc.Core/Internal/DebugStats.cs b/src/csharp/Grpc.Core/Internal/DebugStats.cs index ef9d9afe11c..8793450ff36 100644 --- a/src/csharp/Grpc.Core/Internal/DebugStats.cs +++ b/src/csharp/Grpc.Core/Internal/DebugStats.cs @@ -36,12 +36,39 @@ using System.Threading; namespace Grpc.Core.Internal { - internal static class DebugStats + internal class DebugStats { - public static readonly AtomicCounter ActiveClientCalls = new AtomicCounter(); + public readonly AtomicCounter ActiveClientCalls = new AtomicCounter(); - public static readonly AtomicCounter ActiveServerCalls = new AtomicCounter(); + public readonly AtomicCounter ActiveServerCalls = new AtomicCounter(); - public static readonly AtomicCounter PendingBatchCompletions = new AtomicCounter(); + public readonly AtomicCounter PendingBatchCompletions = new AtomicCounter(); + + /// + /// Checks the debug stats and take action for any inconsistency found. + /// + public void CheckOK() + { + var remainingClientCalls = ActiveClientCalls.Count; + if (remainingClientCalls != 0) + { + DebugWarning(string.Format("Detected {0} client calls that weren't disposed properly.", remainingClientCalls)); + } + var remainingServerCalls = ActiveServerCalls.Count; + if (remainingServerCalls != 0) + { + DebugWarning(string.Format("Detected {0} server calls that weren't disposed properly.", remainingServerCalls)); + } + var pendingBatchCompletions = PendingBatchCompletions.Count; + if (pendingBatchCompletions != 0) + { + DebugWarning(string.Format("Detected {0} pending batch completions.", pendingBatchCompletions)); + } + } + + private void DebugWarning(string message) + { + throw new Exception("Shutdown check: " + message); + } } } diff --git a/src/csharp/Grpc.Core/Internal/Enums.cs b/src/csharp/Grpc.Core/Internal/Enums.cs index af11b5b9f3c..185098160b6 100644 --- a/src/csharp/Grpc.Core/Internal/Enums.cs +++ b/src/csharp/Grpc.Core/Internal/Enums.cs @@ -90,4 +90,19 @@ namespace Grpc.Core.Internal /* operation completion */ OpComplete } + + /// + /// gpr_clock_type from grpc/support/time.h + /// + internal enum GPRClockType + { + /* Monotonic clock */ + Monotonic, + + /* Realtime clock */ + Realtime, + + /* Timespan - the distance between two time points */ + Timespan + } } diff --git a/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs b/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs index 89b44a4e2bd..b77e8930447 100644 --- a/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs +++ b/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs @@ -45,14 +45,16 @@ namespace Grpc.Core.Internal /// internal class GrpcThreadPool { + readonly GrpcEnvironment environment; readonly object myLock = new object(); readonly List threads = new List(); readonly int poolSize; CompletionQueueSafeHandle cq; - public GrpcThreadPool(int poolSize) + public GrpcThreadPool(GrpcEnvironment environment, int poolSize) { + this.environment = environment; this.poolSize = poolSize; } @@ -80,7 +82,7 @@ namespace Grpc.Core.Internal { cq.Shutdown(); - Console.WriteLine("Waiting for GPRC threads to finish."); + Console.WriteLine("Waiting for GRPC threads to finish."); foreach (var thread in threads) { thread.Join(); @@ -122,7 +124,7 @@ namespace Grpc.Core.Internal IntPtr tag = ev.tag; try { - var callback = GrpcEnvironment.CompletionRegistry.Extract(tag); + var callback = environment.CompletionRegistry.Extract(tag); callback(success); } catch (Exception e) diff --git a/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs index c9c4d954c91..80aa7f56034 100644 --- a/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs @@ -54,11 +54,11 @@ namespace Grpc.Core.Internal public static MetadataArraySafeHandle Create(Metadata metadata) { - var entries = metadata.Entries; - var metadataArray = grpcsharp_metadata_array_create(new UIntPtr((ulong)entries.Count)); - for (int i = 0; i < entries.Count; i++) + // TODO(jtattermusch): we might wanna check that the metadata is readonly + var metadataArray = grpcsharp_metadata_array_create(new UIntPtr((ulong)metadata.Count)); + for (int i = 0; i < metadata.Count; i++) { - grpcsharp_metadata_array_add(metadataArray, entries[i].Key, entries[i].ValueBytes, new UIntPtr((ulong)entries[i].ValueBytes.Length)); + grpcsharp_metadata_array_add(metadataArray, metadata[i].Key, metadata[i].ValueBytes, new UIntPtr((ulong)metadata[i].ValueBytes.Length)); } return metadataArray; } diff --git a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs index c0e5bae13f0..594e46b1598 100644 --- a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs +++ b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs @@ -42,7 +42,7 @@ namespace Grpc.Core.Internal { internal interface IServerCallHandler { - Task HandleCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq); + Task HandleCall(string methodName, CallSafeHandle call, GrpcEnvironment environment); } internal class UnaryServerCallHandler : IServerCallHandler @@ -58,11 +58,12 @@ namespace Grpc.Core.Internal this.handler = handler; } - public async Task HandleCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq) + public async Task HandleCall(string methodName, CallSafeHandle call, GrpcEnvironment environment) { var asyncCall = new AsyncCallServer( method.ResponseMarshaller.Serializer, - method.RequestMarshaller.Deserializer); + method.RequestMarshaller.Deserializer, + environment); asyncCall.Initialize(call); var finishedTask = asyncCall.ServerSideCallAsync(); @@ -110,11 +111,12 @@ namespace Grpc.Core.Internal this.handler = handler; } - public async Task HandleCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq) + public async Task HandleCall(string methodName, CallSafeHandle call, GrpcEnvironment environment) { var asyncCall = new AsyncCallServer( method.ResponseMarshaller.Serializer, - method.RequestMarshaller.Deserializer); + method.RequestMarshaller.Deserializer, + environment); asyncCall.Initialize(call); var finishedTask = asyncCall.ServerSideCallAsync(); @@ -163,11 +165,12 @@ namespace Grpc.Core.Internal this.handler = handler; } - public async Task HandleCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq) + public async Task HandleCall(string methodName, CallSafeHandle call, GrpcEnvironment environment) { var asyncCall = new AsyncCallServer( method.ResponseMarshaller.Serializer, - method.RequestMarshaller.Deserializer); + method.RequestMarshaller.Deserializer, + environment); asyncCall.Initialize(call); var finishedTask = asyncCall.ServerSideCallAsync(); @@ -219,11 +222,12 @@ namespace Grpc.Core.Internal this.handler = handler; } - public async Task HandleCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq) + public async Task HandleCall(string methodName, CallSafeHandle call, GrpcEnvironment environment) { var asyncCall = new AsyncCallServer( method.ResponseMarshaller.Serializer, - method.RequestMarshaller.Deserializer); + method.RequestMarshaller.Deserializer, + environment); asyncCall.Initialize(call); var finishedTask = asyncCall.ServerSideCallAsync(); @@ -255,11 +259,11 @@ namespace Grpc.Core.Internal internal class NoSuchMethodCallHandler : IServerCallHandler { - public async Task HandleCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq) + public async Task HandleCall(string methodName, CallSafeHandle call, GrpcEnvironment environment) { // We don't care about the payload type here. var asyncCall = new AsyncCallServer( - (payload) => payload, (payload) => payload); + (payload) => payload, (payload) => payload, environment); asyncCall.Initialize(call); var finishedTask = asyncCall.ServerSideCallAsync(); diff --git a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs index 83dbb910aa7..9e1170e6ddb 100644 --- a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs @@ -91,19 +91,19 @@ namespace Grpc.Core.Internal { grpcsharp_server_start(this); } - - public void ShutdownAndNotify(CompletionQueueSafeHandle cq, BatchCompletionDelegate callback) + + public void ShutdownAndNotify(BatchCompletionDelegate callback, GrpcEnvironment environment) { var ctx = BatchContextSafeHandle.Create(); - GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback); - grpcsharp_server_shutdown_and_notify_callback(this, cq, ctx); + environment.CompletionRegistry.RegisterBatchCompletion(ctx, callback); + grpcsharp_server_shutdown_and_notify_callback(this, environment.CompletionQueue, ctx); } - public void RequestCall(CompletionQueueSafeHandle cq, BatchCompletionDelegate callback) + public void RequestCall(BatchCompletionDelegate callback, GrpcEnvironment environment) { var ctx = BatchContextSafeHandle.Create(); - GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback); - grpcsharp_server_request_call(this, cq, ctx).CheckOk(); + environment.CompletionRegistry.RegisterBatchCompletion(ctx, callback); + grpcsharp_server_request_call(this, environment.CompletionQueue, ctx).CheckOk(); } protected override bool ReleaseHandle() diff --git a/src/csharp/Grpc.Core/Internal/Timespec.cs b/src/csharp/Grpc.Core/Internal/Timespec.cs index 775af27db90..de783f5a4bc 100644 --- a/src/csharp/Grpc.Core/Internal/Timespec.cs +++ b/src/csharp/Grpc.Core/Internal/Timespec.cs @@ -55,7 +55,8 @@ namespace Grpc.Core.Internal // NOTE: on linux 64bit sizeof(gpr_timespec) = 16, on windows 32bit sizeof(gpr_timespec) = 8 // so IntPtr seems to have the right size to work on both. public System.IntPtr tv_sec; - public System.IntPtr tv_nsec; + public int tv_nsec; + public GPRClockType clock_type; /// /// Timespec a long time in the future. @@ -99,12 +100,13 @@ namespace Grpc.Core.Internal public Timespec Add(TimeSpan timeSpan) { - long nanos = tv_nsec.ToInt64() + (timeSpan.Ticks % TimeSpan.TicksPerSecond) * NanosPerTick; + long nanos = (long)tv_nsec + (timeSpan.Ticks % TimeSpan.TicksPerSecond) * NanosPerTick; long overflow_sec = (nanos > NanosPerSecond) ? 1 : 0; Timespec result; - result.tv_nsec = new IntPtr(nanos % NanosPerSecond); + result.tv_nsec = (int)(nanos % NanosPerSecond); result.tv_sec = new IntPtr(tv_sec.ToInt64() + (timeSpan.Ticks / TimeSpan.TicksPerSecond) + overflow_sec); + result.clock_type = GPRClockType.Realtime; return result; } } diff --git a/src/csharp/Grpc.Core/Metadata.cs b/src/csharp/Grpc.Core/Metadata.cs index eccec26a616..4552d39d88e 100644 --- a/src/csharp/Grpc.Core/Metadata.cs +++ b/src/csharp/Grpc.Core/Metadata.cs @@ -30,55 +30,163 @@ #endregion using System; +using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; +using System.Collections.Specialized; using System.Runtime.InteropServices; using System.Text; +using Grpc.Core.Utils; + namespace Grpc.Core { /// - /// gRPC call metadata. + /// Provides access to read and write metadata values to be exchanged during a call. /// - public class Metadata + public sealed class Metadata : IList { - public static readonly Metadata Empty = new Metadata(ImmutableList.Empty); + /// + /// An read-only instance of metadata containing no entries. + /// + public static readonly Metadata Empty = new Metadata().Freeze(); + + readonly List entries; + bool readOnly; + + public Metadata() + { + this.entries = new List(); + } + + public Metadata(ICollection entries) + { + this.entries = new List(entries); + } + + /// + /// Makes this object read-only. + /// + /// this object + public Metadata Freeze() + { + this.readOnly = true; + return this; + } + + // TODO: add support for access by key + + #region IList members + + public int IndexOf(Metadata.Entry item) + { + return entries.IndexOf(item); + } - readonly ImmutableList entries; + public void Insert(int index, Metadata.Entry item) + { + CheckWriteable(); + entries.Insert(index, item); + } - public Metadata(ImmutableList entries) + public void RemoveAt(int index) { - this.entries = entries; + CheckWriteable(); + entries.RemoveAt(index); } - public ImmutableList Entries + public Metadata.Entry this[int index] { get { - return this.entries; + return entries[index]; + } + + set + { + CheckWriteable(); + entries[index] = value; } } - public static Builder CreateBuilder() + public void Add(Metadata.Entry item) + { + CheckWriteable(); + entries.Add(item); + } + + public void Clear() + { + CheckWriteable(); + entries.Clear(); + } + + public bool Contains(Metadata.Entry item) + { + return entries.Contains(item); + } + + public void CopyTo(Metadata.Entry[] array, int arrayIndex) { - return new Builder(); + entries.CopyTo(array, arrayIndex); } - - public struct MetadataEntry + + public int Count + { + get { return entries.Count; } + } + + public bool IsReadOnly + { + get { return readOnly; } + } + + public bool Remove(Metadata.Entry item) + { + CheckWriteable(); + return entries.Remove(item); + } + + public IEnumerator GetEnumerator() + { + return entries.GetEnumerator(); + } + + IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return entries.GetEnumerator(); + } + + private void CheckWriteable() + { + Preconditions.CheckState(!readOnly, "Object is read only"); + } + + #endregion + + /// + /// Metadata entry + /// + public struct Entry { + private static readonly Encoding Encoding = Encoding.ASCII; + readonly string key; - readonly byte[] valueBytes; + string value; + byte[] valueBytes; - public MetadataEntry(string key, byte[] valueBytes) + public Entry(string key, byte[] valueBytes) { - this.key = key; - this.valueBytes = valueBytes; + this.key = Preconditions.CheckNotNull(key); + this.value = null; + this.valueBytes = Preconditions.CheckNotNull(valueBytes); } - public MetadataEntry(string key, string value) + public Entry(string key, string value) { - this.key = key; - this.valueBytes = Encoding.ASCII.GetBytes(value); + this.key = Preconditions.CheckNotNull(key); + this.value = Preconditions.CheckNotNull(value); + this.valueBytes = null; } public string Key @@ -89,38 +197,29 @@ namespace Grpc.Core } } - // TODO: using ByteString would guarantee immutability. public byte[] ValueBytes { get { - return this.valueBytes; + if (valueBytes == null) + { + valueBytes = Encoding.GetBytes(value); + } + return valueBytes; } } - } - public class Builder - { - readonly List entries = new List(); - - public List Entries + public string Value { get { - return entries; + if (value == null) + { + value = Encoding.GetString(valueBytes); + } + return value; } } - - public Builder Add(MetadataEntry entry) - { - entries.Add(entry); - return this; - } - - public Metadata Build() - { - return new Metadata(entries.ToImmutableList()); - } } } } diff --git a/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs index c57eef65aac..2b3d7530f2c 100644 --- a/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs @@ -9,6 +9,5 @@ using System.Runtime.CompilerServices; [assembly: AssemblyCopyright("Google Inc. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("0.6.*")] [assembly: InternalsVisibleTo("Grpc.Core.Tests")] diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs index 8e818885d18..cbf77196cf3 100644 --- a/src/csharp/Grpc.Core/Server.cs +++ b/src/csharp/Grpc.Core/Server.cs @@ -52,6 +52,7 @@ namespace Grpc.Core /// public const int PickUnusedPort = 0; + readonly GrpcEnvironment environment; readonly ServerSafeHandle handle; readonly object myLock = new object(); @@ -67,9 +68,10 @@ namespace Grpc.Core /// Channel options. public Server(IEnumerable options = null) { + this.environment = GrpcEnvironment.GetInstance(); using (var channelArgs = ChannelOptions.CreateChannelArgs(options)) { - this.handle = ServerSafeHandle.NewServer(GetCompletionQueue(), channelArgs); + this.handle = ServerSafeHandle.NewServer(environment.CompletionQueue, channelArgs); } } @@ -144,7 +146,7 @@ namespace Grpc.Core shutdownRequested = true; } - handle.ShutdownAndNotify(GetCompletionQueue(), HandleServerShutdown); + handle.ShutdownAndNotify(HandleServerShutdown, environment); await shutdownTcs.Task; handle.Dispose(); } @@ -173,7 +175,7 @@ namespace Grpc.Core shutdownRequested = true; } - handle.ShutdownAndNotify(GetCompletionQueue(), HandleServerShutdown); + handle.ShutdownAndNotify(HandleServerShutdown, environment); handle.CancelAllCalls(); await shutdownTcs.Task; handle.Dispose(); @@ -208,7 +210,7 @@ namespace Grpc.Core { if (!shutdownRequested) { - handle.RequestCall(GetCompletionQueue(), HandleNewServerRpc); + handle.RequestCall(HandleNewServerRpc, environment); } } } @@ -225,7 +227,7 @@ namespace Grpc.Core { callHandler = new NoSuchMethodCallHandler(); } - await callHandler.HandleCall(method, call, GetCompletionQueue()); + await callHandler.HandleCall(method, call, environment); } catch (Exception e) { @@ -259,10 +261,5 @@ namespace Grpc.Core { shutdownTcs.SetResult(null); } - - private static CompletionQueueSafeHandle GetCompletionQueue() - { - return GrpcEnvironment.ThreadPool.CompletionQueue; - } } } diff --git a/src/csharp/Grpc.Core/Version.cs b/src/csharp/Grpc.Core/Version.cs new file mode 100644 index 00000000000..f1db1f61578 --- /dev/null +++ b/src/csharp/Grpc.Core/Version.cs @@ -0,0 +1,5 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// The current version of gRPC C#. +[assembly: AssemblyVersion("0.6.0.*")] diff --git a/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj b/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj index 19bb4347aa8..5d5401593d0 100644 --- a/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj +++ b/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj @@ -1,4 +1,4 @@ - + Debug @@ -35,6 +35,9 @@ + + Version.cs + @@ -49,4 +52,4 @@ Grpc.Examples - + \ No newline at end of file diff --git a/src/csharp/Grpc.Examples.MathClient/MathClient.cs b/src/csharp/Grpc.Examples.MathClient/MathClient.cs index 360fe928dd8..cfe2a069160 100644 --- a/src/csharp/Grpc.Examples.MathClient/MathClient.cs +++ b/src/csharp/Grpc.Examples.MathClient/MathClient.cs @@ -39,22 +39,20 @@ namespace math { public static void Main(string[] args) { - GrpcEnvironment.Initialize(); - using (Channel channel = new Channel("127.0.0.1", 23456)) { - Math.IMathClient stub = new Math.MathClient(channel); - MathExamples.DivExample(stub); + Math.IMathClient client = new Math.MathClient(channel); + MathExamples.DivExample(client); - MathExamples.DivAsyncExample(stub).Wait(); + MathExamples.DivAsyncExample(client).Wait(); - MathExamples.FibExample(stub).Wait(); + MathExamples.FibExample(client).Wait(); - MathExamples.SumExample(stub).Wait(); + MathExamples.SumExample(client).Wait(); - MathExamples.DivManyExample(stub).Wait(); + MathExamples.DivManyExample(client).Wait(); - MathExamples.DependendRequestsExample(stub).Wait(); + MathExamples.DependendRequestsExample(client).Wait(); } GrpcEnvironment.Shutdown(); diff --git a/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs index a57c540215c..0fb0dbd510a 100644 --- a/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs @@ -9,4 +9,3 @@ using System.Runtime.CompilerServices; [assembly: AssemblyCopyright("Google Inc. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("0.6.*")] diff --git a/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj b/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj index ba6586ee5e2..677d87da205 100644 --- a/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj +++ b/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj @@ -35,6 +35,9 @@ + + Version.cs + diff --git a/src/csharp/Grpc.Examples.MathServer/MathServer.cs b/src/csharp/Grpc.Examples.MathServer/MathServer.cs index d05e3f28080..f4409851127 100644 --- a/src/csharp/Grpc.Examples.MathServer/MathServer.cs +++ b/src/csharp/Grpc.Examples.MathServer/MathServer.cs @@ -42,8 +42,6 @@ namespace math { string host = "0.0.0.0"; - GrpcEnvironment.Initialize(); - Server server = new Server(); server.AddServiceDefinition(Math.BindService(new MathServiceImpl())); int port = server.AddListeningPort(host, 23456); diff --git a/src/csharp/Grpc.Examples.MathServer/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples.MathServer/Properties/AssemblyInfo.cs index 6c772cb45cf..63035b6c63b 100644 --- a/src/csharp/Grpc.Examples.MathServer/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.Examples.MathServer/Properties/AssemblyInfo.cs @@ -9,4 +9,3 @@ using System.Runtime.CompilerServices; [assembly: AssemblyCopyright("Google Inc. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("0.6.*")] diff --git a/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj b/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj index 6e84add42ba..d59d7515d1f 100644 --- a/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj +++ b/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj @@ -43,6 +43,9 @@ + + Version.cs + diff --git a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs index aadd49f795d..e7c4b331208 100644 --- a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs +++ b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs @@ -49,33 +49,30 @@ namespace math.Tests string host = "localhost"; Server server; Channel channel; - Math.IMathClient client; + Math.MathClient client; [TestFixtureSetUp] public void Init() { - GrpcEnvironment.Initialize(); - server = new Server(); server.AddServiceDefinition(Math.BindService(new MathServiceImpl())); int port = server.AddListeningPort(host, Server.PickUnusedPort); server.Start(); channel = new Channel(host, port); + client = Math.NewClient(channel); // TODO(jtattermusch): get rid of the custom header here once we have dedicated tests // for header support. - var stubConfig = new StubConfiguration((headerBuilder) => + client.HeaderInterceptor = (metadata) => { - headerBuilder.Add(new Metadata.MetadataEntry("customHeader", "abcdef")); - }); - client = Math.NewStub(channel, stubConfig); + metadata.Add(new Metadata.Entry("customHeader", "abcdef")); + }; } [TestFixtureTearDown] public void Cleanup() { channel.Dispose(); - server.ShutdownAsync().Wait(); GrpcEnvironment.Shutdown(); } diff --git a/src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs index 4acaeaa438e..846afb46164 100644 --- a/src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs @@ -9,4 +9,3 @@ using System.Runtime.CompilerServices; [assembly: AssemblyCopyright("Google Inc. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("0.6.*")] diff --git a/src/csharp/Grpc.Examples/Grpc.Examples.csproj b/src/csharp/Grpc.Examples/Grpc.Examples.csproj index 5ce490f4034..eaf24a253c0 100644 --- a/src/csharp/Grpc.Examples/Grpc.Examples.csproj +++ b/src/csharp/Grpc.Examples/Grpc.Examples.csproj @@ -40,6 +40,9 @@ + + Version.cs + diff --git a/src/csharp/Grpc.Examples/MathExamples.cs b/src/csharp/Grpc.Examples/MathExamples.cs index d2cfbee18f7..7deb6516893 100644 --- a/src/csharp/Grpc.Examples/MathExamples.cs +++ b/src/csharp/Grpc.Examples/MathExamples.cs @@ -38,29 +38,29 @@ namespace math { public static class MathExamples { - public static void DivExample(Math.IMathClient stub) + public static void DivExample(Math.IMathClient client) { - DivReply result = stub.Div(new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build()); + DivReply result = client.Div(new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build()); Console.WriteLine("Div Result: " + result); } - public static async Task DivAsyncExample(Math.IMathClient stub) + public static async Task DivAsyncExample(Math.IMathClient client) { - Task resultTask = stub.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build()); + Task resultTask = client.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build()); DivReply result = await resultTask; Console.WriteLine("DivAsync Result: " + result); } - public static async Task FibExample(Math.IMathClient stub) + public static async Task FibExample(Math.IMathClient client) { - using (var call = stub.Fib(new FibArgs.Builder { Limit = 5 }.Build())) + using (var call = client.Fib(new FibArgs.Builder { Limit = 5 }.Build())) { List result = await call.ResponseStream.ToList(); Console.WriteLine("Fib Result: " + string.Join("|", result)); } } - public static async Task SumExample(Math.IMathClient stub) + public static async Task SumExample(Math.IMathClient client) { var numbers = new List { @@ -69,14 +69,14 @@ namespace math new Num.Builder { Num_ = 3 }.Build() }; - using (var call = stub.Sum()) + using (var call = client.Sum()) { await call.RequestStream.WriteAll(numbers); Console.WriteLine("Sum Result: " + await call.Result); } } - public static async Task DivManyExample(Math.IMathClient stub) + public static async Task DivManyExample(Math.IMathClient client) { var divArgsList = new List { @@ -84,14 +84,14 @@ namespace math new DivArgs.Builder { Dividend = 100, Divisor = 21 }.Build(), new DivArgs.Builder { Dividend = 7, Divisor = 2 }.Build() }; - using (var call = stub.DivMany()) + using (var call = client.DivMany()) { await call.RequestStream.WriteAll(divArgsList); Console.WriteLine("DivMany Result: " + string.Join("|", await call.ResponseStream.ToList())); } } - public static async Task DependendRequestsExample(Math.IMathClient stub) + public static async Task DependendRequestsExample(Math.IMathClient client) { var numbers = new List { @@ -101,13 +101,13 @@ namespace math }; Num sum; - using (var sumCall = stub.Sum()) + using (var sumCall = client.Sum()) { await sumCall.RequestStream.WriteAll(numbers); sum = await sumCall.Result; } - DivReply result = await stub.DivAsync(new DivArgs.Builder { Dividend = sum.Num_, Divisor = numbers.Count }.Build()); + DivReply result = await client.DivAsync(new DivArgs.Builder { Dividend = sum.Num_, Divisor = numbers.Count }.Build()); Console.WriteLine("Avg Result: " + result); } } diff --git a/src/csharp/Grpc.Examples/MathGrpc.cs b/src/csharp/Grpc.Examples/MathGrpc.cs index b9efc44e8c1..1805972ce33 100644 --- a/src/csharp/Grpc.Examples/MathGrpc.cs +++ b/src/csharp/Grpc.Examples/MathGrpc.cs @@ -41,14 +41,14 @@ namespace math { __Marshaller_Num, __Marshaller_Num); - // client-side stub interface + // client interface public interface IMathClient { - global::math.DivReply Div(global::math.DivArgs request, CancellationToken token = default(CancellationToken)); - Task DivAsync(global::math.DivArgs request, CancellationToken token = default(CancellationToken)); - AsyncDuplexStreamingCall DivMany(CancellationToken token = default(CancellationToken)); - AsyncServerStreamingCall Fib(global::math.FibArgs request, CancellationToken token = default(CancellationToken)); - AsyncClientStreamingCall Sum(CancellationToken token = default(CancellationToken)); + global::math.DivReply Div(global::math.DivArgs request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); + Task DivAsync(global::math.DivArgs request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncDuplexStreamingCall DivMany(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncServerStreamingCall Fib(global::math.FibArgs request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncClientStreamingCall Sum(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); } // server-side interface @@ -61,38 +61,35 @@ namespace math { } // client stub - public class MathClient : AbstractStub, IMathClient + public class MathClient : ClientBase, IMathClient { - public MathClient(Channel channel) : this(channel, StubConfiguration.Default) + public MathClient(Channel channel) : base(channel) { } - public MathClient(Channel channel, StubConfiguration config) : base(channel, config) + public global::math.DivReply Div(global::math.DivArgs request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) { + var call = CreateCall(__ServiceName, __Method_Div, headers); + return Calls.BlockingUnaryCall(call, request, cancellationToken); } - public global::math.DivReply Div(global::math.DivArgs request, CancellationToken token = default(CancellationToken)) + public Task DivAsync(global::math.DivArgs request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_Div); - return Calls.BlockingUnaryCall(call, request, token); + var call = CreateCall(__ServiceName, __Method_Div, headers); + return Calls.AsyncUnaryCall(call, request, cancellationToken); } - public Task DivAsync(global::math.DivArgs request, CancellationToken token = default(CancellationToken)) + public AsyncDuplexStreamingCall DivMany(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_Div); - return Calls.AsyncUnaryCall(call, request, token); + var call = CreateCall(__ServiceName, __Method_DivMany, headers); + return Calls.AsyncDuplexStreamingCall(call, cancellationToken); } - public AsyncDuplexStreamingCall DivMany(CancellationToken token = default(CancellationToken)) + public AsyncServerStreamingCall Fib(global::math.FibArgs request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_DivMany); - return Calls.AsyncDuplexStreamingCall(call, token); + var call = CreateCall(__ServiceName, __Method_Fib, headers); + return Calls.AsyncServerStreamingCall(call, request, cancellationToken); } - public AsyncServerStreamingCall Fib(global::math.FibArgs request, CancellationToken token = default(CancellationToken)) + public AsyncClientStreamingCall Sum(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_Fib); - return Calls.AsyncServerStreamingCall(call, request, token); - } - public AsyncClientStreamingCall Sum(CancellationToken token = default(CancellationToken)) - { - var call = CreateCall(__ServiceName, __Method_Sum); - return Calls.AsyncClientStreamingCall(call, token); + var call = CreateCall(__ServiceName, __Method_Sum, headers); + return Calls.AsyncClientStreamingCall(call, cancellationToken); } } @@ -106,17 +103,12 @@ namespace math { .AddMethod(__Method_Sum, serviceImpl.Sum).Build(); } - // creates a new client stub - public static IMathClient NewStub(Channel channel) + // creates a new client + public static MathClient NewClient(Channel channel) { return new MathClient(channel); } - // creates a new client stub - public static IMathClient NewStub(Channel channel, StubConfiguration config) - { - return new MathClient(channel, config); - } } } #endregion diff --git a/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs index 60a7aaea137..92111389cb9 100644 --- a/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs @@ -9,4 +9,3 @@ using System.Runtime.CompilerServices; [assembly: AssemblyCopyright("Google Inc. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("0.6.*")] diff --git a/src/csharp/Grpc.HealthCheck.Tests/.gitignore b/src/csharp/Grpc.HealthCheck.Tests/.gitignore new file mode 100644 index 00000000000..1746e3269ed --- /dev/null +++ b/src/csharp/Grpc.HealthCheck.Tests/.gitignore @@ -0,0 +1,2 @@ +bin +obj diff --git a/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj b/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj new file mode 100644 index 00000000000..72e110302b6 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj @@ -0,0 +1,82 @@ + + + + + Debug + AnyCPU + {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D} + Library + Properties + Grpc.HealthCheck.Tests + Grpc.HealthCheck.Tests + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Google.ProtocolBuffers.2.4.1.555\lib\net40\Google.ProtocolBuffers.dll + + + ..\packages\Google.ProtocolBuffers.2.4.1.555\lib\net40\Google.ProtocolBuffers.Serialization.dll + + + ..\packages\NUnit.2.6.4\lib\nunit.framework.dll + + + + + + + + + + + + Version.cs + + + + + + + + {ccc4440e-49f7-4790-b0af-feabb0837ae7} + Grpc.Core + + + {aa5e328a-8835-49d7-98ed-c29f2b3049f0} + Grpc.HealthCheck + + + + + + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs new file mode 100644 index 00000000000..73ff0e74b57 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs @@ -0,0 +1,97 @@ +#region Copyright notice and license +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Grpc.Core; +using Grpc.Health.V1Alpha; +using NUnit.Framework; + +namespace Grpc.HealthCheck.Tests +{ + /// + /// Health client talks to health server. + /// + public class HealthClientServerTest + { + const string Host = "localhost"; + Server server; + Channel channel; + Grpc.Health.V1Alpha.Health.IHealthClient client; + Grpc.HealthCheck.HealthServiceImpl serviceImpl; + + [TestFixtureSetUp] + public void Init() + { + serviceImpl = new HealthServiceImpl(); + + server = new Server(); + server.AddServiceDefinition(Grpc.Health.V1Alpha.Health.BindService(serviceImpl)); + int port = server.AddListeningPort(Host, Server.PickUnusedPort); + server.Start(); + channel = new Channel(Host, port); + + client = Grpc.Health.V1Alpha.Health.NewClient(channel); + } + + [TestFixtureTearDown] + public void Cleanup() + { + channel.Dispose(); + + server.ShutdownAsync().Wait(); + GrpcEnvironment.Shutdown(); + } + + [Test] + public void ServiceIsRunning() + { + serviceImpl.SetStatus("", "", HealthCheckResponse.Types.ServingStatus.SERVING); + + var response = client.Check(HealthCheckRequest.CreateBuilder().SetHost("").SetService("").Build()); + Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.SERVING, response.Status); + } + + [Test] + public void ServiceDoesntExist() + { + // TODO(jtattermusch): currently, this returns wrong status code, because we don't enable sending arbitrary status code from + // server handlers yet. + Assert.Throws(typeof(RpcException), () => client.Check(HealthCheckRequest.CreateBuilder().SetHost("").SetService("nonexistent.service").Build())); + } + + // TODO(jtattermusch): add test with timeout once timeouts are supported + } +} \ No newline at end of file diff --git a/src/csharp/Grpc.HealthCheck.Tests/HealthServiceImplTest.cs b/src/csharp/Grpc.HealthCheck.Tests/HealthServiceImplTest.cs new file mode 100644 index 00000000000..9b7c4f21406 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck.Tests/HealthServiceImplTest.cs @@ -0,0 +1,107 @@ +#region Copyright notice and license +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Grpc.Core; +using Grpc.Health.V1Alpha; +using NUnit.Framework; + +namespace Grpc.HealthCheck.Tests +{ + /// + /// Tests for HealthCheckServiceImpl + /// + public class HealthServiceImplTest + { + [Test] + public void SetStatus() + { + var impl = new HealthServiceImpl(); + impl.SetStatus("", "", HealthCheckResponse.Types.ServingStatus.SERVING); + Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.SERVING, GetStatusHelper(impl, "", "")); + + impl.SetStatus("", "", HealthCheckResponse.Types.ServingStatus.NOT_SERVING); + Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.NOT_SERVING, GetStatusHelper(impl, "", "")); + + impl.SetStatus("virtual-host", "", HealthCheckResponse.Types.ServingStatus.UNKNOWN); + Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.UNKNOWN, GetStatusHelper(impl, "virtual-host", "")); + + impl.SetStatus("virtual-host", "grpc.test.TestService", HealthCheckResponse.Types.ServingStatus.SERVING); + Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.SERVING, GetStatusHelper(impl, "virtual-host", "grpc.test.TestService")); + } + + [Test] + public void ClearStatus() + { + var impl = new HealthServiceImpl(); + impl.SetStatus("", "", HealthCheckResponse.Types.ServingStatus.SERVING); + impl.SetStatus("virtual-host", "", HealthCheckResponse.Types.ServingStatus.UNKNOWN); + + impl.ClearStatus("", ""); + + Assert.Throws(Is.TypeOf(typeof(RpcException)).And.Property("Status").Property("StatusCode").EqualTo(StatusCode.NotFound), () => GetStatusHelper(impl, "", "")); + Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.UNKNOWN, GetStatusHelper(impl, "virtual-host", "")); + } + + [Test] + public void ClearAll() + { + var impl = new HealthServiceImpl(); + impl.SetStatus("", "", HealthCheckResponse.Types.ServingStatus.SERVING); + impl.SetStatus("virtual-host", "", HealthCheckResponse.Types.ServingStatus.UNKNOWN); + + impl.ClearAll(); + Assert.Throws(typeof(RpcException), () => GetStatusHelper(impl, "", "")); + Assert.Throws(typeof(RpcException), () => GetStatusHelper(impl, "virtual-host", "")); + } + + [Test] + public void NullsRejected() + { + var impl = new HealthServiceImpl(); + Assert.Throws(typeof(NullReferenceException), () => impl.SetStatus(null, "", HealthCheckResponse.Types.ServingStatus.SERVING)); + Assert.Throws(typeof(NullReferenceException), () => impl.SetStatus("", null, HealthCheckResponse.Types.ServingStatus.SERVING)); + + Assert.Throws(typeof(NullReferenceException), () => impl.ClearStatus(null, "")); + Assert.Throws(typeof(NullReferenceException), () => impl.ClearStatus("", null)); + } + + private static HealthCheckResponse.Types.ServingStatus GetStatusHelper(HealthServiceImpl impl, string host, string service) + { + return impl.Check(null, HealthCheckRequest.CreateBuilder().SetHost(host).SetService(service).Build()).Result.Status; + } + } +} diff --git a/src/csharp/Grpc.HealthCheck.Tests/Properties/AssemblyInfo.cs b/src/csharp/Grpc.HealthCheck.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..d5660305bef --- /dev/null +++ b/src/csharp/Grpc.HealthCheck.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Grpc.HealthCheck.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] diff --git a/src/csharp/Grpc.HealthCheck.Tests/packages.config b/src/csharp/Grpc.HealthCheck.Tests/packages.config new file mode 100644 index 00000000000..050c4eaed67 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck.Tests/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.HealthCheck/.gitignore b/src/csharp/Grpc.HealthCheck/.gitignore new file mode 100644 index 00000000000..1746e3269ed --- /dev/null +++ b/src/csharp/Grpc.HealthCheck/.gitignore @@ -0,0 +1,2 @@ +bin +obj diff --git a/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj new file mode 100644 index 00000000000..4ebb6446dd2 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj @@ -0,0 +1,79 @@ + + + + + Debug + AnyCPU + {AA5E328A-8835-49D7-98ED-C29F2B3049F0} + Library + Properties + Grpc.HealthCheck + Grpc.HealthCheck + v4.5 + 512 + bin\$(Configuration)\Grpc.HealthCheck.Xml + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Google.ProtocolBuffers.2.4.1.555\lib\net40\Google.ProtocolBuffers.dll + + + ..\packages\Google.ProtocolBuffers.2.4.1.555\lib\net40\Google.ProtocolBuffers.Serialization.dll + + + + + ..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll + + + + + + + + + + Version.cs + + + + + + + + + + + + + + {ccc4440e-49f7-4790-b0af-feabb0837ae7} + Grpc.Core + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec new file mode 100644 index 00000000000..ca35b368053 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec @@ -0,0 +1,28 @@ + + + + Grpc.HealthCheck + gRPC C# Healthchecking + Implementation of gRPC health service + Example implementation of grpc.health.v1alpha service that can be used for health-checking. + $version$ + Google Inc. + grpc-packages + https://github.com/grpc/grpc/blob/master/LICENSE + https://github.com/grpc/grpc + false + Copyright 2015, Google Inc. + gRPC health check + + + + + + + + + + + + + diff --git a/src/csharp/Grpc.HealthCheck/Health.cs b/src/csharp/Grpc.HealthCheck/Health.cs new file mode 100644 index 00000000000..361382d4bd6 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck/Health.cs @@ -0,0 +1,687 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: health.proto +#pragma warning disable 1591, 0612, 3021 +#region Designer generated code + +using pb = global::Google.ProtocolBuffers; +using pbc = global::Google.ProtocolBuffers.Collections; +using pbd = global::Google.ProtocolBuffers.Descriptors; +using scg = global::System.Collections.Generic; +namespace Grpc.Health.V1Alpha { + + namespace Proto { + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public static partial class Health { + + #region Extension registration + public static void RegisterAllExtensions(pb::ExtensionRegistry registry) { + } + #endregion + #region Static variables + internal static pbd::MessageDescriptor internal__static_grpc_health_v1alpha_HealthCheckRequest__Descriptor; + internal static pb::FieldAccess.FieldAccessorTable internal__static_grpc_health_v1alpha_HealthCheckRequest__FieldAccessorTable; + internal static pbd::MessageDescriptor internal__static_grpc_health_v1alpha_HealthCheckResponse__Descriptor; + internal static pb::FieldAccess.FieldAccessorTable internal__static_grpc_health_v1alpha_HealthCheckResponse__FieldAccessorTable; + #endregion + #region Descriptor + public static pbd::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbd::FileDescriptor descriptor; + + static Health() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "CgxoZWFsdGgucHJvdG8SE2dycGMuaGVhbHRoLnYxYWxwaGEiMwoSSGVhbHRo", + "Q2hlY2tSZXF1ZXN0EgwKBGhvc3QYASABKAkSDwoHc2VydmljZRgCIAEoCSKZ", + "AQoTSGVhbHRoQ2hlY2tSZXNwb25zZRJGCgZzdGF0dXMYASABKA4yNi5ncnBj", + "LmhlYWx0aC52MWFscGhhLkhlYWx0aENoZWNrUmVzcG9uc2UuU2VydmluZ1N0", + "YXR1cyI6Cg1TZXJ2aW5nU3RhdHVzEgsKB1VOS05PV04QABILCgdTRVJWSU5H", + "EAESDwoLTk9UX1NFUlZJTkcQAjJkCgZIZWFsdGgSWgoFQ2hlY2sSJy5ncnBj", + "LmhlYWx0aC52MWFscGhhLkhlYWx0aENoZWNrUmVxdWVzdBooLmdycGMuaGVh", + "bHRoLnYxYWxwaGEuSGVhbHRoQ2hlY2tSZXNwb25zZUIWqgITR3JwYy5IZWFs", + "dGguVjFBbHBoYQ==")); + pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) { + descriptor = root; + internal__static_grpc_health_v1alpha_HealthCheckRequest__Descriptor = Descriptor.MessageTypes[0]; + internal__static_grpc_health_v1alpha_HealthCheckRequest__FieldAccessorTable = + new pb::FieldAccess.FieldAccessorTable(internal__static_grpc_health_v1alpha_HealthCheckRequest__Descriptor, + new string[] { "Host", "Service", }); + internal__static_grpc_health_v1alpha_HealthCheckResponse__Descriptor = Descriptor.MessageTypes[1]; + internal__static_grpc_health_v1alpha_HealthCheckResponse__FieldAccessorTable = + new pb::FieldAccess.FieldAccessorTable(internal__static_grpc_health_v1alpha_HealthCheckResponse__Descriptor, + new string[] { "Status", }); + pb::ExtensionRegistry registry = pb::ExtensionRegistry.CreateInstance(); + RegisterAllExtensions(registry); + return registry; + }; + pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData, + new pbd::FileDescriptor[] { + }, assigner); + } + #endregion + + } + } + #region Messages + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class HealthCheckRequest : pb::GeneratedMessage { + private HealthCheckRequest() { } + private static readonly HealthCheckRequest defaultInstance = new HealthCheckRequest().MakeReadOnly(); + private static readonly string[] _healthCheckRequestFieldNames = new string[] { "host", "service" }; + private static readonly uint[] _healthCheckRequestFieldTags = new uint[] { 10, 18 }; + public static HealthCheckRequest DefaultInstance { + get { return defaultInstance; } + } + + public override HealthCheckRequest DefaultInstanceForType { + get { return DefaultInstance; } + } + + protected override HealthCheckRequest ThisMessage { + get { return this; } + } + + public static pbd::MessageDescriptor Descriptor { + get { return global::Grpc.Health.V1Alpha.Proto.Health.internal__static_grpc_health_v1alpha_HealthCheckRequest__Descriptor; } + } + + protected override pb::FieldAccess.FieldAccessorTable InternalFieldAccessors { + get { return global::Grpc.Health.V1Alpha.Proto.Health.internal__static_grpc_health_v1alpha_HealthCheckRequest__FieldAccessorTable; } + } + + public const int HostFieldNumber = 1; + private bool hasHost; + private string host_ = ""; + public bool HasHost { + get { return hasHost; } + } + public string Host { + get { return host_; } + } + + public const int ServiceFieldNumber = 2; + private bool hasService; + private string service_ = ""; + public bool HasService { + get { return hasService; } + } + public string Service { + get { return service_; } + } + + public override bool IsInitialized { + get { + return true; + } + } + + public override void WriteTo(pb::ICodedOutputStream output) { + CalcSerializedSize(); + string[] field_names = _healthCheckRequestFieldNames; + if (hasHost) { + output.WriteString(1, field_names[0], Host); + } + if (hasService) { + output.WriteString(2, field_names[1], Service); + } + UnknownFields.WriteTo(output); + } + + private int memoizedSerializedSize = -1; + public override int SerializedSize { + get { + int size = memoizedSerializedSize; + if (size != -1) return size; + return CalcSerializedSize(); + } + } + + private int CalcSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasHost) { + size += pb::CodedOutputStream.ComputeStringSize(1, Host); + } + if (hasService) { + size += pb::CodedOutputStream.ComputeStringSize(2, Service); + } + size += UnknownFields.SerializedSize; + memoizedSerializedSize = size; + return size; + } + public static HealthCheckRequest ParseFrom(pb::ByteString data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static HealthCheckRequest ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static HealthCheckRequest ParseFrom(byte[] data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static HealthCheckRequest ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static HealthCheckRequest ParseFrom(global::System.IO.Stream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static HealthCheckRequest ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + public static HealthCheckRequest ParseDelimitedFrom(global::System.IO.Stream input) { + return CreateBuilder().MergeDelimitedFrom(input).BuildParsed(); + } + public static HealthCheckRequest ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed(); + } + public static HealthCheckRequest ParseFrom(pb::ICodedInputStream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static HealthCheckRequest ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + private HealthCheckRequest MakeReadOnly() { + return this; + } + + public static Builder CreateBuilder() { return new Builder(); } + public override Builder ToBuilder() { return CreateBuilder(this); } + public override Builder CreateBuilderForType() { return new Builder(); } + public static Builder CreateBuilder(HealthCheckRequest prototype) { + return new Builder(prototype); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Builder : pb::GeneratedBuilder { + protected override Builder ThisBuilder { + get { return this; } + } + public Builder() { + result = DefaultInstance; + resultIsReadOnly = true; + } + internal Builder(HealthCheckRequest cloneFrom) { + result = cloneFrom; + resultIsReadOnly = true; + } + + private bool resultIsReadOnly; + private HealthCheckRequest result; + + private HealthCheckRequest PrepareBuilder() { + if (resultIsReadOnly) { + HealthCheckRequest original = result; + result = new HealthCheckRequest(); + resultIsReadOnly = false; + MergeFrom(original); + } + return result; + } + + public override bool IsInitialized { + get { return result.IsInitialized; } + } + + protected override HealthCheckRequest MessageBeingBuilt { + get { return PrepareBuilder(); } + } + + public override Builder Clear() { + result = DefaultInstance; + resultIsReadOnly = true; + return this; + } + + public override Builder Clone() { + if (resultIsReadOnly) { + return new Builder(result); + } else { + return new Builder().MergeFrom(result); + } + } + + public override pbd::MessageDescriptor DescriptorForType { + get { return global::Grpc.Health.V1Alpha.HealthCheckRequest.Descriptor; } + } + + public override HealthCheckRequest DefaultInstanceForType { + get { return global::Grpc.Health.V1Alpha.HealthCheckRequest.DefaultInstance; } + } + + public override HealthCheckRequest BuildPartial() { + if (resultIsReadOnly) { + return result; + } + resultIsReadOnly = true; + return result.MakeReadOnly(); + } + + public override Builder MergeFrom(pb::IMessage other) { + if (other is HealthCheckRequest) { + return MergeFrom((HealthCheckRequest) other); + } else { + base.MergeFrom(other); + return this; + } + } + + public override Builder MergeFrom(HealthCheckRequest other) { + if (other == global::Grpc.Health.V1Alpha.HealthCheckRequest.DefaultInstance) return this; + PrepareBuilder(); + if (other.HasHost) { + Host = other.Host; + } + if (other.HasService) { + Service = other.Service; + } + this.MergeUnknownFields(other.UnknownFields); + return this; + } + + public override Builder MergeFrom(pb::ICodedInputStream input) { + return MergeFrom(input, pb::ExtensionRegistry.Empty); + } + + public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + PrepareBuilder(); + pb::UnknownFieldSet.Builder unknownFields = null; + uint tag; + string field_name; + while (input.ReadTag(out tag, out field_name)) { + if(tag == 0 && field_name != null) { + int field_ordinal = global::System.Array.BinarySearch(_healthCheckRequestFieldNames, field_name, global::System.StringComparer.Ordinal); + if(field_ordinal >= 0) + tag = _healthCheckRequestFieldTags[field_ordinal]; + else { + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + continue; + } + } + switch (tag) { + case 0: { + throw pb::InvalidProtocolBufferException.InvalidTag(); + } + default: { + if (pb::WireFormat.IsEndGroupTag(tag)) { + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + break; + } + case 10: { + result.hasHost = input.ReadString(ref result.host_); + break; + } + case 18: { + result.hasService = input.ReadString(ref result.service_); + break; + } + } + } + + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + + + public bool HasHost { + get { return result.hasHost; } + } + public string Host { + get { return result.Host; } + set { SetHost(value); } + } + public Builder SetHost(string value) { + pb::ThrowHelper.ThrowIfNull(value, "value"); + PrepareBuilder(); + result.hasHost = true; + result.host_ = value; + return this; + } + public Builder ClearHost() { + PrepareBuilder(); + result.hasHost = false; + result.host_ = ""; + return this; + } + + public bool HasService { + get { return result.hasService; } + } + public string Service { + get { return result.Service; } + set { SetService(value); } + } + public Builder SetService(string value) { + pb::ThrowHelper.ThrowIfNull(value, "value"); + PrepareBuilder(); + result.hasService = true; + result.service_ = value; + return this; + } + public Builder ClearService() { + PrepareBuilder(); + result.hasService = false; + result.service_ = ""; + return this; + } + } + static HealthCheckRequest() { + object.ReferenceEquals(global::Grpc.Health.V1Alpha.Proto.Health.Descriptor, null); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class HealthCheckResponse : pb::GeneratedMessage { + private HealthCheckResponse() { } + private static readonly HealthCheckResponse defaultInstance = new HealthCheckResponse().MakeReadOnly(); + private static readonly string[] _healthCheckResponseFieldNames = new string[] { "status" }; + private static readonly uint[] _healthCheckResponseFieldTags = new uint[] { 8 }; + public static HealthCheckResponse DefaultInstance { + get { return defaultInstance; } + } + + public override HealthCheckResponse DefaultInstanceForType { + get { return DefaultInstance; } + } + + protected override HealthCheckResponse ThisMessage { + get { return this; } + } + + public static pbd::MessageDescriptor Descriptor { + get { return global::Grpc.Health.V1Alpha.Proto.Health.internal__static_grpc_health_v1alpha_HealthCheckResponse__Descriptor; } + } + + protected override pb::FieldAccess.FieldAccessorTable InternalFieldAccessors { + get { return global::Grpc.Health.V1Alpha.Proto.Health.internal__static_grpc_health_v1alpha_HealthCheckResponse__FieldAccessorTable; } + } + + #region Nested types + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public static partial class Types { + public enum ServingStatus { + UNKNOWN = 0, + SERVING = 1, + NOT_SERVING = 2, + } + + } + #endregion + + public const int StatusFieldNumber = 1; + private bool hasStatus; + private global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus status_ = global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus.UNKNOWN; + public bool HasStatus { + get { return hasStatus; } + } + public global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus Status { + get { return status_; } + } + + public override bool IsInitialized { + get { + return true; + } + } + + public override void WriteTo(pb::ICodedOutputStream output) { + CalcSerializedSize(); + string[] field_names = _healthCheckResponseFieldNames; + if (hasStatus) { + output.WriteEnum(1, field_names[0], (int) Status, Status); + } + UnknownFields.WriteTo(output); + } + + private int memoizedSerializedSize = -1; + public override int SerializedSize { + get { + int size = memoizedSerializedSize; + if (size != -1) return size; + return CalcSerializedSize(); + } + } + + private int CalcSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasStatus) { + size += pb::CodedOutputStream.ComputeEnumSize(1, (int) Status); + } + size += UnknownFields.SerializedSize; + memoizedSerializedSize = size; + return size; + } + public static HealthCheckResponse ParseFrom(pb::ByteString data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static HealthCheckResponse ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static HealthCheckResponse ParseFrom(byte[] data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static HealthCheckResponse ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static HealthCheckResponse ParseFrom(global::System.IO.Stream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static HealthCheckResponse ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + public static HealthCheckResponse ParseDelimitedFrom(global::System.IO.Stream input) { + return CreateBuilder().MergeDelimitedFrom(input).BuildParsed(); + } + public static HealthCheckResponse ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed(); + } + public static HealthCheckResponse ParseFrom(pb::ICodedInputStream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static HealthCheckResponse ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + private HealthCheckResponse MakeReadOnly() { + return this; + } + + public static Builder CreateBuilder() { return new Builder(); } + public override Builder ToBuilder() { return CreateBuilder(this); } + public override Builder CreateBuilderForType() { return new Builder(); } + public static Builder CreateBuilder(HealthCheckResponse prototype) { + return new Builder(prototype); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Builder : pb::GeneratedBuilder { + protected override Builder ThisBuilder { + get { return this; } + } + public Builder() { + result = DefaultInstance; + resultIsReadOnly = true; + } + internal Builder(HealthCheckResponse cloneFrom) { + result = cloneFrom; + resultIsReadOnly = true; + } + + private bool resultIsReadOnly; + private HealthCheckResponse result; + + private HealthCheckResponse PrepareBuilder() { + if (resultIsReadOnly) { + HealthCheckResponse original = result; + result = new HealthCheckResponse(); + resultIsReadOnly = false; + MergeFrom(original); + } + return result; + } + + public override bool IsInitialized { + get { return result.IsInitialized; } + } + + protected override HealthCheckResponse MessageBeingBuilt { + get { return PrepareBuilder(); } + } + + public override Builder Clear() { + result = DefaultInstance; + resultIsReadOnly = true; + return this; + } + + public override Builder Clone() { + if (resultIsReadOnly) { + return new Builder(result); + } else { + return new Builder().MergeFrom(result); + } + } + + public override pbd::MessageDescriptor DescriptorForType { + get { return global::Grpc.Health.V1Alpha.HealthCheckResponse.Descriptor; } + } + + public override HealthCheckResponse DefaultInstanceForType { + get { return global::Grpc.Health.V1Alpha.HealthCheckResponse.DefaultInstance; } + } + + public override HealthCheckResponse BuildPartial() { + if (resultIsReadOnly) { + return result; + } + resultIsReadOnly = true; + return result.MakeReadOnly(); + } + + public override Builder MergeFrom(pb::IMessage other) { + if (other is HealthCheckResponse) { + return MergeFrom((HealthCheckResponse) other); + } else { + base.MergeFrom(other); + return this; + } + } + + public override Builder MergeFrom(HealthCheckResponse other) { + if (other == global::Grpc.Health.V1Alpha.HealthCheckResponse.DefaultInstance) return this; + PrepareBuilder(); + if (other.HasStatus) { + Status = other.Status; + } + this.MergeUnknownFields(other.UnknownFields); + return this; + } + + public override Builder MergeFrom(pb::ICodedInputStream input) { + return MergeFrom(input, pb::ExtensionRegistry.Empty); + } + + public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + PrepareBuilder(); + pb::UnknownFieldSet.Builder unknownFields = null; + uint tag; + string field_name; + while (input.ReadTag(out tag, out field_name)) { + if(tag == 0 && field_name != null) { + int field_ordinal = global::System.Array.BinarySearch(_healthCheckResponseFieldNames, field_name, global::System.StringComparer.Ordinal); + if(field_ordinal >= 0) + tag = _healthCheckResponseFieldTags[field_ordinal]; + else { + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + continue; + } + } + switch (tag) { + case 0: { + throw pb::InvalidProtocolBufferException.InvalidTag(); + } + default: { + if (pb::WireFormat.IsEndGroupTag(tag)) { + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + break; + } + case 8: { + object unknown; + if(input.ReadEnum(ref result.status_, out unknown)) { + result.hasStatus = true; + } else if(unknown is int) { + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + unknownFields.MergeVarintField(1, (ulong)(int)unknown); + } + break; + } + } + } + + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + + + public bool HasStatus { + get { return result.hasStatus; } + } + public global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus Status { + get { return result.Status; } + set { SetStatus(value); } + } + public Builder SetStatus(global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus value) { + PrepareBuilder(); + result.hasStatus = true; + result.status_ = value; + return this; + } + public Builder ClearStatus() { + PrepareBuilder(); + result.hasStatus = false; + result.status_ = global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus.UNKNOWN; + return this; + } + } + static HealthCheckResponse() { + object.ReferenceEquals(global::Grpc.Health.V1Alpha.Proto.Health.Descriptor, null); + } + } + + #endregion + +} + +#endregion Designer generated code diff --git a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs new file mode 100644 index 00000000000..3aebdcb5573 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs @@ -0,0 +1,70 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: health.proto +#region Designer generated code + +using System; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; + +namespace Grpc.Health.V1Alpha { + public static class Health + { + static readonly string __ServiceName = "grpc.health.v1alpha.Health"; + + static readonly Marshaller __Marshaller_HealthCheckRequest = Marshallers.Create((arg) => arg.ToByteArray(), global::Grpc.Health.V1Alpha.HealthCheckRequest.ParseFrom); + static readonly Marshaller __Marshaller_HealthCheckResponse = Marshallers.Create((arg) => arg.ToByteArray(), global::Grpc.Health.V1Alpha.HealthCheckResponse.ParseFrom); + + static readonly Method __Method_Check = new Method( + MethodType.Unary, + "Check", + __Marshaller_HealthCheckRequest, + __Marshaller_HealthCheckResponse); + + // client interface + public interface IHealthClient + { + global::Grpc.Health.V1Alpha.HealthCheckResponse Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); + Task CheckAsync(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); + } + + // server-side interface + public interface IHealth + { + Task Check(ServerCallContext context, global::Grpc.Health.V1Alpha.HealthCheckRequest request); + } + + // client stub + public class HealthClient : ClientBase, IHealthClient + { + public HealthClient(Channel channel) : base(channel) + { + } + public global::Grpc.Health.V1Alpha.HealthCheckResponse Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__ServiceName, __Method_Check, headers); + return Calls.BlockingUnaryCall(call, request, cancellationToken); + } + public Task CheckAsync(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__ServiceName, __Method_Check, headers); + return Calls.AsyncUnaryCall(call, request, cancellationToken); + } + } + + // creates service definition that can be registered with a server + public static ServerServiceDefinition BindService(IHealth serviceImpl) + { + return ServerServiceDefinition.CreateBuilder(__ServiceName) + .AddMethod(__Method_Check, serviceImpl.Check).Build(); + } + + // creates a new client + public static HealthClient NewClient(Channel channel) + { + return new HealthClient(channel); + } + + } +} +#endregion diff --git a/src/csharp/Grpc.HealthCheck/HealthServiceImpl.cs b/src/csharp/Grpc.HealthCheck/HealthServiceImpl.cs new file mode 100644 index 00000000000..db3a2a09420 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck/HealthServiceImpl.cs @@ -0,0 +1,132 @@ +#region Copyright notice and license +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Grpc.Core; +using Grpc.Core.Utils; +using Grpc.Health.V1Alpha; + +namespace Grpc.HealthCheck +{ + /// + /// Implementation of a simple Health service. Useful for health checking. + /// + /// Registering service with a server: + /// + /// var serviceImpl = new HealthServiceImpl(); + /// server = new Server(); + /// server.AddServiceDefinition(Grpc.Health.V1Alpha.Health.BindService(serviceImpl)); + /// + /// + public class HealthServiceImpl : Grpc.Health.V1Alpha.Health.IHealth + { + private readonly object myLock = new object(); + private readonly Dictionary statusMap = + new Dictionary(); + + /// + /// Sets the health status for given host and service. + /// + /// The host. Cannot be null. + /// The service. Cannot be null. + /// the health status + public void SetStatus(string host, string service, HealthCheckResponse.Types.ServingStatus status) + { + lock (myLock) + { + statusMap[CreateKey(host, service)] = status; + } + } + + /// + /// Clears health status for given host and service. + /// + /// The host. Cannot be null. + /// The service. Cannot be null. + public void ClearStatus(string host, string service) + { + lock (myLock) + { + statusMap.Remove(CreateKey(host, service)); + } + } + + /// + /// Clears statuses for all hosts and services. + /// + public void ClearAll() + { + lock (myLock) + { + statusMap.Clear(); + } + } + + public Task Check(ServerCallContext context, HealthCheckRequest request) + { + lock (myLock) + { + var host = request.HasHost ? request.Host : ""; + var service = request.HasService ? request.Service : ""; + + HealthCheckResponse.Types.ServingStatus status; + if (!statusMap.TryGetValue(CreateKey(host, service), out status)) + { + // TODO(jtattermusch): returning specific status from server handler is not supported yet. + throw new RpcException(new Status(StatusCode.NotFound, "")); + } + return Task.FromResult(HealthCheckResponse.CreateBuilder().SetStatus(status).Build()); + } + } + + private static Key CreateKey(string host, string service) + { + return new Key(host, service); + } + + private struct Key + { + public Key(string host, string service) + { + this.Host = Preconditions.CheckNotNull(host); + this.Service = Preconditions.CheckNotNull(service); + } + + readonly string Host; + readonly string Service; + } + } +} diff --git a/src/csharp/Grpc.HealthCheck/Properties/AssemblyInfo.cs b/src/csharp/Grpc.HealthCheck/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..41a54a98bc8 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Grpc.HealthCheck")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] \ No newline at end of file diff --git a/src/csharp/Grpc.HealthCheck/packages.config b/src/csharp/Grpc.HealthCheck/packages.config new file mode 100644 index 00000000000..094a30981e4 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.Core/Stub/StubConfiguration.cs b/src/csharp/Grpc.HealthCheck/proto/health.proto similarity index 64% rename from src/csharp/Grpc.Core/Stub/StubConfiguration.cs rename to src/csharp/Grpc.HealthCheck/proto/health.proto index 5bcb5b40d2d..08df7e104e1 100644 --- a/src/csharp/Grpc.Core/Stub/StubConfiguration.cs +++ b/src/csharp/Grpc.HealthCheck/proto/health.proto @@ -1,5 +1,3 @@ -#region Copyright notice and license - // Copyright 2015, Google Inc. // All rights reserved. // @@ -29,36 +27,26 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#endregion - -using System; -using Grpc.Core.Internal; -using Grpc.Core.Utils; - -namespace Grpc.Core -{ - public delegate void HeaderInterceptorDelegate(Metadata.Builder headerBuilder); +// TODO(jtattermusch): switch to proto3 once C# supports that. +syntax = "proto2"; - public class StubConfiguration - { - /// - /// The default stub configuration. - /// - public static readonly StubConfiguration Default = new StubConfiguration((headerBuilder) => { }); +package grpc.health.v1alpha; +option csharp_namespace = "Grpc.Health.V1Alpha"; - readonly HeaderInterceptorDelegate headerInterceptor; - - public StubConfiguration(HeaderInterceptorDelegate headerInterceptor) - { - this.headerInterceptor = Preconditions.CheckNotNull(headerInterceptor); - } +message HealthCheckRequest { + optional string host = 1; + optional string service = 2; +} - public HeaderInterceptorDelegate HeaderInterceptor - { - get - { - return headerInterceptor; - } - } - } +message HealthCheckResponse { + enum ServingStatus { + UNKNOWN = 0; + SERVING = 1; + NOT_SERVING = 2; + } + optional ServingStatus status = 1; } + +service Health { + rpc Check(HealthCheckRequest) returns (HealthCheckResponse); +} \ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj b/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj index df05c535e24..328acb5b476 100644 --- a/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj +++ b/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj @@ -36,6 +36,9 @@ + + Version.cs + diff --git a/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs index 9a389a1e308..f51f2796c43 100644 --- a/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs @@ -9,4 +9,3 @@ using System.Runtime.CompilerServices; [assembly: AssemblyCopyright("Google Inc. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("0.6.*")] diff --git a/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj b/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj index 235897c888e..ae184c1dc7e 100644 --- a/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj +++ b/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj @@ -36,6 +36,9 @@ + + Version.cs + diff --git a/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs index ff31035d534..f68d9a3ddcc 100644 --- a/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs @@ -9,4 +9,3 @@ using System.Runtime.CompilerServices; [assembly: AssemblyCopyright("Google Inc. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("0.6.*")] diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj index a6d847ca653..af4a75a034b 100644 --- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj +++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj @@ -75,6 +75,9 @@ + + Version.cs + diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs index f0be522bc6e..05e732dbd46 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs @@ -102,8 +102,6 @@ namespace Grpc.IntegrationTesting private void Run() { - GrpcEnvironment.Initialize(); - Credentials credentials = null; if (options.useTls) { @@ -121,7 +119,7 @@ namespace Grpc.IntegrationTesting using (Channel channel = new Channel(options.serverHost, options.serverPort.Value, credentials, channelOptions)) { - var stubConfig = StubConfiguration.Default; + TestService.TestServiceClient client = new TestService.TestServiceClient(channel); if (options.testCase == "service_account_creds" || options.testCase == "compute_engine_creds") { var credential = GoogleCredential.GetApplicationDefault(); @@ -129,13 +127,11 @@ namespace Grpc.IntegrationTesting { credential = credential.CreateScoped(new[] { AuthScope }); } - stubConfig = new StubConfiguration(OAuth2InterceptorFactory.Create(credential)); + client.HeaderInterceptor = OAuth2InterceptorFactory.Create(credential); } - TestService.ITestServiceClient client = new TestService.TestServiceClient(channel, stubConfig); RunTestCase(options.testCase, client); } - GrpcEnvironment.Shutdown(); } @@ -366,7 +362,7 @@ namespace Grpc.IntegrationTesting Console.WriteLine("running cancel_after_begin"); var cts = new CancellationTokenSource(); - using (var call = client.StreamingInputCall(cts.Token)) + using (var call = client.StreamingInputCall(cancellationToken: cts.Token)) { // TODO(jtattermusch): we need this to ensure call has been initiated once we cancel it. await Task.Delay(1000); @@ -393,7 +389,7 @@ namespace Grpc.IntegrationTesting Console.WriteLine("running cancel_after_first_response"); var cts = new CancellationTokenSource(); - using (var call = client.FullDuplexCall(cts.Token)) + using (var call = client.FullDuplexCall(cancellationToken: cts.Token)) { await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder() .SetResponseType(PayloadType.COMPRESSABLE) diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs index 1a733450c1a..f306289cfb5 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs @@ -55,8 +55,6 @@ namespace Grpc.IntegrationTesting [TestFixtureSetUp] public void Init() { - GrpcEnvironment.Initialize(); - server = new Server(); server.AddServiceDefinition(TestService.BindService(new TestServiceImpl())); int port = server.AddListeningPort(host, Server.PickUnusedPort, TestCredentials.CreateTestServerCredentials()); @@ -67,14 +65,13 @@ namespace Grpc.IntegrationTesting new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride) }; channel = new Channel(host, port, TestCredentials.CreateTestClientCredentials(true), options); - client = TestService.NewStub(channel); + client = TestService.NewClient(channel); } [TestFixtureTearDown] public void Cleanup() { channel.Dispose(); - server.ShutdownAsync().Wait(); GrpcEnvironment.Shutdown(); } diff --git a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs index 87c3cbe1d4c..9475e66c408 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs @@ -88,8 +88,6 @@ namespace Grpc.IntegrationTesting private void Run() { - GrpcEnvironment.Initialize(); - var server = new Server(); server.AddServiceDefinition(TestService.BindService(new TestServiceImpl())); diff --git a/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs index 7134b04892d..1beb0bbb413 100644 --- a/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs @@ -9,4 +9,3 @@ using System.Runtime.CompilerServices; [assembly: AssemblyCopyright("Google Inc. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("0.6.*")] diff --git a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs index ee077f9f56f..96d9b237177 100644 --- a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs +++ b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs @@ -56,17 +56,17 @@ namespace grpc.testing { __Marshaller_StreamingOutputCallRequest, __Marshaller_StreamingOutputCallResponse); - // client-side stub interface + // client interface public interface ITestServiceClient { - global::grpc.testing.Empty EmptyCall(global::grpc.testing.Empty request, CancellationToken token = default(CancellationToken)); - Task EmptyCallAsync(global::grpc.testing.Empty request, CancellationToken token = default(CancellationToken)); - global::grpc.testing.SimpleResponse UnaryCall(global::grpc.testing.SimpleRequest request, CancellationToken token = default(CancellationToken)); - Task UnaryCallAsync(global::grpc.testing.SimpleRequest request, CancellationToken token = default(CancellationToken)); - AsyncServerStreamingCall StreamingOutputCall(global::grpc.testing.StreamingOutputCallRequest request, CancellationToken token = default(CancellationToken)); - AsyncClientStreamingCall StreamingInputCall(CancellationToken token = default(CancellationToken)); - AsyncDuplexStreamingCall FullDuplexCall(CancellationToken token = default(CancellationToken)); - AsyncDuplexStreamingCall HalfDuplexCall(CancellationToken token = default(CancellationToken)); + global::grpc.testing.Empty EmptyCall(global::grpc.testing.Empty request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); + Task EmptyCallAsync(global::grpc.testing.Empty request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); + global::grpc.testing.SimpleResponse UnaryCall(global::grpc.testing.SimpleRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); + Task UnaryCallAsync(global::grpc.testing.SimpleRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncServerStreamingCall StreamingOutputCall(global::grpc.testing.StreamingOutputCallRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncClientStreamingCall StreamingInputCall(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncDuplexStreamingCall FullDuplexCall(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncDuplexStreamingCall HalfDuplexCall(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); } // server-side interface @@ -81,53 +81,50 @@ namespace grpc.testing { } // client stub - public class TestServiceClient : AbstractStub, ITestServiceClient + public class TestServiceClient : ClientBase, ITestServiceClient { - public TestServiceClient(Channel channel) : this(channel, StubConfiguration.Default) + public TestServiceClient(Channel channel) : base(channel) { } - public TestServiceClient(Channel channel, StubConfiguration config) : base(channel, config) + public global::grpc.testing.Empty EmptyCall(global::grpc.testing.Empty request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) { + var call = CreateCall(__ServiceName, __Method_EmptyCall, headers); + return Calls.BlockingUnaryCall(call, request, cancellationToken); } - public global::grpc.testing.Empty EmptyCall(global::grpc.testing.Empty request, CancellationToken token = default(CancellationToken)) + public Task EmptyCallAsync(global::grpc.testing.Empty request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_EmptyCall); - return Calls.BlockingUnaryCall(call, request, token); + var call = CreateCall(__ServiceName, __Method_EmptyCall, headers); + return Calls.AsyncUnaryCall(call, request, cancellationToken); } - public Task EmptyCallAsync(global::grpc.testing.Empty request, CancellationToken token = default(CancellationToken)) + public global::grpc.testing.SimpleResponse UnaryCall(global::grpc.testing.SimpleRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_EmptyCall); - return Calls.AsyncUnaryCall(call, request, token); + var call = CreateCall(__ServiceName, __Method_UnaryCall, headers); + return Calls.BlockingUnaryCall(call, request, cancellationToken); } - public global::grpc.testing.SimpleResponse UnaryCall(global::grpc.testing.SimpleRequest request, CancellationToken token = default(CancellationToken)) + public Task UnaryCallAsync(global::grpc.testing.SimpleRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_UnaryCall); - return Calls.BlockingUnaryCall(call, request, token); + var call = CreateCall(__ServiceName, __Method_UnaryCall, headers); + return Calls.AsyncUnaryCall(call, request, cancellationToken); } - public Task UnaryCallAsync(global::grpc.testing.SimpleRequest request, CancellationToken token = default(CancellationToken)) + public AsyncServerStreamingCall StreamingOutputCall(global::grpc.testing.StreamingOutputCallRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_UnaryCall); - return Calls.AsyncUnaryCall(call, request, token); + var call = CreateCall(__ServiceName, __Method_StreamingOutputCall, headers); + return Calls.AsyncServerStreamingCall(call, request, cancellationToken); } - public AsyncServerStreamingCall StreamingOutputCall(global::grpc.testing.StreamingOutputCallRequest request, CancellationToken token = default(CancellationToken)) + public AsyncClientStreamingCall StreamingInputCall(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_StreamingOutputCall); - return Calls.AsyncServerStreamingCall(call, request, token); + var call = CreateCall(__ServiceName, __Method_StreamingInputCall, headers); + return Calls.AsyncClientStreamingCall(call, cancellationToken); } - public AsyncClientStreamingCall StreamingInputCall(CancellationToken token = default(CancellationToken)) + public AsyncDuplexStreamingCall FullDuplexCall(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_StreamingInputCall); - return Calls.AsyncClientStreamingCall(call, token); + var call = CreateCall(__ServiceName, __Method_FullDuplexCall, headers); + return Calls.AsyncDuplexStreamingCall(call, cancellationToken); } - public AsyncDuplexStreamingCall FullDuplexCall(CancellationToken token = default(CancellationToken)) + public AsyncDuplexStreamingCall HalfDuplexCall(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_FullDuplexCall); - return Calls.AsyncDuplexStreamingCall(call, token); - } - public AsyncDuplexStreamingCall HalfDuplexCall(CancellationToken token = default(CancellationToken)) - { - var call = CreateCall(__ServiceName, __Method_HalfDuplexCall); - return Calls.AsyncDuplexStreamingCall(call, token); + var call = CreateCall(__ServiceName, __Method_HalfDuplexCall, headers); + return Calls.AsyncDuplexStreamingCall(call, cancellationToken); } } @@ -143,17 +140,12 @@ namespace grpc.testing { .AddMethod(__Method_HalfDuplexCall, serviceImpl.HalfDuplexCall).Build(); } - // creates a new client stub - public static ITestServiceClient NewStub(Channel channel) + // creates a new client + public static TestServiceClient NewClient(Channel channel) { return new TestServiceClient(channel); } - // creates a new client stub - public static ITestServiceClient NewStub(Channel channel, StubConfiguration config) - { - return new TestServiceClient(channel, config); - } } } #endregion diff --git a/src/csharp/Grpc.Tools.nuspec b/src/csharp/Grpc.Tools.nuspec index 0f4fa79277e..eabf5dc7dbc 100644 --- a/src/csharp/Grpc.Tools.nuspec +++ b/src/csharp/Grpc.Tools.nuspec @@ -5,13 +5,13 @@ gRPC C# Tools Tools for C# implementation of gRPC - an RPC library and framework Precompiled Windows binaries for generating protocol buffer messages and gRPC client/server code - 0.6.0 + $version$ Google Inc. grpc-packages https://github.com/grpc/grpc/blob/master/LICENSE https://github.com/grpc/grpc false - protoc.exe - protocol buffer compiler v3.0.0-alpha-3; grpc_csharp_plugin.exe - gRPC C# protoc plugin version 0.6.0 + protoc.exe - protocol buffer compiler v3.0.0-alpha-3; grpc_csharp_plugin.exe - gRPC C# protoc plugin version $version$ Copyright 2015, Google Inc. gRPC RPC Protocol HTTP/2 diff --git a/src/csharp/Grpc.nuspec b/src/csharp/Grpc.nuspec index 70203a6203f..7fbd8619230 100644 --- a/src/csharp/Grpc.nuspec +++ b/src/csharp/Grpc.nuspec @@ -5,17 +5,17 @@ gRPC C# C# implementation of gRPC - an RPC library and framework C# implementation of gRPC - an RPC library and framework. See project site for more info. - 0.6.0 + $version$ Google Inc. grpc-packages https://github.com/grpc/grpc/blob/master/LICENSE https://github.com/grpc/grpc false - Release 0.6.0 of gRPC C# + Release $version$ of gRPC C# Copyright 2015, Google Inc. gRPC RPC Protocol HTTP/2 - + diff --git a/src/csharp/Grpc.sln b/src/csharp/Grpc.sln index 978739f23aa..705e4fb1c21 100644 --- a/src/csharp/Grpc.sln +++ b/src/csharp/Grpc.sln @@ -28,57 +28,68 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{B5B871 .nuget\packages.config = .nuget\packages.config EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.HealthCheck", "Grpc.HealthCheck\Grpc.HealthCheck.csproj", "{AA5E328A-8835-49D7-98ED-C29F2B3049F0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.HealthCheck.Tests", "Grpc.HealthCheck.Tests\Grpc.HealthCheck.Tests.csproj", "{F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|x86.ActiveCfg = Debug|Any CPU - {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|x86.Build.0 = Debug|Any CPU - {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|x86.ActiveCfg = Release|Any CPU - {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|x86.Build.0 = Release|Any CPU - {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|x86.ActiveCfg = Debug|Any CPU - {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|x86.Build.0 = Debug|Any CPU - {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|x86.ActiveCfg = Release|Any CPU - {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|x86.Build.0 = Release|Any CPU - {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.ActiveCfg = Debug|Any CPU - {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.Build.0 = Debug|Any CPU - {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.ActiveCfg = Release|Any CPU - {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.Build.0 = Release|Any CPU {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Debug|x86.ActiveCfg = Debug|Any CPU {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Debug|x86.Build.0 = Debug|Any CPU {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Release|x86.ActiveCfg = Release|Any CPU {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Release|x86.Build.0 = Release|Any CPU - {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.ActiveCfg = Debug|x86 - {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.Build.0 = Debug|x86 - {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.ActiveCfg = Release|x86 - {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.Build.0 = Release|x86 - {C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|x86.ActiveCfg = Debug|x86 - {C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|x86.Build.0 = Debug|x86 - {C61154BA-DD4A-4838-8420-0162A28925E0}.Release|x86.ActiveCfg = Release|x86 - {C61154BA-DD4A-4838-8420-0162A28925E0}.Release|x86.Build.0 = Release|x86 {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Debug|x86.ActiveCfg = Debug|x86 {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Debug|x86.Build.0 = Debug|x86 {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|x86.ActiveCfg = Release|x86 {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|x86.Build.0 = Release|x86 + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.ActiveCfg = Debug|x86 + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.Build.0 = Debug|x86 + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.ActiveCfg = Release|x86 + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.Build.0 = Release|x86 + {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|x86.ActiveCfg = Debug|Any CPU + {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|x86.Build.0 = Debug|Any CPU + {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|x86.ActiveCfg = Release|Any CPU + {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|x86.Build.0 = Release|Any CPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.ActiveCfg = Debug|Any CPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.Build.0 = Debug|Any CPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.ActiveCfg = Release|Any CPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.Build.0 = Release|Any CPU {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Debug|x86.ActiveCfg = Debug|x86 {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Debug|x86.Build.0 = Debug|x86 {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Release|x86.ActiveCfg = Release|x86 {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Release|x86.Build.0 = Release|x86 - {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|x86.ActiveCfg = Debug|x86 - {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|x86.Build.0 = Debug|x86 - {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|x86.ActiveCfg = Release|x86 - {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|x86.Build.0 = Release|x86 + {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Debug|x86.ActiveCfg = Debug|Any CPU + {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Debug|x86.Build.0 = Debug|Any CPU + {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Release|x86.ActiveCfg = Release|Any CPU + {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Release|x86.Build.0 = Release|Any CPU {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Debug|x86.ActiveCfg = Debug|Any CPU {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Debug|x86.Build.0 = Debug|Any CPU {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Release|x86.ActiveCfg = Release|Any CPU {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Release|x86.Build.0 = Release|Any CPU + {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|x86.ActiveCfg = Debug|x86 + {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|x86.Build.0 = Debug|x86 + {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|x86.ActiveCfg = Release|x86 + {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|x86.Build.0 = Release|x86 + {C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|x86.ActiveCfg = Debug|x86 + {C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|x86.Build.0 = Debug|x86 + {C61154BA-DD4A-4838-8420-0162A28925E0}.Release|x86.ActiveCfg = Release|x86 + {C61154BA-DD4A-4838-8420-0162A28925E0}.Release|x86.Build.0 = Release|x86 + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|x86.ActiveCfg = Debug|Any CPU + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|x86.Build.0 = Debug|Any CPU + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|x86.ActiveCfg = Release|Any CPU + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|x86.Build.0 = Release|Any CPU + {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Debug|x86.ActiveCfg = Debug|Any CPU + {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Debug|x86.Build.0 = Debug|Any CPU + {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Release|x86.ActiveCfg = Release|Any CPU + {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = Grpc.Examples\Grpc.Examples.csproj - EndGlobalSection EndGlobal diff --git a/src/csharp/build_packages.bat b/src/csharp/build_packages.bat index 3412129fb2b..c3e5fe8817d 100644 --- a/src/csharp/build_packages.bat +++ b/src/csharp/build_packages.bat @@ -1,5 +1,9 @@ @rem Builds gRPC NuGet packages +@rem Current package versions +set VERSION=0.6.0 +set CORE_VERSION=0.10.0 + @rem Adjust the location of nuget.exe set NUGET=C:\nuget\nuget.exe @@ -10,11 +14,12 @@ endlocal @call buildall.bat || goto :error -%NUGET% pack ..\..\vsprojects\nuget_package\grpc.native.csharp_ext.nuspec || goto :error -%NUGET% pack Grpc.Core\Grpc.Core.nuspec -Symbols || goto :error -%NUGET% pack Grpc.Auth\Grpc.Auth.nuspec -Symbols || goto :error -%NUGET% pack Grpc.Tools.nuspec || goto :error -%NUGET% pack Grpc.nuspec || goto :error +%NUGET% pack ..\..\vsprojects\nuget_package\grpc.native.csharp_ext.nuspec -Version %CORE_VERSION% || goto :error +%NUGET% pack Grpc.Auth\Grpc.Auth.nuspec -Symbols -Version %VERSION% || goto :error +%NUGET% pack Grpc.Core\Grpc.Core.nuspec -Symbols -Version %VERSION% -Properties GrpcNativeCsharpExtVersion=%CORE_VERSION% || goto :error +%NUGET% pack Grpc.HealthCheck\Grpc.HealthCheck.nuspec -Symbols -Version %VERSION% || goto :error +%NUGET% pack Grpc.Tools.nuspec -Version %VERSION% || goto :error +%NUGET% pack Grpc.nuspec -Version %VERSION% || goto :error goto :EOF diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index ec125db78bf..7dd1959a5f7 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -302,12 +302,13 @@ grpcsharp_completion_queue_destroy(grpc_completion_queue *cq) { GPR_EXPORT grpc_event GPR_CALLTYPE grpcsharp_completion_queue_next(grpc_completion_queue *cq) { - return grpc_completion_queue_next(cq, gpr_inf_future); + return grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME)); } GPR_EXPORT grpc_event GPR_CALLTYPE grpcsharp_completion_queue_pluck(grpc_completion_queue *cq, void *tag) { - return grpc_completion_queue_pluck(cq, tag, gpr_inf_future); + return grpc_completion_queue_pluck(cq, tag, + gpr_inf_future(GPR_CLOCK_REALTIME)); } /* Channel */ @@ -379,10 +380,10 @@ grpcsharp_channel_args_destroy(grpc_channel_args *args) { /* Timespec */ -GPR_EXPORT gpr_timespec GPR_CALLTYPE gprsharp_now(void) { return gpr_now(); } +GPR_EXPORT gpr_timespec GPR_CALLTYPE gprsharp_now(void) { return gpr_now(GPR_CLOCK_REALTIME); } GPR_EXPORT gpr_timespec GPR_CALLTYPE gprsharp_inf_future(void) { - return gpr_inf_future; + return gpr_inf_future(GPR_CLOCK_REALTIME); } GPR_EXPORT gpr_int32 GPR_CALLTYPE gprsharp_sizeof_timespec(void) { diff --git a/src/csharp/generate_proto_csharp.sh b/src/csharp/generate_proto_csharp.sh index f980787bb70..7c3ba709220 100755 --- a/src/csharp/generate_proto_csharp.sh +++ b/src/csharp/generate_proto_csharp.sh @@ -32,12 +32,17 @@ set +e cd $(dirname $0) +PROTOC=../../bins/opt/protobuf/protoc PLUGIN=protoc-gen-grpc=../../bins/opt/grpc_csharp_plugin EXAMPLES_DIR=Grpc.Examples INTEROP_DIR=Grpc.IntegrationTesting +HEALTHCHECK_DIR=Grpc.HealthCheck -protoc --plugin=$PLUGIN --grpc_out=$EXAMPLES_DIR \ +$PROTOC --plugin=$PLUGIN --grpc_out=$EXAMPLES_DIR \ -I $EXAMPLES_DIR/proto $EXAMPLES_DIR/proto/math.proto -protoc --plugin=$PLUGIN --grpc_out=$INTEROP_DIR \ +$PROTOC --plugin=$PLUGIN --grpc_out=$INTEROP_DIR \ -I $INTEROP_DIR/proto $INTEROP_DIR/proto/test.proto + +$PROTOC --plugin=$PLUGIN --grpc_out=$HEALTHCHECK_DIR \ + -I $HEALTHCHECK_DIR/proto $HEALTHCHECK_DIR/proto/health.proto diff --git a/src/node/ext/completion_queue_async_worker.cc b/src/node/ext/completion_queue_async_worker.cc index 4be208c82da..1215c97e193 100644 --- a/src/node/ext/completion_queue_async_worker.cc +++ b/src/node/ext/completion_queue_async_worker.cc @@ -62,7 +62,8 @@ CompletionQueueAsyncWorker::CompletionQueueAsyncWorker() CompletionQueueAsyncWorker::~CompletionQueueAsyncWorker() {} void CompletionQueueAsyncWorker::Execute() { - result = grpc_completion_queue_next(queue, gpr_inf_future); + result = + grpc_completion_queue_next(queue, gpr_inf_future(GPR_CLOCK_REALTIME)); if (!result.success) { SetErrorMessage("The batch encountered an error"); } diff --git a/src/node/ext/server.cc b/src/node/ext/server.cc index 51c55ba9657..34cde9ffab0 100644 --- a/src/node/ext/server.cc +++ b/src/node/ext/server.cc @@ -161,7 +161,8 @@ void Server::ShutdownServer() { grpc_server_shutdown_and_notify(this->wrapped_server, this->shutdown_queue, NULL); - grpc_completion_queue_pluck(this->shutdown_queue, NULL, gpr_inf_future); + grpc_completion_queue_pluck(this->shutdown_queue, NULL, + gpr_inf_future(GPR_CLOCK_REALTIME)); this->wrapped_server = NULL; } } diff --git a/src/node/ext/timeval.cc b/src/node/ext/timeval.cc index bc3237f7a6c..60de4d816dd 100644 --- a/src/node/ext/timeval.cc +++ b/src/node/ext/timeval.cc @@ -42,18 +42,19 @@ namespace node { gpr_timespec MillisecondsToTimespec(double millis) { if (millis == std::numeric_limits::infinity()) { - return gpr_inf_future; + return gpr_inf_future(GPR_CLOCK_REALTIME); } else if (millis == -std::numeric_limits::infinity()) { - return gpr_inf_past; + return gpr_inf_past(GPR_CLOCK_REALTIME); } else { - return gpr_time_from_micros(static_cast(millis * 1000)); + return gpr_time_from_micros(static_cast(millis * 1000), + GPR_CLOCK_REALTIME); } } double TimespecToMilliseconds(gpr_timespec timespec) { - if (gpr_time_cmp(timespec, gpr_inf_future) == 0) { + if (gpr_time_cmp(timespec, gpr_inf_future(GPR_CLOCK_REALTIME)) == 0) { return std::numeric_limits::infinity(); - } else if (gpr_time_cmp(timespec, gpr_inf_past) == 0) { + } else if (gpr_time_cmp(timespec, gpr_inf_past(GPR_CLOCK_REALTIME)) == 0) { return -std::numeric_limits::infinity(); } else { return (static_cast(timespec.tv_sec) * 1000 + diff --git a/src/node/src/server.js b/src/node/src/server.js index 9ac428f3ee2..00be400e61a 100644 --- a/src/node/src/server.js +++ b/src/node/src/server.js @@ -55,7 +55,7 @@ var EventEmitter = require('events').EventEmitter; */ function handleError(call, error) { var status = { - code: grpc.status.INTERNAL, + code: grpc.status.UNKNOWN, details: 'Unknown Error', metadata: {} }; @@ -142,12 +142,12 @@ function setUpWritable(stream, serialize) { stream.on('finish', sendStatus); /** * Set the pending status to a given error status. If the error does not have - * code or details properties, the code will be set to grpc.status.INTERNAL + * code or details properties, the code will be set to grpc.status.UNKNOWN * and the details will be set to 'Unknown Error'. * @param {Error} err The error object */ function setStatus(err) { - var code = grpc.status.INTERNAL; + var code = grpc.status.UNKNOWN; var details = 'Unknown Error'; var metadata = {}; if (err.hasOwnProperty('message')) { diff --git a/src/node/test/surface_test.js b/src/node/test/surface_test.js index 8d1f99aaee0..125957277f0 100644 --- a/src/node/test/surface_test.js +++ b/src/node/test/surface_test.js @@ -418,6 +418,48 @@ describe('Other conditions', function() { }); }); }); + describe('Error object should contain the status', function() { + it('for a unary call', function(done) { + client.unary({error: true}, function(err, data) { + assert(err); + assert.strictEqual(err.code, grpc.status.UNKNOWN); + assert.strictEqual(err.message, 'Requested error'); + done(); + }); + }); + it('for a client stream call', function(done) { + var call = client.clientStream(function(err, data) { + assert(err); + assert.strictEqual(err.code, grpc.status.UNKNOWN); + assert.strictEqual(err.message, 'Requested error'); + done(); + }); + call.write({error: false}); + call.write({error: true}); + call.end(); + }); + it('for a server stream call', function(done) { + var call = client.serverStream({error: true}); + call.on('data', function(){}); + call.on('error', function(error) { + assert.strictEqual(error.code, grpc.status.UNKNOWN); + assert.strictEqual(error.message, 'Requested error'); + done(); + }); + }); + it('for a bidi stream call', function(done) { + var call = client.bidiStream(); + call.write({error: false}); + call.write({error: true}); + call.end(); + call.on('data', function(){}); + call.on('error', function(error) { + assert.strictEqual(error.code, grpc.status.UNKNOWN); + assert.strictEqual(error.message, 'Requested error'); + done(); + }); + }); + }); }); describe('Cancelling surface client', function() { var client; diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h index cba53fa2f67..4a8b7fff486 100644 --- a/src/objective-c/GRPCClient/GRPCCall.h +++ b/src/objective-c/GRPCClient/GRPCCall.h @@ -52,7 +52,7 @@ extern id const kGRPCStatusMetadataKey; // Represents a single gRPC remote call. -@interface GRPCCall : NSObject +@interface GRPCCall : GRXWriter // These HTTP headers will be passed to the server as part of this call. Each HTTP header is a // name-value pair with string names and either string or binary values. @@ -89,7 +89,7 @@ extern id const kGRPCStatusMetadataKey; // To finish a call right away, invoke cancel. - (instancetype)initWithHost:(NSString *)host path:(NSString *)path - requestsWriter:(id)requestsWriter NS_DESIGNATED_INITIALIZER; + requestsWriter:(GRXWriter *)requestsWriter NS_DESIGNATED_INITIALIZER; // Finishes the request side of this call, notifies the server that the RPC // should be cancelled, and finishes the response side of the call with an error diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m index 4ac4e4d37f5..53e5abe177b 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -79,7 +79,7 @@ NSString * const kGRPCStatusMetadataKey = @"io.grpc.StatusMetadataKey"; // all. This wrapper over our actual writeable ensures thread-safety and // correct ordering. GRPCDelegateWrapper *_responseWriteable; - id _requestWriter; + GRXWriter *_requestWriter; NSMutableDictionary *_requestMetadata; NSMutableDictionary *_responseMetadata; @@ -94,7 +94,7 @@ NSString * const kGRPCStatusMetadataKey = @"io.grpc.StatusMetadataKey"; // Designated initializer - (instancetype)initWithHost:(NSString *)host path:(NSString *)path - requestsWriter:(id)requestWriter { + requestsWriter:(GRXWriter *)requestWriter { if (!host || !path) { [NSException raise:NSInvalidArgumentException format:@"Neither host nor method can be nil."]; } diff --git a/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h b/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h index 1ef245fe37b..9a30a2f9660 100644 --- a/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h +++ b/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h @@ -33,8 +33,9 @@ #import +#import + @protocol GRXWriteable; -@protocol GRXWriter; // This is a thread-safe wrapper over a GRXWriteable instance. It lets one // enqueue calls to a GRXWriteable instance for the main thread, guaranteeing @@ -54,7 +55,7 @@ // writesFinishedWithError: is sent to the writeable, and released after that. // This is used to create a retain cycle that keeps both objects alive until the // writing is explicitly finished. -- (instancetype)initWithWriteable:(id)writeable writer:(id)writer +- (instancetype)initWithWriteable:(id)writeable writer:(GRXWriter *)writer NS_DESIGNATED_INITIALIZER; // Enqueues writeValue: to be sent to the writeable in the main thread. diff --git a/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.m b/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.m index 59c0565494b..294cfb7e239 100644 --- a/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.m +++ b/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.m @@ -38,7 +38,7 @@ @interface GRPCDelegateWrapper () // These are atomic so that cancellation can nillify them from any thread. @property(atomic, strong) id writeable; -@property(atomic, strong) id writer; +@property(atomic, strong) GRXWriter *writer; @end @implementation GRPCDelegateWrapper { @@ -52,7 +52,7 @@ } // Designated initializer -- (instancetype)initWithWriteable:(id)writeable writer:(id)writer { +- (instancetype)initWithWriteable:(id)writeable writer:(GRXWriter *)writer { if (self = [super init]) { _writeableQueue = dispatch_get_main_queue(); _writeable = writeable; diff --git a/src/objective-c/ProtoRPC/ProtoRPC.h b/src/objective-c/ProtoRPC/ProtoRPC.h index fcc0a507feb..bd926b73287 100644 --- a/src/objective-c/ProtoRPC/ProtoRPC.h +++ b/src/objective-c/ProtoRPC/ProtoRPC.h @@ -40,7 +40,7 @@ - (instancetype)initWithHost:(NSString *)host method:(ProtoMethod *)method - requestsWriter:(id)requestsWriter + requestsWriter:(GRXWriter *)requestsWriter responseClass:(Class)responseClass responsesWriteable:(id)responsesWriteable NS_DESIGNATED_INITIALIZER; diff --git a/src/objective-c/ProtoRPC/ProtoRPC.m b/src/objective-c/ProtoRPC/ProtoRPC.m index fe3ccf05413..889d71a3084 100644 --- a/src/objective-c/ProtoRPC/ProtoRPC.m +++ b/src/objective-c/ProtoRPC/ProtoRPC.m @@ -35,7 +35,6 @@ #import #import -#import #import @implementation ProtoRPC { @@ -46,7 +45,7 @@ #pragma clang diagnostic ignored "-Wobjc-designated-initializers" - (instancetype)initWithHost:(NSString *)host path:(NSString *)path - requestsWriter:(id)requestsWriter { + requestsWriter:(GRXWriter *)requestsWriter { [NSException raise:NSInvalidArgumentException format:@"Please use ProtoRPC's designated initializer instead."]; return nil; @@ -56,7 +55,7 @@ // Designated initializer - (instancetype)initWithHost:(NSString *)host method:(ProtoMethod *)method - requestsWriter:(id)requestsWriter + requestsWriter:(GRXWriter *)requestsWriter responseClass:(Class)responseClass responsesWriteable:(id)responsesWriteable { // Because we can't tell the type system to constrain the class, we need to check at runtime: @@ -65,12 +64,11 @@ format:@"A protobuf class to parse the responses must be provided."]; } // A writer that serializes the proto messages to send. - id bytesWriter = - [[[GRXWriter alloc] initWithWriter:requestsWriter] map:^id(GPBMessage *proto) { - // TODO(jcanizales): Fail with an understandable error message if the requestsWriter isn't - // sending GPBMessages. - return [proto data]; - }]; + GRXWriter *bytesWriter = [requestsWriter map:^id(GPBMessage *proto) { + // TODO(jcanizales): Fail with an understandable error message if the requestsWriter isn't + // sending GPBMessages. + return [proto data]; + }]; if ((self = [super initWithHost:host path:method.HTTPPath requestsWriter:bytesWriter])) { // A writeable that parses the proto messages received. _responseWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { diff --git a/src/objective-c/ProtoRPC/ProtoService.h b/src/objective-c/ProtoRPC/ProtoService.h index c5ef820f48a..2e8cb33696b 100644 --- a/src/objective-c/ProtoRPC/ProtoService.h +++ b/src/objective-c/ProtoRPC/ProtoService.h @@ -35,7 +35,7 @@ @class ProtoRPC; @protocol GRXWriteable; -@protocol GRXWriter; +@class GRXWriter; @interface ProtoService : NSObject - (instancetype)initWithHost:(NSString *)host @@ -43,7 +43,7 @@ serviceName:(NSString *)serviceName NS_DESIGNATED_INITIALIZER; - (ProtoRPC *)RPCToMethod:(NSString *)method - requestsWriter:(id)requestsWriter + requestsWriter:(GRXWriter *)requestsWriter responseClass:(Class)responseClass responsesWriteable:(id)responsesWriteable; @end diff --git a/src/objective-c/ProtoRPC/ProtoService.m b/src/objective-c/ProtoRPC/ProtoService.m index d7c5b6a8501..fccc6aadc9c 100644 --- a/src/objective-c/ProtoRPC/ProtoService.m +++ b/src/objective-c/ProtoRPC/ProtoService.m @@ -66,7 +66,7 @@ } - (ProtoRPC *)RPCToMethod:(NSString *)method - requestsWriter:(id)requestsWriter + requestsWriter:(GRXWriter *)requestsWriter responseClass:(Class)responseClass responsesWriteable:(id)responsesWriteable { ProtoMethod *methodName = [[ProtoMethod alloc] initWithPackage:_packageName diff --git a/src/objective-c/RxLibrary/GRXBufferedPipe.h b/src/objective-c/RxLibrary/GRXBufferedPipe.h index 5e876a73bfe..b6296e1ed72 100644 --- a/src/objective-c/RxLibrary/GRXBufferedPipe.h +++ b/src/objective-c/RxLibrary/GRXBufferedPipe.h @@ -51,7 +51,7 @@ // pipe will keep buffering all data written to it, your application could run out of memory and // crash. If you want to react to flow control signals to prevent that, instead of using this class // you can implement an object that conforms to GRXWriter. -@interface GRXBufferedPipe : NSObject +@interface GRXBufferedPipe : GRXWriter // Convenience constructor. + (instancetype)pipe; diff --git a/src/objective-c/RxLibrary/GRXForwardingWriter.h b/src/objective-c/RxLibrary/GRXForwardingWriter.h new file mode 100644 index 00000000000..d004333d2b4 --- /dev/null +++ b/src/objective-c/RxLibrary/GRXForwardingWriter.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#import "GRXWriter.h" + +// A "proxy" class that simply forwards values, completion, and errors from its +// input writer to its writeable. +// It is useful as a superclass for pipes that act as a transformation of their +// input writer, and for classes that represent objects with input and +// output sequences of values, like an RPC. +@interface GRXForwardingWriter : GRXWriter +- (instancetype)initWithWriter:(GRXWriter *)writer NS_DESIGNATED_INITIALIZER; +@end diff --git a/src/objective-c/RxLibrary/GRXForwardingWriter.m b/src/objective-c/RxLibrary/GRXForwardingWriter.m new file mode 100644 index 00000000000..2342f51ab36 --- /dev/null +++ b/src/objective-c/RxLibrary/GRXForwardingWriter.m @@ -0,0 +1,112 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#import "GRXForwardingWriter.h" + +@interface GRXForwardingWriter () +@end + +@implementation GRXForwardingWriter { + GRXWriter *_writer; + id _writeable; +} + +- (instancetype)init { + return [self initWithWriter:nil]; +} + +// Designated initializer +- (instancetype)initWithWriter:(GRXWriter *)writer { + if (!writer) { + [NSException raise:NSInvalidArgumentException format:@"writer can't be nil."]; + } + if ((self = [super init])) { + _writer = writer; + } + return self; +} + +// This is used to send a completion or an error to the writeable. It nillifies +// our reference to it in order to guarantee no more messages are sent to it, +// and to release it. +- (void)finishOutputWithError:(NSError *)errorOrNil { + id writeable = _writeable; + _writeable = nil; + [writeable writesFinishedWithError:errorOrNil]; +} + +// This is used to stop the input writer. It nillifies our reference to it +// to release it. +- (void)finishInput { + GRXWriter *writer = _writer; + _writer = nil; + writer.state = GRXWriterStateFinished; +} + +#pragma mark GRXWriteable implementation + +- (void)writeValue:(id)value { + [_writeable writeValue:value]; +} + +- (void)writesFinishedWithError:(NSError *)errorOrNil { + _writer = nil; + [self finishOutputWithError:errorOrNil]; +} + +#pragma mark GRXWriter implementation + +- (GRXWriterState)state { + return _writer ? _writer.state : GRXWriterStateFinished; +} + +- (void)setState:(GRXWriterState)state { + if (state == GRXWriterStateFinished) { + _writeable = nil; + [self finishInput]; + } else { + _writer.state = state; + } +} + +- (void)startWithWriteable:(id)writeable { + _writeable = writeable; + [_writer startWithWriteable:self]; +} + +- (void)finishWithError:(NSError *)errorOrNil { + [self finishOutputWithError:errorOrNil]; + [self finishInput]; +} + +@end diff --git a/src/objective-c/RxLibrary/GRXImmediateWriter.h b/src/objective-c/RxLibrary/GRXImmediateWriter.h index f86d38dcd81..b171f0c760a 100644 --- a/src/objective-c/RxLibrary/GRXImmediateWriter.h +++ b/src/objective-c/RxLibrary/GRXImmediateWriter.h @@ -40,15 +40,15 @@ // // Unless the writeable callback pauses them or stops them early, these writers will do all their // interactions with the writeable before the start method returns. -@interface GRXImmediateWriter : NSObject +@interface GRXImmediateWriter : GRXWriter // Returns a writer that pulls values from the passed NSEnumerator instance and pushes them to // its writeable. The NSEnumerator is released when it finishes. -+ (id)writerWithEnumerator:(NSEnumerator *)enumerator; ++ (GRXWriter *)writerWithEnumerator:(NSEnumerator *)enumerator; // Returns a writer that pushes to its writeable the successive values returned by the passed // block. When the block first returns nil, it is released. -+ (id)writerWithValueSupplier:(id (^)())block; ++ (GRXWriter *)writerWithValueSupplier:(id (^)())block; // Returns a writer that iterates over the values of the passed container and pushes them to // its writeable. The container is released when the iteration is over. @@ -56,18 +56,18 @@ // Note that the usual speed gain of NSFastEnumeration over NSEnumerator results from not having to // call one method per element. Because GRXWriteable instances accept values one by one, that speed // gain doesn't happen here. -+ (id)writerWithContainer:(id)container; ++ (GRXWriter *)writerWithContainer:(id)container; // Returns a writer that sends the passed value to its writeable and then finishes (releasing the // value). -+ (id)writerWithValue:(id)value; ++ (GRXWriter *)writerWithValue:(id)value; // Returns a writer that, as part of its start method, sends the passed error to the writeable // (then releasing the error). -+ (id)writerWithError:(NSError *)error; ++ (GRXWriter *)writerWithError:(NSError *)error; // Returns a writer that, as part of its start method, finishes immediately without sending any // values to its writeable. -+ (id)emptyWriter; ++ (GRXWriter *)emptyWriter; @end diff --git a/src/objective-c/RxLibrary/GRXImmediateWriter.m b/src/objective-c/RxLibrary/GRXImmediateWriter.m index 0b4979872e2..b6d2b2cac0d 100644 --- a/src/objective-c/RxLibrary/GRXImmediateWriter.m +++ b/src/objective-c/RxLibrary/GRXImmediateWriter.m @@ -63,19 +63,19 @@ return [[self alloc] initWithEnumerator:enumerator error:errorOrNil]; } -+ (id)writerWithEnumerator:(NSEnumerator *)enumerator { ++ (GRXWriter *)writerWithEnumerator:(NSEnumerator *)enumerator { return [self writerWithEnumerator:enumerator error:nil]; } -+ (id)writerWithValueSupplier:(id (^)())block { ++ (GRXWriter *)writerWithValueSupplier:(id (^)())block { return [self writerWithEnumerator:[NSEnumerator grx_enumeratorWithValueSupplier:block]]; } -+ (id)writerWithContainer:(id)container { ++ (GRXWriter *)writerWithContainer:(id)container { return [self writerWithEnumerator:[NSEnumerator grx_enumeratorWithContainer:container]];; } -+ (id)writerWithValue:(id)value { ++ (GRXWriter *)writerWithValue:(id)value { if (value) { return [self writerWithEnumerator:[NSEnumerator grx_enumeratorWithSingleValue:value]]; } else { @@ -83,7 +83,7 @@ } } -+ (id)writerWithError:(NSError *)error { ++ (GRXWriter *)writerWithError:(NSError *)error { if (error) { return [self writerWithEnumerator:nil error:error]; } else { @@ -91,7 +91,7 @@ } } -+ (id)emptyWriter { ++ (GRXWriter *)emptyWriter { static GRXImmediateWriter *emptyWriter; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ diff --git a/src/objective-c/RxLibrary/GRXWriter+Immediate.m b/src/objective-c/RxLibrary/GRXWriter+Immediate.m index 39c54f86ec6..1d55eb35293 100644 --- a/src/objective-c/RxLibrary/GRXWriter+Immediate.m +++ b/src/objective-c/RxLibrary/GRXWriter+Immediate.m @@ -38,27 +38,27 @@ @implementation GRXWriter (Immediate) + (instancetype)writerWithEnumerator:(NSEnumerator *)enumerator { - return [[self alloc] initWithWriter:[GRXImmediateWriter writerWithEnumerator:enumerator]]; + return [GRXImmediateWriter writerWithEnumerator:enumerator]; } + (instancetype)writerWithValueSupplier:(id (^)())block { - return [[self alloc] initWithWriter:[GRXImmediateWriter writerWithValueSupplier:block]]; + return [GRXImmediateWriter writerWithValueSupplier:block]; } + (instancetype)writerWithContainer:(id)container { - return [[self alloc] initWithWriter:[GRXImmediateWriter writerWithContainer:container]]; + return [GRXImmediateWriter writerWithContainer:container]; } + (instancetype)writerWithValue:(id)value { - return [[self alloc] initWithWriter:[GRXImmediateWriter writerWithValue:value]]; + return [GRXImmediateWriter writerWithValue:value]; } + (instancetype)writerWithError:(NSError *)error { - return [[self alloc] initWithWriter:[GRXImmediateWriter writerWithError:error]]; + return [GRXImmediateWriter writerWithError:error]; } + (instancetype)emptyWriter { - return [[self alloc] initWithWriter:[GRXImmediateWriter emptyWriter]]; + return [GRXImmediateWriter emptyWriter]; } @end diff --git a/src/objective-c/RxLibrary/GRXWriter.h b/src/objective-c/RxLibrary/GRXWriter.h index dcf44e31433..5d6e1a472af 100644 --- a/src/objective-c/RxLibrary/GRXWriter.h +++ b/src/objective-c/RxLibrary/GRXWriter.h @@ -85,7 +85,7 @@ typedef NS_ENUM(NSInteger, GRXWriterState) { // Unless otherwise indicated by a conforming class, no messages should be sent // concurrently to a GRXWriter. I.e., conforming classes aren't required to // be thread-safe. -@protocol GRXWriter +@interface GRXWriter : NSObject // This property can be used to query the current state of the writer, which // determines how it might currently use its writeable. Some state transitions can @@ -116,12 +116,3 @@ typedef NS_ENUM(NSInteger, GRXWriterState) { // can't remember the details in the presence of concurrency. - (void)finishWithError:(NSError *)errorOrNil; @end - -// A "proxy" class that simply forwards values, completion, and errors from its -// input writer to its writeable. -// It is useful as a superclass for pipes that act as a transformation of their -// input writer, and for classes that represent objects with input and -// output sequences of values, like an RPC. -@interface GRXWriter : NSObject -- (instancetype)initWithWriter:(id)writer NS_DESIGNATED_INITIALIZER; -@end diff --git a/src/objective-c/RxLibrary/GRXWriter.m b/src/objective-c/RxLibrary/GRXWriter.m index cc143835605..019fcbd7858 100644 --- a/src/objective-c/RxLibrary/GRXWriter.m +++ b/src/objective-c/RxLibrary/GRXWriter.m @@ -33,80 +33,6 @@ #import "GRXWriter.h" -@interface GRXWriter () -@end - -@implementation GRXWriter { - id _writer; - id _writeable; -} - -- (instancetype)init { - return [self initWithWriter:nil]; -} - -// Designated initializer -- (instancetype)initWithWriter:(id)writer { - if (!writer) { - [NSException raise:NSInvalidArgumentException format:@"writer can't be nil."]; - } - if ((self = [super init])) { - _writer = writer; - } - return self; -} - -// This is used to send a completion or an error to the writeable. It nillifies -// our reference to it in order to guarantee no more messages are sent to it, -// and to release it. -- (void)finishOutputWithError:(NSError *)errorOrNil { - id writeable = _writeable; - _writeable = nil; - [writeable writesFinishedWithError:errorOrNil]; -} - -// This is used to stop the input writer. It nillifies our reference to it -// to release it. -- (void)finishInput { - id writer = _writer; - _writer = nil; - writer.state = GRXWriterStateFinished; -} - -#pragma mark GRXWriteable implementation - -- (void)writeValue:(id)value { - [_writeable writeValue:value]; -} - -- (void)writesFinishedWithError:(NSError *)errorOrNil { - _writer = nil; - [self finishOutputWithError:errorOrNil]; -} - -#pragma mark GRXWriter implementation - -- (GRXWriterState)state { - return _writer ? _writer.state : GRXWriterStateFinished; -} - -- (void)setState:(GRXWriterState)state { - if (state == GRXWriterStateFinished) { - _writeable = nil; - [self finishInput]; - } else { - _writer.state = state; - } -} - -- (void)startWithWriteable:(id)writeable { - _writeable = writeable; - [_writer startWithWriteable:self]; -} - -- (void)finishWithError:(NSError *)errorOrNil { - [self finishOutputWithError:errorOrNil]; - [self finishInput]; -} +@implementation GRXWriter @end diff --git a/src/objective-c/RxLibrary/transformations/GRXMappingWriter.h b/src/objective-c/RxLibrary/transformations/GRXMappingWriter.h index 55f6f82f20b..43b87068647 100644 --- a/src/objective-c/RxLibrary/transformations/GRXMappingWriter.h +++ b/src/objective-c/RxLibrary/transformations/GRXMappingWriter.h @@ -31,10 +31,10 @@ * */ -#import "RxLibrary/GRXWriter.h" +#import "RxLibrary/GRXForwardingWriter.h" // A "proxy" writer that transforms all the values of its input writer by using a mapping function. -@interface GRXMappingWriter : GRXWriter -- (instancetype)initWithWriter:(id)writer map:(id (^)(id value))map +@interface GRXMappingWriter : GRXForwardingWriter +- (instancetype)initWithWriter:(GRXWriter *)writer map:(id (^)(id value))map NS_DESIGNATED_INITIALIZER; @end diff --git a/src/objective-c/RxLibrary/transformations/GRXMappingWriter.m b/src/objective-c/RxLibrary/transformations/GRXMappingWriter.m index 2cdfea1b675..f3242e4fa95 100644 --- a/src/objective-c/RxLibrary/transformations/GRXMappingWriter.m +++ b/src/objective-c/RxLibrary/transformations/GRXMappingWriter.m @@ -37,19 +37,19 @@ static id (^kIdentity)(id value) = ^id(id value) { return value; }; -@interface GRXWriter () +@interface GRXForwardingWriter () @end @implementation GRXMappingWriter { id (^_map)(id value); } -- (instancetype)initWithWriter:(id)writer { +- (instancetype)initWithWriter:(GRXWriter *)writer { return [self initWithWriter:writer map:nil]; } // Designated initializer -- (instancetype)initWithWriter:(id)writer map:(id (^)(id value))map { +- (instancetype)initWithWriter:(GRXWriter *)writer map:(id (^)(id value))map { if ((self = [super initWithWriter:writer])) { _map = map ?: kIdentity; } diff --git a/src/objective-c/tests/GRPCClientTests.m b/src/objective-c/tests/GRPCClientTests.m index f9c2d5d8d6e..3210ad70502 100644 --- a/src/objective-c/tests/GRPCClientTests.m +++ b/src/objective-c/tests/GRPCClientTests.m @@ -87,7 +87,7 @@ static ProtoMethod *kUnaryCallMethod; [call startWithWriteable:responsesWriteable]; - [self waitForExpectationsWithTimeout:2. handler:nil]; + [self waitForExpectationsWithTimeout:4 handler:nil]; } - (void)testEmptyRPC { @@ -109,7 +109,7 @@ static ProtoMethod *kUnaryCallMethod; [call startWithWriteable:responsesWriteable]; - [self waitForExpectationsWithTimeout:2. handler:nil]; + [self waitForExpectationsWithTimeout:4 handler:nil]; } - (void)testSimpleProtoRPC { @@ -120,7 +120,7 @@ static ProtoMethod *kUnaryCallMethod; request.responseSize = 100; request.fillUsername = YES; request.fillOauthScope = YES; - id requestsWriter = [GRXWriter writerWithValue:[request data]]; + GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]]; GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress path:kUnaryCallMethod.HTTPPath @@ -141,7 +141,7 @@ static ProtoMethod *kUnaryCallMethod; [call startWithWriteable:responsesWriteable]; - [self waitForExpectationsWithTimeout:2. handler:nil]; + [self waitForExpectationsWithTimeout:4 handler:nil]; } - (void)testMetadata { @@ -150,7 +150,7 @@ static ProtoMethod *kUnaryCallMethod; RMTSimpleRequest *request = [RMTSimpleRequest message]; request.fillUsername = YES; request.fillOauthScope = YES; - id requestsWriter = [GRXWriter writerWithValue:[request data]]; + GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]]; GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress path:kUnaryCallMethod.HTTPPath @@ -173,7 +173,7 @@ static ProtoMethod *kUnaryCallMethod; [call startWithWriteable:responsesWriteable]; - [self waitForExpectationsWithTimeout:2. handler:nil]; + [self waitForExpectationsWithTimeout:4 handler:nil]; } @end diff --git a/src/objective-c/tests/InteropTests.m b/src/objective-c/tests/InteropTests.m index 74f6b231cfd..501f33317a1 100644 --- a/src/objective-c/tests/InteropTests.m +++ b/src/objective-c/tests/InteropTests.m @@ -103,7 +103,7 @@ [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:2 handler:nil]; + [self waitForExpectationsWithTimeout:4 handler:nil]; } - (void)testLargeUnaryRPC { @@ -125,7 +125,7 @@ [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:4 handler:nil]; + [self waitForExpectationsWithTimeout:8 handler:nil]; } - (void)testClientStreamingRPC { @@ -143,7 +143,7 @@ RMTStreamingInputCallRequest *request4 = [RMTStreamingInputCallRequest message]; request4.payload.body = [NSMutableData dataWithLength:45904]; - id writer = [GRXWriter writerWithContainer:@[request1, request2, request3, request4]]; + GRXWriter *writer = [GRXWriter writerWithContainer:@[request1, request2, request3, request4]]; [_service streamingInputCallWithRequestsWriter:writer handler:^(RMTStreamingInputCallResponse *response, @@ -157,7 +157,7 @@ [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:4 handler:nil]; + [self waitForExpectationsWithTimeout:8 handler:nil]; } - (void)testServerStreamingRPC { @@ -193,7 +193,7 @@ } }]; - [self waitForExpectationsWithTimeout:4 handler:nil]; + [self waitForExpectationsWithTimeout:8 handler:nil]; } - (void)testPingPongRPC { @@ -236,7 +236,7 @@ [expectation fulfill]; } }]; - [self waitForExpectationsWithTimeout:2 handler:nil]; + [self waitForExpectationsWithTimeout:4 handler:nil]; } - (void)testEmptyStreamRPC { @@ -282,10 +282,11 @@ [requestsBuffer writeValue:request]; - __block ProtoRPC *call = [_service RPCToFullDuplexCallWithRequestsWriter:requestsBuffer - eventHandler:^(BOOL done, - RMTStreamingOutputCallResponse *response, - NSError *error) { + __block ProtoRPC *call = + [_service RPCToFullDuplexCallWithRequestsWriter:requestsBuffer + eventHandler:^(BOOL done, + RMTStreamingOutputCallResponse *response, + NSError *error) { if (receivedResponse) { XCTAssert(done, @"Unexpected extra response %@", response); XCTAssertEqual(error.code, GRPC_STATUS_CANCELLED); @@ -299,7 +300,7 @@ } }]; [call start]; - [self waitForExpectationsWithTimeout:4 handler:nil]; + [self waitForExpectationsWithTimeout:8 handler:nil]; } @end diff --git a/src/objective-c/tests/LocalClearTextTests.m b/src/objective-c/tests/LocalClearTextTests.m index 10c9f13ea3b..4317614dd9d 100644 --- a/src/objective-c/tests/LocalClearTextTests.m +++ b/src/objective-c/tests/LocalClearTextTests.m @@ -64,7 +64,7 @@ static NSString * const kService = @"RouteGuide"; // interface:kService // method:@"EmptyCall"]; // -// id requestsWriter = [GRXWriter writerWithValue:[NSData data]]; +// GRXWriter *requestsWriter = [GRXWriter writerWithValue:[NSData data]]; // // GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost // method:method @@ -91,7 +91,7 @@ static NSString * const kService = @"RouteGuide"; service:kService method:@"RecordRoute"]; - id requestsWriter = [GRXWriter emptyWriter]; + GRXWriter *requestsWriter = [GRXWriter emptyWriter]; GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost path:method.HTTPPath @@ -122,7 +122,7 @@ static NSString * const kService = @"RouteGuide"; RGDPoint *point = [RGDPoint message]; point.latitude = 28E7; point.longitude = -15E7; - id requestsWriter = [GRXWriter writerWithValue:[point data]]; + GRXWriter *requestsWriter = [GRXWriter writerWithValue:[point data]]; GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost path:method.HTTPPath diff --git a/src/php/bin/determine_extension_dir.sh b/src/php/bin/determine_extension_dir.sh index 6bbd934bf13..b4342ac89fa 100755 --- a/src/php/bin/determine_extension_dir.sh +++ b/src/php/bin/determine_extension_dir.sh @@ -27,12 +27,12 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - set -e default_extension_dir=$(php-config --extension-dir) -if command -v brew >/dev/null && [ -d $(brew --prefix)/opt/grpc-php ]; then - # homebrew and the grpc-php formula are installed - extension_dir="-d extension_dir="$(brew --prefix)/opt/grpc-php +if command -v brew > /dev/null && \ + brew ls --versions | grep php5[56]-grpc > /dev/null; then + # the grpc php extension was installed by homebrew + : elif [ ! -e $default_extension_dir/grpc.so ]; then # the grpc extension is not found in the default PHP extension dir # try the source modules directory @@ -45,5 +45,7 @@ elif [ ! -e $default_extension_dir/grpc.so ]; then for f in $default_extension_dir/*.so; do ln -s $f $module_dir/$(basename $f) &> /dev/null || true done - extension_dir="-d extension_dir="$module_dir + extension_dir="-d extension_dir=${module_dir} -d extension=grpc.so" +else + extension_dir="-d extension=grpc.so" fi diff --git a/src/php/bin/interop_client.sh b/src/php/bin/interop_client.sh index 42e075cbe88..17b888dd4ed 100755 --- a/src/php/bin/interop_client.sh +++ b/src/php/bin/interop_client.sh @@ -31,5 +31,5 @@ set -e cd $(dirname $0) source ./determine_extension_dir.sh -php $extension_dir -d extension=grpc.so \ +php $extension_dir \ ../tests/interop/interop_client.php $@ 1>&2 diff --git a/src/php/bin/run_gen_code_test.sh b/src/php/bin/run_gen_code_test.sh index 03a9101a458..6e56c7207c2 100755 --- a/src/php/bin/run_gen_code_test.sh +++ b/src/php/bin/run_gen_code_test.sh @@ -31,8 +31,8 @@ set -e cd $(dirname $0) source ./determine_extension_dir.sh -export GRPC_TEST_HOST=localhost:7071 -php $extension_dir -d extension=grpc.so $(which phpunit) -v --debug --strict \ +export GRPC_TEST_HOST=localhost:50051 +php $extension_dir $(which phpunit) -v --debug --strict \ ../tests/generated_code/GeneratedCodeTest.php -php $extension_dir -d extension=grpc.so $(which phpunit) -v --debug --strict \ +php $extension_dir $(which phpunit) -v --debug --strict \ ../tests/generated_code/GeneratedCodeWithCallbackTest.php diff --git a/src/php/bin/run_tests.sh b/src/php/bin/run_tests.sh index 4c37285455b..953f408ea8d 100755 --- a/src/php/bin/run_tests.sh +++ b/src/php/bin/run_tests.sh @@ -33,5 +33,5 @@ set -e cd $(dirname $0) source ./determine_extension_dir.sh -php $extension_dir -d extension=grpc.so $(which phpunit) -v --debug --strict \ +php $extension_dir $(which phpunit) -v --debug --strict \ ../tests/unit_tests diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c index 10a4946ea60..b67bae75682 100644 --- a/src/php/ext/grpc/call.c +++ b/src/php/ext/grpc/call.c @@ -408,7 +408,7 @@ PHP_METHOD(Call, startBatch) { goto cleanup; } event = grpc_completion_queue_pluck(completion_queue, call->wrapped, - gpr_inf_future); + gpr_inf_future(GPR_CLOCK_REALTIME)); if (!event.success) { zend_throw_exception(spl_ce_LogicException, "The batch failed for some reason", diff --git a/src/php/ext/grpc/completion_queue.c b/src/php/ext/grpc/completion_queue.c index b24c83757ca..c653a592efd 100644 --- a/src/php/ext/grpc/completion_queue.c +++ b/src/php/ext/grpc/completion_queue.c @@ -43,8 +43,9 @@ void grpc_php_init_completion_queue(TSRMLS_D) { void grpc_php_shutdown_completion_queue(TSRMLS_D) { grpc_completion_queue_shutdown(completion_queue); - while (grpc_completion_queue_next(completion_queue, gpr_inf_future).type != - GRPC_QUEUE_SHUTDOWN) + while (grpc_completion_queue_next(completion_queue, + gpr_inf_future(GPR_CLOCK_REALTIME)) + .type != GRPC_QUEUE_SHUTDOWN) ; grpc_completion_queue_destroy(completion_queue); } diff --git a/src/php/ext/grpc/server.c b/src/php/ext/grpc/server.c index 02c886c715c..c319526e420 100644 --- a/src/php/ext/grpc/server.c +++ b/src/php/ext/grpc/server.c @@ -64,7 +64,8 @@ void free_wrapped_grpc_server(void *object TSRMLS_DC) { wrapped_grpc_server *server = (wrapped_grpc_server *)object; if (server->wrapped != NULL) { grpc_server_shutdown_and_notify(server->wrapped, completion_queue, NULL); - grpc_completion_queue_pluck(completion_queue, NULL, gpr_inf_future); + grpc_completion_queue_pluck(completion_queue, NULL, + gpr_inf_future(GPR_CLOCK_REALTIME)); grpc_server_destroy(server->wrapped); } efree(server); @@ -143,7 +144,8 @@ PHP_METHOD(Server, requestCall) { (long)error_code TSRMLS_CC); goto cleanup; } - event = grpc_completion_queue_pluck(completion_queue, NULL, gpr_inf_future); + event = grpc_completion_queue_pluck(completion_queue, NULL, + gpr_inf_future(GPR_CLOCK_REALTIME)); if (!event.success) { zend_throw_exception(spl_ce_LogicException, "Failed to request a call for some reason", diff --git a/src/php/ext/grpc/timeval.c b/src/php/ext/grpc/timeval.c index 8a278d67607..4fd069e19a1 100644 --- a/src/php/ext/grpc/timeval.c +++ b/src/php/ext/grpc/timeval.c @@ -98,7 +98,7 @@ PHP_METHOD(Timeval, __construct) { "Timeval expects a long", 1 TSRMLS_CC); return; } - gpr_timespec time = gpr_time_from_micros(microseconds); + gpr_timespec time = gpr_time_from_micros(microseconds, GPR_TIMESPAN); memcpy(&timeval->wrapped, &time, sizeof(gpr_timespec)); } @@ -208,7 +208,7 @@ PHP_METHOD(Timeval, similar) { * @return Timeval The current time */ PHP_METHOD(Timeval, now) { - zval *now = grpc_php_wrap_timeval(gpr_now()); + zval *now = grpc_php_wrap_timeval(gpr_now(GPR_CLOCK_REALTIME)); RETURN_DESTROY_ZVAL(now); } @@ -217,7 +217,8 @@ PHP_METHOD(Timeval, now) { * @return Timeval Zero length time interval */ PHP_METHOD(Timeval, zero) { - zval *grpc_php_timeval_zero = grpc_php_wrap_timeval(gpr_time_0); + zval *grpc_php_timeval_zero = + grpc_php_wrap_timeval(gpr_time_0(GPR_CLOCK_REALTIME)); RETURN_ZVAL(grpc_php_timeval_zero, false, /* Copy original before returning? */ true /* Destroy original before returning */); @@ -228,7 +229,8 @@ PHP_METHOD(Timeval, zero) { * @return Timeval Infinite future time value */ PHP_METHOD(Timeval, infFuture) { - zval *grpc_php_timeval_inf_future = grpc_php_wrap_timeval(gpr_inf_future); + zval *grpc_php_timeval_inf_future = + grpc_php_wrap_timeval(gpr_inf_future(GPR_CLOCK_REALTIME)); RETURN_DESTROY_ZVAL(grpc_php_timeval_inf_future); } @@ -237,7 +239,8 @@ PHP_METHOD(Timeval, infFuture) { * @return Timeval Infinite past time value */ PHP_METHOD(Timeval, infPast) { - zval *grpc_php_timeval_inf_past = grpc_php_wrap_timeval(gpr_inf_past); + zval *grpc_php_timeval_inf_past = + grpc_php_wrap_timeval(gpr_inf_past(GPR_CLOCK_REALTIME)); RETURN_DESTROY_ZVAL(grpc_php_timeval_inf_past); } diff --git a/src/python/interop/setup.py b/src/python/interop/setup.py index 502fcbedd84..75012b0d8f4 100644 --- a/src/python/interop/setup.py +++ b/src/python/interop/setup.py @@ -45,7 +45,7 @@ _PACKAGE_DATA = { 'credentials/server1.pem',] } -_INSTALL_REQUIRES = ['oauth2client>=1.4.7', 'grpcio>=0.4.0a4'] +_INSTALL_REQUIRES = ['oauth2client>=1.4.7', 'grpcio>=0.10.0a0'] setuptools.setup( name='interop', diff --git a/src/python/requirements.txt b/src/python/requirements.txt index 41d633a2dd6..43395df03b9 100644 --- a/src/python/requirements.txt +++ b/src/python/requirements.txt @@ -1,4 +1,3 @@ enum34==1.0.4 futures==2.2.0 protobuf==3.0.0a3 -cython>=0.22 diff --git a/src/python/src/grpc/_adapter/_c/utility.c b/src/python/src/grpc/_adapter/_c/utility.c index 480a720c21f..000c8d0c382 100644 --- a/src/python/src/grpc/_adapter/_c/utility.c +++ b/src/python/src/grpc/_adapter/_c/utility.c @@ -385,10 +385,12 @@ static int pygrpc_isinf(double x) { gpr_timespec pygrpc_cast_double_to_gpr_timespec(double seconds) { gpr_timespec result; if (pygrpc_isinf(seconds)) { - result = seconds > 0.0 ? gpr_inf_future : gpr_inf_past; + result = seconds > 0.0 ? gpr_inf_future(GPR_CLOCK_REALTIME) + : gpr_inf_past(GPR_CLOCK_REALTIME); } else { result.tv_sec = (time_t)seconds; result.tv_nsec = ((seconds - result.tv_sec) * 1e9); + result.clock_type = GPR_CLOCK_REALTIME; } return result; } diff --git a/src/python/src/grpc/_links/__init__.py b/src/python/src/grpc/_links/__init__.py new file mode 100644 index 00000000000..70865191060 --- /dev/null +++ b/src/python/src/grpc/_links/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + diff --git a/src/python/src/grpc/_links/_lonely_invocation_link_test.py b/src/python/src/grpc/_links/_lonely_invocation_link_test.py new file mode 100644 index 00000000000..3d629f43871 --- /dev/null +++ b/src/python/src/grpc/_links/_lonely_invocation_link_test.py @@ -0,0 +1,88 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""A test of invocation-side code unconnected to an RPC server.""" + +import unittest + +from grpc._adapter import _intermediary_low +from grpc._links import invocation +from grpc.framework.common import test_constants +from grpc.framework.interfaces.links import links +from grpc.framework.interfaces.links import test_cases +from grpc.framework.interfaces.links import test_utilities + +_NULL_BEHAVIOR = lambda unused_argument: None + + +class LonelyInvocationLinkTest(unittest.TestCase): + + def testUpAndDown(self): + channel = _intermediary_low.Channel('nonexistent:54321', None) + invocation_link = invocation.invocation_link(channel, 'nonexistent', {}, {}) + + invocation_link.start() + invocation_link.stop() + + def _test_lonely_invocation_with_termination(self, termination): + test_operation_id = object() + test_group = 'test package.Test Service' + test_method = 'test method' + invocation_link_mate = test_utilities.RecordingLink() + + channel = _intermediary_low.Channel('nonexistent:54321', None) + invocation_link = invocation.invocation_link( + channel, 'nonexistent', {(test_group, test_method): _NULL_BEHAVIOR}, + {(test_group, test_method): _NULL_BEHAVIOR}) + invocation_link.join_link(invocation_link_mate) + invocation_link.start() + + ticket = links.Ticket( + test_operation_id, 0, test_group, test_method, + links.Ticket.Subscription.FULL, test_constants.SHORT_TIMEOUT, 1, None, + None, None, None, None, termination) + invocation_link.accept_ticket(ticket) + invocation_link_mate.block_until_tickets_satisfy(test_cases.terminated) + + invocation_link.stop() + + self.assertIsNot( + invocation_link_mate.tickets()[-1].termination, + links.Ticket.Termination.COMPLETION) + + def testLonelyInvocationLinkWithCommencementTicket(self): + self._test_lonely_invocation_with_termination(None) + + def testLonelyInvocationLinkWithEntireTicket(self): + self._test_lonely_invocation_with_termination( + links.Ticket.Termination.COMPLETION) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/python/src/grpc/_links/_proto_scenarios.py b/src/python/src/grpc/_links/_proto_scenarios.py new file mode 100644 index 00000000000..ccf3c297824 --- /dev/null +++ b/src/python/src/grpc/_links/_proto_scenarios.py @@ -0,0 +1,261 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Test scenarios using protocol buffers.""" + +import abc +import threading + +from grpc._junkdrawer import math_pb2 + + +class ProtoScenario(object): + """An RPC test scenario using protocol buffers.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def group_and_method(self): + """Access the test group and method. + + Returns: + The test group and method as a pair. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_request(self, request): + """Serialize a request protocol buffer. + + Args: + request: A request protocol buffer. + + Returns: + The bytestring serialization of the given request protocol buffer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_request(self, request_bytestring): + """Deserialize a request protocol buffer. + + Args: + request_bytestring: The bytestring serialization of a request protocol + buffer. + + Returns: + The request protocol buffer deserialized from the given byte string. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_response(self, response): + """Serialize a response protocol buffer. + + Args: + response: A response protocol buffer. + + Returns: + The bytestring serialization of the given response protocol buffer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_response(self, response_bytestring): + """Deserialize a response protocol buffer. + + Args: + response_bytestring: The bytestring serialization of a response protocol + buffer. + + Returns: + The response protocol buffer deserialized from the given byte string. + """ + raise NotImplementedError() + + @abc.abstractmethod + def requests(self): + """Access the sequence of requests for this scenario. + + Returns: + A sequence of request protocol buffers. + """ + raise NotImplementedError() + + @abc.abstractmethod + def response_for_request(self, request): + """Access the response for a particular request. + + Args: + request: A request protocol buffer. + + Returns: + The response protocol buffer appropriate for the given request. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify_requests(self, experimental_requests): + """Verify the requests transmitted through the system under test. + + Args: + experimental_requests: The request protocol buffers transmitted through + the system under test. + + Returns: + True if the requests satisfy this test scenario; False otherwise. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify_responses(self, experimental_responses): + """Verify the responses transmitted through the system under test. + + Args: + experimental_responses: The response protocol buffers transmitted through + the system under test. + + Returns: + True if the responses satisfy this test scenario; False otherwise. + """ + raise NotImplementedError() + + +class EmptyScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + def group_and_method(self): + return 'math.Math', 'DivMany' + + def serialize_request(self, request): + raise ValueError('This should not be necessary to call!') + + def deserialize_request(self, request_bytestring): + raise ValueError('This should not be necessary to call!') + + def serialize_response(self, response): + raise ValueError('This should not be necessary to call!') + + def deserialize_response(self, response_bytestring): + raise ValueError('This should not be necessary to call!') + + def requests(self): + return () + + def response_for_request(self, request): + raise ValueError('This should not be necessary to call!') + + def verify_requests(self, experimental_requests): + return not experimental_requests + + def verify_responses(self, experimental_responses): + return not experimental_responses + + +class BidirectionallyUnaryScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + _DIVIDEND = 59 + _DIVISOR = 7 + _QUOTIENT = 8 + _REMAINDER = 3 + + _REQUEST = math_pb2.DivArgs(dividend=_DIVIDEND, divisor=_DIVISOR) + _RESPONSE = math_pb2.DivReply(quotient=_QUOTIENT, remainder=_REMAINDER) + + def group_and_method(self): + return 'math.Math', 'Div' + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, request_bytestring): + return math_pb2.DivArgs.FromString(request_bytestring) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, response_bytestring): + return math_pb2.DivReply.FromString(response_bytestring) + + def requests(self): + return [self._REQUEST] + + def response_for_request(self, request): + return self._RESPONSE + + def verify_requests(self, experimental_requests): + return tuple(experimental_requests) == (self._REQUEST,) + + def verify_responses(self, experimental_responses): + return tuple(experimental_responses) == (self._RESPONSE,) + + +class BidirectionallyStreamingScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + _STREAM_LENGTH = 200 + _REQUESTS = tuple( + math_pb2.DivArgs(dividend=59 + index, divisor=7 + index) + for index in range(_STREAM_LENGTH)) + + def __init__(self): + self._lock = threading.Lock() + self._responses = [] + + def group_and_method(self): + return 'math.Math', 'DivMany' + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, request_bytestring): + return math_pb2.DivArgs.FromString(request_bytestring) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, response_bytestring): + return math_pb2.DivReply.FromString(response_bytestring) + + def requests(self): + return self._REQUESTS + + def response_for_request(self, request): + quotient, remainder = divmod(request.dividend, request.divisor) + response = math_pb2.DivReply(quotient=quotient, remainder=remainder) + with self._lock: + self._responses.append(response) + return response + + def verify_requests(self, experimental_requests): + return tuple(experimental_requests) == self._REQUESTS + + def verify_responses(self, experimental_responses): + with self._lock: + return tuple(experimental_responses) == tuple(self._responses) diff --git a/src/python/src/grpc/_links/_transmission_test.py b/src/python/src/grpc/_links/_transmission_test.py new file mode 100644 index 00000000000..c5ef1edb253 --- /dev/null +++ b/src/python/src/grpc/_links/_transmission_test.py @@ -0,0 +1,226 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Tests transmission of tickets across gRPC-on-the-wire.""" + +import unittest + +from grpc._adapter import _intermediary_low +from grpc._links import _proto_scenarios +from grpc._links import invocation +from grpc._links import service +from grpc.framework.common import test_constants +from grpc.framework.interfaces.links import links +from grpc.framework.interfaces.links import test_cases +from grpc.framework.interfaces.links import test_utilities + +_IDENTITY = lambda x: x + + +class TransmissionTest(test_cases.TransmissionTest, unittest.TestCase): + + def create_transmitting_links(self): + service_link = service.service_link( + {self.group_and_method(): self.deserialize_request}, + {self.group_and_method(): self.serialize_response}) + port = service_link.add_port(0, None) + service_link.start() + channel = _intermediary_low.Channel('localhost:%d' % port, None) + invocation_link = invocation.invocation_link( + channel, 'localhost', + {self.group_and_method(): self.serialize_request}, + {self.group_and_method(): self.deserialize_response}) + invocation_link.start() + return invocation_link, service_link + + def destroy_transmitting_links(self, invocation_side_link, service_side_link): + invocation_side_link.stop() + service_side_link.stop_gracefully() + + def create_invocation_initial_metadata(self): + return ( + ('first invocation initial metadata key', 'just a string value'), + ('second invocation initial metadata key', '0123456789'), + ('third invocation initial metadata key-bin', '\x00\x57' * 100), + ) + + def create_invocation_terminal_metadata(self): + return None + + def create_service_initial_metadata(self): + return ( + ('first service initial metadata key', 'just another string value'), + ('second service initial metadata key', '9876543210'), + ('third service initial metadata key-bin', '\x00\x59\x02' * 100), + ) + + def create_service_terminal_metadata(self): + return ( + ('first service terminal metadata key', 'yet another string value'), + ('second service terminal metadata key', 'abcdefghij'), + ('third service terminal metadata key-bin', '\x00\x37' * 100), + ) + + def create_invocation_completion(self): + return None, None + + def create_service_completion(self): + return _intermediary_low.Code.OK, 'An exuberant test "details" message!' + + def assertMetadataEqual(self, original_metadata, transmitted_metadata): + self.assertSequenceEqual(original_metadata, transmitted_metadata) + + +class RoundTripTest(unittest.TestCase): + + def testZeroMessageRoundTrip(self): + test_operation_id = object() + test_group = 'test package.Test Group' + test_method = 'test method' + identity_transformation = {(test_group, test_method): _IDENTITY} + test_code = _intermediary_low.Code.OK + test_message = 'a test message' + + service_link = service.service_link( + identity_transformation, identity_transformation) + service_mate = test_utilities.RecordingLink() + service_link.join_link(service_mate) + port = service_link.add_port(0, None) + service_link.start() + channel = _intermediary_low.Channel('localhost:%d' % port, None) + invocation_link = invocation.invocation_link( + channel, 'localhost', identity_transformation, identity_transformation) + invocation_mate = test_utilities.RecordingLink() + invocation_link.join_link(invocation_mate) + invocation_link.start() + + invocation_ticket = links.Ticket( + test_operation_id, 0, test_group, test_method, + links.Ticket.Subscription.FULL, test_constants.LONG_TIMEOUT, None, None, + None, None, None, None, links.Ticket.Termination.COMPLETION) + invocation_link.accept_ticket(invocation_ticket) + service_mate.block_until_tickets_satisfy(test_cases.terminated) + + service_ticket = links.Ticket( + service_mate.tickets()[-1].operation_id, 0, None, None, None, None, + None, None, None, None, test_code, test_message, + links.Ticket.Termination.COMPLETION) + service_link.accept_ticket(service_ticket) + invocation_mate.block_until_tickets_satisfy(test_cases.terminated) + + invocation_link.stop() + service_link.stop_gracefully() + + self.assertIs( + service_mate.tickets()[-1].termination, + links.Ticket.Termination.COMPLETION) + self.assertIs( + invocation_mate.tickets()[-1].termination, + links.Ticket.Termination.COMPLETION) + + def _perform_scenario_test(self, scenario): + test_operation_id = object() + test_group, test_method = scenario.group_and_method() + test_code = _intermediary_low.Code.OK + test_message = 'a scenario test message' + + service_link = service.service_link( + {(test_group, test_method): scenario.deserialize_request}, + {(test_group, test_method): scenario.serialize_response}) + service_mate = test_utilities.RecordingLink() + service_link.join_link(service_mate) + port = service_link.add_port(0, None) + service_link.start() + channel = _intermediary_low.Channel('localhost:%d' % port, None) + invocation_link = invocation.invocation_link( + channel, 'localhost', + {(test_group, test_method): scenario.serialize_request}, + {(test_group, test_method): scenario.deserialize_response}) + invocation_mate = test_utilities.RecordingLink() + invocation_link.join_link(invocation_mate) + invocation_link.start() + + invocation_ticket = links.Ticket( + test_operation_id, 0, test_group, test_method, + links.Ticket.Subscription.FULL, test_constants.LONG_TIMEOUT, None, None, + None, None, None, None, None) + invocation_link.accept_ticket(invocation_ticket) + requests = scenario.requests() + for request_index, request in enumerate(requests): + request_ticket = links.Ticket( + test_operation_id, 1 + request_index, None, None, None, None, 1, None, + request, None, None, None, None) + invocation_link.accept_ticket(request_ticket) + service_mate.block_until_tickets_satisfy( + test_cases.at_least_n_payloads_received_predicate(1 + request_index)) + response_ticket = links.Ticket( + service_mate.tickets()[0].operation_id, request_index, None, None, + None, None, 1, None, scenario.response_for_request(request), None, + None, None, None) + service_link.accept_ticket(response_ticket) + invocation_mate.block_until_tickets_satisfy( + test_cases.at_least_n_payloads_received_predicate(1 + request_index)) + request_count = len(requests) + invocation_completion_ticket = links.Ticket( + test_operation_id, request_count + 1, None, None, None, None, None, + None, None, None, None, None, links.Ticket.Termination.COMPLETION) + invocation_link.accept_ticket(invocation_completion_ticket) + service_mate.block_until_tickets_satisfy(test_cases.terminated) + service_completion_ticket = links.Ticket( + service_mate.tickets()[0].operation_id, request_count, None, None, None, + None, None, None, None, None, test_code, test_message, + links.Ticket.Termination.COMPLETION) + service_link.accept_ticket(service_completion_ticket) + invocation_mate.block_until_tickets_satisfy(test_cases.terminated) + + invocation_link.stop() + service_link.stop_gracefully() + + observed_requests = tuple( + ticket.payload for ticket in service_mate.tickets() + if ticket.payload is not None) + observed_responses = tuple( + ticket.payload for ticket in invocation_mate.tickets() + if ticket.payload is not None) + self.assertTrue(scenario.verify_requests(observed_requests)) + self.assertTrue(scenario.verify_responses(observed_responses)) + + def testEmptyScenario(self): + self._perform_scenario_test(_proto_scenarios.EmptyScenario()) + + def testBidirectionallyUnaryScenario(self): + self._perform_scenario_test(_proto_scenarios.BidirectionallyUnaryScenario()) + + def testBidirectionallyStreamingScenario(self): + self._perform_scenario_test( + _proto_scenarios.BidirectionallyStreamingScenario()) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/src/grpc/_links/invocation.py b/src/python/src/grpc/_links/invocation.py new file mode 100644 index 00000000000..0058ae91f87 --- /dev/null +++ b/src/python/src/grpc/_links/invocation.py @@ -0,0 +1,363 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""The RPC-invocation-side bridge between RPC Framework and GRPC-on-the-wire.""" + +import abc +import enum +import logging +import threading +import time + +from grpc._adapter import _intermediary_low +from grpc.framework.foundation import activated +from grpc.framework.foundation import logging_pool +from grpc.framework.foundation import relay +from grpc.framework.interfaces.links import links + + +@enum.unique +class _Read(enum.Enum): + AWAITING_METADATA = 'awaiting metadata' + READING = 'reading' + AWAITING_ALLOWANCE = 'awaiting allowance' + CLOSED = 'closed' + + +@enum.unique +class _HighWrite(enum.Enum): + OPEN = 'open' + CLOSED = 'closed' + + +@enum.unique +class _LowWrite(enum.Enum): + OPEN = 'OPEN' + ACTIVE = 'ACTIVE' + CLOSED = 'CLOSED' + + +class _RPCState(object): + + def __init__( + self, call, request_serializer, response_deserializer, sequence_number, + read, allowance, high_write, low_write): + self.call = call + self.request_serializer = request_serializer + self.response_deserializer = response_deserializer + self.sequence_number = sequence_number + self.read = read + self.allowance = allowance + self.high_write = high_write + self.low_write = low_write + + +class _Kernel(object): + + def __init__( + self, channel, host, request_serializers, response_deserializers, + ticket_relay): + self._lock = threading.Lock() + self._channel = channel + self._host = host + self._request_serializers = request_serializers + self._response_deserializers = response_deserializers + self._relay = ticket_relay + + self._completion_queue = None + self._rpc_states = None + self._pool = None + + def _on_write_event(self, operation_id, unused_event, rpc_state): + if rpc_state.high_write is _HighWrite.CLOSED: + rpc_state.call.complete(operation_id) + rpc_state.low_write = _LowWrite.CLOSED + else: + ticket = links.Ticket( + operation_id, rpc_state.sequence_number, None, None, None, None, 1, + None, None, None, None, None, None) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + rpc_state.low_write = _LowWrite.OPEN + + def _on_read_event(self, operation_id, event, rpc_state): + if event.bytes is None: + rpc_state.read = _Read.CLOSED + else: + if 0 < rpc_state.allowance: + rpc_state.allowance -= 1 + rpc_state.call.read(operation_id) + else: + rpc_state.read = _Read.AWAITING_ALLOWANCE + ticket = links.Ticket( + operation_id, rpc_state.sequence_number, None, None, None, None, None, + None, rpc_state.response_deserializer(event.bytes), None, None, None, + None) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + + def _on_metadata_event(self, operation_id, event, rpc_state): + rpc_state.allowance -= 1 + rpc_state.call.read(operation_id) + rpc_state.read = _Read.READING + ticket = links.Ticket( + operation_id, rpc_state.sequence_number, None, None, + links.Ticket.Subscription.FULL, None, None, event.metadata, None, None, + None, None, None) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + + def _on_finish_event(self, operation_id, event, rpc_state): + self._rpc_states.pop(operation_id, None) + if event.status.code is _intermediary_low.Code.OK: + termination = links.Ticket.Termination.COMPLETION + elif event.status.code is _intermediary_low.Code.CANCELLED: + termination = links.Ticket.Termination.CANCELLATION + elif event.status.code is _intermediary_low.Code.DEADLINE_EXCEEDED: + termination = links.Ticket.Termination.EXPIRATION + else: + termination = links.Ticket.Termination.TRANSMISSION_FAILURE + ticket = links.Ticket( + operation_id, rpc_state.sequence_number, None, None, None, None, None, + None, None, event.metadata, event.status.code, event.status.details, + termination) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + + def _spin(self, completion_queue): + while True: + event = completion_queue.get(None) + if event.kind is _intermediary_low.Event.Kind.STOP: + return + operation_id = event.tag + with self._lock: + if self._completion_queue is None: + continue + rpc_state = self._rpc_states.get(operation_id) + if rpc_state is not None: + if event.kind is _intermediary_low.Event.Kind.WRITE_ACCEPTED: + self._on_write_event(operation_id, event, rpc_state) + elif event.kind is _intermediary_low.Event.Kind.METADATA_ACCEPTED: + self._on_metadata_event(operation_id, event, rpc_state) + elif event.kind is _intermediary_low.Event.Kind.READ_ACCEPTED: + self._on_read_event(operation_id, event, rpc_state) + elif event.kind is _intermediary_low.Event.Kind.FINISH: + self._on_finish_event(operation_id, event, rpc_state) + elif event.kind is _intermediary_low.Event.Kind.COMPLETE_ACCEPTED: + pass + else: + logging.error('Illegal RPC event! %s', (event,)) + + def _invoke( + self, operation_id, group, method, initial_metadata, payload, termination, + timeout, allowance): + """Invoke an RPC. + + Args: + operation_id: Any object to be used as an operation ID for the RPC. + group: The group to which the RPC method belongs. + method: The RPC method name. + initial_metadata: The initial metadata object for the RPC. + payload: A payload object for the RPC or None if no payload was given at + invocation-time. + termination: A links.Ticket.Termination value or None indicated whether or + not more writes will follow from this side of the RPC. + timeout: A duration of time in seconds to allow for the RPC. + allowance: The number of payloads (beyond the free first one) that the + local ticket exchange mate has granted permission to be read. + """ + if termination is links.Ticket.Termination.COMPLETION: + high_write = _HighWrite.CLOSED + elif termination is None: + high_write = _HighWrite.OPEN + else: + return + + request_serializer = self._request_serializers.get((group, method)) + response_deserializer = self._response_deserializers.get((group, method)) + if request_serializer is None or response_deserializer is None: + cancellation_ticket = links.Ticket( + operation_id, 0, None, None, None, None, None, None, None, None, None, + None, links.Ticket.Termination.CANCELLATION) + self._relay.add_value(cancellation_ticket) + return + + call = _intermediary_low.Call( + self._channel, self._completion_queue, '/%s/%s' % (group, method), + self._host, time.time() + timeout) + if initial_metadata is not None: + for metadata_key, metadata_value in initial_metadata: + call.add_metadata(metadata_key, metadata_value) + call.invoke(self._completion_queue, operation_id, operation_id) + if payload is None: + if high_write is _HighWrite.CLOSED: + call.complete(operation_id) + low_write = _LowWrite.CLOSED + else: + low_write = _LowWrite.OPEN + else: + call.write(request_serializer(payload), operation_id) + low_write = _LowWrite.ACTIVE + self._rpc_states[operation_id] = _RPCState( + call, request_serializer, response_deserializer, 0, + _Read.AWAITING_METADATA, 1 if allowance is None else (1 + allowance), + high_write, low_write) + + def _advance(self, operation_id, rpc_state, payload, termination, allowance): + if payload is not None: + rpc_state.call.write(rpc_state.request_serializer(payload), operation_id) + rpc_state.low_write = _LowWrite.ACTIVE + + if allowance is not None: + if rpc_state.read is _Read.AWAITING_ALLOWANCE: + rpc_state.allowance += allowance - 1 + rpc_state.call.read(operation_id) + rpc_state.read = _Read.READING + else: + rpc_state.allowance += allowance + + if termination is links.Ticket.Termination.COMPLETION: + rpc_state.high_write = _HighWrite.CLOSED + if rpc_state.low_write is _LowWrite.OPEN: + rpc_state.call.complete(operation_id) + rpc_state.low_write = _LowWrite.CLOSED + elif termination is not None: + rpc_state.call.cancel() + + def add_ticket(self, ticket): + with self._lock: + if self._completion_queue is None: + return + if ticket.sequence_number == 0: + self._invoke( + ticket.operation_id, ticket.group, ticket.method, + ticket.initial_metadata, ticket.payload, ticket.termination, + ticket.timeout, ticket.allowance) + else: + rpc_state = self._rpc_states.get(ticket.operation_id) + if rpc_state is not None: + self._advance( + ticket.operation_id, rpc_state, ticket.payload, + ticket.termination, ticket.allowance) + + def start(self): + """Starts this object. + + This method must be called before attempting to exchange tickets with this + object. + """ + with self._lock: + self._completion_queue = _intermediary_low.CompletionQueue() + self._rpc_states = {} + self._pool = logging_pool.pool(1) + self._pool.submit(self._spin, self._completion_queue) + + def stop(self): + """Stops this object. + + This method must be called for proper termination of this object, and no + attempts to exchange tickets with this object may be made after this method + has been called. + """ + with self._lock: + self._completion_queue.stop() + self._completion_queue = None + pool = self._pool + self._pool = None + self._rpc_states = None + pool.shutdown(wait=True) + + +class InvocationLink(links.Link, activated.Activated): + """A links.Link for use on the invocation-side of a gRPC connection. + + Implementations of this interface are only valid for use when activated. + """ + __metaclass__ = abc.ABCMeta + + +class _InvocationLink(InvocationLink): + + def __init__( + self, channel, host, request_serializers, response_deserializers): + self._relay = relay.relay(None) + self._kernel = _Kernel( + channel, host, request_serializers, response_deserializers, self._relay) + + def _start(self): + self._relay.start() + self._kernel.start() + return self + + def _stop(self): + self._kernel.stop() + self._relay.stop() + + def accept_ticket(self, ticket): + """See links.Link.accept_ticket for specification.""" + self._kernel.add_ticket(ticket) + + def join_link(self, link): + """See links.Link.join_link for specification.""" + self._relay.set_behavior(link.accept_ticket) + + def __enter__(self): + """See activated.Activated.__enter__ for specification.""" + return self._start() + + def __exit__(self, exc_type, exc_val, exc_tb): + """See activated.Activated.__exit__ for specification.""" + self._stop() + return False + + def start(self): + """See activated.Activated.start for specification.""" + return self._start() + + def stop(self): + """See activated.Activated.stop for specification.""" + self._stop() + + +def invocation_link(channel, host, request_serializers, response_deserializers): + """Creates an InvocationLink. + + Args: + channel: A channel for use by the link. + host: The host to specify when invoking RPCs. + request_serializers: A dict from group-method pair to request object + serialization behavior. + response_deserializers: A dict from group-method pair to response object + deserialization behavior. + + Returns: + An InvocationLink. + """ + return _InvocationLink( + channel, host, request_serializers, response_deserializers) diff --git a/src/python/src/grpc/_links/service.py b/src/python/src/grpc/_links/service.py new file mode 100644 index 00000000000..7783e918248 --- /dev/null +++ b/src/python/src/grpc/_links/service.py @@ -0,0 +1,402 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""The RPC-service-side bridge between RPC Framework and GRPC-on-the-wire.""" + +import abc +import enum +import logging +import threading +import time + +from grpc._adapter import _intermediary_low +from grpc.framework.foundation import logging_pool +from grpc.framework.foundation import relay +from grpc.framework.interfaces.links import links + + +@enum.unique +class _Read(enum.Enum): + READING = 'reading' + AWAITING_ALLOWANCE = 'awaiting allowance' + CLOSED = 'closed' + + +@enum.unique +class _HighWrite(enum.Enum): + OPEN = 'open' + CLOSED = 'closed' + + +@enum.unique +class _LowWrite(enum.Enum): + """The possible categories of low-level write state.""" + + OPEN = 'OPEN' + ACTIVE = 'ACTIVE' + CLOSED = 'CLOSED' + + +class _RPCState(object): + + def __init__( + self, request_deserializer, response_serializer, sequence_number, read, + allowance, high_write, low_write, premetadataed, terminal_metadata, code, + message): + self.request_deserializer = request_deserializer + self.response_serializer = response_serializer + self.sequence_number = sequence_number + self.read = read + self.allowance = allowance + self.high_write = high_write + self.low_write = low_write + self.premetadataed = premetadataed + self.terminal_metadata = terminal_metadata + self.code = code + self.message = message + + +def _metadatafy(call, metadata): + for metadata_key, metadata_value in metadata: + call.add_metadata(metadata_key, metadata_value) + + +class _Kernel(object): + + def __init__(self, request_deserializers, response_serializers, ticket_relay): + self._lock = threading.Lock() + self._request_deserializers = request_deserializers + self._response_serializers = response_serializers + self._relay = ticket_relay + + self._completion_queue = None + self._server = None + self._rpc_states = {} + self._pool = None + + def _on_service_acceptance_event(self, event, server): + server.service(None) + + service_acceptance = event.service_acceptance + call = service_acceptance.call + call.accept(self._completion_queue, call) + try: + group, method = service_acceptance.method.split('/')[1:3] + except ValueError: + logging.info('Illegal path "%s"!', service_acceptance.method) + return + request_deserializer = self._request_deserializers.get((group, method)) + response_serializer = self._response_serializers.get((group, method)) + if request_deserializer is None or response_serializer is None: + # TODO(nathaniel): Terminate the RPC with code NOT_FOUND. + call.cancel() + return + + call.read(call) + self._rpc_states[call] = _RPCState( + request_deserializer, response_serializer, 1, _Read.READING, 0, + _HighWrite.OPEN, _LowWrite.OPEN, False, None, None, None) + ticket = links.Ticket( + call, 0, group, method, links.Ticket.Subscription.FULL, + service_acceptance.deadline - time.time(), None, event.metadata, None, + None, None, None, None) + self._relay.add_value(ticket) + + def _on_read_event(self, event): + call = event.tag + rpc_state = self._rpc_states.get(call, None) + if rpc_state is None: + return + + if event.bytes is None: + rpc_state.read = _Read.CLOSED + payload = None + termination = links.Ticket.Termination.COMPLETION + else: + if 0 < rpc_state.allowance: + rpc_state.allowance -= 1 + call.read(call) + else: + rpc_state.read = _Read.AWAITING_ALLOWANCE + payload = rpc_state.request_deserializer(event.bytes) + termination = None + ticket = links.Ticket( + call, rpc_state.sequence_number, None, None, None, None, None, None, + payload, None, None, None, termination) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + + def _on_write_event(self, event): + call = event.tag + rpc_state = self._rpc_states.get(call, None) + if rpc_state is None: + return + + if rpc_state.high_write is _HighWrite.CLOSED: + if rpc_state.terminal_metadata is not None: + _metadatafy(call, rpc_state.terminal_metadata) + call.status( + _intermediary_low.Status(rpc_state.code, rpc_state.message), call) + rpc_state.low_write = _LowWrite.CLOSED + else: + ticket = links.Ticket( + call, rpc_state.sequence_number, None, None, None, None, 1, None, + None, None, None, None, None) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + rpc_state.low_write = _LowWrite.OPEN + + def _on_finish_event(self, event): + call = event.tag + rpc_state = self._rpc_states.pop(call, None) + if rpc_state is None: + return + code = event.status.code + if code is _intermediary_low.Code.OK: + return + + if code is _intermediary_low.Code.CANCELLED: + termination = links.Ticket.Termination.CANCELLATION + elif code is _intermediary_low.Code.DEADLINE_EXCEEDED: + termination = links.Ticket.Termination.EXPIRATION + else: + termination = links.Ticket.Termination.TRANSMISSION_FAILURE + ticket = links.Ticket( + call, rpc_state.sequence_number, None, None, None, None, None, None, + None, None, None, None, termination) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + + def _spin(self, completion_queue, server): + while True: + event = completion_queue.get(None) + if event.kind is _intermediary_low.Event.Kind.STOP: + return + with self._lock: + if self._server is None: + continue + elif event.kind is _intermediary_low.Event.Kind.SERVICE_ACCEPTED: + self._on_service_acceptance_event(event, server) + elif event.kind is _intermediary_low.Event.Kind.READ_ACCEPTED: + self._on_read_event(event) + elif event.kind is _intermediary_low.Event.Kind.WRITE_ACCEPTED: + self._on_write_event(event) + elif event.kind is _intermediary_low.Event.Kind.COMPLETE_ACCEPTED: + pass + elif event.kind is _intermediary_low.Event.Kind.FINISH: + self._on_finish_event(event) + else: + logging.error('Illegal event! %s', (event,)) + + def add_ticket(self, ticket): + with self._lock: + if self._server is None: + return + call = ticket.operation_id + rpc_state = self._rpc_states.get(call) + if rpc_state is None: + return + + if ticket.initial_metadata is not None: + _metadatafy(call, ticket.initial_metadata) + call.premetadata() + rpc_state.premetadataed = True + elif not rpc_state.premetadataed: + if (ticket.terminal_metadata is not None or + ticket.payload is not None or + ticket.termination is links.Ticket.Termination.COMPLETION or + ticket.code is not None or + ticket.message is not None): + call.premetadata() + rpc_state.premetadataed = True + + if ticket.allowance is not None: + if rpc_state.read is _Read.AWAITING_ALLOWANCE: + rpc_state.allowance += ticket.allowance - 1 + call.read(call) + rpc_state.read = _Read.READING + else: + rpc_state.allowance += ticket.allowance + + if ticket.payload is not None: + call.write(rpc_state.response_serializer(ticket.payload), call) + rpc_state.low_write = _LowWrite.ACTIVE + + if ticket.terminal_metadata is not None: + rpc_state.terminal_metadata = ticket.terminal_metadata + if ticket.code is not None: + rpc_state.code = ticket.code + if ticket.message is not None: + rpc_state.message = ticket.message + + if ticket.termination is links.Ticket.Termination.COMPLETION: + rpc_state.high_write = _HighWrite.CLOSED + if rpc_state.low_write is _LowWrite.OPEN: + if rpc_state.terminal_metadata is not None: + _metadatafy(call, rpc_state.terminal_metadata) + status = _intermediary_low.Status( + _intermediary_low.Code.OK + if rpc_state.code is None else rpc_state.code, + '' if rpc_state.message is None else rpc_state.message) + call.status(status, call) + rpc_state.low_write = _LowWrite.CLOSED + elif ticket.termination is not None: + call.cancel() + self._rpc_states.pop(call, None) + + def add_port(self, port, server_credentials): + with self._lock: + address = '[::]:%d' % port + if self._server is None: + self._completion_queue = _intermediary_low.CompletionQueue() + self._server = _intermediary_low.Server(self._completion_queue) + if server_credentials is None: + return self._server.add_http2_addr(address) + else: + return self._server.add_secure_http2_addr(address, server_credentials) + + def start(self): + with self._lock: + if self._server is None: + self._completion_queue = _intermediary_low.CompletionQueue() + self._server = _intermediary_low.Server(self._completion_queue) + self._pool = logging_pool.pool(1) + self._pool.submit(self._spin, self._completion_queue, self._server) + self._server.start() + self._server.service(None) + + def graceful_stop(self): + with self._lock: + self._server.stop() + self._server = None + self._completion_queue.stop() + self._completion_queue = None + pool = self._pool + self._pool = None + self._rpc_states = None + pool.shutdown(wait=True) + + def immediate_stop(self): + # TODO(nathaniel): Implementation. + raise NotImplementedError( + 'TODO(nathaniel): after merge of rewritten lower layers') + + +class ServiceLink(links.Link): + """A links.Link for use on the service-side of a gRPC connection. + + Implementations of this interface are only valid for use between calls to + their start method and one of their stop methods. + """ + + @abc.abstractmethod + def add_port(self, port, server_credentials): + """Adds a port on which to service RPCs after this link has been started. + + Args: + port: The port on which to service RPCs, or zero to request that a port be + automatically selected and used. + server_credentials: A ServerCredentials object, or None for insecure + service. + + Returns: + A port on which RPCs will be serviced after this link has been started. + """ + raise NotImplementedError() + + @abc.abstractmethod + def start(self): + """Starts this object. + + This method must be called before attempting to use this Link in ticket + exchange. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stop_gracefully(self): + """Stops this link. + + New RPCs will be rejected as soon as this method is called, but ongoing RPCs + will be allowed to continue until they terminate. This method blocks until + all RPCs have terminated. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stop_immediately(self): + """Stops this link. + + All in-progress RPCs will be terminated immediately. + """ + raise NotImplementedError() + + +class _ServiceLink(ServiceLink): + + def __init__(self, request_deserializers, response_serializers): + self._relay = relay.relay(None) + self._kernel = _Kernel( + request_deserializers, response_serializers, self._relay) + + def accept_ticket(self, ticket): + self._kernel.add_ticket(ticket) + + def join_link(self, link): + self._relay.set_behavior(link.accept_ticket) + + def add_port(self, port, server_credentials): + return self._kernel.add_port(port, server_credentials) + + def start(self): + self._relay.start() + return self._kernel.start() + + def stop_gracefully(self): + self._kernel.graceful_stop() + self._relay.stop() + + def stop_immediately(self): + self._kernel.immediate_stop() + self._relay.stop() + + +def service_link(request_deserializers, response_serializers): + """Creates a ServiceLink. + + Args: + request_deserializers: A dict from group-method pair to request object + deserialization behavior. + response_serializers: A dict from group-method pair to response ojbect + serialization behavior. + + Returns: + A ServiceLink. + """ + return _ServiceLink(request_deserializers, response_serializers) diff --git a/src/python/src/grpc/framework/common/test_constants.py b/src/python/src/grpc/framework/common/test_constants.py new file mode 100644 index 00000000000..237b8754ed2 --- /dev/null +++ b/src/python/src/grpc/framework/common/test_constants.py @@ -0,0 +1,37 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Constants shared among tests throughout RPC Framework.""" + +# Value for maximum duration in seconds of RPCs that may time out as part of a +# test. +SHORT_TIMEOUT = 4 +# Absurdly large value for maximum duration in seconds for should-not-time-out +# RPCs made during tests. +LONG_TIMEOUT = 3000 diff --git a/src/python/src/grpc/framework/common/test_control.py b/src/python/src/grpc/framework/common/test_control.py new file mode 100644 index 00000000000..3960c4e6495 --- /dev/null +++ b/src/python/src/grpc/framework/common/test_control.py @@ -0,0 +1,87 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Code for instructing systems under test to block or fail.""" + +import abc +import contextlib +import threading + + +class Control(object): + """An object that accepts program control from a system under test. + + Systems under test passed a Control should call its control() method + frequently during execution. The control() method may block, raise an + exception, or do nothing, all according to the enclosing test's desire for + the system under test to simulate hanging, failing, or functioning. + """ + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def control(self): + """Potentially does anything.""" + raise NotImplementedError() + + +class PauseFailControl(Control): + """A Control that can be used to pause or fail code under control.""" + + def __init__(self): + self._condition = threading.Condition() + self._paused = False + self._fail = False + + def control(self): + with self._condition: + if self._fail: + raise ValueError() + + while self._paused: + self._condition.wait() + + @contextlib.contextmanager + def pause(self): + """Pauses code under control while controlling code is in context.""" + with self._condition: + self._paused = True + yield + with self._condition: + self._paused = False + self._condition.notify_all() + + @contextlib.contextmanager + def fail(self): + """Fails code under control while controlling code is in context.""" + with self._condition: + self._fail = True + yield + with self._condition: + self._fail = False diff --git a/src/python/src/grpc/framework/common/test_coverage.py b/src/python/src/grpc/framework/common/test_coverage.py new file mode 100644 index 00000000000..a7ed3582c40 --- /dev/null +++ b/src/python/src/grpc/framework/common/test_coverage.py @@ -0,0 +1,116 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Governs coverage for tests of RPCs throughout RPC Framework.""" + +import abc + +# This code is designed for use with the unittest module. +# pylint: disable=invalid-name + + +class Coverage(object): + """Specification of test coverage.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def testSuccessfulUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSequentialInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testParallelInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedStreamRequestStreamResponse(self): + raise NotImplementedError() diff --git a/src/python/src/grpc/framework/foundation/relay.py b/src/python/src/grpc/framework/foundation/relay.py new file mode 100644 index 00000000000..9c239465520 --- /dev/null +++ b/src/python/src/grpc/framework/foundation/relay.py @@ -0,0 +1,175 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Implementations of in-order work deference.""" + +import abc +import enum +import threading + +from grpc.framework.foundation import activated +from grpc.framework.foundation import logging_pool + +_NULL_BEHAVIOR = lambda unused_value: None + + +class Relay(object): + """Performs work submitted to it in another thread. + + Performs work in the order in which work was submitted to it; otherwise there + would be no reason to use an implementation of this interface instead of a + thread pool. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def add_value(self, value): + """Adds a value to be passed to the behavior registered with this Relay. + + Args: + value: A value that will be passed to a call made in another thread to the + behavior registered with this Relay. + """ + raise NotImplementedError() + + @abc.abstractmethod + def set_behavior(self, behavior): + """Sets the behavior that this Relay should call when passed values. + + Args: + behavior: The behavior that this Relay should call in another thread when + passed a value, or None to have passed values ignored. + """ + raise NotImplementedError() + + +class _PoolRelay(activated.Activated, Relay): + + @enum.unique + class _State(enum.Enum): + INACTIVE = 'inactive' + IDLE = 'idle' + SPINNING = 'spinning' + + def __init__(self, pool, behavior): + self._condition = threading.Condition() + self._pool = pool + self._own_pool = pool is None + self._state = _PoolRelay._State.INACTIVE + self._activated = False + self._spinning = False + self._values = [] + self._behavior = _NULL_BEHAVIOR if behavior is None else behavior + + def _spin(self, behavior, value): + while True: + behavior(value) + with self._condition: + if self._values: + value = self._values.pop(0) + behavior = self._behavior + else: + self._state = _PoolRelay._State.IDLE + self._condition.notify_all() + break + + def add_value(self, value): + with self._condition: + if self._state is _PoolRelay._State.INACTIVE: + raise ValueError('add_value not valid on inactive Relay!') + elif self._state is _PoolRelay._State.IDLE: + self._pool.submit(self._spin, self._behavior, value) + self._state = _PoolRelay._State.SPINNING + else: + self._values.append(value) + + def set_behavior(self, behavior): + with self._condition: + self._behavior = _NULL_BEHAVIOR if behavior is None else behavior + + def _start(self): + with self._condition: + self._state = _PoolRelay._State.IDLE + if self._own_pool: + self._pool = logging_pool.pool(1) + return self + + def _stop(self): + with self._condition: + while self._state is _PoolRelay._State.SPINNING: + self._condition.wait() + if self._own_pool: + self._pool.shutdown(wait=True) + self._state = _PoolRelay._State.INACTIVE + + def __enter__(self): + return self._start() + + def __exit__(self, exc_type, exc_val, exc_tb): + self._stop() + return False + + def start(self): + return self._start() + + def stop(self): + self._stop() + + +def relay(behavior): + """Creates a Relay. + + Args: + behavior: The behavior to be called by the created Relay, or None to have + passed values dropped until a different behavior is given to the returned + Relay later. + + Returns: + An object that is both an activated.Activated and a Relay. The object is + only valid for use as a Relay when activated. + """ + return _PoolRelay(None, behavior) + + +def pool_relay(pool, behavior): + """Creates a Relay that uses a given thread pool. + + This object will make use of at most one thread in the given pool. + + Args: + pool: A futures.ThreadPoolExecutor for use by the created Relay. + behavior: The behavior to be called by the created Relay, or None to have + passed values dropped until a different behavior is given to the returned + Relay later. + + Returns: + An object that is both an activated.Activated and a Relay. The object is + only valid for use as a Relay when activated. + """ + return _PoolRelay(pool, behavior) diff --git a/src/python/src/grpc/framework/interfaces/__init__.py b/src/python/src/grpc/framework/interfaces/__init__.py new file mode 100644 index 00000000000..70865191060 --- /dev/null +++ b/src/python/src/grpc/framework/interfaces/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + diff --git a/src/python/src/grpc/framework/interfaces/links/__init__.py b/src/python/src/grpc/framework/interfaces/links/__init__.py new file mode 100644 index 00000000000..70865191060 --- /dev/null +++ b/src/python/src/grpc/framework/interfaces/links/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + diff --git a/src/python/src/grpc/framework/interfaces/links/links.py b/src/python/src/grpc/framework/interfaces/links/links.py new file mode 100644 index 00000000000..5ebbac8a6f2 --- /dev/null +++ b/src/python/src/grpc/framework/interfaces/links/links.py @@ -0,0 +1,124 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""The low-level ticket-exchanging-links interface of RPC Framework.""" + +import abc +import collections +import enum + + +class Ticket( + collections.namedtuple( + 'Ticket', + ['operation_id', 'sequence_number', 'group', 'method', 'subscription', + 'timeout', 'allowance', 'initial_metadata', 'payload', + 'terminal_metadata', 'code', 'message', 'termination'])): + """A sum type for all values sent from a front to a back. + + Attributes: + operation_id: A unique-with-respect-to-equality hashable object identifying + a particular operation. + sequence_number: A zero-indexed integer sequence number identifying the + ticket's place in the stream of tickets sent in one direction for the + particular operation. + group: The group to which the method of the operation belongs. Must be + present in the first ticket from invocation side to service side. Ignored + for all other tickets exchanged during the operation. + method: The name of an operation. Must be present in the first ticket from + invocation side to service side. Ignored for all other tickets exchanged + during the operation. + subscription: A Subscription value describing the interest one side has in + receiving information from the other side. Must be present in the first + ticket from either side. Ignored for all other tickets exchanged during + the operation. + timeout: A nonzero length of time (measured from the beginning of the + operation) to allow for the entire operation. Must be present in the first + ticket from invocation side to service side. Optional for all other + tickets exchanged during the operation. Receipt of a value from the other + side of the operation indicates the value in use by that side. Setting a + value on a later ticket allows either side to request time extensions (or + even time reductions!) on in-progress operations. + allowance: A positive integer granting permission for a number of payloads + to be transmitted to the communicating side of the operation, or None if + no additional allowance is being granted with this ticket. + initial_metadata: An optional metadata value communicated from one side to + the other at the beginning of the operation. May be non-None in at most + one ticket from each side. Any non-None value must appear no later than + the first payload value. + payload: A customer payload object. May be None. + terminal_metadata: A metadata value comminicated from one side to the other + at the end of the operation. May be non-None in the same ticket as + the code and message, but must be None for all earlier tickets. + code: A value communicated at operation completion. May be None. + message: A value communicated at operation completion. May be None. + termination: A Termination value describing the end of the operation, or + None if the operation has not yet terminated. If set, no further tickets + may be sent in the same direction. + """ + + @enum.unique + class Subscription(enum.Enum): + """Identifies the level of subscription of a side of an operation.""" + + NONE = 'none' + TERMINATION = 'termination' + FULL = 'full' + + @enum.unique + class Termination(enum.Enum): + """Identifies the termination of an operation.""" + + COMPLETION = 'completion' + CANCELLATION = 'cancellation' + EXPIRATION = 'expiration' + LOCAL_SHUTDOWN = 'local shutdown' + RECEPTION_FAILURE = 'reception failure' + TRANSMISSION_FAILURE = 'transmission failure' + LOCAL_FAILURE = 'local failure' + REMOTE_FAILURE = 'remote failure' + + +class Link(object): + """Accepts and emits tickets.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def accept_ticket(self, ticket): + """Accept a Ticket. + + Args: + ticket: Any Ticket. + """ + raise NotImplementedError() + + @abc.abstractmethod + def join_link(self, link): + """Mates this object with a peer with which it will exchange tickets.""" + raise NotImplementedError() diff --git a/src/python/src/grpc/framework/interfaces/links/test_cases.py b/src/python/src/grpc/framework/interfaces/links/test_cases.py new file mode 100644 index 00000000000..3ac212ebdfb --- /dev/null +++ b/src/python/src/grpc/framework/interfaces/links/test_cases.py @@ -0,0 +1,332 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Tests of the links interface of RPC Framework.""" + +# unittest is referenced from specification in this module. +import abc +import unittest # pylint: disable=unused-import + +from grpc.framework.common import test_constants +from grpc.framework.interfaces.links import links +from grpc.framework.interfaces.links import test_utilities + + +def at_least_n_payloads_received_predicate(n): + def predicate(ticket_sequence): + payload_count = 0 + for ticket in ticket_sequence: + if ticket.payload is not None: + payload_count += 1 + if n <= payload_count: + return True + else: + return False + return predicate + + +def terminated(ticket_sequence): + return ticket_sequence and ticket_sequence[-1].termination is not None + +_TRANSMISSION_GROUP = 'test.Group' +_TRANSMISSION_METHOD = 'TestMethod' + + +class TransmissionTest(object): + """Tests ticket transmission between two connected links. + + This class must be mixed into a unittest.TestCase that implements the abstract + methods it provides. + """ + __metaclass__ = abc.ABCMeta + + # This is a unittest.TestCase mix-in. + # pylint: disable=invalid-name + + @abc.abstractmethod + def create_transmitting_links(self): + """Creates two connected links for use in this test. + + Returns: + Two links.Links, the first of which will be used on the invocation side + of RPCs and the second of which will be used on the service side of + RPCs. + """ + raise NotImplementedError() + + @abc.abstractmethod + def destroy_transmitting_links(self, invocation_side_link, service_side_link): + """Destroys the two connected links created for this test. + + + Args: + invocation_side_link: The link used on the invocation side of RPCs in + this test. + service_side_link: The link used on the service side of RPCs in this + test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_invocation_initial_metadata(self): + """Creates a value for use as invocation-side initial metadata. + + Returns: + A metadata value appropriate for use as invocation-side initial metadata + or None if invocation-side initial metadata transmission is not + supported by the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_invocation_terminal_metadata(self): + """Creates a value for use as invocation-side terminal metadata. + + Returns: + A metadata value appropriate for use as invocation-side terminal + metadata or None if invocation-side terminal metadata transmission is + not supported by the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_service_initial_metadata(self): + """Creates a value for use as service-side initial metadata. + + Returns: + A metadata value appropriate for use as service-side initial metadata or + None if service-side initial metadata transmission is not supported by + the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_service_terminal_metadata(self): + """Creates a value for use as service-side terminal metadata. + + Returns: + A metadata value appropriate for use as service-side terminal metadata or + None if service-side terminal metadata transmission is not supported by + the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_invocation_completion(self): + """Creates values for use as invocation-side code and message. + + Returns: + An invocation-side code value and an invocation-side message value. + Either or both may be None if invocation-side code and/or + invocation-side message transmission is not supported by the links + under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_service_completion(self): + """Creates values for use as service-side code and message. + + Returns: + A service-side code value and a service-side message value. Either or + both may be None if service-side code and/or service-side message + transmission is not supported by the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def assertMetadataEqual(self, original_metadata, transmitted_metadata): + """Asserts that two metadata objects are equal. + + Args: + original_metadata: A metadata object used in this test. + transmitted_metadata: A metadata object obtained after transmission + through the system under test. + + Raises: + AssertionError: if the two metadata objects are not equal. + """ + raise NotImplementedError() + + def group_and_method(self): + """Returns the group and method used in this test case. + + Returns: + A pair of the group and method used in this test case. + """ + return _TRANSMISSION_GROUP, _TRANSMISSION_METHOD + + def serialize_request(self, request): + """Serializes a request value used in this test case. + + Args: + request: A request value created by this test case. + + Returns: + A bytestring that is the serialization of the given request. + """ + return request + + def deserialize_request(self, serialized_request): + """Deserializes a request value used in this test case. + + Args: + serialized_request: A bytestring that is the serialization of some request + used in this test case. + + Returns: + The request value encoded by the given bytestring. + """ + return serialized_request + + def serialize_response(self, response): + """Serializes a response value used in this test case. + + Args: + response: A response value created by this test case. + + Returns: + A bytestring that is the serialization of the given response. + """ + return response + + def deserialize_response(self, serialized_response): + """Deserializes a response value used in this test case. + + Args: + serialized_response: A bytestring that is the serialization of some + response used in this test case. + + Returns: + The response value encoded by the given bytestring. + """ + return serialized_response + + def _assert_is_valid_metadata_payload_sequence( + self, ticket_sequence, payloads, initial_metadata, terminal_metadata): + initial_metadata_seen = False + seen_payloads = [] + terminal_metadata_seen = False + + for ticket in ticket_sequence: + if ticket.initial_metadata is not None: + self.assertFalse(initial_metadata_seen) + self.assertFalse(seen_payloads) + self.assertFalse(terminal_metadata_seen) + self.assertMetadataEqual(initial_metadata, ticket.initial_metadata) + initial_metadata_seen = True + + if ticket.payload is not None: + self.assertFalse(terminal_metadata_seen) + seen_payloads.append(ticket.payload) + + if ticket.terminal_metadata is not None: + self.assertFalse(terminal_metadata_seen) + self.assertMetadataEqual(terminal_metadata, ticket.terminal_metadata) + terminal_metadata_seen = True + self.assertSequenceEqual(payloads, seen_payloads) + + def _assert_is_valid_invocation_sequence( + self, ticket_sequence, group, method, payloads, initial_metadata, + terminal_metadata, termination): + self.assertLess(0, len(ticket_sequence)) + self.assertEqual(group, ticket_sequence[0].group) + self.assertEqual(method, ticket_sequence[0].method) + self._assert_is_valid_metadata_payload_sequence( + ticket_sequence, payloads, initial_metadata, terminal_metadata) + self.assertIs(termination, ticket_sequence[-1].termination) + + def _assert_is_valid_service_sequence( + self, ticket_sequence, payloads, initial_metadata, terminal_metadata, + code, message, termination): + self.assertLess(0, len(ticket_sequence)) + self._assert_is_valid_metadata_payload_sequence( + ticket_sequence, payloads, initial_metadata, terminal_metadata) + self.assertEqual(code, ticket_sequence[-1].code) + self.assertEqual(message, ticket_sequence[-1].message) + self.assertIs(termination, ticket_sequence[-1].termination) + + def setUp(self): + self._invocation_link, self._service_link = self.create_transmitting_links() + self._invocation_mate = test_utilities.RecordingLink() + self._service_mate = test_utilities.RecordingLink() + self._invocation_link.join_link(self._invocation_mate) + self._service_link.join_link(self._service_mate) + + def tearDown(self): + self.destroy_transmitting_links(self._invocation_link, self._service_link) + + def testSimplestRoundTrip(self): + """Tests transmission of one ticket in each direction.""" + invocation_operation_id = object() + invocation_payload = b'\x07' * 1023 + timeout = test_constants.LONG_TIMEOUT + invocation_initial_metadata = self.create_invocation_initial_metadata() + invocation_terminal_metadata = self.create_invocation_terminal_metadata() + invocation_code, invocation_message = self.create_invocation_completion() + service_payload = b'\x08' * 1025 + service_initial_metadata = self.create_service_initial_metadata() + service_terminal_metadata = self.create_service_terminal_metadata() + service_code, service_message = self.create_service_completion() + + original_invocation_ticket = links.Ticket( + invocation_operation_id, 0, _TRANSMISSION_GROUP, _TRANSMISSION_METHOD, + links.Ticket.Subscription.FULL, timeout, 0, invocation_initial_metadata, + invocation_payload, invocation_terminal_metadata, invocation_code, + invocation_message, links.Ticket.Termination.COMPLETION) + self._invocation_link.accept_ticket(original_invocation_ticket) + + # TODO(nathaniel): This shouldn't be necessary. Detecting the end of the + # invocation-side ticket sequence shouldn't require granting allowance for + # another payload. + self._service_mate.block_until_tickets_satisfy( + at_least_n_payloads_received_predicate(1)) + service_operation_id = self._service_mate.tickets()[0].operation_id + self._service_link.accept_ticket( + links.Ticket( + service_operation_id, 0, None, None, links.Ticket.Subscription.FULL, + None, 1, None, None, None, None, None, None)) + + self._service_mate.block_until_tickets_satisfy(terminated) + self._assert_is_valid_invocation_sequence( + self._service_mate.tickets(), _TRANSMISSION_GROUP, _TRANSMISSION_METHOD, + (invocation_payload,), invocation_initial_metadata, + invocation_terminal_metadata, links.Ticket.Termination.COMPLETION) + + original_service_ticket = links.Ticket( + service_operation_id, 1, None, None, links.Ticket.Subscription.FULL, + timeout, 0, service_initial_metadata, service_payload, + service_terminal_metadata, service_code, service_message, + links.Ticket.Termination.COMPLETION) + self._service_link.accept_ticket(original_service_ticket) + self._invocation_mate.block_until_tickets_satisfy(terminated) + self._assert_is_valid_service_sequence( + self._invocation_mate.tickets(), (service_payload,), + service_initial_metadata, service_terminal_metadata, service_code, + service_message, links.Ticket.Termination.COMPLETION) diff --git a/src/python/src/grpc/framework/interfaces/links/test_utilities.py b/src/python/src/grpc/framework/interfaces/links/test_utilities.py new file mode 100644 index 00000000000..6c2e3346aaa --- /dev/null +++ b/src/python/src/grpc/framework/interfaces/links/test_utilities.py @@ -0,0 +1,66 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""State and behavior appropriate for use in tests.""" + +import threading + +from grpc.framework.interfaces.links import links + + +class RecordingLink(links.Link): + """A Link that records every ticket passed to it.""" + + def __init__(self): + self._condition = threading.Condition() + self._tickets = [] + + def accept_ticket(self, ticket): + with self._condition: + self._tickets.append(ticket) + self._condition.notify_all() + + def join_link(self, link): + pass + + def block_until_tickets_satisfy(self, predicate): + """Blocks until the received tickets satisfy the given predicate. + + Args: + predicate: A callable that takes a sequence of tickets and returns a + boolean value. + """ + with self._condition: + while not predicate(self._tickets): + self._condition.wait() + + def tickets(self): + """Returns a copy of the list of all tickets received by this Link.""" + with self._condition: + return tuple(self._tickets) diff --git a/src/python/src/grpc/framework/interfaces/links/utilities.py b/src/python/src/grpc/framework/interfaces/links/utilities.py new file mode 100644 index 00000000000..6e4fd76d932 --- /dev/null +++ b/src/python/src/grpc/framework/interfaces/links/utilities.py @@ -0,0 +1,44 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Utilities provided as part of the links interface.""" + +from grpc.framework.interfaces.links import links + + +class _NullLink(links.Link): + """A do-nothing links.Link.""" + + def accept_ticket(self, ticket): + pass + + def join_link(self, link): + pass + +NULL_LINK = _NullLink() diff --git a/src/python/src/setup.py b/src/python/src/setup.py index 193285ac9bb..a857ae98cc2 100644 --- a/src/python/src/setup.py +++ b/src/python/src/setup.py @@ -33,7 +33,6 @@ import os import sys from distutils import core as _core -from distutils import extension as _extension import setuptools @@ -54,19 +53,6 @@ _C_EXTENSION_SOURCES = ( 'grpc/_adapter/_c/types/server.c', ) -_CYTHON_EXTENSION_PACKAGE_NAMES = ( -) - -_CYTHON_EXTENSION_MODULE_NAMES = ( - 'grpc._cython.cygrpc', - 'grpc._cython._cygrpc.call', - 'grpc._cython._cygrpc.channel', - 'grpc._cython._cygrpc.completion_queue', - 'grpc._cython._cygrpc.credentials', - 'grpc._cython._cygrpc.records', - 'grpc._cython._cygrpc.server', -) - _EXTENSION_INCLUDE_DIRECTORIES = ( '.', ) @@ -84,45 +70,13 @@ _C_EXTENSION_MODULE = _core.Extension( include_dirs=list(_EXTENSION_INCLUDE_DIRECTORIES), libraries=list(_EXTENSION_LIBRARIES), ) -_C_EXTENSION_MODULES = [_C_EXTENSION_MODULE] - - -def cython_extensions(package_names, module_names, include_dirs, libraries, - build_with_cython=False): - file_extension = 'pyx' if build_with_cython else 'c' - module_files = [name.replace('.', '/') + '.' + file_extension - for name in module_names] - extensions = [ - _extension.Extension( - name=module_name, sources=[module_file], - include_dirs=include_dirs, libraries=libraries - ) for (module_name, module_file) in zip(module_names, module_files) - ] - if build_with_cython: - import Cython.Build - return Cython.Build.cythonize(extensions) - else: - return extensions - -_CYTHON_EXTENSION_MODULES = cython_extensions( - list(_CYTHON_EXTENSION_PACKAGE_NAMES), list(_CYTHON_EXTENSION_MODULE_NAMES), - list(_EXTENSION_INCLUDE_DIRECTORIES), list(_EXTENSION_LIBRARIES), - bool(_BUILD_WITH_CYTHON)) - -# TODO(atash): We shouldn't need to gate any C code based on the python version -# from the distutils build system. Remove this hackery once we're on Cython and -# 3.x C API compliant. -_EXTENSION_MODULES = list(_CYTHON_EXTENSION_MODULES) -if sys.version_info[0:2] <= (2, 7): - _EXTENSION_MODULES += _C_EXTENSION_MODULES - +_EXTENSION_MODULES = [_C_EXTENSION_MODULE] _PACKAGES = ( 'grpc', 'grpc._adapter', - 'grpc._cython', - 'grpc._cython._cygrpc', 'grpc._junkdrawer', + 'grpc._links', 'grpc.early_adopter', 'grpc.framework', 'grpc.framework.alpha', @@ -131,20 +85,22 @@ _PACKAGES = ( 'grpc.framework.face', 'grpc.framework.face.testing', 'grpc.framework.foundation', + 'grpc.framework.interfaces', + 'grpc.framework.interfaces.links', ) _PACKAGE_DIRECTORIES = { 'grpc': 'grpc', 'grpc._adapter': 'grpc/_adapter', - 'grpc._cython': 'grpc/_cython', 'grpc._junkdrawer': 'grpc/_junkdrawer', + 'grpc._links': 'grpc/_links', 'grpc.early_adopter': 'grpc/early_adopter', 'grpc.framework': 'grpc/framework', } setuptools.setup( name='grpcio', - version='0.9.0a1', + version='0.10.0a0', ext_modules=_EXTENSION_MODULES, packages=list(_PACKAGES), package_dir=_PACKAGE_DIRECTORIES, diff --git a/src/ruby/ext/grpc/rb_completion_queue.c b/src/ruby/ext/grpc/rb_completion_queue.c index 8fb3949b3dc..f89439f4c10 100644 --- a/src/ruby/ext/grpc/rb_completion_queue.c +++ b/src/ruby/ext/grpc/rb_completion_queue.c @@ -82,7 +82,7 @@ static void grpc_rb_completion_queue_shutdown_drain(grpc_completion_queue *cq) { next_call.cq = cq; next_call.event.type = GRPC_QUEUE_TIMEOUT; /* TODO: the timeout should be a module level constant that defaults - * to gpr_inf_future. + * to gpr_inf_future(GPR_CLOCK_REALTIME). * * - at the moment this does not work, it stalls. Using a small timeout like * this one works, and leads to fast test run times; a longer timeout was @@ -91,7 +91,8 @@ static void grpc_rb_completion_queue_shutdown_drain(grpc_completion_queue *cq) { * - investigate further, this is probably another example of C-level cleanup * not working consistently in all cases. */ - next_call.timeout = gpr_time_add(gpr_now(), gpr_time_from_micros(5e3)); + next_call.timeout = gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), + gpr_time_from_micros(5e3, GPR_TIMESPAN)); do { rb_thread_call_without_gvl(grpc_rb_completion_queue_next_no_gil, (void *)&next_call, NULL, NULL); @@ -143,7 +144,7 @@ grpc_event grpc_rb_completion_queue_pluck_event(VALUE self, VALUE tag, TypedData_Get_Struct(self, grpc_completion_queue, &grpc_rb_completion_queue_data_type, next_call.cq); if (TYPE(timeout) == T_NIL) { - next_call.timeout = gpr_inf_future; + next_call.timeout = gpr_inf_future(GPR_CLOCK_REALTIME); } else { next_call.timeout = grpc_rb_time_timeval(timeout, /* absolute time*/ 0); } diff --git a/src/ruby/ext/grpc/rb_grpc.c b/src/ruby/ext/grpc/rb_grpc.c index 699548b9403..f138bf6dd53 100644 --- a/src/ruby/ext/grpc/rb_grpc.c +++ b/src/ruby/ext/grpc/rb_grpc.c @@ -98,6 +98,7 @@ gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) { const char *tstr = interval ? "time interval" : "time"; const char *want = " want | + + @@ -232,6 +235,8 @@ + + @@ -242,8 +247,6 @@ - - diff --git a/vsprojects/grpc++/grpc++.vcxproj.filters b/vsprojects/grpc++/grpc++.vcxproj.filters index e63c77a53d8..c5d8db57aec 100644 --- a/vsprojects/grpc++/grpc++.vcxproj.filters +++ b/vsprojects/grpc++/grpc++.vcxproj.filters @@ -7,6 +7,9 @@ src\cpp\client + + src\cpp\common + src\cpp\common @@ -58,6 +61,9 @@ src\cpp\server + + src\cpp\server + src\cpp\server @@ -73,9 +79,6 @@ src\cpp\server - - src\cpp\server - src\cpp\util @@ -99,6 +102,9 @@ include\grpc++ + + include\grpc++ + include\grpc++ @@ -126,6 +132,9 @@ include\grpc++ + + include\grpc++ + include\grpc++ @@ -221,9 +230,6 @@ src\cpp\common - - src\cpp\server - diff --git a/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj b/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj index 944e7e00013..f03715b353d 100644 --- a/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj +++ b/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj @@ -149,6 +149,7 @@ + @@ -158,6 +159,7 @@ + @@ -188,7 +190,6 @@ - @@ -221,6 +222,8 @@ + + @@ -231,8 +234,6 @@ - - diff --git a/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj.filters b/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj.filters index 73b0a5dccdb..8f7f3bcd5e9 100644 --- a/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj.filters +++ b/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj.filters @@ -46,6 +46,9 @@ src\cpp\server + + src\cpp\server + src\cpp\server @@ -61,9 +64,6 @@ src\cpp\server - - src\cpp\server - src\cpp\util @@ -87,6 +87,9 @@ include\grpc++ + + include\grpc++ + include\grpc++ @@ -114,6 +117,9 @@ include\grpc++ + + include\grpc++ + include\grpc++ @@ -200,9 +206,6 @@ src\cpp\common - - src\cpp\server - diff --git a/vsprojects/grpc/grpc.vcxproj b/vsprojects/grpc/grpc.vcxproj index 865e76ef10b..16744b181b9 100644 --- a/vsprojects/grpc/grpc.vcxproj +++ b/vsprojects/grpc/grpc.vcxproj @@ -481,6 +481,8 @@ + + diff --git a/vsprojects/grpc/grpc.vcxproj.filters b/vsprojects/grpc/grpc.vcxproj.filters index f560fef9ed7..de9f20521ca 100644 --- a/vsprojects/grpc/grpc.vcxproj.filters +++ b/vsprojects/grpc/grpc.vcxproj.filters @@ -316,6 +316,9 @@ src\core\surface + + src\core\surface + src\core\transport\chttp2 diff --git a/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj b/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj index 4ead6f8d98a..02c791f995d 100644 --- a/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj +++ b/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj @@ -416,6 +416,8 @@ + + diff --git a/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj.filters b/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj.filters index 45ca1f7c33b..333a71f5648 100644 --- a/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj.filters +++ b/vsprojects/grpc_unsecure/grpc_unsecure.vcxproj.filters @@ -247,6 +247,9 @@ src\core\surface + + src\core\surface + src\core\transport\chttp2