Merge branch 'selectivity' into ALL-the-things

reviewable/pr11336/r2^2
Craig Tiller 8 years ago
commit 71005b169a
  1. 4
      BUILD
  2. 34
      CMakeLists.txt
  3. 40
      Makefile
  4. 14
      build.yaml
  5. 2
      gRPC-Core.podspec
  6. 2
      gRPC-ProtoRPC.podspec
  7. 2
      gRPC-RxLibrary.podspec
  8. 2
      gRPC.podspec
  9. 1
      grpc.def
  10. 8
      include/grpc++/grpc++.h
  11. 7
      include/grpc++/server_builder.h
  12. 6
      include/grpc/grpc.h
  13. 2
      package.json
  14. 4
      package.xml
  15. 11
      src/core/ext/census/intrusive_hash_map.c
  16. 2
      src/core/ext/census/intrusive_hash_map.h
  17. 77
      src/core/ext/filters/client_channel/channel_connectivity.c
  18. 107
      src/core/ext/filters/client_channel/client_channel.c
  19. 6
      src/core/ext/filters/client_channel/client_channel.h
  20. 2
      src/cpp/common/version_cc.cc
  21. 9
      src/cpp/server/server_builder.cc
  22. 6
      src/csharp/Grpc.Auth/Grpc.Auth.csproj
  23. 8
      src/csharp/Grpc.Core.Testing/Grpc.Core.Testing.csproj
  24. 41
      src/csharp/Grpc.Core/Channel.cs
  25. 4
      src/csharp/Grpc.Core/Grpc.Core.csproj
  26. 2
      src/csharp/Grpc.Core/Version.csproj.include
  27. 4
      src/csharp/Grpc.Core/VersionInfo.cs
  28. 6
      src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj
  29. 6
      src/csharp/Grpc.Reflection/Grpc.Reflection.csproj
  30. 12
      src/csharp/build_packages_dotnetcli.bat
  31. 16
      src/csharp/build_packages_dotnetcli.sh
  32. 37
      src/node/ext/call.cc
  33. 1
      src/node/ext/call.h
  34. 4
      src/node/health_check/package.json
  35. 40
      src/node/performance/benchmark_client.js
  36. 12
      src/node/src/client.js
  37. 1
      src/node/test/common_test.js
  38. 25
      src/node/test/surface_test.js
  39. 2
      src/node/tools/package.json
  40. 2
      src/objective-c/!ProtoCompiler-gRPCPlugin.podspec
  41. 2
      src/objective-c/GRPCClient/private/version.h
  42. 2
      src/php/composer.json
  43. 2
      src/php/ext/grpc/version.h
  44. 7
      src/python/grpcio/grpc/_channel.py
  45. 2
      src/python/grpcio/grpc/_grpcio_metadata.py
  46. 2
      src/python/grpcio/grpc_version.py
  47. 2
      src/python/grpcio_health_checking/grpc_version.py
  48. 5
      src/python/grpcio_reflection/grpc_reflection/v1alpha/reflection.py
  49. 2
      src/python/grpcio_reflection/grpc_version.py
  50. 2
      src/python/grpcio_tests/grpc_version.py
  51. 1
      src/python/grpcio_tests/tests/tests.json
  52. 70
      src/python/grpcio_tests/tests/unit/_reconnect_test.py
  53. 5
      src/ruby/end2end/channel_closing_driver.rb
  54. 3
      src/ruby/end2end/channel_state_driver.rb
  55. 96
      src/ruby/end2end/grpc_class_init_client.rb
  56. 15
      src/ruby/end2end/grpc_class_init_driver.rb
  57. 63
      src/ruby/end2end/multiple_killed_watching_threads_driver.rb
  58. 2
      src/ruby/end2end/sig_int_during_channel_watch_client.rb
  59. 5
      src/ruby/end2end/sig_int_during_channel_watch_driver.rb
  60. 474
      src/ruby/ext/grpc/rb_channel.c
  61. 12
      src/ruby/ext/grpc/rb_event_thread.c
  62. 20
      src/ruby/ext/grpc/rb_grpc.c
  63. 2
      src/ruby/ext/grpc/rb_grpc_imports.generated.c
  64. 3
      src/ruby/ext/grpc/rb_grpc_imports.generated.h
  65. 2
      src/ruby/lib/grpc/version.rb
  66. 34
      src/ruby/spec/channel_connection_spec.rb
  67. 2
      src/ruby/tools/version.rb
  68. 10
      templates/src/csharp/build_packages_dotnetcli.bat.template
  69. 10
      templates/src/csharp/build_packages_dotnetcli.sh.template
  70. 4
      test/core/census/intrusive_hash_map_test.c
  71. 69
      test/core/surface/concurrent_connectivity_test.c
  72. 214
      test/core/surface/num_external_connectivity_watchers_test.c
  73. 3
      test/core/surface/sequential_connectivity_test.c
  74. 12
      test/cpp/microbenchmarks/bm_fullstack_trickle.cc
  75. 14
      tools/distrib/pylint_code.sh
  76. 2
      tools/distrib/python/grpcio_tools/grpc_version.py
  77. 3
      tools/dockerfile/test/cxx_alpine_x64/Dockerfile
  78. 3
      tools/dockerfile/test/python_alpine_x64/Dockerfile
  79. 3
      tools/dockerfile/test/python_jessie_x64/Dockerfile
  80. 2
      tools/doxygen/Doxyfile.c++
  81. 2
      tools/doxygen/Doxyfile.c++.internal
  82. 72
      tools/internal_ci/helper_scripts/prepare_build_macos_rc
  83. 4
      tools/internal_ci/linux/grpc_build_artifacts.sh
  84. 39
      tools/internal_ci/linux/grpc_sanity.cfg
  85. 2
      tools/internal_ci/linux/grpc_sanity.sh
  86. 2
      tools/internal_ci/macos/grpc_master.sh
  87. 2
      tools/profiling/microbenchmarks/bm_json.py
  88. 17
      tools/run_tests/generated/sources_and_headers.json
  89. 24
      tools/run_tests/generated/tests.json
  90. 1
      tools/run_tests/helper_scripts/build_python.sh
  91. 1
      tools/run_tests/helper_scripts/run_ruby_end2end_tests.sh
  92. 44
      tools/run_tests/performance/scenario_config.py
  93. 7
      tools/run_tests/python_utils/jobset.py
  94. 2
      tools/run_tests/run_tests.py
  95. 27
      vsprojects/buildtests_c.sln
  96. 199
      vsprojects/vcxproj/test/num_external_connectivity_watchers_test/num_external_connectivity_watchers_test.vcxproj
  97. 21
      vsprojects/vcxproj/test/num_external_connectivity_watchers_test/num_external_connectivity_watchers_test.vcxproj.filters

