Merge pull request #9753 from ctiller/zalloc

Add zalloc, convert a bunch of files to use it
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f2a2380..82cf852 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -395,6 +395,7 @@
 add_dependencies(buildtests_c grpc_channel_args_test)
 add_dependencies(buildtests_c grpc_channel_stack_test)
 add_dependencies(buildtests_c grpc_completion_queue_test)
+add_dependencies(buildtests_c grpc_completion_queue_threading_test)
 add_dependencies(buildtests_c grpc_credentials_test)
 add_dependencies(buildtests_c grpc_fetch_oauth2)
 add_dependencies(buildtests_c grpc_invalid_channel_args_test)
@@ -465,12 +466,14 @@
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_c tcp_client_posix_test)
 endif()
+add_dependencies(buildtests_c tcp_client_uv_test)
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_c tcp_posix_test)
 endif()
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_c tcp_server_posix_test)
 endif()
+add_dependencies(buildtests_c tcp_server_uv_test)
 add_dependencies(buildtests_c time_averaged_stats_test)
 add_dependencies(buildtests_c timeout_encoding_test)
 add_dependencies(buildtests_c timer_heap_test)
@@ -570,6 +573,9 @@
 add_dependencies(buildtests_cxx bm_call_create)
 endif()
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
+add_dependencies(buildtests_cxx bm_chttp2_hpack)
+endif()
+if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_cxx bm_closure)
 endif()
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
@@ -581,6 +587,9 @@
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_cxx bm_fullstack)
 endif()
+if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
+add_dependencies(buildtests_cxx bm_metadata)
+endif()
 add_dependencies(buildtests_cxx channel_arguments_test)
 add_dependencies(buildtests_cxx channel_filter_test)
 add_dependencies(buildtests_cxx cli_call_test)
@@ -1334,6 +1343,8 @@
   src/core/lib/tsi/ssl_transport_security.c
   src/core/lib/tsi/transport_security.c
   src/core/ext/transport/chttp2/client/chttp2_connector.c
+  src/core/ext/load_reporting/load_reporting.c
+  src/core/ext/load_reporting/load_reporting_filter.c
   src/core/plugin_registry/grpc_cronet_plugin_registry.c
 )
 
@@ -3780,6 +3791,7 @@
   test/core/end2end/tests/hpack_size.c
   test/core/end2end/tests/idempotent_request.c
   test/core/end2end/tests/invoke_large_request.c
+  test/core/end2end/tests/keepalive_timeout.c
   test/core/end2end/tests/large_metadata.c
   test/core/end2end/tests/load_reporting_hook.c
   test/core/end2end/tests/max_concurrent_streams.c
@@ -3869,6 +3881,7 @@
   test/core/end2end/tests/hpack_size.c
   test/core/end2end/tests/idempotent_request.c
   test/core/end2end/tests/invoke_large_request.c
+  test/core/end2end/tests/keepalive_timeout.c
   test/core/end2end/tests/large_metadata.c
   test/core/end2end/tests/load_reporting_hook.c
   test/core/end2end/tests/max_concurrent_streams.c
