diff options
54 files changed, 1580 insertions, 2095 deletions
@@ -104,6 +104,10 @@ cc_library( "include/grpc/support/sync_win32.h", "include/grpc/support/thd.h", "include/grpc/support/time.h", + "include/grpc/support/tls.h", + "include/grpc/support/tls_gcc.h", + "include/grpc/support/tls_msvc.h", + "include/grpc/support/tls_pthread.h", "include/grpc/support/useful.h", ], includes = [ @@ -583,6 +583,7 @@ gpr_string_test: $(BINDIR)/$(CONFIG)/gpr_string_test gpr_sync_test: $(BINDIR)/$(CONFIG)/gpr_sync_test gpr_thd_test: $(BINDIR)/$(CONFIG)/gpr_thd_test gpr_time_test: $(BINDIR)/$(CONFIG)/gpr_time_test +gpr_tls_test: $(BINDIR)/$(CONFIG)/gpr_tls_test gpr_useful_test: $(BINDIR)/$(CONFIG)/gpr_useful_test grpc_base64_test: $(BINDIR)/$(CONFIG)/grpc_base64_test grpc_byte_buffer_reader_test: $(BINDIR)/$(CONFIG)/grpc_byte_buffer_reader_test @@ -1081,7 +1082,7 @@ privatelibs_cxx: $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.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)/census_hash_table_test $(BINDIR)/$(CONFIG)/census_statistics_multiple_writers_circular_buffer_test $(BINDIR)/$(CONFIG)/census_statistics_multiple_writers_test $(BINDIR)/$(CONFIG)/census_statistics_performance_test $(BINDIR)/$(CONFIG)/census_statistics_quick_test $(BINDIR)/$(CONFIG)/census_statistics_small_log_test $(BINDIR)/$(CONFIG)/census_stub_test $(BINDIR)/$(CONFIG)/census_window_stats_test $(BINDIR)/$(CONFIG)/chttp2_status_conversion_test $(BINDIR)/$(CONFIG)/chttp2_stream_encoder_test $(BINDIR)/$(CONFIG)/chttp2_stream_map_test $(BINDIR)/$(CONFIG)/chttp2_transport_end2end_test $(BINDIR)/$(CONFIG)/dualstack_socket_test $(BINDIR)/$(CONFIG)/echo_client $(BINDIR)/$(CONFIG)/echo_server $(BINDIR)/$(CONFIG)/echo_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_useful_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_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)/metadata_buffer_test $(BINDIR)/$(CONFIG)/multi_init_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)/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_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_ping_pong_streaming_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_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_writes_done_hangs_with_pending_read_legacy_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_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_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_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_writes_done_hangs_with_pending_read_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_writes_done_hangs_with_pending_read_legacy_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_no_op_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_ping_pong_streaming_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_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_payload_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_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_legacy_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_no_op_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_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_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_payload_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_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_legacy_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_no_op_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_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_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_writes_done_hangs_with_pending_read_legacy_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_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_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_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_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_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_legacy_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)/census_hash_table_test $(BINDIR)/$(CONFIG)/census_statistics_multiple_writers_circular_buffer_test $(BINDIR)/$(CONFIG)/census_statistics_multiple_writers_test $(BINDIR)/$(CONFIG)/census_statistics_performance_test $(BINDIR)/$(CONFIG)/census_statistics_quick_test $(BINDIR)/$(CONFIG)/census_statistics_small_log_test $(BINDIR)/$(CONFIG)/census_stub_test $(BINDIR)/$(CONFIG)/census_window_stats_test $(BINDIR)/$(CONFIG)/chttp2_status_conversion_test $(BINDIR)/$(CONFIG)/chttp2_stream_encoder_test $(BINDIR)/$(CONFIG)/chttp2_stream_map_test $(BINDIR)/$(CONFIG)/chttp2_transport_end2end_test $(BINDIR)/$(CONFIG)/dualstack_socket_test $(BINDIR)/$(CONFIG)/echo_client $(BINDIR)/$(CONFIG)/echo_server $(BINDIR)/$(CONFIG)/echo_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_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_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)/metadata_buffer_test $(BINDIR)/$(CONFIG)/multi_init_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)/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_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_ping_pong_streaming_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_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_writes_done_hangs_with_pending_read_legacy_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_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_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_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_writes_done_hangs_with_pending_read_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_writes_done_hangs_with_pending_read_legacy_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_no_op_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_ping_pong_streaming_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_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_payload_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_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_legacy_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_no_op_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_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_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_payload_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_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_legacy_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_no_op_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_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_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_writes_done_hangs_with_pending_read_legacy_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_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_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_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_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_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_legacy_test buildtests_cxx: privatelibs_cxx $(BINDIR)/$(CONFIG)/async_end2end_test $(BINDIR)/$(CONFIG)/channel_arguments_test $(BINDIR)/$(CONFIG)/cli_call_test $(BINDIR)/$(CONFIG)/credentials_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)/pubsub_client $(BINDIR)/$(CONFIG)/pubsub_publisher_test $(BINDIR)/$(CONFIG)/pubsub_subscriber_test $(BINDIR)/$(CONFIG)/qps_driver $(BINDIR)/$(CONFIG)/qps_smoke_test $(BINDIR)/$(CONFIG)/qps_worker $(BINDIR)/$(CONFIG)/status_test $(BINDIR)/$(CONFIG)/thread_pool_test @@ -1158,6 +1159,8 @@ test_c: buildtests_c $(Q) $(BINDIR)/$(CONFIG)/gpr_thd_test || ( echo test gpr_thd_test failed ; exit 1 ) $(E) "[RUN] Testing gpr_time_test" $(Q) $(BINDIR)/$(CONFIG)/gpr_time_test || ( echo test gpr_time_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_tls_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_tls_test || ( echo test gpr_tls_test failed ; exit 1 ) $(E) "[RUN] Testing gpr_useful_test" $(Q) $(BINDIR)/$(CONFIG)/gpr_useful_test || ( echo test gpr_useful_test failed ; exit 1 ) $(E) "[RUN] Testing grpc_base64_test" @@ -2435,6 +2438,10 @@ PUBLIC_HEADERS_C += \ include/grpc/support/sync_win32.h \ include/grpc/support/thd.h \ include/grpc/support/time.h \ + include/grpc/support/tls.h \ + include/grpc/support/tls_gcc.h \ + include/grpc/support/tls_msvc.h \ + include/grpc/support/tls_pthread.h \ include/grpc/support/useful.h \ LIBGPR_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGPR_SRC)))) @@ -6974,6 +6981,37 @@ endif endif +GPR_TLS_TEST_SRC = \ + test/core/support/tls_test.c \ + +GPR_TLS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_TLS_TEST_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL with ALPN. + +$(BINDIR)/$(CONFIG)/gpr_tls_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gpr_tls_test: $(GPR_TLS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_TLS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_tls_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/tls_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + +deps_gpr_tls_test: $(GPR_TLS_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_TLS_TEST_OBJS:.o=.dep) +endif +endif + + GPR_USEFUL_TEST_SRC = \ test/core/support/useful_test.c \ diff --git a/build.json b/build.json index d00b0393ec..577324620c 100644 --- a/build.json +++ b/build.json @@ -305,6 +305,10 @@ "include/grpc/support/sync_win32.h", "include/grpc/support/thd.h", "include/grpc/support/time.h", + "include/grpc/support/tls.h", + "include/grpc/support/tls_gcc.h", + "include/grpc/support/tls_msvc.h", + "include/grpc/support/tls_pthread.h", "include/grpc/support/useful.h" ], "headers": [ @@ -556,6 +560,7 @@ ], "deps": [ "grpc++_test_util", + "grpc_test_util", "grpc++", "grpc", "gpr" @@ -1191,6 +1196,18 @@ ] }, { + "name": "gpr_tls_test", + "build": "test", + "language": "c", + "src": [ + "test/core/support/tls_test.c" + ], + "deps": [ + "gpr_test_util", + "gpr" + ] + }, + { "name": "gpr_useful_test", "build": "test", "language": "c", diff --git a/include/grpc/support/port_platform.h b/include/grpc/support/port_platform.h index 41185dbf64..0bb3e16c8d 100644 --- a/include/grpc/support/port_platform.h +++ b/include/grpc/support/port_platform.h @@ -55,14 +55,17 @@ #define GPR_WINSOCK_SOCKET 1 #ifdef __GNUC__ #define GPR_GCC_ATOMIC 1 +#define GPR_GCC_TLS 1 #else #define GPR_WIN32_ATOMIC 1 +#define GPR_MSVC_TLS 1 #endif #elif defined(ANDROID) || defined(__ANDROID__) #define GPR_ANDROID 1 #define GPR_ARCH_32 1 #define GPR_CPU_LINUX 1 #define GPR_GCC_SYNC 1 +#define GPR_GCC_TLS 1 #define GPR_POSIX_MULTIPOLL_WITH_POLL 1 #define GPR_POSIX_WAKEUP_FD 1 #define GPR_LINUX_EVENTFD 1 @@ -88,6 +91,7 @@ #include <features.h> #define GPR_CPU_LINUX 1 #define GPR_GCC_ATOMIC 1 +#define GPR_GCC_TLS 1 #define GPR_LINUX 1 #define GPR_LINUX_MULTIPOLL_WITH_EPOLL 1 #define GPR_POSIX_WAKEUP_FD 1 @@ -134,6 +138,32 @@ #define GPR_CPU_POSIX 1 #endif #define GPR_GCC_ATOMIC 1 +#define GPR_GCC_TLS 1 +#define GPR_POSIX_LOG 1 +#define GPR_POSIX_MULTIPOLL_WITH_POLL 1 +#define GPR_POSIX_WAKEUP_FD 1 +#define GPR_POSIX_NO_SPECIAL_WAKEUP_FD 1 +#define GPR_POSIX_SOCKET 1 +#define GPR_POSIX_SOCKETADDR 1 +#define GPR_POSIX_SOCKETUTILS 1 +#define GPR_POSIX_ENV 1 +#define GPR_POSIX_FILE 1 +#define GPR_POSIX_STRING 1 +#define GPR_POSIX_SYNC 1 +#define GPR_POSIX_TIME 1 +#define GPR_GETPID_IN_UNISTD_H 1 +#ifdef _LP64 +#define GPR_ARCH_64 1 +#else /* _LP64 */ +#define GPR_ARCH_32 1 +#endif /* _LP64 */ +#elif defined(__FreeBSD__) +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif +#define GPR_CPU_POSIX 1 +#define GPR_GCC_ATOMIC 1 +#define GPR_GCC_TLS 1 #define GPR_POSIX_LOG 1 #define GPR_POSIX_MULTIPOLL_WITH_POLL 1 #define GPR_POSIX_WAKEUP_FD 1 @@ -190,16 +220,20 @@ #error Must define exactly one of GPR_ARCH_32, GPR_ARCH_64 #endif -#if defined(GPR_CPU_LINUX) + defined(GPR_CPU_POSIX) + defined(GPR_WIN32) + defined(GPR_CPU_IPHONE) != 1 -#error Must define exactly one of GPR_CPU_LINUX, GPR_CPU_POSIX, GPR_WIN32, GPR_CPU_IPHONE +#if defined(GPR_CPU_LINUX) + defined(GPR_CPU_POSIX) + defined(GPR_WIN32) + defined(GPR_CPU_IPHONE) + defined(GPR_CPU_CUSTOM) != 1 +#error Must define exactly one of GPR_CPU_LINUX, GPR_CPU_POSIX, GPR_WIN32, GPR_CPU_IPHONE, GPR_CPU_CUSTOM #endif #if defined(GPR_POSIX_MULTIPOLL_WITH_POLL) && !defined(GPR_POSIX_SOCKET) #error Must define GPR_POSIX_SOCKET to use GPR_POSIX_MULTIPOLL_WITH_POLL #endif -#if defined(GPR_POSIX_SOCKET) + defined(GPR_WIN32) != 1 -#error Must define exactly one of GPR_POSIX_SOCKET, GPR_WIN32 +#if defined(GPR_POSIX_SOCKET) + defined(GPR_WINSOCK_SOCKET) + defined(GPR_CUSTOM_SOCKET) != 1 +#error Must define exactly one of GPR_POSIX_SOCKET, GPR_WINSOCK_SOCKET, GPR_CUSTOM_SOCKET +#endif + +#if defined(GPR_MSVC_TLS) + defined(GPR_GCC_TLS) + defined(GPR_PTHREAD_TLS) + defined(GPR_CUSTOM_TLS) != 1 +#error Must define exactly one of GPR_MSVC_TLS, GPR_GCC_TLS, GPR_PTHREAD_TLS, defined(GPR_CUSTOM_TLS) #endif typedef int16_t gpr_int16; diff --git a/include/grpc/support/sync.h b/include/grpc/support/sync.h index 35b2d12e77..1cdde1d2d0 100644 --- a/include/grpc/support/sync.h +++ b/include/grpc/support/sync.h @@ -60,7 +60,7 @@ #include <grpc/support/sync_posix.h> #elif defined(GPR_WIN32) #include <grpc/support/sync_win32.h> -#else +#elif !defined(GPR_CUSTOM_SYNC) #error Unable to determine platform for sync #endif diff --git a/include/grpc/support/tls.h b/include/grpc/support/tls.h new file mode 100644 index 0000000000..1077fdec29 --- /dev/null +++ b/include/grpc/support/tls.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 GRPC_SUPPORT_TLS_H +#define GRPC_SUPPORT_TLS_H + +#include "port_platform.h" + +/* Thread local storage. + + A minimal wrapper that should be implementable across many compilers, + and implementable efficiently across most modern compilers. + + Thread locals have type gpr_intptr. + + Declaring a thread local variable 'foo': + GPR_TLS_DECL(foo, initial_value); + Thread locals always have static scope. + + Initializing a thread local (must be done at library initialization + time): + gpr_tls_init(&foo); + + Destroying a thread local: + gpr_tls_destroy(&foo); + + Setting a thread local: + gpr_tls_set(&foo, new_value); + + Accessing a thread local: + current_value = gpr_tls_get(&foo, value); + + ALL functions here may be implemented as macros. */ + +#ifdef GPR_GCC_TLS +#include "tls_gcc.h" +#endif + +#ifdef GPR_MSVC_TLS +#include "tls_msvc.h" +#endif + +#ifdef GPR_PTHREAD_TLS +#include "tls_pthread.h" +#endif + +#endif diff --git a/src/ruby/ext/grpc/rb_event.h b/include/grpc/support/tls_gcc.h index 3105934b11..a078b104ea 100644 --- a/src/ruby/ext/grpc/rb_event.h +++ b/include/grpc/support/tls_gcc.h @@ -31,23 +31,22 @@ * */ -#ifndef GRPC_RB_EVENT_H_ -#define GRPC_RB_EVENT_H_ +#ifndef GRPC_SUPPORT_TLS_GCC_H +#define GRPC_SUPPORT_TLS_GCC_H -#include <ruby.h> -#include <grpc/grpc.h> +/* Thread local storage based on gcc compiler primitives. + #include tls.h to use this - and see that file for documentation */ -/* rb_cEvent is the Event class whose instances proxy grpc_event. */ -extern VALUE rb_cEvent; +struct gpr_gcc_thread_local { + gpr_intptr value; +}; -/* rb_cEventError is the ruby class that acts the exception thrown during rpc - event processing. */ -extern VALUE rb_eEventError; +#define GPR_TLS_DECL(name) \ + static __thread struct gpr_gcc_thread_local name = {0} -/* Used to create new ruby event objects */ -VALUE grpc_rb_new_event(grpc_event *ev); +#define gpr_tls_init(tls) do {} while (0) +#define gpr_tls_destroy(tls) do {} while (0) +#define gpr_tls_set(tls, new_value) (((tls)->value) = (new_value)) +#define gpr_tls_get(tls) ((tls)->value) -/* Initializes the Event and EventError classes. */ -void Init_grpc_event(); - -#endif /* GRPC_RB_EVENT_H_ */ +#endif diff --git a/src/ruby/ext/grpc/rb_metadata.h b/include/grpc/support/tls_msvc.h index 251072f658..e574c11884 100644 --- a/src/ruby/ext/grpc/rb_metadata.h +++ b/include/grpc/support/tls_msvc.h @@ -31,23 +31,22 @@ * */ -#ifndef GRPC_RB_METADATA_H_ -#define GRPC_RB_METADATA_H_ +#ifndef GRPC_SUPPORT_TLS_GCC_H +#define GRPC_SUPPORT_TLS_GCC_H -#include <grpc/grpc.h> -#include <ruby.h> +/* Thread local storage based on ms visual c compiler primitives. + #include tls.h to use this - and see that file for documentation */ -/* rb_cMetadata is the Metadata class whose instances proxy grpc_metadata. */ -extern VALUE rb_cMetadata; +struct gpr_msvc_thread_local { + gpr_intptr value; +}; -/* grpc_rb_metadata_create_with_mark creates a grpc_rb_metadata with a ruby mark - * object that will be kept alive while the metadata is alive. */ -extern VALUE grpc_rb_metadata_create_with_mark(VALUE mark, grpc_metadata* md); +#define GPR_TLS_DECL(name) \ + static __thread struct gpr_msvc_thread_local name = {0} -/* Gets the wrapped metadata from the ruby wrapper */ -grpc_metadata* grpc_rb_get_wrapped_metadata(VALUE v); +#define gpr_tls_init(tls) do {} while (0) +#define gpr_tls_destroy(tls) do {} while (0) +#define gpr_tls_set(tls, new_value) (((tls)->value) = (new_value)) +#define gpr_tls_get(tls) ((tls)->value) -/* Initializes the Metadata class. */ -void Init_grpc_metadata(); - -#endif /* GRPC_RB_METADATA_H_ */ +#endif diff --git a/include/grpc/support/tls_pthread.h b/include/grpc/support/tls_pthread.h new file mode 100644 index 0000000000..249c4d1cab --- /dev/null +++ b/include/grpc/support/tls_pthread.h @@ -0,0 +1,53 @@ +/* + * + * 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_SUPPORT_TLS_PTHREAD_H +#define GRPC_SUPPORT_TLS_PTHREAD_H + +/* Thread local storage based on pthread library calls. + #include tls.h to use this - and see that file for documentation */ + +struct gpr_pthread_thread_local { + pthread_key_t key; +}; + +#define GPR_TLS_DECL(name) \ + static struct gpr_pthread_thread_local name = {0} + +#define gpr_tls_init(tls) GPR_ASSERT(0 == pthread_key_create(&(tls)->key, NULL)) +#define gpr_tls_destroy(tls) pthread_key_delete((tls)->key) +#define gpr_tls_set(tls, new_value) \ + GPR_ASSERT(pthread_setspecific((tls)->key, (void*)(new_value)) == 0) +#define gpr_tls_get(tls) ((gpr_intptr)pthread_getspecific((tls)->key)) + +#endif diff --git a/src/compiler/cpp_generator.cc b/src/compiler/cpp_generator.cc index c78f0333d8..bd8bf65349 100644 --- a/src/compiler/cpp_generator.cc +++ b/src/compiler/cpp_generator.cc @@ -198,17 +198,18 @@ grpc::string GetHeaderIncludes(const grpc::protobuf::FileDescriptor *file, temp.append("\n"); - std::vector<grpc::string> parts = - grpc_generator::tokenize(file->package(), "."); + if (!file->package().empty()) { + std::vector<grpc::string> parts = + grpc_generator::tokenize(file->package(), "."); - for (auto part = parts.begin(); part != parts.end(); part++) { - temp.append("namespace "); - temp.append(*part); - temp.append(" {\n"); + for (auto part = parts.begin(); part != parts.end(); part++) { + temp.append("namespace "); + temp.append(*part); + temp.append(" {\n"); + } + temp.append("\n"); } - temp.append("\n"); - return temp; } @@ -431,15 +432,18 @@ grpc::string GetHeaderEpilogue(const grpc::protobuf::FileDescriptor *file, vars["filename"] = file->name(); vars["filename_identifier"] = FilenameIdentifier(file->name()); - std::vector<grpc::string> parts = - grpc_generator::tokenize(file->package(), "."); + if (!file->package().empty()) { + std::vector<grpc::string> parts = + grpc_generator::tokenize(file->package(), "."); - for (auto part = parts.rbegin(); part != parts.rend(); part++) { - vars["part"] = *part; - printer.Print(vars, "} // namespace $part$\n"); + for (auto part = parts.rbegin(); part != parts.rend(); part++) { + vars["part"] = *part; + printer.Print(vars, "} // namespace $part$\n"); + } + printer.Print(vars, "\n"); } - printer.Print(vars, "\n\n"); + printer.Print(vars, "\n"); printer.Print(vars, "#endif // GRPC_$filename_identifier$__INCLUDED\n"); return output; @@ -480,12 +484,14 @@ grpc::string GetSourceIncludes(const grpc::protobuf::FileDescriptor *file, printer.Print(vars, "#include <grpc++/impl/service_type.h>\n"); printer.Print(vars, "#include <grpc++/stream.h>\n"); - std::vector<grpc::string> parts = - grpc_generator::tokenize(file->package(), "."); + if (!file->package().empty()) { + std::vector<grpc::string> parts = + grpc_generator::tokenize(file->package(), "."); - for (auto part = parts.begin(); part != parts.end(); part++) { - vars["part"] = *part; - printer.Print(vars, "namespace $part$ {\n"); + for (auto part = parts.begin(); part != parts.end(); part++) { + vars["part"] = *part; + printer.Print(vars, "namespace $part$ {\n"); + } } printer.Print(vars, "\n"); @@ -860,17 +866,18 @@ grpc::string GetSourceEpilogue(const grpc::protobuf::FileDescriptor *file, const Parameters ¶ms) { grpc::string temp; - std::vector<grpc::string> parts = - grpc_generator::tokenize(file->package(), "."); + if (!file->package().empty()) { + std::vector<grpc::string> parts = + grpc_generator::tokenize(file->package(), "."); - for (auto part = parts.begin(); part != parts.end(); part++) { - temp.append("} // namespace "); - temp.append(*part); + for (auto part = parts.begin(); part != parts.end(); part++) { + temp.append("} // namespace "); + temp.append(*part); + temp.append("\n"); + } temp.append("\n"); } - temp.append("\n"); - return temp; } diff --git a/src/ruby/ext/grpc/rb_byte_buffer.c b/src/ruby/ext/grpc/rb_byte_buffer.c index ff5a114de5..e3a5277f54 100644 --- a/src/ruby/ext/grpc/rb_byte_buffer.c +++ b/src/ruby/ext/grpc/rb_byte_buffer.c @@ -39,203 +39,29 @@ #include <grpc/support/slice.h> #include "rb_grpc.h" -/* grpc_rb_byte_buffer wraps a grpc_byte_buffer. It provides a peer ruby - * object, 'mark' to minimize copying when a byte_buffer is created from - * ruby. */ -typedef struct grpc_rb_byte_buffer { - /* Holder of ruby objects involved in constructing the status */ - VALUE mark; - /* The actual status */ - grpc_byte_buffer *wrapped; -} grpc_rb_byte_buffer; - -/* Destroys ByteBuffer instances. */ -static void grpc_rb_byte_buffer_free(void *p) { - grpc_rb_byte_buffer *bb = NULL; - if (p == NULL) { - return; - }; - bb = (grpc_rb_byte_buffer *)p; - - /* Deletes the wrapped object if the mark object is Qnil, which indicates - * that no other object is the actual owner. */ - if (bb->wrapped != NULL && bb->mark == Qnil) { - grpc_byte_buffer_destroy(bb->wrapped); - } - - xfree(p); -} - -/* Protects the mark object from GC */ -static void grpc_rb_byte_buffer_mark(void *p) { - grpc_rb_byte_buffer *bb = NULL; - if (p == NULL) { - return; - } - bb = (grpc_rb_byte_buffer *)p; - - /* If it's not already cleaned up, mark the mark object */ - if (bb->mark != Qnil && BUILTIN_TYPE(bb->mark) != T_NONE) { - rb_gc_mark(bb->mark); - } +grpc_byte_buffer* grpc_rb_s_to_byte_buffer(char *string, size_t length) { + gpr_slice slice = gpr_slice_from_copied_buffer(string, length); + grpc_byte_buffer *buffer = grpc_byte_buffer_create(&slice, 1); + gpr_slice_unref(slice); + return buffer; } -/* id_source is the name of the hidden ivar the preserves the original - * byte_buffer source string */ -static ID id_source; - -/* Allocates ByteBuffer instances. - - Provides safe default values for the byte_buffer fields. */ -static VALUE grpc_rb_byte_buffer_alloc(VALUE cls) { - grpc_rb_byte_buffer *wrapper = ALLOC(grpc_rb_byte_buffer); - wrapper->wrapped = NULL; - wrapper->mark = Qnil; - return Data_Wrap_Struct(cls, grpc_rb_byte_buffer_mark, - grpc_rb_byte_buffer_free, wrapper); -} - -/* Clones ByteBuffer instances. - - Gives ByteBuffer a consistent implementation of Ruby's object copy/dup - protocol. */ -static VALUE grpc_rb_byte_buffer_init_copy(VALUE copy, VALUE orig) { - grpc_rb_byte_buffer *orig_bb = NULL; - grpc_rb_byte_buffer *copy_bb = NULL; - - if (copy == orig) { - return copy; - } - - /* Raise an error if orig is not a metadata object or a subclass. */ - if (TYPE(orig) != T_DATA || - RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_byte_buffer_free) { - rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cByteBuffer)); - } - - Data_Get_Struct(orig, grpc_rb_byte_buffer, orig_bb); - Data_Get_Struct(copy, grpc_rb_byte_buffer, copy_bb); - - /* use ruby's MEMCPY to make a byte-for-byte copy of the metadata wrapper - * object. */ - MEMCPY(copy_bb, orig_bb, grpc_rb_byte_buffer, 1); - return copy; -} - -/* id_empty is used to return the empty string from to_s when necessary. */ -static ID id_empty; - -static VALUE grpc_rb_byte_buffer_to_s(VALUE self) { - grpc_rb_byte_buffer *wrapper = NULL; - grpc_byte_buffer *bb = NULL; - grpc_byte_buffer_reader *reader = NULL; - char *output = NULL; +VALUE grpc_rb_byte_buffer_to_s(grpc_byte_buffer *buffer) { size_t length = 0; + char *string = NULL; size_t offset = 0; - VALUE output_obj = Qnil; + grpc_byte_buffer_reader *reader = NULL; gpr_slice next; + if (buffer == NULL) { + return Qnil; - Data_Get_Struct(self, grpc_rb_byte_buffer, wrapper); - output_obj = rb_ivar_get(wrapper->mark, id_source); - if (output_obj != Qnil) { - /* From ruby, ByteBuffers are immutable so if a source is set, return that - * as the to_s value */ - return output_obj; - } - - /* Read the bytes. */ - bb = wrapper->wrapped; - if (bb == NULL) { - return rb_id2str(id_empty); - } - length = grpc_byte_buffer_length(bb); - if (length == 0) { - return rb_id2str(id_empty); } - reader = grpc_byte_buffer_reader_create(bb); - output = xmalloc(length); + length = grpc_byte_buffer_length(buffer); + string = xmalloc(length + 1); + reader = grpc_byte_buffer_reader_create(buffer); while (grpc_byte_buffer_reader_next(reader, &next) != 0) { - memcpy(output + offset, GPR_SLICE_START_PTR(next), GPR_SLICE_LENGTH(next)); + memcpy(string + offset, GPR_SLICE_START_PTR(next), GPR_SLICE_LENGTH(next)); offset += GPR_SLICE_LENGTH(next); } - output_obj = rb_str_new(output, length); - - /* Save a references to the computed string in the mark object so that the - * calling to_s does not do any allocations. */ - wrapper->mark = rb_class_new_instance(0, NULL, rb_cObject); - rb_ivar_set(wrapper->mark, id_source, output_obj); - - return output_obj; -} - -/* Initializes ByteBuffer instances. */ -static VALUE grpc_rb_byte_buffer_init(VALUE self, VALUE src) { - gpr_slice a_slice; - grpc_rb_byte_buffer *wrapper = NULL; - grpc_byte_buffer *byte_buffer = NULL; - - if (TYPE(src) != T_STRING) { - rb_raise(rb_eTypeError, "bad byte_buffer arg: got <%s>, want <String>", - rb_obj_classname(src)); - return Qnil; - } - Data_Get_Struct(self, grpc_rb_byte_buffer, wrapper); - a_slice = gpr_slice_malloc(RSTRING_LEN(src)); - memcpy(GPR_SLICE_START_PTR(a_slice), RSTRING_PTR(src), RSTRING_LEN(src)); - byte_buffer = grpc_byte_buffer_create(&a_slice, 1); - gpr_slice_unref(a_slice); - - if (byte_buffer == NULL) { - rb_raise(rb_eArgError, "could not create a byte_buffer, not sure why"); - return Qnil; - } - wrapper->wrapped = byte_buffer; - - /* Save a references to the original string in the mark object so that the - * pointers used there is valid for the lifetime of the object. */ - wrapper->mark = rb_class_new_instance(0, NULL, rb_cObject); - rb_ivar_set(wrapper->mark, id_source, src); - - return self; -} - -/* rb_cByteBuffer is the ruby class that proxies grpc_byte_buffer. */ -VALUE rb_cByteBuffer = Qnil; - -void Init_grpc_byte_buffer() { - rb_cByteBuffer = - rb_define_class_under(rb_mGrpcCore, "ByteBuffer", rb_cObject); - - /* Allocates an object managed by the ruby runtime */ - rb_define_alloc_func(rb_cByteBuffer, grpc_rb_byte_buffer_alloc); - - /* Provides a ruby constructor and support for dup/clone. */ - rb_define_method(rb_cByteBuffer, "initialize", grpc_rb_byte_buffer_init, 1); - rb_define_method(rb_cByteBuffer, "initialize_copy", - grpc_rb_byte_buffer_init_copy, 1); - - /* Provides a to_s method that returns the buffer value */ - rb_define_method(rb_cByteBuffer, "to_s", grpc_rb_byte_buffer_to_s, 0); - - id_source = rb_intern("__source"); - id_empty = rb_intern(""); -} - -VALUE grpc_rb_byte_buffer_create_with_mark(VALUE mark, grpc_byte_buffer *bb) { - grpc_rb_byte_buffer *byte_buffer = NULL; - if (bb == NULL) { - return Qnil; - } - byte_buffer = ALLOC(grpc_rb_byte_buffer); - byte_buffer->wrapped = bb; - byte_buffer->mark = mark; - return Data_Wrap_Struct(rb_cByteBuffer, grpc_rb_byte_buffer_mark, - grpc_rb_byte_buffer_free, byte_buffer); -} - -/* Gets the wrapped byte_buffer from the ruby wrapper */ -grpc_byte_buffer *grpc_rb_get_wrapped_byte_buffer(VALUE v) { - grpc_rb_byte_buffer *wrapper = NULL; - Data_Get_Struct(v, grpc_rb_byte_buffer, wrapper); - return wrapper->wrapped; + return rb_str_new(string, length); } diff --git a/src/ruby/ext/grpc/rb_byte_buffer.h b/src/ruby/ext/grpc/rb_byte_buffer.h index 6ef72f3e75..96b9009dae 100644 --- a/src/ruby/ext/grpc/rb_byte_buffer.h +++ b/src/ruby/ext/grpc/rb_byte_buffer.h @@ -37,18 +37,10 @@ #include <grpc/grpc.h> #include <ruby.h> -/* rb_cByteBuffer is the ByteBuffer class whose instances proxy - grpc_byte_buffer. */ -extern VALUE rb_cByteBuffer; +/* Converts a char* with a length to a grpc_byte_buffer */ +grpc_byte_buffer *grpc_rb_s_to_byte_buffer(char *string, size_t length); -/* Initializes the ByteBuffer class. */ -void Init_grpc_byte_buffer(); - -/* grpc_rb_byte_buffer_create_with_mark creates a grpc_rb_byte_buffer with a - * ruby mark object that will be kept alive while the byte_buffer is alive. */ -VALUE grpc_rb_byte_buffer_create_with_mark(VALUE mark, grpc_byte_buffer* bb); - -/* Gets the wrapped byte_buffer from its ruby object. */ -grpc_byte_buffer* grpc_rb_get_wrapped_byte_buffer(VALUE v); +/* Converts a grpc_byte_buffer to a ruby string */ +VALUE grpc_rb_byte_buffer_to_s(grpc_byte_buffer *buffer); #endif /* GRPC_RB_BYTE_BUFFER_H_ */ diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c index b5a256d5a6..18cb4ec4f2 100644 --- a/src/ruby/ext/grpc/rb_call.c +++ b/src/ruby/ext/grpc/rb_call.c @@ -36,11 +36,19 @@ #include <ruby.h> #include <grpc/grpc.h> +#include <grpc/support/alloc.h> + #include "rb_byte_buffer.h" #include "rb_completion_queue.h" -#include "rb_metadata.h" #include "rb_grpc.h" +/* rb_sBatchResult is struct class used to hold the results of a batch call */ +static VALUE rb_sBatchResult; + +/* rb_cMdAry is the MetadataArray class whose instances proxy + * grpc_metadata_array. */ +static VALUE rb_cMdAry; + /* id_cq is the name of the hidden ivar that preserves a reference to a * completion queue */ static ID id_cq; @@ -62,6 +70,15 @@ static ID id_metadata; * received by the call and subsequently saved on it. */ static ID id_status; +/* sym_* are the symbol for attributes of rb_sBatchResult. */ +static VALUE sym_send_message; +static VALUE sym_send_metadata; +static VALUE sym_send_close; +static VALUE sym_send_status; +static VALUE sym_message; +static VALUE sym_status; +static VALUE sym_cancelled; + /* hash_all_calls is a hash of Call address -> reference count that is used to * track the creation and destruction of rb_call instances. */ @@ -101,84 +118,6 @@ const char *grpc_call_error_detail_of(grpc_call_error err) { return detail; } -/* grpc_rb_call_add_metadata_hash_cb is the hash iteration callback used by - grpc_rb_call_add_metadata. -*/ -int grpc_rb_call_add_metadata_hash_cb(VALUE key, VALUE val, VALUE call_obj) { - grpc_call *call = NULL; - grpc_metadata *md = NULL; - VALUE md_obj = Qnil; - VALUE md_obj_args[2]; - VALUE flags = rb_ivar_get(call_obj, id_flags); - grpc_call_error err; - int array_length; - int i; - - /* Construct a metadata object from key and value and add it */ - Data_Get_Struct(call_obj, grpc_call, call); - md_obj_args[0] = key; - - if (TYPE(val) == T_ARRAY) { - /* If the value is an array, add each value in the array separately */ - array_length = RARRAY_LEN(val); - for (i = 0; i < array_length; i++) { - md_obj_args[1] = rb_ary_entry(val, i); - md_obj = rb_class_new_instance(2, md_obj_args, rb_cMetadata); - md = grpc_rb_get_wrapped_metadata(md_obj); - err = grpc_call_add_metadata_old(call, md, NUM2UINT(flags)); - if (err != GRPC_CALL_OK) { - rb_raise(rb_eCallError, "add metadata failed: %s (code=%d)", - grpc_call_error_detail_of(err), err); - return ST_STOP; - } - } - } else { - md_obj_args[1] = val; - md_obj = rb_class_new_instance(2, md_obj_args, rb_cMetadata); - md = grpc_rb_get_wrapped_metadata(md_obj); - err = grpc_call_add_metadata_old(call, md, NUM2UINT(flags)); - if (err != GRPC_CALL_OK) { - rb_raise(rb_eCallError, "add metadata failed: %s (code=%d)", - grpc_call_error_detail_of(err), err); - return ST_STOP; - } - } - - return ST_CONTINUE; -} - -/* - call-seq: - call.add_metadata(completion_queue, hash_elements, flags=nil) - - Add metadata elements to the call from a ruby hash, to be sent upon - invocation. flags is a bit-field combination of the write flags defined - above. REQUIRES: grpc_call_invoke/grpc_call_accept have not been - called on this call. Produces no events. */ - -static VALUE grpc_rb_call_add_metadata(int argc, VALUE *argv, VALUE self) { - VALUE metadata; - VALUE flags = Qnil; - ID id_size = rb_intern("size"); - - /* "11" == 1 mandatory args, 1 (flags) is optional */ - rb_scan_args(argc, argv, "11", &metadata, &flags); - if (NIL_P(flags)) { - flags = UINT2NUM(0); /* Default to no flags */ - } - if (TYPE(metadata) != T_HASH) { - rb_raise(rb_eTypeError, "add metadata failed: metadata should be a hash"); - return Qnil; - } - if (NUM2UINT(rb_funcall(metadata, id_size, 0)) == 0) { - return Qnil; - } - rb_ivar_set(self, id_flags, flags); - rb_ivar_set(self, id_input_md, metadata); - rb_hash_foreach(metadata, grpc_rb_call_add_metadata_hash_cb, self); - return Qnil; -} - /* Called by clients to cancel an RPC on the server. Can be called multiple times, from any thread. */ static VALUE grpc_rb_call_cancel(VALUE self) { @@ -196,75 +135,18 @@ static VALUE grpc_rb_call_cancel(VALUE self) { /* call-seq: - call.invoke(completion_queue, tag, flags=nil) - - Invoke the RPC. Starts sending metadata and request headers on the wire. - flags is a bit-field combination of the write flags defined above. - REQUIRES: Can be called at most once per call. - Can only be called on the client. - Produces a GRPC_INVOKE_ACCEPTED event on completion. */ -static VALUE grpc_rb_call_invoke(int argc, VALUE *argv, VALUE self) { - VALUE cqueue = Qnil; - VALUE metadata_read_tag = Qnil; - VALUE finished_tag = Qnil; - VALUE flags = Qnil; - grpc_call *call = NULL; - grpc_completion_queue *cq = NULL; - grpc_call_error err; + status = call.status - /* "31" == 3 mandatory args, 1 (flags) is optional */ - rb_scan_args(argc, argv, "31", &cqueue, &metadata_read_tag, &finished_tag, - &flags); - if (NIL_P(flags)) { - flags = UINT2NUM(0); /* Default to no flags */ - } - cq = grpc_rb_get_wrapped_completion_queue(cqueue); - Data_Get_Struct(self, grpc_call, call); - err = grpc_call_invoke_old(call, cq, ROBJECT(metadata_read_tag), - ROBJECT(finished_tag), NUM2UINT(flags)); - if (err != GRPC_CALL_OK) { - rb_raise(rb_eCallError, "invoke failed: %s (code=%d)", - grpc_call_error_detail_of(err), err); - } - - /* Add the completion queue as an instance attribute, prevents it from being - * GCed until this call object is GCed */ - rb_ivar_set(self, id_cq, cqueue); - - return Qnil; -} - -/* Initiate a read on a call. Output event contains a byte buffer with the - result of the read. - REQUIRES: No other reads are pending on the call. It is only safe to start - the next read after the corresponding read event is received. */ -static VALUE grpc_rb_call_start_read(VALUE self, VALUE tag) { - grpc_call *call = NULL; - grpc_call_error err; - Data_Get_Struct(self, grpc_call, call); - err = grpc_call_start_read_old(call, ROBJECT(tag)); - if (err != GRPC_CALL_OK) { - rb_raise(rb_eCallError, "start read failed: %s (code=%d)", - grpc_call_error_detail_of(err), err); - } - - return Qnil; -} - -/* - call-seq: - status = call.status - - Gets the status object saved the call. */ + Gets the status object saved the call. */ static VALUE grpc_rb_call_get_status(VALUE self) { return rb_ivar_get(self, id_status); } /* call-seq: - call.status = status + call.status = status - Saves a status object on the call. */ + Saves a status object on the call. */ static VALUE grpc_rb_call_set_status(VALUE self, VALUE status) { if (!NIL_P(status) && rb_obj_class(status) != rb_sStatus) { rb_raise(rb_eTypeError, "bad status: got:<%s> want: <Struct::Status>", @@ -277,18 +159,18 @@ static VALUE grpc_rb_call_set_status(VALUE self, VALUE status) { /* call-seq: - metadata = call.metadata + metadata = call.metadata - Gets the metadata object saved the call. */ + Gets the metadata object saved the call. */ static VALUE grpc_rb_call_get_metadata(VALUE self) { return rb_ivar_get(self, id_metadata); } /* call-seq: - call.metadata = metadata + call.metadata = metadata - Saves the metadata hash on the call. */ + Saves the metadata hash on the call. */ static VALUE grpc_rb_call_set_metadata(VALUE self, VALUE metadata) { if (!NIL_P(metadata) && TYPE(metadata) != T_HASH) { rb_raise(rb_eTypeError, "bad metadata: got:<%s> want: <Hash>", @@ -299,147 +181,402 @@ static VALUE grpc_rb_call_set_metadata(VALUE self, VALUE metadata) { return rb_ivar_set(self, id_metadata, metadata); } -/* - call-seq: - call.start_write(byte_buffer, tag, flags=nil) - - Queue a byte buffer for writing. - flags is a bit-field combination of the write flags defined above. - A write with byte_buffer null is allowed, and will not send any bytes on the - wire. If this is performed without GRPC_WRITE_BUFFER_HINT flag it provides - a mechanism to flush any previously buffered writes to outgoing flow control. - REQUIRES: No other writes are pending on the call. It is only safe to - start the next write after the corresponding write_accepted event - is received. - GRPC_INVOKE_ACCEPTED must have been received by the application - prior to calling this on the client. On the server, - grpc_call_accept must have been called successfully. - Produces a GRPC_WRITE_ACCEPTED event. */ -static VALUE grpc_rb_call_start_write(int argc, VALUE *argv, VALUE self) { - VALUE byte_buffer = Qnil; - VALUE tag = Qnil; - VALUE flags = Qnil; - grpc_call *call = NULL; - grpc_byte_buffer *bfr = NULL; - grpc_call_error err; +/* grpc_rb_md_ary_fill_hash_cb is the hash iteration callback used + to fill grpc_metadata_array. + + it's capacity should have been computed via a prior call to + grpc_rb_md_ary_fill_hash_cb +*/ +int grpc_rb_md_ary_fill_hash_cb(VALUE key, VALUE val, VALUE md_ary_obj) { + grpc_metadata_array *md_ary = NULL; + int array_length; + int i; + + /* Construct a metadata object from key and value and add it */ + Data_Get_Struct(md_ary_obj, grpc_metadata_array, md_ary); - /* "21" == 2 mandatory args, 1 (flags) is optional */ - rb_scan_args(argc, argv, "21", &byte_buffer, &tag, &flags); - if (NIL_P(flags)) { - flags = UINT2NUM(0); /* Default to no flags */ + if (TYPE(val) == T_ARRAY) { + /* If the value is an array, add capacity for each value in the array */ + array_length = RARRAY_LEN(val); + for (i = 0; i < array_length; i++) { + if (TYPE(key) == T_SYMBOL) { + md_ary->metadata[md_ary->count].key = (char *)rb_id2name(SYM2ID(key)); + } else { /* StringValueCStr does all other type exclusions for us */ + md_ary->metadata[md_ary->count].key = StringValueCStr(key); + } + md_ary->metadata[md_ary->count].value = RSTRING_PTR(rb_ary_entry(val, i)); + md_ary->metadata[md_ary->count].value_length = + RSTRING_LEN(rb_ary_entry(val, i)); + md_ary->count += 1; + } + } else { + if (TYPE(key) == T_SYMBOL) { + md_ary->metadata[md_ary->count].key = (char *)rb_id2name(SYM2ID(key)); + } else { /* StringValueCStr does all other type exclusions for us */ + md_ary->metadata[md_ary->count].key = StringValueCStr(key); + } + md_ary->metadata[md_ary->count].value = RSTRING_PTR(val); + md_ary->metadata[md_ary->count].value_length = RSTRING_LEN(val); + md_ary->count += 1; } - bfr = grpc_rb_get_wrapped_byte_buffer(byte_buffer); - Data_Get_Struct(self, grpc_call, call); - err = grpc_call_start_write_old(call, bfr, ROBJECT(tag), NUM2UINT(flags)); - if (err != GRPC_CALL_OK) { - rb_raise(rb_eCallError, "start write failed: %s (code=%d)", - grpc_call_error_detail_of(err), err); + + return ST_CONTINUE; +} + +/* grpc_rb_md_ary_capacity_hash_cb is the hash iteration callback used + to pre-compute the capacity a grpc_metadata_array. +*/ +int grpc_rb_md_ary_capacity_hash_cb(VALUE key, VALUE val, VALUE md_ary_obj) { + grpc_metadata_array *md_ary = NULL; + + /* Construct a metadata object from key and value and add it */ + Data_Get_Struct(md_ary_obj, grpc_metadata_array, md_ary); + + if (TYPE(val) == T_ARRAY) { + /* If the value is an array, add capacity for each value in the array */ + md_ary->capacity += RARRAY_LEN(val); + } else { + md_ary->capacity += 1; + } + return ST_CONTINUE; +} + +/* grpc_rb_md_ary_convert converts a ruby metadata hash into + a grpc_metadata_array. +*/ +void grpc_rb_md_ary_convert(VALUE md_ary_hash, grpc_metadata_array *md_ary) { + VALUE md_ary_obj = Qnil; + if (md_ary_hash == Qnil) { + return; /* Do nothing if the expected has value is nil */ + } + if (TYPE(md_ary_hash) != T_HASH) { + rb_raise(rb_eTypeError, "md_ary_convert: got <%s>, want <Hash>", + rb_obj_classname(md_ary_hash)); + return; } - return Qnil; + /* Initialize the array, compute it's capacity, then fill it. */ + grpc_metadata_array_init(md_ary); + md_ary_obj = Data_Wrap_Struct(rb_cMdAry, GC_NOT_MARKED, GC_DONT_FREE, md_ary); + rb_hash_foreach(md_ary_hash, grpc_rb_md_ary_capacity_hash_cb, md_ary_obj); + md_ary->metadata = gpr_malloc(md_ary->capacity * sizeof(grpc_metadata)); + rb_hash_foreach(md_ary_hash, grpc_rb_md_ary_fill_hash_cb, md_ary_obj); } -/* Queue a status for writing. - - call-seq: - tag = Object.new - call.write_status(200, "OK", tag) - - REQUIRES: No other writes are pending on the call. It is only safe to - start the next write after the corresponding write_accepted event - is received. - GRPC_INVOKE_ACCEPTED must have been received by the application - prior to calling this. - Only callable on the server. - Produces a GRPC_FINISHED event when the status is sent and the stream is - fully closed */ -static VALUE grpc_rb_call_start_write_status(VALUE self, VALUE code, - VALUE status, VALUE tag) { - grpc_call *call = NULL; - grpc_call_error err; - Data_Get_Struct(self, grpc_call, call); - err = grpc_call_start_write_status_old(call, NUM2UINT(code), - StringValueCStr(status), ROBJECT(tag)); - if (err != GRPC_CALL_OK) { - rb_raise(rb_eCallError, "start write status: %s (code=%d)", - grpc_call_error_detail_of(err), err); +/* Converts a metadata array to a hash. */ +VALUE grpc_rb_md_ary_to_h(grpc_metadata_array *md_ary) { + VALUE key = Qnil; + VALUE new_ary = Qnil; + VALUE value = Qnil; + VALUE result = rb_hash_new(); + size_t i; + + for (i = 0; i < md_ary->count; i++) { + key = rb_str_new2(md_ary->metadata[i].key); + value = rb_hash_aref(result, key); + if (value == Qnil) { + value = rb_str_new(md_ary->metadata[i].value, + md_ary->metadata[i].value_length); + rb_hash_aset(result, key, value); + } else if (TYPE(value) == T_ARRAY) { + /* Add the string to the returned array */ + rb_ary_push(value, + rb_str_new(md_ary->metadata[i].value, + md_ary->metadata[i].value_length)); + } else { + /* Add the current value with this key and the new one to an array */ + new_ary = rb_ary_new(); + rb_ary_push(new_ary, value); + rb_ary_push(new_ary, + rb_str_new(md_ary->metadata[i].value, + md_ary->metadata[i].value_length)); + rb_hash_aset(result, key, new_ary); + } } + return result; +} - return Qnil; +/* grpc_rb_call_check_op_keys_hash_cb is a hash iteration func that checks + each key of an ops hash is valid. +*/ +int grpc_rb_call_check_op_keys_hash_cb(VALUE key, VALUE val, VALUE ops_ary) { + /* Update the capacity; the value is an array, add capacity for each value in + * the array */ + if (TYPE(key) != T_FIXNUM) { + rb_raise(rb_eTypeError, "invalid operation : got <%s>, want <Fixnum>", + rb_obj_classname(key)); + return ST_STOP; + } + switch(NUM2INT(key)) { + case GRPC_OP_SEND_INITIAL_METADATA: + case GRPC_OP_SEND_MESSAGE: + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + case GRPC_OP_SEND_STATUS_FROM_SERVER: + case GRPC_OP_RECV_INITIAL_METADATA: + case GRPC_OP_RECV_MESSAGE: + case GRPC_OP_RECV_STATUS_ON_CLIENT: + case GRPC_OP_RECV_CLOSE_ON_SERVER: + rb_ary_push(ops_ary, key); + return ST_CONTINUE; + default: + rb_raise(rb_eTypeError, "invalid operation : bad value %d", + NUM2INT(key)); + }; + return ST_STOP; } -/* No more messages to send. - REQUIRES: No other writes are pending on the call. */ -static VALUE grpc_rb_call_writes_done(VALUE self, VALUE tag) { - grpc_call *call = NULL; - grpc_call_error err; - Data_Get_Struct(self, grpc_call, call); - err = grpc_call_writes_done_old(call, ROBJECT(tag)); - if (err != GRPC_CALL_OK) { - rb_raise(rb_eCallError, "writes done: %s (code=%d)", - grpc_call_error_detail_of(err), err); +/* grpc_rb_op_update_status_from_server adds the values in a ruby status + struct to the 'send_status_from_server' portion of an op. +*/ +void grpc_rb_op_update_status_from_server(grpc_op *op, + grpc_metadata_array* md_ary, + VALUE status) { + VALUE code = rb_struct_aref(status, sym_code); + VALUE details = rb_struct_aref(status, sym_details); + VALUE metadata_hash = rb_struct_aref(status, sym_metadata); + + /* TODO: add check to ensure status is the correct struct type */ + if (TYPE(code) != T_FIXNUM) { + rb_raise(rb_eTypeError, "invalid code : got <%s>, want <Fixnum>", + rb_obj_classname(code)); + return; + } + if (TYPE(details) != T_STRING) { + rb_raise(rb_eTypeError, "invalid details : got <%s>, want <String>", + rb_obj_classname(code)); + return; } + op->data.send_status_from_server.status = NUM2INT(code); + op->data.send_status_from_server.status_details = StringValueCStr(details); + grpc_rb_md_ary_convert(metadata_hash, md_ary); + op->data.send_status_from_server.trailing_metadata_count = md_ary->count; + op->data.send_status_from_server.trailing_metadata = md_ary->metadata; +} - return Qnil; +/* run_batch_stack holds various values used by the + * grpc_rb_call_run_batch function */ +typedef struct run_batch_stack { + /* The batch ops */ + grpc_op ops[8]; /* 8 is the maximum number of operations */ + size_t op_num; /* tracks the last added operation */ + + /* Data being sent */ + grpc_metadata_array send_metadata; + grpc_metadata_array send_trailing_metadata; + + /* Data being received */ + grpc_byte_buffer *recv_message; + grpc_metadata_array recv_metadata; + grpc_metadata_array recv_trailing_metadata; + int recv_cancelled; + grpc_status_code recv_status; + char *recv_status_details; + size_t recv_status_details_capacity; +} run_batch_stack; + +/* grpc_run_batch_stack_init ensures the run_batch_stack is properly + * initialized */ +static void grpc_run_batch_stack_init(run_batch_stack* st) { + MEMZERO(st, run_batch_stack, 1); + grpc_metadata_array_init(&st->send_metadata); + grpc_metadata_array_init(&st->send_trailing_metadata); + grpc_metadata_array_init(&st->recv_metadata); + grpc_metadata_array_init(&st->recv_trailing_metadata); + st->op_num = 0; } -/* call-seq: - call.server_end_initial_metadata(flag) - - Only to be called on servers, before sending messages. - flags is a bit-field combination of the write flags defined above. - - REQUIRES: Can be called at most once per call. - Can only be called on the server, must be called after - grpc_call_server_accept - Produces no events */ -static VALUE grpc_rb_call_server_end_initial_metadata(int argc, VALUE *argv, - VALUE self) { - VALUE flags = Qnil; - grpc_call *call = NULL; - grpc_call_error err; +/* grpc_run_batch_stack_cleanup ensures the run_batch_stack is properly + * cleaned up */ +static void grpc_run_batch_stack_cleanup(run_batch_stack* st) { + grpc_metadata_array_destroy(&st->send_metadata); + grpc_metadata_array_destroy(&st->send_trailing_metadata); + grpc_metadata_array_destroy(&st->recv_metadata); + grpc_metadata_array_destroy(&st->recv_trailing_metadata); + if (st->recv_status_details != NULL) { + gpr_free(st->recv_status_details); + } +} - /* "01" == 1 (flags) is optional */ - rb_scan_args(argc, argv, "01", &flags); - if (NIL_P(flags)) { - flags = UINT2NUM(0); /* Default to no flags */ +/* grpc_run_batch_stack_fill_ops fills the run_batch_stack ops array from + * ops_hash */ +static void grpc_run_batch_stack_fill_ops(run_batch_stack* st, VALUE ops_hash) { + VALUE this_op = Qnil; + VALUE this_value = Qnil; + VALUE ops_ary = rb_ary_new(); + size_t i = 0; + + /* Create a ruby array with just the operation keys */ + rb_hash_foreach(ops_hash, grpc_rb_call_check_op_keys_hash_cb, ops_ary); + + /* Fill the ops array */ + for (i = 0; i < (size_t)RARRAY_LEN(ops_ary); i++) { + this_op = rb_ary_entry(ops_ary, i); + this_value = rb_hash_aref(ops_hash, this_op); + switch(NUM2INT(this_op)) { + case GRPC_OP_SEND_INITIAL_METADATA: + /* N.B. later there is no need to explicitly delete the metadata keys + * and values, they are references to data in ruby objects. */ + grpc_rb_md_ary_convert(this_value, &st->send_metadata); + st->ops[st->op_num].data.send_initial_metadata.count = + st->send_metadata.count; + st->ops[st->op_num].data.send_initial_metadata.metadata = + st->send_metadata.metadata; + break; + case GRPC_OP_SEND_MESSAGE: + st->ops[st->op_num].data.send_message = + grpc_rb_s_to_byte_buffer(RSTRING_PTR(this_value), + RSTRING_LEN(this_value)); + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + /* N.B. later there is no need to explicitly delete the metadata keys + * and values, they are references to data in ruby objects. */ + grpc_rb_op_update_status_from_server(&st->ops[st->op_num], + &st->send_trailing_metadata, + this_value); + break; + case GRPC_OP_RECV_INITIAL_METADATA: + st->ops[st->op_num].data.recv_initial_metadata = &st->recv_metadata; + break; + case GRPC_OP_RECV_MESSAGE: + st->ops[st->op_num].data.recv_message = &st->recv_message; + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + st->ops[st->op_num].data.recv_status_on_client.trailing_metadata = + &st->recv_trailing_metadata; + st->ops[st->op_num].data.recv_status_on_client.status = + &st->recv_status; + st->ops[st->op_num].data.recv_status_on_client.status_details = + &st->recv_status_details; + st->ops[st->op_num].data.recv_status_on_client.status_details_capacity = + &st->recv_status_details_capacity; + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + st->ops[st->op_num].data.recv_close_on_server.cancelled = + &st->recv_cancelled; + break; + default: + grpc_run_batch_stack_cleanup(st); + rb_raise(rb_eTypeError, "invalid operation : bad value %d", + NUM2INT(this_op)); + }; + st->ops[st->op_num].op = (grpc_op_type)NUM2INT(this_op); + st->op_num++; } - Data_Get_Struct(self, grpc_call, call); - err = grpc_call_server_end_initial_metadata_old(call, NUM2UINT(flags)); - if (err != GRPC_CALL_OK) { - rb_raise(rb_eCallError, "end_initial_metadata failed: %s (code=%d)", - grpc_call_error_detail_of(err), err); +} + +/* grpc_run_batch_stack_build_result fills constructs a ruby BatchResult struct + after the results have run */ +static VALUE grpc_run_batch_stack_build_result(run_batch_stack* st) { + size_t i = 0; + VALUE result = rb_struct_new(rb_sBatchResult, Qnil, Qnil, Qnil, Qnil, Qnil, + Qnil, Qnil, Qnil, NULL); + for (i = 0; i < st->op_num; i++) { + switch(st->ops[i].op) { + case GRPC_OP_SEND_INITIAL_METADATA: + rb_struct_aset(result, sym_send_metadata, Qtrue); + break; + case GRPC_OP_SEND_MESSAGE: + rb_struct_aset(result, sym_send_message, Qtrue); + grpc_byte_buffer_destroy(st->ops[i].data.send_message); + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + rb_struct_aset(result, sym_send_close, Qtrue); + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + rb_struct_aset(result, sym_send_status, Qtrue); + break; + case GRPC_OP_RECV_INITIAL_METADATA: + rb_struct_aset(result, sym_metadata, + grpc_rb_md_ary_to_h(&st->recv_metadata)); + case GRPC_OP_RECV_MESSAGE: + rb_struct_aset(result, sym_message, + grpc_rb_byte_buffer_to_s(st->recv_message)); + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + rb_struct_aset( + result, + sym_status, + rb_struct_new(rb_sStatus, + UINT2NUM(st->recv_status), + (st->recv_status_details == NULL + ? Qnil + : rb_str_new2(st->recv_status_details)), + grpc_rb_md_ary_to_h(&st->recv_trailing_metadata), + NULL)); + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + rb_struct_aset(result, sym_send_close, Qtrue); + break; + default: + break; + } } - return Qnil; + return result; } /* call-seq: - call.server_accept(completion_queue, finished_tag) - - Accept an incoming RPC, binding a completion queue to it. - To be called before sending or receiving messages. - - REQUIRES: Can be called at most once per call. - Can only be called on the server. - Produces a GRPC_FINISHED event with finished_tag when the call has been - completed (there may be other events for the call pending at this - time) */ -static VALUE grpc_rb_call_server_accept(VALUE self, VALUE cqueue, - VALUE finished_tag) { + cq = CompletionQueue.new + ops = { + GRPC::Core::CallOps::SEND_INITIAL_METADATA => <op_value>, + GRPC::Core::CallOps::SEND_MESSAGE => <op_value>, + ... + } + tag = Object.new + timeout = 10 + call.start_batch(cqueue, tag, timeout, ops) + + Start a batch of operations defined in the array ops; when complete, post a + completion of type 'tag' to the completion queue bound to the call. + + Also waits for the batch to complete, until timeout is reached. + The order of ops specified in the batch has no significance. + Only one operation of each type can be active at once in any given + batch */ +static VALUE grpc_rb_call_run_batch(VALUE self, VALUE cqueue, VALUE tag, + VALUE timeout, VALUE ops_hash) { + run_batch_stack st; grpc_call *call = NULL; - grpc_completion_queue *cq = grpc_rb_get_wrapped_completion_queue(cqueue); + grpc_event *ev = NULL; grpc_call_error err; + VALUE result = Qnil; Data_Get_Struct(self, grpc_call, call); - err = grpc_call_server_accept_old(call, cq, ROBJECT(finished_tag)); + + /* Validate the ops args, adding them to a ruby array */ + if (TYPE(ops_hash) != T_HASH) { + rb_raise(rb_eTypeError, "call#run_batch: ops hash should be a hash"); + return Qnil; + } + grpc_run_batch_stack_init(&st); + grpc_run_batch_stack_fill_ops(&st, ops_hash); + + /* call grpc_call_start_batch, then wait for it to complete using + * pluck_event */ + err = grpc_call_start_batch(call, st.ops, st.op_num, ROBJECT(tag)); if (err != GRPC_CALL_OK) { - rb_raise(rb_eCallError, "server_accept failed: %s (code=%d)", + grpc_run_batch_stack_cleanup(&st); + rb_raise(rb_eCallError, "grpc_call_start_batch failed with %s (code=%d)", grpc_call_error_detail_of(err), err); + return; + } + ev = grpc_rb_completion_queue_pluck_event(cqueue, tag, timeout); + if (ev == NULL) { + grpc_run_batch_stack_cleanup(&st); + rb_raise(rb_eOutOfTime, "grpc_call_start_batch timed out"); + return; + } + if (ev->data.op_complete != GRPC_OP_OK) { + grpc_run_batch_stack_cleanup(&st); + rb_raise(rb_eCallError, "start_batch completion failed, (code=%d)", + ev->data.op_complete); + return; } - /* Add the completion queue as an instance attribute, prevents it from being - * GCed until this call object is GCed */ - rb_ivar_set(self, id_cq, cqueue); - return Qnil; + /* Build and return the BatchResult struct result */ + result = grpc_run_batch_stack_build_result(&st); + grpc_run_batch_stack_cleanup(&st); + return result; } /* rb_cCall is the ruby class that proxies grpc_call. */ @@ -449,6 +586,10 @@ VALUE rb_cCall = Qnil; operations; */ VALUE rb_eCallError = Qnil; +/* rb_eOutOfTime is the ruby class of the exception thrown to indicate + a timeout. */ +VALUE rb_eOutOfTime = Qnil; + void Init_grpc_error_codes() { /* Constants representing the error codes of grpc_call_error in grpc.h */ VALUE rb_RpcErrors = rb_define_module_under(rb_mGrpcCore, "RpcErrors"); @@ -500,11 +641,35 @@ void Init_grpc_error_codes() { rb_obj_freeze(rb_error_code_details); } +void Init_grpc_op_codes() { + /* Constants representing operation type codes in grpc.h */ + VALUE rb_CallOps = rb_define_module_under(rb_mGrpcCore, "CallOps"); + rb_define_const(rb_CallOps, "SEND_INITIAL_METADATA", + UINT2NUM(GRPC_OP_SEND_INITIAL_METADATA)); + rb_define_const(rb_CallOps, "SEND_MESSAGE", UINT2NUM(GRPC_OP_SEND_MESSAGE)); + rb_define_const(rb_CallOps, "SEND_CLOSE_FROM_CLIENT", + UINT2NUM(GRPC_OP_SEND_CLOSE_FROM_CLIENT)); + rb_define_const(rb_CallOps, "SEND_STATUS_FROM_SERVER", + UINT2NUM(GRPC_OP_SEND_STATUS_FROM_SERVER)); + rb_define_const(rb_CallOps, "RECV_INITIAL_METADATA", + UINT2NUM(GRPC_OP_RECV_INITIAL_METADATA)); + rb_define_const(rb_CallOps, "RECV_MESSAGE", + UINT2NUM(GRPC_OP_RECV_MESSAGE)); + rb_define_const(rb_CallOps, "RECV_STATUS_ON_CLIENT", + UINT2NUM(GRPC_OP_RECV_STATUS_ON_CLIENT)); + rb_define_const(rb_CallOps, "RECV_CLOSE_ON_SERVER", + UINT2NUM(GRPC_OP_RECV_CLOSE_ON_SERVER)); +} + void Init_grpc_call() { /* CallError inherits from Exception to signal that it is non-recoverable */ rb_eCallError = rb_define_class_under(rb_mGrpcCore, "CallError", rb_eException); + rb_eOutOfTime = + rb_define_class_under(rb_mGrpcCore, "OutOfTime", rb_eException); rb_cCall = rb_define_class_under(rb_mGrpcCore, "Call", rb_cObject); + rb_cMdAry = rb_define_class_under(rb_mGrpcCore, "MetadataArray", + rb_cObject); /* Prevent allocation or inialization of the Call class */ rb_define_alloc_func(rb_cCall, grpc_rb_cannot_alloc); @@ -512,17 +677,8 @@ void Init_grpc_call() { rb_define_method(rb_cCall, "initialize_copy", grpc_rb_cannot_init_copy, 1); /* Add ruby analogues of the Call methods. */ - rb_define_method(rb_cCall, "server_accept", grpc_rb_call_server_accept, 2); - rb_define_method(rb_cCall, "server_end_initial_metadata", - grpc_rb_call_server_end_initial_metadata, -1); - rb_define_method(rb_cCall, "add_metadata", grpc_rb_call_add_metadata, -1); + rb_define_method(rb_cCall, "run_batch", grpc_rb_call_run_batch, 4); rb_define_method(rb_cCall, "cancel", grpc_rb_call_cancel, 0); - rb_define_method(rb_cCall, "invoke", grpc_rb_call_invoke, -1); - rb_define_method(rb_cCall, "start_read", grpc_rb_call_start_read, 1); - rb_define_method(rb_cCall, "start_write", grpc_rb_call_start_write, -1); - rb_define_method(rb_cCall, "start_write_status", - grpc_rb_call_start_write_status, 3); - rb_define_method(rb_cCall, "writes_done", grpc_rb_call_writes_done, 1); rb_define_method(rb_cCall, "status", grpc_rb_call_get_status, 0); rb_define_method(rb_cCall, "status=", grpc_rb_call_set_status, 1); rb_define_method(rb_cCall, "metadata", grpc_rb_call_get_metadata, 0); @@ -537,12 +693,35 @@ void Init_grpc_call() { id_flags = rb_intern("__flags"); id_input_md = rb_intern("__input_md"); + /* Ids used in constructing the batch result. */ + sym_send_message = ID2SYM(rb_intern("send_message")); + sym_send_metadata = ID2SYM(rb_intern("send_metadata")); + sym_send_close = ID2SYM(rb_intern("send_close")); + sym_send_status = ID2SYM(rb_intern("send_status")); + sym_message = ID2SYM(rb_intern("message")); + sym_status = ID2SYM(rb_intern("status")); + sym_cancelled = ID2SYM(rb_intern("cancelled")); + + /* The Struct used to return the run_batch result. */ + rb_sBatchResult = rb_struct_define( + "BatchResult", + "send_message", + "send_metadata", + "send_close", + "send_status", + "message", + "metadata", + "status", + "cancelled", + NULL); + /* The hash for reference counting calls, to ensure they can't be destroyed * more than once */ hash_all_calls = rb_hash_new(); rb_define_const(rb_cCall, "INTERNAL_ALL_CALLs", hash_all_calls); Init_grpc_error_codes(); + Init_grpc_op_codes(); } /* Gets the call from the ruby object */ diff --git a/src/ruby/ext/grpc/rb_call.h b/src/ruby/ext/grpc/rb_call.h index bb51759a46..03e570863c 100644 --- a/src/ruby/ext/grpc/rb_call.h +++ b/src/ruby/ext/grpc/rb_call.h @@ -46,13 +46,20 @@ VALUE grpc_rb_wrap_call(grpc_call* c); /* Provides the details of an call error */ const char* grpc_call_error_detail_of(grpc_call_error err); +/* Converts a metadata array to a hash. */ +VALUE grpc_rb_md_ary_to_h(grpc_metadata_array *md_ary); + /* rb_cCall is the Call class whose instances proxy grpc_call. */ extern VALUE rb_cCall; -/* rb_cCallError is the ruby class of the exception thrown during call +/* rb_eCallError is the ruby class of the exception thrown during call operations. */ extern VALUE rb_eCallError; +/* rb_eOutOfTime is the ruby class of the exception thrown to indicate + a timeout. */ +extern VALUE rb_eOutOfTime; + /* Initializes the Call class. */ void Init_grpc_call(); diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c index 2a48f46ce2..76a65fe6fe 100644 --- a/src/ruby/ext/grpc/rb_channel.c +++ b/src/ruby/ext/grpc/rb_channel.c @@ -49,10 +49,16 @@ static ID id_channel; /* id_target is the name of the hidden ivar that preserves a reference to the - * target string used to create the call, preserved so that is does not get + * target string used to create the call, preserved so that it does not get * GCed before the channel */ static ID id_target; +/* id_cqueue is the name of the hidden ivar that preserves a reference to the + * completion queue used to create the call, preserved so that it does not get + * GCed before the channel */ +static ID id_cqueue; + + /* Used during the conversion of a hash to channel args during channel setup */ static VALUE rb_cChannelArgs; @@ -142,6 +148,7 @@ static VALUE grpc_rb_channel_init(int argc, VALUE *argv, VALUE self) { if (ch == NULL) { rb_raise(rb_eRuntimeError, "could not create an rpc channel to target:%s", target_chars); + return Qnil; } rb_ivar_set(self, id_target, target); wrapper->wrapped = ch; @@ -164,6 +171,7 @@ static VALUE grpc_rb_channel_init_copy(VALUE copy, VALUE orig) { if (TYPE(orig) != T_DATA || RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_channel_free) { rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cChannel)); + return Qnil; } Data_Get_Struct(orig, grpc_rb_channel, orig_ch); @@ -177,34 +185,42 @@ static VALUE grpc_rb_channel_init_copy(VALUE copy, VALUE orig) { /* Create a call given a grpc_channel, in order to call method. The request is not sent until grpc_call_invoke is called. */ -static VALUE grpc_rb_channel_create_call(VALUE self, VALUE method, VALUE host, - VALUE deadline) { +static VALUE grpc_rb_channel_create_call(VALUE self, VALUE cqueue, VALUE method, + VALUE host, VALUE deadline) { VALUE res = Qnil; grpc_rb_channel *wrapper = NULL; - grpc_channel *ch = NULL; grpc_call *call = NULL; + grpc_channel *ch = NULL; + grpc_completion_queue *cq = NULL; char *method_chars = StringValueCStr(method); char *host_chars = StringValueCStr(host); + cq = grpc_rb_get_wrapped_completion_queue(cqueue); Data_Get_Struct(self, grpc_rb_channel, wrapper); ch = wrapper->wrapped; if (ch == NULL) { rb_raise(rb_eRuntimeError, "closed!"); + return Qnil; } call = - grpc_channel_create_call_old(ch, method_chars, host_chars, - grpc_rb_time_timeval(deadline, - /* absolute time */ 0)); + grpc_channel_create_call(ch, cq, method_chars, host_chars, + grpc_rb_time_timeval(deadline, + /* absolute time */ 0)); if (call == NULL) { rb_raise(rb_eRuntimeError, "cannot create call with method %s", method_chars); + return Qnil; } res = grpc_rb_wrap_call(call); - /* Make this channel an instance attribute of the call so that is is not GCed + /* Make this channel an instance attribute of the call so that it is not GCed * before the call. */ rb_ivar_set(res, id_channel, self); + + /* Make the completion queue an instance attribute of the call so that it is + * not GCed before the call. */ + rb_ivar_set(res, id_cqueue, cqueue); return res; } @@ -240,11 +256,12 @@ void Init_grpc_channel() { 1); /* Add ruby analogues of the Channel methods. */ - rb_define_method(rb_cChannel, "create_call", grpc_rb_channel_create_call, 3); + rb_define_method(rb_cChannel, "create_call", grpc_rb_channel_create_call, 4); rb_define_method(rb_cChannel, "destroy", grpc_rb_channel_destroy, 0); rb_define_alias(rb_cChannel, "close", "destroy"); id_channel = rb_intern("__channel"); + id_cqueue = rb_intern("__cqueue"); id_target = rb_intern("__target"); rb_define_const(rb_cChannel, "SSL_TARGET", ID2SYM(rb_intern(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG))); diff --git a/src/ruby/ext/grpc/rb_completion_queue.c b/src/ruby/ext/grpc/rb_completion_queue.c index 3fdbdd837a..688db94851 100644 --- a/src/ruby/ext/grpc/rb_completion_queue.c +++ b/src/ruby/ext/grpc/rb_completion_queue.c @@ -38,7 +38,6 @@ #include <grpc/grpc.h> #include <grpc/support/time.h> #include "rb_grpc.h" -#include "rb_event.h" /* Used to allow grpc_completion_queue_next call to release the GIL */ typedef struct next_call_stack { @@ -140,8 +139,19 @@ static VALUE grpc_rb_completion_queue_next(VALUE self, VALUE timeout) { /* Blocks until the next event for given tag is available, and returns the * event. */ -static VALUE grpc_rb_completion_queue_pluck(VALUE self, VALUE tag, - VALUE timeout) { +VALUE grpc_rb_completion_queue_pluck(VALUE self, VALUE tag, + VALUE timeout) { + grpc_event *ev = grpc_rb_completion_queue_pluck_event(self, tag, timeout); + if (ev == NULL) { + return Qnil; + } + return grpc_rb_new_event(ev); +} + +/* Blocks until the next event for given tag is available, and returns the + * event. */ +grpc_event* grpc_rb_completion_queue_pluck_event(VALUE self, VALUE tag, + VALUE timeout) { next_call_stack next_call; MEMZERO(&next_call, next_call_stack, 1); Data_Get_Struct(self, grpc_completion_queue, next_call.cq); @@ -151,9 +161,9 @@ static VALUE grpc_rb_completion_queue_pluck(VALUE self, VALUE tag, rb_thread_call_without_gvl(grpc_rb_completion_queue_pluck_no_gil, (void *)&next_call, NULL, NULL); if (next_call.event == NULL) { - return Qnil; + return NULL; } - return grpc_rb_new_event(next_call.event); + return next_call.event; } /* rb_cCompletionQueue is the ruby class that proxies grpc_completion_queue. */ diff --git a/src/ruby/ext/grpc/rb_completion_queue.h b/src/ruby/ext/grpc/rb_completion_queue.h index 38025ea2d2..13715ccaa7 100644 --- a/src/ruby/ext/grpc/rb_completion_queue.h +++ b/src/ruby/ext/grpc/rb_completion_queue.h @@ -40,6 +40,14 @@ /* Gets the wrapped completion queue from the ruby wrapper */ grpc_completion_queue *grpc_rb_get_wrapped_completion_queue(VALUE v); +/** + * Makes the implementation of CompletionQueue#pluck available in other files + * + * This avoids having code that holds the GIL repeated at multiple sites. + */ +grpc_event* grpc_rb_completion_queue_pluck_event(VALUE cqueue, VALUE tag, + VALUE timeout); + /* rb_cCompletionQueue is the CompletionQueue class whose instances proxy grpc_completion_queue. */ extern VALUE rb_cCompletionQueue; diff --git a/src/ruby/ext/grpc/rb_event.c b/src/ruby/ext/grpc/rb_event.c deleted file mode 100644 index 2e64af4c84..0000000000 --- a/src/ruby/ext/grpc/rb_event.c +++ /dev/null @@ -1,361 +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 "rb_event.h" - -#include <ruby.h> - -#include <grpc/grpc.h> -#include "rb_grpc.h" -#include "rb_byte_buffer.h" -#include "rb_call.h" -#include "rb_metadata.h" - -/* grpc_rb_event wraps a grpc_event. It provides a peer ruby object, - * 'mark' to minimize copying when an event is created from ruby. */ -typedef struct grpc_rb_event { - /* Holder of ruby objects involved in constructing the channel */ - VALUE mark; - /* The actual event */ - grpc_event *wrapped; -} grpc_rb_event; - -/* rb_mCompletionType is a ruby module that holds the completion type values */ -VALUE rb_mCompletionType = Qnil; - -/* Destroys Event instances. */ -static void grpc_rb_event_free(void *p) { - grpc_rb_event *ev = NULL; - if (p == NULL) { - return; - }; - ev = (grpc_rb_event *)p; - - /* Deletes the wrapped object if the mark object is Qnil, which indicates - * that no other object is the actual owner. */ - if (ev->wrapped != NULL && ev->mark == Qnil) { - grpc_event_finish(ev->wrapped); - rb_warning("event gc: destroyed the c event"); - } else { - rb_warning("event gc: did not destroy the c event"); - } - - xfree(p); -} - -/* Protects the mark object from GC */ -static void grpc_rb_event_mark(void *p) { - grpc_rb_event *event = NULL; - if (p == NULL) { - return; - } - event = (grpc_rb_event *)p; - if (event->mark != Qnil) { - rb_gc_mark(event->mark); - } -} - -static VALUE grpc_rb_event_result(VALUE self); - -/* Obtains the type of an event. */ -static VALUE grpc_rb_event_type(VALUE self) { - grpc_event *event = NULL; - grpc_rb_event *wrapper = NULL; - Data_Get_Struct(self, grpc_rb_event, wrapper); - if (wrapper->wrapped == NULL) { - rb_raise(rb_eRuntimeError, "finished!"); - return Qnil; - } - - event = wrapper->wrapped; - switch (event->type) { - case GRPC_QUEUE_SHUTDOWN: - return rb_const_get(rb_mCompletionType, rb_intern("QUEUE_SHUTDOWN")); - - case GRPC_READ: - return rb_const_get(rb_mCompletionType, rb_intern("READ")); - - case GRPC_WRITE_ACCEPTED: - grpc_rb_event_result(self); /* validates the result */ - return rb_const_get(rb_mCompletionType, rb_intern("WRITE_ACCEPTED")); - - case GRPC_FINISH_ACCEPTED: - grpc_rb_event_result(self); /* validates the result */ - return rb_const_get(rb_mCompletionType, rb_intern("FINISH_ACCEPTED")); - - case GRPC_CLIENT_METADATA_READ: - return rb_const_get(rb_mCompletionType, - rb_intern("CLIENT_METADATA_READ")); - - case GRPC_FINISHED: - return rb_const_get(rb_mCompletionType, rb_intern("FINISHED")); - - case GRPC_SERVER_RPC_NEW: - return rb_const_get(rb_mCompletionType, rb_intern("SERVER_RPC_NEW")); - - default: - rb_raise(rb_eRuntimeError, "unrecognized event code for an rpc event:%d", - event->type); - } - return Qnil; /* should not be reached */ -} - -/* Obtains the tag associated with an event. */ -static VALUE grpc_rb_event_tag(VALUE self) { - grpc_event *event = NULL; - grpc_rb_event *wrapper = NULL; - Data_Get_Struct(self, grpc_rb_event, wrapper); - if (wrapper->wrapped == NULL) { - rb_raise(rb_eRuntimeError, "finished!"); - return Qnil; - } - - event = wrapper->wrapped; - if (event->tag == NULL) { - return Qnil; - } - return (VALUE)event->tag; -} - -/* Obtains the call associated with an event. */ -static VALUE grpc_rb_event_call(VALUE self) { - grpc_event *event = NULL; - grpc_rb_event *wrapper = NULL; - Data_Get_Struct(self, grpc_rb_event, wrapper); - if (wrapper->wrapped == NULL) { - rb_raise(rb_eRuntimeError, "finished!"); - return Qnil; - } - - event = wrapper->wrapped; - if (event->call != NULL) { - return grpc_rb_wrap_call(event->call); - } - return Qnil; -} - -/* Obtains the metadata associated with an event. */ -static VALUE grpc_rb_event_metadata(VALUE self) { - grpc_event *event = NULL; - grpc_rb_event *wrapper = NULL; - grpc_metadata *metadata = NULL; - VALUE key = Qnil; - VALUE new_ary = Qnil; - VALUE result = Qnil; - VALUE value = Qnil; - size_t count = 0; - size_t i = 0; - Data_Get_Struct(self, grpc_rb_event, wrapper); - if (wrapper->wrapped == NULL) { - rb_raise(rb_eRuntimeError, "finished!"); - return Qnil; - } - - /* Figure out which metadata to read. */ - event = wrapper->wrapped; - switch (event->type) { - case GRPC_CLIENT_METADATA_READ: - count = event->data.client_metadata_read.count; - metadata = event->data.client_metadata_read.elements; - break; - - case GRPC_FINISHED: - count = event->data.finished.metadata_count; - metadata = event->data.finished.metadata_elements; - break; - - case GRPC_SERVER_RPC_NEW: - count = event->data.server_rpc_new.metadata_count; - metadata = event->data.server_rpc_new.metadata_elements; - break; - - default: - rb_raise(rb_eRuntimeError, - "bug: bad event type metadata. got %d; want %d|%d:%d", - event->type, GRPC_CLIENT_METADATA_READ, GRPC_FINISHED, - GRPC_SERVER_RPC_NEW); - return Qnil; - } - - result = rb_hash_new(); - for (i = 0; i < count; i++) { - key = rb_str_new2(metadata[i].key); - value = rb_hash_aref(result, key); - if (value == Qnil) { - value = rb_str_new(metadata[i].value, metadata[i].value_length); - rb_hash_aset(result, key, value); - } else if (TYPE(value) == T_ARRAY) { - /* Add the string to the returned array */ - rb_ary_push(value, - rb_str_new(metadata[i].value, metadata[i].value_length)); - } else { - /* Add the current value with this key and the new one to an array */ - new_ary = rb_ary_new(); - rb_ary_push(new_ary, value); - rb_ary_push(new_ary, - rb_str_new(metadata[i].value, metadata[i].value_length)); - rb_hash_aset(result, key, new_ary); - } - } - return result; -} - -/* Obtains the data associated with an event. */ -static VALUE grpc_rb_event_result(VALUE self) { - grpc_event *event = NULL; - grpc_rb_event *wrapper = NULL; - Data_Get_Struct(self, grpc_rb_event, wrapper); - if (wrapper->wrapped == NULL) { - rb_raise(rb_eRuntimeError, "finished!"); - return Qnil; - } - event = wrapper->wrapped; - - switch (event->type) { - case GRPC_QUEUE_SHUTDOWN: - return Qnil; - - case GRPC_READ: - return grpc_rb_byte_buffer_create_with_mark(self, event->data.read); - - case GRPC_FINISH_ACCEPTED: - if (event->data.finish_accepted == GRPC_OP_OK) { - return Qnil; - } - rb_raise(rb_eEventError, "finish failed, not sure why (code=%d)", - event->data.finish_accepted); - break; - - case GRPC_WRITE_ACCEPTED: - if (event->data.write_accepted == GRPC_OP_OK) { - return Qnil; - } - rb_raise(rb_eEventError, "write failed, not sure why (code=%d)", - event->data.write_accepted); - break; - - case GRPC_CLIENT_METADATA_READ: - return grpc_rb_event_metadata(self); - - case GRPC_FINISHED: - return rb_struct_new(rb_sStatus, UINT2NUM(event->data.finished.status), - (event->data.finished.details == NULL - ? Qnil - : rb_str_new2(event->data.finished.details)), - grpc_rb_event_metadata(self), NULL); - break; - - case GRPC_SERVER_RPC_NEW: - return rb_struct_new( - rb_sNewServerRpc, rb_str_new2(event->data.server_rpc_new.method), - rb_str_new2(event->data.server_rpc_new.host), - Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED, GC_DONT_FREE, - (void *)&event->data.server_rpc_new.deadline), - grpc_rb_event_metadata(self), NULL); - - default: - rb_raise(rb_eRuntimeError, "unrecognized event code for an rpc event:%d", - event->type); - } - - return Qfalse; -} - -static VALUE grpc_rb_event_finish(VALUE self) { - grpc_event *event = NULL; - grpc_rb_event *wrapper = NULL; - Data_Get_Struct(self, grpc_rb_event, wrapper); - if (wrapper->wrapped == NULL) { /* already closed */ - return Qnil; - } - event = wrapper->wrapped; - grpc_event_finish(event); - wrapper->wrapped = NULL; - wrapper->mark = Qnil; - return Qnil; -} - -/* rb_cEvent is the Event class whose instances proxy grpc_event */ -VALUE rb_cEvent = Qnil; - -/* rb_eEventError is the ruby class of the exception thrown on failures during - rpc event processing. */ -VALUE rb_eEventError = Qnil; - -void Init_grpc_event() { - rb_eEventError = - rb_define_class_under(rb_mGrpcCore, "EventError", rb_eStandardError); - rb_cEvent = rb_define_class_under(rb_mGrpcCore, "Event", rb_cObject); - - /* Prevent allocation or inialization from ruby. */ - rb_define_alloc_func(rb_cEvent, grpc_rb_cannot_alloc); - rb_define_method(rb_cEvent, "initialize", grpc_rb_cannot_init, 0); - rb_define_method(rb_cEvent, "initialize_copy", grpc_rb_cannot_init_copy, 1); - - /* Accessors for the data available in an event. */ - rb_define_method(rb_cEvent, "call", grpc_rb_event_call, 0); - rb_define_method(rb_cEvent, "result", grpc_rb_event_result, 0); - rb_define_method(rb_cEvent, "tag", grpc_rb_event_tag, 0); - rb_define_method(rb_cEvent, "type", grpc_rb_event_type, 0); - rb_define_method(rb_cEvent, "finish", grpc_rb_event_finish, 0); - rb_define_alias(rb_cEvent, "close", "finish"); - - /* Constants representing the completion types */ - rb_mCompletionType = - rb_define_module_under(rb_mGrpcCore, "CompletionType"); - rb_define_const(rb_mCompletionType, "QUEUE_SHUTDOWN", - INT2NUM(GRPC_QUEUE_SHUTDOWN)); - rb_define_const(rb_mCompletionType, "OP_COMPLETE", INT2NUM(GRPC_OP_COMPLETE)); - rb_define_const(rb_mCompletionType, "READ", INT2NUM(GRPC_READ)); - rb_define_const(rb_mCompletionType, "WRITE_ACCEPTED", - INT2NUM(GRPC_WRITE_ACCEPTED)); - rb_define_const(rb_mCompletionType, "FINISH_ACCEPTED", - INT2NUM(GRPC_FINISH_ACCEPTED)); - rb_define_const(rb_mCompletionType, "CLIENT_METADATA_READ", - INT2NUM(GRPC_CLIENT_METADATA_READ)); - rb_define_const(rb_mCompletionType, "FINISHED", INT2NUM(GRPC_FINISHED)); - rb_define_const(rb_mCompletionType, "SERVER_RPC_NEW", - INT2NUM(GRPC_SERVER_RPC_NEW)); - rb_define_const(rb_mCompletionType, "SERVER_SHUTDOWN", - INT2NUM(GRPC_SERVER_SHUTDOWN)); - rb_define_const(rb_mCompletionType, "RESERVED", - INT2NUM(GRPC_COMPLETION_DO_NOT_USE)); -} - -VALUE grpc_rb_new_event(grpc_event *ev) { - grpc_rb_event *wrapper = ALLOC(grpc_rb_event); - wrapper->wrapped = ev; - wrapper->mark = Qnil; - return Data_Wrap_Struct(rb_cEvent, grpc_rb_event_mark, grpc_rb_event_free, - wrapper); -} diff --git a/src/ruby/ext/grpc/rb_grpc.c b/src/ruby/ext/grpc/rb_grpc.c index 400efd0dfa..d4335dd1ba 100644 --- a/src/ruby/ext/grpc/rb_grpc.c +++ b/src/ruby/ext/grpc/rb_grpc.c @@ -39,12 +39,9 @@ #include <grpc/grpc.h> #include <grpc/support/time.h> -#include "rb_byte_buffer.h" #include "rb_call.h" #include "rb_channel.h" #include "rb_completion_queue.h" -#include "rb_event.h" -#include "rb_metadata.h" #include "rb_server.h" #include "rb_credentials.h" #include "rb_server_credentials.h" @@ -195,7 +192,7 @@ static ID id_inspect; /* id_to_s is the to_s method found on various ruby objects. */ static ID id_to_s; -/* Converts `a wrapped time constant to a standard time. */ +/* Converts a wrapped time constant to a standard time. */ VALUE grpc_rb_time_val_to_time(VALUE self) { gpr_timespec *time_const = NULL; Data_Get_Struct(self, gpr_timespec, time_const); @@ -257,16 +254,16 @@ void Init_grpc() { rb_mGRPC = rb_define_module("GRPC"); rb_mGrpcCore = rb_define_module_under(rb_mGRPC, "Core"); rb_sNewServerRpc = rb_struct_define("NewServerRpc", "method", "host", - "deadline", "metadata", NULL); + "deadline", "metadata", "call", NULL); rb_sStatus = rb_struct_define("Status", "code", "details", "metadata", NULL); + sym_code = ID2SYM(rb_intern("code")); + sym_details = ID2SYM(rb_intern("details")); + sym_metadata = ID2SYM(rb_intern("metadata")); - Init_grpc_byte_buffer(); - Init_grpc_event(); Init_grpc_channel(); Init_grpc_completion_queue(); Init_grpc_call(); Init_grpc_credentials(); - Init_grpc_metadata(); Init_grpc_server(); Init_grpc_server_credentials(); Init_grpc_status_codes(); diff --git a/src/ruby/ext/grpc/rb_grpc.h b/src/ruby/ext/grpc/rb_grpc.h index 851f5ee69f..4ff9b7f6e9 100644 --- a/src/ruby/ext/grpc/rb_grpc.h +++ b/src/ruby/ext/grpc/rb_grpc.h @@ -50,6 +50,15 @@ extern VALUE rb_sNewServerRpc; /* rb_sStruct is the struct that holds status details. */ extern VALUE rb_sStatus; +/* sym_code is the symbol for the code attribute of rb_sStatus. */ +VALUE sym_code; + +/* sym_details is the symbol for the details attribute of rb_sStatus. */ +VALUE sym_details; + +/* sym_metadata is the symbol for the metadata attribute of rb_sStatus. */ +VALUE sym_metadata; + /* GC_NOT_MARKED is used in calls to Data_Wrap_Struct to indicate that the wrapped struct does not need to participate in ruby gc. */ extern const RUBY_DATA_FUNC GC_NOT_MARKED; diff --git a/src/ruby/ext/grpc/rb_metadata.c b/src/ruby/ext/grpc/rb_metadata.c deleted file mode 100644 index 7622a8c57e..0000000000 --- a/src/ruby/ext/grpc/rb_metadata.c +++ /dev/null @@ -1,215 +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 "rb_metadata.h" - -#include <ruby.h> -#include <string.h> - -#include <grpc/grpc.h> -#include "rb_grpc.h" - -/* grpc_rb_metadata wraps a grpc_metadata. It provides a peer ruby object, - * 'mark' to minimize copying when a metadata is created from ruby. */ -typedef struct grpc_rb_metadata { - /* Holder of ruby objects involved in constructing the metadata */ - VALUE mark; - /* The actual metadata */ - grpc_metadata *wrapped; -} grpc_rb_metadata; - -/* Destroys Metadata instances. */ -static void grpc_rb_metadata_free(void *p) { - if (p == NULL) { - return; - }; - - /* Because metadata is only created during a call to grpc_call_add_metadata, - * and the call takes ownership of the metadata, this does not free the - * wrapped struct, only the wrapper */ - xfree(p); -} - -/* Protects the mark object from GC */ -static void grpc_rb_metadata_mark(void *p) { - grpc_rb_metadata *md = NULL; - if (p == NULL) { - return; - } - - md = (grpc_rb_metadata *)p; - /* If it's not already cleaned up, mark the mark object */ - if (md->mark != Qnil && BUILTIN_TYPE(md->mark) != T_NONE) { - rb_gc_mark(md->mark); - } -} - -/* Allocates Metadata instances. - - Provides safe default values for the Metadata fields. */ -static VALUE grpc_rb_metadata_alloc(VALUE cls) { - grpc_rb_metadata *wrapper = ALLOC(grpc_rb_metadata); - wrapper->wrapped = NULL; - wrapper->mark = Qnil; - return Data_Wrap_Struct(cls, grpc_rb_metadata_mark, grpc_rb_metadata_free, - wrapper); -} - -/* id_key and id_value are the names of the hidden ivars that preserve the - * original byte_buffer source string */ -static ID id_key; -static ID id_value; - -/* Initializes Metadata instances. */ -static VALUE grpc_rb_metadata_init(VALUE self, VALUE key, VALUE value) { - grpc_rb_metadata *wrapper = NULL; - grpc_metadata *md = ALLOC(grpc_metadata); - - /* Use direct pointers to the strings wrapped by the ruby object to avoid - * copying */ - Data_Get_Struct(self, grpc_rb_metadata, wrapper); - wrapper->wrapped = md; - if (TYPE(key) == T_SYMBOL) { - md->key = (char *)rb_id2name(SYM2ID(key)); - } else { /* StringValueCStr does all other type exclusions for us */ - md->key = StringValueCStr(key); - } - md->value = RSTRING_PTR(value); - md->value_length = RSTRING_LEN(value); - - /* Save references to the original values on the mark object so that the - * pointers used there are valid for the lifetime of the object. */ - wrapper->mark = rb_class_new_instance(0, NULL, rb_cObject); - rb_ivar_set(wrapper->mark, id_key, key); - rb_ivar_set(wrapper->mark, id_value, value); - - return self; -} - -/* Clones Metadata instances. - - Gives Metadata a consistent implementation of Ruby's object copy/dup - protocol. */ -static VALUE grpc_rb_metadata_init_copy(VALUE copy, VALUE orig) { - grpc_rb_metadata *orig_md = NULL; - grpc_rb_metadata *copy_md = NULL; - - if (copy == orig) { - return copy; - } - - /* Raise an error if orig is not a metadata object or a subclass. */ - if (TYPE(orig) != T_DATA || - RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_metadata_free) { - rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cMetadata)); - } - - Data_Get_Struct(orig, grpc_rb_metadata, orig_md); - Data_Get_Struct(copy, grpc_rb_metadata, copy_md); - - /* use ruby's MEMCPY to make a byte-for-byte copy of the metadata wrapper - * object. */ - MEMCPY(copy_md, orig_md, grpc_rb_metadata, 1); - return copy; -} - -/* Gets the key from a metadata instance. */ -static VALUE grpc_rb_metadata_key(VALUE self) { - VALUE key = Qnil; - grpc_rb_metadata *wrapper = NULL; - grpc_metadata *md = NULL; - - Data_Get_Struct(self, grpc_rb_metadata, wrapper); - if (wrapper->mark != Qnil) { - key = rb_ivar_get(wrapper->mark, id_key); - if (key != Qnil) { - return key; - } - } - - md = wrapper->wrapped; - if (md == NULL || md->key == NULL) { - return Qnil; - } - return rb_str_new2(md->key); -} - -/* Gets the value from a metadata instance. */ -static VALUE grpc_rb_metadata_value(VALUE self) { - VALUE val = Qnil; - grpc_rb_metadata *wrapper = NULL; - grpc_metadata *md = NULL; - - Data_Get_Struct(self, grpc_rb_metadata, wrapper); - if (wrapper->mark != Qnil) { - val = rb_ivar_get(wrapper->mark, id_value); - if (val != Qnil) { - return val; - } - } - - md = wrapper->wrapped; - if (md == NULL || md->value == NULL) { - return Qnil; - } - return rb_str_new2(md->value); -} - -/* rb_cMetadata is the Metadata class whose instances proxy grpc_metadata. */ -VALUE rb_cMetadata = Qnil; -void Init_grpc_metadata() { - rb_cMetadata = - rb_define_class_under(rb_mGrpcCore, "Metadata", rb_cObject); - - /* Allocates an object managed by the ruby runtime */ - rb_define_alloc_func(rb_cMetadata, grpc_rb_metadata_alloc); - - /* Provides a ruby constructor and support for dup/clone. */ - rb_define_method(rb_cMetadata, "initialize", grpc_rb_metadata_init, 2); - rb_define_method(rb_cMetadata, "initialize_copy", grpc_rb_metadata_init_copy, - 1); - - /* Provides accessors for the code and details. */ - rb_define_method(rb_cMetadata, "key", grpc_rb_metadata_key, 0); - rb_define_method(rb_cMetadata, "value", grpc_rb_metadata_value, 0); - - id_key = rb_intern("__key"); - id_value = rb_intern("__value"); -} - -/* Gets the wrapped metadata from the ruby wrapper */ -grpc_metadata *grpc_rb_get_wrapped_metadata(VALUE v) { - grpc_rb_metadata *wrapper = NULL; - Data_Get_Struct(v, grpc_rb_metadata, wrapper); - return wrapper->wrapped; -} diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c index c54f02e87a..9a49285db2 100644 --- a/src/ruby/ext/grpc/rb_server.c +++ b/src/ruby/ext/grpc/rb_server.c @@ -46,6 +46,9 @@ /* rb_cServer is the ruby class that proxies grpc_server. */ VALUE rb_cServer = Qnil; +/* id_at is the constructor method of the ruby standard Time class. */ +static ID id_at; + /* grpc_rb_server wraps a grpc_server. It provides a peer ruby object, 'mark' to minimize copying when a server is created from ruby. */ typedef struct grpc_rb_server { @@ -152,18 +155,89 @@ static VALUE grpc_rb_server_init_copy(VALUE copy, VALUE orig) { return copy; } -static VALUE grpc_rb_server_request_call(VALUE self, VALUE tag_new) { - grpc_call_error err; +/* request_call_stack holds various values used by the + * grpc_rb_server_request_call function */ +typedef struct request_call_stack { + grpc_call_details details; + grpc_metadata_array md_ary; +} request_call_stack; + +/* grpc_request_call_stack_init ensures the request_call_stack is properly + * initialized */ +static void grpc_request_call_stack_init(request_call_stack* st) { + MEMZERO(st, request_call_stack, 1); + grpc_metadata_array_init(&st->md_ary); + grpc_call_details_init(&st->details); + st->details.method = NULL; + st->details.host = NULL; +} + +/* grpc_request_call_stack_cleanup ensures the request_call_stack is properly + * cleaned up */ +static void grpc_request_call_stack_cleanup(request_call_stack* st) { + grpc_metadata_array_destroy(&st->md_ary); + grpc_call_details_destroy(&st->details); +} + +/* call-seq: + cq = CompletionQueue.new + tag = Object.new + timeout = 10 + server.request_call(cqueue, tag, timeout) + + Requests notification of a new call on a server. */ +static VALUE grpc_rb_server_request_call(VALUE self, VALUE cqueue, + VALUE tag_new, VALUE timeout) { grpc_rb_server *s = NULL; + grpc_call *call = NULL; + grpc_event *ev = NULL; + grpc_call_error err; + request_call_stack st; + VALUE result; Data_Get_Struct(self, grpc_rb_server, s); if (s->wrapped == NULL) { rb_raise(rb_eRuntimeError, "closed!"); + return Qnil; } else { - err = grpc_server_request_call_old(s->wrapped, ROBJECT(tag_new)); + grpc_request_call_stack_init(&st); + /* call grpc_server_request_call, then wait for it to complete using + * pluck_event */ + err = grpc_server_request_call( + s->wrapped, &call, &st.details, &st.md_ary, + grpc_rb_get_wrapped_completion_queue(cqueue), + ROBJECT(tag_new)); if (err != GRPC_CALL_OK) { - rb_raise(rb_eCallError, "server request failed: %s (code=%d)", + grpc_request_call_stack_cleanup(&st); + rb_raise(rb_eCallError, "grpc_server_request_call failed: %s (code=%d)", grpc_call_error_detail_of(err), err); + return Qnil; } + ev = grpc_rb_completion_queue_pluck_event(cqueue, tag_new, timeout); + if (ev == NULL) { + grpc_request_call_stack_cleanup(&st); + return Qnil; + } + if (ev->data.op_complete != GRPC_OP_OK) { + grpc_request_call_stack_cleanup(&st); + grpc_event_finish(ev); + rb_raise(rb_eCallError, "request_call completion failed: (code=%d)", + ev->data.op_complete); + return Qnil; + } + + /* build the NewServerRpc struct result */ + result = rb_struct_new( + rb_sNewServerRpc, + rb_str_new2(st.details.method), + rb_str_new2(st.details.host), + rb_funcall(rb_cTime, id_at, 2, INT2NUM(st.details.deadline.tv_sec), + INT2NUM(st.details.deadline.tv_nsec)), + grpc_rb_md_ary_to_h(&st.md_ary), + grpc_rb_wrap_call(call), + NULL); + grpc_event_finish(ev); + grpc_request_call_stack_cleanup(&st); + return result; } return Qnil; } @@ -249,12 +323,13 @@ void Init_grpc_server() { rb_define_method(rb_cServer, "initialize_copy", grpc_rb_server_init_copy, 1); /* Add the server methods. */ - rb_define_method(rb_cServer, "request_call", grpc_rb_server_request_call, 1); + rb_define_method(rb_cServer, "request_call", grpc_rb_server_request_call, 3); rb_define_method(rb_cServer, "start", grpc_rb_server_start, 0); rb_define_method(rb_cServer, "destroy", grpc_rb_server_destroy, 0); rb_define_alias(rb_cServer, "close", "destroy"); rb_define_method(rb_cServer, "add_http2_port", grpc_rb_server_add_http2_port, -1); + id_at = rb_intern("at"); } /* Gets the wrapped server from the ruby wrapper */ diff --git a/src/ruby/lib/grpc.rb b/src/ruby/lib/grpc.rb index dd02ef7666..b0f68035cd 100644 --- a/src/ruby/lib/grpc.rb +++ b/src/ruby/lib/grpc.rb @@ -31,7 +31,6 @@ require 'grpc/errors' require 'grpc/grpc' require 'grpc/logconfig' require 'grpc/version' -require 'grpc/core/event' require 'grpc/core/time_consts' require 'grpc/generic/active_call' require 'grpc/generic/client_stub' diff --git a/src/ruby/lib/grpc/core/event.rb b/src/ruby/lib/grpc/core/event.rb deleted file mode 100644 index 194aa8ecac..0000000000 --- a/src/ruby/lib/grpc/core/event.rb +++ /dev/null @@ -1,44 +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. - -require 'grpc' - -# GRPC contains the General RPC module. -module GRPC - module Core - # Event is a class defined in the c extension - # - # Here, we add an inspect method. - class Event - def inspect - "<#{self.class}: type:#{type}, tag:#{tag} result:#{result}>" - end - end - end -end diff --git a/src/ruby/lib/grpc/errors.rb b/src/ruby/lib/grpc/errors.rb index 58944872b5..b23793730f 100644 --- a/src/ruby/lib/grpc/errors.rb +++ b/src/ruby/lib/grpc/errors.rb @@ -31,10 +31,6 @@ require 'grpc' # GRPC contains the General RPC module. module GRPC - # OutOfTime is an exception class that indicates that an RPC exceeded its - # deadline. - OutOfTime = Class.new(StandardError) - # BadStatus is an exception class that indicates that an error occurred at # either end of a GRPC connection. When raised, it indicates that a status # error should be returned to the other end of a GRPC connection; when diff --git a/src/ruby/lib/grpc/generic/active_call.rb b/src/ruby/lib/grpc/generic/active_call.rb index 6256330e88..489349c2c9 100644 --- a/src/ruby/lib/grpc/generic/active_call.rb +++ b/src/ruby/lib/grpc/generic/active_call.rb @@ -30,20 +30,14 @@ require 'forwardable' require 'grpc/generic/bidi_call' -def assert_event_type(ev, want) - fail OutOfTime if ev.nil? - got = ev.type - fail "Unexpected rpc event: got #{got}, want #{want}" unless got == want -end - # GRPC contains the General RPC module. module GRPC # The ActiveCall class provides simple methods for sending marshallable # data to a call class ActiveCall - include Core::CompletionType include Core::StatusCodes include Core::TimeConsts + include Core::CallOps attr_reader(:deadline) # client_invoke begins a client invocation. @@ -61,15 +55,14 @@ module GRPC # @param q [CompletionQueue] the completion queue # @param deadline [Fixnum,TimeSpec] the deadline def self.client_invoke(call, q, _deadline, **kw) - fail(ArgumentError, 'not a call') unless call.is_a? Core::Call + fail(TypeError, '!Core::Call') unless call.is_a? Core::Call unless q.is_a? Core::CompletionQueue - fail(ArgumentError, 'not a CompletionQueue') + fail(TypeError, '!Core::CompletionQueue') end - call.add_metadata(kw) if kw.length > 0 - client_metadata_read = Object.new - finished_tag = Object.new - call.invoke(q, client_metadata_read, finished_tag) - [finished_tag, client_metadata_read] + metadata_tag = Object.new + call.run_batch(q, metadata_tag, INFINITE_FUTURE, + SEND_INITIAL_METADATA => kw) + metadata_tag end # Creates an ActiveCall. @@ -91,25 +84,21 @@ module GRPC # @param marshal [Function] f(obj)->string that marshal requests # @param unmarshal [Function] f(string)->obj that unmarshals responses # @param deadline [Fixnum] the deadline for the call to complete - # @param finished_tag [Object] the object used as the call's finish tag, - # if the call has begun - # @param read_metadata_tag [Object] the object used as the call's finish - # tag, if the call has begun + # @param metadata_tag [Object] the object use obtain metadata for clients # @param started [true|false] indicates if the call has begun - def initialize(call, q, marshal, unmarshal, deadline, finished_tag: nil, - read_metadata_tag: nil, started: true) - fail(ArgumentError, 'not a call') unless call.is_a? Core::Call + def initialize(call, q, marshal, unmarshal, deadline, started: true, + metadata_tag: nil) + fail(TypeError, '!Core::Call') unless call.is_a? Core::Call unless q.is_a? Core::CompletionQueue - fail(ArgumentError, 'not a CompletionQueue') + fail(TypeError, '!Core::CompletionQueue') end @call = call @cq = q @deadline = deadline - @finished_tag = finished_tag - @read_metadata_tag = read_metadata_tag @marshal = marshal @started = started @unmarshal = unmarshal + @metadata_tag = metadata_tag end # Obtains the status of the call. @@ -176,51 +165,38 @@ module GRPC # writes_done indicates that all writes are completed. # - # It blocks until the remote endpoint acknowledges by sending a FINISHED - # event, unless assert_finished is set to false. Any calls to - # #remote_send after this call will fail. + # It blocks until the remote endpoint acknowledges with at status unless + # assert_finished is set to false. Any calls to #remote_send after this + # call will fail. # # @param assert_finished [true, false] when true(default), waits for # FINISHED. def writes_done(assert_finished = true) - @call.writes_done(self) - ev = @cq.pluck(self, INFINITE_FUTURE) - begin - assert_event_type(ev, FINISH_ACCEPTED) - logger.debug("Writes done: waiting for finish? #{assert_finished}") - ensure - ev.close - end - + ops = { + SEND_CLOSE_FROM_CLIENT => nil + } + ops[RECV_STATUS_ON_CLIENT] = nil if assert_finished + @call.run_batch(@cq, self, INFINITE_FUTURE, ops) return unless assert_finished - ev = @cq.pluck(@finished_tag, INFINITE_FUTURE) - fail 'unexpected nil event' if ev.nil? - ev.close @call.status end - # finished waits until the call is completed. + # finished waits until a client call is completed. # - # It blocks until the remote endpoint acknowledges by sending a FINISHED - # event. + # It blocks until the remote endpoint acknowledges by sending a status. def finished - ev = @cq.pluck(@finished_tag, INFINITE_FUTURE) - begin - fail "unexpected event: #{ev.inspect}" unless ev.type == FINISHED - if @call.metadata.nil? - @call.metadata = ev.result.metadata - else - @call.metadata.merge!(ev.result.metadata) - end - - if ev.result.code != Core::StatusCodes::OK - fail BadStatus.new(ev.result.code, ev.result.details) - end - res = ev.result - ensure - ev.close + batch_result = @call.run_batch(@cq, self, INFINITE_FUTURE, + RECV_STATUS_ON_CLIENT => nil) + if @call.metadata.nil? + @call.metadata = batch_result.metadata + elsif !batch_result.metadata.nil? + @call.metadata.merge!(batch_result.metadata) end - res + if batch_result.status.code != Core::StatusCodes::OK + fail BadStatus.new(batch_result.status.code, + batch_result.status.details) + end + batch_result end # remote_send sends a request to the remote endpoint. @@ -232,72 +208,50 @@ module GRPC # @param marshalled [false, true] indicates if the object is already # marshalled. def remote_send(req, marshalled = false) - assert_queue_is_ready logger.debug("sending #{req.inspect}, marshalled? #{marshalled}") if marshalled payload = req else payload = @marshal.call(req) end - @call.start_write(Core::ByteBuffer.new(payload), self) - - # call queue#pluck, and wait for WRITE_ACCEPTED, so as not to return - # until the flow control allows another send on this call. - ev = @cq.pluck(self, INFINITE_FUTURE) - begin - assert_event_type(ev, WRITE_ACCEPTED) - ensure - ev.close - end + @call.run_batch(@cq, self, INFINITE_FUTURE, SEND_MESSAGE => payload) end - # send_status sends a status to the remote endpoint + # send_status sends a status to the remote endpoint. # # @param code [int] the status code to send # @param details [String] details # @param assert_finished [true, false] when true(default), waits for # FINISHED. def send_status(code = OK, details = '', assert_finished = false) - assert_queue_is_ready - @call.start_write_status(code, details, self) - ev = @cq.pluck(self, INFINITE_FUTURE) - begin - assert_event_type(ev, FINISH_ACCEPTED) - ensure - ev.close - end - logger.debug("Status sent: #{code}:'#{details}'") - return finished if assert_finished + ops = { + SEND_STATUS_FROM_SERVER => Struct::Status.new(code, details) + } + ops[RECV_CLOSE_ON_SERVER] = nil if assert_finished + @call.run_batch(@cq, self, INFINITE_FUTURE, ops) nil end # remote_read reads a response from the remote endpoint. # - # It blocks until the remote endpoint sends a READ or FINISHED event. On - # a READ, it returns the response after unmarshalling it. On - # FINISHED, it returns nil if the status is OK, otherwise raising - # BadStatus + # It blocks until the remote endpoint replies with a message or status. + # On receiving a message, it returns the response after unmarshalling it. + # On receiving a status, it returns nil if the status is OK, otherwise + # raising BadStatus def remote_read - if @call.metadata.nil? && !@read_metadata_tag.nil? - ev = @cq.pluck(@read_metadata_tag, INFINITE_FUTURE) - assert_event_type(ev, CLIENT_METADATA_READ) - @call.metadata = ev.result - @read_metadata_tag = nil + ops = { RECV_MESSAGE => nil } + ops[RECV_INITIAL_METADATA] = nil unless @metadata_tag.nil? + batch_result = @call.run_batch(@cq, self, INFINITE_FUTURE, ops) + unless @metadata_tag.nil? + @call.metadata = batch_result.metadata + @metadata_tag = nil end - - @call.start_read(self) - ev = @cq.pluck(self, INFINITE_FUTURE) - begin - assert_event_type(ev, READ) - logger.debug("received req: #{ev.result.inspect}") - unless ev.result.nil? - logger.debug("received req.to_s: #{ev.result}") - res = @unmarshal.call(ev.result.to_s) - logger.debug("received_req (unmarshalled): #{res.inspect}") - return res - end - ensure - ev.close + logger.debug("received req: #{batch_result}") + unless batch_result.nil? || batch_result.message.nil? + logger.debug("received req.to_s: #{batch_result.message}") + res = @unmarshal.call(batch_result.message) + logger.debug("received_req (unmarshalled): #{res.inspect}") + return res end logger.debug('found nil; the final response has been sent') nil @@ -324,7 +278,6 @@ module GRPC return enum_for(:each_remote_read) unless block_given? loop do resp = remote_read - break if resp.is_a? Struct::Status # is an OK status break if resp.nil? # the last response was received yield resp end @@ -461,8 +414,7 @@ module GRPC # @return [Enumerator, nil] a response Enumerator def bidi_streamer(requests, **kw, &blk) start_call(**kw) unless @started - bd = BidiCall.new(@call, @cq, @marshal, @unmarshal, @deadline, - @finished_tag) + bd = BidiCall.new(@call, @cq, @marshal, @unmarshal, @deadline) bd.run_on_client(requests, &blk) end @@ -478,8 +430,7 @@ module GRPC # # @param gen_each_reply [Proc] generates the BiDi stream replies def run_server_bidi(gen_each_reply) - bd = BidiCall.new(@call, @cq, @marshal, @unmarshal, @deadline, - @finished_tag) + bd = BidiCall.new(@call, @cq, @marshal, @unmarshal, @deadline) bd.run_on_server(gen_each_reply) end @@ -516,21 +467,5 @@ module GRPC # a Operation on the client. Operation = view_class(:cancel, :cancelled, :deadline, :execute, :metadata, :status) - - # confirms that no events are enqueued, and that the queue is not - # shutdown. - def assert_queue_is_ready - ev = nil - begin - ev = @cq.pluck(self, ZERO) - fail "unexpected event #{ev.inspect}" unless ev.nil? - rescue OutOfTime - logging.debug('timed out waiting for next event') - # expected, nothing should be on the queue and the deadline was ZERO, - # except things using another tag - ensure - ev.close unless ev.nil? - end - end end end diff --git a/src/ruby/lib/grpc/generic/bidi_call.rb b/src/ruby/lib/grpc/generic/bidi_call.rb index c66deaae60..1c1b3b0db7 100644 --- a/src/ruby/lib/grpc/generic/bidi_call.rb +++ b/src/ruby/lib/grpc/generic/bidi_call.rb @@ -30,18 +30,12 @@ require 'forwardable' require 'grpc/grpc' -def assert_event_type(ev, want) - fail OutOfTime if ev.nil? - got = ev.type - fail("Unexpected rpc event: got #{got}, want #{want}") unless got == want -end - # GRPC contains the General RPC module. module GRPC # The BiDiCall class orchestrates exection of a BiDi stream on a client or # server. class BidiCall - include Core::CompletionType + include Core::CallOps include Core::StatusCodes include Core::TimeConsts @@ -63,8 +57,7 @@ module GRPC # @param marshal [Function] f(obj)->string that marshal requests # @param unmarshal [Function] f(string)->obj that unmarshals responses # @param deadline [Fixnum] the deadline for the call to complete - # @param finished_tag [Object] the object used as the call's finish tag, - def initialize(call, q, marshal, unmarshal, deadline, finished_tag) + def initialize(call, q, marshal, unmarshal, deadline) fail(ArgumentError, 'not a call') unless call.is_a? Core::Call unless q.is_a? Core::CompletionQueue fail(ArgumentError, 'not a CompletionQueue') @@ -72,7 +65,6 @@ module GRPC @call = call @cq = q @deadline = deadline - @finished_tag = finished_tag @marshal = marshal @readq = Queue.new @unmarshal = unmarshal @@ -146,30 +138,14 @@ module GRPC requests.each do |req| count += 1 payload = @marshal.call(req) - @call.start_write(Core::ByteBuffer.new(payload), write_tag) - ev = @cq.pluck(write_tag, INFINITE_FUTURE) - begin - assert_event_type(ev, WRITE_ACCEPTED) - ensure - ev.close - end + @call.run_batch(@cq, write_tag, INFINITE_FUTURE, + SEND_MESSAGE => payload) end if is_client - @call.writes_done(write_tag) - ev = @cq.pluck(write_tag, INFINITE_FUTURE) - begin - assert_event_type(ev, FINISH_ACCEPTED) - ensure - ev.close - end logger.debug("bidi-client: sent #{count} reqs, waiting to finish") - ev = @cq.pluck(@finished_tag, INFINITE_FUTURE) - begin - assert_event_type(ev, FINISHED) - ensure - ev.close - end - logger.debug('bidi-client: finished received') + @call.run_batch(@cq, write_tag, INFINITE_FUTURE, + SEND_CLOSE_FROM_CLIENT => nil, + RECV_STATUS_ON_CLIENT => nil) end rescue StandardError => e logger.warn('bidi: write_loop failed') @@ -189,25 +165,20 @@ module GRPC loop do logger.debug("waiting for read #{count}") count += 1 - @call.start_read(read_tag) - ev = @cq.pluck(read_tag, INFINITE_FUTURE) - begin - assert_event_type(ev, READ) - - # handle the next event. - if ev.result.nil? - @readq.push(END_OF_READS) - logger.debug('done reading!') - break - end - - # push the latest read onto the queue and continue reading - logger.debug("received req: #{ev.result}") - res = @unmarshal.call(ev.result.to_s) - @readq.push(res) - ensure - ev.close + # TODO: ensure metadata is read if available, currently it's not + batch_result = @call.run_batch(@cq, read_tag, INFINITE_FUTURE, + RECV_MESSAGE => nil) + # handle the next message + if batch_result.message.nil? + @readq.push(END_OF_READS) + logger.debug('done reading!') + break end + + # push the latest read onto the queue and continue reading + logger.debug("received req: #{batch_result.message}") + res = @unmarshal.call(batch_result.message) + @readq.push(res) end rescue StandardError => e diff --git a/src/ruby/lib/grpc/generic/client_stub.rb b/src/ruby/lib/grpc/generic/client_stub.rb index 01328d4a5b..6547a1499e 100644 --- a/src/ruby/lib/grpc/generic/client_stub.rb +++ b/src/ruby/lib/grpc/generic/client_stub.rb @@ -35,9 +35,10 @@ module GRPC # ClientStub represents an endpoint used to send requests to GRPC servers. class ClientStub include Core::StatusCodes + include Core::TimeConsts - # Default deadline is 5 seconds. - DEFAULT_DEADLINE = 5 + # Default timeout is 5 seconds. + DEFAULT_TIMEOUT = 5 # setup_channel is used by #initialize to constuct a channel from its # arguments. @@ -76,8 +77,8 @@ module GRPC # present the host and arbitrary keyword arg areignored, and the RPC # connection uses this channel. # - # - :deadline - # when present, this is the default deadline used for calls + # - :timeout + # when present, this is the default timeout used for calls # # - :update_metadata # when present, this a func that takes a hash and returns a hash @@ -87,13 +88,13 @@ module GRPC # @param host [String] the host the stub connects to # @param q [Core::CompletionQueue] used to wait for events # @param channel_override [Core::Channel] a pre-created channel - # @param deadline [Number] the default deadline to use in requests + # @param timeout [Number] the default timeout to use in requests # @param creds [Core::Credentials] the channel # @param update_metadata a func that updates metadata as described above # @param kw [KeywordArgs]the channel arguments def initialize(host, q, channel_override: nil, - deadline: DEFAULT_DEADLINE, + timeout: nil, creds: nil, update_metadata: nil, **kw) @@ -103,7 +104,7 @@ module GRPC @update_metadata = ClientStub.check_update_metadata(update_metadata) alt_host = kw[Core::Channel::SSL_TARGET] @host = alt_host.nil? ? host : alt_host - @deadline = deadline + @timeout = timeout.nil? ? DEFAULT_TIMEOUT : timeout end # request_response sends a request to a GRPC server, and returns the @@ -140,12 +141,12 @@ module GRPC # @param req [Object] the request sent to the server # @param marshal [Function] f(obj)->string that marshals requests # @param unmarshal [Function] f(string)->obj that unmarshals responses - # @param deadline [Numeric] (optional) the max completion time in seconds + # @param timeout [Numeric] (optional) the max completion time in seconds # @param return_op [true|false] return an Operation if true # @return [Object] the response received from the server - def request_response(method, req, marshal, unmarshal, deadline = nil, + def request_response(method, req, marshal, unmarshal, timeout = nil, return_op: false, **kw) - c = new_active_call(method, marshal, unmarshal, deadline || @deadline) + c = new_active_call(method, marshal, unmarshal, timeout) md = @update_metadata.nil? ? kw : @update_metadata.call(kw.clone) return c.request_response(req, **md) unless return_op @@ -197,12 +198,12 @@ module GRPC # @param requests [Object] an Enumerable of requests to send # @param marshal [Function] f(obj)->string that marshals requests # @param unmarshal [Function] f(string)->obj that unmarshals responses - # @param deadline [Numeric] the max completion time in seconds + # @param timeout [Numeric] the max completion time in seconds # @param return_op [true|false] return an Operation if true # @return [Object|Operation] the response received from the server - def client_streamer(method, requests, marshal, unmarshal, deadline = nil, + def client_streamer(method, requests, marshal, unmarshal, timeout = nil, return_op: false, **kw) - c = new_active_call(method, marshal, unmarshal, deadline || @deadline) + c = new_active_call(method, marshal, unmarshal, timeout) md = @update_metadata.nil? ? kw : @update_metadata.call(kw.clone) return c.client_streamer(requests, **md) unless return_op @@ -262,13 +263,13 @@ module GRPC # @param req [Object] the request sent to the server # @param marshal [Function] f(obj)->string that marshals requests # @param unmarshal [Function] f(string)->obj that unmarshals responses - # @param deadline [Numeric] the max completion time in seconds + # @param timeout [Numeric] the max completion time in seconds # @param return_op [true|false]return an Operation if true # @param blk [Block] when provided, is executed for each response # @return [Enumerator|Operation|nil] as discussed above - def server_streamer(method, req, marshal, unmarshal, deadline = nil, + def server_streamer(method, req, marshal, unmarshal, timeout = nil, return_op: false, **kw, &blk) - c = new_active_call(method, marshal, unmarshal, deadline || @deadline) + c = new_active_call(method, marshal, unmarshal, timeout) md = @update_metadata.nil? ? kw : @update_metadata.call(kw.clone) return c.server_streamer(req, **md, &blk) unless return_op @@ -367,13 +368,13 @@ module GRPC # @param requests [Object] an Enumerable of requests to send # @param marshal [Function] f(obj)->string that marshals requests # @param unmarshal [Function] f(string)->obj that unmarshals responses - # @param deadline [Numeric] (optional) the max completion time in seconds + # @param timeout [Numeric] (optional) the max completion time in seconds # @param blk [Block] when provided, is executed for each response # @param return_op [true|false] return an Operation if true # @return [Enumerator|nil|Operation] as discussed above - def bidi_streamer(method, requests, marshal, unmarshal, deadline = nil, + def bidi_streamer(method, requests, marshal, unmarshal, timeout = nil, return_op: false, **kw, &blk) - c = new_active_call(method, marshal, unmarshal, deadline || @deadline) + c = new_active_call(method, marshal, unmarshal, timeout) md = @update_metadata.nil? ? kw : @update_metadata.call(kw.clone) return c.bidi_streamer(requests, **md, &blk) unless return_op @@ -390,15 +391,14 @@ module GRPC # Creates a new active stub # - # @param ch [GRPC::Channel] the channel used to create the stub. + # @param method [string] the method being called. # @param marshal [Function] f(obj)->string that marshals requests # @param unmarshal [Function] f(string)->obj that unmarshals responses - # @param deadline [TimeConst] - def new_active_call(ch, marshal, unmarshal, deadline = nil) - absolute_deadline = Core::TimeConsts.from_relative_time(deadline) - call = @ch.create_call(ch, @host, absolute_deadline) - ActiveCall.new(call, @queue, marshal, unmarshal, absolute_deadline, - started: false) + # @param timeout [TimeConst] + def new_active_call(method, marshal, unmarshal, timeout = nil) + deadline = from_relative_time(timeout.nil? ? @timeout : timeout) + call = @ch.create_call(@queue, method, @host, deadline) + ActiveCall.new(call, @queue, marshal, unmarshal, deadline, started: false) end end end diff --git a/src/ruby/lib/grpc/generic/rpc_desc.rb b/src/ruby/lib/grpc/generic/rpc_desc.rb index 2cb3d2eebf..3e48b8e51d 100644 --- a/src/ruby/lib/grpc/generic/rpc_desc.rb +++ b/src/ruby/lib/grpc/generic/rpc_desc.rb @@ -81,7 +81,6 @@ module GRPC active_call.run_server_bidi(mth) end send_status(active_call, OK, 'OK') - active_call.finished rescue BadStatus => e # this is raised by handlers that want GRPC to send an application # error code and detail message. @@ -91,15 +90,11 @@ module GRPC # This is raised by GRPC internals but should rarely, if ever happen. # Log it, but don't notify the other endpoint.. logger.warn("failed call: #{active_call}\n#{e}") - rescue OutOfTime + rescue Core::OutOfTime # This is raised when active_call#method.call exceeeds the deadline # event. Send a status of deadline exceeded logger.warn("late call: #{active_call}") send_status(active_call, DEADLINE_EXCEEDED, 'late') - rescue Core::EventError => e - # This is raised by GRPC internals but should rarely, if ever happen. - # Log it, but don't notify the other endpoint.. - logger.warn("failed call: #{active_call}\n#{e}") rescue StandardError => e # This will usuaally be an unhandled error in the handling code. # Send back a UNKNOWN status to the client @@ -142,7 +137,7 @@ module GRPC def send_status(active_client, code, details) details = 'Not sure why' if details.nil? - active_client.send_status(code, details) + active_client.send_status(code, details, code == OK) rescue StandardError => e logger.warn("Could not send status #{code}:#{details}") logger.warn(e) diff --git a/src/ruby/lib/grpc/generic/rpc_server.rb b/src/ruby/lib/grpc/generic/rpc_server.rb index 35e84023be..30a4bf1532 100644 --- a/src/ruby/lib/grpc/generic/rpc_server.rb +++ b/src/ruby/lib/grpc/generic/rpc_server.rb @@ -38,7 +38,7 @@ module GRPC # RpcServer hosts a number of services and makes them available on the # network. class RpcServer - include Core::CompletionType + include Core::CallOps include Core::TimeConsts extend ::Forwardable @@ -202,20 +202,14 @@ module GRPC end @pool.start @server.start - server_tag = Object.new + request_call_tag = Object.new until stopped? - @server.request_call(server_tag) - ev = @cq.pluck(server_tag, @poll_period) - next if ev.nil? - if ev.type != SERVER_RPC_NEW - logger.warn("bad evt: got:#{ev.type}, want:#{SERVER_RPC_NEW}") - ev.close - next - end - c = new_active_server_call(ev.call, ev.result) + deadline = from_relative_time(@poll_period) + an_rpc = @server.request_call(@cq, request_call_tag, deadline) + next if an_rpc.nil? + c = new_active_server_call(an_rpc) unless c.nil? - mth = ev.result.method.to_sym - ev.close + mth = an_rpc.method.to_sym @pool.schedule(c) do |call| rpc_descs[mth].run_server_method(call, rpc_handlers[mth]) end @@ -224,46 +218,49 @@ module GRPC @running = false end - def new_active_server_call(call, new_server_rpc) - # Accept the call. This is necessary even if a status is to be sent - # back immediately - finished_tag = Object.new - call_queue = Core::CompletionQueue.new - call.metadata = new_server_rpc.metadata # store the metadata - call.server_accept(call_queue, finished_tag) - call.server_end_initial_metadata - - # Send UNAVAILABLE if there are too many unprocessed jobs + # Sends UNAVAILABLE if there are too many unprocessed jobs + def available?(an_rpc) jobs_count, max = @pool.jobs_waiting, @max_waiting_requests logger.info("waiting: #{jobs_count}, max: #{max}") - if @pool.jobs_waiting > @max_waiting_requests - logger.warn("NOT AVAILABLE: too many jobs_waiting: #{new_server_rpc}") - noop = proc { |x| x } - c = ActiveCall.new(call, call_queue, noop, noop, - new_server_rpc.deadline, - finished_tag: finished_tag) - c.send_status(StatusCodes::UNAVAILABLE, '') - return nil - end + return an_rpc if @pool.jobs_waiting <= @max_waiting_requests + logger.warn("NOT AVAILABLE: too many jobs_waiting: #{an_rpc}") + noop = proc { |x| x } + c = ActiveCall.new(an_rpc.call, @cq, noop, noop, an_rpc.deadline) + c.send_status(StatusCodes::UNAVAILABLE, '') + nil + end - # Send NOT_FOUND if the method does not exist - mth = new_server_rpc.method.to_sym - unless rpc_descs.key?(mth) - logger.warn("NOT_FOUND: #{new_server_rpc}") - noop = proc { |x| x } - c = ActiveCall.new(call, call_queue, noop, noop, - new_server_rpc.deadline, - finished_tag: finished_tag) - c.send_status(StatusCodes::NOT_FOUND, '') - return nil - end + # Sends NOT_FOUND if the method can't be found + def found?(an_rpc) + mth = an_rpc.method.to_sym + return an_rpc if rpc_descs.key?(mth) + logger.warn("NOT_FOUND: #{an_rpc}") + noop = proc { |x| x } + c = ActiveCall.new(an_rpc.call, @cq, noop, noop, an_rpc.deadline) + c.send_status(StatusCodes::NOT_FOUND, '') + nil + end + + def new_active_server_call(an_rpc) + # Accept the call. This is necessary even if a status is to be sent + # back immediately + return nil if an_rpc.nil? || an_rpc.call.nil? + + # allow the metadata to be accessed from the call + handle_call_tag = Object.new + an_rpc.call.metadata = an_rpc.metadata + # TODO: add a hook to send md + an_rpc.call.run_batch(@cq, handle_call_tag, INFINITE_FUTURE, + SEND_INITIAL_METADATA => nil) + return nil unless available?(an_rpc) + return nil unless found?(an_rpc) # Create the ActiveCall - rpc_desc = rpc_descs[mth] - logger.info("deadline is #{new_server_rpc.deadline}; (now=#{Time.now})") - ActiveCall.new(call, call_queue, + logger.info("deadline is #{an_rpc.deadline}; (now=#{Time.now})") + rpc_desc = rpc_descs[an_rpc.method.to_sym] + ActiveCall.new(an_rpc.call, @cq, rpc_desc.marshal_proc, rpc_desc.unmarshal_proc(:input), - new_server_rpc.deadline, finished_tag: finished_tag) + an_rpc.deadline) end # Pool is a simple thread pool for running server requests. diff --git a/src/ruby/spec/alloc_spec.rb b/src/ruby/spec/alloc_spec.rb deleted file mode 100644 index 88e7e2b3e7..0000000000 --- a/src/ruby/spec/alloc_spec.rb +++ /dev/null @@ -1,44 +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. - -require 'grpc' - -describe 'Wrapped classes where .new cannot create an instance' do - describe GRPC::Core::Event do - it 'should fail .new fail with a runtime error' do - expect { GRPC::Core::Event.new }.to raise_error(TypeError) - end - end - - describe GRPC::Core::Call do - it 'should fail .new fail with a runtime error' do - expect { GRPC::Core::Event.new }.to raise_error(TypeError) - end - end -end diff --git a/src/ruby/spec/byte_buffer_spec.rb b/src/ruby/spec/byte_buffer_spec.rb deleted file mode 100644 index e1833ebb3a..0000000000 --- a/src/ruby/spec/byte_buffer_spec.rb +++ /dev/null @@ -1,67 +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. - -require 'grpc' - -describe GRPC::Core::ByteBuffer do - describe '#new' do - it 'is constructed from a string' do - expect { GRPC::Core::ByteBuffer.new('#new') }.not_to raise_error - end - - it 'can be constructed from the empty string' do - expect { GRPC::Core::ByteBuffer.new('') }.not_to raise_error - end - - it 'cannot be constructed from nil' do - expect { GRPC::Core::ByteBuffer.new(nil) }.to raise_error TypeError - end - - it 'cannot be constructed from non-strings' do - [1, Object.new, :a_symbol].each do |x| - expect { GRPC::Core::ByteBuffer.new(x) }.to raise_error TypeError - end - end - end - - describe '#to_s' do - it 'is the string value the ByteBuffer was constructed with' do - expect(GRPC::Core::ByteBuffer.new('#to_s').to_s).to eq('#to_s') - end - end - - describe '#dup' do - it 'makes an instance whose #to_s is the original string value' do - bb = GRPC::Core::ByteBuffer.new('#dup') - a_copy = bb.dup - expect(a_copy.to_s).to eq('#dup') - expect(a_copy.dup.to_s).to eq('#dup') - end - end -end diff --git a/src/ruby/spec/call_spec.rb b/src/ruby/spec/call_spec.rb index 2617564571..4977c10a7e 100644 --- a/src/ruby/spec/call_spec.rb +++ b/src/ruby/spec/call_spec.rb @@ -66,51 +66,34 @@ describe GRPC::Core::RpcErrors do end end -describe GRPC::Core::Call do +describe GRPC::Core::CallOps do before(:each) do - @tag = Object.new - @client_queue = GRPC::Core::CompletionQueue.new - fake_host = 'localhost:10101' - @ch = GRPC::Core::Channel.new(fake_host, nil) - end - - describe '#start_read' do - xit 'should fail if called immediately' do - blk = proc { make_test_call.start_read(@tag) } - expect(&blk).to raise_error GRPC::Core::CallError - end - end - - describe '#start_write' do - xit 'should fail if called immediately' do - bytes = GRPC::Core::ByteBuffer.new('test string') - blk = proc { make_test_call.start_write(bytes, @tag) } - expect(&blk).to raise_error GRPC::Core::CallError - end + @known_types = { + SEND_INITIAL_METADATA: 0, + SEND_MESSAGE: 1, + SEND_CLOSE_FROM_CLIENT: 2, + SEND_STATUS_FROM_SERVER: 3, + RECV_INITIAL_METADATA: 4, + RECV_MESSAGE: 5, + RECV_STATUS_ON_CLIENT: 6, + RECV_CLOSE_ON_SERVER: 7 + } end - describe '#start_write_status' do - xit 'should fail if called immediately' do - blk = proc { make_test_call.start_write_status(153, 'x', @tag) } - expect(&blk).to raise_error GRPC::Core::CallError - end + it 'should have symbols for all the known operation types' do + m = GRPC::Core::CallOps + syms_and_codes = m.constants.collect { |c| [c, m.const_get(c)] } + expect(Hash[syms_and_codes]).to eq(@known_types) end +end - describe '#writes_done' do - xit 'should fail if called immediately' do - blk = proc { make_test_call.writes_done(Object.new) } - expect(&blk).to raise_error GRPC::Core::CallError - end - end +describe GRPC::Core::Call do + let(:client_queue) { GRPC::Core::CompletionQueue.new } + let(:test_tag) { Object.new } + let(:fake_host) { 'localhost:10101' } - describe '#add_metadata' do - it 'adds metadata to a call without fail' do - call = make_test_call - n = 37 - one_md = proc { |x| [sprintf('key%d', x), sprintf('value%d', x)] } - metadata = Hash[n.times.collect { |i| one_md.call i }] - expect { call.add_metadata(metadata) }.to_not raise_error - end + before(:each) do + @ch = GRPC::Core::Channel.new(fake_host, nil) end describe '#status' do @@ -154,7 +137,7 @@ describe GRPC::Core::Call do end def make_test_call - @ch.create_call('dummy_method', 'dummy_host', deadline) + @ch.create_call(client_queue, 'dummy_method', 'dummy_host', deadline) end def deadline diff --git a/src/ruby/spec/channel_spec.rb b/src/ruby/spec/channel_spec.rb index af73294abe..31e38d71b8 100644 --- a/src/ruby/spec/channel_spec.rb +++ b/src/ruby/spec/channel_spec.rb @@ -36,16 +36,13 @@ def load_test_certs end describe GRPC::Core::Channel do - FAKE_HOST = 'localhost:0' + let(:fake_host) { 'localhost:0' } + let(:cq) { GRPC::Core::CompletionQueue.new } def create_test_cert GRPC::Core::Credentials.new(load_test_certs[0]) end - before(:each) do - @cq = GRPC::Core::CompletionQueue.new - end - shared_examples '#new' do it 'take a host name without channel args' do expect { GRPC::Core::Channel.new('dummy_host', nil) }.not_to raise_error @@ -115,25 +112,23 @@ describe GRPC::Core::Channel do describe '#create_call' do it 'creates a call OK' do - host = FAKE_HOST - ch = GRPC::Core::Channel.new(host, nil) + ch = GRPC::Core::Channel.new(fake_host, nil) deadline = Time.now + 5 blk = proc do - ch.create_call('dummy_method', 'dummy_host', deadline) + ch.create_call(cq, 'dummy_method', 'dummy_host', deadline) end expect(&blk).to_not raise_error end it 'raises an error if called on a closed channel' do - host = FAKE_HOST - ch = GRPC::Core::Channel.new(host, nil) + ch = GRPC::Core::Channel.new(fake_host, nil) ch.close deadline = Time.now + 5 blk = proc do - ch.create_call('dummy_method', 'dummy_host', deadline) + ch.create_call(cq, 'dummy_method', 'dummy_host', deadline) end expect(&blk).to raise_error(RuntimeError) end @@ -141,15 +136,13 @@ describe GRPC::Core::Channel do describe '#destroy' do it 'destroys a channel ok' do - host = FAKE_HOST - ch = GRPC::Core::Channel.new(host, nil) + ch = GRPC::Core::Channel.new(fake_host, nil) blk = proc { ch.destroy } expect(&blk).to_not raise_error end it 'can be called more than once without error' do - host = FAKE_HOST - ch = GRPC::Core::Channel.new(host, nil) + ch = GRPC::Core::Channel.new(fake_host, nil) blk = proc { ch.destroy } blk.call expect(&blk).to_not raise_error @@ -164,15 +157,13 @@ describe GRPC::Core::Channel do describe '#close' do it 'closes a channel ok' do - host = FAKE_HOST - ch = GRPC::Core::Channel.new(host, nil) + ch = GRPC::Core::Channel.new(fake_host, nil) blk = proc { ch.close } expect(&blk).to_not raise_error end it 'can be called more than once without error' do - host = FAKE_HOST - ch = GRPC::Core::Channel.new(host, nil) + ch = GRPC::Core::Channel.new(fake_host, nil) blk = proc { ch.close } blk.call expect(&blk).to_not raise_error diff --git a/src/ruby/spec/client_server_spec.rb b/src/ruby/spec/client_server_spec.rb index 49a2d3bb4d..1a2afbe1f9 100644 --- a/src/ruby/spec/client_server_spec.rb +++ b/src/ruby/spec/client_server_spec.rb @@ -30,7 +30,6 @@ require 'grpc' require 'spec_helper' -include GRPC::Core::CompletionType include GRPC::Core def load_test_certs @@ -40,6 +39,8 @@ def load_test_certs end shared_context 'setup: tags' do + let(:sent_message) { 'sent message' } + let(:reply_text) { 'the reply' } before(:example) do @server_finished_tag = Object.new @client_finished_tag = Object.new @@ -52,153 +53,136 @@ shared_context 'setup: tags' do Time.now + 2 end - def expect_next_event_on(queue, type, tag) - ev = queue.pluck(tag, deadline) - if type.nil? - expect(ev).to be_nil - else - expect(ev).to_not be_nil - expect(ev.type).to be(type) - end - ev - end - def server_allows_client_to_proceed - @server.request_call(@server_tag) - ev = @server_queue.pluck(@server_tag, deadline) - expect(ev).not_to be_nil - expect(ev.type).to be(SERVER_RPC_NEW) - server_call = ev.call - server_call.server_accept(@server_queue, @server_finished_tag) - server_call.server_end_initial_metadata + recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline) + expect(recvd_rpc).to_not eq nil + server_call = recvd_rpc.call + ops = { CallOps::SEND_INITIAL_METADATA => {} } + svr_batch = server_call.run_batch(@server_queue, @server_tag, deadline, ops) + expect(svr_batch.send_metadata).to be true server_call end - def server_responds_with(server_call, reply_text) - reply = ByteBuffer.new(reply_text) - server_call.start_read(@server_tag) - ev = @server_queue.pluck(@server_tag, TimeConsts::INFINITE_FUTURE) - expect(ev.type).to be(READ) - server_call.start_write(reply, @server_tag) - ev = @server_queue.pluck(@server_tag, TimeConsts::INFINITE_FUTURE) - expect(ev).not_to be_nil - expect(ev.type).to be(WRITE_ACCEPTED) - end - - def client_sends(call, sent = 'a message') - req = ByteBuffer.new(sent) - call.start_write(req, @tag) - ev = @client_queue.pluck(@tag, TimeConsts::INFINITE_FUTURE) - expect(ev).not_to be_nil - expect(ev.type).to be(WRITE_ACCEPTED) - sent - end - def new_client_call - @ch.create_call('/method', 'foo.test.google.fr', deadline) + @ch.create_call(@client_queue, '/method', 'foo.test.google.fr', deadline) end end shared_examples 'basic GRPC message delivery is OK' do + include GRPC::Core include_context 'setup: tags' - it 'servers receive requests from clients and start responding' do - reply = ByteBuffer.new('the server payload') + it 'servers receive requests from clients and can respond' do call = new_client_call - call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag) - - # check the server rpc new was received - # @server.request_call(@server_tag) - # ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag) - - # accept the call - # server_call = ev.call - # server_call.server_accept(@server_queue, @server_finished_tag) - # server_call.server_end_initial_metadata - server_call = server_allows_client_to_proceed - - # client sends a message - msg = client_sends(call) + client_ops = { + CallOps::SEND_INITIAL_METADATA => {}, + CallOps::SEND_MESSAGE => sent_message + } + batch_result = call.run_batch(@client_queue, @client_tag, deadline, + client_ops) + expect(batch_result.send_metadata).to be true + expect(batch_result.send_message).to be true # confirm the server can read the inbound message - server_call.start_read(@server_tag) - ev = expect_next_event_on(@server_queue, READ, @server_tag) - expect(ev.result.to_s).to eq(msg) - - # the server response - server_call.start_write(reply, @server_tag) - expect_next_event_on(@server_queue, WRITE_ACCEPTED, @server_tag) + server_call = server_allows_client_to_proceed + server_ops = { + CallOps::RECV_MESSAGE => nil + } + svr_batch = server_call.run_batch(@server_queue, @server_tag, deadline, + server_ops) + expect(svr_batch.message).to eq(sent_message) end it 'responses written by servers are received by the client' do call = new_client_call - call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag) - server_call = server_allows_client_to_proceed - client_sends(call) - server_responds_with(server_call, 'server_response') + client_ops = { + CallOps::SEND_INITIAL_METADATA => {}, + CallOps::SEND_MESSAGE => sent_message + } + batch_result = call.run_batch(@client_queue, @client_tag, deadline, + client_ops) + expect(batch_result.send_metadata).to be true + expect(batch_result.send_message).to be true - call.start_read(@tag) - ev = expect_next_event_on(@client_queue, READ, @tag) - expect(ev.result.to_s).to eq('server_response') + # confirm the server can read the inbound message + server_call = server_allows_client_to_proceed + server_ops = { + CallOps::RECV_MESSAGE => nil, + CallOps::SEND_MESSAGE => reply_text + } + svr_batch = server_call.run_batch(@server_queue, @server_tag, deadline, + server_ops) + expect(svr_batch.message).to eq(sent_message) + expect(svr_batch.send_message).to be true end it 'servers can ignore a client write and send a status' do call = new_client_call - call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag) - - # check the server rpc new was received - @server.request_call(@server_tag) - ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag) - expect(ev.tag).to be(@server_tag) - - # accept the call - need to do this to sent status. - server_call = ev.call - server_call.server_accept(@server_queue, @server_finished_tag) - server_call.server_end_initial_metadata - server_call.start_write_status(StatusCodes::NOT_FOUND, 'not found', - @server_tag) - - # Client sends some data - client_sends(call) - - # client gets an empty response for the read, preceeded by some metadata. - call.start_read(@tag) - expect_next_event_on(@client_queue, CLIENT_METADATA_READ, - @client_metadata_tag) - ev = expect_next_event_on(@client_queue, READ, @tag) - expect(ev.tag).to be(@tag) - expect(ev.result.to_s).to eq('') - - # finally, after client sends writes_done, they get the finished. - call.writes_done(@tag) - expect_next_event_on(@client_queue, FINISH_ACCEPTED, @tag) - ev = expect_next_event_on(@client_queue, FINISHED, @client_finished_tag) - expect(ev.result.code).to eq(StatusCodes::NOT_FOUND) + client_ops = { + CallOps::SEND_INITIAL_METADATA => {}, + CallOps::SEND_MESSAGE => sent_message + } + batch_result = call.run_batch(@client_queue, @client_tag, deadline, + client_ops) + expect(batch_result.send_metadata).to be true + expect(batch_result.send_message).to be true + + # confirm the server can read the inbound message + the_status = Struct::Status.new(StatusCodes::OK, 'OK') + server_call = server_allows_client_to_proceed + server_ops = { + CallOps::SEND_STATUS_FROM_SERVER => the_status + } + svr_batch = server_call.run_batch(@server_queue, @server_tag, deadline, + server_ops) + expect(svr_batch.message).to eq nil + expect(svr_batch.send_status).to be true end it 'completes calls by sending status to client and server' do call = new_client_call - call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag) + client_ops = { + CallOps::SEND_INITIAL_METADATA => {}, + CallOps::SEND_MESSAGE => sent_message + } + batch_result = call.run_batch(@client_queue, @client_tag, deadline, + client_ops) + expect(batch_result.send_metadata).to be true + expect(batch_result.send_message).to be true + + # confirm the server can read the inbound message and respond + the_status = Struct::Status.new(StatusCodes::OK, 'OK', {}) server_call = server_allows_client_to_proceed - client_sends(call) - server_responds_with(server_call, 'server_response') - server_call.start_write_status(10_101, 'status code is 10101', @server_tag) - - # first the client says writes are done - call.start_read(@tag) - expect_next_event_on(@client_queue, READ, @tag) - call.writes_done(@tag) - - # but nothing happens until the server sends a status - expect_next_event_on(@server_queue, FINISH_ACCEPTED, @server_tag) - ev = expect_next_event_on(@server_queue, FINISHED, @server_finished_tag) - expect(ev.result).to be_a(Struct::Status) - - # client gets FINISHED - expect_next_event_on(@client_queue, FINISH_ACCEPTED, @tag) - ev = expect_next_event_on(@client_queue, FINISHED, @client_finished_tag) - expect(ev.result.details).to eq('status code is 10101') - expect(ev.result.code).to eq(10_101) + server_ops = { + CallOps::RECV_MESSAGE => nil, + CallOps::SEND_MESSAGE => reply_text, + CallOps::SEND_STATUS_FROM_SERVER => the_status + } + svr_batch = server_call.run_batch(@server_queue, @server_tag, deadline, + server_ops) + expect(svr_batch.message).to eq sent_message + expect(svr_batch.send_status).to be true + expect(svr_batch.send_message).to be true + + # confirm the client can receive the server response and status. + client_ops = { + CallOps::SEND_CLOSE_FROM_CLIENT => nil, + CallOps::RECV_MESSAGE => nil, + CallOps::RECV_STATUS_ON_CLIENT => nil + } + batch_result = call.run_batch(@client_queue, @client_tag, deadline, + client_ops) + expect(batch_result.send_close).to be true + expect(batch_result.message).to eq reply_text + expect(batch_result.status).to eq the_status + + # confirm the server can receive the client close. + server_ops = { + CallOps::RECV_CLOSE_ON_SERVER => nil + } + svr_batch = server_call.run_batch(@server_queue, @server_tag, deadline, + server_ops) + expect(svr_batch.send_close).to be true end end @@ -224,25 +208,33 @@ shared_examples 'GRPC metadata delivery works OK' do it 'raises an exception if a metadata key is invalid' do @bad_keys.each do |md| call = new_client_call - expect { call.add_metadata(md) }.to raise_error + client_ops = { + CallOps::SEND_INITIAL_METADATA => md + } + blk = proc do + call.run_batch(@client_queue, @client_tag, deadline, + client_ops) + end + expect(&blk).to raise_error end end it 'sends all the metadata pairs when keys and values are valid' do @valid_metadata.each do |md| call = new_client_call - call.add_metadata(md) - - # Client begins a call OK - call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag) - - # ... server has all metadata available even though the client did not - # send a write - @server.request_call(@server_tag) - ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag) + client_ops = { + CallOps::SEND_INITIAL_METADATA => md + } + batch_result = call.run_batch(@client_queue, @client_tag, deadline, + client_ops) + expect(batch_result.send_metadata).to be true + + # confirm the server can receive the client metadata + recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline) + expect(recvd_rpc).to_not eq nil + recvd_md = recvd_rpc.metadata replace_symbols = Hash[md.each_pair.collect { |x, y| [x.to_s, y] }] - result = ev.result.metadata - expect(result.merge(replace_symbols)).to eq(result) + expect(recvd_md).to eq(recvd_md.merge(replace_symbols)) end end end @@ -266,55 +258,81 @@ shared_examples 'GRPC metadata delivery works OK' do it 'raises an exception if a metadata key is invalid' do @bad_keys.each do |md| call = new_client_call - call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag) + # client signals that it's done sending metadata to allow server to + # respond + client_ops = { + CallOps::SEND_INITIAL_METADATA => nil + } + call.run_batch(@client_queue, @client_tag, deadline, client_ops) # server gets the invocation - @server.request_call(@server_tag) - ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag) - expect { ev.call.add_metadata(md) }.to raise_error + recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline) + expect(recvd_rpc).to_not eq nil + server_ops = { + CallOps::SEND_INITIAL_METADATA => md + } + blk = proc do + recvd_rpc.call.run_batch(@server_queue, @server_tag, deadline, + server_ops) + end + expect(&blk).to raise_error end end - it 'sends a hash that contains the status when no metadata is added' do + it 'sends an empty hash if no metadata is added' do call = new_client_call - call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag) - - # server gets the invocation - @server.request_call(@server_tag) - ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag) - server_call = ev.call - - # ... server accepts the call without adding metadata - server_call.server_accept(@server_queue, @server_finished_tag) - server_call.server_end_initial_metadata - - # there is the HTTP status metadata, though there should not be any - # TODO: update this with the bug number to be resolved - ev = expect_next_event_on(@client_queue, CLIENT_METADATA_READ, - @client_metadata_tag) - expect(ev.result).to eq({}) + # client signals that it's done sending metadata to allow server to + # respond + client_ops = { + CallOps::SEND_INITIAL_METADATA => nil + } + call.run_batch(@client_queue, @client_tag, deadline, client_ops) + + # server gets the invocation but sends no metadata back + recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline) + expect(recvd_rpc).to_not eq nil + server_call = recvd_rpc.call + server_ops = { + CallOps::SEND_INITIAL_METADATA => nil + } + server_call.run_batch(@server_queue, @server_tag, deadline, server_ops) + + # client receives nothing as expected + client_ops = { + CallOps::RECV_INITIAL_METADATA => nil + } + batch_result = call.run_batch(@client_queue, @client_tag, deadline, + client_ops) + expect(batch_result.metadata).to eq({}) end it 'sends all the pairs when keys and values are valid' do @valid_metadata.each do |md| call = new_client_call - call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag) - - # server gets the invocation - @server.request_call(@server_tag) - ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag) - server_call = ev.call - - # ... server adds metadata and accepts the call - server_call.add_metadata(md) - server_call.server_accept(@server_queue, @server_finished_tag) - server_call.server_end_initial_metadata - - # Now the client can read the metadata - ev = expect_next_event_on(@client_queue, CLIENT_METADATA_READ, - @client_metadata_tag) + # client signals that it's done sending metadata to allow server to + # respond + client_ops = { + CallOps::SEND_INITIAL_METADATA => nil + } + call.run_batch(@client_queue, @client_tag, deadline, client_ops) + + # server gets the invocation but sends no metadata back + recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline) + expect(recvd_rpc).to_not eq nil + server_call = recvd_rpc.call + server_ops = { + CallOps::SEND_INITIAL_METADATA => md + } + server_call.run_batch(@server_queue, @server_tag, deadline, server_ops) + + # client receives nothing as expected + client_ops = { + CallOps::RECV_INITIAL_METADATA => nil + } + batch_result = call.run_batch(@client_queue, @client_tag, deadline, + client_ops) replace_symbols = Hash[md.each_pair.collect { |x, y| [x.to_s, y] }] - expect(ev.result).to eq(replace_symbols) + expect(batch_result.metadata).to eq(replace_symbols) end end end diff --git a/src/ruby/spec/event_spec.rb b/src/ruby/spec/event_spec.rb deleted file mode 100644 index 7d92fcd792..0000000000 --- a/src/ruby/spec/event_spec.rb +++ /dev/null @@ -1,53 +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. - -require 'grpc' - -describe GRPC::Core::CompletionType do - before(:each) do - @known_types = { - QUEUE_SHUTDOWN: 0, - OP_COMPLETE: 1, - READ: 2, - WRITE_ACCEPTED: 3, - FINISH_ACCEPTED: 4, - CLIENT_METADATA_READ: 5, - FINISHED: 6, - SERVER_RPC_NEW: 7, - SERVER_SHUTDOWN: 8, - RESERVED: 9 - } - end - - it 'should have all the known types' do - mod = GRPC::Core::CompletionType - blk = proc { Hash[mod.constants.collect { |c| [c, mod.const_get(c)] }] } - expect(blk.call).to eq(@known_types) - end -end diff --git a/src/ruby/spec/generic/active_call_spec.rb b/src/ruby/spec/generic/active_call_spec.rb index 96e07cacb4..575871afb1 100644 --- a/src/ruby/spec/generic/active_call_spec.rb +++ b/src/ruby/spec/generic/active_call_spec.rb @@ -34,12 +34,11 @@ include GRPC::Core::StatusCodes describe GRPC::ActiveCall do ActiveCall = GRPC::ActiveCall Call = GRPC::Core::Call - CompletionType = GRPC::Core::CompletionType + CallOps = GRPC::Core::CallOps before(:each) do @pass_through = proc { |x| x } @server_tag = Object.new - @server_done_tag = Object.new @tag = Object.new @client_queue = GRPC::Core::CompletionQueue.new @@ -48,7 +47,7 @@ describe GRPC::ActiveCall do @server = GRPC::Core::Server.new(@server_queue, nil) server_port = @server.add_http2_port(host) @server.start - @ch = GRPC::Core::Channel.new("localhost:#{server_port}", nil) + @ch = GRPC::Core::Channel.new("0.0.0.0:#{server_port}", nil) end after(:each) do @@ -58,12 +57,10 @@ describe GRPC::ActiveCall do describe 'restricted view methods' do before(:each) do call = make_test_call - done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, - deadline) + md_tag = ActiveCall.client_invoke(call, @client_queue, deadline) @client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, - finished_tag: done_tag, - read_metadata_tag: meta_tag) + metadata_tag: md_tag) end describe '#multi_req_view' do @@ -90,48 +87,45 @@ describe GRPC::ActiveCall do describe '#remote_send' do it 'allows a client to send a payload to the server' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, - deadline) + md_tag = ActiveCall.client_invoke(call, @client_queue, deadline) @client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, - finished_tag: done_tag, - read_metadata_tag: meta_tag) + metadata_tag: md_tag) msg = 'message is a string' @client_call.remote_send(msg) # check that server rpc new was received - @server.request_call(@server_tag) - ev = @server_queue.next(deadline) - expect(ev.type).to be(CompletionType::SERVER_RPC_NEW) - expect(ev.call).to be_a(Call) - expect(ev.tag).to be(@server_tag) + recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline) + expect(recvd_rpc).to_not eq nil + recvd_call = recvd_rpc.call # Accept the call, and verify that the server reads the response ok. - ev.call.server_accept(@client_queue, @server_tag) - ev.call.server_end_initial_metadata - server_call = ActiveCall.new(ev.call, @client_queue, @pass_through, + server_ops = { + CallOps::SEND_INITIAL_METADATA => {} + } + recvd_call.run_batch(@server_queue, @server_tag, deadline, server_ops) + server_call = ActiveCall.new(recvd_call, @server_queue, @pass_through, @pass_through, deadline) expect(server_call.remote_read).to eq(msg) end it 'marshals the payload using the marshal func' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, - deadline) + ActiveCall.client_invoke(call, @client_queue, deadline) marshal = proc { |x| 'marshalled:' + x } client_call = ActiveCall.new(call, @client_queue, marshal, - @pass_through, deadline, - finished_tag: done_tag, - read_metadata_tag: meta_tag) + @pass_through, deadline) msg = 'message is a string' client_call.remote_send(msg) # confirm that the message was marshalled - @server.request_call(@server_tag) - ev = @server_queue.next(deadline) - ev.call.server_accept(@client_queue, @server_tag) - ev.call.server_end_initial_metadata - server_call = ActiveCall.new(ev.call, @client_queue, @pass_through, + recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline) + recvd_call = recvd_rpc.call + server_ops = { + CallOps::SEND_INITIAL_METADATA => nil + } + recvd_call.run_batch(@server_queue, @server_tag, deadline, server_ops) + server_call = ActiveCall.new(recvd_call, @server_queue, @pass_through, @pass_through, deadline) expect(server_call.remote_read).to eq('marshalled:' + msg) end @@ -142,23 +136,22 @@ describe GRPC::ActiveCall do call = make_test_call ActiveCall.client_invoke(call, @client_queue, deadline, k1: 'v1', k2: 'v2') - @server.request_call(@server_tag) - ev = @server_queue.next(deadline) - expect(ev).to_not be_nil - expect(ev.result.metadata['k1']).to eq('v1') - expect(ev.result.metadata['k2']).to eq('v2') + recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline) + recvd_call = recvd_rpc.call + expect(recvd_call).to_not be_nil + expect(recvd_rpc.metadata).to_not be_nil + expect(recvd_rpc.metadata['k1']).to eq('v1') + expect(recvd_rpc.metadata['k2']).to eq('v2') end end describe '#remote_read' do it 'reads the response sent by a server' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, - deadline) + md_tag = ActiveCall.client_invoke(call, @client_queue, deadline) client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, - finished_tag: done_tag, - read_metadata_tag: meta_tag) + metadata_tag: md_tag) msg = 'message is a string' client_call.remote_send(msg) server_call = expect_server_to_receive(msg) @@ -168,12 +161,10 @@ describe GRPC::ActiveCall do it 'saves no metadata when the server adds no metadata' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, - deadline) + md_tag = ActiveCall.client_invoke(call, @client_queue, deadline) client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, - finished_tag: done_tag, - read_metadata_tag: meta_tag) + metadata_tag: md_tag) msg = 'message is a string' client_call.remote_send(msg) server_call = expect_server_to_receive(msg) @@ -185,12 +176,10 @@ describe GRPC::ActiveCall do it 'saves metadata add by the server' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, - deadline) + md_tag = ActiveCall.client_invoke(call, @client_queue, deadline) client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, - finished_tag: done_tag, - read_metadata_tag: meta_tag) + metadata_tag: md_tag) msg = 'message is a string' client_call.remote_send(msg) server_call = expect_server_to_receive(msg, k1: 'v1', k2: 'v2') @@ -203,12 +192,10 @@ describe GRPC::ActiveCall do it 'get a nil msg before a status when an OK status is sent' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, - deadline) + md_tag = ActiveCall.client_invoke(call, @client_queue, deadline) client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, - finished_tag: done_tag, - read_metadata_tag: meta_tag) + metadata_tag: md_tag) msg = 'message is a string' client_call.remote_send(msg) client_call.writes_done(false) @@ -222,13 +209,11 @@ describe GRPC::ActiveCall do it 'unmarshals the response using the unmarshal func' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, - deadline) + md_tag = ActiveCall.client_invoke(call, @client_queue, deadline) unmarshal = proc { |x| 'unmarshalled:' + x } client_call = ActiveCall.new(call, @client_queue, @pass_through, unmarshal, deadline, - finished_tag: done_tag, - read_metadata_tag: meta_tag) + metadata_tag: md_tag) # confirm the client receives the unmarshalled message msg = 'message is a string' @@ -249,13 +234,11 @@ describe GRPC::ActiveCall do it 'the returns an enumerator that can read n responses' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, - deadline) + md_tag = ActiveCall.client_invoke(call, @client_queue, deadline) client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, - finished_tag: done_tag, - read_metadata_tag: meta_tag) - msg = 'message is 4a string' + metadata_tag: md_tag) + msg = 'message is a string' reply = 'server_response' client_call.remote_send(msg) server_call = expect_server_to_receive(msg) @@ -269,12 +252,10 @@ describe GRPC::ActiveCall do it 'the returns an enumerator that stops after an OK Status' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, - deadline) + md_tag = ActiveCall.client_invoke(call, @client_queue, deadline) client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, - read_metadata_tag: meta_tag, - finished_tag: done_tag) + metadata_tag: md_tag) msg = 'message is a string' reply = 'server_response' client_call.remote_send(msg) @@ -294,12 +275,10 @@ describe GRPC::ActiveCall do describe '#writes_done' do it 'finishes ok if the server sends a status response' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, - deadline) + md_tag = ActiveCall.client_invoke(call, @client_queue, deadline) client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, - finished_tag: done_tag, - read_metadata_tag: meta_tag) + metadata_tag: md_tag) msg = 'message is a string' client_call.remote_send(msg) expect { client_call.writes_done(false) }.to_not raise_error @@ -312,12 +291,10 @@ describe GRPC::ActiveCall do it 'finishes ok if the server sends an early status response' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, - deadline) + md_tag = ActiveCall.client_invoke(call, @client_queue, deadline) client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, - read_metadata_tag: meta_tag, - finished_tag: done_tag) + metadata_tag: md_tag) msg = 'message is a string' client_call.remote_send(msg) server_call = expect_server_to_receive(msg) @@ -330,12 +307,10 @@ describe GRPC::ActiveCall do it 'finishes ok if writes_done is true' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, - deadline) + md_tag = ActiveCall.client_invoke(call, @client_queue, deadline) client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, - read_metadata_tag: meta_tag, - finished_tag: done_tag) + metadata_tag: md_tag) msg = 'message is a string' client_call.remote_send(msg) server_call = expect_server_to_receive(msg) @@ -353,21 +328,20 @@ describe GRPC::ActiveCall do end def expect_server_to_be_invoked(**kw) - @server.request_call(@server_tag) - ev = @server_queue.next(deadline) - ev.call.add_metadata(kw) - ev.call.server_accept(@client_queue, @server_done_tag) - ev.call.server_end_initial_metadata - ActiveCall.new(ev.call, @client_queue, @pass_through, - @pass_through, deadline, - finished_tag: @server_done_tag) + recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline) + expect(recvd_rpc).to_not eq nil + recvd_call = recvd_rpc.call + recvd_call.run_batch(@server_queue, @server_tag, deadline, + CallOps::SEND_INITIAL_METADATA => kw) + ActiveCall.new(recvd_call, @server_queue, @pass_through, + @pass_through, deadline) end def make_test_call - @ch.create_call('dummy_method', 'dummy_host', deadline) + @ch.create_call(@client_queue, '/method', 'a.dummy.host', deadline) end def deadline - Time.now + 1 # in 1 second; arbitrary + Time.now + 2 # in 2 seconds; arbitrary end end diff --git a/src/ruby/spec/generic/client_stub_spec.rb b/src/ruby/spec/generic/client_stub_spec.rb index 0c98fc40d9..193c5f2a03 100644 --- a/src/ruby/spec/generic/client_stub_spec.rb +++ b/src/ruby/spec/generic/client_stub_spec.rb @@ -30,15 +30,41 @@ require 'grpc' require 'xray/thread_dump_signal_handler' -NOOP = proc { |x| x } -FAKE_HOST = 'localhost:0' +# Notifier is useful high-level synchronization primitive. +class Notifier + attr_reader :payload, :notified + alias_method :notified?, :notified + + def initialize + @mutex = Mutex.new + @cvar = ConditionVariable.new + @notified = false + @payload = nil + end + + def wait + @mutex.synchronize do + @cvar.wait(@mutex) until notified? + end + end + + def notify(payload) + @mutex.synchronize do + return Error.new('already notified') if notified? + @payload = payload + @notified = true + @cvar.signal + return nil + end + end +end def wakey_thread(&blk) - awake_mutex, awake_cond = Mutex.new, ConditionVariable.new + n = Notifier.new t = Thread.new do - blk.call(awake_mutex, awake_cond) + blk.call(n) end - awake_mutex.synchronize { awake_cond.wait(awake_mutex) } + n.wait t end @@ -50,8 +76,11 @@ end include GRPC::Core::StatusCodes include GRPC::Core::TimeConsts +include GRPC::Core::CallOps describe 'ClientStub' do + let(:noop) { proc { |x| x } } + before(:each) do Thread.abort_on_exception = true @server = nil @@ -66,61 +95,56 @@ describe 'ClientStub' do end describe '#new' do + let(:fake_host) { 'localhost:0' } it 'can be created from a host and args' do - host = FAKE_HOST opts = { a_channel_arg: 'an_arg' } blk = proc do - GRPC::ClientStub.new(host, @cq, **opts) + GRPC::ClientStub.new(fake_host, @cq, **opts) end expect(&blk).not_to raise_error end it 'can be created with a default deadline' do - host = FAKE_HOST opts = { a_channel_arg: 'an_arg', deadline: 5 } blk = proc do - GRPC::ClientStub.new(host, @cq, **opts) + GRPC::ClientStub.new(fake_host, @cq, **opts) end expect(&blk).not_to raise_error end it 'can be created with an channel override' do - host = FAKE_HOST opts = { a_channel_arg: 'an_arg', channel_override: @ch } blk = proc do - GRPC::ClientStub.new(host, @cq, **opts) + GRPC::ClientStub.new(fake_host, @cq, **opts) end expect(&blk).not_to raise_error end it 'cannot be created with a bad channel override' do - host = FAKE_HOST blk = proc do opts = { a_channel_arg: 'an_arg', channel_override: Object.new } - GRPC::ClientStub.new(host, @cq, **opts) + GRPC::ClientStub.new(fake_host, @cq, **opts) end expect(&blk).to raise_error end it 'cannot be created with bad credentials' do - host = FAKE_HOST blk = proc do opts = { a_channel_arg: 'an_arg', creds: Object.new } - GRPC::ClientStub.new(host, @cq, **opts) + GRPC::ClientStub.new(fake_host, @cq, **opts) end expect(&blk).to raise_error end it 'can be created with test test credentials' do certs = load_test_certs - host = FAKE_HOST blk = proc do opts = { GRPC::Core::Channel::SSL_TARGET => 'foo.test.google.fr', a_channel_arg: 'an_arg', creds: GRPC::Core::Credentials.new(certs[0], nil, nil) } - GRPC::ClientStub.new(host, @cq, **opts) + GRPC::ClientStub.new(fake_host, @cq, **opts) end expect(&blk).to_not raise_error end @@ -187,7 +211,7 @@ describe 'ClientStub' do describe 'without a call operation' do def get_response(stub) - stub.request_response(@method, @sent_msg, NOOP, NOOP, + stub.request_response(@method, @sent_msg, noop, noop, k1: 'v1', k2: 'v2') end @@ -196,7 +220,7 @@ describe 'ClientStub' do describe 'via a call operation' do def get_response(stub) - op = stub.request_response(@method, @sent_msg, NOOP, NOOP, + op = stub.request_response(@method, @sent_msg, noop, noop, return_op: true, k1: 'v1', k2: 'v2') expect(op).to be_a(GRPC::ActiveCall::Operation) op.execute @@ -259,7 +283,7 @@ describe 'ClientStub' do describe 'without a call operation' do def get_response(stub) - stub.client_streamer(@method, @sent_msgs, NOOP, NOOP, + stub.client_streamer(@method, @sent_msgs, noop, noop, k1: 'v1', k2: 'v2') end @@ -268,7 +292,7 @@ describe 'ClientStub' do describe 'via a call operation' do def get_response(stub) - op = stub.client_streamer(@method, @sent_msgs, NOOP, NOOP, + op = stub.client_streamer(@method, @sent_msgs, noop, noop, return_op: true, k1: 'v1', k2: 'v2') expect(op).to be_a(GRPC::ActiveCall::Operation) op.execute @@ -333,7 +357,7 @@ describe 'ClientStub' do describe 'without a call operation' do def get_responses(stub) - e = stub.server_streamer(@method, @sent_msg, NOOP, NOOP, + e = stub.server_streamer(@method, @sent_msg, noop, noop, k1: 'v1', k2: 'v2') expect(e).to be_a(Enumerator) e @@ -344,7 +368,7 @@ describe 'ClientStub' do describe 'via a call operation' do def get_responses(stub) - op = stub.server_streamer(@method, @sent_msg, NOOP, NOOP, + op = stub.server_streamer(@method, @sent_msg, noop, noop, return_op: true, k1: 'v1', k2: 'v2') expect(op).to be_a(GRPC::ActiveCall::Operation) e = op.execute @@ -361,34 +385,30 @@ describe 'ClientStub' do before(:each) do @sent_msgs = Array.new(3) { |i| 'msg_' + (i + 1).to_s } @replys = Array.new(3) { |i| 'reply_' + (i + 1).to_s } + server_port = create_test_server + @host = "localhost:#{server_port}" end it 'supports sending all the requests first', bidi: true do - server_port = create_test_server - host = "localhost:#{server_port}" th = run_bidi_streamer_handle_inputs_first(@sent_msgs, @replys, @pass) - stub = GRPC::ClientStub.new(host, @cq) + stub = GRPC::ClientStub.new(@host, @cq) e = get_responses(stub) expect(e.collect { |r| r }).to eq(@replys) th.join end it 'supports client-initiated ping pong', bidi: true do - server_port = create_test_server - host = "localhost:#{server_port}" th = run_bidi_streamer_echo_ping_pong(@sent_msgs, @pass, true) - stub = GRPC::ClientStub.new(host, @cq) + stub = GRPC::ClientStub.new(@host, @cq) e = get_responses(stub) expect(e.collect { |r| r }).to eq(@sent_msgs) th.join end it 'supports a server-initiated ping pong', bidi: true do - server_port = create_test_server - host = "localhost:#{server_port}" th = run_bidi_streamer_echo_ping_pong(@sent_msgs, @pass, false) - stub = GRPC::ClientStub.new(host, @cq) + stub = GRPC::ClientStub.new(@host, @cq) e = get_responses(stub) expect(e.collect { |r| r }).to eq(@sent_msgs) th.join @@ -397,7 +417,7 @@ describe 'ClientStub' do describe 'without a call operation' do def get_responses(stub) - e = stub.bidi_streamer(@method, @sent_msgs, NOOP, NOOP) + e = stub.bidi_streamer(@method, @sent_msgs, noop, noop) expect(e).to be_a(Enumerator) e end @@ -407,7 +427,7 @@ describe 'ClientStub' do describe 'via a call operation' do def get_responses(stub) - op = stub.bidi_streamer(@method, @sent_msgs, NOOP, NOOP, + op = stub.bidi_streamer(@method, @sent_msgs, noop, noop, return_op: true) expect(op).to be_a(GRPC::ActiveCall::Operation) e = op.execute @@ -421,8 +441,8 @@ describe 'ClientStub' do def run_server_streamer(expected_input, replys, status, **kw) wanted_metadata = kw.clone - wakey_thread do |mtx, cnd| - c = expect_server_to_be_invoked(mtx, cnd) + wakey_thread do |notifier| + c = expect_server_to_be_invoked(notifier) wanted_metadata.each do |k, v| expect(c.metadata[k.to_s]).to eq(v) end @@ -434,8 +454,8 @@ describe 'ClientStub' do def run_bidi_streamer_handle_inputs_first(expected_inputs, replys, status) - wakey_thread do |mtx, cnd| - c = expect_server_to_be_invoked(mtx, cnd) + wakey_thread do |notifier| + c = expect_server_to_be_invoked(notifier) expected_inputs.each { |i| expect(c.remote_read).to eq(i) } replys.each { |r| c.remote_send(r) } c.send_status(status, status == @pass ? 'OK' : 'NOK', true) @@ -443,8 +463,8 @@ describe 'ClientStub' do end def run_bidi_streamer_echo_ping_pong(expected_inputs, status, client_starts) - wakey_thread do |mtx, cnd| - c = expect_server_to_be_invoked(mtx, cnd) + wakey_thread do |notifier| + c = expect_server_to_be_invoked(notifier) expected_inputs.each do |i| if client_starts expect(c.remote_read).to eq(i) @@ -460,8 +480,8 @@ describe 'ClientStub' do def run_client_streamer(expected_inputs, resp, status, **kw) wanted_metadata = kw.clone - wakey_thread do |mtx, cnd| - c = expect_server_to_be_invoked(mtx, cnd) + wakey_thread do |notifier| + c = expect_server_to_be_invoked(notifier) expected_inputs.each { |i| expect(c.remote_read).to eq(i) } wanted_metadata.each do |k, v| expect(c.metadata[k.to_s]).to eq(v) @@ -473,8 +493,8 @@ describe 'ClientStub' do def run_request_response(expected_input, resp, status, **kw) wanted_metadata = kw.clone - wakey_thread do |mtx, cnd| - c = expect_server_to_be_invoked(mtx, cnd) + wakey_thread do |notifier| + c = expect_server_to_be_invoked(notifier) expect(c.remote_read).to eq(expected_input) wanted_metadata.each do |k, v| expect(c.metadata[k.to_s]).to eq(v) @@ -490,24 +510,16 @@ describe 'ClientStub' do @server.add_http2_port('0.0.0.0:0') end - def start_test_server(awake_mutex, awake_cond) + def expect_server_to_be_invoked(notifier) @server.start - @server_tag = Object.new - @server.request_call(@server_tag) - awake_mutex.synchronize { awake_cond.signal } - end - - def expect_server_to_be_invoked(awake_mutex, awake_cond) - start_test_server(awake_mutex, awake_cond) - ev = @server_queue.pluck(@server_tag, INFINITE_FUTURE) - fail OutOfTime if ev.nil? - server_call = ev.call - server_call.metadata = ev.result.metadata - finished_tag = Object.new - server_call.server_accept(@server_queue, finished_tag) - server_call.server_end_initial_metadata - GRPC::ActiveCall.new(server_call, @server_queue, NOOP, NOOP, - INFINITE_FUTURE, - finished_tag: finished_tag) + notifier.notify(nil) + server_tag = Object.new + recvd_rpc = @server.request_call(@server_queue, server_tag, + INFINITE_FUTURE) + recvd_call = recvd_rpc.call + recvd_call.metadata = recvd_rpc.metadata + recvd_call.run_batch(@server_queue, server_tag, Time.now + 2, + SEND_INITIAL_METADATA => nil) + GRPC::ActiveCall.new(recvd_call, @server_queue, noop, noop, INFINITE_FUTURE) end end diff --git a/src/ruby/spec/generic/rpc_desc_spec.rb b/src/ruby/spec/generic/rpc_desc_spec.rb index 39d1e83748..a68299465c 100644 --- a/src/ruby/spec/generic/rpc_desc_spec.rb +++ b/src/ruby/spec/generic/rpc_desc_spec.rb @@ -37,7 +37,6 @@ describe GRPC::RpcDesc do INTERNAL = GRPC::Core::StatusCodes::INTERNAL UNKNOWN = GRPC::Core::StatusCodes::UNKNOWN CallError = GRPC::Core::CallError - EventError = GRPC::Core::EventError before(:each) do @request_response = RpcDesc.new('rr', Object.new, Object.new, 'encode', @@ -63,24 +62,17 @@ describe GRPC::RpcDesc do it 'sends the specified status if BadStatus is raised' do expect(@call).to receive(:remote_read).once.and_return(Object.new) - expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK') + expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK', false) @request_response.run_server_method(@call, method(:bad_status)) end it 'sends status UNKNOWN if other StandardErrors are raised' do expect(@call).to receive(:remote_read).once.and_return(Object.new) - expect(@call).to receive(:send_status) .once.with(UNKNOWN, @no_reason) + expect(@call).to receive(:send_status) .once.with(UNKNOWN, @no_reason, + false) @request_response.run_server_method(@call, method(:other_error)) end - it 'absorbs EventError with no further action' do - expect(@call).to receive(:remote_read).once.and_raise(EventError) - blk = proc do - @request_response.run_server_method(@call, method(:fake_reqresp)) - end - expect(&blk).to_not raise_error - end - it 'absorbs CallError with no further action' do expect(@call).to receive(:remote_read).once.and_raise(CallError) blk = proc do @@ -93,8 +85,7 @@ describe GRPC::RpcDesc do req = Object.new expect(@call).to receive(:remote_read).once.and_return(req) expect(@call).to receive(:remote_send).once.with(@ok_response) - expect(@call).to receive(:send_status).once.with(OK, 'OK') - expect(@call).to receive(:finished).once + expect(@call).to receive(:send_status).once.with(OK, 'OK', true) @request_response.run_server_method(@call, method(:fake_reqresp)) end end @@ -107,23 +98,16 @@ describe GRPC::RpcDesc do end it 'sends the specified status if BadStatus is raised' do - expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK') + expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK', false) @client_streamer.run_server_method(@call, method(:bad_status_alt)) end it 'sends status UNKNOWN if other StandardErrors are raised' do - expect(@call).to receive(:send_status) .once.with(UNKNOWN, @no_reason) + expect(@call).to receive(:send_status) .once.with(UNKNOWN, @no_reason, + false) @client_streamer.run_server_method(@call, method(:other_error_alt)) end - it 'absorbs EventError with no further action' do - expect(@call).to receive(:remote_send).once.and_raise(EventError) - blk = proc do - @client_streamer.run_server_method(@call, method(:fake_clstream)) - end - expect(&blk).to_not raise_error - end - it 'absorbs CallError with no further action' do expect(@call).to receive(:remote_send).once.and_raise(CallError) blk = proc do @@ -134,8 +118,7 @@ describe GRPC::RpcDesc do it 'sends a response and closes the stream if there no errors' do expect(@call).to receive(:remote_send).once.with(@ok_response) - expect(@call).to receive(:send_status).once.with(OK, 'OK') - expect(@call).to receive(:finished).once + expect(@call).to receive(:send_status).once.with(OK, 'OK', true) @client_streamer.run_server_method(@call, method(:fake_clstream)) end end @@ -149,24 +132,17 @@ describe GRPC::RpcDesc do it 'sends the specified status if BadStatus is raised' do expect(@call).to receive(:remote_read).once.and_return(Object.new) - expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK') + expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK', false) @server_streamer.run_server_method(@call, method(:bad_status)) end it 'sends status UNKNOWN if other StandardErrors are raised' do expect(@call).to receive(:remote_read).once.and_return(Object.new) - expect(@call).to receive(:send_status) .once.with(UNKNOWN, @no_reason) + expect(@call).to receive(:send_status) .once.with(UNKNOWN, @no_reason, + false) @server_streamer.run_server_method(@call, method(:other_error)) end - it 'absorbs EventError with no further action' do - expect(@call).to receive(:remote_read).once.and_raise(EventError) - blk = proc do - @server_streamer.run_server_method(@call, method(:fake_svstream)) - end - expect(&blk).to_not raise_error - end - it 'absorbs CallError with no further action' do expect(@call).to receive(:remote_read).once.and_raise(CallError) blk = proc do @@ -179,8 +155,7 @@ describe GRPC::RpcDesc do req = Object.new expect(@call).to receive(:remote_read).once.and_return(req) expect(@call).to receive(:remote_send).twice.with(@ok_response) - expect(@call).to receive(:send_status).once.with(OK, 'OK') - expect(@call).to receive(:finished).once + expect(@call).to receive(:send_status).once.with(OK, 'OK', true) @server_streamer.run_server_method(@call, method(:fake_svstream)) end end @@ -197,20 +172,20 @@ describe GRPC::RpcDesc do it 'sends the specified status if BadStatus is raised' do e = GRPC::BadStatus.new(@bs_code, 'NOK') expect(@call).to receive(:run_server_bidi).and_raise(e) - expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK') + expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK', false) @bidi_streamer.run_server_method(@call, method(:bad_status_alt)) end it 'sends status UNKNOWN if other StandardErrors are raised' do expect(@call).to receive(:run_server_bidi).and_raise(StandardError) - expect(@call).to receive(:send_status).once.with(UNKNOWN, @no_reason) + expect(@call).to receive(:send_status).once.with(UNKNOWN, @no_reason, + false) @bidi_streamer.run_server_method(@call, method(:other_error_alt)) end it 'closes the stream if there no errors' do expect(@call).to receive(:run_server_bidi) - expect(@call).to receive(:send_status).once.with(OK, 'OK') - expect(@call).to receive(:finished).once + expect(@call).to receive(:send_status).once.with(OK, 'OK', true) @bidi_streamer.run_server_method(@call, method(:fake_bidistream)) end end diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb index 34e5cdcd04..f409d73e2f 100644 --- a/src/ruby/spec/generic/rpc_server_spec.rb +++ b/src/ruby/spec/generic/rpc_server_spec.rb @@ -364,7 +364,7 @@ describe GRPC::RpcServer do @srv.wait_till_running req = EchoMsg.new stub = SlowStub.new(@host, **@client_opts) - deadline = service.delay + 0.5 # wait for long enough + deadline = service.delay + 1.0 # wait for long enough expect(stub.an_rpc(req, deadline, k1: 'v1', k2: 'v2')).to be_a(EchoMsg) wanted_md = [{ 'k1' => 'v1', 'k2' => 'v2' }] expect(service.received_md).to eq(wanted_md) diff --git a/src/ruby/spec/metadata_spec.rb b/src/ruby/spec/metadata_spec.rb deleted file mode 100644 index 2472866692..0000000000 --- a/src/ruby/spec/metadata_spec.rb +++ /dev/null @@ -1,64 +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. - -require 'grpc' - -describe GRPC::Core::Metadata do - describe '#new' do - it 'should create instances' do - expect { GRPC::Core::Metadata.new('a key', 'a value') }.to_not raise_error - end - end - - describe '#key' do - md = GRPC::Core::Metadata.new('a key', 'a value') - it 'should be the constructor value' do - expect(md.key).to eq('a key') - end - end - - describe '#value' do - md = GRPC::Core::Metadata.new('a key', 'a value') - it 'should be the constuctor value' do - expect(md.value).to eq('a value') - end - end - - describe '#dup' do - it 'should create a copy that returns the correct key' do - md = GRPC::Core::Metadata.new('a key', 'a value') - expect(md.dup.key).to eq('a key') - end - - it 'should create a copy that returns the correct value' do - md = GRPC::Core::Metadata.new('a key', 'a value') - expect(md.dup.value).to eq('a value') - end - end -end diff --git a/test/core/iomgr/tcp_server_posix_test.c b/test/core/iomgr/tcp_server_posix_test.c index 6b80ee1ee8..1c02c4b2f8 100644 --- a/test/core/iomgr/tcp_server_posix_test.c +++ b/test/core/iomgr/tcp_server_posix_test.c @@ -38,6 +38,7 @@ #include <grpc/support/time.h> #include "test/core/util/test_config.h" +#include <sys/socket.h> #include <netinet/in.h> #include <string.h> #include <unistd.h> diff --git a/test/core/support/tls_test.c b/test/core/support/tls_test.c new file mode 100644 index 0000000000..8632fd4490 --- /dev/null +++ b/test/core/support/tls_test.c @@ -0,0 +1,82 @@ +/* + * + * 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 of gpr thread local storage support. */ + +#include <stdio.h> +#include <stdlib.h> +#include <grpc/support/log.h> +#include <grpc/support/sync.h> +#include <grpc/support/thd.h> +#include <grpc/support/tls.h> +#include "test/core/util/test_config.h" + +#define NUM_THREADS 100 + +GPR_TLS_DECL(test_var); + +static void thd_body(void *arg) { + gpr_intptr i; + + GPR_ASSERT(gpr_tls_get(&test_var) == 0); + + for (i = 0; i < 10000000; i++) { + gpr_tls_set(&test_var, i); + GPR_ASSERT(gpr_tls_get(&test_var) == i); + } +} + +/* ------------------------------------------------- */ + +int main(int argc, char *argv[]) { + gpr_thd_options opt = gpr_thd_options_default(); + int i; + gpr_thd_id threads[NUM_THREADS]; + + grpc_test_init(argc, argv); + + gpr_tls_init(&test_var); + + gpr_thd_options_set_joinable(&opt); + + for (i = 0; i < NUM_THREADS; i++) { + gpr_thd_new(&threads[i], thd_body, NULL, &opt); + } + for (i = 0; i < NUM_THREADS; i++) { + gpr_thd_join(threads[i]); + } + + gpr_tls_destroy(&test_var); + + return 0; +} diff --git a/tools/buildgen/generate_projects.sh b/tools/buildgen/generate_projects.sh index 7a12440db2..45f08df38f 100755 --- a/tools/buildgen/generate_projects.sh +++ b/tools/buildgen/generate_projects.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # Copyright 2015, Google Inc. # All rights reserved. # @@ -31,7 +31,7 @@ set -e -if [ "x$TEST" == "x" ] ; then +if [ "x$TEST" = "x" ] ; then TEST=false fi @@ -61,12 +61,12 @@ for dir in . ; do out=${out%.*} # strip template extension json_files="build.json $end2end_test_build" data=`for i in $json_files; do echo -n "-d $i "; done` - if [ $TEST == true ] ; then + if [ "x$TEST" = "xtrue" ] ; then actual_out=$out out=`mktemp /tmp/gentXXXXXX` fi $mako_renderer $plugins $data -o $out $file - if [ $TEST == true ] ; then + if [ "x$TEST" = "xtrue" ] ; then diff -q $out $actual_out rm $out fi diff --git a/tools/dockerfile/grpc_python/Dockerfile b/tools/dockerfile/grpc_python/Dockerfile index 62ef785a31..aa29685bee 100644 --- a/tools/dockerfile/grpc_python/Dockerfile +++ b/tools/dockerfile/grpc_python/Dockerfile @@ -66,5 +66,8 @@ RUN cd /var/local/git/grpc \ # Add a cacerts directory containing the Google root pem file, allowing the interop client to access the production test instance ADD cacerts cacerts +# Add a service_account directory containing the auth creds file +ADD service_account service_account + # Specify the default command such that the interop server runs on its known testing port CMD ["/bin/bash", "-l", "-c", "python2.7 -m interop.server --use_tls --port 8050"] diff --git a/tools/gce_setup/grpc_docker.sh b/tools/gce_setup/grpc_docker.sh index 40634291cf..c68903b816 100755 --- a/tools/gce_setup/grpc_docker.sh +++ b/tools/gce_setup/grpc_docker.sh @@ -1047,7 +1047,7 @@ grpc_interop_gen_python_cmd() { # flags= .... # generic flags to include the command # cmd=$($grpc_gen_test_cmd $flags) grpc_cloud_prod_auth_service_account_creds_gen_python_cmd() { - local cmd_prefix="sudo docker run grpc/ruby bin/bash -l -c"; + local cmd_prefix="sudo docker run grpc/python bin/bash -l -c"; local gfe_flags=$(_grpc_prod_gfe_flags) local added_gfe_flags=$(_grpc_default_creds_test_flags) local env_prefix="SSL_CERT_FILE=/cacerts/roots.pem" @@ -1062,7 +1062,7 @@ grpc_cloud_prod_auth_service_account_creds_gen_python_cmd() { # flags= .... # generic flags to include the command # cmd=$($grpc_gen_test_cmd $flags) grpc_cloud_prod_auth_compute_engine_creds_gen_python_cmd() { - local cmd_prefix="sudo docker run grpc/ruby bin/bash -l -c"; + local cmd_prefix="sudo docker run grpc/python bin/bash -l -c"; local gfe_flags=$(_grpc_prod_gfe_flags) local added_gfe_flags=$(_grpc_gce_test_flags) local env_prefix="SSL_CERT_FILE=/cacerts/roots.pem" diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index 3cf6ddf262..14ee1790c6 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2.7 +#!/usr/bin/env python # Copyright 2015, Google Inc. # All rights reserved. # diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json index eab07040e7..b25616474d 100644 --- a/tools/run_tests/tests.json +++ b/tools/run_tests/tests.json @@ -179,6 +179,11 @@ { "flaky": false, "language": "c", + "name": "gpr_tls_test" + }, + { + "flaky": false, + "language": "c", "name": "gpr_useful_test" }, { diff --git a/vsprojects/vs2010/Grpc.mak b/vsprojects/vs2010/Grpc.mak index 203c787287..727aa37781 100644 --- a/vsprojects/vs2010/Grpc.mak +++ b/vsprojects/vs2010/Grpc.mak @@ -53,13 +53,13 @@ grpc_test_util: $(OUT_DIR): mkdir $(OUT_DIR) -buildtests: alarm_heap_test.exe alarm_list_test.exe alarm_test.exe alpn_test.exe bin_encoder_test.exe census_hash_table_test.exe census_statistics_multiple_writers_circular_buffer_test.exe census_statistics_multiple_writers_test.exe census_statistics_performance_test.exe census_statistics_quick_test.exe census_statistics_small_log_test.exe census_stats_store_test.exe census_stub_test.exe census_trace_store_test.exe census_window_stats_test.exe chttp2_status_conversion_test.exe chttp2_stream_encoder_test.exe chttp2_stream_map_test.exe chttp2_transport_end2end_test.exe dualstack_socket_test.exe echo_test.exe fd_posix_test.exe fling_stream_test.exe fling_test.exe gpr_cancellable_test.exe gpr_cmdline_test.exe gpr_env_test.exe gpr_file_test.exe gpr_histogram_test.exe gpr_host_port_test.exe gpr_log_test.exe gpr_slice_buffer_test.exe gpr_slice_test.exe gpr_string_test.exe gpr_sync_test.exe gpr_thd_test.exe gpr_time_test.exe gpr_useful_test.exe grpc_base64_test.exe grpc_byte_buffer_reader_test.exe grpc_channel_stack_test.exe grpc_completion_queue_test.exe grpc_credentials_test.exe grpc_json_token_test.exe grpc_stream_op_test.exe hpack_parser_test.exe hpack_table_test.exe httpcli_format_request_test.exe httpcli_parser_test.exe httpcli_test.exe json_rewrite_test.exe json_test.exe lame_client_test.exe message_compress_test.exe metadata_buffer_test.exe multi_init_test.exe murmur_hash_test.exe no_server_test.exe poll_kick_posix_test.exe resolve_address_test.exe secure_endpoint_test.exe sockaddr_utils_test.exe tcp_client_posix_test.exe tcp_posix_test.exe tcp_server_posix_test.exe time_averaged_stats_test.exe time_test.exe timeout_encoding_test.exe transport_metadata_test.exe transport_security_test.exe +buildtests: alarm_heap_test.exe alarm_list_test.exe alarm_test.exe alpn_test.exe bin_encoder_test.exe census_hash_table_test.exe census_statistics_multiple_writers_circular_buffer_test.exe census_statistics_multiple_writers_test.exe census_statistics_performance_test.exe census_statistics_quick_test.exe census_statistics_small_log_test.exe census_stats_store_test.exe census_stub_test.exe census_trace_store_test.exe census_window_stats_test.exe chttp2_status_conversion_test.exe chttp2_stream_encoder_test.exe chttp2_stream_map_test.exe chttp2_transport_end2end_test.exe dualstack_socket_test.exe echo_test.exe fd_posix_test.exe fling_stream_test.exe fling_test.exe gpr_cancellable_test.exe gpr_cmdline_test.exe gpr_env_test.exe gpr_file_test.exe gpr_histogram_test.exe gpr_host_port_test.exe gpr_log_test.exe gpr_slice_buffer_test.exe gpr_slice_test.exe gpr_string_test.exe gpr_sync_test.exe gpr_thd_test.exe gpr_time_test.exe gpr_tls_test.exe gpr_useful_test.exe grpc_base64_test.exe grpc_byte_buffer_reader_test.exe grpc_channel_stack_test.exe grpc_completion_queue_test.exe grpc_credentials_test.exe grpc_json_token_test.exe grpc_stream_op_test.exe hpack_parser_test.exe hpack_table_test.exe httpcli_format_request_test.exe httpcli_parser_test.exe httpcli_test.exe json_rewrite_test.exe json_test.exe lame_client_test.exe message_compress_test.exe metadata_buffer_test.exe multi_init_test.exe murmur_hash_test.exe no_server_test.exe poll_kick_posix_test.exe resolve_address_test.exe secure_endpoint_test.exe sockaddr_utils_test.exe tcp_client_posix_test.exe tcp_posix_test.exe tcp_server_posix_test.exe time_averaged_stats_test.exe time_test.exe timeout_encoding_test.exe transport_metadata_test.exe transport_security_test.exe echo All tests built. -test: alarm_heap_test alarm_list_test alarm_test alpn_test bin_encoder_test census_hash_table_test census_statistics_multiple_writers_circular_buffer_test census_statistics_multiple_writers_test census_statistics_performance_test census_statistics_quick_test census_statistics_small_log_test census_stats_store_test census_stub_test census_trace_store_test census_window_stats_test chttp2_status_conversion_test chttp2_stream_encoder_test chttp2_stream_map_test chttp2_transport_end2end_test dualstack_socket_test echo_test fd_posix_test fling_stream_test fling_test gpr_cancellable_test gpr_cmdline_test gpr_env_test gpr_file_test gpr_histogram_test gpr_host_port_test gpr_log_test gpr_slice_buffer_test gpr_slice_test gpr_string_test gpr_sync_test gpr_thd_test gpr_time_test gpr_useful_test grpc_base64_test grpc_byte_buffer_reader_test grpc_channel_stack_test grpc_completion_queue_test grpc_credentials_test grpc_json_token_test grpc_stream_op_test hpack_parser_test hpack_table_test httpcli_format_request_test httpcli_parser_test httpcli_test json_rewrite_test json_test lame_client_test message_compress_test metadata_buffer_test multi_init_test murmur_hash_test no_server_test poll_kick_posix_test resolve_address_test secure_endpoint_test sockaddr_utils_test tcp_client_posix_test tcp_posix_test tcp_server_posix_test time_averaged_stats_test time_test timeout_encoding_test transport_metadata_test transport_security_test +test: alarm_heap_test alarm_list_test alarm_test alpn_test bin_encoder_test census_hash_table_test census_statistics_multiple_writers_circular_buffer_test census_statistics_multiple_writers_test census_statistics_performance_test census_statistics_quick_test census_statistics_small_log_test census_stats_store_test census_stub_test census_trace_store_test census_window_stats_test chttp2_status_conversion_test chttp2_stream_encoder_test chttp2_stream_map_test chttp2_transport_end2end_test dualstack_socket_test echo_test fd_posix_test fling_stream_test fling_test gpr_cancellable_test gpr_cmdline_test gpr_env_test gpr_file_test gpr_histogram_test gpr_host_port_test gpr_log_test gpr_slice_buffer_test gpr_slice_test gpr_string_test gpr_sync_test gpr_thd_test gpr_time_test gpr_tls_test gpr_useful_test grpc_base64_test grpc_byte_buffer_reader_test grpc_channel_stack_test grpc_completion_queue_test grpc_credentials_test grpc_json_token_test grpc_stream_op_test hpack_parser_test hpack_table_test httpcli_format_request_test httpcli_parser_test httpcli_test json_rewrite_test json_test lame_client_test message_compress_test metadata_buffer_test multi_init_test murmur_hash_test no_server_test poll_kick_posix_test resolve_address_test secure_endpoint_test sockaddr_utils_test tcp_client_posix_test tcp_posix_test tcp_server_posix_test time_averaged_stats_test time_test timeout_encoding_test transport_metadata_test transport_security_test echo All tests ran. -test_gpr: gpr_cancellable_test gpr_cmdline_test gpr_env_test gpr_file_test gpr_histogram_test gpr_host_port_test gpr_log_test gpr_slice_buffer_test gpr_slice_test gpr_string_test gpr_sync_test gpr_thd_test gpr_time_test gpr_useful_test +test_gpr: gpr_cancellable_test gpr_cmdline_test gpr_env_test gpr_file_test gpr_histogram_test gpr_host_port_test gpr_log_test gpr_slice_buffer_test gpr_slice_test gpr_string_test gpr_sync_test gpr_thd_test gpr_time_test gpr_tls_test gpr_useful_test echo All tests ran. alarm_heap_test.exe: grpc_test_util @@ -398,6 +398,14 @@ gpr_time_test: gpr_time_test.exe echo Running gpr_time_test $(OUT_DIR)\gpr_time_test.exe +gpr_tls_test.exe: grpc_test_util + echo Building gpr_tls_test + $(CC) $(CFLAGS) /Fo:$(OUT_DIR)\ ..\..\test\core\support\tls_test.c + $(LINK) $(LFLAGS) /OUT:"$(OUT_DIR)\gpr_tls_test.exe" Debug\gpr_test_util.lib Debug\gpr.lib $(LIBS) $(OUT_DIR)\tls_test.obj +gpr_tls_test: gpr_tls_test.exe + echo Running gpr_tls_test + $(OUT_DIR)\gpr_tls_test.exe + gpr_useful_test.exe: grpc_test_util echo Building gpr_useful_test $(CC) $(CFLAGS) /Fo:$(OUT_DIR)\ ..\..\test\core\support\useful_test.c diff --git a/vsprojects/vs2010/gpr.vcxproj b/vsprojects/vs2010/gpr.vcxproj index d23124c86c..62ab6f3bd0 100644 --- a/vsprojects/vs2010/gpr.vcxproj +++ b/vsprojects/vs2010/gpr.vcxproj @@ -97,6 +97,10 @@ <ClInclude Include="..\..\include\grpc\support\sync_win32.h" /> <ClInclude Include="..\..\include\grpc\support\thd.h" /> <ClInclude Include="..\..\include\grpc\support\time.h" /> + <ClInclude Include="..\..\include\grpc\support\tls.h" /> + <ClInclude Include="..\..\include\grpc\support\tls_gcc.h" /> + <ClInclude Include="..\..\include\grpc\support\tls_msvc.h" /> + <ClInclude Include="..\..\include\grpc\support\tls_pthread.h" /> <ClInclude Include="..\..\include\grpc\support\useful.h" /> </ItemGroup> <ItemGroup> diff --git a/vsprojects/vs2010/gpr.vcxproj.filters b/vsprojects/vs2010/gpr.vcxproj.filters index 1f8794441b..13fdb3fef8 100644 --- a/vsprojects/vs2010/gpr.vcxproj.filters +++ b/vsprojects/vs2010/gpr.vcxproj.filters @@ -171,6 +171,18 @@ <ClInclude Include="..\..\include\grpc\support\time.h"> <Filter>include\grpc\support</Filter> </ClInclude> + <ClInclude Include="..\..\include\grpc\support\tls.h"> + <Filter>include\grpc\support</Filter> + </ClInclude> + <ClInclude Include="..\..\include\grpc\support\tls_gcc.h"> + <Filter>include\grpc\support</Filter> + </ClInclude> + <ClInclude Include="..\..\include\grpc\support\tls_msvc.h"> + <Filter>include\grpc\support</Filter> + </ClInclude> + <ClInclude Include="..\..\include\grpc\support\tls_pthread.h"> + <Filter>include\grpc\support</Filter> + </ClInclude> <ClInclude Include="..\..\include\grpc\support\useful.h"> <Filter>include\grpc\support</Filter> </ClInclude> diff --git a/vsprojects/vs2013/Grpc.mak b/vsprojects/vs2013/Grpc.mak index 203c787287..727aa37781 100644 --- a/vsprojects/vs2013/Grpc.mak +++ b/vsprojects/vs2013/Grpc.mak @@ -53,13 +53,13 @@ grpc_test_util: $(OUT_DIR): mkdir $(OUT_DIR) -buildtests: alarm_heap_test.exe alarm_list_test.exe alarm_test.exe alpn_test.exe bin_encoder_test.exe census_hash_table_test.exe census_statistics_multiple_writers_circular_buffer_test.exe census_statistics_multiple_writers_test.exe census_statistics_performance_test.exe census_statistics_quick_test.exe census_statistics_small_log_test.exe census_stats_store_test.exe census_stub_test.exe census_trace_store_test.exe census_window_stats_test.exe chttp2_status_conversion_test.exe chttp2_stream_encoder_test.exe chttp2_stream_map_test.exe chttp2_transport_end2end_test.exe dualstack_socket_test.exe echo_test.exe fd_posix_test.exe fling_stream_test.exe fling_test.exe gpr_cancellable_test.exe gpr_cmdline_test.exe gpr_env_test.exe gpr_file_test.exe gpr_histogram_test.exe gpr_host_port_test.exe gpr_log_test.exe gpr_slice_buffer_test.exe gpr_slice_test.exe gpr_string_test.exe gpr_sync_test.exe gpr_thd_test.exe gpr_time_test.exe gpr_useful_test.exe grpc_base64_test.exe grpc_byte_buffer_reader_test.exe grpc_channel_stack_test.exe grpc_completion_queue_test.exe grpc_credentials_test.exe grpc_json_token_test.exe grpc_stream_op_test.exe hpack_parser_test.exe hpack_table_test.exe httpcli_format_request_test.exe httpcli_parser_test.exe httpcli_test.exe json_rewrite_test.exe json_test.exe lame_client_test.exe message_compress_test.exe metadata_buffer_test.exe multi_init_test.exe murmur_hash_test.exe no_server_test.exe poll_kick_posix_test.exe resolve_address_test.exe secure_endpoint_test.exe sockaddr_utils_test.exe tcp_client_posix_test.exe tcp_posix_test.exe tcp_server_posix_test.exe time_averaged_stats_test.exe time_test.exe timeout_encoding_test.exe transport_metadata_test.exe transport_security_test.exe +buildtests: alarm_heap_test.exe alarm_list_test.exe alarm_test.exe alpn_test.exe bin_encoder_test.exe census_hash_table_test.exe census_statistics_multiple_writers_circular_buffer_test.exe census_statistics_multiple_writers_test.exe census_statistics_performance_test.exe census_statistics_quick_test.exe census_statistics_small_log_test.exe census_stats_store_test.exe census_stub_test.exe census_trace_store_test.exe census_window_stats_test.exe chttp2_status_conversion_test.exe chttp2_stream_encoder_test.exe chttp2_stream_map_test.exe chttp2_transport_end2end_test.exe dualstack_socket_test.exe echo_test.exe fd_posix_test.exe fling_stream_test.exe fling_test.exe gpr_cancellable_test.exe gpr_cmdline_test.exe gpr_env_test.exe gpr_file_test.exe gpr_histogram_test.exe gpr_host_port_test.exe gpr_log_test.exe gpr_slice_buffer_test.exe gpr_slice_test.exe gpr_string_test.exe gpr_sync_test.exe gpr_thd_test.exe gpr_time_test.exe gpr_tls_test.exe gpr_useful_test.exe grpc_base64_test.exe grpc_byte_buffer_reader_test.exe grpc_channel_stack_test.exe grpc_completion_queue_test.exe grpc_credentials_test.exe grpc_json_token_test.exe grpc_stream_op_test.exe hpack_parser_test.exe hpack_table_test.exe httpcli_format_request_test.exe httpcli_parser_test.exe httpcli_test.exe json_rewrite_test.exe json_test.exe lame_client_test.exe message_compress_test.exe metadata_buffer_test.exe multi_init_test.exe murmur_hash_test.exe no_server_test.exe poll_kick_posix_test.exe resolve_address_test.exe secure_endpoint_test.exe sockaddr_utils_test.exe tcp_client_posix_test.exe tcp_posix_test.exe tcp_server_posix_test.exe time_averaged_stats_test.exe time_test.exe timeout_encoding_test.exe transport_metadata_test.exe transport_security_test.exe echo All tests built. -test: alarm_heap_test alarm_list_test alarm_test alpn_test bin_encoder_test census_hash_table_test census_statistics_multiple_writers_circular_buffer_test census_statistics_multiple_writers_test census_statistics_performance_test census_statistics_quick_test census_statistics_small_log_test census_stats_store_test census_stub_test census_trace_store_test census_window_stats_test chttp2_status_conversion_test chttp2_stream_encoder_test chttp2_stream_map_test chttp2_transport_end2end_test dualstack_socket_test echo_test fd_posix_test fling_stream_test fling_test gpr_cancellable_test gpr_cmdline_test gpr_env_test gpr_file_test gpr_histogram_test gpr_host_port_test gpr_log_test gpr_slice_buffer_test gpr_slice_test gpr_string_test gpr_sync_test gpr_thd_test gpr_time_test gpr_useful_test grpc_base64_test grpc_byte_buffer_reader_test grpc_channel_stack_test grpc_completion_queue_test grpc_credentials_test grpc_json_token_test grpc_stream_op_test hpack_parser_test hpack_table_test httpcli_format_request_test httpcli_parser_test httpcli_test json_rewrite_test json_test lame_client_test message_compress_test metadata_buffer_test multi_init_test murmur_hash_test no_server_test poll_kick_posix_test resolve_address_test secure_endpoint_test sockaddr_utils_test tcp_client_posix_test tcp_posix_test tcp_server_posix_test time_averaged_stats_test time_test timeout_encoding_test transport_metadata_test transport_security_test +test: alarm_heap_test alarm_list_test alarm_test alpn_test bin_encoder_test census_hash_table_test census_statistics_multiple_writers_circular_buffer_test census_statistics_multiple_writers_test census_statistics_performance_test census_statistics_quick_test census_statistics_small_log_test census_stats_store_test census_stub_test census_trace_store_test census_window_stats_test chttp2_status_conversion_test chttp2_stream_encoder_test chttp2_stream_map_test chttp2_transport_end2end_test dualstack_socket_test echo_test fd_posix_test fling_stream_test fling_test gpr_cancellable_test gpr_cmdline_test gpr_env_test gpr_file_test gpr_histogram_test gpr_host_port_test gpr_log_test gpr_slice_buffer_test gpr_slice_test gpr_string_test gpr_sync_test gpr_thd_test gpr_time_test gpr_tls_test gpr_useful_test grpc_base64_test grpc_byte_buffer_reader_test grpc_channel_stack_test grpc_completion_queue_test grpc_credentials_test grpc_json_token_test grpc_stream_op_test hpack_parser_test hpack_table_test httpcli_format_request_test httpcli_parser_test httpcli_test json_rewrite_test json_test lame_client_test message_compress_test metadata_buffer_test multi_init_test murmur_hash_test no_server_test poll_kick_posix_test resolve_address_test secure_endpoint_test sockaddr_utils_test tcp_client_posix_test tcp_posix_test tcp_server_posix_test time_averaged_stats_test time_test timeout_encoding_test transport_metadata_test transport_security_test echo All tests ran. -test_gpr: gpr_cancellable_test gpr_cmdline_test gpr_env_test gpr_file_test gpr_histogram_test gpr_host_port_test gpr_log_test gpr_slice_buffer_test gpr_slice_test gpr_string_test gpr_sync_test gpr_thd_test gpr_time_test gpr_useful_test +test_gpr: gpr_cancellable_test gpr_cmdline_test gpr_env_test gpr_file_test gpr_histogram_test gpr_host_port_test gpr_log_test gpr_slice_buffer_test gpr_slice_test gpr_string_test gpr_sync_test gpr_thd_test gpr_time_test gpr_tls_test gpr_useful_test echo All tests ran. alarm_heap_test.exe: grpc_test_util @@ -398,6 +398,14 @@ gpr_time_test: gpr_time_test.exe echo Running gpr_time_test $(OUT_DIR)\gpr_time_test.exe +gpr_tls_test.exe: grpc_test_util + echo Building gpr_tls_test + $(CC) $(CFLAGS) /Fo:$(OUT_DIR)\ ..\..\test\core\support\tls_test.c + $(LINK) $(LFLAGS) /OUT:"$(OUT_DIR)\gpr_tls_test.exe" Debug\gpr_test_util.lib Debug\gpr.lib $(LIBS) $(OUT_DIR)\tls_test.obj +gpr_tls_test: gpr_tls_test.exe + echo Running gpr_tls_test + $(OUT_DIR)\gpr_tls_test.exe + gpr_useful_test.exe: grpc_test_util echo Building gpr_useful_test $(CC) $(CFLAGS) /Fo:$(OUT_DIR)\ ..\..\test\core\support\useful_test.c diff --git a/vsprojects/vs2013/gpr.vcxproj b/vsprojects/vs2013/gpr.vcxproj index e0fa68e035..1876c0ff0a 100644 --- a/vsprojects/vs2013/gpr.vcxproj +++ b/vsprojects/vs2013/gpr.vcxproj @@ -99,6 +99,10 @@ <ClInclude Include="..\..\include\grpc\support\sync_win32.h" /> <ClInclude Include="..\..\include\grpc\support\thd.h" /> <ClInclude Include="..\..\include\grpc\support\time.h" /> + <ClInclude Include="..\..\include\grpc\support\tls.h" /> + <ClInclude Include="..\..\include\grpc\support\tls_gcc.h" /> + <ClInclude Include="..\..\include\grpc\support\tls_msvc.h" /> + <ClInclude Include="..\..\include\grpc\support\tls_pthread.h" /> <ClInclude Include="..\..\include\grpc\support\useful.h" /> </ItemGroup> <ItemGroup> diff --git a/vsprojects/vs2013/gpr.vcxproj.filters b/vsprojects/vs2013/gpr.vcxproj.filters index 1f8794441b..13fdb3fef8 100644 --- a/vsprojects/vs2013/gpr.vcxproj.filters +++ b/vsprojects/vs2013/gpr.vcxproj.filters @@ -171,6 +171,18 @@ <ClInclude Include="..\..\include\grpc\support\time.h"> <Filter>include\grpc\support</Filter> </ClInclude> + <ClInclude Include="..\..\include\grpc\support\tls.h"> + <Filter>include\grpc\support</Filter> + </ClInclude> + <ClInclude Include="..\..\include\grpc\support\tls_gcc.h"> + <Filter>include\grpc\support</Filter> + </ClInclude> + <ClInclude Include="..\..\include\grpc\support\tls_msvc.h"> + <Filter>include\grpc\support</Filter> + </ClInclude> + <ClInclude Include="..\..\include\grpc\support\tls_pthread.h"> + <Filter>include\grpc\support</Filter> + </ClInclude> <ClInclude Include="..\..\include\grpc\support\useful.h"> <Filter>include\grpc\support</Filter> </ClInclude> |