@ -51,9 +51,9 @@ load(
# This should be updated along with build.yaml # This should be updated along with build.yaml
g_stands_for = "gregarious" g_stands_for = "gregarious"
core_version = "3.0.0-dev" core_version = "4.0.0-dev"
version = "1.4.0-dev" version = "1.5.0-dev"
grpc_cc_library( grpc_cc_library(
name = "gpr", name = "gpr",

@ -39,7 +39,7 @@
cmake_minimum_required(VERSION 2.8) cmake_minimum_required(VERSION 2.8)
set(PACKAGE_NAME "grpc") set(PACKAGE_NAME "grpc")
set(PACKAGE_VERSION "1.4.0-dev") set(PACKAGE_VERSION "1.5.0-dev")
set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
set(PACKAGE_TARNAME "${PACKAGE_NAME}-${PACKAGE_VERSION}") set(PACKAGE_TARNAME "${PACKAGE_NAME}-${PACKAGE_VERSION}")
set(PACKAGE_BUGREPORT "https://github.com/grpc/grpc/issues/") set(PACKAGE_BUGREPORT "https://github.com/grpc/grpc/issues/")
@ -482,6 +482,7 @@ add_dependencies(buildtests_c mlog_test)
add_dependencies(buildtests_c multiple_server_queues_test) add_dependencies(buildtests_c multiple_server_queues_test)
add_dependencies(buildtests_c murmur_hash_test) add_dependencies(buildtests_c murmur_hash_test)
add_dependencies(buildtests_c no_server_test) add_dependencies(buildtests_c no_server_test)
add_dependencies(buildtests_c num_external_connectivity_watchers_test)
add_dependencies(buildtests_c parse_address_test) add_dependencies(buildtests_c parse_address_test)
add_dependencies(buildtests_c percent_encoding_test) add_dependencies(buildtests_c percent_encoding_test)
if(_gRPC_PLATFORM_LINUX) if(_gRPC_PLATFORM_LINUX)
@ -7513,6 +7514,37 @@ target_link_libraries(no_server_test
endif (gRPC_BUILD_TESTS) endif (gRPC_BUILD_TESTS)
if (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS)
add_executable(num_external_connectivity_watchers_test
test/core/surface/num_external_connectivity_watchers_test.c
)
target_include_directories(num_external_connectivity_watchers_test
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
PRIVATE ${BORINGSSL_ROOT_DIR}/include
PRIVATE ${PROTOBUF_ROOT_DIR}/src
PRIVATE ${BENCHMARK_ROOT_DIR}/include
PRIVATE ${ZLIB_ROOT_DIR}
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
PRIVATE ${CARES_BUILD_INCLUDE_DIR}
PRIVATE ${CARES_INCLUDE_DIR}
PRIVATE ${CARES_PLATFORM_INCLUDE_DIR}
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/cares/cares
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include
)
target_link_libraries(num_external_connectivity_watchers_test
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
grpc
gpr_test_util
gpr
)
endif (gRPC_BUILD_TESTS)
if (gRPC_BUILD_TESTS)
add_executable(parse_address_test add_executable(parse_address_test
test/core/client_channel/parse_address_test.c test/core/client_channel/parse_address_test.c
) )

@ -423,8 +423,8 @@ Q = @
endif endif
CORE_VERSION = 4.0.0-dev CORE_VERSION = 4.0.0-dev
CPP_VERSION = 1.4.0-dev CPP_VERSION = 1.5.0-dev
CSHARP_VERSION = 1.4.0-dev CSHARP_VERSION = 1.5.0-dev
CPPFLAGS_NO_ARCH += $(addprefix -I, $(INCLUDES)) $(addprefix -D, $(DEFINES)) CPPFLAGS_NO_ARCH += $(addprefix -I, $(INCLUDES)) $(addprefix -D, $(DEFINES))
CPPFLAGS += $(CPPFLAGS_NO_ARCH) $(ARCH_FLAGS) CPPFLAGS += $(CPPFLAGS_NO_ARCH) $(ARCH_FLAGS)
@ -1063,6 +1063,7 @@ murmur_hash_test: $(BINDIR)/$(CONFIG)/murmur_hash_test
nanopb_fuzzer_response_test: $(BINDIR)/$(CONFIG)/nanopb_fuzzer_response_test nanopb_fuzzer_response_test: $(BINDIR)/$(CONFIG)/nanopb_fuzzer_response_test
nanopb_fuzzer_serverlist_test: $(BINDIR)/$(CONFIG)/nanopb_fuzzer_serverlist_test nanopb_fuzzer_serverlist_test: $(BINDIR)/$(CONFIG)/nanopb_fuzzer_serverlist_test
no_server_test: $(BINDIR)/$(CONFIG)/no_server_test no_server_test: $(BINDIR)/$(CONFIG)/no_server_test
num_external_connectivity_watchers_test: $(BINDIR)/$(CONFIG)/num_external_connectivity_watchers_test
parse_address_test: $(BINDIR)/$(CONFIG)/parse_address_test parse_address_test: $(BINDIR)/$(CONFIG)/parse_address_test
percent_decode_fuzzer: $(BINDIR)/$(CONFIG)/percent_decode_fuzzer percent_decode_fuzzer: $(BINDIR)/$(CONFIG)/percent_decode_fuzzer
percent_encode_fuzzer: $(BINDIR)/$(CONFIG)/percent_encode_fuzzer percent_encode_fuzzer: $(BINDIR)/$(CONFIG)/percent_encode_fuzzer
@ -1442,6 +1443,7 @@ buildtests_c: privatelibs_c \
$(BINDIR)/$(CONFIG)/multiple_server_queues_test \ $(BINDIR)/$(CONFIG)/multiple_server_queues_test \
$(BINDIR)/$(CONFIG)/murmur_hash_test \ $(BINDIR)/$(CONFIG)/murmur_hash_test \
$(BINDIR)/$(CONFIG)/no_server_test \ $(BINDIR)/$(CONFIG)/no_server_test \
$(BINDIR)/$(CONFIG)/num_external_connectivity_watchers_test \
$(BINDIR)/$(CONFIG)/parse_address_test \ $(BINDIR)/$(CONFIG)/parse_address_test \
$(BINDIR)/$(CONFIG)/percent_encoding_test \ $(BINDIR)/$(CONFIG)/percent_encoding_test \
$(BINDIR)/$(CONFIG)/pollset_set_test \ $(BINDIR)/$(CONFIG)/pollset_set_test \
@ -1911,6 +1913,8 @@ test_c: buildtests_c
$(Q) $(BINDIR)/$(CONFIG)/murmur_hash_test || ( echo test murmur_hash_test failed ; exit 1 ) $(Q) $(BINDIR)/$(CONFIG)/murmur_hash_test || ( echo test murmur_hash_test failed ; exit 1 )
$(E) "[RUN] Testing no_server_test" $(E) "[RUN] Testing no_server_test"
$(Q) $(BINDIR)/$(CONFIG)/no_server_test || ( echo test no_server_test failed ; exit 1 ) $(Q) $(BINDIR)/$(CONFIG)/no_server_test || ( echo test no_server_test failed ; exit 1 )
$(E) "[RUN] Testing num_external_connectivity_watchers_test"
$(Q) $(BINDIR)/$(CONFIG)/num_external_connectivity_watchers_test || ( echo test num_external_connectivity_watchers_test failed ; exit 1 )
$(E) "[RUN] Testing parse_address_test" $(E) "[RUN] Testing parse_address_test"
$(Q) $(BINDIR)/$(CONFIG)/parse_address_test || ( echo test parse_address_test failed ; exit 1 ) $(Q) $(BINDIR)/$(CONFIG)/parse_address_test || ( echo test parse_address_test failed ; exit 1 )
$(E) "[RUN] Testing percent_encoding_test" $(E) "[RUN] Testing percent_encoding_test"
@ -11756,6 +11760,38 @@ endif
endif endif
NUM_EXTERNAL_CONNECTIVITY_WATCHERS_TEST_SRC = \
test/core/surface/num_external_connectivity_watchers_test.c \
NUM_EXTERNAL_CONNECTIVITY_WATCHERS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(NUM_EXTERNAL_CONNECTIVITY_WATCHERS_TEST_SRC))))
ifeq ($(NO_SECURE),true)
# You can't build secure targets if you don't have OpenSSL.
$(BINDIR)/$(CONFIG)/num_external_connectivity_watchers_test: openssl_dep_error
else
$(BINDIR)/$(CONFIG)/num_external_connectivity_watchers_test: $(NUM_EXTERNAL_CONNECTIVITY_WATCHERS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
$(E) "[LD] Linking $@"
$(Q) mkdir -p `dirname $@`
$(Q) $(LD) $(LDFLAGS) $(NUM_EXTERNAL_CONNECTIVITY_WATCHERS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/num_external_connectivity_watchers_test
endif
$(OBJDIR)/$(CONFIG)/test/core/surface/num_external_connectivity_watchers_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
deps_num_external_connectivity_watchers_test: $(NUM_EXTERNAL_CONNECTIVITY_WATCHERS_TEST_OBJS:.o=.dep)
ifneq ($(NO_SECURE),true)
ifneq ($(NO_DEPS),true)
-include $(NUM_EXTERNAL_CONNECTIVITY_WATCHERS_TEST_OBJS:.o=.dep)
endif
endif
PARSE_ADDRESS_TEST_SRC = \ PARSE_ADDRESS_TEST_SRC = \
test/core/client_channel/parse_address_test.c \ test/core/client_channel/parse_address_test.c \

@ -14,7 +14,7 @@ settings:
'#10': See the expand_version.py for all the quirks here '#10': See the expand_version.py for all the quirks here
core_version: 4.0.0-dev core_version: 4.0.0-dev
g_stands_for: gregarious g_stands_for: gregarious
version: 1.4.0-dev version: 1.5.0-dev
filegroups: filegroups:
- name: census - name: census
public_headers: public_headers:
@ -2702,6 +2702,18 @@ targets:
- grpc - grpc
- gpr_test_util - gpr_test_util
- gpr - gpr
- name: num_external_connectivity_watchers_test
build: test
language: c
src:
- test/core/surface/num_external_connectivity_watchers_test.c
deps:
- grpc_test_util
- grpc
- gpr_test_util
- gpr
exclude_iomgrs:
- uv
- name: parse_address_test - name: parse_address_test
build: test build: test
language: c language: c

@ -37,7 +37,7 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = 'gRPC-Core' s.name = 'gRPC-Core'
version = '1.4.0-dev' version = '1.5.0-dev'
s.version = version s.version = version
s.summary = 'Core cross-platform gRPC library, written in C' s.summary = 'Core cross-platform gRPC library, written in C'
s.homepage = 'http://www.grpc.io' s.homepage = 'http://www.grpc.io'

@ -36,7 +36,7 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = 'gRPC-ProtoRPC' s.name = 'gRPC-ProtoRPC'
version = '1.4.0-dev' version = '1.5.0-dev'
s.version = version s.version = version
s.summary = 'RPC library for Protocol Buffers, based on gRPC' s.summary = 'RPC library for Protocol Buffers, based on gRPC'
s.homepage = 'http://www.grpc.io' s.homepage = 'http://www.grpc.io'

@ -36,7 +36,7 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = 'gRPC-RxLibrary' s.name = 'gRPC-RxLibrary'
version = '1.4.0-dev' version = '1.5.0-dev'
s.version = version s.version = version
s.summary = 'Reactive Extensions library for iOS/OSX.' s.summary = 'Reactive Extensions library for iOS/OSX.'
s.homepage = 'http://www.grpc.io' s.homepage = 'http://www.grpc.io'

@ -35,7 +35,7 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = 'gRPC' s.name = 'gRPC'
version = '1.4.0-dev' version = '1.5.0-dev'
s.version = version s.version = version
s.summary = 'gRPC client library for iOS/OSX' s.summary = 'gRPC client library for iOS/OSX'
s.homepage = 'http://www.grpc.io' s.homepage = 'http://www.grpc.io'

@ -65,6 +65,7 @@ EXPORTS
grpc_alarm_cancel grpc_alarm_cancel
grpc_alarm_destroy grpc_alarm_destroy
grpc_channel_check_connectivity_state grpc_channel_check_connectivity_state
grpc_channel_num_external_connectivity_watchers
grpc_channel_watch_connectivity_state grpc_channel_watch_connectivity_state
grpc_channel_create_call grpc_channel_create_call
grpc_channel_ping grpc_channel_ping

@ -34,18 +34,18 @@
/// \mainpage gRPC C++ API /// \mainpage gRPC C++ API
/// ///
/// The gRPC C++ API mainly consists of the following classes: /// The gRPC C++ API mainly consists of the following classes:
// /// <br>
/// - grpc::Channel, which represents the connection to an endpoint. See [the /// - grpc::Channel, which represents the connection to an endpoint. See [the
/// gRPC Concepts page](http://www.grpc.io/docs/guides/concepts.html) for more /// gRPC Concepts page](http://www.grpc.io/docs/guides/concepts.html) for more
/// details. Channels are created by the factory function grpc::CreateChannel. /// details. Channels are created by the factory function grpc::CreateChannel.
// ///
/// - grpc::CompletionQueue, the producer-consumer queue used for all /// - grpc::CompletionQueue, the producer-consumer queue used for all
/// asynchronous communication with the gRPC runtime. /// asynchronous communication with the gRPC runtime.
// ///
/// - grpc::ClientContext and grpc::ServerContext, where optional configuration /// - grpc::ClientContext and grpc::ServerContext, where optional configuration
/// for an RPC can be set, such as setting custom metadata to be conveyed to the /// for an RPC can be set, such as setting custom metadata to be conveyed to the
/// peer, compression settings, authentication, etc. /// peer, compression settings, authentication, etc.
// ///
/// - grpc::Server, representing a gRPC server, created by grpc::ServerBuilder. /// - grpc::Server, representing a gRPC server, created by grpc::ServerBuilder.
/// ///
/// Streaming calls are handled with the streaming classes in /// Streaming calls are handled with the streaming classes in

@ -150,15 +150,16 @@ class ServerBuilder {
/// ///
/// It can be invoked multiple times. /// It can be invoked multiple times.
/// ///
/// \param addr The address to try to bind to the server (eg, localhost:1234, /// \param addr_uri The address to try to bind to the server in URI form. If
/// 192.168.1.1:31416, [::1]:27182, etc.). /// the scheme name is omitted, "dns:///" is assumed. Valid values include
/// dns:///localhost:1234, / 192.168.1.1:31416, dns:///[::1]:27182, etc.).
/// \params creds The credentials associated with the server. /// \params creds The credentials associated with the server.
/// \param selected_port[out] If not `nullptr`, gets populated with the port /// \param selected_port[out] If not `nullptr`, gets populated with the port
/// number bound to the \a grpc::Server for the corresponding endpoint after /// number bound to the \a grpc::Server for the corresponding endpoint after
/// it is successfully bound, 0 otherwise. /// it is successfully bound, 0 otherwise.
/// ///
// TODO(dgq): the "port" part seems to be a misnomer. // TODO(dgq): the "port" part seems to be a misnomer.
ServerBuilder& AddListeningPort(const grpc::string& addr, ServerBuilder& AddListeningPort(const grpc::string& addr_uri,
std::shared_ptr<ServerCredentials> creds, std::shared_ptr<ServerCredentials> creds,
int* selected_port = nullptr); int* selected_port = nullptr);

@ -178,6 +178,12 @@ GRPCAPI void grpc_alarm_destroy(grpc_alarm *alarm);
GRPCAPI grpc_connectivity_state grpc_channel_check_connectivity_state( GRPCAPI grpc_connectivity_state grpc_channel_check_connectivity_state(
grpc_channel *channel, int try_to_connect); grpc_channel *channel, int try_to_connect);
/** Number of active "external connectivity state watchers" attached to a
* channel.
* Useful for testing. **/
GRPCAPI int grpc_channel_num_external_connectivity_watchers(
grpc_channel *channel);
/** Watch for a change in connectivity state. /** Watch for a change in connectivity state.
Once the channel connectivity state is different from last_observed_state, Once the channel connectivity state is different from last_observed_state,
tag will be enqueued on cq with success=1. tag will be enqueued on cq with success=1.

@ -1,6 +1,6 @@
{ {
"name": "grpc", "name": "grpc",
"version": "1.4.0-dev", "version": "1.5.0-dev",
"author": "Google Inc.", "author": "Google Inc.",
"description": "gRPC Library for Node", "description": "gRPC Library for Node",
"homepage": "http://www.grpc.io/", "homepage": "http://www.grpc.io/",

@ -13,8 +13,8 @@
<date>2017-05-22</date> <date>2017-05-22</date>
<time>16:06:07</time> <time>16:06:07</time>
<version> <version>
<release>1.4.0dev</release> <release>1.5.0dev</release>
<api>1.4.0dev</api> <api>1.5.0dev</api>
</version> </version>
<stability> <stability>
<release>beta</release> <release>beta</release>

@ -37,7 +37,7 @@
extern bool hm_index_compare(const hm_index *A, const hm_index *B); extern bool hm_index_compare(const hm_index *A, const hm_index *B);
/* Simple hashing function that takes lower 32 bits. */ /* Simple hashing function that takes lower 32 bits. */
static inline uint32_t chunked_vector_hasher(uint64_t key) { static __inline uint32_t chunked_vector_hasher(uint64_t key) {
return (uint32_t)key; return (uint32_t)key;
} }
@ -45,7 +45,7 @@ static inline uint32_t chunked_vector_hasher(uint64_t key) {
static const size_t VECTOR_CHUNK_SIZE = (1 << 20) / sizeof(void *); static const size_t VECTOR_CHUNK_SIZE = (1 << 20) / sizeof(void *);
/* Helper functions which return buckets from the chunked vector. */ /* Helper functions which return buckets from the chunked vector. */
static inline void **get_mutable_bucket(const chunked_vector *buckets, static __inline void **get_mutable_bucket(const chunked_vector *buckets,
uint32_t index) { uint32_t index) {
if (index < VECTOR_CHUNK_SIZE) { if (index < VECTOR_CHUNK_SIZE) {
return &buckets->first_[index]; return &buckets->first_[index];
@ -54,7 +54,8 @@ static inline void **get_mutable_bucket(const chunked_vector *buckets,
return &buckets->rest_[rest_index][index % VECTOR_CHUNK_SIZE]; return &buckets->rest_[rest_index][index % VECTOR_CHUNK_SIZE];
} }
static inline void *get_bucket(const chunked_vector *buckets, uint32_t index) { static __inline void *get_bucket(const chunked_vector *buckets,
uint32_t index) {
if (index < VECTOR_CHUNK_SIZE) { if (index < VECTOR_CHUNK_SIZE) {
return buckets->first_[index]; return buckets->first_[index];
} }
@ -63,7 +64,7 @@ static inline void *get_bucket(const chunked_vector *buckets, uint32_t index) {
} }
/* Helper function. */ /* Helper function. */
static inline size_t RestSize(const chunked_vector *vec) { static __inline size_t RestSize(const chunked_vector *vec) {
return (vec->size_ <= VECTOR_CHUNK_SIZE) return (vec->size_ <= VECTOR_CHUNK_SIZE)
? 0 ? 0
: (vec->size_ - VECTOR_CHUNK_SIZE - 1) / VECTOR_CHUNK_SIZE + 1; : (vec->size_ - VECTOR_CHUNK_SIZE - 1) / VECTOR_CHUNK_SIZE + 1;
@ -222,7 +223,7 @@ hm_item *intrusive_hash_map_erase(intrusive_hash_map *hash_map, uint64_t key) {
* array_size-1. Returns true if it is a new hm_item and false if the hm_item * array_size-1. Returns true if it is a new hm_item and false if the hm_item
* already existed. * already existed.
*/ */
static inline bool intrusive_hash_map_internal_insert(chunked_vector *buckets, static __inline bool intrusive_hash_map_internal_insert(chunked_vector *buckets,
uint32_t hash_mask, uint32_t hash_mask,
hm_item *item) { hm_item *item) {
const uint64_t key = item->key; const uint64_t key = item->key;

@ -101,7 +101,7 @@ typedef struct hm_index {
/* Returns true if two hm_indices point to the same object within the hash map /* Returns true if two hm_indices point to the same object within the hash map
* and false otherwise. */ * and false otherwise. */
inline bool hm_index_compare(const hm_index *A, const hm_index *B) { __inline bool hm_index_compare(const hm_index *A, const hm_index *B) {
return (A->item == B->item && A->bucket_index == B->bucket_index); return (A->item == B->item && A->bucket_index == B->bucket_index);
} }

@ -67,9 +67,8 @@ grpc_connectivity_state grpc_channel_check_connectivity_state(
typedef enum { typedef enum {
WAITING, WAITING,
CALLING_BACK, READY_TO_CALL_BACK,
CALLING_BACK_AND_FINISHED, CALLING_BACK_AND_FINISHED,
CALLED_BACK
} callback_phase; } callback_phase;
typedef struct { typedef struct {
@ -77,11 +76,13 @@ typedef struct {
callback_phase phase; callback_phase phase;
grpc_closure on_complete; grpc_closure on_complete;
grpc_closure on_timeout; grpc_closure on_timeout;
grpc_closure watcher_timer_init;
grpc_timer alarm; grpc_timer alarm;
grpc_connectivity_state state; grpc_connectivity_state state;
grpc_completion_queue *cq; grpc_completion_queue *cq;
grpc_cq_completion completion_storage; grpc_cq_completion completion_storage;
grpc_channel *channel; grpc_channel *channel;
grpc_error *error;
void *tag; void *tag;
} state_watcher; } state_watcher;
@ -105,11 +106,8 @@ static void finished_completion(grpc_exec_ctx *exec_ctx, void *pw,
gpr_mu_lock(&w->mu); gpr_mu_lock(&w->mu);
switch (w->phase) { switch (w->phase) {
case WAITING: case WAITING:
case CALLED_BACK: case READY_TO_CALL_BACK:
GPR_UNREACHABLE_CODE(return ); GPR_UNREACHABLE_CODE(return );
case CALLING_BACK:
w->phase = CALLED_BACK;
break;
case CALLING_BACK_AND_FINISHED: case CALLING_BACK_AND_FINISHED:
delete = 1; delete = 1;
break; break;
@ -123,10 +121,14 @@ static void finished_completion(grpc_exec_ctx *exec_ctx, void *pw,
static void partly_done(grpc_exec_ctx *exec_ctx, state_watcher *w, static void partly_done(grpc_exec_ctx *exec_ctx, state_watcher *w,
bool due_to_completion, grpc_error *error) { bool due_to_completion, grpc_error *error) {
int delete = 0;
if (due_to_completion) { if (due_to_completion) {
grpc_timer_cancel(exec_ctx, &w->alarm); grpc_timer_cancel(exec_ctx, &w->alarm);
} else {
grpc_channel_element *client_channel_elem = grpc_channel_stack_last_element(
grpc_channel_get_channel_stack(w->channel));
grpc_client_channel_watch_connectivity_state(exec_ctx, client_channel_elem,
grpc_cq_pollset(w->cq), NULL,
&w->on_complete, NULL);
} }
gpr_mu_lock(&w->mu); gpr_mu_lock(&w->mu);
@ -147,25 +149,27 @@ static void partly_done(grpc_exec_ctx *exec_ctx, state_watcher *w,
} }
switch (w->phase) { switch (w->phase) {
case WAITING: case WAITING:
w->phase = CALLING_BACK; GRPC_ERROR_REF(error);
grpc_cq_end_op(exec_ctx, w->cq, w->tag, GRPC_ERROR_REF(error), w->error = error;
finished_completion, w, &w->completion_storage); w->phase = READY_TO_CALL_BACK;
break; break;
case CALLING_BACK: case READY_TO_CALL_BACK:
if (error != GRPC_ERROR_NONE) {
GPR_ASSERT(!due_to_completion);
GRPC_ERROR_UNREF(w->error);
GRPC_ERROR_REF(error);
w->error = error;
}
w->phase = CALLING_BACK_AND_FINISHED; w->phase = CALLING_BACK_AND_FINISHED;
grpc_cq_end_op(exec_ctx, w->cq, w->tag, w->error, finished_completion, w,
&w->completion_storage);
break; break;
case CALLING_BACK_AND_FINISHED: case CALLING_BACK_AND_FINISHED:
GPR_UNREACHABLE_CODE(return ); GPR_UNREACHABLE_CODE(return );
case CALLED_BACK:
delete = 1;
break; break;
} }
gpr_mu_unlock(&w->mu); gpr_mu_unlock(&w->mu);
if (delete) {
delete_state_watcher(exec_ctx, w);
}
GRPC_ERROR_UNREF(error); GRPC_ERROR_UNREF(error);
} }
@ -179,6 +183,28 @@ static void timeout_complete(grpc_exec_ctx *exec_ctx, void *pw,
partly_done(exec_ctx, pw, false, GRPC_ERROR_REF(error)); partly_done(exec_ctx, pw, false, GRPC_ERROR_REF(error));
} }
int grpc_channel_num_external_connectivity_watchers(grpc_channel *channel) {
grpc_channel_element *client_channel_elem =
grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel));
return grpc_client_channel_num_external_connectivity_watchers(
client_channel_elem);
}
typedef struct watcher_timer_init_arg {
state_watcher *w;
gpr_timespec deadline;
} watcher_timer_init_arg;
static void watcher_timer_init(grpc_exec_ctx *exec_ctx, void *arg,
grpc_error *error_ignored) {
watcher_timer_init_arg *wa = (watcher_timer_init_arg *)arg;
grpc_timer_init(exec_ctx, &wa->w->alarm,
gpr_convert_clock_type(wa->deadline, GPR_CLOCK_MONOTONIC),
&wa->w->on_timeout, gpr_now(GPR_CLOCK_MONOTONIC));
gpr_free(wa);
}
void grpc_channel_watch_connectivity_state( void grpc_channel_watch_connectivity_state(
grpc_channel *channel, grpc_connectivity_state last_observed_state, grpc_channel *channel, grpc_connectivity_state last_observed_state,
gpr_timespec deadline, grpc_completion_queue *cq, void *tag) { gpr_timespec deadline, grpc_completion_queue *cq, void *tag) {
@ -208,16 +234,19 @@ void grpc_channel_watch_connectivity_state(
w->cq = cq; w->cq = cq;
w->tag = tag; w->tag = tag;
w->channel = channel; w->channel = channel;
w->error = NULL;
grpc_timer_init(&exec_ctx, &w->alarm, watcher_timer_init_arg *wa = gpr_malloc(sizeof(watcher_timer_init_arg));
gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC), wa->w = w;
&w->on_timeout, gpr_now(GPR_CLOCK_MONOTONIC)); wa->deadline = deadline;
grpc_closure_init(&w->watcher_timer_init, watcher_timer_init, wa,
grpc_schedule_on_exec_ctx);
if (client_channel_elem->filter == &grpc_client_channel_filter) { if (client_channel_elem->filter == &grpc_client_channel_filter) {
GRPC_CHANNEL_INTERNAL_REF(channel, "watch_channel_connectivity"); GRPC_CHANNEL_INTERNAL_REF(channel, "watch_channel_connectivity");
grpc_client_channel_watch_connectivity_state(&exec_ctx, client_channel_elem, grpc_client_channel_watch_connectivity_state(
grpc_cq_pollset(cq), &w->state, &exec_ctx, client_channel_elem, grpc_cq_pollset(cq), &w->state,
&w->on_complete); &w->on_complete, &w->watcher_timer_init);
} else { } else {
abort(); abort();
} }

@ -167,6 +167,8 @@ static void *method_parameters_create_from_json(const grpc_json *json) {
return value; return value;
} }
struct external_connectivity_watcher;
/************************************************************************* /*************************************************************************
* CHANNEL-WIDE FUNCTIONS * CHANNEL-WIDE FUNCTIONS
*/ */
@ -204,6 +206,11 @@ typedef struct client_channel_channel_data {
/** interested parties (owned) */ /** interested parties (owned) */
grpc_pollset_set *interested_parties; grpc_pollset_set *interested_parties;
/* external_connectivity_watcher_list head is guarded by its own mutex, since
* counts need to be grabbed immediately without polling on a cq */
gpr_mu external_connectivity_watcher_list_mu;
struct external_connectivity_watcher *external_connectivity_watcher_list_head;
/* the following properties are guarded by a mutex since API's require them /* the following properties are guarded by a mutex since API's require them
to be instantaneously available */ to be instantaneously available */
gpr_mu info_mu; gpr_mu info_mu;
@ -661,6 +668,12 @@ static grpc_error *cc_init_channel_elem(grpc_exec_ctx *exec_ctx,
// Initialize data members. // Initialize data members.
chand->combiner = grpc_combiner_create(); chand->combiner = grpc_combiner_create();
gpr_mu_init(&chand->info_mu); gpr_mu_init(&chand->info_mu);
gpr_mu_init(&chand->external_connectivity_watcher_list_mu);
gpr_mu_lock(&chand->external_connectivity_watcher_list_mu);
chand->external_connectivity_watcher_list_head = NULL;
gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu);
chand->owning_stack = args->channel_stack; chand->owning_stack = args->channel_stack;
grpc_closure_init(&chand->on_resolver_result_changed, grpc_closure_init(&chand->on_resolver_result_changed,
on_resolver_result_changed_locked, chand, on_resolver_result_changed_locked, chand,
@ -748,6 +761,7 @@ static void cc_destroy_channel_elem(grpc_exec_ctx *exec_ctx,
grpc_pollset_set_destroy(exec_ctx, chand->interested_parties); grpc_pollset_set_destroy(exec_ctx, chand->interested_parties);
GRPC_COMBINER_UNREF(exec_ctx, chand->combiner, "client_channel"); GRPC_COMBINER_UNREF(exec_ctx, chand->combiner, "client_channel");
gpr_mu_destroy(&chand->info_mu); gpr_mu_destroy(&chand->info_mu);
gpr_mu_destroy(&chand->external_connectivity_watcher_list_mu);
} }
/************************************************************************* /*************************************************************************
@ -1449,14 +1463,79 @@ grpc_connectivity_state grpc_client_channel_check_connectivity_state(
return out; return out;
} }
typedef struct { typedef struct external_connectivity_watcher {
channel_data *chand; channel_data *chand;
grpc_pollset *pollset; grpc_pollset *pollset;
grpc_closure *on_complete; grpc_closure *on_complete;
grpc_closure *watcher_timer_init;
grpc_connectivity_state *state; grpc_connectivity_state *state;
grpc_closure my_closure; grpc_closure my_closure;
struct external_connectivity_watcher *next;
} external_connectivity_watcher; } external_connectivity_watcher;
static external_connectivity_watcher *lookup_external_connectivity_watcher(
channel_data *chand, grpc_closure *on_complete) {
gpr_mu_lock(&chand->external_connectivity_watcher_list_mu);
external_connectivity_watcher *w =
chand->external_connectivity_watcher_list_head;
while (w != NULL && w->on_complete != on_complete) {
w = w->next;
}
gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu);
return w;
}
static void external_connectivity_watcher_list_append(
channel_data *chand, external_connectivity_watcher *w) {
GPR_ASSERT(!lookup_external_connectivity_watcher(chand, w->on_complete));
gpr_mu_lock(&w->chand->external_connectivity_watcher_list_mu);
GPR_ASSERT(!w->next);
w->next = chand->external_connectivity_watcher_list_head;
chand->external_connectivity_watcher_list_head = w;
gpr_mu_unlock(&w->chand->external_connectivity_watcher_list_mu);
}
static void external_connectivity_watcher_list_remove(
channel_data *chand, external_connectivity_watcher *too_remove) {
GPR_ASSERT(
lookup_external_connectivity_watcher(chand, too_remove->on_complete));
gpr_mu_lock(&chand->external_connectivity_watcher_list_mu);
if (too_remove == chand->external_connectivity_watcher_list_head) {
chand->external_connectivity_watcher_list_head = too_remove->next;
gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu);
return;
}
external_connectivity_watcher *w =
chand->external_connectivity_watcher_list_head;
while (w != NULL) {
if (w->next == too_remove) {
w->next = w->next->next;
gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu);
return;
}
w = w->next;
}
GPR_UNREACHABLE_CODE(return );
}
int grpc_client_channel_num_external_connectivity_watchers(
grpc_channel_element *elem) {
channel_data *chand = elem->channel_data;
int count = 0;
gpr_mu_lock(&chand->external_connectivity_watcher_list_mu);
external_connectivity_watcher *w =
chand->external_connectivity_watcher_list_head;
while (w != NULL) {
count++;
w = w->next;
}
gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu);
return count;
}
static void on_external_watch_complete(grpc_exec_ctx *exec_ctx, void *arg, static void on_external_watch_complete(grpc_exec_ctx *exec_ctx, void *arg,
grpc_error *error) { grpc_error *error) {
external_connectivity_watcher *w = arg; external_connectivity_watcher *w = arg;
@ -1465,6 +1544,7 @@ static void on_external_watch_complete(grpc_exec_ctx *exec_ctx, void *arg,
w->pollset); w->pollset);
GRPC_CHANNEL_STACK_UNREF(exec_ctx, w->chand->owning_stack, GRPC_CHANNEL_STACK_UNREF(exec_ctx, w->chand->owning_stack,
"external_connectivity_watcher"); "external_connectivity_watcher");
external_connectivity_watcher_list_remove(w->chand, w);
gpr_free(w); gpr_free(w);
grpc_closure_run(exec_ctx, follow_up, GRPC_ERROR_REF(error)); grpc_closure_run(exec_ctx, follow_up, GRPC_ERROR_REF(error));
} }
@ -1472,21 +1552,42 @@ static void on_external_watch_complete(grpc_exec_ctx *exec_ctx, void *arg,
static void watch_connectivity_state_locked(grpc_exec_ctx *exec_ctx, void *arg, static void watch_connectivity_state_locked(grpc_exec_ctx *exec_ctx, void *arg,
grpc_error *error_ignored) { grpc_error *error_ignored) {
external_connectivity_watcher *w = arg; external_connectivity_watcher *w = arg;
external_connectivity_watcher *found = NULL;
if (w->state != NULL) {
external_connectivity_watcher_list_append(w->chand, w);
grpc_closure_run(exec_ctx, w->watcher_timer_init, GRPC_ERROR_NONE);
grpc_closure_init(&w->my_closure, on_external_watch_complete, w, grpc_closure_init(&w->my_closure, on_external_watch_complete, w,
grpc_schedule_on_exec_ctx); grpc_schedule_on_exec_ctx);
grpc_connectivity_state_notify_on_state_change( grpc_connectivity_state_notify_on_state_change(
exec_ctx, &w->chand->state_tracker, w->state, &w->my_closure); exec_ctx, &w->chand->state_tracker, w->state, &w->my_closure);
} else {
GPR_ASSERT(w->watcher_timer_init == NULL);
found = lookup_external_connectivity_watcher(w->chand, w->on_complete);
if (found) {
GPR_ASSERT(found->on_complete == w->on_complete);
grpc_connectivity_state_notify_on_state_change(
exec_ctx, &found->chand->state_tracker, NULL, &found->my_closure);
}
grpc_pollset_set_del_pollset(exec_ctx, w->chand->interested_parties,
w->pollset);
GRPC_CHANNEL_STACK_UNREF(exec_ctx, w->chand->owning_stack,
"external_connectivity_watcher");
gpr_free(w);
}
} }
void grpc_client_channel_watch_connectivity_state( void grpc_client_channel_watch_connectivity_state(
grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, grpc_pollset *pollset, grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, grpc_pollset *pollset,
grpc_connectivity_state *state, grpc_closure *closure) { grpc_connectivity_state *state, grpc_closure *closure,
grpc_closure *watcher_timer_init) {
channel_data *chand = elem->channel_data; channel_data *chand = elem->channel_data;
external_connectivity_watcher *w = gpr_malloc(sizeof(*w)); external_connectivity_watcher *w = gpr_zalloc(sizeof(*w));
w->chand = chand; w->chand = chand;
w->pollset = pollset; w->pollset = pollset;
w->on_complete = closure; w->on_complete = closure;
w->state = state; w->state = state;
w->watcher_timer_init = watcher_timer_init;
grpc_pollset_set_add_pollset(exec_ctx, chand->interested_parties, pollset); grpc_pollset_set_add_pollset(exec_ctx, chand->interested_parties, pollset);
GRPC_CHANNEL_STACK_REF(w->chand->owning_stack, GRPC_CHANNEL_STACK_REF(w->chand->owning_stack,
"external_connectivity_watcher"); "external_connectivity_watcher");

@ -53,9 +53,13 @@ extern const grpc_channel_filter grpc_client_channel_filter;
grpc_connectivity_state grpc_client_channel_check_connectivity_state( grpc_connectivity_state grpc_client_channel_check_connectivity_state(
grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect); grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect);
int grpc_client_channel_num_external_connectivity_watchers(
grpc_channel_element *elem);
void grpc_client_channel_watch_connectivity_state( void grpc_client_channel_watch_connectivity_state(
grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, grpc_pollset *pollset, grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, grpc_pollset *pollset,
grpc_connectivity_state *state, grpc_closure *on_complete); grpc_connectivity_state *state, grpc_closure *on_complete,
grpc_closure *watcher_timer_init);
/* Debug helper: pull the subchannel call from a call stack element */ /* Debug helper: pull the subchannel call from a call stack element */
grpc_subchannel_call *grpc_client_channel_get_subchannel_call( grpc_subchannel_call *grpc_client_channel_get_subchannel_call(

@ -37,5 +37,5 @@
#include <grpc++/grpc++.h> #include <grpc++/grpc++.h>
namespace grpc { namespace grpc {
grpc::string Version() { return "1.4.0-dev"; } grpc::string Version() { return "1.5.0-dev"; }
} }

@ -172,8 +172,15 @@ ServerBuilder& ServerBuilder::SetResourceQuota(
} }
ServerBuilder& ServerBuilder::AddListeningPort( ServerBuilder& ServerBuilder::AddListeningPort(
const grpc::string& addr, std::shared_ptr<ServerCredentials> creds, const grpc::string& addr_uri, std::shared_ptr<ServerCredentials> creds,
int* selected_port) { int* selected_port) {
const grpc::string uri_scheme = "dns:";
grpc::string addr = addr_uri;
if (addr_uri.compare(0, uri_scheme.size(), uri_scheme) == 0) {
size_t pos = uri_scheme.size();
while (addr_uri[pos] == '/') ++pos; // Skip slashes.
addr = addr_uri.substr(pos);
}
Port port = {addr, creds, selected_port}; Port port = {addr, creds, selected_port};
ports_.push_back(port); ports_.push_back(port);
return *this; return *this;

@ -16,6 +16,8 @@
<PackageProjectUrl>https://github.com/grpc/grpc</PackageProjectUrl> <PackageProjectUrl>https://github.com/grpc/grpc</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</PackageLicenseUrl>
<NetStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard1.5' ">1.6.0</NetStandardImplicitPackageVersion> <NetStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard1.5' ">1.6.0</NetStandardImplicitPackageVersion>
<IncludeSymbols>true</IncludeSymbols>
<IncludeSource>true</IncludeSource>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -23,7 +25,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="../Grpc.Core/Grpc.Core.csproj" /> <ProjectReference Include="../Grpc.Core/Grpc.Core.csproj">
<PrivateAssets>None</PrivateAssets>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\Grpc.Core\Version.csproj.include" /> <Import Project="..\Grpc.Core\Version.csproj.include" />
<Import Project="..\Grpc.Core\Common.csproj.include" /> <Import Project="..\Grpc.Core\Common.csproj.include" />
@ -16,6 +16,8 @@
<PackageProjectUrl>https://github.com/grpc/grpc</PackageProjectUrl> <PackageProjectUrl>https://github.com/grpc/grpc</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</PackageLicenseUrl>
<NetStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard1.5' ">1.6.0</NetStandardImplicitPackageVersion> <NetStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard1.5' ">1.6.0</NetStandardImplicitPackageVersion>
<IncludeSymbols>true</IncludeSymbols>
<IncludeSource>true</IncludeSource>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -23,7 +25,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="../Grpc.Core/Grpc.Core.csproj" /> <ProjectReference Include="../Grpc.Core/Grpc.Core.csproj">
<PrivateAssets>None</PrivateAssets>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' "> <ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">

@ -59,6 +59,8 @@ namespace Grpc.Core
readonly ChannelSafeHandle handle; readonly ChannelSafeHandle handle;
readonly Dictionary<string, ChannelOption> options; readonly Dictionary<string, ChannelOption> options;
readonly Task connectivityWatcherTask;
bool shutdownRequested; bool shutdownRequested;
/// <summary> /// <summary>
@ -99,6 +101,9 @@ namespace Grpc.Core
this.handle = ChannelSafeHandle.CreateInsecure(target, nativeChannelArgs); this.handle = ChannelSafeHandle.CreateInsecure(target, nativeChannelArgs);
} }
} }
// TODO(jtattermusch): Workaround for https://github.com/GoogleCloudPlatform/google-cloud-dotnet/issues/822.
// Remove once retries are supported in C core
this.connectivityWatcherTask = RunConnectivityWatcherAsync();
GrpcEnvironment.RegisterChannel(this); GrpcEnvironment.RegisterChannel(this);
} }
@ -244,7 +249,7 @@ namespace Grpc.Core
handle.Dispose(); handle.Dispose();
await GrpcEnvironment.ReleaseAsync().ConfigureAwait(false); await Task.WhenAll(GrpcEnvironment.ReleaseAsync(), connectivityWatcherTask).ConfigureAwait(false);
} }
internal ChannelSafeHandle Handle internal ChannelSafeHandle Handle
@ -299,6 +304,40 @@ namespace Grpc.Core
} }
} }
/// <summary>
/// Constantly Watches channel connectivity status to work around https://github.com/GoogleCloudPlatform/google-cloud-dotnet/issues/822
/// </summary>
private async Task RunConnectivityWatcherAsync()
{
try
{
var lastState = State;
while (lastState != ChannelState.Shutdown)
{
lock (myLock)
{
if (shutdownRequested)
{
break;
}
}
try
{
await WaitForStateChangedAsync(lastState, DateTime.UtcNow.AddSeconds(1)).ConfigureAwait(false);
}
catch (TaskCanceledException)
{
// ignore timeout
}
lastState = State;
}
}
catch (ObjectDisposedException) {
// during shutdown, channel is going to be disposed.
}
}
private static void EnsureUserAgentChannelOption(Dictionary<string, ChannelOption> options) private static void EnsureUserAgentChannelOption(Dictionary<string, ChannelOption> options)
{ {
var key = ChannelOptions.PrimaryUserAgentString; var key = ChannelOptions.PrimaryUserAgentString;

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<Import Project="Version.csproj.include" /> <Import Project="Version.csproj.include" />
<Import Project="Common.csproj.include" /> <Import Project="Common.csproj.include" />
@ -15,6 +15,8 @@
<PackageProjectUrl>https://github.com/grpc/grpc</PackageProjectUrl> <PackageProjectUrl>https://github.com/grpc/grpc</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</PackageLicenseUrl>
<NetStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard1.5' ">1.6.0</NetStandardImplicitPackageVersion> <NetStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard1.5' ">1.6.0</NetStandardImplicitPackageVersion>
<IncludeSymbols>true</IncludeSymbols>
<IncludeSource>true</IncludeSource>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

@ -1,7 +1,7 @@
<!-- This file is generated --> <!-- This file is generated -->
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<GrpcCsharpVersion>1.4.0-dev</GrpcCsharpVersion> <GrpcCsharpVersion>1.5.0-dev</GrpcCsharpVersion>
<GoogleProtobufVersion>3.3.0</GoogleProtobufVersion> <GoogleProtobufVersion>3.3.0</GoogleProtobufVersion>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

@ -48,11 +48,11 @@ namespace Grpc.Core
/// <summary> /// <summary>
/// Current <c>AssemblyFileVersion</c> of gRPC C# assemblies /// Current <c>AssemblyFileVersion</c> of gRPC C# assemblies
/// </summary> /// </summary>
public const string CurrentAssemblyFileVersion = "1.4.0.0"; public const string CurrentAssemblyFileVersion = "1.5.0.0";
/// <summary> /// <summary>
/// Current version of gRPC C# /// Current version of gRPC C#
/// </summary> /// </summary>
public const string CurrentVersion = "1.4.0-dev"; public const string CurrentVersion = "1.5.0-dev";
} }
} }

@ -15,6 +15,8 @@
<PackageProjectUrl>https://github.com/grpc/grpc</PackageProjectUrl> <PackageProjectUrl>https://github.com/grpc/grpc</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</PackageLicenseUrl>
<NetStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard1.5' ">1.6.0</NetStandardImplicitPackageVersion> <NetStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard1.5' ">1.6.0</NetStandardImplicitPackageVersion>
<IncludeSymbols>true</IncludeSymbols>
<IncludeSource>true</IncludeSource>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -22,7 +24,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="../Grpc.Core/Grpc.Core.csproj" /> <ProjectReference Include="../Grpc.Core/Grpc.Core.csproj">
<PrivateAssets>None</PrivateAssets>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

@ -15,6 +15,8 @@
<PackageProjectUrl>https://github.com/grpc/grpc</PackageProjectUrl> <PackageProjectUrl>https://github.com/grpc/grpc</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</PackageLicenseUrl>
<NetStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard1.5' ">1.6.0</NetStandardImplicitPackageVersion> <NetStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard1.5' ">1.6.0</NetStandardImplicitPackageVersion>
<IncludeSymbols>true</IncludeSymbols>
<IncludeSource>true</IncludeSource>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -22,7 +24,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="../Grpc.Core/Grpc.Core.csproj" /> <ProjectReference Include="../Grpc.Core/Grpc.Core.csproj">
<PrivateAssets>None</PrivateAssets>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

@ -28,7 +28,7 @@
@rem OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. @rem OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@rem Current package versions @rem Current package versions
set VERSION=1.4.0-dev set VERSION=1.5.0-dev
@rem Adjust the location of nuget.exe @rem Adjust the location of nuget.exe
set NUGET=C:\nuget\nuget.exe set NUGET=C:\nuget\nuget.exe
@ -51,11 +51,11 @@ powershell -Command "cp -r ..\..\platform=*\artifacts\protoc_* protoc_plugins"
@rem To be able to build, we also need to put grpc_csharp_ext to its normal location @rem To be able to build, we also need to put grpc_csharp_ext to its normal location
xcopy /Y /I nativelibs\csharp_ext_windows_x64\grpc_csharp_ext.dll ..\..\cmake\build\x64\Release\ xcopy /Y /I nativelibs\csharp_ext_windows_x64\grpc_csharp_ext.dll ..\..\cmake\build\x64\Release\
%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.Core --output ..\..\..\artifacts || goto :error %DOTNET% pack --configuration Release Grpc.Core --output ..\..\..\artifacts || goto :error
%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.Core.Testing --output ..\..\..\artifacts || goto :error %DOTNET% pack --configuration Release Grpc.Core.Testing --output ..\..\..\artifacts || goto :error
%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.Auth --output ..\..\..\artifacts || goto :error %DOTNET% pack --configuration Release Grpc.Auth --output ..\..\..\artifacts || goto :error
%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.HealthCheck --output ..\..\..\artifacts || goto :error %DOTNET% pack --configuration Release Grpc.HealthCheck --output ..\..\..\artifacts || goto :error
%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.Reflection --output ..\..\..\artifacts || goto :error %DOTNET% pack --configuration Release Grpc.Reflection --output ..\..\..\artifacts || goto :error
%NUGET% pack Grpc.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts || goto :error %NUGET% pack Grpc.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts || goto :error
%NUGET% pack Grpc.Tools.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts %NUGET% pack Grpc.Tools.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts

@ -48,13 +48,13 @@ dotnet restore Grpc.sln
mkdir -p ../../libs/opt mkdir -p ../../libs/opt
cp nativelibs/csharp_ext_linux_x64/libgrpc_csharp_ext.so ../../libs/opt cp nativelibs/csharp_ext_linux_x64/libgrpc_csharp_ext.so ../../libs/opt
dotnet pack --configuration Release --include-symbols --include-source Grpc.Core --output ../../../artifacts dotnet pack --configuration Release Grpc.Core --output ../../../artifacts
dotnet pack --configuration Release --include-symbols --include-source Grpc.Core.Testing --output ../../../artifacts dotnet pack --configuration Release Grpc.Core.Testing --output ../../../artifacts
dotnet pack --configuration Release --include-symbols --include-source Grpc.Auth --output ../../../artifacts dotnet pack --configuration Release Grpc.Auth --output ../../../artifacts
dotnet pack --configuration Release --include-symbols --include-source Grpc.HealthCheck --output ../../../artifacts dotnet pack --configuration Release Grpc.HealthCheck --output ../../../artifacts
dotnet pack --configuration Release --include-symbols --include-source Grpc.Reflection --output ../../../artifacts dotnet pack --configuration Release Grpc.Reflection --output ../../../artifacts
nuget pack Grpc.nuspec -Version "1.4.0-dev" -OutputDirectory ../../artifacts nuget pack Grpc.nuspec -Version "1.5.0-dev" -OutputDirectory ../../artifacts
nuget pack Grpc.Tools.nuspec -Version "1.4.0-dev" -OutputDirectory ../../artifacts nuget pack Grpc.Tools.nuspec -Version "1.5.0-dev" -OutputDirectory ../../artifacts
(cd ../../artifacts && zip csharp_nugets_dotnetcli.zip *.nupkg) (cd ../../artifacts && zip csharp_nugets_dotnetcli.zip *.nupkg)

@ -508,9 +508,14 @@ void Call::DestroyCall() {
} }
Call::Call(grpc_call *call) Call::Call(grpc_call *call)
: wrapped_call(call), pending_batches(0), has_final_op_completed(false) {} : wrapped_call(call), pending_batches(0), has_final_op_completed(false) {
peer = grpc_call_get_peer(call);
}
Call::~Call() { DestroyCall(); } Call::~Call() {
DestroyCall();
gpr_free(peer);
}
void Call::Init(Local<Object> exports) { void Call::Init(Local<Object> exports) {
HandleScope scope; HandleScope scope;
@ -662,6 +667,16 @@ NAN_METHOD(Call::StartBatch) {
} }
Local<Function> callback_func = info[1].As<Function>(); Local<Function> callback_func = info[1].As<Function>();
Call *call = ObjectWrap::Unwrap<Call>(info.This()); Call *call = ObjectWrap::Unwrap<Call>(info.This());
if (call->wrapped_call == NULL) {
/* This implies that the call has completed and has been destroyed. To
* emulate
* previous behavior, we should call the callback immediately with an error,
* as though the batch had failed in core */
Local<Value> argv[] = {
Nan::Error("The async function failed because the call has completed")};
Nan::Call(callback_func, Nan::New<Object>(), 1, argv);
return;
}
Local<Object> obj = Nan::To<Object>(info[0]).ToLocalChecked(); Local<Object> obj = Nan::To<Object>(info[0]).ToLocalChecked();
Local<Array> keys = Nan::GetOwnPropertyNames(obj).ToLocalChecked(); Local<Array> keys = Nan::GetOwnPropertyNames(obj).ToLocalChecked();
size_t nops = keys->Length(); size_t nops = keys->Length();
@ -727,6 +742,11 @@ NAN_METHOD(Call::Cancel) {
return Nan::ThrowTypeError("cancel can only be called on Call objects"); return Nan::ThrowTypeError("cancel can only be called on Call objects");
} }
Call *call = ObjectWrap::Unwrap<Call>(info.This()); Call *call = ObjectWrap::Unwrap<Call>(info.This());
if (call->wrapped_call == NULL) {
/* Cancel is supposed to be idempotent. If the call has already finished,
* cancel should just complete silently */
return;
}
grpc_call_error error = grpc_call_cancel(call->wrapped_call, NULL); grpc_call_error error = grpc_call_cancel(call->wrapped_call, NULL);
if (error != GRPC_CALL_OK) { if (error != GRPC_CALL_OK) {
return Nan::ThrowError(nanErrorWithCode("cancel failed", error)); return Nan::ThrowError(nanErrorWithCode("cancel failed", error));
@ -747,6 +767,11 @@ NAN_METHOD(Call::CancelWithStatus) {
"cancelWithStatus's second argument must be a string"); "cancelWithStatus's second argument must be a string");
} }
Call *call = ObjectWrap::Unwrap<Call>(info.This()); Call *call = ObjectWrap::Unwrap<Call>(info.This());
if (call->wrapped_call == NULL) {
/* Cancel is supposed to be idempotent. If the call has already finished,
* cancel should just complete silently */
return;
}
grpc_status_code code = grpc_status_code code =
static_cast<grpc_status_code>(Nan::To<uint32_t>(info[0]).FromJust()); static_cast<grpc_status_code>(Nan::To<uint32_t>(info[0]).FromJust());
if (code == GRPC_STATUS_OK) { if (code == GRPC_STATUS_OK) {
@ -763,9 +788,7 @@ NAN_METHOD(Call::GetPeer) {
return Nan::ThrowTypeError("getPeer can only be called on Call objects"); return Nan::ThrowTypeError("getPeer can only be called on Call objects");
} }
Call *call = ObjectWrap::Unwrap<Call>(info.This()); Call *call = ObjectWrap::Unwrap<Call>(info.This());
char *peer = grpc_call_get_peer(call->wrapped_call); Local<Value> peer_value = Nan::New(call->peer).ToLocalChecked();
Local<Value> peer_value = Nan::New(peer).ToLocalChecked();
gpr_free(peer);
info.GetReturnValue().Set(peer_value); info.GetReturnValue().Set(peer_value);
} }
@ -780,6 +803,10 @@ NAN_METHOD(Call::SetCredentials) {
"setCredentials' first argument must be a CallCredentials"); "setCredentials' first argument must be a CallCredentials");
} }
Call *call = ObjectWrap::Unwrap<Call>(info.This()); Call *call = ObjectWrap::Unwrap<Call>(info.This());
if (call->wrapped_call == NULL) {
return Nan::ThrowError(
"Cannot set credentials on a call that has already started");
}
CallCredentials *creds_object = ObjectWrap::Unwrap<CallCredentials>( CallCredentials *creds_object = ObjectWrap::Unwrap<CallCredentials>(
Nan::To<Object>(info[0]).ToLocalChecked()); Nan::To<Object>(info[0]).ToLocalChecked());
grpc_call_credentials *creds = creds_object->GetWrappedCredentials(); grpc_call_credentials *creds = creds_object->GetWrappedCredentials();

@ -96,6 +96,7 @@ class Call : public Nan::ObjectWrap {
call, this is GRPC_OP_RECV_STATUS_ON_CLIENT and for a server call, this call, this is GRPC_OP_RECV_STATUS_ON_CLIENT and for a server call, this
is GRPC_OP_SEND_STATUS_FROM_SERVER */ is GRPC_OP_SEND_STATUS_FROM_SERVER */
bool has_final_op_completed; bool has_final_op_completed;
char *peer;
}; };
class Op { class Op {

@ -1,6 +1,6 @@
{ {
"name": "grpc-health-check", "name": "grpc-health-check",
"version": "1.4.0-dev", "version": "1.5.0-dev",
"author": "Google Inc.", "author": "Google Inc.",
"description": "Health check service for use with gRPC", "description": "Health check service for use with gRPC",
"repository": { "repository": {
@ -15,7 +15,7 @@
} }
], ],
"dependencies": { "dependencies": {
"grpc": "^1.4.0-dev", "grpc": "^1.5.0-dev",
"lodash": "^3.9.3", "lodash": "^3.9.3",
"google-protobuf": "^3.0.0" "google-protobuf": "^3.0.0"
}, },

@ -227,19 +227,23 @@ BenchmarkClient.prototype.startClosedLoop = function(
makeCall = function(client) { makeCall = function(client) {
if (self.running) { if (self.running) {
self.pending_calls++; self.pending_calls++;
var start_time = process.hrtime();
var call = client.streamingCall(); var call = client.streamingCall();
var start_time = process.hrtime();
call.write(argument); call.write(argument);
call.on('data', function() { call.on('data', function() {
});
call.on('end', function() {
var time_diff = process.hrtime(start_time); var time_diff = process.hrtime(start_time);
self.histogram.add(timeDiffToNanos(time_diff)); self.histogram.add(timeDiffToNanos(time_diff));
makeCall(client);
self.pending_calls--; self.pending_calls--;
if ((!self.running) && self.pending_calls == 0) { if (self.running) {
self.pending_calls++;
start_time = process.hrtime();
call.write(argument);
} else {
call.end();
if (self.pending_calls == 0) {
self.emit('finished'); self.emit('finished');
} }
}
}); });
call.on('error', function(error) { call.on('error', function(error) {
self.emit('error', new Error('Client error: ' + error.message)); self.emit('error', new Error('Client error: ' + error.message));
@ -317,30 +321,8 @@ BenchmarkClient.prototype.startPoisson = function(
} }
}; };
} else { } else {
makeCall = function(client, poisson) { self.emit('error', new Error('Streaming Poisson benchmarks not supported'));
if (self.running) { return;
self.pending_calls++;
var start_time = process.hrtime();
var call = client.streamingCall();
call.write(argument);
call.on('data', function() {
});
call.on('end', function() {
var time_diff = process.hrtime(start_time);
self.histogram.add(timeDiffToNanos(time_diff));
self.pending_calls--;
if ((!self.running) && self.pending_calls == 0) {
self.emit('finished');
}
});
call.on('error', function(error) {
self.emit('error', new Error('Client error: ' + error.message));
self.running = false;
});
} else {
poisson.stop();
}
};
} }
var averageIntervalMs = (1 / offered_load) * 1000; var averageIntervalMs = (1 / offered_load) * 1000;

@ -147,6 +147,12 @@ function _write(chunk, encoding, callback) {
/* jshint validthis: true */ /* jshint validthis: true */
var batch = {}; var batch = {};
var message; var message;
var self = this;
if (this.writeFailed) {
/* Once a write fails, just call the callback immediately to let the caller
flush any pending writes. */
setImmediate(callback);
}
try { try {
message = this.serialize(chunk); message = this.serialize(chunk);
} catch (e) { } catch (e) {
@ -167,8 +173,10 @@ function _write(chunk, encoding, callback) {
batch[grpc.opType.SEND_MESSAGE] = message; batch[grpc.opType.SEND_MESSAGE] = message;
this.call.startBatch(batch, function(err, event) { this.call.startBatch(batch, function(err, event) {
if (err) { if (err) {
// Something has gone wrong. Stop writing by failing to call callback /* Assume that the call is complete and that writing failed because a
return; status was received. In that case, set a flag to discard all future
writes */
self.writeFailed = true;
} }
callback(); callback();
}); });

@ -100,7 +100,6 @@ describe('Proto message long int serialize and deserialize', function() {
var longNumDeserialize = deserializeCls(messages_proto.LongValues, var longNumDeserialize = deserializeCls(messages_proto.LongValues,
num_options); num_options);
var serialized = longSerialize({int_64: pos_value}); var serialized = longSerialize({int_64: pos_value});
console.log(longDeserialize(serialized));
assert.strictEqual(typeof longDeserialize(serialized).int_64, 'string'); assert.strictEqual(typeof longDeserialize(serialized).int_64, 'string');
/* With the longsAsStrings option disabled, long values are represented as /* With the longsAsStrings option disabled, long values are represented as
* objects with 3 keys: low, high, and unsigned */ * objects with 3 keys: low, high, and unsigned */

@ -1110,6 +1110,18 @@ describe('Other conditions', function() {
done(); done();
}); });
}); });
it('after the call has fully completed', function(done) {
var peer;
var call = client.unary({error: false}, function(err, data) {
assert.ifError(err);
setImmediate(function() {
assert.strictEqual(peer, call.getPeer());
done();
});
});
peer = call.getPeer();
assert.strictEqual(typeof peer, 'string');
});
}); });
}); });
describe('Call propagation', function() { describe('Call propagation', function() {
@ -1352,4 +1364,17 @@ describe('Cancelling surface client', function() {
}); });
call.cancel(); call.cancel();
}); });
it('Should be idempotent', function(done) {
var call = client.div({'divisor': 0, 'dividend': 0}, function(err, resp) {
assert.strictEqual(err.code, grpc.status.CANCELLED);
// Call asynchronously to try cancelling after call is fully completed
setImmediate(function() {
assert.doesNotThrow(function() {
call.cancel();
});
done();
});
});
call.cancel();
});
}); });