@@ -5431,6 +5444,33 @@
 )
 
 endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_executable(grpc_completion_queue_threading_test
+  test/core/surface/completion_queue_threading_test.c
+)
+
+
+target_include_directories(grpc_completion_queue_threading_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 ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include
+)
+
+target_link_libraries(grpc_completion_queue_threading_test
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc_test_util
+  grpc
+  gpr_test_util
+  gpr
+)
+
+endif (gRPC_BUILD_TESTS)
 
 add_executable(grpc_create_jwt
   test/core/security/create_jwt.c
@@ -6898,6 +6938,33 @@
 endif()
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
+
+add_executable(tcp_client_uv_test
+  test/core/iomgr/tcp_client_uv_test.c
+)
+
+
+target_include_directories(tcp_client_uv_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 ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include
+)
+
+target_link_libraries(tcp_client_uv_test
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc_test_util
+  grpc
+  gpr_test_util
+  gpr
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 
 add_executable(tcp_posix_test
@@ -6957,6 +7024,33 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+add_executable(tcp_server_uv_test
+  test/core/iomgr/tcp_server_uv_test.c
+)
+
+
+target_include_directories(tcp_server_uv_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 ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include
+)
+
+target_link_libraries(tcp_server_uv_test
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc_test_util
+  grpc
+  gpr_test_util
+  gpr
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
 add_executable(time_averaged_stats_test
   test/core/iomgr/time_averaged_stats_test.c
 )
@@ -7404,6 +7498,44 @@
 if (gRPC_BUILD_TESTS)
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 
+add_executable(bm_chttp2_hpack
+  test/cpp/microbenchmarks/bm_chttp2_hpack.cc
+  third_party/googletest/src/gtest-all.cc
+)
+
+
+target_include_directories(bm_chttp2_hpack
+  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 ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include
+  PRIVATE third_party/googletest/include
+  PRIVATE third_party/googletest
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(bm_chttp2_hpack
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  benchmark
+  grpc++_test_util
+  grpc_test_util
+  grpc++
+  grpc
+  gpr_test_util
+  gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif()
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
+
 add_executable(bm_closure
   test/cpp/microbenchmarks/bm_closure.cc
   third_party/googletest/src/gtest-all.cc
@@ -7428,7 +7560,9 @@
   ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
   benchmark
+  grpc++_test_util
   grpc_test_util
+  grpc++
   grpc
   gpr_test_util
   gpr
@@ -7552,6 +7686,42 @@
 endif()
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
+if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
+
+add_executable(bm_metadata
+  test/cpp/microbenchmarks/bm_metadata.cc
+  third_party/googletest/src/gtest-all.cc
+)
+
+
+target_include_directories(bm_metadata
+  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 ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include
+  PRIVATE third_party/googletest/include
+  PRIVATE third_party/googletest
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(bm_metadata
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  benchmark
+  grpc_test_util
+  grpc
+  gpr_test_util
+  gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif()
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
 
 add_executable(channel_arguments_test
   test/cpp/common/channel_arguments_test.cc
diff --git a/Makefile b/Makefile
index 0b0e127..153d534 100644
--- a/Makefile
+++ b/Makefile
@@ -959,6 +959,7 @@
 grpc_channel_args_test: $(BINDIR)/$(CONFIG)/grpc_channel_args_test
 grpc_channel_stack_test: $(BINDIR)/$(CONFIG)/grpc_channel_stack_test
 grpc_completion_queue_test: $(BINDIR)/$(CONFIG)/grpc_completion_queue_test
+grpc_completion_queue_threading_test: $(BINDIR)/$(CONFIG)/grpc_completion_queue_threading_test
 grpc_create_jwt: $(BINDIR)/$(CONFIG)/grpc_create_jwt
 grpc_credentials_test: $(BINDIR)/$(CONFIG)/grpc_credentials_test
 grpc_fetch_oauth2: $(BINDIR)/$(CONFIG)/grpc_fetch_oauth2
@@ -1023,8 +1024,10 @@
 ssl_server_fuzzer: $(BINDIR)/$(CONFIG)/ssl_server_fuzzer
 status_conversion_test: $(BINDIR)/$(CONFIG)/status_conversion_test
 tcp_client_posix_test: $(BINDIR)/$(CONFIG)/tcp_client_posix_test
+tcp_client_uv_test: $(BINDIR)/$(CONFIG)/tcp_client_uv_test
 tcp_posix_test: $(BINDIR)/$(CONFIG)/tcp_posix_test
 tcp_server_posix_test: $(BINDIR)/$(CONFIG)/tcp_server_posix_test
+tcp_server_uv_test: $(BINDIR)/$(CONFIG)/tcp_server_uv_test
 time_averaged_stats_test: $(BINDIR)/$(CONFIG)/time_averaged_stats_test
 timeout_encoding_test: $(BINDIR)/$(CONFIG)/timeout_encoding_test
 timer_heap_test: $(BINDIR)/$(CONFIG)/timer_heap_test
@@ -1041,10 +1044,12 @@
 async_end2end_test: $(BINDIR)/$(CONFIG)/async_end2end_test
 auth_property_iterator_test: $(BINDIR)/$(CONFIG)/auth_property_iterator_test
 bm_call_create: $(BINDIR)/$(CONFIG)/bm_call_create
+bm_chttp2_hpack: $(BINDIR)/$(CONFIG)/bm_chttp2_hpack
 bm_closure: $(BINDIR)/$(CONFIG)/bm_closure
 bm_cq: $(BINDIR)/$(CONFIG)/bm_cq
 bm_error: $(BINDIR)/$(CONFIG)/bm_error
 bm_fullstack: $(BINDIR)/$(CONFIG)/bm_fullstack
+bm_metadata: $(BINDIR)/$(CONFIG)/bm_metadata
 channel_arguments_test: $(BINDIR)/$(CONFIG)/channel_arguments_test
 channel_filter_test: $(BINDIR)/$(CONFIG)/channel_filter_test
 cli_call_test: $(BINDIR)/$(CONFIG)/cli_call_test
@@ -1321,6 +1326,7 @@
   $(BINDIR)/$(CONFIG)/grpc_channel_args_test \
   $(BINDIR)/$(CONFIG)/grpc_channel_stack_test \
   $(BINDIR)/$(CONFIG)/grpc_completion_queue_test \
+  $(BINDIR)/$(CONFIG)/grpc_completion_queue_threading_test \
   $(BINDIR)/$(CONFIG)/grpc_credentials_test \
   $(BINDIR)/$(CONFIG)/grpc_fetch_oauth2 \
   $(BINDIR)/$(CONFIG)/grpc_invalid_channel_args_test \
@@ -1371,8 +1377,10 @@
   $(BINDIR)/$(CONFIG)/socket_utils_test \
   $(BINDIR)/$(CONFIG)/status_conversion_test \
   $(BINDIR)/$(CONFIG)/tcp_client_posix_test \
+  $(BINDIR)/$(CONFIG)/tcp_client_uv_test \
   $(BINDIR)/$(CONFIG)/tcp_posix_test \
   $(BINDIR)/$(CONFIG)/tcp_server_posix_test \
+  $(BINDIR)/$(CONFIG)/tcp_server_uv_test \
   $(BINDIR)/$(CONFIG)/time_averaged_stats_test \
   $(BINDIR)/$(CONFIG)/timeout_encoding_test \
   $(BINDIR)/$(CONFIG)/timer_heap_test \
@@ -1449,10 +1457,12 @@
   $(BINDIR)/$(CONFIG)/async_end2end_test \
   $(BINDIR)/$(CONFIG)/auth_property_iterator_test \
   $(BINDIR)/$(CONFIG)/bm_call_create \
+  $(BINDIR)/$(CONFIG)/bm_chttp2_hpack \
   $(BINDIR)/$(CONFIG)/bm_closure \
   $(BINDIR)/$(CONFIG)/bm_cq \
   $(BINDIR)/$(CONFIG)/bm_error \
   $(BINDIR)/$(CONFIG)/bm_fullstack \
+  $(BINDIR)/$(CONFIG)/bm_metadata \
   $(BINDIR)/$(CONFIG)/channel_arguments_test \
   $(BINDIR)/$(CONFIG)/channel_filter_test \
   $(BINDIR)/$(CONFIG)/cli_call_test \
@@ -1558,10 +1568,12 @@
   $(BINDIR)/$(CONFIG)/async_end2end_test \
   $(BINDIR)/$(CONFIG)/auth_property_iterator_test \
   $(BINDIR)/$(CONFIG)/bm_call_create \
+  $(BINDIR)/$(CONFIG)/bm_chttp2_hpack \
   $(BINDIR)/$(CONFIG)/bm_closure \
   $(BINDIR)/$(CONFIG)/bm_cq \
   $(BINDIR)/$(CONFIG)/bm_error \
   $(BINDIR)/$(CONFIG)/bm_fullstack \
+  $(BINDIR)/$(CONFIG)/bm_metadata \
   $(BINDIR)/$(CONFIG)/channel_arguments_test \
   $(BINDIR)/$(CONFIG)/channel_filter_test \
   $(BINDIR)/$(CONFIG)/cli_call_test \
@@ -1725,6 +1737,8 @@
 	$(Q) $(BINDIR)/$(CONFIG)/grpc_channel_stack_test || ( echo test grpc_channel_stack_test failed ; exit 1 )
 	$(E) "[RUN]     Testing grpc_completion_queue_test"
 	$(Q) $(BINDIR)/$(CONFIG)/grpc_completion_queue_test || ( echo test grpc_completion_queue_test failed ; exit 1 )
+	$(E) "[RUN]     Testing grpc_completion_queue_threading_test"
+	$(Q) $(BINDIR)/$(CONFIG)/grpc_completion_queue_threading_test || ( echo test grpc_completion_queue_threading_test failed ; exit 1 )
 	$(E) "[RUN]     Testing grpc_credentials_test"
 	$(Q) $(BINDIR)/$(CONFIG)/grpc_credentials_test || ( echo test grpc_credentials_test failed ; exit 1 )
 	$(E) "[RUN]     Testing grpc_invalid_channel_args_test"
@@ -1813,10 +1827,14 @@
 	$(Q) $(BINDIR)/$(CONFIG)/status_conversion_test || ( echo test status_conversion_test failed ; exit 1 )
 	$(E) "[RUN]     Testing tcp_client_posix_test"
 	$(Q) $(BINDIR)/$(CONFIG)/tcp_client_posix_test || ( echo test tcp_client_posix_test failed ; exit 1 )
+	$(E) "[RUN]     Testing tcp_client_uv_test"
+	$(Q) $(BINDIR)/$(CONFIG)/tcp_client_uv_test || ( echo test tcp_client_uv_test failed ; exit 1 )
 	$(E) "[RUN]     Testing tcp_posix_test"
 	$(Q) $(BINDIR)/$(CONFIG)/tcp_posix_test || ( echo test tcp_posix_test failed ; exit 1 )
 	$(E) "[RUN]     Testing tcp_server_posix_test"
 	$(Q) $(BINDIR)/$(CONFIG)/tcp_server_posix_test || ( echo test tcp_server_posix_test failed ; exit 1 )
+	$(E) "[RUN]     Testing tcp_server_uv_test"
+	$(Q) $(BINDIR)/$(CONFIG)/tcp_server_uv_test || ( echo test tcp_server_uv_test failed ; exit 1 )
 	$(E) "[RUN]     Testing time_averaged_stats_test"
 	$(Q) $(BINDIR)/$(CONFIG)/time_averaged_stats_test || ( echo test time_averaged_stats_test failed ; exit 1 )
 	$(E) "[RUN]     Testing timeout_encoding_test"
@@ -1879,6 +1897,8 @@
 	$(Q) $(BINDIR)/$(CONFIG)/auth_property_iterator_test || ( echo test auth_property_iterator_test failed ; exit 1 )
 	$(E) "[RUN]     Testing bm_call_create"
 	$(Q) $(BINDIR)/$(CONFIG)/bm_call_create || ( echo test bm_call_create failed ; exit 1 )
+	$(E) "[RUN]     Testing bm_chttp2_hpack"
+	$(Q) $(BINDIR)/$(CONFIG)/bm_chttp2_hpack || ( echo test bm_chttp2_hpack failed ; exit 1 )
 	$(E) "[RUN]     Testing bm_closure"
 	$(Q) $(BINDIR)/$(CONFIG)/bm_closure || ( echo test bm_closure failed ; exit 1 )
 	$(E) "[RUN]     Testing bm_cq"
@@ -1887,6 +1907,8 @@
 	$(Q) $(BINDIR)/$(CONFIG)/bm_error || ( echo test bm_error failed ; exit 1 )
 	$(E) "[RUN]     Testing bm_fullstack"
 	$(Q) $(BINDIR)/$(CONFIG)/bm_fullstack || ( echo test bm_fullstack failed ; exit 1 )
+	$(E) "[RUN]     Testing bm_metadata"
+	$(Q) $(BINDIR)/$(CONFIG)/bm_metadata || ( echo test bm_metadata failed ; exit 1 )
 	$(E) "[RUN]     Testing channel_arguments_test"
 	$(Q) $(BINDIR)/$(CONFIG)/channel_arguments_test || ( echo test channel_arguments_test failed ; exit 1 )
 	$(E) "[RUN]     Testing channel_filter_test"
@@ -3187,6 +3209,8 @@
     src/core/lib/tsi/ssl_transport_security.c \
     src/core/lib/tsi/transport_security.c \
     src/core/ext/transport/chttp2/client/chttp2_connector.c \
+    src/core/ext/load_reporting/load_reporting.c \
+    src/core/ext/load_reporting/load_reporting_filter.c \
     src/core/plugin_registry/grpc_cronet_plugin_registry.c \
 
 PUBLIC_HEADERS_C += \
@@ -7680,6 +7704,7 @@
     test/core/end2end/tests/hpack_size.c \
     test/core/end2end/tests/idempotent_request.c \
     test/core/end2end/tests/invoke_large_request.c \
+    test/core/end2end/tests/keepalive_timeout.c \
     test/core/end2end/tests/large_metadata.c \
     test/core/end2end/tests/load_reporting_hook.c \
     test/core/end2end/tests/max_concurrent_streams.c \
@@ -7768,6 +7793,7 @@
     test/core/end2end/tests/hpack_size.c \
     test/core/end2end/tests/idempotent_request.c \
     test/core/end2end/tests/invoke_large_request.c \
+    test/core/end2end/tests/keepalive_timeout.c \
     test/core/end2end/tests/large_metadata.c \
     test/core/end2end/tests/load_reporting_hook.c \
     test/core/end2end/tests/max_concurrent_streams.c \
@@ -9677,6 +9703,38 @@
 endif
 
 
+GRPC_COMPLETION_QUEUE_THREADING_TEST_SRC = \
+    test/core/surface/completion_queue_threading_test.c \
+
+GRPC_COMPLETION_QUEUE_THREADING_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_COMPLETION_QUEUE_THREADING_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/grpc_completion_queue_threading_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/grpc_completion_queue_threading_test: $(GRPC_COMPLETION_QUEUE_THREADING_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) $(GRPC_COMPLETION_QUEUE_THREADING_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)/grpc_completion_queue_threading_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/surface/completion_queue_threading_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_grpc_completion_queue_threading_test: $(GRPC_COMPLETION_QUEUE_THREADING_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(GRPC_COMPLETION_QUEUE_THREADING_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 GRPC_CREATE_JWT_SRC = \
     test/core/security/create_jwt.c \
 
@@ -11725,6 +11783,38 @@
 endif
 
 
+TCP_CLIENT_UV_TEST_SRC = \
+    test/core/iomgr/tcp_client_uv_test.c \
+
+TCP_CLIENT_UV_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(TCP_CLIENT_UV_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/tcp_client_uv_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/tcp_client_uv_test: $(TCP_CLIENT_UV_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) $(TCP_CLIENT_UV_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)/tcp_client_uv_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/iomgr/tcp_client_uv_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_tcp_client_uv_test: $(TCP_CLIENT_UV_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(TCP_CLIENT_UV_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 TCP_POSIX_TEST_SRC = \
     test/core/iomgr/tcp_posix_test.c \
 
@@ -11789,6 +11879,38 @@
 endif
 
 
+TCP_SERVER_UV_TEST_SRC = \
+    test/core/iomgr/tcp_server_uv_test.c \
+
+TCP_SERVER_UV_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(TCP_SERVER_UV_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/tcp_server_uv_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/tcp_server_uv_test: $(TCP_SERVER_UV_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) $(TCP_SERVER_UV_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)/tcp_server_uv_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/iomgr/tcp_server_uv_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_tcp_server_uv_test: $(TCP_SERVER_UV_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(TCP_SERVER_UV_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 TIME_AVERAGED_STATS_TEST_SRC = \
     test/core/iomgr/time_averaged_stats_test.c \
 
@@ -12345,6 +12467,49 @@
 endif
 
 
+BM_CHTTP2_HPACK_SRC = \
+    test/cpp/microbenchmarks/bm_chttp2_hpack.cc \
+
+BM_CHTTP2_HPACK_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(BM_CHTTP2_HPACK_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/bm_chttp2_hpack: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/bm_chttp2_hpack: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/bm_chttp2_hpack: $(PROTOBUF_DEP) $(BM_CHTTP2_HPACK_OBJS) $(LIBDIR)/$(CONFIG)/libbenchmark.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(BM_CHTTP2_HPACK_OBJS) $(LIBDIR)/$(CONFIG)/libbenchmark.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/bm_chttp2_hpack
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/cpp/microbenchmarks/bm_chttp2_hpack.o:  $(LIBDIR)/$(CONFIG)/libbenchmark.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_bm_chttp2_hpack: $(BM_CHTTP2_HPACK_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(BM_CHTTP2_HPACK_OBJS:.o=.dep)
+endif
+endif
+
+
 BM_CLOSURE_SRC = \
     test/cpp/microbenchmarks/bm_closure.cc \
 
@@ -12368,16 +12533,16 @@
 
 else
 
-$(BINDIR)/$(CONFIG)/bm_closure: $(PROTOBUF_DEP) $(BM_CLOSURE_OBJS) $(LIBDIR)/$(CONFIG)/libbenchmark.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(BINDIR)/$(CONFIG)/bm_closure: $(PROTOBUF_DEP) $(BM_CLOSURE_OBJS) $(LIBDIR)/$(CONFIG)/libbenchmark.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LDXX) $(LDFLAGS) $(BM_CLOSURE_OBJS) $(LIBDIR)/$(CONFIG)/libbenchmark.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/bm_closure
+	$(Q) $(LDXX) $(LDFLAGS) $(BM_CLOSURE_OBJS) $(LIBDIR)/$(CONFIG)/libbenchmark.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/bm_closure
 
 endif
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/cpp/microbenchmarks/bm_closure.o:  $(LIBDIR)/$(CONFIG)/libbenchmark.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(OBJDIR)/$(CONFIG)/test/cpp/microbenchmarks/bm_closure.o:  $(LIBDIR)/$(CONFIG)/libbenchmark.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
 deps_bm_closure: $(BM_CLOSURE_OBJS:.o=.dep)
 
@@ -12517,6 +12682,49 @@
 endif
 
 
+BM_METADATA_SRC = \
+    test/cpp/microbenchmarks/bm_metadata.cc \
+
+BM_METADATA_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(BM_METADATA_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/bm_metadata: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/bm_metadata: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/bm_metadata: $(PROTOBUF_DEP) $(BM_METADATA_OBJS) $(LIBDIR)/$(CONFIG)/libbenchmark.a $(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) $(LDXX) $(LDFLAGS) $(BM_METADATA_OBJS) $(LIBDIR)/$(CONFIG)/libbenchmark.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/bm_metadata
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/cpp/microbenchmarks/bm_metadata.o:  $(LIBDIR)/$(CONFIG)/libbenchmark.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_bm_metadata: $(BM_METADATA_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(BM_METADATA_OBJS:.o=.dep)
+endif
+endif
+
+
 CHANNEL_ARGUMENTS_TEST_SRC = \
     test/cpp/common/channel_arguments_test.cc \
 
diff --git a/binding.gyp b/binding.gyp
index 7190082..b8a1a52 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -38,7 +38,12 @@
 # https://n8.io/converting-a-c-library-to-gyp/
 {
   'variables': {
-    'runtime%': 'node'
+    'runtime%': 'node',
+    # UV integration in C core is disabled by default while bugs are ironed
+    # out. It can be re-enabled for one build by setting the npm config
+    # variable grpc_uv to true, and it can be re-enabled permanently by
+    # setting it to true here.
+    'grpc_uv%': 'false'
   },
   'target_defaults': {
     'include_dirs': [
@@ -49,11 +54,11 @@
       'GPR_BACKWARDS_COMPATIBILITY_MODE'
     ],
     'conditions': [
-      ['runtime=="node"', {
+      ['runtime=="node" and grpc_uv=="true"', {
         'defines': [
           # Disabling this while bugs are ironed out. Uncomment this to
           # re-enable libuv integration in C core.
-          # 'GRPC_UV'
+          'GRPC_UV'
         ]
       }],
       ['OS!="win" and runtime=="electron"', {
@@ -70,19 +75,10 @@
           'OPENSSL_NO_ASM'
         ]
       }, {
-        # Based on logic above, we know that this must be a non-Windows system
-        'variables': {
-          # The output of "node --version" is "v[version]". We use cut to
-          # remove the first character.
-          'target%': '<!(node --version | cut -c2-)'
-        },
-        # Empirically, Node only exports ALPN symbols if its major version is >0.
-        # io.js always reports versions >0 and always exports ALPN symbols.
-        # Therefore, Node's major version will be truthy if and only if it
-        # supports ALPN. The target is "[major].[minor].[patch]". We split by
-        # periods and take the first field to get the major version.
+        # As of the beginning of 2017, we only support versions of Node with
+        # embedded versions of OpenSSL that support ALPN
         'defines': [
-          'TSI_OPENSSL_ALPN_SUPPORT=<!(echo <(target) | cut -d. -f1)'
+          'TSI_OPENSSL_ALPN_SUPPORT=1'
         ],
         'include_dirs': [
           '<(node_root_dir)/deps/openssl/openssl/include',
@@ -891,6 +887,8 @@
         "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",
       ],
diff --git a/build.yaml b/build.yaml
index 33cd692..09d5824 100644
--- a/build.yaml
+++ b/build.yaml
@@ -978,6 +978,7 @@
   - grpc_base
   - grpc_transport_cronet_client_secure
   - grpc_transport_chttp2_client_secure
+  - grpc_load_reporting
   generate_plugin_registry: true
   platforms:
   - linux
@@ -2013,6 +2014,16 @@
   - grpc
   - gpr_test_util
   - gpr
+- name: grpc_completion_queue_threading_test
+  build: test
+  language: c
+  src:
+  - test/core/surface/completion_queue_threading_test.c
+  deps:
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
   exclude_iomgrs:
   - uv
 - name: grpc_create_jwt
@@ -2115,6 +2126,8 @@
   - grpc
   - gpr_test_util
   - gpr
+  exclude_iomgrs:
+  - uv
   platforms:
   - linux
   secure: true
@@ -2545,8 +2558,6 @@
   - grpc
   - gpr_test_util
   - gpr
-  exclude_iomgrs:
-  - uv
 - name: resource_quota_test
   cpu_cost: 30
   build: test
@@ -2746,6 +2757,19 @@
   - mac
   - linux
   - posix
+- name: tcp_client_uv_test
+  cpu_cost: 0.5
+  build: test
+  language: c
+  src:
+  - test/core/iomgr/tcp_client_uv_test.c
+  deps:
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
+  exclude_iomgrs:
+  - native
 - name: tcp_posix_test
   cpu_cost: 0.2
   build: test
@@ -2779,6 +2803,18 @@
   - mac
   - linux
   - posix
+- name: tcp_server_uv_test
+  build: test
+  language: c
+  src:
+  - test/core/iomgr/tcp_server_uv_test.c
+  deps:
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
+  exclude_iomgrs:
+  - native
 - name: time_averaged_stats_test
   build: test
   language: c
@@ -2980,6 +3016,25 @@
   - mac
   - linux
   - posix
+- name: bm_chttp2_hpack
+  build: test
+  language: c++
+  src:
+  - test/cpp/microbenchmarks/bm_chttp2_hpack.cc
+  deps:
+  - benchmark
+  - grpc++_test_util
+  - grpc_test_util
+  - grpc++
+  - grpc
+  - gpr_test_util
+  - gpr
+  args:
+  - --benchmark_min_time=0
+  platforms:
+  - mac
+  - linux
+  - posix
 - name: bm_closure
   build: test
   language: c++
@@ -2987,7 +3042,9 @@
   - test/cpp/microbenchmarks/bm_closure.cc
   deps:
   - benchmark
+  - grpc++_test_util
   - grpc_test_util
+  - grpc++
   - grpc
   - gpr_test_util
   - gpr
@@ -3050,6 +3107,27 @@
   - gpr
   args:
   - --benchmark_min_time=0
+  excluded_poll_engines:
+  - poll
+  - poll-cv
+  platforms:
+  - mac
+  - linux
+  - posix
+  timeout_seconds: 1200
+- name: bm_metadata
+  build: test
+  language: c++
+  src:
+  - test/cpp/microbenchmarks/bm_metadata.cc
+  deps:
+  - benchmark
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
+  args:
+  - --benchmark_min_time=0
   platforms:
   - mac
   - linux
@@ -4063,6 +4141,8 @@
   - 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:
diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h
index da936bf..e5c7313 100644
--- a/include/grpc/impl/codegen/grpc_types.h
+++ b/include/grpc/impl/codegen/grpc_types.h
@@ -193,6 +193,16 @@
 /** How much data are we willing to queue up per stream if
     GRPC_WRITE_BUFFER_HINT is set? This is an upper bound */
 #define GRPC_ARG_HTTP2_WRITE_BUFFER_SIZE "grpc.http2.write_buffer_size"
+/** After a duration of this time the client pings the server to see if the
+    transport is still alive. Int valued, seconds. */
+#define GRPC_ARG_HTTP2_KEEPALIVE_TIME "grpc.http2.keepalive_time"
+/** After waiting for a duration of this time, if the client does not receive
+    the ping ack, it will close the transport. Int valued, seconds. */
+#define GRPC_ARG_HTTP2_KEEPALIVE_TIMEOUT "grpc.http2.keepalive_timeout"
+/** Is it permissible to send keepalive pings without any outstanding streams.
+    Int valued, 0(false)/1(true). */
+#define GRPC_ARG_HTTP2_KEEPALIVE_PERMIT_WITHOUT_CALLS \
+  "grpc.http2.keepalive_permit_without_calls"
 /** Default authority to pass if none specified on call construction. A string.
  * */
 #define GRPC_ARG_DEFAULT_AUTHORITY "grpc.default_authority"
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.c b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
index 68e01e5..da4c7dc 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.c
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
@@ -48,16 +48,19 @@
 #include "src/core/ext/transport/chttp2/transport/varint.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/http/parser.h"
+#include "src/core/lib/iomgr/timer.h"
 #include "src/core/lib/iomgr/workqueue.h"
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/support/env.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/transport/error_utils.h"
 #include "src/core/lib/transport/http2_errors.h"
 #include "src/core/lib/transport/static_metadata.h"
 #include "src/core/lib/transport/status_conversion.h"
 #include "src/core/lib/transport/timeout_encoding.h"
+#include "src/core/lib/transport/transport.h"
 #include "src/core/lib/transport/transport_impl.h"
 
 #define DEFAULT_WINDOW 65535
@@ -66,6 +69,10 @@
 #define MAX_WRITE_BUFFER_SIZE (64 * 1024 * 1024)
 #define DEFAULT_MAX_HEADER_LIST_SIZE (16 * 1024)
 
+#define DEFAULT_KEEPALIVE_TIME_SECOND INT_MAX
+#define DEFAULT_KEEPALIVE_TIMEOUT_SECOND 20
+#define DEFAULT_KEEPALIVE_PERMIT_WITHOUT_CALLS false
+
 #define MAX_CLIENT_STREAM_ID 0x7fffffffu
 int grpc_http_trace = 0;
 int grpc_flowctl_trace = 0;
@@ -139,6 +146,16 @@
 #define DEFAULT_MIN_TIME_BETWEEN_PINGS_MS 0
 #define DEFAULT_MAX_PINGS_BETWEEN_DATA 3
 
+/** keepalive-relevant functions */
+static void init_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                                       grpc_error *error);
+static void start_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                                        grpc_error *error);
+static void finish_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                                         grpc_error *error);
+static void keepalive_watchdog_fired_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                                            grpc_error *error);
+
 /*******************************************************************************
  * CONSTRUCTION/DESTRUCTION/REFCOUNTING
  */
@@ -253,6 +270,17 @@
                     grpc_combiner_scheduler(t->combiner, false));
   grpc_closure_init(&t->finish_bdp_ping_locked, finish_bdp_ping_locked, t,
                     grpc_combiner_scheduler(t->combiner, false));
+  grpc_closure_init(&t->init_keepalive_ping_locked, init_keepalive_ping_locked,
+                    t, grpc_combiner_scheduler(t->combiner, false));
+  grpc_closure_init(&t->start_keepalive_ping_locked,
+                    start_keepalive_ping_locked, t,
+                    grpc_combiner_scheduler(t->combiner, false));
+  grpc_closure_init(&t->finish_keepalive_ping_locked,
+                    finish_keepalive_ping_locked, t,
+                    grpc_combiner_scheduler(t->combiner, false));
+  grpc_closure_init(&t->keepalive_watchdog_fired_locked,
+                    keepalive_watchdog_fired_locked, t,
+                    grpc_combiner_scheduler(t->combiner, false));
 
   grpc_bdp_estimator_init(&t->bdp_estimator, t->peer_string);
   t->last_pid_update = gpr_now(GPR_CLOCK_MONOTONIC);
@@ -314,6 +342,18 @@
           gpr_time_from_millis(DEFAULT_MIN_TIME_BETWEEN_PINGS_MS, GPR_TIMESPAN),
   };
 
+  /* client-side keepalive setting */
+  t->keepalive_time =
+      DEFAULT_KEEPALIVE_TIME_SECOND == INT_MAX
+          ? gpr_inf_future(GPR_TIMESPAN)
+          : gpr_time_from_seconds(DEFAULT_KEEPALIVE_TIME_SECOND, GPR_TIMESPAN);
+  t->keepalive_timeout =
+      DEFAULT_KEEPALIVE_TIMEOUT_SECOND == INT_MAX
+          ? gpr_inf_future(GPR_TIMESPAN)
+          : gpr_time_from_seconds(DEFAULT_KEEPALIVE_TIMEOUT_SECOND,
+                                  GPR_TIMESPAN);
+  t->keepalive_permit_without_calls = DEFAULT_KEEPALIVE_PERMIT_WITHOUT_CALLS;
+
   if (channel_args) {
     for (i = 0; i < channel_args->num_args; i++) {
       if (0 == strcmp(channel_args->args[i].key,
@@ -361,6 +401,28 @@
                  strcmp(channel_args->args[i].key, GRPC_ARG_HTTP2_BDP_PROBE)) {
         t->enable_bdp_probe = grpc_channel_arg_get_integer(
             &channel_args->args[i], (grpc_integer_options){1, 0, 1});
+      } else if (0 == strcmp(channel_args->args[i].key,
+                             GRPC_ARG_HTTP2_KEEPALIVE_TIME)) {
+        const int value = grpc_channel_arg_get_integer(
+            &channel_args->args[i],
+            (grpc_integer_options){DEFAULT_KEEPALIVE_TIME_SECOND, 1, INT_MAX});
+        t->keepalive_time = value == INT_MAX
+                                ? gpr_inf_future(GPR_TIMESPAN)
+                                : gpr_time_from_seconds(value, GPR_TIMESPAN);
+      } else if (0 == strcmp(channel_args->args[i].key,
+                             GRPC_ARG_HTTP2_KEEPALIVE_TIMEOUT)) {
+        const int value = grpc_channel_arg_get_integer(
+            &channel_args->args[i],
+            (grpc_integer_options){DEFAULT_KEEPALIVE_TIMEOUT_SECOND, 0,
+                                   INT_MAX});
+        t->keepalive_timeout = value == INT_MAX
+                                   ? gpr_inf_future(GPR_TIMESPAN)
+                                   : gpr_time_from_seconds(value, GPR_TIMESPAN);
+      } else if (0 == strcmp(channel_args->args[i].key,
+                             GRPC_ARG_HTTP2_KEEPALIVE_PERMIT_WITHOUT_CALLS)) {
+        t->keepalive_permit_without_calls =
+            (uint32_t)grpc_channel_arg_get_integer(
+                &channel_args->args[i], (grpc_integer_options){0, 0, 1});
       } else {
         static const struct {
           const char *channel_arg_name;
@@ -412,6 +474,16 @@
   t->ping_state.pings_before_data_required =
       t->ping_policy.max_pings_without_data;
 
+  /** Start client-side keepalive pings */
+  if (t->is_client) {
+    t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_WAITING;
+    GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping");
+    grpc_timer_init(
+        exec_ctx, &t->keepalive_ping_timer,
+        gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_time),
+        &t->init_keepalive_ping_locked, gpr_now(GPR_CLOCK_MONOTONIC));
+  }
+
   grpc_chttp2_initiate_write(exec_ctx, t, false, "init");
   post_benign_reclaimer(exec_ctx, t);
 }
@@ -456,6 +528,22 @@
     connectivity_state_set(exec_ctx, t, GRPC_CHANNEL_SHUTDOWN,
                            GRPC_ERROR_REF(error), "close_transport");
     grpc_endpoint_shutdown(exec_ctx, t->ep, GRPC_ERROR_REF(error));
+    if (t->is_client) {
+      switch (t->keepalive_state) {
+        case GRPC_CHTTP2_KEEPALIVE_STATE_WAITING: {
+          grpc_timer_cancel(exec_ctx, &t->keepalive_ping_timer);
+          break;
+        }
+        case GRPC_CHTTP2_KEEPALIVE_STATE_PINGING: {
+          grpc_timer_cancel(exec_ctx, &t->keepalive_ping_timer);
+          grpc_timer_cancel(exec_ctx, &t->keepalive_watchdog_timer);
+          break;
+        }
+        case GRPC_CHTTP2_KEEPALIVE_STATE_DYING: {
+          break;
+        }
+      }
+    }
 
     /* flush writable stream list to avoid dangling references */
     grpc_chttp2_stream *s;
@@ -1983,6 +2071,77 @@
   GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "bdp_ping");
 }
 
+static void init_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                                       grpc_error *error) {
+  grpc_chttp2_transport *t = arg;
+  GPR_ASSERT(t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_WAITING);
+  if (error == GRPC_ERROR_NONE && !(t->destroying || t->closed)) {
+    if (t->keepalive_permit_without_calls || t->stream_map.count > 0) {
+      t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_PINGING;
+      GRPC_CHTTP2_REF_TRANSPORT(t, "keepalive ping end");
+      send_ping_locked(exec_ctx, t, GRPC_CHTTP2_PING_ON_NEXT_WRITE,
+                       &t->start_keepalive_ping_locked,
+                       &t->finish_keepalive_ping_locked);
+    } else {
+      GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping");
+      grpc_timer_init(
+          exec_ctx, &t->keepalive_ping_timer,
+          gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_time),
+          &t->init_keepalive_ping_locked, gpr_now(GPR_CLOCK_MONOTONIC));
+    }
+  }
+  GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "init keepalive ping");
+}
+
+static void start_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                                        grpc_error *error) {
+  grpc_chttp2_transport *t = arg;
+  GRPC_CHTTP2_REF_TRANSPORT(t, "keepalive watchdog");
+  grpc_timer_init(
+      exec_ctx, &t->keepalive_watchdog_timer,
+      gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_timeout),
+      &t->keepalive_watchdog_fired_locked, gpr_now(GPR_CLOCK_MONOTONIC));
+}
+
+static void finish_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                                         grpc_error *error) {
+  grpc_chttp2_transport *t = arg;
+  if (t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_PINGING) {
+    if (error == GRPC_ERROR_NONE) {
+      t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_WAITING;
+      grpc_timer_cancel(exec_ctx, &t->keepalive_watchdog_timer);
+      GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping");
+      grpc_timer_init(
+          exec_ctx, &t->keepalive_ping_timer,
+          gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_time),
+          grpc_closure_create(init_keepalive_ping_locked, t,
+                              grpc_combiner_scheduler(t->combiner, false)),
+          gpr_now(GPR_CLOCK_MONOTONIC));
+    }
+  }
+  GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "keepalive ping end");
+}
+
+static void keepalive_watchdog_fired_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                                            grpc_error *error) {
+  grpc_chttp2_transport *t = arg;
+  if (t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_PINGING) {
+    if (error == GRPC_ERROR_NONE) {
+      t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_DYING;
+      close_transport_locked(exec_ctx, t,
+                             GRPC_ERROR_CREATE("keepalive watchdog timeout"));
+    }
+  } else {
+    /** The watchdog timer should have been cancelled by
+        finish_keepalive_ping_locked. */
+    if (error != GRPC_ERROR_CANCELLED) {
+      gpr_log(GPR_ERROR, "keepalive_ping_end state error: %d (expect: %d)",
+              t->keepalive_state, GRPC_CHTTP2_KEEPALIVE_STATE_PINGING);
+    }
+  }
+  GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "keepalive watchdog");
+}
+
 /*******************************************************************************
  * CALLBACK LOOP
  */
diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.c b/src/core/ext/transport/chttp2/transport/hpack_encoder.c
index 63df8e1..84586cd 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_encoder.c
+++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.c
@@ -173,6 +173,7 @@
 
 static uint8_t *add_tiny_header_data(framer_state *st, size_t len) {
   ensure_space(st, len);
+  st->stats->header_bytes += len;
   return grpc_slice_buffer_tiny_add(st->output, len);
 }
 
diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h
index 5d41f4b..d26812a 100644
--- a/src/core/ext/transport/chttp2/transport/internal.h
+++ b/src/core/ext/transport/chttp2/transport/internal.h
@@ -50,6 +50,7 @@
 #include "src/core/ext/transport/chttp2/transport/stream_map.h"
 #include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/timer.h"
 #include "src/core/lib/transport/bdp_estimator.h"
 #include "src/core/lib/transport/connectivity_state.h"
 #include "src/core/lib/transport/pid_controller.h"
@@ -208,6 +209,12 @@
   grpc_closure finished_action;
 };
 
+typedef enum {
+  GRPC_CHTTP2_KEEPALIVE_STATE_WAITING,
+  GRPC_CHTTP2_KEEPALIVE_STATE_PINGING,
+  GRPC_CHTTP2_KEEPALIVE_STATE_DYING,
+} grpc_chttp2_keepalive_state;
+
 struct grpc_chttp2_transport {
   grpc_transport base; /* must be first */
   gpr_refcount refs;
@@ -382,6 +389,28 @@
   grpc_closure benign_reclaimer_locked;
   /** destructive cleanup closure */
   grpc_closure destructive_reclaimer_locked;
+
+  /* keep-alive ping support */
+  /** Closure to initialize a keepalive ping */
+  grpc_closure init_keepalive_ping_locked;
+  /** Closure to run when the keepalive ping is sent */
+  grpc_closure start_keepalive_ping_locked;
+  /** Cousure to run when the keepalive ping ack is received */
+  grpc_closure finish_keepalive_ping_locked;
+  /** Closrue to run when the keepalive ping timeouts */
+  grpc_closure keepalive_watchdog_fired_locked;
+  /** timer to initiate ping events */
+  grpc_timer keepalive_ping_timer;
+  /** watchdog to kill the transport when waiting for the keepalive ping */
+  grpc_timer keepalive_watchdog_timer;
+  /** time duration in between pings */
+  gpr_timespec keepalive_time;
+  /** grace period for a ping to complete before watchdog kicks in */
+  gpr_timespec keepalive_timeout;
+  /** if keepalive pings are allowed when there's no outstanding streams */
+  bool keepalive_permit_without_calls;
+  /** keep-alive state machine state */
+  grpc_chttp2_keepalive_state keepalive_state;
 };
 
 typedef enum {
diff --git a/src/core/lib/iomgr/tcp_uv.c b/src/core/lib/iomgr/tcp_uv.c
index 5fb398c..5541c62 100644
--- a/src/core/lib/iomgr/tcp_uv.c
+++ b/src/core/lib/iomgr/tcp_uv.c
@@ -79,8 +79,6 @@
   grpc_pollset *pollset;
 } grpc_tcp;
 
-static void uv_close_callback(uv_handle_t *handle) { gpr_free(handle); }
-
 static void tcp_free(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) {
   grpc_resource_user_unref(exec_ctx, tcp->resource_user);
   gpr_free(tcp);
@@ -119,6 +117,13 @@
 static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); }
 #endif
 
+static void uv_close_callback(uv_handle_t *handle) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_tcp *tcp = handle->data;
+  TCP_UNREF(&exec_ctx, tcp, "destroy");
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
 static void alloc_uv_buf(uv_handle_t *handle, size_t suggested_size,
                          uv_buf_t *buf) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
@@ -313,7 +318,6 @@
   grpc_network_status_unregister_endpoint(ep);
   grpc_tcp *tcp = (grpc_tcp *)ep;
   uv_close((uv_handle_t *)tcp->handle, uv_close_callback);
-  TCP_UNREF(exec_ctx, tcp, "destroy");
 }
 
 static char *uv_get_peer(grpc_endpoint *ep) {
diff --git a/src/core/lib/surface/call.c b/src/core/lib/surface/call.c
index 0f03d6d..cc57654 100644
--- a/src/core/lib/surface/call.c
+++ b/src/core/lib/surface/call.c
@@ -112,7 +112,7 @@
                                  .error = (grpc_error *)(atm & ~(gpr_atm)1)};
 }
 
-#define MAX_ERRORS_PER_BATCH 3
+#define MAX_ERRORS_PER_BATCH 4
 
 typedef struct batch_control {
   grpc_call *call;
@@ -254,7 +254,7 @@
 static void process_data_after_md(grpc_exec_ctx *exec_ctx, batch_control *bctl);
 static void post_batch_completion(grpc_exec_ctx *exec_ctx, batch_control *bctl);
 static void add_batch_error(grpc_exec_ctx *exec_ctx, batch_control *bctl,
-                            grpc_error *error);
+                            grpc_error *error, bool has_cancelled);
 
 static void add_init_error(grpc_error **composite, grpc_error *new) {
   if (new == GRPC_ERROR_NONE) return;
@@ -1222,6 +1222,11 @@
   grpc_call *call = bctl->call;
   gpr_mu_lock(&bctl->call->mu);
   if (error != GRPC_ERROR_NONE) {
+    if (call->receiving_stream != NULL) {
+      grpc_byte_stream_destroy(exec_ctx, call->receiving_stream);
+      call->receiving_stream = NULL;
+    }
+    add_batch_error(exec_ctx, bctl, GRPC_ERROR_REF(error), true);
     cancel_with_error(exec_ctx, call, STATUS_FROM_SURFACE,
                       GRPC_ERROR_REF(error));
   }
@@ -1288,10 +1293,10 @@
 }
 
 static void add_batch_error(grpc_exec_ctx *exec_ctx, batch_control *bctl,
-                            grpc_error *error) {
+                            grpc_error *error, bool has_cancelled) {
   if (error == GRPC_ERROR_NONE) return;
   int idx = (int)gpr_atm_no_barrier_fetch_add(&bctl->num_errors, 1);
-  if (idx == 0) {
+  if (idx == 0 && !has_cancelled) {
     cancel_with_error(exec_ctx, bctl->call, STATUS_FROM_CORE,
                       GRPC_ERROR_REF(error));
   }
@@ -1305,7 +1310,7 @@
 
   gpr_mu_lock(&call->mu);
 
-  add_batch_error(exec_ctx, bctl, GRPC_ERROR_REF(error));
+  add_batch_error(exec_ctx, bctl, GRPC_ERROR_REF(error), false);
   if (error == GRPC_ERROR_NONE) {
     grpc_metadata_batch *md =
         &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */];
@@ -1342,7 +1347,7 @@
                          grpc_error *error) {
   batch_control *bctl = bctlp;
 
-  add_batch_error(exec_ctx, bctl, GRPC_ERROR_REF(error));
+  add_batch_error(exec_ctx, bctl, GRPC_ERROR_REF(error), false);
   finish_batch_step(exec_ctx, bctl);
 }
 
diff --git a/src/core/lib/surface/server.c b/src/core/lib/surface/server.c
index d6a401a..b360579 100644
--- a/src/core/lib/surface/server.c
+++ b/src/core/lib/surface/server.c
@@ -540,7 +540,8 @@
         &calld->kill_zombie_closure, kill_zombie,
         grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0),
         grpc_schedule_on_exec_ctx);
-    grpc_closure_sched(exec_ctx, &calld->kill_zombie_closure, error);
+    grpc_closure_sched(exec_ctx, &calld->kill_zombie_closure,
+                       GRPC_ERROR_REF(error));
     return;
   }
 
diff --git a/src/core/plugin_registry/grpc_cronet_plugin_registry.c b/src/core/plugin_registry/grpc_cronet_plugin_registry.c
index d339ed3..c97f47b 100644
--- a/src/core/plugin_registry/grpc_cronet_plugin_registry.c
+++ b/src/core/plugin_registry/grpc_cronet_plugin_registry.c
@@ -37,10 +37,14 @@
 extern void grpc_chttp2_plugin_shutdown(void);
 extern void grpc_client_channel_init(void);
 extern void grpc_client_channel_shutdown(void);
+extern void grpc_load_reporting_plugin_init(void);
+extern void grpc_load_reporting_plugin_shutdown(void);
 
 void grpc_register_built_in_plugins(void) {
   grpc_register_plugin(grpc_chttp2_plugin_init,
                        grpc_chttp2_plugin_shutdown);
   grpc_register_plugin(grpc_client_channel_init,
                        grpc_client_channel_shutdown);
+  grpc_register_plugin(grpc_load_reporting_plugin_init,
+                       grpc_load_reporting_plugin_shutdown);
 }
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
index 68a8f48..32a52ee 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
@@ -45,6 +45,7 @@
 using Google.Protobuf;
 using Grpc.Auth;
 using Grpc.Core;
+using Grpc.Core.Logging;
 using Grpc.Core.Utils;
 using Grpc.Testing;
 using Newtonsoft.Json.Linq;
@@ -95,6 +96,7 @@
 
         public static void Run(string[] args)
         {
+            GrpcEnvironment.SetLogger(new ConsoleLogger());
             var parserResult = Parser.Default.ParseArguments<ClientOptions>(args)
                 .WithNotParsed(errors => Environment.Exit(1))
                 .WithParsed(options =>
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs
index 4118f99..be99f92 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs
@@ -41,6 +41,7 @@
 using CommandLine;
 using CommandLine.Text;
 using Grpc.Core;
+using Grpc.Core.Logging;
 using Grpc.Core.Utils;
 using Grpc.Testing;
 using NUnit.Framework;
@@ -68,6 +69,7 @@
 
         public static void Run(string[] args)
         {
+            GrpcEnvironment.SetLogger(new ConsoleLogger());
             var parserResult = Parser.Default.ParseArguments<ServerOptions>(args)
                 .WithNotParsed(errors => Environment.Exit(1))
                 .WithParsed(options =>
diff --git a/src/csharp/Grpc.IntegrationTesting/QpsWorker.cs b/src/csharp/Grpc.IntegrationTesting/QpsWorker.cs
index 62a7347..b17b2c2 100644
--- a/src/csharp/Grpc.IntegrationTesting/QpsWorker.cs
+++ b/src/csharp/Grpc.IntegrationTesting/QpsWorker.cs
@@ -42,6 +42,7 @@
 using CommandLine;
 using CommandLine.Text;
 using Grpc.Core;
+using Grpc.Core.Logging;
 using Grpc.Core.Utils;
 using Grpc.Testing;
 using NUnit.Framework;
@@ -65,6 +66,7 @@
 
         public static void Run(string[] args)
         {
+            GrpcEnvironment.SetLogger(new ConsoleLogger());
             var parserResult = Parser.Default.ParseArguments<ServerOptions>(args)
                 .WithNotParsed((x) => Environment.Exit(1))
                 .WithParsed(options =>
diff --git a/src/csharp/Grpc.IntegrationTesting/StressTestClient.cs b/src/csharp/Grpc.IntegrationTesting/StressTestClient.cs
index 750613b..dbf8844 100644
--- a/src/csharp/Grpc.IntegrationTesting/StressTestClient.cs
+++ b/src/csharp/Grpc.IntegrationTesting/StressTestClient.cs
@@ -92,6 +92,7 @@
 
         public static void Run(string[] args)
         {
+            GrpcEnvironment.SetLogger(new ConsoleLogger());
             var parserResult = Parser.Default.ParseArguments<ClientOptions>(args)
                 .WithNotParsed((x) => Environment.Exit(1))
                 .WithParsed(options => {
diff --git a/src/node/ext/completion_queue_threadpool.cc b/src/node/ext/completion_queue_threadpool.cc
index 4881542..1917074 100644
--- a/src/node/ext/completion_queue_threadpool.cc
+++ b/src/node/ext/completion_queue_threadpool.cc
@@ -174,7 +174,6 @@
 }
 
 void CompletionQueueNext() {
-  gpr_log(GPR_DEBUG, "Called CompletionQueueNext");
   CompletionQueueAsyncWorker::Next();
 }
 
diff --git a/src/node/ext/server.cc b/src/node/ext/server.cc
index 4761b28..ccb55aa 100644
--- a/src/node/ext/server.cc
+++ b/src/node/ext/server.cc
@@ -78,8 +78,6 @@
 Nan::Callback *Server::constructor;
 Persistent<FunctionTemplate> Server::fun_tpl;
 
-static Callback *shutdown_callback;
-
 class NewCallOp : public Op {
  public:
   NewCallOp() {
@@ -128,51 +126,6 @@
   std::string GetTypeString() const { return "new_call"; }
 };
 
-class ServerShutdownOp : public Op {
- public:
-  ServerShutdownOp(grpc_server *server): server(server) {
-  }
-
-  ~ServerShutdownOp() {
-  }
-
-  Local<Value> GetNodeValue() const {
-    return Nan::New<External>(reinterpret_cast<void *>(server));
-  }
-
-  bool ParseOp(Local<Value> value, grpc_op *out) {
-    return true;
-  }
-  bool IsFinalOp() {
-    return false;
-  }
-
-  grpc_server *server;
-
- protected:
-  std::string GetTypeString() const { return "shutdown"; }
-};
-
-NAN_METHOD(ServerShutdownCallback) {
-  if (!info[0]->IsNull()) {
-    return Nan::ThrowError("forceShutdown failed somehow");
-  }
-  MaybeLocal<Object> maybe_result = Nan::To<Object>(info[1]);
-  Local<Object> result = maybe_result.ToLocalChecked();
-  Local<Value> server_val = Nan::Get(
-      result, Nan::New("shutdown").ToLocalChecked()).ToLocalChecked();
-  Local<External> server_extern = server_val.As<External>();
-  grpc_server *server = reinterpret_cast<grpc_server *>(server_extern->Value());
-  grpc_server_destroy(server);
-}
-
-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);
@@ -187,11 +140,6 @@
   Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked();
   Nan::Set(exports, Nan::New("Server").ToLocalChecked(), ctr);
   constructor = new Callback(ctr);
-
-  Local<FunctionTemplate>callback_tpl =
-      Nan::New<FunctionTemplate>(ServerShutdownCallback);
-  shutdown_callback = new Callback(
-      Nan::GetFunction(callback_tpl).ToLocalChecked());
 }
 
 bool Server::HasInstance(Local<Value> val) {
@@ -199,21 +147,6 @@
   return Nan::New(fun_tpl)->HasInstance(val);
 }
 
-void Server::ShutdownServer() {
-  if (this->wrapped_server != NULL) {
-    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));
-    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.h b/src/node/ext/server.h
index 9e6a7bd..ab5fc21 100644
--- a/src/node/ext/server.h
+++ b/src/node/ext/server.h
@@ -73,6 +73,7 @@
   static Nan::Persistent<v8::FunctionTemplate> fun_tpl;
 
   grpc_server *wrapped_server;
+  grpc_completion_queue *shutdown_queue;
 };
 
 }  // namespace node
diff --git a/src/node/ext/server_generic.cc b/src/node/ext/server_generic.cc
new file mode 100644
index 0000000..0cf20f7
--- /dev/null
+++ b/src/node/ext/server_generic.cc
@@ -0,0 +1,73 @@
+/*
+ *
+ * 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
new file mode 100644
index 0000000..bf8b609
--- /dev/null
+++ b/src/node/ext/server_uv.cc
@@ -0,0 +1,131 @@
+/*
+ *
+ * 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 v8::External;
+using v8::Function;
+using v8::FunctionTemplate;
+using v8::Local;
+using v8::MaybeLocal;
+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::New<External>(reinterpret_cast<void *>(server));
+  }
+
+  bool ParseOp(Local<Value> value, grpc_op *out) {
+    return true;
+  }
+  bool IsFinalOp() {
+    return false;
+  }
+
+  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");
+  }
+  MaybeLocal<Object> maybe_result = Nan::To<Object>(info[1]);
+  Local<Object> result = maybe_result.ToLocalChecked();
+  Local<Value> server_val = Nan::Get(
+      result, Nan::New("shutdown").ToLocalChecked()).ToLocalChecked();
+  Local<External> server_extern = server_val.As<External>();
+  grpc_server *server = reinterpret_cast<grpc_server *>(server_extern->Value());
+  grpc_server_destroy(server);
+}
+
+void Server::ShutdownServer() {
+  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));
+    grpc_server_cancel_all_calls(this->wrapped_server);
+    CompletionQueueNext();
+    this->wrapped_server = NULL;
+  }
+}
+
+}  // namespace grpc
+}  // namespace node
+
+#endif /* GRPC_UV */
diff --git a/src/proto/grpc/testing/echo_messages.proto b/src/proto/grpc/testing/echo_messages.proto
index b405acf..efb6f4d 100644
--- a/src/proto/grpc/testing/echo_messages.proto
+++ b/src/proto/grpc/testing/echo_messages.proto
@@ -50,6 +50,7 @@
   bool skip_cancelled_check = 9;
   string expected_transport_security_type = 10;
   DebugInfo debug_info = 11;
+  bool server_die = 12; // Server should not see a request with this set.
 }
 
 message EchoRequest {
diff --git a/templates/binding.gyp.template b/templates/binding.gyp.template
index 4b2f6ac..9d7034e 100644
--- a/templates/binding.gyp.template
+++ b/templates/binding.gyp.template
@@ -40,7 +40,12 @@
   # https://n8.io/converting-a-c-library-to-gyp/
   {
     'variables': {
-      'runtime%': 'node'
+      'runtime%': 'node',
+      # UV integration in C core is disabled by default while bugs are ironed
+      # out. It can be re-enabled for one build by setting the npm config
+      # variable grpc_uv to true, and it can be re-enabled permanently by
+      # setting it to true here.
+      'grpc_uv%': 'false'
     },
     'target_defaults': {
       'include_dirs': [
@@ -51,11 +56,11 @@
         'GPR_BACKWARDS_COMPATIBILITY_MODE'
       ],
       'conditions': [
-        ['runtime=="node"', {
+        ['runtime=="node" and grpc_uv=="true"', {
           'defines': [
             # Disabling this while bugs are ironed out. Uncomment this to
             # re-enable libuv integration in C core.
-            # 'GRPC_UV'
+            'GRPC_UV'
           ]
         }],
         ['OS!="win" and runtime=="electron"', {
@@ -72,19 +77,10 @@
             'OPENSSL_NO_ASM'
           ]
         }, {
-          # Based on logic above, we know that this must be a non-Windows system
-          'variables': {
-            # The output of "node --version" is "v[version]". We use cut to
-            # remove the first character.
-            'target%': '<!(node --version | cut -c2-)'
-          },
-          # Empirically, Node only exports ALPN symbols if its major version is >0.
-          # io.js always reports versions >0 and always exports ALPN symbols.
-          # Therefore, Node's major version will be truthy if and only if it
-          # supports ALPN. The target is "[major].[minor].[patch]". We split by
-          # periods and take the first field to get the major version.
+          # As of the beginning of 2017, we only support versions of Node with
+          # embedded versions of OpenSSL that support ALPN
           'defines': [
-            'TSI_OPENSSL_ALPN_SUPPORT=<!(echo <(target) | cut -d. -f1)'
+            'TSI_OPENSSL_ALPN_SUPPORT=1'
           ],
           'include_dirs': [
             '<(node_root_dir)/deps/openssl/openssl/include',
diff --git a/templates/tools/run_tests/generated/tests.json.template b/templates/tools/run_tests/generated/tests.json.template
index 2815dbb..10ab2e4 100644
--- a/templates/tools/run_tests/generated/tests.json.template
+++ b/templates/tools/run_tests/generated/tests.json.template
@@ -2,18 +2,28 @@
 --- |
   <%!
   import json
+
+  def gen_one_target(tgt):
+    out = {"name": tgt.name,
+           "language": tgt.language,
+           "platforms": tgt.platforms,
+           "ci_platforms": tgt.ci_platforms,
+           "gtest": tgt.gtest,
+           "exclude_configs": tgt.get("exclude_configs", []),
+           "exclude_iomgrs": tgt.get("exclude_iomgrs", []),
+           "args": tgt.get("args", []),
+           "flaky": tgt.flaky,
+           "cpu_cost": tgt.get("cpu_cost", 1.0)}
+    timeout_seconds = tgt.get("timeout_seconds", None)
+    if timeout_seconds:
+      out['timeout_seconds'] = timeout_seconds
+    excluded_poll_engines = tgt.get("excluded_poll_engines", None)
+    if excluded_poll_engines:
+      out['excluded_poll_engines'] = excluded_poll_engines
+    return out
   %>
 
-  ${json.dumps([{"name": tgt.name,
-                 "language": tgt.language,
-                 "platforms": tgt.platforms,
-                 "ci_platforms": tgt.ci_platforms,
-                 "gtest": tgt.gtest,
-                 "exclude_configs": tgt.get("exclude_configs", []),
-                 "exclude_iomgrs": tgt.get("exclude_iomgrs", []),
-                 "args": tgt.get("args", []),
-                 "flaky": tgt.flaky,
-                 "cpu_cost": tgt.get("cpu_cost", 1.0)}
+  ${json.dumps([gen_one_target(tgt)
                 for tgt in targets
                 if tgt.get('run', True) and tgt.build == 'test'] +
                 tests,
diff --git a/test/core/end2end/end2end_nosec_tests.c b/test/core/end2end/end2end_nosec_tests.c
index fdfa31b..b351bde 100644
--- a/test/core/end2end/end2end_nosec_tests.c
+++ b/test/core/end2end/end2end_nosec_tests.c
@@ -89,6 +89,8 @@
 extern void idempotent_request_pre_init(void);
 extern void invoke_large_request(grpc_end2end_test_config config);
 extern void invoke_large_request_pre_init(void);
+extern void keepalive_timeout(grpc_end2end_test_config config);
+extern void keepalive_timeout_pre_init(void);
 extern void large_metadata(grpc_end2end_test_config config);
 extern void large_metadata_pre_init(void);
 extern void load_reporting_hook(grpc_end2end_test_config config);
@@ -168,6 +170,7 @@
   hpack_size_pre_init();
   idempotent_request_pre_init();
   invoke_large_request_pre_init();
+  keepalive_timeout_pre_init();
   large_metadata_pre_init();
   load_reporting_hook_pre_init();
   max_concurrent_streams_pre_init();
@@ -225,6 +228,7 @@
     hpack_size(config);
     idempotent_request(config);
     invoke_large_request(config);
+    keepalive_timeout(config);
     large_metadata(config);
     load_reporting_hook(config);
     max_concurrent_streams(config);
@@ -343,6 +347,10 @@
       invoke_large_request(config);
       continue;
     }
+    if (0 == strcmp("keepalive_timeout", argv[i])) {
+      keepalive_timeout(config);
+      continue;
+    }
     if (0 == strcmp("large_metadata", argv[i])) {
       large_metadata(config);
       continue;
diff --git a/test/core/end2end/end2end_tests.c b/test/core/end2end/end2end_tests.c
index f529d6a..199c09e 100644
--- a/test/core/end2end/end2end_tests.c
+++ b/test/core/end2end/end2end_tests.c
@@ -91,6 +91,8 @@
 extern void idempotent_request_pre_init(void);
 extern void invoke_large_request(grpc_end2end_test_config config);
 extern void invoke_large_request_pre_init(void);
+extern void keepalive_timeout(grpc_end2end_test_config config);
+extern void keepalive_timeout_pre_init(void);
 extern void large_metadata(grpc_end2end_test_config config);
 extern void large_metadata_pre_init(void);
 extern void load_reporting_hook(grpc_end2end_test_config config);
@@ -171,6 +173,7 @@
   hpack_size_pre_init();
   idempotent_request_pre_init();
   invoke_large_request_pre_init();
+  keepalive_timeout_pre_init();
   large_metadata_pre_init();
   load_reporting_hook_pre_init();
   max_concurrent_streams_pre_init();
@@ -229,6 +232,7 @@
     hpack_size(config);
     idempotent_request(config);
     invoke_large_request(config);
+    keepalive_timeout(config);
     large_metadata(config);
     load_reporting_hook(config);
     max_concurrent_streams(config);
@@ -351,6 +355,10 @@
       invoke_large_request(config);
       continue;
     }
+    if (0 == strcmp("keepalive_timeout", argv[i])) {
+      keepalive_timeout(config);
+      continue;
+    }
     if (0 == strcmp("large_metadata", argv[i])) {
       large_metadata(config);
       continue;
diff --git a/test/core/end2end/gen_build_yaml.py b/test/core/end2end/gen_build_yaml.py
index 5071299..0c74953 100755
--- a/test/core/end2end/gen_build_yaml.py
+++ b/test/core/end2end/gen_build_yaml.py
@@ -119,6 +119,7 @@
     'high_initial_seqno': default_test_options,
     'idempotent_request': default_test_options,
     'invoke_large_request': default_test_options,
+    'keepalive_timeout': default_test_options._replace(proxyable=False),
     'large_metadata': default_test_options,
     'max_concurrent_streams': default_test_options._replace(proxyable=False),
     'max_message_length': default_test_options,
diff --git a/test/core/end2end/generate_tests.bzl b/test/core/end2end/generate_tests.bzl
index 95c06de..431c699 100755
--- a/test/core/end2end/generate_tests.bzl
+++ b/test/core/end2end/generate_tests.bzl
@@ -106,6 +106,7 @@
     'high_initial_seqno': test_options(),
     'idempotent_request': test_options(),
     'invoke_large_request': test_options(),
+    'keepalive_timeout': test_options(proxyable=False),
     'large_metadata': test_options(),
     'max_concurrent_streams': test_options(proxyable=False),
     'max_message_length': test_options(),
diff --git a/test/core/end2end/goaway_server_test.c b/test/core/end2end/goaway_server_test.c
index c67c1f1..a9634bf 100644
--- a/test/core/end2end/goaway_server_test.c
+++ b/test/core/end2end/goaway_server_test.c
@@ -31,6 +31,12 @@
  *
  */
 
+/* With the addition of a libuv endpoint, sockaddr.h now includes uv.h when
+   using that endpoint. Because of various transitive includes in uv.h,
+   including windows.h on Windows, uv.h must be included before other system
+   headers. Therefore, sockaddr.h must always be included first */
+#include "src/core/lib/iomgr/sockaddr.h"
+
 #include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
diff --git a/test/core/end2end/tests/keepalive_timeout.c b/test/core/end2end/tests/keepalive_timeout.c
new file mode 100644
index 0000000..4296be3
--- /dev/null
+++ b/test/core/end2end/tests/keepalive_timeout.c
@@ -0,0 +1,240 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/support/env.h"
+#include "test/core/end2end/cq_verifier.h"
+
+static void *tag(intptr_t t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char *test_name,
+                                            grpc_channel_args *client_args,
+                                            grpc_channel_args *server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_time(void) { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_time(), NULL);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->cq, tag(1000));
+  GPR_ASSERT(
+      grpc_completion_queue_pluck(f->cq, tag(1000), five_seconds_time(), NULL)
+          .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+}
+
+/* Client sends a request, server replies with a payload, then waits for the
+   keepalive watchdog timeouts before returning status. */
+static void test_keepalive_timeout(grpc_end2end_test_config config) {
+  grpc_call *c;
+  grpc_call *s;
+  grpc_slice response_payload_slice =
+      grpc_slice_from_copied_string("hello world");
+  grpc_byte_buffer *response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  gpr_timespec deadline = five_seconds_time();
+
+  grpc_arg keepalive_args[2];
+  keepalive_args[0].type = GRPC_ARG_INTEGER;
+  keepalive_args[0].key = GRPC_ARG_HTTP2_KEEPALIVE_TIME;
+  keepalive_args[0].value.integer = 2;
+  keepalive_args[1].type = GRPC_ARG_INTEGER;
+  keepalive_args[1].key = GRPC_ARG_HTTP2_KEEPALIVE_TIMEOUT;
+  keepalive_args[1].value.integer = 0;
+
+  grpc_channel_args *client_args = NULL;
+  client_args = grpc_channel_args_copy_and_add(client_args, keepalive_args, 2);
+
+  grpc_end2end_test_fixture f =
+      begin_test(config, "keepalive_timeout", client_args, NULL);
+  cq_verifier *cqv = cq_verifier_create(f.cq);
+  grpc_op ops[6];
+  grpc_op *op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_byte_buffer *response_payload_recv = NULL;
+  grpc_call_details call_details;
+  grpc_status_code status;
+  grpc_call_error error;
+  grpc_slice details;
+
+  c = grpc_channel_create_call(
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
+      NULL);
+  GPR_ASSERT(c);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &response_payload_recv;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(
+                                 f.server, &s, &call_details,
+                                 &request_metadata_recv, f.cq, f.cq, tag(101)));
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
+  cq_verify(cqv);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = response_payload;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  cq_verify(cqv);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
+  cq_verify(cqv);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(3), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(3), 1);
+  cq_verify(cqv);
+
+  char *details_str = grpc_slice_to_c_string(details);
+  char *method_str = grpc_slice_to_c_string(call_details.method);
+  GPR_ASSERT(status == GRPC_STATUS_UNAVAILABLE);
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "keepalive watchdog timeout"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
+  validate_host_override_string("foo.test.google.fr:1234", call_details.host,
+                                config);
+
+  gpr_free(details_str);
+  gpr_free(method_str);
+
+  grpc_slice_unref(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+
+  grpc_call_destroy(c);
+  grpc_call_destroy(s);
+
+  cq_verifier_destroy(cqv);
+
+  grpc_byte_buffer_destroy(response_payload);
+  grpc_byte_buffer_destroy(response_payload_recv);
+
+  if (client_args != NULL) {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_channel_args_destroy(&exec_ctx, client_args);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void keepalive_timeout(grpc_end2end_test_config config) {
+  test_keepalive_timeout(config);
+}
+
+void keepalive_timeout_pre_init(void) {}
diff --git a/test/core/handshake/client_ssl.c b/test/core/handshake/client_ssl.c
index 1a06fd6..5cfe60d 100644
--- a/test/core/handshake/client_ssl.c
+++ b/test/core/handshake/client_ssl.c
@@ -31,6 +31,11 @@
  *
  */
 
+#include "src/core/lib/iomgr/port.h"
+
+// This test won't work except with posix sockets enabled
+#ifdef GRPC_POSIX_SOCKET
+
 #include <arpa/inet.h>
 #include <openssl/err.h>
 #include <openssl/ssl.h>
@@ -324,3 +329,9 @@
   GPR_ASSERT(!client_ssl_test("foo"));
   return 0;
 }
+
+#else /* GRPC_POSIX_SOCKET */
+
+int main(int argc, char **argv) { return 1; }
+
+#endif /* GRPC_POSIX_SOCKET */
diff --git a/test/core/iomgr/resolve_address_test.c b/test/core/iomgr/resolve_address_test.c
index a695010..ea79adc 100644
--- a/test/core/iomgr/resolve_address_test.c
+++ b/test/core/iomgr/resolve_address_test.c
@@ -35,7 +35,6 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/sync.h>
-#include <grpc/support/thd.h>
 #include <grpc/support/time.h>
 #include "src/core/lib/iomgr/executor.h"
 #include "src/core/lib/iomgr/iomgr.h"
@@ -63,6 +62,7 @@
   args->pollset_set = grpc_pollset_set_create();
   grpc_pollset_set_add_pollset(exec_ctx, args->pollset_set, args->pollset);
   args->addrs = NULL;
+  gpr_atm_rel_store(&args->done_atm, 0);
 }
 
 void args_finish(grpc_exec_ctx *exec_ctx, args_struct *args) {
@@ -85,8 +85,7 @@
                       gpr_time_from_seconds(seconds, GPR_TIMESPAN));
 }
 
-static void actually_poll(void *argsp) {
-  args_struct *args = argsp;
+static void poll_pollset_until_request_done(args_struct *args) {
   gpr_timespec deadline = n_sec_deadline(10);
   while (true) {
     bool done = gpr_atm_acq_load(&args->done_atm) != 0;
@@ -111,12 +110,6 @@
   gpr_event_set(&args->ev, (void *)1);
 }
 
-static void poll_pollset_until_request_done(args_struct *args) {
-  gpr_atm_rel_store(&args->done_atm, 0);
-  gpr_thd_id id;
-  gpr_thd_new(&id, actually_poll, args, NULL);
-}
-
 static void must_succeed(grpc_exec_ctx *exec_ctx, void *argsp,
                          grpc_error *err) {
   args_struct *args = argsp;
@@ -124,23 +117,30 @@
   GPR_ASSERT(args->addrs != NULL);
   GPR_ASSERT(args->addrs->naddrs > 0);
   gpr_atm_rel_store(&args->done_atm, 1);
+  gpr_mu_lock(args->mu);
+  GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(args->pollset, NULL));
+  gpr_mu_unlock(args->mu);
 }
 
 static void must_fail(grpc_exec_ctx *exec_ctx, void *argsp, grpc_error *err) {
   args_struct *args = argsp;
   GPR_ASSERT(err != GRPC_ERROR_NONE);
   gpr_atm_rel_store(&args->done_atm, 1);
+  gpr_mu_lock(args->mu);
+  GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(args->pollset, NULL));
+  gpr_mu_unlock(args->mu);
 }
 
 static void test_localhost(void) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   args_struct args;
   args_init(&exec_ctx, &args);
-  poll_pollset_until_request_done(&args);
   grpc_resolve_address(
       &exec_ctx, "localhost:1", NULL, args.pollset_set,
       grpc_closure_create(must_succeed, &args, grpc_schedule_on_exec_ctx),
       &args.addrs);
+  grpc_exec_ctx_flush(&exec_ctx);
+  poll_pollset_until_request_done(&args);
   args_finish(&exec_ctx, &args);
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -149,11 +149,26 @@
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   args_struct args;
   args_init(&exec_ctx, &args);
-  poll_pollset_until_request_done(&args);
   grpc_resolve_address(
       &exec_ctx, "localhost", "1", args.pollset_set,
       grpc_closure_create(must_succeed, &args, grpc_schedule_on_exec_ctx),
       &args.addrs);
+  grpc_exec_ctx_flush(&exec_ctx);
+  poll_pollset_until_request_done(&args);
+  args_finish(&exec_ctx, &args);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+static void test_non_numeric_default_port(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  args_struct args;
+  args_init(&exec_ctx, &args);
+  grpc_resolve_address(
+      &exec_ctx, "localhost", "https", args.pollset_set,
+      grpc_closure_create(must_succeed, &args, grpc_schedule_on_exec_ctx),
+      &args.addrs);
+  grpc_exec_ctx_flush(&exec_ctx);
+  poll_pollset_until_request_done(&args);
   args_finish(&exec_ctx, &args);
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -162,11 +177,12 @@
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   args_struct args;
   args_init(&exec_ctx, &args);
-  poll_pollset_until_request_done(&args);
   grpc_resolve_address(
       &exec_ctx, "localhost", NULL, args.pollset_set,
       grpc_closure_create(must_fail, &args, grpc_schedule_on_exec_ctx),
       &args.addrs);
+  grpc_exec_ctx_flush(&exec_ctx);
+  poll_pollset_until_request_done(&args);
   args_finish(&exec_ctx, &args);
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -175,11 +191,12 @@
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   args_struct args;
   args_init(&exec_ctx, &args);
-  poll_pollset_until_request_done(&args);
   grpc_resolve_address(
       &exec_ctx, "[2001:db8::1]:1", NULL, args.pollset_set,
       grpc_closure_create(must_succeed, &args, grpc_schedule_on_exec_ctx),
       &args.addrs);
+  grpc_exec_ctx_flush(&exec_ctx);
+  poll_pollset_until_request_done(&args);
   args_finish(&exec_ctx, &args);
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -193,11 +210,12 @@
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
     args_struct args;
     args_init(&exec_ctx, &args);
-    poll_pollset_until_request_done(&args);
     grpc_resolve_address(
         &exec_ctx, kCases[i], "80", args.pollset_set,
         grpc_closure_create(must_succeed, &args, grpc_schedule_on_exec_ctx),
         &args.addrs);
+    grpc_exec_ctx_flush(&exec_ctx);
+    poll_pollset_until_request_done(&args);
     args_finish(&exec_ctx, &args);
     grpc_exec_ctx_finish(&exec_ctx);
   }
@@ -212,11 +230,12 @@
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
     args_struct args;
     args_init(&exec_ctx, &args);
-    poll_pollset_until_request_done(&args);
     grpc_resolve_address(
         &exec_ctx, kCases[i], NULL, args.pollset_set,
         grpc_closure_create(must_fail, &args, grpc_schedule_on_exec_ctx),
         &args.addrs);
+    grpc_exec_ctx_flush(&exec_ctx);
+    poll_pollset_until_request_done(&args);
     args_finish(&exec_ctx, &args);
     grpc_exec_ctx_finish(&exec_ctx);
   }
@@ -231,11 +250,12 @@
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
     args_struct args;
     args_init(&exec_ctx, &args);
-    poll_pollset_until_request_done(&args);
     grpc_resolve_address(
         &exec_ctx, kCases[i], "1", args.pollset_set,
         grpc_closure_create(must_fail, &args, grpc_schedule_on_exec_ctx),
         &args.addrs);
+    grpc_exec_ctx_flush(&exec_ctx);
+    poll_pollset_until_request_done(&args);
     args_finish(&exec_ctx, &args);
     grpc_exec_ctx_finish(&exec_ctx);
   }
@@ -247,6 +267,7 @@
   grpc_iomgr_init();
   test_localhost();
   test_default_port();
+  test_non_numeric_default_port();
   test_missing_default_port();
   test_ipv6_with_port();
   test_ipv6_without_port();
diff --git a/test/core/iomgr/tcp_client_posix_test.c b/test/core/iomgr/tcp_client_posix_test.c
index 21bb290..b324b5a 100644
--- a/test/core/iomgr/tcp_client_posix_test.c
+++ b/test/core/iomgr/tcp_client_posix_test.c
@@ -31,6 +31,11 @@
  *
  */
 
+#include "src/core/lib/iomgr/port.h"
+
+// This test won't work except with posix sockets enabled
+#ifdef GRPC_POSIX_SOCKET
+
 #include "src/core/lib/iomgr/tcp_client.h"
 
 #include <errno.h>
@@ -216,3 +221,9 @@
   gpr_free(g_pollset);
   return 0;
 }
+
+#else /* GRPC_POSIX_SOCKET */
+
+int main(int argc, char **argv) { return 1; }
+
+#endif /* GRPC_POSIX_SOCKET */
diff --git a/test/core/iomgr/tcp_client_uv_test.c b/test/core/iomgr/tcp_client_uv_test.c
new file mode 100644
index 0000000..f8938d0
--- /dev/null
+++ b/test/core/iomgr/tcp_client_uv_test.c
@@ -0,0 +1,222 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "src/core/lib/iomgr/port.h"
+
+// This test won't work except with libuv
+#ifdef GRPC_UV
+
+#include <uv.h>
+
+#include <string.h>
+
+#include "src/core/lib/iomgr/tcp_client.h"
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/iomgr/iomgr.h"
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "test/core/util/test_config.h"
+
+static gpr_mu *g_mu;
+static grpc_pollset *g_pollset;
+static int g_connections_complete = 0;
+static grpc_endpoint *g_connecting = NULL;
+
+static gpr_timespec test_deadline(void) {
+  return GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10);
+}
+
+static void finish_connection() {
+  gpr_mu_lock(g_mu);
+  g_connections_complete++;
+  GPR_ASSERT(
+      GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, NULL)));
+  gpr_mu_unlock(g_mu);
+}
+
+static void must_succeed(grpc_exec_ctx *exec_ctx, void *arg,
+                         grpc_error *error) {
+  GPR_ASSERT(g_connecting != NULL);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
+  grpc_endpoint_shutdown(exec_ctx, g_connecting);
+  grpc_endpoint_destroy(exec_ctx, g_connecting);
+  g_connecting = NULL;
+  finish_connection();
+}
+
+static void must_fail(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
+  GPR_ASSERT(g_connecting == NULL);
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
+  finish_connection();
+}
+
+static void close_cb(uv_handle_t *handle) { gpr_free(handle); }
+
+static void connection_cb(uv_stream_t *server, int status) {
+  uv_tcp_t *client_handle = gpr_malloc(sizeof(uv_tcp_t));
+  GPR_ASSERT(0 == status);
+  GPR_ASSERT(0 == uv_tcp_init(uv_default_loop(), client_handle));
+  GPR_ASSERT(0 == uv_accept(server, (uv_stream_t *)client_handle));
+  uv_close((uv_handle_t *)client_handle, close_cb);
+}
+
+void test_succeeds(void) {
+  grpc_resolved_address resolved_addr;
+  struct sockaddr_in *addr = (struct sockaddr_in *)resolved_addr.addr;
+  uv_tcp_t *svr_handle = gpr_malloc(sizeof(uv_tcp_t));
+  int connections_complete_before;
+  grpc_closure done;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+
+  gpr_log(GPR_DEBUG, "test_succeeds");
+
+  memset(&resolved_addr, 0, sizeof(resolved_addr));
+  resolved_addr.len = sizeof(struct sockaddr_in);
+  addr->sin_family = AF_INET;
+
+  /* create a dummy server */
+  GPR_ASSERT(0 == uv_tcp_init(uv_default_loop(), svr_handle));
+  GPR_ASSERT(0 == uv_tcp_bind(svr_handle, (struct sockaddr *)addr, 0));
+  GPR_ASSERT(0 == uv_listen((uv_stream_t *)svr_handle, 1, connection_cb));
+
+  gpr_mu_lock(g_mu);
+  connections_complete_before = g_connections_complete;
+  gpr_mu_unlock(g_mu);
+
+  /* connect to it */
+  GPR_ASSERT(uv_tcp_getsockname(svr_handle, (struct sockaddr *)addr,
+                                (int *)&resolved_addr.len) == 0);
+  grpc_closure_init(&done, must_succeed, NULL, grpc_schedule_on_exec_ctx);
+  grpc_tcp_client_connect(&exec_ctx, &done, &g_connecting, NULL, NULL,
+                          &resolved_addr, gpr_inf_future(GPR_CLOCK_REALTIME));
+
+  gpr_mu_lock(g_mu);
+
+  while (g_connections_complete == connections_complete_before) {
+    grpc_pollset_worker *worker = NULL;
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC),
+                          GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5))));
+    gpr_mu_unlock(g_mu);
+    grpc_exec_ctx_flush(&exec_ctx);
+    gpr_mu_lock(g_mu);
+  }
+
+  // This will get cleaned up when the pollset runs again or gets shutdown
+  uv_close((uv_handle_t *)svr_handle, close_cb);
+
+  gpr_mu_unlock(g_mu);
+
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+void test_fails(void) {
+  grpc_resolved_address resolved_addr;
+  struct sockaddr_in *addr = (struct sockaddr_in *)resolved_addr.addr;
+  int connections_complete_before;
+  grpc_closure done;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+
+  gpr_log(GPR_DEBUG, "test_fails");
+
+  memset(&resolved_addr, 0, sizeof(resolved_addr));
+  resolved_addr.len = sizeof(struct sockaddr_in);
+  addr->sin_family = AF_INET;
+
+  gpr_mu_lock(g_mu);
+  connections_complete_before = g_connections_complete;
+  gpr_mu_unlock(g_mu);
+
+  /* connect to a broken address */
+  grpc_closure_init(&done, must_fail, NULL, grpc_schedule_on_exec_ctx);
+  grpc_tcp_client_connect(&exec_ctx, &done, &g_connecting, NULL, NULL,
+                          &resolved_addr, gpr_inf_future(GPR_CLOCK_REALTIME));
+
+  gpr_mu_lock(g_mu);
+
+  /* wait for the connection callback to finish */
+  while (g_connections_complete == connections_complete_before) {
+    grpc_pollset_worker *worker = NULL;
+    gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
+    gpr_timespec polling_deadline = test_deadline();
+    if (!grpc_timer_check(&exec_ctx, now, &polling_deadline)) {
+      GPR_ASSERT(GRPC_LOG_IF_ERROR(
+          "pollset_work", grpc_pollset_work(&exec_ctx, g_pollset, &worker, now,
+                                            polling_deadline)));
+    }
+    gpr_mu_unlock(g_mu);
+    grpc_exec_ctx_flush(&exec_ctx);
+    gpr_mu_lock(g_mu);
+  }
+
+  gpr_mu_unlock(g_mu);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p,
+                            grpc_error *error) {
+  grpc_pollset_destroy(p);
+}
+
+int main(int argc, char **argv) {
+  grpc_closure destroyed;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_test_init(argc, argv);
+  grpc_init();
+  g_pollset = gpr_malloc(grpc_pollset_size());
+  grpc_pollset_init(g_pollset, &g_mu);
+  grpc_exec_ctx_finish(&exec_ctx);
+  test_succeeds();
+  gpr_log(GPR_ERROR, "End of first test");
+  test_fails();
+  grpc_closure_init(&destroyed, destroy_pollset, g_pollset,
+                    grpc_schedule_on_exec_ctx);
+  grpc_pollset_shutdown(&exec_ctx, g_pollset, &destroyed);
+  grpc_exec_ctx_finish(&exec_ctx);
+  grpc_shutdown();
+  gpr_free(g_pollset);
+  return 0;
+}
+
+#else /* GRPC_UV */
+
+int main(int argc, char **argv) { return 1; }
+
+#endif /* GRPC_UV */
diff --git a/test/core/iomgr/tcp_server_uv_test.c b/test/core/iomgr/tcp_server_uv_test.c
new file mode 100644
index 0000000..7b458c9
--- /dev/null
+++ b/test/core/iomgr/tcp_server_uv_test.c
@@ -0,0 +1,339 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "src/core/lib/iomgr/port.h"
+
+// This test won't work except with libuv
+#ifdef GRPC_UV
+
+#include <uv.h>
+
+#include "src/core/lib/iomgr/tcp_server.h"
+
+#include <string.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/iomgr/iomgr.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST(x) gpr_log(GPR_INFO, "%s", #x)
+
+static gpr_mu *g_mu;
+static grpc_pollset *g_pollset;
+static int g_nconnects = 0;
+
+typedef struct on_connect_result {
+  /* Owns a ref to server. */
+  grpc_tcp_server *server;
+  unsigned port_index;
+  unsigned fd_index;
+} on_connect_result;
+
+typedef struct server_weak_ref {
+  grpc_tcp_server *server;
+
+  /* arg is this server_weak_ref. */
+  grpc_closure server_shutdown;
+} server_weak_ref;
+
+static on_connect_result g_result = {NULL, 0, 0};
+
+static void on_connect_result_init(on_connect_result *result) {
+  result->server = NULL;
+  result->port_index = 0;
+  result->fd_index = 0;
+}
+
+static void on_connect_result_set(on_connect_result *result,
+                                  const grpc_tcp_server_acceptor *acceptor) {
+  result->server = grpc_tcp_server_ref(acceptor->from_server);
+  result->port_index = acceptor->port_index;
+  result->fd_index = acceptor->fd_index;
+}
+
+static void server_weak_ref_shutdown(grpc_exec_ctx *exec_ctx, void *arg,
+                                     grpc_error *error) {
+  server_weak_ref *weak_ref = arg;
+  weak_ref->server = NULL;
+}
+
+static void server_weak_ref_init(server_weak_ref *weak_ref) {
+  weak_ref->server = NULL;
+  grpc_closure_init(&weak_ref->server_shutdown, server_weak_ref_shutdown,
+                    weak_ref, grpc_schedule_on_exec_ctx);
+}
+
+/* Make weak_ref->server_shutdown a shutdown_starting cb on server.
+   grpc_tcp_server promises that the server object will live until
+   weak_ref->server_shutdown has returned. A strong ref on grpc_tcp_server
+   should be held until server_weak_ref_set() returns to avoid a race where the
+   server is deleted before the shutdown_starting cb is added. */
+static void server_weak_ref_set(server_weak_ref *weak_ref,
+                                grpc_tcp_server *server) {
+  grpc_tcp_server_shutdown_starting_add(server, &weak_ref->server_shutdown);
+  weak_ref->server = server;
+}
+
+static void on_connect(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *tcp,
+                       grpc_pollset *pollset,
+                       grpc_tcp_server_acceptor *acceptor) {
+  grpc_endpoint_shutdown(exec_ctx, tcp);
+  grpc_endpoint_destroy(exec_ctx, tcp);
+
+  on_connect_result temp_result;
+  on_connect_result_set(&temp_result, acceptor);
+  gpr_free(acceptor);
+
+  gpr_mu_lock(g_mu);
+  g_result = temp_result;
+  g_nconnects++;
+  GPR_ASSERT(
+      GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, NULL)));
+  gpr_mu_unlock(g_mu);
+}
+
+static void test_no_op(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_tcp_server *s;
+  GPR_ASSERT(GRPC_ERROR_NONE ==
+             grpc_tcp_server_create(&exec_ctx, NULL, NULL, &s));
+  grpc_tcp_server_unref(&exec_ctx, s);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+static void test_no_op_with_start(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_tcp_server *s;
+  GPR_ASSERT(GRPC_ERROR_NONE ==
+             grpc_tcp_server_create(&exec_ctx, NULL, NULL, &s));
+  LOG_TEST("test_no_op_with_start");
+  grpc_tcp_server_start(&exec_ctx, s, NULL, 0, on_connect, NULL);
+  grpc_tcp_server_unref(&exec_ctx, s);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+static void test_no_op_with_port(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_resolved_address resolved_addr;
+  struct sockaddr_in *addr = (struct sockaddr_in *)resolved_addr.addr;
+  grpc_tcp_server *s;
+  GPR_ASSERT(GRPC_ERROR_NONE ==
+             grpc_tcp_server_create(&exec_ctx, NULL, NULL, &s));
+  LOG_TEST("test_no_op_with_port");
+
+  memset(&resolved_addr, 0, sizeof(resolved_addr));
+  resolved_addr.len = sizeof(struct sockaddr_in);
+  addr->sin_family = AF_INET;
+  int port;
+  GPR_ASSERT(grpc_tcp_server_add_port(s, &resolved_addr, &port) ==
+                 GRPC_ERROR_NONE &&
+             port > 0);
+
+  grpc_tcp_server_unref(&exec_ctx, s);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+static void test_no_op_with_port_and_start(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_resolved_address resolved_addr;
+  struct sockaddr_in *addr = (struct sockaddr_in *)resolved_addr.addr;
+  grpc_tcp_server *s;
+  GPR_ASSERT(GRPC_ERROR_NONE ==
+             grpc_tcp_server_create(&exec_ctx, NULL, NULL, &s));
+  LOG_TEST("test_no_op_with_port_and_start");
+  int port;
+
+  memset(&resolved_addr, 0, sizeof(resolved_addr));
+  resolved_addr.len = sizeof(struct sockaddr_in);
+  addr->sin_family = AF_INET;
+  GPR_ASSERT(grpc_tcp_server_add_port(s, &resolved_addr, &port) ==
+                 GRPC_ERROR_NONE &&
+             port > 0);
+
+  grpc_tcp_server_start(&exec_ctx, s, NULL, 0, on_connect, NULL);
+
+  grpc_tcp_server_unref(&exec_ctx, s);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+static void connect_cb(uv_connect_t *req, int status) {
+  GPR_ASSERT(status == 0);
+  gpr_free(req);
+}
+
+static void close_cb(uv_handle_t *handle) { gpr_free(handle); }
+
+static void tcp_connect(grpc_exec_ctx *exec_ctx, const struct sockaddr *remote,
+                        socklen_t remote_len, on_connect_result *result) {
+  gpr_timespec deadline = GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10);
+  uv_tcp_t *client_handle = gpr_malloc(sizeof(uv_tcp_t));
+  uv_connect_t *req = gpr_malloc(sizeof(uv_connect_t));
+  int nconnects_before;
+
+  gpr_mu_lock(g_mu);
+  nconnects_before = g_nconnects;
+  on_connect_result_init(&g_result);
+  GPR_ASSERT(uv_tcp_init(uv_default_loop(), client_handle) == 0);
+  gpr_log(GPR_DEBUG, "start connect");
+  GPR_ASSERT(uv_tcp_connect(req, client_handle, remote, connect_cb) == 0);
+  gpr_log(GPR_DEBUG, "wait");
+  while (g_nconnects == nconnects_before &&
+         gpr_time_cmp(deadline, gpr_now(deadline.clock_type)) > 0) {
+    grpc_pollset_worker *worker = NULL;
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC), deadline)));
+    gpr_mu_unlock(g_mu);
+    grpc_exec_ctx_finish(exec_ctx);
+    gpr_mu_lock(g_mu);
+  }
+  gpr_log(GPR_DEBUG, "wait done");
+  GPR_ASSERT(g_nconnects == nconnects_before + 1);
+  uv_close((uv_handle_t *)client_handle, close_cb);
+  *result = g_result;
+
+  gpr_mu_unlock(g_mu);
+}
+
+/* Tests a tcp server with multiple ports. */
+static void test_connect(unsigned n) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_resolved_address resolved_addr;
+  grpc_resolved_address resolved_addr1;
+  struct sockaddr_storage *addr = (struct sockaddr_storage *)resolved_addr.addr;
+  struct sockaddr_storage *addr1 =
+      (struct sockaddr_storage *)resolved_addr1.addr;
+  int svr_port;
+  int svr1_port;
+  grpc_tcp_server *s;
+  GPR_ASSERT(GRPC_ERROR_NONE ==
+             grpc_tcp_server_create(&exec_ctx, NULL, NULL, &s));
+  unsigned i;
+  server_weak_ref weak_ref;
+  server_weak_ref_init(&weak_ref);
+  LOG_TEST("test_connect");
+  gpr_log(GPR_INFO, "clients=%d", n);
+  memset(&resolved_addr, 0, sizeof(resolved_addr));
+  memset(&resolved_addr1, 0, sizeof(resolved_addr1));
+  resolved_addr.len = sizeof(struct sockaddr_storage);
+  resolved_addr1.len = sizeof(struct sockaddr_storage);
+  addr->ss_family = addr1->ss_family = AF_INET;
+  GPR_ASSERT(GRPC_ERROR_NONE ==
+             grpc_tcp_server_add_port(s, &resolved_addr, &svr_port));
+  GPR_ASSERT(svr_port > 0);
+  GPR_ASSERT(uv_ip6_addr("::", svr_port, (struct sockaddr_in6 *)addr) == 0);
+  /* Cannot use wildcard (port==0), because add_port() will try to reuse the
+     same port as a previous add_port(). */
+  svr1_port = grpc_pick_unused_port_or_die();
+  grpc_sockaddr_set_port(&resolved_addr1, svr1_port);
+  GPR_ASSERT(grpc_tcp_server_add_port(s, &resolved_addr1, &svr_port) ==
+                 GRPC_ERROR_NONE &&
+             svr_port == svr1_port);
+
+  grpc_tcp_server_start(&exec_ctx, s, &g_pollset, 1, on_connect, NULL);
+
+  GPR_ASSERT(uv_ip6_addr("::", svr_port, (struct sockaddr_in6 *)addr1) == 0);
+
+  for (i = 0; i < n; i++) {
+    on_connect_result result;
+    on_connect_result_init(&result);
+    tcp_connect(&exec_ctx, (struct sockaddr *)addr,
+                (socklen_t)resolved_addr.len, &result);
+    GPR_ASSERT(result.port_index == 0);
+    GPR_ASSERT(result.server == s);
+    if (weak_ref.server == NULL) {
+      server_weak_ref_set(&weak_ref, result.server);
+    }
+    grpc_tcp_server_unref(&exec_ctx, result.server);
+
+    on_connect_result_init(&result);
+    tcp_connect(&exec_ctx, (struct sockaddr *)addr1,
+                (socklen_t)resolved_addr1.len, &result);
+    GPR_ASSERT(result.port_index == 1);
+    GPR_ASSERT(result.server == s);
+    grpc_tcp_server_unref(&exec_ctx, result.server);
+  }
+
+  /* Weak ref to server valid until final unref. */
+  GPR_ASSERT(weak_ref.server != NULL);
+
+  grpc_tcp_server_unref(&exec_ctx, s);
+  grpc_exec_ctx_finish(&exec_ctx);
+
+  /* Weak ref lost. */
+  GPR_ASSERT(weak_ref.server == NULL);
+}
+
+static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p,
+                            grpc_error *error) {
+  grpc_pollset_destroy(p);
+}
+
+int main(int argc, char **argv) {
+  grpc_closure destroyed;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_test_init(argc, argv);
+  grpc_init();
+  g_pollset = gpr_malloc(grpc_pollset_size());
+  grpc_pollset_init(g_pollset, &g_mu);
+
+  test_no_op();
+  test_no_op_with_start();
+  test_no_op_with_port();
+  test_no_op_with_port_and_start();
+  test_connect(1);
+  test_connect(10);
+
+  grpc_closure_init(&destroyed, destroy_pollset, g_pollset,
+                    grpc_schedule_on_exec_ctx);
+  grpc_pollset_shutdown(&exec_ctx, g_pollset, &destroyed);
+  grpc_exec_ctx_finish(&exec_ctx);
+  grpc_shutdown();
+  gpr_free(g_pollset);
+  return 0;
+}
+
+#else /* GRPC_UV */
+
+int main(int argc, char **argv) { return 1; }
+
+#endif /* GRPC_UV */
diff --git a/test/core/iomgr/timer_list_test.c b/test/core/iomgr/timer_list_test.c
index 8d7ac3f..85ad527 100644
--- a/test/core/iomgr/timer_list_test.c
+++ b/test/core/iomgr/timer_list_test.c
@@ -31,6 +31,11 @@
  *
  */
 
+#include "src/core/lib/iomgr/port.h"
+
+// This test only works with the generic timer implementation
+#ifdef GRPC_TIMER_USE_GENERIC
+
 #include "src/core/lib/iomgr/timer.h"
 
 #include <string.h>
@@ -169,3 +174,9 @@
   destruction_test();
   return 0;
 }
+
+#else /* GRPC_TIMER_USE_GENERIC */
+
+int main(int argc, char **argv) { return 1; }
+
+#endif /* GRPC_TIMER_USE_GENERIC */
diff --git a/test/core/surface/completion_queue_test.c b/test/core/surface/completion_queue_test.c
index b8ea909..07f6a98 100644
--- a/test/core/surface/completion_queue_test.c
+++ b/test/core/surface/completion_queue_test.c
@@ -35,7 +35,6 @@
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
-#include <grpc/support/thd.h>
 #include <grpc/support/time.h>
 #include <grpc/support/useful.h>
 #include "src/core/lib/iomgr/iomgr.h"
@@ -194,223 +193,6 @@
   grpc_completion_queue_destroy(cc);
 }
 
-struct thread_state {
-  grpc_completion_queue *cc;
-  void *tag;
-};
-
-static void pluck_one(void *arg) {
-  struct thread_state *state = arg;
-  grpc_completion_queue_pluck(state->cc, state->tag,
-                              gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
-}
-
-static void test_too_many_plucks(void) {
-  grpc_event ev;
-  grpc_completion_queue *cc;
-  void *tags[GRPC_MAX_COMPLETION_QUEUE_PLUCKERS];
-  grpc_cq_completion completions[GPR_ARRAY_SIZE(tags)];
-  gpr_thd_id thread_ids[GPR_ARRAY_SIZE(tags)];
-  struct thread_state thread_states[GPR_ARRAY_SIZE(tags)];
-  gpr_thd_options thread_options = gpr_thd_options_default();
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  unsigned i, j;
-
-  LOG_TEST("test_too_many_plucks");
-
-  cc = grpc_completion_queue_create(NULL);
-  gpr_thd_options_set_joinable(&thread_options);
-
-  for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
-    tags[i] = create_test_tag();
-    for (j = 0; j < i; j++) {
-      GPR_ASSERT(tags[i] != tags[j]);
-    }
-    thread_states[i].cc = cc;
-    thread_states[i].tag = tags[i];
-    gpr_thd_new(thread_ids + i, pluck_one, thread_states + i, &thread_options);
-  }
-
-  /* wait until all other threads are plucking */
-  gpr_sleep_until(grpc_timeout_milliseconds_to_deadline(1000));
-
-  ev = grpc_completion_queue_pluck(cc, create_test_tag(),
-                                   gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
-  GPR_ASSERT(ev.type == GRPC_QUEUE_TIMEOUT);
-
-  for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
-    grpc_cq_begin_op(cc, tags[i]);
-    grpc_cq_end_op(&exec_ctx, cc, tags[i], GRPC_ERROR_NONE,
-                   do_nothing_end_completion, NULL, &completions[i]);
-  }
-
-  for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
-    gpr_thd_join(thread_ids[i]);
-  }
-
-  shutdown_and_destroy(cc);
-  grpc_exec_ctx_finish(&exec_ctx);
-}
-
-#define TEST_THREAD_EVENTS 10000
-
-typedef struct test_thread_options {
-  gpr_event on_started;
-  gpr_event *phase1;
-  gpr_event on_phase1_done;
-  gpr_event *phase2;
-  gpr_event on_finished;
-  size_t events_triggered;
-  int id;
-  grpc_completion_queue *cc;
-} test_thread_options;
-
-gpr_timespec ten_seconds_time(void) {
-  return grpc_timeout_seconds_to_deadline(10);
-}
-
-static void free_completion(grpc_exec_ctx *exec_ctx, void *arg,
-                            grpc_cq_completion *completion) {
-  gpr_free(completion);
-}
-
-static void producer_thread(void *arg) {
-  test_thread_options *opt = arg;
-  int i;
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-
-  gpr_log(GPR_INFO, "producer %d started", opt->id);
-  gpr_event_set(&opt->on_started, (void *)(intptr_t)1);
-  GPR_ASSERT(gpr_event_wait(opt->phase1, ten_seconds_time()));
-
-  gpr_log(GPR_INFO, "producer %d phase 1", opt->id);
-  for (i = 0; i < TEST_THREAD_EVENTS; i++) {
-    grpc_cq_begin_op(opt->cc, (void *)(intptr_t)1);
-  }
-
-  gpr_log(GPR_INFO, "producer %d phase 1 done", opt->id);
-  gpr_event_set(&opt->on_phase1_done, (void *)(intptr_t)1);
-  GPR_ASSERT(gpr_event_wait(opt->phase2, ten_seconds_time()));
-
-  gpr_log(GPR_INFO, "producer %d phase 2", opt->id);
-  for (i = 0; i < TEST_THREAD_EVENTS; i++) {
-    grpc_cq_end_op(&exec_ctx, opt->cc, (void *)(intptr_t)1, GRPC_ERROR_NONE,
-                   free_completion, NULL,
-                   gpr_malloc(sizeof(grpc_cq_completion)));
-    opt->events_triggered++;
-    grpc_exec_ctx_finish(&exec_ctx);
-  }
-
-  gpr_log(GPR_INFO, "producer %d phase 2 done", opt->id);
-  gpr_event_set(&opt->on_finished, (void *)(intptr_t)1);
-  grpc_exec_ctx_finish(&exec_ctx);
-}
-
-static void consumer_thread(void *arg) {
-  test_thread_options *opt = arg;
-  grpc_event ev;
-
-  gpr_log(GPR_INFO, "consumer %d started", opt->id);
-  gpr_event_set(&opt->on_started, (void *)(intptr_t)1);
-  GPR_ASSERT(gpr_event_wait(opt->phase1, ten_seconds_time()));
-
-  gpr_log(GPR_INFO, "consumer %d phase 1", opt->id);
-
-  gpr_log(GPR_INFO, "consumer %d phase 1 done", opt->id);
-  gpr_event_set(&opt->on_phase1_done, (void *)(intptr_t)1);
-  GPR_ASSERT(gpr_event_wait(opt->phase2, ten_seconds_time()));
-
-  gpr_log(GPR_INFO, "consumer %d phase 2", opt->id);
-  for (;;) {
-    ev = grpc_completion_queue_next(opt->cc, ten_seconds_time(), NULL);
-    switch (ev.type) {
-      case GRPC_OP_COMPLETE:
-        GPR_ASSERT(ev.success);
-        opt->events_triggered++;
-        break;
-      case GRPC_QUEUE_SHUTDOWN:
-        gpr_log(GPR_INFO, "consumer %d phase 2 done", opt->id);
-        gpr_event_set(&opt->on_finished, (void *)(intptr_t)1);
-        return;
-      case GRPC_QUEUE_TIMEOUT:
-        gpr_log(GPR_ERROR, "Invalid timeout received");
-        abort();
-    }
-  }
-}
-
-static void test_threading(size_t producers, size_t consumers) {
-  test_thread_options *options =
-      gpr_malloc((producers + consumers) * sizeof(test_thread_options));
-  gpr_event phase1 = GPR_EVENT_INIT;
-  gpr_event phase2 = GPR_EVENT_INIT;
-  grpc_completion_queue *cc = grpc_completion_queue_create(NULL);
-  size_t i;
-  size_t total_consumed = 0;
-  static int optid = 101;
-
-  gpr_log(GPR_INFO, "%s: %" PRIuPTR " producers, %" PRIuPTR " consumers",
-          "test_threading", producers, consumers);
-
-  /* start all threads: they will wait for phase1 */
-  for (i = 0; i < producers + consumers; i++) {
-    gpr_thd_id id;
-    gpr_event_init(&options[i].on_started);
-    gpr_event_init(&options[i].on_phase1_done);
-    gpr_event_init(&options[i].on_finished);
-    options[i].phase1 = &phase1;
-    options[i].phase2 = &phase2;
-    options[i].events_triggered = 0;
-    options[i].cc = cc;
-    options[i].id = optid++;
-    GPR_ASSERT(gpr_thd_new(&id,
-                           i < producers ? producer_thread : consumer_thread,
-                           options + i, NULL));
-    gpr_event_wait(&options[i].on_started, ten_seconds_time());
-  }
-
-  /* start phase1: producers will pre-declare all operations they will
-     complete */
-  gpr_log(GPR_INFO, "start phase 1");
-  gpr_event_set(&phase1, (void *)(intptr_t)1);
-
-  gpr_log(GPR_INFO, "wait phase 1");
-  for (i = 0; i < producers + consumers; i++) {
-    GPR_ASSERT(gpr_event_wait(&options[i].on_phase1_done, ten_seconds_time()));
-  }
-  gpr_log(GPR_INFO, "done phase 1");
-
-  /* start phase2: operations will complete, and consumers will consume them */
-  gpr_log(GPR_INFO, "start phase 2");
-  gpr_event_set(&phase2, (void *)(intptr_t)1);
-
-  /* in parallel, we shutdown the completion channel - all events should still
-     be consumed */
-  grpc_completion_queue_shutdown(cc);
-
-  /* join all threads */
-  gpr_log(GPR_INFO, "wait phase 2");
-  for (i = 0; i < producers + consumers; i++) {
-    GPR_ASSERT(gpr_event_wait(&options[i].on_finished, ten_seconds_time()));
-  }
-  gpr_log(GPR_INFO, "done phase 2");
-
-  /* destroy the completion channel */
-  grpc_completion_queue_destroy(cc);
-
-  /* verify that everything was produced and consumed */
-  for (i = 0; i < producers + consumers; i++) {
-    if (i < producers) {
-      GPR_ASSERT(options[i].events_triggered == TEST_THREAD_EVENTS);
-    } else {
-      total_consumed += options[i].events_triggered;
-    }
-  }
-  GPR_ASSERT(total_consumed == producers * TEST_THREAD_EVENTS);
-
-  gpr_free(options);
-}
-
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
   grpc_init();
@@ -422,11 +204,6 @@
   test_cq_end_op();
   test_pluck();
   test_pluck_after_shutdown();
-  test_too_many_plucks();
-  test_threading(1, 1);
-  test_threading(1, 10);
-  test_threading(10, 1);
-  test_threading(10, 10);
   grpc_shutdown();
   return 0;
 }
diff --git a/test/core/surface/completion_queue_threading_test.c b/test/core/surface/completion_queue_threading_test.c
new file mode 100644
index 0000000..2d55ead
--- /dev/null
+++ b/test/core/surface/completion_queue_threading_test.c
@@ -0,0 +1,290 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/surface/completion_queue.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "src/core/lib/iomgr/iomgr.h"
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST(x) gpr_log(GPR_INFO, "%s", x)
+
+static void *create_test_tag(void) {
+  static intptr_t i = 0;
+  return (void *)(++i);
+}
+
+/* helper for tests to shutdown correctly and tersely */
+static void shutdown_and_destroy(grpc_completion_queue *cc) {
+  grpc_event ev;
+  grpc_completion_queue_shutdown(cc);
+  ev = grpc_completion_queue_next(cc, gpr_inf_past(GPR_CLOCK_REALTIME), NULL);
+  GPR_ASSERT(ev.type == GRPC_QUEUE_SHUTDOWN);
+  grpc_completion_queue_destroy(cc);
+}
+
+static void do_nothing_end_completion(grpc_exec_ctx *exec_ctx, void *arg,
+                                      grpc_cq_completion *c) {}
+
+struct thread_state {
+  grpc_completion_queue *cc;
+  void *tag;
+};
+
+static void pluck_one(void *arg) {
+  struct thread_state *state = arg;
+  grpc_completion_queue_pluck(state->cc, state->tag,
+                              gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+}
+
+static void test_too_many_plucks(void) {
+  grpc_event ev;
+  grpc_completion_queue *cc;
+  void *tags[GRPC_MAX_COMPLETION_QUEUE_PLUCKERS];
+  grpc_cq_completion completions[GPR_ARRAY_SIZE(tags)];
+  gpr_thd_id thread_ids[GPR_ARRAY_SIZE(tags)];
+  struct thread_state thread_states[GPR_ARRAY_SIZE(tags)];
+  gpr_thd_options thread_options = gpr_thd_options_default();
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  unsigned i, j;
+
+  LOG_TEST("test_too_many_plucks");
+
+  cc = grpc_completion_queue_create(NULL);
+  gpr_thd_options_set_joinable(&thread_options);
+
+  for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
+    tags[i] = create_test_tag();
+    for (j = 0; j < i; j++) {
+      GPR_ASSERT(tags[i] != tags[j]);
+    }
+    thread_states[i].cc = cc;
+    thread_states[i].tag = tags[i];
+    gpr_thd_new(thread_ids + i, pluck_one, thread_states + i, &thread_options);
+  }
+
+  /* wait until all other threads are plucking */
+  gpr_sleep_until(grpc_timeout_milliseconds_to_deadline(1000));
+
+  ev = grpc_completion_queue_pluck(cc, create_test_tag(),
+                                   gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+  GPR_ASSERT(ev.type == GRPC_QUEUE_TIMEOUT);
+
+  for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
+    grpc_cq_begin_op(cc, tags[i]);
+    grpc_cq_end_op(&exec_ctx, cc, tags[i], GRPC_ERROR_NONE,
+                   do_nothing_end_completion, NULL, &completions[i]);
+  }
+
+  for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
+    gpr_thd_join(thread_ids[i]);
+  }
+
+  shutdown_and_destroy(cc);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+#define TEST_THREAD_EVENTS 10000
+
+typedef struct test_thread_options {
+  gpr_event on_started;
+  gpr_event *phase1;
+  gpr_event on_phase1_done;
+  gpr_event *phase2;
+  gpr_event on_finished;
+  size_t events_triggered;
+  int id;
+  grpc_completion_queue *cc;
+} test_thread_options;
+
+gpr_timespec ten_seconds_time(void) {
+  return grpc_timeout_seconds_to_deadline(10);
+}
+
+static void free_completion(grpc_exec_ctx *exec_ctx, void *arg,
+                            grpc_cq_completion *completion) {
+  gpr_free(completion);
+}
+
+static void producer_thread(void *arg) {
+  test_thread_options *opt = arg;
+  int i;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+
+  gpr_log(GPR_INFO, "producer %d started", opt->id);
+  gpr_event_set(&opt->on_started, (void *)(intptr_t)1);
+  GPR_ASSERT(gpr_event_wait(opt->phase1, ten_seconds_time()));
+
+  gpr_log(GPR_INFO, "producer %d phase 1", opt->id);
+  for (i = 0; i < TEST_THREAD_EVENTS; i++) {
+    grpc_cq_begin_op(opt->cc, (void *)(intptr_t)1);
+  }
+
+  gpr_log(GPR_INFO, "producer %d phase 1 done", opt->id);
+  gpr_event_set(&opt->on_phase1_done, (void *)(intptr_t)1);
+  GPR_ASSERT(gpr_event_wait(opt->phase2, ten_seconds_time()));
+
+  gpr_log(GPR_INFO, "producer %d phase 2", opt->id);
+  for (i = 0; i < TEST_THREAD_EVENTS; i++) {
+    grpc_cq_end_op(&exec_ctx, opt->cc, (void *)(intptr_t)1, GRPC_ERROR_NONE,
+                   free_completion, NULL,
+                   gpr_malloc(sizeof(grpc_cq_completion)));
+    opt->events_triggered++;
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
+
+  gpr_log(GPR_INFO, "producer %d phase 2 done", opt->id);
+  gpr_event_set(&opt->on_finished, (void *)(intptr_t)1);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+static void consumer_thread(void *arg) {
+  test_thread_options *opt = arg;
+  grpc_event ev;
+
+  gpr_log(GPR_INFO, "consumer %d started", opt->id);
+  gpr_event_set(&opt->on_started, (void *)(intptr_t)1);
+  GPR_ASSERT(gpr_event_wait(opt->phase1, ten_seconds_time()));
+
+  gpr_log(GPR_INFO, "consumer %d phase 1", opt->id);
+
+  gpr_log(GPR_INFO, "consumer %d phase 1 done", opt->id);
+  gpr_event_set(&opt->on_phase1_done, (void *)(intptr_t)1);
+  GPR_ASSERT(gpr_event_wait(opt->phase2, ten_seconds_time()));
+
+  gpr_log(GPR_INFO, "consumer %d phase 2", opt->id);
+  for (;;) {
+    ev = grpc_completion_queue_next(opt->cc, ten_seconds_time(), NULL);
+    switch (ev.type) {
+      case GRPC_OP_COMPLETE:
+        GPR_ASSERT(ev.success);
+        opt->events_triggered++;
+        break;
+      case GRPC_QUEUE_SHUTDOWN:
+        gpr_log(GPR_INFO, "consumer %d phase 2 done", opt->id);
+        gpr_event_set(&opt->on_finished, (void *)(intptr_t)1);
+        return;
+      case GRPC_QUEUE_TIMEOUT:
+        gpr_log(GPR_ERROR, "Invalid timeout received");
+        abort();
+    }
+  }
+}
+
+static void test_threading(size_t producers, size_t consumers) {
+  test_thread_options *options =
+      gpr_malloc((producers + consumers) * sizeof(test_thread_options));
+  gpr_event phase1 = GPR_EVENT_INIT;
+  gpr_event phase2 = GPR_EVENT_INIT;
+  grpc_completion_queue *cc = grpc_completion_queue_create(NULL);
+  size_t i;
+  size_t total_consumed = 0;
+  static int optid = 101;
+
+  gpr_log(GPR_INFO, "%s: %" PRIuPTR " producers, %" PRIuPTR " consumers",
+          "test_threading", producers, consumers);
+
+  /* start all threads: they will wait for phase1 */
+  for (i = 0; i < producers + consumers; i++) {
+    gpr_thd_id id;
+    gpr_event_init(&options[i].on_started);
+    gpr_event_init(&options[i].on_phase1_done);
+    gpr_event_init(&options[i].on_finished);
+    options[i].phase1 = &phase1;
+    options[i].phase2 = &phase2;
+    options[i].events_triggered = 0;
+    options[i].cc = cc;
+    options[i].id = optid++;
+    GPR_ASSERT(gpr_thd_new(&id,
+                           i < producers ? producer_thread : consumer_thread,
+                           options + i, NULL));
+    gpr_event_wait(&options[i].on_started, ten_seconds_time());
+  }
+
+  /* start phase1: producers will pre-declare all operations they will
+     complete */
+  gpr_log(GPR_INFO, "start phase 1");
+  gpr_event_set(&phase1, (void *)(intptr_t)1);
+
+  gpr_log(GPR_INFO, "wait phase 1");
+  for (i = 0; i < producers + consumers; i++) {
+    GPR_ASSERT(gpr_event_wait(&options[i].on_phase1_done, ten_seconds_time()));
+  }
+  gpr_log(GPR_INFO, "done phase 1");
+
+  /* start phase2: operations will complete, and consumers will consume them */
+  gpr_log(GPR_INFO, "start phase 2");
+  gpr_event_set(&phase2, (void *)(intptr_t)1);
+
+  /* in parallel, we shutdown the completion channel - all events should still
+     be consumed */
+  grpc_completion_queue_shutdown(cc);
+
+  /* join all threads */
+  gpr_log(GPR_INFO, "wait phase 2");
+  for (i = 0; i < producers + consumers; i++) {
+    GPR_ASSERT(gpr_event_wait(&options[i].on_finished, ten_seconds_time()));
+  }
+  gpr_log(GPR_INFO, "done phase 2");
+
+  /* destroy the completion channel */
+  grpc_completion_queue_destroy(cc);
+
+  /* verify that everything was produced and consumed */
+  for (i = 0; i < producers + consumers; i++) {
+    if (i < producers) {
+      GPR_ASSERT(options[i].events_triggered == TEST_THREAD_EVENTS);
+    } else {
+      total_consumed += options[i].events_triggered;
+    }
+  }
+  GPR_ASSERT(total_consumed == producers * TEST_THREAD_EVENTS);
+
+  gpr_free(options);
+}
+
+int main(int argc, char **argv) {
+  grpc_test_init(argc, argv);
+  grpc_init();
+  test_too_many_plucks();
+  test_threading(1, 1);
+  test_threading(1, 10);
+  test_threading(10, 1);
+  test_threading(10, 10);
+  grpc_shutdown();
+  return 0;
+}
diff --git a/test/core/util/mock_endpoint.c b/test/core/util/mock_endpoint.c
index d531ec6..b8fed7e 100644
--- a/test/core/util/mock_endpoint.c
+++ b/test/core/util/mock_endpoint.c
@@ -31,6 +31,12 @@
  *
  */
 
+/* With the addition of a libuv endpoint, sockaddr.h now includes uv.h when
+   using that endpoint. Because of various transitive includes in uv.h,
+   including windows.h on Windows, uv.h must be included before other system
+   headers. Therefore, sockaddr.h must always be included first */
+#include "src/core/lib/iomgr/sockaddr.h"
+
 #include "test/core/util/mock_endpoint.h"
 
 #include <inttypes.h>
diff --git a/test/core/util/passthru_endpoint.c b/test/core/util/passthru_endpoint.c
index 1e82c73..5f27f9a 100644
--- a/test/core/util/passthru_endpoint.c
+++ b/test/core/util/passthru_endpoint.c
@@ -31,6 +31,12 @@
  *
  */
 
+/* With the addition of a libuv endpoint, sockaddr.h now includes uv.h when
+   using that endpoint. Because of various transitive includes in uv.h,
+   including windows.h on Windows, uv.h must be included before other system
+   headers. Therefore, sockaddr.h must always be included first */
+#include "src/core/lib/iomgr/sockaddr.h"
+
 #include "test/core/util/passthru_endpoint.h"
 
 #include <inttypes.h>
diff --git a/test/core/util/test_config.c b/test/core/util/test_config.c
index 94aab27..0180d6f 100644
--- a/test/core/util/test_config.c
+++ b/test/core/util/test_config.c
@@ -215,6 +215,16 @@
 #include <stdio.h>
 #include <string.h>
 
+#define SIGNAL_NAMES_LENGTH 32
+
+static const char *const signal_names[] = {
+    NULL,      "SIGHUP",  "SIGINT",    "SIGQUIT", "SIGILL",    "SIGTRAP",
+    "SIGABRT", "SIGBUS",  "SIGFPE",    "SIGKILL", "SIGUSR1",   "SIGSEGV",
+    "SIGUSR2", "SIGPIPE", "SIGALRM",   "SIGTERM", "SIGSTKFLT", "SIGCHLD",
+    "SIGCONT", "SIGSTOP", "SIGTSTP",   "SIGTTIN", "SIGTTOU",   "SIGURG",
+    "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH",  "SIGIO",
+    "SIGPWR",  "SIGSYS"};
+
 static char g_alt_stack[GPR_MAX(MINSIGSTKSZ, 65536)];
 
 #define MAX_FRAMES 32
@@ -240,7 +250,11 @@
   int addrlen;
 
   output_string("\n\n\n*******************************\nCaught signal ");
-  output_num(signum);
+  if (signum > 0 && signum < SIGNAL_NAMES_LENGTH) {
+    output_string(signal_names[signum]);
+  } else {
+    output_num(signum);
+  }
   output_string("\n");
 
   addrlen = backtrace(addrlist, GPR_ARRAY_SIZE(addrlist));
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index 47e5c5b..df78557 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -901,8 +901,7 @@
   EchoRequest request;
   EchoResponse response;
   request.set_message(string(kMaxMessageSize_ * 2, 'a'));
-  // cancelled is not guaranteed to appear before the end of the service handler
-  request.mutable_param()->set_skip_cancelled_check(true);
+  request.mutable_param()->set_server_die(true);
 
   ClientContext context;
   Status s = stub_->Echo(&context, request, &response);
diff --git a/test/cpp/end2end/test_service_impl.cc b/test/cpp/end2end/test_service_impl.cc
index 0010477..59d36e9 100644
--- a/test/cpp/end2end/test_service_impl.cc
+++ b/test/cpp/end2end/test_service_impl.cc
@@ -88,6 +88,10 @@
 
 Status TestServiceImpl::Echo(ServerContext* context, const EchoRequest* request,
                              EchoResponse* response) {
+  if (request->has_param() && request->param().server_die()) {
+    gpr_log(GPR_ERROR, "The request should not reach application handler.");
+    GPR_ASSERT(0);
+  }
   int server_try_cancel = GetIntValueFromMetadata(
       kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL);
   if (server_try_cancel > DO_NOT_CANCEL) {
diff --git a/test/cpp/microbenchmarks/bm_chttp2_hpack.cc b/test/cpp/microbenchmarks/bm_chttp2_hpack.cc
new file mode 100644
index 0000000..5fb3f37
--- /dev/null
+++ b/test/cpp/microbenchmarks/bm_chttp2_hpack.cc
@@ -0,0 +1,443 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/* Microbenchmarks around CHTTP2 HPACK operations */
+
+#include <grpc/support/log.h>
+#include <string.h>
+#include <sstream>
+extern "C" {
+#include "src/core/ext/transport/chttp2/transport/hpack_encoder.h"
+#include "src/core/ext/transport/chttp2/transport/hpack_parser.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/transport/static_metadata.h"
+}
+#include "third_party/benchmark/include/benchmark/benchmark.h"
+
+static struct Init {
+  Init() { grpc_init(); }
+  ~Init() { grpc_shutdown(); }
+} g_init;
+
+////////////////////////////////////////////////////////////////////////////////
+// HPACK encoder
+//
+
+static void BM_HpackEncoderInitDestroy(benchmark::State &state) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_chttp2_hpack_compressor c;
+  while (state.KeepRunning()) {
+    grpc_chttp2_hpack_compressor_init(&c);
+    grpc_chttp2_hpack_compressor_destroy(&exec_ctx, &c);
+    grpc_exec_ctx_flush(&exec_ctx);
+  }
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_HpackEncoderInitDestroy);
+
+template <class Fixture>
+static void BM_HpackEncoderEncodeHeader(benchmark::State &state) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+
+  grpc_metadata_batch b;
+  grpc_metadata_batch_init(&b);
+  std::vector<grpc_mdelem> elems = Fixture::GetElems(&exec_ctx);
+  std::vector<grpc_linked_mdelem> storage(elems.size());
+  for (size_t i = 0; i < elems.size(); i++) {
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "addmd",
+        grpc_metadata_batch_add_tail(&exec_ctx, &b, &storage[i], elems[i])));
+  }
+
+  grpc_chttp2_hpack_compressor c;
+  grpc_chttp2_hpack_compressor_init(&c);
+  grpc_transport_one_way_stats stats;
+  memset(&stats, 0, sizeof(stats));
+  grpc_slice_buffer outbuf;
+  grpc_slice_buffer_init(&outbuf);
+  while (state.KeepRunning()) {
+    grpc_chttp2_encode_header(&exec_ctx, &c, (uint32_t)state.iterations(), &b,
+                              state.range(0), state.range(1), &stats, &outbuf);
+    grpc_slice_buffer_reset_and_unref_internal(&exec_ctx, &outbuf);
+    grpc_exec_ctx_flush(&exec_ctx);
+  }
+  grpc_metadata_batch_destroy(&exec_ctx, &b);
+  grpc_chttp2_hpack_compressor_destroy(&exec_ctx, &c);
+  grpc_slice_buffer_destroy_internal(&exec_ctx, &outbuf);
+  grpc_exec_ctx_finish(&exec_ctx);
+
+  std::ostringstream label;
+  label << "framing_bytes/iter:" << (static_cast<double>(stats.framing_bytes) /
+                                     static_cast<double>(state.iterations()))
+        << " header_bytes/iter:" << (static_cast<double>(stats.header_bytes) /
+                                     static_cast<double>(state.iterations()));
+  state.SetLabel(label.str());
+}
+
+namespace hpack_encoder_fixtures {
+
+class EmptyBatch {
+ public:
+  static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) {
+    return {};
+  }
+};
+
+class SingleStaticElem {
+ public:
+  static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) {
+    return {GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE};
+  }
+};
+
+class SingleInternedElem {
+ public:
+  static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) {
+    return {grpc_mdelem_from_slices(
+        exec_ctx, grpc_slice_intern(grpc_slice_from_static_string("abc")),
+        grpc_slice_intern(grpc_slice_from_static_string("def")))};
+  }
+};
+
+class SingleInternedKeyElem {
+ public:
+  static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) {
+    return {grpc_mdelem_from_slices(
+        exec_ctx, grpc_slice_intern(grpc_slice_from_static_string("abc")),
+        grpc_slice_from_static_string("def"))};
+  }
+};
+
+class SingleNonInternedElem {
+ public:
+  static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) {
+    return {grpc_mdelem_from_slices(exec_ctx,
+                                    grpc_slice_from_static_string("abc"),
+                                    grpc_slice_from_static_string("def"))};
+  }
+};
+
+class RepresentativeClientInitialMetadata {
+ public:
+  static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) {
+    return {
+        GRPC_MDELEM_SCHEME_HTTP, GRPC_MDELEM_METHOD_POST,
+        grpc_mdelem_from_slices(
+            exec_ctx, GRPC_MDSTR_PATH,
+            grpc_slice_intern(grpc_slice_from_static_string("/foo/bar"))),
+        grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_AUTHORITY,
+                                grpc_slice_intern(grpc_slice_from_static_string(
+                                    "foo.test.google.fr:1234"))),
+        GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE_COMMA_GZIP,
+        GRPC_MDELEM_TE_TRAILERS,
+        GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC,
+        grpc_mdelem_from_slices(
+            exec_ctx, GRPC_MDSTR_USER_AGENT,
+            grpc_slice_intern(grpc_slice_from_static_string(
+                "grpc-c/3.0.0-dev (linux; chttp2; green)")))};
+  }
+};
+
+class RepresentativeServerInitialMetadata {
+ public:
+  static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) {
+    return {GRPC_MDELEM_STATUS_200,
+            GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC,
+            GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE_COMMA_GZIP};
+  }
+};
+
+class RepresentativeServerTrailingMetadata {
+ public:
+  static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) {
+    return {GRPC_MDELEM_GRPC_STATUS_0};
+  }
+};
+
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, EmptyBatch)->Args({0, 16384});
+// test with eof (shouldn't affect anything)
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, EmptyBatch)->Args({1, 16384});
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleStaticElem)
+    ->Args({0, 16384});
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleInternedKeyElem)
+    ->Args({0, 16384});
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleInternedElem)
+    ->Args({0, 16384});
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleNonInternedElem)
+    ->Args({0, 16384});
+// test with a tiny frame size, to highlight continuation costs
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleNonInternedElem)
+    ->Args({0, 1});
+
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
+                   RepresentativeClientInitialMetadata)
+    ->Args({0, 16384});
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
+                   RepresentativeServerInitialMetadata)
+    ->Args({0, 16384});
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
+                   RepresentativeServerTrailingMetadata)
+    ->Args({1, 16384});
+
+}  // namespace hpack_encoder_fixtures
+
+////////////////////////////////////////////////////////////////////////////////
+// HPACK parser
+//
+
+static void BM_HpackParserInitDestroy(benchmark::State &state) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_chttp2_hpack_parser p;
+  while (state.KeepRunning()) {
+    grpc_chttp2_hpack_parser_init(&exec_ctx, &p);
+    grpc_chttp2_hpack_parser_destroy(&exec_ctx, &p);
+    grpc_exec_ctx_flush(&exec_ctx);
+  }
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_HpackParserInitDestroy);
+
+static void UnrefHeader(grpc_exec_ctx *exec_ctx, void *user_data,
+                        grpc_mdelem md) {
+  GRPC_MDELEM_UNREF(exec_ctx, md);
+}
+
+template <class Fixture>
+static void BM_HpackParserParseHeader(benchmark::State &state) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  std::vector<grpc_slice> init_slices = Fixture::GetInitSlices();
+  std::vector<grpc_slice> benchmark_slices = Fixture::GetBenchmarkSlices();
+  grpc_chttp2_hpack_parser p;
+  grpc_chttp2_hpack_parser_init(&exec_ctx, &p);
+  p.on_header = UnrefHeader;
+  p.on_header_user_data = nullptr;
+  for (auto slice : init_slices) {
+    grpc_chttp2_hpack_parser_parse(&exec_ctx, &p, slice);
+  }
+  while (state.KeepRunning()) {
+    for (auto slice : benchmark_slices) {
+      grpc_chttp2_hpack_parser_parse(&exec_ctx, &p, slice);
+    }
+    grpc_exec_ctx_flush(&exec_ctx);
+  }
+  grpc_chttp2_hpack_parser_destroy(&exec_ctx, &p);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+namespace hpack_parser_fixtures {
+
+static grpc_slice MakeSlice(std::initializer_list<uint8_t> bytes) {
+  grpc_slice s = grpc_slice_malloc(bytes.size());
+  uint8_t *p = GRPC_SLICE_START_PTR(s);
+  for (auto b : bytes) {
+    *p++ = b;
+  }
+  return s;
+}
+
+class EmptyBatch {
+ public:
+  static std::vector<grpc_slice> GetInitSlices() { return {}; }
+  static std::vector<grpc_slice> GetBenchmarkSlices() {
+    return {MakeSlice({})};
+  }
+};
+
+class IndexedSingleStaticElem {
+ public:
+  static std::vector<grpc_slice> GetInitSlices() {
+    return {MakeSlice(
+        {0x40, 0x07, ':', 's', 't', 'a', 't', 'u', 's', 0x03, '2', '0', '0'})};
+  }
+  static std::vector<grpc_slice> GetBenchmarkSlices() {
+    return {MakeSlice({0xbe})};
+  }
+};
+
+class AddIndexedSingleStaticElem {
+ public:
+  static std::vector<grpc_slice> GetInitSlices() { return {}; }
+  static std::vector<grpc_slice> GetBenchmarkSlices() {
+    return {MakeSlice(
+        {0x40, 0x07, ':', 's', 't', 'a', 't', 'u', 's', 0x03, '2', '0', '0'})};
+  }
+};
+
+class KeyIndexedSingleStaticElem {
+ public:
+  static std::vector<grpc_slice> GetInitSlices() {
+    return {MakeSlice(
+        {0x40, 0x07, ':', 's', 't', 'a', 't', 'u', 's', 0x03, '2', '0', '0'})};
+  }
+  static std::vector<grpc_slice> GetBenchmarkSlices() {
+    return {MakeSlice({0x7e, 0x03, 'd', 'e', 'f'})};
+  }
+};
+
+class IndexedSingleInternedElem {
+ public:
+  static std::vector<grpc_slice> GetInitSlices() {
+    return {MakeSlice({0x40, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})};
+  }
+  static std::vector<grpc_slice> GetBenchmarkSlices() {
+    return {MakeSlice({0xbe})};
+  }
+};
+
+class AddIndexedSingleInternedElem {
+ public:
+  static std::vector<grpc_slice> GetInitSlices() { return {}; }
+  static std::vector<grpc_slice> GetBenchmarkSlices() {
+    return {MakeSlice({0x40, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})};
+  }
+};
+
+class KeyIndexedSingleInternedElem {
+ public:
+  static std::vector<grpc_slice> GetInitSlices() {
+    return {MakeSlice({0x40, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})};
+  }
+  static std::vector<grpc_slice> GetBenchmarkSlices() {
+    return {MakeSlice({0x7e, 0x03, 'g', 'h', 'i'})};
+  }
+};
+
+class NonIndexedElem {
+ public:
+  static std::vector<grpc_slice> GetInitSlices() { return {}; }
+  static std::vector<grpc_slice> GetBenchmarkSlices() {
+    return {MakeSlice({0x00, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})};
+  }
+};
+
+class RepresentativeClientInitialMetadata {
+ public:
+  static std::vector<grpc_slice> GetInitSlices() {
+    return {grpc_slice_from_static_string(
+        // generated with:
+        // ```
+        // tools/codegen/core/gen_header_frame.py --compression inc --no_framing
+        // < test/core/bad_client/tests/simple_request.headers
+        // ```
+        "@\x05:path\x08/foo/bar"
+        "@\x07:scheme\x04http"
+        "@\x07:method\x04POST"
+        "@\x0a:authority\x09localhost"
+        "@\x0c"
+        "content-type\x10"
+        "application/grpc"
+        "@\x14grpc-accept-encoding\x15identity,deflate,gzip"
+        "@\x02te\x08trailers"
+        "@\x0auser-agent\"bad-client grpc-c/0.12.0.0 (linux)")};
+  }
+  static std::vector<grpc_slice> GetBenchmarkSlices() {
+    // generated with:
+    // ```
+    // tools/codegen/core/gen_header_frame.py --compression pre --no_framing
+    // --hex < test/core/bad_client/tests/simple_request.headers
+    // ```
+    return {MakeSlice({0xc5, 0xc4, 0xc3, 0xc2, 0xc1, 0xc0, 0xbf, 0xbe})};
+  }
+};
+
+class RepresentativeServerInitialMetadata {
+ public:
+  static std::vector<grpc_slice> GetInitSlices() {
+    return {grpc_slice_from_static_string(
+        // generated with:
+        // ```
+        // tools/codegen/core/gen_header_frame.py --compression inc --no_framing
+        // <
+        // test/cpp/microbenchmarks/representative_server_initial_metadata.headers
+        // ```
+        "@\x07:status\x03"
+        "200"
+        "@\x0c"
+        "content-type\x10"
+        "application/grpc"
+        "@\x14grpc-accept-encoding\x15identity,deflate,gzip")};
+  }
+  static std::vector<grpc_slice> GetBenchmarkSlices() {
+    // generated with:
+    // ```
+    // tools/codegen/core/gen_header_frame.py --compression pre --no_framing
+    // --hex <
+    // test/cpp/microbenchmarks/representative_server_initial_metadata.headers
+    // ```
+    return {MakeSlice({0xc0, 0xbf, 0xbe})};
+  }
+};
+
+class RepresentativeServerTrailingMetadata {
+ public:
+  static std::vector<grpc_slice> GetInitSlices() {
+    return {grpc_slice_from_static_string(
+        // generated with:
+        // ```
+        // tools/codegen/core/gen_header_frame.py --compression inc --no_framing
+        // <
+        // test/cpp/microbenchmarks/representative_server_trailing_metadata.headers
+        // ```
+        "@\x0bgrpc-status\x01"
+        "0"
+        "@\x0cgrpc-message\x00")};
+  }
+  static std::vector<grpc_slice> GetBenchmarkSlices() {
+    // generated with:
+    // ```
+    // tools/codegen/core/gen_header_frame.py --compression pre --no_framing
+    // --hex <
+    // test/cpp/microbenchmarks/representative_server_trailing_metadata.headers
+    // ```
+    return {MakeSlice({0xbf, 0xbe})};
+  }
+};
+
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, EmptyBatch);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, IndexedSingleStaticElem);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, AddIndexedSingleStaticElem);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, KeyIndexedSingleStaticElem);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, IndexedSingleInternedElem);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, AddIndexedSingleInternedElem);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, KeyIndexedSingleInternedElem);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedElem);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader,
+                   RepresentativeClientInitialMetadata);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader,
+                   RepresentativeServerInitialMetadata);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader,
+                   RepresentativeServerTrailingMetadata);
+
+}  // namespace hpack_parser_fixtures
+
+BENCHMARK_MAIN();
diff --git a/test/cpp/microbenchmarks/bm_cq.cc b/test/cpp/microbenchmarks/bm_cq.cc
index 195dcef..c017474 100644
--- a/test/cpp/microbenchmarks/bm_cq.cc
+++ b/test/cpp/microbenchmarks/bm_cq.cc
@@ -113,6 +113,32 @@
 }
 BENCHMARK(BM_Pass1Core);
 
