Merge pull request #11154 from kpayson64/hack_for_disconnects

Reconnect disconnected channels automatically
diff --git a/BUILD b/BUILD
index 4087216..166e6ba 100644
--- a/BUILD
+++ b/BUILD
@@ -40,9 +40,9 @@
 # This should be updated along with build.yaml
 g_stands_for = "gentle"
 
-core_version = "3.0.0-pre1"
+core_version = "3.0.0"
 
-version = "1.3.0-pre1"
+version = "1.3.5"
 
 grpc_cc_library(
     name = "gpr",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 81dba56..cff8f85 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -39,7 +39,7 @@
 cmake_minimum_required(VERSION 2.8)
 
 set(PACKAGE_NAME      "grpc")
-set(PACKAGE_VERSION   "1.3.0-pre1")
+set(PACKAGE_VERSION   "1.3.5")
 set(PACKAGE_STRING    "${PACKAGE_NAME} ${PACKAGE_VERSION}")
 set(PACKAGE_TARNAME   "${PACKAGE_NAME}-${PACKAGE_VERSION}")
 set(PACKAGE_BUGREPORT "https://github.com/grpc/grpc/issues/")
@@ -475,6 +475,7 @@
 add_dependencies(buildtests_c multiple_server_queues_test)
 add_dependencies(buildtests_c murmur_hash_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 percent_encoding_test)
 if(_gRPC_PLATFORM_LINUX)
@@ -7580,6 +7581,37 @@
 endif (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
   test/core/client_channel/parse_address_test.c
 )
diff --git a/Makefile b/Makefile
index 01e06f1..77206ba 100644
--- a/Makefile
+++ b/Makefile
@@ -419,9 +419,9 @@
 Q = @
 endif
 
-CORE_VERSION = 3.0.0-pre1
-CPP_VERSION = 1.3.0-pre1
-CSHARP_VERSION = 1.3.0-pre1
+CORE_VERSION = 3.0.0
+CPP_VERSION = 1.3.5
+CSHARP_VERSION = 1.3.5
 
 CPPFLAGS_NO_ARCH += $(addprefix -I, $(INCLUDES)) $(addprefix -D, $(DEFINES))
 CPPFLAGS += $(CPPFLAGS_NO_ARCH) $(ARCH_FLAGS)
@@ -1056,6 +1056,7 @@
 nanopb_fuzzer_response_test: $(BINDIR)/$(CONFIG)/nanopb_fuzzer_response_test
 nanopb_fuzzer_serverlist_test: $(BINDIR)/$(CONFIG)/nanopb_fuzzer_serverlist_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
 percent_decode_fuzzer: $(BINDIR)/$(CONFIG)/percent_decode_fuzzer
 percent_encode_fuzzer: $(BINDIR)/$(CONFIG)/percent_encode_fuzzer
@@ -1427,6 +1428,7 @@
   $(BINDIR)/$(CONFIG)/multiple_server_queues_test \
   $(BINDIR)/$(CONFIG)/murmur_hash_test \
   $(BINDIR)/$(CONFIG)/no_server_test \
+  $(BINDIR)/$(CONFIG)/num_external_connectivity_watchers_test \
   $(BINDIR)/$(CONFIG)/parse_address_test \
   $(BINDIR)/$(CONFIG)/percent_encoding_test \
   $(BINDIR)/$(CONFIG)/pollset_set_test \
@@ -1883,6 +1885,8 @@
 	$(Q) $(BINDIR)/$(CONFIG)/murmur_hash_test || ( echo test murmur_hash_test failed ; exit 1 )
 	$(E) "[RUN]     Testing no_server_test"
 	$(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"
 	$(Q) $(BINDIR)/$(CONFIG)/parse_address_test || ( echo test parse_address_test failed ; exit 1 )
 	$(E) "[RUN]     Testing percent_encoding_test"
@@ -8241,8 +8245,8 @@
 
 LIBARES_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBARES_SRC))))
 
-$(LIBARES_OBJS): CPPFLAGS += -Ithird_party/cares -Ithird_party/cares/cares $(if $(subst Linux,,$(SYSTEM)),,-Ithird_party/cares/config_linux) $(if $(subst Darwin,,$(SYSTEM)),,-Ithird_party/cares/config_darwin) -fvisibility=hidden -D_GNU_SOURCE -DWIN32_LEAN_AND_MEAN -D_HAS_EXCEPTIONS=0 -DNOMINMAX -DHAVE_CONFIG_H
-$(LIBARES_OBJS): CFLAGS += -Wno-sign-conversion -Wno-invalid-source-encoding
+$(LIBARES_OBJS): CPPFLAGS += -Ithird_party/cares -Ithird_party/cares/cares $(if $(subst Linux,,$(SYSTEM)),,-Ithird_party/cares/config_linux) $(if $(subst Darwin,,$(SYSTEM)),,-Ithird_party/cares/config_darwin) -fvisibility=hidden -D_GNU_SOURCE -DWIN32_LEAN_AND_MEAN -D_HAS_EXCEPTIONS=0 -DNOMINMAX $(if $(subst MINGW32,,$(SYSTEM)),-DHAVE_CONFIG_H,)
+$(LIBARES_OBJS): CFLAGS += -Wno-sign-conversion $(if $(subst MINGW32,,$(SYSTEM)),-Wno-invalid-source-encoding,)
 
 $(LIBDIR)/$(CONFIG)/libares.a: $(ZLIB_DEP)  $(LIBARES_OBJS) 
 	$(E) "[AR]      Creating $@"
@@ -11810,6 +11814,38 @@
 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 = \
     test/core/client_channel/parse_address_test.c \
 
diff --git a/PYTHON-MANIFEST.in b/PYTHON-MANIFEST.in
index 8465305..bb76b38 100644
--- a/PYTHON-MANIFEST.in
+++ b/PYTHON-MANIFEST.in
@@ -7,7 +7,7 @@
 graft third_party/boringssl
 graft third_party/nanopb
 graft third_party/zlib
-graft third_party/c-ares
+graft third_party/cares
 include src/python/grpcio/_spawn_patch.py
 include src/python/grpcio/commands.py
 include src/python/grpcio/grpc_version.py
diff --git a/Rakefile b/Rakefile
index cc02aa1..7f8d3a2 100755
--- a/Rakefile
+++ b/Rakefile
@@ -80,7 +80,7 @@
   grpc_config = ENV['GRPC_CONFIG'] || 'opt'
   verbose = ENV['V'] || '0'
 
-  env = 'CPPFLAGS="-D_WIN32_WINNT=0x600 -DUNICODE -D_UNICODE -Wno-unused-variable -Wno-unused-result" '
+  env = 'CPPFLAGS="-D_WIN32_WINNT=0x600 -DUNICODE -D_UNICODE -Wno-unused-variable -Wno-unused-result -DCARES_STATICLIB" '
   env += 'LDFLAGS=-static '
   env += 'SYSTEM=MINGW32 '
   env += 'EMBED_ZLIB=true '
diff --git a/WORKSPACE b/WORKSPACE
index 5a3c4de..5ba82f3 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -77,12 +77,6 @@
     path = "third_party/gflags",
 )
 
-git_repository(
-    name   = "mongoose_repo",
-    commit = "4120a97945b41195a6223a600dae8e3b19bed19e",
-    remote = "https://github.com/makdharma/mongoose.git"
-)
-
 new_local_repository(
     name = "submodule_benchmark",
     path = "third_party/benchmark",
diff --git a/binding.gyp b/binding.gyp
index 16334a2..a449730 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -39,15 +39,16 @@
 {
   'variables': {
     'runtime%': 'node',
-    # UV integration in C core is enabled by default. It can be disabled
-    # by setting this argument to anything else.
-    'grpc_uv%': 'true',
     # Some Node installations use the system installation of OpenSSL, and on
     # some systems, the system OpenSSL still does not have ALPN support. This
     # will let users recompile gRPC to work without ALPN.
     'grpc_alpn%': 'true',
     # Indicates that the library should be built with gcov.
-    'grpc_gcov%': 'false'
+    'grpc_gcov%': 'false',
+    # Indicates that the library should be built with compatibility for musl
+    # libc, so that it can run on Alpine Linux. This is only necessary if not
+    # building on Alpine Linux
+    'grpc_alpine%': 'false'
   },
   'target_defaults': {
     'configurations': {
@@ -86,17 +87,11 @@
       'include'
     ],
     'defines': [
-      'GPR_BACKWARDS_COMPATIBILITY_MODE'
+      'GPR_BACKWARDS_COMPATIBILITY_MODE',
+      'GRPC_ARES=0',
+      'GRPC_UV'
     ],
     'conditions': [
-      ['grpc_uv=="true"', {
-        'defines': [
-          'GRPC_ARES=0',
-          # Disabling this while bugs are ironed out. Uncomment this to
-          # re-enable libuv integration in C core.
-          'GRPC_UV'
-        ]
-      }],
       ['grpc_gcov=="true"', {
         'cflags': [
             '-O0',
@@ -115,6 +110,11 @@
             '-rdynamic',
         ],
       }],
+      ['grpc_alpine=="true"', {
+        'defines': [
+          'GPR_MUSL_LIBC_COMPAT'
+        ]
+      }],
       ['OS!="win" and runtime=="electron"', {
         "defines": [
           'OPENSSL_NO_THREADS'
@@ -535,6 +535,10 @@
             }
           ]
         },
+      ]
+    }],
+    ['OS == "win"', {
+      'targets': [
         # Only want to compile zlib under Windows
         {
           'cflags': [
@@ -569,7 +573,6 @@
     }]
   ],
   'targets': [
-
     {
       'cflags': [
         '-std=c99',
@@ -648,7 +651,6 @@
       'type': 'static_library',
       'dependencies': [
         'gpr',
-        'node_modules/cares/deps/cares/cares.gyp:cares',
       ],
       'sources': [
         'src/core/lib/surface/init.c',
@@ -938,20 +940,16 @@
         "src/node/ext/call_credentials.cc",
         "src/node/ext/channel.cc",
         "src/node/ext/channel_credentials.cc",
-        "src/node/ext/completion_queue_threadpool.cc",
-        "src/node/ext/completion_queue_uv.cc",
+        "src/node/ext/completion_queue.cc",
         "src/node/ext/node_grpc.cc",
         "src/node/ext/server.cc",
         "src/node/ext/server_credentials.cc",
-        "src/node/ext/server_generic.cc",
-        "src/node/ext/server_uv.cc",
         "src/node/ext/slice.cc",
         "src/node/ext/timeval.cc",
       ],
       "dependencies": [
         "grpc",
         "gpr",
-        "node_modules/cares/deps/cares/cares.gyp:cares",
       ]
     },
     {
diff --git a/build.yaml b/build.yaml
index 781118f..3af5906 100644
--- a/build.yaml
+++ b/build.yaml
@@ -12,9 +12,9 @@
   '#08': Use "-preN" suffixes to identify pre-release versions
   '#09': Per-language overrides are possible with (eg) ruby_version tag here
   '#10': See the expand_version.py for all the quirks here
-  core_version: 3.0.0-pre1
+  core_version: 3.0.0
   g_stands_for: gentle
-  version: 1.3.0-pre1
+  version: 1.3.5
 filegroups:
 - name: census
   public_headers:
@@ -2586,6 +2586,18 @@
   - grpc
   - gpr_test_util
   - 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
   build: test
   language: c
@@ -4407,10 +4419,11 @@
       UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1:suppressions=tools/ubsan_suppressions.txt
 defaults:
   ares:
-    CFLAGS: -Wno-sign-conversion -Wno-invalid-source-encoding
+    CFLAGS: -Wno-sign-conversion $(if $(subst MINGW32,,$(SYSTEM)),-Wno-invalid-source-encoding,)
     CPPFLAGS: -Ithird_party/cares -Ithird_party/cares/cares $(if $(subst Linux,,$(SYSTEM)),,-Ithird_party/cares/config_linux)
       $(if $(subst Darwin,,$(SYSTEM)),,-Ithird_party/cares/config_darwin) -fvisibility=hidden
-      -D_GNU_SOURCE -DWIN32_LEAN_AND_MEAN -D_HAS_EXCEPTIONS=0 -DNOMINMAX -DHAVE_CONFIG_H
+      -D_GNU_SOURCE -DWIN32_LEAN_AND_MEAN -D_HAS_EXCEPTIONS=0 -DNOMINMAX $(if $(subst
+      MINGW32,,$(SYSTEM)),-DHAVE_CONFIG_H,)
   benchmark:
     CPPFLAGS: -Ithird_party/benchmark/include -DHAVE_POSIX_REGEX
   boringssl:
@@ -4456,13 +4469,10 @@
   - src/node/ext/call_credentials.cc
   - src/node/ext/channel.cc
   - src/node/ext/channel_credentials.cc
-  - src/node/ext/completion_queue_threadpool.cc
-  - src/node/ext/completion_queue_uv.cc
+  - src/node/ext/completion_queue.cc
   - src/node/ext/node_grpc.cc
   - src/node/ext/server.cc
   - src/node/ext/server_credentials.cc
-  - src/node/ext/server_generic.cc
-  - src/node/ext/server_uv.cc
   - src/node/ext/slice.cc
   - src/node/ext/timeval.cc
 openssl_fallback:
@@ -4473,7 +4483,6 @@
   deps:
   - grpc
   - gpr
-  - ares
   - boringssl
   headers:
   - src/php/ext/grpc/byte_buffer.h
diff --git a/config.m4 b/config.m4
index 74b60c924..f80a205 100644
--- a/config.m4
+++ b/config.m4
@@ -8,8 +8,6 @@
   PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/include)
   PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/src/php/ext/grpc)
   PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/third_party/boringssl/include)
-  PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/third_party/cares)
-  PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/third_party/cares/cares)
 
   LIBS="-lpthread $LIBS"
 
@@ -21,10 +19,8 @@
 
   case $host in
     *darwin*)
-      PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/third_party/cares/config_darwin)
       ;;
     *)
-      PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/third_party/cares/config_linux)
       PHP_ADD_LIBRARY(rt,,GRPC_SHARED_LIBADD)
       PHP_ADD_LIBRARY(rt)
       ;;
@@ -626,59 +622,10 @@
     third_party/boringssl/ssl/tls13_server.c \
     third_party/boringssl/ssl/tls_method.c \
     third_party/boringssl/ssl/tls_record.c \
-    third_party/cares/cares/ares__close_sockets.c \
-    third_party/cares/cares/ares__get_hostent.c \
-    third_party/cares/cares/ares__read_line.c \
-    third_party/cares/cares/ares__timeval.c \
-    third_party/cares/cares/ares_cancel.c \
-    third_party/cares/cares/ares_create_query.c \
-    third_party/cares/cares/ares_data.c \
-    third_party/cares/cares/ares_destroy.c \
-    third_party/cares/cares/ares_expand_name.c \
-    third_party/cares/cares/ares_expand_string.c \
-    third_party/cares/cares/ares_fds.c \
-    third_party/cares/cares/ares_free_hostent.c \
-    third_party/cares/cares/ares_free_string.c \
-    third_party/cares/cares/ares_getenv.c \
-    third_party/cares/cares/ares_gethostbyaddr.c \
-    third_party/cares/cares/ares_gethostbyname.c \
-    third_party/cares/cares/ares_getnameinfo.c \
-    third_party/cares/cares/ares_getopt.c \
-    third_party/cares/cares/ares_getsock.c \
-    third_party/cares/cares/ares_init.c \
-    third_party/cares/cares/ares_library_init.c \
-    third_party/cares/cares/ares_llist.c \
-    third_party/cares/cares/ares_mkquery.c \
-    third_party/cares/cares/ares_nowarn.c \
-    third_party/cares/cares/ares_options.c \
-    third_party/cares/cares/ares_parse_a_reply.c \
-    third_party/cares/cares/ares_parse_aaaa_reply.c \
-    third_party/cares/cares/ares_parse_mx_reply.c \
-    third_party/cares/cares/ares_parse_naptr_reply.c \
-    third_party/cares/cares/ares_parse_ns_reply.c \
-    third_party/cares/cares/ares_parse_ptr_reply.c \
-    third_party/cares/cares/ares_parse_soa_reply.c \
-    third_party/cares/cares/ares_parse_srv_reply.c \
-    third_party/cares/cares/ares_parse_txt_reply.c \
-    third_party/cares/cares/ares_platform.c \
-    third_party/cares/cares/ares_process.c \
-    third_party/cares/cares/ares_query.c \
-    third_party/cares/cares/ares_search.c \
-    third_party/cares/cares/ares_send.c \
-    third_party/cares/cares/ares_strcasecmp.c \
-    third_party/cares/cares/ares_strdup.c \
-    third_party/cares/cares/ares_strerror.c \
-    third_party/cares/cares/ares_timeout.c \
-    third_party/cares/cares/ares_version.c \
-    third_party/cares/cares/ares_writev.c \
-    third_party/cares/cares/bitncmp.c \
-    third_party/cares/cares/inet_net_pton.c \
-    third_party/cares/cares/inet_ntop.c \
-    third_party/cares/cares/windows_port.c \
     , $ext_shared, , -Wall -Werror \
     -Wno-parentheses-equality -Wno-unused-value -std=c11 \
     -fvisibility=hidden -DOPENSSL_NO_ASM -D_GNU_SOURCE -DWIN32_LEAN_AND_MEAN \
-    -D_HAS_EXCEPTIONS=0 -DNOMINMAX)
+    -D_HAS_EXCEPTIONS=0 -DNOMINMAX -DGRPC_ARES=0)
 
   PHP_ADD_BUILD_DIR($ext_builddir/src/php/ext/grpc)
 
@@ -771,6 +718,5 @@
   PHP_ADD_BUILD_DIR($ext_builddir/third_party/boringssl/crypto/x509)
   PHP_ADD_BUILD_DIR($ext_builddir/third_party/boringssl/crypto/x509v3)
   PHP_ADD_BUILD_DIR($ext_builddir/third_party/boringssl/ssl)
-  PHP_ADD_BUILD_DIR($ext_builddir/third_party/cares/cares)
   PHP_ADD_BUILD_DIR($ext_builddir/third_party/nanopb)
 fi
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index bd129f7..5d1ba61 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -37,7 +37,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-Core'
-  version = '1.3.0-pre1'
+  version = '1.3.5'
   s.version  = version
   s.summary  = 'Core cross-platform gRPC library, written in C'
   s.homepage = 'http://www.grpc.io'
diff --git a/gRPC-ProtoRPC.podspec b/gRPC-ProtoRPC.podspec
index de30032..e60ea7c 100644
--- a/gRPC-ProtoRPC.podspec
+++ b/gRPC-ProtoRPC.podspec
@@ -36,7 +36,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-ProtoRPC'
-  version = '1.3.0-pre1'
+  version = '1.3.5'
   s.version  = version
   s.summary  = 'RPC library for Protocol Buffers, based on gRPC'
   s.homepage = 'http://www.grpc.io'
diff --git a/gRPC-RxLibrary.podspec b/gRPC-RxLibrary.podspec
index 642b4ca..d82130a 100644
--- a/gRPC-RxLibrary.podspec
+++ b/gRPC-RxLibrary.podspec
@@ -36,7 +36,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-RxLibrary'
-  version = '1.3.0-pre1'
+  version = '1.3.5'
   s.version  = version
   s.summary  = 'Reactive Extensions library for iOS/OSX.'
   s.homepage = 'http://www.grpc.io'
diff --git a/gRPC.podspec b/gRPC.podspec
index ac3057e..5aa8a86 100644
--- a/gRPC.podspec
+++ b/gRPC.podspec
@@ -35,7 +35,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC'
-  version = '1.3.0-pre1'
+  version = '1.3.5'
   s.version  = version
   s.summary  = 'gRPC client library for iOS/OSX'
   s.homepage = 'http://www.grpc.io'
diff --git a/grpc.def b/grpc.def
index 1589316..534a23d 100644
--- a/grpc.def
+++ b/grpc.def
@@ -65,6 +65,7 @@
     grpc_alarm_cancel
     grpc_alarm_destroy
     grpc_channel_check_connectivity_state
+    grpc_channel_num_external_connectivity_watchers
     grpc_channel_watch_connectivity_state
     grpc_channel_create_call
     grpc_channel_ping
diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h
index e088435..bdb8617 100644
--- a/include/grpc/grpc.h
+++ b/include/grpc/grpc.h
@@ -225,6 +225,12 @@
 GRPCAPI grpc_connectivity_state grpc_channel_check_connectivity_state(
     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.
     Once the channel connectivity state is different from last_observed_state,
     tag will be enqueued on cq with success=1.
diff --git a/include/grpc/impl/codegen/port_platform.h b/include/grpc/impl/codegen/port_platform.h
index 813e08b..a149279 100644
--- a/include/grpc/impl/codegen/port_platform.h
+++ b/include/grpc/impl/codegen/port_platform.h
@@ -189,7 +189,7 @@
 #ifdef __GLIBC__
 #define GPR_POSIX_CRASH_HANDLER 1
 #else /* musl libc */
-#define GRPC_MSG_IOVLEN_TYPE int
+#define GPR_MUSL_LIBC_COMPAT 1
 #endif
 #elif defined(__APPLE__)
 #include <Availability.h>
diff --git a/package.json b/package.json
index d4f7b56..c83f51c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "grpc",
-  "version": "1.3.0-pre1",
+  "version": "1.3.5",
   "author": "Google Inc.",
   "description": "gRPC Library for Node",
   "homepage": "http://www.grpc.io/",
@@ -34,8 +34,7 @@
     "lodash": "^4.15.0",
     "nan": "^2.0.0",
     "node-pre-gyp": "^0.6.0",
-    "protobufjs": "^6.7.0",
-    "cares": "^1.1.5"
+    "protobufjs": "^5.0.0"
   },
   "devDependencies": {
     "async": "^2.0.1",
diff --git a/package.xml b/package.xml
index 54a7bae..1c91f1c 100644
--- a/package.xml
+++ b/package.xml
@@ -10,11 +10,11 @@
   <email>grpc-packages@google.com</email>
   <active>yes</active>
  </lead>
- <date>2017-03-01</date>
+ <date>2017-05-05</date>
  <time>16:06:07</time>
  <version>
-  <release>1.3.0RC1</release>
-  <api>1.3.0RC1</api>
+  <release>1.3.5</release>
+  <api>1.3.5</api>
  </version>
  <stability>
   <release>beta</release>
@@ -22,8 +22,7 @@
  </stability>
  <license>BSD</license>
  <notes>
-- Added arg info macros #9751
-- Updated codegen to be consistent with protobuf #9492
+- Fixed some memory leaks #9559, #10996
  </notes>
  <contents>
   <dir baseinstalldir="/" name="/">
@@ -1027,79 +1026,7 @@
     <file baseinstalldir="/" name="third_party/boringssl/ssl/tls13_server.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/ssl/tls_method.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/ssl/tls_record.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_data.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_dns.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_getenv.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_getopt.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_inet_net_pton.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_iphlpapi.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_ipv6.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_library_init.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_llist.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_nowarn.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_platform.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_private.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_rules.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_setup.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_strcasecmp.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_strdup.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_version.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/bitncmp.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/config-win32.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/setup_once.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/ares_build.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/config_linux/ares_config.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/config_darwin/ares_config.h" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares__close_sockets.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares__get_hostent.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares__read_line.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares__timeval.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_cancel.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_create_query.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_data.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_destroy.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_expand_name.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_expand_string.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_fds.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_free_hostent.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_free_string.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_getenv.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_gethostbyaddr.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_gethostbyname.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_getnameinfo.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_getopt.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_getsock.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_init.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_library_init.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_llist.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_mkquery.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_nowarn.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_options.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_parse_a_reply.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_parse_aaaa_reply.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_parse_mx_reply.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_parse_naptr_reply.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_parse_ns_reply.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_parse_ptr_reply.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_parse_soa_reply.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_parse_srv_reply.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_parse_txt_reply.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_platform.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_process.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_query.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_search.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_send.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_strcasecmp.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_strdup.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_strerror.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_timeout.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_version.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/ares_writev.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/bitncmp.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/inet_net_pton.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/inet_ntop.c" role="src" />
-    <file baseinstalldir="/" name="third_party/cares/cares/windows_port.c" role="src" />
+    <file name="LICENSE" role="doc" />
   </dir>
  </contents>
  <dependencies>
@@ -1415,5 +1342,21 @@
 - Updated codegen to be consistent with protobuf #9492
    </notes>
   </release>
+  <release>
+   <version>
+    <release>1.2.0</release>
+    <api>1.2.0</api>
+   </version>
+   <stability>
+    <release>stable</release>
+    <api>stable</api>
+   </stability>
+   <date>2017-03-20</date>
+   <license>BSD</license>
+   <notes>
+- Added arg info macros #9751
+- Updated codegen to be consistent with protobuf #9492
+   </notes>
+  </release>
  </changelog>
 </package>
diff --git a/setup.py b/setup.py
index e050646..b1ac4f3 100644
--- a/setup.py
+++ b/setup.py
@@ -141,6 +141,8 @@
 CYTHON_HELPER_C_FILES = ()
 
 CORE_C_FILES = tuple(grpc_core_dependencies.CORE_SOURCE_FILES)
+if "win32" in sys.platform and "64bit" in platform.architecture()[0]:
+  CORE_C_FILES = filter(lambda x: 'third_party/cares' not in x, CORE_C_FILES)
 
 EXTENSION_INCLUDE_DIRECTORIES = (
     (PYTHON_STEM,) + CORE_INCLUDE + BORINGSSL_INCLUDE + ZLIB_INCLUDE +
@@ -160,7 +162,9 @@
 if "win32" in sys.platform:
   DEFINE_MACROS += (('WIN32_LEAN_AND_MEAN', 1), ('CARES_STATICLIB', 1),)
   if '64bit' in platform.architecture()[0]:
-    DEFINE_MACROS += (('MS_WIN64', 1),)
+    # TODO(zyc): Re-enble c-ares on x64 windows after fixing the
+    # ares_library_init compilation issue
+    DEFINE_MACROS += (('MS_WIN64', 1), ('GRPC_ARES', 0),)
   elif sys.version_info >= (3, 5):
     # For some reason, this is needed to get access to inet_pton/inet_ntop
     # on msvc, but only for 32 bits
diff --git a/src/core/ext/filters/client_channel/channel_connectivity.c b/src/core/ext/filters/client_channel/channel_connectivity.c
index 62f58fb..5f3ffd4 100644
--- a/src/core/ext/filters/client_channel/channel_connectivity.c
+++ b/src/core/ext/filters/client_channel/channel_connectivity.c
@@ -67,9 +67,8 @@
 
 typedef enum {
   WAITING,
-  CALLING_BACK,
+  READY_TO_CALL_BACK,
   CALLING_BACK_AND_FINISHED,
-  CALLED_BACK
 } callback_phase;
 
 typedef struct {
@@ -77,11 +76,13 @@
   callback_phase phase;
   grpc_closure on_complete;
   grpc_closure on_timeout;
+  grpc_closure watcher_timer_init;
   grpc_timer alarm;
   grpc_connectivity_state state;
   grpc_completion_queue *cq;
   grpc_cq_completion completion_storage;
   grpc_channel *channel;
+  grpc_error *error;
   void *tag;
 } state_watcher;
 
@@ -105,11 +106,8 @@
   gpr_mu_lock(&w->mu);
   switch (w->phase) {
     case WAITING:
-    case CALLED_BACK:
+    case READY_TO_CALL_BACK:
       GPR_UNREACHABLE_CODE(return );
-    case CALLING_BACK:
-      w->phase = CALLED_BACK;
-      break;
     case CALLING_BACK_AND_FINISHED:
       delete = 1;
       break;
@@ -123,10 +121,14 @@
 
 static void partly_done(grpc_exec_ctx *exec_ctx, state_watcher *w,
                         bool due_to_completion, grpc_error *error) {
-  int delete = 0;
-
   if (due_to_completion) {
     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);
@@ -147,25 +149,27 @@
   }
   switch (w->phase) {
     case WAITING:
-      w->phase = CALLING_BACK;
-      grpc_cq_end_op(exec_ctx, w->cq, w->tag, GRPC_ERROR_REF(error),
-                     finished_completion, w, &w->completion_storage);
+      GRPC_ERROR_REF(error);
+      w->error = error;
+      w->phase = READY_TO_CALL_BACK;
       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;
+      grpc_cq_end_op(exec_ctx, w->cq, w->tag, w->error, finished_completion, w,
+                     &w->completion_storage);
       break;
     case CALLING_BACK_AND_FINISHED:
       GPR_UNREACHABLE_CODE(return );
-    case CALLED_BACK:
-      delete = 1;
       break;
   }
   gpr_mu_unlock(&w->mu);
 
-  if (delete) {
-    delete_state_watcher(exec_ctx, w);
-  }
-
   GRPC_ERROR_UNREF(error);
 }
 
@@ -179,6 +183,28 @@
   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(
     grpc_channel *channel, grpc_connectivity_state last_observed_state,
     gpr_timespec deadline, grpc_completion_queue *cq, void *tag) {
@@ -208,16 +234,19 @@
   w->cq = cq;
   w->tag = tag;
   w->channel = channel;
+  w->error = NULL;
 
-  grpc_timer_init(&exec_ctx, &w->alarm,
-                  gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC),
-                  &w->on_timeout, gpr_now(GPR_CLOCK_MONOTONIC));
+  watcher_timer_init_arg *wa = gpr_malloc(sizeof(watcher_timer_init_arg));
+  wa->w = w;
+  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) {
     GRPC_CHANNEL_INTERNAL_REF(channel, "watch_channel_connectivity");
-    grpc_client_channel_watch_connectivity_state(&exec_ctx, client_channel_elem,
-                                                 grpc_cq_pollset(cq), &w->state,
-                                                 &w->on_complete);
+    grpc_client_channel_watch_connectivity_state(
+        &exec_ctx, client_channel_elem, grpc_cq_pollset(cq), &w->state,
+        &w->on_complete, &w->watcher_timer_init);
   } else {
     abort();
   }
diff --git a/src/core/ext/filters/client_channel/client_channel.c b/src/core/ext/filters/client_channel/client_channel.c
index 83e3b8f..4bd153e 100644
--- a/src/core/ext/filters/client_channel/client_channel.c
+++ b/src/core/ext/filters/client_channel/client_channel.c
@@ -174,6 +174,8 @@
   return value;
 }
 
+struct external_connectivity_watcher;
+
 /*************************************************************************
  * CHANNEL-WIDE FUNCTIONS
  */
@@ -209,6 +211,11 @@
   /** interested parties (owned) */
   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
      to be instantaneously available */
   gpr_mu info_mu;
@@ -632,6 +639,12 @@
   // Initialize data members.
   chand->combiner = grpc_combiner_create(NULL);
   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;
   grpc_closure_init(&chand->on_resolver_result_changed,
                     on_resolver_result_changed_locked, chand,
@@ -718,6 +731,7 @@
   grpc_pollset_set_destroy(exec_ctx, chand->interested_parties);
   GRPC_COMBINER_UNREF(exec_ctx, chand->combiner, "client_channel");
   gpr_mu_destroy(&chand->info_mu);
+  gpr_mu_destroy(&chand->external_connectivity_watcher_list_mu);
 }
 
 /*************************************************************************
@@ -1361,14 +1375,79 @@
   return out;
 }
 
-typedef struct {
+typedef struct external_connectivity_watcher {
   channel_data *chand;
   grpc_pollset *pollset;
   grpc_closure *on_complete;
+  grpc_closure *watcher_timer_init;
   grpc_connectivity_state *state;
   grpc_closure my_closure;
+  struct external_connectivity_watcher *next;
 } 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,
                                        grpc_error *error) {
   external_connectivity_watcher *w = arg;
@@ -1377,6 +1456,7 @@
                                w->pollset);
   GRPC_CHANNEL_STACK_UNREF(exec_ctx, w->chand->owning_stack,
                            "external_connectivity_watcher");
+  external_connectivity_watcher_list_remove(w->chand, w);
   gpr_free(w);
   grpc_closure_run(exec_ctx, follow_up, GRPC_ERROR_REF(error));
 }
@@ -1384,21 +1464,42 @@
 static void watch_connectivity_state_locked(grpc_exec_ctx *exec_ctx, void *arg,
                                             grpc_error *error_ignored) {
   external_connectivity_watcher *w = arg;
-  grpc_closure_init(&w->my_closure, on_external_watch_complete, w,
-                    grpc_schedule_on_exec_ctx);
-  grpc_connectivity_state_notify_on_state_change(
-      exec_ctx, &w->chand->state_tracker, w->state, &w->my_closure);
+  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_schedule_on_exec_ctx);
+    grpc_connectivity_state_notify_on_state_change(
+        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(
     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) {
   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->pollset = pollset;
   w->on_complete = on_complete;
   w->state = state;
+  w->watcher_timer_init = watcher_timer_init;
+
   grpc_pollset_set_add_pollset(exec_ctx, chand->interested_parties, pollset);
   GRPC_CHANNEL_STACK_REF(w->chand->owning_stack,
                          "external_connectivity_watcher");
diff --git a/src/core/ext/filters/client_channel/client_channel.h b/src/core/ext/filters/client_channel/client_channel.h
index 8d2490e..356a7ab 100644
--- a/src/core/ext/filters/client_channel/client_channel.h
+++ b/src/core/ext/filters/client_channel/client_channel.h
@@ -53,9 +53,13 @@
 grpc_connectivity_state grpc_client_channel_check_connectivity_state(
     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(
     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 */
 grpc_subchannel_call *grpc_client_channel_get_subchannel_call(
diff --git a/src/core/lib/iomgr/port.h b/src/core/lib/iomgr/port.h
index 269dc35..2a553f4 100644
--- a/src/core/lib/iomgr/port.h
+++ b/src/core/lib/iomgr/port.h
@@ -88,6 +88,7 @@
 #ifndef __GLIBC__
 #define GRPC_LINUX_EPOLL 1
 #define GRPC_LINUX_EVENTFD 1
+#define GRPC_MSG_IOVLEN_TYPE int
 #endif
 #ifndef GRPC_LINUX_EVENTFD
 #define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1
diff --git a/src/core/lib/iomgr/tcp_server_uv.c b/src/core/lib/iomgr/tcp_server_uv.c
index e924694..d446e53 100644
--- a/src/core/lib/iomgr/tcp_server_uv.c
+++ b/src/core/lib/iomgr/tcp_server_uv.c
@@ -56,6 +56,8 @@
   int port;
   /* linked list */
   struct grpc_tcp_listener *next;
+
+  bool closed;
 };
 
 struct grpc_tcp_server {
@@ -77,6 +79,8 @@
   /* shutdown callback */
   grpc_closure *shutdown_complete;
 
+  bool shutdown;
+
   grpc_resource_quota *resource_quota;
 };
 
@@ -109,6 +113,7 @@
   s->shutdown_starting.head = NULL;
   s->shutdown_starting.tail = NULL;
   s->shutdown_complete = shutdown_complete;
+  s->shutdown = false;
   *server = s;
   return GRPC_ERROR_NONE;
 }
@@ -125,6 +130,7 @@
 }
 
 static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
+  GPR_ASSERT(s->shutdown);
   if (s->shutdown_complete != NULL) {
     grpc_closure_sched(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE);
   }
@@ -144,21 +150,31 @@
   grpc_tcp_listener *sp = (grpc_tcp_listener *)handle->data;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   sp->server->open_ports--;
-  if (sp->server->open_ports == 0) {
+  if (sp->server->open_ports == 0 && sp->server->shutdown) {
     finish_shutdown(&exec_ctx, sp->server);
   }
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
+static void close_listener(grpc_tcp_listener *sp) {
+  if (!sp->closed) {
+    sp->closed = true;
+    uv_close((uv_handle_t *)sp->handle, handle_close_callback);
+  }
+}
+
 static void tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
   int immediately_done = 0;
   grpc_tcp_listener *sp;
 
+  GPR_ASSERT(!s->shutdown);
+  s->shutdown = true;
+
   if (s->open_ports == 0) {
     immediately_done = 1;
   }
   for (sp = s->head; sp; sp = sp->next) {
-    uv_close((uv_handle_t *)sp->handle, handle_close_callback);
+    close_listener(sp);
   }
 
   if (immediately_done) {
@@ -196,9 +212,14 @@
   int err;
 
   if (status < 0) {
-    gpr_log(GPR_INFO, "Skipping on_accept due to error: %s",
-            uv_strerror(status));
-    return;
+    switch (status) {
+      case UV_EINTR:
+      case UV_EAGAIN:
+        return;
+      default:
+        close_listener(sp);
+        return;
+    }
   }
 
   client = gpr_malloc(sizeof(uv_tcp_t));
@@ -287,6 +308,7 @@
   sp->handle = handle;
   sp->port = port;
   sp->port_index = port_index;
+  sp->closed = false;
   handle->data = sp;
   s->open_ports++;
   GPR_ASSERT(sp->handle);
diff --git a/src/core/lib/iomgr/tcp_uv.c b/src/core/lib/iomgr/tcp_uv.c
index 8e8db9f..b9ca1a3 100644
--- a/src/core/lib/iomgr/tcp_uv.c
+++ b/src/core/lib/iomgr/tcp_uv.c
@@ -88,12 +88,12 @@
 #ifdef GRPC_TCP_REFCOUNT_DEBUG
 #define TCP_UNREF(exec_ctx, tcp, reason) \
   tcp_unref((exec_ctx), (tcp), (reason), __FILE__, __LINE__)
-#define TCP_REF(tcp, reason) \
-  tcp_ref((exec_ctx), (tcp), (reason), __FILE__, __LINE__)
+#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__)
 static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp,
                       const char *reason, const char *file, int line) {
-  gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "TCP unref %p : %s %d -> %d", tcp,
-          reason, tcp->refcount.count, tcp->refcount.count - 1);
+  gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+          "TCP unref %p : %s %" PRIiPTR " -> %" PRIiPTR, tcp, reason,
+          tcp->refcount.count, tcp->refcount.count - 1);
   if (gpr_unref(&tcp->refcount)) {
     tcp_free(exec_ctx, tcp);
   }
@@ -101,8 +101,9 @@
 
 static void tcp_ref(grpc_tcp *tcp, const char *reason, const char *file,
                     int line) {
-  gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "TCP   ref %p : %s %d -> %d", tcp,
-          reason, tcp->refcount.count, tcp->refcount.count + 1);
+  gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+          "TCP   ref %p : %s %" PRIiPTR " -> %" PRIiPTR, tcp, reason,
+          tcp->refcount.count, tcp->refcount.count + 1);
   gpr_ref(&tcp->refcount);
 }
 #else
@@ -311,6 +312,7 @@
     tcp->shutting_down = true;
     uv_shutdown_t *req = &tcp->shutdown_req;
     uv_shutdown(req, (uv_stream_t *)tcp->handle, shutdown_callback);
+    grpc_resource_user_shutdown(exec_ctx, tcp->resource_user);
   }
   GRPC_ERROR_UNREF(why);
 }
diff --git a/src/core/lib/support/cpu_linux.c b/src/core/lib/support/cpu_linux.c
index 1e50f59..b826dde 100644
--- a/src/core/lib/support/cpu_linux.c
+++ b/src/core/lib/support/cpu_linux.c
@@ -67,16 +67,16 @@
 }
 
 unsigned gpr_cpu_current_cpu(void) {
-#ifdef __GLIBC__
+#ifdef GPR_MUSL_LIBC_COMPAT
+  // sched_getcpu() is undefined on musl
+  return 0;
+#else
   int cpu = sched_getcpu();
   if (cpu < 0) {
     gpr_log(GPR_ERROR, "Error determining current CPU: %s\n", strerror(errno));
     return 0;
   }
   return (unsigned)cpu;
-#else
-  // sched_getcpu() is undefined on musl
-  return 0;
 #endif
 }
 
diff --git a/src/core/lib/support/wrap_memcpy.c b/src/core/lib/support/wrap_memcpy.c
index 050cc6d..deb8d6b 100644
--- a/src/core/lib/support/wrap_memcpy.c
+++ b/src/core/lib/support/wrap_memcpy.c
@@ -31,6 +31,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <string.h>
 
 /* Provide a wrapped memcpy for targets that need to be backwards
@@ -40,7 +42,7 @@
  */
 
 #ifdef __linux__
-#if defined(__x86_64__) && defined(__GNU_LIBRARY__)
+#if defined(__x86_64__) && !defined(GPR_MUSL_LIBC_COMPAT)
 __asm__(".symver memcpy,memcpy@GLIBC_2.2.5");
 void *__wrap_memcpy(void *destination, const void *source, size_t num) {
   return memcpy(destination, source, num);
diff --git a/src/core/lib/surface/version.c b/src/core/lib/surface/version.c
index 959b45d..b800e03 100644
--- a/src/core/lib/surface/version.c
+++ b/src/core/lib/surface/version.c
@@ -36,6 +36,6 @@
 
 #include <grpc/grpc.h>
 
-const char *grpc_version_string(void) { return "3.0.0-pre1"; }
+const char *grpc_version_string(void) { return "3.0.0"; }
 
 const char *grpc_g_stands_for(void) { return "gentle"; }
diff --git a/src/cpp/common/version_cc.cc b/src/cpp/common/version_cc.cc
index 16a295d..0a1b725 100644
--- a/src/cpp/common/version_cc.cc
+++ b/src/cpp/common/version_cc.cc
@@ -37,5 +37,5 @@
 #include <grpc++/grpc++.h>
 
 namespace grpc {
-grpc::string Version() { return "1.3.0-pre1"; }
+grpc::string Version() { return "1.3.5"; }
 }
diff --git a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs
index 4d90470..c57c260 100644
--- a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs
+++ b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs
@@ -141,7 +141,8 @@
         {
             if (server == null)
             {
-                server = new Server
+                // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755
+                server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) })
                 {
                     Services = { serviceDefinition },
                     Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs
index 4f29c35..51ae11f 100644
--- a/src/csharp/Grpc.Core/Channel.cs
+++ b/src/csharp/Grpc.Core/Channel.cs
@@ -59,6 +59,8 @@
         readonly ChannelSafeHandle handle;
         readonly Dictionary<string, ChannelOption> options;
 
+        readonly Task connectivityWatcherTask;
+
         bool shutdownRequested;
 
         /// <summary>
@@ -99,6 +101,9 @@
                     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);
         }
 
@@ -244,7 +249,7 @@
 
             handle.Dispose();
 
-            await GrpcEnvironment.ReleaseAsync().ConfigureAwait(false);
+            await Task.WhenAll(GrpcEnvironment.ReleaseAsync(), connectivityWatcherTask).ConfigureAwait(false);
         }
 
         internal ChannelSafeHandle Handle
@@ -299,6 +304,40 @@
             }
         }
 
+        /// <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)
         {
             var key = ChannelOptions.PrimaryUserAgentString;
diff --git a/src/csharp/Grpc.Core/ChannelOptions.cs b/src/csharp/Grpc.Core/ChannelOptions.cs
index 46a2c66..5de9b1e 100644
--- a/src/csharp/Grpc.Core/ChannelOptions.cs
+++ b/src/csharp/Grpc.Core/ChannelOptions.cs
@@ -172,6 +172,9 @@
         /// <summary>Secondary user agent: goes at the end of the user-agent metadata</summary>
         public const string SecondaryUserAgentString = "grpc.secondary_user_agent";
 
+        /// <summary>If non-zero, allow the use of SO_REUSEPORT for server if it's available (default 1)</summary>
+        public const string SoReuseport = "grpc.so_reuseport";
+
         /// <summary>
         /// Creates native object for a collection of channel options.
         /// </summary>
diff --git a/src/csharp/Grpc.Core/Version.csproj.include b/src/csharp/Grpc.Core/Version.csproj.include
index 5409c98..63536ec 100755
--- a/src/csharp/Grpc.Core/Version.csproj.include
+++ b/src/csharp/Grpc.Core/Version.csproj.include
@@ -1,7 +1,7 @@
 <!-- This file is generated -->
 <Project>
   <PropertyGroup>
-    <GrpcCsharpVersion>1.3.0-pre1</GrpcCsharpVersion>
+    <GrpcCsharpVersion>1.3.5</GrpcCsharpVersion>
     <GoogleProtobufVersion>3.2.0</GoogleProtobufVersion>
   </PropertyGroup>
 </Project>
diff --git a/src/csharp/Grpc.Core/VersionInfo.cs b/src/csharp/Grpc.Core/VersionInfo.cs
index 03d4a96..fed3829 100644
--- a/src/csharp/Grpc.Core/VersionInfo.cs
+++ b/src/csharp/Grpc.Core/VersionInfo.cs
@@ -48,11 +48,11 @@
         /// <summary>
         /// Current <c>AssemblyFileVersion</c> of gRPC C# assemblies
         /// </summary>
-        public const string CurrentAssemblyFileVersion = "1.3.0.0";
+        public const string CurrentAssemblyFileVersion = "1.3.5.0";
 
         /// <summary>
         /// Current version of gRPC C#
         /// </summary>
-        public const string CurrentVersion = "1.3.0-pre1";
+        public const string CurrentVersion = "1.3.5";
     }
 }
