Merge pull request #359 from jboeuf/ssl_default_creds_integration
Adding support for loading the SSL roots from an environment variable.
diff --git a/.gitignore b/.gitignore
index 6eb55b1..9c9ae5a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,11 @@
# python compiled objects
*.pyc
+#eclipse project files
+.cproject
+.project
+.settings
+
# cache for run_tests.py
.run_tests_cache
diff --git a/Makefile b/Makefile
index 2447aeb..40f98b9 100644
--- a/Makefile
+++ b/Makefile
@@ -189,11 +189,13 @@
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)
@@ -385,14 +387,15 @@
end2end_test: bins/$(CONFIG)/end2end_test
interop_client: bins/$(CONFIG)/interop_client
interop_server: bins/$(CONFIG)/interop_server
+tips_client: bins/$(CONFIG)/tips_client
+tips_publisher_test: bins/$(CONFIG)/tips_publisher_test
+tips_subscriber_test: bins/$(CONFIG)/tips_subscriber_test
qps_client: bins/$(CONFIG)/qps_client
qps_server: bins/$(CONFIG)/qps_server
ruby_plugin: bins/$(CONFIG)/ruby_plugin
status_test: bins/$(CONFIG)/status_test
sync_client_async_server_test: bins/$(CONFIG)/sync_client_async_server_test
thread_pool_test: bins/$(CONFIG)/thread_pool_test
-tips_client: bins/$(CONFIG)/tips_client
-tips_client_test: bins/$(CONFIG)/tips_client_test
chttp2_fake_security_cancel_after_accept_test: bins/$(CONFIG)/chttp2_fake_security_cancel_after_accept_test
chttp2_fake_security_cancel_after_accept_and_writes_closed_test: bins/$(CONFIG)/chttp2_fake_security_cancel_after_accept_and_writes_closed_test
chttp2_fake_security_cancel_after_invoke_test: bins/$(CONFIG)/chttp2_fake_security_cancel_after_invoke_test
@@ -552,13 +555,13 @@
static: static_c static_cxx
-static_c: libs/$(CONFIG)/libgpr.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgrpc_unsecure.a
+static_c: libs/$(CONFIG)/libgpr.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgrpc_unsecure.a libs/$(CONFIG)/libgrpc_csharp_ext.a
static_cxx: libs/$(CONFIG)/libgrpc++.a
shared: shared_c shared_cxx
-shared_c: libs/$(CONFIG)/libgpr.$(SHARED_EXT) libs/$(CONFIG)/libgrpc.$(SHARED_EXT) libs/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT)
+shared_c: libs/$(CONFIG)/libgpr.$(SHARED_EXT) libs/$(CONFIG)/libgrpc.$(SHARED_EXT) libs/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT) libs/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT)
shared_cxx: libs/$(CONFIG)/libgrpc++.$(SHARED_EXT)
@@ -572,7 +575,7 @@
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)/qps_client bins/$(CONFIG)/qps_server bins/$(CONFIG)/status_test bins/$(CONFIG)/sync_client_async_server_test bins/$(CONFIG)/thread_pool_test bins/$(CONFIG)/tips_client bins/$(CONFIG)/tips_client_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
test: test_c test_cxx
@@ -980,6 +983,10 @@
$(Q) ./bins/$(CONFIG)/credentials_test || ( echo test credentials_test failed ; exit 1 )
$(E) "[RUN] Testing end2end_test"
$(Q) ./bins/$(CONFIG)/end2end_test || ( echo test end2end_test failed ; exit 1 )
+ $(E) "[RUN] Testing tips_publisher_test"
+ $(Q) ./bins/$(CONFIG)/tips_publisher_test || ( echo test tips_publisher_test failed ; exit 1 )
+ $(E) "[RUN] Testing tips_subscriber_test"
+ $(Q) ./bins/$(CONFIG)/tips_subscriber_test || ( echo test tips_subscriber_test failed ; exit 1 )
$(E) "[RUN] Testing qps_client"
$(Q) ./bins/$(CONFIG)/qps_client || ( echo test qps_client failed ; exit 1 )
$(E) "[RUN] Testing qps_server"
@@ -990,8 +997,6 @@
$(Q) ./bins/$(CONFIG)/sync_client_async_server_test || ( echo test sync_client_async_server_test failed ; exit 1 )
$(E) "[RUN] Testing thread_pool_test"
$(Q) ./bins/$(CONFIG)/thread_pool_test || ( echo test thread_pool_test failed ; exit 1 )
- $(E) "[RUN] Testing tips_client_test"
- $(Q) ./bins/$(CONFIG)/tips_client_test || ( echo test tips_client_test failed ; exit 1 )
tools: privatelibs bins/$(CONFIG)/gen_hpack_tables bins/$(CONFIG)/grpc_fetch_oauth2
@@ -1019,6 +1024,8 @@
$(Q) $(STRIP) libs/$(CONFIG)/libgrpc.a
$(E) "[STRIP] Stripping libgrpc_unsecure.a"
$(Q) $(STRIP) libs/$(CONFIG)/libgrpc_unsecure.a
+ $(E) "[STRIP] Stripping libgrpc_csharp_ext.a"
+ $(Q) $(STRIP) libs/$(CONFIG)/libgrpc_csharp_ext.a
endif
strip-static_cxx: static_cxx
@@ -1035,6 +1042,8 @@
$(Q) $(STRIP) libs/$(CONFIG)/libgrpc.$(SHARED_EXT)
$(E) "[STRIP] Stripping libgrpc_unsecure.so"
$(Q) $(STRIP) libs/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT)
+ $(E) "[STRIP] Stripping libgrpc_csharp_ext.so"
+ $(Q) $(STRIP) libs/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT)
endif
strip-shared_cxx: shared_cxx
@@ -1140,6 +1149,8 @@
$(Q) $(INSTALL) libs/$(CONFIG)/libgrpc.a $(prefix)/lib/libgrpc.a
$(E) "[INSTALL] Installing libgrpc_unsecure.a"
$(Q) $(INSTALL) libs/$(CONFIG)/libgrpc_unsecure.a $(prefix)/lib/libgrpc_unsecure.a
+ $(E) "[INSTALL] Installing libgrpc_csharp_ext.a"
+ $(Q) $(INSTALL) libs/$(CONFIG)/libgrpc_csharp_ext.a $(prefix)/lib/libgrpc_csharp_ext.a
install-static_cxx: static_cxx strip-static_cxx
$(E) "[INSTALL] Installing libgrpc++.a"
@@ -1179,6 +1190,17 @@
$(Q) ln -sf libgrpc_unsecure.$(SHARED_EXT) $(prefix)/lib/libgrpc_unsecure.so
endif
endif
+ifeq ($(SYSTEM),MINGW32)
+ $(E) "[INSTALL] Installing grpc_csharp_ext.$(SHARED_EXT)"
+ $(Q) $(INSTALL) libs/$(CONFIG)/grpc_csharp_ext.$(SHARED_EXT) $(prefix)/lib/grpc_csharp_ext.$(SHARED_EXT)
+ $(Q) $(INSTALL) libs/$(CONFIG)/libgrpc_csharp_ext-imp.a $(prefix)/lib/libgrpc_csharp_ext-imp.a
+else
+ $(E) "[INSTALL] Installing libgrpc_csharp_ext.$(SHARED_EXT)"
+ $(Q) $(INSTALL) libs/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT) $(prefix)/lib/libgrpc_csharp_ext.$(SHARED_EXT)
+ifneq ($(SYSTEM),Darwin)
+ $(Q) ln -sf libgrpc_csharp_ext.$(SHARED_EXT) $(prefix)/lib/libgrpc_csharp_ext.so
+endif
+endif
ifneq ($(SYSTEM),MINGW32)
ifneq ($(SYSTEM),Darwin)
$(Q) ldconfig
@@ -1263,11 +1285,7 @@
include/grpc/support/sync_posix.h \
include/grpc/support/sync_win32.h \
include/grpc/support/thd.h \
- include/grpc/support/thd_posix.h \
- include/grpc/support/thd_win32.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))))
@@ -1451,6 +1469,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 \
@@ -1577,6 +1596,7 @@
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/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 @@
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/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:
@@ -2260,7 +2283,8 @@
gens/examples/tips/label.pb.cc \
gens/examples/tips/empty.pb.cc \
gens/examples/tips/pubsub.pb.cc \
- examples/tips/client.cc \
+ examples/tips/publisher.cc \
+ examples/tips/subscriber.cc \
LIBTIPS_CLIENT_LIB_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBTIPS_CLIENT_LIB_SRC))))
@@ -2278,7 +2302,8 @@
examples/tips/label.proto: $(OPENSSL_DEP)
examples/tips/empty.proto: $(OPENSSL_DEP)
examples/tips/pubsub.proto: $(OPENSSL_DEP)
-examples/tips/client.cc: $(OPENSSL_DEP)
+examples/tips/publisher.cc: $(OPENSSL_DEP)
+examples/tips/subscriber.cc: $(OPENSSL_DEP)
endif
libs/$(CONFIG)/libtips_client_lib.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBTIPS_CLIENT_LIB_OBJS)
@@ -2305,7 +2330,73 @@
-objs/$(CONFIG)/examples/tips/client.o: gens/examples/tips/label.pb.cc gens/examples/tips/empty.pb.cc gens/examples/tips/pubsub.pb.cc
+objs/$(CONFIG)/examples/tips/publisher.o: gens/examples/tips/label.pb.cc gens/examples/tips/empty.pb.cc gens/examples/tips/pubsub.pb.cc
+objs/$(CONFIG)/examples/tips/subscriber.o: gens/examples/tips/label.pb.cc gens/examples/tips/empty.pb.cc gens/examples/tips/pubsub.pb.cc
+
+
+LIBGRPC_CSHARP_EXT_SRC = \
+ src/csharp/ext/grpc_csharp_ext.c \
+
+
+LIBGRPC_CSHARP_EXT_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_CSHARP_EXT_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure libraries if you don't have OpenSSL with ALPN.
+
+libs/$(CONFIG)/libgrpc_csharp_ext.a: openssl_dep_error
+
+ifeq ($(SYSTEM),MINGW32)
+libs/$(CONFIG)/grpc_csharp_ext.$(SHARED_EXT): openssl_dep_error
+else
+libs/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT): openssl_dep_error
+endif
+
+else
+
+ifneq ($(OPENSSL_DEP),)
+src/csharp/ext/grpc_csharp_ext.c: $(OPENSSL_DEP)
+endif
+
+libs/$(CONFIG)/libgrpc_csharp_ext.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBGRPC_CSHARP_EXT_OBJS)
+ $(E) "[AR] Creating $@"
+ $(Q) mkdir -p `dirname $@`
+ $(Q) rm -f libs/$(CONFIG)/libgrpc_csharp_ext.a
+ $(Q) $(AR) rcs libs/$(CONFIG)/libgrpc_csharp_ext.a $(LIBGRPC_CSHARP_EXT_OBJS)
+ifeq ($(SYSTEM),Darwin)
+ $(Q) ranlib libs/$(CONFIG)/libgrpc_csharp_ext.a
+endif
+
+
+
+ifeq ($(SYSTEM),MINGW32)
+libs/$(CONFIG)/grpc_csharp_ext.$(SHARED_EXT): $(LIBGRPC_CSHARP_EXT_OBJS) $(ZLIB_DEP)libs/$(CONFIG)/gpr.$(SHARED_EXT)libs/$(CONFIG)/grpc.$(SHARED_EXT) $(OPENSSL_DEP)
+ $(E) "[LD] Linking $@"
+ $(Q) mkdir -p `dirname $@`
+ $(Q) $(LD) $(LDFLAGS) -Llibs/$(CONFIG) -shared -Wl,--output-def=libs/$(CONFIG)/grpc_csharp_ext.def -Wl,--out-implib=libs/$(CONFIG)/libgrpc_csharp_ext-imp.a -o libs/$(CONFIG)/grpc_csharp_ext.$(SHARED_EXT) $(LIBGRPC_CSHARP_EXT_OBJS) $(LDLIBS) $(LDLIBS_SECURE) $(OPENSSL_MERGE_LIBS) -lgpr-imp -lgrpc-imp
+else
+libs/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT): $(LIBGRPC_CSHARP_EXT_OBJS) $(ZLIB_DEP) libs/$(CONFIG)/libgpr.$(SHARED_EXT) libs/$(CONFIG)/libgrpc.$(SHARED_EXT) $(OPENSSL_DEP)
+ $(E) "[LD] Linking $@"
+ $(Q) mkdir -p `dirname $@`
+ifeq ($(SYSTEM),Darwin)
+ $(Q) $(LD) $(LDFLAGS) -Llibs/$(CONFIG) -dynamiclib -o libs/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT) $(LIBGRPC_CSHARP_EXT_OBJS) $(LDLIBS) $(LDLIBS_SECURE) $(OPENSSL_MERGE_LIBS) -lgpr -lgrpc
+else
+ $(Q) $(LD) $(LDFLAGS) -Llibs/$(CONFIG) -shared -Wl,-soname,libgrpc_csharp_ext.so.0 -o libs/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT) $(LIBGRPC_CSHARP_EXT_OBJS) $(LDLIBS) $(LDLIBS_SECURE) $(OPENSSL_MERGE_LIBS) -lgpr -lgrpc
+ $(Q) ln -sf libgrpc_csharp_ext.$(SHARED_EXT) libs/$(CONFIG)/libgrpc_csharp_ext.so.0
+ $(Q) ln -sf libgrpc_csharp_ext.$(SHARED_EXT) libs/$(CONFIG)/libgrpc_csharp_ext.so
+endif
+endif
+
+
+endif
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(LIBGRPC_CSHARP_EXT_OBJS:.o=.dep)
+endif
+endif
+
+objs/$(CONFIG)/src/csharp/ext/grpc_csharp_ext.o:
LIBEND2END_FIXTURE_CHTTP2_FAKE_SECURITY_SRC = \
@@ -5769,6 +5860,99 @@
endif
+TIPS_CLIENT_SRC = \
+ examples/tips/main.cc \
+
+TIPS_CLIENT_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(TIPS_CLIENT_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL with ALPN.
+
+bins/$(CONFIG)/tips_client: openssl_dep_error
+
+else
+
+bins/$(CONFIG)/tips_client: $(TIPS_CLIENT_OBJS) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+ $(E) "[LD] Linking $@"
+ $(Q) mkdir -p `dirname $@`
+ $(Q) $(LDXX) $(LDFLAGS) $(TIPS_CLIENT_OBJS) $(GTEST_LIB) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS) $(LDLIBS_SECURE) -o bins/$(CONFIG)/tips_client
+
+endif
+
+objs/$(CONFIG)/examples/tips/main.o: libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+
+deps_tips_client: $(TIPS_CLIENT_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(TIPS_CLIENT_OBJS:.o=.dep)
+endif
+endif
+
+
+TIPS_PUBLISHER_TEST_SRC = \
+ examples/tips/publisher_test.cc \
+
+TIPS_PUBLISHER_TEST_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(TIPS_PUBLISHER_TEST_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL with ALPN.
+
+bins/$(CONFIG)/tips_publisher_test: openssl_dep_error
+
+else
+
+bins/$(CONFIG)/tips_publisher_test: $(TIPS_PUBLISHER_TEST_OBJS) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+ $(E) "[LD] Linking $@"
+ $(Q) mkdir -p `dirname $@`
+ $(Q) $(LDXX) $(LDFLAGS) $(TIPS_PUBLISHER_TEST_OBJS) $(GTEST_LIB) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS) $(LDLIBS_SECURE) -o bins/$(CONFIG)/tips_publisher_test
+
+endif
+
+objs/$(CONFIG)/examples/tips/publisher_test.o: libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+
+deps_tips_publisher_test: $(TIPS_PUBLISHER_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(TIPS_PUBLISHER_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+TIPS_SUBSCRIBER_TEST_SRC = \
+ examples/tips/subscriber_test.cc \
+
+TIPS_SUBSCRIBER_TEST_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(TIPS_SUBSCRIBER_TEST_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL with ALPN.
+
+bins/$(CONFIG)/tips_subscriber_test: openssl_dep_error
+
+else
+
+bins/$(CONFIG)/tips_subscriber_test: $(TIPS_SUBSCRIBER_TEST_OBJS) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+ $(E) "[LD] Linking $@"
+ $(Q) mkdir -p `dirname $@`
+ $(Q) $(LDXX) $(LDFLAGS) $(TIPS_SUBSCRIBER_TEST_OBJS) $(GTEST_LIB) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS) $(LDLIBS_SECURE) -o bins/$(CONFIG)/tips_subscriber_test
+
+endif
+
+objs/$(CONFIG)/examples/tips/subscriber_test.o: libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+
+deps_tips_subscriber_test: $(TIPS_SUBSCRIBER_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(TIPS_SUBSCRIBER_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
QPS_CLIENT_SRC = \
gens/test/cpp/qps/qpstest.pb.cc \
test/cpp/qps/client.cc \
@@ -5949,68 +6133,6 @@
endif
-TIPS_CLIENT_SRC = \
- examples/tips/client_main.cc \
-
-TIPS_CLIENT_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(TIPS_CLIENT_SRC))))
-
-ifeq ($(NO_SECURE),true)
-
-# You can't build secure targets if you don't have OpenSSL with ALPN.
-
-bins/$(CONFIG)/tips_client: openssl_dep_error
-
-else
-
-bins/$(CONFIG)/tips_client: $(TIPS_CLIENT_OBJS) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
- $(E) "[LD] Linking $@"
- $(Q) mkdir -p `dirname $@`
- $(Q) $(LDXX) $(LDFLAGS) $(TIPS_CLIENT_OBJS) $(GTEST_LIB) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS) $(LDLIBS_SECURE) -o bins/$(CONFIG)/tips_client
-
-endif
-
-objs/$(CONFIG)/examples/tips/client_main.o: libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
-
-deps_tips_client: $(TIPS_CLIENT_OBJS:.o=.dep)
-
-ifneq ($(NO_SECURE),true)
-ifneq ($(NO_DEPS),true)
--include $(TIPS_CLIENT_OBJS:.o=.dep)
-endif
-endif
-
-
-TIPS_CLIENT_TEST_SRC = \
- examples/tips/client_test.cc \
-
-TIPS_CLIENT_TEST_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(TIPS_CLIENT_TEST_SRC))))
-
-ifeq ($(NO_SECURE),true)
-
-# You can't build secure targets if you don't have OpenSSL with ALPN.
-
-bins/$(CONFIG)/tips_client_test: openssl_dep_error
-
-else
-
-bins/$(CONFIG)/tips_client_test: $(TIPS_CLIENT_TEST_OBJS) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
- $(E) "[LD] Linking $@"
- $(Q) mkdir -p `dirname $@`
- $(Q) $(LDXX) $(LDFLAGS) $(TIPS_CLIENT_TEST_OBJS) $(GTEST_LIB) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS) $(LDLIBS_SECURE) -o bins/$(CONFIG)/tips_client_test
-
-endif
-
-objs/$(CONFIG)/examples/tips/client_test.o: libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
-
-deps_tips_client_test: $(TIPS_CLIENT_TEST_OBJS:.o=.dep)
-
-ifneq ($(NO_SECURE),true)
-ifneq ($(NO_DEPS),true)
--include $(TIPS_CLIENT_TEST_OBJS:.o=.dep)
-endif
-endif
-
-
CHTTP2_FAKE_SECURITY_CANCEL_AFTER_ACCEPT_TEST_SRC = \
CHTTP2_FAKE_SECURITY_CANCEL_AFTER_ACCEPT_TEST_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_FAKE_SECURITY_CANCEL_AFTER_ACCEPT_TEST_SRC))))
diff --git a/build.json b/build.json
index 704c1bd..170b148 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",
@@ -220,11 +222,7 @@
"include/grpc/support/sync_posix.h",
"include/grpc/support/sync_win32.h",
"include/grpc/support/thd.h",
- "include/grpc/support/thd_posix.h",
- "include/grpc/support/thd_win32.h",
"include/grpc/support/time.h",
- "include/grpc/support/time_posix.h",
- "include/grpc/support/time_win32.h",
"include/grpc/support/useful.h"
],
"headers": [
@@ -441,13 +439,26 @@
"examples/tips/label.proto",
"examples/tips/empty.proto",
"examples/tips/pubsub.proto",
- "examples/tips/client.cc"
+ "examples/tips/publisher.cc",
+ "examples/tips/subscriber.cc"
],
"deps": [
"grpc++",
"grpc",
"gpr"
]
+ },
+ {
+ "name": "grpc_csharp_ext",
+ "build": "all",
+ "language": "c",
+ "deps": [
+ "gpr",
+ "grpc"
+ ],
+ "src": [
+ "src/csharp/ext/grpc_csharp_ext.c"
+ ]
}
],
"targets": [
@@ -1600,6 +1611,58 @@
"run": false
},
{
+ "name": "tips_client",
+ "build": "test",
+ "language": "c++",
+ "src": [
+ "examples/tips/main.cc"
+ ],
+ "deps": [
+ "tips_client_lib",
+ "grpc++_test_util",
+ "grpc_test_util",
+ "grpc++",
+ "grpc",
+ "gpr_test_util",
+ "gpr"
+ ],
+ "run": false
+ },
+ {
+ "name": "tips_publisher_test",
+ "build": "test",
+ "language": "c++",
+ "src": [
+ "examples/tips/publisher_test.cc"
+ ],
+ "deps": [
+ "tips_client_lib",
+ "grpc++_test_util",
+ "grpc_test_util",
+ "grpc++",
+ "grpc",
+ "gpr_test_util",
+ "gpr"
+ ]
+ },
+ {
+ "name": "tips_subscriber_test",
+ "build": "test",
+ "language": "c++",
+ "src": [
+ "examples/tips/subscriber_test.cc"
+ ],
+ "deps": [
+ "tips_client_lib",
+ "grpc++_test_util",
+ "grpc_test_util",
+ "grpc++",
+ "grpc",
+ "gpr_test_util",
+ "gpr"
+ ]
+ },
+ {
"name": "qps_client",
"build": "test",
"language": "c++",
@@ -1695,41 +1758,6 @@
"gpr_test_util",
"gpr"
]
- },
- {
- "name": "tips_client",
- "build": "test",
- "language": "c++",
- "src": [
- "examples/tips/client_main.cc"
- ],
- "deps": [
- "tips_client_lib",
- "grpc++_test_util",
- "grpc_test_util",
- "grpc++",
- "grpc",
- "gpr_test_util",
- "gpr"
- ],
- "run": false
- },
- {
- "name": "tips_client_test",
- "build": "test",
- "language": "c++",
- "src": [
- "examples/tips/client_test.cc"
- ],
- "deps": [
- "tips_client_lib",
- "grpc++_test_util",
- "grpc_test_util",
- "grpc++",
- "grpc",
- "gpr_test_util",
- "gpr"
- ]
}
]
}
diff --git a/examples/tips/README b/examples/tips/README
new file mode 100644
index 0000000..ae7d096
--- /dev/null
+++ b/examples/tips/README
@@ -0,0 +1,26 @@
+C++ Client implementation for Cloud Pub/Sub service (TIPS)
+(https://developers.google.com/apis-explorer/#p/pubsub/v1beta1/).
+
+"Google Cloud Pub/Sub" API needs to be enabled at
+https://console.developers.google.com/project to open the access for a client.
+Select the project name, select the "APIs" under "APIs & auth", and turn
+on "Google Cloud Pub/Sub" API.
+
+To run the client from Google Compute Engine (GCE), the GCE instance needs to
+be created with scope "https://www.googleapis.com/auth/cloud-platform" as below:
+
+gcloud compute instances create instance-name
+ --image debian-7 --scopes https://www.googleapis.com/auth/cloud-platform
+
+To run the client from GCE:
+make tips_client
+bins/opt/tips_client --project_id="your project id"
+
+A service account credential is required to run the client from other
+environments, which can be generated as a JSON key file from
+https://console.developers.google.com/project/. To run the client with a service
+account credential:
+
+bins/opt/tips_client
+ --project_id="your project id"
+ --service_account_key_file="absolute path to the JSON key file"
diff --git a/examples/tips/client_main.cc b/examples/tips/client_main.cc
deleted file mode 100644
index 5a3a0da..0000000
--- a/examples/tips/client_main.cc
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- *
- * 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 <chrono>
-#include <fstream>
-#include <memory>
-#include <sstream>
-#include <string>
-#include <thread>
-
-#include <grpc/grpc.h>
-#include <grpc/support/log.h>
-#include <google/gflags.h>
-#include <grpc++/channel_interface.h>
-#include <grpc++/create_channel.h>
-#include <grpc++/credentials.h>
-#include <grpc++/status.h>
-
-#include "examples/tips/client.h"
-#include "test/cpp/util/create_test_channel.h"
-
-DEFINE_int32(server_port, 443, "Server port.");
-DEFINE_string(server_host,
- "pubsub-staging.googleapis.com", "Server host to connect to");
-DEFINE_string(service_account_key_file, "",
- "Path to service account json key file.");
-DEFINE_string(oauth_scope, "", "Scope for OAuth tokens.");
-
-grpc::string GetServiceAccountJsonKey() {
- static grpc::string json_key;
- if (json_key.empty()) {
- std::ifstream json_key_file(FLAGS_service_account_key_file);
- std::stringstream key_stream;
- key_stream << json_key_file.rdbuf();
- json_key = key_stream.str();
- }
- return json_key;
-}
-
-int main(int argc, char** argv) {
- grpc_init();
- google::ParseCommandLineFlags(&argc, &argv, true);
- gpr_log(GPR_INFO, "Start TIPS client");
-
- const int host_port_buf_size = 1024;
- char host_port[host_port_buf_size];
- snprintf(host_port, host_port_buf_size, "%s:%d", FLAGS_server_host.c_str(),
- FLAGS_server_port);
-
- std::unique_ptr<grpc::Credentials> creds;
- if (FLAGS_service_account_key_file != "") {
- grpc::string json_key = GetServiceAccountJsonKey();
- creds = grpc::CredentialsFactory::ServiceAccountCredentials(
- json_key, FLAGS_oauth_scope, std::chrono::hours(1));
- } else {
- creds = grpc::CredentialsFactory::ComputeEngineCredentials();
- }
-
- std::shared_ptr<grpc::ChannelInterface> channel(
- grpc::CreateTestChannel(
- host_port,
- FLAGS_server_host,
- true, // enable SSL
- true, // use prod roots
- creds));
-
- grpc::examples::tips::Client client(channel);
-
- grpc::Status s = client.CreateTopic("/topics/stoked-keyword-656/testtopics");
- gpr_log(GPR_INFO, "return code %d, %s", s.code(), s.details().c_str());
- GPR_ASSERT(s.IsOk());
-
- s = client.GetTopic("/topics/stoked-keyword-656/testtopics");
- gpr_log(GPR_INFO, "return code %d, %s", s.code(), s.details().c_str());
- GPR_ASSERT(s.IsOk());
-
- s = client.DeleteTopic("/topics/stoked-keyword-656/testtopics");
- gpr_log(GPR_INFO, "return code %d, %s", s.code(), s.details().c_str());
- GPR_ASSERT(s.IsOk());
-
- channel.reset();
- grpc_shutdown();
- return 0;
-}
diff --git a/examples/tips/empty.proto b/examples/tips/empty.proto
index adf66b5..86aaa84 100644
--- a/examples/tips/empty.proto
+++ b/examples/tips/empty.proto
@@ -1,3 +1,5 @@
+// This file will be moved to a new location.
+
syntax = "proto2";
package proto2;
diff --git a/examples/tips/label.proto b/examples/tips/label.proto
index e93ac9d..6ac786f 100644
--- a/examples/tips/label.proto
+++ b/examples/tips/label.proto
@@ -1,3 +1,5 @@
+// This file will be moved to a new location.
+
// Labels provide a way to associate user-defined metadata with various
// objects. Labels may be used to organize objects into non-hierarchical
// groups; think metadata tags attached to mp3s.
diff --git a/examples/tips/main.cc b/examples/tips/main.cc
new file mode 100644
index 0000000..df9d984
--- /dev/null
+++ b/examples/tips/main.cc
@@ -0,0 +1,178 @@
+/*
+ *
+ * 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 <chrono>
+#include <fstream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <thread>
+
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+#include <google/gflags.h>
+#include <grpc++/channel_interface.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/credentials.h>
+#include <grpc++/status.h>
+
+#include "examples/tips/publisher.h"
+#include "examples/tips/subscriber.h"
+#include "test/cpp/util/create_test_channel.h"
+
+DEFINE_int32(server_port, 443, "Server port.");
+DEFINE_string(server_host,
+ "pubsub-staging.googleapis.com", "Server host to connect to");
+DEFINE_string(project_id, "", "GCE project id such as stoked-keyword-656");
+DEFINE_string(service_account_key_file, "",
+ "Path to service account json key file.");
+DEFINE_string(oauth_scope,
+ "https://www.googleapis.com/auth/cloud-platform",
+ "Scope for OAuth tokens.");
+
+namespace {
+
+const char kTopic[] = "testtopics";
+const char kSubscriptionName[] = "testsubscription";
+const char kMessageData[] = "Test Data";
+
+} // namespace
+
+grpc::string GetServiceAccountJsonKey() {
+ grpc::string json_key;
+ if (json_key.empty()) {
+ std::ifstream json_key_file(FLAGS_service_account_key_file);
+ std::stringstream key_stream;
+ key_stream << json_key_file.rdbuf();
+ json_key = key_stream.str();
+ }
+ return json_key;
+}
+
+int main(int argc, char** argv) {
+ grpc_init();
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ gpr_log(GPR_INFO, "Start TIPS client");
+
+ std::ostringstream ss;
+
+ std::unique_ptr<grpc::Credentials> creds;
+ if (FLAGS_service_account_key_file != "") {
+ grpc::string json_key = GetServiceAccountJsonKey();
+ creds = grpc::CredentialsFactory::ServiceAccountCredentials(
+ json_key, FLAGS_oauth_scope, std::chrono::hours(1));
+ } else {
+ creds = grpc::CredentialsFactory::ComputeEngineCredentials();
+ }
+
+ ss << FLAGS_server_host << ":" << FLAGS_server_port;
+ std::shared_ptr<grpc::ChannelInterface> channel(
+ grpc::CreateTestChannel(
+ ss.str(),
+ FLAGS_server_host,
+ true, // enable SSL
+ true, // use prod roots
+ creds));
+
+ grpc::examples::tips::Publisher publisher(channel);
+ grpc::examples::tips::Subscriber subscriber(channel);
+
+ GPR_ASSERT(FLAGS_project_id != "");
+ ss.str("");
+ ss << "/topics/" << FLAGS_project_id << "/" << kTopic;
+ grpc::string topic = ss.str();
+
+ ss.str("");
+ ss << FLAGS_project_id << "/" << kSubscriptionName;
+ grpc::string subscription_name = ss.str();
+
+ // Clean up test topic and subcription if they exist before.
+ grpc::string subscription_topic;
+ if (subscriber.GetSubscription(
+ subscription_name, &subscription_topic).IsOk()) {
+ subscriber.DeleteSubscription(subscription_name);
+ }
+ if (publisher.GetTopic(topic).IsOk()) publisher.DeleteTopic(topic);
+
+ grpc::Status s = publisher.CreateTopic(topic);
+ gpr_log(GPR_INFO, "Create topic returns code %d, %s",
+ s.code(), s.details().c_str());
+ GPR_ASSERT(s.IsOk());
+
+ s = publisher.GetTopic(topic);
+ gpr_log(GPR_INFO, "Get topic returns code %d, %s",
+ s.code(), s.details().c_str());
+ GPR_ASSERT(s.IsOk());
+
+ std::vector<grpc::string> topics;
+ s = publisher.ListTopics(FLAGS_project_id, &topics);
+ gpr_log(GPR_INFO, "List topic returns code %d, %s",
+ s.code(), s.details().c_str());
+ bool topic_found = false;
+ for (unsigned int i = 0; i < topics.size(); i++) {
+ if (topics[i] == topic) topic_found = true;
+ gpr_log(GPR_INFO, "topic: %s", topics[i].c_str());
+ }
+ GPR_ASSERT(s.IsOk());
+ GPR_ASSERT(topic_found);
+
+ s = subscriber.CreateSubscription(topic, subscription_name);
+ gpr_log(GPR_INFO, "create subscrption returns code %d, %s",
+ s.code(), s.details().c_str());
+ GPR_ASSERT(s.IsOk());
+
+ s = publisher.Publish(topic, kMessageData);
+ gpr_log(GPR_INFO, "Publish %s returns code %d, %s",
+ kMessageData, s.code(), s.details().c_str());
+ GPR_ASSERT(s.IsOk());
+
+ grpc::string data;
+ s = subscriber.Pull(subscription_name, &data);
+ gpr_log(GPR_INFO, "Pull %s", data.c_str());
+
+ s = subscriber.DeleteSubscription(subscription_name);
+ gpr_log(GPR_INFO, "Delete subscription returns code %d, %s",
+ s.code(), s.details().c_str());
+ GPR_ASSERT(s.IsOk());
+
+ s = publisher.DeleteTopic(topic);
+ gpr_log(GPR_INFO, "Delete topic returns code %d, %s",
+ s.code(), s.details().c_str());
+ GPR_ASSERT(s.IsOk());
+
+ subscriber.Shutdown();
+ publisher.Shutdown();
+ channel.reset();
+ grpc_shutdown();
+ return 0;
+}
diff --git a/examples/tips/client.cc b/examples/tips/publisher.cc
similarity index 68%
rename from examples/tips/client.cc
rename to examples/tips/publisher.cc
index f9d5319..eae8731 100644
--- a/examples/tips/client.cc
+++ b/examples/tips/publisher.cc
@@ -31,9 +31,11 @@
*
*/
+#include <sstream>
+
#include <grpc++/client_context.h>
-#include "examples/tips/client.h"
+#include "examples/tips/publisher.h"
using tech::pubsub::Topic;
using tech::pubsub::DeleteTopicRequest;
@@ -41,16 +43,22 @@
using tech::pubsub::PublisherService;
using tech::pubsub::ListTopicsRequest;
using tech::pubsub::ListTopicsResponse;
+using tech::pubsub::PublishRequest;
+using tech::pubsub::PubsubMessage;
namespace grpc {
namespace examples {
namespace tips {
-Client::Client(std::shared_ptr<ChannelInterface> channel)
+Publisher::Publisher(std::shared_ptr<ChannelInterface> channel)
: stub_(PublisherService::NewStub(channel)) {
}
-Status Client::CreateTopic(grpc::string topic) {
+void Publisher::Shutdown() {
+ stub_.reset();
+}
+
+Status Publisher::CreateTopic(const grpc::string& topic) {
Topic request;
Topic response;
request.set_name(topic);
@@ -59,15 +67,28 @@
return stub_->CreateTopic(&context, request, &response);
}
-Status Client::ListTopics() {
+Status Publisher::ListTopics(const grpc::string& project_id,
+ std::vector<grpc::string>* topics) {
ListTopicsRequest request;
ListTopicsResponse response;
ClientContext context;
- return stub_->ListTopics(&context, request, &response);
+ std::ostringstream ss;
+ ss << "cloud.googleapis.com/project in (/projects/" << project_id << ")";
+ request.set_query(ss.str());
+
+ Status s = stub_->ListTopics(&context, request, &response);
+
+ tech::pubsub::Topic topic;
+ for (int i = 0; i < response.topic_size(); i++) {
+ topic = response.topic(i);
+ topics->push_back(topic.name());
+ }
+
+ return s;
}
-Status Client::GetTopic(grpc::string topic) {
+Status Publisher::GetTopic(const grpc::string& topic) {
GetTopicRequest request;
Topic response;
ClientContext context;
@@ -77,7 +98,7 @@
return stub_->GetTopic(&context, request, &response);
}
-Status Client::DeleteTopic(grpc::string topic) {
+Status Publisher::DeleteTopic(const grpc::string& topic) {
DeleteTopicRequest request;
proto2::Empty response;
ClientContext context;
@@ -87,6 +108,17 @@
return stub_->DeleteTopic(&context, request, &response);
}
+Status Publisher::Publish(const grpc::string& topic, const grpc::string& data) {
+ PublishRequest request;
+ proto2::Empty response;
+ ClientContext context;
+
+ request.mutable_message()->set_data(data);
+ request.set_topic(topic);
+
+ return stub_->Publish(&context, request, &response);
+}
+
} // namespace tips
} // namespace examples
} // namespace grpc
diff --git a/examples/tips/client.h b/examples/tips/publisher.h
similarity index 77%
rename from examples/tips/client.h
rename to examples/tips/publisher.h
index 661ee5c..d8d7353 100644
--- a/examples/tips/client.h
+++ b/examples/tips/publisher.h
@@ -31,8 +31,8 @@
*
*/
-#ifndef __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
-#define __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
+#ifndef __GRPCPP_EXAMPLES_TIPS_PUBLISHER_H_
+#define __GRPCPP_EXAMPLES_TIPS_PUBLISHER_H_
#include <grpc++/channel_interface.h>
#include <grpc++/status.h>
@@ -43,13 +43,18 @@
namespace examples {
namespace tips {
-class Client {
+class Publisher {
public:
- Client(std::shared_ptr<grpc::ChannelInterface> channel);
- Status CreateTopic(grpc::string topic);
- Status GetTopic(grpc::string topic);
- Status DeleteTopic(grpc::string topic);
- Status ListTopics();
+ Publisher(std::shared_ptr<ChannelInterface> channel);
+ void Shutdown();
+
+ Status CreateTopic(const grpc::string& topic);
+ Status GetTopic(const grpc::string& topic);
+ Status DeleteTopic(const grpc::string& topic);
+ Status ListTopics(const grpc::string& project_id,
+ std::vector<grpc::string>* topics);
+
+ Status Publish(const grpc::string& topic, const grpc::string& data);
private:
std::unique_ptr<tech::pubsub::PublisherService::Stub> stub_;
@@ -59,4 +64,4 @@
} // namespace examples
} // namespace grpc
-#endif // __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
+#endif // __GRPCPP_EXAMPLES_TIPS_PUBLISHER_H_
diff --git a/examples/tips/client_test.cc b/examples/tips/publisher_test.cc
similarity index 60%
rename from examples/tips/client_test.cc
rename to examples/tips/publisher_test.cc
index 69238f2..e46576a 100644
--- a/examples/tips/client_test.cc
+++ b/examples/tips/publisher_test.cc
@@ -41,7 +41,7 @@
#include <grpc++/status.h>
#include <gtest/gtest.h>
-#include "examples/tips/client.h"
+#include "examples/tips/publisher.h"
#include "test/core/util/port.h"
#include "test/core/util/test_config.h"
@@ -51,9 +51,11 @@
namespace testing {
namespace {
+const char kProjectId[] = "project id";
const char kTopic[] = "test topic";
+const char kMessageData[] = "test message data";
-class PublishServiceImpl : public tech::pubsub::PublisherService::Service {
+class PublisherServiceImpl : public tech::pubsub::PublisherService::Service {
public:
Status CreateTopic(::grpc::ServerContext* context,
const ::tech::pubsub::Topic* request,
@@ -61,34 +63,81 @@
EXPECT_EQ(request->name(), kTopic);
return Status::OK;
}
+
+ Status Publish(ServerContext* context,
+ const ::tech::pubsub::PublishRequest* request,
+ ::proto2::Empty* response) override {
+ EXPECT_EQ(request->message().data(), kMessageData);
+ return Status::OK;
+ }
+
+ Status GetTopic(ServerContext* context,
+ const ::tech::pubsub::GetTopicRequest* request,
+ ::tech::pubsub::Topic* response) override {
+ EXPECT_EQ(request->topic(), kTopic);
+ return Status::OK;
+ }
+
+ Status ListTopics(ServerContext* context,
+ const ::tech::pubsub::ListTopicsRequest* request,
+ ::tech::pubsub::ListTopicsResponse* response) override {
+ std::ostringstream ss;
+ ss << "cloud.googleapis.com/project in (/projects/" << kProjectId << ")";
+ EXPECT_EQ(request->query(), ss.str());
+ response->add_topic()->set_name(kTopic);
+ return Status::OK;
+ }
+
+ Status DeleteTopic(ServerContext* context,
+ const ::tech::pubsub::DeleteTopicRequest* request,
+ ::proto2::Empty* response) override {
+ EXPECT_EQ(request->topic(), kTopic);
+ return Status::OK;
+ }
+
};
-class End2endTest : public ::testing::Test {
+class PublisherTest : public ::testing::Test {
protected:
+ // Setup a server and a client for PublisherService.
void SetUp() override {
int port = grpc_pick_unused_port_or_die();
server_address_ << "localhost:" << port;
- // Setup server
ServerBuilder builder;
builder.AddPort(server_address_.str());
builder.RegisterService(service_.service());
server_ = builder.BuildAndStart();
channel_ = CreateChannel(server_address_.str(), ChannelArguments());
+
+ publisher_.reset(new grpc::examples::tips::Publisher(channel_));
}
- void TearDown() override { server_->Shutdown(); }
+ void TearDown() override {
+ server_->Shutdown();
+ publisher_->Shutdown();
+ }
- std::unique_ptr<Server> server_;
std::ostringstream server_address_;
- PublishServiceImpl service_;
+ std::unique_ptr<Server> server_;
+ PublisherServiceImpl service_;
std::shared_ptr<ChannelInterface> channel_;
+
+ std::unique_ptr<grpc::examples::tips::Publisher> publisher_;
};
-TEST_F(End2endTest, CreateTopic) {
- grpc::examples::tips::Client client(channel_);
- client.CreateTopic(kTopic);
+TEST_F(PublisherTest, TestPublisher) {
+ EXPECT_TRUE(publisher_->CreateTopic(kTopic).IsOk());
+
+ EXPECT_TRUE(publisher_->Publish(kTopic, kMessageData).IsOk());
+
+ EXPECT_TRUE(publisher_->GetTopic(kTopic).IsOk());
+
+ std::vector<grpc::string> topics;
+ EXPECT_TRUE(publisher_->ListTopics(kProjectId, &topics).IsOk());
+ EXPECT_EQ(topics.size(), 1);
+ EXPECT_EQ(topics[0], kTopic);
}
} // namespace
diff --git a/examples/tips/pubsub.proto b/examples/tips/pubsub.proto
index 0b3bd5d..a2dd2f5 100644
--- a/examples/tips/pubsub.proto
+++ b/examples/tips/pubsub.proto
@@ -1,3 +1,5 @@
+// This file will be moved to a new location.
+
// Specification of the Pubsub API.
syntax = "proto2";
diff --git a/examples/tips/subscriber.cc b/examples/tips/subscriber.cc
new file mode 100644
index 0000000..c067322
--- /dev/null
+++ b/examples/tips/subscriber.cc
@@ -0,0 +1,118 @@
+/*
+ *
+ * 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 <grpc++/client_context.h>
+
+#include "examples/tips/subscriber.h"
+
+using tech::pubsub::Topic;
+using tech::pubsub::DeleteTopicRequest;
+using tech::pubsub::GetTopicRequest;
+using tech::pubsub::SubscriberService;
+using tech::pubsub::ListTopicsRequest;
+using tech::pubsub::ListTopicsResponse;
+using tech::pubsub::PublishRequest;
+using tech::pubsub::PubsubMessage;
+
+namespace grpc {
+namespace examples {
+namespace tips {
+
+Subscriber::Subscriber(std::shared_ptr<ChannelInterface> channel)
+ : stub_(SubscriberService::NewStub(channel)) {
+}
+
+void Subscriber::Shutdown() {
+ stub_.reset();
+}
+
+Status Subscriber::CreateSubscription(const grpc::string& topic,
+ const grpc::string& name) {
+ tech::pubsub::Subscription request;
+ tech::pubsub::Subscription response;
+ ClientContext context;
+
+ request.set_topic(topic);
+ request.set_name(name);
+
+ return stub_->CreateSubscription(&context, request, &response);
+}
+
+Status Subscriber::GetSubscription(const grpc::string& name,
+ grpc::string* topic) {
+ tech::pubsub::GetSubscriptionRequest request;
+ tech::pubsub::Subscription response;
+ ClientContext context;
+
+ request.set_subscription(name);
+
+ Status s = stub_->GetSubscription(&context, request, &response);
+ *topic = response.topic();
+ return s;
+}
+
+Status Subscriber::DeleteSubscription(const grpc::string& name) {
+ tech::pubsub::DeleteSubscriptionRequest request;
+ proto2::Empty response;
+ ClientContext context;
+
+ request.set_subscription(name);
+
+ return stub_->DeleteSubscription(&context, request, &response);
+}
+
+Status Subscriber::Pull(const grpc::string& name, grpc::string* data) {
+ tech::pubsub::PullRequest request;
+ tech::pubsub::PullResponse response;
+ ClientContext context;
+
+ request.set_subscription(name);
+ Status s = stub_->Pull(&context, request, &response);
+ if (s.IsOk()) {
+ tech::pubsub::PubsubEvent event = response.pubsub_event();
+ if (event.has_message()) {
+ *data = event.message().data();
+ }
+ tech::pubsub::AcknowledgeRequest ack;
+ proto2::Empty empty;
+ ClientContext ack_context;
+ ack.set_subscription(name);
+ ack.add_ack_id(response.ack_id());
+ stub_->Acknowledge(&ack_context, ack, &empty);
+ }
+ return s;
+}
+
+} // namespace tips
+} // namespace examples
+} // namespace grpc
diff --git a/examples/tips/client.h b/examples/tips/subscriber.h
similarity index 75%
copy from examples/tips/client.h
copy to examples/tips/subscriber.h
index 661ee5c..ed706ff 100644
--- a/examples/tips/client.h
+++ b/examples/tips/subscriber.h
@@ -31,8 +31,8 @@
*
*/
-#ifndef __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
-#define __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
+#ifndef __GRPCPP_EXAMPLES_TIPS_SUBSCRIBER_H_
+#define __GRPCPP_EXAMPLES_TIPS_SUBSCRIBER_H_
#include <grpc++/channel_interface.h>
#include <grpc++/status.h>
@@ -43,20 +43,26 @@
namespace examples {
namespace tips {
-class Client {
+class Subscriber {
public:
- Client(std::shared_ptr<grpc::ChannelInterface> channel);
- Status CreateTopic(grpc::string topic);
- Status GetTopic(grpc::string topic);
- Status DeleteTopic(grpc::string topic);
- Status ListTopics();
+ Subscriber(std::shared_ptr<ChannelInterface> channel);
+ void Shutdown();
+
+ Status CreateSubscription(const grpc::string& topic,
+ const grpc::string& name);
+
+ Status GetSubscription(const grpc::string& name, grpc::string* topic);
+
+ Status DeleteSubscription(const grpc::string& name);
+
+ Status Pull(const grpc::string& name, grpc::string* data);
private:
- std::unique_ptr<tech::pubsub::PublisherService::Stub> stub_;
+ std::unique_ptr<tech::pubsub::SubscriberService::Stub> stub_;
};
} // namespace tips
} // namespace examples
} // namespace grpc
-#endif // __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
+#endif // __GRPCPP_EXAMPLES_TIPS_SUBSCRIBER_H_
diff --git a/examples/tips/subscriber_test.cc b/examples/tips/subscriber_test.cc
new file mode 100644
index 0000000..595a6a1
--- /dev/null
+++ b/examples/tips/subscriber_test.cc
@@ -0,0 +1,157 @@
+/*
+ *
+ * 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 <grpc++/channel_arguments.h>
+#include <grpc++/channel_interface.h>
+#include <grpc++/client_context.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include <grpc++/server_context.h>
+#include <grpc++/status.h>
+#include <gtest/gtest.h>
+
+#include "examples/tips/subscriber.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+namespace grpc {
+namespace testing {
+namespace {
+
+const char kTopic[] = "test topic";
+const char kSubscriptionName[] = "subscription name";
+const char kData[] = "Message data";
+
+class SubscriberServiceImpl : public tech::pubsub::SubscriberService::Service {
+ public:
+ Status CreateSubscription(ServerContext* context,
+ const tech::pubsub::Subscription* request,
+ tech::pubsub::Subscription* response) override {
+ EXPECT_EQ(request->topic(), kTopic);
+ EXPECT_EQ(request->name(), kSubscriptionName);
+ return Status::OK;
+ }
+
+ Status GetSubscription(ServerContext* context,
+ const tech::pubsub::GetSubscriptionRequest* request,
+ tech::pubsub::Subscription* response) override {
+ EXPECT_EQ(request->subscription(), kSubscriptionName);
+ response->set_topic(kTopic);
+ return Status::OK;
+ }
+
+ Status DeleteSubscription(
+ ServerContext* context,
+ const tech::pubsub::DeleteSubscriptionRequest* request,
+ proto2::Empty* response) override {
+ EXPECT_EQ(request->subscription(), kSubscriptionName);
+ return Status::OK;
+ }
+
+ Status Pull(ServerContext* context,
+ const tech::pubsub::PullRequest* request,
+ tech::pubsub::PullResponse* response) override {
+ EXPECT_EQ(request->subscription(), kSubscriptionName);
+ response->set_ack_id("1");
+ response->mutable_pubsub_event()->mutable_message()->set_data(kData);
+ return Status::OK;
+ }
+
+ Status Acknowledge(ServerContext* context,
+ const tech::pubsub::AcknowledgeRequest* request,
+ proto2::Empty* response) override {
+ return Status::OK;
+ }
+
+};
+
+class SubscriberTest : public ::testing::Test {
+ protected:
+ // Setup a server and a client for SubscriberService.
+ void SetUp() override {
+ int port = grpc_pick_unused_port_or_die();
+ server_address_ << "localhost:" << port;
+ ServerBuilder builder;
+ builder.AddPort(server_address_.str());
+ builder.RegisterService(service_.service());
+ server_ = builder.BuildAndStart();
+
+ channel_ = CreateChannel(server_address_.str(), ChannelArguments());
+
+ subscriber_.reset(new grpc::examples::tips::Subscriber(channel_));
+ }
+
+ void TearDown() override {
+ server_->Shutdown();
+ subscriber_->Shutdown();
+ }
+
+ std::ostringstream server_address_;
+ std::unique_ptr<Server> server_;
+ SubscriberServiceImpl service_;
+
+ std::shared_ptr<ChannelInterface> channel_;
+
+ std::unique_ptr<grpc::examples::tips::Subscriber> subscriber_;
+};
+
+TEST_F(SubscriberTest, TestSubscriber) {
+ EXPECT_TRUE(subscriber_->CreateSubscription(kTopic,
+ kSubscriptionName).IsOk());
+
+ grpc::string topic;
+ EXPECT_TRUE(subscriber_->GetSubscription(kSubscriptionName,
+ &topic).IsOk());
+ EXPECT_EQ(topic, kTopic);
+
+ grpc::string data;
+ EXPECT_TRUE(subscriber_->Pull(kSubscriptionName,
+ &data).IsOk());
+
+ EXPECT_TRUE(subscriber_->DeleteSubscription(kSubscriptionName).IsOk());
+}
+
+} // namespace
+} // namespace testing
+} // namespace grpc
+
+int main(int argc, char** argv) {
+ grpc_test_init(argc, argv);
+ grpc_init();
+ ::testing::InitGoogleTest(&argc, argv);
+ gpr_log(GPR_INFO, "Start test ...");
+ int result = RUN_ALL_TESTS();
+ grpc_shutdown();
+ return result;
+}
diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h
index a9fae0d..c18e47e 100644
--- a/include/grpc/grpc.h
+++ b/include/grpc/grpc.h
@@ -183,17 +183,16 @@
} 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 @@
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 @@
} 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 @@
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/support/thd.h b/include/grpc/support/thd.h
index 91e0c9f..92d40b4 100644
--- a/include/grpc/support/thd.h
+++ b/include/grpc/support/thd.h
@@ -44,18 +44,12 @@
#include <grpc/support/port_platform.h>
-#if defined(GPR_POSIX_SYNC)
-#include <grpc/support/thd_posix.h>
-#elif defined(GPR_WIN32)
-#include <grpc/support/thd_win32.h>
-#else
-#error could not determine platform for thd
-#endif
-
#ifdef __cplusplus
extern "C" {
#endif
+typedef gpr_uint64 gpr_thd_id;
+
/* Thread creation options. */
typedef struct {
int flags; /* Flags below can be set here. Default value 0. */
@@ -72,6 +66,9 @@
/* Return a gpr_thd_options struct with all fields set to defaults. */
gpr_thd_options gpr_thd_options_default(void);
+/* Returns the identifier of the current thread. */
+gpr_thd_id gpr_thd_currentid(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/grpc/support/thd_posix.h b/include/grpc/support/thd_posix.h
deleted file mode 100644
index b688e45..0000000
--- a/include/grpc/support/thd_posix.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- *
- * 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_THD_POSIX_H__
-#define __GRPC_SUPPORT_THD_POSIX_H__
-/* Posix variant of gpr_thd_platform.h. */
-
-#include <pthread.h>
-
-typedef pthread_t gpr_thd_id;
-
-#endif /* __GRPC_SUPPORT_THD_POSIX_H__ */
diff --git a/include/grpc/support/thd_win32.h b/include/grpc/support/thd_win32.h
deleted file mode 100644
index b4ab3c7..0000000
--- a/include/grpc/support/thd_win32.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- *
- * 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_THD_WIN32_H__
-#define __GRPC_SUPPORT_THD_WIN32_H__
-
-/* Win32 variant of gpr_thd_platform.h */
-
-#include <windows.h>
-#include <grpc/support/atm.h>
-
-typedef int gpr_thd_id;
-
-#endif /* __GRPC_SUPPORT_THD_WIN32_H__ */
diff --git a/include/grpc/support/time.h b/include/grpc/support/time.h
index 6327a2c..9fb1d0b 100644
--- a/include/grpc/support/time.h
+++ b/include/grpc/support/time.h
@@ -34,31 +34,22 @@
#ifndef __GRPC_SUPPORT_TIME_H__
#define __GRPC_SUPPORT_TIME_H__
/* Time support.
- We use gpr_timespec, which is typedefed to struct timespec on platforms which
- have it. On some machines, absolute times may be in local time. */
-
-/* Platform specific header declares gpr_timespec.
- gpr_timespec contains:
- time_t tv_sec; // seconds since start of 1970
- int tv_nsec; // nanoseconds; always in 0..999999999; never negative.
- */
+ We use gpr_timespec, which is analogous to struct timespec. On some
+ machines, absolute times may be in local time. */
#include <grpc/support/port_platform.h>
-
-#if defined(GPR_POSIX_TIME)
-#include <grpc/support/time_posix.h>
-#elif defined(GPR_WIN32)
-#include <grpc/support/time_win32.h>
-#else
-#error could not determine platform for time
-#endif
-
#include <stddef.h>
+#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif
+typedef struct gpr_timespec {
+ time_t tv_sec;
+ int tv_nsec;
+} gpr_timespec;
+
/* Time constants. */
extern const gpr_timespec gpr_time_0; /* The zero time interval. */
extern const gpr_timespec gpr_inf_future; /* The far future */
@@ -103,10 +94,6 @@
/* Sleep until at least 'until' - an absolute timeout */
void gpr_sleep_until(gpr_timespec until);
-struct timeval gpr_timeval_from_timespec(gpr_timespec t);
-
-gpr_timespec gpr_timespec_from_timeval(struct timeval t);
-
double gpr_timespec_to_micros(gpr_timespec t);
#ifdef __cplusplus
diff --git a/include/grpc/support/time_posix.h b/include/grpc/support/time_posix.h
deleted file mode 100644
index 9ff6f7f..0000000
--- a/include/grpc/support/time_posix.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- *
- * 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_TIME_POSIX_H__
-#define __GRPC_SUPPORT_TIME_POSIX_H__
-/* Posix variant of gpr_time_platform.h */
-
-#include <sys/time.h>
-#include <time.h>
-
-typedef struct timespec gpr_timespec;
-
-#endif /* __GRPC_SUPPORT_TIME_POSIX_H__ */
diff --git a/src/core/channel/channel_stack.c b/src/core/channel/channel_stack.c
index e28bbd7..d9e722c 100644
--- a/src/core/channel/channel_stack.c
+++ b/src/core/channel/channel_stack.c
@@ -210,6 +210,7 @@
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 @@
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 @@
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 bcb024f..507b91b 100644
--- a/src/core/channel/client_channel.c
+++ b/src/core/channel/client_channel.c
@@ -298,6 +298,7 @@
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 @@
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 d35cede..61a6caf 100644
--- a/src/core/channel/connected_channel.c
+++ b/src/core/channel/connected_channel.c
@@ -298,10 +298,6 @@
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 @@
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 5b80368..7884b21 100644
--- a/src/core/iomgr/alarm.c
+++ b/src/core/iomgr/alarm.c
@@ -335,10 +335,6 @@
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 5c6b869..8503292 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/iomgr.c b/src/core/iomgr/iomgr.c
index 8989b49..3d6114c 100644
--- a/src/core/iomgr/iomgr.c
+++ b/src/core/iomgr/iomgr.c
@@ -51,6 +51,7 @@
static gpr_mu g_mu;
static gpr_cv g_cv;
+static gpr_cv g_rcv;
static delayed_callback *g_cbs_head = NULL;
static delayed_callback *g_cbs_tail = NULL;
static int g_shutdown;
@@ -86,6 +87,7 @@
gpr_thd_id id;
gpr_mu_init(&g_mu);
gpr_cv_init(&g_cv);
+ gpr_cv_init(&g_rcv);
grpc_alarm_list_init(gpr_now());
g_refs = 0;
grpc_iomgr_platform_init();
@@ -115,7 +117,7 @@
gpr_mu_lock(&g_mu);
}
if (g_refs) {
- if (gpr_cv_wait(&g_cv, &g_mu, shutdown_deadline) && g_cbs_head == NULL) {
+ if (gpr_cv_wait(&g_rcv, &g_mu, shutdown_deadline) && g_cbs_head == NULL) {
gpr_log(GPR_DEBUG,
"Failed to free %d iomgr objects before shutdown deadline: "
"memory leaks are likely",
@@ -126,12 +128,14 @@
}
gpr_mu_unlock(&g_mu);
+ grpc_kick_poller();
gpr_event_wait(&g_background_callback_executor_done, gpr_inf_future);
grpc_iomgr_platform_shutdown();
grpc_alarm_list_shutdown();
gpr_mu_destroy(&g_mu);
gpr_cv_destroy(&g_cv);
+ gpr_cv_destroy(&g_rcv);
}
void grpc_iomgr_ref(void) {
@@ -143,7 +147,7 @@
void grpc_iomgr_unref(void) {
gpr_mu_lock(&g_mu);
if (0 == --g_refs) {
- gpr_cv_signal(&g_cv);
+ gpr_cv_signal(&g_rcv);
}
gpr_mu_unlock(&g_mu);
}
diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c
index 994dbe4..b1c2c64 100644
--- a/src/core/iomgr/pollset_posix.c
+++ b/src/core/iomgr/pollset_posix.c
@@ -80,9 +80,7 @@
}
}
-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 0168116..575f884 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 f971cb3..7ef5894 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 <grpc/support/port_platform.h>
#ifdef GPR_LINUX
diff --git a/src/core/iomgr/socket_utils_posix.c b/src/core/iomgr/socket_utils_posix.c
index 06c5033..9184b2a 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 <fcntl.h>
diff --git a/src/core/iomgr/tcp_server_posix.c b/src/core/iomgr/tcp_server_posix.c
index d169d23..091f0aa 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 <grpc/support/port_platform.h>
#ifdef GPR_POSIX_SOCKET
-#define _GNU_SOURCE
#include "src/core/iomgr/tcp_server.h"
#include <limits.h>
diff --git a/src/core/support/cpu_linux.c b/src/core/support/cpu_linux.c
index eab8b7f..ad82174 100644
--- a/src/core/support/cpu_linux.c
+++ b/src/core/support/cpu_linux.c
@@ -31,44 +31,17 @@
*
*/
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif /* _GNU_SOURCE */
+
#include <grpc/support/port_platform.h>
#ifdef GPR_CPU_LINUX
#include "src/core/support/cpu.h"
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#define GRPC_GNU_SOURCE
-#endif
-
-#ifndef __USE_GNU
-#define __USE_GNU
-#define GRPC_USE_GNU
-#endif
-
-#ifndef __USE_MISC
-#define __USE_MISC
-#define GRPC_USE_MISC
-#endif
-
#include <sched.h>
-
-#ifdef GRPC_GNU_SOURCE
-#undef _GNU_SOURCE
-#undef GRPC_GNU_SOURCE
-#endif
-
-#ifdef GRPC_USE_GNU
-#undef __USE_GNU
-#undef GRPC_USE_GNU
-#endif
-
-#ifdef GRPC_USE_MISC
-#undef __USE_MISC
-#undef GRPC_USE_MISC
-#endif
-
#include <errno.h>
#include <unistd.h>
#include <string.h>
diff --git a/src/core/support/log_linux.c b/src/core/support/log_linux.c
index a0307e1..a64faa9 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 <grpc/support/port_platform.h>
#ifdef GPR_LINUX
diff --git a/src/core/support/log_posix.c b/src/core/support/log_posix.c
index ab2d2e5..05f45de 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 <grpc/support/port_platform.h>
#if defined(GPR_POSIX_LOG)
diff --git a/src/core/support/string_posix.c b/src/core/support/string_posix.c
index 5783281..a6bb805 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/support/sync_posix.c b/src/core/support/sync_posix.c
index 7f0e4a9..a28a4c6 100644
--- a/src/core/support/sync_posix.c
+++ b/src/core/support/sync_posix.c
@@ -33,11 +33,17 @@
/* Posix gpr synchroization support code. */
+#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 199309L
+#undef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 199309L
+#endif
+
#include <grpc/support/port_platform.h>
#ifdef GPR_POSIX_SYNC
#include <errno.h>
+#include <time.h>
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
#include <grpc/support/time.h>
@@ -67,7 +73,10 @@
if (gpr_time_cmp(abs_deadline, gpr_inf_future) == 0) {
err = pthread_cond_wait(cv, mu);
} else {
- err = pthread_cond_timedwait(cv, mu, &abs_deadline);
+ struct timespec abs_deadline_ts;
+ abs_deadline_ts.tv_sec = abs_deadline.tv_sec;
+ abs_deadline_ts.tv_nsec = abs_deadline.tv_nsec;
+ err = pthread_cond_timedwait(cv, mu, &abs_deadline_ts);
}
GPR_ASSERT(err == 0 || err == ETIMEDOUT || err == EAGAIN);
return err == ETIMEDOUT;
diff --git a/src/core/support/thd_posix.c b/src/core/support/thd_posix.c
index bac1d9c..74ca942 100644
--- a/src/core/support/thd_posix.c
+++ b/src/core/support/thd_posix.c
@@ -62,17 +62,19 @@
const gpr_thd_options *options) {
int thread_started;
pthread_attr_t attr;
+ pthread_t p;
struct thd_arg *a = gpr_malloc(sizeof(*a));
a->body = thd_body;
a->arg = arg;
GPR_ASSERT(pthread_attr_init(&attr) == 0);
GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0);
- thread_started = (pthread_create(t, &attr, &thread_body, a) == 0);
+ thread_started = (pthread_create(&p, &attr, &thread_body, a) == 0);
GPR_ASSERT(pthread_attr_destroy(&attr) == 0);
if (!thread_started) {
gpr_free(a);
}
+ *t = (gpr_thd_id)p;
return thread_started;
}
@@ -82,4 +84,8 @@
return options;
}
+gpr_thd_id gpr_thd_currentid(void) {
+ return (gpr_thd_id)pthread_self();
+}
+
#endif /* GPR_POSIX_SYNC */
diff --git a/src/core/support/thd_win32.c b/src/core/support/thd_win32.c
index 1762f87..2ee1417 100644
--- a/src/core/support/thd_win32.c
+++ b/src/core/support/thd_win32.c
@@ -58,16 +58,18 @@
int gpr_thd_new(gpr_thd_id *t, void (*thd_body)(void *arg), void *arg,
const gpr_thd_options *options) {
HANDLE handle;
+ DWORD thread_id;
struct thd_arg *a = gpr_malloc(sizeof(*a));
a->body = thd_body;
a->arg = arg;
*t = 0;
- handle = CreateThread(NULL, 64 * 1024, thread_body, a, 0, NULL);
+ handle = CreateThread(NULL, 64 * 1024, thread_body, a, 0, &thread_id);
if (handle == NULL) {
gpr_free(a);
} else {
CloseHandle(handle); /* threads are "detached" */
}
+ *t = (gpr_thd_id)thread_id;
return handle != NULL;
}
@@ -77,4 +79,8 @@
return options;
}
+gpr_thd_id gpr_thd_currentid(void) {
+ return (gpr_thd_id)GetCurrentThreadId();
+}
+
#endif /* GPR_WIN32 */
diff --git a/src/core/support/time.c b/src/core/support/time.c
index 9724331..268a43c 100644
--- a/src/core/support/time.c
+++ b/src/core/support/time.c
@@ -234,22 +234,6 @@
}
}
-struct timeval gpr_timeval_from_timespec(gpr_timespec t) {
- /* TODO(klempner): Consider whether this should round up, since it is likely
- to be used for delays */
- struct timeval tv;
- tv.tv_sec = t.tv_sec;
- tv.tv_usec = t.tv_nsec / 1000;
- return tv;
-}
-
-gpr_timespec gpr_timespec_from_timeval(struct timeval t) {
- gpr_timespec ts;
- ts.tv_sec = t.tv_sec;
- ts.tv_nsec = t.tv_usec * 1000;
- return ts;
-}
-
gpr_int32 gpr_time_to_millis(gpr_timespec t) {
if (t.tv_sec >= 2147483) {
if (t.tv_sec == 2147483 && t.tv_nsec < 648 * GPR_NS_PER_MS) {
diff --git a/src/core/support/time_posix.c b/src/core/support/time_posix.c
index 9e11f8a..7f0f028 100644
--- a/src/core/support/time_posix.c
+++ b/src/core/support/time_posix.c
@@ -34,7 +34,8 @@
/* Posix code for gpr time support. */
/* So we get nanosleep and clock_* */
-#ifndef _POSIX_C_SOURCE
+#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 199309L
+#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 199309L
#endif
@@ -47,11 +48,25 @@
#include <unistd.h>
#include <grpc/support/time.h>
+static struct timespec timespec_from_gpr(gpr_timespec gts) {
+ struct timespec rv;
+ rv.tv_sec = gts.tv_sec;
+ rv.tv_nsec = gts.tv_nsec;
+ return rv;
+}
+
#if _POSIX_TIMERS > 0
+static gpr_timespec gpr_from_timespec(struct timespec ts) {
+ gpr_timespec rv;
+ rv.tv_sec = ts.tv_sec;
+ rv.tv_nsec = ts.tv_nsec;
+ return rv;
+}
+
gpr_timespec gpr_now(void) {
- gpr_timespec now;
+ struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
- return now;
+ return gpr_from_timespec(now);
}
#else
/* For some reason Apple's OSes haven't implemented clock_gettime. */
@@ -69,6 +84,7 @@
void gpr_sleep_until(gpr_timespec until) {
gpr_timespec now;
gpr_timespec delta;
+ struct timespec delta_ts;
for (;;) {
/* We could simplify by using clock_nanosleep instead, but it might be
@@ -79,7 +95,8 @@
}
delta = gpr_time_sub(until, now);
- if (nanosleep(&delta, NULL) == 0) {
+ delta_ts = timespec_from_gpr(delta);
+ if (nanosleep(&delta_ts, NULL) == 0) {
break;
}
}
diff --git a/src/core/surface/byte_buffer_queue.c b/src/core/surface/byte_buffer_queue.c
new file mode 100644
index 0000000..9709a66
--- /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 <grpc/support/alloc.h>
+#include <grpc/support/useful.h>
+
+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/include/grpc/support/time_win32.h b/src/core/surface/byte_buffer_queue.h
similarity index 65%
rename from include/grpc/support/time_win32.h
rename to src/core/surface/byte_buffer_queue.h
index e62ad64..358a42d 100644
--- a/include/grpc/support/time_win32.h
+++ b/src/core/surface/byte_buffer_queue.h
@@ -31,16 +31,29 @@
*
*/
-#ifndef __GRPC_SUPPORT_TIME_WIN32_H__
-#define __GRPC_SUPPORT_TIME_WIN32_H__
-/* Win32 variant of gpr_time_platform.h */
+#ifndef __GRPC_INTERNAL_SURFACE_BYTE_BUFFER_QUEUE_H__
+#define __GRPC_INTERNAL_SURFACE_BYTE_BUFFER_QUEUE_H__
-#include <Winsock.h>
-#include <time.h>
+#include <grpc/byte_buffer.h>
-typedef struct gpr_timespec {
- time_t tv_sec;
- long tv_nsec;
-} gpr_timespec;
+/* 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;
-#endif /* __GRPC_SUPPORT_TIME_WIN32_H__ */
+/* 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 5a24264..c68ce5a 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 <grpc/support/alloc.h>
@@ -45,251 +46,722 @@
#include <stdlib.h>
#include <string.h>
-#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 struct {
- grpc_byte_buffer *byte_buffer;
- void *user_data;
- void (*on_finish)(void *user_data, grpc_op_error error);
-} pending_read;
-
-/* 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;
-
-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;
- }
-
- 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;
-}
-
-/* grpc_call proper */
-
-/* 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;
+ SEND_NOTHING,
+ SEND_INITIAL_METADATA,
+ SEND_MESSAGE,
+ SEND_TRAILING_METADATA_AND_FINISH,
+ SEND_FINISH
+} send_action;
+
+typedef struct {
+ grpc_ioreq_completion_func on_complete;
+ void *user_data;
+ grpc_op_error status;
+} completed_request;
+
+/* See request_set in grpc_call below for a description */
+#define REQSET_EMPTY 255
+#define REQSET_DONE 254
+
+typedef struct {
+ /* 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;
+
+typedef struct {
+ gpr_uint8 is_set;
+ grpc_status_code code;
+ grpc_mdstr *details;
+} received_status;
+
+/* 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;
+
+typedef enum {
+ 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;
+ /* TODO(ctiller): share with cq if possible? */
+ gpr_mu mu;
- call_state state;
+ /* 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);
}
+ for (i = 0; i < c->owned_metadata_count; i++) {
+ grpc_mdelem_unref(c->owned_metadata[i]);
+ }
+ 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);
+}
+
+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);
+ }
+ }
+}
+
+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;
+}
+
+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);
+ }
+ call->status[source].details = status;
+}
+
+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;
+}
+
+static void request_more_data(grpc_call *call) {
+ grpc_call_op op;
+
+ /* call down */
+ op.type = GRPC_REQUEST_DATA;
+ op.dir = GRPC_CALL_DOWN;
+ op.flags = 0;
+ op.done_cb = do_nothing;
+ op.user_data = NULL;
+
+ grpc_call_execute_op(call, &op);
+}
+
+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 lock(grpc_call *call) { gpr_mu_lock(&call->mu); }
+
+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;
+
+ if (need_more_data) {
+ call->need_more_data = 0;
+ }
+
+ if (num_completed_requests != 0) {
+ memcpy(completed_requests, call->completed_requests,
+ sizeof(completed_requests));
+ call->num_completed_requests = 0;
+ }
+
+ if (!call->sending) {
+ sa = choose_send_action(call);
+ if (sa != SEND_NOTHING) {
+ call->sending = 1;
+ grpc_call_internal_ref(call);
+ }
+ }
+
+ gpr_mu_unlock(&call->mu);
+
+ if (need_more_data) {
+ request_more_data(call);
+ }
+
+ if (sa != SEND_NOTHING) {
+ enact_send_action(call, sa);
+ }
+
+ for (i = 0; i < num_completed_requests; i++) {
+ completed_requests[i].on_complete(call, completed_requests[i].status,
+ completed_requests[i].user_data);
+ }
+}
+
+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;
+
+no_details:
+ if (0 == *args.details_capacity) {
+ *args.details_capacity = 8;
+ *args.details = gpr_malloc(*args.details_capacity);
+ }
+ **args.details = 0;
+}
+
+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 (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;
+ }
+}
+
+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);
+ }
+}
+
+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);
+}
+
+static void finish_write_step(void *pc, grpc_op_error error) {
+ finish_send_op(pc, GRPC_IOREQ_SEND_MESSAGE, error);
+}
+
+static void finish_finish_step(void *pc, grpc_op_error error) {
+ finish_send_op(pc, GRPC_IOREQ_SEND_CLOSE, error);
+}
+
+static void finish_start_step(void *pc, grpc_op_error error) {
+ finish_send_op(pc, GRPC_IOREQ_SEND_INITIAL_METADATA, error);
+}
+
+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 send_metadata(grpc_call *call, grpc_mdelem *elem) {
+ grpc_call_op op;
+ 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;
+ grpc_call_execute_op(call, &op);
+}
+
+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 (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;
+ }
+}
+
+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;
+}
+
+static void finish_read_ops(grpc_call *call) {
+ int empty;
+
+ 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 {
+ empty = grpc_bbq_empty(&call->incoming_queue);
+ }
+
+ 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;
+ }
+}
+
+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;
+ }
+}
+
+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;
+
+ if (nreqs == 0) {
+ return GRPC_CALL_OK;
+ }
+
+ set = reqs[0].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;
+
+ call->request_data[op] = data;
+ call->request_set[op] = set;
+ }
+
+ 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 (have_ops & (1u << GRPC_IOREQ_RECV_MESSAGE)) {
+ call->need_more_data = 1;
+ }
+
+ finish_read_ops(call);
+ early_out_write_ops(call);
+
+ return GRPC_CALL_OK;
+}
+
+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);
+}
+
+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;
+}
+
+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;
}
void grpc_call_destroy(grpc_call *c) {
int cancel;
- gpr_mu_lock(&c->read_mu);
+ lock(c);
if (c->have_alarm) {
grpc_alarm_cancel(&c->alarm);
c->have_alarm = 0;
}
- cancel = !c->received_finish;
- gpr_mu_unlock(&c->read_mu);
+ cancel = c->read_state != READ_STATE_STREAM_CLOSED;
+ unlock(c);
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;
- }
-}
-
-static void maybe_set_status_details(grpc_call *call, grpc_mdstr *status) {
- if (!call->status_details) {
- call->status_details = grpc_mdstr_ref(status);
- }
+ grpc_call_internal_unref(c, 1);
}
grpc_call_error grpc_call_cancel(grpc_call *c) {
@@ -314,12 +786,10 @@
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);
- }
- gpr_mu_unlock(&c->read_mu);
+ 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);
}
@@ -330,635 +800,10 @@
elem->filter->call_op(elem, NULL, op);
}
-void grpc_call_add_mdelem(grpc_call *call, grpc_mdelem *mdelem,
- gpr_uint32 flags) {
- grpc_call_element *elem;
- grpc_call_op op;
-
- GPR_ASSERT(call->state < CALL_FINISHED);
-
- op.type = GRPC_SEND_METADATA;
- op.dir = GRPC_CALL_DOWN;
- op.flags = flags;
- 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);
-}
-
-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 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;
-
- 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;
-
- elem = CALL_ELEM_FROM_CALL(call, 0);
- elem->filter->call_op(elem, NULL, &op);
- } else {
- done_writes_done(call, error);
- }
- }
-
- 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 (call->state >= CALL_STARTED || call->cq) {
- gpr_log(GPR_ERROR, "call is already invoked");
- return GRPC_CALL_ERROR_ALREADY_INVOKED;
- }
-
- 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;
- }
-
- 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 (flags & GRPC_WRITE_NO_COMPRESS) {
- return GRPC_CALL_ERROR_INVALID_FLAGS;
- }
-
- /* 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;
- }
-
- 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;
- }
-
- /* 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;
- }
- gpr_mu_unlock(&call->read_mu);
-
- return GRPC_CALL_OK;
-}
-
-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;
- }
-
- if (call->state >= CALL_STARTED) {
- gpr_log(GPR_ERROR, "call is already started");
- return GRPC_CALL_ERROR_ALREADY_INVOKED;
- }
-
- if (flags & GRPC_WRITE_NO_COMPRESS) {
- return GRPC_CALL_ERROR_INVALID_FLAGS;
- }
-
- /* 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);
-
- return GRPC_CALL_OK;
-}
-
-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;
-
- gpr_mu_lock(&call->read_mu);
- count = grpc_metadata_buffer_count(&call->incoming_metadata);
- elements = grpc_metadata_buffer_extract_elements(&call->incoming_metadata);
-
- 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 void request_more_data(grpc_call *call) {
- grpc_call_element *elem;
- grpc_call_op op;
-
- /* call down */
- op.type = GRPC_REQUEST_DATA;
- op.dir = GRPC_CALL_DOWN;
- op.flags = 0;
- 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_error grpc_call_start_read_old(grpc_call *call, void *tag) {
- gpr_uint8 request_more = 0;
-
- switch (call->state) {
- case CALL_CREATED:
- return GRPC_CALL_ERROR_NOT_INVOKED;
- case CALL_BOUNDCQ:
- case CALL_STARTED:
- 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;
- }
-
- grpc_cq_begin_op(call->cq, call, GRPC_READ);
-
- 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;
- }
- } else if (prq_is_empty(&call->prq) && call->received_finish) {
- finish_call(call);
- }
-
- gpr_mu_unlock(&call->read_mu);
-
- if (request_more) {
- request_more_data(call);
- }
-
- 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:
- 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);
-
- /* 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);
- 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;
-}
-
-grpc_call_error grpc_call_writes_done_old(grpc_call *call, void *tag) {
- grpc_call_element *elem;
- grpc_call_op op;
-
- if (!call->is_client) {
- return GRPC_CALL_ERROR_NOT_ON_SERVER;
- }
-
- 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;
- }
-
- if (call->have_write) {
- return GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
- }
-
- grpc_cq_begin_op(call->cq, call, GRPC_FINISH_ACCEPTED);
-
- call->write_tag = tag;
- call->have_write = 1;
-
- gpr_mu_lock(&call->read_mu);
- if (!call->received_start) {
- call->pending_writes_done = 1;
-
- gpr_mu_unlock(&call->read_mu);
- } else {
- gpr_mu_unlock(&call->read_mu);
-
- op.type = GRPC_SEND_FINISH;
- op.dir = GRPC_CALL_DOWN;
- op.flags = 0;
- op.done_cb = done_writes_done;
- op.user_data = call;
-
- elem = CALL_ELEM_FROM_CALL(call, 0);
- elem->filter->call_op(elem, NULL, &op);
- }
-
- return GRPC_CALL_OK;
-}
-
-grpc_call_error grpc_call_start_write_status_old(grpc_call *call,
- grpc_status_code status,
- const char *details,
- void *tag) {
- grpc_call_element *elem;
- grpc_call_op op;
-
- if (call->is_client) {
- return GRPC_CALL_ERROR_NOT_ON_CLIENT;
- }
-
- 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;
- }
-
- if (call->have_write) {
- return GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
- }
-
- elem = CALL_ELEM_FROM_CALL(call, 0);
-
- if (details && details[0]) {
- grpc_mdelem *md = grpc_mdelem_from_strings(call->metadata_context,
- "grpc-message", details);
-
- 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);
- }
-
- /* 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);
-
- 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);
- }
-
- 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;
-
- elem->filter->call_op(elem, NULL, &op);
-
- return GRPC_CALL_OK;
-}
-
-/* we offset status by a small amount when storing it into transport metadata
- as metadata cannot store a 0 value (which is used as OK for grpc_status_codes
- */
-#define STATUS_OFFSET 1
-static void destroy_status(void *ignored) {}
-
-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;
- } else {
- if (!gpr_parse_bytes_to_uint32(grpc_mdstr_as_c_string(md->value),
- GPR_SLICE_LENGTH(md->value->slice),
- &status)) {
- status = GRPC_STATUS_UNKNOWN; /* could not parse status code */
- }
- grpc_mdelem_set_user_data(md, destroy_status,
- (void *)(gpr_intptr)(status + STATUS_OFFSET));
- }
- return status;
-}
-
-void grpc_call_recv_metadata(grpc_call_element *elem, grpc_call_op *op) {
- grpc_call *call = CALL_FROM_TOP_ELEM(elem);
- grpc_mdelem *md = op->data.metadata;
- grpc_mdstr *key = md->key;
-
- if (key == grpc_channel_get_status_string(call->channel)) {
- maybe_set_status_code(call, 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);
- grpc_mdelem_unref(md);
- op->done_cb(op->user_data, GRPC_OP_OK);
- } else {
- grpc_metadata_buffer_queue(&call->incoming_metadata, op);
- }
-}
-
-void grpc_call_recv_finish(grpc_call_element *elem, int is_full_close) {
- grpc_call *call = CALL_FROM_TOP_ELEM(elem);
-
- gpr_mu_lock(&call->read_mu);
-
- 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;
-
- call->received_metadata = 1;
-
- 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);
- }
- 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);
- }
- } else {
- call->reads_done = 1;
- }
- gpr_mu_unlock(&call->read_mu);
-}
-
-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);
-
- 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);
- }
- gpr_mu_unlock(&call->read_mu);
-}
-
grpc_call *grpc_call_from_top_element(grpc_call_element *elem) {
return CALL_FROM_TOP_ELEM(elem);
}
-grpc_metadata_buffer *grpc_call_get_metadata_buffer(grpc_call *call) {
- return &call->incoming_metadata;
-}
-
static void call_alarm(void *arg, int success) {
grpc_call *call = arg;
if (success) {
@@ -969,7 +814,7 @@
grpc_call_cancel(call);
}
}
- grpc_call_internal_unref(call);
+ grpc_call_internal_unref(call, 1);
}
void grpc_call_set_deadline(grpc_call_element *elem, gpr_timespec deadline) {
@@ -983,7 +828,403 @@
grpc_alarm_init(&call->alarm, deadline, call_alarm, call, gpr_now());
}
+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);
+}
+
+void grpc_call_read_closed(grpc_call_element *elem) {
+ set_read_state(CALL_FROM_TOP_ELEM(elem), READ_STATE_READ_CLOSED);
+}
+
+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
+ as metadata cannot store a 0 value (which is used as OK for grpc_status_codes
+ */
+#define STATUS_OFFSET 1
+static void destroy_status(void *ignored) {}
+
+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;
+ } else {
+ if (!gpr_parse_bytes_to_uint32(grpc_mdstr_as_c_string(md->value),
+ GPR_SLICE_LENGTH(md->value->slice),
+ &status)) {
+ status = GRPC_STATUS_UNKNOWN; /* could not parse status code */
+ }
+ grpc_mdelem_set_user_data(md, destroy_status,
+ (void *)(gpr_intptr)(status + STATUS_OFFSET));
+ }
+ return status;
+}
+
+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_mdstr *key = md->key;
+ grpc_metadata_array *dest;
+ grpc_metadata *mdusr;
+
+ lock(call);
+ if (key == grpc_channel_get_status_string(call->channel)) {
+ set_status_code(call, STATUS_FROM_WIRE, decode_status(md));
+ grpc_mdelem_unref(md);
+ } else if (key == grpc_channel_get_message_string(call->channel)) {
+ set_status_details(call, STATUS_FROM_WIRE, grpc_mdstr_ref(md->value));
+ grpc_mdelem_unref(md);
+ } else {
+ 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);
+}
+
grpc_call_stack *grpc_call_get_call_stack(grpc_call *call) {
return CALL_STACK_FROM_CALL(call);
}
+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);
+}
+
+/*
+ * 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;
+
+ size_t details_capacity;
+ char *details;
+ grpc_status_code status;
+
+ 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));
+ }
+ 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);
+ }
+ gpr_free(ls->md_out[i]);
+ }
+ 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);
+}
+
+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);
+
+ } else {
+ grpc_cq_end_client_metadata_read(call->cq, tag, call, do_nothing, NULL, 0,
+ NULL);
+ }
+ unlock(call);
+}
+
+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_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 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;
+}
+
+static void finish_read_event(void *p, grpc_op_error error) {
+ if (p) grpc_byte_buffer_destroy(p);
+}
+
+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_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 804b387..936fb29 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 <grpc/grpc.h>
+/* 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 @@
/* 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 93a2c06..b33bd7b 100644
--- a/src/core/surface/channel.c
+++ b/src/core/surface/channel.c
@@ -51,7 +51,10 @@
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 @@
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 *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 @@
/* 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 @@
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 b3ea2ed..ff9bbc2 100644
--- a/src/core/surface/channel.h
+++ b/src/core/surface/channel.h
@@ -45,6 +45,8 @@
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 a7c9b90..64ee9d5 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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
@@ -56,23 +57,23 @@
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 @@
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 2bf31c5..ae3b960 100644
--- a/src/core/surface/completion_queue.c
+++ b/src/core/surface/completion_queue.c
@@ -173,18 +173,6 @@
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 @@
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 @@
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 8598407..fea8336 100644
--- a/src/core/surface/completion_queue.h
+++ b/src/core/surface/completion_queue.h
@@ -97,6 +97,10 @@
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 8975d31..7c76bf9 100644
--- a/src/core/surface/event_string.c
+++ b/src/core/surface/event_string.c
@@ -87,10 +87,10 @@
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 6098ac7..411dbab 100644
--- a/src/core/surface/lame_client.c
+++ b/src/core/surface/lame_client.c
@@ -50,26 +50,16 @@
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 @@
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 9e2e4d5..455bd43 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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/useful.h>
@@ -63,11 +64,24 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
}
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 @@
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 @@
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 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 @@
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 @@
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 @@
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 @@
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);
+}
- return GRPC_CALL_OK;
+static void begin_request(grpc_server *server, grpc_completion_queue *cq,
+ grpc_metadata_array *initial_metadata,
+ call_data *call_data, void *tag) {
+ abort();
+}
+
+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 c4e3ca5..2af18c3 100644
--- a/src/core/transport/chttp2/stream_encoder.c
+++ b/src/core/transport/chttp2/stream_encoder.c
@@ -432,7 +432,7 @@
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 48a1005..ea579cf 100644
--- a/src/core/transport/chttp2_transport.c
+++ b/src/core/transport/chttp2_transport.c
@@ -184,7 +184,6 @@
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 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 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 @@
/* 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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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/core/tsi/ssl_transport_security.c b/src/core/tsi/ssl_transport_security.c
index 0f8cbcc..e23421f 100644
--- a/src/core/tsi/ssl_transport_security.c
+++ b/src/core/tsi/ssl_transport_security.c
@@ -37,6 +37,7 @@
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
#include <grpc/support/useful.h>
#include "src/core/tsi/transport_security.h"
@@ -103,11 +104,32 @@
/* --- Library Initialization. ---*/
static gpr_once init_openssl_once = GPR_ONCE_INIT;
+static gpr_mu *openssl_mutexes = NULL;
+
+static void openssl_locking_cb(int mode, int type, const char* file, int line) {
+ if (mode & CRYPTO_LOCK) {
+ gpr_mu_lock(&openssl_mutexes[type]);
+ } else {
+ gpr_mu_unlock(&openssl_mutexes[type]);
+ }
+}
+
+static unsigned long openssl_thread_id_cb(void) {
+ return (unsigned long)gpr_thd_currentid();
+}
static void init_openssl(void) {
+ int i;
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
+ openssl_mutexes = malloc(CRYPTO_num_locks() * sizeof(gpr_mu));
+ GPR_ASSERT(openssl_mutexes != NULL);
+ for (i = 0; i < CRYPTO_num_locks(); i++) {
+ gpr_mu_init(&openssl_mutexes[i]);
+ }
+ CRYPTO_set_locking_callback(openssl_locking_cb);
+ CRYPTO_set_id_callback(openssl_thread_id_cb);
}
/* --- Ssl utils. ---*/
diff --git a/src/cpp/client/channel.cc b/src/cpp/client/channel.cc
index 5822e30..3f39364 100644
--- a/src/cpp/client/channel.cc
+++ b/src/cpp/client/channel.cc
@@ -102,6 +102,7 @@
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<char *>(call);
void *metadata_read_tag = reinterpret_cast<char *>(call) + 2;
diff --git a/src/cpp/server/async_server_context.cc b/src/cpp/server/async_server_context.cc
index 2dd3cd1..886e794 100644
--- a/src/cpp/server/async_server_context.cc
+++ b/src/cpp/server/async_server_context.cc
@@ -54,7 +54,7 @@
void AsyncServerContext::Accept(grpc_completion_queue *cq) {
GPR_ASSERT(grpc_call_server_accept_old(call_, cq, this) == GRPC_CALL_OK);
- GPR_ASSERT(grpc_call_server_end_initial_metadata_old(call_, 0) ==
+ GPR_ASSERT(grpc_call_server_end_initial_metadata_old(call_, GRPC_WRITE_BUFFER_HINT) ==
GRPC_CALL_OK);
}
diff --git a/src/cpp/server/server_rpc_handler.cc b/src/cpp/server/server_rpc_handler.cc
index 061ac1c..bf02de8 100644
--- a/src/cpp/server/server_rpc_handler.cc
+++ b/src/cpp/server/server_rpc_handler.cc
@@ -77,7 +77,7 @@
if (status.IsOk()) {
// Send the response if we get an ok status.
- async_server_context_->StartWrite(*response, 0);
+ async_server_context_->StartWrite(*response, GRPC_WRITE_BUFFER_HINT);
type = WaitForNextEvent();
if (type != CompletionQueue::SERVER_WRITE_OK) {
status = Status(StatusCode::INTERNAL, "Error writing response.");
diff --git a/src/csharp/.gitignore b/src/csharp/.gitignore
new file mode 100644
index 0000000..dbf38f3
--- /dev/null
+++ b/src/csharp/.gitignore
@@ -0,0 +1,2 @@
+*.userprefs
+test-results
diff --git a/src/csharp/Grpc.sln b/src/csharp/Grpc.sln
new file mode 100644
index 0000000..5890617
--- /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 0000000..2cc8cca
--- /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 0000000..6799109
--- /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
+{
+// /// <summary>
+// /// Dummy local implementation of math service.
+// /// </summary>
+// public class DummyMathServiceClient : IMathServiceClient
+// {
+// public DivReply Div(DivArgs args, CancellationToken token = default(CancellationToken))
+// {
+// // TODO: cancellation...
+// return DivInternal(args);
+// }
+//
+// public Task<DivReply> DivAsync(DivArgs args, CancellationToken token = default(CancellationToken))
+// {
+// return Task.Factory.StartNew(() => DivInternal(args), token);
+// }
+//
+// public IObservable<Num> 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<Num> Sum(IObservable<Num> inputs, CancellationToken token = default(CancellationToken))
+// {
+// // TODO: implement
+// inputs = null;
+// return Task.Factory.StartNew(() => Num.CreateBuilder().Build(), token);
+// }
+//
+// public IObservable<DivReply> DivMany(IObservable<DivArgs> inputs, CancellationToken token = default(CancellationToken))
+// {
+// // TODO: implement
+// inputs = null;
+// return new List<DivReply> { }.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<Num> 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 0000000..d45b702
--- /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<DivReply> 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<DivReply> 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<Num>();
+ stub.Fib(new FibArgs.Builder { Limit = 5 }.Build(), recorder);
+
+ List<Num> numbers = recorder.ToList().Result;
+ Console.WriteLine("Fib Result: " + string.Join("|", recorder.ToList().Result));
+ }
+
+ public static void SumExample(IMathServiceClient stub)
+ {
+ List<Num> numbers = new List<Num>{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<DivArgs> divArgsList = new List<DivArgs>{
+ 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<DivReply>();
+
+ 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<Num>
+ { new Num.Builder{ Num_ = 1 }.Build(),
+ new Num.Builder{ Num_ = 2 }.Build(), new Num.Builder{ Num_ = 3 }.Build()
+ };
+
+ numberList.ToObservable();
+
+ //IObserver<Num> numbers;
+ //Task<Num> 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 0000000..d037782
--- /dev/null
+++ b/src/csharp/GrpcApi/GrpcApi.csproj
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>10.0.0</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{7DC1433E-3225-42C7-B7EA-546D56E27A4B}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <RootNamespace>GrpcApi</RootNamespace>
+ <AssemblyName>GrpcApi</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug</OutputPath>
+ <DefineConstants>DEBUG;</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>full</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Reactive.Linq, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="System.Data.Linq" />
+ <Reference Include="System.Reactive.Interfaces, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="System.Reactive.Core, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Google.ProtocolBuffers">
+ <HintPath>..\lib\Google.ProtocolBuffers.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Examples.cs" />
+ <Compile Include="IMathServiceClient.cs" />
+ <Compile Include="Math.cs" />
+ <Compile Include="DummyMathServiceClient.cs" />
+ <Compile Include="MathServiceClientStub.cs" />
+ <Compile Include="RecordingObserver.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ItemGroup>
+ <ProjectReference Include="..\GrpcCore\GrpcCore.csproj">
+ <Project>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</Project>
+ <Name>GrpcCore</Name>
+ </ProjectReference>
+ </ItemGroup>
+</Project>
\ 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 0000000..51385a3
--- /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
+{
+ /// <summary>
+ /// Hand-written stub for MathService defined in math.proto.
+ /// This code will be generated by gRPC codegen in the future.
+ /// </summary>
+ public interface IMathServiceClient
+ {
+ DivReply Div(DivArgs args, CancellationToken token = default(CancellationToken));
+
+ Task<DivReply> DivAsync(DivArgs args, CancellationToken token = default(CancellationToken));
+
+ Task Fib(FibArgs args, IObserver<Num> outputs, CancellationToken token = default(CancellationToken));
+
+ ClientStreamingAsyncResult<Num, Num> Sum(CancellationToken token = default(CancellationToken));
+
+ IObserver<DivArgs> DivMany(IObserver<DivReply> 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 0000000..2d70033
--- /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<global::math.DivArgs, global::math.DivArgs.Builder> internal__static_math_DivArgs__FieldAccessorTable;
+ internal static pbd::MessageDescriptor internal__static_math_DivReply__Descriptor;
+ internal static pb::FieldAccess.FieldAccessorTable<global::math.DivReply, global::math.DivReply.Builder> internal__static_math_DivReply__FieldAccessorTable;
+ internal static pbd::MessageDescriptor internal__static_math_FibArgs__Descriptor;
+ internal static pb::FieldAccess.FieldAccessorTable<global::math.FibArgs, global::math.FibArgs.Builder> internal__static_math_FibArgs__FieldAccessorTable;
+ internal static pbd::MessageDescriptor internal__static_math_Num__Descriptor;
+ internal static pb::FieldAccess.FieldAccessorTable<global::math.Num, global::math.Num.Builder> internal__static_math_Num__FieldAccessorTable;
+ internal static pbd::MessageDescriptor internal__static_math_FibReply__Descriptor;
+ internal static pb::FieldAccess.FieldAccessorTable<global::math.FibReply, global::math.FibReply.Builder> 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<global::math.DivArgs, global::math.DivArgs.Builder>(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<global::math.DivReply, global::math.DivReply.Builder>(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<global::math.FibArgs, global::math.FibArgs.Builder>(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<global::math.Num, global::math.Num.Builder>(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<global::math.FibReply, global::math.FibReply.Builder>(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<DivArgs, DivArgs.Builder> {
+ 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<DivArgs, DivArgs.Builder> 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<DivArgs, Builder> {
+ 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<DivReply, DivReply.Builder> {
+ 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<DivReply, DivReply.Builder> 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<DivReply, Builder> {
+ 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<FibArgs, FibArgs.Builder> {
+ 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<FibArgs, FibArgs.Builder> 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<FibArgs, Builder> {
+ 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<Num, Num.Builder> {
+ 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<Num, Num.Builder> 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<Num, Builder> {
+ 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<FibReply, FibReply.Builder> {
+ 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<FibReply, FibReply.Builder> 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<FibReply, Builder> {
+ 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 0000000..493c186
--- /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
+{
+ /// <summary>
+ /// Implementation of math service stub (this is handwritten version of code
+ /// that will normally be generated).
+ /// </summary>
+ 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<DivArgs, DivReply>("/math.Math/Div", Serialize_DivArgs, Deserialize_DivReply, methodTimeout, channel);
+ return Calls.BlockingUnaryCall(call, args, token);
+ }
+
+ public Task<DivReply> DivAsync(DivArgs args, CancellationToken token = default(CancellationToken))
+ {
+ var call = new Google.GRPC.Core.Call<DivArgs, DivReply>("/math.Math/Div", Serialize_DivArgs, Deserialize_DivReply, methodTimeout, channel);
+ return Calls.AsyncUnaryCall(call, args, token);
+ }
+
+ public Task Fib(FibArgs args, IObserver<Num> outputs, CancellationToken token = default(CancellationToken))
+ {
+ var call = new Google.GRPC.Core.Call<FibArgs, Num>("/math.Math/Fib", Serialize_FibArgs, Deserialize_Num, methodTimeout, channel);
+ return Calls.AsyncServerStreamingCall(call, args, outputs, token);
+ }
+
+ public ClientStreamingAsyncResult<Num, Num> Sum(CancellationToken token = default(CancellationToken))
+ {
+ var call = new Google.GRPC.Core.Call<Num, Num>("/math.Math/Sum", Serialize_Num, Deserialize_Num, methodTimeout, channel);
+ return Calls.AsyncClientStreamingCall(call, token);
+ }
+
+ public IObserver<DivArgs> DivMany(IObserver<DivReply> outputs, CancellationToken token = default(CancellationToken))
+ {
+ var call = new Google.GRPC.Core.Call<DivArgs, DivReply>("/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 0000000..b08816b
--- /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 0000000..725f12c
--- /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 0000000..8ba3787
--- /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<T> : IObserver<T>
+ {
+ TaskCompletionSource<List<T>> tcs = new TaskCompletionSource<List<T>>();
+ List<T> data = new List<T>();
+
+ public void OnCompleted()
+ {
+ tcs.SetResult(data);
+ }
+
+ public void OnError(Exception error)
+ {
+ tcs.SetException(error);
+ }
+
+ public void OnNext(T value)
+ {
+ data.Add(value);
+ }
+
+ public Task<List<T>> ToList() {
+ return tcs.Task;
+ }
+ }
+}
+
diff --git a/src/csharp/GrpcApi/math.proto b/src/csharp/GrpcApi/math.proto
new file mode 100755
index 0000000..e98b99e
--- /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 0000000..ba077a4
--- /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 0000000..bf257e5
--- /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<TRequest, TResponse>
+ {
+ readonly string methodName;
+ readonly Func<TRequest, byte[]> requestSerializer;
+ readonly Func<byte[], TResponse> responseDeserializer;
+ readonly TimeSpan timeout;
+ readonly Channel channel;
+
+ // TODO: channel param should be removed in the future.
+ public Call(string methodName,
+ Func<TRequest, byte[]> requestSerializer,
+ Func<byte[], TResponse> 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<TRequest, byte[]> RequestSerializer
+ {
+ get
+ {
+ return this.requestSerializer;
+ }
+ }
+
+ public Func<byte[], TResponse> ResponseDeserializer
+ {
+ get
+ {
+ return this.responseDeserializer;
+ }
+ }
+ }
+}
+
diff --git a/src/csharp/GrpcCore/Calls.cs b/src/csharp/GrpcCore/Calls.cs
new file mode 100644
index 0000000..c3e51cb
--- /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
+
+ /// <summary>
+ /// Helper methods for generated stubs to make RPC calls.
+ /// </summary>
+ public static class Calls
+ {
+ public static TResponse BlockingUnaryCall<TRequest, TResponse>(Call<TRequest, TResponse> 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<TResponse> AsyncUnaryCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, CancellationToken token)
+ {
+ var asyncCall = new AsyncCall<TRequest, TResponse>(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<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, IObserver<TResponse> outputs, CancellationToken token)
+ {
+ var asyncCall = new AsyncCall<TRequest, TResponse>(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<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, CancellationToken token)
+ {
+ var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer);
+ asyncCall.Initialize(call.Channel, call.MethodName);
+ asyncCall.Start(false, GetCompletionQueue());
+
+ var task = asyncCall.ReadAsync();
+ var inputs = new StreamingInputObserver<TRequest, TResponse>(asyncCall);
+ return new ClientStreamingAsyncResult<TRequest, TResponse>(task, inputs);
+ }
+
+ public static TResponse BlockingClientStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, IObservable<TRequest> inputs, CancellationToken token)
+ {
+ throw new NotImplementedException();
+ }
+
+ public static IObserver<TRequest> DuplexStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, IObserver<TResponse> outputs, CancellationToken token)
+ {
+ var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer);
+ asyncCall.Initialize(call.Channel, call.MethodName);
+ asyncCall.Start(false, GetCompletionQueue());
+
+ asyncCall.StartReadingToStream(outputs);
+ var inputs = new StreamingInputObserver<TRequest, TResponse>(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 0000000..b0d8bee
--- /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
+ {
+ /// <summary>
+ /// Make sure GPRC environment is initialized before any channels get used.
+ /// </summary>
+ 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 0000000..9e7312c
--- /dev/null
+++ b/src/csharp/GrpcCore/ClientStreamingAsyncResult.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Threading.Tasks;
+
+namespace Google.GRPC.Core
+{
+ /// <summary>
+ /// Return type for client streaming async method.
+ /// </summary>
+ public struct ClientStreamingAsyncResult<TRequest, TResponse>
+ {
+ readonly Task<TResponse> task;
+ readonly IObserver<TRequest> inputs;
+
+ public ClientStreamingAsyncResult(Task<TResponse> task, IObserver<TRequest> inputs)
+ {
+ this.task = task;
+ this.inputs = inputs;
+ }
+
+ public Task<TResponse> Task
+ {
+ get
+ {
+ return this.task;
+ }
+ }
+
+ public IObserver<TRequest> Inputs
+ {
+ get
+ {
+ return this.inputs;
+ }
+ }
+ }
+}
+
diff --git a/src/csharp/GrpcCore/GrpcCore.csproj b/src/csharp/GrpcCore/GrpcCore.csproj
new file mode 100644
index 0000000..f0c84e7
--- /dev/null
+++ b/src/csharp/GrpcCore/GrpcCore.csproj
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>10.0.0</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <RootNamespace>GrpcCore</RootNamespace>
+ <AssemblyName>GrpcCore</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug</OutputPath>
+ <DefineConstants>DEBUG;</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>full</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="RpcException.cs" />
+ <Compile Include="Calls.cs" />
+ <Compile Include="Call.cs" />
+ <Compile Include="ClientStreamingAsyncResult.cs" />
+ <Compile Include="GrpcEnvironment.cs" />
+ <Compile Include="Status.cs" />
+ <Compile Include="StatusCode.cs" />
+ <Compile Include="Server.cs" />
+ <Compile Include="Channel.cs" />
+ <Compile Include="Internal\CallSafeHandle.cs" />
+ <Compile Include="Internal\ChannelSafeHandle.cs" />
+ <Compile Include="Internal\CompletionQueueSafeHandle.cs" />
+ <Compile Include="Internal\Enums.cs" />
+ <Compile Include="Internal\Event.cs" />
+ <Compile Include="Internal\SafeHandleZeroIsInvalid.cs" />
+ <Compile Include="Internal\Timespec.cs" />
+ <Compile Include="Internal\GrpcThreadPool.cs" />
+ <Compile Include="Internal\AsyncCall.cs" />
+ <Compile Include="Internal\ServerSafeHandle.cs" />
+ <Compile Include="Internal\StreamingInputObserver.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ItemGroup>
+ <Folder Include="Internal\" />
+ </ItemGroup>
+</Project>
\ 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 0000000..7a644f49
--- /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
+{
+ /// <summary>
+ /// 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.
+ /// </summary>
+ 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);
+
+ /// <summary>
+ /// Makes sure GRPC environment is initialized.
+ /// </summary>
+ public static void EnsureInitialized() {
+ lock(staticLock)
+ {
+ if (!initCalled)
+ {
+ initCalled = true;
+ GrpcInit();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Shuts down the GRPC environment if it was initialized before.
+ /// Repeated invocations have no effect.
+ /// </summary>
+ public static void Shutdown()
+ {
+ lock(staticLock)
+ {
+ if (initCalled && !shutdownCalled)
+ {
+ shutdownCalled = true;
+ GrpcShutdown();
+ }
+ }
+
+ }
+
+ /// <summary>
+ /// Initializes GRPC C Core library.
+ /// </summary>
+ private static void GrpcInit()
+ {
+ grpc_init();
+ threadPool.Start();
+ // TODO: use proper logging here
+ Console.WriteLine("GRPC initialized.");
+ }
+
+ /// <summary>
+ /// Shutdown GRPC C Core library.
+ /// </summary>
+ 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 0000000..e83ca0e
--- /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
+{
+ /// <summary>
+ /// Listener for call events that can be delivered from a completion queue.
+ /// </summary>
+ 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);
+ }
+
+ /// <summary>
+ /// Handle native call lifecycle and provides convenience methods.
+ /// </summary>
+ internal class AsyncCall<TWrite, TRead>: ICallEventListener, IDisposable
+ {
+ readonly Func<TWrite, byte[]> serializer;
+ readonly Func<byte[], TRead> 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<Status> finishedStatus;
+
+ TaskCompletionSource<object> writeTcs;
+ TaskCompletionSource<TRead> readTcs;
+ TaskCompletionSource<object> halfcloseTcs = new TaskCompletionSource<object>();
+ TaskCompletionSource<Status> finishedTcs = new TaskCompletionSource<Status>();
+
+ IObserver<TRead> readObserver;
+
+ public AsyncCall(Func<TWrite, byte[]> serializer, Func<byte[], TRead> 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<TRead> ReadAsync()
+ {
+ return StartRead().Task;
+ }
+
+ public Task<Status> Finished
+ {
+ get
+ {
+ return finishedTcs.Task;
+ }
+ }
+
+ /// <summary>
+ /// Initiates reading to given observer.
+ /// </summary>
+ public void StartReadingToStream(IObserver<TRead> 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<object> 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<object>();
+ 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<TRead> 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<TRead>();
+ 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<TRead> oldTcs = null;
+ IObserver<TRead> 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<object> 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 0000000..6c9c58a
--- /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);
+
+ /// <summary>
+ /// grpc_call from <grpc/grpc.h>
+ /// </summary>
+ 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()
+ {
+ }
+
+ /// <summary>
+ /// Creates a client call.
+ /// </summary>
+ 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 0000000..3a09d8b
--- /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
+{
+ /// <summary>
+ /// grpc_channel from <grpc/grpc.h>
+ /// </summary>
+ 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 0000000..73dd3ed
--- /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
+{
+ /// <summary>
+ /// grpc_completion_queue from <grpc/grpc.h>
+ /// </summary>
+ 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 0000000..46e3bca
--- /dev/null
+++ b/src/csharp/GrpcCore/Internal/Enums.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Google.GRPC.Core.Internal
+{
+ /// <summary>
+ /// from grpc/grpc.h
+ /// </summary>
+ 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
+ }
+
+ /// <summary>
+ /// grpc_completion_type from grpc/grpc.h
+ /// </summary>
+ 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 */
+ }
+
+ /// <summary>
+ /// grpc_op_error from grpc/grpc.h
+ /// </summary>
+ 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 0000000..7056005
--- /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
+{
+ /// <summary>
+ /// grpc_event from grpc/grpc.h
+ /// </summary>
+ 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!
+ /// <summary>
+ /// Not owned version of
+ /// grpc_event from grpc/grpc.h
+ /// </summary>
+ 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 0000000..1139e54
--- /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
+{
+ /// <summary>
+ /// Pool of threads polling on the same completion queue.
+ /// </summary>
+ internal class GrpcThreadPool
+ {
+ readonly object myLock = new object();
+ readonly List<Thread> threads = new List<Thread>();
+ readonly int poolSize;
+ readonly Action<EventSafeHandle> eventHandler;
+
+ CompletionQueueSafeHandle cq;
+
+ public GrpcThreadPool(int poolSize) {
+ this.poolSize = poolSize;
+ }
+
+ internal GrpcThreadPool(int poolSize, Action<EventSafeHandle> 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;
+ }
+
+ /// <summary>
+ /// Body of the polling thread.
+ /// </summary>
+ 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.");
+ }
+
+ /// <summary>
+ /// Body of the polling thread.
+ /// </summary>
+ 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 0000000..5a1252b
--- /dev/null
+++ b/src/csharp/GrpcCore/Internal/SafeHandleZeroIsInvalid.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Google.GRPC.Core.Internal
+{
+ /// <summary>
+ /// Safe handle to wrap native objects.
+ /// </summary>
+ 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 0000000..0d38bce
--- /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
+{
+ /// <summary>
+ /// grpc_server from grpc/grpc.h
+ /// </summary>
+ 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 0000000..d483e53
--- /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<TWrite, TRead> : IObserver<TWrite>
+ {
+ readonly AsyncCall<TWrite, TRead> call;
+
+ public StreamingInputObserver(AsyncCall<TWrite, TRead> 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 0000000..8ffaf70
--- /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
+{
+ /// <summary>
+ /// gpr_timespec from grpc/support/time.h
+ /// </summary>
+ [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;
+
+ /// <summary>
+ /// Timespec a long time in the future.
+ /// </summary>
+ 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();
+ }
+ }
+
+ /// <summary>
+ /// Creates a GPR deadline from current instant and given timeout.
+ /// </summary>
+ /// <returns>The from timeout.</returns>
+ 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 0000000..74aba25
--- /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 0000000..8811c3a
--- /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 0000000..68da1a8
--- /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
+{
+ /// <summary>
+ /// Server is implemented only to be able to do
+ /// in-process testing.
+ /// </summary>
+ 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<NewRpcInfo> newRpcQueue = new BlockingCollection<NewRpcInfo>();
+ 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<byte[], byte[]> asyncCall = new AsyncCall<byte[], byte[]>(
+ (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 0000000..f1212f8
--- /dev/null
+++ b/src/csharp/GrpcCore/Status.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Google.GRPC.Core
+{
+ /// <summary>
+ /// Represents RPC result.
+ /// </summary>
+ 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 0000000..80fc8bd
--- /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.
+ /// <summary>
+ /// grpc_status_code from grpc/status.h
+ /// </summary>
+ 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 0000000..2cc8cca
--- /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 0000000..823ee94
--- /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<string, string> CreateCall(Channel channel)
+ {
+ return new Call<string, string>("/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 0000000..3de0f58
--- /dev/null
+++ b/src/csharp/GrpcCoreTests/GrpcCoreTests.csproj
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>10.0.0</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{86EC5CB4-4EA2-40A2-8057-86542A0353BB}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <RootNamespace>GrpcCoreTests</RootNamespace>
+ <AssemblyName>GrpcCoreTests</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug</OutputPath>
+ <DefineConstants>DEBUG;</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>full</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="nunit.framework, Version=2.6.0.0, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77">
+ <Private>False</Private>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="ClientServerTest.cs" />
+ <Compile Include="ServerTest.cs" />
+ <Compile Include="Utils.cs" />
+ <Compile Include="GrpcEnvironmentTest.cs" />
+ <Compile Include="TimespecTest.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ItemGroup>
+ <ProjectReference Include="..\GrpcCore\GrpcCore.csproj">
+ <Project>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</Project>
+ <Name>GrpcCore</Name>
+ </ProjectReference>
+ </ItemGroup>
+</Project>
\ 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 0000000..136878d
--- /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 0000000..565b1e2
--- /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 0000000..b34101b
--- /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 0000000..a5a6abd
--- /dev/null
+++ b/src/csharp/GrpcCoreTests/TestResult.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!--This file represents the results of running a test suite-->
+<test-results name="/usr/local/google/home/jtattermusch/github/grpc/src/csharp/GrpcCoreTests/bin/Debug/GrpcCoreTests.dll" total="3" errors="0" failures="0" not-run="0" inconclusive="0" ignored="0" skipped="0" invalid="0" date="2015-01-29" time="19:40:47">
+ <environment nunit-version="2.6.0.0" clr-version="4.0.30319.17020" os-version="Unix 3.13.0.43" platform="Unix" cwd="/usr/local/google/home/jtattermusch/github/grpc/src/csharp/GrpcCoreTests" machine-name="jtattermusch.mtv.corp.google.com" user="jtattermusch" user-domain="jtattermusch.mtv.corp.google.com" />
+ <culture-info current-culture="en-US" current-uiculture="en-US" />
+ <test-suite type="Assembly" name="/usr/local/google/home/jtattermusch/github/grpc/src/csharp/GrpcCoreTests/bin/Debug/GrpcCoreTests.dll" executed="True" result="Success" success="True" time="0.172" asserts="0">
+ <results>
+ <test-suite type="Namespace" name="Google" executed="True" result="Success" success="True" time="0.166" asserts="0">
+ <results>
+ <test-suite type="Namespace" name="GRPC" executed="True" result="Success" success="True" time="0.166" asserts="0">
+ <results>
+ <test-suite type="Namespace" name="Core" executed="True" result="Success" success="True" time="0.166" asserts="0">
+ <results>
+ <test-suite type="Namespace" name="Tests" executed="True" result="Success" success="True" time="0.166" asserts="0">
+ <results>
+ <test-suite type="TestFixture" name="CallsTest" executed="True" result="Success" success="True" time="0.009" asserts="0">
+ <results>
+ <test-case name="Google.GRPC.Core.Tests.CallsTest.Test1" executed="True" result="Success" success="True" time="0.004" asserts="0" />
+ </results>
+ </test-suite>
+ <test-suite type="TestFixture" name="ClientServerTest" executed="True" result="Success" success="True" time="0.149" asserts="0">
+ <results>
+ <test-case name="Google.GRPC.Core.Tests.ClientServerTest.EmptyCall" executed="True" result="Success" success="True" time="0.111" asserts="0" />
+ </results>
+ </test-suite>
+ <test-suite type="TestFixture" name="ServerTest" executed="True" result="Success" success="True" time="0.001" asserts="0">
+ <results>
+ <test-case name="Google.GRPC.Core.Tests.ServerTest.StartAndShutdownServer" executed="True" result="Success" success="True" time="0.001" asserts="0" />
+ </results>
+ </test-suite>
+ </results>
+ </test-suite>
+ </results>
+ </test-suite>
+ </results>
+ </test-suite>
+ </results>
+ </test-suite>
+ </results>
+ </test-suite>
+</test-results>
\ 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 0000000..484bad7
--- /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 0000000..b0c0a7b
--- /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
+{
+ /// <summary>
+ /// Testing utils.
+ /// </summary>
+ 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 0000000..ba077a4
--- /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 0000000..31ce7f1
--- /dev/null
+++ b/src/csharp/GrpcDemo/GrpcDemo.csproj
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+ <ProductVersion>10.0.0</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <RootNamespace>GrpcDemo</RootNamespace>
+ <AssemblyName>GrpcDemo</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug</OutputPath>
+ <DefineConstants>DEBUG;</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <Externalconsole>true</Externalconsole>
+ <PlatformTarget>x86</PlatformTarget>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+ <DebugType>full</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <Externalconsole>true</Externalconsole>
+ <PlatformTarget>x86</PlatformTarget>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Program.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ItemGroup>
+ <ProjectReference Include="..\GrpcApi\GrpcApi.csproj">
+ <Project>{7DC1433E-3225-42C7-B7EA-546D56E27A4B}</Project>
+ <Name>GrpcApi</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\GrpcCore\GrpcCore.csproj">
+ <Project>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</Project>
+ <Name>GrpcCore</Name>
+ </ProjectReference>
+ </ItemGroup>
+</Project>
\ 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 0000000..258762d
--- /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 0000000..b8e1406
--- /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
new file mode 100755
index 0000000..c2f988c
--- /dev/null
+++ b/src/csharp/README.md
@@ -0,0 +1,54 @@
+gRPC C#
+=======
+
+A C# implementation of gRPC, Google's RPC library.
+
+EXPERIMENTAL ONLY
+-----------------
+
+**This gRPC C# implementation is work-in-progress and is not expected to work yet.**
+
+- The implementation is a wrapper around gRPC C core library
+- Code only runs under mono currently, building gGRPC C core library under Windows
+ is in progress.
+- 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/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c
new file mode 100644
index 0000000..74d11c6
--- /dev/null
+++ b/src/csharp/ext/grpc_csharp_ext.c
@@ -0,0 +1,113 @@
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/slice.h>
+
+#include <string.h>
+
+grpc_byte_buffer *string_to_byte_buffer(const char *buffer, size_t len) {
+ gpr_slice slice = gpr_slice_from_copied_buffer(buffer, len);
+ grpc_byte_buffer *bb = grpc_byte_buffer_create(&slice, 1);
+ gpr_slice_unref(slice);
+ return bb;
+}
+
+void grpc_call_start_write_from_copied_buffer(grpc_call *call,
+ const char *buffer, size_t len,
+ void *tag, gpr_uint32 flags) {
+ grpc_byte_buffer *byte_buffer = string_to_byte_buffer(buffer, len);
+ GPR_ASSERT(grpc_call_start_write_old(call, byte_buffer, tag, flags) ==
+ GRPC_CALL_OK);
+ grpc_byte_buffer_destroy(byte_buffer);
+}
+
+grpc_completion_type grpc_event_type(const grpc_event *event) {
+ return event->type;
+}
+
+grpc_op_error grpc_event_write_accepted(const grpc_event *event) {
+ GPR_ASSERT(event->type == GRPC_WRITE_ACCEPTED);
+ return event->data.invoke_accepted;
+}
+
+grpc_op_error grpc_event_finish_accepted(const grpc_event *event) {
+ GPR_ASSERT(event->type == GRPC_FINISH_ACCEPTED);
+ return event->data.finish_accepted;
+}
+
+grpc_status_code grpc_event_finished_status(const grpc_event *event) {
+ GPR_ASSERT(event->type == GRPC_FINISHED);
+ return event->data.finished.status;
+}
+
+const char *grpc_event_finished_details(const grpc_event *event) {
+ GPR_ASSERT(event->type == GRPC_FINISHED);
+ return event->data.finished.details;
+}
+
+gpr_intptr grpc_event_read_length(const grpc_event *event) {
+ GPR_ASSERT(event->type == GRPC_READ);
+ if (!event->data.read) {
+ return -1;
+ }
+ return grpc_byte_buffer_length(event->data.read);
+}
+
+/*
+ * Copies data from read event to a buffer. Fatal error occurs if
+ * buffer is too small.
+ */
+void grpc_event_read_copy_to_buffer(const grpc_event *event, char *buffer,
+ size_t buffer_len) {
+ grpc_byte_buffer_reader *reader;
+ gpr_slice slice;
+ size_t offset = 0;
+
+ GPR_ASSERT(event->type == GRPC_READ);
+ reader = grpc_byte_buffer_reader_create(event->data.read);
+
+ GPR_ASSERT(event->data.read);
+ while (grpc_byte_buffer_reader_next(reader, &slice)) {
+ size_t len = GPR_SLICE_LENGTH(slice);
+ GPR_ASSERT(offset + len <= buffer_len);
+ memcpy(buffer + offset, GPR_SLICE_START_PTR(slice),
+ GPR_SLICE_LENGTH(slice));
+ offset += len;
+ gpr_slice_unref(slice);
+ }
+ grpc_byte_buffer_reader_destroy(reader);
+}
+
+grpc_call *grpc_event_call(const grpc_event *event) {
+ /* we only allow this for newly incoming server calls. */
+ GPR_ASSERT(event->type == GRPC_SERVER_RPC_NEW);
+ return event->call;
+}
+
+const char *grpc_event_server_rpc_new_method(const grpc_event *event) {
+ GPR_ASSERT(event->type == GRPC_SERVER_RPC_NEW);
+ return event->data.server_rpc_new.method;
+}
+
+grpc_completion_type grpc_completion_queue_next_with_callback(
+ grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type t;
+ void (*callback)(grpc_event *);
+
+ ev = grpc_completion_queue_next(cq, gpr_inf_future);
+ t = ev->type;
+ if (ev->tag) {
+ /* call the callback in ev->tag */
+ /* C forbids to cast object pointers to function pointers, so
+ * we cast to intptr first.
+ */
+ callback = (void (*)(grpc_event *))(gpr_intptr)ev->tag;
+ (*callback)(ev);
+ }
+ grpc_event_finish(ev);
+
+ /* return completion type to allow some handling for events that have no
+ * tag - such as GRPC_QUEUE_SHUTDOWN
+ */
+ return t;
+}
diff --git a/src/csharp/lib/Google.ProtocolBuffers.dll b/src/csharp/lib/Google.ProtocolBuffers.dll
new file mode 100755
index 0000000..ce2f466
--- /dev/null
+++ b/src/csharp/lib/Google.ProtocolBuffers.dll
Binary files differ
diff --git a/src/node/README.md b/src/node/README.md
index 55329d8..c342b7c 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/src/node/ext/timeval.cc b/src/node/ext/timeval.cc
index 687e335..20d52f0 100644
--- a/src/node/ext/timeval.cc
+++ b/src/node/ext/timeval.cc
@@ -56,9 +56,8 @@
} else if (gpr_time_cmp(timespec, gpr_inf_past) == 0) {
return -std::numeric_limits<double>::infinity();
} else {
- struct timeval time = gpr_timeval_from_timespec(timespec);
- return (static_cast<double>(time.tv_sec) * 1000 +
- static_cast<double>(time.tv_usec) / 1000);
+ return (static_cast<double>(timespec.tv_sec) * 1000 +
+ static_cast<double>(timespec.tv_nsec) / 1000000);
}
}
diff --git a/src/php/README.md b/src/php/README.md
index 176bcfa..620c68f 100755
--- a/src/php/README.md
+++ b/src/php/README.md
@@ -7,31 +7,25 @@
## ENVIRONMENT
-To build a PHP environment that works with this extension, download and extract
-PHP 5.5 (5.6 may also work), configure it, and install it:
+Install `php5` and `php5-dev`.
-```bash
-apt-get install libxml2 libxml2-dev
-curl http://php.net/get/php-5.5.16.tar.gz
-tar -xf php-5.5.16.tar.gz
-cd php-5.5.16
-./configure --with-zlib=/usr --with-libxml-dir=ext/libxml --with-openssl=/usr/local/ssl
-make
-make install
-```
+To run the tests, additionally install `php5-readline` and `phpunit`.
-To also download and install the patched protoc and PHP code generator:
+Alternatively, build and install PHP 5.5 or later from source with standard
+configuration options.
+
+To also download and install protoc and the PHP code generator.
```bash
apt-get install -y procps
curl -sSL https://get.rvm.io | sudo bash -s stable --ruby
-git clone sso://team/one-platform-grpc-team/protobuf
+git clone git@github.com:google/protobuf.git
cd protobuf
./configure
make
make install
-git clone sso://team/one-platform-grpc-team/grpc-php-protobuf-php
-cd grpc-php-protobuf-php
+git clone git@github.com:murgatroid99/Protobuf-PHP.git
+cd Protobuf-PHP
rake pear:package version=1.0
pear install Protobuf-1.0.tgz
```
@@ -52,5 +46,4 @@
There is also a generated code test (`./bin/run_gen_code_test.sh`), which tests
the stub `./tests/generated_code/math.php` against a running localhost server
serving the math service. That stub is generated from
-`./tests/generated_code/math.proto` with the head of the repo
-`sso://team/one-platform-grpc-team/grpc-php-protobuf-php`.
+`./tests/generated_code/math.proto`.
diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c
index bd3490b..3bc9ce2 100644
--- a/src/php/ext/grpc/call.c
+++ b/src/php/ext/grpc/call.c
@@ -85,73 +85,25 @@
memcpy(str_val, elem->value, elem->value_length);
if (zend_hash_find(array_hash, str_key, key_len, (void **)data) ==
SUCCESS) {
- switch (Z_TYPE_P(*data)) {
- case IS_STRING:
- MAKE_STD_ZVAL(inner_array);
- array_init(inner_array);
- add_next_index_zval(inner_array, *data);
- add_assoc_zval(array, str_key, inner_array);
- break;
- case IS_ARRAY:
- inner_array = *data;
- break;
- default:
- zend_throw_exception(zend_exception_get_default(),
- "Metadata hash somehow contains wrong types.",
- 1 TSRMLS_CC);
+ if (Z_TYPE_P(*data) != IS_ARRAY) {
+ zend_throw_exception(zend_exception_get_default(),
+ "Metadata hash somehow contains wrong types.",
+ 1 TSRMLS_CC);
efree(str_key);
efree(str_val);
return NULL;
}
- add_next_index_stringl(inner_array, str_val, elem->value_length, false);
+ add_next_index_stringl(*data, str_val, elem->value_length, false);
} else {
- add_assoc_stringl(array, str_key, str_val, elem->value_length, false);
+ MAKE_STD_ZVAL(inner_array);
+ array_init(inner_array);
+ add_next_index_stringl(inner_array, str_val, elem->value_length, false);
+ add_assoc_zval(array, str_key, inner_array);
}
}
return array;
}
-int php_grpc_call_add_metadata_array_walk(void *elem TSRMLS_DC, int num_args,
- va_list args,
- zend_hash_key *hash_key) {
- grpc_call_error error_code;
- zval **data = (zval **)elem;
- grpc_metadata metadata;
- grpc_call *call = va_arg(args, grpc_call *);
- gpr_uint32 flags = va_arg(args, gpr_uint32);
- const char *key;
- HashTable *inner_hash;
- /* We assume that either two args were passed, and we are in the recursive
- case (and the second argument is the key), or one arg was passed and
- hash_key is the string key. */
- if (num_args > 2) {
- key = va_arg(args, const char *);
- } else {
- /* TODO(mlumish): If possible, check that hash_key is a string */
- key = hash_key->arKey;
- }
- switch (Z_TYPE_P(*data)) {
- case IS_STRING:
- metadata.key = (char *)key;
- metadata.value = Z_STRVAL_P(*data);
- metadata.value_length = Z_STRLEN_P(*data);
- error_code = grpc_call_add_metadata_old(call, &metadata, 0u);
- MAYBE_THROW_CALL_ERROR(add_metadata, error_code);
- break;
- case IS_ARRAY:
- inner_hash = Z_ARRVAL_P(*data);
- zend_hash_apply_with_arguments(inner_hash TSRMLS_CC,
- php_grpc_call_add_metadata_array_walk, 3,
- call, flags, key);
- break;
- default:
- zend_throw_exception(zend_exception_get_default(),
- "Metadata hash somehow contains wrong types.",
- 1 TSRMLS_CC);
- }
- return ZEND_HASH_APPLY_KEEP;
-}
-
/**
* Constructs a new instance of the Call class.
* @param Channel $channel The channel to associate the call with. Must not be
@@ -204,8 +156,18 @@
PHP_METHOD(Call, add_metadata) {
wrapped_grpc_call *call =
(wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC);
+ grpc_metadata metadata;
+ grpc_call_error error_code;
zval *array;
+ zval **inner_array;
+ zval **value;
HashTable *array_hash;
+ HashPosition array_pointer;
+ HashTable *inner_array_hash;
+ HashPosition inner_array_pointer;
+ char *key;
+ uint key_len;
+ ulong index;
long flags = 0;
/* "a|l" == 1 array, 1 optional long */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &flags) ==
@@ -216,9 +178,41 @@
return;
}
array_hash = Z_ARRVAL_P(array);
- zend_hash_apply_with_arguments(array_hash TSRMLS_CC,
- php_grpc_call_add_metadata_array_walk, 2,
- call->wrapped, (gpr_uint32)flags);
+ for (zend_hash_internal_pointer_reset_ex(array_hash, &array_pointer);
+ zend_hash_get_current_data_ex(array_hash, (void**)&inner_array,
+ &array_pointer) == SUCCESS;
+ zend_hash_move_forward_ex(array_hash, &array_pointer)) {
+ if (zend_hash_get_current_key_ex(array_hash, &key, &key_len, &index, 0,
+ &array_pointer) != HASH_KEY_IS_STRING) {
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "metadata keys must be strings", 1 TSRMLS_CC);
+ return;
+ }
+ if (Z_TYPE_P(*inner_array) != IS_ARRAY) {
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "metadata values must be arrays",
+ 1 TSRMLS_CC);
+ return;
+ }
+ inner_array_hash = Z_ARRVAL_P(*inner_array);
+ for (zend_hash_internal_pointer_reset_ex(inner_array_hash,
+ &inner_array_pointer);
+ zend_hash_get_current_data_ex(inner_array_hash, (void**)&value,
+ &inner_array_pointer) == SUCCESS;
+ zend_hash_move_forward_ex(inner_array_hash, &inner_array_pointer)) {
+ if (Z_TYPE_P(*value) != IS_STRING) {
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "metadata values must be arrays of strings",
+ 1 TSRMLS_CC);
+ return;
+ }
+ metadata.key = key;
+ metadata.value = Z_STRVAL_P(*value);
+ metadata.value_length = Z_STRLEN_P(*value);
+ error_code = grpc_call_add_metadata_old(call->wrapped, &metadata, 0u);
+ MAYBE_THROW_CALL_ERROR(add_metadata, error_code);
+ }
+ }
}
/**
diff --git a/src/php/ext/grpc/call.h b/src/php/ext/grpc/call.h
index 232c5d7..ba1f1e7 100644
--- a/src/php/ext/grpc/call.h
+++ b/src/php/ext/grpc/call.h
@@ -19,6 +19,7 @@
zend_throw_exception(spl_ce_LogicException, \
#func_name " was called incorrectly", \
(long)error_code TSRMLS_CC); \
+ return; \
} \
} while (0)
diff --git a/src/php/ext/grpc/server.c b/src/php/ext/grpc/server.c
index bc4fcf0..47ea38d 100644
--- a/src/php/ext/grpc/server.c
+++ b/src/php/ext/grpc/server.c
@@ -146,7 +146,7 @@
"add_http2_port expects a string", 1 TSRMLS_CC);
return;
}
- RETURN_BOOL(grpc_server_add_http2_port(server->wrapped, addr));
+ RETURN_LONG(grpc_server_add_http2_port(server->wrapped, addr));
}
PHP_METHOD(Server, add_secure_http2_port) {
@@ -161,7 +161,7 @@
"add_http2_port expects a string", 1 TSRMLS_CC);
return;
}
- RETURN_BOOL(grpc_server_add_secure_http2_port(server->wrapped, addr));
+ RETURN_LONG(grpc_server_add_secure_http2_port(server->wrapped, addr));
}
/**
diff --git a/src/php/lib/Grpc/AbstractSurfaceActiveCall.php b/src/php/lib/Grpc/AbstractSurfaceActiveCall.php
index 53c7d4c..83e4719 100755
--- a/src/php/lib/Grpc/AbstractSurfaceActiveCall.php
+++ b/src/php/lib/Grpc/AbstractSurfaceActiveCall.php
@@ -44,7 +44,7 @@
protected function _read() {
$response = $this->active_call->read();
- if ($response == null) {
+ if ($response === null) {
return null;
}
return call_user_func($this->deserialize, $response);
diff --git a/src/php/lib/Grpc/ActiveCall.php b/src/php/lib/Grpc/ActiveCall.php
index e0ea43a..847cfee 100755
--- a/src/php/lib/Grpc/ActiveCall.php
+++ b/src/php/lib/Grpc/ActiveCall.php
@@ -66,12 +66,7 @@
* @param ByteBuffer $data The data to write
*/
public function write($data) {
- if($this->call->start_write($data,
- WRITE_ACCEPTED,
- $this->flags) != OP_OK) {
- // TODO(mlumish): more useful error
- throw new \Exception("Cannot call write after writesDone");
- }
+ $this->call->start_write($data, WRITE_ACCEPTED, $this->flags);
$this->completion_queue->pluck(WRITE_ACCEPTED, Timeval::inf_future());
}
diff --git a/src/php/lib/Grpc/ServerStreamingSurfaceActiveCall.php b/src/php/lib/Grpc/ServerStreamingSurfaceActiveCall.php
index 082f995..f131d6b 100755
--- a/src/php/lib/Grpc/ServerStreamingSurfaceActiveCall.php
+++ b/src/php/lib/Grpc/ServerStreamingSurfaceActiveCall.php
@@ -31,7 +31,7 @@
* @return An iterator of response values
*/
public function responses() {
- while(($response = $this->_read()) != null) {
+ while(($response = $this->_read()) !== null) {
yield $response;
}
}
diff --git a/src/php/tests/generated_code/GeneratedCodeTest.php b/src/php/tests/generated_code/GeneratedCodeTest.php
index 42d25e4..ee7b871 100755
--- a/src/php/tests/generated_code/GeneratedCodeTest.php
+++ b/src/php/tests/generated_code/GeneratedCodeTest.php
@@ -17,9 +17,9 @@
$div_arg->setDividend(7);
$div_arg->setDivisor(4);
list($response, $status) = self::$client->Div($div_arg)->wait();
- $this->assertEquals(1, $response->getQuotient());
- $this->assertEquals(3, $response->getRemainder());
- $this->assertEquals(\Grpc\STATUS_OK, $status->code);
+ $this->assertSame(1, $response->getQuotient());
+ $this->assertSame(3, $response->getRemainder());
+ $this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testServerStreaming() {
@@ -31,9 +31,9 @@
return $num->getNum();
};
$values = array_map($extract_num, $result_array);
- $this->assertEquals([1, 1, 2, 3, 5, 8, 13], $values);
+ $this->assertSame([1, 1, 2, 3, 5, 8, 13], $values);
$status = $call->getStatus();
- $this->assertEquals(\Grpc\STATUS_OK, $status->code);
+ $this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testClientStreaming() {
@@ -46,8 +46,8 @@
};
$call = self::$client->Sum($num_iter());
list($response, $status) = $call->wait();
- $this->assertEquals(21, $response->getNum());
- $this->assertEquals(\Grpc\STATUS_OK, $status->code);
+ $this->assertSame(21, $response->getNum());
+ $this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testBidiStreaming() {
@@ -58,11 +58,11 @@
$div_arg->setDivisor(2);
$call->write($div_arg);
$response = $call->read();
- $this->assertEquals($i, $response->getQuotient());
- $this->assertEquals(1, $response->getRemainder());
+ $this->assertSame($i, $response->getQuotient());
+ $this->assertSame(1, $response->getRemainder());
}
$call->writesDone();
$status = $call->getStatus();
- $this->assertEquals(\Grpc\STATUS_OK, $status->code);
+ $this->assertSame(\Grpc\STATUS_OK, $status->code);
}
}
\ No newline at end of file
diff --git a/src/php/tests/interop/interop_client.php b/src/php/tests/interop/interop_client.php
index 2ff2be7..5266e9a 100755
--- a/src/php/tests/interop/interop_client.php
+++ b/src/php/tests/interop/interop_client.php
@@ -26,8 +26,8 @@
*/
function emptyUnary($stub) {
list($result, $status) = $stub->EmptyCall(new grpc\testing\EmptyMessage())->wait();
- hardAssert($status->code == Grpc\STATUS_OK, 'Call did not complete successfully');
- hardAssert($result != null, 'Call completed with a null response');
+ hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
+ hardAssert($result !== null, 'Call completed with a null response');
}
/**
@@ -49,14 +49,14 @@
$request->setPayload($payload);
list($result, $status) = $stub->UnaryCall($request)->wait();
- hardAssert($status->code == Grpc\STATUS_OK, 'Call did not complete successfully');
- hardAssert($result != null, 'Call returned a null response');
+ hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
+ hardAssert($result !== null, 'Call returned a null response');
$payload = $result->getPayload();
- hardAssert($payload->getType() == grpc\testing\PayloadType::COMPRESSABLE,
+ hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
'Payload had the wrong type');
- hardAssert(strlen($payload->getBody()) == $response_len,
+ hardAssert(strlen($payload->getBody()) === $response_len,
'Payload had the wrong length');
- hardAssert($payload->getBody() == str_repeat("\0", $response_len),
+ hardAssert($payload->getBody() === str_repeat("\0", $response_len),
'Payload had the wrong content');
}
@@ -78,8 +78,8 @@
}, $request_lengths);
list($result, $status) = $stub->StreamingInputCall($requests)->wait();
- hardAssert($status->code == Grpc\STATUS_OK, 'Call did not complete successfully');
- hardAssert($result->getAggregatedPayloadSize() == 74922,
+ hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
+ hardAssert($result->getAggregatedPayloadSize() === 74922,
'aggregated_payload_size was incorrect');
}
@@ -100,15 +100,15 @@
}
$call = $stub->StreamingOutputCall($request);
- hardAssert($call->getStatus()->code == Grpc\STATUS_OK,
+ hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
'Call did not complete successfully');
$i = 0;
foreach($call->responses() as $value) {
hardAssert($i < 4, 'Too many responses');
$payload = $value->getPayload();
- hardAssert($payload->getType() == grpc\testing\PayloadType::COMPRESSABLE,
+ hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
'Payload ' . $i . ' had the wrong type');
- hardAssert(strlen($payload->getBody()) == $sizes[$i],
+ hardAssert(strlen($payload->getBody()) === $sizes[$i],
'Response ' . $i . ' had the wrong length');
}
}
@@ -136,19 +136,38 @@
$call->write($request);
$response = $call->read();
- hardAssert($response != null, 'Server returned too few responses');
+ hardAssert($response !== null, 'Server returned too few responses');
$payload = $response->getPayload();
- hardAssert($payload->getType() == grpc\testing\PayloadType::COMPRESSABLE,
+ hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
'Payload ' . $i . ' had the wrong type');
- hardAssert(strlen($payload->getBody()) == $response_lengths[$i],
+ hardAssert(strlen($payload->getBody()) === $response_lengths[$i],
'Payload ' . $i . ' had the wrong length');
}
$call->writesDone();
- hardAssert($call->read() == null, 'Server returned too many responses');
- hardAssert($call->getStatus()->code == Grpc\STATUS_OK,
+ hardAssert($call->read() === null, 'Server returned too many responses');
+ hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
'Call did not complete successfully');
}
+function cancelAfterFirstResponse($stub) {
+ $call = $stub->FullDuplexCall();
+ $request = new grpc\testing\StreamingOutputCallRequest();
+ $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
+ $response_parameters = new grpc\testing\ResponseParameters();
+ $response_parameters->setSize(31415);
+ $request->addResponseParameters($response_parameters);
+ $payload = new grpc\testing\Payload();
+ $payload->setBody(str_repeat("\0", 27182));
+ $request->setPayload($payload);
+
+ $call->write($request);
+ $response = $call->read();
+
+ $call->cancel();
+ hardAssert($call->getStatus()->code === Grpc\STATUS_CANCELLED,
+ 'Call status was not CANCELLED');
+}
+
$args = getopt('', array('server_host:', 'server_port:', 'test_case:'));
if (!array_key_exists('server_host', $args) ||
!array_key_exists('server_port', $args) ||
@@ -187,4 +206,6 @@
case 'ping_pong':
pingPong($stub);
break;
+ case 'cancel_after_first_response':
+ cancelAfterFirstResponse($stub);
}
\ No newline at end of file
diff --git a/src/php/tests/unit_tests/CallTest.php b/src/php/tests/unit_tests/CallTest.php
index 795831c..8f709b7 100755
--- a/src/php/tests/unit_tests/CallTest.php
+++ b/src/php/tests/unit_tests/CallTest.php
@@ -1,16 +1,17 @@
<?php
class CallTest extends PHPUnit_Framework_TestCase{
static $server;
+ static $port;
public static function setUpBeforeClass() {
$cq = new Grpc\CompletionQueue();
self::$server = new Grpc\Server($cq, []);
- self::$server->add_http2_port('localhost:9001');
+ self::$port = self::$server->add_http2_port('0.0.0.0:0');
}
public function setUp() {
$this->cq = new Grpc\CompletionQueue();
- $this->channel = new Grpc\Channel('localhost:9001', []);
+ $this->channel = new Grpc\Channel('localhost:' . self::$port, []);
$this->call = new Grpc\Call($this->channel,
'/foo',
Grpc\Timeval::inf_future());
@@ -46,7 +47,7 @@
}
public function testAddSingleMetadata() {
- $this->call->add_metadata(['key' => 'value'], 0);
+ $this->call->add_metadata(['key' => ['value']], 0);
/* Dummy assert: Checks that the previous call completed without error */
$this->assertTrue(true);
}
@@ -59,7 +60,7 @@
public function testAddSingleAndMultiValueMetadata() {
$this->call->add_metadata(
- ['key1' => 'value1',
+ ['key1' => ['value1'],
'key2' => ['value2', 'value3']], 0);
/* Dummy assert: Checks that the previous call completed without error */
$this->assertTrue(true);
diff --git a/src/php/tests/unit_tests/EndToEndTest.php b/src/php/tests/unit_tests/EndToEndTest.php
index 78c5e9f..05104c0 100755
--- a/src/php/tests/unit_tests/EndToEndTest.php
+++ b/src/php/tests/unit_tests/EndToEndTest.php
@@ -1,13 +1,11 @@
<?php
-require __DIR__ . '/../util/port_picker.php';
class EndToEndTest extends PHPUnit_Framework_TestCase{
public function setUp() {
$this->client_queue = new Grpc\CompletionQueue();
$this->server_queue = new Grpc\CompletionQueue();
$this->server = new Grpc\Server($this->server_queue, []);
- $address = '127.0.0.1:' . getNewPort();
- $this->server->add_http2_port($address);
- $this->channel = new Grpc\Channel($address, []);
+ $port = $this->server->add_http2_port('0.0.0.0:0');
+ $this->channel = new Grpc\Channel('localhost:' . $port, []);
}
public function tearDown() {
@@ -24,62 +22,52 @@
'dummy_method',
$deadline);
$tag = 1;
- $this->assertEquals(Grpc\CALL_OK,
- $call->invoke($this->client_queue,
- $tag,
- $tag));
-
+ $call->invoke($this->client_queue, $tag, $tag);
$server_tag = 2;
$call->writes_done($tag);
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\FINISH_ACCEPTED, $event->type);
- $this->assertEquals(Grpc\OP_OK, $event->data);
+ $this->assertSame(Grpc\FINISH_ACCEPTED, $event->type);
+ $this->assertSame(Grpc\OP_OK, $event->data);
// check that a server rpc new was received
$this->server->start();
$this->server->request_call($server_tag);
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\SERVER_RPC_NEW, $event->type);
+ $this->assertSame(Grpc\SERVER_RPC_NEW, $event->type);
$server_call = $event->call;
$this->assertNotNull($server_call);
- $this->assertEquals(Grpc\CALL_OK,
- $server_call->server_accept($this->server_queue,
- $server_tag));
+ $server_call->server_accept($this->server_queue, $server_tag);
- $this->assertEquals(Grpc\CALL_OK,
- $server_call->server_end_initial_metadata());
+ $server_call->server_end_initial_metadata();
// the server sends the status
- $this->assertEquals(Grpc\CALL_OK,
- $server_call->start_write_status(Grpc\STATUS_OK,
- $status_text,
- $server_tag));
+ $server_call->start_write_status(Grpc\STATUS_OK, $status_text, $server_tag);
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\FINISH_ACCEPTED, $event->type);
- $this->assertEquals(Grpc\OP_OK, $event->data);
+ $this->assertSame(Grpc\FINISH_ACCEPTED, $event->type);
+ $this->assertSame(Grpc\OP_OK, $event->data);
// the client gets CLIENT_METADATA_READ
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\CLIENT_METADATA_READ, $event->type);
+ $this->assertSame(Grpc\CLIENT_METADATA_READ, $event->type);
// the client gets FINISHED
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\FINISHED, $event->type);
+ $this->assertSame(Grpc\FINISHED, $event->type);
$status = $event->data;
- $this->assertEquals(Grpc\STATUS_OK, $status->code);
- $this->assertEquals($status_text, $status->details);
+ $this->assertSame(Grpc\STATUS_OK, $status->code);
+ $this->assertSame($status_text, $status->details);
// and the server gets FINISHED
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\FINISHED, $event->type);
+ $this->assertSame(Grpc\FINISHED, $event->type);
$status = $event->data;
unset($call);
@@ -96,10 +84,7 @@
'dummy_method',
$deadline);
$tag = 1;
- $this->assertEquals(Grpc\CALL_OK,
- $call->invoke($this->client_queue,
- $tag,
- $tag));
+ $call->invoke($this->client_queue, $tag, $tag);
$server_tag = 2;
@@ -107,76 +92,69 @@
$call->start_write($req_text, $tag);
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\WRITE_ACCEPTED, $event->type);
+ $this->assertSame(Grpc\WRITE_ACCEPTED, $event->type);
// check that a server rpc new was received
$this->server->start();
$this->server->request_call($server_tag);
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\SERVER_RPC_NEW, $event->type);
+ $this->assertSame(Grpc\SERVER_RPC_NEW, $event->type);
$server_call = $event->call;
$this->assertNotNull($server_call);
- $this->assertEquals(Grpc\CALL_OK,
- $server_call->server_accept($this->server_queue,
- $server_tag));
+ $server_call->server_accept($this->server_queue, $server_tag);
- $this->assertEquals(Grpc\CALL_OK,
- $server_call->server_end_initial_metadata());
+ $server_call->server_end_initial_metadata();
// start the server read
$server_call->start_read($server_tag);
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\READ, $event->type);
- $this->assertEquals($req_text, $event->data);
+ $this->assertSame(Grpc\READ, $event->type);
+ $this->assertSame($req_text, $event->data);
// the server replies
- $this->assertEquals(Grpc\CALL_OK,
- $server_call->start_write($reply_text, $server_tag));
+ $server_call->start_write($reply_text, $server_tag);
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\WRITE_ACCEPTED, $event->type);
+ $this->assertSame(Grpc\WRITE_ACCEPTED, $event->type);
// the client reads the metadata
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\CLIENT_METADATA_READ, $event->type);
+ $this->assertSame(Grpc\CLIENT_METADATA_READ, $event->type);
// the client reads the reply
$call->start_read($tag);
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\READ, $event->type);
- $this->assertEquals($reply_text, $event->data);
+ $this->assertSame(Grpc\READ, $event->type);
+ $this->assertSame($reply_text, $event->data);
// the client sends writes done
$call->writes_done($tag);
$event = $this->client_queue->next($deadline);
- $this->assertEquals(Grpc\FINISH_ACCEPTED, $event->type);
- $this->assertEquals(Grpc\OP_OK, $event->data);
+ $this->assertSame(Grpc\FINISH_ACCEPTED, $event->type);
+ $this->assertSame(Grpc\OP_OK, $event->data);
// the server sends the status
- $this->assertEquals(Grpc\CALL_OK,
- $server_call->start_write_status(GRPC\STATUS_OK,
- $status_text,
- $server_tag));
+ $server_call->start_write_status(GRPC\STATUS_OK, $status_text, $server_tag);
$event = $this->server_queue->next($deadline);
- $this->assertEquals(Grpc\FINISH_ACCEPTED, $event->type);
- $this->assertEquals(Grpc\OP_OK, $event->data);
+ $this->assertSame(Grpc\FINISH_ACCEPTED, $event->type);
+ $this->assertSame(Grpc\OP_OK, $event->data);
// the client gets FINISHED
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\FINISHED, $event->type);
+ $this->assertSame(Grpc\FINISHED, $event->type);
$status = $event->data;
- $this->assertEquals(Grpc\STATUS_OK, $status->code);
- $this->assertEquals($status_text, $status->details);
+ $this->assertSame(Grpc\STATUS_OK, $status->code);
+ $this->assertSame($status_text, $status->details);
// and the server gets FINISHED
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\FINISHED, $event->type);
+ $this->assertSame(Grpc\FINISHED, $event->type);
unset($call);
unset($server_call);
diff --git a/src/php/tests/unit_tests/SecureEndToEndTest.php b/src/php/tests/unit_tests/SecureEndToEndTest.php
index 7c3ad8a..5e95b11 100755
--- a/src/php/tests/unit_tests/SecureEndToEndTest.php
+++ b/src/php/tests/unit_tests/SecureEndToEndTest.php
@@ -11,10 +11,9 @@
file_get_contents(dirname(__FILE__) . '/../data/server1.pem'));
$this->server = new Grpc\Server($this->server_queue,
['credentials' => $server_credentials]);
- $address = '127.0.0.1:' . getNewPort();
- $this->server->add_secure_http2_port($address);
+ $port = $this->server->add_secure_http2_port('0.0.0.0:0');
$this->channel = new Grpc\Channel(
- $address,
+ 'localhost:' . $port,
[
'grpc.ssl_target_name_override' => 'foo.test.google.com',
'credentials' => $credentials
@@ -36,59 +35,50 @@
'dummy_method',
$deadline);
$tag = 1;
- $this->assertEquals(Grpc\CALL_OK,
- $call->invoke($this->client_queue,
- $tag,
- $tag));
+ $call->invoke($this->client_queue, $tag, $tag);
$server_tag = 2;
$call->writes_done($tag);
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\FINISH_ACCEPTED, $event->type);
- $this->assertEquals(Grpc\OP_OK, $event->data);
+ $this->assertSame(Grpc\FINISH_ACCEPTED, $event->type);
+ $this->assertSame(Grpc\OP_OK, $event->data);
// check that a server rpc new was received
$this->server->request_call($server_tag);
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\SERVER_RPC_NEW, $event->type);
+ $this->assertSame(Grpc\SERVER_RPC_NEW, $event->type);
$server_call = $event->call;
$this->assertNotNull($server_call);
- $this->assertEquals(Grpc\CALL_OK,
- $server_call->server_accept($this->server_queue,
- $server_tag));
+ $server_call->server_accept($this->server_queue, $server_tag);
- $this->assertEquals(Grpc\CALL_OK,
- $server_call->server_end_initial_metadata());
+ $server_call->server_end_initial_metadata();
// the server sends the status
- $this->assertEquals(Grpc\CALL_OK,
- $server_call->start_write_status(Grpc\STATUS_OK,
- $status_text,
- $server_tag));
+ $server_call->start_write_status(Grpc\STATUS_OK, $status_text, $server_tag);
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\FINISH_ACCEPTED, $event->type);
- $this->assertEquals(Grpc\OP_OK, $event->data);
+ $this->assertSame(Grpc\FINISH_ACCEPTED, $event->type);
+ $this->assertSame(Grpc\OP_OK, $event->data);
// the client gets CLIENT_METADATA_READ
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\CLIENT_METADATA_READ, $event->type);
+ $this->assertSame(Grpc\CLIENT_METADATA_READ, $event->type);
// the client gets FINISHED
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\FINISHED, $event->type);
+ $this->assertSame(Grpc\FINISHED, $event->type);
$status = $event->data;
- $this->assertEquals(Grpc\STATUS_OK, $status->code);
- $this->assertEquals($status_text, $status->details);
+ $this->assertSame(Grpc\STATUS_OK, $status->code);
+ $this->assertSame($status_text, $status->details);
// and the server gets FINISHED
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\FINISHED, $event->type);
+ $this->assertSame(Grpc\FINISHED, $event->type);
$status = $event->data;
unset($call);
@@ -106,10 +96,7 @@
'dummy_method',
$deadline);
$tag = 1;
- $this->assertEquals(Grpc\CALL_OK,
- $call->invoke($this->client_queue,
- $tag,
- $tag));
+ $call->invoke($this->client_queue, $tag, $tag);
$server_tag = 2;
@@ -117,75 +104,68 @@
$call->start_write($req_text, $tag);
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\WRITE_ACCEPTED, $event->type);
+ $this->assertSame(Grpc\WRITE_ACCEPTED, $event->type);
// check that a server rpc new was received
$this->server->request_call($server_tag);
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\SERVER_RPC_NEW, $event->type);
+ $this->assertSame(Grpc\SERVER_RPC_NEW, $event->type);
$server_call = $event->call;
$this->assertNotNull($server_call);
- $this->assertEquals(Grpc\CALL_OK,
- $server_call->server_accept($this->server_queue,
- $server_tag));
+ $server_call->server_accept($this->server_queue, $server_tag);
- $this->assertEquals(Grpc\CALL_OK,
- $server_call->server_end_initial_metadata());
+ $server_call->server_end_initial_metadata();
// start the server read
$server_call->start_read($server_tag);
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\READ, $event->type);
- $this->assertEquals($req_text, $event->data);
+ $this->assertSame(Grpc\READ, $event->type);
+ $this->assertSame($req_text, $event->data);
// the server replies
- $this->assertEquals(Grpc\CALL_OK,
- $server_call->start_write($reply_text, $server_tag));
+ $server_call->start_write($reply_text, $server_tag);
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\WRITE_ACCEPTED, $event->type);
+ $this->assertSame(Grpc\WRITE_ACCEPTED, $event->type);
// the client reads the metadata
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\CLIENT_METADATA_READ, $event->type);
+ $this->assertSame(Grpc\CLIENT_METADATA_READ, $event->type);
// the client reads the reply
$call->start_read($tag);
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\READ, $event->type);
- $this->assertEquals($reply_text, $event->data);
+ $this->assertSame(Grpc\READ, $event->type);
+ $this->assertSame($reply_text, $event->data);
// the client sends writes done
$call->writes_done($tag);
$event = $this->client_queue->next($deadline);
- $this->assertEquals(Grpc\FINISH_ACCEPTED, $event->type);
- $this->assertEquals(Grpc\OP_OK, $event->data);
+ $this->assertSame(Grpc\FINISH_ACCEPTED, $event->type);
+ $this->assertSame(Grpc\OP_OK, $event->data);
// the server sends the status
- $this->assertEquals(Grpc\CALL_OK,
- $server_call->start_write_status(GRPC\STATUS_OK,
- $status_text,
- $server_tag));
+ $server_call->start_write_status(GRPC\STATUS_OK, $status_text, $server_tag);
$event = $this->server_queue->next($deadline);
- $this->assertEquals(Grpc\FINISH_ACCEPTED, $event->type);
- $this->assertEquals(Grpc\OP_OK, $event->data);
+ $this->assertSame(Grpc\FINISH_ACCEPTED, $event->type);
+ $this->assertSame(Grpc\OP_OK, $event->data);
// the client gets FINISHED
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\FINISHED, $event->type);
+ $this->assertSame(Grpc\FINISHED, $event->type);
$status = $event->data;
- $this->assertEquals(Grpc\STATUS_OK, $status->code);
- $this->assertEquals($status_text, $status->details);
+ $this->assertSame(Grpc\STATUS_OK, $status->code);
+ $this->assertSame($status_text, $status->details);
// and the server gets FINISHED
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
- $this->assertEquals(Grpc\FINISHED, $event->type);
+ $this->assertSame(Grpc\FINISHED, $event->type);
unset($call);
unset($server_call);
diff --git a/src/php/tests/unit_tests/TimevalTest.php b/src/php/tests/unit_tests/TimevalTest.php
index 6af9fba..067254b 100755
--- a/src/php/tests/unit_tests/TimevalTest.php
+++ b/src/php/tests/unit_tests/TimevalTest.php
@@ -2,7 +2,7 @@
class TimevalTest extends PHPUnit_Framework_TestCase{
public function testCompareSame() {
$zero = Grpc\Timeval::zero();
- $this->assertEquals(0, Grpc\Timeval::compare($zero, $zero));
+ $this->assertSame(0, Grpc\Timeval::compare($zero, $zero));
}
public function testPastIsLessThanZero() {
diff --git a/src/php/tests/util/port_picker.php b/src/php/tests/util/port_picker.php
deleted file mode 100755
index d869d8b..0000000
--- a/src/php/tests/util/port_picker.php
+++ /dev/null
@@ -1,6 +0,0 @@
-<?php
-function getNewPort() {
- static $port = 10000;
- $port += 1;
- return $port;
-}
\ No newline at end of file
diff --git a/templates/Makefile.template b/templates/Makefile.template
index 142d188..c34949c 100644
--- a/templates/Makefile.template
+++ b/templates/Makefile.template
@@ -206,11 +206,13 @@
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 83b83ab..5450dfb 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 <unistd.h>
#include <assert.h>
#include <stdio.h>
diff --git a/test/core/end2end/README b/test/core/end2end/README
index 31598b6..59daec4 100644
--- a/test/core/end2end/README
+++ b/test/core/end2end/README
@@ -5,6 +5,3 @@
- add the code to the relevant directory
- update gen_build_json.py to reflect the change
- regenerate projects
-// MOE:begin_strip
-- update net/grpc/c/BUILD to reflect the change
-// MOE:end_strip
\ No newline at end of file
diff --git a/test/core/end2end/cq_verifier.c b/test/core/end2end/cq_verifier.c
index 287f83e..904ed77 100644
--- a/test/core/end2end/cq_verifier.c
+++ b/test/core/end2end/cq_verifier.c
@@ -70,6 +70,7 @@
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 @@
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 @@
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 @@
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 @@
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 eeb454c..9d893f6 100644
--- a/test/core/end2end/dualstack_socket_test.c
+++ b/test/core/end2end/dualstack_socket_test.c
@@ -142,7 +142,6 @@
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/tests/census_simple_request.c b/test/core/end2end/tests/census_simple_request.c
index 1edb5b0..4cbaa65 100644
--- a/test/core/end2end/tests/census_simple_request.c
+++ b/test/core/end2end/tests/census_simple_request.c
@@ -135,7 +135,6 @@
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 0e26577..4830b85 100644
--- a/test/core/end2end/tests/max_concurrent_streams.c
+++ b/test/core/end2end/tests/max_concurrent_streams.c
@@ -138,7 +138,6 @@
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 @@
/* 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 192d1ab..db0d6d8 100644
--- a/test/core/end2end/tests/simple_request.c
+++ b/test/core/end2end/tests/simple_request.c
@@ -139,7 +139,6 @@
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 @@
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/client.c b/test/core/fling/client.c
index cd2efc3..a91dfba 100644
--- a/test/core/fling/client.c
+++ b/test/core/fling/client.c
@@ -65,6 +65,7 @@
grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future));
grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future));
grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future));
+ grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future));
grpc_call_destroy(call);
call = NULL;
}
diff --git a/test/core/fling/fling_stream_test.c b/test/core/fling/fling_stream_test.c
index 7f52fb1..1db2f1a 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 <unistd.h>
#include <assert.h>
#include <stdio.h>
diff --git a/test/core/fling/fling_test.c b/test/core/fling/fling_test.c
index b2272f2..4f41a21 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 <unistd.h>
#include <assert.h>
#include <stdio.h>
diff --git a/test/core/json/json_test.c b/test/core/json/json_test.c
index 11659a5..6d0227a 100644
--- a/test/core/json/json_test.c
+++ b/test/core/json/json_test.c
@@ -151,7 +151,7 @@
GPR_ASSERT(!json);
}
- free(scratchpad);
+ gpr_free(scratchpad);
}
}
@@ -166,6 +166,7 @@
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/surface/completion_queue_test.c b/test/core/surface/completion_queue_test.c
index dc459d6..875cf3e 100644
--- a/test/core/surface/completion_queue_test.c
+++ b/test/core/surface/completion_queue_test.c
@@ -105,32 +105,6 @@
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 @@
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_base/README.md b/tools/dockerfile/grpc_base/README.md
index 4745141..e3b5f2e 100644
--- a/tools/dockerfile/grpc_base/README.md
+++ b/tools/dockerfile/grpc_base/README.md
@@ -4,8 +4,7 @@
Dockerfile for creating the base gRPC development Docker instance.
For now, this assumes that the development will be done on GCE instances, with source code on Git-on-Borg.
-As of 2014/09/29, it includes
+As of 2015/02/02, it includes
- git
- some useful tools like curl, emacs, strace, telnet etc
-- downloads the gerrit-compute-tools and installs the script that allows access to gerrit when on git-on-borg
- a patched version of protoc, to allow protos with stream tags to work
diff --git a/tools/dockerfile/grpc_java/README.md b/tools/dockerfile/grpc_java/README.md
index 2da2393..808f0fc 100644
--- a/tools/dockerfile/grpc_java/README.md
+++ b/tools/dockerfile/grpc_java/README.md
@@ -5,5 +5,5 @@
As of 2014/12 this
- is based on the gRPC Java base
- - pulls from gRPC Java on git-on-borg
+ - pulls from gRPC Java on GitHub
- installs it and runs the tests
\ No newline at end of file
diff --git a/tools/dockerfile/grpc_node/Dockerfile b/tools/dockerfile/grpc_node/Dockerfile
index baec0e2..ce582d2 100644
--- a/tools/dockerfile/grpc_node/Dockerfile
+++ b/tools/dockerfile/grpc_node/Dockerfile
@@ -11,4 +11,4 @@
RUN cd /var/local/git/grpc/src/node && npm install && node-gyp rebuild
-CMD ["/usr/bin/nodejs", "/var/local/git/grpc/src/node/interop/interop_server.js", "--use_tls=true", "--port 8040"]
\ No newline at end of file
+CMD ["/usr/bin/nodejs", "/var/local/git/grpc/src/node/interop/interop_server.js", "--use_tls=true", "--port=8040"]
\ No newline at end of file
diff --git a/tools/dockerfile/grpc_node_base/Dockerfile b/tools/dockerfile/grpc_node_base/Dockerfile
index 4ca0e53..28bd7b2 100644
--- a/tools/dockerfile/grpc_node_base/Dockerfile
+++ b/tools/dockerfile/grpc_node_base/Dockerfile
@@ -15,5 +15,8 @@
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/README.md b/tools/dockerfile/grpc_php/README.md
index a37389f..f3c332b 100644
--- a/tools/dockerfile/grpc_php/README.md
+++ b/tools/dockerfile/grpc_php/README.md
@@ -5,6 +5,6 @@
As of 2014/10 this
- is based on the GRPC PHP base
-- adds a pull of the HEAD GRPC PHP source from git-on-borg
+- adds a pull of the HEAD GRPC PHP source from GitHub
- it builds it
- runs the tests, i.e, the image won't be created if the tests don't pass
diff --git a/tools/dockerfile/grpc_php_base/Dockerfile b/tools/dockerfile/grpc_php_base/Dockerfile
index 900d8ab..ef58f3a 100644
--- a/tools/dockerfile/grpc_php_base/Dockerfile
+++ b/tools/dockerfile/grpc_php_base/Dockerfile
@@ -45,13 +45,9 @@
&& ./configure --with-zlib=/usr --with-libxml-dir=ext/libxml \
&& make -j12 && make install
-# Start the daemon that allows access to the protected git-on-borg repos
-RUN git clone https://gerrit.googlesource.com/gcompute-tools /var/local/git/gcompute-tools
-RUN /var/local/git/gcompute-tools/git-cookie-authdaemon
-
# Download the patched PHP protobuf so that PHP gRPC clients can be generated
# from proto3 schemas.
-RUN git clone https://team.googlesource.com/one-platform-grpc-team/grpc-php-protobuf-php /var/local/git/protobuf-php
+RUN git clone git@github.com:murgatroid99/Protobuf-PHP.git /var/local/git/protobuf-php
# Install ruby (via RVM) as ruby tools are dependencies for building Protobuf
# PHP extensions.
@@ -88,5 +84,8 @@
&& 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/Dockerfile b/tools/dockerfile/grpc_ruby/Dockerfile
index c84548c..47972e7 100644
--- a/tools/dockerfile/grpc_ruby/Dockerfile
+++ b/tools/dockerfile/grpc_ruby/Dockerfile
@@ -6,17 +6,19 @@
&& git pull --recurse-submodules \
&& git submodule update --init --recursive
-# TODO: remove this, once make install is fixed
-RUN touch /var/local/git/grpc/include/grpc/support/string.h
-
# Build the C core.
RUN make install_c -C /var/local/git/grpc
# Build ruby gRPC and run its tests
RUN /bin/bash -l -c 'cd /var/local/git/grpc/src/ruby && bundle && rake'
-# Add a cacerts directory containing the Google root pem file, allowing the ruby client to access the production test instance
+# Add a cacerts directory containing the Google root pem file, allowing the
+# ruby client to access the production test instance
ADD cacerts cacerts
-# Specify the default command such that the interop server runs on its known testing port
+# Add a service_account directory containing the auth creds file
+ADD service_account service_account
+
+# Specify the default command such that the interop server runs on its known
+# testing port
CMD ["/bin/bash", "-l", "-c", "ruby /var/local/git/grpc/src/ruby/bin/interop/interop_server.rb --use_tls --port 8060"]
diff --git a/tools/dockerfile/grpc_ruby/README.md b/tools/dockerfile/grpc_ruby/README.md
index 51fb2f5..eaa8382 100644
--- a/tools/dockerfile/grpc_ruby/README.md
+++ b/tools/dockerfile/grpc_ruby/README.md
@@ -5,6 +5,6 @@
As of 2014/10 this
- is based on the GRPC Ruby base
-- adds a pull of the HEAD gRPC Ruby source from git-on-borg
+- adds a pull of the HEAD gRPC Ruby source from GitHub
- it builds it
- runs the tests, i.e, the image won't be created if the tests don't pass
diff --git a/tools/dockerfile/grpc_ruby_base/Dockerfile b/tools/dockerfile/grpc_ruby_base/Dockerfile
index 787f129..ec4544d 100644
--- a/tools/dockerfile/grpc_ruby_base/Dockerfile
+++ b/tools/dockerfile/grpc_ruby_base/Dockerfile
@@ -53,3 +53,6 @@
./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 0000000..49b3c43
--- /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 0c1163a..200f859 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 a97cc88..2e02653 100755
--- a/tools/gce_setup/grpc_docker.sh
+++ b/tools/gce_setup/grpc_docker.sh
@@ -317,7 +317,7 @@
echo "$FUNCNAME: missing arg: test_case" 1>&2
return 1
}
- echo "--server_host=$server_ip --server_port=$port --test_case=$test_case"
+ echo "--server_host_override=foo.test.google.fr --server_host=$server_ip --server_port=$port --test_case=$test_case"
}
# checks the positional args and assigns them to variables visible in the caller
@@ -590,7 +590,7 @@
done
}
-grpc_launch_server_args() {
+_grpc_show_servers_args() {
[[ -n $1 ]] && { # host
host=$1
shift
@@ -598,9 +598,78 @@
echo "$FUNCNAME: missing arg: host" 1>&2
return 1
}
+}
- [[ -n $1 ]] && { # server_type
- case $1 in
+
+# Shows servers on a docker instance.
+#
+# call-seq;
+# grpc_show_servers <server_name>
+# E.g
+# grpc_show_server grpc-docker-server
+#
+# Shows the grpc servers on the GCE instance <server_name>
+grpc_show_servers() {
+ # declare vars local so that they don't pollute the shell environment
+ # where they this func is used.
+ local grpc_zone grpc_project dry_run # set by _grpc_set_project_and_zone
+ # set by _grpc_show_servers
+ local host
+
+ # set the project zone and check that all necessary args are provided
+ _grpc_set_project_and_zone -f _grpc_show_servers_args "$@" || return 1
+ gce_has_instance $grpc_project $host || return 1;
+
+ local cmd="sudo docker ps | grep grpc_"
+ local ssh_cmd="bash -l -c \"$cmd\""
+ echo "will run:"
+ echo " $ssh_cmd"
+ echo "on $host"
+ [[ $dry_run == 1 ]] && continue # don't run the command on a dry run
+ gcloud compute $project_opt ssh $zone_opt $host --command "$cmd"
+}
+
+_grpc_launch_servers_args() {
+ [[ -n $1 ]] && { # host
+ host=$1
+ shift
+ } || {
+ echo "$FUNCNAME: missing arg: host" 1>&2
+ return 1
+ }
+ [[ -n $1 ]] && {
+ servers="$@"
+ } || {
+ servers="cxx java go node ruby"
+ echo "$FUNCNAME: no servers specified, will launch defaults '$servers'"
+ }
+}
+
+# Launches servers on a docker instance.
+#
+# call-seq;
+# grpc_launch_servers <server_name> [server1 server2 ...]
+# E.g
+# grpc_launch_server grpc-docker-server ruby node
+#
+# Restarts all the specified servers on the GCE instance <server_name>
+# If no servers are specified, it launches all known servers
+grpc_launch_servers() {
+ # declare vars local so that they don't pollute the shell environment
+ # where they this func is used.
+ local grpc_zone grpc_project dry_run # set by _grpc_set_project_and_zone
+ # set by _grpc_launch_servers_args
+ local host servers
+
+ # set the project zone and check that all necessary args are provided
+ _grpc_set_project_and_zone -f _grpc_launch_servers_args "$@" || return 1
+ gce_has_instance $grpc_project $host || return 1;
+
+ # launch each of the servers in turn
+ for server in $servers
+ do
+ local grpc_port
+ case $server in
cxx) grpc_port=8010 ;;
go) grpc_port=8020 ;;
java) grpc_port=8030 ;;
@@ -609,44 +678,22 @@
ruby) grpc_port=8060 ;;
*) echo "bad server_type: $1" 1>&2; return 1 ;;
esac
- docker_label="grpc/$1"
- docker_name="grpc_interop_$1"
- shift
- } || {
- echo "$FUNCNAME: missing arg: server_type" 1>&2
- return 1
- }
-}
+ local docker_label="grpc/$server"
+ local docker_name="grpc_interop_$server"
-# Launches a server on a docker instance.
-#
-# call-seq;
-# grpc_launch_server <server_name> <server_type>
-#
-# Runs the server_type on a GCE instance running docker with server_name
-grpc_launch_server() {
- # declare vars local so that they don't pollute the shell environment
- # where they this func is used.
- local grpc_zone grpc_project dry_run # set by _grpc_set_project_and_zone
- # set by grpc_launch_server_args
- local docker_label docker_name host grpc_port
-
- # set the project zone and check that all necessary args are provided
- _grpc_set_project_and_zone -f grpc_launch_server_args "$@" || return 1
- gce_has_instance $grpc_project $host || return 1;
-
- cmd="sudo docker kill $docker_name > /dev/null 2>&1; "
- cmd+="sudo docker rm $docker_name > /dev/null 2>&1; "
- cmd+="sudo docker run -d --name $docker_name"
- cmd+=" -p $grpc_port:$grpc_port $docker_label"
- local project_opt="--project $grpc_project"
- local zone_opt="--zone $grpc_zone"
- local ssh_cmd="bash -l -c \"$cmd\""
- echo "will run:"
- 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"
+ cmd="sudo docker kill $docker_name > /dev/null 2>&1; "
+ cmd+="sudo docker rm $docker_name > /dev/null 2>&1; "
+ cmd+="sudo docker run -d --name $docker_name"
+ cmd+=" -p $grpc_port:$grpc_port $docker_label"
+ local project_opt="--project $grpc_project"
+ local zone_opt="--zone $grpc_zone"
+ local ssh_cmd="bash -l -c \"$cmd\""
+ echo "will run:"
+ 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"
+ done
}
# Runs a test command on a docker instance.
@@ -715,7 +762,16 @@
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.
@@ -761,7 +817,16 @@
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.
@@ -822,7 +887,6 @@
echo $the_cmd
}
-
# constructs the full dockerized java interop test cmd.
#
# call-seq:
@@ -832,12 +896,43 @@
local cmd_prefix="sudo docker run grpc/ruby bin/bash -l -c"
local test_script="/var/local/git/grpc/src/ruby/bin/interop/interop_client.rb"
local test_script+=" --use_tls"
- local gfe_flags=" --server_port=443 --server_host=grpc-test.sandbox.google.com --server_host_override=grpc-test.sandbox.google.com"
+ local gfe_flags=$(_grpc_prod_gfe_flags)
local env_prefix="SSL_CERT_FILE=/cacerts/roots.pem"
local the_cmd="$cmd_prefix '$env_prefix ruby $test_script $gfe_flags $@'"
echo $the_cmd
}
+# constructs the full dockerized ruby service_account auth interop test cmd.
+#
+# call-seq:
+# flags= .... # generic flags to include the command
+# cmd=$($grpc_gen_test_cmd $flags)
+grpc_cloud_prod_auth_service_account_creds_gen_ruby_cmd() {
+ local cmd_prefix="sudo docker run grpc/ruby bin/bash -l -c";
+ local test_script="/var/local/git/grpc/src/ruby/bin/interop/interop_client.rb"
+ local test_script+=" --use_tls"
+ local gfe_flags=$(_grpc_prod_gfe_flags)
+ local added_gfe_flags=$(_grpc_svc_acc_test_flags)
+ local env_prefix="SSL_CERT_FILE=/cacerts/roots.pem"
+ local the_cmd="$cmd_prefix '$env_prefix ruby $test_script $gfe_flags $added_gfe_flag $@'"
+ echo $the_cmd
+}
+
+# constructs the full dockerized ruby gce auth interop test cmd.
+#
+# call-seq:
+# flags= .... # generic flags to include the command
+# cmd=$($grpc_gen_test_cmd $flags)
+grpc_cloud_prod_auth_compute_engine_creds_gen_ruby_cmd() {
+ local cmd_prefix="sudo docker run grpc/ruby bin/bash -l -c";
+ local test_script="/var/local/git/grpc/src/ruby/bin/interop/interop_client.rb"
+ local test_script+=" --use_tls"
+ local gfe_flags=$(_grpc_prod_gfe_flags)
+ local added_gfe_flags=$(_grpc_gce_test_flags)
+ local env_prefix="SSL_CERT_FILE=/cacerts/roots.pem"
+ local the_cmd="$cmd_prefix '$env_prefix ruby $test_script $gfe_flags $added_gfe_flag $@'"
+ echo $the_cmd
+}
# constructs the full dockerized Go interop test cmd.
#
@@ -874,7 +969,7 @@
grpc_interop_gen_java_cmd() {
local cmd_prefix="sudo docker run grpc/java";
local test_script="/var/local/git/grpc-java/run-test-client.sh";
- local test_script+=" --server_host_override=foo.test.google.fr --use_test_ca=true --use_tls=true"
+ local test_script+=" --use_test_ca=true --use_tls=true"
local the_cmd="$cmd_prefix $test_script $@";
echo $the_cmd
}
@@ -888,14 +983,14 @@
local cmd_prefix="sudo docker run grpc/java";
local test_script="/var/local/git/grpc-java/run-test-client.sh";
local test_script+=" --use_tls=true"
- local gfe_flags=" --server_port=443 --server_host=grpc-test.sandbox.google.com --server_host_override=grpc-test.sandbox.google.com"
+ local gfe_flags=$(_grpc_prod_gfe_flags)
local the_cmd="$cmd_prefix $test_script $gfe_flags $@";
echo $the_cmd
}
# constructs the full dockerized php interop test cmd.
#
-# TODO(mlumish): update this to use the script once that's on git-on-borg
+# TODO(mlumish): update this to use the script once that's on git
#
# call-seq:
# flags= .... # generic flags to include the command
@@ -909,8 +1004,19 @@
echo $the_cmd
}
-# constructs the full dockerized cpp interop test cmd.
+# constructs the full dockerized node interop test cmd.
#
+# call-seq:
+# flags= .... # generic flags to include the command
+# cmd=$($grpc_gen_test_cmd $flags)
+grpc_interop_gen_node_cmd() {
+ local cmd_prefix="sudo docker run grpc/node";
+ local test_script="/usr/bin/nodejs /var/local/git/grpc/src/node/interop/interop_client.js --use_tls=true";
+ local the_cmd="$cmd_prefix $test_script $@";
+ echo $the_cmd
+}
+
+# constructs the full dockerized cpp interop test cmd.
#
# call-seq:
# flags= .... # generic flags to include the command
@@ -922,55 +1028,60 @@
echo $the_cmd
}
-grpc_interop_gen_node_cmd() {
- local cmd_prefix="sudo docker run grpc/node";
- local test_script="/usr/bin/nodejs /var/local/git/grpc/src/node/interop/interop_client.js --use_tls=true";
- local the_cmd="$cmd_prefix $test_script $@";
- echo $the_cmd
-}
-
-# constructs the full dockerized cpp interop test cmd.
-#
+# constructs the full dockerized cpp gce=>prod interop test cmd.
#
# call-seq:
# flags= .... # generic flags to include the command
# cmd=$($grpc_gen_test_cmd $flags)
grpc_cloud_prod_gen_cxx_cmd() {
local cmd_prefix="sudo docker run grpc/cxx";
- local test_script="/var/local/git/grpc/bins/opt/interop_client --enable_ssl";
- local gfe_flags=" --use_prod_roots --server_port=443 --server_host=grpc-test.sandbox.google.com --server_host_override=grpc-test.sandbox.google.com"
+ local test_script="/var/local/git/grpc/bins/opt/interop_client --enable_ssl --use_prod_roots";
+ local gfe_flags=$(_grpc_prod_gfe_flags)
local the_cmd="$cmd_prefix $test_script $gfe_flags $@";
echo $the_cmd
}
-# constructs the full dockerized cpp interop test cmd.
-#
+# constructs the full dockerized cpp service_account auth interop test cmd.
#
# call-seq:
# flags= .... # generic flags to include the command
# cmd=$($grpc_gen_test_cmd $flags)
grpc_cloud_prod_auth_service_account_creds_gen_cxx_cmd() {
local cmd_prefix="sudo docker run grpc/cxx";
- local test_script="/var/local/git/grpc/bins/opt/interop_client --enable_ssl";
- local gfe_flags=" --use_prod_roots --server_port=443 --server_host=grpc-test.sandbox.google.com --server_host_override=grpc-test.sandbox.google.com"
- local added_gfe_flags=" --service_account_key_file=/service_account/stubbyCloudTestingTest-7dd63462c60c.json --oauth_scope=https://www.googleapis.com/auth/xapi.zoo"
+ local test_script="/var/local/git/grpc/bins/opt/interop_client --enable_ssl --use_prod_roots";
+ local gfe_flags=$(_grpc_prod_gfe_flags)
+ local added_gfe_flags=$(_grpc_svc_acc_test_flags)
local the_cmd="$cmd_prefix $test_script $gfe_flags $added_gfe_flags $@";
echo $the_cmd
}
-# constructs the full dockerized cpp interop test cmd.
-#
+# constructs the full dockerized cpp gce auth interop test cmd.
#
# call-seq:
# flags= .... # generic flags to include the command
# cmd=$($grpc_gen_test_cmd $flags)
grpc_cloud_prod_auth_compute_engine_creds_gen_cxx_cmd() {
local cmd_prefix="sudo docker run grpc/cxx";
- local test_script="/var/local/git/grpc/bins/opt/interop_client --enable_ssl";
- local gfe_flags=" --use_prod_roots --server_port=443 --server_host=grpc-test.sandbox.google.com --server_host_override=grpc-test.sandbox.google.com"
- local added_gfe_flags=" --default_service_account=155450119199-r5aaqa2vqoa9g5mv2m6s3m1l293rlmel@developer.gserviceaccount.com --oauth_scope=https://www.googleapis.com/auth/xapi.zoo"
+ local test_script="/var/local/git/grpc/bins/opt/interop_client --enable_ssl --use_prod_roots";
+ local gfe_flags=$(_grpc_prod_gfe_flags)
+ local added_gfe_flags=$(_grpc_gce_test_flags)
local the_cmd="$cmd_prefix $test_script $gfe_flags $added_gfe_flags $@";
echo $the_cmd
}
-# TODO(grpc-team): add grpc_interop_gen_xxx_cmd for python|nodejs
+# outputs the flags passed to gfe tests
+_grpc_prod_gfe_flags() {
+ echo " --server_port=443 --server_host=grpc-test.sandbox.google.com --server_host_override=grpc-test.sandbox.google.com"
+}
+
+# outputs the flags passed to the service account auth tests
+_grpc_svc_acc_test_flags() {
+ echo " --service_account_key_file=/service_account/stubbyCloudTestingTest-7dd63462c60c.json --oauth_scope=https://www.googleapis.com/auth/xapi.zoo"
+}
+
+# outputs the flags passed to the gcloud auth tests
+_grpc_gce_test_flags() {
+ echo " --default_service_account=155450119199-r5aaqa2vqoa9g5mv2m6s3m1l293rlmel@developer.gserviceaccount.com --oauth_scope=https://www.googleapis.com/auth/xapi.zoo"
+}
+
+# TODO(grpc-team): add grpc_interop_gen_xxx_cmd for python
diff --git a/tools/gce_setup/interop_test_runner.sh b/tools/gce_setup/interop_test_runner.sh
index edc8bba..456ad4b 100755
--- a/tools/gce_setup/interop_test_runner.sh
+++ b/tools/gce_setup/interop_test_runner.sh
@@ -1,4 +1,8 @@
#!/bin/bash
+thisfile=$(readlink -ne "${BASH_SOURCE[0]}")
+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
@@ -13,15 +17,21 @@
do
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
+ echo " ['$test_case', '$client', '$server', true]," >> /tmp/interop_result.txt
else
- echo "$test_case $client $server failed" >> /tmp/interop_result.txt
+ echo " ['$test_case', '$client', '$server', false]," >> /tmp/interop_result.txt
fi
done
done
done
- gsutil cp /tmp/interop_result.txt gs://stoked-keyword-656-output/interop_result.txt
- rm /tmp/interop_result.txt
+ 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
}
set -x
diff --git a/tools/gce_setup/new_grpc_docker_builder.sh b/tools/gce_setup/new_grpc_docker_builder.sh
index 5d4fc36..ea36cc5 100755
--- a/tools/gce_setup/new_grpc_docker_builder.sh
+++ b/tools/gce_setup/new_grpc_docker_builder.sh
@@ -86,7 +86,6 @@
[[ -n $the_address ]] && address_flag="--address $the_address"
local the_image='container-vm-v20140925'
local scopes='compute-rw storage-full'
- scopes+=' https://www.googleapis.com/auth/gerritcodereview'
scopes+=' https://www.googleapis.com/auth/xapi.zoo'
gcloud --project $project compute instances create $instance \
$address_flag \
diff --git a/tools/gce_setup/new_grpc_docker_builder_on_startup.sh b/tools/gce_setup/new_grpc_docker_builder_on_startup.sh
index 87e8aac..cfd0541 100755
--- a/tools/gce_setup/new_grpc_docker_builder_on_startup.sh
+++ b/tools/gce_setup/new_grpc_docker_builder_on_startup.sh
@@ -3,8 +3,7 @@
#
# A grpc-docker GCE machine is based on docker container image.
#
-# On startup, it copies the grpc dockerfiles to a local directory, and update its address
-# so that the docker containers within it have git-on-borg-access.
+# On startup, it copies the grpc dockerfiles to a local directory, and update its address.
# _load_metadata curls a metadata url
_load_metadata() {
@@ -42,7 +41,82 @@
source $script_path
}
+# Args:
+# $1: numerator
+# $2: denominator
+# $3: threshold (optional; defaults to $THRESHOLD)
+#
+# Returns:
+# 1 if (numerator / denominator > threshold)
+# 0 otherwise
+_gce_disk_cmp_ratio() {
+ local DEFAULT_THRESHOLD="1.1"
+ local numer="${1}"
+ local denom="${2}"
+ local threshold="${3:-${DEFAULT_THRESHOLD}}"
+
+ if `which python > /dev/null 2>&1`; then
+ python -c "print(1 if (1. * ${numer} / ${denom} > ${threshold}) else 0)"
+ else
+ echo "Can't find python; calculation not done." 1>&2
+ return 1
+ fi
+}
+
+# Repartitions the disk or resizes the file system, depending on the current
+# state of the partition table.
+#
+# Automates the process described in
+# - https://cloud.google.com/compute/docs/disks/persistent-disks#repartitionrootpd
+_gce_disk_maybe_resize_then_reboot() {
+ # Determine the size in blocks, of the whole disk and the first partition.
+ local dev_sda="$(fdisk -s /dev/sda)"
+ local dev_sda1="$(fdisk -s /dev/sda1)"
+ local dev_sda1_start="$(sudo fdisk -l /dev/sda | grep /dev/sda1 | sed -e 's/ \+/ /g' | cut -d' ' -f 3)"
+
+ # Use fdisk to
+ # - first see if the partion 1 is using as much of the disk as it should
+ # - then to resize the partition if it's not
+ #
+ # fdisk(1) flags:
+ # -c: disable DOS compatibility mode
+ # -u: change display mode to sectors (from cylinders)
+ #
+ # fdisk(1) commands:
+ # d: delete partition (automatically selects the first one)
+ # n: new partition
+ # p: primary
+ # 1: partition number
+ # $dev_sda1_start: specify the value for the start sector, the default may be incorrect
+ # <1 blank lines>: accept the defaults for end sectors
+ # w: write partition table
+ if [ $(_gce_disk_cmp_ratio "${dev_sda}" "${dev_sda1}") -eq 1 ]; then
+ echo "$FUNCNAME: Updating the partition table to use full ${dev_sda} instead ${dev_sda1}"
+ cat <<EOF | fdisk -c -u /dev/sda
+d
+n
+p
+1
+$dev_sda1_start
+
+w
+EOF
+ echo "$FUNCNAME: ... updated the partition table"
+ shutdown -r now
+ return 0
+ fi
+
+ # After repartitioning, use resize2fs to expand sda1.
+ local df_size="$(df -B 1K / | grep ' /$' | sed -e 's/ \+/ /g' | cut -d' ' -f 2)"
+ if [ $(_gce_disk_cmp_ratio "${dev_sda}" "${df_size}") -eq 1 ]; then
+ echo "$FUNCNAME: resizing the partition to make full use of it"
+ resize2fs /dev/sda1
+ echo "$FUNCNAME: ... resize completed"
+ fi
+}
+
main() {
+ _gce_disk_maybe_resize_then_reboot
local script_attr='shared_startup_script_url'
_source_gs_script $script_attr || {
echo "halting, script 'attributes/$script_attr' could not be sourced"
@@ -54,10 +128,6 @@
# Install git and emacs
apt-get update && apt-get install -y git emacs || return 1
- # Enable access to git repos on git-on-borg
- local git_root='/var/local/git'
- install_gob_daemon $git_root/gerrit-gcompute-tools || return 1
-
# Startup the docker registry
grpc_docker_launch_registry && grpc_docker_pull_known
diff --git a/tools/gce_setup/post.html b/tools/gce_setup/post.html
new file mode 100644
index 0000000..57cbc8c
--- /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});
+ }
+ </script>
+ </head>
+ <body>
+ <div id="table_div"></div>
+ </body>
+</html>
diff --git a/tools/gce_setup/pre.html b/tools/gce_setup/pre.html
new file mode 100644
index 0000000..74ce5ce
--- /dev/null
+++ b/tools/gce_setup/pre.html
@@ -0,0 +1,14 @@
+<html>
+ <head>
+ <script type="text/javascript" src="https://www.google.com/jsapi"></script>
+ <script type="text/javascript">
+ google.load("visualization", "1", {packages:["table"]});
+ google.setOnLoadCallback(drawTable);
+
+ function drawTable() {
+ var data = new google.visualization.DataTable();
+ data.addColumn('string', 'TestCase');
+ data.addColumn('string', 'Client');
+ data.addColumn('string', 'Server');
+ data.addColumn('boolean', 'Pass');
+ data.addRows([
diff --git a/tools/gce_setup/shared_startup_funcs.sh b/tools/gce_setup/shared_startup_funcs.sh
index 3300eb2..eea9408 100755
--- a/tools/gce_setup/shared_startup_funcs.sh
+++ b/tools/gce_setup/shared_startup_funcs.sh
@@ -251,37 +251,6 @@
}
}
-# Allows instances to checkout repos on git-on-borg.
-#
-install_gob_daemon() {
- local gob_dir=$1
- [[ -n $gob_dir ]] || { echo "missing args: gob_dir" >&2; return 1; }
-
- local gob_repo=$2
- [[ -n $gob_repo ]] || gob_repo='https://gerrit.googlesource.com/gcompute-tools/'
-
- if [[ -e $gob_dir ]]
- then
- rm -fv $gob_dir || {
- echo "could not remove existing git repo at $gob_dir" >&2
- return 1
- }
- fi
-
- git clone $gob_repo $gob_dir || { echo "failed to pull gerrit cookie repo" >&2; return 1; }
- local startup_script=/etc/profile.d/gob_cookie_daemon.sh
-
- cat <<EOF >> $startup_script
-#!/bin/bash
-
-$gob_dir/git-cookie-authdaemon
-
-EOF
-
- chmod 755 $startup_script
- $startup_script
-}
-
# grpc_docker_add_docker_group
#
# Adds a docker group, restarts docker, relaunches the docker registry
@@ -403,7 +372,9 @@
[[ -d $dockerfile_dir ]] || { echo "$FUNCNAME: not a valid dir: $dockerfile_dir"; return 1; }
- # For specific base images, sync the ssh key into the .ssh dir in the dockerfile context
+ # For specific base images, sync private files.
+ #
+ # - the ssh key, ssh certs and/or service account info.
[[ $image_label == "grpc/base" ]] && {
grpc_docker_sync_github_key $dockerfile_dir/.ssh 'base_ssh_key' || return 1;
}
@@ -415,6 +386,7 @@
}
[[ $image_label == "grpc/ruby" ]] && {
grpc_docker_sync_roots_pem $dockerfile_dir/cacerts || return 1;
+ grpc_docker_sync_service_account $dockerfile_dir/service_account || return 1;
}
[[ $image_label == "grpc/cxx" ]] && {
grpc_docker_sync_service_account $dockerfile_dir/service_account || return 1;
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index 280c3f0..cb54c0d 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -108,10 +108,11 @@
_CONFIGS = {
'dbg': SimpleConfig('dbg'),
'opt': SimpleConfig('opt'),
- 'tsan': SimpleConfig('tsan'),
+ 'tsan': SimpleConfig('tsan', environ={
+ 'TSAN_OPTIONS': 'suppressions=tools/tsan_suppressions.txt'}),
'msan': SimpleConfig('msan'),
'asan': SimpleConfig('asan', environ={
- 'ASAN_OPTIONS': 'detect_leaks=1:color=always'}),
+ 'ASAN_OPTIONS': 'detect_leaks=1:color=always:suppressions=tools/tsan_suppressions.txt'}),
'gcov': SimpleConfig('gcov'),
'memcheck': ValgrindConfig('valgrind', 'memcheck'),
'helgrind': ValgrindConfig('dbg', 'helgrind')
diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json
index 50331ae..facef4e 100644
--- a/tools/run_tests/tests.json
+++ b/tools/run_tests/tests.json
@@ -279,6 +279,14 @@
},
{
"language": "c++",
+ "name": "tips_publisher_test"
+ },
+ {
+ "language": "c++",
+ "name": "tips_subscriber_test"
+ },
+ {
+ "language": "c++",
"name": "status_test"
},
{
@@ -290,10 +298,6 @@
"name": "thread_pool_test"
},
{
- "language": "c++",
- "name": "tips_client_test"
- },
- {
"language": "c",
"name": "chttp2_fake_security_cancel_after_accept_test"
},
diff --git a/tools/tsan_suppressions.txt b/tools/tsan_suppressions.txt
new file mode 100644
index 0000000..23d57f9
--- /dev/null
+++ b/tools/tsan_suppressions.txt
@@ -0,0 +1,2 @@
+# OPENSSL_cleanse does racy access to a global
+race:OPENSSL_cleanse
diff --git a/vsprojects/vs2013/gpr.vcxproj b/vsprojects/vs2013/gpr.vcxproj
index 3cbbfc2..d1f7085 100644
--- a/vsprojects/vs2013/gpr.vcxproj
+++ b/vsprojects/vs2013/gpr.vcxproj
@@ -91,11 +91,7 @@
<ClInclude Include="..\..\include\grpc\support\sync_posix.h" />
<ClInclude Include="..\..\include\grpc\support\sync_win32.h" />
<ClInclude Include="..\..\include\grpc\support\thd.h" />
- <ClInclude Include="..\..\include\grpc\support\thd_posix.h" />
- <ClInclude Include="..\..\include\grpc\support\thd_win32.h" />
<ClInclude Include="..\..\include\grpc\support\time.h" />
- <ClInclude Include="..\..\include\grpc\support\time_posix.h" />
- <ClInclude Include="..\..\include\grpc\support\time_win32.h" />
<ClInclude Include="..\..\include\grpc\support\useful.h" />
</ItemGroup>
<ItemGroup>
diff --git a/vsprojects/vs2013/gpr.vcxproj.filters b/vsprojects/vs2013/gpr.vcxproj.filters
index eb79b8d..13add83 100644
--- a/vsprojects/vs2013/gpr.vcxproj.filters
+++ b/vsprojects/vs2013/gpr.vcxproj.filters
@@ -153,21 +153,9 @@
<ClInclude Include="..\..\include\grpc\support\thd.h">
<Filter>include\grpc\support</Filter>
</ClInclude>
- <ClInclude Include="..\..\include\grpc\support\thd_posix.h">
- <Filter>include\grpc\support</Filter>
- </ClInclude>
- <ClInclude Include="..\..\include\grpc\support\thd_win32.h">
- <Filter>include\grpc\support</Filter>
- </ClInclude>
<ClInclude Include="..\..\include\grpc\support\time.h">
<Filter>include\grpc\support</Filter>
</ClInclude>
- <ClInclude Include="..\..\include\grpc\support\time_posix.h">
- <Filter>include\grpc\support</Filter>
- </ClInclude>
- <ClInclude Include="..\..\include\grpc\support\time_win32.h">
- <Filter>include\grpc\support</Filter>
- </ClInclude>
<ClInclude Include="..\..\include\grpc\support\useful.h">
<Filter>include\grpc\support</Filter>
</ClInclude>
diff --git a/vsprojects/vs2013/grpc.vcxproj b/vsprojects/vs2013/grpc.vcxproj
index 21a1f06..8c12b2d 100644
--- a/vsprojects/vs2013/grpc.vcxproj
+++ b/vsprojects/vs2013/grpc.vcxproj
@@ -146,6 +146,7 @@
<ClInclude Include="..\..\src\core\statistics\census_tracing.h" />
<ClInclude Include="..\..\src\core\statistics\hash_table.h" />
<ClInclude Include="..\..\src\core\statistics\window_stats.h" />
+ <ClInclude Include="..\..\src\core\surface\byte_buffer_queue.h" />
<ClInclude Include="..\..\src\core\surface\call.h" />
<ClInclude Include="..\..\src\core\surface\channel.h" />
<ClInclude Include="..\..\src\core\surface\client.h" />
@@ -312,6 +313,8 @@
</ClCompile>
<ClCompile Include="..\..\src\core\surface\byte_buffer.c">
</ClCompile>
+ <ClCompile Include="..\..\src\core\surface\byte_buffer_queue.c">
+ </ClCompile>
<ClCompile Include="..\..\src\core\surface\byte_buffer_reader.c">
</ClCompile>
<ClCompile Include="..\..\src\core\surface\call.c">
diff --git a/vsprojects/vs2013/grpc.vcxproj.filters b/vsprojects/vs2013/grpc.vcxproj.filters
index 3af681a..62f0b60 100644
--- a/vsprojects/vs2013/grpc.vcxproj.filters
+++ b/vsprojects/vs2013/grpc.vcxproj.filters
@@ -202,6 +202,9 @@
<ClCompile Include="..\..\src\core\surface\byte_buffer.c">
<Filter>src\core\surface</Filter>
</ClCompile>
+ <ClCompile Include="..\..\src\core\surface\byte_buffer_queue.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
<ClCompile Include="..\..\src\core\surface\byte_buffer_reader.c">
<Filter>src\core\surface</Filter>
</ClCompile>
@@ -521,6 +524,9 @@
<ClInclude Include="..\..\src\core\statistics\window_stats.h">
<Filter>src\core\statistics</Filter>
</ClInclude>
+ <ClInclude Include="..\..\src\core\surface\byte_buffer_queue.h">
+ <Filter>src\core\surface</Filter>
+ </ClInclude>
<ClInclude Include="..\..\src\core\surface\call.h">
<Filter>src\core\surface</Filter>
</ClInclude>
diff --git a/vsprojects/vs2013/grpc_unsecure.vcxproj b/vsprojects/vs2013/grpc_unsecure.vcxproj
index 21a1f06..8c12b2d 100644
--- a/vsprojects/vs2013/grpc_unsecure.vcxproj
+++ b/vsprojects/vs2013/grpc_unsecure.vcxproj
@@ -146,6 +146,7 @@
<ClInclude Include="..\..\src\core\statistics\census_tracing.h" />
<ClInclude Include="..\..\src\core\statistics\hash_table.h" />
<ClInclude Include="..\..\src\core\statistics\window_stats.h" />
+ <ClInclude Include="..\..\src\core\surface\byte_buffer_queue.h" />
<ClInclude Include="..\..\src\core\surface\call.h" />
<ClInclude Include="..\..\src\core\surface\channel.h" />
<ClInclude Include="..\..\src\core\surface\client.h" />
@@ -312,6 +313,8 @@
</ClCompile>
<ClCompile Include="..\..\src\core\surface\byte_buffer.c">
</ClCompile>
+ <ClCompile Include="..\..\src\core\surface\byte_buffer_queue.c">
+ </ClCompile>
<ClCompile Include="..\..\src\core\surface\byte_buffer_reader.c">
</ClCompile>
<ClCompile Include="..\..\src\core\surface\call.c">
diff --git a/vsprojects/vs2013/grpc_unsecure.vcxproj.filters b/vsprojects/vs2013/grpc_unsecure.vcxproj.filters
index 4dadb61..5ed5e9b 100644
--- a/vsprojects/vs2013/grpc_unsecure.vcxproj.filters
+++ b/vsprojects/vs2013/grpc_unsecure.vcxproj.filters
@@ -163,6 +163,9 @@
<ClCompile Include="..\..\src\core\surface\byte_buffer.c">
<Filter>src\core\surface</Filter>
</ClCompile>
+ <ClCompile Include="..\..\src\core\surface\byte_buffer_queue.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
<ClCompile Include="..\..\src\core\surface\byte_buffer_reader.c">
<Filter>src\core\surface</Filter>
</ClCompile>
@@ -446,6 +449,9 @@
<ClInclude Include="..\..\src\core\statistics\window_stats.h">
<Filter>src\core\statistics</Filter>
</ClInclude>
+ <ClInclude Include="..\..\src\core\surface\byte_buffer_queue.h">
+ <Filter>src\core\surface</Filter>
+ </ClInclude>
<ClInclude Include="..\..\src\core\surface\call.h">
<Filter>src\core\surface</Filter>
</ClInclude>