+static void BM_Pluck1Core(benchmark::State& state) {
+  grpc_completion_queue* cq = grpc_completion_queue_create(NULL);
+  gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  while (state.KeepRunning()) {
+    grpc_cq_completion completion;
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_cq_begin_op(cq, NULL);
+    grpc_cq_end_op(&exec_ctx, cq, NULL, GRPC_ERROR_NONE,
+                   DoneWithCompletionOnStack, NULL, &completion);
+    grpc_exec_ctx_finish(&exec_ctx);
+    grpc_completion_queue_pluck(cq, NULL, deadline, NULL);
+  }
+  grpc_completion_queue_destroy(cq);
+}
+BENCHMARK(BM_Pluck1Core);
+
+static void BM_EmptyCore(benchmark::State& state) {
+  grpc_completion_queue* cq = grpc_completion_queue_create(NULL);
+  gpr_timespec deadline = gpr_inf_past(GPR_CLOCK_MONOTONIC);
+  while (state.KeepRunning()) {
+    grpc_completion_queue_next(cq, deadline, NULL);
+  }
+  grpc_completion_queue_destroy(cq);
+}
+BENCHMARK(BM_EmptyCore);
+
 }  // namespace testing
 }  // namespace grpc
 
diff --git a/test/cpp/microbenchmarks/bm_metadata.cc b/test/cpp/microbenchmarks/bm_metadata.cc
new file mode 100644
index 0000000..7f81fba
--- /dev/null
+++ b/test/cpp/microbenchmarks/bm_metadata.cc
@@ -0,0 +1,282 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* Test out various metadata handling primitives */
+
+#include <grpc/grpc.h>
+
+extern "C" {
+#include "src/core/lib/transport/metadata.h"
+#include "src/core/lib/transport/static_metadata.h"
+}
+
+#include "third_party/benchmark/include/benchmark/benchmark.h"
+
+static class InitializeStuff {
+ public:
+  InitializeStuff() { grpc_init(); }
+  ~InitializeStuff() { grpc_shutdown(); }
+} initialize_stuff;
+
+static void BM_SliceFromStatic(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(grpc_slice_from_static_string("abc"));
+  }
+}
+BENCHMARK(BM_SliceFromStatic);
+
+static void BM_SliceFromCopied(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    grpc_slice_unref(grpc_slice_from_copied_string("abc"));
+  }
+}
+BENCHMARK(BM_SliceFromCopied);
+
+static void BM_SliceIntern(benchmark::State& state) {
+  gpr_slice slice = grpc_slice_from_static_string("abc");
+  while (state.KeepRunning()) {
+    grpc_slice_unref(grpc_slice_intern(slice));
+  }
+}
+BENCHMARK(BM_SliceIntern);
+
+static void BM_SliceReIntern(benchmark::State& state) {
+  gpr_slice slice = grpc_slice_intern(grpc_slice_from_static_string("abc"));
+  while (state.KeepRunning()) {
+    grpc_slice_unref(grpc_slice_intern(slice));
+  }
+  grpc_slice_unref(slice);
+}
+BENCHMARK(BM_SliceReIntern);
+
+static void BM_SliceInternStaticMetadata(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    grpc_slice_intern(GRPC_MDSTR_GZIP);
+  }
+}
+BENCHMARK(BM_SliceInternStaticMetadata);
+
+static void BM_SliceInternEqualToStaticMetadata(benchmark::State& state) {
+  gpr_slice slice = grpc_slice_from_static_string("gzip");
+  while (state.KeepRunning()) {
+    grpc_slice_intern(slice);
+  }
+}
+BENCHMARK(BM_SliceInternEqualToStaticMetadata);
+
+static void BM_MetadataFromNonInternedSlices(benchmark::State& state) {
+  gpr_slice k = grpc_slice_from_static_string("key");
+  gpr_slice v = grpc_slice_from_static_string("value");
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  while (state.KeepRunning()) {
+    GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_create(&exec_ctx, k, v, NULL));
+  }
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_MetadataFromNonInternedSlices);
+
+static void BM_MetadataFromInternedSlices(benchmark::State& state) {
+  gpr_slice k = grpc_slice_intern(grpc_slice_from_static_string("key"));
+  gpr_slice v = grpc_slice_intern(grpc_slice_from_static_string("value"));
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  while (state.KeepRunning()) {
+    GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_create(&exec_ctx, k, v, NULL));
+  }
+  grpc_exec_ctx_finish(&exec_ctx);
+  grpc_slice_unref(k);
+  grpc_slice_unref(v);
+}
+BENCHMARK(BM_MetadataFromInternedSlices);
+
+static void BM_MetadataFromInternedSlicesAlreadyInIndex(
+    benchmark::State& state) {
+  gpr_slice k = grpc_slice_intern(grpc_slice_from_static_string("key"));
+  gpr_slice v = grpc_slice_intern(grpc_slice_from_static_string("value"));
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_mdelem seed = grpc_mdelem_create(&exec_ctx, k, v, NULL);
+  while (state.KeepRunning()) {
+    GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_create(&exec_ctx, k, v, NULL));
+  }
+  GRPC_MDELEM_UNREF(&exec_ctx, seed);
+  grpc_exec_ctx_finish(&exec_ctx);
+  grpc_slice_unref(k);
+  grpc_slice_unref(v);
+}
+BENCHMARK(BM_MetadataFromInternedSlicesAlreadyInIndex);
+
+static void BM_MetadataFromInternedKey(benchmark::State& state) {
+  gpr_slice k = grpc_slice_intern(grpc_slice_from_static_string("key"));
+  gpr_slice v = grpc_slice_from_static_string("value");
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  while (state.KeepRunning()) {
+    GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_create(&exec_ctx, k, v, NULL));
+  }
+  grpc_exec_ctx_finish(&exec_ctx);
+  grpc_slice_unref(k);
+}
+BENCHMARK(BM_MetadataFromInternedKey);
+
+static void BM_MetadataFromNonInternedSlicesWithBackingStore(
+    benchmark::State& state) {
+  gpr_slice k = grpc_slice_from_static_string("key");
+  gpr_slice v = grpc_slice_from_static_string("value");
+  char backing_store[sizeof(grpc_mdelem_data)];
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  while (state.KeepRunning()) {
+    GRPC_MDELEM_UNREF(
+        &exec_ctx,
+        grpc_mdelem_create(&exec_ctx, k, v,
+                           reinterpret_cast<grpc_mdelem_data*>(backing_store)));
+  }
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_MetadataFromNonInternedSlicesWithBackingStore);
+
+static void BM_MetadataFromInternedSlicesWithBackingStore(
+    benchmark::State& state) {
+  gpr_slice k = grpc_slice_intern(grpc_slice_from_static_string("key"));
+  gpr_slice v = grpc_slice_intern(grpc_slice_from_static_string("value"));
+  char backing_store[sizeof(grpc_mdelem_data)];
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  while (state.KeepRunning()) {
+    GRPC_MDELEM_UNREF(
+        &exec_ctx,
+        grpc_mdelem_create(&exec_ctx, k, v,
+                           reinterpret_cast<grpc_mdelem_data*>(backing_store)));
+  }
+  grpc_exec_ctx_finish(&exec_ctx);
+  grpc_slice_unref(k);
+  grpc_slice_unref(v);
+}
+BENCHMARK(BM_MetadataFromInternedSlicesWithBackingStore);
+
+static void BM_MetadataFromInternedKeyWithBackingStore(
+    benchmark::State& state) {
+  gpr_slice k = grpc_slice_intern(grpc_slice_from_static_string("key"));
+  gpr_slice v = grpc_slice_from_static_string("value");
+  char backing_store[sizeof(grpc_mdelem_data)];
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  while (state.KeepRunning()) {
+    GRPC_MDELEM_UNREF(
+        &exec_ctx,
+        grpc_mdelem_create(&exec_ctx, k, v,
+                           reinterpret_cast<grpc_mdelem_data*>(backing_store)));
+  }
+  grpc_exec_ctx_finish(&exec_ctx);
+  grpc_slice_unref(k);
+}
+BENCHMARK(BM_MetadataFromInternedKeyWithBackingStore);
+
+static void BM_MetadataFromStaticMetadataStrings(benchmark::State& state) {
+  gpr_slice k = GRPC_MDSTR_STATUS;
+  gpr_slice v = GRPC_MDSTR_200;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  while (state.KeepRunning()) {
+    GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_create(&exec_ctx, k, v, NULL));
+  }
+  grpc_exec_ctx_finish(&exec_ctx);
+  grpc_slice_unref(k);
+}
+BENCHMARK(BM_MetadataFromStaticMetadataStrings);
+
+static void BM_MetadataFromStaticMetadataStringsNotIndexed(
+    benchmark::State& state) {
+  gpr_slice k = GRPC_MDSTR_STATUS;
+  gpr_slice v = GRPC_MDSTR_GZIP;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  while (state.KeepRunning()) {
+    GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_create(&exec_ctx, k, v, NULL));
+  }
+  grpc_exec_ctx_finish(&exec_ctx);
+  grpc_slice_unref(k);
+}
+BENCHMARK(BM_MetadataFromStaticMetadataStringsNotIndexed);
+
+static void BM_MetadataRefUnrefExternal(benchmark::State& state) {
+  char backing_store[sizeof(grpc_mdelem_data)];
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_mdelem el =
+      grpc_mdelem_create(&exec_ctx, grpc_slice_from_static_string("a"),
+                         grpc_slice_from_static_string("b"),
+                         reinterpret_cast<grpc_mdelem_data*>(backing_store));
+  while (state.KeepRunning()) {
+    GRPC_MDELEM_UNREF(&exec_ctx, GRPC_MDELEM_REF(el));
+  }
+  GRPC_MDELEM_UNREF(&exec_ctx, el);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_MetadataRefUnrefExternal);
+
+static void BM_MetadataRefUnrefInterned(benchmark::State& state) {
+  char backing_store[sizeof(grpc_mdelem_data)];
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  gpr_slice k = grpc_slice_intern(grpc_slice_from_static_string("key"));
+  gpr_slice v = grpc_slice_intern(grpc_slice_from_static_string("value"));
+  grpc_mdelem el = grpc_mdelem_create(
+      &exec_ctx, k, v, reinterpret_cast<grpc_mdelem_data*>(backing_store));
+  grpc_slice_unref(k);
+  grpc_slice_unref(v);
+  while (state.KeepRunning()) {
+    GRPC_MDELEM_UNREF(&exec_ctx, GRPC_MDELEM_REF(el));
+  }
+  GRPC_MDELEM_UNREF(&exec_ctx, el);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_MetadataRefUnrefInterned);
+
+static void BM_MetadataRefUnrefAllocated(benchmark::State& state) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_mdelem el =
+      grpc_mdelem_create(&exec_ctx, grpc_slice_from_static_string("a"),
+                         grpc_slice_from_static_string("b"), NULL);
+  while (state.KeepRunning()) {
+    GRPC_MDELEM_UNREF(&exec_ctx, GRPC_MDELEM_REF(el));
+  }
+  GRPC_MDELEM_UNREF(&exec_ctx, el);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_MetadataRefUnrefAllocated);
+
+static void BM_MetadataRefUnrefStatic(benchmark::State& state) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_mdelem el =
+      grpc_mdelem_create(&exec_ctx, GRPC_MDSTR_STATUS, GRPC_MDSTR_200, NULL);
+  while (state.KeepRunning()) {
+    GRPC_MDELEM_UNREF(&exec_ctx, GRPC_MDELEM_REF(el));
+  }
+  GRPC_MDELEM_UNREF(&exec_ctx, el);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_MetadataRefUnrefStatic);
+
+BENCHMARK_MAIN();
diff --git a/test/cpp/microbenchmarks/representative_server_initial_metadata.headers b/test/cpp/microbenchmarks/representative_server_initial_metadata.headers
new file mode 100644
index 0000000..d3e6933
--- /dev/null
+++ b/test/cpp/microbenchmarks/representative_server_initial_metadata.headers
@@ -0,0 +1,4 @@
+:status: 200
+content-type: application/grpc
+grpc-accept-encoding: identity,deflate,gzip
+
diff --git a/test/cpp/microbenchmarks/representative_server_trailing_metadata.headers b/test/cpp/microbenchmarks/representative_server_trailing_metadata.headers
new file mode 100644
index 0000000..544d089
--- /dev/null
+++ b/test/cpp/microbenchmarks/representative_server_trailing_metadata.headers
@@ -0,0 +1,3 @@
+grpc-status: 0
+grpc-message:
+
diff --git a/tools/codegen/core/gen_header_frame.py b/tools/codegen/core/gen_header_frame.py
index ee47626..c92ff3c 100755
--- a/tools/codegen/core/gen_header_frame.py
+++ b/tools/codegen/core/gen_header_frame.py
@@ -37,8 +37,41 @@
 
 import json
 import sys
