diff --git a/Makefile b/Makefile index 846d9772d4e..40f98b90cad 100644 --- a/Makefile +++ b/Makefile @@ -189,11 +189,13 @@ OPENSSL_ALPN_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o /dev/null test/build/ope ZLIB_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o /dev/null test/build/zlib.c -lz $(LDFLAGS) PERFTOOLS_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o /dev/null test/build/perftools.c -lprofiler $(LDFLAGS) +ifndef REQUIRE_CUSTOM_LIBRARIES_$(CONFIG) HAS_SYSTEM_PERFTOOLS = $(shell $(PERFTOOLS_CHECK_CMD) 2> /dev/null && echo true || echo false) ifeq ($(HAS_SYSTEM_PERFTOOLS),true) DEFINES += GRPC_HAVE_PERFTOOLS LIBS += profiler endif +endif ifndef REQUIRE_CUSTOM_LIBRARIES_$(CONFIG) HAS_SYSTEM_OPENSSL_ALPN = $(shell $(OPENSSL_ALPN_CHECK_CMD) 2> /dev/null && echo true || echo false) @@ -336,6 +338,8 @@ gpr_cmdline_test: bins/$(CONFIG)/gpr_cmdline_test gpr_histogram_test: bins/$(CONFIG)/gpr_histogram_test gpr_host_port_test: bins/$(CONFIG)/gpr_host_port_test gpr_log_test: bins/$(CONFIG)/gpr_log_test +gpr_file_test: bins/$(CONFIG)/gpr_file_test +gpr_env_test: bins/$(CONFIG)/gpr_env_test gpr_slice_buffer_test: bins/$(CONFIG)/gpr_slice_buffer_test gpr_slice_test: bins/$(CONFIG)/gpr_slice_test gpr_string_test: bins/$(CONFIG)/gpr_string_test @@ -569,7 +573,7 @@ privatelibs_cxx: libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libtips_cl buildtests: buildtests_c buildtests_cxx -buildtests_c: privatelibs_c bins/$(CONFIG)/alarm_heap_test bins/$(CONFIG)/alarm_list_test bins/$(CONFIG)/alarm_test bins/$(CONFIG)/alpn_test bins/$(CONFIG)/bin_encoder_test bins/$(CONFIG)/census_hash_table_test bins/$(CONFIG)/census_statistics_multiple_writers_circular_buffer_test bins/$(CONFIG)/census_statistics_multiple_writers_test bins/$(CONFIG)/census_statistics_performance_test bins/$(CONFIG)/census_statistics_quick_test bins/$(CONFIG)/census_statistics_small_log_test bins/$(CONFIG)/census_stub_test bins/$(CONFIG)/census_window_stats_test bins/$(CONFIG)/chttp2_status_conversion_test bins/$(CONFIG)/chttp2_stream_encoder_test bins/$(CONFIG)/chttp2_stream_map_test bins/$(CONFIG)/chttp2_transport_end2end_test bins/$(CONFIG)/dualstack_socket_test bins/$(CONFIG)/echo_client bins/$(CONFIG)/echo_server bins/$(CONFIG)/echo_test bins/$(CONFIG)/fd_posix_test bins/$(CONFIG)/fling_client bins/$(CONFIG)/fling_server bins/$(CONFIG)/fling_stream_test bins/$(CONFIG)/fling_test bins/$(CONFIG)/gpr_cancellable_test bins/$(CONFIG)/gpr_cmdline_test bins/$(CONFIG)/gpr_histogram_test bins/$(CONFIG)/gpr_host_port_test bins/$(CONFIG)/gpr_log_test bins/$(CONFIG)/gpr_slice_buffer_test bins/$(CONFIG)/gpr_slice_test bins/$(CONFIG)/gpr_string_test bins/$(CONFIG)/gpr_sync_test bins/$(CONFIG)/gpr_thd_test bins/$(CONFIG)/gpr_time_test bins/$(CONFIG)/gpr_useful_test bins/$(CONFIG)/grpc_base64_test bins/$(CONFIG)/grpc_byte_buffer_reader_test bins/$(CONFIG)/grpc_channel_stack_test bins/$(CONFIG)/grpc_completion_queue_test bins/$(CONFIG)/grpc_credentials_test bins/$(CONFIG)/grpc_json_token_test bins/$(CONFIG)/grpc_stream_op_test bins/$(CONFIG)/hpack_parser_test bins/$(CONFIG)/hpack_table_test bins/$(CONFIG)/httpcli_format_request_test bins/$(CONFIG)/httpcli_parser_test bins/$(CONFIG)/httpcli_test bins/$(CONFIG)/json_rewrite bins/$(CONFIG)/json_rewrite_test bins/$(CONFIG)/json_test bins/$(CONFIG)/lame_client_test bins/$(CONFIG)/message_compress_test bins/$(CONFIG)/metadata_buffer_test bins/$(CONFIG)/murmur_hash_test bins/$(CONFIG)/no_server_test bins/$(CONFIG)/poll_kick_posix_test bins/$(CONFIG)/resolve_address_test bins/$(CONFIG)/secure_endpoint_test bins/$(CONFIG)/sockaddr_utils_test bins/$(CONFIG)/tcp_client_posix_test bins/$(CONFIG)/tcp_posix_test bins/$(CONFIG)/tcp_server_posix_test bins/$(CONFIG)/time_averaged_stats_test bins/$(CONFIG)/time_test bins/$(CONFIG)/timeout_encoding_test bins/$(CONFIG)/transport_metadata_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_accept_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_invoke_test bins/$(CONFIG)/chttp2_fake_security_cancel_before_invoke_test bins/$(CONFIG)/chttp2_fake_security_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_fake_security_census_simple_request_test bins/$(CONFIG)/chttp2_fake_security_disappearing_server_test bins/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_fake_security_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_fake_security_invoke_large_request_test bins/$(CONFIG)/chttp2_fake_security_max_concurrent_streams_test bins/$(CONFIG)/chttp2_fake_security_no_op_test bins/$(CONFIG)/chttp2_fake_security_ping_pong_streaming_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_simple_delayed_request_test bins/$(CONFIG)/chttp2_fake_security_simple_request_test bins/$(CONFIG)/chttp2_fake_security_thread_stress_test bins/$(CONFIG)/chttp2_fake_security_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_fullstack_no_op_test bins/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_fullstack_simple_request_test bins/$(CONFIG)/chttp2_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_no_op_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_no_op_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_invoke_test bins/$(CONFIG)/chttp2_socket_pair_cancel_before_invoke_test bins/$(CONFIG)/chttp2_socket_pair_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_socket_pair_census_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_disappearing_server_test bins/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_socket_pair_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_socket_pair_invoke_large_request_test bins/$(CONFIG)/chttp2_socket_pair_max_concurrent_streams_test bins/$(CONFIG)/chttp2_socket_pair_no_op_test bins/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_test bins/$(CONFIG)/chttp2_socket_pair_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_thread_stress_test bins/$(CONFIG)/chttp2_socket_pair_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_census_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_disappearing_server_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_no_op_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_thread_stress_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test +buildtests_c: privatelibs_c bins/$(CONFIG)/alarm_heap_test bins/$(CONFIG)/alarm_list_test bins/$(CONFIG)/alarm_test bins/$(CONFIG)/alpn_test bins/$(CONFIG)/bin_encoder_test bins/$(CONFIG)/census_hash_table_test bins/$(CONFIG)/census_statistics_multiple_writers_circular_buffer_test bins/$(CONFIG)/census_statistics_multiple_writers_test bins/$(CONFIG)/census_statistics_performance_test bins/$(CONFIG)/census_statistics_quick_test bins/$(CONFIG)/census_statistics_small_log_test bins/$(CONFIG)/census_stub_test bins/$(CONFIG)/census_window_stats_test bins/$(CONFIG)/chttp2_status_conversion_test bins/$(CONFIG)/chttp2_stream_encoder_test bins/$(CONFIG)/chttp2_stream_map_test bins/$(CONFIG)/chttp2_transport_end2end_test bins/$(CONFIG)/dualstack_socket_test bins/$(CONFIG)/echo_client bins/$(CONFIG)/echo_server bins/$(CONFIG)/echo_test bins/$(CONFIG)/fd_posix_test bins/$(CONFIG)/fling_client bins/$(CONFIG)/fling_server bins/$(CONFIG)/fling_stream_test bins/$(CONFIG)/fling_test bins/$(CONFIG)/gpr_cancellable_test bins/$(CONFIG)/gpr_cmdline_test bins/$(CONFIG)/gpr_histogram_test bins/$(CONFIG)/gpr_host_port_test bins/$(CONFIG)/gpr_log_test bins/$(CONFIG)/gpr_file_test bins/$(CONFIG)/gpr_env_test bins/$(CONFIG)/gpr_slice_buffer_test bins/$(CONFIG)/gpr_slice_test bins/$(CONFIG)/gpr_string_test bins/$(CONFIG)/gpr_sync_test bins/$(CONFIG)/gpr_thd_test bins/$(CONFIG)/gpr_time_test bins/$(CONFIG)/gpr_useful_test bins/$(CONFIG)/grpc_base64_test bins/$(CONFIG)/grpc_byte_buffer_reader_test bins/$(CONFIG)/grpc_channel_stack_test bins/$(CONFIG)/grpc_completion_queue_test bins/$(CONFIG)/grpc_credentials_test bins/$(CONFIG)/grpc_json_token_test bins/$(CONFIG)/grpc_stream_op_test bins/$(CONFIG)/hpack_parser_test bins/$(CONFIG)/hpack_table_test bins/$(CONFIG)/httpcli_format_request_test bins/$(CONFIG)/httpcli_parser_test bins/$(CONFIG)/httpcli_test bins/$(CONFIG)/json_rewrite bins/$(CONFIG)/json_rewrite_test bins/$(CONFIG)/json_test bins/$(CONFIG)/lame_client_test bins/$(CONFIG)/message_compress_test bins/$(CONFIG)/metadata_buffer_test bins/$(CONFIG)/murmur_hash_test bins/$(CONFIG)/no_server_test bins/$(CONFIG)/poll_kick_posix_test bins/$(CONFIG)/resolve_address_test bins/$(CONFIG)/secure_endpoint_test bins/$(CONFIG)/sockaddr_utils_test bins/$(CONFIG)/tcp_client_posix_test bins/$(CONFIG)/tcp_posix_test bins/$(CONFIG)/tcp_server_posix_test bins/$(CONFIG)/time_averaged_stats_test bins/$(CONFIG)/time_test bins/$(CONFIG)/timeout_encoding_test bins/$(CONFIG)/transport_metadata_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_accept_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_invoke_test bins/$(CONFIG)/chttp2_fake_security_cancel_before_invoke_test bins/$(CONFIG)/chttp2_fake_security_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_fake_security_census_simple_request_test bins/$(CONFIG)/chttp2_fake_security_disappearing_server_test bins/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_fake_security_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_fake_security_invoke_large_request_test bins/$(CONFIG)/chttp2_fake_security_max_concurrent_streams_test bins/$(CONFIG)/chttp2_fake_security_no_op_test bins/$(CONFIG)/chttp2_fake_security_ping_pong_streaming_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_simple_delayed_request_test bins/$(CONFIG)/chttp2_fake_security_simple_request_test bins/$(CONFIG)/chttp2_fake_security_thread_stress_test bins/$(CONFIG)/chttp2_fake_security_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_fullstack_no_op_test bins/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_fullstack_simple_request_test bins/$(CONFIG)/chttp2_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_no_op_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_no_op_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_invoke_test bins/$(CONFIG)/chttp2_socket_pair_cancel_before_invoke_test bins/$(CONFIG)/chttp2_socket_pair_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_socket_pair_census_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_disappearing_server_test bins/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_socket_pair_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_socket_pair_invoke_large_request_test bins/$(CONFIG)/chttp2_socket_pair_max_concurrent_streams_test bins/$(CONFIG)/chttp2_socket_pair_no_op_test bins/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_test bins/$(CONFIG)/chttp2_socket_pair_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_thread_stress_test bins/$(CONFIG)/chttp2_socket_pair_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_census_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_disappearing_server_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_no_op_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_thread_stress_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test buildtests_cxx: privatelibs_cxx bins/$(CONFIG)/channel_arguments_test bins/$(CONFIG)/credentials_test bins/$(CONFIG)/end2end_test bins/$(CONFIG)/interop_client bins/$(CONFIG)/interop_server bins/$(CONFIG)/tips_client bins/$(CONFIG)/tips_publisher_test bins/$(CONFIG)/tips_subscriber_test bins/$(CONFIG)/qps_client bins/$(CONFIG)/qps_server bins/$(CONFIG)/status_test bins/$(CONFIG)/sync_client_async_server_test bins/$(CONFIG)/thread_pool_test @@ -630,6 +634,10 @@ test_c: buildtests_c $(Q) ./bins/$(CONFIG)/gpr_host_port_test || ( echo test gpr_host_port_test failed ; exit 1 ) $(E) "[RUN] Testing gpr_log_test" $(Q) ./bins/$(CONFIG)/gpr_log_test || ( echo test gpr_log_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_file_test" + $(Q) ./bins/$(CONFIG)/gpr_file_test || ( echo test gpr_file_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_env_test" + $(Q) ./bins/$(CONFIG)/gpr_env_test || ( echo test gpr_env_test failed ; exit 1 ) $(E) "[RUN] Testing gpr_slice_buffer_test" $(Q) ./bins/$(CONFIG)/gpr_slice_buffer_test || ( echo test gpr_slice_buffer_test failed ; exit 1 ) $(E) "[RUN] Testing gpr_slice_test" @@ -1230,6 +1238,12 @@ LIBGPR_SRC = \ src/core/support/cmdline.c \ src/core/support/cpu_linux.c \ src/core/support/cpu_posix.c \ + src/core/support/env_linux.c \ + src/core/support/env_posix.c \ + src/core/support/env_win32.c \ + src/core/support/file.c \ + src/core/support/file_posix.c \ + src/core/support/file_win32.c \ src/core/support/histogram.c \ src/core/support/host_port.c \ src/core/support/log.c \ @@ -1272,8 +1286,6 @@ PUBLIC_HEADERS_C += \ include/grpc/support/sync_win32.h \ include/grpc/support/thd.h \ include/grpc/support/time.h \ - include/grpc/support/time_posix.h \ - include/grpc/support/time_win32.h \ include/grpc/support/useful.h \ LIBGPR_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGPR_SRC)))) @@ -1317,6 +1329,12 @@ objs/$(CONFIG)/src/core/support/cancellable.o: objs/$(CONFIG)/src/core/support/cmdline.o: objs/$(CONFIG)/src/core/support/cpu_linux.o: objs/$(CONFIG)/src/core/support/cpu_posix.o: +objs/$(CONFIG)/src/core/support/env_linux.o: +objs/$(CONFIG)/src/core/support/env_posix.o: +objs/$(CONFIG)/src/core/support/env_win32.o: +objs/$(CONFIG)/src/core/support/file.o: +objs/$(CONFIG)/src/core/support/file_posix.o: +objs/$(CONFIG)/src/core/support/file_win32.o: objs/$(CONFIG)/src/core/support/histogram.o: objs/$(CONFIG)/src/core/support/host_port.o: objs/$(CONFIG)/src/core/support/log.o: @@ -1451,6 +1469,7 @@ LIBGRPC_SRC = \ src/core/statistics/hash_table.c \ src/core/statistics/window_stats.c \ src/core/surface/byte_buffer.c \ + src/core/surface/byte_buffer_queue.c \ src/core/surface/byte_buffer_reader.c \ src/core/surface/call.c \ src/core/surface/channel.c \ @@ -1577,6 +1596,7 @@ src/core/statistics/census_tracing.c: $(OPENSSL_DEP) src/core/statistics/hash_table.c: $(OPENSSL_DEP) src/core/statistics/window_stats.c: $(OPENSSL_DEP) src/core/surface/byte_buffer.c: $(OPENSSL_DEP) +src/core/surface/byte_buffer_queue.c: $(OPENSSL_DEP) src/core/surface/byte_buffer_reader.c: $(OPENSSL_DEP) src/core/surface/call.c: $(OPENSSL_DEP) src/core/surface/channel.c: $(OPENSSL_DEP) @@ -1725,6 +1745,7 @@ objs/$(CONFIG)/src/core/statistics/census_tracing.o: objs/$(CONFIG)/src/core/statistics/hash_table.o: objs/$(CONFIG)/src/core/statistics/window_stats.o: objs/$(CONFIG)/src/core/surface/byte_buffer.o: +objs/$(CONFIG)/src/core/surface/byte_buffer_queue.o: objs/$(CONFIG)/src/core/surface/byte_buffer_reader.o: objs/$(CONFIG)/src/core/surface/call.o: objs/$(CONFIG)/src/core/surface/channel.o: @@ -1892,6 +1913,7 @@ LIBGRPC_UNSECURE_SRC = \ src/core/statistics/hash_table.c \ src/core/statistics/window_stats.c \ src/core/surface/byte_buffer.c \ + src/core/surface/byte_buffer_queue.c \ src/core/surface/byte_buffer_reader.c \ src/core/surface/call.c \ src/core/surface/channel.c \ @@ -2023,6 +2045,7 @@ objs/$(CONFIG)/src/core/statistics/census_tracing.o: objs/$(CONFIG)/src/core/statistics/hash_table.o: objs/$(CONFIG)/src/core/statistics/window_stats.o: objs/$(CONFIG)/src/core/surface/byte_buffer.o: +objs/$(CONFIG)/src/core/surface/byte_buffer_queue.o: objs/$(CONFIG)/src/core/surface/byte_buffer_reader.o: objs/$(CONFIG)/src/core/surface/call.o: objs/$(CONFIG)/src/core/surface/channel.o: @@ -4316,6 +4339,68 @@ endif endif +GPR_FILE_TEST_SRC = \ + test/core/support/file_test.c \ + +GPR_FILE_TEST_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_FILE_TEST_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL with ALPN. + +bins/$(CONFIG)/gpr_file_test: openssl_dep_error + +else + +bins/$(CONFIG)/gpr_file_test: $(GPR_FILE_TEST_OBJS) libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_FILE_TEST_OBJS) libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o bins/$(CONFIG)/gpr_file_test + +endif + +objs/$(CONFIG)/test/core/support/file_test.o: libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a + +deps_gpr_file_test: $(GPR_FILE_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_FILE_TEST_OBJS:.o=.dep) +endif +endif + + +GPR_ENV_TEST_SRC = \ + test/core/support/env_test.c \ + +GPR_ENV_TEST_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_ENV_TEST_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL with ALPN. + +bins/$(CONFIG)/gpr_env_test: openssl_dep_error + +else + +bins/$(CONFIG)/gpr_env_test: $(GPR_ENV_TEST_OBJS) libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_ENV_TEST_OBJS) libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o bins/$(CONFIG)/gpr_env_test + +endif + +objs/$(CONFIG)/test/core/support/env_test.o: libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a + +deps_gpr_env_test: $(GPR_ENV_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_ENV_TEST_OBJS:.o=.dep) +endif +endif + + GPR_SLICE_BUFFER_TEST_SRC = \ test/core/support/slice_buffer_test.c \ diff --git a/build.json b/build.json index 6fa3495aa30..170b1480671 100644 --- a/build.json +++ b/build.json @@ -73,6 +73,7 @@ "src/core/statistics/census_tracing.h", "src/core/statistics/hash_table.h", "src/core/statistics/window_stats.h", + "src/core/surface/byte_buffer_queue.h", "src/core/surface/call.h", "src/core/surface/channel.h", "src/core/surface/client.h", @@ -159,6 +160,7 @@ "src/core/statistics/hash_table.c", "src/core/statistics/window_stats.c", "src/core/surface/byte_buffer.c", + "src/core/surface/byte_buffer_queue.c", "src/core/surface/byte_buffer_reader.c", "src/core/surface/call.c", "src/core/surface/channel.c", @@ -221,12 +223,12 @@ "include/grpc/support/sync_win32.h", "include/grpc/support/thd.h", "include/grpc/support/time.h", - "include/grpc/support/time_posix.h", - "include/grpc/support/time_win32.h", "include/grpc/support/useful.h" ], "headers": [ "src/core/support/cpu.h", + "src/core/support/env.h", + "src/core/support/file.h", "src/core/support/murmur_hash.h", "src/core/support/string.h", "src/core/support/thd_internal.h" @@ -237,6 +239,12 @@ "src/core/support/cmdline.c", "src/core/support/cpu_linux.c", "src/core/support/cpu_posix.c", + "src/core/support/env_linux.c", + "src/core/support/env_posix.c", + "src/core/support/env_win32.c", + "src/core/support/file.c", + "src/core/support/file_posix.c", + "src/core/support/file_win32.c", "src/core/support/histogram.c", "src/core/support/host_port.c", "src/core/support/log.c", @@ -923,6 +931,30 @@ "gpr" ] }, + { + "name": "gpr_file_test", + "build": "test", + "language": "c", + "src": [ + "test/core/support/file_test.c" + ], + "deps": [ + "gpr_test_util", + "gpr" + ] + }, + { + "name": "gpr_env_test", + "build": "test", + "language": "c", + "src": [ + "test/core/support/env_test.c" + ], + "deps": [ + "gpr_test_util", + "gpr" + ] + }, { "name": "gpr_slice_buffer_test", "build": "test", diff --git a/include/grpc++/credentials.h b/include/grpc++/credentials.h index 987d890b4f8..52304d7f369 100644 --- a/include/grpc++/credentials.h +++ b/include/grpc++/credentials.h @@ -66,14 +66,13 @@ class Credentials final { // Options used to build SslCredentials // pem_roots_cert is the buffer containing the PEM encoding of the server root -// certificates. This parameter cannot be empty. +// certificates. If this parameter is empty, the default roots will be used. // pem_private_key is the buffer containing the PEM encoding of the client's // private key. This parameter can be empty if the client does not have a // private key. // pem_cert_chain is the buffer containing the PEM encoding of the client's // certificate chain. This parameter can be empty if the client does not have // a certificate chain. -// TODO(jboeuf) Change it to point to a file. struct SslCredentialsOptions { grpc::string pem_root_certs; grpc::string pem_private_key; diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h index a9fae0df909..c18e47e54b7 100644 --- a/include/grpc/grpc.h +++ b/include/grpc/grpc.h @@ -183,17 +183,16 @@ typedef struct grpc_metadata { } grpc_metadata; typedef enum grpc_completion_type { - GRPC_QUEUE_SHUTDOWN, /* Shutting down */ - GRPC_READ, /* A read has completed */ - GRPC_INVOKE_ACCEPTED, /* An invoke call has been accepted by flow - control */ - GRPC_WRITE_ACCEPTED, /* A write has been accepted by - flow control */ + GRPC_QUEUE_SHUTDOWN, /* Shutting down */ + GRPC_IOREQ, /* grpc_call_ioreq completion */ + GRPC_READ, /* A read has completed */ + GRPC_WRITE_ACCEPTED, /* A write has been accepted by + flow control */ GRPC_FINISH_ACCEPTED, /* writes_done or write_status has been accepted */ GRPC_CLIENT_METADATA_READ, /* The metadata array sent by server received at client */ - GRPC_FINISHED, /* An RPC has finished. The event contains status. - On the server this will be OK or Cancelled. */ + GRPC_FINISHED, /* An RPC has finished. The event contains status. + On the server this will be OK or Cancelled. */ GRPC_SERVER_RPC_NEW, /* A new RPC has arrived at the server */ GRPC_SERVER_SHUTDOWN, /* The server has finished shutting down */ GRPC_COMPLETION_DO_NOT_USE /* must be last, forces users to include @@ -213,6 +212,7 @@ typedef struct grpc_event { grpc_op_error write_accepted; grpc_op_error finish_accepted; grpc_op_error invoke_accepted; + grpc_op_error ioreq; struct { size_t count; grpc_metadata *elements; @@ -233,6 +233,57 @@ typedef struct grpc_event { } data; } grpc_event; +typedef struct { + size_t count; + size_t capacity; + grpc_metadata *metadata; +} grpc_metadata_array; + +typedef struct { + const char *method; + const char *host; + gpr_timespec deadline; +} grpc_call_details; + +typedef enum { + GRPC_OP_SEND_INITIAL_METADATA = 0, + GRPC_OP_SEND_MESSAGE, + GRPC_OP_SEND_CLOSE_FROM_CLIENT, + GRPC_OP_SEND_STATUS_FROM_SERVER, + GRPC_OP_RECV_INITIAL_METADATA, + GRPC_OP_RECV_MESSAGES, + GRPC_OP_RECV_STATUS_ON_CLIENT, + GRPC_OP_RECV_CLOSE_ON_SERVER +} grpc_op_type; + +typedef struct grpc_op { + grpc_op_type op; + union { + struct { + size_t count; + const grpc_metadata *metadata; + } send_initial_metadata; + grpc_byte_buffer *send_message; + struct { + size_t trailing_metadata_count; + grpc_metadata *trailing_metadata; + grpc_status_code status; + const char *status_details; + } send_status_from_server; + grpc_metadata_array *recv_initial_metadata; + grpc_byte_buffer **recv_message; + struct { + grpc_metadata_array *trailing_metadata; + grpc_status_code *status; + char **status_details; + size_t *status_details_capacity; + } recv_status_on_client; + struct { + int *cancelled; + } recv_close_on_server; + } data; +} grpc_op; + /* Initialize the grpc library */ void grpc_init(void); @@ -279,6 +330,9 @@ grpc_call *grpc_channel_create_call_old(grpc_channel *channel, const char *method, const char *host, gpr_timespec deadline); +grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, + size_t nops, void *tag); + /* Create a client channel */ grpc_channel *grpc_channel_create(const char *target, const grpc_channel_args *args); diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h index 0732a8f83a5..731959069fc 100644 --- a/include/grpc/grpc_security.h +++ b/include/grpc/grpc_security.h @@ -54,6 +54,12 @@ void grpc_credentials_release(grpc_credentials *creds); /* Creates default credentials. */ grpc_credentials *grpc_default_credentials_create(void); +/* Environment variable that points to the default SSL roots file. This file + must be a PEM encoded file with all the roots such as the one that can be + downloaded from https://pki.google.com/roots.pem. */ +#define GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR \ + "GRPC_DEFAULT_SSL_ROOTS_FILE_PATH" + /* Object that holds a private key / certificate chain pair in PEM format. */ typedef struct { /* private_key is the NULL-terminated string containing the PEM encoding of diff --git a/include/grpc/support/port_platform.h b/include/grpc/support/port_platform.h index 2bf53483157..e99099c651c 100644 --- a/include/grpc/support/port_platform.h +++ b/include/grpc/support/port_platform.h @@ -61,6 +61,8 @@ #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 @@ -74,6 +76,8 @@ #define GPR_LINUX_EVENTFD 1 #define GPR_POSIX_SOCKET 1 #define GPR_POSIX_SOCKETADDR 1 +#define GPR_LINUX_ENV 1 +#define GPR_POSIX_FILE 1 #define GPR_POSIX_STRING 1 #define GPR_POSIX_SYNC 1 #define GPR_POSIX_TIME 1 @@ -93,6 +97,8 @@ #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 diff --git a/src/core/channel/channel_stack.c b/src/core/channel/channel_stack.c index e28bbd798d7..d9e722c4f1e 100644 --- a/src/core/channel/channel_stack.c +++ b/src/core/channel/channel_stack.c @@ -210,6 +210,7 @@ void grpc_call_element_recv_metadata(grpc_call_element *cur_elem, metadata_op.dir = GRPC_CALL_UP; metadata_op.done_cb = do_nothing; metadata_op.user_data = NULL; + metadata_op.flags = 0; metadata_op.data.metadata = mdelem; grpc_call_next_op(cur_elem, &metadata_op); } @@ -221,6 +222,7 @@ void grpc_call_element_send_metadata(grpc_call_element *cur_elem, metadata_op.dir = GRPC_CALL_DOWN; metadata_op.done_cb = do_nothing; metadata_op.user_data = NULL; + metadata_op.flags = 0; metadata_op.data.metadata = mdelem; grpc_call_next_op(cur_elem, &metadata_op); } @@ -231,14 +233,16 @@ void grpc_call_element_send_cancel(grpc_call_element *cur_elem) { cancel_op.dir = GRPC_CALL_DOWN; cancel_op.done_cb = do_nothing; cancel_op.user_data = NULL; + cancel_op.flags = 0; grpc_call_next_op(cur_elem, &cancel_op); } void grpc_call_element_send_finish(grpc_call_element *cur_elem) { - grpc_call_op cancel_op; - cancel_op.type = GRPC_SEND_FINISH; - cancel_op.dir = GRPC_CALL_DOWN; - cancel_op.done_cb = do_nothing; - cancel_op.user_data = NULL; - grpc_call_next_op(cur_elem, &cancel_op); + grpc_call_op finish_op; + finish_op.type = GRPC_SEND_FINISH; + finish_op.dir = GRPC_CALL_DOWN; + finish_op.done_cb = do_nothing; + finish_op.user_data = NULL; + finish_op.flags = 0; + grpc_call_next_op(cur_elem, &finish_op); } diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c index bcb024f2ac5..507b91b8a63 100644 --- a/src/core/channel/client_channel.c +++ b/src/core/channel/client_channel.c @@ -298,6 +298,7 @@ static void channel_op(grpc_channel_element *elem, grpc_channel_element *from_elem, grpc_channel_op *op) { channel_data *chand = elem->channel_data; grpc_child_channel *child_channel; + grpc_channel_op rop; GPR_ASSERT(elem->filter == &grpc_client_channel_filter); switch (op->type) { @@ -323,6 +324,10 @@ static void channel_op(grpc_channel_element *elem, if (child_channel) { grpc_child_channel_destroy(child_channel, 1); } + /* fake a transport closed to satisfy the refcounting in client */ + rop.type = GRPC_TRANSPORT_CLOSED; + rop.dir = GRPC_CALL_UP; + grpc_channel_next_op(elem, &rop); break; case GRPC_TRANSPORT_GOAWAY: /* receiving goaway: if it's from our active child, drop the active child; diff --git a/src/core/channel/connected_channel.c b/src/core/channel/connected_channel.c index d35cede97b5..61a6caf0328 100644 --- a/src/core/channel/connected_channel.c +++ b/src/core/channel/connected_channel.c @@ -298,10 +298,6 @@ static void recv_error(channel_data *chand, call_data *calld, int line, static void do_nothing(void *calldata, grpc_op_error error) {} -static void done_message(void *user_data, grpc_op_error error) { - grpc_byte_buffer_destroy(user_data); -} - static void finish_message(channel_data *chand, call_data *calld) { grpc_call_element *elem = calld->elem; grpc_call_op call_op; @@ -309,9 +305,9 @@ static void finish_message(channel_data *chand, call_data *calld) { call_op.flags = 0; /* if we got all the bytes for this message, call up the stack */ call_op.type = GRPC_RECV_MESSAGE; - call_op.done_cb = done_message; + call_op.done_cb = do_nothing; /* TODO(ctiller): this could be a lot faster if coded directly */ - call_op.user_data = call_op.data.message = grpc_byte_buffer_create( + call_op.data.message = grpc_byte_buffer_create( calld->incoming_message.slices, calld->incoming_message.count); gpr_slice_buffer_reset_and_unref(&calld->incoming_message); diff --git a/src/core/iomgr/alarm.c b/src/core/iomgr/alarm.c index 5b80368e3a2..7884b21a1e2 100644 --- a/src/core/iomgr/alarm.c +++ b/src/core/iomgr/alarm.c @@ -335,10 +335,6 @@ static int run_some_expired_alarms(gpr_mu *drop_mu, gpr_timespec now, gpr_mu_unlock(&g_mu); gpr_mu_unlock(&g_checker_mu); - } else if (next) { - gpr_mu_lock(&g_mu); - *next = gpr_time_min(*next, g_shard_queue[0]->min_deadline); - gpr_mu_unlock(&g_mu); } if (n && drop_mu) { diff --git a/src/core/iomgr/alarm_internal.h b/src/core/iomgr/alarm_internal.h index 5c6b869302e..8503292fd1a 100644 --- a/src/core/iomgr/alarm_internal.h +++ b/src/core/iomgr/alarm_internal.h @@ -39,6 +39,15 @@ /* iomgr internal api for dealing with alarms */ +/* Check for alarms to be run, and run them. + Return non zero if alarm callbacks were executed. + Drops drop_mu if it is non-null before executing callbacks. + If next is non-null, TRY to update *next with the next running alarm + IF that alarm occurs before *next current value. + *next is never guaranteed to be updated on any given execution; however, + with high probability at least one thread in the system will see an update + at any time slice. */ + int grpc_alarm_check(gpr_mu *drop_mu, gpr_timespec now, gpr_timespec *next); void grpc_alarm_list_init(gpr_timespec now); diff --git a/src/core/iomgr/pollset_kick.c b/src/core/iomgr/pollset_kick.c index 238ec75c61c..f0211b8274d 100644 --- a/src/core/iomgr/pollset_kick.c +++ b/src/core/iomgr/pollset_kick.c @@ -48,49 +48,49 @@ /* This implementation is based on a freelist of wakeup fds, with extra logic to * handle kicks while there is no attached fd. */ +/* TODO(klempner): Autosize this, and consider providing a way to disable the + * cap entirely on systems with large fd limits */ #define GRPC_MAX_CACHED_WFDS 50 -#define GRPC_WFD_LOW_WATERMARK 25 static grpc_kick_fd_info *fd_freelist = NULL; static int fd_freelist_count = 0; static gpr_mu fd_freelist_mu; static grpc_kick_fd_info *allocate_wfd(void) { - grpc_kick_fd_info *info; + grpc_kick_fd_info *info = NULL; gpr_mu_lock(&fd_freelist_mu); if (fd_freelist != NULL) { info = fd_freelist; fd_freelist = fd_freelist->next; --fd_freelist_count; - } else { + } + gpr_mu_unlock(&fd_freelist_mu); + if (info == NULL) { info = gpr_malloc(sizeof(*info)); grpc_wakeup_fd_create(&info->wakeup_fd); info->next = NULL; } - gpr_mu_unlock(&fd_freelist_mu); return info; } -static void destroy_wfd(void) { - /* assumes fd_freelist_mu is held */ - grpc_kick_fd_info *current = fd_freelist; - fd_freelist = fd_freelist->next; - fd_freelist_count--; - grpc_wakeup_fd_destroy(¤t->wakeup_fd); - gpr_free(current); +static void destroy_wfd(grpc_kick_fd_info* wfd) { + grpc_wakeup_fd_destroy(&wfd->wakeup_fd); + gpr_free(wfd); } static void free_wfd(grpc_kick_fd_info *fd_info) { gpr_mu_lock(&fd_freelist_mu); - fd_info->next = fd_freelist; - fd_freelist = fd_info; - fd_freelist_count++; - if (fd_freelist_count > GRPC_MAX_CACHED_WFDS) { - while (fd_freelist_count > GRPC_WFD_LOW_WATERMARK) { - destroy_wfd(); - } + if (fd_freelist_count < GRPC_MAX_CACHED_WFDS) { + fd_info->next = fd_freelist; + fd_freelist = fd_info; + fd_freelist_count++; + fd_info = NULL; } gpr_mu_unlock(&fd_freelist_mu); + + if (fd_info) { + destroy_wfd(fd_info); + } } void grpc_pollset_kick_init(grpc_pollset_kick_state *kick_state) { @@ -148,6 +148,11 @@ void grpc_pollset_kick_global_init(void) { } void grpc_pollset_kick_global_destroy(void) { + while (fd_freelist != NULL) { + grpc_kick_fd_info *current = fd_freelist; + fd_freelist = fd_freelist->next; + destroy_wfd(current); + } grpc_wakeup_fd_global_destroy(); gpr_mu_destroy(&fd_freelist_mu); } diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c index 994dbe495de..b1c2c64a18c 100644 --- a/src/core/iomgr/pollset_posix.c +++ b/src/core/iomgr/pollset_posix.c @@ -80,9 +80,7 @@ void grpc_pollset_kick(grpc_pollset *p) { } } -void grpc_pollset_force_kick(grpc_pollset *p) { - grpc_pollset_kick_kick(&p->kick_state); -} +void grpc_pollset_force_kick(grpc_pollset *p) { grpc_pollset_kick_kick(&p->kick_state); } /* global state management */ diff --git a/src/core/iomgr/resolve_address.c b/src/core/iomgr/resolve_address.c index 01681168ce4..575f884d91a 100644 --- a/src/core/iomgr/resolve_address.c +++ b/src/core/iomgr/resolve_address.c @@ -31,7 +31,9 @@ * */ +#ifndef _POSIX_SOURCE #define _POSIX_SOURCE +#endif #include "src/core/iomgr/sockaddr.h" #include "src/core/iomgr/resolve_address.h" diff --git a/src/core/iomgr/socket_utils_linux.c b/src/core/iomgr/socket_utils_linux.c index f971cb33bcc..7ef58940c24 100644 --- a/src/core/iomgr/socket_utils_linux.c +++ b/src/core/iomgr/socket_utils_linux.c @@ -31,7 +31,9 @@ * */ +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include #ifdef GPR_LINUX diff --git a/src/core/iomgr/socket_utils_posix.c b/src/core/iomgr/socket_utils_posix.c index 06c5033d457..9184b2a47cf 100644 --- a/src/core/iomgr/socket_utils_posix.c +++ b/src/core/iomgr/socket_utils_posix.c @@ -35,7 +35,6 @@ #ifdef GPR_POSIX_SOCKETUTILS -#define _BSD_SOURCE #include "src/core/iomgr/socket_utils_posix.h" #include diff --git a/src/core/iomgr/tcp_server_posix.c b/src/core/iomgr/tcp_server_posix.c index d169d232718..091f0aab1a6 100644 --- a/src/core/iomgr/tcp_server_posix.c +++ b/src/core/iomgr/tcp_server_posix.c @@ -31,11 +31,15 @@ * */ +/* FIXME: "posix" files shouldn't be depending on _GNU_SOURCE */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #ifdef GPR_POSIX_SOCKET -#define _GNU_SOURCE #include "src/core/iomgr/tcp_server.h" #include diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c index 7b7d8f32116..6f0d72c0c3d 100644 --- a/src/core/security/credentials.c +++ b/src/core/security/credentials.c @@ -216,14 +216,10 @@ static void ssl_copy_key_material(const char *input, unsigned char **output, static void ssl_build_config(const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair, grpc_ssl_config *config) { - if (pem_root_certs == NULL) { - /* TODO(jboeuf): Get them from the environment. */ - gpr_log(GPR_ERROR, "Default SSL roots not yet implemented."); - } else { + if (pem_root_certs != NULL) { ssl_copy_key_material(pem_root_certs, &config->pem_root_certs, &config->pem_root_certs_size); } - if (pem_key_cert_pair != NULL) { GPR_ASSERT(pem_key_cert_pair->private_key != NULL); GPR_ASSERT(pem_key_cert_pair->cert_chain != NULL); diff --git a/src/core/security/security_context.c b/src/core/security/security_context.c index 58cd458415d..1edec297759 100644 --- a/src/core/security/security_context.c +++ b/src/core/security/security_context.c @@ -39,6 +39,8 @@ #include "src/core/channel/http_client_filter.h" #include "src/core/security/credentials.h" #include "src/core/security/secure_endpoint.h" +#include "src/core/support/env.h" +#include "src/core/support/file.h" #include "src/core/support/string.h" #include "src/core/surface/lame_client.h" #include "src/core/transport/chttp2/alpn.h" @@ -319,6 +321,28 @@ static grpc_security_context_vtable ssl_channel_vtable = { static grpc_security_context_vtable ssl_server_vtable = { ssl_server_destroy, ssl_server_create_handshaker, ssl_server_check_peer}; +static gpr_slice default_pem_root_certs; + +static void init_default_pem_root_certs(void) { + char *default_root_certs_path = + gpr_getenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR); + if (default_root_certs_path == NULL) { + default_pem_root_certs = gpr_empty_slice(); + } else { + default_pem_root_certs = gpr_load_file(default_root_certs_path, NULL); + gpr_free(default_root_certs_path); + } +} + +static size_t get_default_pem_roots(const unsigned char **pem_root_certs) { + /* TODO(jboeuf@google.com): Maybe revisit the approach which consists in + loading all the roots once for the lifetime of the process. */ + static gpr_once once = GPR_ONCE_INIT; + gpr_once_init(&once, init_default_pem_root_certs); + *pem_root_certs = GPR_SLICE_START_PTR(default_pem_root_certs); + return GPR_SLICE_LENGTH(default_pem_root_certs); +} + grpc_security_status grpc_ssl_channel_security_context_create( grpc_credentials *request_metadata_creds, const grpc_ssl_config *config, const char *secure_peer_name, grpc_channel_security_context **ctx) { @@ -330,6 +354,8 @@ grpc_security_status grpc_ssl_channel_security_context_create( tsi_result result = TSI_OK; grpc_ssl_channel_security_context *c; size_t i; + const unsigned char *pem_root_certs; + size_t pem_root_certs_size; for (i = 0; i < num_alpn_protocols; i++) { alpn_protocol_strings[i] = @@ -338,9 +364,8 @@ grpc_security_status grpc_ssl_channel_security_context_create( strlen(grpc_chttp2_get_alpn_version_index(i)); } - if (config == NULL || secure_peer_name == NULL || - config->pem_root_certs == NULL) { - gpr_log(GPR_ERROR, "An ssl channel needs a secure name and root certs."); + if (config == NULL || secure_peer_name == NULL) { + gpr_log(GPR_ERROR, "An ssl channel needs a config and a secure name."); goto error; } if (!check_request_metadata_creds(request_metadata_creds)) { @@ -357,11 +382,20 @@ grpc_security_status grpc_ssl_channel_security_context_create( if (secure_peer_name != NULL) { c->secure_peer_name = gpr_strdup(secure_peer_name); } + if (config->pem_root_certs == NULL) { + pem_root_certs_size = get_default_pem_roots(&pem_root_certs); + if (pem_root_certs == NULL || pem_root_certs_size == 0) { + gpr_log(GPR_ERROR, "Could not get default pem root certs."); + goto error; + } + } else { + pem_root_certs = config->pem_root_certs; + pem_root_certs_size = config->pem_root_certs_size; + } result = tsi_create_ssl_client_handshaker_factory( config->pem_private_key, config->pem_private_key_size, - config->pem_cert_chain, config->pem_cert_chain_size, - config->pem_root_certs, config->pem_root_certs_size, - GRPC_SSL_CIPHER_SUITES, alpn_protocol_strings, + config->pem_cert_chain, config->pem_cert_chain_size, pem_root_certs, + pem_root_certs_size, GRPC_SSL_CIPHER_SUITES, alpn_protocol_strings, alpn_protocol_string_lengths, num_alpn_protocols, &c->handshaker_factory); if (result != TSI_OK) { gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", diff --git a/src/core/support/env.h b/src/core/support/env.h new file mode 100644 index 00000000000..81dda7d838a --- /dev/null +++ b/src/core/support/env.h @@ -0,0 +1,60 @@ +/* + * + * Copyright 2014, 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_ENV_H__ +#define __GRPC_SUPPORT_ENV_H__ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Env utility functions */ + +/* Gets the environment variable value with the specified name. + Returns a newly allocated string. It is the responsability of the caller to + gpr_free the return value if not NULL (which means that the environment + variable exists). */ +char *gpr_getenv(const char *name); + +/* Sets the the environment with the specified name to the specified value. */ +void gpr_setenv(const char *name, const char *value); + +#ifdef __cplusplus +} +#endif + +#endif /* __GRPC_SUPPORT_ENV_H__ */ diff --git a/src/core/support/env_linux.c b/src/core/support/env_linux.c new file mode 100644 index 00000000000..28e3d1450f9 --- /dev/null +++ b/src/core/support/env_linux.c @@ -0,0 +1,61 @@ +/* + * + * Copyright 2014, 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. + * + */ + +/* for secure_getenv. */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include + +#ifdef GPR_LINUX_ENV + +#include "src/core/support/env.h" + +#include + +#include + +#include "src/core/support/string.h" + +char *gpr_getenv(const char *name) { + char *result = secure_getenv(name); + return result == NULL ? result : gpr_strdup(result); +} + +void gpr_setenv(const char *name, const char *value) { + int res = setenv(name, value, 1); + GPR_ASSERT(res == 0); +} + +#endif /* GPR_LINUX_ENV */ diff --git a/src/core/support/env_posix.c b/src/core/support/env_posix.c new file mode 100644 index 00000000000..bcbff9a1770 --- /dev/null +++ b/src/core/support/env_posix.c @@ -0,0 +1,56 @@ +/* + * + * Copyright 2014, 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 + +#ifdef GPR_POSIX_ENV + +#include "src/core/support/env.h" + +#include + +#include + +#include "src/core/support/string.h" + +char *gpr_getenv(const char *name) { + char *result = getenv(name); + return result == NULL ? result : gpr_strdup(result); +} + +void gpr_setenv(const char *name, const char *value) { + int res = setenv(name, value, 1); + GPR_ASSERT(res == 0); +} + +#endif /* GPR_POSIX_ENV */ diff --git a/src/core/support/env_win32.c b/src/core/support/env_win32.c new file mode 100644 index 00000000000..a31fa79d68e --- /dev/null +++ b/src/core/support/env_win32.c @@ -0,0 +1,60 @@ +/* + * + * Copyright 2014, 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 + +#ifdef GPR_WIN32 + +#include "src/core/support/env.h" + +#include + +#include + +char *gpr_getenv(const char *name) { + size_t required_size; + char *result = NULL; + + getenv_s(&required_size, NULL, 0, name); + if (required_size == 0) return NULL; + result = gpr_malloc(required_size); + getenv_s(&required_size, result, required_size, name); + return result; +} + +void gpr_setenv(const char *name, const char *value) { + errno_t res = _putenv_s(name, value); + GPR_ASSERT(res == 0); +} + +#endif /* GPR_WIN32 */ diff --git a/src/core/support/file.c b/src/core/support/file.c new file mode 100644 index 00000000000..c0bb1b66a06 --- /dev/null +++ b/src/core/support/file.c @@ -0,0 +1,89 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/support/file.h" + +#include +#include + +#include +#include + +#include "src/core/support/string.h" + +gpr_slice gpr_load_file(const char *filename, int *success) { + unsigned char *contents = NULL; + size_t contents_size = 0; + unsigned char buf[4096]; + char *error_msg = NULL; + gpr_slice result = gpr_empty_slice(); + FILE *file = fopen(filename, "rb"); + + if (file == NULL) { + gpr_asprintf(&error_msg, "Could not open file %s (error = %s).", filename, + strerror(errno)); + GPR_ASSERT(error_msg != NULL); + goto end; + } + + while (1) { + size_t bytes_read = fread(buf, 1, sizeof(buf), file); + if (bytes_read > 0) { + contents = gpr_realloc(contents, contents_size + bytes_read); + memcpy(contents + contents_size, buf, bytes_read); + contents_size += bytes_read; + } + if (bytes_read < sizeof(buf)) { + if (ferror(file)) { + gpr_asprintf(&error_msg, "Error %s occured while reading file %s.", + strerror(errno), filename); + GPR_ASSERT(error_msg != NULL); + goto end; + } else { + GPR_ASSERT(feof(file)); + break; + } + } + } + if (success != NULL) *success = 1; + result = gpr_slice_new(contents, contents_size, gpr_free); + +end: + if (error_msg != NULL) { + gpr_log(GPR_ERROR, "%s", error_msg); + gpr_free(error_msg); + if (success != NULL) *success = 0; + } + if (file != NULL) fclose(file); + return result; +} diff --git a/src/core/support/file.h b/src/core/support/file.h new file mode 100644 index 00000000000..92f420e7ceb --- /dev/null +++ b/src/core/support/file.h @@ -0,0 +1,61 @@ +/* + * + * Copyright 2014, 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_FILE_H__ +#define __GRPC_SUPPORT_FILE_H__ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* File utility functions */ + +/* Loads the content of a file into a slice. The success parameter, if not NULL, + will be set to 1 in case of success and 0 in case of failure. */ +gpr_slice gpr_load_file(const char *filename, int *success); + +/* Creates a temporary file from a prefix. + If tmp_filename is not NULL, *tmp_filename is assigned the name of the + created file and it is the responsibility of the caller to gpr_free it + unless an error occurs in which case it will be set to NULL. */ +FILE *gpr_tmpfile(const char *prefix, char **tmp_filename); + +#ifdef __cplusplus +} +#endif + +#endif /* __GRPC_SUPPORT_FILE_H__ */ diff --git a/src/core/support/file_posix.c b/src/core/support/file_posix.c new file mode 100644 index 00000000000..21a985012eb --- /dev/null +++ b/src/core/support/file_posix.c @@ -0,0 +1,97 @@ +/* + * + * Copyright 2014, 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. + * + */ + +/* Posix code for gpr fdopen and mkstemp support. */ + +#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 200112L +#undef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200112L +#endif + +/* Don't know why I have to do this for mkstemp, looks like _POSIX_C_SOURCE + should be enough... */ +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif + +#include + +#ifdef GPR_POSIX_FILE + +#include "src/core/support/file.h" + +#include +#include +#include +#include + +#include +#include + +#include "src/core/support/string.h" + +FILE *gpr_tmpfile(const char *prefix, char **tmp_filename) { + FILE *result = NULL; + char *template; + int fd; + + if (tmp_filename != NULL) *tmp_filename = NULL; + + gpr_asprintf(&template, "%s_XXXXXX", prefix); + GPR_ASSERT(template != NULL); + + fd = mkstemp(template); + if (fd == -1) { + gpr_log(GPR_ERROR, "mkstemp failed for template %s with error %s.", + template, strerror(errno)); + goto end; + } + result = fdopen(fd, "w+"); + if (result == NULL) { + gpr_log(GPR_ERROR, "Could not open file %s from fd %d (error = %s).", + template, fd, strerror(errno)); + unlink(template); + close(fd); + goto end; + } + +end: + if (result != NULL && tmp_filename != NULL) { + *tmp_filename = template; + } else { + gpr_free(template); + } + return result; +} + +#endif /* GPR_POSIX_FILE */ diff --git a/src/core/support/file_win32.c b/src/core/support/file_win32.c new file mode 100644 index 00000000000..d415281e0d3 --- /dev/null +++ b/src/core/support/file_win32.c @@ -0,0 +1,78 @@ +/* + * + * Copyright 2014, 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 + +#ifdef GPR_WIN32 + +#include "src/core/support/file.h" + +#include +#include +#include + +#include +#include + +FILE *gpr_tmpfile(const char *prefix, char **tmp_filename) { + FILE *result = NULL; + char *template; + + if (tmp_filename != NULL) *tmp_filename = NULL; + + gpr_asprintf(&template, "%s_XXXXXX", prefix); + GPR_ASSERT(template != NULL); + + /* _mktemp_s can only create a maximum of 26 file names for any combination of + base and template values which is kind of sad... We may revisit this + function later to have something better... */ + if (_mktemp_s(template, strlen(template) + 1) != 0) { + gpr_log(LOG_ERROR, "Could not create tmp file."); + goto end; + } + if (fopen_s(&result, template, "wb+") != 0) { + gpr_log(GPR_ERROR, "Could not open file %s", template); + result = NULL; + goto end; + } + +end: + if (result != NULL && tmp_filename != NULL) { + *tmp_filename = template; + } else { + gpr_free(template); + } + return result; +} + +#endif /* GPR_WIN32 */ diff --git a/src/core/support/log_linux.c b/src/core/support/log_linux.c index a0307e1a9a4..a64faa98bd9 100644 --- a/src/core/support/log_linux.c +++ b/src/core/support/log_linux.c @@ -31,8 +31,14 @@ * */ +#ifndef _POSIX_SOURCE #define _POSIX_SOURCE +#endif + +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif + #include #ifdef GPR_LINUX diff --git a/src/core/support/log_posix.c b/src/core/support/log_posix.c index ab2d2e5a740..05f45de1308 100644 --- a/src/core/support/log_posix.c +++ b/src/core/support/log_posix.c @@ -31,11 +31,16 @@ * */ -#ifndef _POSIX_C_SOURCE +#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 200112L +#undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200112L #endif +/* FIXME: "posix" files probably shouldn't depend on _GNU_SOURCE */ +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif + #include #if defined(GPR_POSIX_LOG) diff --git a/src/core/support/string_posix.c b/src/core/support/string_posix.c index 57832810ad3..a6bb8058e6c 100644 --- a/src/core/support/string_posix.c +++ b/src/core/support/string_posix.c @@ -33,7 +33,8 @@ /* Posix code for gpr snprintf support. */ -#ifndef _POSIX_C_SOURCE +#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 200112L +#undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200112L #endif diff --git a/src/core/surface/byte_buffer_queue.c b/src/core/surface/byte_buffer_queue.c new file mode 100644 index 00000000000..9709a665bac --- /dev/null +++ b/src/core/surface/byte_buffer_queue.c @@ -0,0 +1,84 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/surface/byte_buffer_queue.h" +#include +#include + +static void bba_destroy(grpc_bbq_array *array, size_t start_pos) { + size_t i; + for (i = start_pos; i < array->count; i++) { + grpc_byte_buffer_destroy(array->data[i]); + } + gpr_free(array->data); +} + +/* Append an operation to an array, expanding as needed */ +static void bba_push(grpc_bbq_array *a, grpc_byte_buffer *buffer) { + if (a->count == a->capacity) { + a->capacity = GPR_MAX(a->capacity * 2, 8); + a->data = gpr_realloc(a->data, sizeof(grpc_byte_buffer *) * a->capacity); + } + a->data[a->count++] = buffer; +} + +void grpc_bbq_destroy(grpc_byte_buffer_queue *q) { + bba_destroy(&q->filling, 0); + bba_destroy(&q->draining, q->drain_pos); +} + +int grpc_bbq_empty(grpc_byte_buffer_queue *q) { + return (q->drain_pos == q->draining.count && q->filling.count == 0); +} + +void grpc_bbq_push(grpc_byte_buffer_queue *q, grpc_byte_buffer *buffer) { + bba_push(&q->filling, buffer); +} + +grpc_byte_buffer *grpc_bbq_pop(grpc_byte_buffer_queue *q) { + grpc_bbq_array temp_array; + + if (q->drain_pos == q->draining.count) { + if (q->filling.count == 0) { + return NULL; + } + q->draining.count = 0; + q->drain_pos = 0; + /* swap arrays */ + temp_array = q->filling; + q->filling = q->draining; + q->draining = temp_array; + } + + return q->draining.data[q->drain_pos++]; +} diff --git a/src/core/surface/byte_buffer_queue.h b/src/core/surface/byte_buffer_queue.h new file mode 100644 index 00000000000..358a42d5af0 --- /dev/null +++ b/src/core/surface/byte_buffer_queue.h @@ -0,0 +1,59 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __GRPC_INTERNAL_SURFACE_BYTE_BUFFER_QUEUE_H__ +#define __GRPC_INTERNAL_SURFACE_BYTE_BUFFER_QUEUE_H__ + +#include + +/* TODO(ctiller): inline an element or two into this struct to avoid per-call + allocations */ +typedef struct { + grpc_byte_buffer **data; + size_t count; + size_t capacity; +} grpc_bbq_array; + +/* should be initialized by zeroing memory */ +typedef struct { + size_t drain_pos; + grpc_bbq_array filling; + grpc_bbq_array draining; +} grpc_byte_buffer_queue; + +void grpc_bbq_destroy(grpc_byte_buffer_queue *q); +grpc_byte_buffer *grpc_bbq_pop(grpc_byte_buffer_queue *q); +int grpc_bbq_empty(grpc_byte_buffer_queue *q); +void grpc_bbq_push(grpc_byte_buffer_queue *q, grpc_byte_buffer *bb); + +#endif /* __GRPC_INTERNAL_SURFACE_BYTE_BUFFER_QUEUE_H__ */ diff --git a/src/core/surface/call.c b/src/core/surface/call.c index 5a24264ccec..c68ce5a6a81 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -36,6 +36,7 @@ #include "src/core/channel/metadata_buffer.h" #include "src/core/iomgr/alarm.h" #include "src/core/support/string.h" +#include "src/core/surface/byte_buffer_queue.h" #include "src/core/surface/channel.h" #include "src/core/surface/completion_queue.h" #include @@ -45,814 +46,804 @@ #include #include -#define INVALID_TAG ((void *)0xdeadbeef) +typedef struct legacy_state legacy_state; +static void destroy_legacy_state(legacy_state *ls); -/* Pending read queue +typedef enum { REQ_INITIAL = 0, REQ_READY, REQ_DONE } req_state; - This data structure tracks reads that need to be presented to the completion - queue but are waiting for the application to ask for them. */ - -#define INITIAL_PENDING_READ_COUNT 4 +typedef enum { + SEND_NOTHING, + SEND_INITIAL_METADATA, + SEND_MESSAGE, + SEND_TRAILING_METADATA_AND_FINISH, + SEND_FINISH +} send_action; typedef struct { - grpc_byte_buffer *byte_buffer; + grpc_ioreq_completion_func on_complete; void *user_data; - void (*on_finish)(void *user_data, grpc_op_error error); -} pending_read; + grpc_op_error status; +} completed_request; -/* TODO(ctiller): inline an element or two into this struct to avoid per-call - allocations */ -typedef struct { - pending_read *data; - size_t count; - size_t capacity; -} pending_read_array; +/* See request_set in grpc_call below for a description */ +#define REQSET_EMPTY 255 +#define REQSET_DONE 254 typedef struct { - size_t drain_pos; - pending_read_array filling; - pending_read_array draining; -} pending_read_queue; - -static void pra_init(pending_read_array *array) { - array->data = gpr_malloc(sizeof(pending_read) * INITIAL_PENDING_READ_COUNT); - array->count = 0; - array->capacity = INITIAL_PENDING_READ_COUNT; -} - -static void pra_destroy(pending_read_array *array, - size_t finish_starting_from) { - size_t i; - for (i = finish_starting_from; i < array->count; i++) { - array->data[i].on_finish(array->data[i].user_data, GRPC_OP_ERROR); - } - gpr_free(array->data); -} - -/* Append an operation to an array, expanding as needed */ -static void pra_push(pending_read_array *a, grpc_byte_buffer *buffer, - void (*on_finish)(void *user_data, grpc_op_error error), - void *user_data) { - if (a->count == a->capacity) { - a->capacity *= 2; - a->data = gpr_realloc(a->data, sizeof(pending_read) * a->capacity); - } - a->data[a->count].byte_buffer = buffer; - a->data[a->count].user_data = user_data; - a->data[a->count].on_finish = on_finish; - a->count++; -} - -static void prq_init(pending_read_queue *q) { - q->drain_pos = 0; - pra_init(&q->filling); - pra_init(&q->draining); -} - -static void prq_destroy(pending_read_queue *q) { - pra_destroy(&q->filling, 0); - pra_destroy(&q->draining, q->drain_pos); -} - -static int prq_is_empty(pending_read_queue *q) { - return (q->drain_pos == q->draining.count && q->filling.count == 0); -} - -static void prq_push(pending_read_queue *q, grpc_byte_buffer *buffer, - void (*on_finish)(void *user_data, grpc_op_error error), - void *user_data) { - pra_push(&q->filling, buffer, on_finish, user_data); -} - -/* Take the first queue element and move it to the completion queue. Do nothing - if q is empty */ -static int prq_pop_to_cq(pending_read_queue *q, void *tag, grpc_call *call, - grpc_completion_queue *cq) { - pending_read_array temp_array; - pending_read *pr; - - if (q->drain_pos == q->draining.count) { - if (q->filling.count == 0) { - return 0; - } - q->draining.count = 0; - q->drain_pos = 0; - /* swap arrays */ - temp_array = q->filling; - q->filling = q->draining; - q->draining = temp_array; - } + /* Overall status of the operation: starts OK, may degrade to + non-OK */ + grpc_op_error status; + /* Completion function to call at the end of the operation */ + grpc_ioreq_completion_func on_complete; + void *user_data; + /* a bit mask of which request ops are needed (1u << opid) */ + gpr_uint32 need_mask; + /* a bit mask of which request ops are now completed */ + gpr_uint32 complete_mask; +} reqinfo_master; + +/* Status data for a request can come from several sources; this + enumerates them all, and acts as a priority sorting for which + status to return to the application - earlier entries override + later ones */ +typedef enum { + /* Status came from the application layer overriding whatever + the wire says */ + STATUS_FROM_API_OVERRIDE = 0, + /* Status came from 'the wire' - or somewhere below the surface + layer */ + STATUS_FROM_WIRE, + STATUS_SOURCE_COUNT +} status_source; - pr = q->draining.data + q->drain_pos; - q->drain_pos++; - grpc_cq_end_read(cq, tag, call, pr->on_finish, pr->user_data, - pr->byte_buffer); - return 1; -} +typedef struct { + gpr_uint8 is_set; + grpc_status_code code; + grpc_mdstr *details; +} received_status; -/* grpc_call proper */ +/* How far through the GRPC stream have we read? */ +typedef enum { + /* We are still waiting for initial metadata to complete */ + READ_STATE_INITIAL = 0, + /* We have gotten initial metadata, and are reading either + messages or trailing metadata */ + READ_STATE_GOT_INITIAL_METADATA, + /* The stream is closed for reading */ + READ_STATE_READ_CLOSED, + /* The stream is closed for reading & writing */ + READ_STATE_STREAM_CLOSED +} read_state; -/* the state of a call, based upon which functions have been called against - said call */ typedef enum { - CALL_CREATED, - CALL_BOUNDCQ, - CALL_STARTED, - CALL_FINISHED -} call_state; + WRITE_STATE_INITIAL = 0, + WRITE_STATE_STARTED, + WRITE_STATE_WRITE_CLOSED +} write_state; struct grpc_call { grpc_completion_queue *cq; grpc_channel *channel; grpc_mdctx *metadata_context; - - call_state state; + /* TODO(ctiller): share with cq if possible? */ + gpr_mu mu; + + /* how far through the stream have we read? */ + read_state read_state; + /* how far through the stream have we written? */ + write_state write_state; + /* client or server call */ gpr_uint8 is_client; - gpr_uint8 have_write; - grpc_metadata_buffer incoming_metadata; - - /* protects variables in this section */ - gpr_mu read_mu; - gpr_uint8 received_start; - gpr_uint8 start_ok; - gpr_uint8 reads_done; - gpr_uint8 received_finish; - gpr_uint8 received_metadata; - gpr_uint8 have_read; + /* is the alarm set */ gpr_uint8 have_alarm; - gpr_uint8 pending_writes_done; - gpr_uint8 got_status_code; - /* The current outstanding read message tag (only valid if have_read == 1) */ - void *read_tag; - void *metadata_tag; - void *finished_tag; - pending_read_queue prq; - + /* are we currently performing a send operation */ + gpr_uint8 sending; + /* pairs with completed_requests */ + gpr_uint8 num_completed_requests; + /* flag that we need to request more data */ + gpr_uint8 need_more_data; + + /* Active ioreqs. + request_set and request_data contain one element per active ioreq + operation. + + request_set[op] is an integer specifying a set of operations to which + the request belongs: + - if it is < GRPC_IOREQ_OP_COUNT, then this operation is pending + completion, and the integer represents to which group of operations + the ioreq belongs. Each group is represented by one master, and the + integer in request_set is an index into masters to find the master + data. + - if it is REQSET_EMPTY, the ioreq op is inactive and available to be + started + - finally, if request_set[op] is REQSET_DONE, then the operation is + complete and unavailable to be started again + + request_data[op] is the request data as supplied by the initiator of + a request, and is valid iff request_set[op] <= GRPC_IOREQ_OP_COUNT. + The set fields are as per the request type specified by op. + + Finally, one element of masters is set per active _set_ of ioreq + operations. It describes work left outstanding, result status, and + what work to perform upon operation completion. As one ioreq of each + op type can be active at once, by convention we choose the first element + of the group to be the master -- ie the master of in-progress operation + op is masters[request_set[op]]. This allows constant time allocation + and a strong upper bound of a count of masters to be calculated. */ + gpr_uint8 request_set[GRPC_IOREQ_OP_COUNT]; + grpc_ioreq_data request_data[GRPC_IOREQ_OP_COUNT]; + reqinfo_master masters[GRPC_IOREQ_OP_COUNT]; + + /* Dynamic array of ioreq's that have completed: the count of + elements is queued in num_completed_requests. + This list is built up under lock(), and flushed entirely during + unlock(). + We know the upper bound of the number of elements as we can only + have one ioreq of each type active at once. */ + completed_request completed_requests[GRPC_IOREQ_OP_COUNT]; + /* Incoming buffer of messages */ + grpc_byte_buffer_queue incoming_queue; + /* Buffered read metadata waiting to be returned to the application. + Element 0 is initial metadata, element 1 is trailing metadata. */ + grpc_metadata_array buffered_metadata[2]; + /* All metadata received - unreffed at once at the end of the call */ + grpc_mdelem **owned_metadata; + size_t owned_metadata_count; + size_t owned_metadata_capacity; + + /* Received call statuses from various sources */ + received_status status[STATUS_SOURCE_COUNT]; + + /* Deadline alarm - if have_alarm is non-zero */ grpc_alarm alarm; - /* The current outstanding send message/context/invoke/end tag (only valid if - have_write == 1) */ - void *write_tag; - grpc_byte_buffer *pending_write; - gpr_uint32 pending_write_flags; - - /* The final status of the call */ - grpc_status_code status_code; - grpc_mdstr *status_details; - + /* Call refcount - to keep the call alive during asynchronous operations */ gpr_refcount internal_refcount; + + /* Data that the legacy api needs to track. To be deleted at some point + soon */ + legacy_state *legacy_state; }; -#define CALL_STACK_FROM_CALL(call) ((grpc_call_stack *)((call) + 1)) +#define CALL_STACK_FROM_CALL(call) ((grpc_call_stack *)((call)+1)) #define CALL_FROM_CALL_STACK(call_stack) (((grpc_call *)(call_stack)) - 1) #define CALL_ELEM_FROM_CALL(call, idx) \ grpc_call_stack_element(CALL_STACK_FROM_CALL(call), idx) #define CALL_FROM_TOP_ELEM(top_elem) \ CALL_FROM_CALL_STACK(grpc_call_stack_from_top_element(top_elem)) +#define SWAP(type, x, y) \ + do { \ + type temp = x; \ + x = y; \ + y = temp; \ + } while (0) + static void do_nothing(void *ignored, grpc_op_error also_ignored) {} +static send_action choose_send_action(grpc_call *call); +static void enact_send_action(grpc_call *call, send_action sa); grpc_call *grpc_call_create(grpc_channel *channel, const void *server_transport_data) { + size_t i; grpc_channel_stack *channel_stack = grpc_channel_get_channel_stack(channel); grpc_call *call = gpr_malloc(sizeof(grpc_call) + channel_stack->call_stack_size); - call->cq = NULL; + memset(call, 0, sizeof(grpc_call)); + gpr_mu_init(&call->mu); call->channel = channel; + call->is_client = server_transport_data == NULL; + for (i = 0; i < GRPC_IOREQ_OP_COUNT; i++) { + call->request_set[i] = REQSET_EMPTY; + } + if (call->is_client) { + call->request_set[GRPC_IOREQ_SEND_TRAILING_METADATA] = REQSET_DONE; + call->request_set[GRPC_IOREQ_SEND_STATUS] = REQSET_DONE; + } grpc_channel_internal_ref(channel); call->metadata_context = grpc_channel_get_metadata_context(channel); - call->state = CALL_CREATED; - call->is_client = (server_transport_data == NULL); - call->write_tag = INVALID_TAG; - call->read_tag = INVALID_TAG; - call->metadata_tag = INVALID_TAG; - call->finished_tag = INVALID_TAG; - call->have_read = 0; - call->have_write = 0; - call->have_alarm = 0; - call->received_metadata = 0; - call->got_status_code = 0; - call->start_ok = 0; - call->status_code = - server_transport_data != NULL ? GRPC_STATUS_OK : GRPC_STATUS_UNKNOWN; - call->status_details = NULL; - call->received_finish = 0; - call->reads_done = 0; - call->received_start = 0; - call->pending_write = NULL; - call->pending_writes_done = 0; - grpc_metadata_buffer_init(&call->incoming_metadata); - gpr_ref_init(&call->internal_refcount, 1); + /* one ref is dropped in response to destroy, the other in + stream_closed */ + gpr_ref_init(&call->internal_refcount, 2); grpc_call_stack_init(channel_stack, server_transport_data, CALL_STACK_FROM_CALL(call)); - prq_init(&call->prq); - gpr_mu_init(&call->read_mu); return call; } void grpc_call_internal_ref(grpc_call *c) { gpr_ref(&c->internal_refcount); } -void grpc_call_internal_unref(grpc_call *c) { - if (gpr_unref(&c->internal_refcount)) { - grpc_call_stack_destroy(CALL_STACK_FROM_CALL(c)); - grpc_metadata_buffer_destroy(&c->incoming_metadata, GRPC_OP_OK); - if (c->status_details) { - grpc_mdstr_unref(c->status_details); +static void destroy_call(void *call, int ignored_success) { + size_t i; + grpc_call *c = call; + grpc_call_stack_destroy(CALL_STACK_FROM_CALL(c)); + grpc_channel_internal_unref(c->channel); + gpr_mu_destroy(&c->mu); + for (i = 0; i < STATUS_SOURCE_COUNT; i++) { + if (c->status[i].details) { + grpc_mdstr_unref(c->status[i].details); } - prq_destroy(&c->prq); - gpr_mu_destroy(&c->read_mu); - grpc_channel_internal_unref(c->channel); - gpr_free(c); } -} - -void grpc_call_destroy(grpc_call *c) { - int cancel; - gpr_mu_lock(&c->read_mu); - if (c->have_alarm) { - grpc_alarm_cancel(&c->alarm); - c->have_alarm = 0; + for (i = 0; i < c->owned_metadata_count; i++) { + grpc_mdelem_unref(c->owned_metadata[i]); } - cancel = !c->received_finish; - gpr_mu_unlock(&c->read_mu); - if (cancel) grpc_call_cancel(c); - grpc_call_internal_unref(c); -} - -static void maybe_set_status_code(grpc_call *call, gpr_uint32 status) { - if (!call->got_status_code) { - call->status_code = status; - call->got_status_code = 1; + gpr_free(c->owned_metadata); + for (i = 0; i < GPR_ARRAY_SIZE(c->buffered_metadata); i++) { + gpr_free(c->buffered_metadata[i].metadata); } + if (c->legacy_state) { + destroy_legacy_state(c->legacy_state); + } + grpc_bbq_destroy(&c->incoming_queue); + gpr_free(c); } -static void maybe_set_status_details(grpc_call *call, grpc_mdstr *status) { - if (!call->status_details) { - call->status_details = grpc_mdstr_ref(status); +void grpc_call_internal_unref(grpc_call *c, int allow_immediate_deletion) { + if (gpr_unref(&c->internal_refcount)) { + if (allow_immediate_deletion) { + destroy_call(c, 1); + } else { + grpc_iomgr_add_callback(destroy_call, c); + } } } -grpc_call_error grpc_call_cancel(grpc_call *c) { - grpc_call_element *elem; - grpc_call_op op; - - op.type = GRPC_CANCEL_OP; - op.dir = GRPC_CALL_DOWN; - op.flags = 0; - op.done_cb = do_nothing; - op.user_data = NULL; - - elem = CALL_ELEM_FROM_CALL(c, 0); - elem->filter->call_op(elem, NULL, &op); - - return GRPC_CALL_OK; +static void set_status_code(grpc_call *call, status_source source, + gpr_uint32 status) { + call->status[source].is_set = 1; + call->status[source].code = status; } -grpc_call_error grpc_call_cancel_with_status(grpc_call *c, - grpc_status_code status, - const char *description) { - grpc_mdstr *details = - description ? grpc_mdstr_from_string(c->metadata_context, description) - : NULL; - gpr_mu_lock(&c->read_mu); - maybe_set_status_code(c, status); - if (details) { - maybe_set_status_details(c, details); +static void set_status_details(grpc_call *call, status_source source, + grpc_mdstr *status) { + if (call->status[source].details != NULL) { + grpc_mdstr_unref(call->status[source].details); } - gpr_mu_unlock(&c->read_mu); - return grpc_call_cancel(c); + call->status[source].details = status; } -void grpc_call_execute_op(grpc_call *call, grpc_call_op *op) { - grpc_call_element *elem; - GPR_ASSERT(op->dir == GRPC_CALL_DOWN); - elem = CALL_ELEM_FROM_CALL(call, 0); - elem->filter->call_op(elem, NULL, op); +static grpc_call_error bind_cq(grpc_call *call, grpc_completion_queue *cq) { + if (call->cq) return GRPC_CALL_ERROR_ALREADY_INVOKED; + call->cq = cq; + return GRPC_CALL_OK; } -void grpc_call_add_mdelem(grpc_call *call, grpc_mdelem *mdelem, - gpr_uint32 flags) { - grpc_call_element *elem; +static void request_more_data(grpc_call *call) { grpc_call_op op; - GPR_ASSERT(call->state < CALL_FINISHED); - - op.type = GRPC_SEND_METADATA; + /* call down */ + op.type = GRPC_REQUEST_DATA; op.dir = GRPC_CALL_DOWN; - op.flags = flags; + op.flags = 0; op.done_cb = do_nothing; op.user_data = NULL; - op.data.metadata = mdelem; - - elem = CALL_ELEM_FROM_CALL(call, 0); - elem->filter->call_op(elem, NULL, &op); -} - -grpc_call_error grpc_call_add_metadata_old(grpc_call *call, - grpc_metadata *metadata, - gpr_uint32 flags) { - grpc_mdelem *mdelem; - - if (call->is_client) { - if (call->state >= CALL_STARTED) { - return GRPC_CALL_ERROR_ALREADY_INVOKED; - } - } else { - if (call->state >= CALL_FINISHED) { - return GRPC_CALL_ERROR_ALREADY_FINISHED; - } - } - - mdelem = grpc_mdelem_from_string_and_buffer( - call->metadata_context, metadata->key, (gpr_uint8 *)metadata->value, - metadata->value_length); - grpc_call_add_mdelem(call, mdelem, flags); - return GRPC_CALL_OK; -} -static void finish_call(grpc_call *call) { - size_t count; - grpc_metadata *elements; - count = grpc_metadata_buffer_count(&call->incoming_metadata); - elements = grpc_metadata_buffer_extract_elements(&call->incoming_metadata); - grpc_cq_end_finished( - call->cq, call->finished_tag, call, grpc_metadata_buffer_cleanup_elements, - elements, call->status_code, - call->status_details - ? (char *)grpc_mdstr_as_c_string(call->status_details) - : NULL, - elements, count); + grpc_call_execute_op(call, &op); } -static void done_write(void *user_data, grpc_op_error error) { - grpc_call *call = user_data; - void *tag = call->write_tag; - - GPR_ASSERT(call->have_write); - call->have_write = 0; - call->write_tag = INVALID_TAG; - grpc_cq_end_write_accepted(call->cq, tag, call, NULL, NULL, error); +static int is_op_live(grpc_call *call, grpc_ioreq_op op) { + gpr_uint8 set = call->request_set[op]; + reqinfo_master *master; + if (set >= GRPC_IOREQ_OP_COUNT) return 0; + master = &call->masters[set]; + return (master->complete_mask & (1u << op)) == 0; } -static void done_writes_done(void *user_data, grpc_op_error error) { - grpc_call *call = user_data; - void *tag = call->write_tag; - - GPR_ASSERT(call->have_write); - call->have_write = 0; - call->write_tag = INVALID_TAG; - grpc_cq_end_finish_accepted(call->cq, tag, call, NULL, NULL, error); -} - -static void call_started(void *user_data, grpc_op_error error) { - grpc_call *call = user_data; - grpc_call_element *elem; - grpc_byte_buffer *pending_write = NULL; - gpr_uint32 pending_write_flags = 0; - gpr_uint8 pending_writes_done = 0; - int ok; - grpc_call_op op; - - gpr_mu_lock(&call->read_mu); - GPR_ASSERT(!call->received_start); - call->received_start = 1; - ok = call->start_ok = (error == GRPC_OP_OK); - pending_write = call->pending_write; - pending_write_flags = call->pending_write_flags; - pending_writes_done = call->pending_writes_done; - gpr_mu_unlock(&call->read_mu); - - if (pending_write) { - if (ok) { - op.type = GRPC_SEND_MESSAGE; - op.dir = GRPC_CALL_DOWN; - op.flags = pending_write_flags; - op.done_cb = done_write; - op.user_data = call; - op.data.message = pending_write; +static void lock(grpc_call *call) { gpr_mu_lock(&call->mu); } - elem = CALL_ELEM_FROM_CALL(call, 0); - elem->filter->call_op(elem, NULL, &op); - } else { - done_write(call, error); - } - grpc_byte_buffer_destroy(pending_write); - } - if (pending_writes_done) { - if (ok) { - op.type = GRPC_SEND_FINISH; - op.dir = GRPC_CALL_DOWN; - op.flags = 0; - op.done_cb = done_writes_done; - op.user_data = call; +static void unlock(grpc_call *call) { + send_action sa = SEND_NOTHING; + completed_request completed_requests[GRPC_IOREQ_OP_COUNT]; + int num_completed_requests = call->num_completed_requests; + int need_more_data = + call->need_more_data && + !call->sending && + call->write_state >= WRITE_STATE_STARTED; + int i; - elem = CALL_ELEM_FROM_CALL(call, 0); - elem->filter->call_op(elem, NULL, &op); - } else { - done_writes_done(call, error); - } + if (need_more_data) { + call->need_more_data = 0; } - grpc_call_internal_unref(call); -} - -grpc_call_error grpc_call_invoke_old(grpc_call *call, grpc_completion_queue *cq, - void *metadata_read_tag, - void *finished_tag, gpr_uint32 flags) { - grpc_call_element *elem; - grpc_call_op op; - - /* validate preconditions */ - if (!call->is_client) { - gpr_log(GPR_ERROR, "can only call %s on clients", __FUNCTION__); - return GRPC_CALL_ERROR_NOT_ON_SERVER; + if (num_completed_requests != 0) { + memcpy(completed_requests, call->completed_requests, + sizeof(completed_requests)); + call->num_completed_requests = 0; } - if (call->state >= CALL_STARTED || call->cq) { - gpr_log(GPR_ERROR, "call is already invoked"); - return GRPC_CALL_ERROR_ALREADY_INVOKED; + if (!call->sending) { + sa = choose_send_action(call); + if (sa != SEND_NOTHING) { + call->sending = 1; + grpc_call_internal_ref(call); + } } - if (call->have_write) { - gpr_log(GPR_ERROR, "can only have one pending write operation at a time"); - return GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - } + gpr_mu_unlock(&call->mu); - if (call->have_read) { - gpr_log(GPR_ERROR, "can only have one pending read operation at a time"); - return GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + if (need_more_data) { + request_more_data(call); } - if (flags & GRPC_WRITE_NO_COMPRESS) { - return GRPC_CALL_ERROR_INVALID_FLAGS; + if (sa != SEND_NOTHING) { + enact_send_action(call, sa); } - /* inform the completion queue of an incoming operation */ - grpc_cq_begin_op(cq, call, GRPC_FINISHED); - grpc_cq_begin_op(cq, call, GRPC_CLIENT_METADATA_READ); - - gpr_mu_lock(&call->read_mu); - - /* update state */ - call->cq = cq; - call->state = CALL_STARTED; - call->finished_tag = finished_tag; - - if (call->received_finish) { - /* handle early cancellation */ - grpc_cq_end_client_metadata_read(call->cq, metadata_read_tag, call, NULL, - NULL, 0, NULL); - finish_call(call); - - /* early out.. unlock & return */ - gpr_mu_unlock(&call->read_mu); - return GRPC_CALL_OK; + for (i = 0; i < num_completed_requests; i++) { + completed_requests[i].on_complete(call, completed_requests[i].status, + completed_requests[i].user_data); } - - call->metadata_tag = metadata_read_tag; - - gpr_mu_unlock(&call->read_mu); - - /* call down the filter stack */ - op.type = GRPC_SEND_START; - op.dir = GRPC_CALL_DOWN; - op.flags = flags; - op.done_cb = call_started; - op.data.start.pollset = grpc_cq_pollset(cq); - op.user_data = call; - grpc_call_internal_ref(call); - - elem = CALL_ELEM_FROM_CALL(call, 0); - elem->filter->call_op(elem, NULL, &op); - - return GRPC_CALL_OK; } -grpc_call_error grpc_call_server_accept_old(grpc_call *call, - grpc_completion_queue *cq, - void *finished_tag) { - /* validate preconditions */ - if (call->is_client) { - gpr_log(GPR_ERROR, "can only call %s on servers", __FUNCTION__); - return GRPC_CALL_ERROR_NOT_ON_CLIENT; - } - - if (call->state >= CALL_BOUNDCQ) { - gpr_log(GPR_ERROR, "call is already accepted"); - return GRPC_CALL_ERROR_ALREADY_ACCEPTED; +static void get_final_status(grpc_call *call, grpc_recv_status_args args) { + int i; + for (i = 0; i < STATUS_SOURCE_COUNT; i++) { + if (call->status[i].is_set) { + *args.code = call->status[i].code; + if (!args.details) return; + if (call->status[i].details) { + gpr_slice details = call->status[i].details->slice; + size_t len = GPR_SLICE_LENGTH(details); + if (len + 1 > *args.details_capacity) { + *args.details_capacity = + GPR_MAX(len + 1, *args.details_capacity * 3 / 2); + *args.details = gpr_realloc(*args.details, *args.details_capacity); + } + memcpy(*args.details, GPR_SLICE_START_PTR(details), len); + (*args.details)[len] = 0; + } else { + goto no_details; + } + return; + } } + *args.code = GRPC_STATUS_UNKNOWN; + if (!args.details) return; - /* inform the completion queue of an incoming operation (corresponding to - finished_tag) */ - grpc_cq_begin_op(cq, call, GRPC_FINISHED); - - /* update state */ - gpr_mu_lock(&call->read_mu); - call->state = CALL_BOUNDCQ; - call->cq = cq; - call->finished_tag = finished_tag; - call->received_start = 1; - if (prq_is_empty(&call->prq) && call->received_finish) { - finish_call(call); - - /* early out.. unlock & return */ - gpr_mu_unlock(&call->read_mu); - return GRPC_CALL_OK; +no_details: + if (0 == *args.details_capacity) { + *args.details_capacity = 8; + *args.details = gpr_malloc(*args.details_capacity); } - gpr_mu_unlock(&call->read_mu); - - return GRPC_CALL_OK; + **args.details = 0; } -grpc_call_error grpc_call_server_end_initial_metadata_old(grpc_call *call, - gpr_uint32 flags) { - grpc_call_element *elem; - grpc_call_op op; - - /* validate preconditions */ - if (call->is_client) { - gpr_log(GPR_ERROR, "can only call %s on servers", __FUNCTION__); - return GRPC_CALL_ERROR_NOT_ON_CLIENT; +static void finish_live_ioreq_op(grpc_call *call, grpc_ioreq_op op, + grpc_op_error status) { + completed_request *cr; + gpr_uint8 master_set = call->request_set[op]; + reqinfo_master *master; + size_t i; + /* ioreq is live: we need to do something */ + master = &call->masters[master_set]; + master->complete_mask |= 1u << op; + if (status != GRPC_OP_OK) { + master->status = status; + master->complete_mask = master->need_mask; } - - if (call->state >= CALL_STARTED) { - gpr_log(GPR_ERROR, "call is already started"); - return GRPC_CALL_ERROR_ALREADY_INVOKED; + if (master->complete_mask == master->need_mask) { + for (i = 0; i < GRPC_IOREQ_OP_COUNT; i++) { + if (call->request_set[i] != master_set) { + continue; + } + call->request_set[i] = REQSET_DONE; + switch ((grpc_ioreq_op)i) { + case GRPC_IOREQ_RECV_MESSAGE: + case GRPC_IOREQ_SEND_MESSAGE: + if (master->status == GRPC_OP_OK) { + call->request_set[i] = REQSET_EMPTY; + } else { + call->write_state = WRITE_STATE_WRITE_CLOSED; + } + break; + case GRPC_IOREQ_RECV_CLOSE: + case GRPC_IOREQ_SEND_INITIAL_METADATA: + case GRPC_IOREQ_SEND_TRAILING_METADATA: + case GRPC_IOREQ_SEND_STATUS: + case GRPC_IOREQ_SEND_CLOSE: + break; + case GRPC_IOREQ_RECV_STATUS: + get_final_status( + call, call->request_data[GRPC_IOREQ_RECV_STATUS].recv_status); + break; + case GRPC_IOREQ_RECV_INITIAL_METADATA: + SWAP(grpc_metadata_array, call->buffered_metadata[0], + *call->request_data[GRPC_IOREQ_RECV_INITIAL_METADATA] + .recv_metadata); + break; + case GRPC_IOREQ_RECV_TRAILING_METADATA: + SWAP(grpc_metadata_array, call->buffered_metadata[1], + *call->request_data[GRPC_IOREQ_RECV_TRAILING_METADATA] + .recv_metadata); + break; + case GRPC_IOREQ_OP_COUNT: + abort(); + break; + } + } + cr = &call->completed_requests[call->num_completed_requests++]; + cr->status = master->status; + cr->on_complete = master->on_complete; + cr->user_data = master->user_data; } +} - if (flags & GRPC_WRITE_NO_COMPRESS) { - return GRPC_CALL_ERROR_INVALID_FLAGS; +static void finish_ioreq_op(grpc_call *call, grpc_ioreq_op op, + grpc_op_error status) { + if (is_op_live(call, op)) { + finish_live_ioreq_op(call, op, status); } +} - /* update state */ - call->state = CALL_STARTED; - - /* call down */ - op.type = GRPC_SEND_START; - op.dir = GRPC_CALL_DOWN; - op.flags = flags; - op.done_cb = do_nothing; - op.data.start.pollset = grpc_cq_pollset(call->cq); - op.user_data = NULL; - - elem = CALL_ELEM_FROM_CALL(call, 0); - elem->filter->call_op(elem, NULL, &op); +static void finish_send_op(grpc_call *call, grpc_ioreq_op op, + grpc_op_error error) { + lock(call); + finish_ioreq_op(call, op, error); + call->sending = 0; + unlock(call); + grpc_call_internal_unref(call, 0); +} - return GRPC_CALL_OK; +static void finish_write_step(void *pc, grpc_op_error error) { + finish_send_op(pc, GRPC_IOREQ_SEND_MESSAGE, error); } -void grpc_call_client_initial_metadata_complete( - grpc_call_element *surface_element) { - grpc_call *call = grpc_call_from_top_element(surface_element); - size_t count; - grpc_metadata *elements; +static void finish_finish_step(void *pc, grpc_op_error error) { + finish_send_op(pc, GRPC_IOREQ_SEND_CLOSE, error); +} - gpr_mu_lock(&call->read_mu); - count = grpc_metadata_buffer_count(&call->incoming_metadata); - elements = grpc_metadata_buffer_extract_elements(&call->incoming_metadata); +static void finish_start_step(void *pc, grpc_op_error error) { + finish_send_op(pc, GRPC_IOREQ_SEND_INITIAL_METADATA, error); +} - GPR_ASSERT(!call->received_metadata); - grpc_cq_end_client_metadata_read(call->cq, call->metadata_tag, call, - grpc_metadata_buffer_cleanup_elements, - elements, count, elements); - call->received_metadata = 1; - call->metadata_tag = INVALID_TAG; - gpr_mu_unlock(&call->read_mu); +static send_action choose_send_action(grpc_call *call) { + switch (call->write_state) { + case WRITE_STATE_INITIAL: + if (is_op_live(call, GRPC_IOREQ_SEND_INITIAL_METADATA)) { + call->write_state = WRITE_STATE_STARTED; + return SEND_INITIAL_METADATA; + } + return SEND_NOTHING; + case WRITE_STATE_STARTED: + if (is_op_live(call, GRPC_IOREQ_SEND_MESSAGE)) { + return SEND_MESSAGE; + } + if (is_op_live(call, GRPC_IOREQ_SEND_CLOSE)) { + call->write_state = WRITE_STATE_WRITE_CLOSED; + finish_ioreq_op(call, GRPC_IOREQ_SEND_TRAILING_METADATA, GRPC_OP_OK); + finish_ioreq_op(call, GRPC_IOREQ_SEND_STATUS, GRPC_OP_OK); + return call->is_client ? SEND_FINISH + : SEND_TRAILING_METADATA_AND_FINISH; + } + return SEND_NOTHING; + case WRITE_STATE_WRITE_CLOSED: + return SEND_NOTHING; + } + gpr_log(GPR_ERROR, "should never reach here"); + abort(); + return SEND_NOTHING; } -static void request_more_data(grpc_call *call) { - grpc_call_element *elem; +static void send_metadata(grpc_call *call, grpc_mdelem *elem) { grpc_call_op op; - - /* call down */ - op.type = GRPC_REQUEST_DATA; + op.type = GRPC_SEND_METADATA; op.dir = GRPC_CALL_DOWN; op.flags = 0; + op.data.metadata = elem; op.done_cb = do_nothing; op.user_data = NULL; - - elem = CALL_ELEM_FROM_CALL(call, 0); - elem->filter->call_op(elem, NULL, &op); + grpc_call_execute_op(call, &op); } -grpc_call_error grpc_call_start_read_old(grpc_call *call, void *tag) { - gpr_uint8 request_more = 0; +static void enact_send_action(grpc_call *call, send_action sa) { + grpc_ioreq_data data; + grpc_call_op op; + size_t i; + char status_str[GPR_LTOA_MIN_BUFSIZE]; - switch (call->state) { - case CALL_CREATED: - return GRPC_CALL_ERROR_NOT_INVOKED; - case CALL_BOUNDCQ: - case CALL_STARTED: + switch (sa) { + case SEND_NOTHING: + abort(); + break; + case SEND_INITIAL_METADATA: + data = call->request_data[GRPC_IOREQ_SEND_INITIAL_METADATA]; + for (i = 0; i < data.send_metadata.count; i++) { + const grpc_metadata *md = &data.send_metadata.metadata[i]; + send_metadata(call, + grpc_mdelem_from_string_and_buffer( + call->metadata_context, md->key, + (const gpr_uint8 *)md->value, md->value_length)); + } + op.type = GRPC_SEND_START; + op.dir = GRPC_CALL_DOWN; + op.flags = 0; + op.data.start.pollset = grpc_cq_pollset(call->cq); + op.done_cb = finish_start_step; + op.user_data = call; + grpc_call_execute_op(call, &op); + break; + case SEND_MESSAGE: + data = call->request_data[GRPC_IOREQ_SEND_MESSAGE]; + op.type = GRPC_SEND_MESSAGE; + op.dir = GRPC_CALL_DOWN; + op.flags = 0; + op.data.message = data.send_message; + op.done_cb = finish_write_step; + op.user_data = call; + grpc_call_execute_op(call, &op); + break; + case SEND_TRAILING_METADATA_AND_FINISH: + /* send trailing metadata */ + data = call->request_data[GRPC_IOREQ_SEND_TRAILING_METADATA]; + for (i = 0; i < data.send_metadata.count; i++) { + const grpc_metadata *md = &data.send_metadata.metadata[i]; + send_metadata(call, + grpc_mdelem_from_string_and_buffer( + call->metadata_context, md->key, + (const gpr_uint8 *)md->value, md->value_length)); + } + /* send status */ + /* TODO(ctiller): cache common status values */ + data = call->request_data[GRPC_IOREQ_SEND_STATUS]; + gpr_ltoa(data.send_status.code, status_str); + send_metadata( + call, + grpc_mdelem_from_metadata_strings( + call->metadata_context, + grpc_mdstr_ref(grpc_channel_get_status_string(call->channel)), + grpc_mdstr_from_string(call->metadata_context, status_str))); + if (data.send_status.details) { + send_metadata( + call, + grpc_mdelem_from_metadata_strings( + call->metadata_context, + grpc_mdstr_ref(grpc_channel_get_message_string(call->channel)), + grpc_mdstr_from_string(call->metadata_context, + data.send_status.details))); + } + /* fallthrough: see choose_send_action for details */ + case SEND_FINISH: + op.type = GRPC_SEND_FINISH; + op.dir = GRPC_CALL_DOWN; + op.flags = 0; + op.done_cb = finish_finish_step; + op.user_data = call; + grpc_call_execute_op(call, &op); break; - case CALL_FINISHED: - return GRPC_CALL_ERROR_ALREADY_FINISHED; } +} - gpr_mu_lock(&call->read_mu); - - if (call->have_read) { - gpr_mu_unlock(&call->read_mu); - return GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; +static grpc_call_error start_ioreq_error(grpc_call *call, + gpr_uint32 mutated_ops, + grpc_call_error ret) { + size_t i; + for (i = 0; i < GRPC_IOREQ_OP_COUNT; i++) { + if (mutated_ops & (1u << i)) { + call->request_set[i] = REQSET_EMPTY; + } } + return ret; +} - grpc_cq_begin_op(call->cq, call, GRPC_READ); +static void finish_read_ops(grpc_call *call) { + int empty; - if (!prq_pop_to_cq(&call->prq, tag, call, call->cq)) { - if (call->reads_done) { - grpc_cq_end_read(call->cq, tag, call, do_nothing, NULL, NULL); - } else { - call->read_tag = tag; - call->have_read = 1; - request_more = call->received_start; + if (is_op_live(call, GRPC_IOREQ_RECV_MESSAGE)) { + empty = + (NULL == (*call->request_data[GRPC_IOREQ_RECV_MESSAGE].recv_message = + grpc_bbq_pop(&call->incoming_queue))); + if (!empty) { + finish_live_ioreq_op(call, GRPC_IOREQ_RECV_MESSAGE, GRPC_OP_OK); + empty = grpc_bbq_empty(&call->incoming_queue); } - } else if (prq_is_empty(&call->prq) && call->received_finish) { - finish_call(call); + } else { + empty = grpc_bbq_empty(&call->incoming_queue); } - gpr_mu_unlock(&call->read_mu); - - if (request_more) { - request_more_data(call); + switch (call->read_state) { + case READ_STATE_STREAM_CLOSED: + if (empty) { + finish_ioreq_op(call, GRPC_IOREQ_RECV_CLOSE, GRPC_OP_OK); + } + /* fallthrough */ + case READ_STATE_READ_CLOSED: + if (empty) { + finish_ioreq_op(call, GRPC_IOREQ_RECV_MESSAGE, GRPC_OP_OK); + } + finish_ioreq_op(call, GRPC_IOREQ_RECV_STATUS, GRPC_OP_OK); + finish_ioreq_op(call, GRPC_IOREQ_RECV_TRAILING_METADATA, GRPC_OP_OK); + /* fallthrough */ + case READ_STATE_GOT_INITIAL_METADATA: + finish_ioreq_op(call, GRPC_IOREQ_RECV_INITIAL_METADATA, GRPC_OP_OK); + /* fallthrough */ + case READ_STATE_INITIAL: + /* do nothing */ + break; } - - return GRPC_CALL_OK; } -grpc_call_error grpc_call_start_write_old(grpc_call *call, - grpc_byte_buffer *byte_buffer, - void *tag, gpr_uint32 flags) { - grpc_call_element *elem; - grpc_call_op op; - - switch (call->state) { - case CALL_CREATED: - case CALL_BOUNDCQ: - return GRPC_CALL_ERROR_NOT_INVOKED; - case CALL_STARTED: +static void early_out_write_ops(grpc_call *call) { + switch (call->write_state) { + case WRITE_STATE_WRITE_CLOSED: + finish_ioreq_op(call, GRPC_IOREQ_SEND_MESSAGE, GRPC_OP_ERROR); + finish_ioreq_op(call, GRPC_IOREQ_SEND_STATUS, GRPC_OP_ERROR); + finish_ioreq_op(call, GRPC_IOREQ_SEND_TRAILING_METADATA, GRPC_OP_ERROR); + finish_ioreq_op(call, GRPC_IOREQ_SEND_CLOSE, GRPC_OP_OK); + /* fallthrough */ + case WRITE_STATE_STARTED: + finish_ioreq_op(call, GRPC_IOREQ_SEND_INITIAL_METADATA, GRPC_OP_ERROR); + /* fallthrough */ + case WRITE_STATE_INITIAL: + /* do nothing */ break; - case CALL_FINISHED: - return GRPC_CALL_ERROR_ALREADY_FINISHED; - } - - if (call->have_write) { - return GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; } +} - grpc_cq_begin_op(call->cq, call, GRPC_WRITE_ACCEPTED); +static grpc_call_error start_ioreq(grpc_call *call, const grpc_ioreq *reqs, + size_t nreqs, + grpc_ioreq_completion_func completion, + void *user_data) { + size_t i; + gpr_uint32 have_ops = 0; + grpc_ioreq_op op; + reqinfo_master *master; + grpc_ioreq_data data; + gpr_uint8 set; - /* TODO(ctiller): if flags & GRPC_WRITE_BUFFER_HINT == 0, this indicates a - flush, and that flush should be propogated down from here */ - if (byte_buffer == NULL) { - grpc_cq_end_write_accepted(call->cq, tag, call, NULL, NULL, GRPC_OP_OK); + if (nreqs == 0) { return GRPC_CALL_OK; } - call->write_tag = tag; - call->have_write = 1; - - gpr_mu_lock(&call->read_mu); - if (!call->received_start) { - call->pending_write = grpc_byte_buffer_copy(byte_buffer); - call->pending_write_flags = flags; - - gpr_mu_unlock(&call->read_mu); - } else { - gpr_mu_unlock(&call->read_mu); - - op.type = GRPC_SEND_MESSAGE; - op.dir = GRPC_CALL_DOWN; - op.flags = flags; - op.done_cb = done_write; - op.user_data = call; - op.data.message = byte_buffer; - - elem = CALL_ELEM_FROM_CALL(call, 0); - elem->filter->call_op(elem, NULL, &op); - } - - return GRPC_CALL_OK; -} + set = reqs[0].op; -grpc_call_error grpc_call_writes_done_old(grpc_call *call, void *tag) { - grpc_call_element *elem; - grpc_call_op op; + for (i = 0; i < nreqs; i++) { + op = reqs[i].op; + if (call->request_set[op] < GRPC_IOREQ_OP_COUNT) { + return start_ioreq_error(call, have_ops, + GRPC_CALL_ERROR_TOO_MANY_OPERATIONS); + } else if (call->request_set[op] == REQSET_DONE) { + return start_ioreq_error(call, have_ops, GRPC_CALL_ERROR_ALREADY_INVOKED); + } + have_ops |= 1u << op; + data = reqs[i].data; - if (!call->is_client) { - return GRPC_CALL_ERROR_NOT_ON_SERVER; + call->request_data[op] = data; + call->request_set[op] = set; } - switch (call->state) { - case CALL_CREATED: - case CALL_BOUNDCQ: - return GRPC_CALL_ERROR_NOT_INVOKED; - case CALL_FINISHED: - return GRPC_CALL_ERROR_ALREADY_FINISHED; - case CALL_STARTED: - break; - } + master = &call->masters[set]; + master->status = GRPC_OP_OK; + master->need_mask = have_ops; + master->complete_mask = 0; + master->on_complete = completion; + master->user_data = user_data; - if (call->have_write) { - return GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + if (have_ops & (1u << GRPC_IOREQ_RECV_MESSAGE)) { + call->need_more_data = 1; } - grpc_cq_begin_op(call->cq, call, GRPC_FINISH_ACCEPTED); + finish_read_ops(call); + early_out_write_ops(call); - call->write_tag = tag; - call->have_write = 1; + return GRPC_CALL_OK; +} - gpr_mu_lock(&call->read_mu); - if (!call->received_start) { - call->pending_writes_done = 1; +static void call_start_ioreq_done(grpc_call *call, grpc_op_error status, + void *user_data) { + grpc_cq_end_ioreq(call->cq, user_data, call, do_nothing, NULL, status); +} - gpr_mu_unlock(&call->read_mu); - } else { - gpr_mu_unlock(&call->read_mu); +grpc_call_error grpc_call_start_ioreq(grpc_call *call, const grpc_ioreq *reqs, + size_t nreqs, void *tag) { + grpc_call_error err; + lock(call); + err = start_ioreq(call, reqs, nreqs, call_start_ioreq_done, tag); + unlock(call); + return err; +} - op.type = GRPC_SEND_FINISH; - op.dir = GRPC_CALL_DOWN; - op.flags = 0; - op.done_cb = done_writes_done; - op.user_data = call; +grpc_call_error grpc_call_start_ioreq_and_call_back( + grpc_call *call, const grpc_ioreq *reqs, size_t nreqs, + grpc_ioreq_completion_func on_complete, void *user_data) { + grpc_call_error err; + lock(call); + err = start_ioreq(call, reqs, nreqs, on_complete, user_data); + unlock(call); + return err; +} - elem = CALL_ELEM_FROM_CALL(call, 0); - elem->filter->call_op(elem, NULL, &op); +void grpc_call_destroy(grpc_call *c) { + int cancel; + lock(c); + if (c->have_alarm) { + grpc_alarm_cancel(&c->alarm); + c->have_alarm = 0; } - - return GRPC_CALL_OK; + cancel = c->read_state != READ_STATE_STREAM_CLOSED; + unlock(c); + if (cancel) grpc_call_cancel(c); + grpc_call_internal_unref(c, 1); } -grpc_call_error grpc_call_start_write_status_old(grpc_call *call, - grpc_status_code status, - const char *details, - void *tag) { +grpc_call_error grpc_call_cancel(grpc_call *c) { grpc_call_element *elem; grpc_call_op op; - if (call->is_client) { - return GRPC_CALL_ERROR_NOT_ON_CLIENT; - } + op.type = GRPC_CANCEL_OP; + op.dir = GRPC_CALL_DOWN; + op.flags = 0; + op.done_cb = do_nothing; + op.user_data = NULL; - switch (call->state) { - case CALL_CREATED: - case CALL_BOUNDCQ: - return GRPC_CALL_ERROR_NOT_INVOKED; - case CALL_FINISHED: - return GRPC_CALL_ERROR_ALREADY_FINISHED; - case CALL_STARTED: - break; - } + elem = CALL_ELEM_FROM_CALL(c, 0); + elem->filter->call_op(elem, NULL, &op); - if (call->have_write) { - return GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - } + return GRPC_CALL_OK; +} +grpc_call_error grpc_call_cancel_with_status(grpc_call *c, + grpc_status_code status, + const char *description) { + grpc_mdstr *details = + description ? grpc_mdstr_from_string(c->metadata_context, description) + : NULL; + lock(c); + set_status_code(c, STATUS_FROM_API_OVERRIDE, status); + set_status_details(c, STATUS_FROM_API_OVERRIDE, details); + unlock(c); + return grpc_call_cancel(c); +} + +void grpc_call_execute_op(grpc_call *call, grpc_call_op *op) { + grpc_call_element *elem; + GPR_ASSERT(op->dir == GRPC_CALL_DOWN); elem = CALL_ELEM_FROM_CALL(call, 0); + elem->filter->call_op(elem, NULL, op); +} - if (details && details[0]) { - grpc_mdelem *md = grpc_mdelem_from_strings(call->metadata_context, - "grpc-message", details); +grpc_call *grpc_call_from_top_element(grpc_call_element *elem) { + return CALL_FROM_TOP_ELEM(elem); +} - op.type = GRPC_SEND_METADATA; - op.dir = GRPC_CALL_DOWN; - op.flags = 0; - op.done_cb = do_nothing; - op.user_data = NULL; - op.data.metadata = md; - elem->filter->call_op(elem, NULL, &op); +static void call_alarm(void *arg, int success) { + grpc_call *call = arg; + if (success) { + if (call->is_client) { + grpc_call_cancel_with_status(call, GRPC_STATUS_DEADLINE_EXCEEDED, + "Deadline Exceeded"); + } else { + grpc_call_cancel(call); + } } + grpc_call_internal_unref(call, 1); +} - /* always send status */ - { - grpc_mdelem *md; - char buffer[GPR_LTOA_MIN_BUFSIZE]; - gpr_ltoa(status, buffer); - md = - grpc_mdelem_from_strings(call->metadata_context, "grpc-status", buffer); +void grpc_call_set_deadline(grpc_call_element *elem, gpr_timespec deadline) { + grpc_call *call = CALL_FROM_TOP_ELEM(elem); - op.type = GRPC_SEND_METADATA; - op.dir = GRPC_CALL_DOWN; - op.flags = 0; - op.done_cb = do_nothing; - op.user_data = NULL; - op.data.metadata = md; - elem->filter->call_op(elem, NULL, &op); + if (call->have_alarm) { + gpr_log(GPR_ERROR, "Attempt to set deadline alarm twice"); } + grpc_call_internal_ref(call); + call->have_alarm = 1; + grpc_alarm_init(&call->alarm, deadline, call_alarm, call, gpr_now()); +} - grpc_cq_begin_op(call->cq, call, GRPC_FINISH_ACCEPTED); - - call->state = CALL_FINISHED; - call->write_tag = tag; - call->have_write = 1; - - op.type = GRPC_SEND_FINISH; - op.dir = GRPC_CALL_DOWN; - op.flags = 0; - op.done_cb = done_writes_done; - op.user_data = call; +static void set_read_state(grpc_call *call, read_state state) { + lock(call); + GPR_ASSERT(call->read_state < state); + call->read_state = state; + finish_read_ops(call); + unlock(call); +} - elem->filter->call_op(elem, NULL, &op); +void grpc_call_read_closed(grpc_call_element *elem) { + set_read_state(CALL_FROM_TOP_ELEM(elem), READ_STATE_READ_CLOSED); +} - return GRPC_CALL_OK; +void grpc_call_stream_closed(grpc_call_element *elem) { + grpc_call *call = CALL_FROM_TOP_ELEM(elem); + set_read_state(call, READ_STATE_STREAM_CLOSED); + grpc_call_internal_unref(call, 0); } /* we offset status by a small amount when storing it into transport metadata @@ -865,7 +856,7 @@ static gpr_uint32 decode_status(grpc_mdelem *md) { gpr_uint32 status; void *user_data = grpc_mdelem_get_user_data(md, destroy_status); if (user_data) { - status = ((gpr_uint32)(gpr_intptr)user_data) - STATUS_OFFSET; + status = ((gpr_uint32)(gpr_intptr) user_data) - STATUS_OFFSET; } else { if (!gpr_parse_bytes_to_uint32(grpc_mdstr_as_c_string(md->value), GPR_SLICE_LENGTH(md->value->slice), @@ -878,112 +869,362 @@ static gpr_uint32 decode_status(grpc_mdelem *md) { return status; } -void grpc_call_recv_metadata(grpc_call_element *elem, grpc_call_op *op) { +void grpc_call_recv_message(grpc_call_element *elem, + grpc_byte_buffer *byte_buffer) { + grpc_call *call = CALL_FROM_TOP_ELEM(elem); + lock(call); + grpc_bbq_push(&call->incoming_queue, byte_buffer); + finish_read_ops(call); + unlock(call); +} + +void grpc_call_recv_metadata(grpc_call_element *elem, grpc_mdelem *md) { grpc_call *call = CALL_FROM_TOP_ELEM(elem); - grpc_mdelem *md = op->data.metadata; grpc_mdstr *key = md->key; + grpc_metadata_array *dest; + grpc_metadata *mdusr; + lock(call); if (key == grpc_channel_get_status_string(call->channel)) { - maybe_set_status_code(call, decode_status(md)); + set_status_code(call, STATUS_FROM_WIRE, decode_status(md)); grpc_mdelem_unref(md); - op->done_cb(op->user_data, GRPC_OP_OK); } else if (key == grpc_channel_get_message_string(call->channel)) { - maybe_set_status_details(call, md->value); + set_status_details(call, STATUS_FROM_WIRE, grpc_mdstr_ref(md->value)); grpc_mdelem_unref(md); - op->done_cb(op->user_data, GRPC_OP_OK); } else { - grpc_metadata_buffer_queue(&call->incoming_metadata, op); + dest = &call->buffered_metadata[call->read_state >= + READ_STATE_GOT_INITIAL_METADATA]; + if (dest->count == dest->capacity) { + dest->capacity = GPR_MAX(dest->capacity + 8, dest->capacity * 2); + dest->metadata = + gpr_realloc(dest->metadata, sizeof(grpc_metadata) * dest->capacity); + } + mdusr = &dest->metadata[dest->count++]; + mdusr->key = (char *)grpc_mdstr_as_c_string(md->key); + mdusr->value = (char *)grpc_mdstr_as_c_string(md->value); + mdusr->value_length = GPR_SLICE_LENGTH(md->value->slice); + if (call->owned_metadata_count == call->owned_metadata_capacity) { + call->owned_metadata_capacity = GPR_MAX( + call->owned_metadata_capacity + 8, call->owned_metadata_capacity * 2); + call->owned_metadata = + gpr_realloc(call->owned_metadata, + sizeof(grpc_mdelem *) * call->owned_metadata_capacity); + } + call->owned_metadata[call->owned_metadata_count++] = md; } + unlock(call); } -void grpc_call_recv_finish(grpc_call_element *elem, int is_full_close) { - grpc_call *call = CALL_FROM_TOP_ELEM(elem); +grpc_call_stack *grpc_call_get_call_stack(grpc_call *call) { + return CALL_STACK_FROM_CALL(call); +} - gpr_mu_lock(&call->read_mu); +void grpc_call_initial_metadata_complete(grpc_call_element *surface_element) { + grpc_call *call = grpc_call_from_top_element(surface_element); + set_read_state(call, READ_STATE_GOT_INITIAL_METADATA); +} - if (call->have_read) { - grpc_cq_end_read(call->cq, call->read_tag, call, do_nothing, NULL, NULL); - call->read_tag = INVALID_TAG; - call->have_read = 0; - } - if (call->is_client && !call->received_metadata && call->cq) { - size_t count; - grpc_metadata *elements; +/* + * LEGACY API IMPLEMENTATION + * All this code will disappear as soon as wrappings are updated + */ + +struct legacy_state { + gpr_uint8 md_out_buffer; + size_t md_out_count[2]; + size_t md_out_capacity[2]; + grpc_metadata *md_out[2]; + grpc_byte_buffer *msg_out; + + /* input buffers */ + grpc_metadata_array initial_md_in; + grpc_metadata_array trailing_md_in; - call->received_metadata = 1; + size_t details_capacity; + char *details; + grpc_status_code status; - count = grpc_metadata_buffer_count(&call->incoming_metadata); - elements = grpc_metadata_buffer_extract_elements(&call->incoming_metadata); - grpc_cq_end_client_metadata_read(call->cq, call->metadata_tag, call, - grpc_metadata_buffer_cleanup_elements, - elements, count, elements); + char *send_details; + + size_t msg_in_read_idx; + grpc_byte_buffer *msg_in; + + void *finished_tag; +}; + +static legacy_state *get_legacy_state(grpc_call *call) { + if (call->legacy_state == NULL) { + call->legacy_state = gpr_malloc(sizeof(legacy_state)); + memset(call->legacy_state, 0, sizeof(legacy_state)); } - if (is_full_close) { - if (call->have_alarm) { - grpc_alarm_cancel(&call->alarm); - call->have_alarm = 0; - } - call->received_finish = 1; - if (prq_is_empty(&call->prq) && call->cq != NULL) { - finish_call(call); + return call->legacy_state; +} + +static void destroy_legacy_state(legacy_state *ls) { + size_t i, j; + for (i = 0; i < 2; i++) { + for (j = 0; j < ls->md_out_count[i]; j++) { + gpr_free(ls->md_out[i][j].key); + gpr_free(ls->md_out[i][j].value); } - } else { - call->reads_done = 1; + gpr_free(ls->md_out[i]); } - gpr_mu_unlock(&call->read_mu); + gpr_free(ls->initial_md_in.metadata); + gpr_free(ls->trailing_md_in.metadata); + gpr_free(ls->details); + gpr_free(ls->send_details); + gpr_free(ls); } -void grpc_call_recv_message(grpc_call_element *elem, grpc_byte_buffer *message, - void (*on_finish)(void *user_data, - grpc_op_error error), - void *user_data) { - grpc_call *call = CALL_FROM_TOP_ELEM(elem); +grpc_call_error grpc_call_add_metadata_old(grpc_call *call, + grpc_metadata *metadata, + gpr_uint32 flags) { + legacy_state *ls; + grpc_metadata *mdout; + + lock(call); + ls = get_legacy_state(call); + + if (ls->md_out_count[ls->md_out_buffer] == + ls->md_out_capacity[ls->md_out_buffer]) { + ls->md_out_capacity[ls->md_out_buffer] = + GPR_MAX(ls->md_out_capacity[ls->md_out_buffer] * 3 / 2, + ls->md_out_capacity[ls->md_out_buffer] + 8); + ls->md_out[ls->md_out_buffer] = gpr_realloc( + ls->md_out[ls->md_out_buffer], + sizeof(grpc_metadata) * ls->md_out_capacity[ls->md_out_buffer]); + } + mdout = &ls->md_out[ls->md_out_buffer][ls->md_out_count[ls->md_out_buffer]++]; + mdout->key = gpr_strdup(metadata->key); + mdout->value = gpr_malloc(metadata->value_length); + mdout->value_length = metadata->value_length; + memcpy(mdout->value, metadata->value, metadata->value_length); + + unlock(call); + + return GRPC_CALL_OK; +} + +static void finish_status(grpc_call *call, grpc_op_error status, + void *ignored) { + legacy_state *ls; + + lock(call); + ls = get_legacy_state(call); + grpc_cq_end_finished(call->cq, ls->finished_tag, call, do_nothing, NULL, + ls->status, ls->details, ls->trailing_md_in.metadata, + ls->trailing_md_in.count); + unlock(call); +} + +static void finish_recv_metadata(grpc_call *call, grpc_op_error status, + void *tag) { + legacy_state *ls; + + lock(call); + ls = get_legacy_state(call); + if (status == GRPC_OP_OK) { + grpc_cq_end_client_metadata_read(call->cq, tag, call, do_nothing, NULL, + ls->initial_md_in.count, + ls->initial_md_in.metadata); - gpr_mu_lock(&call->read_mu); - if (call->have_read) { - grpc_cq_end_read(call->cq, call->read_tag, call, on_finish, user_data, - message); - call->read_tag = INVALID_TAG; - call->have_read = 0; } else { - prq_push(&call->prq, message, on_finish, user_data); + grpc_cq_end_client_metadata_read(call->cq, tag, call, do_nothing, NULL, 0, + NULL); } - gpr_mu_unlock(&call->read_mu); + unlock(call); } -grpc_call *grpc_call_from_top_element(grpc_call_element *elem) { - return CALL_FROM_TOP_ELEM(elem); +static void finish_send_metadata(grpc_call *call, grpc_op_error status, + void *tag) {} + +grpc_call_error grpc_call_invoke_old(grpc_call *call, grpc_completion_queue *cq, + void *metadata_read_tag, + void *finished_tag, gpr_uint32 flags) { + grpc_ioreq reqs[3]; + legacy_state *ls; + grpc_call_error err; + + grpc_cq_begin_op(cq, call, GRPC_CLIENT_METADATA_READ); + grpc_cq_begin_op(cq, call, GRPC_FINISHED); + + lock(call); + ls = get_legacy_state(call); + err = bind_cq(call, cq); + if (err != GRPC_CALL_OK) goto done; + + ls->finished_tag = finished_tag; + + reqs[0].op = GRPC_IOREQ_SEND_INITIAL_METADATA; + reqs[0].data.send_metadata.count = ls->md_out_count[ls->md_out_buffer]; + reqs[0].data.send_metadata.metadata = ls->md_out[ls->md_out_buffer]; + ls->md_out_buffer++; + err = start_ioreq(call, reqs, 1, finish_send_metadata, NULL); + if (err != GRPC_CALL_OK) goto done; + + reqs[0].op = GRPC_IOREQ_RECV_INITIAL_METADATA; + reqs[0].data.recv_metadata = &ls->initial_md_in; + err = start_ioreq(call, reqs, 1, finish_recv_metadata, metadata_read_tag); + if (err != GRPC_CALL_OK) goto done; + + reqs[0].op = GRPC_IOREQ_RECV_TRAILING_METADATA; + reqs[0].data.recv_metadata = &ls->trailing_md_in; + reqs[1].op = GRPC_IOREQ_RECV_STATUS; + reqs[1].data.recv_status.details = &ls->details; + reqs[1].data.recv_status.details_capacity = &ls->details_capacity; + reqs[1].data.recv_status.code = &ls->status; + reqs[2].op = GRPC_IOREQ_RECV_CLOSE; + err = start_ioreq(call, reqs, 3, finish_status, NULL); + if (err != GRPC_CALL_OK) goto done; + +done: + unlock(call); + return err; } -grpc_metadata_buffer *grpc_call_get_metadata_buffer(grpc_call *call) { - return &call->incoming_metadata; +grpc_call_error grpc_call_server_accept_old(grpc_call *call, + grpc_completion_queue *cq, + void *finished_tag) { + grpc_ioreq reqs[2]; + grpc_call_error err; + legacy_state *ls; + + /* inform the completion queue of an incoming operation (corresponding to + finished_tag) */ + grpc_cq_begin_op(cq, call, GRPC_FINISHED); + + lock(call); + ls = get_legacy_state(call); + + err = bind_cq(call, cq); + if (err != GRPC_CALL_OK) return err; + + ls->finished_tag = finished_tag; + + reqs[0].op = GRPC_IOREQ_RECV_STATUS; + reqs[0].data.recv_status.details = NULL; + reqs[0].data.recv_status.details_capacity = 0; + reqs[0].data.recv_status.code = &ls->status; + reqs[1].op = GRPC_IOREQ_RECV_CLOSE; + err = start_ioreq(call, reqs, 2, finish_status, NULL); + unlock(call); + return err; } -static void call_alarm(void *arg, int success) { - grpc_call *call = arg; - if (success) { - if (call->is_client) { - grpc_call_cancel_with_status(call, GRPC_STATUS_DEADLINE_EXCEEDED, - "Deadline Exceeded"); - } else { - grpc_call_cancel(call); - } - } - grpc_call_internal_unref(call); +static void finish_send_initial_metadata(grpc_call *call, grpc_op_error status, + void *tag) {} + +grpc_call_error grpc_call_server_end_initial_metadata_old(grpc_call *call, + gpr_uint32 flags) { + grpc_ioreq req; + grpc_call_error err; + legacy_state *ls; + + lock(call); + ls = get_legacy_state(call); + req.op = GRPC_IOREQ_SEND_INITIAL_METADATA; + req.data.send_metadata.count = ls->md_out_count[ls->md_out_buffer]; + req.data.send_metadata.metadata = ls->md_out[ls->md_out_buffer]; + err = start_ioreq(call, &req, 1, finish_send_initial_metadata, NULL); + unlock(call); + + return err; } -void grpc_call_set_deadline(grpc_call_element *elem, gpr_timespec deadline) { - grpc_call *call = CALL_FROM_TOP_ELEM(elem); +static void finish_read_event(void *p, grpc_op_error error) { + if (p) grpc_byte_buffer_destroy(p); +} - if (call->have_alarm) { - gpr_log(GPR_ERROR, "Attempt to set deadline alarm twice"); - } - grpc_call_internal_ref(call); - call->have_alarm = 1; - grpc_alarm_init(&call->alarm, deadline, call_alarm, call, gpr_now()); +static void finish_read(grpc_call *call, grpc_op_error error, void *tag) { + legacy_state *ls; + grpc_byte_buffer *msg; + lock(call); + ls = get_legacy_state(call); + msg = ls->msg_in; + grpc_cq_end_read(call->cq, tag, call, finish_read_event, msg, msg); + unlock(call); } -grpc_call_stack *grpc_call_get_call_stack(grpc_call *call) { - return CALL_STACK_FROM_CALL(call); +grpc_call_error grpc_call_start_read_old(grpc_call *call, void *tag) { + legacy_state *ls; + grpc_ioreq req; + grpc_call_error err; + + grpc_cq_begin_op(call->cq, call, GRPC_READ); + + lock(call); + ls = get_legacy_state(call); + req.op = GRPC_IOREQ_RECV_MESSAGE; + req.data.recv_message = &ls->msg_in; + err = start_ioreq(call, &req, 1, finish_read, tag); + unlock(call); + return err; +} + +static void finish_write(grpc_call *call, grpc_op_error status, void *tag) { + lock(call); + grpc_byte_buffer_destroy(get_legacy_state(call)->msg_out); + unlock(call); + grpc_cq_end_write_accepted(call->cq, tag, call, do_nothing, NULL, status); +} + +grpc_call_error grpc_call_start_write_old(grpc_call *call, + grpc_byte_buffer *byte_buffer, + void *tag, gpr_uint32 flags) { + grpc_ioreq req; + legacy_state *ls; + grpc_call_error err; + + grpc_cq_begin_op(call->cq, call, GRPC_WRITE_ACCEPTED); + + lock(call); + ls = get_legacy_state(call); + ls->msg_out = grpc_byte_buffer_copy(byte_buffer); + req.op = GRPC_IOREQ_SEND_MESSAGE; + req.data.send_message = ls->msg_out; + err = start_ioreq(call, &req, 1, finish_write, tag); + unlock(call); + + return err; +} + +static void finish_finish(grpc_call *call, grpc_op_error status, void *tag) { + grpc_cq_end_finish_accepted(call->cq, tag, call, do_nothing, NULL, status); +} + +grpc_call_error grpc_call_writes_done_old(grpc_call *call, void *tag) { + grpc_ioreq req; + grpc_call_error err; + grpc_cq_begin_op(call->cq, call, GRPC_FINISH_ACCEPTED); + + lock(call); + req.op = GRPC_IOREQ_SEND_CLOSE; + err = start_ioreq(call, &req, 1, finish_finish, tag); + unlock(call); + + return err; } +grpc_call_error grpc_call_start_write_status_old(grpc_call *call, + grpc_status_code status, + const char *details, + void *tag) { + grpc_ioreq reqs[3]; + grpc_call_error err; + legacy_state *ls; + grpc_cq_begin_op(call->cq, call, GRPC_FINISH_ACCEPTED); + + lock(call); + ls = get_legacy_state(call); + reqs[0].op = GRPC_IOREQ_SEND_TRAILING_METADATA; + reqs[0].data.send_metadata.count = ls->md_out_count[ls->md_out_buffer]; + reqs[0].data.send_metadata.metadata = ls->md_out[ls->md_out_buffer]; + reqs[1].op = GRPC_IOREQ_SEND_STATUS; + reqs[1].data.send_status.code = status; + reqs[1].data.send_status.details = ls->send_details = gpr_strdup(details); + reqs[2].op = GRPC_IOREQ_SEND_CLOSE; + err = start_ioreq(call, reqs, 3, finish_finish, tag); + unlock(call); + + return err; +} diff --git a/src/core/surface/call.h b/src/core/surface/call.h index 804b387cb19..936fb29f2ef 100644 --- a/src/core/surface/call.h +++ b/src/core/surface/call.h @@ -38,27 +38,73 @@ #include "src/core/channel/metadata_buffer.h" #include +/* Primitive operation types - grpc_op's get rewritten into these */ +typedef enum { + GRPC_IOREQ_RECV_INITIAL_METADATA, + GRPC_IOREQ_RECV_MESSAGE, + GRPC_IOREQ_RECV_TRAILING_METADATA, + GRPC_IOREQ_RECV_STATUS, + GRPC_IOREQ_RECV_CLOSE, + GRPC_IOREQ_SEND_INITIAL_METADATA, + GRPC_IOREQ_SEND_MESSAGE, + GRPC_IOREQ_SEND_TRAILING_METADATA, + GRPC_IOREQ_SEND_STATUS, + GRPC_IOREQ_SEND_CLOSE, + GRPC_IOREQ_OP_COUNT +} grpc_ioreq_op; + +typedef struct { + grpc_status_code *code; + char **details; + size_t *details_capacity; +} grpc_recv_status_args; + +typedef union { + grpc_metadata_array *recv_metadata; + grpc_byte_buffer **recv_message; + grpc_recv_status_args recv_status; + struct { + size_t count; + grpc_metadata *metadata; + } send_metadata; + grpc_byte_buffer *send_message; + struct { + grpc_status_code code; + char *details; + } send_status; +} grpc_ioreq_data; + +typedef struct { + grpc_ioreq_op op; + grpc_ioreq_data data; +} grpc_ioreq; + +typedef void (*grpc_ioreq_completion_func)(grpc_call *call, + grpc_op_error status, + void *user_data); + grpc_call *grpc_call_create(grpc_channel *channel, const void *server_transport_data); void grpc_call_internal_ref(grpc_call *call); -void grpc_call_internal_unref(grpc_call *call); +void grpc_call_internal_unref(grpc_call *call, int allow_immediate_deletion); /* Helpers for grpc_client, grpc_server filters to publish received data to the completion queue/surface layer */ void grpc_call_recv_metadata(grpc_call_element *surface_element, - grpc_call_op *op); -void grpc_call_recv_message( - grpc_call_element *surface_element, grpc_byte_buffer *message, - void (*on_finish)(void *user_data, grpc_op_error error), void *user_data); -void grpc_call_recv_finish(grpc_call_element *surface_element, - int is_full_close); + grpc_mdelem *md); +void grpc_call_recv_message(grpc_call_element *surface_element, + grpc_byte_buffer *message); +void grpc_call_read_closed(grpc_call_element *surface_element); +void grpc_call_stream_closed(grpc_call_element *surface_element); void grpc_call_execute_op(grpc_call *call, grpc_call_op *op); +grpc_call_error grpc_call_start_ioreq_and_call_back( + grpc_call *call, const grpc_ioreq *reqs, size_t nreqs, + grpc_ioreq_completion_func on_complete, void *user_data); -/* Called when it's known that the initial batch of metadata is complete on the - client side (must not be called on the server) */ -void grpc_call_client_initial_metadata_complete( +/* Called when it's known that the initial batch of metadata is complete */ +void grpc_call_initial_metadata_complete( grpc_call_element *surface_element); void grpc_call_set_deadline(grpc_call_element *surface_element, @@ -69,10 +115,4 @@ grpc_call_stack *grpc_call_get_call_stack(grpc_call *call); /* Given the top call_element, get the call object. */ grpc_call *grpc_call_from_top_element(grpc_call_element *surface_element); -/* Get the metadata buffer. */ -grpc_metadata_buffer *grpc_call_get_metadata_buffer(grpc_call *call); - -void grpc_call_add_mdelem(grpc_call *call, grpc_mdelem *mdelem, - gpr_uint32 flags); - #endif /* __GRPC_INTERNAL_SURFACE_CALL_H__ */ diff --git a/src/core/surface/channel.c b/src/core/surface/channel.c index 93a2c0609d8..b33bd7b3575 100644 --- a/src/core/surface/channel.c +++ b/src/core/surface/channel.c @@ -51,7 +51,10 @@ struct grpc_channel { grpc_mdstr *authority_string; }; -#define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack *)((c) + 1)) +#define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack *)((c)+1)) +#define CHANNEL_FROM_CHANNEL_STACK(channel_stack) (((grpc_channel *)(channel_stack)) - 1) +#define CHANNEL_FROM_TOP_ELEM(top_elem) \ + CHANNEL_FROM_CHANNEL_STACK(grpc_channel_stack_from_top_element(top_elem)) grpc_channel *grpc_channel_create_from_filters( const grpc_channel_filter **filters, size_t num_filters, @@ -60,8 +63,8 @@ grpc_channel *grpc_channel_create_from_filters( sizeof(grpc_channel) + grpc_channel_stack_size(filters, num_filters); grpc_channel *channel = gpr_malloc(size); channel->is_client = is_client; - /* decremented by grpc_channel_destroy */ - gpr_ref_init(&channel->refs, 1); + /* decremented by grpc_channel_destroy, and grpc_client_channel_closed if is_client */ + gpr_ref_init(&channel->refs, 1 + is_client); channel->metadata_context = mdctx; channel->grpc_status_string = grpc_mdstr_from_string(mdctx, "grpc-status"); channel->grpc_message_string = grpc_mdstr_from_string(mdctx, "grpc-message"); @@ -80,6 +83,7 @@ grpc_call *grpc_channel_create_call_old(grpc_channel *channel, grpc_call *call; grpc_mdelem *path_mdelem; grpc_mdelem *authority_mdelem; + grpc_call_op op; if (!channel->is_client) { gpr_log(GPR_ERROR, "Cannot create a call on the server."); @@ -91,20 +95,25 @@ grpc_call *grpc_channel_create_call_old(grpc_channel *channel, /* Add :path and :authority headers. */ /* TODO(klempner): Consider optimizing this by stashing mdelems for common values of method and host. */ - grpc_mdstr_ref(channel->path_string); path_mdelem = grpc_mdelem_from_metadata_strings( - channel->metadata_context, channel->path_string, + channel->metadata_context, grpc_mdstr_ref(channel->path_string), grpc_mdstr_from_string(channel->metadata_context, method)); - grpc_call_add_mdelem(call, path_mdelem, 0); + op.type = GRPC_SEND_METADATA; + op.dir = GRPC_CALL_DOWN; + op.flags = 0; + op.data.metadata = path_mdelem; + op.done_cb = do_nothing; + op.user_data = NULL; + grpc_call_execute_op(call, &op); grpc_mdstr_ref(channel->authority_string); authority_mdelem = grpc_mdelem_from_metadata_strings( channel->metadata_context, channel->authority_string, grpc_mdstr_from_string(channel->metadata_context, host)); - grpc_call_add_mdelem(call, authority_mdelem, 0); + op.data.metadata = authority_mdelem; + grpc_call_execute_op(call, &op); if (0 != gpr_time_cmp(absolute_deadline, gpr_inf_future)) { - grpc_call_op op; op.type = GRPC_SEND_DEADLINE; op.dir = GRPC_CALL_DOWN; op.flags = 0; @@ -152,6 +161,10 @@ void grpc_channel_destroy(grpc_channel *channel) { grpc_channel_internal_unref(channel); } +void grpc_client_channel_closed(grpc_channel_element *elem) { + grpc_channel_internal_unref(CHANNEL_FROM_TOP_ELEM(elem)); +} + grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel) { return CHANNEL_STACK_FROM_CHANNEL(channel); } diff --git a/src/core/surface/channel.h b/src/core/surface/channel.h index b3ea2ede403..ff9bbc237ef 100644 --- a/src/core/surface/channel.h +++ b/src/core/surface/channel.h @@ -45,6 +45,8 @@ grpc_mdctx *grpc_channel_get_metadata_context(grpc_channel *channel); grpc_mdstr *grpc_channel_get_status_string(grpc_channel *channel); grpc_mdstr *grpc_channel_get_message_string(grpc_channel *channel); +void grpc_client_channel_closed(grpc_channel_element *elem); + void grpc_channel_internal_ref(grpc_channel *channel); void grpc_channel_internal_unref(grpc_channel *channel); diff --git a/src/core/surface/client.c b/src/core/surface/client.c index a7c9b902ed4..64ee9d51e80 100644 --- a/src/core/surface/client.c +++ b/src/core/surface/client.c @@ -34,6 +34,7 @@ #include "src/core/surface/client.h" #include "src/core/surface/call.h" +#include "src/core/surface/channel.h" #include "src/core/support/string.h" #include #include @@ -56,23 +57,23 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem, grpc_call_next_op(elem, op); break; case GRPC_RECV_METADATA: - grpc_call_recv_metadata(elem, op); + grpc_call_recv_metadata(elem, op->data.metadata); break; case GRPC_RECV_DEADLINE: gpr_log(GPR_ERROR, "Deadline received by client (ignored)"); break; case GRPC_RECV_MESSAGE: - grpc_call_recv_message(elem, op->data.message, op->done_cb, - op->user_data); + grpc_call_recv_message(elem, op->data.message); + op->done_cb(op->user_data, GRPC_OP_OK); break; case GRPC_RECV_HALF_CLOSE: - grpc_call_recv_finish(elem, 0); + grpc_call_read_closed(elem); break; case GRPC_RECV_FINISH: - grpc_call_recv_finish(elem, 1); + grpc_call_stream_closed(elem); break; case GRPC_RECV_END_OF_INITIAL_METADATA: - grpc_call_client_initial_metadata_complete(elem); + grpc_call_initial_metadata_complete(elem); break; default: GPR_ASSERT(op->dir == GRPC_CALL_DOWN); @@ -87,7 +88,7 @@ static void channel_op(grpc_channel_element *elem, gpr_log(GPR_ERROR, "Client cannot accept new calls"); break; case GRPC_TRANSPORT_CLOSED: - gpr_log(GPR_ERROR, "Transport closed"); + grpc_client_channel_closed(elem); break; case GRPC_TRANSPORT_GOAWAY: gpr_slice_unref(op->data.goaway.message); diff --git a/src/core/surface/completion_queue.c b/src/core/surface/completion_queue.c index 2bf31c50a8f..ae3b96035c1 100644 --- a/src/core/surface/completion_queue.c +++ b/src/core/surface/completion_queue.c @@ -173,18 +173,6 @@ void grpc_cq_end_read(grpc_completion_queue *cc, void *tag, grpc_call *call, gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); } -void grpc_cq_end_invoke_accepted(grpc_completion_queue *cc, void *tag, - grpc_call *call, - grpc_event_finish_func on_finish, - void *user_data, grpc_op_error error) { - event *ev; - gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); - ev = add_locked(cc, GRPC_INVOKE_ACCEPTED, tag, call, on_finish, user_data); - ev->base.data.invoke_accepted = error; - end_op_locked(cc, GRPC_INVOKE_ACCEPTED); - gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); -} - void grpc_cq_end_write_accepted(grpc_completion_queue *cc, void *tag, grpc_call *call, grpc_event_finish_func on_finish, @@ -197,6 +185,17 @@ void grpc_cq_end_write_accepted(grpc_completion_queue *cc, void *tag, gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); } +void grpc_cq_end_ioreq(grpc_completion_queue *cc, void *tag, grpc_call *call, + grpc_event_finish_func on_finish, void *user_data, + grpc_op_error error) { + event *ev; + gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); + ev = add_locked(cc, GRPC_IOREQ, tag, call, on_finish, user_data); + ev->base.data.write_accepted = error; + end_op_locked(cc, GRPC_IOREQ); + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); +} + void grpc_cq_end_finish_accepted(grpc_completion_queue *cc, void *tag, grpc_call *call, grpc_event_finish_func on_finish, @@ -389,7 +388,7 @@ void grpc_event_finish(grpc_event *base) { event *ev = (event *)base; ev->on_finish(ev->on_finish_user_data, GRPC_OP_OK); if (ev->base.call) { - grpc_call_internal_unref(ev->base.call); + grpc_call_internal_unref(ev->base.call, 1); } gpr_free(ev); } diff --git a/src/core/surface/completion_queue.h b/src/core/surface/completion_queue.h index 85984075f78..fea8336b633 100644 --- a/src/core/surface/completion_queue.h +++ b/src/core/surface/completion_queue.h @@ -97,6 +97,10 @@ void grpc_cq_end_new_rpc(grpc_completion_queue *cc, void *tag, grpc_call *call, gpr_timespec deadline, size_t metadata_count, grpc_metadata *metadata_elements); +void grpc_cq_end_ioreq(grpc_completion_queue *cc, void *tag, grpc_call *call, + grpc_event_finish_func on_finish, void *user_data, + grpc_op_error error); + void grpc_cq_end_server_shutdown(grpc_completion_queue *cc, void *tag); /* disable polling for some tests */ diff --git a/src/core/surface/event_string.c b/src/core/surface/event_string.c index 8975d312eec..7c76bf93d7c 100644 --- a/src/core/surface/event_string.c +++ b/src/core/surface/event_string.c @@ -87,10 +87,10 @@ char *grpc_event_string(grpc_event *ev) { gpr_strvec_add(&buf, gpr_strdup(" end-of-stream")); } break; - case GRPC_INVOKE_ACCEPTED: - gpr_strvec_add(&buf, gpr_strdup("INVOKE_ACCEPTED: ")); + case GRPC_IOREQ: + gpr_strvec_add(&buf, gpr_strdup("IOREQ: ")); addhdr(&buf, ev); - adderr(&buf, ev->data.invoke_accepted); + adderr(&buf, ev->data.ioreq); break; case GRPC_WRITE_ACCEPTED: gpr_strvec_add(&buf, gpr_strdup("WRITE_ACCEPTED: ")); diff --git a/src/core/surface/lame_client.c b/src/core/surface/lame_client.c index 6098ac78de4..411dbabfd32 100644 --- a/src/core/surface/lame_client.c +++ b/src/core/surface/lame_client.c @@ -50,26 +50,16 @@ typedef struct { grpc_mdelem *message; } channel_data; -static void do_nothing(void *data, grpc_op_error error) {} - static void call_op(grpc_call_element *elem, grpc_call_element *from_elem, grpc_call_op *op) { channel_data *channeld = elem->channel_data; GRPC_CALL_LOG_OP(GPR_INFO, elem, op); switch (op->type) { - case GRPC_SEND_START: { - grpc_call_op set_status_op; - grpc_mdelem_ref(channeld->message); - memset(&set_status_op, 0, sizeof(grpc_call_op)); - set_status_op.dir = GRPC_CALL_UP; - set_status_op.type = GRPC_RECV_METADATA; - set_status_op.done_cb = do_nothing; - set_status_op.data.metadata = channeld->message; - grpc_call_recv_metadata(elem, &set_status_op); - grpc_call_recv_finish(elem, 1); + case GRPC_SEND_START: + grpc_call_recv_metadata(elem, grpc_mdelem_ref(channeld->message)); + grpc_call_stream_closed(elem); break; - } case GRPC_SEND_METADATA: grpc_mdelem_unref(op->data.metadata); break; @@ -86,6 +76,9 @@ static void channel_op(grpc_channel_element *elem, case GRPC_CHANNEL_GOAWAY: gpr_slice_unref(op->data.goaway.message); break; + case GRPC_CHANNEL_DISCONNECT: + grpc_client_channel_closed(elem); + break; default: break; } diff --git a/src/core/surface/server.c b/src/core/surface/server.c index 9e2e4d54780..455bd4337f1 100644 --- a/src/core/surface/server.c +++ b/src/core/surface/server.c @@ -44,6 +44,7 @@ #include "src/core/surface/call.h" #include "src/core/surface/channel.h" #include "src/core/surface/completion_queue.h" +#include "src/core/transport/metadata.h" #include #include #include @@ -63,11 +64,24 @@ typedef struct channel_data channel_data; struct channel_data { grpc_server *server; grpc_channel *channel; + grpc_mdstr *path_key; + grpc_mdstr *authority_key; /* linked list of all channels on a server */ channel_data *next; channel_data *prev; }; +typedef void (*new_call_cb)(grpc_server *server, grpc_completion_queue *cq, + grpc_metadata_array *initial_metadata, + call_data *calld, void *user_data); + +typedef struct { + void *user_data; + grpc_completion_queue *cq; + grpc_metadata_array *initial_metadata; + new_call_cb cb; +} requested_call; + struct grpc_server { size_t channel_filter_count; const grpc_channel_filter **channel_filters; @@ -76,9 +90,9 @@ struct grpc_server { gpr_mu mu; - void **tags; - size_t ntags; - size_t tag_cap; + requested_call *requested_calls; + size_t requested_call_count; + size_t requested_call_capacity; gpr_uint8 shutdown; gpr_uint8 have_shutdown_tag; @@ -107,11 +121,17 @@ typedef enum { ZOMBIED } call_state; +typedef struct legacy_data { grpc_metadata_array *initial_metadata; } legacy_data; + struct call_data { grpc_call *call; call_state state; gpr_timespec deadline; + grpc_mdstr *path; + grpc_mdstr *host; + + legacy_data *legacy; gpr_uint8 included[CALL_LIST_COUNT]; call_link links[CALL_LIST_COUNT]; @@ -179,7 +199,7 @@ static void server_unref(grpc_server *server) { grpc_channel_args_destroy(server->channel_args); gpr_mu_destroy(&server->mu); gpr_free(server->channel_filters); - gpr_free(server->tags); + gpr_free(server->requested_calls); gpr_free(server); } } @@ -210,62 +230,36 @@ static void destroy_channel(channel_data *chand) { grpc_iomgr_add_callback(finish_destroy_channel, chand); } -static void queue_new_rpc(grpc_server *server, call_data *calld, void *tag) { - grpc_call *call = calld->call; - grpc_metadata_buffer *mdbuf = grpc_call_get_metadata_buffer(call); - size_t count = grpc_metadata_buffer_count(mdbuf); - grpc_metadata *elements = grpc_metadata_buffer_extract_elements(mdbuf); - const char *host = NULL; - const char *method = NULL; - size_t i; - - for (i = 0; i < count; i++) { - if (0 == strcmp(elements[i].key, ":authority")) { - host = elements[i].value; - } else if (0 == strcmp(elements[i].key, ":path")) { - method = elements[i].value; - } - } - - grpc_call_internal_ref(call); - grpc_cq_end_new_rpc(server->cq, tag, call, - grpc_metadata_buffer_cleanup_elements, elements, method, - host, calld->deadline, count, elements); -} - static void start_new_rpc(grpc_call_element *elem) { channel_data *chand = elem->channel_data; call_data *calld = elem->call_data; grpc_server *server = chand->server; gpr_mu_lock(&server->mu); - if (server->ntags) { + if (server->requested_call_count > 0) { + requested_call rc = server->requested_calls[--server->requested_call_count]; calld->state = ACTIVATED; - queue_new_rpc(server, calld, server->tags[--server->ntags]); + gpr_mu_unlock(&server->mu); + rc.cb(server, rc.cq, rc.initial_metadata, calld, rc.user_data); } else { calld->state = PENDING; call_list_join(server, calld, PENDING_START); + gpr_mu_unlock(&server->mu); } - gpr_mu_unlock(&server->mu); } static void kill_zombie(void *elem, int success) { grpc_call_destroy(grpc_call_from_top_element(elem)); } -static void finish_rpc(grpc_call_element *elem, int is_full_close) { +static void stream_closed(grpc_call_element *elem) { call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; gpr_mu_lock(&chand->server->mu); switch (calld->state) { case ACTIVATED: - grpc_call_recv_finish(elem, is_full_close); break; case PENDING: - if (!is_full_close) { - grpc_call_recv_finish(elem, is_full_close); - break; - } call_list_remove(chand->server, calld, PENDING_START); /* fallthrough intended */ case NOT_STARTED: @@ -276,27 +270,60 @@ static void finish_rpc(grpc_call_element *elem, int is_full_close) { break; } gpr_mu_unlock(&chand->server->mu); + grpc_call_stream_closed(elem); +} + +static void read_closed(grpc_call_element *elem) { + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + gpr_mu_lock(&chand->server->mu); + switch (calld->state) { + case ACTIVATED: + case PENDING: + grpc_call_read_closed(elem); + break; + case NOT_STARTED: + calld->state = ZOMBIED; + grpc_iomgr_add_callback(kill_zombie, elem); + break; + case ZOMBIED: + break; + } + gpr_mu_unlock(&chand->server->mu); } static void call_op(grpc_call_element *elem, grpc_call_element *from_elemn, grpc_call_op *op) { + channel_data *chand = elem->channel_data; + call_data *calld = elem->call_data; + grpc_mdelem *md; GRPC_CALL_LOG_OP(GPR_INFO, elem, op); switch (op->type) { case GRPC_RECV_METADATA: - grpc_call_recv_metadata(elem, op); + md = op->data.metadata; + if (md->key == chand->path_key) { + calld->path = grpc_mdstr_ref(md->value); + grpc_mdelem_unref(md); + } else if (md->key == chand->authority_key) { + calld->host = grpc_mdstr_ref(md->value); + grpc_mdelem_unref(md); + } else { + grpc_call_recv_metadata(elem, md); + } break; case GRPC_RECV_END_OF_INITIAL_METADATA: start_new_rpc(elem); + grpc_call_initial_metadata_complete(elem); break; case GRPC_RECV_MESSAGE: - grpc_call_recv_message(elem, op->data.message, op->done_cb, - op->user_data); + grpc_call_recv_message(elem, op->data.message); + op->done_cb(op->user_data, GRPC_OP_OK); break; case GRPC_RECV_HALF_CLOSE: - finish_rpc(elem, 0); + read_closed(elem); break; case GRPC_RECV_FINISH: - finish_rpc(elem, 1); + stream_closed(elem); break; case GRPC_RECV_DEADLINE: grpc_call_set_deadline(elem, op->data.deadline); @@ -371,6 +398,7 @@ static void init_call_elem(grpc_call_element *elem, static void destroy_call_elem(grpc_call_element *elem) { channel_data *chand = elem->channel_data; + call_data *calld = elem->call_data; int i; gpr_mu_lock(&chand->server->mu); @@ -383,6 +411,19 @@ static void destroy_call_elem(grpc_call_element *elem) { } gpr_mu_unlock(&chand->server->mu); + if (calld->host) { + grpc_mdstr_unref(calld->host); + } + if (calld->path) { + grpc_mdstr_unref(calld->path); + } + + if (calld->legacy) { + gpr_free(calld->legacy->initial_metadata->metadata); + gpr_free(calld->legacy->initial_metadata); + gpr_free(calld->legacy); + } + server_unref(chand->server); } @@ -395,6 +436,8 @@ static void init_channel_elem(grpc_channel_element *elem, GPR_ASSERT(!is_last); chand->server = NULL; chand->channel = NULL; + chand->path_key = grpc_mdstr_from_string(metadata_context, ":path"); + chand->authority_key = grpc_mdstr_from_string(metadata_context, ":authority"); chand->next = chand->prev = chand; } @@ -406,6 +449,8 @@ static void destroy_channel_elem(grpc_channel_element *elem) { chand->prev->next = chand->next; chand->next = chand->prev = chand; gpr_mu_unlock(&chand->server->mu); + grpc_mdstr_unref(chand->path_key); + grpc_mdstr_unref(chand->authority_key); server_unref(chand->server); } } @@ -413,17 +458,8 @@ static void destroy_channel_elem(grpc_channel_element *elem) { static const grpc_channel_filter server_surface_filter = { call_op, channel_op, sizeof(call_data), init_call_elem, destroy_call_elem, sizeof(channel_data), - init_channel_elem, destroy_channel_elem, "server", }; - -static void early_terminate_requested_calls(grpc_completion_queue *cq, - void **tags, size_t ntags) { - size_t i; - - for (i = 0; i < ntags; i++) { - grpc_cq_end_new_rpc(cq, tags[i], NULL, do_nothing, NULL, NULL, NULL, - gpr_inf_past, 0, NULL); - } -} + init_channel_elem, destroy_channel_elem, "server", +}; grpc_server *grpc_server_create_from_filters(grpc_completion_queue *cq, grpc_channel_filter **filters, @@ -517,8 +553,8 @@ grpc_transport_setup_result grpc_server_setup_transport( void shutdown_internal(grpc_server *server, gpr_uint8 have_shutdown_tag, void *shutdown_tag) { listener *l; - void **tags; - size_t ntags; + requested_call *requested_calls; + size_t requested_call_count; channel_data **channels; channel_data *c; size_t nchannels; @@ -547,10 +583,10 @@ void shutdown_internal(grpc_server *server, gpr_uint8 have_shutdown_tag, i++; } - tags = server->tags; - ntags = server->ntags; - server->tags = NULL; - server->ntags = 0; + requested_calls = server->requested_calls; + requested_call_count = server->requested_call_count; + server->requested_calls = NULL; + server->requested_call_count = 0; server->shutdown = 1; server->have_shutdown_tag = have_shutdown_tag; @@ -579,8 +615,12 @@ void shutdown_internal(grpc_server *server, gpr_uint8 have_shutdown_tag, gpr_free(channels); /* terminate all the requested calls */ - early_terminate_requested_calls(server->cq, tags, ntags); - gpr_free(tags); + for (i = 0; i < requested_call_count; i++) { + requested_calls[i].cb(server, requested_calls[i].cq, + requested_calls[i].initial_metadata, NULL, + requested_calls[i].user_data); + } + gpr_free(requested_calls); /* Shutdown listeners */ for (l = server->listeners; l; l = l->next) { @@ -625,36 +665,105 @@ void grpc_server_add_listener(grpc_server *server, void *arg, server->listeners = l; } -grpc_call_error grpc_server_request_call_old(grpc_server *server, - void *tag_new) { +static grpc_call_error queue_call_request(grpc_server *server, + grpc_completion_queue *cq, + grpc_metadata_array *initial_metadata, + new_call_cb cb, void *user_data) { call_data *calld; - - grpc_cq_begin_op(server->cq, NULL, GRPC_SERVER_RPC_NEW); - + requested_call *rc; gpr_mu_lock(&server->mu); - if (server->shutdown) { gpr_mu_unlock(&server->mu); - early_terminate_requested_calls(server->cq, &tag_new, 1); + cb(server, cq, initial_metadata, NULL, user_data); return GRPC_CALL_OK; } - calld = call_list_remove_head(server, PENDING_START); if (calld) { GPR_ASSERT(calld->state == PENDING); calld->state = ACTIVATED; - queue_new_rpc(server, calld, tag_new); + gpr_mu_unlock(&server->mu); + cb(server, cq, initial_metadata, calld, user_data); + return GRPC_CALL_OK; } else { - if (server->tag_cap == server->ntags) { - server->tag_cap = GPR_MAX(3 * server->tag_cap / 2, server->tag_cap + 1); - server->tags = - gpr_realloc(server->tags, sizeof(void *) * server->tag_cap); + if (server->requested_call_count == server->requested_call_capacity) { + server->requested_call_capacity = + GPR_MAX(server->requested_call_capacity + 8, + server->requested_call_capacity * 2); + server->requested_calls = + gpr_realloc(server->requested_calls, + sizeof(requested_call) * server->requested_call_capacity); } - server->tags[server->ntags++] = tag_new; + rc = &server->requested_calls[server->requested_call_count++]; + rc->cb = cb; + rc->cq = cq; + rc->user_data = user_data; + rc->initial_metadata = initial_metadata; + gpr_mu_unlock(&server->mu); + return GRPC_CALL_OK; } - gpr_mu_unlock(&server->mu); +} + +static void begin_request(grpc_server *server, grpc_completion_queue *cq, + grpc_metadata_array *initial_metadata, + call_data *call_data, void *tag) { + abort(); +} - return GRPC_CALL_OK; +grpc_call_error grpc_server_request_call( + grpc_server *server, grpc_call_details *details, + grpc_metadata_array *initial_metadata, grpc_completion_queue *cq, + void *tag) { + grpc_cq_begin_op(cq, NULL, GRPC_IOREQ); + return queue_call_request(server, cq, initial_metadata, begin_request, tag); +} + +static void publish_legacy_request(grpc_call *call, grpc_op_error status, + void *tag) { + grpc_call_element *elem = + grpc_call_stack_element(grpc_call_get_call_stack(call), 0); + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + grpc_server *server = chand->server; + + if (status == GRPC_OP_OK) { + grpc_cq_end_new_rpc(server->cq, tag, call, do_nothing, NULL, + grpc_mdstr_as_c_string(calld->path), + grpc_mdstr_as_c_string(calld->host), calld->deadline, + calld->legacy->initial_metadata->count, + calld->legacy->initial_metadata->metadata); + } else { + abort(); + } +} + +static void begin_legacy_request(grpc_server *server, grpc_completion_queue *cq, + grpc_metadata_array *initial_metadata, + call_data *calld, void *tag) { + grpc_ioreq req; + if (!calld) { + gpr_free(initial_metadata); + grpc_cq_end_new_rpc(cq, tag, NULL, do_nothing, NULL, NULL, NULL, + gpr_inf_past, 0, NULL); + return; + } + req.op = GRPC_IOREQ_RECV_INITIAL_METADATA; + req.data.recv_metadata = initial_metadata; + calld->legacy = gpr_malloc(sizeof(legacy_data)); + memset(calld->legacy, 0, sizeof(legacy_data)); + calld->legacy->initial_metadata = initial_metadata; + grpc_call_internal_ref(calld->call); + grpc_call_start_ioreq_and_call_back(calld->call, &req, 1, + publish_legacy_request, tag); +} + +grpc_call_error grpc_server_request_call_old(grpc_server *server, + void *tag_new) { + grpc_metadata_array *client_metadata = + gpr_malloc(sizeof(grpc_metadata_array)); + memset(client_metadata, 0, sizeof(*client_metadata)); + grpc_cq_begin_op(server->cq, NULL, GRPC_SERVER_RPC_NEW); + return queue_call_request(server, server->cq, client_metadata, + begin_legacy_request, tag_new); } const grpc_channel_args *grpc_server_get_channel_args(grpc_server *server) { diff --git a/src/core/transport/chttp2/stream_encoder.c b/src/core/transport/chttp2/stream_encoder.c index c4e3ca516d1..2af18c30358 100644 --- a/src/core/transport/chttp2/stream_encoder.c +++ b/src/core/transport/chttp2/stream_encoder.c @@ -432,7 +432,7 @@ static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem, static void deadline_enc(grpc_chttp2_hpack_compressor *c, gpr_timespec deadline, framer_state *st) { - char timeout_str[32]; + char timeout_str[GRPC_CHTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE]; grpc_chttp2_encode_timeout(gpr_time_sub(deadline, gpr_now()), timeout_str); hpack_enc(c, grpc_mdelem_from_metadata_strings( c->mdctx, grpc_mdstr_ref(c->timeout_key_str), diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c index 48a10058331..ea579cf4a52 100644 --- a/src/core/transport/chttp2_transport.c +++ b/src/core/transport/chttp2_transport.c @@ -184,7 +184,6 @@ struct transport { gpr_uint8 is_client; gpr_mu mu; - gpr_cv cv; /* basic state management - what are we doing at the moment? */ gpr_uint8 reading; @@ -328,6 +327,9 @@ static void maybe_start_some_streams(transport *t); static void become_skip_parser(transport *t); +static void recv_data(void *tp, gpr_slice *slices, size_t nslices, + grpc_endpoint_cb_status error); + /* * CONSTRUCTION/DESTRUCTION/REFCOUNTING */ @@ -382,8 +384,8 @@ static void ref_transport(transport *t) { gpr_ref(&t->refs); } static void init_transport(transport *t, grpc_transport_setup_callback setup, void *arg, const grpc_channel_args *channel_args, - grpc_endpoint *ep, grpc_mdctx *mdctx, - int is_client) { + grpc_endpoint *ep, gpr_slice *slices, size_t nslices, + grpc_mdctx *mdctx, int is_client) { size_t i; int j; grpc_transport_setup_result sr; @@ -395,7 +397,6 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup, /* one ref is for destroy, the other for when ep becomes NULL */ gpr_ref_init(&t->refs, 2); gpr_mu_init(&t->mu); - gpr_cv_init(&t->cv); t->metadata_context = mdctx; t->str_grpc_timeout = grpc_mdstr_from_string(t->metadata_context, "grpc-timeout"); @@ -422,6 +423,7 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup, gpr_slice_buffer_init(&t->outbuf); gpr_slice_buffer_init(&t->qbuf); grpc_sopb_init(&t->nuke_later_sopb); + grpc_chttp2_hpack_parser_init(&t->hpack_parser, t->metadata_context); if (is_client) { gpr_slice_buffer_add(&t->qbuf, gpr_slice_from_copied_string(CLIENT_CONNECT_STRING)); @@ -476,14 +478,15 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup, ref_transport(t); gpr_mu_unlock(&t->mu); + ref_transport(t); + recv_data(t, slices, nslices, GRPC_ENDPOINT_CB_OK); + sr = setup(arg, &t->base, t->metadata_context); lock(t); t->cb = sr.callbacks; t->cb_user_data = sr.user_data; - grpc_chttp2_hpack_parser_init(&t->hpack_parser, t->metadata_context); t->calling_back = 0; - gpr_cv_broadcast(&t->cv); unlock(t); unref_transport(t); } @@ -492,9 +495,6 @@ static void destroy_transport(grpc_transport *gt) { transport *t = (transport *)gt; gpr_mu_lock(&t->mu); - while (t->calling_back) { - gpr_cv_wait(&t->cv, &t->mu, gpr_inf_future); - } t->cb = NULL; gpr_mu_unlock(&t->mu); @@ -573,13 +573,6 @@ static void destroy_stream(grpc_transport *gt, grpc_stream *gs) { gpr_mu_lock(&t->mu); - /* await pending callbacks - TODO(ctiller): this could be optimized to check if this stream is getting - callbacks */ - while (t->calling_back) { - gpr_cv_wait(&t->cv, &t->mu, gpr_inf_future); - } - /* stop parsing if we're currently parsing this stream */ if (t->deframe_state == DTS_FRAME && t->incoming_stream_id == s->id && s->id != 0) { @@ -591,7 +584,6 @@ static void destroy_stream(grpc_transport *gt, grpc_stream *gs) { } remove_from_stream_map(t, s); - gpr_cv_broadcast(&t->cv); gpr_mu_unlock(&t->mu); grpc_sopb_destroy(&s->outgoing_sopb); @@ -761,7 +753,6 @@ static void unlock(transport *t) { if (perform_callbacks || call_closed || num_goaways) { lock(t); t->calling_back = 0; - gpr_cv_broadcast(&t->cv); unlock(t); unref_transport(t); } @@ -892,7 +883,6 @@ static void finish_write_common(transport *t, int success) { if (!t->reading) { grpc_endpoint_destroy(t->ep); t->ep = NULL; - gpr_cv_broadcast(&t->cv); unref_transport(t); /* safe because we'll still have the ref for write */ } unlock(t); @@ -957,7 +947,7 @@ static void send_batch(grpc_transport *gt, grpc_stream *gs, grpc_stream_op *ops, stream_list_join(t, s, WRITABLE); } } else { - grpc_stream_ops_unref_owned_objects(ops, ops_count); + grpc_sopb_append(&t->nuke_later_sopb, ops, ops_count); } if (is_last && s->outgoing_sopb.nops == 0 && s->read_closed) { stream_list_join(t, s, PENDING_CALLBACKS); @@ -1673,7 +1663,6 @@ static void recv_data(void *tp, gpr_slice *slices, size_t nslices, if (!t->writing && t->ep) { grpc_endpoint_destroy(t->ep); t->ep = NULL; - gpr_cv_broadcast(&t->cv); unref_transport(t); /* safe as we still have a ref for read */ } unlock(t); @@ -1769,7 +1758,6 @@ void grpc_create_chttp2_transport(grpc_transport_setup_callback setup, size_t nslices, grpc_mdctx *mdctx, int is_client) { transport *t = gpr_malloc(sizeof(transport)); - init_transport(t, setup, arg, channel_args, ep, mdctx, is_client); - ref_transport(t); - recv_data(t, slices, nslices, GRPC_ENDPOINT_CB_OK); + init_transport(t, setup, arg, channel_args, ep, slices, nslices, mdctx, + is_client); } diff --git a/src/cpp/client/channel.cc b/src/cpp/client/channel.cc index 5822e3015fc..3f39364bda2 100644 --- a/src/cpp/client/channel.cc +++ b/src/cpp/client/channel.cc @@ -102,6 +102,7 @@ Status Channel::StartBlockingRpc(const RpcMethod &method, grpc_call *call = grpc_channel_create_call_old( c_channel_, method.name(), target_.c_str(), context->RawDeadline()); context->set_call(call); + grpc_event *ev; void *finished_tag = reinterpret_cast(call); void *metadata_read_tag = reinterpret_cast(call) + 2; diff --git a/src/csharp/Grpc.sln b/src/csharp/Grpc.sln new file mode 100644 index 00000000000..5890617acf5 --- /dev/null +++ b/src/csharp/Grpc.sln @@ -0,0 +1,38 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcDemo", "GrpcDemo\GrpcDemo.csproj", "{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcApi", "GrpcApi\GrpcApi.csproj", "{7DC1433E-3225-42C7-B7EA-546D56E27A4B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcCore", "GrpcCore\GrpcCore.csproj", "{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcCoreTests", "GrpcCoreTests\GrpcCoreTests.csproj", "{86EC5CB4-4EA2-40A2-8057-86542A0353BB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.ActiveCfg = Debug|x86 + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.Build.0 = Debug|x86 + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.ActiveCfg = Release|x86 + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.Build.0 = Release|x86 + {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|x86.ActiveCfg = Debug|Any CPU + {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|x86.Build.0 = Debug|Any CPU + {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|x86.ActiveCfg = Release|Any CPU + {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|x86.Build.0 = Release|Any CPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.ActiveCfg = Debug|Any CPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.Build.0 = Debug|Any CPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.ActiveCfg = Release|Any CPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.Build.0 = Release|Any CPU + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|x86.ActiveCfg = Debug|Any CPU + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|x86.Build.0 = Debug|Any CPU + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|x86.ActiveCfg = Release|Any CPU + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = GrpcDemo\GrpcDemo.csproj + EndGlobalSection +EndGlobal diff --git a/src/csharp/GrpcApi/.gitignore b/src/csharp/GrpcApi/.gitignore new file mode 100644 index 00000000000..2cc8cca52d0 --- /dev/null +++ b/src/csharp/GrpcApi/.gitignore @@ -0,0 +1,2 @@ +test-results +bin diff --git a/src/csharp/GrpcApi/DummyMathServiceClient.cs b/src/csharp/GrpcApi/DummyMathServiceClient.cs new file mode 100644 index 00000000000..6799109be42 --- /dev/null +++ b/src/csharp/GrpcApi/DummyMathServiceClient.cs @@ -0,0 +1,74 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Reactive.Linq; + +namespace math +{ +// /// +// /// Dummy local implementation of math service. +// /// +// public class DummyMathServiceClient : IMathServiceClient +// { +// public DivReply Div(DivArgs args, CancellationToken token = default(CancellationToken)) +// { +// // TODO: cancellation... +// return DivInternal(args); +// } +// +// public Task DivAsync(DivArgs args, CancellationToken token = default(CancellationToken)) +// { +// return Task.Factory.StartNew(() => DivInternal(args), token); +// } +// +// public IObservable Fib(FibArgs args, CancellationToken token = default(CancellationToken)) +// { +// if (args.Limit > 0) +// { +// // TODO: cancellation +// return FibInternal(args.Limit).ToObservable(); +// } +// +// throw new NotImplementedException("Not implemented yet"); +// } +// +// public Task Sum(IObservable inputs, CancellationToken token = default(CancellationToken)) +// { +// // TODO: implement +// inputs = null; +// return Task.Factory.StartNew(() => Num.CreateBuilder().Build(), token); +// } +// +// public IObservable DivMany(IObservable inputs, CancellationToken token = default(CancellationToken)) +// { +// // TODO: implement +// inputs = null; +// return new List { }.ToObservable (); +// } +// +// +// DivReply DivInternal(DivArgs args) +// { +// long quotient = args.Dividend / args.Divisor; +// long remainder = args.Dividend % args.Divisor; +// return new DivReply.Builder{ Quotient = quotient, Remainder = remainder }.Build(); +// } +// +// IEnumerable FibInternal(long n) +// { +// long a = 0; +// yield return new Num.Builder{Num_=a}.Build(); +// +// long b = 1; +// for (long i = 0; i < n - 1; i++) +// { +// long temp = a; +// a = b; +// b = temp + b; +// yield return new Num.Builder{Num_=a}.Build(); +// } +// } +// } +} + diff --git a/src/csharp/GrpcApi/Examples.cs b/src/csharp/GrpcApi/Examples.cs new file mode 100644 index 00000000000..d45b702708e --- /dev/null +++ b/src/csharp/GrpcApi/Examples.cs @@ -0,0 +1,97 @@ +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Reactive.Linq; + +namespace math +{ + public class Examples + { + public static void DivExample(IMathServiceClient stub) + { + DivReply result = stub.Div(new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build()); + Console.WriteLine("Div Result: " + result); + } + + public static void DivAsyncExample(IMathServiceClient stub) + { + Task call = stub.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build()); + DivReply result = call.Result; + Console.WriteLine(result); + } + + public static void DivAsyncWithCancellationExample(IMathServiceClient stub) + { + Task call = stub.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build()); + DivReply result = call.Result; + Console.WriteLine(result); + } + + public static void FibExample(IMathServiceClient stub) + { + var recorder = new RecordingObserver(); + stub.Fib(new FibArgs.Builder { Limit = 5 }.Build(), recorder); + + List numbers = recorder.ToList().Result; + Console.WriteLine("Fib Result: " + string.Join("|", recorder.ToList().Result)); + } + + public static void SumExample(IMathServiceClient stub) + { + List numbers = new List{new Num.Builder { Num_ = 1 }.Build(), + new Num.Builder { Num_ = 2 }.Build(), + new Num.Builder { Num_ = 3 }.Build()}; + + var res = stub.Sum(); + foreach (var num in numbers) { + res.Inputs.OnNext(num); + } + res.Inputs.OnCompleted(); + + Console.WriteLine("Sum Result: " + res.Task.Result); + } + + public static void DivManyExample(IMathServiceClient stub) + { + List divArgsList = new List{ + new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build(), + new DivArgs.Builder { Dividend = 100, Divisor = 21 }.Build(), + new DivArgs.Builder { Dividend = 7, Divisor = 2 }.Build() + }; + + var recorder = new RecordingObserver(); + + var inputs = stub.DivMany(recorder); + foreach (var input in divArgsList) + { + inputs.OnNext(input); + } + inputs.OnCompleted(); + + Console.WriteLine("DivMany Result: " + string.Join("|", recorder.ToList().Result)); + } + + public static void DependendRequestsExample(IMathServiceClient stub) + { + var numberList = new List + { new Num.Builder{ Num_ = 1 }.Build(), + new Num.Builder{ Num_ = 2 }.Build(), new Num.Builder{ Num_ = 3 }.Build() + }; + + numberList.ToObservable(); + + //IObserver numbers; + //Task call = stub.Sum(out numbers); + //foreach (var num in numberList) + //{ + // numbers.OnNext(num); + //} + //numbers.OnCompleted(); + + //Num sum = call.Result; + + //DivReply result = stub.Div(new DivArgs.Builder { Dividend = sum.Num_, Divisor = numberList.Count }.Build()); + } + } +} + diff --git a/src/csharp/GrpcApi/GrpcApi.csproj b/src/csharp/GrpcApi/GrpcApi.csproj new file mode 100644 index 00000000000..d0377828b54 --- /dev/null +++ b/src/csharp/GrpcApi/GrpcApi.csproj @@ -0,0 +1,64 @@ + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {7DC1433E-3225-42C7-B7EA-546D56E27A4B} + Library + GrpcApi + GrpcApi + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + + + full + true + bin\Release + prompt + 4 + false + + + + + False + + + + False + + + False + + + ..\lib\Google.ProtocolBuffers.dll + + + + + + + + + + + + + + + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7} + GrpcCore + + + \ No newline at end of file diff --git a/src/csharp/GrpcApi/IMathServiceClient.cs b/src/csharp/GrpcApi/IMathServiceClient.cs new file mode 100644 index 00000000000..51385a328f4 --- /dev/null +++ b/src/csharp/GrpcApi/IMathServiceClient.cs @@ -0,0 +1,26 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Reactive.Linq; +using Google.GRPC.Core; + +namespace math +{ + /// + /// Hand-written stub for MathService defined in math.proto. + /// This code will be generated by gRPC codegen in the future. + /// + public interface IMathServiceClient + { + DivReply Div(DivArgs args, CancellationToken token = default(CancellationToken)); + + Task DivAsync(DivArgs args, CancellationToken token = default(CancellationToken)); + + Task Fib(FibArgs args, IObserver outputs, CancellationToken token = default(CancellationToken)); + + ClientStreamingAsyncResult Sum(CancellationToken token = default(CancellationToken)); + + IObserver DivMany(IObserver outputs, CancellationToken token = default(CancellationToken)); + } +} \ No newline at end of file diff --git a/src/csharp/GrpcApi/Math.cs b/src/csharp/GrpcApi/Math.cs new file mode 100644 index 00000000000..2d700337ac7 --- /dev/null +++ b/src/csharp/GrpcApi/Math.cs @@ -0,0 +1,1531 @@ +// Generated by ProtoGen, Version=2.4.1.521, Culture=neutral, PublicKeyToken=17b3b1f090c3ea48. DO NOT EDIT! +#pragma warning disable 1591, 0612, 3021 +#region Designer generated code + +using pb = global::Google.ProtocolBuffers; +using pbc = global::Google.ProtocolBuffers.Collections; +using pbd = global::Google.ProtocolBuffers.Descriptors; +using scg = global::System.Collections.Generic; +namespace math { + + namespace Proto { + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public static partial class Math { + + #region Extension registration + public static void RegisterAllExtensions(pb::ExtensionRegistry registry) { + } + #endregion + #region Static variables + internal static pbd::MessageDescriptor internal__static_math_DivArgs__Descriptor; + internal static pb::FieldAccess.FieldAccessorTable internal__static_math_DivArgs__FieldAccessorTable; + internal static pbd::MessageDescriptor internal__static_math_DivReply__Descriptor; + internal static pb::FieldAccess.FieldAccessorTable internal__static_math_DivReply__FieldAccessorTable; + internal static pbd::MessageDescriptor internal__static_math_FibArgs__Descriptor; + internal static pb::FieldAccess.FieldAccessorTable internal__static_math_FibArgs__FieldAccessorTable; + internal static pbd::MessageDescriptor internal__static_math_Num__Descriptor; + internal static pb::FieldAccess.FieldAccessorTable internal__static_math_Num__FieldAccessorTable; + internal static pbd::MessageDescriptor internal__static_math_FibReply__Descriptor; + internal static pb::FieldAccess.FieldAccessorTable internal__static_math_FibReply__FieldAccessorTable; + #endregion + #region Descriptor + public static pbd::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbd::FileDescriptor descriptor; + + static Math() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "CgptYXRoLnByb3RvEgRtYXRoIiwKB0RpdkFyZ3MSEAoIZGl2aWRlbmQYASAB", + "KAMSDwoHZGl2aXNvchgCIAEoAyIvCghEaXZSZXBseRIQCghxdW90aWVudBgB", + "IAEoAxIRCglyZW1haW5kZXIYAiABKAMiGAoHRmliQXJncxINCgVsaW1pdBgB", + "IAEoAyISCgNOdW0SCwoDbnVtGAEgASgDIhkKCEZpYlJlcGx5Eg0KBWNvdW50", + "GAEgASgDMqQBCgRNYXRoEiYKA0RpdhINLm1hdGguRGl2QXJncxoOLm1hdGgu", + "RGl2UmVwbHkiABIuCgdEaXZNYW55Eg0ubWF0aC5EaXZBcmdzGg4ubWF0aC5E", + "aXZSZXBseSIAKAEwARIjCgNGaWISDS5tYXRoLkZpYkFyZ3MaCS5tYXRoLk51", + "bSIAMAESHwoDU3VtEgkubWF0aC5OdW0aCS5tYXRoLk51bSIAKAE=")); + pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) { + descriptor = root; + internal__static_math_DivArgs__Descriptor = Descriptor.MessageTypes[0]; + internal__static_math_DivArgs__FieldAccessorTable = + new pb::FieldAccess.FieldAccessorTable(internal__static_math_DivArgs__Descriptor, + new string[] { "Dividend", "Divisor", }); + internal__static_math_DivReply__Descriptor = Descriptor.MessageTypes[1]; + internal__static_math_DivReply__FieldAccessorTable = + new pb::FieldAccess.FieldAccessorTable(internal__static_math_DivReply__Descriptor, + new string[] { "Quotient", "Remainder", }); + internal__static_math_FibArgs__Descriptor = Descriptor.MessageTypes[2]; + internal__static_math_FibArgs__FieldAccessorTable = + new pb::FieldAccess.FieldAccessorTable(internal__static_math_FibArgs__Descriptor, + new string[] { "Limit", }); + internal__static_math_Num__Descriptor = Descriptor.MessageTypes[3]; + internal__static_math_Num__FieldAccessorTable = + new pb::FieldAccess.FieldAccessorTable(internal__static_math_Num__Descriptor, + new string[] { "Num_", }); + internal__static_math_FibReply__Descriptor = Descriptor.MessageTypes[4]; + internal__static_math_FibReply__FieldAccessorTable = + new pb::FieldAccess.FieldAccessorTable(internal__static_math_FibReply__Descriptor, + new string[] { "Count", }); + pb::ExtensionRegistry registry = pb::ExtensionRegistry.CreateInstance(); + RegisterAllExtensions(registry); + return registry; + }; + pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData, + new pbd::FileDescriptor[] { + }, assigner); + } + #endregion + + } + } + #region Messages + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class DivArgs : pb::GeneratedMessage { + private DivArgs() { } + private static readonly DivArgs defaultInstance = new DivArgs().MakeReadOnly(); + private static readonly string[] _divArgsFieldNames = new string[] { "dividend", "divisor" }; + private static readonly uint[] _divArgsFieldTags = new uint[] { 8, 16 }; + public static DivArgs DefaultInstance { + get { return defaultInstance; } + } + + public override DivArgs DefaultInstanceForType { + get { return DefaultInstance; } + } + + protected override DivArgs ThisMessage { + get { return this; } + } + + public static pbd::MessageDescriptor Descriptor { + get { return global::math.Proto.Math.internal__static_math_DivArgs__Descriptor; } + } + + protected override pb::FieldAccess.FieldAccessorTable InternalFieldAccessors { + get { return global::math.Proto.Math.internal__static_math_DivArgs__FieldAccessorTable; } + } + + public const int DividendFieldNumber = 1; + private bool hasDividend; + private long dividend_; + public bool HasDividend { + get { return hasDividend; } + } + public long Dividend { + get { return dividend_; } + } + + public const int DivisorFieldNumber = 2; + private bool hasDivisor; + private long divisor_; + public bool HasDivisor { + get { return hasDivisor; } + } + public long Divisor { + get { return divisor_; } + } + + public override bool IsInitialized { + get { + return true; + } + } + + public override void WriteTo(pb::ICodedOutputStream output) { + int size = SerializedSize; + string[] field_names = _divArgsFieldNames; + if (hasDividend) { + output.WriteInt64(1, field_names[0], Dividend); + } + if (hasDivisor) { + output.WriteInt64(2, field_names[1], Divisor); + } + UnknownFields.WriteTo(output); + } + + private int memoizedSerializedSize = -1; + public override int SerializedSize { + get { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasDividend) { + size += pb::CodedOutputStream.ComputeInt64Size(1, Dividend); + } + if (hasDivisor) { + size += pb::CodedOutputStream.ComputeInt64Size(2, Divisor); + } + size += UnknownFields.SerializedSize; + memoizedSerializedSize = size; + return size; + } + } + + public static DivArgs ParseFrom(pb::ByteString data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static DivArgs ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static DivArgs ParseFrom(byte[] data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static DivArgs ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static DivArgs ParseFrom(global::System.IO.Stream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static DivArgs ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + public static DivArgs ParseDelimitedFrom(global::System.IO.Stream input) { + return CreateBuilder().MergeDelimitedFrom(input).BuildParsed(); + } + public static DivArgs ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed(); + } + public static DivArgs ParseFrom(pb::ICodedInputStream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static DivArgs ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + private DivArgs MakeReadOnly() { + return this; + } + + public static Builder CreateBuilder() { return new Builder(); } + public override Builder ToBuilder() { return CreateBuilder(this); } + public override Builder CreateBuilderForType() { return new Builder(); } + public static Builder CreateBuilder(DivArgs prototype) { + return new Builder(prototype); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Builder : pb::GeneratedBuilder { + protected override Builder ThisBuilder { + get { return this; } + } + public Builder() { + result = DefaultInstance; + resultIsReadOnly = true; + } + internal Builder(DivArgs cloneFrom) { + result = cloneFrom; + resultIsReadOnly = true; + } + + private bool resultIsReadOnly; + private DivArgs result; + + private DivArgs PrepareBuilder() { + if (resultIsReadOnly) { + DivArgs original = result; + result = new DivArgs(); + resultIsReadOnly = false; + MergeFrom(original); + } + return result; + } + + public override bool IsInitialized { + get { return result.IsInitialized; } + } + + protected override DivArgs MessageBeingBuilt { + get { return PrepareBuilder(); } + } + + public override Builder Clear() { + result = DefaultInstance; + resultIsReadOnly = true; + return this; + } + + public override Builder Clone() { + if (resultIsReadOnly) { + return new Builder(result); + } else { + return new Builder().MergeFrom(result); + } + } + + public override pbd::MessageDescriptor DescriptorForType { + get { return global::math.DivArgs.Descriptor; } + } + + public override DivArgs DefaultInstanceForType { + get { return global::math.DivArgs.DefaultInstance; } + } + + public override DivArgs BuildPartial() { + if (resultIsReadOnly) { + return result; + } + resultIsReadOnly = true; + return result.MakeReadOnly(); + } + + public override Builder MergeFrom(pb::IMessage other) { + if (other is DivArgs) { + return MergeFrom((DivArgs) other); + } else { + base.MergeFrom(other); + return this; + } + } + + public override Builder MergeFrom(DivArgs other) { + if (other == global::math.DivArgs.DefaultInstance) return this; + PrepareBuilder(); + if (other.HasDividend) { + Dividend = other.Dividend; + } + if (other.HasDivisor) { + Divisor = other.Divisor; + } + this.MergeUnknownFields(other.UnknownFields); + return this; + } + + public override Builder MergeFrom(pb::ICodedInputStream input) { + return MergeFrom(input, pb::ExtensionRegistry.Empty); + } + + public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + PrepareBuilder(); + pb::UnknownFieldSet.Builder unknownFields = null; + uint tag; + string field_name; + while (input.ReadTag(out tag, out field_name)) { + if(tag == 0 && field_name != null) { + int field_ordinal = global::System.Array.BinarySearch(_divArgsFieldNames, field_name, global::System.StringComparer.Ordinal); + if(field_ordinal >= 0) + tag = _divArgsFieldTags[field_ordinal]; + else { + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + continue; + } + } + switch (tag) { + case 0: { + throw pb::InvalidProtocolBufferException.InvalidTag(); + } + default: { + if (pb::WireFormat.IsEndGroupTag(tag)) { + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + break; + } + case 8: { + result.hasDividend = input.ReadInt64(ref result.dividend_); + break; + } + case 16: { + result.hasDivisor = input.ReadInt64(ref result.divisor_); + break; + } + } + } + + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + + + public bool HasDividend { + get { return result.hasDividend; } + } + public long Dividend { + get { return result.Dividend; } + set { SetDividend(value); } + } + public Builder SetDividend(long value) { + PrepareBuilder(); + result.hasDividend = true; + result.dividend_ = value; + return this; + } + public Builder ClearDividend() { + PrepareBuilder(); + result.hasDividend = false; + result.dividend_ = 0L; + return this; + } + + public bool HasDivisor { + get { return result.hasDivisor; } + } + public long Divisor { + get { return result.Divisor; } + set { SetDivisor(value); } + } + public Builder SetDivisor(long value) { + PrepareBuilder(); + result.hasDivisor = true; + result.divisor_ = value; + return this; + } + public Builder ClearDivisor() { + PrepareBuilder(); + result.hasDivisor = false; + result.divisor_ = 0L; + return this; + } + } + static DivArgs() { + object.ReferenceEquals(global::math.Proto.Math.Descriptor, null); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class DivReply : pb::GeneratedMessage { + private DivReply() { } + private static readonly DivReply defaultInstance = new DivReply().MakeReadOnly(); + private static readonly string[] _divReplyFieldNames = new string[] { "quotient", "remainder" }; + private static readonly uint[] _divReplyFieldTags = new uint[] { 8, 16 }; + public static DivReply DefaultInstance { + get { return defaultInstance; } + } + + public override DivReply DefaultInstanceForType { + get { return DefaultInstance; } + } + + protected override DivReply ThisMessage { + get { return this; } + } + + public static pbd::MessageDescriptor Descriptor { + get { return global::math.Proto.Math.internal__static_math_DivReply__Descriptor; } + } + + protected override pb::FieldAccess.FieldAccessorTable InternalFieldAccessors { + get { return global::math.Proto.Math.internal__static_math_DivReply__FieldAccessorTable; } + } + + public const int QuotientFieldNumber = 1; + private bool hasQuotient; + private long quotient_; + public bool HasQuotient { + get { return hasQuotient; } + } + public long Quotient { + get { return quotient_; } + } + + public const int RemainderFieldNumber = 2; + private bool hasRemainder; + private long remainder_; + public bool HasRemainder { + get { return hasRemainder; } + } + public long Remainder { + get { return remainder_; } + } + + public override bool IsInitialized { + get { + return true; + } + } + + public override void WriteTo(pb::ICodedOutputStream output) { + int size = SerializedSize; + string[] field_names = _divReplyFieldNames; + if (hasQuotient) { + output.WriteInt64(1, field_names[0], Quotient); + } + if (hasRemainder) { + output.WriteInt64(2, field_names[1], Remainder); + } + UnknownFields.WriteTo(output); + } + + private int memoizedSerializedSize = -1; + public override int SerializedSize { + get { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasQuotient) { + size += pb::CodedOutputStream.ComputeInt64Size(1, Quotient); + } + if (hasRemainder) { + size += pb::CodedOutputStream.ComputeInt64Size(2, Remainder); + } + size += UnknownFields.SerializedSize; + memoizedSerializedSize = size; + return size; + } + } + + public static DivReply ParseFrom(pb::ByteString data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static DivReply ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static DivReply ParseFrom(byte[] data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static DivReply ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static DivReply ParseFrom(global::System.IO.Stream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static DivReply ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + public static DivReply ParseDelimitedFrom(global::System.IO.Stream input) { + return CreateBuilder().MergeDelimitedFrom(input).BuildParsed(); + } + public static DivReply ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed(); + } + public static DivReply ParseFrom(pb::ICodedInputStream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static DivReply ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + private DivReply MakeReadOnly() { + return this; + } + + public static Builder CreateBuilder() { return new Builder(); } + public override Builder ToBuilder() { return CreateBuilder(this); } + public override Builder CreateBuilderForType() { return new Builder(); } + public static Builder CreateBuilder(DivReply prototype) { + return new Builder(prototype); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Builder : pb::GeneratedBuilder { + protected override Builder ThisBuilder { + get { return this; } + } + public Builder() { + result = DefaultInstance; + resultIsReadOnly = true; + } + internal Builder(DivReply cloneFrom) { + result = cloneFrom; + resultIsReadOnly = true; + } + + private bool resultIsReadOnly; + private DivReply result; + + private DivReply PrepareBuilder() { + if (resultIsReadOnly) { + DivReply original = result; + result = new DivReply(); + resultIsReadOnly = false; + MergeFrom(original); + } + return result; + } + + public override bool IsInitialized { + get { return result.IsInitialized; } + } + + protected override DivReply MessageBeingBuilt { + get { return PrepareBuilder(); } + } + + public override Builder Clear() { + result = DefaultInstance; + resultIsReadOnly = true; + return this; + } + + public override Builder Clone() { + if (resultIsReadOnly) { + return new Builder(result); + } else { + return new Builder().MergeFrom(result); + } + } + + public override pbd::MessageDescriptor DescriptorForType { + get { return global::math.DivReply.Descriptor; } + } + + public override DivReply DefaultInstanceForType { + get { return global::math.DivReply.DefaultInstance; } + } + + public override DivReply BuildPartial() { + if (resultIsReadOnly) { + return result; + } + resultIsReadOnly = true; + return result.MakeReadOnly(); + } + + public override Builder MergeFrom(pb::IMessage other) { + if (other is DivReply) { + return MergeFrom((DivReply) other); + } else { + base.MergeFrom(other); + return this; + } + } + + public override Builder MergeFrom(DivReply other) { + if (other == global::math.DivReply.DefaultInstance) return this; + PrepareBuilder(); + if (other.HasQuotient) { + Quotient = other.Quotient; + } + if (other.HasRemainder) { + Remainder = other.Remainder; + } + this.MergeUnknownFields(other.UnknownFields); + return this; + } + + public override Builder MergeFrom(pb::ICodedInputStream input) { + return MergeFrom(input, pb::ExtensionRegistry.Empty); + } + + public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + PrepareBuilder(); + pb::UnknownFieldSet.Builder unknownFields = null; + uint tag; + string field_name; + while (input.ReadTag(out tag, out field_name)) { + if(tag == 0 && field_name != null) { + int field_ordinal = global::System.Array.BinarySearch(_divReplyFieldNames, field_name, global::System.StringComparer.Ordinal); + if(field_ordinal >= 0) + tag = _divReplyFieldTags[field_ordinal]; + else { + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + continue; + } + } + switch (tag) { + case 0: { + throw pb::InvalidProtocolBufferException.InvalidTag(); + } + default: { + if (pb::WireFormat.IsEndGroupTag(tag)) { + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + break; + } + case 8: { + result.hasQuotient = input.ReadInt64(ref result.quotient_); + break; + } + case 16: { + result.hasRemainder = input.ReadInt64(ref result.remainder_); + break; + } + } + } + + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + + + public bool HasQuotient { + get { return result.hasQuotient; } + } + public long Quotient { + get { return result.Quotient; } + set { SetQuotient(value); } + } + public Builder SetQuotient(long value) { + PrepareBuilder(); + result.hasQuotient = true; + result.quotient_ = value; + return this; + } + public Builder ClearQuotient() { + PrepareBuilder(); + result.hasQuotient = false; + result.quotient_ = 0L; + return this; + } + + public bool HasRemainder { + get { return result.hasRemainder; } + } + public long Remainder { + get { return result.Remainder; } + set { SetRemainder(value); } + } + public Builder SetRemainder(long value) { + PrepareBuilder(); + result.hasRemainder = true; + result.remainder_ = value; + return this; + } + public Builder ClearRemainder() { + PrepareBuilder(); + result.hasRemainder = false; + result.remainder_ = 0L; + return this; + } + } + static DivReply() { + object.ReferenceEquals(global::math.Proto.Math.Descriptor, null); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class FibArgs : pb::GeneratedMessage { + private FibArgs() { } + private static readonly FibArgs defaultInstance = new FibArgs().MakeReadOnly(); + private static readonly string[] _fibArgsFieldNames = new string[] { "limit" }; + private static readonly uint[] _fibArgsFieldTags = new uint[] { 8 }; + public static FibArgs DefaultInstance { + get { return defaultInstance; } + } + + public override FibArgs DefaultInstanceForType { + get { return DefaultInstance; } + } + + protected override FibArgs ThisMessage { + get { return this; } + } + + public static pbd::MessageDescriptor Descriptor { + get { return global::math.Proto.Math.internal__static_math_FibArgs__Descriptor; } + } + + protected override pb::FieldAccess.FieldAccessorTable InternalFieldAccessors { + get { return global::math.Proto.Math.internal__static_math_FibArgs__FieldAccessorTable; } + } + + public const int LimitFieldNumber = 1; + private bool hasLimit; + private long limit_; + public bool HasLimit { + get { return hasLimit; } + } + public long Limit { + get { return limit_; } + } + + public override bool IsInitialized { + get { + return true; + } + } + + public override void WriteTo(pb::ICodedOutputStream output) { + int size = SerializedSize; + string[] field_names = _fibArgsFieldNames; + if (hasLimit) { + output.WriteInt64(1, field_names[0], Limit); + } + UnknownFields.WriteTo(output); + } + + private int memoizedSerializedSize = -1; + public override int SerializedSize { + get { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasLimit) { + size += pb::CodedOutputStream.ComputeInt64Size(1, Limit); + } + size += UnknownFields.SerializedSize; + memoizedSerializedSize = size; + return size; + } + } + + public static FibArgs ParseFrom(pb::ByteString data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static FibArgs ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static FibArgs ParseFrom(byte[] data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static FibArgs ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static FibArgs ParseFrom(global::System.IO.Stream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static FibArgs ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + public static FibArgs ParseDelimitedFrom(global::System.IO.Stream input) { + return CreateBuilder().MergeDelimitedFrom(input).BuildParsed(); + } + public static FibArgs ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed(); + } + public static FibArgs ParseFrom(pb::ICodedInputStream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static FibArgs ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + private FibArgs MakeReadOnly() { + return this; + } + + public static Builder CreateBuilder() { return new Builder(); } + public override Builder ToBuilder() { return CreateBuilder(this); } + public override Builder CreateBuilderForType() { return new Builder(); } + public static Builder CreateBuilder(FibArgs prototype) { + return new Builder(prototype); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Builder : pb::GeneratedBuilder { + protected override Builder ThisBuilder { + get { return this; } + } + public Builder() { + result = DefaultInstance; + resultIsReadOnly = true; + } + internal Builder(FibArgs cloneFrom) { + result = cloneFrom; + resultIsReadOnly = true; + } + + private bool resultIsReadOnly; + private FibArgs result; + + private FibArgs PrepareBuilder() { + if (resultIsReadOnly) { + FibArgs original = result; + result = new FibArgs(); + resultIsReadOnly = false; + MergeFrom(original); + } + return result; + } + + public override bool IsInitialized { + get { return result.IsInitialized; } + } + + protected override FibArgs MessageBeingBuilt { + get { return PrepareBuilder(); } + } + + public override Builder Clear() { + result = DefaultInstance; + resultIsReadOnly = true; + return this; + } + + public override Builder Clone() { + if (resultIsReadOnly) { + return new Builder(result); + } else { + return new Builder().MergeFrom(result); + } + } + + public override pbd::MessageDescriptor DescriptorForType { + get { return global::math.FibArgs.Descriptor; } + } + + public override FibArgs DefaultInstanceForType { + get { return global::math.FibArgs.DefaultInstance; } + } + + public override FibArgs BuildPartial() { + if (resultIsReadOnly) { + return result; + } + resultIsReadOnly = true; + return result.MakeReadOnly(); + } + + public override Builder MergeFrom(pb::IMessage other) { + if (other is FibArgs) { + return MergeFrom((FibArgs) other); + } else { + base.MergeFrom(other); + return this; + } + } + + public override Builder MergeFrom(FibArgs other) { + if (other == global::math.FibArgs.DefaultInstance) return this; + PrepareBuilder(); + if (other.HasLimit) { + Limit = other.Limit; + } + this.MergeUnknownFields(other.UnknownFields); + return this; + } + + public override Builder MergeFrom(pb::ICodedInputStream input) { + return MergeFrom(input, pb::ExtensionRegistry.Empty); + } + + public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + PrepareBuilder(); + pb::UnknownFieldSet.Builder unknownFields = null; + uint tag; + string field_name; + while (input.ReadTag(out tag, out field_name)) { + if(tag == 0 && field_name != null) { + int field_ordinal = global::System.Array.BinarySearch(_fibArgsFieldNames, field_name, global::System.StringComparer.Ordinal); + if(field_ordinal >= 0) + tag = _fibArgsFieldTags[field_ordinal]; + else { + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + continue; + } + } + switch (tag) { + case 0: { + throw pb::InvalidProtocolBufferException.InvalidTag(); + } + default: { + if (pb::WireFormat.IsEndGroupTag(tag)) { + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + break; + } + case 8: { + result.hasLimit = input.ReadInt64(ref result.limit_); + break; + } + } + } + + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + + + public bool HasLimit { + get { return result.hasLimit; } + } + public long Limit { + get { return result.Limit; } + set { SetLimit(value); } + } + public Builder SetLimit(long value) { + PrepareBuilder(); + result.hasLimit = true; + result.limit_ = value; + return this; + } + public Builder ClearLimit() { + PrepareBuilder(); + result.hasLimit = false; + result.limit_ = 0L; + return this; + } + } + static FibArgs() { + object.ReferenceEquals(global::math.Proto.Math.Descriptor, null); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Num : pb::GeneratedMessage { + private Num() { } + private static readonly Num defaultInstance = new Num().MakeReadOnly(); + private static readonly string[] _numFieldNames = new string[] { "num" }; + private static readonly uint[] _numFieldTags = new uint[] { 8 }; + public static Num DefaultInstance { + get { return defaultInstance; } + } + + public override Num DefaultInstanceForType { + get { return DefaultInstance; } + } + + protected override Num ThisMessage { + get { return this; } + } + + public static pbd::MessageDescriptor Descriptor { + get { return global::math.Proto.Math.internal__static_math_Num__Descriptor; } + } + + protected override pb::FieldAccess.FieldAccessorTable InternalFieldAccessors { + get { return global::math.Proto.Math.internal__static_math_Num__FieldAccessorTable; } + } + + public const int Num_FieldNumber = 1; + private bool hasNum_; + private long num_; + public bool HasNum_ { + get { return hasNum_; } + } + public long Num_ { + get { return num_; } + } + + public override bool IsInitialized { + get { + return true; + } + } + + public override void WriteTo(pb::ICodedOutputStream output) { + int size = SerializedSize; + string[] field_names = _numFieldNames; + if (hasNum_) { + output.WriteInt64(1, field_names[0], Num_); + } + UnknownFields.WriteTo(output); + } + + private int memoizedSerializedSize = -1; + public override int SerializedSize { + get { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasNum_) { + size += pb::CodedOutputStream.ComputeInt64Size(1, Num_); + } + size += UnknownFields.SerializedSize; + memoizedSerializedSize = size; + return size; + } + } + + public static Num ParseFrom(pb::ByteString data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static Num ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static Num ParseFrom(byte[] data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static Num ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static Num ParseFrom(global::System.IO.Stream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static Num ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + public static Num ParseDelimitedFrom(global::System.IO.Stream input) { + return CreateBuilder().MergeDelimitedFrom(input).BuildParsed(); + } + public static Num ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed(); + } + public static Num ParseFrom(pb::ICodedInputStream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static Num ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + private Num MakeReadOnly() { + return this; + } + + public static Builder CreateBuilder() { return new Builder(); } + public override Builder ToBuilder() { return CreateBuilder(this); } + public override Builder CreateBuilderForType() { return new Builder(); } + public static Builder CreateBuilder(Num prototype) { + return new Builder(prototype); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Builder : pb::GeneratedBuilder { + protected override Builder ThisBuilder { + get { return this; } + } + public Builder() { + result = DefaultInstance; + resultIsReadOnly = true; + } + internal Builder(Num cloneFrom) { + result = cloneFrom; + resultIsReadOnly = true; + } + + private bool resultIsReadOnly; + private Num result; + + private Num PrepareBuilder() { + if (resultIsReadOnly) { + Num original = result; + result = new Num(); + resultIsReadOnly = false; + MergeFrom(original); + } + return result; + } + + public override bool IsInitialized { + get { return result.IsInitialized; } + } + + protected override Num MessageBeingBuilt { + get { return PrepareBuilder(); } + } + + public override Builder Clear() { + result = DefaultInstance; + resultIsReadOnly = true; + return this; + } + + public override Builder Clone() { + if (resultIsReadOnly) { + return new Builder(result); + } else { + return new Builder().MergeFrom(result); + } + } + + public override pbd::MessageDescriptor DescriptorForType { + get { return global::math.Num.Descriptor; } + } + + public override Num DefaultInstanceForType { + get { return global::math.Num.DefaultInstance; } + } + + public override Num BuildPartial() { + if (resultIsReadOnly) { + return result; + } + resultIsReadOnly = true; + return result.MakeReadOnly(); + } + + public override Builder MergeFrom(pb::IMessage other) { + if (other is Num) { + return MergeFrom((Num) other); + } else { + base.MergeFrom(other); + return this; + } + } + + public override Builder MergeFrom(Num other) { + if (other == global::math.Num.DefaultInstance) return this; + PrepareBuilder(); + if (other.HasNum_) { + Num_ = other.Num_; + } + this.MergeUnknownFields(other.UnknownFields); + return this; + } + + public override Builder MergeFrom(pb::ICodedInputStream input) { + return MergeFrom(input, pb::ExtensionRegistry.Empty); + } + + public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + PrepareBuilder(); + pb::UnknownFieldSet.Builder unknownFields = null; + uint tag; + string field_name; + while (input.ReadTag(out tag, out field_name)) { + if(tag == 0 && field_name != null) { + int field_ordinal = global::System.Array.BinarySearch(_numFieldNames, field_name, global::System.StringComparer.Ordinal); + if(field_ordinal >= 0) + tag = _numFieldTags[field_ordinal]; + else { + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + continue; + } + } + switch (tag) { + case 0: { + throw pb::InvalidProtocolBufferException.InvalidTag(); + } + default: { + if (pb::WireFormat.IsEndGroupTag(tag)) { + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + break; + } + case 8: { + result.hasNum_ = input.ReadInt64(ref result.num_); + break; + } + } + } + + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + + + public bool HasNum_ { + get { return result.hasNum_; } + } + public long Num_ { + get { return result.Num_; } + set { SetNum_(value); } + } + public Builder SetNum_(long value) { + PrepareBuilder(); + result.hasNum_ = true; + result.num_ = value; + return this; + } + public Builder ClearNum_() { + PrepareBuilder(); + result.hasNum_ = false; + result.num_ = 0L; + return this; + } + } + static Num() { + object.ReferenceEquals(global::math.Proto.Math.Descriptor, null); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class FibReply : pb::GeneratedMessage { + private FibReply() { } + private static readonly FibReply defaultInstance = new FibReply().MakeReadOnly(); + private static readonly string[] _fibReplyFieldNames = new string[] { "count" }; + private static readonly uint[] _fibReplyFieldTags = new uint[] { 8 }; + public static FibReply DefaultInstance { + get { return defaultInstance; } + } + + public override FibReply DefaultInstanceForType { + get { return DefaultInstance; } + } + + protected override FibReply ThisMessage { + get { return this; } + } + + public static pbd::MessageDescriptor Descriptor { + get { return global::math.Proto.Math.internal__static_math_FibReply__Descriptor; } + } + + protected override pb::FieldAccess.FieldAccessorTable InternalFieldAccessors { + get { return global::math.Proto.Math.internal__static_math_FibReply__FieldAccessorTable; } + } + + public const int CountFieldNumber = 1; + private bool hasCount; + private long count_; + public bool HasCount { + get { return hasCount; } + } + public long Count { + get { return count_; } + } + + public override bool IsInitialized { + get { + return true; + } + } + + public override void WriteTo(pb::ICodedOutputStream output) { + int size = SerializedSize; + string[] field_names = _fibReplyFieldNames; + if (hasCount) { + output.WriteInt64(1, field_names[0], Count); + } + UnknownFields.WriteTo(output); + } + + private int memoizedSerializedSize = -1; + public override int SerializedSize { + get { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasCount) { + size += pb::CodedOutputStream.ComputeInt64Size(1, Count); + } + size += UnknownFields.SerializedSize; + memoizedSerializedSize = size; + return size; + } + } + + public static FibReply ParseFrom(pb::ByteString data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static FibReply ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static FibReply ParseFrom(byte[] data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static FibReply ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static FibReply ParseFrom(global::System.IO.Stream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static FibReply ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + public static FibReply ParseDelimitedFrom(global::System.IO.Stream input) { + return CreateBuilder().MergeDelimitedFrom(input).BuildParsed(); + } + public static FibReply ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed(); + } + public static FibReply ParseFrom(pb::ICodedInputStream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static FibReply ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + private FibReply MakeReadOnly() { + return this; + } + + public static Builder CreateBuilder() { return new Builder(); } + public override Builder ToBuilder() { return CreateBuilder(this); } + public override Builder CreateBuilderForType() { return new Builder(); } + public static Builder CreateBuilder(FibReply prototype) { + return new Builder(prototype); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Builder : pb::GeneratedBuilder { + protected override Builder ThisBuilder { + get { return this; } + } + public Builder() { + result = DefaultInstance; + resultIsReadOnly = true; + } + internal Builder(FibReply cloneFrom) { + result = cloneFrom; + resultIsReadOnly = true; + } + + private bool resultIsReadOnly; + private FibReply result; + + private FibReply PrepareBuilder() { + if (resultIsReadOnly) { + FibReply original = result; + result = new FibReply(); + resultIsReadOnly = false; + MergeFrom(original); + } + return result; + } + + public override bool IsInitialized { + get { return result.IsInitialized; } + } + + protected override FibReply MessageBeingBuilt { + get { return PrepareBuilder(); } + } + + public override Builder Clear() { + result = DefaultInstance; + resultIsReadOnly = true; + return this; + } + + public override Builder Clone() { + if (resultIsReadOnly) { + return new Builder(result); + } else { + return new Builder().MergeFrom(result); + } + } + + public override pbd::MessageDescriptor DescriptorForType { + get { return global::math.FibReply.Descriptor; } + } + + public override FibReply DefaultInstanceForType { + get { return global::math.FibReply.DefaultInstance; } + } + + public override FibReply BuildPartial() { + if (resultIsReadOnly) { + return result; + } + resultIsReadOnly = true; + return result.MakeReadOnly(); + } + + public override Builder MergeFrom(pb::IMessage other) { + if (other is FibReply) { + return MergeFrom((FibReply) other); + } else { + base.MergeFrom(other); + return this; + } + } + + public override Builder MergeFrom(FibReply other) { + if (other == global::math.FibReply.DefaultInstance) return this; + PrepareBuilder(); + if (other.HasCount) { + Count = other.Count; + } + this.MergeUnknownFields(other.UnknownFields); + return this; + } + + public override Builder MergeFrom(pb::ICodedInputStream input) { + return MergeFrom(input, pb::ExtensionRegistry.Empty); + } + + public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + PrepareBuilder(); + pb::UnknownFieldSet.Builder unknownFields = null; + uint tag; + string field_name; + while (input.ReadTag(out tag, out field_name)) { + if(tag == 0 && field_name != null) { + int field_ordinal = global::System.Array.BinarySearch(_fibReplyFieldNames, field_name, global::System.StringComparer.Ordinal); + if(field_ordinal >= 0) + tag = _fibReplyFieldTags[field_ordinal]; + else { + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + continue; + } + } + switch (tag) { + case 0: { + throw pb::InvalidProtocolBufferException.InvalidTag(); + } + default: { + if (pb::WireFormat.IsEndGroupTag(tag)) { + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + break; + } + case 8: { + result.hasCount = input.ReadInt64(ref result.count_); + break; + } + } + } + + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + + + public bool HasCount { + get { return result.hasCount; } + } + public long Count { + get { return result.Count; } + set { SetCount(value); } + } + public Builder SetCount(long value) { + PrepareBuilder(); + result.hasCount = true; + result.count_ = value; + return this; + } + public Builder ClearCount() { + PrepareBuilder(); + result.hasCount = false; + result.count_ = 0L; + return this; + } + } + static FibReply() { + object.ReferenceEquals(global::math.Proto.Math.Descriptor, null); + } + } + + #endregion + + #region Services + /* + * Service generation is now disabled by default, use the following option to enable: + * option (google.protobuf.csharp_file_options).service_generator_type = GENERIC; + */ + #endregion + +} + +#endregion Designer generated code diff --git a/src/csharp/GrpcApi/MathServiceClientStub.cs b/src/csharp/GrpcApi/MathServiceClientStub.cs new file mode 100644 index 00000000000..493c186b8e5 --- /dev/null +++ b/src/csharp/GrpcApi/MathServiceClientStub.cs @@ -0,0 +1,75 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Reactive.Linq; +using Google.GRPC.Core; + +namespace math +{ + /// + /// Implementation of math service stub (this is handwritten version of code + /// that will normally be generated). + /// + public class MathServiceClientStub : IMathServiceClient + { + readonly Channel channel; + readonly TimeSpan methodTimeout; + + public MathServiceClientStub(Channel channel, TimeSpan methodTimeout) + { + this.channel = channel; + this.methodTimeout = methodTimeout; + } + + public DivReply Div(DivArgs args, CancellationToken token = default(CancellationToken)) + { + var call = new Google.GRPC.Core.Call("/math.Math/Div", Serialize_DivArgs, Deserialize_DivReply, methodTimeout, channel); + return Calls.BlockingUnaryCall(call, args, token); + } + + public Task DivAsync(DivArgs args, CancellationToken token = default(CancellationToken)) + { + var call = new Google.GRPC.Core.Call("/math.Math/Div", Serialize_DivArgs, Deserialize_DivReply, methodTimeout, channel); + return Calls.AsyncUnaryCall(call, args, token); + } + + public Task Fib(FibArgs args, IObserver outputs, CancellationToken token = default(CancellationToken)) + { + var call = new Google.GRPC.Core.Call("/math.Math/Fib", Serialize_FibArgs, Deserialize_Num, methodTimeout, channel); + return Calls.AsyncServerStreamingCall(call, args, outputs, token); + } + + public ClientStreamingAsyncResult Sum(CancellationToken token = default(CancellationToken)) + { + var call = new Google.GRPC.Core.Call("/math.Math/Sum", Serialize_Num, Deserialize_Num, methodTimeout, channel); + return Calls.AsyncClientStreamingCall(call, token); + } + + public IObserver DivMany(IObserver outputs, CancellationToken token = default(CancellationToken)) + { + var call = new Google.GRPC.Core.Call("/math.Math/DivMany", Serialize_DivArgs, Deserialize_DivReply, methodTimeout, channel); + return Calls.DuplexStreamingCall(call, outputs, token); + } + + private static byte[] Serialize_DivArgs(DivArgs arg) { + return arg.ToByteArray(); + } + + private static byte[] Serialize_FibArgs(FibArgs arg) { + return arg.ToByteArray(); + } + + private static byte[] Serialize_Num(Num arg) { + return arg.ToByteArray(); + } + + private static DivReply Deserialize_DivReply(byte[] payload) { + return DivReply.CreateBuilder().MergeFrom(payload).Build(); + } + + private static Num Deserialize_Num(byte[] payload) { + return Num.CreateBuilder().MergeFrom(payload).Build(); + } + } +} \ No newline at end of file diff --git a/src/csharp/GrpcApi/Messages.cs b/src/csharp/GrpcApi/Messages.cs new file mode 100644 index 00000000000..b08816bdb7f --- /dev/null +++ b/src/csharp/GrpcApi/Messages.cs @@ -0,0 +1,35 @@ +//using System; + +//namespace Google.GRPC.Examples.Math +//{ +// // Messages in this file are placeholders for actual protobuf message classes +// // that will be generated from math.proto file. +// +// public class DivArgs +// { +// public long Dividend{ get; set; } +// public long Divisor { get; set; } +// } +// +// public class DivReply +// { +// public long Quotient { get; set; } +// public long Remainder { get; set; } +// } +// +// public class FibArgs +// { +// public long Limit { get; set; } +// } +// +// public class Number +// { +// public long Num { get; set; } +// } +// +// public class FibReply +// { +// public long Count { get; set; } +// } +//} + diff --git a/src/csharp/GrpcApi/Properties/AssemblyInfo.cs b/src/csharp/GrpcApi/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..725f12c4860 --- /dev/null +++ b/src/csharp/GrpcApi/Properties/AssemblyInfo.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. +[assembly: AssemblyTitle ("GrpcApi")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("jtattermusch")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. +[assembly: AssemblyVersion ("1.0.*")] +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/src/csharp/GrpcApi/RecordingObserver.cs b/src/csharp/GrpcApi/RecordingObserver.cs new file mode 100644 index 00000000000..8ba3787905a --- /dev/null +++ b/src/csharp/GrpcApi/RecordingObserver.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading.Tasks; +using System.Collections.Generic; + +namespace math +{ + public class RecordingObserver : IObserver + { + TaskCompletionSource> tcs = new TaskCompletionSource>(); + List data = new List(); + + public void OnCompleted() + { + tcs.SetResult(data); + } + + public void OnError(Exception error) + { + tcs.SetException(error); + } + + public void OnNext(T value) + { + data.Add(value); + } + + public Task> ToList() { + return tcs.Task; + } + } +} + diff --git a/src/csharp/GrpcApi/math.proto b/src/csharp/GrpcApi/math.proto new file mode 100755 index 00000000000..e98b99e002a --- /dev/null +++ b/src/csharp/GrpcApi/math.proto @@ -0,0 +1,50 @@ +syntax = "proto2"; + +package math; + +message DivArgs { + optional int64 dividend = 1; + optional int64 divisor = 2; +} + +message DivReply { + optional int64 quotient = 1; + optional int64 remainder = 2; +} + +message FibArgs { + optional int64 limit = 1; +} + +message Num { + optional int64 num = 1; +} + +message FibReply { + optional int64 count = 1; +} + +service Math { + // Div divides args.dividend by args.divisor and returns the quotient and + // remainder. + rpc Div (DivArgs) returns (DivReply) { + } + + // DivMany accepts an arbitrary number of division args from the client stream + // and sends back the results in the reply stream. The stream continues until + // the client closes its end; the server does the same after sending all the + // replies. The stream ends immediately if either end aborts. + rpc DivMany (stream DivArgs) returns (stream DivReply) { + } + + // Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib + // generates up to limit numbers; otherwise it continues until the call is + // canceled. Unlike Fib above, Fib has no final FibReply. + rpc Fib (FibArgs) returns (stream Num) { + } + + // Sum sums a stream of numbers, returning the final result once the stream + // is closed. + rpc Sum (stream Num) returns (Num) { + } +} diff --git a/src/csharp/GrpcCore/.gitignore b/src/csharp/GrpcCore/.gitignore new file mode 100644 index 00000000000..ba077a4031a --- /dev/null +++ b/src/csharp/GrpcCore/.gitignore @@ -0,0 +1 @@ +bin diff --git a/src/csharp/GrpcCore/Call.cs b/src/csharp/GrpcCore/Call.cs new file mode 100644 index 00000000000..bf257e5d598 --- /dev/null +++ b/src/csharp/GrpcCore/Call.cs @@ -0,0 +1,69 @@ +using System; +using Google.GRPC.Core.Internal; + +namespace Google.GRPC.Core +{ + public class Call + { + readonly string methodName; + readonly Func requestSerializer; + readonly Func responseDeserializer; + readonly TimeSpan timeout; + readonly Channel channel; + + // TODO: channel param should be removed in the future. + public Call(string methodName, + Func requestSerializer, + Func responseDeserializer, + TimeSpan timeout, + Channel channel) { + this.methodName = methodName; + this.requestSerializer = requestSerializer; + this.responseDeserializer = responseDeserializer; + this.timeout = timeout; + this.channel = channel; + } + + + public Channel Channel + { + get + { + return this.channel; + } + } + + public TimeSpan Timeout + { + get + { + return this.timeout; + } + } + + public string MethodName + { + get + { + return this.methodName; + } + } + + public Func RequestSerializer + { + get + { + return this.requestSerializer; + } + } + + public Func ResponseDeserializer + { + get + { + return this.responseDeserializer; + } + } + } +} + diff --git a/src/csharp/GrpcCore/Calls.cs b/src/csharp/GrpcCore/Calls.cs new file mode 100644 index 00000000000..c3e51cb4781 --- /dev/null +++ b/src/csharp/GrpcCore/Calls.cs @@ -0,0 +1,85 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Google.GRPC.Core.Internal; + +namespace Google.GRPC.Core +{ + // NOTE: this class is work-in-progress + + /// + /// Helper methods for generated stubs to make RPC calls. + /// + public static class Calls + { + public static TResponse BlockingUnaryCall(Call call, TRequest req, CancellationToken token) + { + //TODO: implement this in real synchronous style once new GRPC C core API is available. + return AsyncUnaryCall(call, req, token).Result; + } + + public static async Task AsyncUnaryCall(Call call, TRequest req, CancellationToken token) + { + var asyncCall = new AsyncCall(call.RequestSerializer, call.ResponseDeserializer); + asyncCall.Initialize(call.Channel, call.MethodName); + asyncCall.Start(false, GetCompletionQueue()); + + await asyncCall.WriteAsync(req); + await asyncCall.WritesCompletedAsync(); + + TResponse response = await asyncCall.ReadAsync(); + + Status status = await asyncCall.Finished; + + if (status.StatusCode != StatusCode.GRPC_STATUS_OK) + { + throw new RpcException(status); + } + return response; + } + + public static async Task AsyncServerStreamingCall(Call call, TRequest req, IObserver outputs, CancellationToken token) + { + var asyncCall = new AsyncCall(call.RequestSerializer, call.ResponseDeserializer); + asyncCall.Initialize(call.Channel, call.MethodName); + asyncCall.Start(false, GetCompletionQueue()); + + asyncCall.StartReadingToStream(outputs); + + await asyncCall.WriteAsync(req); + await asyncCall.WritesCompletedAsync(); + } + + public static ClientStreamingAsyncResult AsyncClientStreamingCall(Call call, CancellationToken token) + { + var asyncCall = new AsyncCall(call.RequestSerializer, call.ResponseDeserializer); + asyncCall.Initialize(call.Channel, call.MethodName); + asyncCall.Start(false, GetCompletionQueue()); + + var task = asyncCall.ReadAsync(); + var inputs = new StreamingInputObserver(asyncCall); + return new ClientStreamingAsyncResult(task, inputs); + } + + public static TResponse BlockingClientStreamingCall(Call call, IObservable inputs, CancellationToken token) + { + throw new NotImplementedException(); + } + + public static IObserver DuplexStreamingCall(Call call, IObserver outputs, CancellationToken token) + { + var asyncCall = new AsyncCall(call.RequestSerializer, call.ResponseDeserializer); + asyncCall.Initialize(call.Channel, call.MethodName); + asyncCall.Start(false, GetCompletionQueue()); + + asyncCall.StartReadingToStream(outputs); + var inputs = new StreamingInputObserver(asyncCall); + return inputs; + } + + private static CompletionQueueSafeHandle GetCompletionQueue() { + return GrpcEnvironment.ThreadPool.CompletionQueue; + } + } +} + diff --git a/src/csharp/GrpcCore/Channel.cs b/src/csharp/GrpcCore/Channel.cs new file mode 100644 index 00000000000..b0d8beeb7bf --- /dev/null +++ b/src/csharp/GrpcCore/Channel.cs @@ -0,0 +1,59 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Google.GRPC.Core.Internal; + +namespace Google.GRPC.Core +{ + public class Channel : IDisposable + { + /// + /// Make sure GPRC environment is initialized before any channels get used. + /// + static Channel() { + GrpcEnvironment.EnsureInitialized(); + } + + readonly ChannelSafeHandle handle; + readonly String target; + + // TODO: add way how to create grpc_secure_channel.... + // TODO: add support for channel args... + public Channel(string target) + { + this.handle = ChannelSafeHandle.Create(target, IntPtr.Zero); + this.target = target; + } + + internal ChannelSafeHandle Handle + { + get + { + return this.handle; + } + } + + public string Target + { + get + { + return this.target; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (handle != null && !handle.IsInvalid) + { + handle.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/src/csharp/GrpcCore/ClientStreamingAsyncResult.cs b/src/csharp/GrpcCore/ClientStreamingAsyncResult.cs new file mode 100644 index 00000000000..9e7312c1fa0 --- /dev/null +++ b/src/csharp/GrpcCore/ClientStreamingAsyncResult.cs @@ -0,0 +1,37 @@ +using System; +using System.Threading.Tasks; + +namespace Google.GRPC.Core +{ + /// + /// Return type for client streaming async method. + /// + public struct ClientStreamingAsyncResult + { + readonly Task task; + readonly IObserver inputs; + + public ClientStreamingAsyncResult(Task task, IObserver inputs) + { + this.task = task; + this.inputs = inputs; + } + + public Task Task + { + get + { + return this.task; + } + } + + public IObserver Inputs + { + get + { + return this.inputs; + } + } + } +} + diff --git a/src/csharp/GrpcCore/GrpcCore.csproj b/src/csharp/GrpcCore/GrpcCore.csproj new file mode 100644 index 00000000000..f0c84e78ea6 --- /dev/null +++ b/src/csharp/GrpcCore/GrpcCore.csproj @@ -0,0 +1,62 @@ + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7} + Library + GrpcCore + GrpcCore + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + + + full + true + bin\Release + prompt + 4 + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/csharp/GrpcCore/GrpcEnvironment.cs b/src/csharp/GrpcCore/GrpcEnvironment.cs new file mode 100644 index 00000000000..7a644f49619 --- /dev/null +++ b/src/csharp/GrpcCore/GrpcEnvironment.cs @@ -0,0 +1,91 @@ +using System; +using Google.GRPC.Core.Internal; +using System.Runtime.InteropServices; + +namespace Google.GRPC.Core +{ + /// + /// Encapsulates initialization and shutdown of GRPC C core library. + /// You should not need to initialize it manually, as static constructors + /// should load the library when needed. + /// + public static class GrpcEnvironment + { + const int THREAD_POOL_SIZE = 1; + + [DllImport("libgrpc.so")] + static extern void grpc_init(); + + [DllImport("libgrpc.so")] + static extern void grpc_shutdown(); + + static object staticLock = new object(); + static bool initCalled = false; + static bool shutdownCalled = false; + + static GrpcThreadPool threadPool = new GrpcThreadPool(THREAD_POOL_SIZE); + + /// + /// Makes sure GRPC environment is initialized. + /// + public static void EnsureInitialized() { + lock(staticLock) + { + if (!initCalled) + { + initCalled = true; + GrpcInit(); + } + } + } + + /// + /// Shuts down the GRPC environment if it was initialized before. + /// Repeated invocations have no effect. + /// + public static void Shutdown() + { + lock(staticLock) + { + if (initCalled && !shutdownCalled) + { + shutdownCalled = true; + GrpcShutdown(); + } + } + + } + + /// + /// Initializes GRPC C Core library. + /// + private static void GrpcInit() + { + grpc_init(); + threadPool.Start(); + // TODO: use proper logging here + Console.WriteLine("GRPC initialized."); + } + + /// + /// Shutdown GRPC C Core library. + /// + private static void GrpcShutdown() + { + threadPool.Stop(); + grpc_shutdown(); + + // TODO: use proper logging here + Console.WriteLine("GRPC shutdown."); + } + + internal static GrpcThreadPool ThreadPool + { + get + { + return threadPool; + } + } + } +} + diff --git a/src/csharp/GrpcCore/Internal/AsyncCall.cs b/src/csharp/GrpcCore/Internal/AsyncCall.cs new file mode 100644 index 00000000000..e83ca0eaa96 --- /dev/null +++ b/src/csharp/GrpcCore/Internal/AsyncCall.cs @@ -0,0 +1,485 @@ +using System; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using System.Runtime.CompilerServices; +using Google.GRPC.Core.Internal; + +namespace Google.GRPC.Core.Internal +{ + /// + /// Listener for call events that can be delivered from a completion queue. + /// + internal interface ICallEventListener { + + void OnClientMetadata(); + + void OnRead(byte[] payload); + + void OnWriteAccepted(GRPCOpError error); + + void OnFinishAccepted(GRPCOpError error); + + // ignore the status on server + void OnFinished(Status status); + } + + /// + /// Handle native call lifecycle and provides convenience methods. + /// + internal class AsyncCall: ICallEventListener, IDisposable + { + readonly Func serializer; + readonly Func deserializer; + + // TODO: make sure the delegate doesn't get garbage collected while + // native callbacks are in the completion queue. + readonly EventCallbackDelegate callbackHandler; + + object myLock = new object(); + bool disposed; + CallSafeHandle call; + + bool started; + bool errorOccured; + + bool cancelRequested; + bool halfcloseRequested; + bool halfclosed; + bool doneWithReading; + Nullable finishedStatus; + + TaskCompletionSource writeTcs; + TaskCompletionSource readTcs; + TaskCompletionSource halfcloseTcs = new TaskCompletionSource(); + TaskCompletionSource finishedTcs = new TaskCompletionSource(); + + IObserver readObserver; + + public AsyncCall(Func serializer, Func deserializer) + { + this.serializer = serializer; + this.deserializer = deserializer; + this.callbackHandler = HandleEvent; + } + + public Task WriteAsync(TWrite msg) + { + return StartWrite(msg, false).Task; + } + + public Task WritesCompletedAsync() + { + WritesDone(); + return halfcloseTcs.Task; + } + + public Task WriteStatusAsync(Status status) + { + WriteStatus(status); + return halfcloseTcs.Task; + } + + public Task ReadAsync() + { + return StartRead().Task; + } + + public Task Finished + { + get + { + return finishedTcs.Task; + } + } + + /// + /// Initiates reading to given observer. + /// + public void StartReadingToStream(IObserver readObserver) { + lock (myLock) + { + CheckStarted(); + if (this.readObserver != null) + { + throw new InvalidOperationException("Already registered an observer."); + } + this.readObserver = readObserver; + StartRead(); + } + } + + public void Initialize(Channel channel, String methodName) { + lock (myLock) + { + this.call = CallSafeHandle.Create(channel.Handle, methodName, channel.Target, Timespec.InfFuture); + } + } + + public void InitializeServer(CallSafeHandle call) + { + lock(myLock) + { + this.call = call; + } + } + + // Client only + public void Start(bool buffered, CompletionQueueSafeHandle cq) + { + lock (myLock) + { + if (started) + { + throw new InvalidOperationException("Already started."); + } + + call.Invoke(cq, buffered, callbackHandler, callbackHandler); + started = true; + } + } + + // Server only + public void Accept(CompletionQueueSafeHandle cq) + { + lock (myLock) + { + if (started) + { + throw new InvalidOperationException("Already started."); + } + + call.ServerAccept(cq, callbackHandler); + call.ServerEndInitialMetadata(0); + started = true; + } + } + + public TaskCompletionSource StartWrite(TWrite msg, bool buffered) + { + lock (myLock) + { + CheckStarted(); + CheckNotFinished(); + CheckNoError(); + CheckCancelNotRequested(); + + if (halfcloseRequested || halfclosed) + { + throw new InvalidOperationException("Already halfclosed."); + } + + if (writeTcs != null) + { + throw new InvalidOperationException("Only one write can be pending at a time"); + } + + // TODO: wrap serialization... + byte[] payload = serializer(msg); + + call.StartWrite(payload, buffered, callbackHandler); + writeTcs = new TaskCompletionSource(); + return writeTcs; + } + } + + // client only + public void WritesDone() + { + lock (myLock) + { + CheckStarted(); + CheckNotFinished(); + CheckNoError(); + CheckCancelNotRequested(); + + if (halfcloseRequested || halfclosed) + { + throw new InvalidOperationException("Already halfclosed."); + } + + call.WritesDone(callbackHandler); + halfcloseRequested = true; + } + } + + // server only + public void WriteStatus(Status status) + { + lock (myLock) + { + CheckStarted(); + CheckNotFinished(); + CheckNoError(); + CheckCancelNotRequested(); + + if (halfcloseRequested || halfclosed) + { + throw new InvalidOperationException("Already halfclosed."); + } + + call.StartWriteStatus(status, callbackHandler); + halfcloseRequested = true; + } + } + + public TaskCompletionSource StartRead() + { + lock (myLock) + { + CheckStarted(); + CheckNotFinished(); + CheckNoError(); + + // TODO: add check for not cancelled? + + if (doneWithReading) + { + throw new InvalidOperationException("Already read the last message."); + } + + if (readTcs != null) + { + throw new InvalidOperationException("Only one read can be pending at a time"); + } + + call.StartRead(callbackHandler); + + readTcs = new TaskCompletionSource(); + return readTcs; + } + } + + public void Cancel() + { + lock (myLock) + { + CheckStarted(); + CheckNotFinished(); + + cancelRequested = true; + } + // grpc_call_cancel is threadsafe + call.Cancel(); + } + + public void CancelWithStatus(Status status) + { + lock (myLock) + { + CheckStarted(); + CheckNotFinished(); + + cancelRequested = true; + } + // grpc_call_cancel_with_status is threadsafe + call.CancelWithStatus(status); + } + + public void OnClientMetadata() + { + // TODO: implement.... + } + + public void OnRead(byte[] payload) + { + TaskCompletionSource oldTcs = null; + IObserver observer = null; + lock (myLock) + { + oldTcs = readTcs; + readTcs = null; + if (payload == null) + { + doneWithReading = true; + } + observer = readObserver; + } + + // TODO: wrap deserialization... + TRead msg = payload != null ? deserializer(payload) : default(TRead); + + oldTcs.SetResult(msg); + + // TODO: make sure we deliver reads in the right order. + + if (observer != null) + { + if (payload != null) + { + // TODO: wrap to handle exceptions + observer.OnNext(msg); + + // start a new read + StartRead(); + } + else + { + // TODO: wrap to handle exceptions; + observer.OnCompleted(); + } + + } + } + + public void OnWriteAccepted(GRPCOpError error) + { + TaskCompletionSource oldTcs = null; + lock (myLock) + { + UpdateErrorOccured(error); + oldTcs = writeTcs; + writeTcs = null; + } + + if (errorOccured) + { + // TODO: use the right type of exception... + oldTcs.SetException(new Exception("Write failed")); + } + else + { + // TODO: where does the continuation run? + oldTcs.SetResult(null); + } + } + + public void OnFinishAccepted(GRPCOpError error) + { + lock (myLock) + { + UpdateErrorOccured(error); + halfclosed = true; + } + + if (errorOccured) + { + halfcloseTcs.SetException(new Exception("Halfclose failed")); + + } + else + { + halfcloseTcs.SetResult(null); + } + + } + + public void OnFinished(Status status) + { + lock (myLock) + { + finishedStatus = status; + + DisposeResourcesIfNeeded(); + } + finishedTcs.SetResult(status); + + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + if (call != null) + { + call.Dispose(); + } + } + disposed = true; + } + } + + private void UpdateErrorOccured(GRPCOpError error) + { + if (error == GRPCOpError.GRPC_OP_ERROR) + { + errorOccured = true; + } + } + + private void CheckStarted() + { + if (!started) + { + throw new InvalidOperationException("Call not started"); + } + } + + private void CheckNoError() + { + if (errorOccured) + { + throw new InvalidOperationException("Error occured when processing call."); + } + } + + private void CheckNotFinished() + { + if (finishedStatus.HasValue) + { + throw new InvalidOperationException("Already finished."); + } + } + + private void CheckCancelNotRequested() + { + if (cancelRequested) + { + throw new InvalidOperationException("Cancel has been requested."); + } + } + + private void DisposeResourcesIfNeeded() + { + if (call != null && started && finishedStatus.HasValue) + { + // TODO: should we also wait for all the pending events to finish? + + call.Dispose(); + } + } + + private void HandleEvent(IntPtr eventPtr) { + try { + var ev = new EventSafeHandleNotOwned(eventPtr); + switch (ev.GetCompletionType()) + { + case GRPCCompletionType.GRPC_CLIENT_METADATA_READ: + OnClientMetadata(); + break; + + case GRPCCompletionType.GRPC_READ: + byte[] payload = ev.GetReadData(); + OnRead(payload); + break; + + case GRPCCompletionType.GRPC_WRITE_ACCEPTED: + OnWriteAccepted(ev.GetWriteAccepted()); + break; + + case GRPCCompletionType.GRPC_FINISH_ACCEPTED: + OnFinishAccepted(ev.GetFinishAccepted()); + break; + + case GRPCCompletionType.GRPC_FINISHED: + OnFinished(ev.GetFinished()); + break; + + default: + throw new ArgumentException("Unexpected completion type"); + } + } catch(Exception e) { + Console.WriteLine("Caught exception in a native handler: " + e); + } + } + } +} \ No newline at end of file diff --git a/src/csharp/GrpcCore/Internal/CallSafeHandle.cs b/src/csharp/GrpcCore/Internal/CallSafeHandle.cs new file mode 100644 index 00000000000..6c9c58a4c3f --- /dev/null +++ b/src/csharp/GrpcCore/Internal/CallSafeHandle.cs @@ -0,0 +1,182 @@ +using System; +using System.Runtime.InteropServices; +using System.Diagnostics; +using Google.GRPC.Core; + +namespace Google.GRPC.Core.Internal +{ + // TODO: we need to make sure that the delegates are not collected before invoked. + internal delegate void EventCallbackDelegate(IntPtr eventPtr); + + /// + /// grpc_call from + /// + internal class CallSafeHandle : SafeHandleZeroIsInvalid + { + const UInt32 GRPC_WRITE_BUFFER_HINT = 1; + + [DllImport("libgrpc.so")] + static extern CallSafeHandle grpc_channel_create_call_old(ChannelSafeHandle channel, string method, string host, Timespec deadline); + + [DllImport("libgrpc.so")] + static extern GRPCCallError grpc_call_add_metadata(CallSafeHandle call, IntPtr metadata, UInt32 flags); + + [DllImport("libgrpc.so")] + static extern GRPCCallError grpc_call_invoke_old(CallSafeHandle call, CompletionQueueSafeHandle cq, IntPtr metadataReadTag, IntPtr finishedTag, UInt32 flags); + + [DllImport("libgrpc.so", EntryPoint = "grpc_call_invoke_old")] + static extern GRPCCallError grpc_call_invoke_old_CALLBACK(CallSafeHandle call, CompletionQueueSafeHandle cq, + [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate metadataReadCallback, + [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate finishedCallback, + UInt32 flags); + + [DllImport("libgrpc.so")] + static extern GRPCCallError grpc_call_server_accept_old(CallSafeHandle call, CompletionQueueSafeHandle completionQueue, IntPtr finishedTag); + + [DllImport("libgrpc.so", EntryPoint = "grpc_call_server_accept_old")] + static extern GRPCCallError grpc_call_server_accept_old_CALLBACK(CallSafeHandle call, CompletionQueueSafeHandle completionQueue, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate finishedCallback); + + [DllImport("libgrpc.so")] + static extern GRPCCallError grpc_call_server_end_initial_metadata_old(CallSafeHandle call, UInt32 flags); + + [DllImport("libgrpc.so")] + static extern GRPCCallError grpc_call_cancel(CallSafeHandle call); + + [DllImport("libgrpc.so")] + static extern GRPCCallError grpc_call_cancel_with_status(CallSafeHandle call, StatusCode status, string description); + + [DllImport("libgrpc.so")] + static extern GRPCCallError grpc_call_start_write_status_old(CallSafeHandle call, StatusCode statusCode, string statusMessage, IntPtr tag); + + [DllImport("libgrpc.so", EntryPoint = "grpc_call_start_write_status_old")] + static extern GRPCCallError grpc_call_start_write_status_old_CALLBACK(CallSafeHandle call, StatusCode statusCode, string statusMessage, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); + + [DllImport("libgrpc.so")] + static extern GRPCCallError grpc_call_writes_done_old(CallSafeHandle call, IntPtr tag); + + [DllImport("libgrpc.so", EntryPoint = "grpc_call_writes_done_old")] + static extern GRPCCallError grpc_call_writes_done_old_CALLBACK(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); + + [DllImport("libgrpc.so")] + static extern GRPCCallError grpc_call_start_read_old(CallSafeHandle call, IntPtr tag); + + [DllImport("libgrpc.so", EntryPoint = "grpc_call_start_read_old")] + static extern GRPCCallError grpc_call_start_read_old_CALLBACK(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); + + [DllImport("libgrpc_csharp_ext.so")] + static extern void grpc_call_start_write_from_copied_buffer(CallSafeHandle call, + byte[] buffer, UIntPtr length, + IntPtr tag, UInt32 flags); + + [DllImport("libgrpc_csharp_ext.so", EntryPoint = "grpc_call_start_write_from_copied_buffer")] + static extern void grpc_call_start_write_from_copied_buffer_CALLBACK(CallSafeHandle call, + byte[] buffer, UIntPtr length, + [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback, + UInt32 flags); + + [DllImport("libgrpc.so")] + static extern void grpc_call_destroy(IntPtr call); + + private CallSafeHandle() + { + } + + /// + /// Creates a client call. + /// + public static CallSafeHandle Create(ChannelSafeHandle channel, string method, string host, Timespec deadline) + { + return grpc_channel_create_call_old(channel, method, host, deadline); + } + + public void Invoke(CompletionQueueSafeHandle cq, IntPtr metadataReadTag, IntPtr finishedTag, bool buffered) + { + AssertCallOk(grpc_call_invoke_old(this, cq, metadataReadTag, finishedTag, GetFlags(buffered))); + } + + public void Invoke(CompletionQueueSafeHandle cq, bool buffered, EventCallbackDelegate metadataReadCallback, EventCallbackDelegate finishedCallback) + { + AssertCallOk(grpc_call_invoke_old_CALLBACK(this, cq, metadataReadCallback, finishedCallback, GetFlags(buffered))); + } + + public void ServerAccept(CompletionQueueSafeHandle cq, IntPtr finishedTag) + { + AssertCallOk(grpc_call_server_accept_old(this, cq, finishedTag)); + } + + public void ServerAccept(CompletionQueueSafeHandle cq, EventCallbackDelegate callback) + { + AssertCallOk(grpc_call_server_accept_old_CALLBACK(this, cq, callback)); + } + + public void ServerEndInitialMetadata(UInt32 flags) + { + AssertCallOk(grpc_call_server_end_initial_metadata_old(this, flags)); + } + + public void StartWrite(byte[] payload, IntPtr tag, bool buffered) + { + grpc_call_start_write_from_copied_buffer(this, payload, new UIntPtr((ulong) payload.Length), tag, GetFlags(buffered)); + } + + public void StartWrite(byte[] payload, bool buffered, EventCallbackDelegate callback) + { + grpc_call_start_write_from_copied_buffer_CALLBACK(this, payload, new UIntPtr((ulong) payload.Length), callback, GetFlags(buffered)); + } + + public void StartWriteStatus(Status status, IntPtr tag) + { + AssertCallOk(grpc_call_start_write_status_old(this, status.StatusCode, status.Detail, tag)); + } + + public void StartWriteStatus(Status status, EventCallbackDelegate callback) + { + AssertCallOk(grpc_call_start_write_status_old_CALLBACK(this, status.StatusCode, status.Detail, callback)); + } + + public void WritesDone(IntPtr tag) + { + AssertCallOk(grpc_call_writes_done_old(this, tag)); + } + + public void WritesDone(EventCallbackDelegate callback) + { + AssertCallOk(grpc_call_writes_done_old_CALLBACK(this, callback)); + } + + public void StartRead(IntPtr tag) + { + AssertCallOk(grpc_call_start_read_old(this, tag)); + } + + public void StartRead(EventCallbackDelegate callback) + { + AssertCallOk(grpc_call_start_read_old_CALLBACK(this, callback)); + } + + public void Cancel() + { + AssertCallOk(grpc_call_cancel(this)); + } + + public void CancelWithStatus(Status status) + { + AssertCallOk(grpc_call_cancel_with_status(this, status.StatusCode, status.Detail)); + } + + protected override bool ReleaseHandle() + { + grpc_call_destroy(handle); + return true; + } + + private static void AssertCallOk(GRPCCallError callError) + { + Trace.Assert(callError == GRPCCallError.GRPC_CALL_OK, "Status not GRPC_CALL_OK"); + } + + private static UInt32 GetFlags(bool buffered) { + return buffered ? 0 : GRPC_WRITE_BUFFER_HINT; + } + } +} \ No newline at end of file diff --git a/src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs b/src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs new file mode 100644 index 00000000000..3a09d8b1b64 --- /dev/null +++ b/src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs @@ -0,0 +1,34 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace Google.GRPC.Core.Internal +{ + /// + /// grpc_channel from + /// + internal class ChannelSafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("libgrpc.so")] + static extern ChannelSafeHandle grpc_channel_create(string target, IntPtr channelArgs); + + [DllImport("libgrpc.so")] + static extern void grpc_channel_destroy(IntPtr channel); + + private ChannelSafeHandle() + { + } + + public static ChannelSafeHandle Create(string target, IntPtr channelArgs) + { + return grpc_channel_create(target, channelArgs); + } + + protected override bool ReleaseHandle() + { + grpc_channel_destroy(handle); + return true; + } + } +} \ No newline at end of file diff --git a/src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs b/src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs new file mode 100644 index 00000000000..73dd3edde33 --- /dev/null +++ b/src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs @@ -0,0 +1,66 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace Google.GRPC.Core.Internal +{ + /// + /// grpc_completion_queue from + /// + internal class CompletionQueueSafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("libgrpc.so")] + static extern CompletionQueueSafeHandle grpc_completion_queue_create(); + + [DllImport("libgrpc.so")] + static extern EventSafeHandle grpc_completion_queue_pluck(CompletionQueueSafeHandle cq, IntPtr tag, Timespec deadline); + + [DllImport("libgrpc.so")] + static extern EventSafeHandle grpc_completion_queue_next(CompletionQueueSafeHandle cq, Timespec deadline); + + [DllImport("libgrpc.so")] + static extern void grpc_completion_queue_shutdown(CompletionQueueSafeHandle cq); + + [DllImport("libgrpc_csharp_ext.so")] + static extern GRPCCompletionType grpc_completion_queue_next_with_callback(CompletionQueueSafeHandle cq); + + [DllImport("libgrpc.so")] + static extern void grpc_completion_queue_destroy(IntPtr cq); + + private CompletionQueueSafeHandle() + { + } + + public static CompletionQueueSafeHandle Create() + { + return grpc_completion_queue_create(); + } + + public EventSafeHandle Next(Timespec deadline) + { + return grpc_completion_queue_next(this, deadline); + } + + public GRPCCompletionType NextWithCallback() + { + return grpc_completion_queue_next_with_callback(this); + } + + public EventSafeHandle Pluck(IntPtr tag, Timespec deadline) + { + return grpc_completion_queue_pluck(this, tag, deadline); + } + + public void Shutdown() + { + grpc_completion_queue_shutdown(this); + } + + protected override bool ReleaseHandle() + { + grpc_completion_queue_destroy(handle); + return true; + } + } +} + diff --git a/src/csharp/GrpcCore/Internal/Enums.cs b/src/csharp/GrpcCore/Internal/Enums.cs new file mode 100644 index 00000000000..46e3bca6ebe --- /dev/null +++ b/src/csharp/GrpcCore/Internal/Enums.cs @@ -0,0 +1,75 @@ +using System; +using System.Runtime.InteropServices; + +namespace Google.GRPC.Core.Internal +{ + /// + /// from grpc/grpc.h + /// + internal enum GRPCCallError + { + /* everything went ok */ + GRPC_CALL_OK = 0, + /* something failed, we don't know what */ + GRPC_CALL_ERROR, + /* this method is not available on the server */ + GRPC_CALL_ERROR_NOT_ON_SERVER, + /* this method is not available on the client */ + GRPC_CALL_ERROR_NOT_ON_CLIENT, + /* this method must be called before server_accept */ + GRPC_CALL_ERROR_ALREADY_ACCEPTED, + /* this method must be called before invoke */ + GRPC_CALL_ERROR_ALREADY_INVOKED, + /* this method must be called after invoke */ + GRPC_CALL_ERROR_NOT_INVOKED, + /* this call is already finished + (writes_done or write_status has already been called) */ + GRPC_CALL_ERROR_ALREADY_FINISHED, + /* there is already an outstanding read/write operation on the call */ + GRPC_CALL_ERROR_TOO_MANY_OPERATIONS, + /* the flags value was illegal for this call */ + GRPC_CALL_ERROR_INVALID_FLAGS + } + + /// + /// grpc_completion_type from grpc/grpc.h + /// + internal enum GRPCCompletionType + { + GRPC_QUEUE_SHUTDOWN, + /* Shutting down */ + GRPC_READ, + /* A read has completed */ + GRPC_INVOKE_ACCEPTED, + /* An invoke call has been accepted by flow + control */ + GRPC_WRITE_ACCEPTED, + /* A write has been accepted by + flow control */ + GRPC_FINISH_ACCEPTED, + /* writes_done or write_status has been accepted */ + GRPC_CLIENT_METADATA_READ, + /* The metadata array sent by server received at + client */ + GRPC_FINISHED, + /* An RPC has finished. The event contains status. + On the server this will be OK or Cancelled. */ + GRPC_SERVER_RPC_NEW, + /* A new RPC has arrived at the server */ + GRPC_COMPLETION_DO_NOT_USE + /* must be last, forces users to include + a default: case */ + } + + /// + /// grpc_op_error from grpc/grpc.h + /// + internal enum GRPCOpError + { + /* everything went ok */ + GRPC_OP_OK = 0, + /* something failed, we don't know what */ + GRPC_OP_ERROR + } +} + diff --git a/src/csharp/GrpcCore/Internal/Event.cs b/src/csharp/GrpcCore/Internal/Event.cs new file mode 100644 index 00000000000..7056005ba65 --- /dev/null +++ b/src/csharp/GrpcCore/Internal/Event.cs @@ -0,0 +1,191 @@ +using System; +using System.Runtime.InteropServices; +using Google.GRPC.Core; + +namespace Google.GRPC.Core.Internal +{ + /// + /// grpc_event from grpc/grpc.h + /// + internal class EventSafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("libgrpc.so")] + static extern void grpc_event_finish(IntPtr ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern GRPCCompletionType grpc_event_type(EventSafeHandle ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern CallSafeHandle grpc_event_call(EventSafeHandle ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern GRPCOpError grpc_event_write_accepted(EventSafeHandle ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern GRPCOpError grpc_event_finish_accepted(EventSafeHandle ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern StatusCode grpc_event_finished_status(EventSafeHandle ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern IntPtr grpc_event_finished_details(EventSafeHandle ev); // returns const char* + + [DllImport("libgrpc_csharp_ext.so")] + static extern IntPtr grpc_event_read_length(EventSafeHandle ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern void grpc_event_read_copy_to_buffer(EventSafeHandle ev, byte[] buffer, UIntPtr bufferLen); + + [DllImport("libgrpc_csharp_ext.so")] + static extern IntPtr grpc_event_server_rpc_new_method(EventSafeHandle ev); // returns const char* + + public GRPCCompletionType GetCompletionType() + { + return grpc_event_type(this); + } + + public GRPCOpError GetWriteAccepted() + { + return grpc_event_write_accepted(this); + } + + public GRPCOpError GetFinishAccepted() + { + return grpc_event_finish_accepted(this); + } + + public Status GetFinished() + { + // TODO: can the native method return string directly? + string details = Marshal.PtrToStringAnsi(grpc_event_finished_details(this)); + return new Status(grpc_event_finished_status(this), details); + } + + public byte[] GetReadData() + { + IntPtr len = grpc_event_read_length(this); + if (len == new IntPtr(-1)) + { + return null; + } + byte[] data = new byte[(int) len]; + grpc_event_read_copy_to_buffer(this, data, new UIntPtr((ulong)data.Length)); + return data; + } + + public CallSafeHandle GetCall() { + return grpc_event_call(this); + } + + public string GetServerRpcNewMethod() { + // TODO: can the native method return string directly? + return Marshal.PtrToStringAnsi(grpc_event_server_rpc_new_method(this)); + } + + //TODO: client_metadata_read event type + + protected override bool ReleaseHandle() + { + grpc_event_finish(handle); + return true; + } + } + + // TODO: this is basically c&p of EventSafeHandle. Unify! + /// + /// Not owned version of + /// grpc_event from grpc/grpc.h + /// + internal class EventSafeHandleNotOwned : SafeHandleZeroIsInvalid + { + [DllImport("libgrpc.so")] + static extern void grpc_event_finish(IntPtr ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern GRPCCompletionType grpc_event_type(EventSafeHandleNotOwned ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern CallSafeHandle grpc_event_call(EventSafeHandleNotOwned ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern GRPCOpError grpc_event_write_accepted(EventSafeHandleNotOwned ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern GRPCOpError grpc_event_finish_accepted(EventSafeHandleNotOwned ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern StatusCode grpc_event_finished_status(EventSafeHandleNotOwned ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern IntPtr grpc_event_finished_details(EventSafeHandleNotOwned ev); // returns const char* + + [DllImport("libgrpc_csharp_ext.so")] + static extern IntPtr grpc_event_read_length(EventSafeHandleNotOwned ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern void grpc_event_read_copy_to_buffer(EventSafeHandleNotOwned ev, byte[] buffer, UIntPtr bufferLen); + + [DllImport("libgrpc_csharp_ext.so")] + static extern IntPtr grpc_event_server_rpc_new_method(EventSafeHandleNotOwned ev); // returns const char* + + public EventSafeHandleNotOwned() : base(false) + { + } + + public EventSafeHandleNotOwned(IntPtr handle) : base(false) + { + SetHandle(handle); + } + + public GRPCCompletionType GetCompletionType() + { + return grpc_event_type(this); + } + + public GRPCOpError GetWriteAccepted() + { + return grpc_event_write_accepted(this); + } + + public GRPCOpError GetFinishAccepted() + { + return grpc_event_finish_accepted(this); + } + + public Status GetFinished() + { + // TODO: can the native method return string directly? + string details = Marshal.PtrToStringAnsi(grpc_event_finished_details(this)); + return new Status(grpc_event_finished_status(this), details); + } + + public byte[] GetReadData() + { + IntPtr len = grpc_event_read_length(this); + if (len == new IntPtr(-1)) + { + return null; + } + byte[] data = new byte[(int) len]; + grpc_event_read_copy_to_buffer(this, data, new UIntPtr((ulong)data.Length)); + return data; + } + + public CallSafeHandle GetCall() { + return grpc_event_call(this); + } + + public string GetServerRpcNewMethod() { + // TODO: can the native method return string directly? + return Marshal.PtrToStringAnsi(grpc_event_server_rpc_new_method(this)); + } + + //TODO: client_metadata_read event type + + protected override bool ReleaseHandle() + { + grpc_event_finish(handle); + return true; + } + } +} \ No newline at end of file diff --git a/src/csharp/GrpcCore/Internal/GrpcThreadPool.cs b/src/csharp/GrpcCore/Internal/GrpcThreadPool.cs new file mode 100644 index 00000000000..1139e54a1d7 --- /dev/null +++ b/src/csharp/GrpcCore/Internal/GrpcThreadPool.cs @@ -0,0 +1,129 @@ +using System; +using Google.GRPC.Core.Internal; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; + +namespace Google.GRPC.Core.Internal +{ + /// + /// Pool of threads polling on the same completion queue. + /// + internal class GrpcThreadPool + { + readonly object myLock = new object(); + readonly List threads = new List(); + readonly int poolSize; + readonly Action eventHandler; + + CompletionQueueSafeHandle cq; + + public GrpcThreadPool(int poolSize) { + this.poolSize = poolSize; + } + + internal GrpcThreadPool(int poolSize, Action eventHandler) { + this.poolSize = poolSize; + this.eventHandler = eventHandler; + } + + public void Start() { + + lock (myLock) + { + if (cq != null) + { + throw new InvalidOperationException("Already started."); + } + + cq = CompletionQueueSafeHandle.Create(); + + for (int i = 0; i < poolSize; i++) + { + threads.Add(CreateAndStartThread(i)); + } + } + } + + public void Stop() { + + lock (myLock) + { + cq.Shutdown(); + + Console.WriteLine("Waiting for GPRC threads to finish."); + foreach (var thread in threads) + { + thread.Join(); + } + + cq.Dispose(); + + } + } + + internal CompletionQueueSafeHandle CompletionQueue + { + get + { + return cq; + } + } + + private Thread CreateAndStartThread(int i) { + Action body; + if (eventHandler != null) + { + body = ThreadBodyWithHandler; + } + else + { + body = ThreadBodyNoHandler; + } + var thread = new Thread(new ThreadStart(body)); + thread.IsBackground = false; + thread.Start(); + if (eventHandler != null) + { + thread.Name = "grpc_server_newrpc " + i; + } + else + { + thread.Name = "grpc " + i; + } + return thread; + } + + /// + /// Body of the polling thread. + /// + private void ThreadBodyNoHandler() + { + GRPCCompletionType completionType; + do + { + completionType = cq.NextWithCallback(); + } while(completionType != GRPCCompletionType.GRPC_QUEUE_SHUTDOWN); + Console.WriteLine("Completion queue has shutdown successfully, thread " + Thread.CurrentThread.Name + " exiting."); + } + + /// + /// Body of the polling thread. + /// + private void ThreadBodyWithHandler() + { + GRPCCompletionType completionType; + do + { + using (EventSafeHandle ev = cq.Next(Timespec.InfFuture)) { + completionType = ev.GetCompletionType(); + eventHandler(ev); + } + } while(completionType != GRPCCompletionType.GRPC_QUEUE_SHUTDOWN); + Console.WriteLine("Completion queue has shutdown successfully, thread " + Thread.CurrentThread.Name + " exiting."); + } + } + +} + diff --git a/src/csharp/GrpcCore/Internal/SafeHandleZeroIsInvalid.cs b/src/csharp/GrpcCore/Internal/SafeHandleZeroIsInvalid.cs new file mode 100644 index 00000000000..5a1252b8814 --- /dev/null +++ b/src/csharp/GrpcCore/Internal/SafeHandleZeroIsInvalid.cs @@ -0,0 +1,28 @@ +using System; +using System.Runtime.InteropServices; + +namespace Google.GRPC.Core.Internal +{ + /// + /// Safe handle to wrap native objects. + /// + internal abstract class SafeHandleZeroIsInvalid : SafeHandle + { + public SafeHandleZeroIsInvalid() : base(IntPtr.Zero, true) + { + } + + public SafeHandleZeroIsInvalid(bool ownsHandle) : base(IntPtr.Zero, ownsHandle) + { + } + + public override bool IsInvalid + { + get + { + return handle == IntPtr.Zero; + } + } + } +} + diff --git a/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs b/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs new file mode 100644 index 00000000000..0d38bce63e3 --- /dev/null +++ b/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs @@ -0,0 +1,76 @@ +using System; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Collections.Concurrent; + +namespace Google.GRPC.Core.Internal +{ + /// + /// grpc_server from grpc/grpc.h + /// + internal sealed class ServerSafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("libgrpc.so", EntryPoint = "grpc_server_request_call_old")] + static extern GRPCCallError grpc_server_request_call_old_CALLBACK(ServerSafeHandle server, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); + + [DllImport("libgrpc.so")] + static extern ServerSafeHandle grpc_server_create(CompletionQueueSafeHandle cq, IntPtr args); + + // TODO: check int representation size + [DllImport("libgrpc.so")] + static extern int grpc_server_add_http2_port(ServerSafeHandle server, string addr); + + // TODO: check int representation size + [DllImport("libgrpc.so")] + static extern int grpc_server_add_secure_http2_port(ServerSafeHandle server, string addr); + + [DllImport("libgrpc.so")] + static extern void grpc_server_start(ServerSafeHandle server); + + [DllImport("libgrpc.so")] + static extern void grpc_server_shutdown(ServerSafeHandle server); + + [DllImport("libgrpc.so")] + static extern void grpc_server_shutdown_and_notify(ServerSafeHandle server, IntPtr tag); + + [DllImport("libgrpc.so")] + static extern void grpc_server_destroy(IntPtr server); + + private ServerSafeHandle() + { + } + + public static ServerSafeHandle NewServer(CompletionQueueSafeHandle cq, IntPtr args) + { + // TODO: also grpc_secure_server_create... + return grpc_server_create(cq, args); + } + + public int AddPort(string addr) + { + // TODO: also grpc_server_add_secure_http2_port... + return grpc_server_add_http2_port(this, addr); + } + + public void Start() + { + grpc_server_start(this); + } + + public void Shutdown() + { + grpc_server_shutdown(this); + } + + public GRPCCallError RequestCall(EventCallbackDelegate callback) + { + return grpc_server_request_call_old_CALLBACK(this, callback); + } + + protected override bool ReleaseHandle() + { + grpc_server_destroy(handle); + return true; + } + } +} \ No newline at end of file diff --git a/src/csharp/GrpcCore/Internal/StreamingInputObserver.cs b/src/csharp/GrpcCore/Internal/StreamingInputObserver.cs new file mode 100644 index 00000000000..d483e53a2db --- /dev/null +++ b/src/csharp/GrpcCore/Internal/StreamingInputObserver.cs @@ -0,0 +1,33 @@ +using System; +using Google.GRPC.Core.Internal; + +namespace Google.GRPC.Core +{ + internal class StreamingInputObserver : IObserver + { + readonly AsyncCall call; + + public StreamingInputObserver(AsyncCall call) + { + this.call = call; + } + + public void OnCompleted() + { + // TODO: how bad is the Wait here? + call.WritesCompletedAsync().Wait(); + } + + public void OnError(Exception error) + { + throw new InvalidOperationException("This should never be called."); + } + + public void OnNext(TWrite value) + { + // TODO: how bad is the Wait here? + call.WriteAsync(value).Wait(); + } + } +} + diff --git a/src/csharp/GrpcCore/Internal/Timespec.cs b/src/csharp/GrpcCore/Internal/Timespec.cs new file mode 100644 index 00000000000..8ffaf70bbfb --- /dev/null +++ b/src/csharp/GrpcCore/Internal/Timespec.cs @@ -0,0 +1,67 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Google.GRPC.Core.Internal +{ + /// + /// gpr_timespec from grpc/support/time.h + /// + [StructLayout(LayoutKind.Sequential)] + internal struct Timespec + { + const int nanosPerSecond = 1000 * 1000 * 1000; + const int nanosPerTick = 100; + + [DllImport("libgpr.so")] + static extern Timespec gpr_now(); + + // TODO: this only works on 64bit linux, can we autoselect the right size of ints? + // perhaps using IntPtr would work. + public System.Int64 tv_sec; + public System.Int64 tv_nsec; + + /// + /// Timespec a long time in the future. + /// + public static Timespec InfFuture + { + get + { + // TODO: set correct value based on the length of the struct + return new Timespec { tv_sec = Int32.MaxValue, tv_nsec = 0 }; + } + } + + public static Timespec Now + { + get + { + return gpr_now(); + } + } + + /// + /// Creates a GPR deadline from current instant and given timeout. + /// + /// The from timeout. + public static Timespec DeadlineFromTimeout(TimeSpan timeout) { + if (timeout == Timeout.InfiniteTimeSpan) + { + return Timespec.InfFuture; + } + return Timespec.Now.Add(timeout); + } + + public Timespec Add(TimeSpan timeSpan) { + long nanos = tv_nsec + (timeSpan.Ticks % TimeSpan.TicksPerSecond) * nanosPerTick; + long overflow_sec = (nanos > nanosPerSecond) ? 1 : 0; + + Timespec result; + result.tv_nsec = nanos % nanosPerSecond; + result.tv_sec = tv_sec + (timeSpan.Ticks / TimeSpan.TicksPerSecond) + overflow_sec; + return result; + } + } +} + diff --git a/src/csharp/GrpcCore/Properties/AssemblyInfo.cs b/src/csharp/GrpcCore/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..74aba257678 --- /dev/null +++ b/src/csharp/GrpcCore/Properties/AssemblyInfo.cs @@ -0,0 +1,24 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. +[assembly: AssemblyTitle ("GrpcCore")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("jtattermusch")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. +[assembly: AssemblyVersion ("1.0.*")] +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + +[assembly: InternalsVisibleTo("GrpcCoreTests")] + diff --git a/src/csharp/GrpcCore/RpcException.cs b/src/csharp/GrpcCore/RpcException.cs new file mode 100644 index 00000000000..8811c3a7c75 --- /dev/null +++ b/src/csharp/GrpcCore/RpcException.cs @@ -0,0 +1,27 @@ +using System; + +namespace Google.GRPC.Core +{ + public class RpcException : Exception + { + private readonly Status status; + + public RpcException(Status status) + { + this.status = status; + } + + public RpcException(Status status, string message) : base(message) + { + this.status = status; + } + + public Status Status { + get + { + return status; + } + } + } +} + diff --git a/src/csharp/GrpcCore/Server.cs b/src/csharp/GrpcCore/Server.cs new file mode 100644 index 00000000000..68da1a83008 --- /dev/null +++ b/src/csharp/GrpcCore/Server.cs @@ -0,0 +1,141 @@ +using System; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Collections.Concurrent; +using Google.GRPC.Core.Internal; + +namespace Google.GRPC.Core +{ + /// + /// Server is implemented only to be able to do + /// in-process testing. + /// + public class Server + { + // TODO: make sure the delegate doesn't get garbage collected while + // native callbacks are in the completion queue. + readonly EventCallbackDelegate newRpcHandler; + + readonly BlockingCollection newRpcQueue = new BlockingCollection(); + readonly ServerSafeHandle handle; + + static Server() { + GrpcEnvironment.EnsureInitialized(); + } + + public Server() + { + // TODO: what is the tag for server shutdown? + this.handle = ServerSafeHandle.NewServer(GetCompletionQueue(), IntPtr.Zero); + this.newRpcHandler = HandleNewRpc; + } + + public int AddPort(string addr) { + return handle.AddPort(addr); + } + + public void Start() + { + handle.Start(); + } + + public void RunRpc() + { + AllowOneRpc(); + + try { + var rpcInfo = newRpcQueue.Take(); + + Console.WriteLine("Server received RPC " + rpcInfo.Method); + + AsyncCall asyncCall = new AsyncCall( + (payload) => payload, (payload) => payload); + + asyncCall.InitializeServer(rpcInfo.Call); + + asyncCall.Accept(GetCompletionQueue()); + + while(true) { + byte[] payload = asyncCall.ReadAsync().Result; + if (payload == null) + { + break; + } + } + + asyncCall.WriteAsync(new byte[] { }).Wait(); + + // TODO: what should be the details? + asyncCall.WriteStatusAsync(new Status(StatusCode.GRPC_STATUS_OK, "")).Wait(); + + asyncCall.Finished.Wait(); + } catch(Exception e) { + Console.WriteLine("Exception while handling RPC: " + e); + } + } + + // TODO: implement disposal properly... + public void Shutdown() { + handle.Shutdown(); + + + //handle.Dispose(); + } + + private void AllowOneRpc() + { + AssertCallOk(handle.RequestCall(newRpcHandler)); + } + + private void HandleNewRpc(IntPtr eventPtr) + { + try + { + var ev = new EventSafeHandleNotOwned(eventPtr); + newRpcQueue.Add(new NewRpcInfo(ev.GetCall(), ev.GetServerRpcNewMethod())); + } + catch (Exception e) + { + Console.WriteLine("Caught exception in a native handler: " + e); + } + } + + private static void AssertCallOk(GRPCCallError callError) + { + Trace.Assert(callError == GRPCCallError.GRPC_CALL_OK, "Status not GRPC_CALL_OK"); + } + + private static CompletionQueueSafeHandle GetCompletionQueue() + { + return GrpcEnvironment.ThreadPool.CompletionQueue; + } + + private struct NewRpcInfo + { + private CallSafeHandle call; + private string method; + + public NewRpcInfo(CallSafeHandle call, string method) + { + this.call = call; + this.method = method; + } + + public CallSafeHandle Call + { + get + { + return this.call; + } + } + + public string Method + { + get + { + return this.method; + } + } + } + } +} \ No newline at end of file diff --git a/src/csharp/GrpcCore/Status.cs b/src/csharp/GrpcCore/Status.cs new file mode 100644 index 00000000000..f1212f8d672 --- /dev/null +++ b/src/csharp/GrpcCore/Status.cs @@ -0,0 +1,36 @@ +using System; +using System.Runtime.InteropServices; + +namespace Google.GRPC.Core +{ + /// + /// Represents RPC result. + /// + public struct Status + { + readonly StatusCode statusCode; + readonly string detail; + + public Status(StatusCode statusCode, string detail) + { + this.statusCode = statusCode; + this.detail = detail; + } + + public StatusCode StatusCode + { + get + { + return statusCode; + } + } + + public string Detail + { + get + { + return detail; + } + } + } +} \ No newline at end of file diff --git a/src/csharp/GrpcCore/StatusCode.cs b/src/csharp/GrpcCore/StatusCode.cs new file mode 100644 index 00000000000..80fc8bd5815 --- /dev/null +++ b/src/csharp/GrpcCore/StatusCode.cs @@ -0,0 +1,150 @@ +using System; + +namespace Google.GRPC.Core +{ + // TODO: element names should changed to comply with C# naming conventions. + /// + /// grpc_status_code from grpc/status.h + /// + public enum StatusCode + { + /* Not an error; returned on success + + HTTP Mapping: 200 OK */ + GRPC_STATUS_OK = 0, + /* The operation was cancelled (typically by the caller). + + HTTP Mapping: 499 Client Closed Request */ + GRPC_STATUS_CANCELLED = 1, + /* Unknown error. An example of where this error may be returned is + if a Status value received from another address space belongs to + an error-space that is not known in this address space. Also + errors raised by APIs that do not return enough error information + may be converted to this error. + + HTTP Mapping: 500 Internal Server Error */ + GRPC_STATUS_UNKNOWN = 2, + /* Client specified an invalid argument. Note that this differs + from FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments + that are problematic regardless of the state of the system + (e.g., a malformed file name). + + HTTP Mapping: 400 Bad Request */ + GRPC_STATUS_INVALID_ARGUMENT = 3, + /* Deadline expired before operation could complete. For operations + that change the state of the system, this error may be returned + even if the operation has completed successfully. For example, a + successful response from a server could have been delayed long + enough for the deadline to expire. + + HTTP Mapping: 504 Gateway Timeout */ + GRPC_STATUS_DEADLINE_EXCEEDED = 4, + /* Some requested entity (e.g., file or directory) was not found. + + HTTP Mapping: 404 Not Found */ + GRPC_STATUS_NOT_FOUND = 5, + /* Some entity that we attempted to create (e.g., file or directory) + already exists. + + HTTP Mapping: 409 Conflict */ + GRPC_STATUS_ALREADY_EXISTS = 6, + /* The caller does not have permission to execute the specified + operation. PERMISSION_DENIED must not be used for rejections + caused by exhausting some resource (use RESOURCE_EXHAUSTED + instead for those errors). PERMISSION_DENIED must not be + used if the caller can not be identified (use UNAUTHENTICATED + instead for those errors). + + HTTP Mapping: 403 Forbidden */ + GRPC_STATUS_PERMISSION_DENIED = 7, + /* The request does not have valid authentication credentials for the + operation. + + HTTP Mapping: 401 Unauthorized */ + GRPC_STATUS_UNAUTHENTICATED = 16, + /* Some resource has been exhausted, perhaps a per-user quota, or + perhaps the entire file system is out of space. + + HTTP Mapping: 429 Too Many Requests */ + GRPC_STATUS_RESOURCE_EXHAUSTED = 8, + /* Operation was rejected because the system is not in a state + required for the operation's execution. For example, directory + to be deleted may be non-empty, an rmdir operation is applied to + a non-directory, etc. + + A litmus test that may help a service implementor in deciding + between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE: + (a) Use UNAVAILABLE if the client can retry just the failing call. + (b) Use ABORTED if the client should retry at a higher-level + (e.g., restarting a read-modify-write sequence). + (c) Use FAILED_PRECONDITION if the client should not retry until + the system state has been explicitly fixed. E.g., if an "rmdir" + fails because the directory is non-empty, FAILED_PRECONDITION + should be returned since the client should not retry unless + they have first fixed up the directory by deleting files from it. + (d) Use FAILED_PRECONDITION if the client performs conditional + REST Get/Update/Delete on a resource and the resource on the + server does not match the condition. E.g., conflicting + read-modify-write on the same resource. + + HTTP Mapping: 400 Bad Request + + NOTE: HTTP spec says 412 Precondition Failed should only be used if + the request contains Etag related headers. So if the server does see + Etag related headers in the request, it may choose to return 412 + instead of 400 for this error code. */ + GRPC_STATUS_FAILED_PRECONDITION = 9, + /* The operation was aborted, typically due to a concurrency issue + like sequencer check failures, transaction aborts, etc. + + See litmus test above for deciding between FAILED_PRECONDITION, + ABORTED, and UNAVAILABLE. + + HTTP Mapping: 409 Conflict */ + GRPC_STATUS_ABORTED = 10, + /* Operation was attempted past the valid range. E.g., seeking or + reading past end of file. + + Unlike INVALID_ARGUMENT, this error indicates a problem that may + be fixed if the system state changes. For example, a 32-bit file + system will generate INVALID_ARGUMENT if asked to read at an + offset that is not in the range [0,2^32-1], but it will generate + OUT_OF_RANGE if asked to read from an offset past the current + file size. + + There is a fair bit of overlap between FAILED_PRECONDITION and + OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific + error) when it applies so that callers who are iterating through + a space can easily look for an OUT_OF_RANGE error to detect when + they are done. + + HTTP Mapping: 400 Bad Request */ + GRPC_STATUS_OUT_OF_RANGE = 11, + /* Operation is not implemented or not supported/enabled in this service. + + HTTP Mapping: 501 Not Implemented */ + GRPC_STATUS_UNIMPLEMENTED = 12, + /* Internal errors. Means some invariants expected by underlying + system has been broken. If you see one of these errors, + something is very broken. + + HTTP Mapping: 500 Internal Server Error */ + GRPC_STATUS_INTERNAL = 13, + /* The service is currently unavailable. This is a most likely a + transient condition and may be corrected by retrying with + a backoff. + + See litmus test above for deciding between FAILED_PRECONDITION, + ABORTED, and UNAVAILABLE. + + HTTP Mapping: 503 Service Unavailable */ + GRPC_STATUS_UNAVAILABLE = 14, + /* Unrecoverable data loss or corruption. + + HTTP Mapping: 500 Internal Server Error */ + GRPC_STATUS_DATA_LOSS = 15, + /* Force users to include a default branch: */ + GRPC_STATUS__DO_NOT_USE = -1 + } +} + diff --git a/src/csharp/GrpcCoreTests/.gitignore b/src/csharp/GrpcCoreTests/.gitignore new file mode 100644 index 00000000000..2cc8cca52d0 --- /dev/null +++ b/src/csharp/GrpcCoreTests/.gitignore @@ -0,0 +1,2 @@ +test-results +bin diff --git a/src/csharp/GrpcCoreTests/ClientServerTest.cs b/src/csharp/GrpcCoreTests/ClientServerTest.cs new file mode 100644 index 00000000000..823ee942882 --- /dev/null +++ b/src/csharp/GrpcCoreTests/ClientServerTest.cs @@ -0,0 +1,48 @@ +using System; +using NUnit.Framework; +using Google.GRPC.Core.Internal; +using System.Threading; +using System.Threading.Tasks; + +namespace Google.GRPC.Core.Tests +{ + public class ClientServerTest + { + string request = "REQUEST"; + string serverAddr = "localhost:" + Utils.PickUnusedPort(); + + [Test] + public void EmptyCall() + { + Server server = new Server(); + server.AddPort(serverAddr); + server.Start(); + + Task.Factory.StartNew( + () => { + server.RunRpc(); + } + ); + + using (Channel channel = new Channel(serverAddr)) + { + CreateCall(channel); + string response = Calls.BlockingUnaryCall(CreateCall(channel), request, default(CancellationToken)); + Console.WriteLine("Received response: " + response); + } + + server.Shutdown(); + + GrpcEnvironment.Shutdown(); + } + + private Call CreateCall(Channel channel) + { + return new Call("/tests.Test/EmptyCall", + (s) => System.Text.Encoding.ASCII.GetBytes(s), + (b) => System.Text.Encoding.ASCII.GetString(b), + Timeout.InfiniteTimeSpan, channel); + } + } +} + diff --git a/src/csharp/GrpcCoreTests/GrpcCoreTests.csproj b/src/csharp/GrpcCoreTests/GrpcCoreTests.csproj new file mode 100644 index 00000000000..3de0f585cda --- /dev/null +++ b/src/csharp/GrpcCoreTests/GrpcCoreTests.csproj @@ -0,0 +1,53 @@ + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {86EC5CB4-4EA2-40A2-8057-86542A0353BB} + Library + GrpcCoreTests + GrpcCoreTests + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + + + full + true + bin\Release + prompt + 4 + false + + + + + False + + + + + + + + + + + + + + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7} + GrpcCore + + + \ No newline at end of file diff --git a/src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs b/src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs new file mode 100644 index 00000000000..136878d76eb --- /dev/null +++ b/src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs @@ -0,0 +1,18 @@ +using System; +using NUnit.Framework; +using Google.GRPC.Core; +using System.Threading; + +namespace Google.GRPC.Core.Tests +{ + public class GrpcEnvironmentTest + { + [Test] + public void InitializeAndShutdownGrpcEnvironment() { + GrpcEnvironment.EnsureInitialized(); + Thread.Sleep(500); + Assert.IsNotNull(GrpcEnvironment.ThreadPool.CompletionQueue); + GrpcEnvironment.Shutdown(); + } + } +} diff --git a/src/csharp/GrpcCoreTests/Properties/AssemblyInfo.cs b/src/csharp/GrpcCoreTests/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..565b1e2bd65 --- /dev/null +++ b/src/csharp/GrpcCoreTests/Properties/AssemblyInfo.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. +[assembly: AssemblyTitle("GrpcCoreTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("jtattermusch")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. +[assembly: AssemblyVersion("1.0.*")] +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/src/csharp/GrpcCoreTests/ServerTest.cs b/src/csharp/GrpcCoreTests/ServerTest.cs new file mode 100644 index 00000000000..b34101bbf59 --- /dev/null +++ b/src/csharp/GrpcCoreTests/ServerTest.cs @@ -0,0 +1,21 @@ +using System; +using NUnit.Framework; +using Google.GRPC.Core.Internal; + +namespace Google.GRPC.Core.Tests +{ + public class ServerTest + { + [Test] + public void StartAndShutdownServer() { + + Server server = new Server(); + server.AddPort("localhost:" + Utils.PickUnusedPort()); + server.Start(); + server.Shutdown(); + + GrpcEnvironment.Shutdown(); + } + + } +} diff --git a/src/csharp/GrpcCoreTests/TestResult.xml b/src/csharp/GrpcCoreTests/TestResult.xml new file mode 100644 index 00000000000..a5a6abd7b95 --- /dev/null +++ b/src/csharp/GrpcCoreTests/TestResult.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/csharp/GrpcCoreTests/TimespecTest.cs b/src/csharp/GrpcCoreTests/TimespecTest.cs new file mode 100644 index 00000000000..484bad7ca19 --- /dev/null +++ b/src/csharp/GrpcCoreTests/TimespecTest.cs @@ -0,0 +1,43 @@ +using System; +using NUnit.Framework; +using Google.GRPC.Core.Internal; + +namespace Google.GRPC.Core.Internal.Tests +{ + public class TimespecTest + { + [Test] + public void Now() + { + var timespec = Timespec.Now; + } + + [Test] + public void Add() + { + var t = new Timespec { tv_sec = 12345, tv_nsec = 123456789 }; + var result = t.Add(TimeSpan.FromTicks(TimeSpan.TicksPerSecond * 10)); + Assert.AreEqual(result.tv_sec, 12355); + Assert.AreEqual(result.tv_nsec, 123456789); + } + + [Test] + public void Add_Nanos() + { + var t = new Timespec { tv_sec = 12345, tv_nsec = 123456789 }; + var result = t.Add(TimeSpan.FromTicks(10)); + Assert.AreEqual(result.tv_sec, 12345); + Assert.AreEqual(result.tv_nsec, 123456789 + 1000); + } + + [Test] + public void Add_NanosOverflow() + { + var t = new Timespec { tv_sec = 12345, tv_nsec = 999999999 }; + var result = t.Add(TimeSpan.FromTicks(TimeSpan.TicksPerSecond * 10 + 10)); + Assert.AreEqual(result.tv_sec, 12356); + Assert.AreEqual(result.tv_nsec, 999); + } + } +} + diff --git a/src/csharp/GrpcCoreTests/Utils.cs b/src/csharp/GrpcCoreTests/Utils.cs new file mode 100644 index 00000000000..b0c0a7b6205 --- /dev/null +++ b/src/csharp/GrpcCoreTests/Utils.cs @@ -0,0 +1,51 @@ +using System; +using System.Net; +using System.Net.Sockets; + +namespace Google.GRPC.Core.Tests +{ + /// + /// Testing utils. + /// + public class Utils + { + static Random random = new Random(); + // TODO: cleanup this code a bit + public static int PickUnusedPort() + { + int port; + do + { + port = random.Next(2000, 50000); + + } while(!IsPortAvailable(port)); + return port; + } + // TODO: cleanup this code a bit + public static bool IsPortAvailable(int port) + { + bool available = true; + + TcpListener server = null; + try + { + IPAddress ipAddress = Dns.GetHostEntry("localhost").AddressList[0]; + server = new TcpListener(ipAddress, port); + server.Start(); + } + catch (Exception ex) + { + available = false; + } + finally + { + if (server != null) + { + server.Stop(); + } + } + return available; + } + } +} + diff --git a/src/csharp/GrpcDemo/.gitignore b/src/csharp/GrpcDemo/.gitignore new file mode 100644 index 00000000000..ba077a4031a --- /dev/null +++ b/src/csharp/GrpcDemo/.gitignore @@ -0,0 +1 @@ +bin diff --git a/src/csharp/GrpcDemo/GrpcDemo.csproj b/src/csharp/GrpcDemo/GrpcDemo.csproj new file mode 100644 index 00000000000..31ce7f133b3 --- /dev/null +++ b/src/csharp/GrpcDemo/GrpcDemo.csproj @@ -0,0 +1,52 @@ + + + + Debug + x86 + 10.0.0 + 2.0 + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0} + Exe + GrpcDemo + GrpcDemo + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + x86 + + + full + true + bin\Release + prompt + 4 + true + x86 + + + + + + + + + + + + {7DC1433E-3225-42C7-B7EA-546D56E27A4B} + GrpcApi + + + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7} + GrpcCore + + + \ No newline at end of file diff --git a/src/csharp/GrpcDemo/Program.cs b/src/csharp/GrpcDemo/Program.cs new file mode 100644 index 00000000000..258762dbb99 --- /dev/null +++ b/src/csharp/GrpcDemo/Program.cs @@ -0,0 +1,28 @@ +using System; +using System.Runtime.InteropServices; +using Google.GRPC.Core; +using System.Threading; +using math; + +namespace Google.GRPC.Demo +{ + class MainClass + { + public static void Main (string[] args) + { + using (Channel channel = new Channel("127.0.0.1:23456")) + { + IMathServiceClient stub = new MathServiceClientStub(channel, Timeout.InfiniteTimeSpan); + Examples.DivExample(stub); + + Examples.FibExample(stub); + + Examples.SumExample(stub); + + Examples.DivManyExample(stub); + } + + GrpcEnvironment.Shutdown(); + } + } +} diff --git a/src/csharp/GrpcDemo/Properties/AssemblyInfo.cs b/src/csharp/GrpcDemo/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..b8e1406da7c --- /dev/null +++ b/src/csharp/GrpcDemo/Properties/AssemblyInfo.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. +[assembly: AssemblyTitle ("GrpcDemo")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("jtattermusch")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. +[assembly: AssemblyVersion ("1.0.*")] +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/src/csharp/README.md b/src/csharp/README.md index 5b56303c147..c2f988cc5e3 100755 --- a/src/csharp/README.md +++ b/src/csharp/README.md @@ -14,9 +14,41 @@ EXPERIMENTAL ONLY - It is very possible that some parts of the code will be heavily refactored or completely rewritten. + +INSTALLATION AND USAGE +---------------------- + +- Compile and install the gRPC C Core library +``` +make shared_c +sudo make install +``` + +- Prerequisites for development: Mono framework, MonoDevelop (IDE) +``` +sudo apt-get install mono-devel +sudo apt-get install monodevelop monodevelop-nunit +sudo apt-get install nunit nunit-console +``` + +- Use MonoDevelop to open the solution Grpc.sln (you can also run unit tests + from there). + +- After building the solution with MonoDevelop, you can use + nunit-console to run the unit tests (currently only running one by + one will make them pass. + +``` +nunit-console GrpcCoreTests.dll +``` + CONTENTS -------- - ext: The extension library that wraps C API to be more digestible by C#. +- GrpcCore: + The main gRPC C# library. +- GrpcApi: + API examples for math.proto. diff --git a/src/csharp/lib/Google.ProtocolBuffers.dll b/src/csharp/lib/Google.ProtocolBuffers.dll new file mode 100755 index 00000000000..ce2f466b243 Binary files /dev/null and b/src/csharp/lib/Google.ProtocolBuffers.dll differ diff --git a/src/node/README.md b/src/node/README.md index 55329d8cb2f..c342b7ca575 100644 --- a/src/node/README.md +++ b/src/node/README.md @@ -1,12 +1,70 @@ -# Node.js GRPC extension +# Node.js gRPC Library -The package is built with +## Installation - node-gyp configure - node-gyp build +First, clone this repository (NPM package coming soon). Then follow the instructions in the `INSTALL` file in the root of the repository to install the C core library that this package depends on. -or, for brevity +Then, simply run `npm install` in or referencing this directory. - node-gyp configure build +## Tests -The tests can be run with `npm test` on a dev install. \ No newline at end of file +To run the test suite, simply run `npm test` in the install location. + +## API + +This library internally uses [ProtoBuf.js](https://github.com/dcodeIO/ProtoBuf.js), and some structures it exports match those exported by that library + +If you require this module, you will get an object with the following members + +```javascript +function load(filename) +``` + +Takes a filename of a [Protocol Buffer](https://developers.google.com/protocol-buffers/) file, and returns an object representing the structure of the protocol buffer in the following way: + + - Namespaces become maps from the names of their direct members to those member objects + - Service definitions become client constructors for clients for that service. They also have a `service` member that can be used for constructing servers. + - Message definitions become Message constructors like those that ProtoBuf.js would create + - Enum definitions become Enum objects like those that ProtoBuf.js would create + - Anything else becomes the relevant reflection object that ProtoBuf.js would create + + +```javascript +function loadObject(reflectionObject) +``` + +Returns the same structure that `load` returns, but takes a reflection object from `ProtoBuf.js` instead of a file name. + +```javascript +function buildServer(serviceArray) +``` + +Takes an array of service objects and returns a constructor for a server that handles requests to all of those services. + + +```javascript +status +``` + +An object mapping status names to status code numbers. + + +```javascript +callError +``` + +An object mapping call error names to codes. This is primarily useful for tracking down certain kinds of internal errors. + + +```javascript +Credentials +``` + +An object with factory methods for creating credential objects for clients. + + +```javascript +ServerCredentials +``` + +An object with factory methods fro creating credential objects for servers. diff --git a/templates/Makefile.template b/templates/Makefile.template index 142d188d0c6..c34949cf2a7 100644 --- a/templates/Makefile.template +++ b/templates/Makefile.template @@ -206,11 +206,13 @@ OPENSSL_ALPN_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o /dev/null test/build/ope ZLIB_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o /dev/null test/build/zlib.c -lz $(LDFLAGS) PERFTOOLS_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o /dev/null test/build/perftools.c -lprofiler $(LDFLAGS) +ifndef REQUIRE_CUSTOM_LIBRARIES_$(CONFIG) HAS_SYSTEM_PERFTOOLS = $(shell $(PERFTOOLS_CHECK_CMD) 2> /dev/null && echo true || echo false) ifeq ($(HAS_SYSTEM_PERFTOOLS),true) DEFINES += GRPC_HAVE_PERFTOOLS LIBS += profiler endif +endif ifndef REQUIRE_CUSTOM_LIBRARIES_$(CONFIG) HAS_SYSTEM_OPENSSL_ALPN = $(shell $(OPENSSL_ALPN_CHECK_CMD) 2> /dev/null && echo true || echo false) diff --git a/test/core/echo/echo_test.c b/test/core/echo/echo_test.c index 83b83ab7ff7..5450dfbef56 100644 --- a/test/core/echo/echo_test.c +++ b/test/core/echo/echo_test.c @@ -31,7 +31,10 @@ * */ +#ifndef _POSIX_SOURCE #define _POSIX_SOURCE +#endif + #include #include #include diff --git a/test/core/end2end/cq_verifier.c b/test/core/end2end/cq_verifier.c index 287f83eebca..904ed772836 100644 --- a/test/core/end2end/cq_verifier.c +++ b/test/core/end2end/cq_verifier.c @@ -70,6 +70,7 @@ typedef struct expectation { union { grpc_op_error finish_accepted; grpc_op_error write_accepted; + grpc_op_error ioreq; struct { const char *method; const char *host; @@ -180,9 +181,6 @@ static void verify_matches(expectation *e, grpc_event *ev) { case GRPC_WRITE_ACCEPTED: GPR_ASSERT(e->data.write_accepted == ev->data.write_accepted); break; - case GRPC_INVOKE_ACCEPTED: - abort(); - break; case GRPC_SERVER_RPC_NEW: GPR_ASSERT(string_equivalent(e->data.server_rpc_new.method, ev->data.server_rpc_new.method)); @@ -222,6 +220,9 @@ static void verify_matches(expectation *e, grpc_event *ev) { GPR_ASSERT(ev->data.read == NULL); } break; + case GRPC_IOREQ: + GPR_ASSERT(e->data.ioreq == ev->data.ioreq); + break; case GRPC_SERVER_SHUTDOWN: break; case GRPC_COMPLETION_DO_NOT_USE: @@ -242,7 +243,9 @@ static void metadata_expectation(gpr_strvec *buf, metadata *md) { gpr_asprintf(&tmp, "%c%s:%s", i ? ',' : '{', md->keys[i], md->values[i]); gpr_strvec_add(buf, tmp); } - gpr_strvec_add(buf, gpr_strdup("}")); + if (md->count) { + gpr_strvec_add(buf, gpr_strdup("}")); + } } } @@ -261,8 +264,9 @@ static void expectation_to_strvec(gpr_strvec *buf, expectation *e) { e->data.write_accepted); gpr_strvec_add(buf, tmp); break; - case GRPC_INVOKE_ACCEPTED: - gpr_strvec_add(buf, gpr_strdup("GRPC_INVOKE_ACCEPTED")); + case GRPC_IOREQ: + gpr_asprintf(&tmp, "GRPC_IOREQ result=%d", e->data.ioreq); + gpr_strvec_add(buf, tmp); break; case GRPC_SERVER_RPC_NEW: timeout = gpr_time_sub(e->data.server_rpc_new.deadline, gpr_now()); diff --git a/test/core/end2end/dualstack_socket_test.c b/test/core/end2end/dualstack_socket_test.c index eeb454ca581..9d893f67a13 100644 --- a/test/core/end2end/dualstack_socket_test.c +++ b/test/core/end2end/dualstack_socket_test.c @@ -142,7 +142,6 @@ void test_connect(const char *server_host, const char *client_host, int port, cq_verify(v_client); cq_expect_finish_accepted(v_server, tag(5), GRPC_OP_OK); - cq_verify(v_server); cq_expect_finished(v_server, tag(102), NULL); cq_verify(v_server); diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c index a70819e47ea..149ac8c07bb 100644 --- a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c +++ b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c @@ -39,6 +39,9 @@ #include "src/core/channel/channel_args.h" #include "src/core/security/credentials.h" #include "src/core/security/security_context.h" +#include "src/core/support/env.h" +#include "src/core/support/file.h" +#include "src/core/support/string.h" #include #include #include @@ -99,7 +102,7 @@ void chttp2_tear_down_secure_fullstack(grpc_end2end_test_fixture *f) { static void chttp2_init_client_simple_ssl_secure_fullstack( grpc_end2end_test_fixture *f, grpc_channel_args *client_args) { grpc_credentials *ssl_creds = - grpc_ssl_credentials_create(test_root_cert, NULL); + grpc_ssl_credentials_create(NULL, NULL); grpc_arg ssl_name_override = {GRPC_ARG_STRING, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, {"foo.test.google.com"}}; @@ -129,8 +132,20 @@ static grpc_end2end_test_config configs[] = { int main(int argc, char **argv) { size_t i; + FILE *roots_file; + size_t roots_size = strlen(test_root_cert); + char *roots_filename; + grpc_test_init(argc, argv); + /* Set the SSL roots env var. */ + roots_file = gpr_tmpfile("chttp2_simple_ssl_fullstack_test", &roots_filename); + GPR_ASSERT(roots_filename != NULL); + GPR_ASSERT(roots_file != NULL); + GPR_ASSERT(fwrite(test_root_cert, 1, roots_size, roots_file) == roots_size); + fclose(roots_file); + gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_filename); + grpc_init(); for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { @@ -139,5 +154,9 @@ int main(int argc, char **argv) { grpc_shutdown(); + /* Cleanup. */ + remove(roots_filename); + gpr_free(roots_filename); + return 0; } diff --git a/test/core/end2end/tests/census_simple_request.c b/test/core/end2end/tests/census_simple_request.c index 1edb5b0d3a1..4cbaa65b321 100644 --- a/test/core/end2end/tests/census_simple_request.c +++ b/test/core/end2end/tests/census_simple_request.c @@ -135,7 +135,6 @@ static void test_body(grpc_end2end_test_fixture f) { cq_verify(v_client); cq_expect_finish_accepted(v_server, tag(5), GRPC_OP_OK); - cq_verify(v_server); cq_expect_finished(v_server, tag(102), NULL); cq_verify(v_server); grpc_call_destroy(c); diff --git a/test/core/end2end/tests/max_concurrent_streams.c b/test/core/end2end/tests/max_concurrent_streams.c index 0e26577903f..4830b85f9b7 100644 --- a/test/core/end2end/tests/max_concurrent_streams.c +++ b/test/core/end2end/tests/max_concurrent_streams.c @@ -138,7 +138,6 @@ static void simple_request_body(grpc_end2end_test_fixture f) { cq_verify(v_client); cq_expect_finish_accepted(v_server, tag(5), GRPC_OP_OK); - cq_verify(v_server); cq_expect_finished(v_server, tag(102), NULL); cq_verify(v_server); @@ -207,7 +206,7 @@ static void test_max_concurrent_streams(grpc_end2end_test_config config) { /* The /alpha or /beta calls started above could be invoked (but NOT both); * check this here */ /* We'll get tag 303 or 403, we want 300, 400 */ - live_call = ((int)(gpr_intptr)ev->tag) - 3; + live_call = ((int)(gpr_intptr) ev->tag) - 3; grpc_event_finish(ev); cq_expect_server_rpc_new(v_server, &s1, tag(100), diff --git a/test/core/end2end/tests/simple_request.c b/test/core/end2end/tests/simple_request.c index 192d1ab87d0..db0d6d81605 100644 --- a/test/core/end2end/tests/simple_request.c +++ b/test/core/end2end/tests/simple_request.c @@ -139,7 +139,6 @@ static void simple_request_body(grpc_end2end_test_fixture f) { cq_verify(v_client); cq_expect_finish_accepted(v_server, tag(5), GRPC_OP_OK); - cq_verify(v_server); cq_expect_finished(v_server, tag(102), NULL); cq_verify(v_server); @@ -180,16 +179,14 @@ static void simple_request_body2(grpc_end2end_test_fixture f) { GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_write_status_old( s, GRPC_STATUS_UNIMPLEMENTED, "xyz", tag(5))); - cq_expect_finish_accepted(v_server, tag(5), GRPC_OP_OK); cq_verify(v_server); cq_expect_client_metadata_read(v_client, tag(2), NULL); - cq_verify(v_client); - cq_expect_finished_with_status(v_client, tag(3), GRPC_STATUS_UNIMPLEMENTED, "xyz", NULL); cq_verify(v_client); + cq_expect_finish_accepted(v_server, tag(5), GRPC_OP_OK); cq_expect_finished(v_server, tag(102), NULL); cq_verify(v_server); diff --git a/test/core/fling/fling_stream_test.c b/test/core/fling/fling_stream_test.c index 7f52fb1bad1..1db2f1a7916 100644 --- a/test/core/fling/fling_stream_test.c +++ b/test/core/fling/fling_stream_test.c @@ -31,7 +31,10 @@ * */ +#ifndef _POSIX_SOURCE #define _POSIX_SOURCE +#endif + #include #include #include diff --git a/test/core/fling/fling_test.c b/test/core/fling/fling_test.c index b2272f20c8e..4f41a21aaaf 100644 --- a/test/core/fling/fling_test.c +++ b/test/core/fling/fling_test.c @@ -31,7 +31,10 @@ * */ +#ifndef _POSIX_SOURCE #define _POSIX_SOURCE +#endif + #include #include #include diff --git a/test/core/json/json_test.c b/test/core/json/json_test.c index 11659a57161..6d0227ad39b 100644 --- a/test/core/json/json_test.c +++ b/test/core/json/json_test.c @@ -151,7 +151,7 @@ static void test_pairs() { GPR_ASSERT(!json); } - free(scratchpad); + gpr_free(scratchpad); } } @@ -166,6 +166,7 @@ static void test_atypical() { grpc_json_destroy(json->child); json->child = brother; grpc_json_destroy(json); + gpr_free(scratchpad); } int main(int argc, char **argv) { diff --git a/test/core/support/env_test.c b/test/core/support/env_test.c new file mode 100644 index 00000000000..36d7adf80b5 --- /dev/null +++ b/test/core/support/env_test.c @@ -0,0 +1,64 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +#include +#include + +#include "src/core/support/env.h" +#include "src/core/support/string.h" +#include "test/core/util/test_config.h" + +#define LOG_TEST_NAME() gpr_log(GPR_INFO, "%s", __FUNCTION__) + +static void test_setenv_getenv(void) { + const char *name = "FOO"; + const char *value = "BAR"; + char *retrieved_value; + + LOG_TEST_NAME(); + + gpr_setenv(name, value); + retrieved_value = gpr_getenv(name); + GPR_ASSERT(retrieved_value != NULL); + GPR_ASSERT(!strcmp(value, retrieved_value)); + gpr_free(retrieved_value); +} + +int main(int argc, char **argv) { + grpc_test_init(argc, argv); + test_setenv_getenv(); + return 0; +} diff --git a/test/core/support/file_test.c b/test/core/support/file_test.c new file mode 100644 index 00000000000..b089954186c --- /dev/null +++ b/test/core/support/file_test.c @@ -0,0 +1,159 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +#include +#include +#include + +#include "src/core/support/file.h" +#include "src/core/support/string.h" +#include "test/core/util/test_config.h" + +#define LOG_TEST_NAME() gpr_log(GPR_INFO, "%s", __FUNCTION__) + +static const char prefix[] = "file_test"; + +static void test_load_empty_file(void) { + FILE *tmp = NULL; + gpr_slice slice; + int success; + char *tmp_name; + + LOG_TEST_NAME(); + + tmp = gpr_tmpfile(prefix, &tmp_name); + GPR_ASSERT(tmp_name != NULL); + GPR_ASSERT(tmp != NULL); + fclose(tmp); + + slice = gpr_load_file(tmp_name, &success); + GPR_ASSERT(success == 1); + GPR_ASSERT(GPR_SLICE_LENGTH(slice) == 0); + + remove(tmp_name); + gpr_free(tmp_name); + gpr_slice_unref(slice); +} + +static void test_load_failure(void) { + FILE *tmp = NULL; + gpr_slice slice; + int success; + char *tmp_name; + + LOG_TEST_NAME(); + + tmp = gpr_tmpfile(prefix, &tmp_name); + GPR_ASSERT(tmp_name != NULL); + GPR_ASSERT(tmp != NULL); + fclose(tmp); + remove(tmp_name); + + slice = gpr_load_file(tmp_name, &success); + GPR_ASSERT(success == 0); + GPR_ASSERT(GPR_SLICE_LENGTH(slice) == 0); + gpr_free(tmp_name); + gpr_slice_unref(slice); +} + +static void test_load_small_file(void) { + FILE *tmp = NULL; + gpr_slice slice; + int success; + char *tmp_name; + const char *blah = "blah"; + + LOG_TEST_NAME(); + + tmp = gpr_tmpfile(prefix, &tmp_name); + GPR_ASSERT(tmp_name != NULL); + GPR_ASSERT(tmp != NULL); + GPR_ASSERT(fwrite(blah, 1, strlen(blah), tmp) == strlen(blah)); + fclose(tmp); + + slice = gpr_load_file(tmp_name, &success); + GPR_ASSERT(success == 1); + GPR_ASSERT(GPR_SLICE_LENGTH(slice) == strlen(blah)); + GPR_ASSERT(!memcmp(GPR_SLICE_START_PTR(slice), blah, strlen(blah))); + + remove(tmp_name); + gpr_free(tmp_name); + gpr_slice_unref(slice); +} + +static void test_load_big_file(void) { + FILE *tmp = NULL; + gpr_slice slice; + int success; + char *tmp_name; + unsigned char buffer[124631]; + unsigned char *current; + size_t i; + + LOG_TEST_NAME(); + + for (i = 0; i < sizeof(buffer); i++) { + buffer[i] = 42; + } + + tmp = gpr_tmpfile(prefix, &tmp_name); + GPR_ASSERT(tmp != NULL); + GPR_ASSERT(tmp_name != NULL); + GPR_ASSERT(fwrite(buffer, 1, sizeof(buffer), tmp) == sizeof(buffer)); + fclose(tmp); + + slice = gpr_load_file(tmp_name, &success); + GPR_ASSERT(success == 1); + GPR_ASSERT(GPR_SLICE_LENGTH(slice) == sizeof(buffer)); + current = GPR_SLICE_START_PTR(slice); + for (i = 0; i < sizeof(buffer); i++) { + GPR_ASSERT(current[i] == 42); + } + + remove(tmp_name); + gpr_free(tmp_name); + gpr_slice_unref(slice); +} + + +int main(int argc, char **argv) { + grpc_test_init(argc, argv); + test_load_empty_file(); + test_load_failure(); + test_load_small_file(); + test_load_big_file(); + return 0; +} diff --git a/test/core/surface/completion_queue_test.c b/test/core/surface/completion_queue_test.c index dc459d62a8f..875cf3e52aa 100644 --- a/test/core/surface/completion_queue_test.c +++ b/test/core/surface/completion_queue_test.c @@ -105,32 +105,6 @@ static void test_cq_end_read(void) { shutdown_and_destroy(cc); } -static void test_cq_end_invoke_accepted(void) { - grpc_event *ev; - grpc_completion_queue *cc; - int on_finish_called = 0; - void *tag = create_test_tag(); - - LOG_TEST(); - - cc = grpc_completion_queue_create(); - - grpc_cq_begin_op(cc, NULL, GRPC_INVOKE_ACCEPTED); - grpc_cq_end_invoke_accepted(cc, tag, NULL, increment_int_on_finish, - &on_finish_called, GRPC_OP_OK); - - ev = grpc_completion_queue_next(cc, gpr_inf_past); - GPR_ASSERT(ev != NULL); - GPR_ASSERT(ev->type == GRPC_INVOKE_ACCEPTED); - GPR_ASSERT(ev->tag == tag); - GPR_ASSERT(ev->data.invoke_accepted == GRPC_OP_OK); - GPR_ASSERT(on_finish_called == 0); - grpc_event_finish(ev); - GPR_ASSERT(on_finish_called == 1); - - shutdown_and_destroy(cc); -} - static void test_cq_end_write_accepted(void) { grpc_event *ev; grpc_completion_queue *cc; @@ -421,7 +395,6 @@ int main(int argc, char **argv) { test_no_op(); test_wait_empty(); test_cq_end_read(); - test_cq_end_invoke_accepted(); test_cq_end_write_accepted(); test_cq_end_finish_accepted(); test_cq_end_client_metadata_read(); diff --git a/tools/dockerfile/grpc_node_base/Dockerfile b/tools/dockerfile/grpc_node_base/Dockerfile index 4ca0e53c0a9..28bd7b2556f 100644 --- a/tools/dockerfile/grpc_node_base/Dockerfile +++ b/tools/dockerfile/grpc_node_base/Dockerfile @@ -15,5 +15,8 @@ RUN cd /var/local/git/grpc && \ git pull --recurse-submodules && \ git submodule update --init --recursive +# Build the C core +RUN make static_c shared_c -j12 -C /var/local/git/grpc + # Define the default command. CMD ["bash"] \ No newline at end of file diff --git a/tools/dockerfile/grpc_php_base/Dockerfile b/tools/dockerfile/grpc_php_base/Dockerfile index e0e86ea717d..ef58f3a887c 100644 --- a/tools/dockerfile/grpc_php_base/Dockerfile +++ b/tools/dockerfile/grpc_php_base/Dockerfile @@ -84,5 +84,8 @@ RUN wget https://phar.phpunit.de/phpunit.phar \ && chmod +x phpunit.phar \ && mv phpunit.phar /usr/local/bin/phpunit +# Build the C core +RUN make static_c shared_c -j12 -C /var/local/git/grpc + # Define the default command. CMD ["bash"] diff --git a/tools/dockerfile/grpc_ruby_base/Dockerfile b/tools/dockerfile/grpc_ruby_base/Dockerfile index 787f1290b0f..ec4544d2fdf 100644 --- a/tools/dockerfile/grpc_ruby_base/Dockerfile +++ b/tools/dockerfile/grpc_ruby_base/Dockerfile @@ -53,3 +53,6 @@ RUN cd /var/local/git/grpc/third_party/protobuf && \ ./autogen.sh && \ ./configure --prefix=/usr && \ make -j12 && make check && make install && make clean + +# Build the C core +RUN make static_c shared_c -j12 -C /var/local/git/grpc diff --git a/tools/gce_setup/builder.sh b/tools/gce_setup/builder.sh new file mode 100755 index 00000000000..49b3c436fee --- /dev/null +++ b/tools/gce_setup/builder.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +main() { + # restart builder vm and wait for images to sync to it + source grpc_docker.sh + ./new_grpc_docker_builder.sh -igrpc-docker-builder-alt-2 -anone + cd ../../ + sleep 3600 + + # build images for all languages + languages=(cxx java go ruby node) + for lan in "${languages[@]}" + do + grpc_update_image $lan + done + + # restart client and server vm and wait for images to sync to them + cd tools/gce_setup + ./new_grpc_docker_builder.sh -igrpc-docker-testclients -anone + ./new_grpc_docker_builder.sh -igrpc-docker-server -anone + sleep 3600 + + # launch images for all languages on server + grpc_launch_servers grpc-docker-server + +} + +set -x +main "$@" diff --git a/tools/gce_setup/cloud_prod_runner.sh b/tools/gce_setup/cloud_prod_runner.sh index 0c1163ad7db..200f859ede5 100755 --- a/tools/gce_setup/cloud_prod_runner.sh +++ b/tools/gce_setup/cloud_prod_runner.sh @@ -2,8 +2,8 @@ main() { source grpc_docker.sh - test_cases=(large_unary empty_unary client_streaming server_streaming) - clients=(cxx java go ruby) + test_cases=(large_unary empty_unary ping_pong client_streaming server_streaming) + clients=(cxx java go ruby node) for test_case in "${test_cases[@]}" do for client in "${clients[@]}" diff --git a/tools/gce_setup/grpc_docker.sh b/tools/gce_setup/grpc_docker.sh index 2ac75f3cc59..2e026538643 100755 --- a/tools/gce_setup/grpc_docker.sh +++ b/tools/gce_setup/grpc_docker.sh @@ -762,7 +762,16 @@ grpc_interop_test() { echo " $ssh_cmd" echo "on $host" [[ $dry_run == 1 ]] && return 0 # don't run the command on a dry run - gcloud compute $project_opt ssh $zone_opt $host --command "$cmd" + gcloud compute $project_opt ssh $zone_opt $host --command "$cmd" & + PID=$! + sleep 10 + echo "pid is $PID" + if ps -p $PID + then + kill $PID + return 1 + fi + } # Runs a test command on a docker instance. @@ -808,7 +817,16 @@ grpc_cloud_prod_test() { echo " $ssh_cmd" echo "on $host" [[ $dry_run == 1 ]] && return 0 # don't run the command on a dry run - gcloud compute $project_opt ssh $zone_opt $host --command "$cmd" + gcloud compute $project_opt ssh $zone_opt $host --command "$cmd" & + PID=$! + sleep 10 + echo "pid is $PID" + if ps -p $PID + then + kill $PID + return 1 + fi + } # Runs a test command on a docker instance. diff --git a/tools/gce_setup/interop_test_runner.sh b/tools/gce_setup/interop_test_runner.sh index 12443079128..456ad4b4722 100755 --- a/tools/gce_setup/interop_test_runner.sh +++ b/tools/gce_setup/interop_test_runner.sh @@ -1,33 +1,8 @@ #!/bin/bash thisfile=$(readlink -ne "${BASH_SOURCE[0]}") - -run_test() { - local test_case=$1 - shift - local client=$1 - shift - local server=$1 - if grpc_interop_test $test_case grpc-docker-testclients $client grpc-docker-server $server - then - echo "$test_case $client $server passed" >> /tmp/interop_result.txt - else - echo "$test_case $client $server failed" >> /tmp/interop_result.txt - fi -} - -time_out() { - local test_case=$1 - shift - local client=$1 - shift - local server=$1 - if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - if ! timeout 20s bash -l -c "source $thisfile && run_test $test_case $client $server" - then - echo "$test_case $client $server timed out" >> /tmp/interop_result.txt - fi - fi -} +current_time=$(date "+%Y-%m-%d-%H-%M-%S") +result_file_name=interop_result.$current_time.html +echo $result_file_name main() { source grpc_docker.sh @@ -40,13 +15,22 @@ main() { do for server in "${servers[@]}" do - time_out $test_case $client $server + if grpc_interop_test $test_case grpc-docker-testclients $client grpc-docker-server $server + then + echo " ['$test_case', '$client', '$server', true]," >> /tmp/interop_result.txt + else + echo " ['$test_case', '$client', '$server', false]," >> /tmp/interop_result.txt + fi done done done if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + cat pre.html /tmp/interop_result.txt post.html > /tmp/interop_result.html gsutil cp /tmp/interop_result.txt gs://stoked-keyword-656-output/interop_result.txt + gsutil cp /tmp/interop_result.html gs://stoked-keyword-656-output/interop_result.html + gsutil cp /tmp/interop_result.html gs://stoked-keyword-656-output/result_history/$result_file_name rm /tmp/interop_result.txt + rm /tmp/interop_result.html fi } diff --git a/tools/gce_setup/post.html b/tools/gce_setup/post.html new file mode 100644 index 00000000000..57cbc8c3694 --- /dev/null +++ b/tools/gce_setup/post.html @@ -0,0 +1,12 @@ + ]); + + var table = new google.visualization.Table(document.getElementById('table_div')); + + table.draw(data, {showRowNumber: true}); + } + + + +
+ + diff --git a/tools/gce_setup/pre.html b/tools/gce_setup/pre.html new file mode 100644 index 00000000000..74ce5ce2028 --- /dev/null +++ b/tools/gce_setup/pre.html @@ -0,0 +1,14 @@ + + + +