@ -1,6 +1,6 @@
{ {
"name": "grpc-tools", "name": "grpc-tools",
"version": "1.4.0-dev", "version": "1.5.0-dev",
"author": "Google Inc.", "author": "Google Inc.",
"description": "Tools for developing with gRPC on Node.js", "description": "Tools for developing with gRPC on Node.js",
"homepage": "http://www.grpc.io/", "homepage": "http://www.grpc.io/",

@ -42,7 +42,7 @@ Pod::Spec.new do |s|
# exclamation mark ensures that other "regular" pods will be able to find it as it'll be installed # exclamation mark ensures that other "regular" pods will be able to find it as it'll be installed
# before them. # before them.
s.name = '!ProtoCompiler-gRPCPlugin' s.name = '!ProtoCompiler-gRPCPlugin'
v = '1.4.0-dev' v = '1.5.0-dev'
s.version = v s.version = v
s.summary = 'The gRPC ProtoC plugin generates Objective-C files from .proto services.' s.summary = 'The gRPC ProtoC plugin generates Objective-C files from .proto services.'
s.description = <<-DESC s.description = <<-DESC

@ -38,4 +38,4 @@
// `tools/buildgen/generate_projects.sh`. // `tools/buildgen/generate_projects.sh`.
#define GRPC_OBJC_VERSION_STRING @"1.4.0-dev" #define GRPC_OBJC_VERSION_STRING @"1.5.0-dev"

@ -2,7 +2,7 @@
"name": "grpc/grpc-dev", "name": "grpc/grpc-dev",
"description": "gRPC library for PHP - for Developement use only", "description": "gRPC library for PHP - for Developement use only",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"version": "1.4.0", "version": "1.5.0",
"require": { "require": {
"php": ">=5.5.0", "php": ">=5.5.0",
"google/protobuf": "^v3.3.0" "google/protobuf": "^v3.3.0"

@ -35,6 +35,6 @@
#ifndef VERSION_H #ifndef VERSION_H
#define VERSION_H #define VERSION_H
#define PHP_GRPC_VERSION "1.4.0" #define PHP_GRPC_VERSION "1.5.0"
#endif /* VERSION_H */ #endif /* VERSION_H */

@ -786,7 +786,7 @@ def _channel_managed_call_management(state):
class _ChannelConnectivityState(object): class _ChannelConnectivityState(object):
def __init__(self, channel): def __init__(self, channel):
self.lock = threading.Lock() self.lock = threading.RLock()
self.channel = channel self.channel = channel
self.polling = False self.polling = False
self.connectivity = None self.connectivity = None
@ -926,6 +926,11 @@ class Channel(grpc.Channel):
self._call_state = _ChannelCallState(self._channel) self._call_state = _ChannelCallState(self._channel)
self._connectivity_state = _ChannelConnectivityState(self._channel) self._connectivity_state = _ChannelConnectivityState(self._channel)
# TODO(https://github.com/grpc/grpc/issues/9884)
# Temporary work around UNAVAILABLE issues
# Remove this once c-core has retry support
_subscribe(self._connectivity_state, lambda *args: None, None)
def subscribe(self, callback, try_to_connect=None): def subscribe(self, callback, try_to_connect=None):
_subscribe(self._connectivity_state, callback, try_to_connect) _subscribe(self._connectivity_state, callback, try_to_connect)

@ -29,4 +29,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc/_grpcio_metadata.py.template`!!! # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc/_grpcio_metadata.py.template`!!!
__version__ = """1.4.0.dev0""" __version__ = """1.5.0.dev0"""

@ -29,4 +29,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc_version.py.template`!!! # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc_version.py.template`!!!
VERSION='1.4.0.dev0' VERSION='1.5.0.dev0'

@ -29,4 +29,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_health_checking/grpc_version.py.template`!!! # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_health_checking/grpc_version.py.template`!!!
VERSION='1.4.0.dev0' VERSION='1.5.0.dev0'

@ -28,8 +28,6 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Reference implementation for reflection in gRPC Python.""" """Reference implementation for reflection in gRPC Python."""
import threading
import grpc import grpc
from google.protobuf import descriptor_pb2 from google.protobuf import descriptor_pb2
from google.protobuf import descriptor_pool from google.protobuf import descriptor_pool
@ -120,6 +118,7 @@ class ReflectionServicer(reflection_pb2_grpc.ServerReflectionServicer):
])) ]))
def ServerReflectionInfo(self, request_iterator, context): def ServerReflectionInfo(self, request_iterator, context):
# pylint: disable=unused-argument
for request in request_iterator: for request in request_iterator:
if request.HasField('file_by_filename'): if request.HasField('file_by_filename'):
yield self._file_by_filename(request.file_by_filename) yield self._file_by_filename(request.file_by_filename)
@ -152,4 +151,4 @@ def enable_server_reflection(service_names, server, pool=None):
pool: DescriptorPool object to use (descriptor_pool.Default() if None). pool: DescriptorPool object to use (descriptor_pool.Default() if None).
""" """
reflection_pb2_grpc.add_ServerReflectionServicer_to_server( reflection_pb2_grpc.add_ServerReflectionServicer_to_server(
ReflectionServicer(service_names, pool), server) ReflectionServicer(service_names, pool=pool), server)

@ -29,4 +29,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_reflection/grpc_version.py.template`!!! # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_reflection/grpc_version.py.template`!!!
VERSION='1.4.0.dev0' VERSION='1.5.0.dev0'

@ -29,4 +29,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_tests/grpc_version.py.template`!!! # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_tests/grpc_version.py.template`!!!
VERSION='1.4.0.dev0' VERSION='1.5.0.dev0'

@ -32,6 +32,7 @@
"unit._invocation_defects_test.InvocationDefectsTest", "unit._invocation_defects_test.InvocationDefectsTest",
"unit._metadata_code_details_test.MetadataCodeDetailsTest", "unit._metadata_code_details_test.MetadataCodeDetailsTest",
"unit._metadata_test.MetadataTest", "unit._metadata_test.MetadataTest",
"unit._reconnect_test.ReconnectTest",
"unit._resource_exhausted_test.ResourceExhaustedTest", "unit._resource_exhausted_test.ResourceExhaustedTest",
"unit._rpc_test.RPCTest", "unit._rpc_test.RPCTest",
"unit._sanity._sanity_test.Sanity", "unit._sanity._sanity_test.Sanity",

@ -0,0 +1,70 @@
# Copyright 2017, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Tests that a channel will reconnect if a connection is dropped"""
import unittest
import grpc
from grpc.framework.foundation import logging_pool
from tests.unit.framework.common import test_constants
_REQUEST = b'\x00\x00\x00'
_RESPONSE = b'\x00\x00\x01'
_UNARY_UNARY = '/test/UnaryUnary'
def _handle_unary_unary(unused_request, unused_servicer_context):
return _RESPONSE
class ReconnectTest(unittest.TestCase):
def test_reconnect(self):
server_pool = logging_pool.pool(test_constants.THREAD_CONCURRENCY)
handler = grpc.method_handlers_generic_handler('test', {
'UnaryUnary':
grpc.unary_unary_rpc_method_handler(_handle_unary_unary)
})
server = grpc.server(server_pool, (handler,))
port = server.add_insecure_port('[::]:0')
server.start()
channel = grpc.insecure_channel('localhost:%d' % port)
multi_callable = channel.unary_unary(_UNARY_UNARY)
self.assertEqual(_RESPONSE, multi_callable(_REQUEST))
server.stop(None)
server = grpc.server(server_pool, (handler,))
server.add_insecure_port('[::]:{}'.format(port))
server.start()
self.assertEqual(_RESPONSE, multi_callable(_REQUEST))
if __name__ == '__main__':
unittest.main(verbosity=2)

@ -61,6 +61,11 @@ def main
'channel is closed while connectivity is watched' 'channel is closed while connectivity is watched'
end end
client_exit_code = $CHILD_STATUS
if client_exit_code != 0
fail "channel closing client failed, exit code #{client_exit_code}"
end
server_runner.stop server_runner.stop
end end

@ -58,6 +58,9 @@ def main
'It likely hangs when ended abruptly' 'It likely hangs when ended abruptly'
end end
# The interrupt in the child process should cause it to
# exit a non-zero status, so don't check it here.
# This test mainly tries to catch deadlock.
server_runner.stop server_runner.stop
end end

@ -34,44 +34,110 @@
require_relative './end2end_common' require_relative './end2end_common'
def main def construct_many(test_proc)
grpc_class = '' thds = []
OptionParser.new do |opts| 4.times do
opts.on('--grpc_class=P', String) do |p| thds << Thread.new do
grpc_class = p 20.times do
test_proc.call
end end
end.parse! end
end
20.times do
test_proc.call
end
thds.each(&:join)
end
def run_gc_stress_test(test_proc)
GC.disable
construct_many(test_proc)
GC.enable
construct_many(test_proc)
GC.start(full_mark: true, immediate_sweep: true)
construct_many(test_proc)
end
def run_concurrency_stress_test(test_proc)
100.times do
Thread.new do
test_proc.call
end
end
test_proc.call
test_proc = nil fail 'exception thrown while child thread initing class'
end
# default (no gc_stress and no concurrency_stress)
def run_default_test(test_proc)
thd = Thread.new do
test_proc.call
end
test_proc.call
thd.join
end
def get_test_proc(grpc_class)
case grpc_class case grpc_class
when 'channel' when 'channel'
test_proc = proc do return proc do
GRPC::Core::Channel.new('dummy_host', nil, :this_channel_is_insecure) GRPC::Core::Channel.new('dummy_host', nil, :this_channel_is_insecure)
end end
when 'server' when 'server'
test_proc = proc do return proc do
GRPC::Core::Server.new({}) GRPC::Core::Server.new({})
end end
when 'channel_credentials' when 'channel_credentials'
test_proc = proc do return proc do
GRPC::Core::ChannelCredentials.new GRPC::Core::ChannelCredentials.new
end end
when 'call_credentials' when 'call_credentials'
test_proc = proc do return proc do
GRPC::Core::CallCredentials.new(proc { |noop| noop }) GRPC::Core::CallCredentials.new(proc { |noop| noop })
end end
when 'compression_options' when 'compression_options'
test_proc = proc do return proc do
GRPC::Core::CompressionOptions.new GRPC::Core::CompressionOptions.new
end end
else else
fail "bad --grpc_class=#{grpc_class} param" fail "bad --grpc_class=#{grpc_class} param"
end end
end
th = Thread.new { test_proc.call } def main
test_proc.call grpc_class = ''
th.join stress_test = ''
OptionParser.new do |opts|
opts.on('--grpc_class=P', String) do |p|
grpc_class = p
end
opts.on('--stress_test=P') do |p|
stress_test = p
end
end.parse!
test_proc = get_test_proc(grpc_class)
# the different test configs need to be ran
# in separate processes, since each one tests
# clean shutdown in a different way
case stress_test
when 'gc'
p 'run gc stress'
run_gc_stress_test(test_proc)
when 'concurrency'
p 'run concurrency stress'
run_concurrency_stress_test(test_proc)
when ''
p 'run default'
run_default_test(test_proc)
else
fail "bad --stress_test=#{stress_test} param"
end
end end
main main

@ -38,13 +38,18 @@ def main
call_credentials call_credentials
compression_options ) compression_options )
# there is room for false positives in this test,
# do a few runs for each config
4.times do
native_grpc_classes.each do |grpc_class| native_grpc_classes.each do |grpc_class|
['', 'gc', 'concurrency'].each do |stress_test_type|
STDERR.puts 'start client' STDERR.puts 'start client'
this_dir = File.expand_path(File.dirname(__FILE__)) this_dir = File.expand_path(File.dirname(__FILE__))
client_path = File.join(this_dir, 'grpc_class_init_client.rb') client_path = File.join(this_dir, 'grpc_class_init_client.rb')
client_pid = Process.spawn(RbConfig.ruby, client_pid = Process.spawn(RbConfig.ruby,
client_path, client_path,
"--grpc_class=#{grpc_class}") "--grpc_class=#{grpc_class}",
"--stress_test=#{stress_test_type}")
begin begin
Timeout.timeout(10) do Timeout.timeout(10) do
Process.wait(client_pid) Process.wait(client_pid)
@ -60,7 +65,13 @@ def main
end end
client_exit_code = $CHILD_STATUS client_exit_code = $CHILD_STATUS
fail "client failed, exit code #{client_exit_code}" if client_exit_code != 0 # concurrency stress test type is expected to exit with a
# non-zero status due to an exception being raised
if client_exit_code != 0 && stress_test_type != 'concurrency'
fail "client failed, exit code #{client_exit_code}"
end
end
end
end end
end end

@ -0,0 +1,63 @@
#!/usr/bin/env ruby
# Copyright 2016, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require_relative './end2end_common'
Thread.abort_on_exception = true
include GRPC::Core::ConnectivityStates
def watch_state(ch)
thd = Thread.new do
state = ch.connectivity_state(false)
fail "non-idle state: #{state}" unless state == IDLE
ch.watch_connectivity_state(IDLE, Time.now + 360)
end
sleep 0.1
thd.kill
end
def main
channels = []
10.times do
ch = GRPC::Core::Channel.new('dummy_host',
nil, :this_channel_is_insecure)
watch_state(ch)
channels << ch
end
# checking state should still be safe to call
channels.each do |c|
fail unless c.connectivity_state(false) == FATAL_FAILURE
end
end
main

@ -46,6 +46,8 @@ def main
end end
end.parse! end.parse!
trap('SIGINT') { exit 0 }
thd = Thread.new do thd = Thread.new do
child_thread_channel = GRPC::Core::Channel.new("localhost:#{server_port}", child_thread_channel = GRPC::Core::Channel.new("localhost:#{server_port}",
{}, {},

@ -63,6 +63,11 @@ def main
'SIGINT is sent while there is an active connectivity_state call' 'SIGINT is sent while there is an active connectivity_state call'
end end
client_exit_code = $CHILD_STATUS
if client_exit_code != 0
fail "sig_int_during_channel_watch_client failed: #{client_exit_code}"
end
server_runner.stop server_runner.stop
end end

@ -68,29 +68,53 @@ static VALUE grpc_rb_cChannel = Qnil;
/* Used during the conversion of a hash to channel args during channel setup */ /* Used during the conversion of a hash to channel args during channel setup */
static VALUE grpc_rb_cChannelArgs; static VALUE grpc_rb_cChannelArgs;
typedef struct bg_watched_channel {
grpc_channel *channel;
// these fields must only be accessed under global_connection_polling_mu
struct bg_watched_channel *next;
int channel_destroyed;
int refcount;
} bg_watched_channel;
/* grpc_rb_channel wraps a grpc_channel. */ /* grpc_rb_channel wraps a grpc_channel. */
typedef struct grpc_rb_channel { typedef struct grpc_rb_channel {
VALUE credentials; VALUE credentials;
/* The actual channel */ /* The actual channel (protected in a wrapper to tell when it's safe to
grpc_channel *wrapped; * destroy) */
int request_safe_destroy; bg_watched_channel *bg_wrapped;
int safe_to_destroy;
grpc_connectivity_state current_connectivity_state;
int mu_init_done;
int abort_watch_connectivity_state;
gpr_mu channel_mu;
gpr_cv channel_cv;
} grpc_rb_channel; } grpc_rb_channel;
/* Forward declarations of functions involved in temporary fix to typedef enum { CONTINUOUS_WATCH, WATCH_STATE_API } watch_state_op_type;
* https://github.com/grpc/grpc/issues/9941 */
typedef struct watch_state_op {
watch_state_op_type op_type;
// from event.success
union {
struct {
int success;
// has been called back due to a cq next call
int called_back;
} api_callback_args;
struct {
bg_watched_channel *bg;
} continuous_watch_callback_args;
} op;
} watch_state_op;
static bg_watched_channel *bg_watched_channel_list_head = NULL;
static void grpc_rb_channel_try_register_connection_polling( static void grpc_rb_channel_try_register_connection_polling(
grpc_rb_channel *wrapper); bg_watched_channel *bg);
static void grpc_rb_channel_safe_destroy(grpc_rb_channel *wrapper);
static void *wait_until_channel_polling_thread_started_no_gil(void *); static void *wait_until_channel_polling_thread_started_no_gil(void *);
static void wait_until_channel_polling_thread_started_unblocking_func(void *); static void wait_until_channel_polling_thread_started_unblocking_func(void *);
static void *channel_init_try_register_connection_polling_without_gil(
void *arg);
typedef struct channel_init_try_register_stack {
grpc_channel *channel;
grpc_rb_channel *wrapper;
} channel_init_try_register_stack;
static grpc_completion_queue *channel_polling_cq; static grpc_completion_queue *channel_polling_cq;
static gpr_mu global_connection_polling_mu; static gpr_mu global_connection_polling_mu;
@ -98,6 +122,42 @@ static gpr_cv global_connection_polling_cv;
static int abort_channel_polling = 0; static int abort_channel_polling = 0;
static int channel_polling_thread_started = 0; static int channel_polling_thread_started = 0;
static int bg_watched_channel_list_lookup(bg_watched_channel *bg);
static bg_watched_channel *bg_watched_channel_list_create_and_add(
grpc_channel *channel);
static void bg_watched_channel_list_free_and_remove(bg_watched_channel *bg);
static void run_poll_channels_loop_unblocking_func(void *arg);
// Needs to be called under global_connection_polling_mu
static void grpc_rb_channel_watch_connection_state_op_complete(
watch_state_op *op, int success) {
GPR_ASSERT(!op->op.api_callback_args.called_back);
op->op.api_callback_args.called_back = 1;
op->op.api_callback_args.success = success;
// wake up the watch API call thats waiting on this op
gpr_cv_broadcast(&global_connection_polling_cv);
}
/* Avoids destroying a channel twice. */
static void grpc_rb_channel_safe_destroy(bg_watched_channel *bg) {
gpr_mu_lock(&global_connection_polling_mu);
GPR_ASSERT(bg_watched_channel_list_lookup(bg));
if (!bg->channel_destroyed) {
grpc_channel_destroy(bg->channel);
bg->channel_destroyed = 1;
}
bg->refcount--;
if (bg->refcount == 0) {
bg_watched_channel_list_free_and_remove(bg);
}
gpr_mu_unlock(&global_connection_polling_mu);
}
static void *channel_safe_destroy_without_gil(void *arg) {
grpc_rb_channel_safe_destroy((bg_watched_channel *)arg);
return NULL;
}
/* Destroys Channel instances. */ /* Destroys Channel instances. */
static void grpc_rb_channel_free(void *p) { static void grpc_rb_channel_free(void *p) {
grpc_rb_channel *ch = NULL; grpc_rb_channel *ch = NULL;
@ -106,14 +166,13 @@ static void grpc_rb_channel_free(void *p) {
}; };
ch = (grpc_rb_channel *)p; ch = (grpc_rb_channel *)p;
if (ch->wrapped != NULL) { if (ch->bg_wrapped != NULL) {
grpc_rb_channel_safe_destroy(ch); /* assumption made here: it's ok to directly gpr_mu_lock the global
ch->wrapped = NULL; * connection polling mutex becuse we're in a finalizer,
} * and we can count on this thread to not be interrupted or
* yield the gil. */
if (ch->mu_init_done) { grpc_rb_channel_safe_destroy(ch->bg_wrapped);
gpr_mu_destroy(&ch->channel_mu); ch->bg_wrapped = NULL;
gpr_cv_destroy(&ch->channel_cv);
} }
xfree(p); xfree(p);
@ -146,7 +205,7 @@ static rb_data_type_t grpc_channel_data_type = {"grpc_channel",
/* Allocates grpc_rb_channel instances. */ /* Allocates grpc_rb_channel instances. */
static VALUE grpc_rb_channel_alloc(VALUE cls) { static VALUE grpc_rb_channel_alloc(VALUE cls) {
grpc_rb_channel *wrapper = ALLOC(grpc_rb_channel); grpc_rb_channel *wrapper = ALLOC(grpc_rb_channel);
wrapper->wrapped = NULL; wrapper->bg_wrapped = NULL;
wrapper->credentials = Qnil; wrapper->credentials = Qnil;
return TypedData_Wrap_Struct(cls, &grpc_channel_data_type, wrapper); return TypedData_Wrap_Struct(cls, &grpc_channel_data_type, wrapper);
} }
@ -168,18 +227,21 @@ static VALUE grpc_rb_channel_init(int argc, VALUE *argv, VALUE self) {
grpc_channel_credentials *creds = NULL; grpc_channel_credentials *creds = NULL;
char *target_chars = NULL; char *target_chars = NULL;
grpc_channel_args args; grpc_channel_args args;
channel_init_try_register_stack stack;
int stop_waiting_for_thread_start = 0;
MEMZERO(&args, grpc_channel_args, 1); MEMZERO(&args, grpc_channel_args, 1);
grpc_ruby_once_init(); grpc_ruby_once_init();
rb_thread_call_without_gvl( rb_thread_call_without_gvl(
wait_until_channel_polling_thread_started_no_gil, NULL, wait_until_channel_polling_thread_started_no_gil,
wait_until_channel_polling_thread_started_unblocking_func, NULL); &stop_waiting_for_thread_start,
wait_until_channel_polling_thread_started_unblocking_func,
&stop_waiting_for_thread_start);
/* "3" == 3 mandatory args */ /* "3" == 3 mandatory args */
rb_scan_args(argc, argv, "3", &target, &channel_args, &credentials); rb_scan_args(argc, argv, "3", &target, &channel_args, &credentials);
TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper); TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper);
wrapper->mu_init_done = 0;
target_chars = StringValueCStr(target); target_chars = StringValueCStr(target);
grpc_rb_hash_convert_to_channel_args(channel_args, &args); grpc_rb_hash_convert_to_channel_args(channel_args, &args);
if (TYPE(credentials) == T_SYMBOL) { if (TYPE(credentials) == T_SYMBOL) {
@ -196,24 +258,11 @@ static VALUE grpc_rb_channel_init(int argc, VALUE *argv, VALUE self) {
} }
GPR_ASSERT(ch); GPR_ASSERT(ch);
stack.channel = ch;
wrapper->wrapped = ch; stack.wrapper = wrapper;
rb_thread_call_without_gvl(
gpr_mu_init(&wrapper->channel_mu); channel_init_try_register_connection_polling_without_gil, &stack, NULL,
gpr_cv_init(&wrapper->channel_cv); NULL);
wrapper->mu_init_done = 1;
gpr_mu_lock(&wrapper->channel_mu);
wrapper->abort_watch_connectivity_state = 0;
wrapper->current_connectivity_state =
grpc_channel_check_connectivity_state(wrapper->wrapped, 0);
wrapper->safe_to_destroy = 0;
wrapper->request_safe_destroy = 0;
gpr_cv_broadcast(&wrapper->channel_cv);
gpr_mu_unlock(&wrapper->channel_mu);
grpc_rb_channel_try_register_connection_polling(wrapper);
if (args.args != NULL) { if (args.args != NULL) {
xfree(args.args); /* Allocated by grpc_rb_hash_convert_to_channel_args */ xfree(args.args); /* Allocated by grpc_rb_hash_convert_to_channel_args */
@ -224,10 +273,31 @@ static VALUE grpc_rb_channel_init(int argc, VALUE *argv, VALUE self) {
return Qnil; return Qnil;
} }
rb_ivar_set(self, id_target, target); rb_ivar_set(self, id_target, target);
wrapper->wrapped = ch;
return self; return self;
} }
typedef struct get_state_stack {
bg_watched_channel *bg;
int try_to_connect;
int out;
} get_state_stack;
static void *get_state_without_gil(void *arg) {
get_state_stack *stack = (get_state_stack *)arg;
gpr_mu_lock(&global_connection_polling_mu);
GPR_ASSERT(abort_channel_polling || channel_polling_thread_started);
if (stack->bg->channel_destroyed) {
stack->out = GRPC_CHANNEL_SHUTDOWN;
} else {
stack->out = grpc_channel_check_connectivity_state(stack->bg->channel,
stack->try_to_connect);
}
gpr_mu_unlock(&global_connection_polling_mu);
return NULL;
}
/* /*
call-seq: call-seq:
ch.connectivity_state -> state ch.connectivity_state -> state
@ -240,59 +310,69 @@ static VALUE grpc_rb_channel_init(int argc, VALUE *argv, VALUE self) {
static VALUE grpc_rb_channel_get_connectivity_state(int argc, VALUE *argv, static VALUE grpc_rb_channel_get_connectivity_state(int argc, VALUE *argv,
VALUE self) { VALUE self) {
VALUE try_to_connect_param = Qfalse; VALUE try_to_connect_param = Qfalse;
int grpc_try_to_connect = 0;
grpc_rb_channel *wrapper = NULL; grpc_rb_channel *wrapper = NULL;
grpc_channel *ch = NULL; get_state_stack stack;
/* "01" == 0 mandatory args, 1 (try_to_connect) is optional */ /* "01" == 0 mandatory args, 1 (try_to_connect) is optional */
rb_scan_args(argc, argv, "01", &try_to_connect_param); rb_scan_args(argc, argv, "01", &try_to_connect_param);
grpc_try_to_connect = RTEST(try_to_connect_param) ? 1 : 0;
TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper); TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper);
ch = wrapper->wrapped; if (wrapper->bg_wrapped == NULL) {
if (ch == NULL) {
rb_raise(rb_eRuntimeError, "closed!"); rb_raise(rb_eRuntimeError, "closed!");
return Qnil; return Qnil;
} }
return LONG2NUM(grpc_channel_check_connectivity_state(wrapper->wrapped,
grpc_try_to_connect)); stack.bg = wrapper->bg_wrapped;
stack.try_to_connect = RTEST(try_to_connect_param) ? 1 : 0;
rb_thread_call_without_gvl(get_state_without_gil, &stack, NULL, NULL);
return LONG2NUM(stack.out);
} }
typedef struct watch_state_stack { typedef struct watch_state_stack {
grpc_rb_channel *wrapper; grpc_channel *channel;
gpr_timespec deadline; gpr_timespec deadline;
int last_state; int last_state;
} watch_state_stack; } watch_state_stack;
static void *watch_channel_state_without_gvl(void *arg) { static void *wait_for_watch_state_op_complete_without_gvl(void *arg) {
watch_state_stack *stack = (watch_state_stack *)arg; watch_state_stack *stack = (watch_state_stack *)arg;
gpr_timespec deadline = stack->deadline; watch_state_op *op = NULL;
grpc_rb_channel *wrapper = stack->wrapper; void *success = (void *)0;
int last_state = stack->last_state;
void *return_value = (void *)0;
gpr_mu_lock(&wrapper->channel_mu); gpr_mu_lock(&global_connection_polling_mu);
while (wrapper->current_connectivity_state == last_state && // its unsafe to do a "watch" after "channel polling abort" because the cq has
!wrapper->request_safe_destroy && !wrapper->safe_to_destroy && // been shut down.
!wrapper->abort_watch_connectivity_state && if (abort_channel_polling) {
gpr_time_cmp(deadline, gpr_now(GPR_CLOCK_REALTIME)) > 0) { gpr_mu_unlock(&global_connection_polling_mu);
gpr_cv_wait(&wrapper->channel_cv, &wrapper->channel_mu, deadline); return (void *)0;
} }
if (wrapper->current_connectivity_state != last_state) { op = gpr_zalloc(sizeof(watch_state_op));
return_value = (void *)1; op->op_type = WATCH_STATE_API;
grpc_channel_watch_connectivity_state(stack->channel, stack->last_state,
stack->deadline, channel_polling_cq,
op);
while (!op->op.api_callback_args.called_back) {
gpr_cv_wait(&global_connection_polling_cv, &global_connection_polling_mu,
gpr_inf_future(GPR_CLOCK_REALTIME));
} }
gpr_mu_unlock(&wrapper->channel_mu); if (op->op.api_callback_args.success) {
success = (void *)1;
}
gpr_free(op);
gpr_mu_unlock(&global_connection_polling_mu);
return return_value; return success;
} }
static void wait_for_watch_state_op_complete_unblocking_func(void *arg) {
static void watch_channel_state_unblocking_func(void *arg) { bg_watched_channel *bg = (bg_watched_channel *)arg;
grpc_rb_channel *wrapper = (grpc_rb_channel *)arg; gpr_mu_lock(&global_connection_polling_mu);
gpr_log(GPR_DEBUG, "GRPC_RUBY: watch channel state unblocking func called"); if (!bg->channel_destroyed) {
gpr_mu_lock(&wrapper->channel_mu); grpc_channel_destroy(bg->channel);
wrapper->abort_watch_connectivity_state = 1; bg->channel_destroyed = 1;
gpr_cv_broadcast(&wrapper->channel_cv); }
gpr_mu_unlock(&wrapper->channel_mu); gpr_mu_unlock(&global_connection_polling_mu);
} }
/* Wait until the channel's connectivity state becomes different from /* Wait until the channel's connectivity state becomes different from
@ -307,11 +387,11 @@ static VALUE grpc_rb_channel_watch_connectivity_state(VALUE self,
VALUE deadline) { VALUE deadline) {
grpc_rb_channel *wrapper = NULL; grpc_rb_channel *wrapper = NULL;
watch_state_stack stack; watch_state_stack stack;
void *out; void *op_success = 0;
TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper); TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper);
if (wrapper->wrapped == NULL) { if (wrapper->bg_wrapped == NULL) {
rb_raise(rb_eRuntimeError, "closed!"); rb_raise(rb_eRuntimeError, "closed!");
return Qnil; return Qnil;
} }
@ -323,16 +403,15 @@ static VALUE grpc_rb_channel_watch_connectivity_state(VALUE self,
return Qnil; return Qnil;
} }
stack.wrapper = wrapper; stack.channel = wrapper->bg_wrapped->channel;
stack.deadline = grpc_rb_time_timeval(deadline, 0); stack.deadline = grpc_rb_time_timeval(deadline, 0),
stack.last_state = NUM2LONG(last_state); stack.last_state = NUM2LONG(last_state);
out =
rb_thread_call_without_gvl(watch_channel_state_without_gvl, &stack, op_success = rb_thread_call_without_gvl(
watch_channel_state_unblocking_func, wrapper); wait_for_watch_state_op_complete_without_gvl, &stack,
if (out) { wait_for_watch_state_op_complete_unblocking_func, wrapper->bg_wrapped);
return Qtrue;
} return op_success ? Qtrue : Qfalse;
return Qfalse;
} }
/* Create a call given a grpc_channel, in order to call method. The request /* Create a call given a grpc_channel, in order to call method. The request
@ -344,7 +423,6 @@ static VALUE grpc_rb_channel_create_call(VALUE self, VALUE parent, VALUE mask,
grpc_rb_channel *wrapper = NULL; grpc_rb_channel *wrapper = NULL;
grpc_call *call = NULL; grpc_call *call = NULL;
grpc_call *parent_call = NULL; grpc_call *parent_call = NULL;
grpc_channel *ch = NULL;
grpc_completion_queue *cq = NULL; grpc_completion_queue *cq = NULL;
int flags = GRPC_PROPAGATE_DEFAULTS; int flags = GRPC_PROPAGATE_DEFAULTS;
grpc_slice method_slice; grpc_slice method_slice;
@ -366,8 +444,7 @@ static VALUE grpc_rb_channel_create_call(VALUE self, VALUE parent, VALUE mask,
cq = grpc_completion_queue_create_for_pluck(NULL); cq = grpc_completion_queue_create_for_pluck(NULL);
TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper); TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper);
ch = wrapper->wrapped; if (wrapper->bg_wrapped == NULL) {
if (ch == NULL) {
rb_raise(rb_eRuntimeError, "closed!"); rb_raise(rb_eRuntimeError, "closed!");
return Qnil; return Qnil;
} }
@ -375,8 +452,8 @@ static VALUE grpc_rb_channel_create_call(VALUE self, VALUE parent, VALUE mask,
method_slice = method_slice =
grpc_slice_from_copied_buffer(RSTRING_PTR(method), RSTRING_LEN(method)); grpc_slice_from_copied_buffer(RSTRING_PTR(method), RSTRING_LEN(method));
call = grpc_channel_create_call(ch, parent_call, flags, cq, method_slice, call = grpc_channel_create_call(wrapper->bg_wrapped->channel, parent_call,
host_slice_ptr, flags, cq, method_slice, host_slice_ptr,
grpc_rb_time_timeval(deadline, grpc_rb_time_timeval(deadline,
/* absolute time */ 0), /* absolute time */ 0),
NULL); NULL);
@ -401,15 +478,16 @@ static VALUE grpc_rb_channel_create_call(VALUE self, VALUE parent, VALUE mask,
} }
/* Closes the channel, calling it's destroy method */ /* Closes the channel, calling it's destroy method */
/* Note this is an API-level call; a wrapped channel's finalizer doesn't call
* this */
static VALUE grpc_rb_channel_destroy(VALUE self) { static VALUE grpc_rb_channel_destroy(VALUE self) {
grpc_rb_channel *wrapper = NULL; grpc_rb_channel *wrapper = NULL;
grpc_channel *ch = NULL;
TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper); TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper);
ch = wrapper->wrapped; if (wrapper->bg_wrapped != NULL) {
if (ch != NULL) { rb_thread_call_without_gvl(channel_safe_destroy_without_gil,
grpc_rb_channel_safe_destroy(wrapper); wrapper->bg_wrapped, NULL, NULL);
wrapper->wrapped = NULL; wrapper->bg_wrapped = NULL;
} }
return Qnil; return Qnil;
@ -422,64 +500,110 @@ static VALUE grpc_rb_channel_get_target(VALUE self) {
char *target = NULL; char *target = NULL;
TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper); TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper);
target = grpc_channel_get_target(wrapper->wrapped); target = grpc_channel_get_target(wrapper->bg_wrapped->channel);
res = rb_str_new2(target); res = rb_str_new2(target);
gpr_free(target); gpr_free(target);
return res; return res;
} }
// Either start polling channel connection state or signal that it's free to /* Needs to be called under global_connection_polling_mu */
// destroy. static int bg_watched_channel_list_lookup(bg_watched_channel *target) {
// Not safe to call while a channel's connection state is polled. bg_watched_channel *cur = bg_watched_channel_list_head;
static void grpc_rb_channel_try_register_connection_polling(
grpc_rb_channel *wrapper) { while (cur != NULL) {
grpc_connectivity_state conn_state; if (cur == target) {
gpr_timespec sleep_time = gpr_time_add( return 1;
gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(20, GPR_TIMESPAN)); }
cur = cur->next;
GPR_ASSERT(wrapper);
GPR_ASSERT(wrapper->wrapped);
gpr_mu_lock(&wrapper->channel_mu);
if (wrapper->request_safe_destroy) {
wrapper->safe_to_destroy = 1;
gpr_cv_broadcast(&wrapper->channel_cv);
gpr_mu_unlock(&wrapper->channel_mu);
return;
} }
gpr_mu_lock(&global_connection_polling_mu);
GPR_ASSERT(channel_polling_thread_started || abort_channel_polling); return 0;
conn_state = grpc_channel_check_connectivity_state(wrapper->wrapped, 0); }
if (conn_state != wrapper->current_connectivity_state) {
wrapper->current_connectivity_state = conn_state; /* Needs to be called under global_connection_polling_mu */
gpr_cv_broadcast(&wrapper->channel_cv); static bg_watched_channel *bg_watched_channel_list_create_and_add(
} grpc_channel *channel) {
// avoid posting work to the channel polling cq if it's been shutdown bg_watched_channel *watched = gpr_zalloc(sizeof(bg_watched_channel));
if (!abort_channel_polling && conn_state != GRPC_CHANNEL_SHUTDOWN) {
grpc_channel_watch_connectivity_state( watched->channel = channel;
wrapper->wrapped, conn_state, sleep_time, channel_polling_cq, wrapper); watched->next = bg_watched_channel_list_head;
} else { watched->refcount = 1;
wrapper->safe_to_destroy = 1; bg_watched_channel_list_head = watched;
gpr_cv_broadcast(&wrapper->channel_cv); return watched;
}
/* Needs to be called under global_connection_polling_mu */
static void bg_watched_channel_list_free_and_remove(
bg_watched_channel *target) {
bg_watched_channel *bg = NULL;
GPR_ASSERT(bg_watched_channel_list_lookup(target));
GPR_ASSERT(target->channel_destroyed && target->refcount == 0);
if (bg_watched_channel_list_head == target) {
bg_watched_channel_list_head = target->next;
gpr_free(target);
return;
} }
bg = bg_watched_channel_list_head;
while (bg != NULL && bg->next != NULL) {
if (bg->next == target) {
bg->next = bg->next->next;
gpr_free(target);
return;
}
bg = bg->next;
}
GPR_ASSERT(0);
}
/* Initialize a grpc_rb_channel's "protected grpc_channel" and try to push
* it onto the background thread for constant watches. */
static void *channel_init_try_register_connection_polling_without_gil(
void *arg) {
channel_init_try_register_stack *stack =
(channel_init_try_register_stack *)arg;
gpr_mu_lock(&global_connection_polling_mu);
stack->wrapper->bg_wrapped =
bg_watched_channel_list_create_and_add(stack->channel);
grpc_rb_channel_try_register_connection_polling(stack->wrapper->bg_wrapped);
gpr_mu_unlock(&global_connection_polling_mu); gpr_mu_unlock(&global_connection_polling_mu);
gpr_mu_unlock(&wrapper->channel_mu); return NULL;
} }
// Note requires wrapper->wrapped, wrapper->channel_mu/cv initialized // Needs to be called under global_connection_poolling_mu
static void grpc_rb_channel_safe_destroy(grpc_rb_channel *wrapper) { static void grpc_rb_channel_try_register_connection_polling(
gpr_mu_lock(&wrapper->channel_mu); bg_watched_channel *bg) {
wrapper->request_safe_destroy = 1; grpc_connectivity_state conn_state;
watch_state_op *op = NULL;
while (!wrapper->safe_to_destroy) { GPR_ASSERT(channel_polling_thread_started || abort_channel_polling);
gpr_cv_wait(&wrapper->channel_cv, &wrapper->channel_mu,
gpr_inf_future(GPR_CLOCK_REALTIME)); if (bg->refcount == 0) {
GPR_ASSERT(bg->channel_destroyed);
bg_watched_channel_list_free_and_remove(bg);
return;
}
GPR_ASSERT(bg->refcount == 1);
if (bg->channel_destroyed || abort_channel_polling) {
return;
} }
GPR_ASSERT(wrapper->safe_to_destroy);
gpr_mu_unlock(&wrapper->channel_mu);
grpc_channel_destroy(wrapper->wrapped); conn_state = grpc_channel_check_connectivity_state(bg->channel, 0);
if (conn_state == GRPC_CHANNEL_SHUTDOWN) {
return;
}
GPR_ASSERT(bg_watched_channel_list_lookup(bg));
// prevent bg from being free'd by GC while background thread is watching it
bg->refcount++;
op = gpr_zalloc(sizeof(watch_state_op));
op->op_type = CONTINUOUS_WATCH;
op->op.continuous_watch_callback_args.bg = bg;
grpc_channel_watch_connectivity_state(bg->channel, conn_state,
gpr_inf_future(GPR_CLOCK_REALTIME),
channel_polling_cq, op);
} }
// Note this loop breaks out with a single call of // Note this loop breaks out with a single call of
@ -490,6 +614,8 @@ static void grpc_rb_channel_safe_destroy(grpc_rb_channel *wrapper) {
// early and falls back to current behavior. // early and falls back to current behavior.
static void *run_poll_channels_loop_no_gil(void *arg) { static void *run_poll_channels_loop_no_gil(void *arg) {
grpc_event event; grpc_event event;
watch_state_op *op = NULL;
bg_watched_channel *bg = NULL;
(void)arg; (void)arg;
gpr_log(GPR_DEBUG, "GRPC_RUBY: run_poll_channels_loop_no_gil - begin"); gpr_log(GPR_DEBUG, "GRPC_RUBY: run_poll_channels_loop_no_gil - begin");
@ -505,10 +631,22 @@ static void *run_poll_channels_loop_no_gil(void *arg) {
if (event.type == GRPC_QUEUE_SHUTDOWN) { if (event.type == GRPC_QUEUE_SHUTDOWN) {
break; break;
} }
gpr_mu_lock(&global_connection_polling_mu);
if (event.type == GRPC_OP_COMPLETE) { if (event.type == GRPC_OP_COMPLETE) {
grpc_rb_channel_try_register_connection_polling( op = (watch_state_op *)event.tag;
(grpc_rb_channel *)event.tag); if (op->op_type == CONTINUOUS_WATCH) {
bg = (bg_watched_channel *)op->op.continuous_watch_callback_args.bg;
bg->refcount--;
grpc_rb_channel_try_register_connection_polling(bg);
gpr_free(op);
} else if (op->op_type == WATCH_STATE_API) {
grpc_rb_channel_watch_connection_state_op_complete(
(watch_state_op *)event.tag, event.success);
} else {
GPR_ASSERT(0);
}
} }
gpr_mu_unlock(&global_connection_polling_mu);
} }
grpc_completion_queue_destroy(channel_polling_cq); grpc_completion_queue_destroy(channel_polling_cq);
gpr_log(GPR_DEBUG, gpr_log(GPR_DEBUG,
@ -519,14 +657,36 @@ static void *run_poll_channels_loop_no_gil(void *arg) {
// Notify the channel polling loop to cleanup and shutdown. // Notify the channel polling loop to cleanup and shutdown.
static void run_poll_channels_loop_unblocking_func(void *arg) { static void run_poll_channels_loop_unblocking_func(void *arg) {
bg_watched_channel *bg = NULL;
(void)arg; (void)arg;
gpr_mu_lock(&global_connection_polling_mu); gpr_mu_lock(&global_connection_polling_mu);
gpr_log(GPR_DEBUG, gpr_log(GPR_DEBUG,
"GRPC_RUBY: grpc_rb_event_unblocking_func - begin aborting " "GRPC_RUBY: run_poll_channels_loop_unblocking_func - begin aborting "
"connection polling"); "connection polling");
// early out after first time through
if (abort_channel_polling) {
gpr_mu_unlock(&global_connection_polling_mu);
return;
}
abort_channel_polling = 1; abort_channel_polling = 1;
// force pending watches to end by switching to shutdown state
bg = bg_watched_channel_list_head;
while (bg != NULL) {
if (!bg->channel_destroyed) {
grpc_channel_destroy(bg->channel);
bg->channel_destroyed = 1;
}
bg = bg->next;
}
grpc_completion_queue_shutdown(channel_polling_cq); grpc_completion_queue_shutdown(channel_polling_cq);
gpr_cv_broadcast(&global_connection_polling_cv);
gpr_mu_unlock(&global_connection_polling_mu); gpr_mu_unlock(&global_connection_polling_mu);
gpr_log(GPR_DEBUG,
"GRPC_RUBY: run_poll_channels_loop_unblocking_func - end aborting "
"connection polling");
} }
// Poll channel connectivity states in background thread without the GIL. // Poll channel connectivity states in background thread without the GIL.
@ -542,10 +702,11 @@ static VALUE run_poll_channels_loop(VALUE arg) {
} }
static void *wait_until_channel_polling_thread_started_no_gil(void *arg) { static void *wait_until_channel_polling_thread_started_no_gil(void *arg) {
(void)arg; int *stop_waiting = (int *)arg;
gpr_log(GPR_DEBUG, "GRPC_RUBY: wait for channel polling thread to start"); gpr_log(GPR_DEBUG, "GRPC_RUBY: wait for channel polling thread to start");
gpr_mu_lock(&global_connection_polling_mu); gpr_mu_lock(&global_connection_polling_mu);
while (!channel_polling_thread_started && !abort_channel_polling) { while (!channel_polling_thread_started && !abort_channel_polling &&
!*stop_waiting) {
gpr_cv_wait(&global_connection_polling_cv, &global_connection_polling_mu, gpr_cv_wait(&global_connection_polling_cv, &global_connection_polling_mu,
gpr_inf_future(GPR_CLOCK_REALTIME)); gpr_inf_future(GPR_CLOCK_REALTIME));
} }
@ -556,15 +717,22 @@ static void *wait_until_channel_polling_thread_started_no_gil(void *arg) {
static void wait_until_channel_polling_thread_started_unblocking_func( static void wait_until_channel_polling_thread_started_unblocking_func(
void *arg) { void *arg) {
(void)arg; int *stop_waiting = (int *)arg;
gpr_mu_lock(&global_connection_polling_mu); gpr_mu_lock(&global_connection_polling_mu);
gpr_log(GPR_DEBUG, gpr_log(GPR_DEBUG,
"GRPC_RUBY: " "GRPC_RUBY: interrupt wait for channel polling thread to start");
"wait_until_channel_polling_thread_started_unblocking_func - begin " *stop_waiting = 1;
"aborting connection polling"); gpr_cv_broadcast(&global_connection_polling_cv);
gpr_mu_unlock(&global_connection_polling_mu);
}
static void *set_abort_channel_polling_without_gil(void *arg) {
(void)arg;
gpr_mu_lock(&global_connection_polling_mu);
abort_channel_polling = 1; abort_channel_polling = 1;
gpr_cv_broadcast(&global_connection_polling_cv); gpr_cv_broadcast(&global_connection_polling_cv);
gpr_mu_unlock(&global_connection_polling_mu); gpr_mu_unlock(&global_connection_polling_mu);
return NULL;
} }
/* Temporary fix for /* Temporary fix for
@ -592,10 +760,8 @@ void grpc_rb_channel_polling_thread_start() {
if (!RTEST(background_thread)) { if (!RTEST(background_thread)) {
gpr_log(GPR_DEBUG, "GRPC_RUBY: failed to spawn channel polling thread"); gpr_log(GPR_DEBUG, "GRPC_RUBY: failed to spawn channel polling thread");
gpr_mu_lock(&global_connection_polling_mu); rb_thread_call_without_gvl(set_abort_channel_polling_without_gil, NULL,
abort_channel_polling = 1; NULL, NULL);
gpr_cv_broadcast(&global_connection_polling_cv);
gpr_mu_unlock(&global_connection_polling_mu);
} }
} }
@ -674,5 +840,5 @@ void Init_grpc_channel() {
grpc_channel *grpc_rb_get_wrapped_channel(VALUE v) { grpc_channel *grpc_rb_get_wrapped_channel(VALUE v) {
grpc_rb_channel *wrapper = NULL; grpc_rb_channel *wrapper = NULL;
TypedData_Get_Struct(v, grpc_rb_channel, &grpc_channel_data_type, wrapper); TypedData_Get_Struct(v, grpc_rb_channel, &grpc_channel_data_type, wrapper);
return wrapper->wrapped; return wrapper->bg_wrapped->channel;
} }

@ -105,16 +105,16 @@ static void *grpc_rb_wait_for_event_no_gil(void *param) {
grpc_rb_event *event = NULL; grpc_rb_event *event = NULL;
(void)param; (void)param;
gpr_mu_lock(&event_queue.mu); gpr_mu_lock(&event_queue.mu);
while ((event = grpc_rb_event_queue_dequeue()) == NULL) { while (!event_queue.abort) {
gpr_cv_wait(&event_queue.cv, &event_queue.mu, if ((event = grpc_rb_event_queue_dequeue()) != NULL) {
gpr_inf_future(GPR_CLOCK_REALTIME));
if (event_queue.abort) {
gpr_mu_unlock(&event_queue.mu); gpr_mu_unlock(&event_queue.mu);
return NULL; return event;
} }
gpr_cv_wait(&event_queue.cv, &event_queue.mu,
gpr_inf_future(GPR_CLOCK_REALTIME));
} }
gpr_mu_unlock(&event_queue.mu); gpr_mu_unlock(&event_queue.mu);
return event; return NULL;
} }
static void grpc_rb_event_unblocking_func(void *arg) { static void grpc_rb_event_unblocking_func(void *arg) {

@ -292,11 +292,12 @@ static gpr_once g_once_init = GPR_ONCE_INIT;
static void grpc_ruby_once_init_internal() { static void grpc_ruby_once_init_internal() {
grpc_init(); grpc_init();
grpc_rb_event_queue_thread_start();
grpc_rb_channel_polling_thread_start();
atexit(grpc_rb_shutdown); atexit(grpc_rb_shutdown);
} }
static VALUE bg_thread_init_rb_mu = Qundef;
static int bg_thread_init_done = 0;
void grpc_ruby_once_init() { void grpc_ruby_once_init() {
/* ruby_vm_at_exit doesn't seem to be working. It would crash once every /* ruby_vm_at_exit doesn't seem to be working. It would crash once every
* blue moon, and some users are getting it repeatedly. See the discussions * blue moon, and some users are getting it repeatedly. See the discussions
@ -309,6 +310,18 @@ void grpc_ruby_once_init() {
* schedule our initialization and destruction only once. * schedule our initialization and destruction only once.
*/ */
gpr_once_init(&g_once_init, grpc_ruby_once_init_internal); gpr_once_init(&g_once_init, grpc_ruby_once_init_internal);
// Avoid calling calling into ruby library (when creating threads here)
// in gpr_once_init. In general, it appears to be unsafe to call
// into the ruby library while holding a non-ruby mutex, because a gil yield
// could end up trying to lock onto that same mutex and deadlocking.
rb_mutex_lock(bg_thread_init_rb_mu);
if (!bg_thread_init_done) {
grpc_rb_event_queue_thread_start();
grpc_rb_channel_polling_thread_start();
bg_thread_init_done = 1;
}
rb_mutex_unlock(bg_thread_init_rb_mu);
} }
void Init_grpc_c() { void Init_grpc_c() {
@ -317,6 +330,9 @@ void Init_grpc_c() {
return; return;
} }
bg_thread_init_rb_mu = rb_mutex_new();
rb_global_variable(&bg_thread_init_rb_mu);
grpc_rb_mGRPC = rb_define_module("GRPC"); grpc_rb_mGRPC = rb_define_module("GRPC");
grpc_rb_mGrpcCore = rb_define_module_under(grpc_rb_mGRPC, "Core"); grpc_rb_mGrpcCore = rb_define_module_under(grpc_rb_mGRPC, "Core");
grpc_rb_sNewServerRpc = rb_struct_define( grpc_rb_sNewServerRpc = rb_struct_define(

@ -103,6 +103,7 @@ grpc_alarm_create_type grpc_alarm_create_import;
grpc_alarm_cancel_type grpc_alarm_cancel_import; grpc_alarm_cancel_type grpc_alarm_cancel_import;
grpc_alarm_destroy_type grpc_alarm_destroy_import; grpc_alarm_destroy_type grpc_alarm_destroy_import;
grpc_channel_check_connectivity_state_type grpc_channel_check_connectivity_state_import; grpc_channel_check_connectivity_state_type grpc_channel_check_connectivity_state_import;
grpc_channel_num_external_connectivity_watchers_type grpc_channel_num_external_connectivity_watchers_import;
grpc_channel_watch_connectivity_state_type grpc_channel_watch_connectivity_state_import; grpc_channel_watch_connectivity_state_type grpc_channel_watch_connectivity_state_import;
grpc_channel_create_call_type grpc_channel_create_call_import; grpc_channel_create_call_type grpc_channel_create_call_import;
grpc_channel_ping_type grpc_channel_ping_import; grpc_channel_ping_type grpc_channel_ping_import;
@ -405,6 +406,7 @@ void grpc_rb_load_imports(HMODULE library) {
grpc_alarm_cancel_import = (grpc_alarm_cancel_type) GetProcAddress(library, "grpc_alarm_cancel"); grpc_alarm_cancel_import = (grpc_alarm_cancel_type) GetProcAddress(library, "grpc_alarm_cancel");
grpc_alarm_destroy_import = (grpc_alarm_destroy_type) GetProcAddress(library, "grpc_alarm_destroy"); grpc_alarm_destroy_import = (grpc_alarm_destroy_type) GetProcAddress(library, "grpc_alarm_destroy");
grpc_channel_check_connectivity_state_import = (grpc_channel_check_connectivity_state_type) GetProcAddress(library, "grpc_channel_check_connectivity_state"); grpc_channel_check_connectivity_state_import = (grpc_channel_check_connectivity_state_type) GetProcAddress(library, "grpc_channel_check_connectivity_state");
grpc_channel_num_external_connectivity_watchers_import = (grpc_channel_num_external_connectivity_watchers_type) GetProcAddress(library, "grpc_channel_num_external_connectivity_watchers");
grpc_channel_watch_connectivity_state_import = (grpc_channel_watch_connectivity_state_type) GetProcAddress(library, "grpc_channel_watch_connectivity_state"); grpc_channel_watch_connectivity_state_import = (grpc_channel_watch_connectivity_state_type) GetProcAddress(library, "grpc_channel_watch_connectivity_state");
grpc_channel_create_call_import = (grpc_channel_create_call_type) GetProcAddress(library, "grpc_channel_create_call"); grpc_channel_create_call_import = (grpc_channel_create_call_type) GetProcAddress(library, "grpc_channel_create_call");
grpc_channel_ping_import = (grpc_channel_ping_type) GetProcAddress(library, "grpc_channel_ping"); grpc_channel_ping_import = (grpc_channel_ping_type) GetProcAddress(library, "grpc_channel_ping");

@ -260,6 +260,9 @@ extern grpc_alarm_destroy_type grpc_alarm_destroy_import;
typedef grpc_connectivity_state(*grpc_channel_check_connectivity_state_type)(grpc_channel *channel, int try_to_connect); typedef grpc_connectivity_state(*grpc_channel_check_connectivity_state_type)(grpc_channel *channel, int try_to_connect);
extern grpc_channel_check_connectivity_state_type grpc_channel_check_connectivity_state_import; extern grpc_channel_check_connectivity_state_type grpc_channel_check_connectivity_state_import;
#define grpc_channel_check_connectivity_state grpc_channel_check_connectivity_state_import #define grpc_channel_check_connectivity_state grpc_channel_check_connectivity_state_import
typedef int(*grpc_channel_num_external_connectivity_watchers_type)(grpc_channel *channel);
extern grpc_channel_num_external_connectivity_watchers_type grpc_channel_num_external_connectivity_watchers_import;
#define grpc_channel_num_external_connectivity_watchers grpc_channel_num_external_connectivity_watchers_import
typedef void(*grpc_channel_watch_connectivity_state_type)(grpc_channel *channel, grpc_connectivity_state last_observed_state, gpr_timespec deadline, grpc_completion_queue *cq, void *tag); typedef void(*grpc_channel_watch_connectivity_state_type)(grpc_channel *channel, grpc_connectivity_state last_observed_state, gpr_timespec deadline, grpc_completion_queue *cq, void *tag);
extern grpc_channel_watch_connectivity_state_type grpc_channel_watch_connectivity_state_import; extern grpc_channel_watch_connectivity_state_type grpc_channel_watch_connectivity_state_import;
#define grpc_channel_watch_connectivity_state grpc_channel_watch_connectivity_state_import #define grpc_channel_watch_connectivity_state grpc_channel_watch_connectivity_state_import

@ -29,5 +29,5 @@
# GRPC contains the General RPC module. # GRPC contains the General RPC module.
module GRPC module GRPC
VERSION = '1.4.0.dev' VERSION = '1.5.0.dev'
end end

@ -28,6 +28,10 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'grpc' require 'grpc'
require 'timeout'
include Timeout
include GRPC::Core
# A test message # A test message
class EchoMsg class EchoMsg
@ -62,7 +66,7 @@ end
EchoStub = EchoService.rpc_stub_class EchoStub = EchoService.rpc_stub_class
def start_server(port = 0) def start_server(port = 0)
@srv = GRPC::RpcServer.new @srv = GRPC::RpcServer.new(pool_size: 1)
server_port = @srv.add_http2_port("localhost:#{port}", :this_port_is_insecure) server_port = @srv.add_http2_port("localhost:#{port}", :this_port_is_insecure)
@srv.handle(EchoService) @srv.handle(EchoService)
@server_thd = Thread.new { @srv.run } @server_thd = Thread.new { @srv.run }
@ -138,4 +142,32 @@ describe 'channel connection behavior' do
stop_server stop_server
end end
it 'concurrent watches on the same channel' do
timeout(180) do
port = start_server
ch = GRPC::Core::Channel.new("localhost:#{port}", {},
:this_channel_is_insecure)
stop_server
thds = []
50.times do
thds << Thread.new do
while ch.connectivity_state(true) != ConnectivityStates::READY
ch.watch_connectivity_state(
ConnectivityStates::READY, Time.now + 60)
break
end
end
end
sleep 0.01
start_server(port)
thds.each(&:join)
stop_server
end
end
end end

@ -29,6 +29,6 @@
module GRPC module GRPC
module Tools module Tools
VERSION = '1.4.0.dev' VERSION = '1.5.0.dev'
end end
end end

@ -53,11 +53,11 @@
@rem To be able to build, we also need to put grpc_csharp_ext to its normal location @rem To be able to build, we also need to put grpc_csharp_ext to its normal location
xcopy /Y /I nativelibs\csharp_ext_windows_x64\grpc_csharp_ext.dll ..\..\cmake\build\x64\Release${"\\"} xcopy /Y /I nativelibs\csharp_ext_windows_x64\grpc_csharp_ext.dll ..\..\cmake\build\x64\Release${"\\"}
%%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.Core --output ..\..\..\artifacts || goto :error %%DOTNET% pack --configuration Release Grpc.Core --output ..\..\..\artifacts || goto :error
%%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.Core.Testing --output ..\..\..\artifacts || goto :error %%DOTNET% pack --configuration Release Grpc.Core.Testing --output ..\..\..\artifacts || goto :error
%%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.Auth --output ..\..\..\artifacts || goto :error %%DOTNET% pack --configuration Release Grpc.Auth --output ..\..\..\artifacts || goto :error
%%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.HealthCheck --output ..\..\..\artifacts || goto :error %%DOTNET% pack --configuration Release Grpc.HealthCheck --output ..\..\..\artifacts || goto :error
%%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.Reflection --output ..\..\..\artifacts || goto :error %%DOTNET% pack --configuration Release Grpc.Reflection --output ..\..\..\artifacts || goto :error
%%NUGET% pack Grpc.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts || goto :error %%NUGET% pack Grpc.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts || goto :error
%%NUGET% pack Grpc.Tools.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts %%NUGET% pack Grpc.Tools.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts

@ -50,11 +50,11 @@
mkdir -p ../../libs/opt mkdir -p ../../libs/opt
cp nativelibs/csharp_ext_linux_x64/libgrpc_csharp_ext.so ../../libs/opt cp nativelibs/csharp_ext_linux_x64/libgrpc_csharp_ext.so ../../libs/opt
dotnet pack --configuration Release --include-symbols --include-source Grpc.Core --output ../../../artifacts dotnet pack --configuration Release Grpc.Core --output ../../../artifacts
dotnet pack --configuration Release --include-symbols --include-source Grpc.Core.Testing --output ../../../artifacts dotnet pack --configuration Release Grpc.Core.Testing --output ../../../artifacts
dotnet pack --configuration Release --include-symbols --include-source Grpc.Auth --output ../../../artifacts dotnet pack --configuration Release Grpc.Auth --output ../../../artifacts
dotnet pack --configuration Release --include-symbols --include-source Grpc.HealthCheck --output ../../../artifacts dotnet pack --configuration Release Grpc.HealthCheck --output ../../../artifacts
dotnet pack --configuration Release --include-symbols --include-source Grpc.Reflection --output ../../../artifacts dotnet pack --configuration Release Grpc.Reflection --output ../../../artifacts
nuget pack Grpc.nuspec -Version "${settings.csharp_version}" -OutputDirectory ../../artifacts nuget pack Grpc.nuspec -Version "${settings.csharp_version}" -OutputDirectory ../../artifacts
nuget pack Grpc.Tools.nuspec -Version "${settings.csharp_version}" -OutputDirectory ../../artifacts nuget pack Grpc.Tools.nuspec -Version "${settings.csharp_version}" -OutputDirectory ../../artifacts

@ -49,7 +49,7 @@ static const uint32_t kInitialLog2Size = 4;
typedef struct object { uint64_t val; } object; typedef struct object { uint64_t val; } object;
/* Helper function to allocate and initialize object. */ /* Helper function to allocate and initialize object. */
static inline object *make_new_object(uint64_t val) { static __inline object *make_new_object(uint64_t val) {
object *obj = (object *)gpr_malloc(sizeof(object)); object *obj = (object *)gpr_malloc(sizeof(object));
obj->val = val; obj->val = val;
return obj; return obj;
@ -63,7 +63,7 @@ typedef struct ptr_item {
/* Helper function that creates a new hash map item. It is up to the user to /* Helper function that creates a new hash map item. It is up to the user to
* free the item that was allocated. */ * free the item that was allocated. */
static inline ptr_item *make_ptr_item(uint64_t key, uint64_t value) { static __inline ptr_item *make_ptr_item(uint64_t key, uint64_t value) {
ptr_item *new_item = (ptr_item *)gpr_malloc(sizeof(ptr_item)); ptr_item *new_item = (ptr_item *)gpr_malloc(sizeof(ptr_item));
new_item->IHM_key = key; new_item->IHM_key = key;
new_item->IHM_hash_link = NULL; new_item->IHM_hash_link = NULL;

@ -61,6 +61,14 @@
#define DELAY_MILLIS 10 #define DELAY_MILLIS 10
#define POLL_MILLIS 3000 #define POLL_MILLIS 3000
#define NUM_OUTER_LOOPS_SHORT_TIMEOUTS 10
#define NUM_INNER_LOOPS_SHORT_TIMEOUTS 100
#define DELAY_MILLIS_SHORT_TIMEOUTS 1
// in a successful test run, POLL_MILLIS should never be reached beause all runs
// should
// end after the shorter delay_millis
#define POLL_MILLIS_SHORT_TIMEOUTS 30000
static void *tag(int n) { return (void *)(uintptr_t)n; } static void *tag(int n) { return (void *)(uintptr_t)n; }
static int detag(void *p) { return (int)(uintptr_t)p; } static int detag(void *p) { return (int)(uintptr_t)p; }
@ -79,6 +87,8 @@ void create_loop_destroy(void *addr) {
grpc_timeout_milliseconds_to_deadline(POLL_MILLIS); grpc_timeout_milliseconds_to_deadline(POLL_MILLIS);
GPR_ASSERT(grpc_completion_queue_next(cq, poll_time, NULL).type == GPR_ASSERT(grpc_completion_queue_next(cq, poll_time, NULL).type ==
GRPC_OP_COMPLETE); GRPC_OP_COMPLETE);
/* check that the watcher from "watch state" was free'd */
GPR_ASSERT(grpc_channel_num_external_connectivity_watchers(chan) == 0);
} }
grpc_channel_destroy(chan); grpc_channel_destroy(chan);
grpc_completion_queue_destroy(cq); grpc_completion_queue_destroy(cq);
@ -168,11 +178,10 @@ static void done_pollset_shutdown(grpc_exec_ctx *exec_ctx, void *pollset,
gpr_free(pollset); gpr_free(pollset);
} }
int main(int argc, char **argv) { int run_concurrent_connectivity_test() {
struct server_thread_args args; struct server_thread_args args;
memset(&args, 0, sizeof(args)); memset(&args, 0, sizeof(args));
grpc_test_init(argc, argv);
grpc_init(); grpc_init();
gpr_thd_id threads[NUM_THREADS]; gpr_thd_id threads[NUM_THREADS];
@ -242,3 +251,59 @@ int main(int argc, char **argv) {
grpc_shutdown(); grpc_shutdown();
return 0; return 0;
} }
void watches_with_short_timeouts(void *addr) {
for (int i = 0; i < NUM_OUTER_LOOPS_SHORT_TIMEOUTS; ++i) {
grpc_completion_queue *cq = grpc_completion_queue_create_for_next(NULL);
grpc_channel *chan = grpc_insecure_channel_create((char *)addr, NULL, NULL);
for (int j = 0; j < NUM_INNER_LOOPS_SHORT_TIMEOUTS; ++j) {
gpr_timespec later_time =
grpc_timeout_milliseconds_to_deadline(DELAY_MILLIS_SHORT_TIMEOUTS);
grpc_connectivity_state state =
grpc_channel_check_connectivity_state(chan, 0);
GPR_ASSERT(state == GRPC_CHANNEL_IDLE);
grpc_channel_watch_connectivity_state(chan, state, later_time, cq, NULL);
gpr_timespec poll_time =
grpc_timeout_milliseconds_to_deadline(POLL_MILLIS_SHORT_TIMEOUTS);
grpc_event ev = grpc_completion_queue_next(cq, poll_time, NULL);
GPR_ASSERT(ev.type == GRPC_OP_COMPLETE);
GPR_ASSERT(ev.success == false);
/* check that the watcher from "watch state" was free'd */
GPR_ASSERT(grpc_channel_num_external_connectivity_watchers(chan) == 0);
}
grpc_channel_destroy(chan);
grpc_completion_queue_destroy(cq);
}
}
// This test tries to catch deadlock situations.
// With short timeouts on "watches" and long timeouts on cq next calls,
// so that a QUEUE_TIMEOUT likely means that something is stuck.
int run_concurrent_watches_with_short_timeouts_test() {
grpc_init();
gpr_thd_id threads[NUM_THREADS];
char *localhost = gpr_strdup("localhost:54321");
gpr_thd_options options = gpr_thd_options_default();
gpr_thd_options_set_joinable(&options);
for (size_t i = 0; i < NUM_THREADS; ++i) {
gpr_thd_new(&threads[i], watches_with_short_timeouts, localhost, &options);
}
for (size_t i = 0; i < NUM_THREADS; ++i) {
gpr_thd_join(threads[i]);
}
gpr_free(localhost);
grpc_shutdown();
return 0;
}
int main(int argc, char **argv) {
grpc_test_init(argc, argv);
run_concurrent_connectivity_test();
run_concurrent_watches_with_short_timeouts_test();
}

@ -0,0 +1,214 @@
/*
*
* Copyright 2016, 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/grpc.h>
#include <grpc/grpc_security.h>
#include <grpc/support/alloc.h>
#include <grpc/support/host_port.h>
#include <grpc/support/log.h>
#include <grpc/support/thd.h>
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "test/core/end2end/data/ssl_test_data.h"
#include "test/core/util/port.h"
#include "test/core/util/test_config.h"
typedef struct test_fixture {
const char *name;
grpc_channel *(*create_channel)(const char *addr);
} test_fixture;
static size_t next_tag = 1;
static void channel_idle_start_watch(grpc_channel *channel,
grpc_completion_queue *cq) {
gpr_timespec connect_deadline = grpc_timeout_milliseconds_to_deadline(1);
GPR_ASSERT(grpc_channel_check_connectivity_state(channel, 0) ==
GRPC_CHANNEL_IDLE);
grpc_channel_watch_connectivity_state(
channel, GRPC_CHANNEL_IDLE, connect_deadline, cq, (void *)(next_tag++));
gpr_log(GPR_DEBUG, "number of active connect watchers: %d",
grpc_channel_num_external_connectivity_watchers(channel));
}
static void channel_idle_poll_for_timeout(grpc_channel *channel,
grpc_completion_queue *cq) {
grpc_event ev =
grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
/* expect watch_connectivity_state to end with a timeout */
GPR_ASSERT(ev.type == GRPC_OP_COMPLETE);
GPR_ASSERT(ev.success == false);
GPR_ASSERT(grpc_channel_check_connectivity_state(channel, 0) ==
GRPC_CHANNEL_IDLE);
}
/* Test and use the "num_external_watchers" call to make sure
* that "connectivity watcher" structs are free'd just after, if
* their corresponding timeouts occur. */
static void run_timeouts_test(const test_fixture *fixture) {
gpr_log(GPR_INFO, "TEST: %s", fixture->name);
char *addr;
grpc_init();
gpr_join_host_port(&addr, "localhost", grpc_pick_unused_port_or_die());
grpc_channel *channel = fixture->create_channel(addr);
grpc_completion_queue *cq = grpc_completion_queue_create_for_next(NULL);
/* start 1 watcher and then let it time out */
channel_idle_start_watch(channel, cq);
channel_idle_poll_for_timeout(channel, cq);
GPR_ASSERT(grpc_channel_num_external_connectivity_watchers(channel) == 0);
/* start 3 watchers and then let them all time out */
for (size_t i = 1; i <= 3; i++) {
channel_idle_start_watch(channel, cq);
}
for (size_t i = 1; i <= 3; i++) {
channel_idle_poll_for_timeout(channel, cq);
}
GPR_ASSERT(grpc_channel_num_external_connectivity_watchers(channel) == 0);
/* start 3 watchers, see one time out, start another 3, and then see them all
* time out */
for (size_t i = 1; i <= 3; i++) {
channel_idle_start_watch(channel, cq);
}
channel_idle_poll_for_timeout(channel, cq);
for (size_t i = 3; i <= 5; i++) {
channel_idle_start_watch(channel, cq);
}
for (size_t i = 1; i <= 5; i++) {
channel_idle_poll_for_timeout(channel, cq);
}
GPR_ASSERT(grpc_channel_num_external_connectivity_watchers(channel) == 0);
grpc_channel_destroy(channel);
grpc_completion_queue_shutdown(cq);
GPR_ASSERT(
grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), NULL)
.type == GRPC_QUEUE_SHUTDOWN);
grpc_completion_queue_destroy(cq);
grpc_shutdown();
gpr_free(addr);
}
/* An edge scenario; sets channel state to explicitly, and outside
* of a polling call. */
static void run_channel_shutdown_before_timeout_test(
const test_fixture *fixture) {
gpr_log(GPR_INFO, "TEST: %s", fixture->name);
char *addr;
grpc_init();
gpr_join_host_port(&addr, "localhost", grpc_pick_unused_port_or_die());
grpc_channel *channel = fixture->create_channel(addr);
grpc_completion_queue *cq = grpc_completion_queue_create_for_next(NULL);
/* start 1 watcher and then shut down the channel before the timer goes off */
GPR_ASSERT(grpc_channel_num_external_connectivity_watchers(channel) == 0);
/* expecting a 30 second timeout to go off much later than the shutdown. */
gpr_timespec connect_deadline = grpc_timeout_seconds_to_deadline(30);
GPR_ASSERT(grpc_channel_check_connectivity_state(channel, 0) ==
GRPC_CHANNEL_IDLE);
grpc_channel_watch_connectivity_state(channel, GRPC_CHANNEL_IDLE,
connect_deadline, cq, (void *)1);
grpc_channel_destroy(channel);
grpc_event ev =
grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
GPR_ASSERT(ev.type == GRPC_OP_COMPLETE);
/* expect success with a state transition to CHANNEL_SHUTDOWN */
GPR_ASSERT(ev.success == true);
grpc_completion_queue_shutdown(cq);
GPR_ASSERT(
grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), NULL)
.type == GRPC_QUEUE_SHUTDOWN);
grpc_completion_queue_destroy(cq);
grpc_shutdown();
gpr_free(addr);
}
static grpc_channel *insecure_test_create_channel(const char *addr) {
return grpc_insecure_channel_create(addr, NULL, NULL);
}
static const test_fixture insecure_test = {
"insecure", insecure_test_create_channel,
};
static grpc_channel *secure_test_create_channel(const char *addr) {
grpc_channel_credentials *ssl_creds =
grpc_ssl_credentials_create(test_root_cert, NULL, NULL);
grpc_arg ssl_name_override = {GRPC_ARG_STRING,
GRPC_SSL_TARGET_NAME_OVERRIDE_ARG,
{"foo.test.google.fr"}};
grpc_channel_args *new_client_args =
grpc_channel_args_copy_and_add(NULL, &ssl_name_override, 1);
grpc_channel *channel =
grpc_secure_channel_create(ssl_creds, addr, new_client_args, NULL);
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_channel_args_destroy(&exec_ctx, new_client_args);
grpc_exec_ctx_finish(&exec_ctx);
}
grpc_channel_credentials_release(ssl_creds);
return channel;
}
static const test_fixture secure_test = {
"secure", secure_test_create_channel,
};
int main(int argc, char **argv) {
grpc_test_init(argc, argv);
run_timeouts_test(&insecure_test);
run_timeouts_test(&secure_test);
run_channel_shutdown_before_timeout_test(&insecure_test);
run_channel_shutdown_before_timeout_test(&secure_test);
}

@ -100,6 +100,9 @@ static void run_test(const test_fixture *fixture) {
connect_deadline, cq, NULL); connect_deadline, cq, NULL);
grpc_event ev = grpc_completion_queue_next( grpc_event ev = grpc_completion_queue_next(
cq, gpr_inf_future(GPR_CLOCK_REALTIME), NULL); cq, gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
/* check that the watcher from "watch state" was free'd */
GPR_ASSERT(grpc_channel_num_external_connectivity_watchers(channels[i]) ==
0);
GPR_ASSERT(ev.type == GRPC_OP_COMPLETE); GPR_ASSERT(ev.type == GRPC_OP_COMPLETE);
GPR_ASSERT(ev.tag == NULL); GPR_ASSERT(ev.tag == NULL);
GPR_ASSERT(ev.success == true); GPR_ASSERT(ev.success == true);

@ -419,18 +419,18 @@ static void BM_PumpUnbalancedUnary_Trickle(benchmark::State& state) {
} }
static void UnaryTrickleArgs(benchmark::internal::Benchmark* b) { static void UnaryTrickleArgs(benchmark::internal::Benchmark* b) {
// A selection of interesting numbers
const int cli_1024k = 1024 * 1024; const int cli_1024k = 1024 * 1024;
const int cli_32M = 32 * 1024 * 1024; const int cli_32M = 32 * 1024 * 1024;
const int svr_256k = 256 * 1024; const int svr_256k = 256 * 1024;
const int svr_4M = 4 * 1024 * 1024; const int svr_4M = 4 * 1024 * 1024;
const int svr_64M = 64 * 1024 * 1024; const int svr_64M = 64 * 1024 * 1024;
for (int bw = 64; bw <= 128 * 1024 * 1024; bw *= 16) { for (int bw = 64; bw <= 128 * 1024 * 1024; bw *= 16) {
b->Args({bw, cli_1024k, svr_256k}); for (auto svr : {svr_256k, svr_4M, svr_64M}) {
b->Args({bw, cli_1024k, svr_4M}); for (auto cli : {cli_1024k, cli_32M}) {
b->Args({bw, cli_1024k, svr_64M}); b->Args({cli, svr, bw});
b->Args({bw, cli_32M, svr_256k}); }
b->Args({bw, cli_32M, svr_4M}); }
b->Args({bw, cli_32M, svr_64M});
} }
} }
BENCHMARK(BM_PumpUnbalancedUnary_Trickle)->Apply(UnaryTrickleArgs); BENCHMARK(BM_PumpUnbalancedUnary_Trickle)->Apply(UnaryTrickleArgs);

@ -31,18 +31,22 @@
set -ex set -ex
# change to root directory # change to root directory
cd $(dirname $0)/../.. cd "$(dirname "$0")/../.."
DIRS=src/python/grpcio/grpc DIRS=(
'src/python/grpcio/grpc'
'src/python/grpcio_reflection/grpc_reflection'
'src/python/grpcio_health_checking/grpc_health'
)
VIRTUALENV=python_pylint_venv VIRTUALENV=python_pylint_venv
virtualenv $VIRTUALENV virtualenv $VIRTUALENV
PYTHON=`realpath $VIRTUALENV/bin/python` PYTHON=$(realpath $VIRTUALENV/bin/python)
$PYTHON -m pip install pylint==1.6.5 $PYTHON -m pip install pylint==1.6.5
for dir in $DIRS; do for dir in "${DIRS[@]}"; do
$PYTHON -m pylint --rcfile=.pylintrc -rn $dir || exit $? $PYTHON -m pylint --rcfile=.pylintrc -rn "$dir" || exit $?
done done
exit 0 exit 0

@ -29,4 +29,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/tools/distrib/python/grpcio_tools/grpc_version.py.template`!!! # AUTO-GENERATED FROM `$REPO_ROOT/templates/tools/distrib/python/grpcio_tools/grpc_version.py.template`!!!
VERSION='1.4.0.dev0' VERSION='1.5.0.dev0'

@ -55,6 +55,9 @@ RUN pip install pip --upgrade
RUN pip install virtualenv RUN pip install virtualenv
RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0
# Google Cloud platform API libraries
RUN pip install --upgrade google-api-python-client
# Prepare ccache # Prepare ccache
RUN ln -s /usr/bin/ccache /usr/local/bin/gcc RUN ln -s /usr/bin/ccache /usr/local/bin/gcc
RUN ln -s /usr/bin/ccache /usr/local/bin/g++ RUN ln -s /usr/bin/ccache /usr/local/bin/g++

@ -55,6 +55,9 @@ RUN pip install pip --upgrade
RUN pip install virtualenv RUN pip install virtualenv
RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0
# Google Cloud platform API libraries
RUN pip install --upgrade google-api-python-client
# Prepare ccache # Prepare ccache
RUN ln -s /usr/bin/ccache /usr/local/bin/gcc RUN ln -s /usr/bin/ccache /usr/local/bin/gcc
RUN ln -s /usr/bin/ccache /usr/local/bin/g++ RUN ln -s /usr/bin/ccache /usr/local/bin/g++

@ -75,7 +75,8 @@ RUN pip install --upgrade google-api-python-client
RUN apt-get update && apt-get install -y \ RUN apt-get update && apt-get install -y \
python-all-dev \ python-all-dev \
python3-all-dev \ python3-all-dev \
python-pip python-pip \
python3-pip
# Install Python packages from PyPI # Install Python packages from PyPI
RUN pip install pip --upgrade RUN pip install pip --upgrade

@ -40,7 +40,7 @@ PROJECT_NAME = "GRPC C++"
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 1.4.0-dev PROJECT_NUMBER = 1.5.0-dev
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a # for a project that appears at the top of each page and should give viewer a

@ -40,7 +40,7 @@ PROJECT_NAME = "GRPC C++"
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 1.4.0-dev PROJECT_NUMBER = 1.5.0-dev
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a # for a project that appears at the top of each page and should give viewer a

@ -0,0 +1,72 @@
#!/bin/bash
# Copyright 2017, 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.
# Source this rc script to prepare the environment for macos builds
# TODO(jtattermusch): remove all deps once installed on MacOS workers
# brew and C++ deps
yes | ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew install autoconf automake libtool ccache cmake gflags gpg wget
# TODO(jtattermusch): install rvm & ruby
# TODO(jtattermusch): install cocoapods
# python
wget https://bootstrap.pypa.io/get-pip.py
sudo python get-pip.py
sudo pip install virtualenv
# TODO(jtattermusch): install python3
# mono
wget https://download.mono-project.com/archive/5.0.1/macos-10-universal/MonoFramework-MDK-5.0.1.1.macos10.xamarin.universal.pkg
sudo installer -pkg MonoFramework-MDK-5.0.1.1.macos10.xamarin.universal.pkg -target /
# dotnet SDK
wget https://go.microsoft.com/fwlink/?linkid=843444 -O dotnet-dev-osx-x64.1.0.1.pkg
sudo installer -pkg dotnet-dev-osx-x64.1.0.1.pkg -target /
ln -s /usr/local/share/dotnet/dotnet /usr/local/bin/dotnet
dotnet --version # bootstrap dotnet SDK
# nvm
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.30.2/install.sh | bash
# bootstrap nvm silently & without terminating this script
set +ex
source ~/.nvm/nvm.sh
set -ex
# node
nvm install 4
nvm alias default 4
npm update npm -g
npm install -g node-pre-gyp
git submodule update --init

@ -35,4 +35,8 @@ cd $(dirname $0)/../../..
source tools/internal_ci/helper_scripts/prepare_build_linux_rc source tools/internal_ci/helper_scripts/prepare_build_linux_rc
# TODO(jtattermusch): install ruby on the internal_ci worker
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
curl -sSL https://get.rvm.io | bash -s stable --ruby
tools/run_tests/task_runner.py -f artifact linux tools/run_tests/task_runner.py -f artifact linux

@ -0,0 +1,39 @@
# Copyright 2017, 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.
# Config file for the internal CI (in protobuf text format)
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/linux/grpc_sanity.sh"
timeout_mins: 20
action {
define_artifacts {
regex: "**/*sponge_log.xml"
}
}

@ -35,4 +35,4 @@ cd $(dirname $0)/../../..
source tools/internal_ci/helper_scripts/prepare_build_linux_rc source tools/internal_ci/helper_scripts/prepare_build_linux_rc
tools/run_tests/run_tests.py -l sanity -c opt -t -x sponge_log.xml --use_docker --report_suite_name sanity_linux_opt tools/run_tests/run_tests_matrix.py -f basictests linux sanity opt --inner_jobs 16 -j 1 --internal_ci

@ -33,7 +33,7 @@ set -ex
# change to grpc repo root # change to grpc repo root
cd $(dirname $0)/../../.. cd $(dirname $0)/../../..
git submodule update --init source tools/internal_ci/helper_scripts/prepare_build_macos_rc
tools/run_tests/run_tests_matrix.py -f basictests macos --internal_ci || FAILED="true" tools/run_tests/run_tests_matrix.py -f basictests macos --internal_ci || FAILED="true"

@ -56,7 +56,7 @@ _BM_SPECS = {
}, },
'BM_PumpUnbalancedUnary_Trickle': { 'BM_PumpUnbalancedUnary_Trickle': {
'tpl': [], 'tpl': [],
'dyn': ['request_size', 'bandwidth_kilobits'], 'dyn': ['cli_req_size', 'svr_req_size', 'bandwidth_kilobits'],
}, },
'BM_ErrorStringOnNewError': { 'BM_ErrorStringOnNewError': {
'tpl': ['fixture'], 'tpl': ['fixture'],

@ -1755,6 +1755,23 @@
"third_party": false, "third_party": false,
"type": "target" "type": "target"
}, },
{
"deps": [
"gpr",
"gpr_test_util",
"grpc",
"grpc_test_util"
],
"headers": [],
"is_filegroup": false,
"language": "c",
"name": "num_external_connectivity_watchers_test",
"src": [
"test/core/surface/num_external_connectivity_watchers_test.c"
],
"third_party": false,
"type": "target"
},
{ {
"deps": [ "deps": [
"gpr", "gpr",

@ -1829,6 +1829,30 @@
"windows" "windows"
] ]
}, },
{
"args": [],
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [
"uv"
],
"flaky": false,
"gtest": false,
"language": "c",
"name": "num_external_connectivity_watchers_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
]
},
{ {
"args": [], "args": [],
"ci_platforms": [ "ci_platforms": [

@ -146,6 +146,7 @@ fi
############################ ############################
# Perform build operations # # Perform build operations #
############################ ############################
$PYTHON -m pip install virtualenv
# Instnatiate the virtualenv, preferring to do so from the relevant python # Instnatiate the virtualenv, preferring to do so from the relevant python
# version. Even if these commands fail (e.g. on Windows due to name conflicts) # version. Even if these commands fail (e.g. on Windows due to name conflicts)

@ -41,4 +41,5 @@ ruby src/ruby/end2end/sig_int_during_channel_watch_driver.rb || EXIT_CODE=1
ruby src/ruby/end2end/killed_client_thread_driver.rb || EXIT_CODE=1 ruby src/ruby/end2end/killed_client_thread_driver.rb || EXIT_CODE=1
ruby src/ruby/end2end/forking_client_driver.rb || EXIT_CODE=1 ruby src/ruby/end2end/forking_client_driver.rb || EXIT_CODE=1
ruby src/ruby/end2end/grpc_class_init_driver.rb || EXIT_CODE=1 ruby src/ruby/end2end/grpc_class_init_driver.rb || EXIT_CODE=1
ruby src/ruby/end2end/multiple_killed_watching_threads_driver.rb || EXIT_CODE=1
exit $EXIT_CODE exit $EXIT_CODE

@ -523,15 +523,14 @@ class NodeLanguage:
def scenarios(self): def scenarios(self):
# TODO(jtattermusch): make this scenario work # TODO(jtattermusch): make this scenario work
#yield _ping_pong_scenario( yield _ping_pong_scenario(
# 'node_generic_async_streaming_ping_pong', rpc_type='STREAMING', 'node_generic_streaming_ping_pong', rpc_type='STREAMING',
# client_type='ASYNC_CLIENT', server_type='ASYNC_GENERIC_SERVER', client_type='ASYNC_CLIENT', server_type='ASYNC_GENERIC_SERVER',
# use_generic_payload=True) use_generic_payload=True)
# TODO(jtattermusch): make this scenario work yield _ping_pong_scenario(
#yield _ping_pong_scenario( 'node_protobuf_streaming_ping_pong', rpc_type='STREAMING',
# 'node_protobuf_async_streaming_ping_pong', rpc_type='STREAMING', client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER')
# client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER')
yield _ping_pong_scenario( yield _ping_pong_scenario(
'node_protobuf_unary_ping_pong', rpc_type='UNARY', 'node_protobuf_unary_ping_pong', rpc_type='UNARY',
@ -564,29 +563,26 @@ class NodeLanguage:
secure=secure, secure=secure,
categories=[SCALABLE]) categories=[SCALABLE])
# TODO(murgatroid99): fix bugs with this scenario and re-enable it yield _ping_pong_scenario(
# yield _ping_pong_scenario( 'node_protobuf_unary_qps_unconstrained', rpc_type='UNARY',
# 'node_protobuf_async_unary_qps_unconstrained', rpc_type='UNARY', client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER',
# client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER', unconstrained_client='async',
# unconstrained_client='async', categories=[SCALABLE, SMOKETEST])
# categories=[SCALABLE, SMOKETEST])
# TODO(jtattermusch): make this scenario work yield _ping_pong_scenario(
#yield _ping_pong_scenario( 'node_protobuf_streaming_qps_unconstrained', rpc_type='STREAMING',
# 'node_protobuf_async_streaming_qps_unconstrained', rpc_type='STREAMING', client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER',
# client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER', unconstrained_client='async')
# unconstrained_client='async')
yield _ping_pong_scenario( yield _ping_pong_scenario(
'node_to_cpp_protobuf_async_unary_ping_pong', rpc_type='UNARY', 'node_to_cpp_protobuf_async_unary_ping_pong', rpc_type='UNARY',
client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER', client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER',
server_language='c++', async_server_threads=1) server_language='c++', async_server_threads=1)
# TODO(jtattermusch): make this scenario work yield _ping_pong_scenario(
#yield _ping_pong_scenario( 'node_to_cpp_protobuf_async_streaming_ping_pong', rpc_type='STREAMING',
# 'node_to_cpp_protobuf_async_streaming_ping_pong', rpc_type='STREAMING', client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER',
# client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER', server_language='c++', async_server_threads=1)
# server_language='c++', async_server_threads=1)
def __str__(self): def __str__(self):
return 'node' return 'node'

@ -276,8 +276,13 @@ class Job(object):
env = sanitized_environment(env) env = sanitized_environment(env)
self._start = time.time() self._start = time.time()
cmdline = self._spec.cmdline cmdline = self._spec.cmdline
if measure_cpu_costs: # The Unix time command is finicky when used with MSBuild, so we don't use it
# with jobs that run MSBuild.
global measure_cpu_costs
if measure_cpu_costs and not 'vsprojects\\build' in cmdline[0]:
cmdline = ['time', '-p'] + cmdline cmdline = ['time', '-p'] + cmdline
else:
measure_cpu_costs = False
try_start = lambda: subprocess.Popen(args=cmdline, try_start = lambda: subprocess.Popen(args=cmdline,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
stdout=self._tempfile, stdout=self._tempfile,

@ -672,7 +672,7 @@ class PythonLanguage(object):
if args.compiler == 'default': if args.compiler == 'default':
if os.name == 'nt': if os.name == 'nt':
return (python27_config,) return (python35_config,)
else: else:
return (python27_config, python34_config,) return (python27_config, python34_config,)
elif args.compiler == 'python2.7': elif args.compiler == 'python2.7':

@ -1350,6 +1350,17 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "no_server_test", "vcxproj\t
{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
EndProjectSection EndProjectSection
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "num_external_connectivity_watchers_test", "vcxproj\test\num_external_connectivity_watchers_test\num_external_connectivity_watchers_test.vcxproj", "{4E856E4A-7497-1B1A-1AED-D4C01E5D873A}"
ProjectSection(myProperties) = preProject
lib = "False"
EndProjectSection
ProjectSection(ProjectDependencies) = postProject
{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B} = {17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}
{29D16885-7228-4C31-81ED-5F9187C7F2A9} = {29D16885-7228-4C31-81ED-5F9187C7F2A9}
{EAB0A629-17A9-44DB-B5FF-E91A721FE037} = {EAB0A629-17A9-44DB-B5FF-E91A721FE037}
{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "parse_address_test", "vcxproj\test\parse_address_test\parse_address_test.vcxproj", "{EDEA8257-AEA8-1B0A-F95B-8D6CD7286463}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "parse_address_test", "vcxproj\test\parse_address_test\parse_address_test.vcxproj", "{EDEA8257-AEA8-1B0A-F95B-8D6CD7286463}"
ProjectSection(myProperties) = preProject ProjectSection(myProperties) = preProject
lib = "False" lib = "False"
@ -3750,6 +3761,22 @@ Global
{A66AC548-E2B9-74CD-293C-43526EE51DCE}.Release-DLL|Win32.Build.0 = Release|Win32 {A66AC548-E2B9-74CD-293C-43526EE51DCE}.Release-DLL|Win32.Build.0 = Release|Win32
{A66AC548-E2B9-74CD-293C-43526EE51DCE}.Release-DLL|x64.ActiveCfg = Release|x64 {A66AC548-E2B9-74CD-293C-43526EE51DCE}.Release-DLL|x64.ActiveCfg = Release|x64
{A66AC548-E2B9-74CD-293C-43526EE51DCE}.Release-DLL|x64.Build.0 = Release|x64 {A66AC548-E2B9-74CD-293C-43526EE51DCE}.Release-DLL|x64.Build.0 = Release|x64
{4E856E4A-7497-1B1A-1AED-D4C01E5D873A}.Debug|Win32.ActiveCfg = Debug|Win32
{4E856E4A-7497-1B1A-1AED-D4C01E5D873A}.Debug|x64.ActiveCfg = Debug|x64
{4E856E4A-7497-1B1A-1AED-D4C01E5D873A}.Release|Win32.ActiveCfg = Release|Win32
{4E856E4A-7497-1B1A-1AED-D4C01E5D873A}.Release|x64.ActiveCfg = Release|x64
{4E856E4A-7497-1B1A-1AED-D4C01E5D873A}.Debug|Win32.Build.0 = Debug|Win32
{4E856E4A-7497-1B1A-1AED-D4C01E5D873A}.Debug|x64.Build.0 = Debug|x64
{4E856E4A-7497-1B1A-1AED-D4C01E5D873A}.Release|Win32.Build.0 = Release|Win32
{4E856E4A-7497-1B1A-1AED-D4C01E5D873A}.Release|x64.Build.0 = Release|x64
{4E856E4A-7497-1B1A-1AED-D4C01E5D873A}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
{4E856E4A-7497-1B1A-1AED-D4C01E5D873A}.Debug-DLL|Win32.Build.0 = Debug|Win32
{4E856E4A-7497-1B1A-1AED-D4C01E5D873A}.Debug-DLL|x64.ActiveCfg = Debug|x64
{4E856E4A-7497-1B1A-1AED-D4C01E5D873A}.Debug-DLL|x64.Build.0 = Debug|x64
{4E856E4A-7497-1B1A-1AED-D4C01E5D873A}.Release-DLL|Win32.ActiveCfg = Release|Win32
{4E856E4A-7497-1B1A-1AED-D4C01E5D873A}.Release-DLL|Win32.Build.0 = Release|Win32
{4E856E4A-7497-1B1A-1AED-D4C01E5D873A}.Release-DLL|x64.ActiveCfg = Release|x64
{4E856E4A-7497-1B1A-1AED-D4C01E5D873A}.Release-DLL|x64.Build.0 = Release|x64
{EDEA8257-AEA8-1B0A-F95B-8D6CD7286463}.Debug|Win32.ActiveCfg = Debug|Win32 {EDEA8257-AEA8-1B0A-F95B-8D6CD7286463}.Debug|Win32.ActiveCfg = Debug|Win32
{EDEA8257-AEA8-1B0A-F95B-8D6CD7286463}.Debug|x64.ActiveCfg = Debug|x64 {EDEA8257-AEA8-1B0A-F95B-8D6CD7286463}.Debug|x64.ActiveCfg = Debug|x64
{EDEA8257-AEA8-1B0A-F95B-8D6CD7286463}.Release|Win32.ActiveCfg = Release|Win32 {EDEA8257-AEA8-1B0A-F95B-8D6CD7286463}.Release|Win32.ActiveCfg = Release|Win32

@ -0,0 +1,199 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{4E856E4A-7497-1B1A-1AED-D4C01E5D873A}</ProjectGuid>
<IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
<IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
<PlatformToolset>v100</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
<PlatformToolset>v110</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(SolutionDir)\..\vsprojects\global.props" />
<Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
<Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
<Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<TargetName>num_external_connectivity_watchers_test</TargetName>
<Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
<Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
<Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
<Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<TargetName>num_external_connectivity_watchers_test</TargetName>
<Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
<Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
<Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
<Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
<MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
<GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
<MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
<GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
<MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
<GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
<MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
<GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="$(SolutionDir)\..\test\core\surface\num_external_connectivity_watchers_test.c">
</ClCompile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
<Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
</ProjectReference>
<ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
<Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
</ProjectReference>
<ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
<Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
</ProjectReference>
<ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
<Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
<Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
<Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
<Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
<Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
<Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
<Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
<Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
</Target>
</Project>

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="$(SolutionDir)\..\test\core\surface\num_external_connectivity_watchers_test.c">
<Filter>test\core\surface</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="test">
<UniqueIdentifier>{9557f01e-947a-775e-4540-bf9a1fd9b19a}</UniqueIdentifier>
</Filter>
<Filter Include="test\core">
<UniqueIdentifier>{2b3a6de2-5820-e21f-5b39-66012c94bfbb}</UniqueIdentifier>
</Filter>
<Filter Include="test\core\surface">
<UniqueIdentifier>{e3f23659-fc16-a4cc-a9e2-c73b625c38f5}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
Loading…
Cancel
Save