+import argparse
 
-set_end_stream = len(sys.argv) > 1 and sys.argv[1] == '--set_end_stream'
+def append_never_indexed(payload_line, n, count, key, value):
+  payload_line.append(0x10)
+  assert(len(key) <= 126)
+  payload_line.append(len(key))
+  payload_line.extend(ord(c) for c in key)
+  assert(len(value) <= 126)
+  payload_line.append(len(value))
+  payload_line.extend(ord(c) for c in value)
+
+def append_inc_indexed(payload_line, n, count, key, value):
+  payload_line.append(0x40)
+  assert(len(key) <= 126)
+  payload_line.append(len(key))
+  payload_line.extend(ord(c) for c in key)
+  assert(len(value) <= 126)
+  payload_line.append(len(value))
+  payload_line.extend(ord(c) for c in value)
+
+def append_pre_indexed(payload_line, n, count, key, value):
+  payload_line.append(0x80 + 61 + count - n)
+
+_COMPRESSORS = {
+  'never': append_never_indexed,
+  'inc': append_inc_indexed,
+  'pre': append_pre_indexed,
+}
+
+argp = argparse.ArgumentParser('Generate header frames')
+argp.add_argument('--set_end_stream', default=False, action='store_const', const=True)
+argp.add_argument('--no_framing', default=False, action='store_const', const=True)
+argp.add_argument('--compression', choices=sorted(_COMPRESSORS.keys()), default='never')
+argp.add_argument('--hex', default=False, action='store_const', const=True)
+args = argp.parse_args()
 
 # parse input, fill in vals
 vals = []