diff --git a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs
index 50dacc2..02bcd18 100644
--- a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs
+++ b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs
@@ -55,7 +55,8 @@
         [TestFixtureSetUp]
         public void Init()
         {
-            server = new Server
+            // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755
+            server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) })
             {
                 Services = { Math.BindService(new MathServiceImpl()) },
                 Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
diff --git a/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs
index 25a58fb..0ebc3c3 100644
--- a/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs
+++ b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs
@@ -57,7 +57,8 @@
         {
             serviceImpl = new HealthServiceImpl();
 
-            server = new Server
+            // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755
+            server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) })
             {
                 Services = { Grpc.Health.V1.Health.BindService(serviceImpl) },
                 Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
diff --git a/src/csharp/Grpc.IntegrationTesting/GeneratedServiceBaseTest.cs b/src/csharp/Grpc.IntegrationTesting/GeneratedServiceBaseTest.cs
index 4216dc1..780a9b7 100644
--- a/src/csharp/Grpc.IntegrationTesting/GeneratedServiceBaseTest.cs
+++ b/src/csharp/Grpc.IntegrationTesting/GeneratedServiceBaseTest.cs
@@ -54,7 +54,8 @@
         [SetUp]
         public void Init()
         {
-            server = new Server
+            // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755
+            server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) })
             {
                 Services = { TestService.BindService(new UnimplementedTestServiceImpl()) },
                 Ports = { { Host, ServerPort.PickUnused, SslServerCredentials.Insecure } }
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs
index 4960a53..04f833f 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs
@@ -56,7 +56,8 @@
         [TestFixtureSetUp]
         public void Init()
         {
-            server = new Server
+            // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755
+            server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) })
             {
                 Services = { TestService.BindService(new TestServiceImpl()) },
                 Ports = { { Host, ServerPort.PickUnused, TestCredentials.CreateSslServerCredentials() } }
diff --git a/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs b/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs
index d55e658..ef63f53 100644
--- a/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs
+++ b/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs
@@ -56,7 +56,8 @@
         [SetUp]
         public void Init()
         {
-            server = new Server
+            // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755
+            server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) })
             {
                 Services = { TestService.BindService(new FakeTestService()) },
                 Ports = { { Host, ServerPort.PickUnused, TestCredentials.CreateSslServerCredentials() } }
diff --git a/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs b/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs
index 377dad6..48ecc23 100644
--- a/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs
+++ b/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs
@@ -67,7 +67,8 @@
             var serverCredentials = new SslServerCredentials(new[] { keyCertPair }, rootCert, true);
             var clientCredentials = new SslCredentials(rootCert, keyCertPair);
 
-            server = new Server
+            // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755
+            server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) })
             {
                 Services = { TestService.BindService(new SslCredentialsTestServiceImpl()) },
                 Ports = { { Host, ServerPort.PickUnused, serverCredentials } }
diff --git a/src/csharp/Grpc.Reflection.Tests/ReflectionClientServerTest.cs b/src/csharp/Grpc.Reflection.Tests/ReflectionClientServerTest.cs
index 1d0845e..22edbed 100644
--- a/src/csharp/Grpc.Reflection.Tests/ReflectionClientServerTest.cs
+++ b/src/csharp/Grpc.Reflection.Tests/ReflectionClientServerTest.cs
@@ -58,7 +58,8 @@
         {
             serviceImpl = new ReflectionServiceImpl(ServerReflection.Descriptor);
 
-            server = new Server
+            // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755
+            server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) })
             {
                 Services = { ServerReflection.BindService(serviceImpl) },
                 Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
diff --git a/src/csharp/build_packages_dotnetcli.bat b/src/csharp/build_packages_dotnetcli.bat
index 2f8d5b8..3aec7aa 100755
--- a/src/csharp/build_packages_dotnetcli.bat
+++ b/src/csharp/build_packages_dotnetcli.bat
@@ -28,7 +28,7 @@
 @rem OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 @rem Current package versions
-set VERSION=1.3.0-pre1
+set VERSION=1.3.5
 
 @rem Adjust the location of nuget.exe
 set NUGET=C:\nuget\nuget.exe
@@ -60,11 +60,11 @@
 @rem To be able to build, we also need to put grpc_csharp_ext to its normal location
 xcopy /Y /I nativelibs\windows_x64\grpc_csharp_ext.dll ..\..\cmake\build\x64\Release\
 
-%DOTNET% pack --configuration Release Grpc.Core --output ..\..\..\artifacts || goto :error
-%DOTNET% pack --configuration Release Grpc.Core.Testing --output ..\..\..\artifacts || goto :error
-%DOTNET% pack --configuration Release Grpc.Auth --output ..\..\..\artifacts || goto :error
-%DOTNET% pack --configuration Release Grpc.HealthCheck --output ..\..\..\artifacts || goto :error
-%DOTNET% pack --configuration Release Grpc.Reflection --output ..\..\..\artifacts || goto :error
+%DOTNET% pack --configuration Release --include-symbols --include-source 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 --include-symbols --include-source Grpc.Auth --output ..\..\..\artifacts || goto :error
+%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.HealthCheck --output ..\..\..\artifacts || goto :error
+%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.Reflection --output ..\..\..\artifacts || goto :error
 
 %NUGET% pack Grpc.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts || goto :error
 %NUGET% pack Grpc.Tools.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts
diff --git a/src/csharp/build_packages_dotnetcli.sh b/src/csharp/build_packages_dotnetcli.sh
index 3096b3e..da585cb 100755
--- a/src/csharp/build_packages_dotnetcli.sh
+++ b/src/csharp/build_packages_dotnetcli.sh
@@ -64,13 +64,13 @@
 mkdir -p ../../libs/opt
 cp nativelibs/linux_x64/libgrpc_csharp_ext.so ../../libs/opt
 
-dotnet pack --configuration Release Grpc.Core --output ../../../artifacts
-dotnet pack --configuration Release Grpc.Core.Testing --output ../../../artifacts
-dotnet pack --configuration Release Grpc.Auth --output ../../../artifacts
-dotnet pack --configuration Release Grpc.HealthCheck --output ../../../artifacts
-dotnet pack --configuration Release Grpc.Reflection --output ../../../artifacts
+dotnet pack --configuration Release --include-symbols --include-source Grpc.Core --output ../../../artifacts
+dotnet pack --configuration Release --include-symbols --include-source Grpc.Core.Testing --output ../../../artifacts
+dotnet pack --configuration Release --include-symbols --include-source Grpc.Auth --output ../../../artifacts
+dotnet pack --configuration Release --include-symbols --include-source Grpc.HealthCheck --output ../../../artifacts
+dotnet pack --configuration Release --include-symbols --include-source Grpc.Reflection --output ../../../artifacts
 
-nuget pack Grpc.nuspec -Version "1.3.0-pre1" -OutputDirectory ../../artifacts
-nuget pack Grpc.Tools.nuspec -Version "1.3.0-pre1" -OutputDirectory ../../artifacts
+nuget pack Grpc.nuspec -Version "1.3.5" -OutputDirectory ../../artifacts
+nuget pack Grpc.Tools.nuspec -Version "1.3.5" -OutputDirectory ../../artifacts
 
 (cd ../../artifacts && zip csharp_nugets_dotnetcli.zip *.nupkg)
diff --git a/src/node/ext/call.cc b/src/node/ext/call.cc
index bd60775..2cc9f63 100644
--- a/src/node/ext/call.cc
+++ b/src/node/ext/call.cc
@@ -46,7 +46,6 @@
 #include "call.h"
 #include "channel.h"
 #include "completion_queue.h"
-#include "completion_queue_async_worker.h"
 #include "call_credentials.h"
 #include "slice.h"
 #include "timeval.h"
@@ -722,6 +721,14 @@
   }
   Local<Function> callback_func = info[1].As<Function>();
   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<Array> keys = Nan::GetOwnPropertyNames(obj).ToLocalChecked();
   size_t nops = keys->Length();
diff --git a/src/node/ext/channel.cc b/src/node/ext/channel.cc
index 1263cc0..2c533f0 100644
--- a/src/node/ext/channel.cc
+++ b/src/node/ext/channel.cc
@@ -42,7 +42,6 @@
 #include "call.h"
 #include "channel.h"
 #include "completion_queue.h"
-#include "completion_queue_async_worker.h"
 #include "channel_credentials.h"
 #include "timeval.h"
 
diff --git a/src/node/ext/completion_queue_uv.cc b/src/node/ext/completion_queue.cc
similarity index 98%
rename from src/node/ext/completion_queue_uv.cc
rename to src/node/ext/completion_queue.cc
index 0f6f7da..a743784 100644
--- a/src/node/ext/completion_queue_uv.cc
+++ b/src/node/ext/completion_queue.cc
@@ -31,8 +31,6 @@
  *
  */
 
-#ifdef GRPC_UV
-
 #include <uv.h>
 #include <node.h>
 #include <v8.h>
@@ -97,5 +95,3 @@
 
 }  // namespace node
 }  // namespace grpc