@@ -52,38 +85,37 @@
   vals.append((key, value))
 
 # generate frame payload binary data
-payload_bytes = [[]] # reserve space for header
+payload_bytes = []
+if not args.no_framing:
+  payload_bytes.append([]) # reserve space for header
 payload_len = 0
+n = 0
 for key, value in vals:
   payload_line = []
-  payload_line.append(0x10)
-  assert(len(key) <= 126)
-  payload_line.append(len(key))
-  payload_line.extend(ord(c) for c in key)
-  assert(len(value) <= 126)
-  payload_line.append(len(value))
-  payload_line.extend(ord(c) for c in value)
+  _COMPRESSORS[args.compression](payload_line, n, len(vals), key, value)
+  n += 1
   payload_len += len(payload_line)
   payload_bytes.append(payload_line)
 
 # fill in header
-flags = 0x04  # END_HEADERS
-if set_end_stream:
-  flags |= 0x01  # END_STREAM
-payload_bytes[0].extend([
-    (payload_len >> 16) & 0xff,
-    (payload_len >> 8) & 0xff,
-    (payload_len) & 0xff,
-    # header frame
-    0x01,
-    # flags
-    flags,
-    # stream id
-    0x00,
-    0x00,
-    0x00,
-    0x01
-])
+if not args.no_framing:
+  flags = 0x04  # END_HEADERS
+  if args.set_end_stream:
+    flags |= 0x01  # END_STREAM
+  payload_bytes[0].extend([
+      (payload_len >> 16) & 0xff,
+      (payload_len >> 8) & 0xff,
+      (payload_len) & 0xff,
+      # header frame
+      0x01,
+      # flags
+      flags,
+      # stream id
+      0x00,
+      0x00,
+      0x00,
+      0x01
+  ])
 
 hex_bytes = [ord(c) for c in "abcdefABCDEF0123456789"]
 
@@ -105,6 +137,11 @@
   return out + "\""
 
 # dump bytes
-for line in payload_bytes:
-  print esc_c(line)
-
+if args.hex:
+  all_bytes = []
+  for line in payload_bytes:
+    all_bytes.extend(line)
+  print '{%s}' % ', '.join('0x%02x' % c for c in all_bytes)
+else:
+  for line in payload_bytes:
+    print esc_c(line)
diff --git a/tools/dockerfile/grpc_artifact_python_manylinux_x64/Dockerfile b/tools/dockerfile/grpc_artifact_python_manylinux_x64/Dockerfile
index 69e624a..7109862 100644
--- a/tools/dockerfile/grpc_artifact_python_manylinux_x64/Dockerfile
+++ b/tools/dockerfile/grpc_artifact_python_manylinux_x64/Dockerfile
@@ -53,6 +53,7 @@
 RUN /opt/python/cp27-cp27mu/bin/pip install cython
 RUN /opt/python/cp34-cp34m/bin/pip install cython
 RUN /opt/python/cp35-cp35m/bin/pip install cython
+RUN /opt/python/cp36-cp36m/bin/pip install cython
 
 ####################################################
 # Install auditwheel with fix for namespace packages
diff --git a/tools/dockerfile/grpc_artifact_python_manylinux_x86/Dockerfile b/tools/dockerfile/grpc_artifact_python_manylinux_x86/Dockerfile
index 9af8007..36286bc 100644
--- a/tools/dockerfile/grpc_artifact_python_manylinux_x86/Dockerfile
+++ b/tools/dockerfile/grpc_artifact_python_manylinux_x86/Dockerfile
@@ -53,6 +53,7 @@
 RUN /opt/python/cp27-cp27mu/bin/pip install cython
 RUN /opt/python/cp34-cp34m/bin/pip install cython
 RUN /opt/python/cp35-cp35m/bin/pip install cython