-
-#endif /* GRPC_UV */
diff --git a/src/node/ext/completion_queue_async_worker.h b/src/node/ext/completion_queue_async_worker.h
deleted file mode 100644
index 6e54116..0000000
--- a/src/node/ext/completion_queue_async_worker.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef NET_GRPC_NODE_COMPLETION_QUEUE_ASYNC_WORKER_H_
-#define NET_GRPC_NODE_COMPLETION_QUEUE_ASYNC_WORKER_H_
-#include <nan.h>
-
-#include "grpc/grpc.h"
-
-namespace grpc {
-namespace node {
-
-/* A worker that asynchronously calls completion_queue_next, and queues onto the
-   node event loop a call to the function stored in the event's tag. */
-class CompletionQueueAsyncWorker : public Nan::AsyncWorker {
- public:
-  CompletionQueueAsyncWorker();
-
-  ~CompletionQueueAsyncWorker();
-  /* Calls completion_queue_next with the provided deadline, and stores the
-     event if there was one or sets an error message if there was not */
-  void Execute();
-
-  /* Returns the completion queue attached to this class */
-  static grpc_completion_queue *GetQueue();
-
-  /* Convenience function to create a worker with the given arguments and queue
-     it to run asynchronously */
-  static void Next();
-
-  /* Initialize the CompletionQueueAsyncWorker class */
-  static void Init(v8::Local<v8::Object> exports);
-
- protected:
-  /* Called when Execute has succeeded (completed without setting an error
-     message). Calls the saved callback with the event that came from
-     completion_queue_next */
-  void HandleOKCallback();
-
-  void HandleErrorCallback();
-
- private:
-  grpc_event result;
-
-  static grpc_completion_queue *queue;
-
-  // Number of grpc_completion_queue_next calls in the thread pool
-  static int current_threads;
-  // Number of grpc_completion_queue_next calls waiting to enter the thread pool
-  static int waiting_next_calls;
-};
-
-}  // namespace node
-}  // namespace grpc
-
-#endif  // NET_GRPC_NODE_COMPLETION_QUEUE_ASYNC_WORKER_H_
diff --git a/src/node/ext/completion_queue_threadpool.cc b/src/node/ext/completion_queue_threadpool.cc
deleted file mode 100644
index 7b1bdda..0000000
--- a/src/node/ext/completion_queue_threadpool.cc
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-/* I don't like using #ifndef, but I don't see a better way to do this */
-#ifndef GRPC_UV
-
-#include <node.h>
-#include <nan.h>
-
-#include "grpc/grpc.h"
-#include "grpc/support/log.h"
-#include "grpc/support/time.h"
-#include "completion_queue.h"
-#include "call.h"
-
-namespace grpc {
-namespace node {
-
-namespace {
-
-/* A worker that asynchronously calls completion_queue_next, and queues onto the
-   node event loop a call to the function stored in the event's tag. */
-class CompletionQueueAsyncWorker : public Nan::AsyncWorker {
- public:
-  CompletionQueueAsyncWorker();
-
-  ~CompletionQueueAsyncWorker();
-  /* Calls completion_queue_next with the provided deadline, and stores the
-     event if there was one or sets an error message if there was not */
-  void Execute();
-
-  /* Returns the completion queue attached to this class */
-  static grpc_completion_queue *GetQueue();
-
-  /* Convenience function to create a worker with the given arguments and queue
-     it to run asynchronously */
-  static void Next();
-
-  /* Initialize the CompletionQueueAsyncWorker class */
-  static void Init(v8::Local<v8::Object> exports);
-
- protected:
-  /* Called when Execute has succeeded (completed without setting an error
-     message). Calls the saved callback with the event that came from
-     completion_queue_next */
-  void HandleOKCallback();
-
-  void HandleErrorCallback();
-
- private:
-  static void TryAddWorker();
-
-  grpc_event result;
-
-  static grpc_completion_queue *queue;
-
-  // Number of grpc_completion_queue_next calls in the thread pool
-  static int current_threads;
-  // Number of grpc_completion_queue_next calls waiting to enter the thread pool
-  static int waiting_next_calls;
-};
-
-const int max_queue_threads = 2;
-
-using v8::Function;
-using v8::Local;
-using v8::Object;
-using v8::Value;
-
-grpc_completion_queue *CompletionQueueAsyncWorker::queue;
-
-// Invariants: current_threads <= max_queue_threads
-// (current_threads == max_queue_threads) || (waiting_next_calls == 0)
-
-int CompletionQueueAsyncWorker::current_threads;
-int CompletionQueueAsyncWorker::waiting_next_calls;
-
-CompletionQueueAsyncWorker::CompletionQueueAsyncWorker()
-    : Nan::AsyncWorker(NULL) {}
-
-CompletionQueueAsyncWorker::~CompletionQueueAsyncWorker() {}
-
-void CompletionQueueAsyncWorker::Execute() {
-  result =
-      grpc_completion_queue_next(queue, gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
-  if (!result.success) {
-    SetErrorMessage("The async function encountered an error");
-  }
-}
-
-grpc_completion_queue *CompletionQueueAsyncWorker::GetQueue() { return queue; }
-
-void CompletionQueueAsyncWorker::TryAddWorker() {
-  if (current_threads < max_queue_threads && waiting_next_calls > 0) {
-    current_threads += 1;
-    waiting_next_calls -= 1;
-    CompletionQueueAsyncWorker *worker = new CompletionQueueAsyncWorker();
-    Nan::AsyncQueueWorker(worker);
-  }
-  GPR_ASSERT(current_threads <= max_queue_threads);
-  GPR_ASSERT((current_threads == max_queue_threads) ||
-             (waiting_next_calls == 0));
-}
-
-void CompletionQueueAsyncWorker::Next() {
-  waiting_next_calls += 1;
-  TryAddWorker();
-}
-
-void CompletionQueueAsyncWorker::Init(Local<Object> exports) {
-  Nan::HandleScope scope;
-  current_threads = 0;
-  waiting_next_calls = 0;
-  queue = grpc_completion_queue_create(NULL);
-}
-
-void CompletionQueueAsyncWorker::HandleOKCallback() {
-  Nan::HandleScope scope;
-  current_threads -= 1;
-  TryAddWorker();
-  CompleteTag(result.tag, NULL);
-
-  DestroyTag(result.tag);
-}
-
-void CompletionQueueAsyncWorker::HandleErrorCallback() {
-  Nan::HandleScope scope;
-  current_threads -= 1;
-  TryAddWorker();
-  CompleteTag(result.tag, ErrorMessage());
-
-  DestroyTag(result.tag);
-}
-
-}  // namespace
-
-grpc_completion_queue *GetCompletionQueue() {
-  return CompletionQueueAsyncWorker::GetQueue();
-}
-
-void CompletionQueueNext() {
-  CompletionQueueAsyncWorker::Next();
-}
-
-void CompletionQueueInit(Local<Object> exports) {
-  CompletionQueueAsyncWorker::Init(exports);
-}
-
-}  // namespace node
-}  // namespace grpc
-
-#endif  /* GRPC_UV */
diff --git a/src/node/ext/node_grpc.cc b/src/node/ext/node_grpc.cc
index 122e5e6..f600cb9 100644
--- a/src/node/ext/node_grpc.cc
+++ b/src/node/ext/node_grpc.cc
@@ -43,18 +43,15 @@
 #include "grpc/support/time.h"
 
 // TODO(murgatroid99): Remove this when the endpoint API becomes public
-#ifdef GRPC_UV
 extern "C" {
 #include "src/core/lib/iomgr/pollset_uv.h"
 }
-#endif
 
 #include "call.h"
 #include "call_credentials.h"
 #include "channel.h"
 #include "channel_credentials.h"
 #include "server.h"
-#include "completion_queue_async_worker.h"
 #include "server_credentials.h"
 #include "slice.h"
 #include "timeval.h"
@@ -438,9 +435,7 @@
   InitWriteFlags(exports);
   InitLogConstants(exports);
 
-#ifdef GRPC_UV
   grpc_pollset_work_run_loop = 0;
-#endif
 
   grpc::node::Call::Init(exports);
   grpc::node::CallCredentials::Init(exports);
diff --git a/src/node/ext/server.cc b/src/node/ext/server.cc
index 5384305..f7c62a3 100644
--- a/src/node/ext/server.cc
+++ b/src/node/ext/server.cc
@@ -41,7 +41,6 @@
 #include <vector>
 #include "call.h"
 #include "completion_queue.h"
-#include "completion_queue_async_worker.h"
 #include "grpc/grpc.h"
 #include "grpc/grpc_security.h"
 #include "grpc/support/log.h"
@@ -78,6 +77,30 @@
 Nan::Callback *Server::constructor;
 Persistent<FunctionTemplate> Server::fun_tpl;
 
+static Callback *shutdown_callback = NULL;
+
+class ServerShutdownOp : public Op {
+ public:
+  ServerShutdownOp(grpc_server *server) : server(server) {}
+
+  ~ServerShutdownOp() {}
+
+  Local<Value> GetNodeValue() const { return Nan::Null(); }
+
+  bool ParseOp(Local<Value> value, grpc_op *out) { return true; }
+  bool IsFinalOp() { return false; }
+  void OnComplete(bool success) {
+    /* Because cancel_all_calls was called, we assume that shutdown_and_notify
+       completes successfully */
+    grpc_server_destroy(server);
+  }
+
+  grpc_server *server;
+
+ protected:
+  std::string GetTypeString() const { return "shutdown"; }
+};
+
 class NewCallOp : public Op {
  public:
   NewCallOp() {
@@ -156,6 +179,10 @@
       server_persist;
 };
 
+Server::Server(grpc_server *server) : wrapped_server(server) {}
+
+Server::~Server() { this->ShutdownServer(); }
+
 void Server::Init(Local<Object> exports) {
   HandleScope scope;
   Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
@@ -184,6 +211,36 @@
   }
 }
 
+NAN_METHOD(ServerShutdownCallback) {
+  if (!info[0]->IsNull()) {
+    return Nan::ThrowError("forceShutdown failed somehow");
+  }
+}
+
+void Server::ShutdownServer() {
+  Nan::HandleScope scope;
+  if (this->wrapped_server != NULL) {
+    if (shutdown_callback == NULL) {
+      Local<FunctionTemplate> callback_tpl =
+          Nan::New<FunctionTemplate>(ServerShutdownCallback);
+      shutdown_callback =
+          new Callback(Nan::GetFunction(callback_tpl).ToLocalChecked());
+    }
+
+    ServerShutdownOp *op = new ServerShutdownOp(this->wrapped_server);
+    unique_ptr<OpVec> ops(new OpVec());
+    ops->push_back(unique_ptr<Op>(op));
+
+    grpc_server_shutdown_and_notify(
+        this->wrapped_server, GetCompletionQueue(),
+        new struct tag(new Callback(**shutdown_callback), ops.release(), NULL,
+                       Nan::Null()));
+    grpc_server_cancel_all_calls(this->wrapped_server);
+    CompletionQueueNext();
+    this->wrapped_server = NULL;
+  }
+}
+
 NAN_METHOD(Server::New) {
   /* If this is not a constructor call, make a constructor call and return
      the result */
diff --git a/src/node/ext/server_generic.cc b/src/node/ext/server_generic.cc
deleted file mode 100644
index 0cf20f7..0000000
--- a/src/node/ext/server_generic.cc
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- *
- * 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.
- *
- */
-
-#ifndef GRPC_UV
-
-#include "server.h"
-
-#include <node.h>
-#include <nan.h>
-#include "grpc/grpc.h"
-#include "grpc/support/time.h"
-
-namespace grpc {
-namespace node {
-
-Server::Server(grpc_server *server) : wrapped_server(server) {
-  shutdown_queue = grpc_completion_queue_create(NULL);
-  grpc_server_register_non_listening_completion_queue(server, shutdown_queue,
-                                                      NULL);
-}
-
-Server::~Server() {
-  this->ShutdownServer();
-  grpc_completion_queue_shutdown(this->shutdown_queue);
-  grpc_completion_queue_destroy(this->shutdown_queue);
-}
-
-void Server::ShutdownServer() {
-  if (this->wrapped_server != NULL) {
-    grpc_server_shutdown_and_notify(this->wrapped_server, this->shutdown_queue,
-                                    NULL);
-    grpc_server_cancel_all_calls(this->wrapped_server);
-    grpc_completion_queue_pluck(this->shutdown_queue, NULL,
-                                gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
-    grpc_server_destroy(this->wrapped_server);
-    this->wrapped_server = NULL;
-  }
-}
-
-}  // namespace grpc
-}  // namespace node
-
-#endif /* GRPC_UV */
diff --git a/src/node/ext/server_uv.cc b/src/node/ext/server_uv.cc
deleted file mode 100644
index 7899383..0000000
--- a/src/node/ext/server_uv.cc
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- *
- * 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.
- *
- */
-
-#ifdef GRPC_UV
-
-#include "server.h"
-
-#include <node.h>
-#include <nan.h>
-#include "grpc/grpc.h"
-#include "grpc/support/time.h"
-
-#include "call.h"
-#include "completion_queue.h"
-
-namespace grpc {
-namespace node {
-
-using Nan::Callback;
-using Nan::MaybeLocal;
-
-using v8::External;
-using v8::Function;
-using v8::FunctionTemplate;
-using v8::Local;
-using v8::Object;
-using v8::Value;
-
-static Callback *shutdown_callback = NULL;
-
-class ServerShutdownOp : public Op {
- public:
-  ServerShutdownOp(grpc_server *server): server(server) {
-  }
-
-  ~ServerShutdownOp() {
-  }
-
-  Local<Value> GetNodeValue() const {
-    return Nan::Null();
-  }
-
-  bool ParseOp(Local<Value> value, grpc_op *out) {
-    return true;
-  }
-  bool IsFinalOp() {
-    return false;
-  }
-  void OnComplete(bool success) {
-    /* Because cancel_all_calls was called, we assume that shutdown_and_notify
-       completes successfully */
-    grpc_server_destroy(server);
-  }
-
-  grpc_server *server;
-
- protected:
-  std::string GetTypeString() const { return "shutdown"; }
-};
-
-Server::Server(grpc_server *server) : wrapped_server(server) {
-}
-
-Server::~Server() {
-  this->ShutdownServer();
-}
-
-NAN_METHOD(ServerShutdownCallback) {
-  if (!info[0]->IsNull()) {
-    return Nan::ThrowError("forceShutdown failed somehow");
-  }
-}
-
-void Server::ShutdownServer() {
-  Nan::HandleScope scope;
-  if (this->wrapped_server != NULL) {
-    if (shutdown_callback == NULL) {
-      Local<FunctionTemplate>callback_tpl =
-          Nan::New<FunctionTemplate>(ServerShutdownCallback);
-      shutdown_callback = new Callback(
-          Nan::GetFunction(callback_tpl).ToLocalChecked());
-    }
-
-    ServerShutdownOp *op = new ServerShutdownOp(this->wrapped_server);
-    unique_ptr<OpVec> ops(new OpVec());
-    ops->push_back(unique_ptr<Op>(op));
-
-    grpc_server_shutdown_and_notify(
-        this->wrapped_server, GetCompletionQueue(),
-        new struct tag(new Callback(**shutdown_callback), ops.release(), NULL,
-                       Nan::Null()));
-    grpc_server_cancel_all_calls(this->wrapped_server);
-    CompletionQueueNext();
-    this->wrapped_server = NULL;
-  }
-}
-
-}  // namespace grpc
-}  // namespace node
-
-#endif /* GRPC_UV */
diff --git a/src/node/health_check/package.json b/src/node/health_check/package.json
index 3d33118..2fdcb12 100644
--- a/src/node/health_check/package.json
+++ b/src/node/health_check/package.json
@@ -1,6 +1,6 @@
 {
   "name": "grpc-health-check",
-  "version": "1.3.0-pre1",
+  "version": "1.3.5",
   "author": "Google Inc.",
   "description": "Health check service for use with gRPC",
   "repository": {
@@ -15,7 +15,7 @@
     }
   ],
   "dependencies": {
-    "grpc": "^1.3.0-pre1",
+    "grpc": "^1.3.5",
     "lodash": "^3.9.3",
     "google-protobuf": "^3.0.0"
   },
diff --git a/src/node/index.js b/src/node/index.js
index 071bfd7..fb313ac 100644
--- a/src/node/index.js
+++ b/src/node/index.js
@@ -64,8 +64,6 @@
  *   Buffers. Defaults to false
  * - longsAsStrings: deserialize long values as strings instead of objects.
  *   Defaults to true
- * - enumsAsStrings: deserialize enum values as strings instead of numbers.
- *   Defaults to true
  * - deprecatedArgumentOrder: Use the beta method argument order for client
  *   methods, with optional arguments after the callback. Defaults to false.
  *   This option is only a temporary stopgap measure to smooth an API breakage.
@@ -99,10 +97,6 @@
   switch (protobufjsVersion) {
     case 6: return protobuf_js_6_common.loadObject(value, options);
     case 5:
-    var deprecation_message = 'Calling grpc.loadObject with an object ' +
-        'generated by ProtoBuf.js 5 is deprecated. Please upgrade to ' +
-        'ProtoBuf.js 6.';
-    common.log(grpc.logVerbosity.INFO, deprecation_message);
     return protobuf_js_5_common.loadObject(value, options);
     default:
     throw new Error('Unrecognized protobufjsVersion', protobufjsVersion);
@@ -111,19 +105,6 @@
 
 var loadObject = exports.loadObject;
 
-function applyProtoRoot(filename, root) {
-  if (_.isString(filename)) {
-    return filename;
-  }
-  filename.root = path.resolve(filename.root) + '/';
-  root.resolvePath = function(originPath, importPath, alreadyNormalized) {
-    return ProtoBuf.util.path.resolve(filename.root,
-                                      importPath,
-                                      alreadyNormalized);
-  };
-  return filename.file;
-}
-
 /**
  * Load a gRPC object from a .proto file. The options object can provide the
  * following options:
@@ -133,8 +114,6 @@
  *   Buffers. Defaults to false
  * - longsAsStrings: deserialize long values as strings instead of objects.
  *   Defaults to true
- * - enumsAsStrings: deserialize enum values as strings instead of numbers.
- *   Defaults to true
  * - deprecatedArgumentOrder: Use the beta method argument order for client
  *   methods, with optional arguments after the callback. Defaults to false.
  *   This option is only a temporary stopgap measure to smooth an API breakage.
@@ -146,17 +125,31 @@
  * @return {Object<string, *>} The resulting gRPC object
  */
 exports.load = function load(filename, format, options) {
-  /* Note: format is currently unused, because the API for loading a proto
-     file or a JSON file is identical in Protobuf.js 6. In the future, there is
-     still the possibility of adding other formats that would be loaded
-     differently */
   options = _.defaults(options, common.defaultGrpcOptions);
-  options.protobufjs_version = 6;
-  var root = new ProtoBuf.Root();
-  var parse_options = {keepCase: !options.convertFieldsToCamelCase};
-  return loadObject(root.loadSync(applyProtoRoot(filename, root),
-                                  parse_options),
-                    options);
+  options.protobufjsVersion = 5;
+  if (!format) {
+    format = 'proto';
+  }
+  var convertFieldsToCamelCaseOriginal = ProtoBuf.convertFieldsToCamelCase;
+  if(options && options.hasOwnProperty('convertFieldsToCamelCase')) {
+    ProtoBuf.convertFieldsToCamelCase = options.convertFieldsToCamelCase;
+  }
+  var builder;
+  try {
+    switch(format) {
+      case 'proto':
+      builder = ProtoBuf.loadProtoFile(filename);
+      break;
+      case 'json':
+      builder = ProtoBuf.loadJsonFile(filename);
+      break;
+      default:
+      throw new Error('Unrecognized format "' + format + '"');
+    }
+  } finally {
+    ProtoBuf.convertFieldsToCamelCase = convertFieldsToCamelCaseOriginal;
+  }
+  return loadObject(builder.ns, options);
 };
 
 var log_template = _.template(
diff --git a/src/node/performance/benchmark_client.js b/src/node/performance/benchmark_client.js
index 5ef5260..e7c426b 100644
--- a/src/node/performance/benchmark_client.js
+++ b/src/node/performance/benchmark_client.js
@@ -88,7 +88,10 @@
  */
 function BenchmarkClient(server_targets, channels, histogram_params,
     security_params) {
-  var options = {};
+  var options = {
+    "grpc.max_receive_message_length": -1,
+    "grpc.max_send_message_length": -1
+  };
   var creds;
   if (security_params) {
     var ca_path;
@@ -180,6 +183,8 @@
 
   self.last_wall_time = process.hrtime();
 
+  self.last_usage = process.cpuUsage();
+
   var makeCall;
 
   var argument;
@@ -270,6 +275,8 @@
 
   self.last_wall_time = process.hrtime();
 
+  self.last_usage = process.cpuUsage();
+
   var makeCall;
 
   var argument;
@@ -354,9 +361,11 @@
  */
 BenchmarkClient.prototype.mark = function(reset) {
   var wall_time_diff = process.hrtime(this.last_wall_time);
+  var usage_diff = process.cpuUsage(this.last_usage);
   var histogram = this.histogram;
   if (reset) {
     this.last_wall_time = process.hrtime();
+    this.last_usage = process.cpuUsage();
     this.histogram = new Histogram(histogram.resolution,
                                    histogram.max_possible);
   }
@@ -371,9 +380,8 @@
       count: histogram.getCount()
     },
     time_elapsed: wall_time_diff[0] + wall_time_diff[1] / 1e9,
-    // Not sure how to measure these values
-    time_user: 0,
-    time_system: 0
+    time_user: usage_diff.user / 1000000,
+    time_system: usage_diff.system / 1000000
   };
 };
 
diff --git a/src/node/performance/benchmark_client_express.js b/src/node/performance/benchmark_client_express.js
index e749956..157bf1b 100644
--- a/src/node/performance/benchmark_client_express.js
+++ b/src/node/performance/benchmark_client_express.js
@@ -95,7 +95,6 @@
     var host_port;
     host_port = server_targets[i % server_targets.length].split(':');
     var new_options = _.assign({hostname: host_port[0], port: +host_port[1]}, options);
-    new_options.agent = new protocol.Agent(new_options);
     this.client_options[i] = new_options;
   }
 
@@ -137,6 +136,7 @@
   }
 
   self.last_wall_time = process.hrtime();
+  self.last_usage = process.cpuUsage();
 
   var argument = {
     response_size: resp_size,
@@ -207,6 +207,7 @@
   }
 
   self.last_wall_time = process.hrtime();
+  self.last_usage = process.cpuUsage();
 
   var argument = {
     response_size: resp_size,
@@ -264,9 +265,11 @@
  */
 BenchmarkClient.prototype.mark = function(reset) {
   var wall_time_diff = process.hrtime(this.last_wall_time);
+  var usage_diff = process.cpuUsage(this.last_usage);
   var histogram = this.histogram;
   if (reset) {
     this.last_wall_time = process.hrtime();
+    this.last_usage = process.cpuUsage();
     this.histogram = new Histogram(histogram.resolution,
                                    histogram.max_possible);
   }
@@ -281,9 +284,8 @@
       count: histogram.getCount()
     },
     time_elapsed: wall_time_diff[0] + wall_time_diff[1] / 1e9,
-    // Not sure how to measure these values
-    time_user: 0,
-    time_system: 0
+    time_user: usage_diff.user / 1000000,
+    time_system: usage_diff.system / 1000000
   };
 };
 
diff --git a/src/node/performance/benchmark_server.js b/src/node/performance/benchmark_server.js
index ea85029..a4d5ee1 100644
--- a/src/node/performance/benchmark_server.js
+++ b/src/node/performance/benchmark_server.js
@@ -132,7 +132,12 @@
     server_creds = grpc.ServerCredentials.createInsecure();
   }
 
-  var server = new grpc.Server();
+  var options = {
+    "grpc.max_receive_message_length": -1,
+    "grpc.max_send_message_length": -1
+  };
+
+  var server = new grpc.Server(options);
   this.port = server.bind(host + ':' + port, server_creds);
   if (generic) {
     server.addService(genericService, {
@@ -156,6 +161,7 @@
 BenchmarkServer.prototype.start = function() {
   this.server.start();
   this.last_wall_time = process.hrtime();
+  this.last_usage = process.cpuUsage();
   this.emit('started');
 };
 
@@ -175,14 +181,15 @@
  */
 BenchmarkServer.prototype.mark = function(reset) {
   var wall_time_diff = process.hrtime(this.last_wall_time);
+  var usage_diff = process.cpuUsage(this.last_usage);
   if (reset) {
     this.last_wall_time = process.hrtime();
+    this.last_usage = process.cpuUsage();
   }
   return {
     time_elapsed: wall_time_diff[0] + wall_time_diff[1] / 1e9,
-    // Not sure how to measure these values
-    time_user: 0,
-    time_system: 0
+    time_user: usage_diff.user / 1000000,
+    time_system: usage_diff.system / 1000000
   };
 };
 
diff --git a/src/node/performance/benchmark_server_express.js b/src/node/performance/benchmark_server_express.js
index 4b695eb..fab4f53 100644
--- a/src/node/performance/benchmark_server_express.js
+++ b/src/node/performance/benchmark_server_express.js
@@ -81,6 +81,7 @@
   var self = this;
   this.server.listen(this.input_port, this.input_hostname, function() {
     self.last_wall_time = process.hrtime();
+    self.last_usage = process.cpuUsage();
     self.emit('started');
   });
 };
@@ -91,14 +92,15 @@
 
 BenchmarkServer.prototype.mark = function(reset) {
   var wall_time_diff = process.hrtime(this.last_wall_time);
+  var usage_diff = process.cpuUsage(this.last_usage);
   if (reset) {
     this.last_wall_time = process.hrtime();
+    this.last_usage = process.cpuUsage();
   }
   return {
     time_elapsed: wall_time_diff[0] + wall_time_diff[1] / 1e9,
-    // Not sure how to measure these values
-    time_user: 0,
-    time_system: 0
+    time_user: usage_diff.user / 1000000,
+    time_system: usage_diff.system / 1000000
   };
 };
 
diff --git a/src/node/src/client.js b/src/node/src/client.js
index 1aaf35c..2073d3e 100644
--- a/src/node/src/client.js
+++ b/src/node/src/client.js
@@ -100,6 +100,12 @@
   /* jshint validthis: true */
   var batch = {};
   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 {
     message = this.serialize(chunk);
   } catch (e) {
@@ -119,8 +125,10 @@
   batch[grpc.opType.SEND_MESSAGE] = message;
   this.call.startBatch(batch, function(err, event) {
     if (err) {
-      // Something has gone wrong. Stop writing by failing to call callback
-      return;
+      /* Assume that the call is complete and that writing failed because a
+         status was received. In that case, set a flag to discard all future
+         writes */
+      self.writeFailed = true;
     }
     callback();
   });
diff --git a/src/node/src/protobuf_js_5_common.js b/src/node/src/protobuf_js_5_common.js
index 62cf2f4..4041e05 100644
--- a/src/node/src/protobuf_js_5_common.js
+++ b/src/node/src/protobuf_js_5_common.js
@@ -45,8 +45,7 @@
  *     objects. Defaults to true
  * @return {function(Buffer):cls} The deserialization function
  */
-exports.deserializeCls = function deserializeCls(cls, binaryAsBase64,
-                                                 longsAsStrings) {
+exports.deserializeCls = function deserializeCls(cls, options) {
   /**
    * Deserialize a buffer to a message object
    * @param {Buffer} arg_buf The buffer to deserialize
@@ -55,7 +54,8 @@
   return function deserialize(arg_buf) {
     // Convert to a native object with binary fields as Buffers (first argument)
     // and longs as strings (second argument)
-    return cls.decode(arg_buf).toRaw(binaryAsBase64, longsAsStrings);
+    return cls.decode(arg_buf).toRaw(options.binaryAsBase64,
+                                     options.longsAsStrings);
   };
 };
 
@@ -128,10 +128,10 @@
       responseType: method.resolvedResponseType,
       requestSerialize: serializeCls(method.resolvedRequestType.build()),
       requestDeserialize: deserializeCls(method.resolvedRequestType.build(),
-                                         binaryAsBase64, longsAsStrings),
+                                         options),
       responseSerialize: serializeCls(method.resolvedResponseType.build()),
       responseDeserialize: deserializeCls(method.resolvedResponseType.build(),
-                                          binaryAsBase64, longsAsStrings)
+                                          options)
     };
   }));
 };
diff --git a/src/node/src/server.js b/src/node/src/server.js
index 3450abe..25640a0 100644
--- a/src/node/src/server.js
+++ b/src/node/src/server.js
@@ -779,6 +779,11 @@
   });
 };
 
+var logAddProtoServiceDeprecationOnce = _.once(function() {
+    common.log(grpc.logVerbosity.INFO,
+               'Server#addProtoService is deprecated. Use addService instead');
+});
+
 /**
  * Add a proto service to the server, with a corresponding implementation
  * @deprecated Use grpc.load and Server#addService instead
@@ -790,8 +795,7 @@
   var options;
   var protobuf_js_5_common = require('./protobuf_js_5_common');
   var protobuf_js_6_common = require('./protobuf_js_6_common');
-  common.log(grpc.logVerbosity.INFO,
-             'Server#addProtoService is deprecated. Use addService instead');
+  logAddProtoServiceDeprecationOnce();
   if (protobuf_js_5_common.isProbablyProtobufJs5(service)) {
     options = _.defaults(service.grpc_options, common.defaultGrpcOptions);
     this.addService(
diff --git a/src/node/test/common_test.js b/src/node/test/common_test.js
index e1ce864..b7c2c6a 100644
--- a/src/node/test/common_test.js
+++ b/src/node/test/common_test.js
@@ -37,16 +37,15 @@
 var _ = require('lodash');
 
 var common = require('../src/common');
-var protobuf_js_6_common = require('../src/protobuf_js_6_common');
+var protobuf_js_5_common = require('../src/protobuf_js_5_common');
 
-var serializeCls = protobuf_js_6_common.serializeCls;
-var deserializeCls = protobuf_js_6_common.deserializeCls;
+var serializeCls = protobuf_js_5_common.serializeCls;
+var deserializeCls = protobuf_js_5_common.deserializeCls;
 
 var ProtoBuf = require('protobufjs');
 
-var messages_proto = new ProtoBuf.Root();
-messages_proto = messages_proto.loadSync(
-    __dirname + '/test_messages.proto', {keepCase: true}).resolveAll();
+var messages_proto = ProtoBuf.loadProtoFile(
+    __dirname + '/test_messages.proto').build();
 
 var default_options = common.defaultGrpcOptions;
 
@@ -101,6 +100,7 @@
     var longNumDeserialize = deserializeCls(messages_proto.LongValues,
                                             num_options);
     var serialized = longSerialize({int_64: pos_value});
+    console.log(longDeserialize(serialized));
     assert.strictEqual(typeof longDeserialize(serialized).int_64, 'string');
     /* With the longsAsStrings option disabled, long values are represented as
      * objects with 3 keys: low, high, and unsigned */
@@ -136,7 +136,8 @@
     var serialized = sequenceSerialize({repeated_field: [10]});
     assert.strictEqual(expected_serialize.compare(serialized), 0);
   });
-  it('should deserialize packed or unpacked repeated', function() {
+  // This tests a bug that was fixed in Protobuf.js 6
+  it.skip('should deserialize packed or unpacked repeated', function() {
     var expectedDeserialize = {
       bytes_field: new Buffer(''),
       repeated_field: [10]
@@ -155,7 +156,8 @@
     assert.deepEqual(unpackedDeserialized, expectedDeserialize);
   });
 });
-describe('Proto message oneof serialize and deserialize', function() {
+// This tests a bug that was fixed in Protobuf.js 6
+describe.skip('Proto message oneof serialize and deserialize', function() {
   var oneofSerialize = serializeCls(messages_proto.OneOfValues);
   var oneofDeserialize = deserializeCls(
       messages_proto.OneOfValues, default_options);
@@ -193,7 +195,8 @@
     assert.deepEqual(enumDeserialize(nameSerialized),
                      enumDeserialize(numberSerialized));
   });
-  it('Should deserialize as a string the enumsAsStrings option', function() {
+  // This tests a bug that was fixed in Protobuf.js 6
+  it.skip('Should correctly handle the enumsAsStrings option', function() {
     var serialized = enumSerialize({enum_value: 'TWO'});
     var nameDeserialized = enumDeserialize(serialized);
     var numberDeserialized = enumIntDeserialize(serialized);
diff --git a/src/node/test/surface_test.js b/src/node/test/surface_test.js
index 783028f..d2f0511 100644
--- a/src/node/test/surface_test.js
+++ b/src/node/test/surface_test.js
@@ -43,9 +43,8 @@
 
 var grpc = require('..');
 
-var math_proto = new ProtoBuf.Root();
-math_proto = math_proto.loadSync(__dirname +
-    '/../../proto/math/math.proto', {keepCase: true});
+var math_proto = ProtoBuf.loadProtoFile(__dirname +
+    '/../../proto/math/math.proto');
 
 var mathService = math_proto.lookup('math.Math');
 var mathServiceAttrs = grpc.loadObject(
@@ -332,9 +331,7 @@
   var server;
   var client;
   before(function() {
-    var test_proto = new ProtoBuf.Root();
-    test_proto = test_proto.loadSync(__dirname + '/echo_service.proto',
-                                         {keepCase: true});
+    var test_proto = ProtoBuf.loadProtoFile(__dirname + '/echo_service.proto');
     var echo_service = test_proto.lookup('EchoService');
     var Client = grpc.loadObject(echo_service);
     server = new grpc.Server();
@@ -357,6 +354,13 @@
       done();
     });
   });
+  it('Should convert an undefined argument to default values', function(done) {
+    client.echo(undefined, function(error, response) {
+      assert.ifError(error);
+      assert.deepEqual(response, {value: '', value2: 0});
+      done();
+    });
+  });
 });
 describe('Generic client and server', function() {
   function toString(val) {
@@ -457,9 +461,7 @@
   var server;
   var metadata;
   before(function() {
-    var test_proto = new ProtoBuf.Root();
-    test_proto = test_proto.loadSync(__dirname + '/test_service.proto',
-                                         {keepCase: true});
+    var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto');
     var test_service = test_proto.lookup('TestService');
     var Client = grpc.loadObject(test_service);
     server = new grpc.Server();
@@ -560,9 +562,7 @@
   var client;
   var badArg = new Buffer([0xFF]);
   before(function() {
-    var test_proto = new ProtoBuf.Root();
-    test_proto = test_proto.loadSync(__dirname + '/test_service.proto',
-                                         {keepCase: true});
+    var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto');
     var test_service = test_proto.lookup('TestService');
     var malformed_test_service = {
       unary: {
@@ -669,9 +669,7 @@
   var client;
   var server;
   before(function() {
-    var test_proto = new ProtoBuf.Root();
-    test_proto = test_proto.loadSync(__dirname + '/test_service.proto',
-                                         {keepCase: true});
+    var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto');
     var test_service = test_proto.lookup('TestService');
     var malformed_test_service = {
       unary: {
@@ -772,16 +770,13 @@
   });
 });
 describe('Other conditions', function() {
-  var test_service;
   var Client;
   var client;
   var server;
   var port;
   before(function() {
-    var test_proto = new ProtoBuf.Root();
-    test_proto = test_proto.loadSync(__dirname + '/test_service.proto',
-                                         {keepCase: true});
-    test_service = test_proto.lookup('TestService');
+    var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto');
+    var test_service = test_proto.lookup('TestService');
     Client = grpc.loadObject(test_service);
     server = new grpc.Server();
     var trailer_metadata = new grpc.Metadata();
@@ -1121,15 +1116,12 @@
   var proxy;
   var proxy_impl;
 
-  var test_service;
   var Client;
   var client;
   var server;
   before(function() {
-    var test_proto = new ProtoBuf.Root();
-    test_proto = test_proto.loadSync(__dirname + '/test_service.proto',
-                                         {keepCase: true});
-    test_service = test_proto.lookup('TestService');
+    var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto');
+    var test_service = test_proto.lookup('TestService');
     server = new grpc.Server();
     Client = grpc.loadObject(test_service);
     server.addService(Client.service, {
diff --git a/src/node/tools/package.json b/src/node/tools/package.json
index 056f28a..cabd403 100644
--- a/src/node/tools/package.json
+++ b/src/node/tools/package.json
@@ -1,6 +1,6 @@
 {
   "name": "grpc-tools",
-  "version": "1.3.0-pre1",
+  "version": "1.3.5",
   "author": "Google Inc.",
   "description": "Tools for developing with gRPC on Node.js",
   "homepage": "http://www.grpc.io/",
diff --git "a/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec" "b/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec"
index e636d9c..23f9186 100644
--- "a/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec"
+++ "b/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec"
@@ -42,7 +42,7 @@
   # exclamation mark ensures that other "regular" pods will be able to find it as it'll be installed
   # before them.
   s.name     = '!ProtoCompiler-gRPCPlugin'
-  v = '1.3.0-pre1'
+  v = '1.3.5'
   s.version  = v
   s.summary  = 'The gRPC ProtoC plugin generates Objective-C files from .proto services.'
   s.description = <<-DESC
diff --git a/src/objective-c/GRPCClient/private/version.h b/src/objective-c/GRPCClient/private/version.h
index b53033d..43ab9bd 100644
--- a/src/objective-c/GRPCClient/private/version.h
+++ b/src/objective-c/GRPCClient/private/version.h
@@ -38,4 +38,4 @@
 // `tools/buildgen/generate_projects.sh`.
 
 
-#define GRPC_OBJC_VERSION_STRING @"1.3.0-pre1"
+#define GRPC_OBJC_VERSION_STRING @"1.3.5"
diff --git a/src/php/composer.json b/src/php/composer.json
index 2b14007..08f2b9a 100644
--- a/src/php/composer.json
+++ b/src/php/composer.json
@@ -2,7 +2,7 @@
   "name": "grpc/grpc-dev",
   "description": "gRPC library for PHP - for Developement use only",
   "license": "BSD-3-Clause",
-  "version": "1.3.0",
+  "version": "1.3.5",
   "require": {
     "php": ">=5.5.0",
     "google/protobuf": "^v3.1.0"
diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c
index 48a374f..86bd40e 100644
--- a/src/php/ext/grpc/call.c
+++ b/src/php/ext/grpc/call.c
@@ -125,7 +125,12 @@
       php_grpc_add_next_index_stringl(inner_array, str_val,
                                       GRPC_SLICE_LENGTH(elem->value), false);
       add_assoc_zval(array, str_key, inner_array);
+      PHP_GRPC_FREE_STD_ZVAL(inner_array);
     }
+    efree(str_key);
+#if PHP_MAJOR_VERSION >= 7
+    efree(str_val);
+#endif
   }
   return array;
 }
@@ -256,8 +261,6 @@
   object_init(result);
   php_grpc_ulong index;
   zval *recv_status;
-  PHP_GRPC_MAKE_STD_ZVAL(recv_status);
-  object_init(recv_status);
   zval *value;
   zval *inner_value;
   zval *message_value;
@@ -439,7 +442,7 @@
   grpc_completion_queue_pluck(completion_queue, call->wrapped,
                               gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
 #if PHP_MAJOR_VERSION >= 7
-  zval recv_md;
+  zval *recv_md;
 #endif
   for (int i = 0; i < op_num; i++) {
     switch(ops[i].op) {
@@ -460,8 +463,10 @@
       array = grpc_parse_metadata_array(&recv_metadata TSRMLS_CC);
       add_property_zval(result, "metadata", array);
 #else
-      recv_md = *grpc_parse_metadata_array(&recv_metadata);
-      add_property_zval(result, "metadata", &recv_md);
+      recv_md = grpc_parse_metadata_array(&recv_metadata);
+      add_property_zval(result, "metadata", recv_md);
+      zval_ptr_dtor(recv_md);
+      PHP_GRPC_FREE_STD_ZVAL(recv_md);
 #endif
       PHP_GRPC_DELREF(array);
       break;
@@ -475,12 +480,16 @@
       }
       break;
     case GRPC_OP_RECV_STATUS_ON_CLIENT:
+      PHP_GRPC_MAKE_STD_ZVAL(recv_status);
+      object_init(recv_status);
 #if PHP_MAJOR_VERSION < 7
       array = grpc_parse_metadata_array(&recv_trailing_metadata TSRMLS_CC);
       add_property_zval(recv_status, "metadata", array);
 #else
-      recv_md = *grpc_parse_metadata_array(&recv_trailing_metadata);
-      add_property_zval(recv_status, "metadata", &recv_md);
+      recv_md = grpc_parse_metadata_array(&recv_trailing_metadata);
+      add_property_zval(recv_status, "metadata", recv_md);
+      zval_ptr_dtor(recv_md);
+      PHP_GRPC_FREE_STD_ZVAL(recv_md);
 #endif
       PHP_GRPC_DELREF(array);
       add_property_long(recv_status, "code", status);
@@ -489,6 +498,9 @@
                                    true);
       gpr_free(status_details_text);
       add_property_zval(result, "status", recv_status);
+#if PHP_MAJOR_VERSION >= 7
+      zval_ptr_dtor(recv_status);
+#endif
       PHP_GRPC_DELREF(recv_status);
       PHP_GRPC_FREE_STD_ZVAL(recv_status);
       break;
diff --git a/src/php/ext/grpc/call_credentials.c b/src/php/ext/grpc/call_credentials.c
index 625c0c6..da7100f 100644
--- a/src/php/ext/grpc/call_credentials.c
+++ b/src/php/ext/grpc/call_credentials.c
@@ -172,34 +172,54 @@
   object_init(arg);
   php_grpc_add_property_string(arg, "service_url", context.service_url, true);
   php_grpc_add_property_string(arg, "method_name", context.method_name, true);
-  zval *retval;
-  PHP_GRPC_MAKE_STD_ZVAL(retval);
+  zval *retval = NULL;
 #if PHP_MAJOR_VERSION < 7
   zval **params[1];
   params[0] = &arg;
   state->fci->params = params;
   state->fci->retval_ptr_ptr = &retval;
 #else
+  PHP_GRPC_MAKE_STD_ZVAL(retval);
   state->fci->params = arg;
   state->fci->retval = retval;
 #endif
   state->fci->param_count = 1;
 
+  PHP_GRPC_DELREF(arg);
+
   /* call the user callback function */
   zend_call_function(state->fci, state->fci_cache TSRMLS_CC);
 
   grpc_status_code code = GRPC_STATUS_OK;
   grpc_metadata_array metadata;
+  bool cleanup = true;
 
   if (Z_TYPE_P(retval) != IS_ARRAY) {
+    cleanup = false;
     code = GRPC_STATUS_INVALID_ARGUMENT;
   } else if (!create_metadata_array(retval, &metadata)) {
-    grpc_metadata_array_destroy(&metadata);
     code = GRPC_STATUS_INVALID_ARGUMENT;
   }
 
+  if (retval != NULL) {
+#if PHP_MAJOR_VERSION < 7
+    zval_ptr_dtor(&retval);
+#else
+    zval_ptr_dtor(arg);
+    zval_ptr_dtor(retval);
+    PHP_GRPC_FREE_STD_ZVAL(arg);
+    PHP_GRPC_FREE_STD_ZVAL(retval);
+#endif
+  }
+
   /* Pass control back to core */
   cb(user_data, metadata.metadata, metadata.count, code, NULL);
+  if (cleanup) {
+    for (int i = 0; i < metadata.count; i++) {
+      grpc_slice_unref(metadata.metadata[i].value);
+    }
+    grpc_metadata_array_destroy(&metadata);
+  }
 }
 
 /* Cleanup function for plugin creds API */
@@ -207,8 +227,10 @@
   plugin_state *state = (plugin_state *)ptr;
   efree(state->fci);
   efree(state->fci_cache);
+#if PHP_MAJOR_VERSION < 7
   PHP_GRPC_FREE_STD_ZVAL(state->fci->params);
   PHP_GRPC_FREE_STD_ZVAL(state->fci->retval);
+#endif
   efree(state);
 }
 
diff --git a/src/python/grpcio/grpc_version.py b/src/python/grpcio/grpc_version.py
index a37630a..52b1397 100644
--- a/src/python/grpcio/grpc_version.py
+++ b/src/python/grpcio/grpc_version.py
@@ -29,4 +29,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc_version.py.template`!!!
 
-VERSION='1.3.0rc1'
+VERSION='1.3.5'
diff --git a/src/python/grpcio_health_checking/grpc_version.py b/src/python/grpcio_health_checking/grpc_version.py
index c08110e..143c1ed 100644
--- a/src/python/grpcio_health_checking/grpc_version.py
+++ b/src/python/grpcio_health_checking/grpc_version.py
@@ -29,4 +29,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_health_checking/grpc_version.py.template`!!!
 
-VERSION='1.3.0rc1'
+VERSION='1.3.5'
diff --git a/src/python/grpcio_reflection/grpc_version.py b/src/python/grpcio_reflection/grpc_version.py
index 4565732..66f1ff9 100644
--- a/src/python/grpcio_reflection/grpc_version.py
+++ b/src/python/grpcio_reflection/grpc_version.py
@@ -29,4 +29,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_reflection/grpc_version.py.template`!!!
 
-VERSION='1.3.0rc1'
+VERSION='1.3.5'
diff --git a/src/python/grpcio_tests/grpc_version.py b/src/python/grpcio_tests/grpc_version.py
index aadfabf..a6da39c 100644
--- a/src/python/grpcio_tests/grpc_version.py
+++ b/src/python/grpcio_tests/grpc_version.py
@@ -29,4 +29,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_tests/grpc_version.py.template`!!!
 
-VERSION='1.3.0rc1'
+VERSION='1.3.5'
diff --git a/src/ruby/end2end/channel_closing_driver.rb b/src/ruby/end2end/channel_closing_driver.rb
index d3e5373..bed8c43 100755
--- a/src/ruby/end2end/channel_closing_driver.rb
+++ b/src/ruby/end2end/channel_closing_driver.rb
@@ -61,6 +61,11 @@
       'channel is closed while connectivity is watched'
   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
 end
 
diff --git a/src/ruby/end2end/channel_state_driver.rb b/src/ruby/end2end/channel_state_driver.rb
index 80fb628..9910076 100755
--- a/src/ruby/end2end/channel_state_driver.rb
+++ b/src/ruby/end2end/channel_state_driver.rb
@@ -58,6 +58,9 @@
            'It likely hangs when ended abruptly'
   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
 end
 
diff --git a/src/ruby/end2end/grpc_class_init_client.rb b/src/ruby/end2end/grpc_class_init_client.rb
index ee79292..e73ca76 100755
--- a/src/ruby/end2end/grpc_class_init_client.rb
+++ b/src/ruby/end2end/grpc_class_init_client.rb
@@ -34,44 +34,110 @@
 
 require_relative './end2end_common'
 
-def main
-  grpc_class = ''
-  OptionParser.new do |opts|
-    opts.on('--grpc_class=P', String) do |p|
-      grpc_class = p
+def construct_many(test_proc)
+  thds = []
+  4.times do
+    thds << Thread.new do
+      20.times do
+        test_proc.call
+      end
     end
-  end.parse!
+  end
+  20.times do
+    test_proc.call
+  end
+  thds.each(&:join)
+end
 
-  test_proc = nil
+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
+
+  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
   when 'channel'
-    test_proc = proc do
+    return proc do
       GRPC::Core::Channel.new('dummy_host', nil, :this_channel_is_insecure)
     end
   when 'server'
-    test_proc = proc do
+    return proc do
       GRPC::Core::Server.new({})
     end
   when 'channel_credentials'
-    test_proc = proc do
+    return proc do
       GRPC::Core::ChannelCredentials.new
     end
   when 'call_credentials'
-    test_proc = proc do
+    return proc do
       GRPC::Core::CallCredentials.new(proc { |noop| noop })
     end
   when 'compression_options'
-    test_proc = proc do
+    return proc do
       GRPC::Core::CompressionOptions.new
     end
   else
     fail "bad --grpc_class=#{grpc_class} param"
   end
+end
 
-  th = Thread.new { test_proc.call }
-  test_proc.call
-  th.join
+def main
+  grpc_class = ''
+  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
 
 main
diff --git a/src/ruby/end2end/grpc_class_init_driver.rb b/src/ruby/end2end/grpc_class_init_driver.rb
index 764d029..c65ed54 100755
--- a/src/ruby/end2end/grpc_class_init_driver.rb
+++ b/src/ruby/end2end/grpc_class_init_driver.rb
@@ -38,29 +38,40 @@
                             call_credentials
                             compression_options )
 
-  native_grpc_classes.each do |grpc_class|
-    STDERR.puts 'start client'
-    this_dir = File.expand_path(File.dirname(__FILE__))
-    client_path = File.join(this_dir, 'grpc_class_init_client.rb')
-    client_pid = Process.spawn(RbConfig.ruby,
-                               client_path,
-                               "--grpc_class=#{grpc_class}")
-    begin
-      Timeout.timeout(10) do
-        Process.wait(client_pid)
-      end
-    rescue Timeout::Error
-      STDERR.puts "timeout waiting for client pid #{client_pid}"
-      Process.kill('SIGKILL', client_pid)
-      Process.wait(client_pid)
-      STDERR.puts 'killed client child'
-      raise 'Timed out waiting for client process. ' \
-        'It likely hangs when the first constructed gRPC object has ' \
-        "type: #{grpc_class}"
-    end
+  # 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|
+      ['', 'gc', 'concurrency'].each do |stress_test_type|
+        STDERR.puts 'start client'
+        this_dir = File.expand_path(File.dirname(__FILE__))
+        client_path = File.join(this_dir, 'grpc_class_init_client.rb')
+        client_pid = Process.spawn(RbConfig.ruby,
+                                   client_path,
+                                   "--grpc_class=#{grpc_class}",
+                                   "--stress_test=#{stress_test_type}")
+        begin
+          Timeout.timeout(10) do
+            Process.wait(client_pid)
+          end
+        rescue Timeout::Error
+          STDERR.puts "timeout waiting for client pid #{client_pid}"
+          Process.kill('SIGKILL', client_pid)
+          Process.wait(client_pid)
+          STDERR.puts 'killed client child'
+          raise 'Timed out waiting for client process. ' \
+            'It likely hangs when the first constructed gRPC object has ' \
+            "type: #{grpc_class}"
+        end
 
-    client_exit_code = $CHILD_STATUS
-    fail "client failed, exit code #{client_exit_code}" if client_exit_code != 0
+        client_exit_code = $CHILD_STATUS
+        # 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
 
diff --git a/src/ruby/end2end/multiple_killed_watching_threads_driver.rb b/src/ruby/end2end/multiple_killed_watching_threads_driver.rb
new file mode 100755
index 0000000..206ec8e
--- /dev/null
+++ b/src/ruby/end2end/multiple_killed_watching_threads_driver.rb
@@ -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
diff --git a/src/ruby/end2end/sig_int_during_channel_watch_client.rb b/src/ruby/end2end/sig_int_during_channel_watch_client.rb
index 389fc5b..0c6a374 100755
--- a/src/ruby/end2end/sig_int_during_channel_watch_client.rb
+++ b/src/ruby/end2end/sig_int_during_channel_watch_client.rb
@@ -46,6 +46,8 @@
     end
   end.parse!
 
+  trap('SIGINT') { exit 0 }
+
   thd = Thread.new do
     child_thread_channel = GRPC::Core::Channel.new("localhost:#{server_port}",
                                                    {},
diff --git a/src/ruby/end2end/sig_int_during_channel_watch_driver.rb b/src/ruby/end2end/sig_int_during_channel_watch_driver.rb
index 670cda0..79a8c13 100755
--- a/src/ruby/end2end/sig_int_during_channel_watch_driver.rb
+++ b/src/ruby/end2end/sig_int_during_channel_watch_driver.rb
@@ -63,6 +63,11 @@
       'SIGINT is sent while there is an active connectivity_state call'
   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
 end
 
diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c
index fb610f5..7aaa71e 100644
--- a/src/ruby/ext/grpc/rb_channel.c
+++ b/src/ruby/ext/grpc/rb_channel.c
@@ -34,9 +34,9 @@
 #include <ruby/ruby.h>
 #include <ruby/thread.h>
 
-#include "rb_grpc_imports.generated.h"
 #include "rb_byte_buffer.h"
 #include "rb_channel.h"
+#include "rb_grpc_imports.generated.h"
 
 #include <grpc/grpc.h>
 #include <grpc/grpc_security.h>
@@ -68,29 +68,53 @@
 /* Used during the conversion of a hash to channel args during channel setup */
 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. */
 typedef struct grpc_rb_channel {
   VALUE credentials;
 
-  /* The actual channel */
-  grpc_channel *wrapped;
-  int request_safe_destroy;
-  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;
+  /* The actual channel (protected in a wrapper to tell when it's safe to
+   * destroy) */
+  bg_watched_channel *bg_wrapped;
 } grpc_rb_channel;
 
-/* Forward declarations of functions involved in temporary fix to
- * https://github.com/grpc/grpc/issues/9941 */
+typedef enum { CONTINUOUS_WATCH, WATCH_STATE_API } watch_state_op_type;
+
+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(
-    grpc_rb_channel *wrapper);
-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_unblocking_func(void*);
+    bg_watched_channel *bg);
+static void *wait_until_channel_polling_thread_started_no_gil(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 gpr_mu global_connection_polling_mu;
@@ -98,6 +122,42 @@
 static int abort_channel_polling = 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. */
 static void grpc_rb_channel_free(void *p) {
   grpc_rb_channel *ch = NULL;
@@ -106,14 +166,13 @@
   };
   ch = (grpc_rb_channel *)p;
 
-  if (ch->wrapped != NULL) {
-    grpc_rb_channel_safe_destroy(ch);
-    ch->wrapped = NULL;
-  }
-
-  if (ch->mu_init_done) {
-    gpr_mu_destroy(&ch->channel_mu);
-    gpr_cv_destroy(&ch->channel_cv);
+  if (ch->bg_wrapped != NULL) {
+    /* assumption made here: it's ok to directly gpr_mu_lock the global
+     * connection polling mutex becuse we're in a finalizer,
+     * and we can count on this thread to not be interrupted or
+     * yield the gil. */
+    grpc_rb_channel_safe_destroy(ch->bg_wrapped);
+    ch->bg_wrapped = NULL;
   }
 
   xfree(p);
@@ -146,7 +205,7 @@
 /* Allocates grpc_rb_channel instances. */
 static VALUE grpc_rb_channel_alloc(VALUE cls) {
   grpc_rb_channel *wrapper = ALLOC(grpc_rb_channel);
-  wrapper->wrapped = NULL;
+  wrapper->bg_wrapped = NULL;
   wrapper->credentials = Qnil;
   return TypedData_Wrap_Struct(cls, &grpc_channel_data_type, wrapper);
 }
@@ -168,17 +227,21 @@
   grpc_channel_credentials *creds = NULL;
   char *target_chars = NULL;
   grpc_channel_args args;
+  channel_init_try_register_stack stack;
+  int stop_waiting_for_thread_start = 0;
   MEMZERO(&args, grpc_channel_args, 1);
 
   grpc_ruby_once_init();
-  rb_thread_call_without_gvl(wait_until_channel_polling_thread_started_no_gil, NULL,
-                             wait_until_channel_polling_thread_started_unblocking_func, NULL);
+  rb_thread_call_without_gvl(
+      wait_until_channel_polling_thread_started_no_gil,
+      &stop_waiting_for_thread_start,
+      wait_until_channel_polling_thread_started_unblocking_func,
+      &stop_waiting_for_thread_start);
 
   /* "3" == 3 mandatory args */
   rb_scan_args(argc, argv, "3", &target, &channel_args, &credentials);
 
   TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper);
-  wrapper->mu_init_done = 0;
   target_chars = StringValueCStr(target);
   grpc_rb_hash_convert_to_channel_args(channel_args, &args);
   if (TYPE(credentials) == T_SYMBOL) {
@@ -195,24 +258,11 @@
   }
 
   GPR_ASSERT(ch);
-
-  wrapper->wrapped = ch;
-
-  gpr_mu_init(&wrapper->channel_mu);
-  gpr_cv_init(&wrapper->channel_cv);
-  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);
+  stack.channel = ch;
+  stack.wrapper = wrapper;
+  rb_thread_call_without_gvl(
+      channel_init_try_register_connection_polling_without_gil, &stack, NULL,
+      NULL);
 
   if (args.args != NULL) {
     xfree(args.args); /* Allocated by grpc_rb_hash_convert_to_channel_args */
@@ -223,10 +273,31 @@
     return Qnil;
   }
   rb_ivar_set(self, id_target, target);
-  wrapper->wrapped = ch;
   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:
     ch.connectivity_state       -> state
@@ -239,59 +310,70 @@
 static VALUE grpc_rb_channel_get_connectivity_state(int argc, VALUE *argv,
                                                     VALUE self) {
   VALUE try_to_connect_param = Qfalse;
-  int grpc_try_to_connect = 0;
   grpc_rb_channel *wrapper = NULL;
-  grpc_channel *ch = NULL;
+  get_state_stack stack;
 
   /* "01" == 0 mandatory args, 1 (try_to_connect) is optional */
   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);
-  ch = wrapper->wrapped;
-  if (ch == NULL) {
+  if (wrapper->bg_wrapped == NULL) {
     rb_raise(rb_eRuntimeError, "closed!");
     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 {
-  grpc_rb_channel *wrapper;
+  grpc_channel *channel;
   gpr_timespec deadline;
   int last_state;
 } watch_state_stack;
 
-static void *watch_channel_state_without_gvl(void *arg) {
-  watch_state_stack *stack = (watch_state_stack*)arg;
-  gpr_timespec deadline = stack->deadline;
-  grpc_rb_channel *wrapper = stack->wrapper;
-  int last_state = stack->last_state;
-  void *return_value = (void*)0;
+static void *wait_for_watch_state_op_complete_without_gvl(void *arg) {
+  watch_state_stack *stack = (watch_state_stack *)arg;
+  watch_state_op *op = NULL;
+  void *success = (void *)0;
 
-  gpr_mu_lock(&wrapper->channel_mu);
-  while(wrapper->current_connectivity_state == last_state &&
-        !wrapper->request_safe_destroy &&
-        !wrapper->safe_to_destroy &&
-        !wrapper->abort_watch_connectivity_state &&
-        gpr_time_cmp(deadline, gpr_now(GPR_CLOCK_REALTIME)) > 0) {
-    gpr_cv_wait(&wrapper->channel_cv, &wrapper->channel_mu, deadline);
+  gpr_mu_lock(&global_connection_polling_mu);
+  // its unsafe to do a "watch" after "channel polling abort" because the cq has
+  // been shut down.
+  if (abort_channel_polling) {
+    gpr_mu_unlock(&global_connection_polling_mu);
+    return (void *)0;
   }
-  if (wrapper->current_connectivity_state != last_state) {
-    return_value = (void*)1;
-  }
-  gpr_mu_unlock(&wrapper->channel_mu);
+  op = gpr_zalloc(sizeof(watch_state_op));
+  op->op_type = WATCH_STATE_API;
+  grpc_channel_watch_connectivity_state(stack->channel, stack->last_state,
+                                        stack->deadline, channel_polling_cq,
+                                        op);
 
-  return return_value;
+  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));
+  }
+  if (op->op.api_callback_args.success) {
+    success = (void *)1;
+  }
+  gpr_free(op);
+  gpr_mu_unlock(&global_connection_polling_mu);
+
+  return success;
 }
 
-static void watch_channel_state_unblocking_func(void *arg) {
-  grpc_rb_channel *wrapper = (grpc_rb_channel*)arg;
-  gpr_log(GPR_DEBUG, "GRPC_RUBY: watch channel state unblocking func called");
-  gpr_mu_lock(&wrapper->channel_mu);
-  wrapper->abort_watch_connectivity_state = 1;
-  gpr_cv_broadcast(&wrapper->channel_cv);
-  gpr_mu_unlock(&wrapper->channel_mu);
+static void wait_for_watch_state_op_complete_unblocking_func(void *arg) {
+  bg_watched_channel *bg = (bg_watched_channel *)arg;
+  gpr_mu_lock(&global_connection_polling_mu);
+  if (!bg->channel_destroyed) {
+    grpc_channel_destroy(bg->channel);
+    bg->channel_destroyed = 1;
+  }
+  gpr_mu_unlock(&global_connection_polling_mu);
 }
 
 /* Wait until the channel's connectivity state becomes different from
@@ -306,28 +388,31 @@
                                                       VALUE deadline) {
   grpc_rb_channel *wrapper = NULL;
   watch_state_stack stack;
-  void* out;
+  void *op_success = 0;
 
   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!");
     return Qnil;
   }
 
   if (!FIXNUM_P(last_state)) {
-    rb_raise(rb_eTypeError, "bad type for last_state. want a GRPC::Core::ChannelState constant");
+    rb_raise(
+        rb_eTypeError,
+        "bad type for last_state. want a GRPC::Core::ChannelState constant");
     return Qnil;
   }
 
-  stack.wrapper = wrapper;
-  stack.deadline = grpc_rb_time_timeval(deadline, 0);
+  stack.channel = wrapper->bg_wrapped->channel;
+  stack.deadline = grpc_rb_time_timeval(deadline, 0),
   stack.last_state = NUM2LONG(last_state);
-  out = rb_thread_call_without_gvl(watch_channel_state_without_gvl, &stack, watch_channel_state_unblocking_func, wrapper);
-  if (out) {
-    return Qtrue;
-  }
-  return Qfalse;
+
+  op_success = rb_thread_call_without_gvl(
+      wait_for_watch_state_op_complete_without_gvl, &stack,
+      wait_for_watch_state_op_complete_unblocking_func, wrapper->bg_wrapped);
+
+  return op_success ? Qtrue : Qfalse;
 }
 
 /* Create a call given a grpc_channel, in order to call method. The request
@@ -339,7 +424,6 @@
   grpc_rb_channel *wrapper = NULL;
   grpc_call *call = NULL;
   grpc_call *parent_call = NULL;
-  grpc_channel *ch = NULL;
   grpc_completion_queue *cq = NULL;
   int flags = GRPC_PROPAGATE_DEFAULTS;
   grpc_slice method_slice;
@@ -361,8 +445,7 @@
 
   cq = grpc_completion_queue_create(NULL);
   TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper);
-  ch = wrapper->wrapped;
-  if (ch == NULL) {
+  if (wrapper->bg_wrapped == NULL) {
     rb_raise(rb_eRuntimeError, "closed!");
     return Qnil;
   }
@@ -370,8 +453,8 @@
   method_slice =
       grpc_slice_from_copied_buffer(RSTRING_PTR(method), RSTRING_LEN(method));
 
-  call = grpc_channel_create_call(ch, parent_call, flags, cq, method_slice,
-                                  host_slice_ptr,
+  call = grpc_channel_create_call(wrapper->bg_wrapped->channel, parent_call,
+                                  flags, cq, method_slice, host_slice_ptr,
                                   grpc_rb_time_timeval(deadline,
                                                        /* absolute time */ 0),
                                   NULL);
@@ -396,15 +479,16 @@
 }
 
 /* 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) {
   grpc_rb_channel *wrapper = NULL;
-  grpc_channel *ch = NULL;
 
   TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper);
-  ch = wrapper->wrapped;
-  if (ch != NULL) {
-    grpc_rb_channel_safe_destroy(wrapper);
-    wrapper->wrapped = NULL;
+  if (wrapper->bg_wrapped != NULL) {
+    rb_thread_call_without_gvl(channel_safe_destroy_without_gil,
+                               wrapper->bg_wrapped, NULL, NULL);
+    wrapper->bg_wrapped = NULL;
   }
 
   return Qnil;
@@ -417,64 +501,110 @@
   char *target = NULL;
 
   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);
   gpr_free(target);
 
   return res;
 }
 
-// Either start polling channel connection state or signal that it's free to
-// destroy.
-// Not safe to call while a channel's connection state is polled.
-static void grpc_rb_channel_try_register_connection_polling(
-  grpc_rb_channel *wrapper) {
-  grpc_connectivity_state conn_state;
-  gpr_timespec sleep_time = gpr_time_add(
-      gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(20, GPR_TIMESPAN));
+/* Needs to be called under global_connection_polling_mu */
+static int bg_watched_channel_list_lookup(bg_watched_channel *target) {
+  bg_watched_channel *cur = bg_watched_channel_list_head;
 
-  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;
+  while (cur != NULL) {
+    if (cur == target) {
+      return 1;
+    }
+    cur = cur->next;
   }
-  gpr_mu_lock(&global_connection_polling_mu);
 
-  GPR_ASSERT(channel_polling_thread_started || abort_channel_polling);
-  conn_state = grpc_channel_check_connectivity_state(wrapper->wrapped, 0);
-  if (conn_state != wrapper->current_connectivity_state) {
-    wrapper->current_connectivity_state = conn_state;
-    gpr_cv_broadcast(&wrapper->channel_cv);
-  }
-  // avoid posting work to the channel polling cq if it's been shutdown
-  if (!abort_channel_polling && conn_state != GRPC_CHANNEL_SHUTDOWN) {
-    grpc_channel_watch_connectivity_state(
-        wrapper->wrapped, conn_state, sleep_time, channel_polling_cq, wrapper);
-  } else {
-    wrapper->safe_to_destroy = 1;
-    gpr_cv_broadcast(&wrapper->channel_cv);
-  }
-  gpr_mu_unlock(&global_connection_polling_mu);
-  gpr_mu_unlock(&wrapper->channel_mu);
+  return 0;
 }
 
-// Note requires wrapper->wrapped, wrapper->channel_mu/cv initialized
-static void grpc_rb_channel_safe_destroy(grpc_rb_channel *wrapper) {
-  gpr_mu_lock(&wrapper->channel_mu);
-  wrapper->request_safe_destroy = 1;
+/* Needs to be called under global_connection_polling_mu */
+static bg_watched_channel *bg_watched_channel_list_create_and_add(
+    grpc_channel *channel) {
+  bg_watched_channel *watched = gpr_zalloc(sizeof(bg_watched_channel));
 
-  while (!wrapper->safe_to_destroy) {
-    gpr_cv_wait(&wrapper->channel_cv, &wrapper->channel_mu,
-                gpr_inf_future(GPR_CLOCK_REALTIME));
+  watched->channel = channel;
+  watched->next = bg_watched_channel_list_head;
+  watched->refcount = 1;
+  bg_watched_channel_list_head = watched;
+  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;
   }
-  GPR_ASSERT(wrapper->safe_to_destroy);
-  gpr_mu_unlock(&wrapper->channel_mu);
+  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);
+}
 
-  grpc_channel_destroy(wrapper->wrapped);
+/* 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);
+  return NULL;
+}
+
+// Needs to be called under global_connection_poolling_mu
+static void grpc_rb_channel_try_register_connection_polling(
+    bg_watched_channel *bg) {
+  grpc_connectivity_state conn_state;
+  watch_state_op *op = NULL;
+
+  GPR_ASSERT(channel_polling_thread_started || abort_channel_polling);
+
+  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;
+  }
+
+  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
@@ -485,6 +615,8 @@
 // early and falls back to current behavior.
 static void *run_poll_channels_loop_no_gil(void *arg) {
   grpc_event event;
+  watch_state_op *op = NULL;
+  bg_watched_channel *bg = NULL;
   (void)arg;
   gpr_log(GPR_DEBUG, "GRPC_RUBY: run_poll_channels_loop_no_gil - begin");
 
@@ -500,29 +632,70 @@
     if (event.type == GRPC_QUEUE_SHUTDOWN) {
       break;
     }
+    gpr_mu_lock(&global_connection_polling_mu);
     if (event.type == GRPC_OP_COMPLETE) {
-      grpc_rb_channel_try_register_connection_polling((grpc_rb_channel *)event.tag);
+      op = (watch_state_op *)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);
-  gpr_log(GPR_DEBUG, "GRPC_RUBY: run_poll_channels_loop_no_gil - exit connection polling loop");
+  gpr_log(GPR_DEBUG,
+          "GRPC_RUBY: run_poll_channels_loop_no_gil - exit connection polling "
+          "loop");
   return NULL;
 }
 
 // Notify the channel polling loop to cleanup and shutdown.
 static void run_poll_channels_loop_unblocking_func(void *arg) {
+  bg_watched_channel *bg = NULL;
   (void)arg;
+
   gpr_mu_lock(&global_connection_polling_mu);
-  gpr_log(GPR_DEBUG, "GRPC_RUBY: run_poll_channels_loop_unblocking_func - begin aborting connection polling");
+  gpr_log(GPR_DEBUG,
+          "GRPC_RUBY: run_poll_channels_loop_unblocking_func - begin aborting "
+          "connection polling");
+  // early out after first time through
+  if (abort_channel_polling) {
+    gpr_mu_unlock(&global_connection_polling_mu);
+    return;
+  }
   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);
+  gpr_cv_broadcast(&global_connection_polling_cv);
   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.
 static VALUE run_poll_channels_loop(VALUE arg) {
   (void)arg;
-  gpr_log(GPR_DEBUG, "GRPC_RUBY: run_poll_channels_loop - create connection polling thread");
+  gpr_log(
+      GPR_DEBUG,
+      "GRPC_RUBY: run_poll_channels_loop - create connection polling thread");
   rb_thread_call_without_gvl(run_poll_channels_loop_no_gil, NULL,
                              run_poll_channels_loop_unblocking_func, NULL);
 
@@ -530,10 +703,11 @@
 }
 
 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_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_inf_future(GPR_CLOCK_REALTIME));
   }
@@ -542,13 +716,24 @@
   return NULL;
 }
 
-static void wait_until_channel_polling_thread_started_unblocking_func(void* arg) {
+static void wait_until_channel_polling_thread_started_unblocking_func(
+    void *arg) {
+  int *stop_waiting = (int *)arg;
+  gpr_mu_lock(&global_connection_polling_mu);
+  gpr_log(GPR_DEBUG,
+          "GRPC_RUBY: interrupt wait for channel polling thread to start");
+  *stop_waiting = 1;
+  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);
-  gpr_log(GPR_DEBUG, "GRPC_RUBY: wait_until_channel_polling_thread_started_unblocking_func - begin aborting connection polling");
   abort_channel_polling = 1;
   gpr_cv_broadcast(&global_connection_polling_cv);
   gpr_mu_unlock(&global_connection_polling_mu);
+  return NULL;
 }
 
 /* Temporary fix for
@@ -576,10 +761,8 @@
 
   if (!RTEST(background_thread)) {
     gpr_log(GPR_DEBUG, "GRPC_RUBY: failed to spawn channel polling thread");
-    gpr_mu_lock(&global_connection_polling_mu);
-    abort_channel_polling = 1;
-    gpr_cv_broadcast(&global_connection_polling_cv);
-    gpr_mu_unlock(&global_connection_polling_mu);
+    rb_thread_call_without_gvl(set_abort_channel_polling_without_gil, NULL,
+                               NULL, NULL);
   }
 }
 
@@ -658,5 +841,5 @@
 grpc_channel *grpc_rb_get_wrapped_channel(VALUE v) {
   grpc_rb_channel *wrapper = NULL;
   TypedData_Get_Struct(v, grpc_rb_channel, &grpc_channel_data_type, wrapper);
-  return wrapper->wrapped;
+  return wrapper->bg_wrapped->channel;
 }
diff --git a/src/ruby/ext/grpc/rb_event_thread.c b/src/ruby/ext/grpc/rb_event_thread.c
index 9e85bbc..f1a08a7 100644
--- a/src/ruby/ext/grpc/rb_event_thread.c
+++ b/src/ruby/ext/grpc/rb_event_thread.c
@@ -106,17 +106,17 @@
   grpc_rb_event *event = NULL;
   (void)param;
   gpr_mu_lock(&event_queue.mu);
-  while ((event = grpc_rb_event_queue_dequeue()) == NULL) {
+  while (!event_queue.abort) {
+    if ((event = grpc_rb_event_queue_dequeue()) != NULL) {
+      gpr_mu_unlock(&event_queue.mu);
+      return event;
+    }
     gpr_cv_wait(&event_queue.cv,
                 &event_queue.mu,
                 gpr_inf_future(GPR_CLOCK_REALTIME));
-    if (event_queue.abort) {
-      gpr_mu_unlock(&event_queue.mu);
-      return NULL;
-    }
   }
   gpr_mu_unlock(&event_queue.mu);
-  return event;
+  return NULL;
 }
 
 static void grpc_rb_event_unblocking_func(void *arg) {
diff --git a/src/ruby/ext/grpc/rb_grpc.c b/src/ruby/ext/grpc/rb_grpc.c
index 584b5db..e0be5d7 100644
--- a/src/ruby/ext/grpc/rb_grpc.c
+++ b/src/ruby/ext/grpc/rb_grpc.c
@@ -295,11 +295,12 @@
 
 static void grpc_ruby_once_init_internal() {
   grpc_init();
-  grpc_rb_event_queue_thread_start();
-  grpc_rb_channel_polling_thread_start();
   atexit(grpc_rb_shutdown);
 }
 
+static VALUE bg_thread_init_rb_mu = Qundef;
+static int bg_thread_init_done = 0;
+
 void grpc_ruby_once_init() {
   /* 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
@@ -312,6 +313,18 @@
    * schedule our initialization and destruction only once.
    */
   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() {
@@ -320,6 +333,9 @@
     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_mGrpcCore = rb_define_module_under(grpc_rb_mGRPC, "Core");
   grpc_rb_sNewServerRpc =
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.c b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
index 063f921..332907c 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.c
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
@@ -103,6 +103,7 @@
 grpc_alarm_cancel_type grpc_alarm_cancel_import;
 grpc_alarm_destroy_type grpc_alarm_destroy_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_create_call_type grpc_channel_create_call_import;
 grpc_channel_ping_type grpc_channel_ping_import;
@@ -400,6 +401,7 @@
   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_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_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");
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
index f5dcd68..e6a3f6d 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
@@ -260,6 +260,9 @@
 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;
 #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);
 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
diff --git a/src/ruby/lib/grpc/version.rb b/src/ruby/lib/grpc/version.rb
index fa80468..2759531 100644
--- a/src/ruby/lib/grpc/version.rb
+++ b/src/ruby/lib/grpc/version.rb
@@ -29,5 +29,5 @@
 
 # GRPC contains the General RPC module.
 module GRPC
-  VERSION = '1.3.0.pre1'
+  VERSION = '1.3.5'
 end
diff --git a/src/ruby/spec/channel_connection_spec.rb b/src/ruby/spec/channel_connection_spec.rb
index 940d68b..c8a7856 100644
--- a/src/ruby/spec/channel_connection_spec.rb
+++ b/src/ruby/spec/channel_connection_spec.rb
@@ -28,6 +28,10 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 require 'grpc'
+require 'timeout'
+
+include Timeout
+include GRPC::Core
 
 # A test message
 class EchoMsg
@@ -62,7 +66,7 @@
 EchoStub = EchoService.rpc_stub_class
 
 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)
   @srv.handle(EchoService)
   @server_thd = Thread.new { @srv.run }
@@ -138,4 +142,32 @@
 
     stop_server
   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
diff --git a/src/ruby/tools/version.rb b/src/ruby/tools/version.rb
index fa19aee..f93c1f1 100644
--- a/src/ruby/tools/version.rb
+++ b/src/ruby/tools/version.rb
@@ -29,6 +29,6 @@
 
 module GRPC
   module Tools
-    VERSION = '1.3.0.pre1'
+    VERSION = '1.3.5'
   end
 end
diff --git a/templates/binding.gyp.template b/templates/binding.gyp.template
index a2e8c58..933174a 100644
--- a/templates/binding.gyp.template
+++ b/templates/binding.gyp.template
@@ -41,15 +41,16 @@
   {
     'variables': {
       'runtime%': 'node',
-      # UV integration in C core is enabled by default. It can be disabled
-      # by setting this argument to anything else.
-      'grpc_uv%': 'true',
       # Some Node installations use the system installation of OpenSSL, and on
       # some systems, the system OpenSSL still does not have ALPN support. This
       # will let users recompile gRPC to work without ALPN.
       'grpc_alpn%': 'true',
       # Indicates that the library should be built with gcov.
-      'grpc_gcov%': 'false'
+      'grpc_gcov%': 'false',
+      # Indicates that the library should be built with compatibility for musl
+      # libc, so that it can run on Alpine Linux. This is only necessary if not
+      # building on Alpine Linux
+      'grpc_alpine%': 'false'
     },
     'target_defaults': {
       'configurations': {
@@ -83,17 +84,11 @@
         'include'
       ],
       'defines': [
-        'GPR_BACKWARDS_COMPATIBILITY_MODE'
+        'GPR_BACKWARDS_COMPATIBILITY_MODE',
+        'GRPC_ARES=0',
+        'GRPC_UV'
       ],
       'conditions': [
-        ['grpc_uv=="true"', {
-          'defines': [
-            'GRPC_ARES=0',
-            # Disabling this while bugs are ironed out. Uncomment this to
-            # re-enable libuv integration in C core.
-            'GRPC_UV'
-          ]
-        }],
         ['grpc_gcov=="true"', {
           % for arg, prop in [('CPPFLAGS', 'cflags'), ('DEFINES', 'defines'), ('LDFLAGS', 'ldflags')]:
           %  if configs['gcov'].get(arg, None) is not None:
@@ -105,6 +100,11 @@
           %  endif
           % endfor
         }],
+        ['grpc_alpine=="true"', {
+          'defines': [
+            'GPR_MUSL_LIBC_COMPAT'
+          ]
+        }],
         ['OS!="win" and runtime=="electron"', {
           "defines": [
             'OPENSSL_NO_THREADS'
@@ -233,6 +233,10 @@
               }
             ]
           },
+        ]
+      }],
+      ['OS == "win"', {
+        'targets': [
           # Only want to compile zlib under Windows
           % for module in node_modules:
           % for lib in libs:
@@ -264,13 +268,6 @@
       }]
     ],
     'targets': [
-  <%
-      for lib in libs:
-        if 'grpc' in lib.transitive_deps or lib.name == 'grpc':
-          lib.deps.append('node_modules/cares/deps/cares/cares.gyp:cares')
-      for module in node_modules:
-        module.deps.append('node_modules/cares/deps/cares/cares.gyp:cares')
-  %>
       % for module in node_modules:
       % for lib in libs:
       % if lib.name in module.transitive_deps and lib.name not in ('boringssl', 'z'):
diff --git a/templates/config.m4.template b/templates/config.m4.template
index 13ff738..38d86e6 100644
--- a/templates/config.m4.template
+++ b/templates/config.m4.template
@@ -10,8 +10,6 @@
     PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/include)
     PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/src/php/ext/grpc)
     PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/third_party/boringssl/include)
-    PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/third_party/cares)
-    PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/third_party/cares/cares)
 
     LIBS="-lpthread $LIBS"
 
@@ -23,10 +21,8 @@
 
     case $host in
       *darwin*)
-        PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/third_party/cares/config_darwin)
         ;;
       *)
-        PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/third_party/cares/config_linux)
         PHP_ADD_LIBRARY(rt,,GRPC_SHARED_LIBADD)
         PHP_ADD_LIBRARY(rt)
         ;;
@@ -46,7 +42,7 @@
       , $ext_shared, , -Wall -Werror ${"\\"}
       -Wno-parentheses-equality -Wno-unused-value -std=c11 ${"\\"}
       -fvisibility=hidden -DOPENSSL_NO_ASM -D_GNU_SOURCE -DWIN32_LEAN_AND_MEAN ${"\\"}
-      -D_HAS_EXCEPTIONS=0 -DNOMINMAX)
+      -D_HAS_EXCEPTIONS=0 -DNOMINMAX -DGRPC_ARES=0)
 
     PHP_ADD_BUILD_DIR($ext_builddir/src/php/ext/grpc)
   <%
diff --git a/templates/package.json.template b/templates/package.json.template
index b69fd28..551c25f 100644
--- a/templates/package.json.template
+++ b/templates/package.json.template
@@ -36,8 +36,7 @@
       "lodash": "^4.15.0",
       "nan": "^2.0.0",
       "node-pre-gyp": "^0.6.0",
-      "protobufjs": "^6.7.0",
-      "cares": "^1.1.5"
+      "protobufjs": "^5.0.0"
     },
     "devDependencies": {
       "async": "^2.0.1",
diff --git a/templates/package.xml.template b/templates/package.xml.template
index 8655cfa..394b815 100644
--- a/templates/package.xml.template
+++ b/templates/package.xml.template
@@ -12,7 +12,7 @@
     <email>grpc-packages@google.com</email>
     <active>yes</active>
    </lead>
-   <date>2017-03-01</date>
+   <date>2017-05-05</date>
    <time>16:06:07</time>
    <version>
     <release>${settings.php_version.php()}</release>
@@ -24,8 +24,7 @@
    </stability>
    <license>BSD</license>
    <notes>
-  - Added arg info macros #9751
-  - Updated codegen to be consistent with protobuf #9492
+  - Fixed some memory leaks #9559, #10996
    </notes>
    <contents>
     <dir baseinstalldir="/" name="/">
@@ -43,6 +42,7 @@
       % endfor
       % endif
       % endfor
+      <file name="LICENSE" role="doc" />
     </dir>
    </contents>
    <dependencies>
@@ -358,5 +358,21 @@
   - Updated codegen to be consistent with protobuf #9492
      </notes>
     </release>
+    <release>
+     <version>
+      <release>1.2.0</release>
+      <api>1.2.0</api>
+     </version>
+     <stability>
+      <release>stable</release>
+      <api>stable</api>
+     </stability>
+     <date>2017-03-20</date>
+     <license>BSD</license>
+     <notes>
+  - Added arg info macros #9751
+  - Updated codegen to be consistent with protobuf #9492
+     </notes>
+    </release>
    </changelog>
   </package>
diff --git a/templates/src/csharp/build_packages_dotnetcli.bat.template b/templates/src/csharp/build_packages_dotnetcli.bat.template
index 91808e0..5f6ffb9 100755
--- a/templates/src/csharp/build_packages_dotnetcli.bat.template
+++ b/templates/src/csharp/build_packages_dotnetcli.bat.template
@@ -62,11 +62,11 @@
   @rem To be able to build, we also need to put grpc_csharp_ext to its normal location
   xcopy /Y /I nativelibs\windows_x64\grpc_csharp_ext.dll ..\..\cmake\build\x64\Release${"\\"}
   
-  %%DOTNET% pack --configuration Release Grpc.Core --output ..\..\..\artifacts || goto :error
-  %%DOTNET% pack --configuration Release Grpc.Core.Testing --output ..\..\..\artifacts || goto :error
-  %%DOTNET% pack --configuration Release Grpc.Auth --output ..\..\..\artifacts || goto :error
-  %%DOTNET% pack --configuration Release Grpc.HealthCheck --output ..\..\..\artifacts || goto :error
-  %%DOTNET% pack --configuration Release Grpc.Reflection --output ..\..\..\artifacts || goto :error
+  %%DOTNET% pack --configuration Release --include-symbols --include-source 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 --include-symbols --include-source Grpc.Auth --output ..\..\..\artifacts || goto :error
+  %%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.HealthCheck --output ..\..\..\artifacts || goto :error
+  %%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.Reflection --output ..\..\..\artifacts || goto :error
   
   %%NUGET% pack Grpc.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts || goto :error
   %%NUGET% pack Grpc.Tools.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts
diff --git a/templates/src/csharp/build_packages_dotnetcli.sh.template b/templates/src/csharp/build_packages_dotnetcli.sh.template
index 374b236..d37f4eb 100755
--- a/templates/src/csharp/build_packages_dotnetcli.sh.template
+++ b/templates/src/csharp/build_packages_dotnetcli.sh.template
@@ -66,11 +66,11 @@
   mkdir -p ../../libs/opt
   cp nativelibs/linux_x64/libgrpc_csharp_ext.so ../../libs/opt
   
-  dotnet pack --configuration Release Grpc.Core --output ../../../artifacts
-  dotnet pack --configuration Release Grpc.Core.Testing --output ../../../artifacts
-  dotnet pack --configuration Release Grpc.Auth --output ../../../artifacts
-  dotnet pack --configuration Release Grpc.HealthCheck --output ../../../artifacts
-  dotnet pack --configuration Release Grpc.Reflection --output ../../../artifacts
+  dotnet pack --configuration Release --include-symbols --include-source Grpc.Core --output ../../../artifacts
+  dotnet pack --configuration Release --include-symbols --include-source Grpc.Core.Testing --output ../../../artifacts
+  dotnet pack --configuration Release --include-symbols --include-source Grpc.Auth --output ../../../artifacts
+  dotnet pack --configuration Release --include-symbols --include-source Grpc.HealthCheck --output ../../../artifacts
+  dotnet pack --configuration Release --include-symbols --include-source Grpc.Reflection --output ../../../artifacts
   
   nuget pack Grpc.nuspec -Version "${settings.csharp_version}" -OutputDirectory ../../artifacts
   nuget pack Grpc.Tools.nuspec -Version "${settings.csharp_version}" -OutputDirectory ../../artifacts
diff --git a/test/core/surface/concurrent_connectivity_test.c b/test/core/surface/concurrent_connectivity_test.c
index 2f7c3df..73f2cd3 100644
--- a/test/core/surface/concurrent_connectivity_test.c
+++ b/test/core/surface/concurrent_connectivity_test.c
@@ -61,6 +61,14 @@
 #define DELAY_MILLIS 10
 #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 int detag(void *p) { return (int)(uintptr_t)p; }
 
@@ -79,6 +87,8 @@
           grpc_timeout_milliseconds_to_deadline(POLL_MILLIS);
       GPR_ASSERT(grpc_completion_queue_next(cq, poll_time, NULL).type ==
                  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_completion_queue_destroy(cq);
@@ -166,11 +176,10 @@
   gpr_free(pollset);
 }
 
-int main(int argc, char **argv) {
+int run_concurrent_connectivity_test() {
   struct server_thread_args args;
   memset(&args, 0, sizeof(args));
 
-  grpc_test_init(argc, argv);
   grpc_init();
 
   gpr_thd_id threads[NUM_THREADS];
@@ -240,3 +249,59 @@
   grpc_shutdown();
   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(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();
+}
diff --git a/test/core/surface/num_external_connectivity_watchers_test.c b/test/core/surface/num_external_connectivity_watchers_test.c
new file mode 100644
index 0000000..96288ab
--- /dev/null
+++ b/test/core/surface/num_external_connectivity_watchers_test.c
@@ -0,0 +1,221 @@
+/*
+ *
+ * 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);
+  GPR_ASSERT(grpc_channel_num_external_connectivity_watchers(channel) == 1);
+  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);
+    GPR_ASSERT(grpc_channel_num_external_connectivity_watchers(channel) ==
+               (int)i);
+  }
+  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);
+    GPR_ASSERT(grpc_channel_num_external_connectivity_watchers(channel) ==
+               (int)i);
+  }
+  channel_idle_poll_for_timeout(channel, cq);
+  for (size_t i = 3; i <= 5; i++) {
+    channel_idle_start_watch(channel, cq);
+  }
+  GPR_ASSERT(grpc_channel_num_external_connectivity_watchers(channel) >= 3);
+  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);
+  GPR_ASSERT(grpc_channel_num_external_connectivity_watchers(channel) == 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);
+}
diff --git a/test/core/surface/sequential_connectivity_test.c b/test/core/surface/sequential_connectivity_test.c
index 5f66f90..8a6dd69 100644
--- a/test/core/surface/sequential_connectivity_test.c
+++ b/test/core/surface/sequential_connectivity_test.c
@@ -99,6 +99,9 @@
                                             connect_deadline, cq, NULL);
       grpc_event ev = grpc_completion_queue_next(
           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.tag == NULL);
       GPR_ASSERT(ev.success == true);
diff --git a/third_party/cares/ares_build.h b/third_party/cares/ares_build.h
index 7d69f1e..d6b3d49 100644
--- a/third_party/cares/ares_build.h
+++ b/third_party/cares/ares_build.h
@@ -251,4 +251,14 @@
   typedef CARES_TYPEOF_ARES_SOCKLEN_T ares_socklen_t;
 #endif
 
+/* Undefine UNICODE, as c-ares does not use the ANSI version of functions */
+/* explicitly. */
+#ifdef UNICODE
+#  undef UNICODE
+#endif
+
+#ifdef _UNICODE
+#  undef _UNICODE
+#endif
+
 #endif /* __CARES_BUILD_H */
diff --git a/third_party/protobuf b/third_party/protobuf
index 4a0dd03..593e917 160000
--- a/third_party/protobuf
+++ b/third_party/protobuf
@@ -1 +1 @@
-Subproject commit 4a0dd03e52e09332c8fd0f8f26a8e0ae9f911182
+Subproject commit 593e917c176b5bc5aafa57bf9f6030d749d91cd5
diff --git a/tools/distrib/python/grpcio_tools/grpc_version.py b/tools/distrib/python/grpcio_tools/grpc_version.py
index 289c0b1..7178681 100644
--- a/tools/distrib/python/grpcio_tools/grpc_version.py
+++ b/tools/distrib/python/grpcio_tools/grpc_version.py
@@ -29,4 +29,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/tools/distrib/python/grpcio_tools/grpc_version.py.template`!!!
 
-VERSION='1.3.0rc1'
+VERSION='1.3.5'
diff --git a/tools/distrib/python/grpcio_tools/protoc_lib_deps.py b/tools/distrib/python/grpcio_tools/protoc_lib_deps.py
index 7e8cd98..c2aa619 100644
--- a/tools/distrib/python/grpcio_tools/protoc_lib_deps.py
+++ b/tools/distrib/python/grpcio_tools/protoc_lib_deps.py
@@ -29,7 +29,7 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 # AUTO-GENERATED BY make_grpcio_tools.py!
-CC_FILES=['google/protobuf/compiler/zip_writer.cc', 'google/protobuf/compiler/subprocess.cc', 'google/protobuf/compiler/ruby/ruby_generator.cc', 'google/protobuf/compiler/python/python_generator.cc', 'google/protobuf/compiler/plugin.pb.cc', 'google/protobuf/compiler/plugin.cc', 'google/protobuf/compiler/php/php_generator.cc', 'google/protobuf/compiler/objectivec/objectivec_primitive_field.cc', 'google/protobuf/compiler/objectivec/objectivec_oneof.cc', 'google/protobuf/compiler/objectivec/objectivec_message_field.cc', 'google/protobuf/compiler/objectivec/objectivec_message.cc', 'google/protobuf/compiler/objectivec/objectivec_map_field.cc', 'google/protobuf/compiler/objectivec/objectivec_helpers.cc', 'google/protobuf/compiler/objectivec/objectivec_generator.cc', 'google/protobuf/compiler/objectivec/objectivec_file.cc', 'google/protobuf/compiler/objectivec/objectivec_field.cc', 'google/protobuf/compiler/objectivec/objectivec_extension.cc', 'google/protobuf/compiler/objectivec/objectivec_enum_field.cc', 'google/protobuf/compiler/objectivec/objectivec_enum.cc', 'google/protobuf/compiler/js/well_known_types_embed.cc', 'google/protobuf/compiler/js/js_generator.cc', 'google/protobuf/compiler/javanano/javanano_primitive_field.cc', 'google/protobuf/compiler/javanano/javanano_message_field.cc', 'google/protobuf/compiler/javanano/javanano_message.cc', 'google/protobuf/compiler/javanano/javanano_map_field.cc', 'google/protobuf/compiler/javanano/javanano_helpers.cc', 'google/protobuf/compiler/javanano/javanano_generator.cc', 'google/protobuf/compiler/javanano/javanano_file.cc', 'google/protobuf/compiler/javanano/javanano_field.cc', 'google/protobuf/compiler/javanano/javanano_extension.cc', 'google/protobuf/compiler/javanano/javanano_enum_field.cc', 'google/protobuf/compiler/javanano/javanano_enum.cc', 'google/protobuf/compiler/java/java_string_field_lite.cc', 'google/protobuf/compiler/java/java_string_field.cc', 'google/protobuf/compiler/java/java_shared_code_generator.cc', 'google/protobuf/compiler/java/java_service.cc', 'google/protobuf/compiler/java/java_primitive_field_lite.cc', 'google/protobuf/compiler/java/java_primitive_field.cc', 'google/protobuf/compiler/java/java_name_resolver.cc', 'google/protobuf/compiler/java/java_message_lite.cc', 'google/protobuf/compiler/java/java_message_field_lite.cc', 'google/protobuf/compiler/java/java_message_field.cc', 'google/protobuf/compiler/java/java_message_builder_lite.cc', 'google/protobuf/compiler/java/java_message_builder.cc', 'google/protobuf/compiler/java/java_message.cc', 'google/protobuf/compiler/java/java_map_field_lite.cc', 'google/protobuf/compiler/java/java_map_field.cc', 'google/protobuf/compiler/java/java_lazy_message_field_lite.cc', 'google/protobuf/compiler/java/java_lazy_message_field.cc', 'google/protobuf/compiler/java/java_helpers.cc', 'google/protobuf/compiler/java/java_generator_factory.cc', 'google/protobuf/compiler/java/java_generator.cc', 'google/protobuf/compiler/java/java_file.cc', 'google/protobuf/compiler/java/java_field.cc', 'google/protobuf/compiler/java/java_extension_lite.cc', 'google/protobuf/compiler/java/java_extension.cc', 'google/protobuf/compiler/java/java_enum_lite.cc', 'google/protobuf/compiler/java/java_enum_field_lite.cc', 'google/protobuf/compiler/java/java_enum_field.cc', 'google/protobuf/compiler/java/java_enum.cc', 'google/protobuf/compiler/java/java_doc_comment.cc', 'google/protobuf/compiler/java/java_context.cc', 'google/protobuf/compiler/csharp/csharp_wrapper_field.cc', 'google/protobuf/compiler/csharp/csharp_source_generator_base.cc', 'google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc', 'google/protobuf/compiler/csharp/csharp_repeated_message_field.cc', 'google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc', 'google/protobuf/compiler/csharp/csharp_reflection_class.cc', 'google/protobuf/compiler/csharp/csharp_primitive_field.cc', 'google/protobuf/compiler/csharp/csharp_message_field.cc', 'google/protobuf/compiler/csharp/csharp_message.cc', 'google/protobuf/compiler/csharp/csharp_map_field.cc', 'google/protobuf/compiler/csharp/csharp_helpers.cc', 'google/protobuf/compiler/csharp/csharp_generator.cc', 'google/protobuf/compiler/csharp/csharp_field_base.cc', 'google/protobuf/compiler/csharp/csharp_enum_field.cc', 'google/protobuf/compiler/csharp/csharp_enum.cc', 'google/protobuf/compiler/csharp/csharp_doc_comment.cc', 'google/protobuf/compiler/cpp/cpp_string_field.cc', 'google/protobuf/compiler/cpp/cpp_service.cc', 'google/protobuf/compiler/cpp/cpp_primitive_field.cc', 'google/protobuf/compiler/cpp/cpp_message_field.cc', 'google/protobuf/compiler/cpp/cpp_message.cc', 'google/protobuf/compiler/cpp/cpp_map_field.cc', 'google/protobuf/compiler/cpp/cpp_helpers.cc', 'google/protobuf/compiler/cpp/cpp_generator.cc', 'google/protobuf/compiler/cpp/cpp_file.cc', 'google/protobuf/compiler/cpp/cpp_field.cc', 'google/protobuf/compiler/cpp/cpp_extension.cc', 'google/protobuf/compiler/cpp/cpp_enum_field.cc', 'google/protobuf/compiler/cpp/cpp_enum.cc', 'google/protobuf/compiler/command_line_interface.cc', 'google/protobuf/compiler/code_generator.cc', 'google/protobuf/wrappers.pb.cc', 'google/protobuf/wire_format.cc', 'google/protobuf/util/type_resolver_util.cc', 'google/protobuf/util/time_util.cc', 'google/protobuf/util/message_differencer.cc', 'google/protobuf/util/json_util.cc', 'google/protobuf/util/internal/utility.cc', 'google/protobuf/util/internal/type_info_test_helper.cc', 'google/protobuf/util/internal/type_info.cc', 'google/protobuf/util/internal/protostream_objectwriter.cc', 'google/protobuf/util/internal/protostream_objectsource.cc', 'google/protobuf/util/internal/proto_writer.cc', 'google/protobuf/util/internal/object_writer.cc', 'google/protobuf/util/internal/json_stream_parser.cc', 'google/protobuf/util/internal/json_objectwriter.cc', 'google/protobuf/util/internal/json_escaping.cc', 'google/protobuf/util/internal/field_mask_utility.cc', 'google/protobuf/util/internal/error_listener.cc', 'google/protobuf/util/internal/default_value_objectwriter.cc', 'google/protobuf/util/internal/datapiece.cc', 'google/protobuf/util/field_mask_util.cc', 'google/protobuf/util/field_comparator.cc', 'google/protobuf/util/delimited_message_util.cc', 'google/protobuf/unknown_field_set.cc', 'google/protobuf/type.pb.cc', 'google/protobuf/timestamp.pb.cc', 'google/protobuf/text_format.cc', 'google/protobuf/stubs/substitute.cc', 'google/protobuf/stubs/mathlimits.cc', 'google/protobuf/struct.pb.cc', 'google/protobuf/source_context.pb.cc', 'google/protobuf/service.cc', 'google/protobuf/reflection_ops.cc', 'google/protobuf/message.cc', 'google/protobuf/map_field.cc', 'google/protobuf/io/zero_copy_stream_impl.cc', 'google/protobuf/io/tokenizer.cc', 'google/protobuf/io/strtod.cc', 'google/protobuf/io/printer.cc', 'google/protobuf/io/gzip_stream.cc', 'google/protobuf/generated_message_reflection.cc', 'google/protobuf/field_mask.pb.cc', 'google/protobuf/extension_set_heavy.cc', 'google/protobuf/empty.pb.cc', 'google/protobuf/dynamic_message.cc', 'google/protobuf/duration.pb.cc', 'google/protobuf/descriptor_database.cc', 'google/protobuf/descriptor.pb.cc', 'google/protobuf/descriptor.cc', 'google/protobuf/compiler/parser.cc', 'google/protobuf/compiler/importer.cc', 'google/protobuf/api.pb.cc', 'google/protobuf/any.pb.cc', 'google/protobuf/any.cc', 'google/protobuf/wire_format_lite.cc', 'google/protobuf/stubs/time.cc', 'google/protobuf/stubs/strutil.cc', 'google/protobuf/stubs/structurally_valid.cc', 'google/protobuf/stubs/stringprintf.cc', 'google/protobuf/stubs/stringpiece.cc', 'google/protobuf/stubs/statusor.cc', 'google/protobuf/stubs/status.cc', 'google/protobuf/stubs/once.cc', 'google/protobuf/stubs/int128.cc', 'google/protobuf/stubs/common.cc', 'google/protobuf/stubs/bytestream.cc', 'google/protobuf/stubs/atomicops_internals_x86_msvc.cc', 'google/protobuf/stubs/atomicops_internals_x86_gcc.cc', 'google/protobuf/repeated_field.cc', 'google/protobuf/message_lite.cc', 'google/protobuf/io/zero_copy_stream_impl_lite.cc', 'google/protobuf/io/zero_copy_stream.cc', 'google/protobuf/io/coded_stream.cc', 'google/protobuf/generated_message_util.cc', 'google/protobuf/extension_set.cc', 'google/protobuf/arenastring.cc', 'google/protobuf/arena.cc', 'google/protobuf/compiler/js/embed.cc']
+CC_FILES=['google/protobuf/compiler/zip_writer.cc', 'google/protobuf/compiler/subprocess.cc', 'google/protobuf/compiler/ruby/ruby_generator.cc', 'google/protobuf/compiler/python/python_generator.cc', 'google/protobuf/compiler/plugin.pb.cc', 'google/protobuf/compiler/plugin.cc', 'google/protobuf/compiler/php/php_generator.cc', 'google/protobuf/compiler/objectivec/objectivec_primitive_field.cc', 'google/protobuf/compiler/objectivec/objectivec_oneof.cc', 'google/protobuf/compiler/objectivec/objectivec_message_field.cc', 'google/protobuf/compiler/objectivec/objectivec_message.cc', 'google/protobuf/compiler/objectivec/objectivec_map_field.cc', 'google/protobuf/compiler/objectivec/objectivec_helpers.cc', 'google/protobuf/compiler/objectivec/objectivec_generator.cc', 'google/protobuf/compiler/objectivec/objectivec_file.cc', 'google/protobuf/compiler/objectivec/objectivec_field.cc', 'google/protobuf/compiler/objectivec/objectivec_extension.cc', 'google/protobuf/compiler/objectivec/objectivec_enum_field.cc', 'google/protobuf/compiler/objectivec/objectivec_enum.cc', 'google/protobuf/compiler/js/well_known_types_embed.cc', 'google/protobuf/compiler/js/js_generator.cc', 'google/protobuf/compiler/javanano/javanano_primitive_field.cc', 'google/protobuf/compiler/javanano/javanano_message_field.cc', 'google/protobuf/compiler/javanano/javanano_message.cc', 'google/protobuf/compiler/javanano/javanano_map_field.cc', 'google/protobuf/compiler/javanano/javanano_helpers.cc', 'google/protobuf/compiler/javanano/javanano_generator.cc', 'google/protobuf/compiler/javanano/javanano_file.cc', 'google/protobuf/compiler/javanano/javanano_field.cc', 'google/protobuf/compiler/javanano/javanano_extension.cc', 'google/protobuf/compiler/javanano/javanano_enum_field.cc', 'google/protobuf/compiler/javanano/javanano_enum.cc', 'google/protobuf/compiler/java/java_string_field_lite.cc', 'google/protobuf/compiler/java/java_string_field.cc', 'google/protobuf/compiler/java/java_shared_code_generator.cc', 'google/protobuf/compiler/java/java_service.cc', 'google/protobuf/compiler/java/java_primitive_field_lite.cc', 'google/protobuf/compiler/java/java_primitive_field.cc', 'google/protobuf/compiler/java/java_name_resolver.cc', 'google/protobuf/compiler/java/java_message_lite.cc', 'google/protobuf/compiler/java/java_message_field_lite.cc', 'google/protobuf/compiler/java/java_message_field.cc', 'google/protobuf/compiler/java/java_message_builder_lite.cc', 'google/protobuf/compiler/java/java_message_builder.cc', 'google/protobuf/compiler/java/java_message.cc', 'google/protobuf/compiler/java/java_map_field_lite.cc', 'google/protobuf/compiler/java/java_map_field.cc', 'google/protobuf/compiler/java/java_lazy_message_field_lite.cc', 'google/protobuf/compiler/java/java_lazy_message_field.cc', 'google/protobuf/compiler/java/java_helpers.cc', 'google/protobuf/compiler/java/java_generator_factory.cc', 'google/protobuf/compiler/java/java_generator.cc', 'google/protobuf/compiler/java/java_file.cc', 'google/protobuf/compiler/java/java_field.cc', 'google/protobuf/compiler/java/java_extension_lite.cc', 'google/protobuf/compiler/java/java_extension.cc', 'google/protobuf/compiler/java/java_enum_lite.cc', 'google/protobuf/compiler/java/java_enum_field_lite.cc', 'google/protobuf/compiler/java/java_enum_field.cc', 'google/protobuf/compiler/java/java_enum.cc', 'google/protobuf/compiler/java/java_doc_comment.cc', 'google/protobuf/compiler/java/java_context.cc', 'google/protobuf/compiler/csharp/csharp_wrapper_field.cc', 'google/protobuf/compiler/csharp/csharp_source_generator_base.cc', 'google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc', 'google/protobuf/compiler/csharp/csharp_repeated_message_field.cc', 'google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc', 'google/protobuf/compiler/csharp/csharp_reflection_class.cc', 'google/protobuf/compiler/csharp/csharp_primitive_field.cc', 'google/protobuf/compiler/csharp/csharp_message_field.cc', 'google/protobuf/compiler/csharp/csharp_message.cc', 'google/protobuf/compiler/csharp/csharp_map_field.cc', 'google/protobuf/compiler/csharp/csharp_helpers.cc', 'google/protobuf/compiler/csharp/csharp_generator.cc', 'google/protobuf/compiler/csharp/csharp_field_base.cc', 'google/protobuf/compiler/csharp/csharp_enum_field.cc', 'google/protobuf/compiler/csharp/csharp_enum.cc', 'google/protobuf/compiler/csharp/csharp_doc_comment.cc', 'google/protobuf/compiler/cpp/cpp_string_field.cc', 'google/protobuf/compiler/cpp/cpp_service.cc', 'google/protobuf/compiler/cpp/cpp_primitive_field.cc', 'google/protobuf/compiler/cpp/cpp_message_field.cc', 'google/protobuf/compiler/cpp/cpp_message.cc', 'google/protobuf/compiler/cpp/cpp_map_field.cc', 'google/protobuf/compiler/cpp/cpp_helpers.cc', 'google/protobuf/compiler/cpp/cpp_generator.cc', 'google/protobuf/compiler/cpp/cpp_file.cc', 'google/protobuf/compiler/cpp/cpp_field.cc', 'google/protobuf/compiler/cpp/cpp_extension.cc', 'google/protobuf/compiler/cpp/cpp_enum_field.cc', 'google/protobuf/compiler/cpp/cpp_enum.cc', 'google/protobuf/compiler/command_line_interface.cc', 'google/protobuf/compiler/code_generator.cc', 'google/protobuf/wrappers.pb.cc', 'google/protobuf/wire_format.cc', 'google/protobuf/util/type_resolver_util.cc', 'google/protobuf/util/time_util.cc', 'google/protobuf/util/message_differencer.cc', 'google/protobuf/util/json_util.cc', 'google/protobuf/util/internal/utility.cc', 'google/protobuf/util/internal/type_info_test_helper.cc', 'google/protobuf/util/internal/type_info.cc', 'google/protobuf/util/internal/protostream_objectwriter.cc', 'google/protobuf/util/internal/protostream_objectsource.cc', 'google/protobuf/util/internal/proto_writer.cc', 'google/protobuf/util/internal/object_writer.cc', 'google/protobuf/util/internal/json_stream_parser.cc', 'google/protobuf/util/internal/json_objectwriter.cc', 'google/protobuf/util/internal/json_escaping.cc', 'google/protobuf/util/internal/field_mask_utility.cc', 'google/protobuf/util/internal/error_listener.cc', 'google/protobuf/util/internal/default_value_objectwriter.cc', 'google/protobuf/util/internal/datapiece.cc', 'google/protobuf/util/field_mask_util.cc', 'google/protobuf/util/field_comparator.cc', 'google/protobuf/unknown_field_set.cc', 'google/protobuf/type.pb.cc', 'google/protobuf/timestamp.pb.cc', 'google/protobuf/text_format.cc', 'google/protobuf/stubs/substitute.cc', 'google/protobuf/stubs/mathlimits.cc', 'google/protobuf/struct.pb.cc', 'google/protobuf/source_context.pb.cc', 'google/protobuf/service.cc', 'google/protobuf/reflection_ops.cc', 'google/protobuf/message.cc', 'google/protobuf/map_field.cc', 'google/protobuf/io/zero_copy_stream_impl.cc', 'google/protobuf/io/tokenizer.cc', 'google/protobuf/io/strtod.cc', 'google/protobuf/io/printer.cc', 'google/protobuf/io/gzip_stream.cc', 'google/protobuf/generated_message_reflection.cc', 'google/protobuf/field_mask.pb.cc', 'google/protobuf/extension_set_heavy.cc', 'google/protobuf/empty.pb.cc', 'google/protobuf/dynamic_message.cc', 'google/protobuf/duration.pb.cc', 'google/protobuf/descriptor_database.cc', 'google/protobuf/descriptor.pb.cc', 'google/protobuf/descriptor.cc', 'google/protobuf/compiler/parser.cc', 'google/protobuf/compiler/importer.cc', 'google/protobuf/api.pb.cc', 'google/protobuf/any.pb.cc', 'google/protobuf/any.cc', 'google/protobuf/wire_format_lite.cc', 'google/protobuf/stubs/time.cc', 'google/protobuf/stubs/strutil.cc', 'google/protobuf/stubs/structurally_valid.cc', 'google/protobuf/stubs/stringprintf.cc', 'google/protobuf/stubs/stringpiece.cc', 'google/protobuf/stubs/statusor.cc', 'google/protobuf/stubs/status.cc', 'google/protobuf/stubs/once.cc', 'google/protobuf/stubs/int128.cc', 'google/protobuf/stubs/common.cc', 'google/protobuf/stubs/bytestream.cc', 'google/protobuf/stubs/atomicops_internals_x86_msvc.cc', 'google/protobuf/stubs/atomicops_internals_x86_gcc.cc', 'google/protobuf/repeated_field.cc', 'google/protobuf/message_lite.cc', 'google/protobuf/io/zero_copy_stream_impl_lite.cc', 'google/protobuf/io/zero_copy_stream.cc', 'google/protobuf/io/coded_stream.cc', 'google/protobuf/generated_message_util.cc', 'google/protobuf/extension_set.cc', 'google/protobuf/arenastring.cc', 'google/protobuf/arena.cc', 'google/protobuf/compiler/js/embed.cc']
 PROTO_FILES=['google/protobuf/wrappers.proto', 'google/protobuf/type.proto', 'google/protobuf/timestamp.proto', 'google/protobuf/struct.proto', 'google/protobuf/source_context.proto', 'google/protobuf/field_mask.proto', 'google/protobuf/empty.proto', 'google/protobuf/duration.proto', 'google/protobuf/descriptor.proto', 'google/protobuf/compiler/plugin.proto', 'google/protobuf/api.proto', 'google/protobuf/any.proto']
 
 CC_INCLUDE='third_party/protobuf/src'
diff --git a/tools/dockerfile/grpc_artifact_linux_armv6/Dockerfile b/tools/dockerfile/grpc_artifact_linux_armv6/Dockerfile
new file mode 100644
index 0000000..b085dd0
--- /dev/null
+++ b/tools/dockerfile/grpc_artifact_linux_armv6/Dockerfile
@@ -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.
+
+# Docker file for building gRPC Raspbian binaries
+
+FROM quay.io/grpc/raspbian_armv6
+
+# Place any extra build instructions between these commands
+# Recommend modifying upstream docker image (quay.io/grpc/raspbian_armv6)
+# for build steps because running them under QEMU is very slow
+# (https://github.com/kpayson64/armv7hf-debian-qemu)
+# RUN [ "cross-build-start" ]
+# RUN [ "cross-build-end" ]
diff --git a/tools/dockerfile/grpc_artifact_linux_armv7/Dockerfile b/tools/dockerfile/grpc_artifact_linux_armv7/Dockerfile
new file mode 100644
index 0000000..d7759a6
--- /dev/null
+++ b/tools/dockerfile/grpc_artifact_linux_armv7/Dockerfile
@@ -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.
+
+# Docker file for building gRPC Raspbian binaries
+
+FROM quay.io/grpc/raspbian_armv7
+
+# Place any extra build instructions between these commands
+# Recommend modifying upstream docker image (quay.io/grpc/raspbian_armv7)
+# for build steps because running them under QEMU is very slow
+# (https://github.com/kpayson64/armv7hf-debian-qemu)
+# RUN [ "cross-build-start" ]
+# RUN [ "cross-build-end" ]
diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++
index 01a2dfb..55980ea 100644
--- a/tools/doxygen/Doxyfile.c++
+++ b/tools/doxygen/Doxyfile.c++
@@ -40,7 +40,7 @@
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 1.3.0-pre1
+PROJECT_NUMBER         = 1.3.5
 
 # 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
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index 7734d8b..7580617 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -40,7 +40,7 @@
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 1.3.0-pre1
+PROJECT_NUMBER         = 1.3.5
 
 # 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
diff --git a/tools/doxygen/Doxyfile.core b/tools/doxygen/Doxyfile.core
index d651c92..a4eacfb 100644
--- a/tools/doxygen/Doxyfile.core
+++ b/tools/doxygen/Doxyfile.core
@@ -40,7 +40,7 @@
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 3.0.0-pre1
+PROJECT_NUMBER         = 3.0.0
 
 # 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
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index f5ffe97..d623fc0 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -40,7 +40,7 @@
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 3.0.0-pre1
+PROJECT_NUMBER         = 3.0.0
 
 # 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
diff --git a/tools/grpcz/BUILD b/tools/grpcz/BUILD
index 5e1faf7..cac7df2 100644
--- a/tools/grpcz/BUILD
+++ b/tools/grpcz/BUILD
@@ -58,6 +58,5 @@
     deps = [
         "//external:gflags",
         "monitoring_proto",
-        "@mongoose_repo//:mongoose_lib",
     ],
 )
diff --git a/tools/grpcz/grpcz_client.cc b/tools/grpcz/grpcz_client.cc
index 47eec8d..a5e66f7 100644
--- a/tools/grpcz/grpcz_client.cc
+++ b/tools/grpcz/grpcz_client.cc
@@ -38,7 +38,7 @@
 #include <grpc/support/log.h>
 
 #include "gflags/gflags.h"
-#include "mongoose.h"
+/* #include "mongoose.h" */
 
 // TODO (makdharma): remove local copies of these protos
 #include "tools/grpcz/census.grpc.pb.h"
@@ -122,8 +122,9 @@
   std::unique_ptr<Monitoring::Stub> stub_;
 };
 
-static struct mg_serve_http_opts s_http_server_opts;
 std::unique_ptr<GrpczClient> g_grpcz_client;
+/*
+static struct mg_serve_http_opts s_http_server_opts;
 
 static void ev_handler(struct mg_connection *nc, int ev, void *p) {
   if (ev == MG_EV_HTTP_REQUEST) {
@@ -141,6 +142,7 @@
   mg_printf(nc, "HTTP/1.0 200 OK\r\n\r\n%s", rendered_html.c_str());
   nc->flags |= MG_F_SEND_AND_CLOSE;
 }
+*/
 
 int main(int argc, char **argv) {
   gflags::ParseCommandLineFlags(&argc, &argv, true);
@@ -156,6 +158,7 @@
     return 0;
   }
 
+  /*
   // Set up a mongoose webserver handler
   struct mg_mgr mgr;
   mg_mgr_init(&mgr, NULL);
@@ -177,5 +180,6 @@
     mg_mgr_poll(&mgr, k_sleep_millis);
   }
   mg_mgr_free(&mgr);
+  */
   return 0;
 }
diff --git a/tools/jenkins/run_c_cpp_test.sh b/tools/jenkins/run_c_cpp_test.sh
new file mode 100755
index 0000000..afa2e78
--- /dev/null
+++ b/tools/jenkins/run_c_cpp_test.sh
@@ -0,0 +1,49 @@
+#!/usr/bin/env 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.
+#
+# This script is invoked by a Jenkins pull request job and executes all
+# args passed to this script if the pull request affect C/C++ code
+set -ex
+
+# Enter the gRPC repo root
+cd $(dirname $0)/../..
+
+AFFECTS_C_CPP=`python -c 'import os; \
+               import sys; \
+               sys.path.insert(0, "tools/run_tests/python_utils"); \
+               import filter_pull_request_tests as filter; \
+               github_target_branch = os.environ.get("ghprbTargetBranch"); \
+               print(filter.affects_c_cpp("origin/%s" % github_target_branch))'`
+
+if [ $AFFECTS_C_CPP == "False" ] ; then
+  echo "This pull request does not affect C/C++. Tests do not need to be run."
+else
+  $@
+fi
diff --git a/tools/run_tests/artifacts/artifact_targets.py b/tools/run_tests/artifacts/artifact_targets.py
index 04702ba..d64c484 100644
--- a/tools/run_tests/artifacts/artifact_targets.py
+++ b/tools/run_tests/artifacts/artifact_targets.py
@@ -41,7 +41,7 @@
 
 def create_docker_jobspec(name, dockerfile_dir, shell_command, environ={},
                    flake_retries=0, timeout_retries=0, timeout_seconds=30*60,
-                   docker_base_image=None):
+                   docker_base_image=None, extra_docker_args=None):
   """Creates jobspec for a task running under docker."""
   environ = environ.copy()
   environ['RUN_COMMAND'] = shell_command
@@ -55,6 +55,8 @@
 
   if docker_base_image is not None:
     docker_env['DOCKER_BASE_IMAGE'] = docker_base_image
+  if extra_docker_args is not None:
+    docker_env['EXTRA_DOCKER_ARGS'] = extra_docker_args
   jobspec = jobset.JobSpec(
           cmdline=['tools/run_tests/dockerize/build_and_run_docker.sh'] + docker_args,
           environ=docker_env,
@@ -102,7 +104,22 @@
 
   def build_jobspec(self):
     environ = {}
-    if self.platform == 'linux':
+    if self.platform == 'linux_extra':
+      # Raspberry Pi build
+      environ['PYTHON'] = '/usr/local/bin/python{}'.format(self.py_version)
+      environ['PIP'] = '/usr/local/bin/pip{}'.format(self.py_version)
+      # https://github.com/resin-io-projects/armv7hf-debian-qemu/issues/9
+      # A QEMU bug causes submodule update to hang, so we copy directly
+      environ['RELATIVE_COPY_PATH'] = '.'
+      extra_args = ' --entrypoint=/usr/bin/qemu-arm-static '
+      return create_docker_jobspec(self.name,
+          'tools/dockerfile/grpc_artifact_linux_{}'.format(self.arch),
+          'tools/run_tests/artifacts/build_artifact_python.sh',
+          environ=environ,
+          timeout_seconds=60*60*5,
+          docker_base_image='quay.io/grpc/raspbian_{}'.format(self.arch),
+          extra_docker_args=extra_args)
+    elif self.platform == 'linux':
       if self.arch == 'x86':
         environ['SETARCH_CMD'] = 'linux32'
       # Inside the manylinux container, the python installations are located in
@@ -332,6 +349,14 @@
            PythonArtifact('linux', 'x86', 'cp34-cp34m'),
            PythonArtifact('linux', 'x86', 'cp35-cp35m'),
            PythonArtifact('linux', 'x86', 'cp36-cp36m'),
+           PythonArtifact('linux_extra', 'armv7', '2.7'),
+           PythonArtifact('linux_extra', 'armv7', '3.4'),
+           PythonArtifact('linux_extra', 'armv7', '3.5'),
+           PythonArtifact('linux_extra', 'armv7', '3.6'),
+           PythonArtifact('linux_extra', 'armv6', '2.7'),
+           PythonArtifact('linux_extra', 'armv6', '3.4'),
+           PythonArtifact('linux_extra', 'armv6', '3.5'),
+           PythonArtifact('linux_extra', 'armv6', '3.6'),
            PythonArtifact('linux', 'x64', 'cp27-cp27m'),
            PythonArtifact('linux', 'x64', 'cp27-cp27mu'),
            PythonArtifact('linux', 'x64', 'cp34-cp34m'),
diff --git a/tools/run_tests/artifacts/build_artifact_node.sh b/tools/run_tests/artifacts/build_artifact_node.sh
index 2da2ac5..7a74755 100755
--- a/tools/run_tests/artifacts/build_artifact_node.sh
+++ b/tools/run_tests/artifacts/build_artifact_node.sh
@@ -48,7 +48,7 @@
 
 for version in ${node_versions[@]}
 do
-  ./node_modules/.bin/node-pre-gyp configure rebuild package testpackage --target=$version --target_arch=$NODE_TARGET_ARCH
+  ./node_modules/.bin/node-pre-gyp configure rebuild package testpackage --target=$version --target_arch=$NODE_TARGET_ARCH --grpc_alpine=true
   cp -r build/stage/* artifacts/
 done
 
diff --git a/tools/run_tests/dockerize/build_and_run_docker.sh b/tools/run_tests/dockerize/build_and_run_docker.sh
index 8c25c86..6189e9a 100755
--- a/tools/run_tests/dockerize/build_and_run_docker.sh
+++ b/tools/run_tests/dockerize/build_and_run_docker.sh
@@ -74,8 +74,9 @@
   -v "$git_root:/var/local/jenkins/grpc:ro" \
   -w /var/local/git/grpc \
   --name=$CONTAINER_NAME \
+  $EXTRA_DOCKER_ARGS \
   $DOCKER_IMAGE_NAME \
-  bash -l "/var/local/jenkins/grpc/$DOCKER_RUN_SCRIPT" || FAILED="true"
+  /bin/bash -l "/var/local/jenkins/grpc/$DOCKER_RUN_SCRIPT" || FAILED="true"
 
 # Copy output artifacts
 if [ "$OUTPUT_DIR" != "" ]
diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json
index f5653d6..5afeed3 100644
--- a/tools/run_tests/generated/sources_and_headers.json
+++ b/tools/run_tests/generated/sources_and_headers.json
@@ -1699,6 +1699,23 @@
     "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": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
     "name": "parse_address_test", 
     "src": [
       "test/core/client_channel/parse_address_test.c"
diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json
index a08caf3..7e35099 100644
--- a/tools/run_tests/generated/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -1773,6 +1773,30 @@
     ], 
     "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": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
     "gtest": false, 
diff --git a/tools/run_tests/helper_scripts/build_node.sh b/tools/run_tests/helper_scripts/build_node.sh
index 2c4cf02..07ecf98 100755
--- a/tools/run_tests/helper_scripts/build_node.sh
+++ b/tools/run_tests/helper_scripts/build_node.sh
@@ -46,6 +46,4 @@
   *) config_flag='--release' ;;
 esac
 
-uv_flag=$2
-
-npm install --unsafe-perm --build-from-source $uv_flag $config_flag
+npm install --unsafe-perm --build-from-source $config_flag
diff --git a/tools/run_tests/helper_scripts/run_ruby_end2end_tests.sh b/tools/run_tests/helper_scripts/run_ruby_end2end_tests.sh
index 6688025..ab882d62 100755
--- a/tools/run_tests/helper_scripts/run_ruby_end2end_tests.sh
+++ b/tools/run_tests/helper_scripts/run_ruby_end2end_tests.sh
@@ -41,4 +41,5 @@
 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/grpc_class_init_driver.rb || EXIT_CODE=1
+ruby src/ruby/end2end/multiple_killed_watching_threads_driver.rb || EXIT_CODE=1
 exit $EXIT_CODE
diff --git a/tools/run_tests/performance/build_performance.sh b/tools/run_tests/performance/build_performance.sh
index 3f7c9fe..5f8749d 100755
--- a/tools/run_tests/performance/build_performance.sh
+++ b/tools/run_tests/performance/build_performance.sh
@@ -61,9 +61,6 @@
   "csharp")
     python tools/run_tests/run_tests.py -l $language -c $CONFIG --build_only -j 8 --compiler coreclr
     ;;
-  "node")
-    python tools/run_tests/run_tests.py -l $language -c $CONFIG --build_only -j 8 --iomgr_platform=uv
-    ;;
   *)
     python tools/run_tests/run_tests.py -l $language -c $CONFIG --build_only -j 8
     ;;
diff --git a/tools/run_tests/performance/scenario_config.py b/tools/run_tests/performance/scenario_config.py
index 200da5e..ce080882 100644
--- a/tools/run_tests/performance/scenario_config.py
+++ b/tools/run_tests/performance/scenario_config.py
@@ -513,7 +513,22 @@
         'node_protobuf_unary_ping_pong_1MB', rpc_type='UNARY',
         client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER',
         req_size=1024*1024, resp_size=1024*1024,
-        categories=[SCALABLE, SMOKETEST])
+        categories=[SCALABLE])
+
+    sizes = [('1B', 1), ('1KB', 1024), ('10KB', 10 * 1024),
+             ('1MB', 1024 * 1024), ('10MB', 10 * 1024 * 1024),
+             ('100MB', 100 * 1024 * 1024)]
+
+    for size_name, size in sizes:
+      for secure in (True, False):
+        yield _ping_pong_scenario(
+            'node_protobuf_unary_ping_pong_%s_resp_%s' %
+            (size_name, 'secure' if secure else 'insecure'),
+            rpc_type='UNARY',
+            client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER',
+            req_size=0, resp_size=size,
+            secure=secure,
+            categories=[SCALABLE])
 
     # TODO(murgatroid99): fix bugs with this scenario and re-enable it
     # yield _ping_pong_scenario(
@@ -528,11 +543,10 @@
     #    client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER',
     #    unconstrained_client='async')
 
-    # TODO(jtattermusch): make this scenario work
-    #yield _ping_pong_scenario(
-    #    'node_to_cpp_protobuf_async_unary_ping_pong', rpc_type='UNARY',
-    #    client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER',
-    #    server_language='c++', async_server_threads=1)
+    yield _ping_pong_scenario(
+        'node_to_cpp_protobuf_async_unary_ping_pong', rpc_type='UNARY',
+        client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER',
+        server_language='c++', async_server_threads=1)
 
     # TODO(jtattermusch): make this scenario work
     #yield _ping_pong_scenario(
@@ -829,6 +843,21 @@
         unconstrained_client='async',
         categories=[SCALABLE, SMOKETEST])
 
+    sizes = [('1B', 1), ('1KB', 1024), ('10KB', 10 * 1024),
+             ('1MB', 1024 * 1024), ('10MB', 10 * 1024 * 1024),
+             ('100MB', 100 * 1024 * 1024)]
+
+    for size_name, size in sizes:
+      for secure in (True, False):
+        yield _ping_pong_scenario(
+            'node_express_json_unary_ping_pong_%s_resp_%s' %
+            (size_name, 'secure' if secure else 'insecure'),
+            rpc_type='UNARY',
+            client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER',
+            req_size=0, resp_size=size,
+            secure=secure,
+            categories=[SCALABLE])
+
   def __str__(self):
     return 'node_express'
 
diff --git a/tools/run_tests/python_utils/filter_pull_request_tests.py b/tools/run_tests/python_utils/filter_pull_request_tests.py
index e013376..958eb56 100644
--- a/tools/run_tests/python_utils/filter_pull_request_tests.py
+++ b/tools/run_tests/python_utils/filter_pull_request_tests.py
@@ -127,6 +127,9 @@
   'setup\.py$': [_PYTHON_TEST_SUITE]
 }
 
+# Regex that combines all keys in _WHITELIST_DICT
+_ALL_TRIGGERS = "(" + ")|(".join(_WHITELIST_DICT.keys()) + ")"
+
 # Add all triggers to their respective test suites
 for trigger, test_suites in six.iteritems(_WHITELIST_DICT):
   for test_suite in test_suites:
@@ -169,6 +172,21 @@
           test.labels[2] not in skippable_labels]
 
 
+def affects_c_cpp(base_branch):
+  """
+  Determines if a pull request's changes affect C/C++. This function exists because
+  there are pull request tests that only test C/C++ code
+  :param base_branch: branch that a pull request is requesting to merge into
+  :return: boolean indicating whether C/C++ changes are made in pull request
+  """
+  changed_files = _get_changed_files(base_branch)
+  # Run all tests if any changed file is not in the whitelist dictionary
+  for changed_file in changed_files:
+    if not re.match(_ALL_TRIGGERS, changed_file):
+      return True
+  return not _can_skip_tests(changed_files, _CPP_TEST_SUITE.triggers + _CORE_TEST_SUITE.triggers)
+
+
 def filter_tests(tests, base_branch):
   """
   Filters out tests that are safe to ignore
@@ -181,11 +199,9 @@
     print('  %s' % changed_file)
   print('')
 
-  # Regex that combines all keys in _WHITELIST_DICT
-  all_triggers = "(" + ")|(".join(_WHITELIST_DICT.keys()) + ")"
-  # Check if all tests have to be run
+  # Run all tests if any changed file is not in the whitelist dictionary
   for changed_file in changed_files:
-    if not re.match(all_triggers, changed_file):
+    if not re.match(_ALL_TRIGGERS, changed_file):
       return(tests)
   # Figure out which language and platform tests to run
   skippable_labels = []
@@ -196,4 +212,3 @@
         skippable_labels.append(label)
   tests = _remove_irrelevant_tests(tests, skippable_labels)
   return tests
-
diff --git a/tools/run_tests/python_utils/jobset.py b/tools/run_tests/python_utils/jobset.py
index 5d812f2..460f359 100755
--- a/tools/run_tests/python_utils/jobset.py
+++ b/tools/run_tests/python_utils/jobset.py
@@ -348,7 +348,7 @@
   """Manages one run of jobs."""
 
   def __init__(self, check_cancelled, maxjobs, newline_on_success, travis,
-               stop_on_failure, add_env, quiet_success):
+               stop_on_failure, add_env, quiet_success, max_time):
     self._running = set()
     self._check_cancelled = check_cancelled
     self._cancelled = False
@@ -360,6 +360,7 @@
     self._stop_on_failure = stop_on_failure
     self._add_env = add_env
     self._quiet_success = quiet_success
+    self._max_time = max_time
     self.resultset = {}
     self._remaining = None
     self._start_time = time.time()
@@ -379,6 +380,12 @@
   def start(self, spec):
     """Start a job. Return True on success, False on failure."""
     while True:
+      if self._max_time > 0 and time.time() - self._start_time > self._max_time:
+        skipped_job_result = JobResult()
+        skipped_job_result.state = 'SKIPPED'
+        message('SKIPPED', spec.shortname, do_newline=True)
+        self.resultset[spec.shortname] = [skipped_job_result]
+        return True
       if self.cancelled(): return False
       current_cpu_cost = self.cpu_cost()
       if current_cpu_cost == 0: break
@@ -474,7 +481,8 @@
         stop_on_failure=False,
         add_env={},
         skip_jobs=False,
-        quiet_success=False):
+        quiet_success=False,
+        max_time=-1):
   if skip_jobs:
     resultset = {}
     skipped_job_result = JobResult()
@@ -486,7 +494,7 @@
   js = Jobset(check_cancelled,
               maxjobs if maxjobs is not None else _DEFAULT_MAX_JOBS,
               newline_on_success, travis, stop_on_failure, add_env,
-              quiet_success)
+              quiet_success, max_time)
   for cmdline, remaining in tag_remaining(cmdlines):
     if not js.start(cmdline):
       break
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index 9130bc9..b556328 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -430,10 +430,6 @@
     _check_compiler(self.args.compiler, ['default', 'node0.12',
                                          'node4', 'node5', 'node6',
                                          'node7', 'electron1.3', 'electron1.6'])
-    if args.iomgr_platform == "uv":
-      self.use_uv = True
-    else:
-      self.use_uv = False
     if self.args.compiler == 'default':
       self.runtime = 'node'
       self.node_version = '7'
@@ -481,7 +477,6 @@
       else:
         config_flag = '--release'
       return [['tools\\run_tests\\helper_scripts\\build_node.bat',
-               '--grpc_uv={}'.format('true' if self.use_uv else 'false'),
                config_flag]]
     else:
       build_script = 'build_node'
@@ -490,8 +485,7 @@
         # building for electron requires a patch version
         self.node_version += '.0'
       return [['tools/run_tests/helper_scripts/{}.sh'.format(build_script),
-               self.node_version,
-               '--grpc_uv={}'.format('true' if self.use_uv else 'false')]]
+               self.node_version]]
 
   def post_tests_steps(self):
     return []
@@ -1210,6 +1204,7 @@
                        'Useful when running many iterations of each test (argument -n).')
 argp.add_argument('--force_default_poller', default=False, action='store_const', const=True,
                   help='Dont try to iterate over many polling strategies when they exist')
+argp.add_argument('--max_time', default=-1, type=int, help='Maximum test runtime in seconds')
 args = argp.parse_args()
 
 if args.force_default_poller:
@@ -1465,7 +1460,7 @@
            not re.search(args.regex_exclude, spec.shortname))))
     # When running on travis, we want out test runs to be as similar as possible
     # for reproducibility purposes.
-    if args.travis:
+    if args.travis and args.max_time <= 0:
       massaged_one_run = sorted(one_run, key=lambda x: x.shortname)
     else:
       # whereas otherwise, we want to shuffle things up to give all tests a
@@ -1493,7 +1488,7 @@
         all_runs, check_cancelled, newline_on_success=newline_on_success,
         travis=args.travis, maxjobs=args.jobs,
         stop_on_failure=args.stop_on_failure,
-        quiet_success=args.quiet_success)
+        quiet_success=args.quiet_success, max_time=args.max_time)
     if resultset:
       for k, v in sorted(resultset.items()):
         num_runs, num_failures = _calculate_num_runs_failures(v)
diff --git a/tools/run_tests/run_tests_matrix.py b/tools/run_tests/run_tests_matrix.py
index 8bc8236..a8e1774 100755
--- a/tools/run_tests/run_tests_matrix.py
+++ b/tools/run_tests/run_tests_matrix.py
@@ -198,7 +198,7 @@
                                 extra_args=extra_args,
                                 inner_jobs=inner_jobs)
 
-  for compiler in ['gcc4.8', 'gcc5.3', 'gcc_musl',
+  for compiler in ['gcc4.8', 'gcc5.3',
                    'clang3.5', 'clang3.6', 'clang3.7']:
     test_jobs += _generate_jobs(languages=['c++'],
                                 configs=['dbg'],
@@ -257,15 +257,6 @@
                               extra_args=extra_args,
                               inner_jobs=inner_jobs)
 
-  test_jobs += _generate_jobs(languages=['python'],
-                              configs=['dbg'],
-                              platforms=['linux'],
-                              arch='default',
-                              compiler='python_alpine',
-                              labels=['portability'],
-                              extra_args=extra_args,
-                              inner_jobs=inner_jobs)
-
   test_jobs += _generate_jobs(languages=['csharp'],
                               configs=['dbg'],
                               platforms=['linux'],
@@ -288,15 +279,6 @@
                               platforms=['linux'],
                               arch='default',
                               compiler='electron1.6',
-                              iomgr_platform='uv',
-                              labels=['portability'],
-                              extra_args=extra_args,
-                              inner_jobs=inner_jobs)
-
-  test_jobs += _generate_jobs(languages=['node'],
-                              configs=['dbg'],
-                              platforms=['linux'],
-                              iomgr_platform='uv',
                               labels=['portability'],
                               extra_args=extra_args,
                               inner_jobs=inner_jobs)
@@ -386,6 +368,9 @@
   argp.add_argument('-n', '--runs_per_test', default=1, type=_runs_per_test_type,
                     help='How many times to run each tests. >1 runs implies ' +
                     'omitting passing test from the output & reports.')
+  argp.add_argument('--max_time', default=-1, type=int,
+                    help='Maximum amount of time to run tests for' +
+                         '(other tests will be skipped)')
   args = argp.parse_args()
 
   extra_args = []
@@ -397,6 +382,8 @@
     extra_args.append('-n')
     extra_args.append('%s' % args.runs_per_test)
     extra_args.append('--quiet_success')
+  if args.max_time > 0:
+    extra_args.extend(('--max_time', '%d' % args.max_time))
 
   all_jobs = _create_test_jobs(extra_args=extra_args, inner_jobs=args.inner_jobs) + \
              _create_portability_test_jobs(extra_args=extra_args, inner_jobs=args.inner_jobs)
diff --git a/tools/run_tests/sanity/check_submodules.sh b/tools/run_tests/sanity/check_submodules.sh
index eaf5a05..c5a0dbb 100755
--- a/tools/run_tests/sanity/check_submodules.sh
+++ b/tools/run_tests/sanity/check_submodules.sh
@@ -46,7 +46,7 @@
  886e7d75368e3f4fab3f4d0d3584e4abfc557755 third_party/boringssl-with-bazel (version_for_cocoapods_7.0-857-g886e7d7)
  30dbc81fb5ffdc98ea9b14b1918bfe4e8779b26e third_party/gflags (v2.2.0)
  ec44c6c1675c25b9827aacd08c02433cccde7780 third_party/googletest (release-1.8.0)
- 4a0dd03e52e09332c8fd0f8f26a8e0ae9f911182 third_party/protobuf (v3.1.0-alpha-1-548-g4a0dd03e)
+ 593e917c176b5bc5aafa57bf9f6030d749d91cd5 third_party/protobuf (v3.1.0-alpha-1-326-g593e917)
  bcad91771b7f0bff28a1cac1981d7ef2b9bcef3c third_party/thrift (bcad917)
  50893291621658f355bc5b4d450a8d06a563053d third_party/zlib (v1.2.8)
  7691f773af79bf75a62d1863fd0f13ebf9dc51b1 third_party/cares/cares (1.12.0)
diff --git a/vsprojects/buildtests_c.sln b/vsprojects/buildtests_c.sln
index c8fcacf..9cb966d 100644
--- a/vsprojects/buildtests_c.sln
+++ b/vsprojects/buildtests_c.sln
@@ -1284,6 +1284,17 @@
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
 	EndProjectSection
 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}"
 	ProjectSection(myProperties) = preProject
         	lib = "False"
@@ -3577,6 +3588,22 @@
 		{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.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|x64.ActiveCfg = Debug|x64
 		{EDEA8257-AEA8-1B0A-F95B-8D6CD7286463}.Release|Win32.ActiveCfg = Release|Win32
diff --git a/vsprojects/vcxproj/test/num_external_connectivity_watchers_test/num_external_connectivity_watchers_test.vcxproj b/vsprojects/vcxproj/test/num_external_connectivity_watchers_test/num_external_connectivity_watchers_test.vcxproj
new file mode 100644
index 0000000..2b373e8
--- /dev/null
+++ b/vsprojects/vcxproj/test/num_external_connectivity_watchers_test/num_external_connectivity_watchers_test.vcxproj
@@ -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>
+
diff --git a/vsprojects/vcxproj/test/num_external_connectivity_watchers_test/num_external_connectivity_watchers_test.vcxproj.filters b/vsprojects/vcxproj/test/num_external_connectivity_watchers_test/num_external_connectivity_watchers_test.vcxproj.filters
new file mode 100644
index 0000000..92a4198
--- /dev/null
+++ b/vsprojects/vcxproj/test/num_external_connectivity_watchers_test/num_external_connectivity_watchers_test.vcxproj.filters
@@ -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>
+