+RUN /opt/python/cp36-cp36m/bin/pip install cython
 
 ####################################################
 # Install auditwheel with fix for namespace packages
diff --git a/tools/internal_ci/linux/grpc_portability_build_only.sh b/tools/internal_ci/linux/grpc_portability_build_only.sh
index 9fac5bc..ebdc0e8 100644
--- a/tools/internal_ci/linux/grpc_portability_build_only.sh
+++ b/tools/internal_ci/linux/grpc_portability_build_only.sh
@@ -35,4 +35,7 @@
 
 git submodule update --init
 
+# download docker images from dockerhub
+export DOCKERHUB_ORGANIZATION=grpctesting
+
 tools/jenkins/run_jenkins_matrix.sh -f portability linux --build_only
diff --git a/tools/profiling/microbenchmarks/bm2bq.py b/tools/profiling/microbenchmarks/bm2bq.py
index 3aa7003..280f217 100755
--- a/tools/profiling/microbenchmarks/bm2bq.py
+++ b/tools/profiling/microbenchmarks/bm2bq.py
@@ -67,7 +67,10 @@
   ('svr_transport_stalls_per_iteration', 'float'),
   ('svr_stream_stalls_per_iteration', 'float'),
   ('atm_cas_per_iteration', 'float'),
-  ('atm_add_per_iteration', 'float')
+  ('atm_add_per_iteration', 'float'),
+  ('end_of_stream', 'boolean'),
+  ('header_bytes_per_iteration', 'float'),
+  ('framing_bytes_per_iteration', 'float'),
 ]
 
 if sys.argv[1] == '--schema':
@@ -131,7 +134,15 @@
   'BM_IsolatedFilter' : {
     'tpl': ['fixture', 'client_mutator'],
     'dyn': [],
-  }
+  },
+  'BM_HpackEncoderEncodeHeader' : {
+    'tpl': ['fixture'],
+    'dyn': ['end_of_stream', 'request_size'],
+  },
+  'BM_HpackParserParseHeader' : {
+    'tpl': ['fixture'],
+    'dyn': [],
+  },
 }
 
 def numericalize(s):
diff --git a/tools/run_tests/artifacts/artifact_targets.py b/tools/run_tests/artifacts/artifact_targets.py
index aba7b8a..e0658f4 100644
--- a/tools/run_tests/artifacts/artifact_targets.py
+++ b/tools/run_tests/artifacts/artifact_targets.py
@@ -40,7 +40,8 @@
 
 
 def create_docker_jobspec(name, dockerfile_dir, shell_command, environ={},
-                   flake_retries=0, timeout_retries=0, timeout_seconds=30*60):
+                   flake_retries=0, timeout_retries=0, timeout_seconds=30*60,
+                   docker_base_image=None):
   """Creates jobspec for a task running under docker."""
   environ = environ.copy()
   environ['RUN_COMMAND'] = shell_command
@@ -51,6 +52,9 @@
   docker_env = {'DOCKERFILE_DIR': dockerfile_dir,
                 'DOCKER_RUN_SCRIPT': 'tools/run_tests/dockerize/docker_run.sh',
                 'OUTPUT_DIR': 'artifacts'}
+
+  if docker_base_image is not None:
+    docker_env['DOCKER_BASE_IMAGE'] = docker_base_image
   jobspec = jobset.JobSpec(
           cmdline=['tools/run_tests/dockerize/build_and_run_docker.sh'] + docker_args,
           environ=docker_env,
@@ -116,7 +120,8 @@
           'tools/dockerfile/grpc_artifact_python_manylinux_%s' % self.arch,
           'tools/run_tests/artifacts/build_artifact_python.sh',
           environ=environ,
-          timeout_seconds=60*60)
+          timeout_seconds=60*60,
+          docker_base_image='quay.io/pypa/manylinux1_i686' if self.arch == 'x86' else 'quay.io/pypa/manylinux1_x86_64')
     elif self.platform == 'windows':
       if 'Python27' in self.py_version or 'Python34' in self.py_version:
         environ['EXT_COMPILER'] = 'mingw32'
@@ -326,19 +331,24 @@
            PythonArtifact('linux', 'x86', 'cp27-cp27mu'),
            PythonArtifact('linux', 'x86', 'cp34-cp34m'),
            PythonArtifact('linux', 'x86', 'cp35-cp35m'),
+           PythonArtifact('linux', 'x86', 'cp36-cp36m'),
            PythonArtifact('linux', 'x64', 'cp27-cp27m'),
            PythonArtifact('linux', 'x64', 'cp27-cp27mu'),
            PythonArtifact('linux', 'x64', 'cp34-cp34m'),
            PythonArtifact('linux', 'x64', 'cp35-cp35m'),
+           PythonArtifact('linux', 'x64', 'cp36-cp36m'),
            PythonArtifact('macos', 'x64', 'python2.7'),
            PythonArtifact('macos', 'x64', 'python3.4'),
            PythonArtifact('macos', 'x64', 'python3.5'),
+           PythonArtifact('macos', 'x64', 'python3.6'),
            PythonArtifact('windows', 'x86', 'Python27_32bits'),
            PythonArtifact('windows', 'x86', 'Python34_32bits'),
            PythonArtifact('windows', 'x86', 'Python35_32bits'),
+           PythonArtifact('windows', 'x86', 'Python36_32bits'),
            PythonArtifact('windows', 'x64', 'Python27'),
            PythonArtifact('windows', 'x64', 'Python34'),
            PythonArtifact('windows', 'x64', 'Python35'),
+           PythonArtifact('windows', 'x64', 'Python36'),
            RubyArtifact('linux', 'x86'),
            RubyArtifact('linux', 'x64'),
            RubyArtifact('macos', 'x64'),
diff --git a/tools/run_tests/dockerize/build_and_run_docker.sh b/tools/run_tests/dockerize/build_and_run_docker.sh
index f52f16e..8c25c86 100755
--- a/tools/run_tests/dockerize/build_and_run_docker.sh
+++ b/tools/run_tests/dockerize/build_and_run_docker.sh
@@ -42,11 +42,18 @@
 # DOCKER_RUN_SCRIPT - Script to run under docker (relative to grpc repo root)
 # OUTPUT_DIR - Directory that will be copied from inside docker after finishing.
 # DOCKERHUB_ORGANIZATION - If set, pull a prebuilt image from given dockerhub org.
+# DOCKER_BASE_IMAGE - If set, pull the latest base image.
 # $@ - Extra args to pass to docker run
 
 # Use image name based on Dockerfile location checksum
 DOCKER_IMAGE_NAME=$(basename $DOCKERFILE_DIR)_$(sha1sum $DOCKERFILE_DIR/Dockerfile | cut -f1 -d\ )
 
+# Pull the base image to force an update
+if [ "$DOCKER_BASE_IMAGE" != "" ]
+then
+  docker pull $DOCKER_BASE_IMAGE
+fi
+
 if [ "$DOCKERHUB_ORGANIZATION" != "" ]
 then
   DOCKER_IMAGE_NAME=$DOCKERHUB_ORGANIZATION/$DOCKER_IMAGE_NAME
diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json
index b04e4bd..1172eaf 100644
--- a/tools/run_tests/generated/sources_and_headers.json
+++ b/tools/run_tests/generated/sources_and_headers.json
@@ -941,6 +941,23 @@
   {
     "deps": [
       "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "grpc_completion_queue_threading_test", 
+    "src": [
+      "test/core/surface/completion_queue_threading_test.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
       "grpc"
     ], 
     "headers": [], 
@@ -2027,6 +2044,23 @@
     "headers": [], 
     "is_filegroup": false, 
     "language": "c", 
+    "name": "tcp_client_uv_test", 
+    "src": [
+      "test/core/iomgr/tcp_client_uv_test.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
     "name": "tcp_posix_test", 
     "src": [
       "test/core/iomgr/tcp_posix_test.c"
@@ -2061,6 +2095,23 @@
     "headers": [], 
     "is_filegroup": false, 
     "language": "c", 
+    "name": "tcp_server_uv_test", 
+    "src": [
+      "test/core/iomgr/tcp_server_uv_test.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
     "name": "time_averaged_stats_test", 
     "src": [
       "test/core/iomgr/time_averaged_stats_test.c"
@@ -2338,6 +2389,28 @@
       "gpr", 
       "gpr_test_util", 
       "grpc", 
+      "grpc++", 
+      "grpc++_test_util", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "bm_chttp2_hpack", 
+    "src": [
+      "test/cpp/microbenchmarks/bm_chttp2_hpack.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "benchmark", 
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc++", 
+      "grpc++_test_util", 
       "grpc_test_util"
     ], 
     "headers": [], 
@@ -2412,6 +2485,24 @@
   }, 
   {
     "deps": [
+      "benchmark", 
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "bm_metadata", 
+    "src": [
+      "test/cpp/microbenchmarks/bm_metadata.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
       "gpr", 
       "grpc", 
       "grpc++"
@@ -5286,6 +5377,7 @@
     "deps": [
       "gpr", 
       "grpc_base", 
+      "grpc_load_reporting", 
       "grpc_transport_chttp2_client_secure", 
       "grpc_transport_cronet_client_secure"
     ], 
@@ -6766,6 +6858,7 @@
       "test/core/end2end/tests/hpack_size.c", 
       "test/core/end2end/tests/idempotent_request.c", 
       "test/core/end2end/tests/invoke_large_request.c", 
+      "test/core/end2end/tests/keepalive_timeout.c", 
       "test/core/end2end/tests/large_metadata.c", 
       "test/core/end2end/tests/load_reporting_hook.c", 
       "test/core/end2end/tests/max_concurrent_streams.c", 
@@ -6837,6 +6930,7 @@
       "test/core/end2end/tests/hpack_size.c", 
       "test/core/end2end/tests/idempotent_request.c", 
       "test/core/end2end/tests/invoke_large_request.c", 
+      "test/core/end2end/tests/keepalive_timeout.c", 
       "test/core/end2end/tests/large_metadata.c", 
       "test/core/end2end/tests/load_reporting_hook.c", 
       "test/core/end2end/tests/max_concurrent_streams.c", 
diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json
index 103a379..7a644f4a 100644
--- a/tools/run_tests/generated/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -1111,13 +1111,35 @@
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c", 
+    "name": "grpc_completion_queue_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ]
+  }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
     ], 
     "flaky": false, 
     "gtest": false, 
     "language": "c", 
-    "name": "grpc_completion_queue_test", 
+    "name": "grpc_completion_queue_threading_test", 
     "platforms": [
       "linux", 
       "mac", 
@@ -1240,7 +1262,9 @@
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
     "flaky": false, 
     "gtest": false, 
     "language": "c", 
@@ -1743,9 +1767,7 @@
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
+    "exclude_iomgrs": [], 
     "flaky": false, 
     "gtest": false, 
     "language": "c", 
@@ -2098,6 +2120,30 @@
     "ci_platforms": [
       "linux", 
       "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 0.5, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "native"
+    ], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c", 
+    "name": "tcp_client_uv_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ]
+  }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
       "posix"
     ], 
     "cpu_cost": 0.2, 
@@ -2147,6 +2193,30 @@
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "native"
+    ], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c", 
+    "name": "tcp_server_uv_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, 
@@ -2484,6 +2554,28 @@
     "flaky": false, 
     "gtest": false, 
     "language": "c++", 
+    "name": "bm_chttp2_hpack", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "--benchmark_min_time=0"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
     "name": "bm_closure", 
     "platforms": [
       "linux", 
@@ -2547,6 +2639,10 @@
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
+    "excluded_poll_engines": [
+      "poll", 
+      "poll-cv"
+    ], 
     "flaky": false, 
     "gtest": false, 
     "language": "c++", 
@@ -2555,6 +2651,29 @@
       "linux", 
       "mac", 
       "posix"
+    ], 
+    "timeout_seconds": 1200
+  }, 
+  {
+    "args": [
+      "--benchmark_min_time=0"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "bm_metadata", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
     ]
   }, 
   {
@@ -5834,6 +5953,29 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -6963,6 +7105,29 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -8069,6 +8234,28 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -9101,6 +9288,29 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fd_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -10184,6 +10394,29 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -11219,6 +11452,25 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -12221,6 +12473,29 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -13348,6 +13623,30 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -14503,6 +14802,29 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -15653,6 +15975,30 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -17765,6 +18111,30 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -18797,6 +19167,30 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -19845,6 +20239,32 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [
+      "msan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_1byte_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -20974,6 +21394,29 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -22103,6 +22546,29 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -24215,6 +24681,29 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -25321,6 +25810,29 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -26427,6 +26939,29 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -27462,6 +27997,29 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fd_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -28522,6 +29080,29 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -29538,6 +30119,25 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -30517,6 +31117,29 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -31620,6 +32243,30 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -32752,6 +33399,29 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -34790,6 +35460,30 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -35798,6 +36492,30 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -36820,6 +37538,32 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [
+      "msan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_1byte_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
@@ -37901,6 +38645,29 @@
   }, 
   {
     "args": [
+      "keepalive_timeout"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "large_metadata"
     ], 
     "ci_platforms": [
diff --git a/tools/run_tests/helper_scripts/build_node.bat b/tools/run_tests/helper_scripts/build_node.bat
index 82e8208..7a67769 100644
--- a/tools/run_tests/helper_scripts/build_node.bat
+++ b/tools/run_tests/helper_scripts/build_node.bat
@@ -38,5 +38,7 @@
   rmdir "%USERPROFILE%\.node-gyp\%%v\include\node\openssl" /S /Q
 )
 
+
+
 @rem rebuild, because it probably failed the first time
-call npm install --build-from-source
\ No newline at end of file
+call npm install --build-from-source %*
diff --git a/tools/run_tests/helper_scripts/build_node.sh b/tools/run_tests/helper_scripts/build_node.sh
index 8a928bb..df3acda 100755
--- a/tools/run_tests/helper_scripts/build_node.sh
+++ b/tools/run_tests/helper_scripts/build_node.sh
@@ -40,4 +40,11 @@
 # change to grpc repo root
 cd $(dirname $0)/../../..
 
-npm install --unsafe-perm --build-from-source
+case "$CONFIG" in
+  'dbg') config_flag='--debug' ;;
+  *) config_flag='--release' ;;
+esac
+
+uv_flag=$2
+
+npm install --unsafe-perm --build-from-source $uv_flag $config_flag
diff --git a/tools/run_tests/helper_scripts/pre_build_node.sh b/tools/run_tests/helper_scripts/pre_build_node.sh
index e63be9d..083cb2b 100755
--- a/tools/run_tests/helper_scripts/pre_build_node.sh
+++ b/tools/run_tests/helper_scripts/pre_build_node.sh
@@ -32,7 +32,7 @@
 NODE_VERSION=$1
 source ~/.nvm/nvm.sh
 
-nvm use $NODE_VERSION
+nvm install $NODE_VERSION
 set -ex
 
 export GRPC_CONFIG=${CONFIG:-opt}
diff --git a/tools/run_tests/performance/run_worker_node.sh b/tools/run_tests/performance/run_worker_node.sh
index 9a53a31..7e24b32 100755
--- a/tools/run_tests/performance/run_worker_node.sh
+++ b/tools/run_tests/performance/run_worker_node.sh
@@ -29,7 +29,7 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 source ~/.nvm/nvm.sh
-nvm use 4
+nvm use 7
 
 set -ex
 
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 ca1d6d4..3734f02 100644
--- a/tools/run_tests/python_utils/filter_pull_request_tests.py
+++ b/tools/run_tests/python_utils/filter_pull_request_tests.py
@@ -98,6 +98,7 @@
   '^test/distrib/php/': [_PHP_TEST_SUITE],
   '^test/distrib/python/': [_PYTHON_TEST_SUITE],
   '^test/distrib/ruby/': [_RUBY_TEST_SUITE],
+  '^tools/internal_ci/': [],
   '^vsprojects/': [_WINDOWS_TEST_SUITE],
   'binding\.gyp$': [_NODE_TEST_SUITE],
   'composer\.json$': [_PHP_TEST_SUITE],
diff --git a/tools/run_tests/run_microbenchmark.py b/tools/run_tests/run_microbenchmark.py
index c524776..4307906 100755
--- a/tools/run_tests/run_microbenchmark.py
+++ b/tools/run_tests/run_microbenchmark.py
@@ -199,7 +199,13 @@
                   default=sorted(collectors.keys()),
                   help='Which collectors should be run against each benchmark')
 argp.add_argument('-b', '--benchmarks',
-                  default=['bm_fullstack', 'bm_closure', 'bm_cq', 'bm_call_create', 'bm_error'],
+                  default=['bm_fullstack',
+                           'bm_closure',
+                           'bm_cq',
+                           'bm_call_create',
+                           'bm_error',
+                           'bm_chttp2_hpack',
+                           'bm_metadata'],
                   nargs='+',
                   type=str,
                   help='Which microbenchmarks should be run')
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index 2d9eb29..9e9af59 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -424,9 +424,13 @@
     _check_compiler(self.args.compiler, ['default', 'node0.12',
                                          'node4', 'node5', 'node6',
                                          'node7', 'electron1.3'])
+    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 = '4'
+      self.node_version = '7'
     else:
       if self.args.compiler.startswith('electron'):
         self.runtime = 'electron'
@@ -455,7 +459,8 @@
       build_script = 'pre_build_node'
       if self.runtime == 'electron':
         build_script += '_electron'
-      return [['tools/run_tests/helper_scripts/{}.sh'.format(build_script), self.node_version]]
+      return [['tools/run_tests/helper_scripts/{}.sh'.format(build_script),
+               self.node_version]]
 
   def make_targets(self):
     return []
@@ -465,14 +470,22 @@
 
   def build_steps(self):
     if self.platform == 'windows':
-      return [['tools\\run_tests\\helper_scripts\\build_node.bat']]
+      if self.config == 'dbg':
+        config_flag = '--debug'
+      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'
       if self.runtime == 'electron':
         build_script += '_electron'
         # building for electron requires a patch version
         self.node_version += '.0'
-      return [['tools/run_tests/helper_scripts/{}.sh'.format(build_script), self.node_version]]
+      return [['tools/run_tests/helper_scripts/{}.sh'.format(build_script),
+               self.node_version,
+               '--grpc_uv={}'.format('true' if self.use_uv else 'false')]]
 
   def post_tests_steps(self):
     return []
diff --git a/tools/run_tests/run_tests_matrix.py b/tools/run_tests/run_tests_matrix.py
index a428fb4..bc4fdab 100755
--- a/tools/run_tests/run_tests_matrix.py
+++ b/tools/run_tests/run_tests_matrix.py
@@ -81,7 +81,7 @@
   return test_job
 
 
-def _generate_jobs(languages, configs, platforms,
+def _generate_jobs(languages, configs, platforms, iomgr_platform = 'native',
                   arch=None, compiler=None,
                   labels=[], extra_args=[],
                   inner_jobs=_DEFAULT_INNER_JOBS):
@@ -89,7 +89,7 @@
   for language in languages:
     for platform in platforms:
       for config in configs:
-        name = '%s_%s_%s' % (language, platform, config)
+        name = '%s_%s_%s_%s' % (language, platform, config, iomgr_platform)
         runtests_args = ['-l', language,
                          '-c', config]
         if arch or compiler:
@@ -156,14 +156,6 @@
                               extra_args=extra_args,
                               inner_jobs=inner_jobs)
 
-  # libuv tests
-  test_jobs += _generate_jobs(languages=['c'],
-                              configs=['dbg', 'opt'],
-                              platforms=['linux'],
-                              labels=['libuv'],
-                              extra_args=extra_args + ['--iomgr_platform=uv'],
-                              inner_jobs=inner_jobs)
-
   return test_jobs
 
 
@@ -244,6 +236,14 @@
                               extra_args=extra_args,
                               inner_jobs=inner_jobs)
 
+  test_jobs += _generate_jobs(languages=['c'],
+                              configs=['dbg'],
+                              platforms=['linux'],
+                              iomgr_platform='uv',
+                              labels=['portability'],
+                              extra_args=extra_args,
+                              inner_jobs=inner_jobs)
+
   test_jobs += _generate_jobs(languages=['node'],
                               configs=['dbg'],
                               platforms=['linux'],
@@ -252,6 +252,33 @@
                               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)
+
+  test_jobs += _generate_jobs(languages=['node'],
+                              configs=['dbg'],
+                              platforms=['linux'],
+                              arch='default',
+                              compiler='node4',
+                              labels=['portability'],
+                              extra_args=extra_args,
+                              inner_jobs=inner_jobs)
+
+  test_jobs += _generate_jobs(languages=['node'],
+                              configs=['dbg'],
+                              platforms=['linux'],
+                              arch='default',
+                              compiler='node6',
+                              labels=['portability'],
+                              extra_args=extra_args,
+                              inner_jobs=inner_jobs)
+
   return test_jobs
 
 
diff --git a/vsprojects/buildtests_c.sln b/vsprojects/buildtests_c.sln
index 12a45ee..29375b9 100644
--- a/vsprojects/buildtests_c.sln
+++ b/vsprojects/buildtests_c.sln
@@ -569,6 +569,17 @@
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "grpc_completion_queue_threading_test", "vcxproj\test\grpc_completion_queue_threading_test\grpc_completion_queue_threading_test.vcxproj", "{E6C389BC-0B47-D692-9BCD-758604CFC45B}"
+	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}") = "grpc_create_jwt", "vcxproj\.\grpc_create_jwt\grpc_create_jwt.vcxproj", "{77971F8D-F583-3E77-0E3C-6C1FB6B1749C}"
 	ProjectSection(myProperties) = preProject
         	lib = "False"
@@ -1434,6 +1445,28 @@
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tcp_client_uv_test", "vcxproj\test\tcp_client_uv_test\tcp_client_uv_test.vcxproj", "{9814D850-F3BB-8C7A-4C78-2751C1E272F4}"
+	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}") = "tcp_server_uv_test", "vcxproj\test\tcp_server_uv_test\tcp_server_uv_test.vcxproj", "{676FA8B1-4800-6F02-1E1D-30517FD9C7F4}"
+	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}") = "test_tcp_server", "vcxproj\.\test_tcp_server\test_tcp_server.vcxproj", "{E3110C46-A148-FF65-08FD-3324829BE7FE}"
 	ProjectSection(myProperties) = preProject
         	lib = "True"
@@ -2481,6 +2514,22 @@
 		{16CDF507-EB91-D76C-F0A7-A914ABFD8C17}.Release-DLL|Win32.Build.0 = Release|Win32
 		{16CDF507-EB91-D76C-F0A7-A914ABFD8C17}.Release-DLL|x64.ActiveCfg = Release|x64
 		{16CDF507-EB91-D76C-F0A7-A914ABFD8C17}.Release-DLL|x64.Build.0 = Release|x64
+		{E6C389BC-0B47-D692-9BCD-758604CFC45B}.Debug|Win32.ActiveCfg = Debug|Win32
+		{E6C389BC-0B47-D692-9BCD-758604CFC45B}.Debug|x64.ActiveCfg = Debug|x64
+		{E6C389BC-0B47-D692-9BCD-758604CFC45B}.Release|Win32.ActiveCfg = Release|Win32
+		{E6C389BC-0B47-D692-9BCD-758604CFC45B}.Release|x64.ActiveCfg = Release|x64
+		{E6C389BC-0B47-D692-9BCD-758604CFC45B}.Debug|Win32.Build.0 = Debug|Win32
+		{E6C389BC-0B47-D692-9BCD-758604CFC45B}.Debug|x64.Build.0 = Debug|x64
+		{E6C389BC-0B47-D692-9BCD-758604CFC45B}.Release|Win32.Build.0 = Release|Win32
+		{E6C389BC-0B47-D692-9BCD-758604CFC45B}.Release|x64.Build.0 = Release|x64
+		{E6C389BC-0B47-D692-9BCD-758604CFC45B}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{E6C389BC-0B47-D692-9BCD-758604CFC45B}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{E6C389BC-0B47-D692-9BCD-758604CFC45B}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{E6C389BC-0B47-D692-9BCD-758604CFC45B}.Debug-DLL|x64.Build.0 = Debug|x64
+		{E6C389BC-0B47-D692-9BCD-758604CFC45B}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{E6C389BC-0B47-D692-9BCD-758604CFC45B}.Release-DLL|Win32.Build.0 = Release|Win32
+		{E6C389BC-0B47-D692-9BCD-758604CFC45B}.Release-DLL|x64.ActiveCfg = Release|x64
+		{E6C389BC-0B47-D692-9BCD-758604CFC45B}.Release-DLL|x64.Build.0 = Release|x64
 		{77971F8D-F583-3E77-0E3C-6C1FB6B1749C}.Debug|Win32.ActiveCfg = Debug|Win32
 		{77971F8D-F583-3E77-0E3C-6C1FB6B1749C}.Debug|x64.ActiveCfg = Debug|x64
 		{77971F8D-F583-3E77-0E3C-6C1FB6B1749C}.Release|Win32.ActiveCfg = Release|Win32
@@ -3713,6 +3762,38 @@
 		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Release-DLL|Win32.Build.0 = Release|Win32
 		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Release-DLL|x64.ActiveCfg = Release|x64
 		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Release-DLL|x64.Build.0 = Release|x64
+		{9814D850-F3BB-8C7A-4C78-2751C1E272F4}.Debug|Win32.ActiveCfg = Debug|Win32
+		{9814D850-F3BB-8C7A-4C78-2751C1E272F4}.Debug|x64.ActiveCfg = Debug|x64
+		{9814D850-F3BB-8C7A-4C78-2751C1E272F4}.Release|Win32.ActiveCfg = Release|Win32
+		{9814D850-F3BB-8C7A-4C78-2751C1E272F4}.Release|x64.ActiveCfg = Release|x64
+		{9814D850-F3BB-8C7A-4C78-2751C1E272F4}.Debug|Win32.Build.0 = Debug|Win32
+		{9814D850-F3BB-8C7A-4C78-2751C1E272F4}.Debug|x64.Build.0 = Debug|x64
+		{9814D850-F3BB-8C7A-4C78-2751C1E272F4}.Release|Win32.Build.0 = Release|Win32
+		{9814D850-F3BB-8C7A-4C78-2751C1E272F4}.Release|x64.Build.0 = Release|x64
+		{9814D850-F3BB-8C7A-4C78-2751C1E272F4}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{9814D850-F3BB-8C7A-4C78-2751C1E272F4}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{9814D850-F3BB-8C7A-4C78-2751C1E272F4}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{9814D850-F3BB-8C7A-4C78-2751C1E272F4}.Debug-DLL|x64.Build.0 = Debug|x64
+		{9814D850-F3BB-8C7A-4C78-2751C1E272F4}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{9814D850-F3BB-8C7A-4C78-2751C1E272F4}.Release-DLL|Win32.Build.0 = Release|Win32
+		{9814D850-F3BB-8C7A-4C78-2751C1E272F4}.Release-DLL|x64.ActiveCfg = Release|x64
+		{9814D850-F3BB-8C7A-4C78-2751C1E272F4}.Release-DLL|x64.Build.0 = Release|x64
+		{676FA8B1-4800-6F02-1E1D-30517FD9C7F4}.Debug|Win32.ActiveCfg = Debug|Win32
+		{676FA8B1-4800-6F02-1E1D-30517FD9C7F4}.Debug|x64.ActiveCfg = Debug|x64
+		{676FA8B1-4800-6F02-1E1D-30517FD9C7F4}.Release|Win32.ActiveCfg = Release|Win32
+		{676FA8B1-4800-6F02-1E1D-30517FD9C7F4}.Release|x64.ActiveCfg = Release|x64
+		{676FA8B1-4800-6F02-1E1D-30517FD9C7F4}.Debug|Win32.Build.0 = Debug|Win32
+		{676FA8B1-4800-6F02-1E1D-30517FD9C7F4}.Debug|x64.Build.0 = Debug|x64
+		{676FA8B1-4800-6F02-1E1D-30517FD9C7F4}.Release|Win32.Build.0 = Release|Win32
+		{676FA8B1-4800-6F02-1E1D-30517FD9C7F4}.Release|x64.Build.0 = Release|x64
+		{676FA8B1-4800-6F02-1E1D-30517FD9C7F4}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{676FA8B1-4800-6F02-1E1D-30517FD9C7F4}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{676FA8B1-4800-6F02-1E1D-30517FD9C7F4}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{676FA8B1-4800-6F02-1E1D-30517FD9C7F4}.Debug-DLL|x64.Build.0 = Debug|x64
+		{676FA8B1-4800-6F02-1E1D-30517FD9C7F4}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{676FA8B1-4800-6F02-1E1D-30517FD9C7F4}.Release-DLL|Win32.Build.0 = Release|Win32
+		{676FA8B1-4800-6F02-1E1D-30517FD9C7F4}.Release-DLL|x64.ActiveCfg = Release|x64
+		{676FA8B1-4800-6F02-1E1D-30517FD9C7F4}.Release-DLL|x64.Build.0 = Release|x64
 		{E3110C46-A148-FF65-08FD-3324829BE7FE}.Debug|Win32.ActiveCfg = Debug|Win32
 		{E3110C46-A148-FF65-08FD-3324829BE7FE}.Debug|x64.ActiveCfg = Debug|x64
 		{E3110C46-A148-FF65-08FD-3324829BE7FE}.Release|Win32.ActiveCfg = Release|Win32
diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj b/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj
index 6506f24..08b3acd 100644
--- a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj
+++ b/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj
@@ -199,6 +199,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\invoke_large_request.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\keepalive_timeout.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\load_reporting_hook.c">
diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj.filters b/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj.filters
index 77e5ebf..3a8670c 100644
--- a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj.filters
+++ b/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj.filters
@@ -73,6 +73,9 @@
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\invoke_large_request.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\keepalive_timeout.c">
+      <Filter>test\core\end2end\tests</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj b/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj
index 176e8f7..96418c3 100644
--- a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj
+++ b/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj
@@ -201,6 +201,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\invoke_large_request.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\keepalive_timeout.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\load_reporting_hook.c">
diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj.filters b/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj.filters
index fa70267..cf40abe 100644
--- a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj.filters
+++ b/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj.filters
@@ -76,6 +76,9 @@
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\invoke_large_request.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\keepalive_timeout.c">
+      <Filter>test\core\end2end\tests</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
diff --git a/vsprojects/vcxproj/test/grpc_completion_queue_threading_test/grpc_completion_queue_threading_test.vcxproj b/vsprojects/vcxproj/test/grpc_completion_queue_threading_test/grpc_completion_queue_threading_test.vcxproj
new file mode 100644
index 0000000..79d5d33
--- /dev/null
+++ b/vsprojects/vcxproj/test/grpc_completion_queue_threading_test/grpc_completion_queue_threading_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>{E6C389BC-0B47-D692-9BCD-758604CFC45B}</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>grpc_completion_queue_threading_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>grpc_completion_queue_threading_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\completion_queue_threading_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/grpc_completion_queue_threading_test/grpc_completion_queue_threading_test.vcxproj.filters b/vsprojects/vcxproj/test/grpc_completion_queue_threading_test/grpc_completion_queue_threading_test.vcxproj.filters
new file mode 100644
index 0000000..fa18cf2
--- /dev/null
+++ b/vsprojects/vcxproj/test/grpc_completion_queue_threading_test/grpc_completion_queue_threading_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\completion_queue_threading_test.c">
+      <Filter>test\core\surface</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="test">
+      <UniqueIdentifier>{610a0893-b941-7804-c634-7e87da165bc8}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core">
+      <UniqueIdentifier>{f1d10be6-3cec-cfc8-a663-f88d860cf243}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\surface">
+      <UniqueIdentifier>{2f9aae89-db75-dc2a-9687-2930fba7c6f2}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/tcp_client_uv_test/tcp_client_uv_test.vcxproj b/vsprojects/vcxproj/test/tcp_client_uv_test/tcp_client_uv_test.vcxproj
new file mode 100644
index 0000000..69643ca
--- /dev/null
+++ b/vsprojects/vcxproj/test/tcp_client_uv_test/tcp_client_uv_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>{9814D850-F3BB-8C7A-4C78-2751C1E272F4}</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>tcp_client_uv_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>tcp_client_uv_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\iomgr\tcp_client_uv_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/tcp_client_uv_test/tcp_client_uv_test.vcxproj.filters b/vsprojects/vcxproj/test/tcp_client_uv_test/tcp_client_uv_test.vcxproj.filters
new file mode 100644
index 0000000..99697ab
--- /dev/null
+++ b/vsprojects/vcxproj/test/tcp_client_uv_test/tcp_client_uv_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\iomgr\tcp_client_uv_test.c">
+      <Filter>test\core\iomgr</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="test">
+      <UniqueIdentifier>{15a54a22-a533-eefa-6597-338c283e2936}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core">
+      <UniqueIdentifier>{184af337-9d94-9c81-4bf0-37452c61fafc}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\iomgr">
+      <UniqueIdentifier>{0ce112f9-44c9-5291-c73c-bef3f84f0806}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/tcp_server_uv_test/tcp_server_uv_test.vcxproj b/vsprojects/vcxproj/test/tcp_server_uv_test/tcp_server_uv_test.vcxproj
new file mode 100644
index 0000000..e553d09
--- /dev/null
+++ b/vsprojects/vcxproj/test/tcp_server_uv_test/tcp_server_uv_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>{676FA8B1-4800-6F02-1E1D-30517FD9C7F4}</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>tcp_server_uv_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>tcp_server_uv_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\iomgr\tcp_server_uv_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/tcp_server_uv_test/tcp_server_uv_test.vcxproj.filters b/vsprojects/vcxproj/test/tcp_server_uv_test/tcp_server_uv_test.vcxproj.filters
new file mode 100644
index 0000000..e334e21
--- /dev/null
+++ b/vsprojects/vcxproj/test/tcp_server_uv_test/tcp_server_uv_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\iomgr\tcp_server_uv_test.c">
+      <Filter>test\core\iomgr</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="test">
+      <UniqueIdentifier>{848adafa-e931-cd69-49b3-483f9e8dbf77}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core">
+      <UniqueIdentifier>{3f3b9db4-63ef-fe87-8ef8-7048b510b224}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\iomgr">
+      <UniqueIdentifier>{cab05942-2e34-7335-58d0-f40c4db9d3ca}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+