diff --git a/.gitignore b/.gitignore
index 3cc35ff7cd1f4357b7649d76d2cc4ea1c8e52711..31124451768e968afa52fc5929c564006ec0f007 100644
--- a/.gitignore
+++ b/.gitignore
@@ -96,10 +96,17 @@ DerivedData
 Pods/
 
 # Artifacts directory
-artifacts/
+/artifacts/
 
 # Git generated files for conflicting
 *.orig
 
 # IDE specific folder for JetBrains IDEs
 .idea/
+
+# Blaze files
+bazel-bin
+bazel-genfiles
+bazel-grpc
+bazel-out
+bazel-testlogs
diff --git a/.gitmodules b/.gitmodules
index c32881cb9514c518183ff5df5bf59009f302a2b6..c8ca8ab046332dc65d9de36fcb69da09ea306205 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -17,6 +17,9 @@
 [submodule "third_party/thrift"]
 	path = third_party/thrift
 	url = https://github.com/apache/thrift.git
-[submodule "third_party/google_benchmark"]
-	path = third_party/google_benchmark
+[submodule "third_party/benchmark"]
+	path = third_party/benchmark
 	url = https://github.com/google/benchmark
+[submodule "third_party/boringssl-with-bazel"]
+	path = third_party/boringssl-with-bazel
+	url = https://boringssl.googlesource.com/boringssl
diff --git a/BUILD b/BUILD
index 9e42799a703cb6f724a8a5a69a0e714b2a6f5bef..3122dc0a321b665f4ee91a25039f06434a1a402c 100644
--- a/BUILD
+++ b/BUILD
@@ -1,11 +1,6 @@
-# GRPC Bazel BUILD file.
-# This currently builds C, C++ and Objective-C code.
-# This file has been automatically generated from a template file.
-# Please look at the templates directory instead.
-# This file can be regenerated from the template by running
-# tools/buildgen/generate_projects.sh
-
-# Copyright 2015, Google Inc.
+# gRPC Bazel BUILD file.
+#
+# Copyright 2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -40,2866 +35,1200 @@ exports_files(["LICENSE"])
 
 package(default_visibility = ["//visibility:public"])
 
+load("//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_proto_plugin")
 
+g_stands_for = "good"
 
+core_version = "2.0.0-dev"
 
+version = "1.1.0-dev"
 
-cc_library(
-  name = "gpr",
-  srcs = [
-    "src/core/lib/profiling/timers.h",
-    "src/core/lib/support/backoff.h",
-    "src/core/lib/support/block_annotate.h",
-    "src/core/lib/support/env.h",
-    "src/core/lib/support/mpscq.h",
-    "src/core/lib/support/murmur_hash.h",
-    "src/core/lib/support/stack_lockfree.h",
-    "src/core/lib/support/string.h",
-    "src/core/lib/support/string_windows.h",
-    "src/core/lib/support/thd_internal.h",
-    "src/core/lib/support/time_precise.h",
-    "src/core/lib/support/tmpfile.h",
-    "src/core/lib/profiling/basic_timers.c",
-    "src/core/lib/profiling/stap_timers.c",
-    "src/core/lib/support/alloc.c",
-    "src/core/lib/support/avl.c",
-    "src/core/lib/support/backoff.c",
-    "src/core/lib/support/cmdline.c",
-    "src/core/lib/support/cpu_iphone.c",
-    "src/core/lib/support/cpu_linux.c",
-    "src/core/lib/support/cpu_posix.c",
-    "src/core/lib/support/cpu_windows.c",
-    "src/core/lib/support/env_linux.c",
-    "src/core/lib/support/env_posix.c",
-    "src/core/lib/support/env_windows.c",
-    "src/core/lib/support/histogram.c",
-    "src/core/lib/support/host_port.c",
-    "src/core/lib/support/log.c",
-    "src/core/lib/support/log_android.c",
-    "src/core/lib/support/log_linux.c",
-    "src/core/lib/support/log_posix.c",
-    "src/core/lib/support/log_windows.c",
-    "src/core/lib/support/mpscq.c",
-    "src/core/lib/support/murmur_hash.c",
-    "src/core/lib/support/stack_lockfree.c",
-    "src/core/lib/support/string.c",
-    "src/core/lib/support/string_posix.c",
-    "src/core/lib/support/string_util_windows.c",
-    "src/core/lib/support/string_windows.c",
-    "src/core/lib/support/subprocess_posix.c",
-    "src/core/lib/support/subprocess_windows.c",
-    "src/core/lib/support/sync.c",
-    "src/core/lib/support/sync_posix.c",
-    "src/core/lib/support/sync_windows.c",
-    "src/core/lib/support/thd.c",
-    "src/core/lib/support/thd_posix.c",
-    "src/core/lib/support/thd_windows.c",
-    "src/core/lib/support/time.c",
-    "src/core/lib/support/time_posix.c",
-    "src/core/lib/support/time_precise.c",
-    "src/core/lib/support/time_windows.c",
-    "src/core/lib/support/tls_pthread.c",
-    "src/core/lib/support/tmpfile_msys.c",
-    "src/core/lib/support/tmpfile_posix.c",
-    "src/core/lib/support/tmpfile_windows.c",
-    "src/core/lib/support/wrap_memcpy.c",
-  ],
-  hdrs = [
-    "include/grpc/support/alloc.h",
-    "include/grpc/support/atm.h",
-    "include/grpc/support/atm_gcc_atomic.h",
-    "include/grpc/support/atm_gcc_sync.h",
-    "include/grpc/support/atm_windows.h",
-    "include/grpc/support/avl.h",
-    "include/grpc/support/cmdline.h",
-    "include/grpc/support/cpu.h",
-    "include/grpc/support/histogram.h",
-    "include/grpc/support/host_port.h",
-    "include/grpc/support/log.h",
-    "include/grpc/support/log_windows.h",
-    "include/grpc/support/port_platform.h",
-    "include/grpc/support/string_util.h",
-    "include/grpc/support/subprocess.h",
-    "include/grpc/support/sync.h",
-    "include/grpc/support/sync_generic.h",
-    "include/grpc/support/sync_posix.h",
-    "include/grpc/support/sync_windows.h",
-    "include/grpc/support/thd.h",
-    "include/grpc/support/time.h",
-    "include/grpc/support/tls.h",
-    "include/grpc/support/tls_gcc.h",
-    "include/grpc/support/tls_msvc.h",
-    "include/grpc/support/tls_pthread.h",
-    "include/grpc/support/useful.h",
-    "include/grpc/impl/codegen/atm.h",
-    "include/grpc/impl/codegen/atm_gcc_atomic.h",
-    "include/grpc/impl/codegen/atm_gcc_sync.h",
-    "include/grpc/impl/codegen/atm_windows.h",
-    "include/grpc/impl/codegen/gpr_types.h",
-    "include/grpc/impl/codegen/port_platform.h",
-    "include/grpc/impl/codegen/slice.h",
-    "include/grpc/impl/codegen/sync.h",
-    "include/grpc/impl/codegen/sync_generic.h",
-    "include/grpc/impl/codegen/sync_posix.h",
-    "include/grpc/impl/codegen/sync_windows.h",
-  ],
-  includes = [
-    "include",
-    ".",
-  ],
-  deps = [
-  ],
+grpc_cc_library(
+    name = "gpr",
+    language = "c",
+    standalone = True,
+    deps = [
+        "gpr_base",
+    ],
 )
 
-
-
-cc_library(
-  name = "grpc",
-  srcs = [
-    "src/core/lib/channel/channel_args.h",
-    "src/core/lib/channel/channel_stack.h",
-    "src/core/lib/channel/channel_stack_builder.h",
-    "src/core/lib/channel/compress_filter.h",
-    "src/core/lib/channel/connected_channel.h",
-    "src/core/lib/channel/context.h",
-    "src/core/lib/channel/deadline_filter.h",
-    "src/core/lib/channel/handshaker.h",
-    "src/core/lib/channel/http_client_filter.h",
-    "src/core/lib/channel/http_server_filter.h",
-    "src/core/lib/channel/message_size_filter.h",
-    "src/core/lib/compression/algorithm_metadata.h",
-    "src/core/lib/compression/message_compress.h",
-    "src/core/lib/debug/trace.h",
-    "src/core/lib/http/format_request.h",
-    "src/core/lib/http/httpcli.h",
-    "src/core/lib/http/parser.h",
-    "src/core/lib/iomgr/closure.h",
-    "src/core/lib/iomgr/combiner.h",
-    "src/core/lib/iomgr/endpoint.h",
-    "src/core/lib/iomgr/endpoint_pair.h",
-    "src/core/lib/iomgr/error.h",
-    "src/core/lib/iomgr/ev_epoll_linux.h",
-    "src/core/lib/iomgr/ev_poll_posix.h",
-    "src/core/lib/iomgr/ev_posix.h",
-    "src/core/lib/iomgr/exec_ctx.h",
-    "src/core/lib/iomgr/executor.h",
-    "src/core/lib/iomgr/iocp_windows.h",
-    "src/core/lib/iomgr/iomgr.h",
-    "src/core/lib/iomgr/iomgr_internal.h",
-    "src/core/lib/iomgr/iomgr_posix.h",
-    "src/core/lib/iomgr/load_file.h",
-    "src/core/lib/iomgr/network_status_tracker.h",
-    "src/core/lib/iomgr/polling_entity.h",
-    "src/core/lib/iomgr/pollset.h",
-    "src/core/lib/iomgr/pollset_set.h",
-    "src/core/lib/iomgr/pollset_set_windows.h",
-    "src/core/lib/iomgr/pollset_uv.h",
-    "src/core/lib/iomgr/pollset_windows.h",
-    "src/core/lib/iomgr/port.h",
-    "src/core/lib/iomgr/resolve_address.h",
-    "src/core/lib/iomgr/resource_quota.h",
-    "src/core/lib/iomgr/sockaddr.h",
-    "src/core/lib/iomgr/sockaddr_posix.h",
-    "src/core/lib/iomgr/sockaddr_utils.h",
-    "src/core/lib/iomgr/sockaddr_windows.h",
-    "src/core/lib/iomgr/socket_mutator.h",
-    "src/core/lib/iomgr/socket_utils.h",
-    "src/core/lib/iomgr/socket_utils_posix.h",
-    "src/core/lib/iomgr/socket_windows.h",
-    "src/core/lib/iomgr/tcp_client.h",
-    "src/core/lib/iomgr/tcp_client_posix.h",
-    "src/core/lib/iomgr/tcp_posix.h",
-    "src/core/lib/iomgr/tcp_server.h",
-    "src/core/lib/iomgr/tcp_uv.h",
-    "src/core/lib/iomgr/tcp_windows.h",
-    "src/core/lib/iomgr/time_averaged_stats.h",
-    "src/core/lib/iomgr/timer.h",
-    "src/core/lib/iomgr/timer_generic.h",
-    "src/core/lib/iomgr/timer_heap.h",
-    "src/core/lib/iomgr/timer_uv.h",
-    "src/core/lib/iomgr/udp_server.h",
-    "src/core/lib/iomgr/unix_sockets_posix.h",
-    "src/core/lib/iomgr/wakeup_fd_cv.h",
-    "src/core/lib/iomgr/wakeup_fd_pipe.h",
-    "src/core/lib/iomgr/wakeup_fd_posix.h",
-    "src/core/lib/iomgr/workqueue.h",
-    "src/core/lib/iomgr/workqueue_uv.h",
-    "src/core/lib/iomgr/workqueue_windows.h",
-    "src/core/lib/json/json.h",
-    "src/core/lib/json/json_common.h",
-    "src/core/lib/json/json_reader.h",
-    "src/core/lib/json/json_writer.h",
-    "src/core/lib/slice/percent_encoding.h",
-    "src/core/lib/slice/slice_string_helpers.h",
-    "src/core/lib/surface/api_trace.h",
-    "src/core/lib/surface/call.h",
-    "src/core/lib/surface/call_test_only.h",
-    "src/core/lib/surface/channel.h",
-    "src/core/lib/surface/channel_init.h",
-    "src/core/lib/surface/channel_stack_type.h",
-    "src/core/lib/surface/completion_queue.h",
-    "src/core/lib/surface/event_string.h",
-    "src/core/lib/surface/init.h",
-    "src/core/lib/surface/lame_client.h",
-    "src/core/lib/surface/server.h",
-    "src/core/lib/transport/byte_stream.h",
-    "src/core/lib/transport/connectivity_state.h",
-    "src/core/lib/transport/mdstr_hash_table.h",
-    "src/core/lib/transport/metadata.h",
-    "src/core/lib/transport/metadata_batch.h",
-    "src/core/lib/transport/pid_controller.h",
-    "src/core/lib/transport/service_config.h",
-    "src/core/lib/transport/static_metadata.h",
-    "src/core/lib/transport/timeout_encoding.h",
-    "src/core/lib/transport/transport.h",
-    "src/core/lib/transport/transport_impl.h",
-    "src/core/ext/transport/chttp2/transport/bin_decoder.h",
-    "src/core/ext/transport/chttp2/transport/bin_encoder.h",
-    "src/core/ext/transport/chttp2/transport/chttp2_transport.h",
-    "src/core/ext/transport/chttp2/transport/frame.h",
-    "src/core/ext/transport/chttp2/transport/frame_data.h",
-    "src/core/ext/transport/chttp2/transport/frame_goaway.h",
-    "src/core/ext/transport/chttp2/transport/frame_ping.h",
-    "src/core/ext/transport/chttp2/transport/frame_rst_stream.h",
-    "src/core/ext/transport/chttp2/transport/frame_settings.h",
-    "src/core/ext/transport/chttp2/transport/frame_window_update.h",
-    "src/core/ext/transport/chttp2/transport/hpack_encoder.h",
-    "src/core/ext/transport/chttp2/transport/hpack_parser.h",
-    "src/core/ext/transport/chttp2/transport/hpack_table.h",
-    "src/core/ext/transport/chttp2/transport/http2_errors.h",
-    "src/core/ext/transport/chttp2/transport/huffsyms.h",
-    "src/core/ext/transport/chttp2/transport/incoming_metadata.h",
-    "src/core/ext/transport/chttp2/transport/internal.h",
-    "src/core/ext/transport/chttp2/transport/status_conversion.h",
-    "src/core/ext/transport/chttp2/transport/stream_map.h",
-    "src/core/ext/transport/chttp2/transport/varint.h",
-    "src/core/ext/transport/chttp2/alpn/alpn.h",
-    "src/core/lib/security/context/security_context.h",
-    "src/core/lib/security/credentials/composite/composite_credentials.h",
-    "src/core/lib/security/credentials/credentials.h",
-    "src/core/lib/security/credentials/fake/fake_credentials.h",
-    "src/core/lib/security/credentials/google_default/google_default_credentials.h",
-    "src/core/lib/security/credentials/iam/iam_credentials.h",
-    "src/core/lib/security/credentials/jwt/json_token.h",
-    "src/core/lib/security/credentials/jwt/jwt_credentials.h",
-    "src/core/lib/security/credentials/jwt/jwt_verifier.h",
-    "src/core/lib/security/credentials/oauth2/oauth2_credentials.h",
-    "src/core/lib/security/credentials/plugin/plugin_credentials.h",
-    "src/core/lib/security/credentials/ssl/ssl_credentials.h",
-    "src/core/lib/security/transport/auth_filters.h",
-    "src/core/lib/security/transport/handshake.h",
-    "src/core/lib/security/transport/secure_endpoint.h",
-    "src/core/lib/security/transport/security_connector.h",
-    "src/core/lib/security/transport/tsi_error.h",
-    "src/core/lib/security/util/b64.h",
-    "src/core/lib/security/util/json_util.h",
-    "src/core/lib/tsi/fake_transport_security.h",
-    "src/core/lib/tsi/ssl_transport_security.h",
-    "src/core/lib/tsi/ssl_types.h",
-    "src/core/lib/tsi/transport_security.h",
-    "src/core/lib/tsi/transport_security_interface.h",
-    "src/core/ext/client_channel/client_channel.h",
-    "src/core/ext/client_channel/client_channel_factory.h",
-    "src/core/ext/client_channel/connector.h",
-    "src/core/ext/client_channel/http_connect_handshaker.h",
-    "src/core/ext/client_channel/initial_connect_string.h",
-    "src/core/ext/client_channel/lb_policy.h",
-    "src/core/ext/client_channel/lb_policy_factory.h",
-    "src/core/ext/client_channel/lb_policy_registry.h",
-    "src/core/ext/client_channel/parse_address.h",
-    "src/core/ext/client_channel/resolver.h",
-    "src/core/ext/client_channel/resolver_factory.h",
-    "src/core/ext/client_channel/resolver_registry.h",
-    "src/core/ext/client_channel/subchannel.h",
-    "src/core/ext/client_channel/subchannel_index.h",
-    "src/core/ext/client_channel/uri_parser.h",
-    "src/core/ext/lb_policy/grpclb/grpclb.h",
-    "src/core/ext/lb_policy/grpclb/load_balancer_api.h",
-    "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h",
-    "src/core/ext/load_reporting/load_reporting.h",
-    "src/core/ext/load_reporting/load_reporting_filter.h",
-    "src/core/ext/census/aggregation.h",
-    "src/core/ext/census/base_resources.h",
-    "src/core/ext/census/census_interface.h",
-    "src/core/ext/census/census_rpc_stats.h",
-    "src/core/ext/census/gen/census.pb.h",
-    "src/core/ext/census/gen/trace_context.pb.h",
-    "src/core/ext/census/grpc_filter.h",
-    "src/core/ext/census/mlog.h",
-    "src/core/ext/census/resource.h",
-    "src/core/ext/census/rpc_metric_id.h",
-    "src/core/ext/census/trace_context.h",
-    "src/core/lib/surface/init.c",
-    "src/core/lib/channel/channel_args.c",
-    "src/core/lib/channel/channel_stack.c",
-    "src/core/lib/channel/channel_stack_builder.c",
-    "src/core/lib/channel/compress_filter.c",
-    "src/core/lib/channel/connected_channel.c",
-    "src/core/lib/channel/deadline_filter.c",
-    "src/core/lib/channel/handshaker.c",
-    "src/core/lib/channel/http_client_filter.c",
-    "src/core/lib/channel/http_server_filter.c",
-    "src/core/lib/channel/message_size_filter.c",
-    "src/core/lib/compression/compression.c",
-    "src/core/lib/compression/message_compress.c",
-    "src/core/lib/debug/trace.c",
-    "src/core/lib/http/format_request.c",
-    "src/core/lib/http/httpcli.c",
-    "src/core/lib/http/parser.c",
-    "src/core/lib/iomgr/closure.c",
-    "src/core/lib/iomgr/combiner.c",
-    "src/core/lib/iomgr/endpoint.c",
-    "src/core/lib/iomgr/endpoint_pair_posix.c",
-    "src/core/lib/iomgr/endpoint_pair_uv.c",
-    "src/core/lib/iomgr/endpoint_pair_windows.c",
-    "src/core/lib/iomgr/error.c",
-    "src/core/lib/iomgr/ev_epoll_linux.c",
-    "src/core/lib/iomgr/ev_poll_posix.c",
-    "src/core/lib/iomgr/ev_posix.c",
-    "src/core/lib/iomgr/exec_ctx.c",
-    "src/core/lib/iomgr/executor.c",
-    "src/core/lib/iomgr/iocp_windows.c",
-    "src/core/lib/iomgr/iomgr.c",
-    "src/core/lib/iomgr/iomgr_posix.c",
-    "src/core/lib/iomgr/iomgr_uv.c",
-    "src/core/lib/iomgr/iomgr_windows.c",
-    "src/core/lib/iomgr/load_file.c",
-    "src/core/lib/iomgr/network_status_tracker.c",
-    "src/core/lib/iomgr/polling_entity.c",
-    "src/core/lib/iomgr/pollset_set_uv.c",
-    "src/core/lib/iomgr/pollset_set_windows.c",
-    "src/core/lib/iomgr/pollset_uv.c",
-    "src/core/lib/iomgr/pollset_windows.c",
-    "src/core/lib/iomgr/resolve_address_posix.c",
-    "src/core/lib/iomgr/resolve_address_uv.c",
-    "src/core/lib/iomgr/resolve_address_windows.c",
-    "src/core/lib/iomgr/resource_quota.c",
-    "src/core/lib/iomgr/sockaddr_utils.c",
-    "src/core/lib/iomgr/socket_mutator.c",
-    "src/core/lib/iomgr/socket_utils_common_posix.c",
-    "src/core/lib/iomgr/socket_utils_linux.c",
-    "src/core/lib/iomgr/socket_utils_posix.c",
-    "src/core/lib/iomgr/socket_utils_uv.c",
-    "src/core/lib/iomgr/socket_utils_windows.c",
-    "src/core/lib/iomgr/socket_windows.c",
-    "src/core/lib/iomgr/tcp_client_posix.c",
-    "src/core/lib/iomgr/tcp_client_uv.c",
-    "src/core/lib/iomgr/tcp_client_windows.c",
-    "src/core/lib/iomgr/tcp_posix.c",
-    "src/core/lib/iomgr/tcp_server_posix.c",
-    "src/core/lib/iomgr/tcp_server_uv.c",
-    "src/core/lib/iomgr/tcp_server_windows.c",
-    "src/core/lib/iomgr/tcp_uv.c",
-    "src/core/lib/iomgr/tcp_windows.c",
-    "src/core/lib/iomgr/time_averaged_stats.c",
-    "src/core/lib/iomgr/timer_generic.c",
-    "src/core/lib/iomgr/timer_heap.c",
-    "src/core/lib/iomgr/timer_uv.c",
-    "src/core/lib/iomgr/udp_server.c",
-    "src/core/lib/iomgr/unix_sockets_posix.c",
-    "src/core/lib/iomgr/unix_sockets_posix_noop.c",
-    "src/core/lib/iomgr/wakeup_fd_cv.c",
-    "src/core/lib/iomgr/wakeup_fd_eventfd.c",
-    "src/core/lib/iomgr/wakeup_fd_nospecial.c",
-    "src/core/lib/iomgr/wakeup_fd_pipe.c",
-    "src/core/lib/iomgr/wakeup_fd_posix.c",
-    "src/core/lib/iomgr/workqueue_uv.c",
-    "src/core/lib/iomgr/workqueue_windows.c",
-    "src/core/lib/json/json.c",
-    "src/core/lib/json/json_reader.c",
-    "src/core/lib/json/json_string.c",
-    "src/core/lib/json/json_writer.c",
-    "src/core/lib/slice/percent_encoding.c",
-    "src/core/lib/slice/slice.c",
-    "src/core/lib/slice/slice_buffer.c",
-    "src/core/lib/slice/slice_string_helpers.c",
-    "src/core/lib/surface/alarm.c",
-    "src/core/lib/surface/api_trace.c",
-    "src/core/lib/surface/byte_buffer.c",
-    "src/core/lib/surface/byte_buffer_reader.c",
-    "src/core/lib/surface/call.c",
-    "src/core/lib/surface/call_details.c",
-    "src/core/lib/surface/call_log_batch.c",
-    "src/core/lib/surface/channel.c",
-    "src/core/lib/surface/channel_init.c",
-    "src/core/lib/surface/channel_ping.c",
-    "src/core/lib/surface/channel_stack_type.c",
-    "src/core/lib/surface/completion_queue.c",
-    "src/core/lib/surface/event_string.c",
-    "src/core/lib/surface/lame_client.c",
-    "src/core/lib/surface/metadata_array.c",
-    "src/core/lib/surface/server.c",
-    "src/core/lib/surface/validate_metadata.c",
-    "src/core/lib/surface/version.c",
-    "src/core/lib/transport/byte_stream.c",
-    "src/core/lib/transport/connectivity_state.c",
-    "src/core/lib/transport/mdstr_hash_table.c",
-    "src/core/lib/transport/metadata.c",
-    "src/core/lib/transport/metadata_batch.c",
-    "src/core/lib/transport/pid_controller.c",
-    "src/core/lib/transport/service_config.c",
-    "src/core/lib/transport/static_metadata.c",
-    "src/core/lib/transport/timeout_encoding.c",
-    "src/core/lib/transport/transport.c",
-    "src/core/lib/transport/transport_op_string.c",
-    "src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c",
-    "src/core/ext/transport/chttp2/transport/bin_decoder.c",
-    "src/core/ext/transport/chttp2/transport/bin_encoder.c",
-    "src/core/ext/transport/chttp2/transport/chttp2_plugin.c",
-    "src/core/ext/transport/chttp2/transport/chttp2_transport.c",
-    "src/core/ext/transport/chttp2/transport/frame_data.c",
-    "src/core/ext/transport/chttp2/transport/frame_goaway.c",
-    "src/core/ext/transport/chttp2/transport/frame_ping.c",
-    "src/core/ext/transport/chttp2/transport/frame_rst_stream.c",
-    "src/core/ext/transport/chttp2/transport/frame_settings.c",
-    "src/core/ext/transport/chttp2/transport/frame_window_update.c",
-    "src/core/ext/transport/chttp2/transport/hpack_encoder.c",
-    "src/core/ext/transport/chttp2/transport/hpack_parser.c",
-    "src/core/ext/transport/chttp2/transport/hpack_table.c",
-    "src/core/ext/transport/chttp2/transport/huffsyms.c",
-    "src/core/ext/transport/chttp2/transport/incoming_metadata.c",
-    "src/core/ext/transport/chttp2/transport/parsing.c",
-    "src/core/ext/transport/chttp2/transport/status_conversion.c",
-    "src/core/ext/transport/chttp2/transport/stream_lists.c",
-    "src/core/ext/transport/chttp2/transport/stream_map.c",
-    "src/core/ext/transport/chttp2/transport/varint.c",
-    "src/core/ext/transport/chttp2/transport/writing.c",
-    "src/core/ext/transport/chttp2/alpn/alpn.c",
-    "src/core/lib/http/httpcli_security_connector.c",
-    "src/core/lib/security/context/security_context.c",
-    "src/core/lib/security/credentials/composite/composite_credentials.c",
-    "src/core/lib/security/credentials/credentials.c",
-    "src/core/lib/security/credentials/credentials_metadata.c",
-    "src/core/lib/security/credentials/fake/fake_credentials.c",
-    "src/core/lib/security/credentials/google_default/credentials_generic.c",
-    "src/core/lib/security/credentials/google_default/google_default_credentials.c",
-    "src/core/lib/security/credentials/iam/iam_credentials.c",
-    "src/core/lib/security/credentials/jwt/json_token.c",
-    "src/core/lib/security/credentials/jwt/jwt_credentials.c",
-    "src/core/lib/security/credentials/jwt/jwt_verifier.c",
-    "src/core/lib/security/credentials/oauth2/oauth2_credentials.c",
-    "src/core/lib/security/credentials/plugin/plugin_credentials.c",
-    "src/core/lib/security/credentials/ssl/ssl_credentials.c",
-    "src/core/lib/security/transport/client_auth_filter.c",
-    "src/core/lib/security/transport/handshake.c",
-    "src/core/lib/security/transport/secure_endpoint.c",
-    "src/core/lib/security/transport/security_connector.c",
-    "src/core/lib/security/transport/server_auth_filter.c",
-    "src/core/lib/security/transport/tsi_error.c",
-    "src/core/lib/security/util/b64.c",
-    "src/core/lib/security/util/json_util.c",
-    "src/core/lib/surface/init_secure.c",
-    "src/core/lib/tsi/fake_transport_security.c",
-    "src/core/lib/tsi/ssl_transport_security.c",
-    "src/core/lib/tsi/transport_security.c",
-    "src/core/ext/transport/chttp2/client/secure/secure_channel_create.c",
-    "src/core/ext/client_channel/channel_connectivity.c",
-    "src/core/ext/client_channel/client_channel.c",
-    "src/core/ext/client_channel/client_channel_factory.c",
-    "src/core/ext/client_channel/client_channel_plugin.c",
-    "src/core/ext/client_channel/connector.c",
-    "src/core/ext/client_channel/default_initial_connect_string.c",
-    "src/core/ext/client_channel/http_connect_handshaker.c",
-    "src/core/ext/client_channel/initial_connect_string.c",
-    "src/core/ext/client_channel/lb_policy.c",
-    "src/core/ext/client_channel/lb_policy_factory.c",
-    "src/core/ext/client_channel/lb_policy_registry.c",
-    "src/core/ext/client_channel/parse_address.c",
-    "src/core/ext/client_channel/resolver.c",
-    "src/core/ext/client_channel/resolver_factory.c",
-    "src/core/ext/client_channel/resolver_registry.c",
-    "src/core/ext/client_channel/subchannel.c",
-    "src/core/ext/client_channel/subchannel_index.c",
-    "src/core/ext/client_channel/uri_parser.c",
-    "src/core/ext/transport/chttp2/server/insecure/server_chttp2.c",
-    "src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c",
-    "src/core/ext/transport/chttp2/client/insecure/channel_create.c",
-    "src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c",
-    "src/core/ext/lb_policy/grpclb/grpclb.c",
-    "src/core/ext/lb_policy/grpclb/load_balancer_api.c",
-    "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c",
-    "src/core/ext/lb_policy/pick_first/pick_first.c",
-    "src/core/ext/lb_policy/round_robin/round_robin.c",
-    "src/core/ext/resolver/dns/native/dns_resolver.c",
-    "src/core/ext/resolver/sockaddr/sockaddr_resolver.c",
-    "src/core/ext/load_reporting/load_reporting.c",
-    "src/core/ext/load_reporting/load_reporting_filter.c",
-    "src/core/ext/census/base_resources.c",
-    "src/core/ext/census/context.c",
-    "src/core/ext/census/gen/census.pb.c",
-    "src/core/ext/census/gen/trace_context.pb.c",
-    "src/core/ext/census/grpc_context.c",
-    "src/core/ext/census/grpc_filter.c",
-    "src/core/ext/census/grpc_plugin.c",
-    "src/core/ext/census/initialize.c",
-    "src/core/ext/census/mlog.c",
-    "src/core/ext/census/operation.c",
-    "src/core/ext/census/placeholders.c",
-    "src/core/ext/census/resource.c",
-    "src/core/ext/census/trace_context.c",
-    "src/core/ext/census/tracing.c",
-    "src/core/plugin_registry/grpc_plugin_registry.c",
-  ],
-  hdrs = [
-    "include/grpc/byte_buffer.h",
-    "include/grpc/byte_buffer_reader.h",
-    "include/grpc/compression.h",
-    "include/grpc/grpc.h",
-    "include/grpc/grpc_posix.h",
-    "include/grpc/grpc_security_constants.h",
-    "include/grpc/slice.h",
-    "include/grpc/slice_buffer.h",
-    "include/grpc/status.h",
-    "include/grpc/impl/codegen/byte_buffer_reader.h",
-    "include/grpc/impl/codegen/compression_types.h",
-    "include/grpc/impl/codegen/connectivity_state.h",
-    "include/grpc/impl/codegen/grpc_types.h",
-    "include/grpc/impl/codegen/propagation_bits.h",
-    "include/grpc/impl/codegen/status.h",
-    "include/grpc/impl/codegen/atm.h",
-    "include/grpc/impl/codegen/atm_gcc_atomic.h",
-    "include/grpc/impl/codegen/atm_gcc_sync.h",
-    "include/grpc/impl/codegen/atm_windows.h",
-    "include/grpc/impl/codegen/gpr_types.h",
-    "include/grpc/impl/codegen/port_platform.h",
-    "include/grpc/impl/codegen/slice.h",
-    "include/grpc/impl/codegen/sync.h",
-    "include/grpc/impl/codegen/sync_generic.h",
-    "include/grpc/impl/codegen/sync_posix.h",
-    "include/grpc/impl/codegen/sync_windows.h",
-    "include/grpc/grpc_security.h",
-    "include/grpc/census.h",
-  ],
-  includes = [
-    "include",
-    ".",
-  ],
-  deps = [
-    "//external:libssl",
-    "//external:zlib",
-    ":gpr",
-    "//external:nanopb",
-  ],
-  copts = [
-    "-std=gnu99",
-  ],
+grpc_cc_library(
+    name = "grpc",
+    srcs = [
+        "src/core/lib/surface/init.c",
+        "src/core/plugin_registry/grpc_plugin_registry.c",
+    ],
+    language = "c",
+    standalone = True,
+    deps = [
+        "census",
+        "grpc_base",
+        "grpc_lb_policy_grpclb",
+        "grpc_lb_policy_pick_first",
+        "grpc_lb_policy_round_robin",
+        "grpc_load_reporting",
+        "grpc_resolver_dns_native",
+        "grpc_resolver_sockaddr",
+        "grpc_secure",
+        "grpc_transport_chttp2_client_insecure",
+        "grpc_transport_chttp2_client_secure",
+        "grpc_transport_chttp2_server_insecure",
+        "grpc_transport_chttp2_server_secure",
+    ],
 )
 
-
-
-cc_library(
-  name = "grpc_cronet",
-  srcs = [
-    "src/core/lib/channel/channel_args.h",
-    "src/core/lib/channel/channel_stack.h",
-    "src/core/lib/channel/channel_stack_builder.h",
-    "src/core/lib/channel/compress_filter.h",
-    "src/core/lib/channel/connected_channel.h",
-    "src/core/lib/channel/context.h",
-    "src/core/lib/channel/deadline_filter.h",
-    "src/core/lib/channel/handshaker.h",
-    "src/core/lib/channel/http_client_filter.h",
-    "src/core/lib/channel/http_server_filter.h",
-    "src/core/lib/channel/message_size_filter.h",
-    "src/core/lib/compression/algorithm_metadata.h",
-    "src/core/lib/compression/message_compress.h",
-    "src/core/lib/debug/trace.h",
-    "src/core/lib/http/format_request.h",
-    "src/core/lib/http/httpcli.h",
-    "src/core/lib/http/parser.h",
-    "src/core/lib/iomgr/closure.h",
-    "src/core/lib/iomgr/combiner.h",
-    "src/core/lib/iomgr/endpoint.h",
-    "src/core/lib/iomgr/endpoint_pair.h",
-    "src/core/lib/iomgr/error.h",
-    "src/core/lib/iomgr/ev_epoll_linux.h",
-    "src/core/lib/iomgr/ev_poll_posix.h",
-    "src/core/lib/iomgr/ev_posix.h",
-    "src/core/lib/iomgr/exec_ctx.h",
-    "src/core/lib/iomgr/executor.h",
-    "src/core/lib/iomgr/iocp_windows.h",
-    "src/core/lib/iomgr/iomgr.h",
-    "src/core/lib/iomgr/iomgr_internal.h",
-    "src/core/lib/iomgr/iomgr_posix.h",
-    "src/core/lib/iomgr/load_file.h",
-    "src/core/lib/iomgr/network_status_tracker.h",
-    "src/core/lib/iomgr/polling_entity.h",
-    "src/core/lib/iomgr/pollset.h",
-    "src/core/lib/iomgr/pollset_set.h",
-    "src/core/lib/iomgr/pollset_set_windows.h",
-    "src/core/lib/iomgr/pollset_uv.h",
-    "src/core/lib/iomgr/pollset_windows.h",
-    "src/core/lib/iomgr/port.h",
-    "src/core/lib/iomgr/resolve_address.h",
-    "src/core/lib/iomgr/resource_quota.h",
-    "src/core/lib/iomgr/sockaddr.h",
-    "src/core/lib/iomgr/sockaddr_posix.h",
-    "src/core/lib/iomgr/sockaddr_utils.h",
-    "src/core/lib/iomgr/sockaddr_windows.h",
-    "src/core/lib/iomgr/socket_mutator.h",
-    "src/core/lib/iomgr/socket_utils.h",
-    "src/core/lib/iomgr/socket_utils_posix.h",
-    "src/core/lib/iomgr/socket_windows.h",
-    "src/core/lib/iomgr/tcp_client.h",
-    "src/core/lib/iomgr/tcp_client_posix.h",
-    "src/core/lib/iomgr/tcp_posix.h",
-    "src/core/lib/iomgr/tcp_server.h",
-    "src/core/lib/iomgr/tcp_uv.h",
-    "src/core/lib/iomgr/tcp_windows.h",
-    "src/core/lib/iomgr/time_averaged_stats.h",
-    "src/core/lib/iomgr/timer.h",
-    "src/core/lib/iomgr/timer_generic.h",
-    "src/core/lib/iomgr/timer_heap.h",
-    "src/core/lib/iomgr/timer_uv.h",
-    "src/core/lib/iomgr/udp_server.h",
-    "src/core/lib/iomgr/unix_sockets_posix.h",
-    "src/core/lib/iomgr/wakeup_fd_cv.h",
-    "src/core/lib/iomgr/wakeup_fd_pipe.h",
-    "src/core/lib/iomgr/wakeup_fd_posix.h",
-    "src/core/lib/iomgr/workqueue.h",
-    "src/core/lib/iomgr/workqueue_uv.h",
-    "src/core/lib/iomgr/workqueue_windows.h",
-    "src/core/lib/json/json.h",
-    "src/core/lib/json/json_common.h",
-    "src/core/lib/json/json_reader.h",
-    "src/core/lib/json/json_writer.h",
-    "src/core/lib/slice/percent_encoding.h",
-    "src/core/lib/slice/slice_string_helpers.h",
-    "src/core/lib/surface/api_trace.h",
-    "src/core/lib/surface/call.h",
-    "src/core/lib/surface/call_test_only.h",
-    "src/core/lib/surface/channel.h",
-    "src/core/lib/surface/channel_init.h",
-    "src/core/lib/surface/channel_stack_type.h",
-    "src/core/lib/surface/completion_queue.h",
-    "src/core/lib/surface/event_string.h",
-    "src/core/lib/surface/init.h",
-    "src/core/lib/surface/lame_client.h",
-    "src/core/lib/surface/server.h",
-    "src/core/lib/transport/byte_stream.h",
-    "src/core/lib/transport/connectivity_state.h",
-    "src/core/lib/transport/mdstr_hash_table.h",
-    "src/core/lib/transport/metadata.h",
-    "src/core/lib/transport/metadata_batch.h",
-    "src/core/lib/transport/pid_controller.h",
-    "src/core/lib/transport/service_config.h",
-    "src/core/lib/transport/static_metadata.h",
-    "src/core/lib/transport/timeout_encoding.h",
-    "src/core/lib/transport/transport.h",
-    "src/core/lib/transport/transport_impl.h",
-    "third_party/objective_c/Cronet/cronet_c_for_grpc.h",
-    "src/core/ext/transport/chttp2/transport/bin_decoder.h",
-    "src/core/ext/transport/chttp2/transport/bin_encoder.h",
-    "src/core/ext/transport/chttp2/transport/chttp2_transport.h",
-    "src/core/ext/transport/chttp2/transport/frame.h",
-    "src/core/ext/transport/chttp2/transport/frame_data.h",
-    "src/core/ext/transport/chttp2/transport/frame_goaway.h",
-    "src/core/ext/transport/chttp2/transport/frame_ping.h",
-    "src/core/ext/transport/chttp2/transport/frame_rst_stream.h",
-    "src/core/ext/transport/chttp2/transport/frame_settings.h",
-    "src/core/ext/transport/chttp2/transport/frame_window_update.h",
-    "src/core/ext/transport/chttp2/transport/hpack_encoder.h",
-    "src/core/ext/transport/chttp2/transport/hpack_parser.h",
-    "src/core/ext/transport/chttp2/transport/hpack_table.h",
-    "src/core/ext/transport/chttp2/transport/http2_errors.h",
-    "src/core/ext/transport/chttp2/transport/huffsyms.h",
-    "src/core/ext/transport/chttp2/transport/incoming_metadata.h",
-    "src/core/ext/transport/chttp2/transport/internal.h",
-    "src/core/ext/transport/chttp2/transport/status_conversion.h",
-    "src/core/ext/transport/chttp2/transport/stream_map.h",
-    "src/core/ext/transport/chttp2/transport/varint.h",
-    "src/core/ext/transport/chttp2/alpn/alpn.h",
-    "src/core/ext/client_channel/client_channel.h",
-    "src/core/ext/client_channel/client_channel_factory.h",
-    "src/core/ext/client_channel/connector.h",
-    "src/core/ext/client_channel/http_connect_handshaker.h",
-    "src/core/ext/client_channel/initial_connect_string.h",
-    "src/core/ext/client_channel/lb_policy.h",
-    "src/core/ext/client_channel/lb_policy_factory.h",
-    "src/core/ext/client_channel/lb_policy_registry.h",
-    "src/core/ext/client_channel/parse_address.h",
-    "src/core/ext/client_channel/resolver.h",
-    "src/core/ext/client_channel/resolver_factory.h",
-    "src/core/ext/client_channel/resolver_registry.h",
-    "src/core/ext/client_channel/subchannel.h",
-    "src/core/ext/client_channel/subchannel_index.h",
-    "src/core/ext/client_channel/uri_parser.h",
-    "src/core/lib/security/context/security_context.h",
-    "src/core/lib/security/credentials/composite/composite_credentials.h",
-    "src/core/lib/security/credentials/credentials.h",
-    "src/core/lib/security/credentials/fake/fake_credentials.h",
-    "src/core/lib/security/credentials/google_default/google_default_credentials.h",
-    "src/core/lib/security/credentials/iam/iam_credentials.h",
-    "src/core/lib/security/credentials/jwt/json_token.h",
-    "src/core/lib/security/credentials/jwt/jwt_credentials.h",
-    "src/core/lib/security/credentials/jwt/jwt_verifier.h",
-    "src/core/lib/security/credentials/oauth2/oauth2_credentials.h",
-    "src/core/lib/security/credentials/plugin/plugin_credentials.h",
-    "src/core/lib/security/credentials/ssl/ssl_credentials.h",
-    "src/core/lib/security/transport/auth_filters.h",
-    "src/core/lib/security/transport/handshake.h",
-    "src/core/lib/security/transport/secure_endpoint.h",
-    "src/core/lib/security/transport/security_connector.h",
-    "src/core/lib/security/transport/tsi_error.h",
-    "src/core/lib/security/util/b64.h",
-    "src/core/lib/security/util/json_util.h",
-    "src/core/lib/tsi/fake_transport_security.h",
-    "src/core/lib/tsi/ssl_transport_security.h",
-    "src/core/lib/tsi/ssl_types.h",
-    "src/core/lib/tsi/transport_security.h",
-    "src/core/lib/tsi/transport_security_interface.h",
-    "src/core/lib/surface/init.c",
-    "src/core/lib/channel/channel_args.c",
-    "src/core/lib/channel/channel_stack.c",
-    "src/core/lib/channel/channel_stack_builder.c",
-    "src/core/lib/channel/compress_filter.c",
-    "src/core/lib/channel/connected_channel.c",
-    "src/core/lib/channel/deadline_filter.c",
-    "src/core/lib/channel/handshaker.c",
-    "src/core/lib/channel/http_client_filter.c",
-    "src/core/lib/channel/http_server_filter.c",
-    "src/core/lib/channel/message_size_filter.c",
-    "src/core/lib/compression/compression.c",
-    "src/core/lib/compression/message_compress.c",
-    "src/core/lib/debug/trace.c",
-    "src/core/lib/http/format_request.c",
-    "src/core/lib/http/httpcli.c",
-    "src/core/lib/http/parser.c",
-    "src/core/lib/iomgr/closure.c",
-    "src/core/lib/iomgr/combiner.c",
-    "src/core/lib/iomgr/endpoint.c",
-    "src/core/lib/iomgr/endpoint_pair_posix.c",
-    "src/core/lib/iomgr/endpoint_pair_uv.c",
-    "src/core/lib/iomgr/endpoint_pair_windows.c",
-    "src/core/lib/iomgr/error.c",
-    "src/core/lib/iomgr/ev_epoll_linux.c",
-    "src/core/lib/iomgr/ev_poll_posix.c",
-    "src/core/lib/iomgr/ev_posix.c",
-    "src/core/lib/iomgr/exec_ctx.c",
-    "src/core/lib/iomgr/executor.c",
-    "src/core/lib/iomgr/iocp_windows.c",
-    "src/core/lib/iomgr/iomgr.c",
-    "src/core/lib/iomgr/iomgr_posix.c",
-    "src/core/lib/iomgr/iomgr_uv.c",
-    "src/core/lib/iomgr/iomgr_windows.c",
-    "src/core/lib/iomgr/load_file.c",
-    "src/core/lib/iomgr/network_status_tracker.c",
-    "src/core/lib/iomgr/polling_entity.c",
-    "src/core/lib/iomgr/pollset_set_uv.c",
-    "src/core/lib/iomgr/pollset_set_windows.c",
-    "src/core/lib/iomgr/pollset_uv.c",
-    "src/core/lib/iomgr/pollset_windows.c",
-    "src/core/lib/iomgr/resolve_address_posix.c",
-    "src/core/lib/iomgr/resolve_address_uv.c",
-    "src/core/lib/iomgr/resolve_address_windows.c",
-    "src/core/lib/iomgr/resource_quota.c",
-    "src/core/lib/iomgr/sockaddr_utils.c",
-    "src/core/lib/iomgr/socket_mutator.c",
-    "src/core/lib/iomgr/socket_utils_common_posix.c",
-    "src/core/lib/iomgr/socket_utils_linux.c",
-    "src/core/lib/iomgr/socket_utils_posix.c",
-    "src/core/lib/iomgr/socket_utils_uv.c",
-    "src/core/lib/iomgr/socket_utils_windows.c",
-    "src/core/lib/iomgr/socket_windows.c",
-    "src/core/lib/iomgr/tcp_client_posix.c",
-    "src/core/lib/iomgr/tcp_client_uv.c",
-    "src/core/lib/iomgr/tcp_client_windows.c",
-    "src/core/lib/iomgr/tcp_posix.c",
-    "src/core/lib/iomgr/tcp_server_posix.c",
-    "src/core/lib/iomgr/tcp_server_uv.c",
-    "src/core/lib/iomgr/tcp_server_windows.c",
-    "src/core/lib/iomgr/tcp_uv.c",
-    "src/core/lib/iomgr/tcp_windows.c",
-    "src/core/lib/iomgr/time_averaged_stats.c",
-    "src/core/lib/iomgr/timer_generic.c",
-    "src/core/lib/iomgr/timer_heap.c",
-    "src/core/lib/iomgr/timer_uv.c",
-    "src/core/lib/iomgr/udp_server.c",
-    "src/core/lib/iomgr/unix_sockets_posix.c",
-    "src/core/lib/iomgr/unix_sockets_posix_noop.c",
-    "src/core/lib/iomgr/wakeup_fd_cv.c",
-    "src/core/lib/iomgr/wakeup_fd_eventfd.c",
-    "src/core/lib/iomgr/wakeup_fd_nospecial.c",
-    "src/core/lib/iomgr/wakeup_fd_pipe.c",
-    "src/core/lib/iomgr/wakeup_fd_posix.c",
-    "src/core/lib/iomgr/workqueue_uv.c",
-    "src/core/lib/iomgr/workqueue_windows.c",
-    "src/core/lib/json/json.c",
-    "src/core/lib/json/json_reader.c",
-    "src/core/lib/json/json_string.c",
-    "src/core/lib/json/json_writer.c",
-    "src/core/lib/slice/percent_encoding.c",
-    "src/core/lib/slice/slice.c",
-    "src/core/lib/slice/slice_buffer.c",
-    "src/core/lib/slice/slice_string_helpers.c",
-    "src/core/lib/surface/alarm.c",
-    "src/core/lib/surface/api_trace.c",
-    "src/core/lib/surface/byte_buffer.c",
-    "src/core/lib/surface/byte_buffer_reader.c",
-    "src/core/lib/surface/call.c",
-    "src/core/lib/surface/call_details.c",
-    "src/core/lib/surface/call_log_batch.c",
-    "src/core/lib/surface/channel.c",
-    "src/core/lib/surface/channel_init.c",
-    "src/core/lib/surface/channel_ping.c",
-    "src/core/lib/surface/channel_stack_type.c",
-    "src/core/lib/surface/completion_queue.c",
-    "src/core/lib/surface/event_string.c",
-    "src/core/lib/surface/lame_client.c",
-    "src/core/lib/surface/metadata_array.c",
-    "src/core/lib/surface/server.c",
-    "src/core/lib/surface/validate_metadata.c",
-    "src/core/lib/surface/version.c",
-    "src/core/lib/transport/byte_stream.c",
-    "src/core/lib/transport/connectivity_state.c",
-    "src/core/lib/transport/mdstr_hash_table.c",
-    "src/core/lib/transport/metadata.c",
-    "src/core/lib/transport/metadata_batch.c",
-    "src/core/lib/transport/pid_controller.c",
-    "src/core/lib/transport/service_config.c",
-    "src/core/lib/transport/static_metadata.c",
-    "src/core/lib/transport/timeout_encoding.c",
-    "src/core/lib/transport/transport.c",
-    "src/core/lib/transport/transport_op_string.c",
-    "src/core/ext/transport/cronet/client/secure/cronet_channel_create.c",
-    "src/core/ext/transport/cronet/transport/cronet_api_dummy.c",
-    "src/core/ext/transport/cronet/transport/cronet_transport.c",
-    "src/core/ext/transport/chttp2/client/secure/secure_channel_create.c",
-    "src/core/ext/transport/chttp2/transport/bin_decoder.c",
-    "src/core/ext/transport/chttp2/transport/bin_encoder.c",
-    "src/core/ext/transport/chttp2/transport/chttp2_plugin.c",
-    "src/core/ext/transport/chttp2/transport/chttp2_transport.c",
-    "src/core/ext/transport/chttp2/transport/frame_data.c",
-    "src/core/ext/transport/chttp2/transport/frame_goaway.c",
-    "src/core/ext/transport/chttp2/transport/frame_ping.c",
-    "src/core/ext/transport/chttp2/transport/frame_rst_stream.c",
-    "src/core/ext/transport/chttp2/transport/frame_settings.c",
-    "src/core/ext/transport/chttp2/transport/frame_window_update.c",
-    "src/core/ext/transport/chttp2/transport/hpack_encoder.c",
-    "src/core/ext/transport/chttp2/transport/hpack_parser.c",
-    "src/core/ext/transport/chttp2/transport/hpack_table.c",
-    "src/core/ext/transport/chttp2/transport/huffsyms.c",
-    "src/core/ext/transport/chttp2/transport/incoming_metadata.c",
-    "src/core/ext/transport/chttp2/transport/parsing.c",
-    "src/core/ext/transport/chttp2/transport/status_conversion.c",
-    "src/core/ext/transport/chttp2/transport/stream_lists.c",
-    "src/core/ext/transport/chttp2/transport/stream_map.c",
-    "src/core/ext/transport/chttp2/transport/varint.c",
-    "src/core/ext/transport/chttp2/transport/writing.c",
-    "src/core/ext/transport/chttp2/alpn/alpn.c",
-    "src/core/ext/client_channel/channel_connectivity.c",
-    "src/core/ext/client_channel/client_channel.c",
-    "src/core/ext/client_channel/client_channel_factory.c",
-    "src/core/ext/client_channel/client_channel_plugin.c",
-    "src/core/ext/client_channel/connector.c",
-    "src/core/ext/client_channel/default_initial_connect_string.c",
-    "src/core/ext/client_channel/http_connect_handshaker.c",
-    "src/core/ext/client_channel/initial_connect_string.c",
-    "src/core/ext/client_channel/lb_policy.c",
-    "src/core/ext/client_channel/lb_policy_factory.c",
-    "src/core/ext/client_channel/lb_policy_registry.c",
-    "src/core/ext/client_channel/parse_address.c",
-    "src/core/ext/client_channel/resolver.c",
-    "src/core/ext/client_channel/resolver_factory.c",
-    "src/core/ext/client_channel/resolver_registry.c",
-    "src/core/ext/client_channel/subchannel.c",
-    "src/core/ext/client_channel/subchannel_index.c",
-    "src/core/ext/client_channel/uri_parser.c",
-    "src/core/lib/http/httpcli_security_connector.c",
-    "src/core/lib/security/context/security_context.c",
-    "src/core/lib/security/credentials/composite/composite_credentials.c",
-    "src/core/lib/security/credentials/credentials.c",
-    "src/core/lib/security/credentials/credentials_metadata.c",
-    "src/core/lib/security/credentials/fake/fake_credentials.c",
-    "src/core/lib/security/credentials/google_default/credentials_generic.c",
-    "src/core/lib/security/credentials/google_default/google_default_credentials.c",
-    "src/core/lib/security/credentials/iam/iam_credentials.c",
-    "src/core/lib/security/credentials/jwt/json_token.c",
-    "src/core/lib/security/credentials/jwt/jwt_credentials.c",
-    "src/core/lib/security/credentials/jwt/jwt_verifier.c",
-    "src/core/lib/security/credentials/oauth2/oauth2_credentials.c",
-    "src/core/lib/security/credentials/plugin/plugin_credentials.c",
-    "src/core/lib/security/credentials/ssl/ssl_credentials.c",
-    "src/core/lib/security/transport/client_auth_filter.c",
-    "src/core/lib/security/transport/handshake.c",
-    "src/core/lib/security/transport/secure_endpoint.c",
-    "src/core/lib/security/transport/security_connector.c",
-    "src/core/lib/security/transport/server_auth_filter.c",
-    "src/core/lib/security/transport/tsi_error.c",
-    "src/core/lib/security/util/b64.c",
-    "src/core/lib/security/util/json_util.c",
-    "src/core/lib/surface/init_secure.c",
-    "src/core/lib/tsi/fake_transport_security.c",
-    "src/core/lib/tsi/ssl_transport_security.c",
-    "src/core/lib/tsi/transport_security.c",
-    "src/core/plugin_registry/grpc_cronet_plugin_registry.c",
-  ],
-  hdrs = [
-    "include/grpc/byte_buffer.h",
-    "include/grpc/byte_buffer_reader.h",
-    "include/grpc/compression.h",
-    "include/grpc/grpc.h",
-    "include/grpc/grpc_posix.h",
-    "include/grpc/grpc_security_constants.h",
-    "include/grpc/slice.h",
-    "include/grpc/slice_buffer.h",
-    "include/grpc/status.h",
-    "include/grpc/impl/codegen/byte_buffer_reader.h",
-    "include/grpc/impl/codegen/compression_types.h",
-    "include/grpc/impl/codegen/connectivity_state.h",
-    "include/grpc/impl/codegen/grpc_types.h",
-    "include/grpc/impl/codegen/propagation_bits.h",
-    "include/grpc/impl/codegen/status.h",
-    "include/grpc/impl/codegen/atm.h",
-    "include/grpc/impl/codegen/atm_gcc_atomic.h",
-    "include/grpc/impl/codegen/atm_gcc_sync.h",
-    "include/grpc/impl/codegen/atm_windows.h",
-    "include/grpc/impl/codegen/gpr_types.h",
-    "include/grpc/impl/codegen/port_platform.h",
-    "include/grpc/impl/codegen/slice.h",
-    "include/grpc/impl/codegen/sync.h",
-    "include/grpc/impl/codegen/sync_generic.h",
-    "include/grpc/impl/codegen/sync_posix.h",
-    "include/grpc/impl/codegen/sync_windows.h",
-    "include/grpc/grpc_cronet.h",
-    "include/grpc/grpc_security.h",
-  ],
-  includes = [
-    "include",
-    ".",
-  ],
-  deps = [
-    "//external:libssl",
-    ":gpr",
-  ],
+grpc_cc_library(
+    name = "grpc_cronet",
+    srcs = [
+        "src/core/lib/surface/init.c",
+        "src/core/plugin_registry/grpc_cronet_plugin_registry.c",
+    ],
+    language = "c",
+    deps = [
+        "grpc_base",
+        "grpc_transport_chttp2_client_secure",
+        "grpc_transport_cronet_client_secure",
+    ],
 )
 
+grpc_cc_library(
+    name = "grpc_unsecure",
+    srcs = [
+        "src/core/lib/surface/init.c",
+        "src/core/lib/surface/init_unsecure.c",
+        "src/core/plugin_registry/grpc_unsecure_plugin_registry.c",
+    ],
+    language = "c",
+    standalone = True,
+    deps = [
+        "census",
+        "grpc_base",
+        "grpc_lb_policy_grpclb",
+        "grpc_lb_policy_pick_first",
+        "grpc_lb_policy_round_robin",
+        "grpc_load_reporting",
+        "grpc_resolver_dns_native",
+        "grpc_resolver_sockaddr",
+        "grpc_transport_chttp2_client_insecure",
+        "grpc_transport_chttp2_server_insecure",
+    ],
+)
 
+grpc_cc_library(
+    name = "grpc++",
+    srcs = [
+        "src/cpp/client/insecure_credentials.cc",
+        "src/cpp/client/secure_credentials.cc",
+        "src/cpp/common/auth_property_iterator.cc",
+        "src/cpp/common/secure_auth_context.cc",
+        "src/cpp/common/secure_channel_arguments.cc",
+        "src/cpp/common/secure_create_auth_context.cc",
+        "src/cpp/server/insecure_server_credentials.cc",
+        "src/cpp/server/secure_server_credentials.cc",
+    ],
+    hdrs = [
+        "include/grpc++/impl/codegen/core_codegen.h",
+        "src/cpp/client/secure_credentials.h",
+        "src/cpp/common/secure_auth_context.h",
+        "src/cpp/server/secure_server_credentials.h",
+    ],
+    language = "c++",
+    standalone = True,
+    deps = [
+        "gpr",
+        "grpc",
+        "grpc++_base",
+        "grpc++_codegen_base",
+        "grpc++_codegen_base_src",
+    ],
+)
 
-cc_library(
-  name = "grpc_unsecure",
-  srcs = [
-    "src/core/lib/channel/channel_args.h",
-    "src/core/lib/channel/channel_stack.h",
-    "src/core/lib/channel/channel_stack_builder.h",
-    "src/core/lib/channel/compress_filter.h",
-    "src/core/lib/channel/connected_channel.h",
-    "src/core/lib/channel/context.h",
-    "src/core/lib/channel/deadline_filter.h",
-    "src/core/lib/channel/handshaker.h",
-    "src/core/lib/channel/http_client_filter.h",
-    "src/core/lib/channel/http_server_filter.h",
-    "src/core/lib/channel/message_size_filter.h",
-    "src/core/lib/compression/algorithm_metadata.h",
-    "src/core/lib/compression/message_compress.h",
-    "src/core/lib/debug/trace.h",
-    "src/core/lib/http/format_request.h",
-    "src/core/lib/http/httpcli.h",
-    "src/core/lib/http/parser.h",
-    "src/core/lib/iomgr/closure.h",
-    "src/core/lib/iomgr/combiner.h",
-    "src/core/lib/iomgr/endpoint.h",
-    "src/core/lib/iomgr/endpoint_pair.h",
-    "src/core/lib/iomgr/error.h",
-    "src/core/lib/iomgr/ev_epoll_linux.h",
-    "src/core/lib/iomgr/ev_poll_posix.h",
-    "src/core/lib/iomgr/ev_posix.h",
-    "src/core/lib/iomgr/exec_ctx.h",
-    "src/core/lib/iomgr/executor.h",
-    "src/core/lib/iomgr/iocp_windows.h",
-    "src/core/lib/iomgr/iomgr.h",
-    "src/core/lib/iomgr/iomgr_internal.h",
-    "src/core/lib/iomgr/iomgr_posix.h",
-    "src/core/lib/iomgr/load_file.h",
-    "src/core/lib/iomgr/network_status_tracker.h",
-    "src/core/lib/iomgr/polling_entity.h",
-    "src/core/lib/iomgr/pollset.h",
-    "src/core/lib/iomgr/pollset_set.h",
-    "src/core/lib/iomgr/pollset_set_windows.h",
-    "src/core/lib/iomgr/pollset_uv.h",
-    "src/core/lib/iomgr/pollset_windows.h",
-    "src/core/lib/iomgr/port.h",
-    "src/core/lib/iomgr/resolve_address.h",
-    "src/core/lib/iomgr/resource_quota.h",
-    "src/core/lib/iomgr/sockaddr.h",
-    "src/core/lib/iomgr/sockaddr_posix.h",
-    "src/core/lib/iomgr/sockaddr_utils.h",
-    "src/core/lib/iomgr/sockaddr_windows.h",
-    "src/core/lib/iomgr/socket_mutator.h",
-    "src/core/lib/iomgr/socket_utils.h",
-    "src/core/lib/iomgr/socket_utils_posix.h",
-    "src/core/lib/iomgr/socket_windows.h",
-    "src/core/lib/iomgr/tcp_client.h",
-    "src/core/lib/iomgr/tcp_client_posix.h",
-    "src/core/lib/iomgr/tcp_posix.h",
-    "src/core/lib/iomgr/tcp_server.h",
-    "src/core/lib/iomgr/tcp_uv.h",
-    "src/core/lib/iomgr/tcp_windows.h",
-    "src/core/lib/iomgr/time_averaged_stats.h",
-    "src/core/lib/iomgr/timer.h",
-    "src/core/lib/iomgr/timer_generic.h",
-    "src/core/lib/iomgr/timer_heap.h",
-    "src/core/lib/iomgr/timer_uv.h",
-    "src/core/lib/iomgr/udp_server.h",
-    "src/core/lib/iomgr/unix_sockets_posix.h",
-    "src/core/lib/iomgr/wakeup_fd_cv.h",
-    "src/core/lib/iomgr/wakeup_fd_pipe.h",
-    "src/core/lib/iomgr/wakeup_fd_posix.h",
-    "src/core/lib/iomgr/workqueue.h",
-    "src/core/lib/iomgr/workqueue_uv.h",
-    "src/core/lib/iomgr/workqueue_windows.h",
-    "src/core/lib/json/json.h",
-    "src/core/lib/json/json_common.h",
-    "src/core/lib/json/json_reader.h",
-    "src/core/lib/json/json_writer.h",
-    "src/core/lib/slice/percent_encoding.h",
-    "src/core/lib/slice/slice_string_helpers.h",
-    "src/core/lib/surface/api_trace.h",
-    "src/core/lib/surface/call.h",
-    "src/core/lib/surface/call_test_only.h",
-    "src/core/lib/surface/channel.h",
-    "src/core/lib/surface/channel_init.h",
-    "src/core/lib/surface/channel_stack_type.h",
-    "src/core/lib/surface/completion_queue.h",
-    "src/core/lib/surface/event_string.h",
-    "src/core/lib/surface/init.h",
-    "src/core/lib/surface/lame_client.h",
-    "src/core/lib/surface/server.h",
-    "src/core/lib/transport/byte_stream.h",
-    "src/core/lib/transport/connectivity_state.h",
-    "src/core/lib/transport/mdstr_hash_table.h",
-    "src/core/lib/transport/metadata.h",
-    "src/core/lib/transport/metadata_batch.h",
-    "src/core/lib/transport/pid_controller.h",
-    "src/core/lib/transport/service_config.h",
-    "src/core/lib/transport/static_metadata.h",
-    "src/core/lib/transport/timeout_encoding.h",
-    "src/core/lib/transport/transport.h",
-    "src/core/lib/transport/transport_impl.h",
-    "src/core/ext/transport/chttp2/transport/bin_decoder.h",
-    "src/core/ext/transport/chttp2/transport/bin_encoder.h",
-    "src/core/ext/transport/chttp2/transport/chttp2_transport.h",
-    "src/core/ext/transport/chttp2/transport/frame.h",
-    "src/core/ext/transport/chttp2/transport/frame_data.h",
-    "src/core/ext/transport/chttp2/transport/frame_goaway.h",
-    "src/core/ext/transport/chttp2/transport/frame_ping.h",
-    "src/core/ext/transport/chttp2/transport/frame_rst_stream.h",
-    "src/core/ext/transport/chttp2/transport/frame_settings.h",
-    "src/core/ext/transport/chttp2/transport/frame_window_update.h",
-    "src/core/ext/transport/chttp2/transport/hpack_encoder.h",
-    "src/core/ext/transport/chttp2/transport/hpack_parser.h",
-    "src/core/ext/transport/chttp2/transport/hpack_table.h",
-    "src/core/ext/transport/chttp2/transport/http2_errors.h",
-    "src/core/ext/transport/chttp2/transport/huffsyms.h",
-    "src/core/ext/transport/chttp2/transport/incoming_metadata.h",
-    "src/core/ext/transport/chttp2/transport/internal.h",
-    "src/core/ext/transport/chttp2/transport/status_conversion.h",
-    "src/core/ext/transport/chttp2/transport/stream_map.h",
-    "src/core/ext/transport/chttp2/transport/varint.h",
-    "src/core/ext/transport/chttp2/alpn/alpn.h",
-    "src/core/ext/client_channel/client_channel.h",
-    "src/core/ext/client_channel/client_channel_factory.h",
-    "src/core/ext/client_channel/connector.h",
-    "src/core/ext/client_channel/http_connect_handshaker.h",
-    "src/core/ext/client_channel/initial_connect_string.h",
-    "src/core/ext/client_channel/lb_policy.h",
-    "src/core/ext/client_channel/lb_policy_factory.h",
-    "src/core/ext/client_channel/lb_policy_registry.h",
-    "src/core/ext/client_channel/parse_address.h",
-    "src/core/ext/client_channel/resolver.h",
-    "src/core/ext/client_channel/resolver_factory.h",
-    "src/core/ext/client_channel/resolver_registry.h",
-    "src/core/ext/client_channel/subchannel.h",
-    "src/core/ext/client_channel/subchannel_index.h",
-    "src/core/ext/client_channel/uri_parser.h",
-    "src/core/ext/load_reporting/load_reporting.h",
-    "src/core/ext/load_reporting/load_reporting_filter.h",
-    "src/core/ext/lb_policy/grpclb/grpclb.h",
-    "src/core/ext/lb_policy/grpclb/load_balancer_api.h",
-    "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h",
-    "src/core/ext/census/aggregation.h",
-    "src/core/ext/census/base_resources.h",
-    "src/core/ext/census/census_interface.h",
-    "src/core/ext/census/census_rpc_stats.h",
-    "src/core/ext/census/gen/census.pb.h",
-    "src/core/ext/census/gen/trace_context.pb.h",
-    "src/core/ext/census/grpc_filter.h",
-    "src/core/ext/census/mlog.h",
-    "src/core/ext/census/resource.h",
-    "src/core/ext/census/rpc_metric_id.h",
-    "src/core/ext/census/trace_context.h",
-    "src/core/lib/surface/init.c",
-    "src/core/lib/surface/init_unsecure.c",
-    "src/core/lib/channel/channel_args.c",
-    "src/core/lib/channel/channel_stack.c",
-    "src/core/lib/channel/channel_stack_builder.c",
-    "src/core/lib/channel/compress_filter.c",
-    "src/core/lib/channel/connected_channel.c",
-    "src/core/lib/channel/deadline_filter.c",
-    "src/core/lib/channel/handshaker.c",
-    "src/core/lib/channel/http_client_filter.c",
-    "src/core/lib/channel/http_server_filter.c",
-    "src/core/lib/channel/message_size_filter.c",
-    "src/core/lib/compression/compression.c",
-    "src/core/lib/compression/message_compress.c",
-    "src/core/lib/debug/trace.c",
-    "src/core/lib/http/format_request.c",
-    "src/core/lib/http/httpcli.c",
-    "src/core/lib/http/parser.c",
-    "src/core/lib/iomgr/closure.c",
-    "src/core/lib/iomgr/combiner.c",
-    "src/core/lib/iomgr/endpoint.c",
-    "src/core/lib/iomgr/endpoint_pair_posix.c",
-    "src/core/lib/iomgr/endpoint_pair_uv.c",
-    "src/core/lib/iomgr/endpoint_pair_windows.c",
-    "src/core/lib/iomgr/error.c",
-    "src/core/lib/iomgr/ev_epoll_linux.c",
-    "src/core/lib/iomgr/ev_poll_posix.c",
-    "src/core/lib/iomgr/ev_posix.c",
-    "src/core/lib/iomgr/exec_ctx.c",
-    "src/core/lib/iomgr/executor.c",
-    "src/core/lib/iomgr/iocp_windows.c",
-    "src/core/lib/iomgr/iomgr.c",
-    "src/core/lib/iomgr/iomgr_posix.c",
-    "src/core/lib/iomgr/iomgr_uv.c",
-    "src/core/lib/iomgr/iomgr_windows.c",
-    "src/core/lib/iomgr/load_file.c",
-    "src/core/lib/iomgr/network_status_tracker.c",
-    "src/core/lib/iomgr/polling_entity.c",
-    "src/core/lib/iomgr/pollset_set_uv.c",
-    "src/core/lib/iomgr/pollset_set_windows.c",
-    "src/core/lib/iomgr/pollset_uv.c",
-    "src/core/lib/iomgr/pollset_windows.c",
-    "src/core/lib/iomgr/resolve_address_posix.c",
-    "src/core/lib/iomgr/resolve_address_uv.c",
-    "src/core/lib/iomgr/resolve_address_windows.c",
-    "src/core/lib/iomgr/resource_quota.c",
-    "src/core/lib/iomgr/sockaddr_utils.c",
-    "src/core/lib/iomgr/socket_mutator.c",
-    "src/core/lib/iomgr/socket_utils_common_posix.c",
-    "src/core/lib/iomgr/socket_utils_linux.c",
-    "src/core/lib/iomgr/socket_utils_posix.c",
-    "src/core/lib/iomgr/socket_utils_uv.c",
-    "src/core/lib/iomgr/socket_utils_windows.c",
-    "src/core/lib/iomgr/socket_windows.c",
-    "src/core/lib/iomgr/tcp_client_posix.c",
-    "src/core/lib/iomgr/tcp_client_uv.c",
-    "src/core/lib/iomgr/tcp_client_windows.c",
-    "src/core/lib/iomgr/tcp_posix.c",
-    "src/core/lib/iomgr/tcp_server_posix.c",
-    "src/core/lib/iomgr/tcp_server_uv.c",
-    "src/core/lib/iomgr/tcp_server_windows.c",
-    "src/core/lib/iomgr/tcp_uv.c",
-    "src/core/lib/iomgr/tcp_windows.c",
-    "src/core/lib/iomgr/time_averaged_stats.c",
-    "src/core/lib/iomgr/timer_generic.c",
-    "src/core/lib/iomgr/timer_heap.c",
-    "src/core/lib/iomgr/timer_uv.c",
-    "src/core/lib/iomgr/udp_server.c",
-    "src/core/lib/iomgr/unix_sockets_posix.c",
-    "src/core/lib/iomgr/unix_sockets_posix_noop.c",
-    "src/core/lib/iomgr/wakeup_fd_cv.c",
-    "src/core/lib/iomgr/wakeup_fd_eventfd.c",
-    "src/core/lib/iomgr/wakeup_fd_nospecial.c",
-    "src/core/lib/iomgr/wakeup_fd_pipe.c",
-    "src/core/lib/iomgr/wakeup_fd_posix.c",
-    "src/core/lib/iomgr/workqueue_uv.c",
-    "src/core/lib/iomgr/workqueue_windows.c",
-    "src/core/lib/json/json.c",
-    "src/core/lib/json/json_reader.c",
-    "src/core/lib/json/json_string.c",
-    "src/core/lib/json/json_writer.c",
-    "src/core/lib/slice/percent_encoding.c",
-    "src/core/lib/slice/slice.c",
-    "src/core/lib/slice/slice_buffer.c",
-    "src/core/lib/slice/slice_string_helpers.c",
-    "src/core/lib/surface/alarm.c",
-    "src/core/lib/surface/api_trace.c",
-    "src/core/lib/surface/byte_buffer.c",
-    "src/core/lib/surface/byte_buffer_reader.c",
-    "src/core/lib/surface/call.c",
-    "src/core/lib/surface/call_details.c",
-    "src/core/lib/surface/call_log_batch.c",
-    "src/core/lib/surface/channel.c",
-    "src/core/lib/surface/channel_init.c",
-    "src/core/lib/surface/channel_ping.c",
-    "src/core/lib/surface/channel_stack_type.c",
-    "src/core/lib/surface/completion_queue.c",
-    "src/core/lib/surface/event_string.c",
-    "src/core/lib/surface/lame_client.c",
-    "src/core/lib/surface/metadata_array.c",
-    "src/core/lib/surface/server.c",
-    "src/core/lib/surface/validate_metadata.c",
-    "src/core/lib/surface/version.c",
-    "src/core/lib/transport/byte_stream.c",
-    "src/core/lib/transport/connectivity_state.c",
-    "src/core/lib/transport/mdstr_hash_table.c",
-    "src/core/lib/transport/metadata.c",
-    "src/core/lib/transport/metadata_batch.c",
-    "src/core/lib/transport/pid_controller.c",
-    "src/core/lib/transport/service_config.c",
-    "src/core/lib/transport/static_metadata.c",
-    "src/core/lib/transport/timeout_encoding.c",
-    "src/core/lib/transport/transport.c",
-    "src/core/lib/transport/transport_op_string.c",
-    "src/core/ext/transport/chttp2/server/insecure/server_chttp2.c",
-    "src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c",
-    "src/core/ext/transport/chttp2/transport/bin_decoder.c",
-    "src/core/ext/transport/chttp2/transport/bin_encoder.c",
-    "src/core/ext/transport/chttp2/transport/chttp2_plugin.c",
-    "src/core/ext/transport/chttp2/transport/chttp2_transport.c",
-    "src/core/ext/transport/chttp2/transport/frame_data.c",
-    "src/core/ext/transport/chttp2/transport/frame_goaway.c",
-    "src/core/ext/transport/chttp2/transport/frame_ping.c",
-    "src/core/ext/transport/chttp2/transport/frame_rst_stream.c",
-    "src/core/ext/transport/chttp2/transport/frame_settings.c",
-    "src/core/ext/transport/chttp2/transport/frame_window_update.c",
-    "src/core/ext/transport/chttp2/transport/hpack_encoder.c",
-    "src/core/ext/transport/chttp2/transport/hpack_parser.c",
-    "src/core/ext/transport/chttp2/transport/hpack_table.c",
-    "src/core/ext/transport/chttp2/transport/huffsyms.c",
-    "src/core/ext/transport/chttp2/transport/incoming_metadata.c",
-    "src/core/ext/transport/chttp2/transport/parsing.c",
-    "src/core/ext/transport/chttp2/transport/status_conversion.c",
-    "src/core/ext/transport/chttp2/transport/stream_lists.c",
-    "src/core/ext/transport/chttp2/transport/stream_map.c",
-    "src/core/ext/transport/chttp2/transport/varint.c",
-    "src/core/ext/transport/chttp2/transport/writing.c",
-    "src/core/ext/transport/chttp2/alpn/alpn.c",
-    "src/core/ext/transport/chttp2/client/insecure/channel_create.c",
-    "src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c",
-    "src/core/ext/client_channel/channel_connectivity.c",
-    "src/core/ext/client_channel/client_channel.c",
-    "src/core/ext/client_channel/client_channel_factory.c",
-    "src/core/ext/client_channel/client_channel_plugin.c",
-    "src/core/ext/client_channel/connector.c",
-    "src/core/ext/client_channel/default_initial_connect_string.c",
-    "src/core/ext/client_channel/http_connect_handshaker.c",
-    "src/core/ext/client_channel/initial_connect_string.c",
-    "src/core/ext/client_channel/lb_policy.c",
-    "src/core/ext/client_channel/lb_policy_factory.c",
-    "src/core/ext/client_channel/lb_policy_registry.c",
-    "src/core/ext/client_channel/parse_address.c",
-    "src/core/ext/client_channel/resolver.c",
-    "src/core/ext/client_channel/resolver_factory.c",
-    "src/core/ext/client_channel/resolver_registry.c",
-    "src/core/ext/client_channel/subchannel.c",
-    "src/core/ext/client_channel/subchannel_index.c",
-    "src/core/ext/client_channel/uri_parser.c",
-    "src/core/ext/resolver/dns/native/dns_resolver.c",
-    "src/core/ext/resolver/sockaddr/sockaddr_resolver.c",
-    "src/core/ext/load_reporting/load_reporting.c",
-    "src/core/ext/load_reporting/load_reporting_filter.c",
-    "src/core/ext/lb_policy/grpclb/grpclb.c",
-    "src/core/ext/lb_policy/grpclb/load_balancer_api.c",
-    "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c",
-    "src/core/ext/lb_policy/pick_first/pick_first.c",
-    "src/core/ext/lb_policy/round_robin/round_robin.c",
-    "src/core/ext/census/base_resources.c",
-    "src/core/ext/census/context.c",
-    "src/core/ext/census/gen/census.pb.c",
-    "src/core/ext/census/gen/trace_context.pb.c",
-    "src/core/ext/census/grpc_context.c",
-    "src/core/ext/census/grpc_filter.c",
-    "src/core/ext/census/grpc_plugin.c",
-    "src/core/ext/census/initialize.c",
-    "src/core/ext/census/mlog.c",
-    "src/core/ext/census/operation.c",
-    "src/core/ext/census/placeholders.c",
-    "src/core/ext/census/resource.c",
-    "src/core/ext/census/trace_context.c",
-    "src/core/ext/census/tracing.c",
-    "src/core/plugin_registry/grpc_unsecure_plugin_registry.c",
-  ],
-  hdrs = [
-    "include/grpc/byte_buffer.h",
-    "include/grpc/byte_buffer_reader.h",
-    "include/grpc/compression.h",
-    "include/grpc/grpc.h",
-    "include/grpc/grpc_posix.h",
-    "include/grpc/grpc_security_constants.h",
-    "include/grpc/slice.h",
-    "include/grpc/slice_buffer.h",
-    "include/grpc/status.h",
-    "include/grpc/impl/codegen/byte_buffer_reader.h",
-    "include/grpc/impl/codegen/compression_types.h",
-    "include/grpc/impl/codegen/connectivity_state.h",
-    "include/grpc/impl/codegen/grpc_types.h",
-    "include/grpc/impl/codegen/propagation_bits.h",
-    "include/grpc/impl/codegen/status.h",
-    "include/grpc/impl/codegen/atm.h",
-    "include/grpc/impl/codegen/atm_gcc_atomic.h",
-    "include/grpc/impl/codegen/atm_gcc_sync.h",
-    "include/grpc/impl/codegen/atm_windows.h",
-    "include/grpc/impl/codegen/gpr_types.h",
-    "include/grpc/impl/codegen/port_platform.h",
-    "include/grpc/impl/codegen/slice.h",
-    "include/grpc/impl/codegen/sync.h",
-    "include/grpc/impl/codegen/sync_generic.h",
-    "include/grpc/impl/codegen/sync_posix.h",
-    "include/grpc/impl/codegen/sync_windows.h",
-    "include/grpc/census.h",
-  ],
-  includes = [
-    "include",
-    ".",
-  ],
-  deps = [
-    ":gpr",
-    "//external:nanopb",
-  ],
-  copts = [
-    "-std=gnu99",
-  ],
+grpc_cc_library(
+    name = "grpc++_unsecure",
+    srcs = [
+        "src/cpp/client/insecure_credentials.cc",
+        "src/cpp/common/insecure_create_auth_context.cc",
+        "src/cpp/server/insecure_server_credentials.cc",
+    ],
+    language = "c++",
+    standalone = True,
+    deps = [
+        "gpr",
+        "grpc++_base",
+        "grpc++_codegen_base",
+        "grpc++_codegen_base_src",
+        "grpc_unsecure",
+    ],
 )
 
+grpc_cc_library(
+    name = "grpc_plugin_support",
+    srcs = [
+        "src/compiler/cpp_generator.cc",
+        "src/compiler/csharp_generator.cc",
+        "src/compiler/node_generator.cc",
+        "src/compiler/objective_c_generator.cc",
+        "src/compiler/php_generator.cc",
+        "src/compiler/python_generator.cc",
+        "src/compiler/ruby_generator.cc",
+    ],
+    hdrs = [
+        "src/compiler/config.h",
+        "src/compiler/cpp_generator.h",
+        "src/compiler/cpp_generator_helpers.h",
+        "src/compiler/csharp_generator.h",
+        "src/compiler/csharp_generator_helpers.h",
+        "src/compiler/generator_helpers.h",
+        "src/compiler/node_generator.h",
+        "src/compiler/node_generator_helpers.h",
+        "src/compiler/objective_c_generator.h",
+        "src/compiler/objective_c_generator_helpers.h",
+        "src/compiler/php_generator.h",
+        "src/compiler/php_generator_helpers.h",
+        "src/compiler/python_generator.h",
+        "src/compiler/ruby_generator.h",
+        "src/compiler/ruby_generator_helpers-inl.h",
+        "src/compiler/ruby_generator_map-inl.h",
+        "src/compiler/ruby_generator_string-inl.h",
+    ],
+    external_deps = [
+        "protobuf_clib",
+    ],
+    language = "c++",
+    deps = [
+        "grpc++_config_proto",
+    ],
+)
 
+grpc_proto_plugin(
+    name = "grpc_cpp_plugin",
+    srcs = ["src/compiler/cpp_plugin.cc"],
+    deps = [":grpc_plugin_support"],
+)
 
-cc_library(
-  name = "grpc++",
-  srcs = [
-    "include/grpc++/impl/codegen/core_codegen.h",
-    "src/cpp/client/secure_credentials.h",
-    "src/cpp/common/secure_auth_context.h",
-    "src/cpp/server/secure_server_credentials.h",
-    "src/cpp/client/create_channel_internal.h",
-    "src/cpp/common/channel_filter.h",
-    "src/cpp/server/dynamic_thread_pool.h",
-    "src/cpp/server/thread_pool_interface.h",
-    "src/cpp/thread_manager/thread_manager.h",
-    "src/cpp/client/insecure_credentials.cc",
-    "src/cpp/client/secure_credentials.cc",
-    "src/cpp/common/auth_property_iterator.cc",
-    "src/cpp/common/secure_auth_context.cc",
-    "src/cpp/common/secure_channel_arguments.cc",
-    "src/cpp/common/secure_create_auth_context.cc",
-    "src/cpp/server/insecure_server_credentials.cc",
-    "src/cpp/server/secure_server_credentials.cc",
-    "src/cpp/client/channel_cc.cc",
-    "src/cpp/client/client_context.cc",
-    "src/cpp/client/create_channel.cc",
-    "src/cpp/client/create_channel_internal.cc",
-    "src/cpp/client/create_channel_posix.cc",
-    "src/cpp/client/credentials_cc.cc",
-    "src/cpp/client/generic_stub.cc",
-    "src/cpp/common/channel_arguments.cc",
-    "src/cpp/common/channel_filter.cc",
-    "src/cpp/common/completion_queue_cc.cc",
-    "src/cpp/common/core_codegen.cc",
-    "src/cpp/common/resource_quota_cc.cc",
-    "src/cpp/common/rpc_method.cc",
-    "src/cpp/common/version_cc.cc",
-    "src/cpp/server/async_generic_service.cc",
-    "src/cpp/server/create_default_thread_pool.cc",
-    "src/cpp/server/dynamic_thread_pool.cc",
-    "src/cpp/server/server_builder.cc",
-    "src/cpp/server/server_cc.cc",
-    "src/cpp/server/server_context.cc",
-    "src/cpp/server/server_credentials.cc",
-    "src/cpp/server/server_posix.cc",
-    "src/cpp/thread_manager/thread_manager.cc",
-    "src/cpp/util/byte_buffer_cc.cc",
-    "src/cpp/util/slice_cc.cc",
-    "src/cpp/util/status.cc",
-    "src/cpp/util/string_ref.cc",
-    "src/cpp/util/time_cc.cc",
-    "src/cpp/codegen/codegen_init.cc",
-  ],
-  hdrs = [
-    "include/grpc++/alarm.h",
-    "include/grpc++/channel.h",
-    "include/grpc++/client_context.h",
-    "include/grpc++/completion_queue.h",
-    "include/grpc++/create_channel.h",
-    "include/grpc++/create_channel_posix.h",
-    "include/grpc++/generic/async_generic_service.h",
-    "include/grpc++/generic/generic_stub.h",
-    "include/grpc++/grpc++.h",
-    "include/grpc++/impl/call.h",
-    "include/grpc++/impl/client_unary_call.h",
-    "include/grpc++/impl/codegen/core_codegen.h",
-    "include/grpc++/impl/grpc_library.h",
-    "include/grpc++/impl/method_handler_impl.h",
-    "include/grpc++/impl/rpc_method.h",
-    "include/grpc++/impl/rpc_service_method.h",
-    "include/grpc++/impl/serialization_traits.h",
-    "include/grpc++/impl/server_builder_option.h",
-    "include/grpc++/impl/server_builder_plugin.h",
-    "include/grpc++/impl/server_initializer.h",
-    "include/grpc++/impl/service_type.h",
-    "include/grpc++/resource_quota.h",
-    "include/grpc++/security/auth_context.h",
-    "include/grpc++/security/auth_metadata_processor.h",
-    "include/grpc++/security/credentials.h",
-    "include/grpc++/security/server_credentials.h",
-    "include/grpc++/server.h",
-    "include/grpc++/server_builder.h",
-    "include/grpc++/server_context.h",
-    "include/grpc++/server_posix.h",
-    "include/grpc++/support/async_stream.h",
-    "include/grpc++/support/async_unary_call.h",
-    "include/grpc++/support/byte_buffer.h",
-    "include/grpc++/support/channel_arguments.h",
-    "include/grpc++/support/config.h",
-    "include/grpc++/support/slice.h",
-    "include/grpc++/support/status.h",
-    "include/grpc++/support/status_code_enum.h",
-    "include/grpc++/support/string_ref.h",
-    "include/grpc++/support/stub_options.h",
-    "include/grpc++/support/sync_stream.h",
-    "include/grpc++/support/time.h",
-    "include/grpc++/impl/codegen/async_stream.h",
-    "include/grpc++/impl/codegen/async_unary_call.h",
-    "include/grpc++/impl/codegen/call.h",
-    "include/grpc++/impl/codegen/call_hook.h",
-    "include/grpc++/impl/codegen/channel_interface.h",
-    "include/grpc++/impl/codegen/client_context.h",
-    "include/grpc++/impl/codegen/client_unary_call.h",
-    "include/grpc++/impl/codegen/completion_queue.h",
-    "include/grpc++/impl/codegen/completion_queue_tag.h",
-    "include/grpc++/impl/codegen/config.h",
-    "include/grpc++/impl/codegen/core_codegen_interface.h",
-    "include/grpc++/impl/codegen/create_auth_context.h",
-    "include/grpc++/impl/codegen/grpc_library.h",
-    "include/grpc++/impl/codegen/method_handler_impl.h",
-    "include/grpc++/impl/codegen/rpc_method.h",
-    "include/grpc++/impl/codegen/rpc_service_method.h",
-    "include/grpc++/impl/codegen/security/auth_context.h",
-    "include/grpc++/impl/codegen/serialization_traits.h",
-    "include/grpc++/impl/codegen/server_context.h",
-    "include/grpc++/impl/codegen/server_interface.h",
-    "include/grpc++/impl/codegen/service_type.h",
-    "include/grpc++/impl/codegen/status.h",
-    "include/grpc++/impl/codegen/status_code_enum.h",
-    "include/grpc++/impl/codegen/status_helper.h",
-    "include/grpc++/impl/codegen/string_ref.h",
-    "include/grpc++/impl/codegen/stub_options.h",
-    "include/grpc++/impl/codegen/sync_stream.h",
-    "include/grpc++/impl/codegen/time.h",
-    "include/grpc/impl/codegen/byte_buffer_reader.h",
-    "include/grpc/impl/codegen/compression_types.h",
-    "include/grpc/impl/codegen/connectivity_state.h",
-    "include/grpc/impl/codegen/grpc_types.h",
-    "include/grpc/impl/codegen/propagation_bits.h",
-    "include/grpc/impl/codegen/status.h",
-    "include/grpc/impl/codegen/atm.h",
-    "include/grpc/impl/codegen/atm_gcc_atomic.h",
-    "include/grpc/impl/codegen/atm_gcc_sync.h",
-    "include/grpc/impl/codegen/atm_windows.h",
-    "include/grpc/impl/codegen/gpr_types.h",
-    "include/grpc/impl/codegen/port_platform.h",
-    "include/grpc/impl/codegen/slice.h",
-    "include/grpc/impl/codegen/sync.h",
-    "include/grpc/impl/codegen/sync_generic.h",
-    "include/grpc/impl/codegen/sync_posix.h",
-    "include/grpc/impl/codegen/sync_windows.h",
-  ],
-  includes = [
-    "include",
-    ".",
-  ],
-  deps = [
-    "//external:libssl",
-    "//external:protobuf_clib",
-    ":grpc",
-  ],
+grpc_proto_plugin(
+    name = "grpc_csharp_plugin",
+    srcs = ["src/compiler/csharp_plugin.cc"],
+    deps = [":grpc_plugin_support"],
 )
 
+grpc_proto_plugin(
+    name = "grpc_node_plugin",
+    srcs = ["src/compiler/node_plugin.cc"],
+    deps = [":grpc_plugin_support"],
+)
 
+grpc_proto_plugin(
+    name = "grpc_objective_c_plugin",
+    srcs = ["src/compiler/objective_c_plugin.cc"],
+    deps = [":grpc_plugin_support"],
+)
 
-cc_library(
-  name = "grpc++_cronet",
-  srcs = [
-    "src/cpp/client/create_channel_internal.h",
-    "src/cpp/common/channel_filter.h",
-    "src/cpp/server/dynamic_thread_pool.h",
-    "src/cpp/server/thread_pool_interface.h",
-    "src/cpp/thread_manager/thread_manager.h",
-    "src/core/ext/transport/chttp2/transport/bin_decoder.h",
-    "src/core/ext/transport/chttp2/transport/bin_encoder.h",
-    "src/core/ext/transport/chttp2/transport/chttp2_transport.h",
-    "src/core/ext/transport/chttp2/transport/frame.h",
-    "src/core/ext/transport/chttp2/transport/frame_data.h",
-    "src/core/ext/transport/chttp2/transport/frame_goaway.h",
-    "src/core/ext/transport/chttp2/transport/frame_ping.h",
-    "src/core/ext/transport/chttp2/transport/frame_rst_stream.h",
-    "src/core/ext/transport/chttp2/transport/frame_settings.h",
-    "src/core/ext/transport/chttp2/transport/frame_window_update.h",
-    "src/core/ext/transport/chttp2/transport/hpack_encoder.h",
-    "src/core/ext/transport/chttp2/transport/hpack_parser.h",
-    "src/core/ext/transport/chttp2/transport/hpack_table.h",
-    "src/core/ext/transport/chttp2/transport/http2_errors.h",
-    "src/core/ext/transport/chttp2/transport/huffsyms.h",
-    "src/core/ext/transport/chttp2/transport/incoming_metadata.h",
-    "src/core/ext/transport/chttp2/transport/internal.h",
-    "src/core/ext/transport/chttp2/transport/status_conversion.h",
-    "src/core/ext/transport/chttp2/transport/stream_map.h",
-    "src/core/ext/transport/chttp2/transport/varint.h",
-    "src/core/lib/channel/channel_args.h",
-    "src/core/lib/channel/channel_stack.h",
-    "src/core/lib/channel/channel_stack_builder.h",
-    "src/core/lib/channel/compress_filter.h",
-    "src/core/lib/channel/connected_channel.h",
-    "src/core/lib/channel/context.h",
-    "src/core/lib/channel/deadline_filter.h",
-    "src/core/lib/channel/handshaker.h",
-    "src/core/lib/channel/http_client_filter.h",
-    "src/core/lib/channel/http_server_filter.h",
-    "src/core/lib/channel/message_size_filter.h",
-    "src/core/lib/compression/algorithm_metadata.h",
-    "src/core/lib/compression/message_compress.h",
-    "src/core/lib/debug/trace.h",
-    "src/core/lib/http/format_request.h",
-    "src/core/lib/http/httpcli.h",
-    "src/core/lib/http/parser.h",
-    "src/core/lib/iomgr/closure.h",
-    "src/core/lib/iomgr/combiner.h",
-    "src/core/lib/iomgr/endpoint.h",
-    "src/core/lib/iomgr/endpoint_pair.h",
-    "src/core/lib/iomgr/error.h",
-    "src/core/lib/iomgr/ev_epoll_linux.h",
-    "src/core/lib/iomgr/ev_poll_posix.h",
-    "src/core/lib/iomgr/ev_posix.h",
-    "src/core/lib/iomgr/exec_ctx.h",
-    "src/core/lib/iomgr/executor.h",
-    "src/core/lib/iomgr/iocp_windows.h",
-    "src/core/lib/iomgr/iomgr.h",
-    "src/core/lib/iomgr/iomgr_internal.h",
-    "src/core/lib/iomgr/iomgr_posix.h",
-    "src/core/lib/iomgr/load_file.h",
-    "src/core/lib/iomgr/network_status_tracker.h",
-    "src/core/lib/iomgr/polling_entity.h",
-    "src/core/lib/iomgr/pollset.h",
-    "src/core/lib/iomgr/pollset_set.h",
-    "src/core/lib/iomgr/pollset_set_windows.h",
-    "src/core/lib/iomgr/pollset_uv.h",
-    "src/core/lib/iomgr/pollset_windows.h",
-    "src/core/lib/iomgr/port.h",
-    "src/core/lib/iomgr/resolve_address.h",
-    "src/core/lib/iomgr/resource_quota.h",
-    "src/core/lib/iomgr/sockaddr.h",
-    "src/core/lib/iomgr/sockaddr_posix.h",
-    "src/core/lib/iomgr/sockaddr_utils.h",
-    "src/core/lib/iomgr/sockaddr_windows.h",
-    "src/core/lib/iomgr/socket_mutator.h",
-    "src/core/lib/iomgr/socket_utils.h",
-    "src/core/lib/iomgr/socket_utils_posix.h",
-    "src/core/lib/iomgr/socket_windows.h",
-    "src/core/lib/iomgr/tcp_client.h",
-    "src/core/lib/iomgr/tcp_client_posix.h",
-    "src/core/lib/iomgr/tcp_posix.h",
-    "src/core/lib/iomgr/tcp_server.h",
-    "src/core/lib/iomgr/tcp_uv.h",
-    "src/core/lib/iomgr/tcp_windows.h",
-    "src/core/lib/iomgr/time_averaged_stats.h",
-    "src/core/lib/iomgr/timer.h",
-    "src/core/lib/iomgr/timer_generic.h",
-    "src/core/lib/iomgr/timer_heap.h",
-    "src/core/lib/iomgr/timer_uv.h",
-    "src/core/lib/iomgr/udp_server.h",
-    "src/core/lib/iomgr/unix_sockets_posix.h",
-    "src/core/lib/iomgr/wakeup_fd_cv.h",
-    "src/core/lib/iomgr/wakeup_fd_pipe.h",
-    "src/core/lib/iomgr/wakeup_fd_posix.h",
-    "src/core/lib/iomgr/workqueue.h",
-    "src/core/lib/iomgr/workqueue_uv.h",
-    "src/core/lib/iomgr/workqueue_windows.h",
-    "src/core/lib/json/json.h",
-    "src/core/lib/json/json_common.h",
-    "src/core/lib/json/json_reader.h",
-    "src/core/lib/json/json_writer.h",
-    "src/core/lib/slice/percent_encoding.h",
-    "src/core/lib/slice/slice_string_helpers.h",
-    "src/core/lib/surface/api_trace.h",
-    "src/core/lib/surface/call.h",
-    "src/core/lib/surface/call_test_only.h",
-    "src/core/lib/surface/channel.h",
-    "src/core/lib/surface/channel_init.h",
-    "src/core/lib/surface/channel_stack_type.h",
-    "src/core/lib/surface/completion_queue.h",
-    "src/core/lib/surface/event_string.h",
-    "src/core/lib/surface/init.h",
-    "src/core/lib/surface/lame_client.h",
-    "src/core/lib/surface/server.h",
-    "src/core/lib/transport/byte_stream.h",
-    "src/core/lib/transport/connectivity_state.h",
-    "src/core/lib/transport/mdstr_hash_table.h",
-    "src/core/lib/transport/metadata.h",
-    "src/core/lib/transport/metadata_batch.h",
-    "src/core/lib/transport/pid_controller.h",
-    "src/core/lib/transport/service_config.h",
-    "src/core/lib/transport/static_metadata.h",
-    "src/core/lib/transport/timeout_encoding.h",
-    "src/core/lib/transport/transport.h",
-    "src/core/lib/transport/transport_impl.h",
-    "src/core/ext/transport/chttp2/alpn/alpn.h",
-    "src/core/ext/client_channel/client_channel.h",
-    "src/core/ext/client_channel/client_channel_factory.h",
-    "src/core/ext/client_channel/connector.h",
-    "src/core/ext/client_channel/http_connect_handshaker.h",
-    "src/core/ext/client_channel/initial_connect_string.h",
-    "src/core/ext/client_channel/lb_policy.h",
-    "src/core/ext/client_channel/lb_policy_factory.h",
-    "src/core/ext/client_channel/lb_policy_registry.h",
-    "src/core/ext/client_channel/parse_address.h",
-    "src/core/ext/client_channel/resolver.h",
-    "src/core/ext/client_channel/resolver_factory.h",
-    "src/core/ext/client_channel/resolver_registry.h",
-    "src/core/ext/client_channel/subchannel.h",
-    "src/core/ext/client_channel/subchannel_index.h",
-    "src/core/ext/client_channel/uri_parser.h",
-    "src/core/ext/census/aggregation.h",
-    "src/core/ext/census/base_resources.h",
-    "src/core/ext/census/census_interface.h",
-    "src/core/ext/census/census_rpc_stats.h",
-    "src/core/ext/census/gen/census.pb.h",
-    "src/core/ext/census/gen/trace_context.pb.h",
-    "src/core/ext/census/grpc_filter.h",
-    "src/core/ext/census/mlog.h",
-    "src/core/ext/census/resource.h",
-    "src/core/ext/census/rpc_metric_id.h",
-    "src/core/ext/census/trace_context.h",
-    "src/cpp/client/cronet_credentials.cc",
-    "src/cpp/client/insecure_credentials.cc",
-    "src/cpp/common/insecure_create_auth_context.cc",
-    "src/cpp/server/insecure_server_credentials.cc",
-    "src/cpp/client/channel_cc.cc",
-    "src/cpp/client/client_context.cc",
-    "src/cpp/client/create_channel.cc",
-    "src/cpp/client/create_channel_internal.cc",
-    "src/cpp/client/create_channel_posix.cc",
-    "src/cpp/client/credentials_cc.cc",
-    "src/cpp/client/generic_stub.cc",
-    "src/cpp/common/channel_arguments.cc",
-    "src/cpp/common/channel_filter.cc",
-    "src/cpp/common/completion_queue_cc.cc",
-    "src/cpp/common/core_codegen.cc",
-    "src/cpp/common/resource_quota_cc.cc",
-    "src/cpp/common/rpc_method.cc",
-    "src/cpp/common/version_cc.cc",
-    "src/cpp/server/async_generic_service.cc",
-    "src/cpp/server/create_default_thread_pool.cc",
-    "src/cpp/server/dynamic_thread_pool.cc",
-    "src/cpp/server/server_builder.cc",
-    "src/cpp/server/server_cc.cc",
-    "src/cpp/server/server_context.cc",
-    "src/cpp/server/server_credentials.cc",
-    "src/cpp/server/server_posix.cc",
-    "src/cpp/thread_manager/thread_manager.cc",
-    "src/cpp/util/byte_buffer_cc.cc",
-    "src/cpp/util/slice_cc.cc",
-    "src/cpp/util/status.cc",
-    "src/cpp/util/string_ref.cc",
-    "src/cpp/util/time_cc.cc",
-    "src/cpp/codegen/codegen_init.cc",
-    "src/core/ext/transport/chttp2/client/insecure/channel_create.c",
-    "src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c",
-    "src/core/ext/transport/chttp2/transport/bin_decoder.c",
-    "src/core/ext/transport/chttp2/transport/bin_encoder.c",
-    "src/core/ext/transport/chttp2/transport/chttp2_plugin.c",
-    "src/core/ext/transport/chttp2/transport/chttp2_transport.c",
-    "src/core/ext/transport/chttp2/transport/frame_data.c",
-    "src/core/ext/transport/chttp2/transport/frame_goaway.c",
-    "src/core/ext/transport/chttp2/transport/frame_ping.c",
-    "src/core/ext/transport/chttp2/transport/frame_rst_stream.c",
-    "src/core/ext/transport/chttp2/transport/frame_settings.c",
-    "src/core/ext/transport/chttp2/transport/frame_window_update.c",
-    "src/core/ext/transport/chttp2/transport/hpack_encoder.c",
-    "src/core/ext/transport/chttp2/transport/hpack_parser.c",
-    "src/core/ext/transport/chttp2/transport/hpack_table.c",
-    "src/core/ext/transport/chttp2/transport/huffsyms.c",
-    "src/core/ext/transport/chttp2/transport/incoming_metadata.c",
-    "src/core/ext/transport/chttp2/transport/parsing.c",
-    "src/core/ext/transport/chttp2/transport/status_conversion.c",
-    "src/core/ext/transport/chttp2/transport/stream_lists.c",
-    "src/core/ext/transport/chttp2/transport/stream_map.c",
-    "src/core/ext/transport/chttp2/transport/varint.c",
-    "src/core/ext/transport/chttp2/transport/writing.c",
-    "src/core/lib/channel/channel_args.c",
-    "src/core/lib/channel/channel_stack.c",
-    "src/core/lib/channel/channel_stack_builder.c",
-    "src/core/lib/channel/compress_filter.c",
-    "src/core/lib/channel/connected_channel.c",
-    "src/core/lib/channel/deadline_filter.c",
-    "src/core/lib/channel/handshaker.c",
-    "src/core/lib/channel/http_client_filter.c",
-    "src/core/lib/channel/http_server_filter.c",
-    "src/core/lib/channel/message_size_filter.c",
-    "src/core/lib/compression/compression.c",
-    "src/core/lib/compression/message_compress.c",
-    "src/core/lib/debug/trace.c",
-    "src/core/lib/http/format_request.c",
-    "src/core/lib/http/httpcli.c",
-    "src/core/lib/http/parser.c",
-    "src/core/lib/iomgr/closure.c",
-    "src/core/lib/iomgr/combiner.c",
-    "src/core/lib/iomgr/endpoint.c",
-    "src/core/lib/iomgr/endpoint_pair_posix.c",
-    "src/core/lib/iomgr/endpoint_pair_uv.c",
-    "src/core/lib/iomgr/endpoint_pair_windows.c",
-    "src/core/lib/iomgr/error.c",
-    "src/core/lib/iomgr/ev_epoll_linux.c",
-    "src/core/lib/iomgr/ev_poll_posix.c",
-    "src/core/lib/iomgr/ev_posix.c",
-    "src/core/lib/iomgr/exec_ctx.c",
-    "src/core/lib/iomgr/executor.c",
-    "src/core/lib/iomgr/iocp_windows.c",
-    "src/core/lib/iomgr/iomgr.c",
-    "src/core/lib/iomgr/iomgr_posix.c",
-    "src/core/lib/iomgr/iomgr_uv.c",
-    "src/core/lib/iomgr/iomgr_windows.c",
-    "src/core/lib/iomgr/load_file.c",
-    "src/core/lib/iomgr/network_status_tracker.c",
-    "src/core/lib/iomgr/polling_entity.c",
-    "src/core/lib/iomgr/pollset_set_uv.c",
-    "src/core/lib/iomgr/pollset_set_windows.c",
-    "src/core/lib/iomgr/pollset_uv.c",
-    "src/core/lib/iomgr/pollset_windows.c",
-    "src/core/lib/iomgr/resolve_address_posix.c",
-    "src/core/lib/iomgr/resolve_address_uv.c",
-    "src/core/lib/iomgr/resolve_address_windows.c",
-    "src/core/lib/iomgr/resource_quota.c",
-    "src/core/lib/iomgr/sockaddr_utils.c",
-    "src/core/lib/iomgr/socket_mutator.c",
-    "src/core/lib/iomgr/socket_utils_common_posix.c",
-    "src/core/lib/iomgr/socket_utils_linux.c",
-    "src/core/lib/iomgr/socket_utils_posix.c",
-    "src/core/lib/iomgr/socket_utils_uv.c",
-    "src/core/lib/iomgr/socket_utils_windows.c",
-    "src/core/lib/iomgr/socket_windows.c",
-    "src/core/lib/iomgr/tcp_client_posix.c",
-    "src/core/lib/iomgr/tcp_client_uv.c",
-    "src/core/lib/iomgr/tcp_client_windows.c",
-    "src/core/lib/iomgr/tcp_posix.c",
-    "src/core/lib/iomgr/tcp_server_posix.c",
-    "src/core/lib/iomgr/tcp_server_uv.c",
-    "src/core/lib/iomgr/tcp_server_windows.c",
-    "src/core/lib/iomgr/tcp_uv.c",
-    "src/core/lib/iomgr/tcp_windows.c",
-    "src/core/lib/iomgr/time_averaged_stats.c",
-    "src/core/lib/iomgr/timer_generic.c",
-    "src/core/lib/iomgr/timer_heap.c",
-    "src/core/lib/iomgr/timer_uv.c",
-    "src/core/lib/iomgr/udp_server.c",
-    "src/core/lib/iomgr/unix_sockets_posix.c",
-    "src/core/lib/iomgr/unix_sockets_posix_noop.c",
-    "src/core/lib/iomgr/wakeup_fd_cv.c",
-    "src/core/lib/iomgr/wakeup_fd_eventfd.c",
-    "src/core/lib/iomgr/wakeup_fd_nospecial.c",
-    "src/core/lib/iomgr/wakeup_fd_pipe.c",
-    "src/core/lib/iomgr/wakeup_fd_posix.c",
-    "src/core/lib/iomgr/workqueue_uv.c",
-    "src/core/lib/iomgr/workqueue_windows.c",
-    "src/core/lib/json/json.c",
-    "src/core/lib/json/json_reader.c",
-    "src/core/lib/json/json_string.c",
-    "src/core/lib/json/json_writer.c",
-    "src/core/lib/slice/percent_encoding.c",
-    "src/core/lib/slice/slice.c",
-    "src/core/lib/slice/slice_buffer.c",
-    "src/core/lib/slice/slice_string_helpers.c",
-    "src/core/lib/surface/alarm.c",
-    "src/core/lib/surface/api_trace.c",
-    "src/core/lib/surface/byte_buffer.c",
-    "src/core/lib/surface/byte_buffer_reader.c",
-    "src/core/lib/surface/call.c",
-    "src/core/lib/surface/call_details.c",
-    "src/core/lib/surface/call_log_batch.c",
-    "src/core/lib/surface/channel.c",
-    "src/core/lib/surface/channel_init.c",
-    "src/core/lib/surface/channel_ping.c",
-    "src/core/lib/surface/channel_stack_type.c",
-    "src/core/lib/surface/completion_queue.c",
-    "src/core/lib/surface/event_string.c",
-    "src/core/lib/surface/lame_client.c",
-    "src/core/lib/surface/metadata_array.c",
-    "src/core/lib/surface/server.c",
-    "src/core/lib/surface/validate_metadata.c",
-    "src/core/lib/surface/version.c",
-    "src/core/lib/transport/byte_stream.c",
-    "src/core/lib/transport/connectivity_state.c",
-    "src/core/lib/transport/mdstr_hash_table.c",
-    "src/core/lib/transport/metadata.c",
-    "src/core/lib/transport/metadata_batch.c",
-    "src/core/lib/transport/pid_controller.c",
-    "src/core/lib/transport/service_config.c",
-    "src/core/lib/transport/static_metadata.c",
-    "src/core/lib/transport/timeout_encoding.c",
-    "src/core/lib/transport/transport.c",
-    "src/core/lib/transport/transport_op_string.c",
-    "src/core/ext/transport/chttp2/alpn/alpn.c",
-    "src/core/ext/client_channel/channel_connectivity.c",
-    "src/core/ext/client_channel/client_channel.c",
-    "src/core/ext/client_channel/client_channel_factory.c",
-    "src/core/ext/client_channel/client_channel_plugin.c",
-    "src/core/ext/client_channel/connector.c",
-    "src/core/ext/client_channel/default_initial_connect_string.c",
-    "src/core/ext/client_channel/http_connect_handshaker.c",
-    "src/core/ext/client_channel/initial_connect_string.c",
-    "src/core/ext/client_channel/lb_policy.c",
-    "src/core/ext/client_channel/lb_policy_factory.c",
-    "src/core/ext/client_channel/lb_policy_registry.c",
-    "src/core/ext/client_channel/parse_address.c",
-    "src/core/ext/client_channel/resolver.c",
-    "src/core/ext/client_channel/resolver_factory.c",
-    "src/core/ext/client_channel/resolver_registry.c",
-    "src/core/ext/client_channel/subchannel.c",
-    "src/core/ext/client_channel/subchannel_index.c",
-    "src/core/ext/client_channel/uri_parser.c",
-    "src/core/ext/transport/chttp2/server/insecure/server_chttp2.c",
-    "src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c",
-    "src/core/ext/census/base_resources.c",
-    "src/core/ext/census/context.c",
-    "src/core/ext/census/gen/census.pb.c",
-    "src/core/ext/census/gen/trace_context.pb.c",
-    "src/core/ext/census/grpc_context.c",
-    "src/core/ext/census/grpc_filter.c",
-    "src/core/ext/census/grpc_plugin.c",
-    "src/core/ext/census/initialize.c",
-    "src/core/ext/census/mlog.c",
-    "src/core/ext/census/operation.c",
-    "src/core/ext/census/placeholders.c",
-    "src/core/ext/census/resource.c",
-    "src/core/ext/census/trace_context.c",
-    "src/core/ext/census/tracing.c",
-  ],
-  hdrs = [
-    "include/grpc++/alarm.h",
-    "include/grpc++/channel.h",
-    "include/grpc++/client_context.h",
-    "include/grpc++/completion_queue.h",
-    "include/grpc++/create_channel.h",
-    "include/grpc++/create_channel_posix.h",
-    "include/grpc++/generic/async_generic_service.h",
-    "include/grpc++/generic/generic_stub.h",
-    "include/grpc++/grpc++.h",
-    "include/grpc++/impl/call.h",
-    "include/grpc++/impl/client_unary_call.h",
-    "include/grpc++/impl/codegen/core_codegen.h",
-    "include/grpc++/impl/grpc_library.h",
-    "include/grpc++/impl/method_handler_impl.h",
-    "include/grpc++/impl/rpc_method.h",
-    "include/grpc++/impl/rpc_service_method.h",
-    "include/grpc++/impl/serialization_traits.h",
-    "include/grpc++/impl/server_builder_option.h",
-    "include/grpc++/impl/server_builder_plugin.h",
-    "include/grpc++/impl/server_initializer.h",
-    "include/grpc++/impl/service_type.h",
-    "include/grpc++/resource_quota.h",
-    "include/grpc++/security/auth_context.h",
-    "include/grpc++/security/auth_metadata_processor.h",
-    "include/grpc++/security/credentials.h",
-    "include/grpc++/security/server_credentials.h",
-    "include/grpc++/server.h",
-    "include/grpc++/server_builder.h",
-    "include/grpc++/server_context.h",
-    "include/grpc++/server_posix.h",
-    "include/grpc++/support/async_stream.h",
-    "include/grpc++/support/async_unary_call.h",
-    "include/grpc++/support/byte_buffer.h",
-    "include/grpc++/support/channel_arguments.h",
-    "include/grpc++/support/config.h",
-    "include/grpc++/support/slice.h",
-    "include/grpc++/support/status.h",
-    "include/grpc++/support/status_code_enum.h",
-    "include/grpc++/support/string_ref.h",
-    "include/grpc++/support/stub_options.h",
-    "include/grpc++/support/sync_stream.h",
-    "include/grpc++/support/time.h",
-    "include/grpc++/impl/codegen/async_stream.h",
-    "include/grpc++/impl/codegen/async_unary_call.h",
-    "include/grpc++/impl/codegen/call.h",
-    "include/grpc++/impl/codegen/call_hook.h",
-    "include/grpc++/impl/codegen/channel_interface.h",
-    "include/grpc++/impl/codegen/client_context.h",
-    "include/grpc++/impl/codegen/client_unary_call.h",
-    "include/grpc++/impl/codegen/completion_queue.h",
-    "include/grpc++/impl/codegen/completion_queue_tag.h",
-    "include/grpc++/impl/codegen/config.h",
-    "include/grpc++/impl/codegen/core_codegen_interface.h",
-    "include/grpc++/impl/codegen/create_auth_context.h",
-    "include/grpc++/impl/codegen/grpc_library.h",
-    "include/grpc++/impl/codegen/method_handler_impl.h",
-    "include/grpc++/impl/codegen/rpc_method.h",
-    "include/grpc++/impl/codegen/rpc_service_method.h",
-    "include/grpc++/impl/codegen/security/auth_context.h",
-    "include/grpc++/impl/codegen/serialization_traits.h",
-    "include/grpc++/impl/codegen/server_context.h",
-    "include/grpc++/impl/codegen/server_interface.h",
-    "include/grpc++/impl/codegen/service_type.h",
-    "include/grpc++/impl/codegen/status.h",
-    "include/grpc++/impl/codegen/status_code_enum.h",
-    "include/grpc++/impl/codegen/status_helper.h",
-    "include/grpc++/impl/codegen/string_ref.h",
-    "include/grpc++/impl/codegen/stub_options.h",
-    "include/grpc++/impl/codegen/sync_stream.h",
-    "include/grpc++/impl/codegen/time.h",
-    "include/grpc/impl/codegen/byte_buffer_reader.h",
-    "include/grpc/impl/codegen/compression_types.h",
-    "include/grpc/impl/codegen/connectivity_state.h",
-    "include/grpc/impl/codegen/grpc_types.h",
-    "include/grpc/impl/codegen/propagation_bits.h",
-    "include/grpc/impl/codegen/status.h",
-    "include/grpc/impl/codegen/atm.h",
-    "include/grpc/impl/codegen/atm_gcc_atomic.h",
-    "include/grpc/impl/codegen/atm_gcc_sync.h",
-    "include/grpc/impl/codegen/atm_windows.h",
-    "include/grpc/impl/codegen/gpr_types.h",
-    "include/grpc/impl/codegen/port_platform.h",
-    "include/grpc/impl/codegen/slice.h",
-    "include/grpc/impl/codegen/sync.h",
-    "include/grpc/impl/codegen/sync_generic.h",
-    "include/grpc/impl/codegen/sync_posix.h",
-    "include/grpc/impl/codegen/sync_windows.h",
-    "include/grpc/byte_buffer.h",
-    "include/grpc/byte_buffer_reader.h",
-    "include/grpc/compression.h",
-    "include/grpc/grpc.h",
-    "include/grpc/grpc_posix.h",
-    "include/grpc/grpc_security_constants.h",
-    "include/grpc/slice.h",
-    "include/grpc/slice_buffer.h",
-    "include/grpc/status.h",
-    "include/grpc/census.h",
-  ],
-  includes = [
-    "include",
-    ".",
-  ],
-  deps = [
-    "//external:libssl",
-    ":gpr",
-    ":grpc_cronet",
-    "//external:nanopb",
-  ],
+grpc_proto_plugin(
+    name = "grpc_php_plugin",
+    srcs = ["src/compiler/php_plugin.cc"],
+    deps = [":grpc_plugin_support"],
 )
 
+grpc_proto_plugin(
+    name = "grpc_python_plugin",
+    srcs = ["src/compiler/python_plugin.cc"],
+    deps = [":grpc_plugin_support"],
+)
 
+grpc_proto_plugin(
+    name = "grpc_ruby_plugin",
+    srcs = ["src/compiler/ruby_plugin.cc"],
+    deps = [":grpc_plugin_support"],
+)
 
-cc_library(
-  name = "grpc++_reflection",
-  srcs = [
-    "src/cpp/ext/proto_server_reflection.h",
-    "src/cpp/ext/proto_server_reflection.cc",
-    "src/cpp/ext/proto_server_reflection_plugin.cc",
-    "src/proto/grpc/reflection/v1alpha/reflection.proto",
-  ],
-  hdrs = [
-    "include/grpc++/ext/proto_server_reflection_plugin.h",
-  ],
-  includes = [
-    "include",
-    ".",
-  ],
-  deps = [
-    ":grpc++",
-  ],
+grpc_cc_library(
+    name = "grpc_csharp_ext",
+    srcs = [
+        "src/csharp/ext/grpc_csharp_ext.c",
+    ],
+    language = "csharp",
+    deps = [
+        "gpr",
+        "grpc",
+    ],
 )
 
+grpc_cc_library(
+    name = "census",
+    srcs = [
+        "src/core/ext/census/base_resources.c",
+        "src/core/ext/census/context.c",
+        "src/core/ext/census/gen/census.pb.c",
+        "src/core/ext/census/gen/trace_context.pb.c",
+        "src/core/ext/census/grpc_context.c",
+        "src/core/ext/census/grpc_filter.c",
+        "src/core/ext/census/grpc_plugin.c",
+        "src/core/ext/census/initialize.c",
+        "src/core/ext/census/mlog.c",
+        "src/core/ext/census/operation.c",
+        "src/core/ext/census/placeholders.c",
+        "src/core/ext/census/resource.c",
+        "src/core/ext/census/trace_context.c",
+        "src/core/ext/census/tracing.c",
+    ],
+    hdrs = [
+        "src/core/ext/census/aggregation.h",
+        "src/core/ext/census/base_resources.h",
+        "src/core/ext/census/census_interface.h",
+        "src/core/ext/census/census_rpc_stats.h",
+        "src/core/ext/census/gen/census.pb.h",
+        "src/core/ext/census/gen/trace_context.pb.h",
+        "src/core/ext/census/grpc_filter.h",
+        "src/core/ext/census/mlog.h",
+        "src/core/ext/census/resource.h",
+        "src/core/ext/census/rpc_metric_id.h",
+        "src/core/ext/census/trace_context.h",
+    ],
+    external_deps = [
+        "nanopb",
+    ],
+    language = "c",
+    public_hdrs = [
+        "include/grpc/census.h",
+    ],
+    deps = [
+        "grpc_base",
+    ],
+)
 
+grpc_cc_library(
+    name = "gpr_base",
+    srcs = [
+        "src/core/lib/profiling/basic_timers.c",
+        "src/core/lib/profiling/stap_timers.c",
+        "src/core/lib/support/alloc.c",
+        "src/core/lib/support/avl.c",
+        "src/core/lib/support/backoff.c",
+        "src/core/lib/support/cmdline.c",
+        "src/core/lib/support/cpu_iphone.c",
+        "src/core/lib/support/cpu_linux.c",
+        "src/core/lib/support/cpu_posix.c",
+        "src/core/lib/support/cpu_windows.c",
+        "src/core/lib/support/env_linux.c",
+        "src/core/lib/support/env_posix.c",
+        "src/core/lib/support/env_windows.c",
+        "src/core/lib/support/histogram.c",
+        "src/core/lib/support/host_port.c",
+        "src/core/lib/support/log.c",
+        "src/core/lib/support/log_android.c",
+        "src/core/lib/support/log_linux.c",
+        "src/core/lib/support/log_posix.c",
+        "src/core/lib/support/log_windows.c",
+        "src/core/lib/support/mpscq.c",
+        "src/core/lib/support/murmur_hash.c",
+        "src/core/lib/support/stack_lockfree.c",
+        "src/core/lib/support/string.c",
+        "src/core/lib/support/string_posix.c",
+        "src/core/lib/support/string_util_windows.c",
+        "src/core/lib/support/string_windows.c",
+        "src/core/lib/support/subprocess_posix.c",
+        "src/core/lib/support/subprocess_windows.c",
+        "src/core/lib/support/sync.c",
+        "src/core/lib/support/sync_posix.c",
+        "src/core/lib/support/sync_windows.c",
+        "src/core/lib/support/thd.c",
+        "src/core/lib/support/thd_posix.c",
+        "src/core/lib/support/thd_windows.c",
+        "src/core/lib/support/time.c",
+        "src/core/lib/support/time_posix.c",
+        "src/core/lib/support/time_precise.c",
+        "src/core/lib/support/time_windows.c",
+        "src/core/lib/support/tls_pthread.c",
+        "src/core/lib/support/tmpfile_msys.c",
+        "src/core/lib/support/tmpfile_posix.c",
+        "src/core/lib/support/tmpfile_windows.c",
+        "src/core/lib/support/wrap_memcpy.c",
+    ],
+    hdrs = [
+        "src/core/lib/profiling/timers.h",
+        "src/core/lib/support/backoff.h",
+        "src/core/lib/support/block_annotate.h",
+        "src/core/lib/support/env.h",
+        "src/core/lib/support/mpscq.h",
+        "src/core/lib/support/murmur_hash.h",
+        "src/core/lib/support/stack_lockfree.h",
+        "src/core/lib/support/string.h",
+        "src/core/lib/support/string_windows.h",
+        "src/core/lib/support/thd_internal.h",
+        "src/core/lib/support/time_precise.h",
+        "src/core/lib/support/tmpfile.h",
+    ],
+    language = "c",
+    public_hdrs = [
+        "include/grpc/support/alloc.h",
+        "include/grpc/support/atm.h",
+        "include/grpc/support/atm_gcc_atomic.h",
+        "include/grpc/support/atm_gcc_sync.h",
+        "include/grpc/support/atm_windows.h",
+        "include/grpc/support/avl.h",
+        "include/grpc/support/cmdline.h",
+        "include/grpc/support/cpu.h",
+        "include/grpc/support/histogram.h",
+        "include/grpc/support/host_port.h",
+        "include/grpc/support/log.h",
+        "include/grpc/support/log_windows.h",
+        "include/grpc/support/port_platform.h",
+        "include/grpc/support/string_util.h",
+        "include/grpc/support/subprocess.h",
+        "include/grpc/support/sync.h",
+        "include/grpc/support/sync_generic.h",
+        "include/grpc/support/sync_posix.h",
+        "include/grpc/support/sync_windows.h",
+        "include/grpc/support/thd.h",
+        "include/grpc/support/time.h",
+        "include/grpc/support/tls.h",
+        "include/grpc/support/tls_gcc.h",
+        "include/grpc/support/tls_msvc.h",
+        "include/grpc/support/tls_pthread.h",
+        "include/grpc/support/useful.h",
+    ],
+    deps = [
+        "gpr_codegen",
+    ],
+)
 
-cc_library(
-  name = "grpc++_unsecure",
-  srcs = [
-    "src/cpp/client/create_channel_internal.h",
-    "src/cpp/common/channel_filter.h",
-    "src/cpp/server/dynamic_thread_pool.h",
-    "src/cpp/server/thread_pool_interface.h",
-    "src/cpp/thread_manager/thread_manager.h",
-    "src/cpp/client/insecure_credentials.cc",
-    "src/cpp/common/insecure_create_auth_context.cc",
-    "src/cpp/server/insecure_server_credentials.cc",
-    "src/cpp/client/channel_cc.cc",
-    "src/cpp/client/client_context.cc",
-    "src/cpp/client/create_channel.cc",
-    "src/cpp/client/create_channel_internal.cc",
-    "src/cpp/client/create_channel_posix.cc",
-    "src/cpp/client/credentials_cc.cc",
-    "src/cpp/client/generic_stub.cc",
-    "src/cpp/common/channel_arguments.cc",
-    "src/cpp/common/channel_filter.cc",
-    "src/cpp/common/completion_queue_cc.cc",
-    "src/cpp/common/core_codegen.cc",
-    "src/cpp/common/resource_quota_cc.cc",
-    "src/cpp/common/rpc_method.cc",
-    "src/cpp/common/version_cc.cc",
-    "src/cpp/server/async_generic_service.cc",
-    "src/cpp/server/create_default_thread_pool.cc",
-    "src/cpp/server/dynamic_thread_pool.cc",
-    "src/cpp/server/server_builder.cc",
-    "src/cpp/server/server_cc.cc",
-    "src/cpp/server/server_context.cc",
-    "src/cpp/server/server_credentials.cc",
-    "src/cpp/server/server_posix.cc",
-    "src/cpp/thread_manager/thread_manager.cc",
-    "src/cpp/util/byte_buffer_cc.cc",
-    "src/cpp/util/slice_cc.cc",
-    "src/cpp/util/status.cc",
-    "src/cpp/util/string_ref.cc",
-    "src/cpp/util/time_cc.cc",
-    "src/cpp/codegen/codegen_init.cc",
-  ],
-  hdrs = [
-    "include/grpc++/alarm.h",
-    "include/grpc++/channel.h",
-    "include/grpc++/client_context.h",
-    "include/grpc++/completion_queue.h",
-    "include/grpc++/create_channel.h",
-    "include/grpc++/create_channel_posix.h",
-    "include/grpc++/generic/async_generic_service.h",
-    "include/grpc++/generic/generic_stub.h",
-    "include/grpc++/grpc++.h",
-    "include/grpc++/impl/call.h",
-    "include/grpc++/impl/client_unary_call.h",
-    "include/grpc++/impl/codegen/core_codegen.h",
-    "include/grpc++/impl/grpc_library.h",
-    "include/grpc++/impl/method_handler_impl.h",
-    "include/grpc++/impl/rpc_method.h",
-    "include/grpc++/impl/rpc_service_method.h",
-    "include/grpc++/impl/serialization_traits.h",
-    "include/grpc++/impl/server_builder_option.h",
-    "include/grpc++/impl/server_builder_plugin.h",
-    "include/grpc++/impl/server_initializer.h",
-    "include/grpc++/impl/service_type.h",
-    "include/grpc++/resource_quota.h",
-    "include/grpc++/security/auth_context.h",
-    "include/grpc++/security/auth_metadata_processor.h",
-    "include/grpc++/security/credentials.h",
-    "include/grpc++/security/server_credentials.h",
-    "include/grpc++/server.h",
-    "include/grpc++/server_builder.h",
-    "include/grpc++/server_context.h",
-    "include/grpc++/server_posix.h",
-    "include/grpc++/support/async_stream.h",
-    "include/grpc++/support/async_unary_call.h",
-    "include/grpc++/support/byte_buffer.h",
-    "include/grpc++/support/channel_arguments.h",
-    "include/grpc++/support/config.h",
-    "include/grpc++/support/slice.h",
-    "include/grpc++/support/status.h",
-    "include/grpc++/support/status_code_enum.h",
-    "include/grpc++/support/string_ref.h",
-    "include/grpc++/support/stub_options.h",
-    "include/grpc++/support/sync_stream.h",
-    "include/grpc++/support/time.h",
-    "include/grpc++/impl/codegen/async_stream.h",
-    "include/grpc++/impl/codegen/async_unary_call.h",
-    "include/grpc++/impl/codegen/call.h",
-    "include/grpc++/impl/codegen/call_hook.h",
-    "include/grpc++/impl/codegen/channel_interface.h",
-    "include/grpc++/impl/codegen/client_context.h",
-    "include/grpc++/impl/codegen/client_unary_call.h",
-    "include/grpc++/impl/codegen/completion_queue.h",
-    "include/grpc++/impl/codegen/completion_queue_tag.h",
-    "include/grpc++/impl/codegen/config.h",
-    "include/grpc++/impl/codegen/core_codegen_interface.h",
-    "include/grpc++/impl/codegen/create_auth_context.h",
-    "include/grpc++/impl/codegen/grpc_library.h",
-    "include/grpc++/impl/codegen/method_handler_impl.h",
-    "include/grpc++/impl/codegen/rpc_method.h",
-    "include/grpc++/impl/codegen/rpc_service_method.h",
-    "include/grpc++/impl/codegen/security/auth_context.h",
-    "include/grpc++/impl/codegen/serialization_traits.h",
-    "include/grpc++/impl/codegen/server_context.h",
-    "include/grpc++/impl/codegen/server_interface.h",
-    "include/grpc++/impl/codegen/service_type.h",
-    "include/grpc++/impl/codegen/status.h",
-    "include/grpc++/impl/codegen/status_code_enum.h",
-    "include/grpc++/impl/codegen/status_helper.h",
-    "include/grpc++/impl/codegen/string_ref.h",
-    "include/grpc++/impl/codegen/stub_options.h",
-    "include/grpc++/impl/codegen/sync_stream.h",
-    "include/grpc++/impl/codegen/time.h",
-    "include/grpc/impl/codegen/byte_buffer_reader.h",
-    "include/grpc/impl/codegen/compression_types.h",
-    "include/grpc/impl/codegen/connectivity_state.h",
-    "include/grpc/impl/codegen/grpc_types.h",
-    "include/grpc/impl/codegen/propagation_bits.h",
-    "include/grpc/impl/codegen/status.h",
-    "include/grpc/impl/codegen/atm.h",
-    "include/grpc/impl/codegen/atm_gcc_atomic.h",
-    "include/grpc/impl/codegen/atm_gcc_sync.h",
-    "include/grpc/impl/codegen/atm_windows.h",
-    "include/grpc/impl/codegen/gpr_types.h",
-    "include/grpc/impl/codegen/port_platform.h",
-    "include/grpc/impl/codegen/slice.h",
-    "include/grpc/impl/codegen/sync.h",
-    "include/grpc/impl/codegen/sync_generic.h",
-    "include/grpc/impl/codegen/sync_posix.h",
-    "include/grpc/impl/codegen/sync_windows.h",
-  ],
-  includes = [
-    "include",
-    ".",
-  ],
-  deps = [
-    "//external:protobuf_clib",
-    ":gpr",
-    ":grpc_unsecure",
-  ],
+grpc_cc_library(
+    name = "gpr_codegen",
+    language = "c",
+    public_hdrs = [
+        "include/grpc/impl/codegen/atm.h",
+        "include/grpc/impl/codegen/atm_gcc_atomic.h",
+        "include/grpc/impl/codegen/atm_gcc_sync.h",
+        "include/grpc/impl/codegen/atm_windows.h",
+        "include/grpc/impl/codegen/gpr_types.h",
+        "include/grpc/impl/codegen/port_platform.h",
+        "include/grpc/impl/codegen/slice.h",
+        "include/grpc/impl/codegen/sync.h",
+        "include/grpc/impl/codegen/sync_generic.h",
+        "include/grpc/impl/codegen/sync_posix.h",
+        "include/grpc/impl/codegen/sync_windows.h",
+    ],
 )
 
+grpc_cc_library(
+    name = "grpc_base",
+    srcs = [
+        "src/core/lib/channel/channel_args.c",
+        "src/core/lib/channel/channel_stack.c",
+        "src/core/lib/channel/channel_stack_builder.c",
+        "src/core/lib/channel/compress_filter.c",
+        "src/core/lib/channel/connected_channel.c",
+        "src/core/lib/channel/deadline_filter.c",
+        "src/core/lib/channel/handshaker.c",
+        "src/core/lib/channel/handshaker_factory.c",
+        "src/core/lib/channel/handshaker_registry.c",
+        "src/core/lib/channel/http_client_filter.c",
+        "src/core/lib/channel/http_server_filter.c",
+        "src/core/lib/channel/message_size_filter.c",
+        "src/core/lib/compression/compression.c",
+        "src/core/lib/compression/message_compress.c",
+        "src/core/lib/debug/trace.c",
+        "src/core/lib/http/format_request.c",
+        "src/core/lib/http/httpcli.c",
+        "src/core/lib/http/parser.c",
+        "src/core/lib/iomgr/closure.c",
+        "src/core/lib/iomgr/combiner.c",
+        "src/core/lib/iomgr/endpoint.c",
+        "src/core/lib/iomgr/endpoint_pair_posix.c",
+        "src/core/lib/iomgr/endpoint_pair_uv.c",
+        "src/core/lib/iomgr/endpoint_pair_windows.c",
+        "src/core/lib/iomgr/error.c",
+        "src/core/lib/iomgr/ev_epoll_linux.c",
+        "src/core/lib/iomgr/ev_poll_posix.c",
+        "src/core/lib/iomgr/ev_posix.c",
+        "src/core/lib/iomgr/exec_ctx.c",
+        "src/core/lib/iomgr/executor.c",
+        "src/core/lib/iomgr/iocp_windows.c",
+        "src/core/lib/iomgr/iomgr.c",
+        "src/core/lib/iomgr/iomgr_posix.c",
+        "src/core/lib/iomgr/iomgr_uv.c",
+        "src/core/lib/iomgr/iomgr_windows.c",
+        "src/core/lib/iomgr/load_file.c",
+        "src/core/lib/iomgr/network_status_tracker.c",
+        "src/core/lib/iomgr/polling_entity.c",
+        "src/core/lib/iomgr/pollset_set_uv.c",
+        "src/core/lib/iomgr/pollset_set_windows.c",
+        "src/core/lib/iomgr/pollset_uv.c",
+        "src/core/lib/iomgr/pollset_windows.c",
+        "src/core/lib/iomgr/resolve_address_posix.c",
+        "src/core/lib/iomgr/resolve_address_uv.c",
+        "src/core/lib/iomgr/resolve_address_windows.c",
+        "src/core/lib/iomgr/resource_quota.c",
+        "src/core/lib/iomgr/sockaddr_utils.c",
+        "src/core/lib/iomgr/socket_mutator.c",
+        "src/core/lib/iomgr/socket_utils_common_posix.c",
+        "src/core/lib/iomgr/socket_utils_linux.c",
+        "src/core/lib/iomgr/socket_utils_posix.c",
+        "src/core/lib/iomgr/socket_utils_uv.c",
+        "src/core/lib/iomgr/socket_utils_windows.c",
+        "src/core/lib/iomgr/socket_windows.c",
+        "src/core/lib/iomgr/tcp_client_posix.c",
+        "src/core/lib/iomgr/tcp_client_uv.c",
+        "src/core/lib/iomgr/tcp_client_windows.c",
+        "src/core/lib/iomgr/tcp_posix.c",
+        "src/core/lib/iomgr/tcp_server_posix.c",
+        "src/core/lib/iomgr/tcp_server_uv.c",
+        "src/core/lib/iomgr/tcp_server_windows.c",
+        "src/core/lib/iomgr/tcp_uv.c",
+        "src/core/lib/iomgr/tcp_windows.c",
+        "src/core/lib/iomgr/time_averaged_stats.c",
+        "src/core/lib/iomgr/timer_generic.c",
+        "src/core/lib/iomgr/timer_heap.c",
+        "src/core/lib/iomgr/timer_uv.c",
+        "src/core/lib/iomgr/udp_server.c",
+        "src/core/lib/iomgr/unix_sockets_posix.c",
+        "src/core/lib/iomgr/unix_sockets_posix_noop.c",
+        "src/core/lib/iomgr/wakeup_fd_cv.c",
+        "src/core/lib/iomgr/wakeup_fd_eventfd.c",
+        "src/core/lib/iomgr/wakeup_fd_nospecial.c",
+        "src/core/lib/iomgr/wakeup_fd_pipe.c",
+        "src/core/lib/iomgr/wakeup_fd_posix.c",
+        "src/core/lib/iomgr/workqueue_uv.c",
+        "src/core/lib/iomgr/workqueue_windows.c",
+        "src/core/lib/json/json.c",
+        "src/core/lib/json/json_reader.c",
+        "src/core/lib/json/json_string.c",
+        "src/core/lib/json/json_writer.c",
+        "src/core/lib/slice/percent_encoding.c",
+        "src/core/lib/slice/slice.c",
+        "src/core/lib/slice/slice_buffer.c",
+        "src/core/lib/slice/slice_string_helpers.c",
+        "src/core/lib/surface/alarm.c",
+        "src/core/lib/surface/api_trace.c",
+        "src/core/lib/surface/byte_buffer.c",
+        "src/core/lib/surface/byte_buffer_reader.c",
+        "src/core/lib/surface/call.c",
+        "src/core/lib/surface/call_details.c",
+        "src/core/lib/surface/call_log_batch.c",
+        "src/core/lib/surface/channel.c",
+        "src/core/lib/surface/channel_init.c",
+        "src/core/lib/surface/channel_ping.c",
+        "src/core/lib/surface/channel_stack_type.c",
+        "src/core/lib/surface/completion_queue.c",
+        "src/core/lib/surface/event_string.c",
+        "src/core/lib/surface/lame_client.c",
+        "src/core/lib/surface/metadata_array.c",
+        "src/core/lib/surface/server.c",
+        "src/core/lib/surface/validate_metadata.c",
+        "src/core/lib/surface/version.c",
+        "src/core/lib/transport/byte_stream.c",
+        "src/core/lib/transport/connectivity_state.c",
+        "src/core/lib/transport/mdstr_hash_table.c",
+        "src/core/lib/transport/metadata.c",
+        "src/core/lib/transport/metadata_batch.c",
+        "src/core/lib/transport/pid_controller.c",
+        "src/core/lib/transport/service_config.c",
+        "src/core/lib/transport/static_metadata.c",
+        "src/core/lib/transport/timeout_encoding.c",
+        "src/core/lib/transport/transport.c",
+        "src/core/lib/transport/transport_op_string.c",
+    ],
+    hdrs = [
+        "src/core/lib/channel/channel_args.h",
+        "src/core/lib/channel/channel_stack.h",
+        "src/core/lib/channel/channel_stack_builder.h",
+        "src/core/lib/channel/compress_filter.h",
+        "src/core/lib/channel/connected_channel.h",
+        "src/core/lib/channel/context.h",
+        "src/core/lib/channel/deadline_filter.h",
+        "src/core/lib/channel/handshaker.h",
+        "src/core/lib/channel/handshaker_factory.h",
+        "src/core/lib/channel/handshaker_registry.h",
+        "src/core/lib/channel/http_client_filter.h",
+        "src/core/lib/channel/http_server_filter.h",
+        "src/core/lib/channel/message_size_filter.h",
+        "src/core/lib/compression/algorithm_metadata.h",
+        "src/core/lib/compression/message_compress.h",
+        "src/core/lib/debug/trace.h",
+        "src/core/lib/http/format_request.h",
+        "src/core/lib/http/httpcli.h",
+        "src/core/lib/http/parser.h",
+        "src/core/lib/iomgr/closure.h",
+        "src/core/lib/iomgr/combiner.h",
+        "src/core/lib/iomgr/endpoint.h",
+        "src/core/lib/iomgr/endpoint_pair.h",
+        "src/core/lib/iomgr/error.h",
+        "src/core/lib/iomgr/ev_epoll_linux.h",
+        "src/core/lib/iomgr/ev_poll_posix.h",
+        "src/core/lib/iomgr/ev_posix.h",
+        "src/core/lib/iomgr/exec_ctx.h",
+        "src/core/lib/iomgr/executor.h",
+        "src/core/lib/iomgr/iocp_windows.h",
+        "src/core/lib/iomgr/iomgr.h",
+        "src/core/lib/iomgr/iomgr_internal.h",
+        "src/core/lib/iomgr/iomgr_posix.h",
+        "src/core/lib/iomgr/load_file.h",
+        "src/core/lib/iomgr/network_status_tracker.h",
+        "src/core/lib/iomgr/polling_entity.h",
+        "src/core/lib/iomgr/pollset.h",
+        "src/core/lib/iomgr/pollset_set.h",
+        "src/core/lib/iomgr/pollset_set_windows.h",
+        "src/core/lib/iomgr/pollset_uv.h",
+        "src/core/lib/iomgr/pollset_windows.h",
+        "src/core/lib/iomgr/port.h",
+        "src/core/lib/iomgr/resolve_address.h",
+        "src/core/lib/iomgr/resource_quota.h",
+        "src/core/lib/iomgr/sockaddr.h",
+        "src/core/lib/iomgr/sockaddr_posix.h",
+        "src/core/lib/iomgr/sockaddr_utils.h",
+        "src/core/lib/iomgr/sockaddr_windows.h",
+        "src/core/lib/iomgr/socket_mutator.h",
+        "src/core/lib/iomgr/socket_utils.h",
+        "src/core/lib/iomgr/socket_utils_posix.h",
+        "src/core/lib/iomgr/socket_windows.h",
+        "src/core/lib/iomgr/tcp_client.h",
+        "src/core/lib/iomgr/tcp_client_posix.h",
+        "src/core/lib/iomgr/tcp_posix.h",
+        "src/core/lib/iomgr/tcp_server.h",
+        "src/core/lib/iomgr/tcp_uv.h",
+        "src/core/lib/iomgr/tcp_windows.h",
+        "src/core/lib/iomgr/time_averaged_stats.h",
+        "src/core/lib/iomgr/timer.h",
+        "src/core/lib/iomgr/timer_generic.h",
+        "src/core/lib/iomgr/timer_heap.h",
+        "src/core/lib/iomgr/timer_uv.h",
+        "src/core/lib/iomgr/udp_server.h",
+        "src/core/lib/iomgr/unix_sockets_posix.h",
+        "src/core/lib/iomgr/wakeup_fd_cv.h",
+        "src/core/lib/iomgr/wakeup_fd_pipe.h",
+        "src/core/lib/iomgr/wakeup_fd_posix.h",
+        "src/core/lib/iomgr/workqueue.h",
+        "src/core/lib/iomgr/workqueue_uv.h",
+        "src/core/lib/iomgr/workqueue_windows.h",
+        "src/core/lib/json/json.h",
+        "src/core/lib/json/json_common.h",
+        "src/core/lib/json/json_reader.h",
+        "src/core/lib/json/json_writer.h",
+        "src/core/lib/slice/percent_encoding.h",
+        "src/core/lib/slice/slice_internal.h",
+        "src/core/lib/slice/slice_string_helpers.h",
+        "src/core/lib/surface/api_trace.h",
+        "src/core/lib/surface/call.h",
+        "src/core/lib/surface/call_test_only.h",
+        "src/core/lib/surface/channel.h",
+        "src/core/lib/surface/channel_init.h",
+        "src/core/lib/surface/channel_stack_type.h",
+        "src/core/lib/surface/completion_queue.h",
+        "src/core/lib/surface/event_string.h",
+        "src/core/lib/surface/init.h",
+        "src/core/lib/surface/lame_client.h",
+        "src/core/lib/surface/server.h",
+        "src/core/lib/transport/byte_stream.h",
+        "src/core/lib/transport/connectivity_state.h",
+        "src/core/lib/transport/mdstr_hash_table.h",
+        "src/core/lib/transport/metadata.h",
+        "src/core/lib/transport/metadata_batch.h",
+        "src/core/lib/transport/pid_controller.h",
+        "src/core/lib/transport/service_config.h",
+        "src/core/lib/transport/static_metadata.h",
+        "src/core/lib/transport/timeout_encoding.h",
+        "src/core/lib/transport/transport.h",
+        "src/core/lib/transport/transport_impl.h",
+    ],
+    external_deps = [
+        "zlib",
+    ],
+    language = "c",
+    public_hdrs = [
+        "include/grpc/byte_buffer.h",
+        "include/grpc/byte_buffer_reader.h",
+        "include/grpc/compression.h",
+        "include/grpc/grpc.h",
+        "include/grpc/grpc_posix.h",
+        "include/grpc/grpc_security_constants.h",
+        "include/grpc/slice.h",
+        "include/grpc/slice_buffer.h",
+        "include/grpc/status.h",
+    ],
+    deps = [
+        "gpr_base",
+        "grpc_codegen",
+    ],
+)
 
+grpc_cc_library(
+    name = "grpc_client_channel",
+    language = "c",
+    srcs = [
+        "src/core/ext/client_channel/channel_connectivity.c",
+        "src/core/ext/client_channel/client_channel.c",
+        "src/core/ext/client_channel/client_channel_factory.c",
+        "src/core/ext/client_channel/client_channel_plugin.c",
+        "src/core/ext/client_channel/connector.c",
+        "src/core/ext/client_channel/default_initial_connect_string.c",
+        "src/core/ext/client_channel/http_connect_handshaker.c",
+        "src/core/ext/client_channel/initial_connect_string.c",
+        "src/core/ext/client_channel/lb_policy.c",
+        "src/core/ext/client_channel/lb_policy_factory.c",
+        "src/core/ext/client_channel/lb_policy_registry.c",
+        "src/core/ext/client_channel/parse_address.c",
+        "src/core/ext/client_channel/resolver.c",
+        "src/core/ext/client_channel/resolver_factory.c",
+        "src/core/ext/client_channel/resolver_registry.c",
+        "src/core/ext/client_channel/subchannel.c",
+        "src/core/ext/client_channel/subchannel_index.c",
+        "src/core/ext/client_channel/uri_parser.c",
+    ],
+    hdrs = [
+        "src/core/ext/client_channel/client_channel.h",
+        "src/core/ext/client_channel/client_channel_factory.h",
+        "src/core/ext/client_channel/connector.h",
+        "src/core/ext/client_channel/http_connect_handshaker.h",
+        "src/core/ext/client_channel/initial_connect_string.h",
+        "src/core/ext/client_channel/lb_policy.h",
+        "src/core/ext/client_channel/lb_policy_factory.h",
+        "src/core/ext/client_channel/lb_policy_registry.h",
+        "src/core/ext/client_channel/parse_address.h",
+        "src/core/ext/client_channel/resolver.h",
+        "src/core/ext/client_channel/resolver_factory.h",
+        "src/core/ext/client_channel/resolver_registry.h",
+        "src/core/ext/client_channel/subchannel.h",
+        "src/core/ext/client_channel/subchannel_index.h",
+        "src/core/ext/client_channel/uri_parser.h",
+    ],
+    deps = [
+        "grpc_base",
+    ],
+)
 
-cc_library(
-  name = "grpc_plugin_support",
-  srcs = [
-    "src/compiler/config.h",
-    "src/compiler/cpp_generator.h",
-    "src/compiler/cpp_generator_helpers.h",
-    "src/compiler/csharp_generator.h",
-    "src/compiler/csharp_generator_helpers.h",
-    "src/compiler/generator_helpers.h",
-    "src/compiler/node_generator.h",
-    "src/compiler/node_generator_helpers.h",
-    "src/compiler/objective_c_generator.h",
-    "src/compiler/objective_c_generator_helpers.h",
-    "src/compiler/php_generator.h",
-    "src/compiler/php_generator_helpers.h",
-    "src/compiler/python_generator.h",
-    "src/compiler/ruby_generator.h",
-    "src/compiler/ruby_generator_helpers-inl.h",
-    "src/compiler/ruby_generator_map-inl.h",
-    "src/compiler/ruby_generator_string-inl.h",
-    "src/compiler/cpp_generator.cc",
-    "src/compiler/csharp_generator.cc",
-    "src/compiler/node_generator.cc",
-    "src/compiler/objective_c_generator.cc",
-    "src/compiler/php_generator.cc",
-    "src/compiler/python_generator.cc",
-    "src/compiler/ruby_generator.cc",
-  ],
-  hdrs = [
-    "include/grpc++/impl/codegen/config_protobuf.h",
-  ],
-  includes = [
-    "include",
-    ".",
-  ],
-  deps = [
-    "//external:protobuf_compiler",
-  ],
+grpc_cc_library(
+    name = "grpc_codegen",
+    language = "c",
+    public_hdrs = [
+        "include/grpc/impl/codegen/byte_buffer_reader.h",
+        "include/grpc/impl/codegen/compression_types.h",
+        "include/grpc/impl/codegen/connectivity_state.h",
+        "include/grpc/impl/codegen/exec_ctx_fwd.h",
+        "include/grpc/impl/codegen/grpc_types.h",
+        "include/grpc/impl/codegen/propagation_bits.h",
+        "include/grpc/impl/codegen/status.h",
+    ],
+    deps = [
+        "gpr_codegen",
+    ],
 )
 
+grpc_cc_library(
+    name = "grpc_lb_policy_grpclb",
+    srcs = [
+        "src/core/ext/lb_policy/grpclb/grpclb.c",
+        "src/core/ext/lb_policy/grpclb/load_balancer_api.c",
+        "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c",
+    ],
+    hdrs = [
+        "src/core/ext/lb_policy/grpclb/grpclb.h",
+        "src/core/ext/lb_policy/grpclb/load_balancer_api.h",
+        "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h",
+    ],
+    external_deps = [
+        "nanopb",
+    ],
+    language = "c",
+    deps = [
+        "grpc_base",
+        "grpc_client_channel",
+    ],
+)
 
+grpc_cc_library(
+    name = "grpc_lb_policy_pick_first",
+    srcs = [
+        "src/core/ext/lb_policy/pick_first/pick_first.c",
+    ],
+    language = "c",
+    deps = [
+        "grpc_base",
+        "grpc_client_channel",
+    ],
+)
 
-cc_library(
-  name = "grpc_csharp_ext",
-  srcs = [
-    "src/csharp/ext/grpc_csharp_ext.c",
-  ],
-  hdrs = [
-  ],
-  includes = [
-    "include",
-    ".",
-  ],
-  deps = [
-    ":grpc",
-    ":gpr",
-  ],
+grpc_cc_library(
+    name = "grpc_lb_policy_round_robin",
+    srcs = [
+        "src/core/ext/lb_policy/round_robin/round_robin.c",
+    ],
+    language = "c",
+    deps = [
+        "grpc_base",
+        "grpc_client_channel",
+    ],
 )
 
+grpc_cc_library(
+    name = "grpc_load_reporting",
+    srcs = [
+        "src/core/ext/load_reporting/load_reporting.c",
+        "src/core/ext/load_reporting/load_reporting_filter.c",
+    ],
+    hdrs = [
+        "src/core/ext/load_reporting/load_reporting.h",
+        "src/core/ext/load_reporting/load_reporting_filter.h",
+    ],
+    language = "c",
+    deps = [
+        "grpc_base",
+    ],
+)
 
+grpc_cc_library(
+    name = "grpc_resolver_dns_native",
+    srcs = [
+        "src/core/ext/resolver/dns/native/dns_resolver.c",
+    ],
+    language = "c",
+    deps = [
+        "grpc_base",
+        "grpc_client_channel",
+    ],
+)
 
+grpc_cc_library(
+    name = "grpc_resolver_sockaddr",
+    srcs = [
+        "src/core/ext/resolver/sockaddr/sockaddr_resolver.c",
+    ],
+    language = "c",
+    deps = [
+        "grpc_base",
+        "grpc_client_channel",
+    ],
+)
 
-objc_library(
-  name = "gpr_objc",
-  srcs = [
-    "src/core/lib/profiling/basic_timers.c",
-    "src/core/lib/profiling/stap_timers.c",
-    "src/core/lib/support/alloc.c",
-    "src/core/lib/support/avl.c",
-    "src/core/lib/support/backoff.c",
-    "src/core/lib/support/cmdline.c",
-    "src/core/lib/support/cpu_iphone.c",
-    "src/core/lib/support/cpu_linux.c",
-    "src/core/lib/support/cpu_posix.c",
-    "src/core/lib/support/cpu_windows.c",
-    "src/core/lib/support/env_linux.c",
-    "src/core/lib/support/env_posix.c",
-    "src/core/lib/support/env_windows.c",
-    "src/core/lib/support/histogram.c",
-    "src/core/lib/support/host_port.c",
-    "src/core/lib/support/log.c",
-    "src/core/lib/support/log_android.c",
-    "src/core/lib/support/log_linux.c",
-    "src/core/lib/support/log_posix.c",
-    "src/core/lib/support/log_windows.c",
-    "src/core/lib/support/mpscq.c",
-    "src/core/lib/support/murmur_hash.c",
-    "src/core/lib/support/stack_lockfree.c",
-    "src/core/lib/support/string.c",
-    "src/core/lib/support/string_posix.c",
-    "src/core/lib/support/string_util_windows.c",
-    "src/core/lib/support/string_windows.c",
-    "src/core/lib/support/subprocess_posix.c",
-    "src/core/lib/support/subprocess_windows.c",
-    "src/core/lib/support/sync.c",
-    "src/core/lib/support/sync_posix.c",
-    "src/core/lib/support/sync_windows.c",
-    "src/core/lib/support/thd.c",
-    "src/core/lib/support/thd_posix.c",
-    "src/core/lib/support/thd_windows.c",
-    "src/core/lib/support/time.c",
-    "src/core/lib/support/time_posix.c",
-    "src/core/lib/support/time_precise.c",
-    "src/core/lib/support/time_windows.c",
-    "src/core/lib/support/tls_pthread.c",
-    "src/core/lib/support/tmpfile_msys.c",
-    "src/core/lib/support/tmpfile_posix.c",
-    "src/core/lib/support/tmpfile_windows.c",
-    "src/core/lib/support/wrap_memcpy.c",
-  ],
-  hdrs = [
-    "include/grpc/support/alloc.h",
-    "include/grpc/support/atm.h",
-    "include/grpc/support/atm_gcc_atomic.h",
-    "include/grpc/support/atm_gcc_sync.h",
-    "include/grpc/support/atm_windows.h",
-    "include/grpc/support/avl.h",
-    "include/grpc/support/cmdline.h",
-    "include/grpc/support/cpu.h",
-    "include/grpc/support/histogram.h",
-    "include/grpc/support/host_port.h",
-    "include/grpc/support/log.h",
-    "include/grpc/support/log_windows.h",
-    "include/grpc/support/port_platform.h",
-    "include/grpc/support/string_util.h",
-    "include/grpc/support/subprocess.h",
-    "include/grpc/support/sync.h",
-    "include/grpc/support/sync_generic.h",
-    "include/grpc/support/sync_posix.h",
-    "include/grpc/support/sync_windows.h",
-    "include/grpc/support/thd.h",
-    "include/grpc/support/time.h",
-    "include/grpc/support/tls.h",
-    "include/grpc/support/tls_gcc.h",
-    "include/grpc/support/tls_msvc.h",
-    "include/grpc/support/tls_pthread.h",
-    "include/grpc/support/useful.h",
-    "include/grpc/impl/codegen/atm.h",
-    "include/grpc/impl/codegen/atm_gcc_atomic.h",
-    "include/grpc/impl/codegen/atm_gcc_sync.h",
-    "include/grpc/impl/codegen/atm_windows.h",
-    "include/grpc/impl/codegen/gpr_types.h",
-    "include/grpc/impl/codegen/port_platform.h",
-    "include/grpc/impl/codegen/slice.h",
-    "include/grpc/impl/codegen/sync.h",
-    "include/grpc/impl/codegen/sync_generic.h",
-    "include/grpc/impl/codegen/sync_posix.h",
-    "include/grpc/impl/codegen/sync_windows.h",
-    "src/core/lib/profiling/timers.h",
-    "src/core/lib/support/backoff.h",
-    "src/core/lib/support/block_annotate.h",
-    "src/core/lib/support/env.h",
-    "src/core/lib/support/mpscq.h",
-    "src/core/lib/support/murmur_hash.h",
-    "src/core/lib/support/stack_lockfree.h",
-    "src/core/lib/support/string.h",
-    "src/core/lib/support/string_windows.h",
-    "src/core/lib/support/thd_internal.h",
-    "src/core/lib/support/time_precise.h",
-    "src/core/lib/support/tmpfile.h",
-  ],
-  includes = [
-    "include",
-    ".",
-  ],
-  deps = [
-  ],
+grpc_cc_library(
+    name = "grpc_secure",
+    srcs = [
+        "src/core/lib/http/httpcli_security_connector.c",
+        "src/core/lib/security/context/security_context.c",
+        "src/core/lib/security/credentials/composite/composite_credentials.c",
+        "src/core/lib/security/credentials/credentials.c",
+        "src/core/lib/security/credentials/credentials_metadata.c",
+        "src/core/lib/security/credentials/fake/fake_credentials.c",
+        "src/core/lib/security/credentials/google_default/credentials_generic.c",
+        "src/core/lib/security/credentials/google_default/google_default_credentials.c",
+        "src/core/lib/security/credentials/iam/iam_credentials.c",
+        "src/core/lib/security/credentials/jwt/json_token.c",
+        "src/core/lib/security/credentials/jwt/jwt_credentials.c",
+        "src/core/lib/security/credentials/jwt/jwt_verifier.c",
+        "src/core/lib/security/credentials/oauth2/oauth2_credentials.c",
+        "src/core/lib/security/credentials/plugin/plugin_credentials.c",
+        "src/core/lib/security/credentials/ssl/ssl_credentials.c",
+        "src/core/lib/security/transport/client_auth_filter.c",
+        "src/core/lib/security/transport/secure_endpoint.c",
+        "src/core/lib/security/transport/security_connector.c",
+        "src/core/lib/security/transport/security_handshaker.c",
+        "src/core/lib/security/transport/server_auth_filter.c",
+        "src/core/lib/security/transport/tsi_error.c",
+        "src/core/lib/security/util/b64.c",
+        "src/core/lib/security/util/json_util.c",
+        "src/core/lib/surface/init_secure.c",
+    ],
+    hdrs = [
+        "src/core/lib/security/context/security_context.h",
+        "src/core/lib/security/credentials/composite/composite_credentials.h",
+        "src/core/lib/security/credentials/credentials.h",
+        "src/core/lib/security/credentials/fake/fake_credentials.h",
+        "src/core/lib/security/credentials/google_default/google_default_credentials.h",
+        "src/core/lib/security/credentials/iam/iam_credentials.h",
+        "src/core/lib/security/credentials/jwt/json_token.h",
+        "src/core/lib/security/credentials/jwt/jwt_credentials.h",
+        "src/core/lib/security/credentials/jwt/jwt_verifier.h",
+        "src/core/lib/security/credentials/oauth2/oauth2_credentials.h",
+        "src/core/lib/security/credentials/plugin/plugin_credentials.h",
+        "src/core/lib/security/credentials/ssl/ssl_credentials.h",
+        "src/core/lib/security/transport/auth_filters.h",
+        "src/core/lib/security/transport/secure_endpoint.h",
+        "src/core/lib/security/transport/security_connector.h",
+        "src/core/lib/security/transport/security_handshaker.h",
+        "src/core/lib/security/transport/tsi_error.h",
+        "src/core/lib/security/util/b64.h",
+        "src/core/lib/security/util/json_util.h",
+    ],
+    language = "c",
+    public_hdrs = [
+        "include/grpc/grpc_security.h",
+    ],
+    deps = [
+        "grpc_base",
+        "grpc_transport_chttp2_alpn",
+        "tsi",
+    ],
 )
 
+grpc_cc_library(
+    name = "grpc_transport_chttp2",
+    srcs = [
+        "src/core/ext/transport/chttp2/transport/bin_decoder.c",
+        "src/core/ext/transport/chttp2/transport/bin_encoder.c",
+        "src/core/ext/transport/chttp2/transport/chttp2_plugin.c",
+        "src/core/ext/transport/chttp2/transport/chttp2_transport.c",
+        "src/core/ext/transport/chttp2/transport/frame_data.c",
+        "src/core/ext/transport/chttp2/transport/frame_goaway.c",
+        "src/core/ext/transport/chttp2/transport/frame_ping.c",
+        "src/core/ext/transport/chttp2/transport/frame_rst_stream.c",
+        "src/core/ext/transport/chttp2/transport/frame_settings.c",
+        "src/core/ext/transport/chttp2/transport/frame_window_update.c",
+        "src/core/ext/transport/chttp2/transport/hpack_encoder.c",
+        "src/core/ext/transport/chttp2/transport/hpack_parser.c",
+        "src/core/ext/transport/chttp2/transport/hpack_table.c",
+        "src/core/ext/transport/chttp2/transport/huffsyms.c",
+        "src/core/ext/transport/chttp2/transport/incoming_metadata.c",
+        "src/core/ext/transport/chttp2/transport/parsing.c",
+        "src/core/ext/transport/chttp2/transport/status_conversion.c",
+        "src/core/ext/transport/chttp2/transport/stream_lists.c",
+        "src/core/ext/transport/chttp2/transport/stream_map.c",
+        "src/core/ext/transport/chttp2/transport/varint.c",
+        "src/core/ext/transport/chttp2/transport/writing.c",
+    ],
+    hdrs = [
+        "src/core/ext/transport/chttp2/transport/bin_decoder.h",
+        "src/core/ext/transport/chttp2/transport/bin_encoder.h",
+        "src/core/ext/transport/chttp2/transport/chttp2_transport.h",
+        "src/core/ext/transport/chttp2/transport/frame.h",
+        "src/core/ext/transport/chttp2/transport/frame_data.h",
+        "src/core/ext/transport/chttp2/transport/frame_goaway.h",
+        "src/core/ext/transport/chttp2/transport/frame_ping.h",
+        "src/core/ext/transport/chttp2/transport/frame_rst_stream.h",
+        "src/core/ext/transport/chttp2/transport/frame_settings.h",
+        "src/core/ext/transport/chttp2/transport/frame_window_update.h",
+        "src/core/ext/transport/chttp2/transport/hpack_encoder.h",
+        "src/core/ext/transport/chttp2/transport/hpack_parser.h",
+        "src/core/ext/transport/chttp2/transport/hpack_table.h",
+        "src/core/ext/transport/chttp2/transport/http2_errors.h",
+        "src/core/ext/transport/chttp2/transport/huffsyms.h",
+        "src/core/ext/transport/chttp2/transport/incoming_metadata.h",
+        "src/core/ext/transport/chttp2/transport/internal.h",
+        "src/core/ext/transport/chttp2/transport/status_conversion.h",
+        "src/core/ext/transport/chttp2/transport/stream_map.h",
+        "src/core/ext/transport/chttp2/transport/varint.h",
+    ],
+    language = "c",
+    deps = [
+        "grpc_base",
+        "grpc_transport_chttp2_alpn",
+    ],
+)
 
+grpc_cc_library(
+    name = "grpc_transport_chttp2_alpn",
+    srcs = [
+        "src/core/ext/transport/chttp2/alpn/alpn.c",
+    ],
+    hdrs = [
+        "src/core/ext/transport/chttp2/alpn/alpn.h",
+    ],
+    language = "c",
+    deps = [
+        "gpr",
+    ],
+)
 
-objc_library(
-  name = "grpc_objc",
-  srcs = [
-    "src/core/lib/surface/init.c",
-    "src/core/lib/channel/channel_args.c",
-    "src/core/lib/channel/channel_stack.c",
-    "src/core/lib/channel/channel_stack_builder.c",
-    "src/core/lib/channel/compress_filter.c",
-    "src/core/lib/channel/connected_channel.c",
-    "src/core/lib/channel/deadline_filter.c",
-    "src/core/lib/channel/handshaker.c",
-    "src/core/lib/channel/http_client_filter.c",
-    "src/core/lib/channel/http_server_filter.c",
-    "src/core/lib/channel/message_size_filter.c",
-    "src/core/lib/compression/compression.c",
-    "src/core/lib/compression/message_compress.c",
-    "src/core/lib/debug/trace.c",
-    "src/core/lib/http/format_request.c",
-    "src/core/lib/http/httpcli.c",
-    "src/core/lib/http/parser.c",
-    "src/core/lib/iomgr/closure.c",
-    "src/core/lib/iomgr/combiner.c",
-    "src/core/lib/iomgr/endpoint.c",
-    "src/core/lib/iomgr/endpoint_pair_posix.c",
-    "src/core/lib/iomgr/endpoint_pair_uv.c",
-    "src/core/lib/iomgr/endpoint_pair_windows.c",
-    "src/core/lib/iomgr/error.c",
-    "src/core/lib/iomgr/ev_epoll_linux.c",
-    "src/core/lib/iomgr/ev_poll_posix.c",
-    "src/core/lib/iomgr/ev_posix.c",
-    "src/core/lib/iomgr/exec_ctx.c",
-    "src/core/lib/iomgr/executor.c",
-    "src/core/lib/iomgr/iocp_windows.c",
-    "src/core/lib/iomgr/iomgr.c",
-    "src/core/lib/iomgr/iomgr_posix.c",
-    "src/core/lib/iomgr/iomgr_uv.c",
-    "src/core/lib/iomgr/iomgr_windows.c",
-    "src/core/lib/iomgr/load_file.c",
-    "src/core/lib/iomgr/network_status_tracker.c",
-    "src/core/lib/iomgr/polling_entity.c",
-    "src/core/lib/iomgr/pollset_set_uv.c",
-    "src/core/lib/iomgr/pollset_set_windows.c",
-    "src/core/lib/iomgr/pollset_uv.c",
-    "src/core/lib/iomgr/pollset_windows.c",
-    "src/core/lib/iomgr/resolve_address_posix.c",
-    "src/core/lib/iomgr/resolve_address_uv.c",
-    "src/core/lib/iomgr/resolve_address_windows.c",
-    "src/core/lib/iomgr/resource_quota.c",
-    "src/core/lib/iomgr/sockaddr_utils.c",
-    "src/core/lib/iomgr/socket_mutator.c",
-    "src/core/lib/iomgr/socket_utils_common_posix.c",
-    "src/core/lib/iomgr/socket_utils_linux.c",
-    "src/core/lib/iomgr/socket_utils_posix.c",
-    "src/core/lib/iomgr/socket_utils_uv.c",
-    "src/core/lib/iomgr/socket_utils_windows.c",
-    "src/core/lib/iomgr/socket_windows.c",
-    "src/core/lib/iomgr/tcp_client_posix.c",
-    "src/core/lib/iomgr/tcp_client_uv.c",
-    "src/core/lib/iomgr/tcp_client_windows.c",
-    "src/core/lib/iomgr/tcp_posix.c",
-    "src/core/lib/iomgr/tcp_server_posix.c",
-    "src/core/lib/iomgr/tcp_server_uv.c",
-    "src/core/lib/iomgr/tcp_server_windows.c",
-    "src/core/lib/iomgr/tcp_uv.c",
-    "src/core/lib/iomgr/tcp_windows.c",
-    "src/core/lib/iomgr/time_averaged_stats.c",
-    "src/core/lib/iomgr/timer_generic.c",
-    "src/core/lib/iomgr/timer_heap.c",
-    "src/core/lib/iomgr/timer_uv.c",
-    "src/core/lib/iomgr/udp_server.c",
-    "src/core/lib/iomgr/unix_sockets_posix.c",
-    "src/core/lib/iomgr/unix_sockets_posix_noop.c",
-    "src/core/lib/iomgr/wakeup_fd_cv.c",
-    "src/core/lib/iomgr/wakeup_fd_eventfd.c",
-    "src/core/lib/iomgr/wakeup_fd_nospecial.c",
-    "src/core/lib/iomgr/wakeup_fd_pipe.c",
-    "src/core/lib/iomgr/wakeup_fd_posix.c",
-    "src/core/lib/iomgr/workqueue_uv.c",
-    "src/core/lib/iomgr/workqueue_windows.c",
-    "src/core/lib/json/json.c",
-    "src/core/lib/json/json_reader.c",
-    "src/core/lib/json/json_string.c",
-    "src/core/lib/json/json_writer.c",
-    "src/core/lib/slice/percent_encoding.c",
-    "src/core/lib/slice/slice.c",
-    "src/core/lib/slice/slice_buffer.c",
-    "src/core/lib/slice/slice_string_helpers.c",
-    "src/core/lib/surface/alarm.c",
-    "src/core/lib/surface/api_trace.c",
-    "src/core/lib/surface/byte_buffer.c",
-    "src/core/lib/surface/byte_buffer_reader.c",
-    "src/core/lib/surface/call.c",
-    "src/core/lib/surface/call_details.c",
-    "src/core/lib/surface/call_log_batch.c",
-    "src/core/lib/surface/channel.c",
-    "src/core/lib/surface/channel_init.c",
-    "src/core/lib/surface/channel_ping.c",
-    "src/core/lib/surface/channel_stack_type.c",
-    "src/core/lib/surface/completion_queue.c",
-    "src/core/lib/surface/event_string.c",
-    "src/core/lib/surface/lame_client.c",
-    "src/core/lib/surface/metadata_array.c",
-    "src/core/lib/surface/server.c",
-    "src/core/lib/surface/validate_metadata.c",
-    "src/core/lib/surface/version.c",
-    "src/core/lib/transport/byte_stream.c",
-    "src/core/lib/transport/connectivity_state.c",
-    "src/core/lib/transport/mdstr_hash_table.c",
-    "src/core/lib/transport/metadata.c",
-    "src/core/lib/transport/metadata_batch.c",
-    "src/core/lib/transport/pid_controller.c",
-    "src/core/lib/transport/service_config.c",
-    "src/core/lib/transport/static_metadata.c",
-    "src/core/lib/transport/timeout_encoding.c",
-    "src/core/lib/transport/transport.c",
-    "src/core/lib/transport/transport_op_string.c",
-    "src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c",
-    "src/core/ext/transport/chttp2/transport/bin_decoder.c",
-    "src/core/ext/transport/chttp2/transport/bin_encoder.c",
-    "src/core/ext/transport/chttp2/transport/chttp2_plugin.c",
-    "src/core/ext/transport/chttp2/transport/chttp2_transport.c",
-    "src/core/ext/transport/chttp2/transport/frame_data.c",
-    "src/core/ext/transport/chttp2/transport/frame_goaway.c",
-    "src/core/ext/transport/chttp2/transport/frame_ping.c",
-    "src/core/ext/transport/chttp2/transport/frame_rst_stream.c",
-    "src/core/ext/transport/chttp2/transport/frame_settings.c",
-    "src/core/ext/transport/chttp2/transport/frame_window_update.c",
-    "src/core/ext/transport/chttp2/transport/hpack_encoder.c",
-    "src/core/ext/transport/chttp2/transport/hpack_parser.c",
-    "src/core/ext/transport/chttp2/transport/hpack_table.c",
-    "src/core/ext/transport/chttp2/transport/huffsyms.c",
-    "src/core/ext/transport/chttp2/transport/incoming_metadata.c",
-    "src/core/ext/transport/chttp2/transport/parsing.c",
-    "src/core/ext/transport/chttp2/transport/status_conversion.c",
-    "src/core/ext/transport/chttp2/transport/stream_lists.c",
-    "src/core/ext/transport/chttp2/transport/stream_map.c",
-    "src/core/ext/transport/chttp2/transport/varint.c",
-    "src/core/ext/transport/chttp2/transport/writing.c",
-    "src/core/ext/transport/chttp2/alpn/alpn.c",
-    "src/core/lib/http/httpcli_security_connector.c",
-    "src/core/lib/security/context/security_context.c",
-    "src/core/lib/security/credentials/composite/composite_credentials.c",
-    "src/core/lib/security/credentials/credentials.c",
-    "src/core/lib/security/credentials/credentials_metadata.c",
-    "src/core/lib/security/credentials/fake/fake_credentials.c",
-    "src/core/lib/security/credentials/google_default/credentials_generic.c",
-    "src/core/lib/security/credentials/google_default/google_default_credentials.c",
-    "src/core/lib/security/credentials/iam/iam_credentials.c",
-    "src/core/lib/security/credentials/jwt/json_token.c",
-    "src/core/lib/security/credentials/jwt/jwt_credentials.c",
-    "src/core/lib/security/credentials/jwt/jwt_verifier.c",
-    "src/core/lib/security/credentials/oauth2/oauth2_credentials.c",
-    "src/core/lib/security/credentials/plugin/plugin_credentials.c",
-    "src/core/lib/security/credentials/ssl/ssl_credentials.c",
-    "src/core/lib/security/transport/client_auth_filter.c",
-    "src/core/lib/security/transport/handshake.c",
-    "src/core/lib/security/transport/secure_endpoint.c",
-    "src/core/lib/security/transport/security_connector.c",
-    "src/core/lib/security/transport/server_auth_filter.c",
-    "src/core/lib/security/transport/tsi_error.c",
-    "src/core/lib/security/util/b64.c",
-    "src/core/lib/security/util/json_util.c",
-    "src/core/lib/surface/init_secure.c",
-    "src/core/lib/tsi/fake_transport_security.c",
-    "src/core/lib/tsi/ssl_transport_security.c",
-    "src/core/lib/tsi/transport_security.c",
-    "src/core/ext/transport/chttp2/client/secure/secure_channel_create.c",
-    "src/core/ext/client_channel/channel_connectivity.c",
-    "src/core/ext/client_channel/client_channel.c",
-    "src/core/ext/client_channel/client_channel_factory.c",
-    "src/core/ext/client_channel/client_channel_plugin.c",
-    "src/core/ext/client_channel/connector.c",
-    "src/core/ext/client_channel/default_initial_connect_string.c",
-    "src/core/ext/client_channel/http_connect_handshaker.c",
-    "src/core/ext/client_channel/initial_connect_string.c",
-    "src/core/ext/client_channel/lb_policy.c",
-    "src/core/ext/client_channel/lb_policy_factory.c",
-    "src/core/ext/client_channel/lb_policy_registry.c",
-    "src/core/ext/client_channel/parse_address.c",
-    "src/core/ext/client_channel/resolver.c",
-    "src/core/ext/client_channel/resolver_factory.c",
-    "src/core/ext/client_channel/resolver_registry.c",
-    "src/core/ext/client_channel/subchannel.c",
-    "src/core/ext/client_channel/subchannel_index.c",
-    "src/core/ext/client_channel/uri_parser.c",
-    "src/core/ext/transport/chttp2/server/insecure/server_chttp2.c",
-    "src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c",
-    "src/core/ext/transport/chttp2/client/insecure/channel_create.c",
-    "src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c",
-    "src/core/ext/lb_policy/grpclb/grpclb.c",
-    "src/core/ext/lb_policy/grpclb/load_balancer_api.c",
-    "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c",
-    "src/core/ext/lb_policy/pick_first/pick_first.c",
-    "src/core/ext/lb_policy/round_robin/round_robin.c",
-    "src/core/ext/resolver/dns/native/dns_resolver.c",
-    "src/core/ext/resolver/sockaddr/sockaddr_resolver.c",
-    "src/core/ext/load_reporting/load_reporting.c",
-    "src/core/ext/load_reporting/load_reporting_filter.c",
-    "src/core/ext/census/base_resources.c",
-    "src/core/ext/census/context.c",
-    "src/core/ext/census/gen/census.pb.c",
-    "src/core/ext/census/gen/trace_context.pb.c",
-    "src/core/ext/census/grpc_context.c",
-    "src/core/ext/census/grpc_filter.c",
-    "src/core/ext/census/grpc_plugin.c",
-    "src/core/ext/census/initialize.c",
-    "src/core/ext/census/mlog.c",
-    "src/core/ext/census/operation.c",
-    "src/core/ext/census/placeholders.c",
-    "src/core/ext/census/resource.c",
-    "src/core/ext/census/trace_context.c",
-    "src/core/ext/census/tracing.c",
-    "src/core/plugin_registry/grpc_plugin_registry.c",
-  ],
+grpc_cc_library(
+  name = "grpc_transport_chttp2_client_connector",
   hdrs = [
-    "include/grpc/byte_buffer.h",
-    "include/grpc/byte_buffer_reader.h",
-    "include/grpc/compression.h",
-    "include/grpc/grpc.h",
-    "include/grpc/grpc_posix.h",
-    "include/grpc/grpc_security_constants.h",
-    "include/grpc/slice.h",
-    "include/grpc/slice_buffer.h",
-    "include/grpc/status.h",
-    "include/grpc/impl/codegen/byte_buffer_reader.h",
-    "include/grpc/impl/codegen/compression_types.h",
-    "include/grpc/impl/codegen/connectivity_state.h",
-    "include/grpc/impl/codegen/grpc_types.h",
-    "include/grpc/impl/codegen/propagation_bits.h",
-    "include/grpc/impl/codegen/status.h",
-    "include/grpc/impl/codegen/atm.h",
-    "include/grpc/impl/codegen/atm_gcc_atomic.h",
-    "include/grpc/impl/codegen/atm_gcc_sync.h",
-    "include/grpc/impl/codegen/atm_windows.h",
-    "include/grpc/impl/codegen/gpr_types.h",
-    "include/grpc/impl/codegen/port_platform.h",
-    "include/grpc/impl/codegen/slice.h",
-    "include/grpc/impl/codegen/sync.h",
-    "include/grpc/impl/codegen/sync_generic.h",
-    "include/grpc/impl/codegen/sync_posix.h",
-    "include/grpc/impl/codegen/sync_windows.h",
-    "include/grpc/grpc_security.h",
-    "include/grpc/census.h",
-    "src/core/lib/channel/channel_args.h",
-    "src/core/lib/channel/channel_stack.h",
-    "src/core/lib/channel/channel_stack_builder.h",
-    "src/core/lib/channel/compress_filter.h",
-    "src/core/lib/channel/connected_channel.h",
-    "src/core/lib/channel/context.h",
-    "src/core/lib/channel/deadline_filter.h",
-    "src/core/lib/channel/handshaker.h",
-    "src/core/lib/channel/http_client_filter.h",
-    "src/core/lib/channel/http_server_filter.h",
-    "src/core/lib/channel/message_size_filter.h",
-    "src/core/lib/compression/algorithm_metadata.h",
-    "src/core/lib/compression/message_compress.h",
-    "src/core/lib/debug/trace.h",
-    "src/core/lib/http/format_request.h",
-    "src/core/lib/http/httpcli.h",
-    "src/core/lib/http/parser.h",
-    "src/core/lib/iomgr/closure.h",
-    "src/core/lib/iomgr/combiner.h",
-    "src/core/lib/iomgr/endpoint.h",
-    "src/core/lib/iomgr/endpoint_pair.h",
-    "src/core/lib/iomgr/error.h",
-    "src/core/lib/iomgr/ev_epoll_linux.h",
-    "src/core/lib/iomgr/ev_poll_posix.h",
-    "src/core/lib/iomgr/ev_posix.h",
-    "src/core/lib/iomgr/exec_ctx.h",
-    "src/core/lib/iomgr/executor.h",
-    "src/core/lib/iomgr/iocp_windows.h",
-    "src/core/lib/iomgr/iomgr.h",
-    "src/core/lib/iomgr/iomgr_internal.h",
-    "src/core/lib/iomgr/iomgr_posix.h",
-    "src/core/lib/iomgr/load_file.h",
-    "src/core/lib/iomgr/network_status_tracker.h",
-    "src/core/lib/iomgr/polling_entity.h",
-    "src/core/lib/iomgr/pollset.h",
-    "src/core/lib/iomgr/pollset_set.h",
-    "src/core/lib/iomgr/pollset_set_windows.h",
-    "src/core/lib/iomgr/pollset_uv.h",
-    "src/core/lib/iomgr/pollset_windows.h",
-    "src/core/lib/iomgr/port.h",
-    "src/core/lib/iomgr/resolve_address.h",
-    "src/core/lib/iomgr/resource_quota.h",
-    "src/core/lib/iomgr/sockaddr.h",
-    "src/core/lib/iomgr/sockaddr_posix.h",
-    "src/core/lib/iomgr/sockaddr_utils.h",
-    "src/core/lib/iomgr/sockaddr_windows.h",
-    "src/core/lib/iomgr/socket_mutator.h",
-    "src/core/lib/iomgr/socket_utils.h",
-    "src/core/lib/iomgr/socket_utils_posix.h",
-    "src/core/lib/iomgr/socket_windows.h",
-    "src/core/lib/iomgr/tcp_client.h",
-    "src/core/lib/iomgr/tcp_client_posix.h",
-    "src/core/lib/iomgr/tcp_posix.h",
-    "src/core/lib/iomgr/tcp_server.h",
-    "src/core/lib/iomgr/tcp_uv.h",
-    "src/core/lib/iomgr/tcp_windows.h",
-    "src/core/lib/iomgr/time_averaged_stats.h",
-    "src/core/lib/iomgr/timer.h",
-    "src/core/lib/iomgr/timer_generic.h",
-    "src/core/lib/iomgr/timer_heap.h",
-    "src/core/lib/iomgr/timer_uv.h",
-    "src/core/lib/iomgr/udp_server.h",
-    "src/core/lib/iomgr/unix_sockets_posix.h",
-    "src/core/lib/iomgr/wakeup_fd_cv.h",
-    "src/core/lib/iomgr/wakeup_fd_pipe.h",
-    "src/core/lib/iomgr/wakeup_fd_posix.h",
-    "src/core/lib/iomgr/workqueue.h",
-    "src/core/lib/iomgr/workqueue_uv.h",
-    "src/core/lib/iomgr/workqueue_windows.h",
-    "src/core/lib/json/json.h",
-    "src/core/lib/json/json_common.h",
-    "src/core/lib/json/json_reader.h",
-    "src/core/lib/json/json_writer.h",
-    "src/core/lib/slice/percent_encoding.h",
-    "src/core/lib/slice/slice_string_helpers.h",
-    "src/core/lib/surface/api_trace.h",
-    "src/core/lib/surface/call.h",
-    "src/core/lib/surface/call_test_only.h",
-    "src/core/lib/surface/channel.h",
-    "src/core/lib/surface/channel_init.h",
-    "src/core/lib/surface/channel_stack_type.h",
-    "src/core/lib/surface/completion_queue.h",
-    "src/core/lib/surface/event_string.h",
-    "src/core/lib/surface/init.h",
-    "src/core/lib/surface/lame_client.h",
-    "src/core/lib/surface/server.h",
-    "src/core/lib/transport/byte_stream.h",
-    "src/core/lib/transport/connectivity_state.h",
-    "src/core/lib/transport/mdstr_hash_table.h",
-    "src/core/lib/transport/metadata.h",
-    "src/core/lib/transport/metadata_batch.h",
-    "src/core/lib/transport/pid_controller.h",
-    "src/core/lib/transport/service_config.h",
-    "src/core/lib/transport/static_metadata.h",
-    "src/core/lib/transport/timeout_encoding.h",
-    "src/core/lib/transport/transport.h",
-    "src/core/lib/transport/transport_impl.h",
-    "src/core/ext/transport/chttp2/transport/bin_decoder.h",
-    "src/core/ext/transport/chttp2/transport/bin_encoder.h",
-    "src/core/ext/transport/chttp2/transport/chttp2_transport.h",
-    "src/core/ext/transport/chttp2/transport/frame.h",
-    "src/core/ext/transport/chttp2/transport/frame_data.h",
-    "src/core/ext/transport/chttp2/transport/frame_goaway.h",
-    "src/core/ext/transport/chttp2/transport/frame_ping.h",
-    "src/core/ext/transport/chttp2/transport/frame_rst_stream.h",
-    "src/core/ext/transport/chttp2/transport/frame_settings.h",
-    "src/core/ext/transport/chttp2/transport/frame_window_update.h",
-    "src/core/ext/transport/chttp2/transport/hpack_encoder.h",
-    "src/core/ext/transport/chttp2/transport/hpack_parser.h",
-    "src/core/ext/transport/chttp2/transport/hpack_table.h",
-    "src/core/ext/transport/chttp2/transport/http2_errors.h",
-    "src/core/ext/transport/chttp2/transport/huffsyms.h",
-    "src/core/ext/transport/chttp2/transport/incoming_metadata.h",
-    "src/core/ext/transport/chttp2/transport/internal.h",
-    "src/core/ext/transport/chttp2/transport/status_conversion.h",
-    "src/core/ext/transport/chttp2/transport/stream_map.h",
-    "src/core/ext/transport/chttp2/transport/varint.h",
-    "src/core/ext/transport/chttp2/alpn/alpn.h",
-    "src/core/lib/security/context/security_context.h",
-    "src/core/lib/security/credentials/composite/composite_credentials.h",
-    "src/core/lib/security/credentials/credentials.h",
-    "src/core/lib/security/credentials/fake/fake_credentials.h",
-    "src/core/lib/security/credentials/google_default/google_default_credentials.h",
-    "src/core/lib/security/credentials/iam/iam_credentials.h",
-    "src/core/lib/security/credentials/jwt/json_token.h",
-    "src/core/lib/security/credentials/jwt/jwt_credentials.h",
-    "src/core/lib/security/credentials/jwt/jwt_verifier.h",
-    "src/core/lib/security/credentials/oauth2/oauth2_credentials.h",
-    "src/core/lib/security/credentials/plugin/plugin_credentials.h",
-    "src/core/lib/security/credentials/ssl/ssl_credentials.h",
-    "src/core/lib/security/transport/auth_filters.h",
-    "src/core/lib/security/transport/handshake.h",
-    "src/core/lib/security/transport/secure_endpoint.h",
-    "src/core/lib/security/transport/security_connector.h",
-    "src/core/lib/security/transport/tsi_error.h",
-    "src/core/lib/security/util/b64.h",
-    "src/core/lib/security/util/json_util.h",
-    "src/core/lib/tsi/fake_transport_security.h",
-    "src/core/lib/tsi/ssl_transport_security.h",
-    "src/core/lib/tsi/ssl_types.h",
-    "src/core/lib/tsi/transport_security.h",
-    "src/core/lib/tsi/transport_security_interface.h",
-    "src/core/ext/client_channel/client_channel.h",
-    "src/core/ext/client_channel/client_channel_factory.h",
-    "src/core/ext/client_channel/connector.h",
-    "src/core/ext/client_channel/http_connect_handshaker.h",
-    "src/core/ext/client_channel/initial_connect_string.h",
-    "src/core/ext/client_channel/lb_policy.h",
-    "src/core/ext/client_channel/lb_policy_factory.h",
-    "src/core/ext/client_channel/lb_policy_registry.h",
-    "src/core/ext/client_channel/parse_address.h",
-    "src/core/ext/client_channel/resolver.h",
-    "src/core/ext/client_channel/resolver_factory.h",
-    "src/core/ext/client_channel/resolver_registry.h",
-    "src/core/ext/client_channel/subchannel.h",
-    "src/core/ext/client_channel/subchannel_index.h",
-    "src/core/ext/client_channel/uri_parser.h",
-    "src/core/ext/lb_policy/grpclb/grpclb.h",
-    "src/core/ext/lb_policy/grpclb/load_balancer_api.h",
-    "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h",
-    "src/core/ext/load_reporting/load_reporting.h",
-    "src/core/ext/load_reporting/load_reporting_filter.h",
-    "src/core/ext/census/aggregation.h",
-    "src/core/ext/census/base_resources.h",
-    "src/core/ext/census/census_interface.h",
-    "src/core/ext/census/census_rpc_stats.h",
-    "src/core/ext/census/gen/census.pb.h",
-    "src/core/ext/census/gen/trace_context.pb.h",
-    "src/core/ext/census/grpc_filter.h",
-    "src/core/ext/census/mlog.h",
-    "src/core/ext/census/resource.h",
-    "src/core/ext/census/rpc_metric_id.h",
-    "src/core/ext/census/trace_context.h",
-  ],
-  includes = [
-    "include",
-    ".",
+   "src/core/ext/transport/chttp2/client/chttp2_connector.h",
   ],
-  deps = [
-    ":gpr_objc",
-    "//external:libssl_objc",
-    "//external:nanopb",
-  ],
-  sdk_dylibs = ["libz"],
-)
-
-
-
-cc_binary(
-  name = "grpc_cpp_plugin",
   srcs = [
-    "src/compiler/cpp_plugin.cc",
+   "src/core/ext/transport/chttp2/client/chttp2_connector.c",
   ],
+  language = "c",
   deps = [
-    "//external:protobuf_compiler",
-    ":grpc_plugin_support",
+   "grpc_transport_chttp2",
+   "grpc_base",
+   "grpc_client_channel",
   ],
 )
 
 
-cc_binary(
-  name = "grpc_csharp_plugin",
-  srcs = [
-    "src/compiler/csharp_plugin.cc",
-  ],
-  deps = [
-    "//external:protobuf_compiler",
-    ":grpc_plugin_support",
-  ],
+grpc_cc_library(
+    name = "grpc_transport_chttp2_client_insecure",
+    srcs = [
+        "src/core/ext/transport/chttp2/client/insecure/channel_create.c",
+        "src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c",
+    ],
+    language = "c",
+    deps = [
+        "grpc_base",
+        "grpc_client_channel",
+        "grpc_transport_chttp2",
+        "grpc_transport_chttp2_client_connector",
+    ],
 )
 
-
-cc_binary(
-  name = "grpc_node_plugin",
-  srcs = [
-    "src/compiler/node_plugin.cc",
-  ],
-  deps = [
-    "//external:protobuf_compiler",
-    ":grpc_plugin_support",
-  ],
+grpc_cc_library(
+    name = "grpc_transport_chttp2_client_secure",
+    srcs = [
+        "src/core/ext/transport/chttp2/client/secure/secure_channel_create.c",
+    ],
+    language = "c",
+    deps = [
+        "grpc_base",
+        "grpc_client_channel",
+        "grpc_secure",
+        "grpc_transport_chttp2",
+        "grpc_transport_chttp2_client_connector",
+    ],
 )
 
-
-cc_binary(
-  name = "grpc_objective_c_plugin",
-  srcs = [
-    "src/compiler/objective_c_plugin.cc",
-  ],
-  deps = [
-    "//external:protobuf_compiler",
-    ":grpc_plugin_support",
-  ],
+grpc_cc_library(
+    name = "grpc_transport_chttp2_server",
+    srcs = [
+        "src/core/ext/transport/chttp2/server/chttp2_server.c",
+    ],
+    hdrs = [
+        "src/core/ext/transport/chttp2/server/chttp2_server.h",
+    ],
+    language = "c",
+    deps = [
+        "grpc_base",
+        "grpc_transport_chttp2",
+    ],
 )
 
-
-cc_binary(
-  name = "grpc_php_plugin",
-  srcs = [
-    "src/compiler/php_plugin.cc",
-  ],
-  deps = [
-    "//external:protobuf_compiler",
-    ":grpc_plugin_support",
-  ],
+grpc_cc_library(
+    name = "grpc_transport_chttp2_server_insecure",
+    srcs = [
+        "src/core/ext/transport/chttp2/server/insecure/server_chttp2.c",
+        "src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c",
+    ],
+    language = "c",
+    deps = [
+        "grpc_base",
+        "grpc_transport_chttp2",
+        "grpc_transport_chttp2_server",
+    ],
 )
 
-
-cc_binary(
-  name = "grpc_python_plugin",
-  srcs = [
-    "src/compiler/python_plugin.cc",
-  ],
-  deps = [
-    "//external:protobuf_compiler",
-    ":grpc_plugin_support",
-  ],
+grpc_cc_library(
+    name = "grpc_transport_chttp2_server_secure",
+    srcs = [
+        "src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c",
+    ],
+    language = "c",
+    deps = [
+        "grpc_base",
+        "grpc_secure",
+        "grpc_transport_chttp2",
+        "grpc_transport_chttp2_server",
+    ],
 )
 
-
-cc_binary(
-  name = "grpc_ruby_plugin",
-  srcs = [
-    "src/compiler/ruby_plugin.cc",
-  ],
-  deps = [
-    "//external:protobuf_compiler",
-    ":grpc_plugin_support",
-  ],
+grpc_cc_library(
+    name = "grpc_transport_cronet_client_secure",
+    srcs = [
+        "src/core/ext/transport/cronet/client/secure/cronet_channel_create.c",
+        "src/core/ext/transport/cronet/transport/cronet_api_dummy.c",
+        "src/core/ext/transport/cronet/transport/cronet_transport.c",
+    ],
+    hdrs = [
+        "third_party/objective_c/Cronet/cronet_c_for_grpc.h",
+    ],
+    language = "c",
+    public_hdrs = [
+        "include/grpc/grpc_cronet.h",
+        "include/grpc/grpc_security.h",
+        "include/grpc/grpc_security_constants.h",
+    ],
+    deps = [
+        "grpc_base",
+        "grpc_transport_chttp2",
+    ],
 )
 
-
-
-
-
-
-
-
-objc_path = "src/objective-c"
-
-rx_library_path = objc_path + "/RxLibrary"
-
-objc_library(
-  name = "rx_library",
-  hdrs = glob([
-    rx_library_path + "/*.h",
-    rx_library_path + "/transformations/*.h",
-  ]),
-  srcs = glob([
-    rx_library_path + "/*.m",
-    rx_library_path + "/transformations/*.m",
-  ]),
-  includes = [objc_path],
-  deps = [
-    ":rx_library_private",
-  ],
+grpc_cc_library(
+    name = "tsi",
+    srcs = [
+        "src/core/lib/tsi/fake_transport_security.c",
+        "src/core/lib/tsi/ssl_transport_security.c",
+        "src/core/lib/tsi/transport_security.c",
+    ],
+    hdrs = [
+        "src/core/lib/tsi/fake_transport_security.h",
+        "src/core/lib/tsi/ssl_transport_security.h",
+        "src/core/lib/tsi/ssl_types.h",
+        "src/core/lib/tsi/transport_security.h",
+        "src/core/lib/tsi/transport_security_interface.h",
+    ],
+    external_deps = [
+        "libssl",
+    ],
+    language = "c",
+    deps = [
+        "gpr",
+    ],
 )
 
-objc_library(
-  name = "rx_library_private",
-  hdrs = glob([rx_library_path + "/private/*.h"]),
-  srcs = glob([rx_library_path + "/private/*.m"]),
-  visibility = ["//visibility:private"],
+grpc_cc_library(
+    name = "grpc++_base",
+    srcs = [
+        "src/cpp/client/channel_cc.cc",
+        "src/cpp/client/client_context.cc",
+        "src/cpp/client/create_channel.cc",
+        "src/cpp/client/create_channel_internal.cc",
+        "src/cpp/client/create_channel_posix.cc",
+        "src/cpp/client/credentials_cc.cc",
+        "src/cpp/client/generic_stub.cc",
+        "src/cpp/common/channel_arguments.cc",
+        "src/cpp/common/channel_filter.cc",
+        "src/cpp/common/completion_queue_cc.cc",
+        "src/cpp/common/core_codegen.cc",
+        "src/cpp/common/rpc_method.cc",
+        "src/cpp/server/async_generic_service.cc",
+        "src/cpp/server/create_default_thread_pool.cc",
+        "src/cpp/server/dynamic_thread_pool.cc",
+        "src/cpp/server/server_builder.cc",
+        "src/cpp/server/server_cc.cc",
+        "src/cpp/server/server_context.cc",
+        "src/cpp/server/server_credentials.cc",
+        "src/cpp/server/server_posix.cc",
+        "src/cpp/util/byte_buffer_cc.cc",
+        "src/cpp/util/slice_cc.cc",
+        "src/cpp/util/status.cc",
+        "src/cpp/util/string_ref.cc",
+        "src/cpp/util/time_cc.cc",
+    ],
+    hdrs = [
+        "src/cpp/client/create_channel_internal.h",
+        "src/cpp/common/channel_filter.h",
+        "src/cpp/server/dynamic_thread_pool.h",
+        "src/cpp/server/thread_pool_interface.h",
+        "src/cpp/thread_manager/thread_manager.h",
+    ],
+    language = "c++",
+    public_hdrs = [
+        "include/grpc++/alarm.h",
+        "include/grpc++/channel.h",
+        "include/grpc++/client_context.h",
+        "include/grpc++/completion_queue.h",
+        "include/grpc++/create_channel.h",
+        "include/grpc++/create_channel_posix.h",
+        "include/grpc++/generic/async_generic_service.h",
+        "include/grpc++/generic/generic_stub.h",
+        "include/grpc++/grpc++.h",
+        "include/grpc++/impl/call.h",
+        "include/grpc++/impl/client_unary_call.h",
+        "include/grpc++/impl/codegen/core_codegen.h",
+        "include/grpc++/impl/grpc_library.h",
+        "include/grpc++/impl/method_handler_impl.h",
+        "include/grpc++/impl/rpc_method.h",
+        "include/grpc++/impl/rpc_service_method.h",
+        "include/grpc++/impl/serialization_traits.h",
+        "include/grpc++/impl/server_builder_option.h",
+        "include/grpc++/impl/server_builder_plugin.h",
+        "include/grpc++/impl/server_initializer.h",
+        "include/grpc++/impl/service_type.h",
+        "include/grpc++/impl/sync_cxx11.h",
+        "include/grpc++/impl/sync_no_cxx11.h",
+        "include/grpc++/resource_quota.h",
+        "include/grpc++/security/auth_context.h",
+        "include/grpc++/security/auth_metadata_processor.h",
+        "include/grpc++/security/credentials.h",
+        "include/grpc++/security/server_credentials.h",
+        "include/grpc++/server.h",
+        "include/grpc++/server_builder.h",
+        "include/grpc++/server_context.h",
+        "include/grpc++/server_posix.h",
+        "include/grpc++/support/async_stream.h",
+        "include/grpc++/support/async_unary_call.h",
+        "include/grpc++/support/byte_buffer.h",
+        "include/grpc++/support/channel_arguments.h",
+        "include/grpc++/support/config.h",
+        "include/grpc++/support/slice.h",
+        "include/grpc++/support/status.h",
+        "include/grpc++/support/status_code_enum.h",
+        "include/grpc++/support/string_ref.h",
+        "include/grpc++/support/stub_options.h",
+        "include/grpc++/support/sync_stream.h",
+        "include/grpc++/support/time.h",
+    ],
+    deps = [
+        "grpc",
+        "grpc++_codegen_base",
+    ],
 )
 
-objc_client_path = objc_path + "/GRPCClient"
+grpc_cc_library(
+    name = "grpc++_codegen_base",
+    language = "c++",
+    public_hdrs = [
+        "include/grpc++/impl/codegen/async_stream.h",
+        "include/grpc++/impl/codegen/async_unary_call.h",
+        "include/grpc++/impl/codegen/call.h",
+        "include/grpc++/impl/codegen/call_hook.h",
+        "include/grpc++/impl/codegen/channel_interface.h",
+        "include/grpc++/impl/codegen/client_context.h",
+        "include/grpc++/impl/codegen/client_unary_call.h",
+        "include/grpc++/impl/codegen/completion_queue.h",
+        "include/grpc++/impl/codegen/completion_queue_tag.h",
+        "include/grpc++/impl/codegen/config.h",
+        "include/grpc++/impl/codegen/core_codegen_interface.h",
+        "include/grpc++/impl/codegen/create_auth_context.h",
+        "include/grpc++/impl/codegen/grpc_library.h",
+        "include/grpc++/impl/codegen/method_handler_impl.h",
+        "include/grpc++/impl/codegen/rpc_method.h",
+        "include/grpc++/impl/codegen/rpc_service_method.h",
+        "include/grpc++/impl/codegen/security/auth_context.h",
+        "include/grpc++/impl/codegen/serialization_traits.h",
+        "include/grpc++/impl/codegen/server_context.h",
+        "include/grpc++/impl/codegen/server_interface.h",
+        "include/grpc++/impl/codegen/service_type.h",
+        "include/grpc++/impl/codegen/status.h",
+        "include/grpc++/impl/codegen/status_code_enum.h",
+        "include/grpc++/impl/codegen/status_helper.h",
+        "include/grpc++/impl/codegen/string_ref.h",
+        "include/grpc++/impl/codegen/stub_options.h",
+        "include/grpc++/impl/codegen/sync_stream.h",
+        "include/grpc++/impl/codegen/time.h",
+    ],
+    deps = [
+        "grpc_codegen",
+    ],
+)
 
-objc_library(
-  name = "grpc_client",
-  hdrs = glob([
-    objc_client_path + "/*.h",
-    objc_client_path + "/private/*.h",
-  ]),
-  srcs = glob([
-    objc_client_path + "/*.m",
-    objc_client_path + "/private/*.m",
-  ]),
-  includes = [objc_path],
-  bundles = [":gRPCCertificates"],
-  deps = [
-    ":grpc_objc",
-    ":rx_library",
-  ],
+grpc_cc_library(
+    name = "grpc++_codegen_base_src",
+    srcs = [
+        "src/cpp/codegen/codegen_init.cc",
+    ],
+    language = "c++",
+    deps = [
+        "grpc++_codegen_base",
+    ],
 )
 
-objc_bundle_library(
-    # The choice of name is signicant here, since it determines the bundle name.
-    name = "gRPCCertificates",
-    resources = ["etc/roots.pem"],
+grpc_cc_library(
+    name = "grpc++_codegen_proto",
+    language = "c++",
+    public_hdrs = [
+        "include/grpc++/impl/codegen/proto_utils.h",
+    ],
+    deps = [
+        "grpc++_codegen_base",
+        "grpc++_config_proto",
+    ],
 )
 
-proto_objc_rpc_path = objc_path + "/ProtoRPC"
+grpc_cc_library(
+    name = "grpc++_config_proto",
+    language = "c++",
+    public_hdrs = [
+        "include/grpc++/impl/codegen/config_protobuf.h",
+    ],
+)
 
-objc_library(
-  name = "proto_objc_rpc",
-  hdrs = glob([
-    proto_objc_rpc_path + "/*.h",
-  ]),
-  srcs = glob([
-    proto_objc_rpc_path + "/*.m",
-  ]),
-  includes = [objc_path],
-  deps = [
-    ":grpc_client",
-    ":rx_library",
-    "//external:protobuf_objc",
-  ],
+grpc_cc_library(
+    name = "thrift_util",
+    language = "c++",
+    public_hdrs = [
+        "include/grpc++/impl/codegen/thrift_serializer.h",
+        "include/grpc++/impl/codegen/thrift_utils.h",
+    ],
+    deps = [
+        "grpc++_codegen_base",
+    ],
 )
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1373b759605c51cdae4d386fad8c0584b4c4fc2f..4627c10e2e82ae9d48bc92351e5336748306824c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -66,7 +66,13 @@ set_property(CACHE gRPC_PROTOBUF_PROVIDER PROPERTY STRINGS "module" "package")
 set(gRPC_USE_PROTO_LITE OFF CACHE BOOL "Use the protobuf-lite library")
 
 if (MSVC)
-  add_definitions( -D_WIN32_WINNT=0x600 )
+  add_definitions(-D_WIN32_WINNT=0x600 -D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_WINSOCK_DEPRECATED_NO_WARNINGS)
+  # needed to compile boringssl
+  add_definitions(/wd4464 /wd4623 /wd4668 /wd4701 /wd4702 /wd4777 /wd5027)
+  # needed to compile protobuf
+  add_definitions(/wd4065 /wd4506)
+  # TODO(jtattermusch): revisit C4267 occurrences throughout the code
+  add_definitions(/wd4267)
 endif()
 
 if (gRPC_USE_PROTO_LITE)
@@ -292,6 +298,8 @@ add_library(grpc
   src/core/lib/channel/connected_channel.c
   src/core/lib/channel/deadline_filter.c
   src/core/lib/channel/handshaker.c
+  src/core/lib/channel/handshaker_factory.c
+  src/core/lib/channel/handshaker_registry.c
   src/core/lib/channel/http_client_filter.c
   src/core/lib/channel/http_server_filter.c
   src/core/lib/channel/message_size_filter.c
@@ -436,9 +444,9 @@ add_library(grpc
   src/core/lib/security/credentials/plugin/plugin_credentials.c
   src/core/lib/security/credentials/ssl/ssl_credentials.c
   src/core/lib/security/transport/client_auth_filter.c
-  src/core/lib/security/transport/handshake.c
   src/core/lib/security/transport/secure_endpoint.c
   src/core/lib/security/transport/security_connector.c
+  src/core/lib/security/transport/security_handshaker.c
   src/core/lib/security/transport/server_auth_filter.c
   src/core/lib/security/transport/tsi_error.c
   src/core/lib/security/util/b64.c
@@ -447,6 +455,7 @@ add_library(grpc
   src/core/lib/tsi/fake_transport_security.c
   src/core/lib/tsi/ssl_transport_security.c
   src/core/lib/tsi/transport_security.c
+  src/core/ext/transport/chttp2/server/chttp2_server.c
   src/core/ext/transport/chttp2/client/secure/secure_channel_create.c
   src/core/ext/client_channel/channel_connectivity.c
   src/core/ext/client_channel/client_channel.c
@@ -466,6 +475,7 @@ add_library(grpc
   src/core/ext/client_channel/subchannel.c
   src/core/ext/client_channel/subchannel_index.c
   src/core/ext/client_channel/uri_parser.c
+  src/core/ext/transport/chttp2/client/chttp2_connector.c
   src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
   src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c
   src/core/ext/transport/chttp2/client/insecure/channel_create.c
@@ -528,6 +538,7 @@ foreach(_hdr
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
+  include/grpc/impl/codegen/exec_ctx_fwd.h
   include/grpc/impl/codegen/grpc_types.h
   include/grpc/impl/codegen/propagation_bits.h
   include/grpc/impl/codegen/status.h
@@ -571,6 +582,8 @@ add_library(grpc_cronet
   src/core/lib/channel/connected_channel.c
   src/core/lib/channel/deadline_filter.c
   src/core/lib/channel/handshaker.c
+  src/core/lib/channel/handshaker_factory.c
+  src/core/lib/channel/handshaker_registry.c
   src/core/lib/channel/http_client_filter.c
   src/core/lib/channel/http_server_filter.c
   src/core/lib/channel/message_size_filter.c
@@ -736,9 +749,9 @@ add_library(grpc_cronet
   src/core/lib/security/credentials/plugin/plugin_credentials.c
   src/core/lib/security/credentials/ssl/ssl_credentials.c
   src/core/lib/security/transport/client_auth_filter.c
-  src/core/lib/security/transport/handshake.c
   src/core/lib/security/transport/secure_endpoint.c
   src/core/lib/security/transport/security_connector.c
+  src/core/lib/security/transport/security_handshaker.c
   src/core/lib/security/transport/server_auth_filter.c
   src/core/lib/security/transport/tsi_error.c
   src/core/lib/security/util/b64.c
@@ -747,6 +760,7 @@ add_library(grpc_cronet
   src/core/lib/tsi/fake_transport_security.c
   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/plugin_registry/grpc_cronet_plugin_registry.c
 )
 
@@ -778,6 +792,7 @@ foreach(_hdr
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
+  include/grpc/impl/codegen/exec_ctx_fwd.h
   include/grpc/impl/codegen/grpc_types.h
   include/grpc/impl/codegen/propagation_bits.h
   include/grpc/impl/codegen/status.h
@@ -822,6 +837,8 @@ add_library(grpc_unsecure
   src/core/lib/channel/connected_channel.c
   src/core/lib/channel/deadline_filter.c
   src/core/lib/channel/handshaker.c
+  src/core/lib/channel/handshaker_factory.c
+  src/core/lib/channel/handshaker_registry.c
   src/core/lib/channel/http_client_filter.c
   src/core/lib/channel/http_server_filter.c
   src/core/lib/channel/message_size_filter.c
@@ -951,8 +968,10 @@ add_library(grpc_unsecure
   src/core/ext/transport/chttp2/transport/varint.c
   src/core/ext/transport/chttp2/transport/writing.c
   src/core/ext/transport/chttp2/alpn/alpn.c
+  src/core/ext/transport/chttp2/server/chttp2_server.c
   src/core/ext/transport/chttp2/client/insecure/channel_create.c
   src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c
+  src/core/ext/transport/chttp2/client/chttp2_connector.c
   src/core/ext/client_channel/channel_connectivity.c
   src/core/ext/client_channel/client_channel.c
   src/core/ext/client_channel/client_channel_factory.c
@@ -1027,6 +1046,7 @@ foreach(_hdr
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
+  include/grpc/impl/codegen/exec_ctx_fwd.h
   include/grpc/impl/codegen/grpc_types.h
   include/grpc/impl/codegen/propagation_bits.h
   include/grpc/impl/codegen/status.h
@@ -1190,6 +1210,7 @@ foreach(_hdr
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
+  include/grpc/impl/codegen/exec_ctx_fwd.h
   include/grpc/impl/codegen/grpc_types.h
   include/grpc/impl/codegen/propagation_bits.h
   include/grpc/impl/codegen/status.h
@@ -1258,6 +1279,7 @@ add_library(grpc++_cronet
   src/cpp/codegen/codegen_init.cc
   src/core/ext/transport/chttp2/client/insecure/channel_create.c
   src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c
+  src/core/ext/transport/chttp2/client/chttp2_connector.c
   src/core/ext/transport/chttp2/transport/bin_decoder.c
   src/core/ext/transport/chttp2/transport/bin_encoder.c
   src/core/ext/transport/chttp2/transport/chttp2_plugin.c
@@ -1286,6 +1308,8 @@ add_library(grpc++_cronet
   src/core/lib/channel/connected_channel.c
   src/core/lib/channel/deadline_filter.c
   src/core/lib/channel/handshaker.c
+  src/core/lib/channel/handshaker_factory.c
+  src/core/lib/channel/handshaker_registry.c
   src/core/lib/channel/http_client_filter.c
   src/core/lib/channel/http_server_filter.c
   src/core/lib/channel/message_size_filter.c
@@ -1412,6 +1436,7 @@ add_library(grpc++_cronet
   src/core/ext/client_channel/uri_parser.c
   src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
   src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c
+  src/core/ext/transport/chttp2/server/chttp2_server.c
   src/core/ext/census/base_resources.c
   src/core/ext/census/context.c
   src/core/ext/census/gen/census.pb.c
@@ -1521,6 +1546,7 @@ foreach(_hdr
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
+  include/grpc/impl/codegen/exec_ctx_fwd.h
   include/grpc/impl/codegen/grpc_types.h
   include/grpc/impl/codegen/propagation_bits.h
   include/grpc/impl/codegen/status.h
@@ -1563,45 +1589,6 @@ if (gRPC_INSTALL)
 endif()
 
   
-add_library(grpc++_reflection
-  src/cpp/ext/proto_server_reflection.cc
-  src/cpp/ext/proto_server_reflection_plugin.cc
-  src/proto/grpc/reflection/v1alpha/reflection.proto
-)
-
-target_include_directories(grpc++_reflection
-  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
-  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
-  PRIVATE ${BORINGSSL_ROOT_DIR}/include
-  PRIVATE ${PROTOBUF_ROOT_DIR}/src
-  PRIVATE ${ZLIB_INCLUDE_DIR}
-  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
-)
-
-target_link_libraries(grpc++_reflection
-  grpc++
-)
-
-foreach(_hdr
-  include/grpc++/ext/proto_server_reflection_plugin.h
-)
-  string(REPLACE "include/" "" _path ${_hdr})
-  get_filename_component(_path ${_path} PATH)
-  install(FILES ${_hdr}
-    DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${_path}"
-  )
-endforeach()
-
-  
-if (gRPC_INSTALL)
-  install(TARGETS grpc++_reflection EXPORT gRPCTargets
-    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  )
-endif()
-
-  
 add_library(grpc++_unsecure
   src/cpp/client/insecure_credentials.cc
   src/cpp/common/insecure_create_auth_context.cc
@@ -1727,6 +1714,7 @@ foreach(_hdr
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
+  include/grpc/impl/codegen/exec_ctx_fwd.h
   include/grpc/impl/codegen/grpc_types.h
   include/grpc/impl/codegen/propagation_bits.h
   include/grpc/impl/codegen/status.h
@@ -1801,35 +1789,6 @@ if (gRPC_INSTALL)
   )
 endif()
 
-  
-add_library(grpc_csharp_ext
-  src/csharp/ext/grpc_csharp_ext.c
-)
-
-target_include_directories(grpc_csharp_ext
-  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
-  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
-  PRIVATE ${BORINGSSL_ROOT_DIR}/include
-  PRIVATE ${PROTOBUF_ROOT_DIR}/src
-  PRIVATE ${ZLIB_INCLUDE_DIR}
-  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
-)
-
-target_link_libraries(grpc_csharp_ext
-  grpc
-  gpr
-)
-
-
-  
-if (gRPC_INSTALL)
-  install(TARGETS grpc_csharp_ext EXPORT gRPCTargets
-    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  )
-endif()
-
 
 
 add_executable(gen_hpack_tables
diff --git a/Makefile b/Makefile
index 248770673a6a71eea402d6fded9d560f3fad7f8a..e4cfd30637bf24031c409b79db76f97cefebbcb1 100644
--- a/Makefile
+++ b/Makefile
@@ -292,12 +292,19 @@ AR = libtool -no_warning_for_no_symbols -o
 endif
 STRIP ?= strip -x
 else
+ifeq ($(SYSTEM),MINGW32)
+ifeq ($(origin AR), default)
+AR = ar rcs
+endif
+STRIP ?= strip --strip-unneeded
+else
 ifeq ($(origin AR), default)
 AR = ar rcs
 endif
 STRIP ?= strip
 endif
 endif
+endif
 INSTALL ?= install
 RM ?= rm -f
 PKG_CONFIG ?= pkg-config
@@ -479,6 +486,7 @@ CPP_PC_TEMPLATE = prefix=$(prefix),exec_prefix=\$${prefix},includedir=\$${prefix
 CSHARP_PC_TEMPLATE = prefix=$(prefix),exec_prefix=\$${prefix},includedir=\$${prefix}/include,libdir=\$${exec_prefix}/lib,,Name: $(PC_NAME),Description: $(PC_DESCRIPTION),Version: $(CSHARP_VERSION),Cflags: -I\$${includedir} $(PC_CFLAGS),Requires.private: $(PC_REQUIRES_PRIVATE),Libs: -L\$${libdir} $(PC_LIB),Libs.private: $(PC_LIBS_PRIVATE)
 
 ifeq ($(SYSTEM),MINGW32)
+EXECUTABLE_SUFFIX = .exe
 SHARED_EXT_CORE = dll
 SHARED_EXT_CPP = dll
 SHARED_EXT_CSHARP = dll
@@ -487,6 +495,7 @@ SHARED_VERSION_CORE = -2
 SHARED_VERSION_CPP = -1
 SHARED_VERSION_CSHARP = -1
 else ifeq ($(SYSTEM),Darwin)
+EXECUTABLE_SUFFIX = 
 SHARED_EXT_CORE = dylib
 SHARED_EXT_CPP = dylib
 SHARED_EXT_CSHARP = dylib
@@ -495,6 +504,7 @@ SHARED_VERSION_CORE =
 SHARED_VERSION_CPP =
 SHARED_VERSION_CSHARP =
 else
+EXECUTABLE_SUFFIX = 
 SHARED_EXT_CORE = so.$(CORE_VERSION)
 SHARED_EXT_CPP = so.$(CPP_VERSION)
 SHARED_EXT_CSHARP = so.$(CSHARP_VERSION)
@@ -1013,6 +1023,9 @@ lame_client_test: $(BINDIR)/$(CONFIG)/lame_client_test
 lb_policies_test: $(BINDIR)/$(CONFIG)/lb_policies_test
 load_file_test: $(BINDIR)/$(CONFIG)/load_file_test
 low_level_ping_pong_benchmark: $(BINDIR)/$(CONFIG)/low_level_ping_pong_benchmark
+memory_profile_client: $(BINDIR)/$(CONFIG)/memory_profile_client
+memory_profile_server: $(BINDIR)/$(CONFIG)/memory_profile_server
+memory_profile_test: $(BINDIR)/$(CONFIG)/memory_profile_test
 message_compress_test: $(BINDIR)/$(CONFIG)/message_compress_test
 mlog_test: $(BINDIR)/$(CONFIG)/mlog_test
 multiple_server_queues_test: $(BINDIR)/$(CONFIG)/multiple_server_queues_test
@@ -1085,6 +1098,7 @@ grpc_ruby_plugin: $(BINDIR)/$(CONFIG)/grpc_ruby_plugin
 grpc_tool_test: $(BINDIR)/$(CONFIG)/grpc_tool_test
 grpclb_api_test: $(BINDIR)/$(CONFIG)/grpclb_api_test
 grpclb_test: $(BINDIR)/$(CONFIG)/grpclb_test
+http2_client: $(BINDIR)/$(CONFIG)/http2_client
 hybrid_end2end_test: $(BINDIR)/$(CONFIG)/hybrid_end2end_test
 interop_client: $(BINDIR)/$(CONFIG)/interop_client
 interop_server: $(BINDIR)/$(CONFIG)/interop_server
@@ -1260,9 +1274,9 @@ pc_cxx: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++.pc
 pc_cxx_unsecure: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++_unsecure.pc
 
 ifeq ($(EMBED_OPENSSL),true)
-privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++_proto_reflection_desc_db.a $(LIBDIR)/$(CONFIG)/libgrpc++_test.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libqps.a $(LIBDIR)/$(CONFIG)/libboringssl_test_util.a $(LIBDIR)/$(CONFIG)/libboringssl_aes_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_asn1_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_base64_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_bio_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_bn_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_bytestring_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_aead_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_cipher_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_cmac_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_ed25519_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_x25519_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_dh_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_digest_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_ec_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_ecdsa_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_err_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_evp_extra_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_evp_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_pbkdf_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_hmac_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_pkcs12_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_pkcs8_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_poly1305_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_rsa_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_x509_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_ssl_test_lib.a $(LIBDIR)/$(CONFIG)/libgoogle_benchmark.a
+privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++_proto_reflection_desc_db.a $(LIBDIR)/$(CONFIG)/libgrpc++_test.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libhttp2_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libqps.a $(LIBDIR)/$(CONFIG)/libboringssl_test_util.a $(LIBDIR)/$(CONFIG)/libboringssl_aes_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_asn1_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_base64_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_bio_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_bn_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_bytestring_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_aead_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_cipher_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_cmac_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_ed25519_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_x25519_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_dh_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_digest_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_ec_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_ecdsa_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_err_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_evp_extra_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_evp_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_pbkdf_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_hmac_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_pkcs12_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_pkcs8_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_poly1305_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_rsa_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_x509_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_ssl_test_lib.a $(LIBDIR)/$(CONFIG)/libbenchmark.a
 else
-privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++_proto_reflection_desc_db.a $(LIBDIR)/$(CONFIG)/libgrpc++_test.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libqps.a $(LIBDIR)/$(CONFIG)/libgoogle_benchmark.a
+privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++_proto_reflection_desc_db.a $(LIBDIR)/$(CONFIG)/libgrpc++_test.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libhttp2_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libqps.a $(LIBDIR)/$(CONFIG)/libbenchmark.a
 endif
 
 
@@ -1348,6 +1362,9 @@ buildtests_c: privatelibs_c \
   $(BINDIR)/$(CONFIG)/lame_client_test \
   $(BINDIR)/$(CONFIG)/lb_policies_test \
   $(BINDIR)/$(CONFIG)/load_file_test \
+  $(BINDIR)/$(CONFIG)/memory_profile_client \
+  $(BINDIR)/$(CONFIG)/memory_profile_server \
+  $(BINDIR)/$(CONFIG)/memory_profile_test \
   $(BINDIR)/$(CONFIG)/message_compress_test \
   $(BINDIR)/$(CONFIG)/mlog_test \
   $(BINDIR)/$(CONFIG)/multiple_server_queues_test \
@@ -1467,6 +1484,7 @@ buildtests_cxx: privatelibs_cxx \
   $(BINDIR)/$(CONFIG)/grpc_tool_test \
   $(BINDIR)/$(CONFIG)/grpclb_api_test \
   $(BINDIR)/$(CONFIG)/grpclb_test \
+  $(BINDIR)/$(CONFIG)/http2_client \
   $(BINDIR)/$(CONFIG)/hybrid_end2end_test \
   $(BINDIR)/$(CONFIG)/interop_client \
   $(BINDIR)/$(CONFIG)/interop_server \
@@ -1560,6 +1578,7 @@ buildtests_cxx: privatelibs_cxx \
   $(BINDIR)/$(CONFIG)/grpc_tool_test \
   $(BINDIR)/$(CONFIG)/grpclb_api_test \
   $(BINDIR)/$(CONFIG)/grpclb_test \
+  $(BINDIR)/$(CONFIG)/http2_client \
   $(BINDIR)/$(CONFIG)/hybrid_end2end_test \
   $(BINDIR)/$(CONFIG)/interop_client \
   $(BINDIR)/$(CONFIG)/interop_server \
@@ -1739,6 +1758,8 @@ test_c: buildtests_c
 	$(Q) $(BINDIR)/$(CONFIG)/lame_client_test || ( echo test lame_client_test failed ; exit 1 )
 	$(E) "[RUN]     Testing load_file_test"
 	$(Q) $(BINDIR)/$(CONFIG)/load_file_test || ( echo test load_file_test failed ; exit 1 )
+	$(E) "[RUN]     Testing memory_profile_test"
+	$(Q) $(BINDIR)/$(CONFIG)/memory_profile_test || ( echo test memory_profile_test failed ; exit 1 )
 	$(E) "[RUN]     Testing message_compress_test"
 	$(Q) $(BINDIR)/$(CONFIG)/message_compress_test || ( echo test message_compress_test failed ; exit 1 )
 	$(E) "[RUN]     Testing multiple_server_queues_test"
@@ -2039,7 +2060,7 @@ $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.pb.cc: src/proto/grpc/lb/v1/load_ba
 $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc: src/proto/grpc/lb/v1/load_balancer.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) 
 	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $<
+	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $<
 endif
 
 ifeq ($(NO_PROTOC),true)
@@ -2054,7 +2075,7 @@ $(GENDIR)/src/proto/grpc/reflection/v1alpha/reflection.pb.cc: src/proto/grpc/ref
 $(GENDIR)/src/proto/grpc/reflection/v1alpha/reflection.grpc.pb.cc: src/proto/grpc/reflection/v1alpha/reflection.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) 
 	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $<
+	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $<
 endif
 
 ifeq ($(NO_PROTOC),true)
@@ -2069,7 +2090,7 @@ $(GENDIR)/src/proto/grpc/testing/compiler_test.pb.cc: src/proto/grpc/testing/com
 $(GENDIR)/src/proto/grpc/testing/compiler_test.grpc.pb.cc: src/proto/grpc/testing/compiler_test.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) 
 	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $<
+	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $<
 endif
 
 ifeq ($(NO_PROTOC),true)
@@ -2084,7 +2105,7 @@ $(GENDIR)/src/proto/grpc/testing/control.pb.cc: src/proto/grpc/testing/control.p
 $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc: src/proto/grpc/testing/control.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/payloads.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.grpc.pb.cc
 	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $<
+	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $<
 endif
 
 ifeq ($(NO_PROTOC),true)
@@ -2099,7 +2120,7 @@ $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc: src/proto/grpc/
 $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc: src/proto/grpc/testing/duplicate/echo_duplicate.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc
 	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $<
+	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $<
 endif
 
 ifeq ($(NO_PROTOC),true)
@@ -2114,7 +2135,7 @@ $(GENDIR)/src/proto/grpc/testing/echo.pb.cc: src/proto/grpc/testing/echo.proto $
 $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc: src/proto/grpc/testing/echo.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc
 	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $<
+	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $<
 endif
 
 ifeq ($(NO_PROTOC),true)
@@ -2129,7 +2150,7 @@ $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc: src/proto/grpc/testing/ech
 $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc: src/proto/grpc/testing/echo_messages.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) 
 	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $<
+	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $<
 endif
 
 ifeq ($(NO_PROTOC),true)
@@ -2144,7 +2165,7 @@ $(GENDIR)/src/proto/grpc/testing/empty.pb.cc: src/proto/grpc/testing/empty.proto
 $(GENDIR)/src/proto/grpc/testing/empty.grpc.pb.cc: src/proto/grpc/testing/empty.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) 
 	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $<
+	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $<
 endif
 
 ifeq ($(NO_PROTOC),true)
@@ -2159,7 +2180,7 @@ $(GENDIR)/src/proto/grpc/testing/messages.pb.cc: src/proto/grpc/testing/messages
 $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc: src/proto/grpc/testing/messages.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) 
 	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $<
+	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $<
 endif
 
 ifeq ($(NO_PROTOC),true)
@@ -2174,7 +2195,7 @@ $(GENDIR)/src/proto/grpc/testing/metrics.pb.cc: src/proto/grpc/testing/metrics.p
 $(GENDIR)/src/proto/grpc/testing/metrics.grpc.pb.cc: src/proto/grpc/testing/metrics.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) 
 	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $<
+	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $<
 endif
 
 ifeq ($(NO_PROTOC),true)
@@ -2189,7 +2210,7 @@ $(GENDIR)/src/proto/grpc/testing/payloads.pb.cc: src/proto/grpc/testing/payloads
 $(GENDIR)/src/proto/grpc/testing/payloads.grpc.pb.cc: src/proto/grpc/testing/payloads.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) 
 	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $<
+	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $<
 endif
 
 ifeq ($(NO_PROTOC),true)
@@ -2204,7 +2225,7 @@ $(GENDIR)/src/proto/grpc/testing/services.pb.cc: src/proto/grpc/testing/services
 $(GENDIR)/src/proto/grpc/testing/services.grpc.pb.cc: src/proto/grpc/testing/services.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc
 	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $<
+	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $<
 endif
 
 ifeq ($(NO_PROTOC),true)
@@ -2219,7 +2240,7 @@ $(GENDIR)/src/proto/grpc/testing/stats.pb.cc: src/proto/grpc/testing/stats.proto
 $(GENDIR)/src/proto/grpc/testing/stats.grpc.pb.cc: src/proto/grpc/testing/stats.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) 
 	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $<
+	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $<
 endif
 
 ifeq ($(NO_PROTOC),true)
@@ -2234,7 +2255,7 @@ $(GENDIR)/src/proto/grpc/testing/test.pb.cc: src/proto/grpc/testing/test.proto $
 $(GENDIR)/src/proto/grpc/testing/test.grpc.pb.cc: src/proto/grpc/testing/test.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/empty.pb.cc $(GENDIR)/src/proto/grpc/testing/empty.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc
 	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $<
+	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $<
 endif
 
 
@@ -2329,7 +2350,7 @@ install-shared_c: shared_c strip-shared_c install-pkg-config_c
 	$(Q) $(INSTALL) -d $(prefix)/lib
 	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/$(SHARED_PREFIX)gpr$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(prefix)/lib/$(SHARED_PREFIX)gpr$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE)
 ifeq ($(SYSTEM),MINGW32)
-	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgpr-imp.a $(prefix)/lib/libgpr-imp.a
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgpr$(SHARED_VERSION_CORE)-dll.a $(prefix)/lib/libgpr.a
 else ifneq ($(SYSTEM),Darwin)
 	$(Q) ln -sf $(SHARED_PREFIX)gpr$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(prefix)/lib/libgpr.so.2
 	$(Q) ln -sf $(SHARED_PREFIX)gpr$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(prefix)/lib/libgpr.so
@@ -2338,7 +2359,7 @@ endif
 	$(Q) $(INSTALL) -d $(prefix)/lib
 	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/$(SHARED_PREFIX)grpc$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(prefix)/lib/$(SHARED_PREFIX)grpc$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE)
 ifeq ($(SYSTEM),MINGW32)
-	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc-imp.a $(prefix)/lib/libgrpc-imp.a
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc$(SHARED_VERSION_CORE)-dll.a $(prefix)/lib/libgrpc.a
 else ifneq ($(SYSTEM),Darwin)
 	$(Q) ln -sf $(SHARED_PREFIX)grpc$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(prefix)/lib/libgrpc.so.2
 	$(Q) ln -sf $(SHARED_PREFIX)grpc$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(prefix)/lib/libgrpc.so
@@ -2347,7 +2368,7 @@ endif
 	$(Q) $(INSTALL) -d $(prefix)/lib
 	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/$(SHARED_PREFIX)grpc_cronet$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(prefix)/lib/$(SHARED_PREFIX)grpc_cronet$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE)
 ifeq ($(SYSTEM),MINGW32)
-	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc_cronet-imp.a $(prefix)/lib/libgrpc_cronet-imp.a
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc_cronet$(SHARED_VERSION_CORE)-dll.a $(prefix)/lib/libgrpc_cronet.a
 else ifneq ($(SYSTEM),Darwin)
 	$(Q) ln -sf $(SHARED_PREFIX)grpc_cronet$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(prefix)/lib/libgrpc_cronet.so.2
 	$(Q) ln -sf $(SHARED_PREFIX)grpc_cronet$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(prefix)/lib/libgrpc_cronet.so
@@ -2356,7 +2377,7 @@ endif
 	$(Q) $(INSTALL) -d $(prefix)/lib
 	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/$(SHARED_PREFIX)grpc_unsecure$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(prefix)/lib/$(SHARED_PREFIX)grpc_unsecure$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE)
 ifeq ($(SYSTEM),MINGW32)
-	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure-imp.a $(prefix)/lib/libgrpc_unsecure-imp.a
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure$(SHARED_VERSION_CORE)-dll.a $(prefix)/lib/libgrpc_unsecure.a
 else ifneq ($(SYSTEM),Darwin)
 	$(Q) ln -sf $(SHARED_PREFIX)grpc_unsecure$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(prefix)/lib/libgrpc_unsecure.so.2
 	$(Q) ln -sf $(SHARED_PREFIX)grpc_unsecure$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(prefix)/lib/libgrpc_unsecure.so
@@ -2373,7 +2394,7 @@ install-shared_cxx: shared_cxx strip-shared_cxx install-shared_c install-pkg-con
 	$(Q) $(INSTALL) -d $(prefix)/lib
 	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/$(SHARED_PREFIX)grpc++$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(prefix)/lib/$(SHARED_PREFIX)grpc++$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP)
 ifeq ($(SYSTEM),MINGW32)
-	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++-imp.a $(prefix)/lib/libgrpc++-imp.a
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++$(SHARED_VERSION_CPP)-dll.a $(prefix)/lib/libgrpc++.a
 else ifneq ($(SYSTEM),Darwin)
 	$(Q) ln -sf $(SHARED_PREFIX)grpc++$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(prefix)/lib/libgrpc++.so.2
 	$(Q) ln -sf $(SHARED_PREFIX)grpc++$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(prefix)/lib/libgrpc++.so
@@ -2382,7 +2403,7 @@ endif
 	$(Q) $(INSTALL) -d $(prefix)/lib
 	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/$(SHARED_PREFIX)grpc++_cronet$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(prefix)/lib/$(SHARED_PREFIX)grpc++_cronet$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP)
 ifeq ($(SYSTEM),MINGW32)
-	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++_cronet-imp.a $(prefix)/lib/libgrpc++_cronet-imp.a
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++_cronet$(SHARED_VERSION_CPP)-dll.a $(prefix)/lib/libgrpc++_cronet.a
 else ifneq ($(SYSTEM),Darwin)
 	$(Q) ln -sf $(SHARED_PREFIX)grpc++_cronet$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(prefix)/lib/libgrpc++_cronet.so.2
 	$(Q) ln -sf $(SHARED_PREFIX)grpc++_cronet$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(prefix)/lib/libgrpc++_cronet.so
@@ -2391,7 +2412,7 @@ endif
 	$(Q) $(INSTALL) -d $(prefix)/lib
 	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/$(SHARED_PREFIX)grpc++_reflection$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(prefix)/lib/$(SHARED_PREFIX)grpc++_reflection$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP)
 ifeq ($(SYSTEM),MINGW32)
-	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++_reflection-imp.a $(prefix)/lib/libgrpc++_reflection-imp.a
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++_reflection$(SHARED_VERSION_CPP)-dll.a $(prefix)/lib/libgrpc++_reflection.a
 else ifneq ($(SYSTEM),Darwin)
 	$(Q) ln -sf $(SHARED_PREFIX)grpc++_reflection$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(prefix)/lib/libgrpc++_reflection.so.2
 	$(Q) ln -sf $(SHARED_PREFIX)grpc++_reflection$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(prefix)/lib/libgrpc++_reflection.so
@@ -2400,7 +2421,7 @@ endif
 	$(Q) $(INSTALL) -d $(prefix)/lib
 	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/$(SHARED_PREFIX)grpc++_unsecure$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(prefix)/lib/$(SHARED_PREFIX)grpc++_unsecure$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP)
 ifeq ($(SYSTEM),MINGW32)
-	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure-imp.a $(prefix)/lib/libgrpc++_unsecure-imp.a
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure$(SHARED_VERSION_CPP)-dll.a $(prefix)/lib/libgrpc++_unsecure.a
 else ifneq ($(SYSTEM),Darwin)
 	$(Q) ln -sf $(SHARED_PREFIX)grpc++_unsecure$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(prefix)/lib/libgrpc++_unsecure.so.2
 	$(Q) ln -sf $(SHARED_PREFIX)grpc++_unsecure$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(prefix)/lib/libgrpc++_unsecure.so
@@ -2417,7 +2438,7 @@ install-shared_csharp: shared_csharp strip-shared_csharp
 	$(Q) $(INSTALL) -d $(prefix)/lib
 	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/$(SHARED_PREFIX)grpc_csharp_ext$(SHARED_VERSION_CSHARP).$(SHARED_EXT_CSHARP) $(prefix)/lib/$(SHARED_PREFIX)grpc_csharp_ext$(SHARED_VERSION_CSHARP).$(SHARED_EXT_CSHARP)
 ifeq ($(SYSTEM),MINGW32)
-	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext-imp.a $(prefix)/lib/libgrpc_csharp_ext-imp.a
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext$(SHARED_VERSION_CSHARP)-dll.a $(prefix)/lib/libgrpc_csharp_ext.a
 else ifneq ($(SYSTEM),Darwin)
 	$(Q) ln -sf $(SHARED_PREFIX)grpc_csharp_ext$(SHARED_VERSION_CSHARP).$(SHARED_EXT_CSHARP) $(prefix)/lib/libgrpc_csharp_ext.so.2
 	$(Q) ln -sf $(SHARED_PREFIX)grpc_csharp_ext$(SHARED_VERSION_CSHARP).$(SHARED_EXT_CSHARP) $(prefix)/lib/libgrpc_csharp_ext.so
@@ -2430,9 +2451,6 @@ endif
 
 
 install-plugins: $(PROTOC_PLUGINS)
-ifeq ($(SYSTEM),MINGW32)
-	$(Q) false
-else
 	$(E) "[INSTALL] Installing grpc protoc plugins"
 	$(Q) $(INSTALL) -d $(prefix)/bin
 	$(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_cpp_plugin $(prefix)/bin/grpc_cpp_plugin
@@ -2448,7 +2466,6 @@ else
 	$(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_python_plugin $(prefix)/bin/grpc_python_plugin
 	$(Q) $(INSTALL) -d $(prefix)/bin
 	$(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_ruby_plugin $(prefix)/bin/grpc_ruby_plugin
-endif
 
 install-pkg-config_c: pc_c pc_c_unsecure
 	$(E) "[INSTALL] Installing C pkg-config files"
@@ -2578,7 +2595,7 @@ ifeq ($(SYSTEM),MINGW32)
 $(LIBDIR)/$(CONFIG)/gpr$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE): $(LIBGPR_OBJS)  $(ZLIB_DEP)
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared gpr.def -Wl,--output-def=$(LIBDIR)/$(CONFIG)/gpr$(SHARED_VERSION_CORE).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgpr$(SHARED_VERSION_CORE)-dll.a -o $(LIBDIR)/$(CONFIG)/gpr$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGPR_OBJS) $(LDLIBS) $(ZLIB_MERGE_LIBS)
+	$(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/gpr$(SHARED_VERSION_CORE).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgpr$(SHARED_VERSION_CORE)-dll.a -o $(LIBDIR)/$(CONFIG)/gpr$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGPR_OBJS) $(LDLIBS) $(ZLIB_MERGE_LIBS)
 else
 $(LIBDIR)/$(CONFIG)/libgpr$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE): $(LIBGPR_OBJS)  $(ZLIB_DEP)
 	$(E) "[LD]      Linking $@"
@@ -2631,6 +2648,8 @@ LIBGRPC_SRC = \
     src/core/lib/channel/connected_channel.c \
     src/core/lib/channel/deadline_filter.c \
     src/core/lib/channel/handshaker.c \
+    src/core/lib/channel/handshaker_factory.c \
+    src/core/lib/channel/handshaker_registry.c \
     src/core/lib/channel/http_client_filter.c \
     src/core/lib/channel/http_server_filter.c \
     src/core/lib/channel/message_size_filter.c \
@@ -2775,9 +2794,9 @@ LIBGRPC_SRC = \
     src/core/lib/security/credentials/plugin/plugin_credentials.c \
     src/core/lib/security/credentials/ssl/ssl_credentials.c \
     src/core/lib/security/transport/client_auth_filter.c \
-    src/core/lib/security/transport/handshake.c \
     src/core/lib/security/transport/secure_endpoint.c \
     src/core/lib/security/transport/security_connector.c \
+    src/core/lib/security/transport/security_handshaker.c \
     src/core/lib/security/transport/server_auth_filter.c \
     src/core/lib/security/transport/tsi_error.c \
     src/core/lib/security/util/b64.c \
@@ -2786,6 +2805,7 @@ LIBGRPC_SRC = \
     src/core/lib/tsi/fake_transport_security.c \
     src/core/lib/tsi/ssl_transport_security.c \
     src/core/lib/tsi/transport_security.c \
+    src/core/ext/transport/chttp2/server/chttp2_server.c \
     src/core/ext/transport/chttp2/client/secure/secure_channel_create.c \
     src/core/ext/client_channel/channel_connectivity.c \
     src/core/ext/client_channel/client_channel.c \
@@ -2805,6 +2825,7 @@ LIBGRPC_SRC = \
     src/core/ext/client_channel/subchannel.c \
     src/core/ext/client_channel/subchannel_index.c \
     src/core/ext/client_channel/uri_parser.c \
+    src/core/ext/transport/chttp2/client/chttp2_connector.c \
     src/core/ext/transport/chttp2/server/insecure/server_chttp2.c \
     src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c \
     src/core/ext/transport/chttp2/client/insecure/channel_create.c \
@@ -2850,6 +2871,7 @@ PUBLIC_HEADERS_C += \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
+    include/grpc/impl/codegen/exec_ctx_fwd.h \
     include/grpc/impl/codegen/grpc_types.h \
     include/grpc/impl/codegen/propagation_bits.h \
     include/grpc/impl/codegen/status.h \
@@ -2896,7 +2918,7 @@ ifeq ($(SYSTEM),MINGW32)
 $(LIBDIR)/$(CONFIG)/grpc$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE): $(LIBGRPC_OBJS)  $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/libgpr.a $(OPENSSL_DEP)
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared grpc.def -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc$(SHARED_VERSION_CORE).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc$(SHARED_VERSION_CORE)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGRPC_OBJS) $(LDLIBS) $(LIBDIR)/$(CONFIG)/libgpr.a $(OPENSSL_MERGE_LIBS) $(LDLIBS_SECURE) $(ZLIB_MERGE_LIBS)
+	$(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc$(SHARED_VERSION_CORE).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc$(SHARED_VERSION_CORE)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGRPC_OBJS) $(LDLIBS) $(LIBDIR)/$(CONFIG)/libgpr.a $(OPENSSL_MERGE_LIBS) $(LDLIBS_SECURE) $(ZLIB_MERGE_LIBS)
 else
 $(LIBDIR)/$(CONFIG)/libgrpc$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE): $(LIBGRPC_OBJS)  $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/libgpr.a $(OPENSSL_DEP)
 	$(E) "[LD]      Linking $@"
@@ -2928,6 +2950,8 @@ LIBGRPC_CRONET_SRC = \
     src/core/lib/channel/connected_channel.c \
     src/core/lib/channel/deadline_filter.c \
     src/core/lib/channel/handshaker.c \
+    src/core/lib/channel/handshaker_factory.c \
+    src/core/lib/channel/handshaker_registry.c \
     src/core/lib/channel/http_client_filter.c \
     src/core/lib/channel/http_server_filter.c \
     src/core/lib/channel/message_size_filter.c \
@@ -3093,9 +3117,9 @@ LIBGRPC_CRONET_SRC = \
     src/core/lib/security/credentials/plugin/plugin_credentials.c \
     src/core/lib/security/credentials/ssl/ssl_credentials.c \
     src/core/lib/security/transport/client_auth_filter.c \
-    src/core/lib/security/transport/handshake.c \
     src/core/lib/security/transport/secure_endpoint.c \
     src/core/lib/security/transport/security_connector.c \
+    src/core/lib/security/transport/security_handshaker.c \
     src/core/lib/security/transport/server_auth_filter.c \
     src/core/lib/security/transport/tsi_error.c \
     src/core/lib/security/util/b64.c \
@@ -3104,6 +3128,7 @@ LIBGRPC_CRONET_SRC = \
     src/core/lib/tsi/fake_transport_security.c \
     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/plugin_registry/grpc_cronet_plugin_registry.c \
 
 PUBLIC_HEADERS_C += \
@@ -3119,6 +3144,7 @@ PUBLIC_HEADERS_C += \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
+    include/grpc/impl/codegen/exec_ctx_fwd.h \
     include/grpc/impl/codegen/grpc_types.h \
     include/grpc/impl/codegen/propagation_bits.h \
     include/grpc/impl/codegen/status.h \
@@ -3165,7 +3191,7 @@ ifeq ($(SYSTEM),MINGW32)
 $(LIBDIR)/$(CONFIG)/grpc_cronet$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE): $(LIBGRPC_CRONET_OBJS)  $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/libgpr.a $(OPENSSL_DEP)
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared grpc_cronet.def -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc_cronet$(SHARED_VERSION_CORE).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc_cronet$(SHARED_VERSION_CORE)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc_cronet$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGRPC_CRONET_OBJS) $(LDLIBS) $(LIBDIR)/$(CONFIG)/libgpr.a $(OPENSSL_MERGE_LIBS) $(LDLIBS_SECURE) $(ZLIB_MERGE_LIBS)
+	$(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc_cronet$(SHARED_VERSION_CORE).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc_cronet$(SHARED_VERSION_CORE)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc_cronet$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGRPC_CRONET_OBJS) $(LDLIBS) $(LIBDIR)/$(CONFIG)/libgpr.a $(OPENSSL_MERGE_LIBS) $(LDLIBS_SECURE) $(ZLIB_MERGE_LIBS)
 else
 $(LIBDIR)/$(CONFIG)/libgrpc_cronet$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE): $(LIBGRPC_CRONET_OBJS)  $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/libgpr.a $(OPENSSL_DEP)
 	$(E) "[LD]      Linking $@"
@@ -3216,6 +3242,8 @@ LIBGRPC_TEST_UTIL_SRC = \
     src/core/lib/channel/connected_channel.c \
     src/core/lib/channel/deadline_filter.c \
     src/core/lib/channel/handshaker.c \
+    src/core/lib/channel/handshaker_factory.c \
+    src/core/lib/channel/handshaker_registry.c \
     src/core/lib/channel/http_client_filter.c \
     src/core/lib/channel/http_server_filter.c \
     src/core/lib/channel/message_size_filter.c \
@@ -3335,6 +3363,7 @@ PUBLIC_HEADERS_C += \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
+    include/grpc/impl/codegen/exec_ctx_fwd.h \
     include/grpc/impl/codegen/grpc_types.h \
     include/grpc/impl/codegen/propagation_bits.h \
     include/grpc/impl/codegen/status.h \
@@ -3433,6 +3462,8 @@ LIBGRPC_UNSECURE_SRC = \
     src/core/lib/channel/connected_channel.c \
     src/core/lib/channel/deadline_filter.c \
     src/core/lib/channel/handshaker.c \
+    src/core/lib/channel/handshaker_factory.c \
+    src/core/lib/channel/handshaker_registry.c \
     src/core/lib/channel/http_client_filter.c \
     src/core/lib/channel/http_server_filter.c \
     src/core/lib/channel/message_size_filter.c \
@@ -3562,8 +3593,10 @@ LIBGRPC_UNSECURE_SRC = \
     src/core/ext/transport/chttp2/transport/varint.c \
     src/core/ext/transport/chttp2/transport/writing.c \
     src/core/ext/transport/chttp2/alpn/alpn.c \
+    src/core/ext/transport/chttp2/server/chttp2_server.c \
     src/core/ext/transport/chttp2/client/insecure/channel_create.c \
     src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c \
+    src/core/ext/transport/chttp2/client/chttp2_connector.c \
     src/core/ext/client_channel/channel_connectivity.c \
     src/core/ext/client_channel/client_channel.c \
     src/core/ext/client_channel/client_channel_factory.c \
@@ -3623,6 +3656,7 @@ PUBLIC_HEADERS_C += \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
+    include/grpc/impl/codegen/exec_ctx_fwd.h \
     include/grpc/impl/codegen/grpc_types.h \
     include/grpc/impl/codegen/propagation_bits.h \
     include/grpc/impl/codegen/status.h \
@@ -3657,7 +3691,7 @@ ifeq ($(SYSTEM),MINGW32)
 $(LIBDIR)/$(CONFIG)/grpc_unsecure$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE): $(LIBGRPC_UNSECURE_OBJS)  $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared grpc_unsecure.def -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc_unsecure$(SHARED_VERSION_CORE).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc_unsecure$(SHARED_VERSION_CORE)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc_unsecure$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGRPC_UNSECURE_OBJS) $(LDLIBS) $(LIBDIR)/$(CONFIG)/libgpr.a $(ZLIB_MERGE_LIBS)
+	$(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc_unsecure$(SHARED_VERSION_CORE).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc_unsecure$(SHARED_VERSION_CORE)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc_unsecure$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGRPC_UNSECURE_OBJS) $(LDLIBS) $(LIBDIR)/$(CONFIG)/libgpr.a $(ZLIB_MERGE_LIBS)
 else
 $(LIBDIR)/$(CONFIG)/libgrpc_unsecure$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE): $(LIBGRPC_UNSECURE_OBJS)  $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
@@ -3867,6 +3901,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
+    include/grpc/impl/codegen/exec_ctx_fwd.h \
     include/grpc/impl/codegen/grpc_types.h \
     include/grpc/impl/codegen/propagation_bits.h \
     include/grpc/impl/codegen/status.h \
@@ -3917,10 +3952,10 @@ endif
 
 
 ifeq ($(SYSTEM),MINGW32)
-$(LIBDIR)/$(CONFIG)/grpc++$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP): $(LIBGRPC++_OBJS)  $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/grpc.$(SHARED_EXT_CORE) $(OPENSSL_DEP)
+$(LIBDIR)/$(CONFIG)/grpc++$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP): $(LIBGRPC++_OBJS)  $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/grpc$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(OPENSSL_DEP)
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared grpc++.def -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc++$(SHARED_VERSION_CPP).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc++$(SHARED_VERSION_CPP)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc++$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(LIBGRPC++_OBJS) $(LDLIBS) $(ZLIB_MERGE_LIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgrpc-imp
+	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc++$(SHARED_VERSION_CPP).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc++$(SHARED_VERSION_CPP)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc++$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(LIBGRPC++_OBJS) $(LDLIBS) $(ZLIB_MERGE_LIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgrpc$(SHARED_VERSION_CORE)-dll
 else
 $(LIBDIR)/$(CONFIG)/libgrpc++$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP): $(LIBGRPC++_OBJS)  $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT_CORE) $(OPENSSL_DEP)
 	$(E) "[LD]      Linking $@"
@@ -3981,6 +4016,7 @@ LIBGRPC++_CRONET_SRC = \
     src/cpp/codegen/codegen_init.cc \
     src/core/ext/transport/chttp2/client/insecure/channel_create.c \
     src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c \
+    src/core/ext/transport/chttp2/client/chttp2_connector.c \
     src/core/ext/transport/chttp2/transport/bin_decoder.c \
     src/core/ext/transport/chttp2/transport/bin_encoder.c \
     src/core/ext/transport/chttp2/transport/chttp2_plugin.c \
@@ -4009,6 +4045,8 @@ LIBGRPC++_CRONET_SRC = \
     src/core/lib/channel/connected_channel.c \
     src/core/lib/channel/deadline_filter.c \
     src/core/lib/channel/handshaker.c \
+    src/core/lib/channel/handshaker_factory.c \
+    src/core/lib/channel/handshaker_registry.c \
     src/core/lib/channel/http_client_filter.c \
     src/core/lib/channel/http_server_filter.c \
     src/core/lib/channel/message_size_filter.c \
@@ -4135,6 +4173,7 @@ LIBGRPC++_CRONET_SRC = \
     src/core/ext/client_channel/uri_parser.c \
     src/core/ext/transport/chttp2/server/insecure/server_chttp2.c \
     src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c \
+    src/core/ext/transport/chttp2/server/chttp2_server.c \
     src/core/ext/census/base_resources.c \
     src/core/ext/census/context.c \
     src/core/ext/census/gen/census.pb.c \
@@ -4227,6 +4266,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
+    include/grpc/impl/codegen/exec_ctx_fwd.h \
     include/grpc/impl/codegen/grpc_types.h \
     include/grpc/impl/codegen/propagation_bits.h \
     include/grpc/impl/codegen/status.h \
@@ -4287,10 +4327,10 @@ endif
 
 
 ifeq ($(SYSTEM),MINGW32)
-$(LIBDIR)/$(CONFIG)/grpc++_cronet$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP): $(LIBGRPC++_CRONET_OBJS)  $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/gpr.$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/grpc_cronet.$(SHARED_EXT_CORE) $(OPENSSL_DEP)
+$(LIBDIR)/$(CONFIG)/grpc++_cronet$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP): $(LIBGRPC++_CRONET_OBJS)  $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/gpr$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/grpc_cronet$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(OPENSSL_DEP)
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared grpc++_cronet.def -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc++_cronet$(SHARED_VERSION_CPP).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc++_cronet$(SHARED_VERSION_CPP)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc++_cronet$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(LIBGRPC++_CRONET_OBJS) $(LDLIBS) $(OPENSSL_MERGE_LIBS) $(LDLIBS_SECURE) $(ZLIB_MERGE_LIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr-imp -lgrpc_cronet-imp
+	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc++_cronet$(SHARED_VERSION_CPP).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc++_cronet$(SHARED_VERSION_CPP)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc++_cronet$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(LIBGRPC++_CRONET_OBJS) $(LDLIBS) $(OPENSSL_MERGE_LIBS) $(LDLIBS_SECURE) $(ZLIB_MERGE_LIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr$(SHARED_VERSION_CORE)-dll -lgrpc_cronet$(SHARED_VERSION_CORE)-dll
 else
 $(LIBDIR)/$(CONFIG)/libgrpc++_cronet$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP): $(LIBGRPC++_CRONET_OBJS)  $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libgrpc_cronet.$(SHARED_EXT_CORE) $(OPENSSL_DEP)
 	$(E) "[LD]      Linking $@"
@@ -4410,10 +4450,10 @@ endif
 
 
 ifeq ($(SYSTEM),MINGW32)
-$(LIBDIR)/$(CONFIG)/grpc++_reflection$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP): $(LIBGRPC++_REFLECTION_OBJS)  $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/grpc++.$(SHARED_EXT_CPP) $(OPENSSL_DEP)
+$(LIBDIR)/$(CONFIG)/grpc++_reflection$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP): $(LIBGRPC++_REFLECTION_OBJS)  $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/grpc++$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(OPENSSL_DEP)
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared grpc++_reflection.def -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc++_reflection$(SHARED_VERSION_CPP).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc++_reflection$(SHARED_VERSION_CPP)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc++_reflection$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(LIBGRPC++_REFLECTION_OBJS) $(LDLIBS) $(ZLIB_MERGE_LIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgrpc++-imp
+	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc++_reflection$(SHARED_VERSION_CPP).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc++_reflection$(SHARED_VERSION_CPP)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc++_reflection$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(LIBGRPC++_REFLECTION_OBJS) $(LDLIBS) $(ZLIB_MERGE_LIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgrpc++$(SHARED_VERSION_CPP)-dll
 else
 $(LIBDIR)/$(CONFIG)/libgrpc++_reflection$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP): $(LIBGRPC++_REFLECTION_OBJS)  $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT_CPP) $(OPENSSL_DEP)
 	$(E) "[LD]      Linking $@"
@@ -4582,6 +4622,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
+    include/grpc/impl/codegen/exec_ctx_fwd.h \
     include/grpc/impl/codegen/grpc_types.h \
     include/grpc/impl/codegen/propagation_bits.h \
     include/grpc/impl/codegen/status.h \
@@ -4760,6 +4801,7 @@ PUBLIC_HEADERS_CXX += \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
+    include/grpc/impl/codegen/exec_ctx_fwd.h \
     include/grpc/impl/codegen/grpc_types.h \
     include/grpc/impl/codegen/propagation_bits.h \
     include/grpc/impl/codegen/status.h \
@@ -4800,10 +4842,10 @@ endif
 
 
 ifeq ($(SYSTEM),MINGW32)
-$(LIBDIR)/$(CONFIG)/grpc++_unsecure$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP): $(LIBGRPC++_UNSECURE_OBJS)  $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/gpr.$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/grpc_unsecure.$(SHARED_EXT_CORE)
+$(LIBDIR)/$(CONFIG)/grpc++_unsecure$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP): $(LIBGRPC++_UNSECURE_OBJS)  $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/gpr$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/grpc_unsecure$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE)
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared grpc++_unsecure.def -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc++_unsecure$(SHARED_VERSION_CPP).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc++_unsecure$(SHARED_VERSION_CPP)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc++_unsecure$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(LIBGRPC++_UNSECURE_OBJS) $(LDLIBS) $(ZLIB_MERGE_LIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr-imp -lgrpc_unsecure-imp
+	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc++_unsecure$(SHARED_VERSION_CPP).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc++_unsecure$(SHARED_VERSION_CPP)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc++_unsecure$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP) $(LIBGRPC++_UNSECURE_OBJS) $(LDLIBS) $(ZLIB_MERGE_LIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr$(SHARED_VERSION_CORE)-dll -lgrpc_unsecure$(SHARED_VERSION_CORE)-dll
 else
 $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure$(SHARED_VERSION_CPP).$(SHARED_EXT_CPP): $(LIBGRPC++_UNSECURE_OBJS)  $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT_CORE)
 	$(E) "[LD]      Linking $@"
@@ -4927,6 +4969,59 @@ ifneq ($(NO_DEPS),true)
 endif
 
 
+LIBHTTP2_CLIENT_MAIN_SRC = \
+    $(GENDIR)/src/proto/grpc/testing/empty.pb.cc $(GENDIR)/src/proto/grpc/testing/empty.grpc.pb.cc \
+    $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc \
+    $(GENDIR)/src/proto/grpc/testing/test.pb.cc $(GENDIR)/src/proto/grpc/testing/test.grpc.pb.cc \
+    test/cpp/interop/http2_client.cc \
+
+PUBLIC_HEADERS_CXX += \
+
+LIBHTTP2_CLIENT_MAIN_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBHTTP2_CLIENT_MAIN_SRC))))
+
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure libraries if you don't have OpenSSL.
+
+$(LIBDIR)/$(CONFIG)/libhttp2_client_main.a: openssl_dep_error
+
+
+else
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build a C++ library if you don't have protobuf - a bit overreached, but still okay.
+
+$(LIBDIR)/$(CONFIG)/libhttp2_client_main.a: protobuf_dep_error
+
+
+else
+
+$(LIBDIR)/$(CONFIG)/libhttp2_client_main.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOBUF_DEP) $(LIBHTTP2_CLIENT_MAIN_OBJS) 
+	$(E) "[AR]      Creating $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libhttp2_client_main.a
+	$(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libhttp2_client_main.a $(LIBHTTP2_CLIENT_MAIN_OBJS) 
+ifeq ($(SYSTEM),Darwin)
+	$(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libhttp2_client_main.a
+endif
+
+
+
+
+endif
+
+endif
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(LIBHTTP2_CLIENT_MAIN_OBJS:.o=.dep)
+endif
+endif
+$(OBJDIR)/$(CONFIG)/test/cpp/interop/http2_client.o: $(GENDIR)/src/proto/grpc/testing/empty.pb.cc $(GENDIR)/src/proto/grpc/testing/empty.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/test.pb.cc $(GENDIR)/src/proto/grpc/testing/test.grpc.pb.cc
+
+
 LIBINTEROP_CLIENT_HELPER_SRC = \
     $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc \
     test/cpp/interop/client_helper.cc \
@@ -5293,7 +5388,7 @@ ifeq ($(SYSTEM),MINGW32)
 $(LIBDIR)/$(CONFIG)/grpc_csharp_ext$(SHARED_VERSION_CSHARP).$(SHARED_EXT_CSHARP): $(LIBGRPC_CSHARP_EXT_OBJS)  $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(OPENSSL_DEP)
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(if $(subst Linux,,$(SYSTEM)),,-Wl$(comma)-wrap$(comma)memcpy) -L$(LIBDIR)/$(CONFIG) -shared grpc_csharp_ext.def -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc_csharp_ext$(SHARED_VERSION_CSHARP).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext$(SHARED_VERSION_CSHARP)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc_csharp_ext$(SHARED_VERSION_CSHARP).$(SHARED_EXT_CSHARP) $(LIBGRPC_CSHARP_EXT_OBJS) $(LDLIBS) $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(ZLIB_MERGE_LIBS)
+	$(Q) $(LD) $(LDFLAGS) $(if $(subst Linux,,$(SYSTEM)),,-Wl$(comma)-wrap$(comma)memcpy) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc_csharp_ext$(SHARED_VERSION_CSHARP).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext$(SHARED_VERSION_CSHARP)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc_csharp_ext$(SHARED_VERSION_CSHARP).$(SHARED_EXT_CSHARP) $(LIBGRPC_CSHARP_EXT_OBJS) $(LDLIBS) $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(ZLIB_MERGE_LIBS)
 else
 $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext$(SHARED_VERSION_CSHARP).$(SHARED_EXT_CSHARP): $(LIBGRPC_CSHARP_EXT_OBJS)  $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(OPENSSL_DEP)
 	$(E) "[LD]      Linking $@"
@@ -6991,43 +7086,43 @@ ifneq ($(NO_DEPS),true)
 endif
 
 
-LIBGOOGLE_BENCHMARK_SRC = \
-    third_party/google_benchmark/src/benchmark.cc \
-    third_party/google_benchmark/src/benchmark_register.cc \
-    third_party/google_benchmark/src/colorprint.cc \
-    third_party/google_benchmark/src/commandlineflags.cc \
-    third_party/google_benchmark/src/complexity.cc \
-    third_party/google_benchmark/src/console_reporter.cc \
-    third_party/google_benchmark/src/csv_reporter.cc \
-    third_party/google_benchmark/src/json_reporter.cc \
-    third_party/google_benchmark/src/reporter.cc \
-    third_party/google_benchmark/src/sleep.cc \
-    third_party/google_benchmark/src/string_util.cc \
-    third_party/google_benchmark/src/sysinfo.cc \
-    third_party/google_benchmark/src/timers.cc \
+LIBBENCHMARK_SRC = \
+    third_party/benchmark/src/benchmark.cc \
+    third_party/benchmark/src/benchmark_register.cc \
+    third_party/benchmark/src/colorprint.cc \
+    third_party/benchmark/src/commandlineflags.cc \
+    third_party/benchmark/src/complexity.cc \
+    third_party/benchmark/src/console_reporter.cc \
+    third_party/benchmark/src/csv_reporter.cc \
+    third_party/benchmark/src/json_reporter.cc \
+    third_party/benchmark/src/reporter.cc \
+    third_party/benchmark/src/sleep.cc \
+    third_party/benchmark/src/string_util.cc \
+    third_party/benchmark/src/sysinfo.cc \
+    third_party/benchmark/src/timers.cc \
 
 PUBLIC_HEADERS_CXX += \
 
-LIBGOOGLE_BENCHMARK_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGOOGLE_BENCHMARK_SRC))))
+LIBBENCHMARK_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBBENCHMARK_SRC))))
 
-$(LIBGOOGLE_BENCHMARK_OBJS): CPPFLAGS += -Ithird_party/google_benchmark/include -DHAVE_POSIX_REGEX
+$(LIBBENCHMARK_OBJS): CPPFLAGS += -Ithird_party/benchmark/include -DHAVE_POSIX_REGEX
 
 ifeq ($(NO_PROTOBUF),true)
 
 # You can't build a C++ library if you don't have protobuf - a bit overreached, but still okay.
 
-$(LIBDIR)/$(CONFIG)/libgoogle_benchmark.a: protobuf_dep_error
+$(LIBDIR)/$(CONFIG)/libbenchmark.a: protobuf_dep_error
 
 
 else
 
-$(LIBDIR)/$(CONFIG)/libgoogle_benchmark.a: $(ZLIB_DEP)  $(PROTOBUF_DEP) $(LIBGOOGLE_BENCHMARK_OBJS) 
+$(LIBDIR)/$(CONFIG)/libbenchmark.a: $(ZLIB_DEP)  $(PROTOBUF_DEP) $(LIBBENCHMARK_OBJS) 
 	$(E) "[AR]      Creating $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libgoogle_benchmark.a
-	$(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgoogle_benchmark.a $(LIBGOOGLE_BENCHMARK_OBJS) 
+	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libbenchmark.a
+	$(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libbenchmark.a $(LIBBENCHMARK_OBJS) 
 ifeq ($(SYSTEM),Darwin)
-	$(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgoogle_benchmark.a
+	$(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libbenchmark.a
 endif
 
 
@@ -7036,7 +7131,7 @@ endif
 endif
 
 ifneq ($(NO_DEPS),true)
--include $(LIBGOOGLE_BENCHMARK_OBJS:.o=.dep)
+-include $(LIBBENCHMARK_OBJS:.o=.dep)
 endif
 
 
@@ -10265,6 +10360,102 @@ endif
 endif
 
 
+MEMORY_PROFILE_CLIENT_SRC = \
+    test/core/memory_usage/client.c \
+
+MEMORY_PROFILE_CLIENT_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(MEMORY_PROFILE_CLIENT_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/memory_profile_client: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/memory_profile_client: $(MEMORY_PROFILE_CLIENT_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) $(MEMORY_PROFILE_CLIENT_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)/memory_profile_client
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/memory_usage/client.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_memory_profile_client: $(MEMORY_PROFILE_CLIENT_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(MEMORY_PROFILE_CLIENT_OBJS:.o=.dep)
+endif
+endif
+
+
+MEMORY_PROFILE_SERVER_SRC = \
+    test/core/memory_usage/server.c \
+
+MEMORY_PROFILE_SERVER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(MEMORY_PROFILE_SERVER_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/memory_profile_server: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/memory_profile_server: $(MEMORY_PROFILE_SERVER_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) $(MEMORY_PROFILE_SERVER_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)/memory_profile_server
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/memory_usage/server.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_memory_profile_server: $(MEMORY_PROFILE_SERVER_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(MEMORY_PROFILE_SERVER_OBJS:.o=.dep)
+endif
+endif
+
+
+MEMORY_PROFILE_TEST_SRC = \
+    test/core/memory_usage/memory_usage_test.c \
+
+MEMORY_PROFILE_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(MEMORY_PROFILE_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/memory_profile_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/memory_profile_test: $(MEMORY_PROFILE_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) $(MEMORY_PROFILE_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)/memory_profile_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/memory_usage/memory_usage_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_memory_profile_test: $(MEMORY_PROFILE_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(MEMORY_PROFILE_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 MESSAGE_COMPRESS_TEST_SRC = \
     test/core/compression/message_compress_test.c \
 
@@ -11729,16 +11920,16 @@ $(BINDIR)/$(CONFIG)/bm_fullstack: protobuf_dep_error
 
 else
 
-$(BINDIR)/$(CONFIG)/bm_fullstack: $(PROTOBUF_DEP) $(BM_FULLSTACK_OBJS) $(LIBDIR)/$(CONFIG)/libgoogle_benchmark.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
+$(BINDIR)/$(CONFIG)/bm_fullstack: $(PROTOBUF_DEP) $(BM_FULLSTACK_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_FULLSTACK_OBJS) $(LIBDIR)/$(CONFIG)/libgoogle_benchmark.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_fullstack
+	$(Q) $(LDXX) $(LDFLAGS) $(BM_FULLSTACK_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_fullstack
 
 endif
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/cpp/microbenchmarks/bm_fullstack.o:  $(LIBDIR)/$(CONFIG)/libgoogle_benchmark.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
+$(OBJDIR)/$(CONFIG)/test/cpp/microbenchmarks/bm_fullstack.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_fullstack: $(BM_FULLSTACK_OBJS:.o=.dep)
 
@@ -12881,6 +13072,37 @@ endif
 $(OBJDIR)/$(CONFIG)/test/cpp/grpclb/grpclb_test.o: $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.pb.cc $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc
 
 
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/http2_client: 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)/http2_client: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/http2_client:  $(LIBDIR)/$(CONFIG)/libhttp2_client_main.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS)  $(LIBDIR)/$(CONFIG)/libhttp2_client_main.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/http2_client
+
+endif
+
+endif
+
+
+
+
 HYBRID_END2END_TEST_SRC = \
     test/cpp/end2end/hybrid_end2end_test.cc \
 
@@ -13185,16 +13407,16 @@ $(BINDIR)/$(CONFIG)/noop-benchmark: protobuf_dep_error
 
 else
 
-$(BINDIR)/$(CONFIG)/noop-benchmark: $(PROTOBUF_DEP) $(NOOP-BENCHMARK_OBJS) $(LIBDIR)/$(CONFIG)/libgoogle_benchmark.a
+$(BINDIR)/$(CONFIG)/noop-benchmark: $(PROTOBUF_DEP) $(NOOP-BENCHMARK_OBJS) $(LIBDIR)/$(CONFIG)/libbenchmark.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LDXX) $(LDFLAGS) $(NOOP-BENCHMARK_OBJS) $(LIBDIR)/$(CONFIG)/libgoogle_benchmark.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/noop-benchmark
+	$(Q) $(LDXX) $(LDFLAGS) $(NOOP-BENCHMARK_OBJS) $(LIBDIR)/$(CONFIG)/libbenchmark.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/noop-benchmark
 
 endif
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/cpp/microbenchmarks/noop-benchmark.o:  $(LIBDIR)/$(CONFIG)/libgoogle_benchmark.a
+$(OBJDIR)/$(CONFIG)/test/cpp/microbenchmarks/noop-benchmark.o:  $(LIBDIR)/$(CONFIG)/libbenchmark.a
 
 deps_noop-benchmark: $(NOOP-BENCHMARK_OBJS:.o=.dep)
 
@@ -16760,9 +16982,9 @@ src/core/lib/security/credentials/oauth2/oauth2_credentials.c: $(OPENSSL_DEP)
 src/core/lib/security/credentials/plugin/plugin_credentials.c: $(OPENSSL_DEP)
 src/core/lib/security/credentials/ssl/ssl_credentials.c: $(OPENSSL_DEP)
 src/core/lib/security/transport/client_auth_filter.c: $(OPENSSL_DEP)
-src/core/lib/security/transport/handshake.c: $(OPENSSL_DEP)
 src/core/lib/security/transport/secure_endpoint.c: $(OPENSSL_DEP)
 src/core/lib/security/transport/security_connector.c: $(OPENSSL_DEP)
+src/core/lib/security/transport/security_handshaker.c: $(OPENSSL_DEP)
 src/core/lib/security/transport/server_auth_filter.c: $(OPENSSL_DEP)
 src/core/lib/security/transport/tsi_error.c: $(OPENSSL_DEP)
 src/core/lib/security/util/b64.c: $(OPENSSL_DEP)
@@ -16798,6 +17020,7 @@ test/core/util/test_tcp_server.c: $(OPENSSL_DEP)
 test/cpp/end2end/test_service_impl.cc: $(OPENSSL_DEP)
 test/cpp/interop/client.cc: $(OPENSSL_DEP)
 test/cpp/interop/client_helper.cc: $(OPENSSL_DEP)
+test/cpp/interop/http2_client.cc: $(OPENSSL_DEP)
 test/cpp/interop/interop_client.cc: $(OPENSSL_DEP)
 test/cpp/interop/interop_server.cc: $(OPENSSL_DEP)
 test/cpp/interop/interop_server_bootstrap.cc: $(OPENSSL_DEP)
diff --git a/README.md b/README.md
index 70cf9fcc30e2e4f00d42480adb74412d94b2a0f9..174e861f5945fd9a4bd81e14c5ca4252b59bad41 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,8 @@ See [INSTALL](INSTALL.md) for installation instructions for various platforms.
 
 See [tools/run_tests](tools/run_tests) for more guidance on how to run various test suites (e.g. unit tests, interop tests, benchmarks)
 
+See [Performance dashboard](http://performance-dot-grpc-testing.appspot.com/explore?dashboard=5712453606309888) for the performance numbers for v1.0.x.
+
 #Repository Structure & Status
 
 This repository contains source code for gRPC libraries for multiple languages written on top of shared C core library [src/core] (src/core).
diff --git a/WORKSPACE b/WORKSPACE
new file mode 100644
index 0000000000000000000000000000000000000000..988310963457f9d8052cded0ceb2a326d58d1703
--- /dev/null
+++ b/WORKSPACE
@@ -0,0 +1,47 @@
+bind(
+    name = "nanopb",
+    actual = "//third_party/nanopb",
+)
+
+bind(
+    name = "libssl",
+    actual = "@submodule_boringssl//:ssl",
+)
+
+bind(
+    name = "zlib",
+    actual = "@submodule_zlib//:z",
+)
+
+bind(
+    name = "protobuf",
+    actual = "@submodule_protobuf//:protobuf",
+)
+
+bind(
+    name = "protobuf_clib",
+    actual = "@submodule_protobuf//:protoc_lib",
+)
+
+bind(
+    name = "protocol_compiler",
+    actual = "@submodule_protobuf//:protoc",
+)
+
+new_local_repository(
+    name = "submodule_boringssl",
+    path = "third_party/boringssl-with-bazel",
+    build_file = "third_party/boringssl-with-bazel/BUILD",
+)
+
+new_local_repository(
+    name = "submodule_zlib",
+    path = "third_party/zlib",
+    build_file = "third_party/zlib.BUILD",
+)
+
+new_local_repository(
+    name = "submodule_protobuf",
+    path = "third_party/protobuf",
+    build_file = "third_party/protobuf/BUILD",
+)
diff --git a/bazel/BUILD b/bazel/BUILD
new file mode 100644
index 0000000000000000000000000000000000000000..940a379404f7b429aa36e281288fd997ab84230e
--- /dev/null
+++ b/bazel/BUILD
@@ -0,0 +1,9 @@
+package(default_visibility = ["//:__subpackages__"])
+
+load(":cc_grpc_library.bzl", "cc_grpc_library")
+
+cc_grpc_library(
+    name = "well_known_protos",
+    srcs = "@submodule_protobuf//:well_known_protos",
+    proto_only = True,
+)
diff --git a/bazel/cc_grpc_library.bzl b/bazel/cc_grpc_library.bzl
new file mode 100644
index 0000000000000000000000000000000000000000..e1dd27b0c3fdae7e79d2801a2f5a298503b2b022
--- /dev/null
+++ b/bazel/cc_grpc_library.bzl
@@ -0,0 +1,62 @@
+"""Generates and compiles C++ grpc stubs from proto_library rules."""
+
+load("//:bazel/generate_cc.bzl", "generate_cc")
+
+def cc_grpc_library(name, srcs, deps, proto_only, **kwargs):
+  """Generates C++ grpc classes from a .proto file.
+
+  Assumes the generated classes will be used in cc_api_version = 2.
+
+  Arguments:
+      name: name of rule.
+      srcs: a single proto_library, which wraps the .proto files with services.
+      deps: a list of C++ proto_library (or cc_proto_library) which provides
+        the compiled code of any message that the services depend on.
+      **kwargs: rest of arguments, e.g., compatible_with and visibility.
+  """
+  if len(srcs) > 1:
+    fail("Only one srcs value supported", "srcs")
+
+  proto_target = "_" + name + "_only"
+  codegen_target = "_" + name + "_codegen"
+  codegen_grpc_target = "_" + name + "_grpc_codegen"
+  proto_deps = ["_" + dep + "_only" for dep in deps if dep.find(':') == -1]
+  proto_deps += [dep.split(':')[0] + ':' + "_" + dep.split(':')[1] + "_only" for dep in deps if dep.find(':') != -1]
+
+  native.proto_library(
+      name = proto_target,
+      srcs = srcs,
+      deps = proto_deps,
+      **kwargs
+  )
+
+  generate_cc(
+      name = codegen_target,
+      srcs = [proto_target],
+      **kwargs
+  )
+
+  if not proto_only:
+    generate_cc(
+        name = codegen_grpc_target,
+        srcs = [proto_target],
+        plugin = "//:grpc_cpp_plugin",
+        **kwargs
+    )
+
+  if not proto_only:
+    native.cc_library(
+        name = name,
+        srcs = [":" + codegen_grpc_target, ":" + codegen_target],
+        hdrs = [":" + codegen_grpc_target, ":" + codegen_target],
+        deps = deps + ["//:grpc++", "//:grpc++_codegen_proto", "//external:protobuf"],
+        **kwargs
+    )
+  else:
+    native.cc_library(
+        name = name,
+        srcs = [":" + codegen_target],
+        hdrs = [":" + codegen_target],
+        deps = deps + ["//external:protobuf"],
+        **kwargs
+    )
diff --git a/bazel/generate_cc.bzl b/bazel/generate_cc.bzl
new file mode 100644
index 0000000000000000000000000000000000000000..3665733681e1f85b87c6247d3b87d06f7716d1f5
--- /dev/null
+++ b/bazel/generate_cc.bzl
@@ -0,0 +1,66 @@
+"""Generates C++ grpc stubs from proto_library rules.
+
+This is an internal rule used by cc_grpc_library, and shouldn't be used
+directly.
+"""
+
+def generate_cc_impl(ctx):
+  """Implementation of the generate_cc rule."""
+  protos = [f for src in ctx.attr.srcs for f in src.proto.direct_sources]
+  includes = [f for src in ctx.attr.srcs for f in src.proto.transitive_imports]
+  outs = []
+  if ctx.executable.plugin:
+    outs += [proto.basename[:-len(".proto")] + ".grpc.pb.h" for proto in protos]
+    outs += [proto.basename[:-len(".proto")] + ".grpc.pb.cc" for proto in protos]
+  else:
+    outs += [proto.basename[:-len(".proto")] + ".pb.h" for proto in protos]
+    outs += [proto.basename[:-len(".proto")] + ".pb.cc" for proto in protos]
+  out_files = [ctx.new_file(out) for out in outs]
+  # The following should be replaced with ctx.configuration.buildout
+  # whenever this is added to Skylark.
+  dir_out = out_files[0].dirname[:-len(protos[0].dirname)]
+
+  arguments = []
+  if ctx.executable.plugin:
+    arguments += ["--plugin=protoc-gen-PLUGIN=" + ctx.executable.plugin.path]
+    arguments += ["--PLUGIN_out=" + ",".join(ctx.attr.flags) + ":" + dir_out]
+  else:
+    arguments += ["--cpp_out=" + ",".join(ctx.attr.flags) + ":" + dir_out]
+  arguments += ["-I{0}={0}".format(include.path) for include in includes]
+  arguments += [proto.path for proto in protos]
+
+  ctx.action(
+      inputs = protos + includes,
+      outputs = out_files,
+      executable = ctx.executable._protoc,
+      arguments = arguments,
+  )
+
+  return struct(files=set(out_files))
+
+generate_cc = rule(
+    attrs = {
+        "srcs": attr.label_list(
+            mandatory = True,
+            non_empty = True,
+            providers = ["proto"],
+        ),
+        "plugin": attr.label(
+            executable = True,
+            providers = ["files_to_run"],
+            cfg = "host",
+        ),
+        "flags": attr.string_list(
+            mandatory = False,
+            allow_empty = True,
+        ),
+        "_protoc": attr.label(
+            default = Label("//external:protocol_compiler"),
+            executable = True,
+            cfg = "host",
+        ),
+    },
+    # We generate .h files, so we need to output to genfiles.
+    output_to_genfiles = True,
+    implementation = generate_cc_impl,
+)
diff --git a/bazel/grpc_build_system.bzl b/bazel/grpc_build_system.bzl
new file mode 100644
index 0000000000000000000000000000000000000000..daf8b78527a23b4a8dd77f89c0c0c95217e7fb59
--- /dev/null
+++ b/bazel/grpc_build_system.bzl
@@ -0,0 +1,68 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#
+# This is for the gRPC build system. This isn't intended to be used outsite of
+# the BUILD file for gRPC. It contains the mapping for the template system we
+# use to generate other platform's build system files.
+#
+
+def grpc_cc_library(name, srcs = [], public_hdrs = [], hdrs = [], external_deps = [], deps = [], standalone = False, language = "C++"):
+  copts = []
+  if language.upper() == "C":
+    copts = ["-std=c99"]
+  native.cc_library(
+    name = name,
+    srcs = srcs,
+    hdrs = hdrs + public_hdrs,
+    deps = deps + ["//external:" + dep for dep in external_deps],
+    copts = copts,
+    linkopts = ["-pthread"],
+    includes = [
+        "include"
+    ]
+  )
+
+def grpc_proto_plugin(name, srcs = [], deps = []):
+  native.cc_binary(
+    name = name,
+    srcs = srcs,
+    deps = deps,
+  )
+
+load("//:bazel/cc_grpc_library.bzl", "cc_grpc_library")
+
+def grpc_proto_library(name, srcs = [], deps = [], well_known_deps = [], has_services = True):
+  cc_grpc_library(
+    name = name,
+    srcs = srcs,
+    deps = deps,
+    proto_only = not has_services,
+  )
+
diff --git a/binding.gyp b/binding.gyp
index 28d7c6b9a5d7f9b08d10c3161091f39e81d046c8..479403ed218d1dfaa0276d47a4dc4f18dbce07bb 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -37,18 +37,68 @@
 # Some of this file is built with the help of
 # https://n8.io/converting-a-c-library-to-gyp/
 {
+  'variables': {
+    'runtime%': 'node'
+  },
   'target_defaults': {
     'include_dirs': [
       '.',
       'include'
     ],
     'defines': [
-      'GRPC_UV'
+      'GPR_BACKWARDS_COMPATIBILITY_MODE'
     ],
     'conditions': [
+      ['runtime=="node"', {
+        'defines': [
+          'GRPC_UV'
+        ]
+      }],
+      ['OS!="win" and runtime=="electron"', {
+        "defines": [
+          'OPENSSL_NO_THREADS'
+        ]
+      }],
+      # This is the condition for using boringssl
+      ['OS=="win" or runtime=="electron"', {
+        "include_dirs": [
+          "third_party/boringssl/include"
+        ],
+        "defines": [
+          '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.
+        'defines': [
+          'TSI_OPENSSL_ALPN_SUPPORT=<!(echo <(target) | cut -d. -f1)'
+        ],
+        'include_dirs': [
+          '<(node_root_dir)/deps/openssl/openssl/include',
+        ],
+        'conditions': [
+         ["target_arch=='ia32'", {
+             "include_dirs": [ "<(node_root_dir)/deps/openssl/config/piii" ]
+         }],
+         ["target_arch=='x64'", {
+             "include_dirs": [ "<(node_root_dir)/deps/openssl/config/k8" ]
+         }],
+         ["target_arch=='arm'", {
+             "include_dirs": [ "<(node_root_dir)/deps/openssl/config/arm" ]
+         }]
+        ]
+      }],
       ['OS == "win"', {
         "include_dirs": [
-          "third_party/boringssl/include",
           "third_party/zlib"
         ],
         "defines": [
@@ -58,8 +108,6 @@
           'UNICODE',
           '_UNICODE',
           'NOMINMAX',
-          'OPENSSL_NO_ASM',
-          'GPR_BACKWARDS_COMPATIBILITY_MODE'
         ],
         "msvs_settings": {
           'VCCLCompilerTool': {
@@ -72,21 +120,8 @@
       }, { # OS != "win"
         'variables': {
           'config': '<!(echo $CONFIG)',
-          # 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.
-        'defines': [
-          'TSI_OPENSSL_ALPN_SUPPORT=<!(echo <(target) | cut -d. -f1)',
-          'GPR_BACKWARDS_COMPATIBILITY_MODE'
-        ],
         'include_dirs': [
-          '<(node_root_dir)/deps/openssl/openssl/include',
           '<(node_root_dir)/deps/zlib'
         ],
         'conditions': [
@@ -101,47 +136,14 @@
               '-fprofile-arcs'
             ]
           }
-         ],
-         ["target_arch=='ia32'", {
-             "include_dirs": [ "<(node_root_dir)/deps/openssl/config/piii" ]
-         }],
-         ["target_arch=='x64'", {
-             "include_dirs": [ "<(node_root_dir)/deps/openssl/config/k8" ]
-         }],
-         ["target_arch=='arm'", {
-             "include_dirs": [ "<(node_root_dir)/deps/openssl/config/arm" ]
-         }]
+         ]
         ]
       }]
     ]
   },
   'conditions': [
-    ['OS == "win"', {
+    ['OS=="win" or runtime=="electron"', {
       'targets': [
-        {
-          # IMPORTANT WINDOWS BUILD INFORMATION
-          # This library does not build on Windows without modifying the Node
-          # development packages that node-gyp downloads in order to build.
-          # Due to https://github.com/nodejs/node/issues/4932, the headers for
-          # BoringSSL conflict with the OpenSSL headers included by default
-          # when including the Node headers. The remedy for this is to remove
-          # the OpenSSL headers, from the downloaded Node development package,
-          # which is typically located in `.node-gyp` in your home directory.
-          'target_name': 'WINDOWS_BUILD_WARNING',
-          'actions': [
-            {
-              'action_name': 'WINDOWS_BUILD_WARNING',
-              'inputs': [
-                'package.json'
-              ],
-              'outputs': [
-                'ignore_this_part'
-              ],
-              'action': ['echo', 'IMPORTANT: Due to https://github.com/nodejs/node/issues/4932, to build this library on Windows, you must first remove <(node_root_dir)/include/node/openssl/']
-            }
-          ]
-        },
-        # Only want to compile BoringSSL and zlib under Windows
         {
           'cflags': [
             '-std=c99',
@@ -453,6 +455,34 @@
             'third_party/boringssl/ssl/tls_record.c',
           ]
         },
+      ]
+    }],
+    ['OS == "win"', {
+      'targets': [
+        {
+          # IMPORTANT WINDOWS BUILD INFORMATION
+          # This library does not build on Windows without modifying the Node
+          # development packages that node-gyp downloads in order to build.
+          # Due to https://github.com/nodejs/node/issues/4932, the headers for
+          # BoringSSL conflict with the OpenSSL headers included by default
+          # when including the Node headers. The remedy for this is to remove
+          # the OpenSSL headers, from the downloaded Node development package,
+          # which is typically located in `.node-gyp` in your home directory.
+          'target_name': 'WINDOWS_BUILD_WARNING',
+          'actions': [
+            {
+              'action_name': 'WINDOWS_BUILD_WARNING',
+              'inputs': [
+                'package.json'
+              ],
+              'outputs': [
+                'ignore_this_part'
+              ],
+              'action': ['echo', 'IMPORTANT: Due to https://github.com/nodejs/node/issues/4932, to build this library on Windows, you must first remove <(node_root_dir)/include/node/openssl/']
+            }
+          ]
+        },
+        # Only want to compile zlib under Windows
         {
           'cflags': [
             '-std=c99',
@@ -572,6 +602,8 @@
         'src/core/lib/channel/connected_channel.c',
         'src/core/lib/channel/deadline_filter.c',
         'src/core/lib/channel/handshaker.c',
+        'src/core/lib/channel/handshaker_factory.c',
+        'src/core/lib/channel/handshaker_registry.c',
         'src/core/lib/channel/http_client_filter.c',
         'src/core/lib/channel/http_server_filter.c',
         'src/core/lib/channel/message_size_filter.c',
@@ -716,9 +748,9 @@
         'src/core/lib/security/credentials/plugin/plugin_credentials.c',
         'src/core/lib/security/credentials/ssl/ssl_credentials.c',
         'src/core/lib/security/transport/client_auth_filter.c',
-        'src/core/lib/security/transport/handshake.c',
         'src/core/lib/security/transport/secure_endpoint.c',
         'src/core/lib/security/transport/security_connector.c',
+        'src/core/lib/security/transport/security_handshaker.c',
         'src/core/lib/security/transport/server_auth_filter.c',
         'src/core/lib/security/transport/tsi_error.c',
         'src/core/lib/security/util/b64.c',
@@ -727,6 +759,7 @@
         'src/core/lib/tsi/fake_transport_security.c',
         'src/core/lib/tsi/ssl_transport_security.c',
         'src/core/lib/tsi/transport_security.c',
+        'src/core/ext/transport/chttp2/server/chttp2_server.c',
         'src/core/ext/transport/chttp2/client/secure/secure_channel_create.c',
         'src/core/ext/client_channel/channel_connectivity.c',
         'src/core/ext/client_channel/client_channel.c',
@@ -746,6 +779,7 @@
         'src/core/ext/client_channel/subchannel.c',
         'src/core/ext/client_channel/subchannel_index.c',
         'src/core/ext/client_channel/uri_parser.c',
+        'src/core/ext/transport/chttp2/client/chttp2_connector.c',
         'src/core/ext/transport/chttp2/server/insecure/server_chttp2.c',
         'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c',
         'src/core/ext/transport/chttp2/client/insecure/channel_create.c',
@@ -803,6 +837,11 @@
         '-g'
       ],
       "conditions": [
+        ['OS=="win" or runtime=="electron"', {
+          'dependencies': [
+            "boringssl",
+          ]
+        }],
         ['OS=="mac"', {
           'xcode_settings': {
             'MACOSX_DEPLOYMENT_TARGET': '10.9',
@@ -814,7 +853,6 @@
         }],
         ['OS=="win"', {
           'dependencies': [
-            "boringssl",
             "z",
           ]
         }],
@@ -831,8 +869,8 @@
         "src/node/ext/call_credentials.cc",
         "src/node/ext/channel.cc",
         "src/node/ext/channel_credentials.cc",
-        "src/node/ext/completion_queue.cc",
-        "src/node/ext/completion_queue_async_worker.cc",
+        "src/node/ext/completion_queue_threadpool.cc",
+        "src/node/ext/completion_queue_uv.cc",
         "src/node/ext/node_grpc.cc",
         "src/node/ext/server.cc",
         "src/node/ext/server_credentials.cc",
diff --git a/build.yaml b/build.yaml
index 510b366b613fe05fa0afa817c99c225f0fff83d7..afb5b14b999f38bafdaba85ffcb1a4d38d9c3dfd 100644
--- a/build.yaml
+++ b/build.yaml
@@ -171,6 +171,8 @@ filegroups:
   - src/core/lib/channel/context.h
   - src/core/lib/channel/deadline_filter.h
   - src/core/lib/channel/handshaker.h
+  - src/core/lib/channel/handshaker_factory.h
+  - src/core/lib/channel/handshaker_registry.h
   - src/core/lib/channel/http_client_filter.h
   - src/core/lib/channel/http_server_filter.h
   - src/core/lib/channel/message_size_filter.h
@@ -237,6 +239,7 @@ filegroups:
   - src/core/lib/json/json_reader.h
   - src/core/lib/json/json_writer.h
   - src/core/lib/slice/percent_encoding.h
+  - src/core/lib/slice/slice_internal.h
   - src/core/lib/slice/slice_string_helpers.h
   - src/core/lib/surface/api_trace.h
   - src/core/lib/surface/call.h
@@ -268,6 +271,8 @@ filegroups:
   - src/core/lib/channel/connected_channel.c
   - src/core/lib/channel/deadline_filter.c
   - src/core/lib/channel/handshaker.c
+  - src/core/lib/channel/handshaker_factory.c
+  - src/core/lib/channel/handshaker_registry.c
   - src/core/lib/channel/http_client_filter.c
   - src/core/lib/channel/http_server_filter.c
   - src/core/lib/channel/message_size_filter.c
@@ -421,6 +426,7 @@ filegroups:
   - include/grpc/impl/codegen/byte_buffer_reader.h
   - include/grpc/impl/codegen/compression_types.h
   - include/grpc/impl/codegen/connectivity_state.h
+  - include/grpc/impl/codegen/exec_ctx_fwd.h
   - include/grpc/impl/codegen/grpc_types.h
   - include/grpc/impl/codegen/propagation_bits.h
   - include/grpc/impl/codegen/status.h
@@ -495,9 +501,9 @@ filegroups:
   - src/core/lib/security/credentials/plugin/plugin_credentials.h
   - src/core/lib/security/credentials/ssl/ssl_credentials.h
   - src/core/lib/security/transport/auth_filters.h
-  - src/core/lib/security/transport/handshake.h
   - src/core/lib/security/transport/secure_endpoint.h
   - src/core/lib/security/transport/security_connector.h
+  - src/core/lib/security/transport/security_handshaker.h
   - src/core/lib/security/transport/tsi_error.h
   - src/core/lib/security/util/b64.h
   - src/core/lib/security/util/json_util.h
@@ -518,9 +524,9 @@ filegroups:
   - src/core/lib/security/credentials/plugin/plugin_credentials.c
   - src/core/lib/security/credentials/ssl/ssl_credentials.c
   - src/core/lib/security/transport/client_auth_filter.c
-  - src/core/lib/security/transport/handshake.c
   - src/core/lib/security/transport/secure_endpoint.c
   - src/core/lib/security/transport/security_connector.c
+  - src/core/lib/security/transport/security_handshaker.c
   - src/core/lib/security/transport/server_auth_filter.c
   - src/core/lib/security/transport/tsi_error.c
   - src/core/lib/security/util/b64.c
@@ -621,11 +627,20 @@ filegroups:
   - src/core/ext/transport/chttp2/alpn/alpn.c
   deps:
   - gpr
+- name: grpc_transport_chttp2_client_connector
+  headers:
+  - src/core/ext/transport/chttp2/client/chttp2_connector.h
+  src:
+  - src/core/ext/transport/chttp2/client/chttp2_connector.c
+  uses:
+  - grpc_transport_chttp2
+  - grpc_base
 - name: grpc_transport_chttp2_client_insecure
   src:
   - src/core/ext/transport/chttp2/client/insecure/channel_create.c
   - src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c
   uses:
+  - grpc_transport_chttp2_client_connector
   - grpc_transport_chttp2
   - grpc_base
   - grpc_client_channel
@@ -637,6 +652,15 @@ filegroups:
   - grpc_base
   - grpc_client_channel
   - grpc_secure
+  - grpc_transport_chttp2_client_connector
+- name: grpc_transport_chttp2_server
+  headers:
+  - src/core/ext/transport/chttp2/server/chttp2_server.h
+  src:
+  - src/core/ext/transport/chttp2/server/chttp2_server.c
+  uses:
+  - grpc_transport_chttp2
+  - grpc_base
 - name: grpc_transport_chttp2_server_insecure
   src:
   - src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
@@ -644,6 +668,7 @@ filegroups:
   uses:
   - grpc_transport_chttp2
   - grpc_base
+  - grpc_transport_chttp2_server
 - name: grpc_transport_chttp2_server_secure
   src:
   - src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
@@ -651,6 +676,7 @@ filegroups:
   - grpc_transport_chttp2
   - grpc_base
   - grpc_secure
+  - grpc_transport_chttp2_server
 - name: grpc_transport_cronet_client_secure
   public_headers:
   - include/grpc/grpc_cronet.h
@@ -1191,6 +1217,22 @@ libs:
   vs_project_guid: '{B6E81D84-2ACB-41B8-8781-493A944C7817}'
   vs_props:
   - protoc
+- name: http2_client_main
+  build: private
+  language: c++
+  headers:
+  - test/cpp/interop/http2_client.h
+  src:
+  - src/proto/grpc/testing/empty.proto
+  - src/proto/grpc/testing/messages.proto
+  - src/proto/grpc/testing/test.proto
+  - test/cpp/interop/http2_client.cc
+  deps:
+  - grpc++_test_util
+  - grpc_test_util
+  - grpc++
+  - grpc
+  - grpc++_test_config
 - name: interop_client_helper
   build: private
   language: c++
@@ -1233,6 +1275,7 @@ libs:
   src:
   - test/cpp/interop/server_helper.cc
   deps:
+  - grpc++_test_util
   - grpc_test_util
   - grpc++
   - grpc
@@ -2296,6 +2339,43 @@ targets:
   - mac
   - linux
   - posix
+- name: memory_profile_client
+  build: test
+  run: false
+  language: c
+  src:
+  - test/core/memory_usage/client.c
+  deps:
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
+- name: memory_profile_server
+  build: test
+  run: false
+  language: c
+  src:
+  - test/core/memory_usage/server.c
+  deps:
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
+- name: memory_profile_test
+  cpu_cost: 1.5
+  build: test
+  language: c
+  src:
+  - test/core/memory_usage/memory_usage_test.c
+  deps:
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
+  platforms:
+  - mac
+  - linux
+  - posix
 - name: message_compress_test
   build: test
   language: c
@@ -2830,7 +2910,7 @@ targets:
   src:
   - test/cpp/microbenchmarks/bm_fullstack.cc
   deps:
-  - google_benchmark
+  - benchmark
   - grpc++_test_util
   - grpc_test_util
   - grpc++
@@ -3165,6 +3245,22 @@ targets:
   - grpc++
   - grpc++_test_util
   - grpc_test_util
+- name: http2_client
+  build: test
+  run: false
+  language: c++
+  src: []
+  deps:
+  - http2_client_main
+  - grpc++_test_util
+  - grpc_test_util
+  - grpc++
+  - grpc
+  - grpc++_test_config
+  platforms:
+  - mac
+  - linux
+  - posix
 - name: hybrid_end2end_test
   gtest: true
   build: test
@@ -3280,7 +3376,7 @@ targets:
   src:
   - test/cpp/microbenchmarks/noop-benchmark.cc
   deps:
-  - google_benchmark
+  - benchmark
 - name: proto_server_reflection_test
   gtest: true
   build: test
@@ -3766,6 +3862,8 @@ configs:
       UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
     timeout_multiplier: 1.5
 defaults:
+  benchmark:
+    CPPFLAGS: -Ithird_party/benchmark/include -DHAVE_POSIX_REGEX
   boringssl:
     CFLAGS: -Wno-sign-conversion -Wno-conversion -Wno-unused-value -Wno-unknown-pragmas
       -Wno-implicit-function-declaration -Wno-unused-variable -Wno-sign-compare $(NO_W_EXTRA_SEMI)
@@ -3774,8 +3872,6 @@ defaults:
   global:
     CPPFLAGS: -g -Wall -Wextra -Werror -Wno-long-long -Wno-unused-parameter
     LDFLAGS: -g
-  google_benchmark:
-    CPPFLAGS: -Ithird_party/google_benchmark/include -DHAVE_POSIX_REGEX
   zlib:
     CFLAGS: -Wno-sign-conversion -Wno-conversion -Wno-unused-value -Wno-implicit-function-declaration
       $(W_NO_SHIFT_NEGATIVE_VALUE) -fvisibility=hidden
@@ -3792,7 +3888,6 @@ node_modules:
   - src/node/ext/channel.h
   - src/node/ext/channel_credentials.h
   - src/node/ext/completion_queue.h
-  - src/node/ext/completion_queue_async_worker.h
   - src/node/ext/server.h
   - src/node/ext/server_credentials.h
   - src/node/ext/timeval.h
@@ -3811,8 +3906,8 @@ node_modules:
   - src/node/ext/call_credentials.cc
   - src/node/ext/channel.cc
   - src/node/ext/channel_credentials.cc
-  - src/node/ext/completion_queue.cc
-  - src/node/ext/completion_queue_async_worker.cc
+  - src/node/ext/completion_queue_threadpool.cc
+  - src/node/ext/completion_queue_uv.cc
   - src/node/ext/node_grpc.cc
   - src/node/ext/server.cc
   - src/node/ext/server_credentials.cc
diff --git a/config.m4 b/config.m4
index 7f65b7c0d821ad49ebf06916c951938e8846baa2..85549e5f4270cd07b503ab6b9d0dfeb77c2ed1e3 100644
--- a/config.m4
+++ b/config.m4
@@ -5,9 +5,9 @@ if test "$PHP_GRPC" != "no"; then
   dnl Write more examples of tests here...
 
   dnl # --with-grpc -> add include path
-  PHP_ADD_INCLUDE(../../grpc/include)
-  PHP_ADD_INCLUDE(../../grpc/src/php/ext/grpc)
-  PHP_ADD_INCLUDE(../../grpc/third_party/boringssl/include)
+  PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/include)
+  PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/src/php/ext/grpc)
+  PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/third_party/boringssl/include)
 
   LIBS="-lpthread $LIBS"
 
@@ -88,6 +88,8 @@ if test "$PHP_GRPC" != "no"; then
     src/core/lib/channel/connected_channel.c \
     src/core/lib/channel/deadline_filter.c \
     src/core/lib/channel/handshaker.c \
+    src/core/lib/channel/handshaker_factory.c \
+    src/core/lib/channel/handshaker_registry.c \
     src/core/lib/channel/http_client_filter.c \
     src/core/lib/channel/http_server_filter.c \
     src/core/lib/channel/message_size_filter.c \
@@ -232,9 +234,9 @@ if test "$PHP_GRPC" != "no"; then
     src/core/lib/security/credentials/plugin/plugin_credentials.c \
     src/core/lib/security/credentials/ssl/ssl_credentials.c \
     src/core/lib/security/transport/client_auth_filter.c \
-    src/core/lib/security/transport/handshake.c \
     src/core/lib/security/transport/secure_endpoint.c \
     src/core/lib/security/transport/security_connector.c \
+    src/core/lib/security/transport/security_handshaker.c \
     src/core/lib/security/transport/server_auth_filter.c \
     src/core/lib/security/transport/tsi_error.c \
     src/core/lib/security/util/b64.c \
@@ -243,6 +245,7 @@ if test "$PHP_GRPC" != "no"; then
     src/core/lib/tsi/fake_transport_security.c \
     src/core/lib/tsi/ssl_transport_security.c \
     src/core/lib/tsi/transport_security.c \
+    src/core/ext/transport/chttp2/server/chttp2_server.c \
     src/core/ext/transport/chttp2/client/secure/secure_channel_create.c \
     src/core/ext/client_channel/channel_connectivity.c \
     src/core/ext/client_channel/client_channel.c \
@@ -262,6 +265,7 @@ if test "$PHP_GRPC" != "no"; then
     src/core/ext/client_channel/subchannel.c \
     src/core/ext/client_channel/subchannel_index.c \
     src/core/ext/client_channel/uri_parser.c \
+    src/core/ext/transport/chttp2/client/chttp2_connector.c \
     src/core/ext/transport/chttp2/server/insecure/server_chttp2.c \
     src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c \
     src/core/ext/transport/chttp2/client/insecure/channel_create.c \
@@ -609,8 +613,10 @@ if test "$PHP_GRPC" != "no"; then
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/resolver/dns/native)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/resolver/sockaddr)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/transport/chttp2/alpn)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/transport/chttp2/client)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/transport/chttp2/client/insecure)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/transport/chttp2/client/secure)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/transport/chttp2/server)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/transport/chttp2/server/insecure)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/transport/chttp2/server/secure)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/transport/chttp2/transport)
diff --git a/doc/connection-backoff.md b/doc/connection-backoff.md
index 251a60f384b3576a55e1788a83d6a31479c4296c..0e83d9b97f1b6a5cc327d54e746b3efeb3e55727 100644
--- a/doc/connection-backoff.md
+++ b/doc/connection-backoff.md
@@ -7,9 +7,10 @@ requests) and instead do some form of exponential backoff.
 
 We have several parameters:
  1. INITIAL_BACKOFF (how long to wait after the first failure before retrying)
- 2. MULTIPLIER (factor with which to multiply backoff after a failed retry)
- 3. MAX_BACKOFF (upper bound on backoff)
- 4. MIN_CONNECT_TIMEOUT (minimum time we're willing to give a connection to
+ 1. MULTIPLIER (factor with which to multiply backoff after a failed retry)
+ 1. JITTER (by how much to randomize backoffs).
+ 1. MAX_BACKOFF (upper bound on backoff)
+ 1. MIN_CONNECT_TIMEOUT (minimum time we're willing to give a connection to
     complete)
 
 ## Proposed Backoff Algorithm
diff --git a/doc/connectivity-semantics-and-api.md b/doc/connectivity-semantics-and-api.md
index cc007eaae3c8f0148e4260f7236f0d1a83325872..6d39619d650673f64605952a31d586f4f9f33948 100644
--- a/doc/connectivity-semantics-and-api.md
+++ b/doc/connectivity-semantics-and-api.md
@@ -16,7 +16,7 @@ reconnect, or in the case of HTTP/2 GO_AWAY, re-resolve the name and reconnect.
 
 To hide the details of all this activity from the user of the gRPC API (i.e.,
 application code) while exposing meaningful information about the state of a
-channel, we use a state machine with four states, defined below:
+channel, we use a state machine with five states, defined below:
 
 CONNECTING: The channel is trying to establish a connection and is waiting to
 make progress on one of the steps involved in name resolution, TCP connection
@@ -116,7 +116,7 @@ Channel State API
 
 All gRPC libraries will expose a channel-level API method to poll the current
 state of a channel. In C++, this method is called GetCurrentState and returns
-an enum for one of the four legal states.
+an enum for one of the five legal states.
 
 All libraries should also expose an API that enables the application (user of
 the gRPC API) to be notified when the channel state changes. Since state
diff --git a/doc/cpp/perf_notes.md b/doc/cpp/perf_notes.md
new file mode 100644
index 0000000000000000000000000000000000000000..c87557558dd0fc81d2839e92312c0fc817d0f257
--- /dev/null
+++ b/doc/cpp/perf_notes.md
@@ -0,0 +1,29 @@
+# C++ Performance Notes
+
+## Streaming write buffering
+
+Generally, each write operation (Write(), WritesDone()) implies a syscall.
+gRPC will try to batch together separate write operations from different
+threads, but currently cannot automatically infer batching in a single stream.
+
+If message k+1 in a stream does not rely on responses from message k, it's
+possible to enable write batching by passing a WriteOptions argument to Write
+with the buffer_hint set:
+
+~~~{.cpp}
+stream_writer->Write(message, WriteOptions().set_buffer_hint());
+~~~
+
+The write will be buffered until one of the following is true:
+- the per-stream buffer is filled (controllable with the channel argument
+  GRPC_ARG_HTTP2_WRITE_BUFFER_SIZE) - this prevents infinite buffering leading
+  to OOM
+- a subsequent Write without buffer_hint set is posted
+- the call is finished for writing (WritesDone() called on the client,
+  or Finish() called on an async server stream, or the service handler returns
+  for a sync server stream)
+
+## Completion Queues and Threading in the Async API
+
+Right now, the best performance trade-off is having numcpu's threads and one
+completion queue per thread.
diff --git a/doc/interop-test-descriptions.md b/doc/interop-test-descriptions.md
index 96d1f2bd7526c3421e867ed94ba9986a10f2b358..da3b97674438b5f36d0b3fe6512f5b0f70a4d156 100644
--- a/doc/interop-test-descriptions.md
+++ b/doc/interop-test-descriptions.md
@@ -716,7 +716,9 @@ Procedure:
 
     ```
     {
-      response_size: 314159
+      response_parameters:{
+        size: 314159
+      }
       payload:{
         body: 271828 bytes of zeros
       }
diff --git a/doc/negative-http2-interop-test-descriptions.md b/doc/negative-http2-interop-test-descriptions.md
new file mode 100644
index 0000000000000000000000000000000000000000..b64fe6a024d6f06658b93cf5461e1db73d62ee89
--- /dev/null
+++ b/doc/negative-http2-interop-test-descriptions.md
@@ -0,0 +1,195 @@
+Negative HTTP/2 Interop Test Case Descriptions
+=======================================
+
+Client and server use
+[test.proto](../src/proto/grpc/testing/test.proto).
+
+Server
+------
+The code for the custom http2 server can be found
+[here](https://github.com/grpc/grpc/tree/master/test/http2_test).
+It is responsible for handling requests and sending responses, and also for 
+fulfilling the behavior of each particular test case.
+
+Server should accept these arguments:
+* --port=PORT
+  * The port the server will run on. For example, "8080"
+* --test_case=TESTCASE
+  * The name of the test case to execute. For example, "goaway"
+
+Client
+------
+
+Clients implement test cases that test certain functionality. Each client is
+provided the test case it is expected to run as a command-line parameter. Names
+should be lowercase and without spaces.
+
+Clients should accept these arguments:
+* --server_host=HOSTNAME
+    * The server host to connect to. For example, "localhost" or "127.0.0.1"
+* --server_port=PORT
+    * The server port to connect to. For example, "8080"
+* --test_case=TESTCASE
+    * The name of the test case to execute. For example, "goaway"
+
+Note
+-----
+
+Note that the server and client must be invoked with the same test case or else
+the test will be meaningless. For convenience, we provide a shell script wrapper
+that invokes both server and client at the same time, with the same test_case.
+This is the preferred way to run these tests.
+
+## Test Cases
+
+### goaway
+
+This test verifies that the client correctly responds to a goaway sent by the
+server. The client should handle the goaway by switching to a new stream without
+the user application having to do a thing.
+
+Client Procedure:
+ 1. Client sends two UnaryCall requests (and sleeps for 1 second in-between).
+ TODO: resolve [9300](https://github.com/grpc/grpc/issues/9300) and remove the 1 second sleep
+ 
+    ```
+    {
+      response_size: 314159
+      payload:{
+        body: 271828 bytes of zeros
+      }
+    }
+    ```
+
+Client asserts:
+* Both calls are successful.
+* Response payload body is 314159 bytes in size.
+
+Server Procedure:
+  1. Server sends a GOAWAY after receiving the first UnaryCall.
+
+Server asserts:
+* Two different connections were used from the client.
+
+### rst_after_header
+
+This test verifies that the client fails correctly when the server sends a
+RST_STREAM immediately after sending headers to the client.
+
+Procedure:
+ 1. Client sends UnaryCall with:
+ 
+    ```
+    {
+      response_size: 314159
+      payload:{
+        body: 271828 bytes of zeros
+      }
+    }
+    ```
+
+Client asserts:
+* Call was not successful.
+
+Server Procedure:
+  1. Server sends a RST_STREAM with error code 0 after sending headers to the client.
+  
+*At the moment the error code and message returned are not standardized throughout all
+languages. Those checks will be added once all client languages behave the same way. [#9142](https://github.com/grpc/grpc/issues/9142) is in flight.*
+
+### rst_during_data
+
+This test verifies that the client fails "correctly" when the server sends a
+RST_STREAM halfway through sending data to the client.
+
+Procedure:
+ 1. Client sends UnaryCall with:
+ 
+    ```
+    {
+      response_size: 314159
+      payload:{
+        body: 271828 bytes of zeros
+      }
+    }
+    ```
+
+Client asserts:
+* Call was not successful.
+
+Server Procedure:
+  1. Server sends a RST_STREAM with error code 0 after sending half of 
+     the requested data to the client.
+
+### rst_after_data
+
+This test verifies that the client fails "correctly" when the server sends a
+RST_STREAM after sending all of the data to the client.
+
+Procedure:
+ 1. Client sends UnaryCall with:
+ 
+    ```
+    {
+      response_size: 314159
+      payload:{
+        body: 271828 bytes of zeros
+      }
+    }
+    ```
+
+Client asserts:
+* Call was not successful.
+
+Server Procedure:
+  1. Server sends a RST_STREAM with error code 0 after sending all of the
+  data to the client.
+
+*Certain client languages allow the data to be accessed even though a RST_STREAM
+was encountered. Once all client languages behave this way, checks will be added on
+the incoming data.*
+
+### ping
+
+This test verifies that the client correctly acknowledges all pings it gets from the
+server.
+
+Procedure:
+ 1. Client sends UnaryCall with:
+ 
+    ```
+    {
+      response_size: 314159
+      payload:{
+        body: 271828 bytes of zeros
+      }
+    }
+    ```
+  
+Client asserts:
+* call was successful.
+* response payload body is 314159 bytes in size.
+
+Server Procedure:
+  1. Server tracks the number of outstanding pings (i.e. +1 when it sends a ping, and -1 
+  when it receives an ack from the client).
+  2. Server sends pings before and after sending headers, also before and after sending data.
+  
+Server Asserts:
+* Number of outstanding pings is 0 when the connection is lost.
+
+### max_streams
+
+This test verifies that the client observes the MAX_CONCURRENT_STREAMS limit set by the server.
+
+Client Procedure:
+  1. Client sends initial UnaryCall to allow the server to update its MAX_CONCURRENT_STREAMS settings.
+  2. Client concurrently sends 10 UnaryCalls.
+  
+Client Asserts:
+* All UnaryCalls were successful, and had the correct type and payload size.
+ 
+Server Procedure:
+  1. Sets MAX_CONCURRENT_STREAMS to one after the connection is made.
+
+*The assertion that the MAX_CONCURRENT_STREAMS limit is upheld occurs in the http2 library we used.*
diff --git a/examples/cpp/helloworld/greeter_client.cc b/examples/cpp/helloworld/greeter_client.cc
index 12209f37dfd750267b025a6f7d8eec8c2621a0dc..61f3953056a8478dceb8689f308a16e3d4c41363 100644
--- a/examples/cpp/helloworld/greeter_client.cc
+++ b/examples/cpp/helloworld/greeter_client.cc
@@ -51,7 +51,7 @@ class GreeterClient {
   GreeterClient(std::shared_ptr<Channel> channel)
       : stub_(Greeter::NewStub(channel)) {}
 
-  // Assambles the client's payload, sends it and presents the response back
+  // Assembles the client's payload, sends it and presents the response back
   // from the server.
   std::string SayHello(const std::string& user) {
     // Data we are sending to the server.
diff --git a/examples/python/helloworld/greeter_client.py b/examples/python/helloworld/greeter_client.py
index 44d42c102b5f864ab4a36d25c488966bbdc0d946..281a68f3c3d3e221d6486f4e09208df447ebb2e1 100644
--- a/examples/python/helloworld/greeter_client.py
+++ b/examples/python/helloworld/greeter_client.py
@@ -34,11 +34,12 @@ from __future__ import print_function
 import grpc
 
 import helloworld_pb2
+import helloworld_pb2_grpc
 
 
 def run():
   channel = grpc.insecure_channel('localhost:50051')
-  stub = helloworld_pb2.GreeterStub(channel)
+  stub = helloworld_pb2_grpc.GreeterStub(channel)
   response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
   print("Greeter client received: " + response.message)
 
diff --git a/examples/python/helloworld/greeter_server.py b/examples/python/helloworld/greeter_server.py
index 37d8bd49ccdce81e2dc5e26be289da55cc289172..0afc21d243fe90bf34c53d6e9791950fc4834490 100644
--- a/examples/python/helloworld/greeter_server.py
+++ b/examples/python/helloworld/greeter_server.py
@@ -35,11 +35,12 @@ import time
 import grpc
 
 import helloworld_pb2
+import helloworld_pb2_grpc
 
 _ONE_DAY_IN_SECONDS = 60 * 60 * 24
 
 
-class Greeter(helloworld_pb2.GreeterServicer):
+class Greeter(helloworld_pb2_grpc.GreeterServicer):
 
   def SayHello(self, request, context):
     return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)
@@ -47,7 +48,7 @@ class Greeter(helloworld_pb2.GreeterServicer):
 
 def serve():
   server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
-  helloworld_pb2.add_GreeterServicer_to_server(Greeter(), server)
+  helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
   server.add_insecure_port('[::]:50051')
   server.start()
   try:
diff --git a/examples/python/helloworld/helloworld_pb2.py b/examples/python/helloworld/helloworld_pb2.py
index 3ce33fbf2bf36f011a2a871ab0d48cc926c60d1d..6665b1f6878f492e24fd1cf85372d9766c3cf9c1 100644
--- a/examples/python/helloworld/helloworld_pb2.py
+++ b/examples/python/helloworld/helloworld_pb2.py
@@ -107,98 +107,123 @@ _sym_db.RegisterMessage(HelloReply)
 
 DESCRIPTOR.has_options = True
 DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\033io.grpc.examples.helloworldB\017HelloWorldProtoP\001\242\002\003HLW'))
-import grpc
-from grpc.beta import implementations as beta_implementations
-from grpc.beta import interfaces as beta_interfaces
-from grpc.framework.common import cardinality
-from grpc.framework.interfaces.face import utilities as face_utilities
+try:
+  # THESE ELEMENTS WILL BE DEPRECATED.
+  # Please use the generated *_pb2_grpc.py files instead.
+  import grpc
+  from grpc.framework.common import cardinality
+  from grpc.framework.interfaces.face import utilities as face_utilities
+  from grpc.beta import implementations as beta_implementations
+  from grpc.beta import interfaces as beta_interfaces
+
+
+  class GreeterStub(object):
+    """The greeting service definition.
+    """
 
+    def __init__(self, channel):
+      """Constructor.
 
-class GreeterStub(object):
-  """The greeting service definition.
-  """
+      Args:
+        channel: A grpc.Channel.
+      """
+      self.SayHello = channel.unary_unary(
+          '/helloworld.Greeter/SayHello',
+          request_serializer=HelloRequest.SerializeToString,
+          response_deserializer=HelloReply.FromString,
+          )
 
-  def __init__(self, channel):
-    """Constructor.
 
-    Args:
-      channel: A grpc.Channel.
+  class GreeterServicer(object):
+    """The greeting service definition.
     """
-    self.SayHello = channel.unary_unary(
-        '/helloworld.Greeter/SayHello',
-        request_serializer=HelloRequest.SerializeToString,
-        response_deserializer=HelloReply.FromString,
-        )
-
-
-class GreeterServicer(object):
-  """The greeting service definition.
-  """
 
-  def SayHello(self, request, context):
-    """Sends a greeting
+    def SayHello(self, request, context):
+      """Sends a greeting
+      """
+      context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+      context.set_details('Method not implemented!')
+      raise NotImplementedError('Method not implemented!')
+
+
+  def add_GreeterServicer_to_server(servicer, server):
+    rpc_method_handlers = {
+        'SayHello': grpc.unary_unary_rpc_method_handler(
+            servicer.SayHello,
+            request_deserializer=HelloRequest.FromString,
+            response_serializer=HelloReply.SerializeToString,
+        ),
+    }
+    generic_handler = grpc.method_handlers_generic_handler(
+        'helloworld.Greeter', rpc_method_handlers)
+    server.add_generic_rpc_handlers((generic_handler,))
+
+
+  class BetaGreeterServicer(object):
+    """The Beta API is deprecated for 0.15.0 and later.
+
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This class was generated
+    only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0."""
+    """The greeting service definition.
     """
-    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
-    context.set_details('Method not implemented!')
-    raise NotImplementedError('Method not implemented!')
-
-
-def add_GreeterServicer_to_server(servicer, server):
-  rpc_method_handlers = {
-      'SayHello': grpc.unary_unary_rpc_method_handler(
-          servicer.SayHello,
-          request_deserializer=HelloRequest.FromString,
-          response_serializer=HelloReply.SerializeToString,
-      ),
-  }
-  generic_handler = grpc.method_handlers_generic_handler(
-      'helloworld.Greeter', rpc_method_handlers)
-  server.add_generic_rpc_handlers((generic_handler,))
-
-
-class BetaGreeterServicer(object):
-  """The greeting service definition.
-  """
-  def SayHello(self, request, context):
-    """Sends a greeting
-    """
-    context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
+    def SayHello(self, request, context):
+      """Sends a greeting
+      """
+      context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
+
 
+  class BetaGreeterStub(object):
+    """The Beta API is deprecated for 0.15.0 and later.
 
-class BetaGreeterStub(object):
-  """The greeting service definition.
-  """
-  def SayHello(self, request, timeout, metadata=None, with_call=False, protocol_options=None):
-    """Sends a greeting
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This class was generated
+    only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0."""
+    """The greeting service definition.
     """
-    raise NotImplementedError()
-  SayHello.future = None
-
-
-def beta_create_Greeter_server(servicer, pool=None, pool_size=None, default_timeout=None, maximum_timeout=None):
-  request_deserializers = {
-    ('helloworld.Greeter', 'SayHello'): HelloRequest.FromString,
-  }
-  response_serializers = {
-    ('helloworld.Greeter', 'SayHello'): HelloReply.SerializeToString,
-  }
-  method_implementations = {
-    ('helloworld.Greeter', 'SayHello'): face_utilities.unary_unary_inline(servicer.SayHello),
-  }
-  server_options = beta_implementations.server_options(request_deserializers=request_deserializers, response_serializers=response_serializers, thread_pool=pool, thread_pool_size=pool_size, default_timeout=default_timeout, maximum_timeout=maximum_timeout)
-  return beta_implementations.server(method_implementations, options=server_options)
-
-
-def beta_create_Greeter_stub(channel, host=None, metadata_transformer=None, pool=None, pool_size=None):
-  request_serializers = {
-    ('helloworld.Greeter', 'SayHello'): HelloRequest.SerializeToString,
-  }
-  response_deserializers = {
-    ('helloworld.Greeter', 'SayHello'): HelloReply.FromString,
-  }
-  cardinalities = {
-    'SayHello': cardinality.Cardinality.UNARY_UNARY,
-  }
-  stub_options = beta_implementations.stub_options(host=host, metadata_transformer=metadata_transformer, request_serializers=request_serializers, response_deserializers=response_deserializers, thread_pool=pool, thread_pool_size=pool_size)
-  return beta_implementations.dynamic_stub(channel, 'helloworld.Greeter', cardinalities, options=stub_options)
+    def SayHello(self, request, timeout, metadata=None, with_call=False, protocol_options=None):
+      """Sends a greeting
+      """
+      raise NotImplementedError()
+    SayHello.future = None
+
+
+  def beta_create_Greeter_server(servicer, pool=None, pool_size=None, default_timeout=None, maximum_timeout=None):
+    """The Beta API is deprecated for 0.15.0 and later.
+
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This function was
+    generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"""
+    request_deserializers = {
+      ('helloworld.Greeter', 'SayHello'): HelloRequest.FromString,
+    }
+    response_serializers = {
+      ('helloworld.Greeter', 'SayHello'): HelloReply.SerializeToString,
+    }
+    method_implementations = {
+      ('helloworld.Greeter', 'SayHello'): face_utilities.unary_unary_inline(servicer.SayHello),
+    }
+    server_options = beta_implementations.server_options(request_deserializers=request_deserializers, response_serializers=response_serializers, thread_pool=pool, thread_pool_size=pool_size, default_timeout=default_timeout, maximum_timeout=maximum_timeout)
+    return beta_implementations.server(method_implementations, options=server_options)
+
+
+  def beta_create_Greeter_stub(channel, host=None, metadata_transformer=None, pool=None, pool_size=None):
+    """The Beta API is deprecated for 0.15.0 and later.
+
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This function was
+    generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"""
+    request_serializers = {
+      ('helloworld.Greeter', 'SayHello'): HelloRequest.SerializeToString,
+    }
+    response_deserializers = {
+      ('helloworld.Greeter', 'SayHello'): HelloReply.FromString,
+    }
+    cardinalities = {
+      'SayHello': cardinality.Cardinality.UNARY_UNARY,
+    }
+    stub_options = beta_implementations.stub_options(host=host, metadata_transformer=metadata_transformer, request_serializers=request_serializers, response_deserializers=response_deserializers, thread_pool=pool, thread_pool_size=pool_size)
+    return beta_implementations.dynamic_stub(channel, 'helloworld.Greeter', cardinalities, options=stub_options)
+except ImportError:
+  pass
 # @@protoc_insertion_point(module_scope)
diff --git a/examples/python/helloworld/helloworld_pb2_grpc.py b/examples/python/helloworld/helloworld_pb2_grpc.py
new file mode 100644
index 0000000000000000000000000000000000000000..682dc36cd89fb3d1c5b7b1da58f90e3c69a1d977
--- /dev/null
+++ b/examples/python/helloworld/helloworld_pb2_grpc.py
@@ -0,0 +1,47 @@
+import grpc
+from grpc.framework.common import cardinality
+from grpc.framework.interfaces.face import utilities as face_utilities
+
+import helloworld_pb2 as helloworld__pb2
+
+
+class GreeterStub(object):
+  """The greeting service definition.
+  """
+
+  def __init__(self, channel):
+    """Constructor.
+
+    Args:
+      channel: A grpc.Channel.
+    """
+    self.SayHello = channel.unary_unary(
+        '/helloworld.Greeter/SayHello',
+        request_serializer=helloworld__pb2.HelloRequest.SerializeToString,
+        response_deserializer=helloworld__pb2.HelloReply.FromString,
+        )
+
+
+class GreeterServicer(object):
+  """The greeting service definition.
+  """
+
+  def SayHello(self, request, context):
+    """Sends a greeting
+    """
+    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+    context.set_details('Method not implemented!')
+    raise NotImplementedError('Method not implemented!')
+
+
+def add_GreeterServicer_to_server(servicer, server):
+  rpc_method_handlers = {
+      'SayHello': grpc.unary_unary_rpc_method_handler(
+          servicer.SayHello,
+          request_deserializer=helloworld__pb2.HelloRequest.FromString,
+          response_serializer=helloworld__pb2.HelloReply.SerializeToString,
+      ),
+  }
+  generic_handler = grpc.method_handlers_generic_handler(
+      'helloworld.Greeter', rpc_method_handlers)
+  server.add_generic_rpc_handlers((generic_handler,))
diff --git a/examples/python/multiplex/helloworld_pb2.py b/examples/python/multiplex/helloworld_pb2.py
index 3ce33fbf2bf36f011a2a871ab0d48cc926c60d1d..6665b1f6878f492e24fd1cf85372d9766c3cf9c1 100644
--- a/examples/python/multiplex/helloworld_pb2.py
+++ b/examples/python/multiplex/helloworld_pb2.py
@@ -107,98 +107,123 @@ _sym_db.RegisterMessage(HelloReply)
 
 DESCRIPTOR.has_options = True
 DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\033io.grpc.examples.helloworldB\017HelloWorldProtoP\001\242\002\003HLW'))
-import grpc
-from grpc.beta import implementations as beta_implementations
-from grpc.beta import interfaces as beta_interfaces
-from grpc.framework.common import cardinality
-from grpc.framework.interfaces.face import utilities as face_utilities
+try:
+  # THESE ELEMENTS WILL BE DEPRECATED.
+  # Please use the generated *_pb2_grpc.py files instead.
+  import grpc
+  from grpc.framework.common import cardinality
+  from grpc.framework.interfaces.face import utilities as face_utilities
+  from grpc.beta import implementations as beta_implementations
+  from grpc.beta import interfaces as beta_interfaces
+
+
+  class GreeterStub(object):
+    """The greeting service definition.
+    """
 
+    def __init__(self, channel):
+      """Constructor.
 
-class GreeterStub(object):
-  """The greeting service definition.
-  """
+      Args:
+        channel: A grpc.Channel.
+      """
+      self.SayHello = channel.unary_unary(
+          '/helloworld.Greeter/SayHello',
+          request_serializer=HelloRequest.SerializeToString,
+          response_deserializer=HelloReply.FromString,
+          )
 
-  def __init__(self, channel):
-    """Constructor.
 
-    Args:
-      channel: A grpc.Channel.
+  class GreeterServicer(object):
+    """The greeting service definition.
     """
-    self.SayHello = channel.unary_unary(
-        '/helloworld.Greeter/SayHello',
-        request_serializer=HelloRequest.SerializeToString,
-        response_deserializer=HelloReply.FromString,
-        )
-
-
-class GreeterServicer(object):
-  """The greeting service definition.
-  """
 
-  def SayHello(self, request, context):
-    """Sends a greeting
+    def SayHello(self, request, context):
+      """Sends a greeting
+      """
+      context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+      context.set_details('Method not implemented!')
+      raise NotImplementedError('Method not implemented!')
+
+
+  def add_GreeterServicer_to_server(servicer, server):
+    rpc_method_handlers = {
+        'SayHello': grpc.unary_unary_rpc_method_handler(
+            servicer.SayHello,
+            request_deserializer=HelloRequest.FromString,
+            response_serializer=HelloReply.SerializeToString,
+        ),
+    }
+    generic_handler = grpc.method_handlers_generic_handler(
+        'helloworld.Greeter', rpc_method_handlers)
+    server.add_generic_rpc_handlers((generic_handler,))
+
+
+  class BetaGreeterServicer(object):
+    """The Beta API is deprecated for 0.15.0 and later.
+
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This class was generated
+    only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0."""
+    """The greeting service definition.
     """
-    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
-    context.set_details('Method not implemented!')
-    raise NotImplementedError('Method not implemented!')
-
-
-def add_GreeterServicer_to_server(servicer, server):
-  rpc_method_handlers = {
-      'SayHello': grpc.unary_unary_rpc_method_handler(
-          servicer.SayHello,
-          request_deserializer=HelloRequest.FromString,
-          response_serializer=HelloReply.SerializeToString,
-      ),
-  }
-  generic_handler = grpc.method_handlers_generic_handler(
-      'helloworld.Greeter', rpc_method_handlers)
-  server.add_generic_rpc_handlers((generic_handler,))
-
-
-class BetaGreeterServicer(object):
-  """The greeting service definition.
-  """
-  def SayHello(self, request, context):
-    """Sends a greeting
-    """
-    context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
+    def SayHello(self, request, context):
+      """Sends a greeting
+      """
+      context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
+
 
+  class BetaGreeterStub(object):
+    """The Beta API is deprecated for 0.15.0 and later.
 
-class BetaGreeterStub(object):
-  """The greeting service definition.
-  """
-  def SayHello(self, request, timeout, metadata=None, with_call=False, protocol_options=None):
-    """Sends a greeting
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This class was generated
+    only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0."""
+    """The greeting service definition.
     """
-    raise NotImplementedError()
-  SayHello.future = None
-
-
-def beta_create_Greeter_server(servicer, pool=None, pool_size=None, default_timeout=None, maximum_timeout=None):
-  request_deserializers = {
-    ('helloworld.Greeter', 'SayHello'): HelloRequest.FromString,
-  }
-  response_serializers = {
-    ('helloworld.Greeter', 'SayHello'): HelloReply.SerializeToString,
-  }
-  method_implementations = {
-    ('helloworld.Greeter', 'SayHello'): face_utilities.unary_unary_inline(servicer.SayHello),
-  }
-  server_options = beta_implementations.server_options(request_deserializers=request_deserializers, response_serializers=response_serializers, thread_pool=pool, thread_pool_size=pool_size, default_timeout=default_timeout, maximum_timeout=maximum_timeout)
-  return beta_implementations.server(method_implementations, options=server_options)
-
-
-def beta_create_Greeter_stub(channel, host=None, metadata_transformer=None, pool=None, pool_size=None):
-  request_serializers = {
-    ('helloworld.Greeter', 'SayHello'): HelloRequest.SerializeToString,
-  }
-  response_deserializers = {
-    ('helloworld.Greeter', 'SayHello'): HelloReply.FromString,
-  }
-  cardinalities = {
-    'SayHello': cardinality.Cardinality.UNARY_UNARY,
-  }
-  stub_options = beta_implementations.stub_options(host=host, metadata_transformer=metadata_transformer, request_serializers=request_serializers, response_deserializers=response_deserializers, thread_pool=pool, thread_pool_size=pool_size)
-  return beta_implementations.dynamic_stub(channel, 'helloworld.Greeter', cardinalities, options=stub_options)
+    def SayHello(self, request, timeout, metadata=None, with_call=False, protocol_options=None):
+      """Sends a greeting
+      """
+      raise NotImplementedError()
+    SayHello.future = None
+
+
+  def beta_create_Greeter_server(servicer, pool=None, pool_size=None, default_timeout=None, maximum_timeout=None):
+    """The Beta API is deprecated for 0.15.0 and later.
+
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This function was
+    generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"""
+    request_deserializers = {
+      ('helloworld.Greeter', 'SayHello'): HelloRequest.FromString,
+    }
+    response_serializers = {
+      ('helloworld.Greeter', 'SayHello'): HelloReply.SerializeToString,
+    }
+    method_implementations = {
+      ('helloworld.Greeter', 'SayHello'): face_utilities.unary_unary_inline(servicer.SayHello),
+    }
+    server_options = beta_implementations.server_options(request_deserializers=request_deserializers, response_serializers=response_serializers, thread_pool=pool, thread_pool_size=pool_size, default_timeout=default_timeout, maximum_timeout=maximum_timeout)
+    return beta_implementations.server(method_implementations, options=server_options)
+
+
+  def beta_create_Greeter_stub(channel, host=None, metadata_transformer=None, pool=None, pool_size=None):
+    """The Beta API is deprecated for 0.15.0 and later.
+
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This function was
+    generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"""
+    request_serializers = {
+      ('helloworld.Greeter', 'SayHello'): HelloRequest.SerializeToString,
+    }
+    response_deserializers = {
+      ('helloworld.Greeter', 'SayHello'): HelloReply.FromString,
+    }
+    cardinalities = {
+      'SayHello': cardinality.Cardinality.UNARY_UNARY,
+    }
+    stub_options = beta_implementations.stub_options(host=host, metadata_transformer=metadata_transformer, request_serializers=request_serializers, response_deserializers=response_deserializers, thread_pool=pool, thread_pool_size=pool_size)
+    return beta_implementations.dynamic_stub(channel, 'helloworld.Greeter', cardinalities, options=stub_options)
+except ImportError:
+  pass
 # @@protoc_insertion_point(module_scope)
diff --git a/examples/python/multiplex/helloworld_pb2_grpc.py b/examples/python/multiplex/helloworld_pb2_grpc.py
new file mode 100644
index 0000000000000000000000000000000000000000..682dc36cd89fb3d1c5b7b1da58f90e3c69a1d977
--- /dev/null
+++ b/examples/python/multiplex/helloworld_pb2_grpc.py
@@ -0,0 +1,47 @@
+import grpc
+from grpc.framework.common import cardinality
+from grpc.framework.interfaces.face import utilities as face_utilities
+
+import helloworld_pb2 as helloworld__pb2
+
+
+class GreeterStub(object):
+  """The greeting service definition.
+  """
+
+  def __init__(self, channel):
+    """Constructor.
+
+    Args:
+      channel: A grpc.Channel.
+    """
+    self.SayHello = channel.unary_unary(
+        '/helloworld.Greeter/SayHello',
+        request_serializer=helloworld__pb2.HelloRequest.SerializeToString,
+        response_deserializer=helloworld__pb2.HelloReply.FromString,
+        )
+
+
+class GreeterServicer(object):
+  """The greeting service definition.
+  """
+
+  def SayHello(self, request, context):
+    """Sends a greeting
+    """
+    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+    context.set_details('Method not implemented!')
+    raise NotImplementedError('Method not implemented!')
+
+
+def add_GreeterServicer_to_server(servicer, server):
+  rpc_method_handlers = {
+      'SayHello': grpc.unary_unary_rpc_method_handler(
+          servicer.SayHello,
+          request_deserializer=helloworld__pb2.HelloRequest.FromString,
+          response_serializer=helloworld__pb2.HelloReply.SerializeToString,
+      ),
+  }
+  generic_handler = grpc.method_handlers_generic_handler(
+      'helloworld.Greeter', rpc_method_handlers)
+  server.add_generic_rpc_handlers((generic_handler,))
diff --git a/examples/python/multiplex/multiplex_client.py b/examples/python/multiplex/multiplex_client.py
index 2e8162926b777f3f50b53dd76608e8d404182cde..b2d2021e029a0ec4179659408451fa5c7362fd87 100644
--- a/examples/python/multiplex/multiplex_client.py
+++ b/examples/python/multiplex/multiplex_client.py
@@ -37,7 +37,9 @@ import time
 import grpc
 
 import helloworld_pb2
+import helloworld_pb2_grpc
 import route_guide_pb2
+import route_guide_pb2_grpc
 import route_guide_resources
 
 
@@ -120,8 +122,8 @@ def guide_route_chat(route_guide_stub):
 
 def run():
   channel = grpc.insecure_channel('localhost:50051')
-  greeter_stub = helloworld_pb2.GreeterStub(channel)
-  route_guide_stub = route_guide_pb2.RouteGuideStub(channel)
+  greeter_stub = helloworld_pb2_grpc.GreeterStub(channel)
+  route_guide_stub = route_guide_pb2_grpc.RouteGuideStub(channel)
   greeter_response = greeter_stub.SayHello(
       helloworld_pb2.HelloRequest(name='you'))
   print("Greeter client received: " + greeter_response.message)
diff --git a/examples/python/multiplex/multiplex_server.py b/examples/python/multiplex/multiplex_server.py
index 32a4ee4a4977fb948d684c270268a5ebbd4d49a0..b8b32e7bf8d1edccc8110797e9136ee14a6c725b 100644
--- a/examples/python/multiplex/multiplex_server.py
+++ b/examples/python/multiplex/multiplex_server.py
@@ -36,7 +36,9 @@ import math
 import grpc
 
 import helloworld_pb2
+import helloworld_pb2_grpc
 import route_guide_pb2
+import route_guide_pb2_grpc
 import route_guide_resources
 
 _ONE_DAY_IN_SECONDS = 60 * 60 * 24
@@ -70,13 +72,13 @@ def _get_distance(start, end):
   return R * c;
 
 
-class _GreeterServicer(helloworld_pb2.GreeterServicer):
+class _GreeterServicer(helloworld_pb2_grpc.GreeterServicer):
 
   def SayHello(self, request, context):
     return helloworld_pb2.HelloReply(message='Hello, {}!'.format(request.name))
 
 
-class _RouteGuideServicer(route_guide_pb2.RouteGuideServicer):
+class _RouteGuideServicer(route_guide_pb2_grpc.RouteGuideServicer):
   """Provides methods that implement functionality of route guide server."""
 
   def __init__(self):
@@ -133,8 +135,8 @@ class _RouteGuideServicer(route_guide_pb2.RouteGuideServicer):
 
 def serve():
   server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
-  helloworld_pb2.add_GreeterServicer_to_server(_GreeterServicer(), server)
-  route_guide_pb2.add_RouteGuideServicer_to_server(
+  helloworld_pb2_grpc.add_GreeterServicer_to_server(_GreeterServicer(), server)
+  route_guide_pb2_grpc.add_RouteGuideServicer_to_server(
       _RouteGuideServicer(), server)
   server.add_insecure_port('[::]:50051')
   server.start()
diff --git a/examples/python/multiplex/route_guide_pb2.py b/examples/python/multiplex/route_guide_pb2.py
index 924e186e06e4ca1094a3c648bf3748f79393464c..e6775eb8140f9eae5c7d466b1d1a88248373a4ab 100644
--- a/examples/python/multiplex/route_guide_pb2.py
+++ b/examples/python/multiplex/route_guide_pb2.py
@@ -277,240 +277,265 @@ _sym_db.RegisterMessage(RouteSummary)
 
 DESCRIPTOR.has_options = True
 DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\033io.grpc.examples.routeguideB\017RouteGuideProtoP\001\242\002\003RTG'))
-import grpc
-from grpc.beta import implementations as beta_implementations
-from grpc.beta import interfaces as beta_interfaces
-from grpc.framework.common import cardinality
-from grpc.framework.interfaces.face import utilities as face_utilities
-
-
-class RouteGuideStub(object):
-  """Interface exported by the server.
-  """
-
-  def __init__(self, channel):
-    """Constructor.
-
-    Args:
-      channel: A grpc.Channel.
+try:
+  # THESE ELEMENTS WILL BE DEPRECATED.
+  # Please use the generated *_pb2_grpc.py files instead.
+  import grpc
+  from grpc.framework.common import cardinality
+  from grpc.framework.interfaces.face import utilities as face_utilities
+  from grpc.beta import implementations as beta_implementations
+  from grpc.beta import interfaces as beta_interfaces
+
+
+  class RouteGuideStub(object):
+    """Interface exported by the server.
     """
-    self.GetFeature = channel.unary_unary(
-        '/routeguide.RouteGuide/GetFeature',
-        request_serializer=Point.SerializeToString,
-        response_deserializer=Feature.FromString,
-        )
-    self.ListFeatures = channel.unary_stream(
-        '/routeguide.RouteGuide/ListFeatures',
-        request_serializer=Rectangle.SerializeToString,
-        response_deserializer=Feature.FromString,
-        )
-    self.RecordRoute = channel.stream_unary(
-        '/routeguide.RouteGuide/RecordRoute',
-        request_serializer=Point.SerializeToString,
-        response_deserializer=RouteSummary.FromString,
-        )
-    self.RouteChat = channel.stream_stream(
-        '/routeguide.RouteGuide/RouteChat',
-        request_serializer=RouteNote.SerializeToString,
-        response_deserializer=RouteNote.FromString,
-        )
-
-
-class RouteGuideServicer(object):
-  """Interface exported by the server.
-  """
-
-  def GetFeature(self, request, context):
-    """A simple RPC.
-
-    Obtains the feature at a given position.
-
-    A feature with an empty name is returned if there's no feature at the given
-    position.
-    """
-    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
-    context.set_details('Method not implemented!')
-    raise NotImplementedError('Method not implemented!')
-
-  def ListFeatures(self, request, context):
-    """A server-to-client streaming RPC.
 
-    Obtains the Features available within the given Rectangle.  Results are
-    streamed rather than returned at once (e.g. in a response message with a
-    repeated field), as the rectangle may cover a large area and contain a
-    huge number of features.
+    def __init__(self, channel):
+      """Constructor.
+
+      Args:
+        channel: A grpc.Channel.
+      """
+      self.GetFeature = channel.unary_unary(
+          '/routeguide.RouteGuide/GetFeature',
+          request_serializer=Point.SerializeToString,
+          response_deserializer=Feature.FromString,
+          )
+      self.ListFeatures = channel.unary_stream(
+          '/routeguide.RouteGuide/ListFeatures',
+          request_serializer=Rectangle.SerializeToString,
+          response_deserializer=Feature.FromString,
+          )
+      self.RecordRoute = channel.stream_unary(
+          '/routeguide.RouteGuide/RecordRoute',
+          request_serializer=Point.SerializeToString,
+          response_deserializer=RouteSummary.FromString,
+          )
+      self.RouteChat = channel.stream_stream(
+          '/routeguide.RouteGuide/RouteChat',
+          request_serializer=RouteNote.SerializeToString,
+          response_deserializer=RouteNote.FromString,
+          )
+
+
+  class RouteGuideServicer(object):
+    """Interface exported by the server.
     """
-    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
-    context.set_details('Method not implemented!')
-    raise NotImplementedError('Method not implemented!')
-
-  def RecordRoute(self, request_iterator, context):
-    """A client-to-server streaming RPC.
 
-    Accepts a stream of Points on a route being traversed, returning a
-    RouteSummary when traversal is completed.
+    def GetFeature(self, request, context):
+      """A simple RPC.
+
+      Obtains the feature at a given position.
+
+      A feature with an empty name is returned if there's no feature at the given
+      position.
+      """
+      context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+      context.set_details('Method not implemented!')
+      raise NotImplementedError('Method not implemented!')
+
+    def ListFeatures(self, request, context):
+      """A server-to-client streaming RPC.
+
+      Obtains the Features available within the given Rectangle.  Results are
+      streamed rather than returned at once (e.g. in a response message with a
+      repeated field), as the rectangle may cover a large area and contain a
+      huge number of features.
+      """
+      context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+      context.set_details('Method not implemented!')
+      raise NotImplementedError('Method not implemented!')
+
+    def RecordRoute(self, request_iterator, context):
+      """A client-to-server streaming RPC.
+
+      Accepts a stream of Points on a route being traversed, returning a
+      RouteSummary when traversal is completed.
+      """
+      context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+      context.set_details('Method not implemented!')
+      raise NotImplementedError('Method not implemented!')
+
+    def RouteChat(self, request_iterator, context):
+      """A Bidirectional streaming RPC.
+
+      Accepts a stream of RouteNotes sent while a route is being traversed,
+      while receiving other RouteNotes (e.g. from other users).
+      """
+      context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+      context.set_details('Method not implemented!')
+      raise NotImplementedError('Method not implemented!')
+
+
+  def add_RouteGuideServicer_to_server(servicer, server):
+    rpc_method_handlers = {
+        'GetFeature': grpc.unary_unary_rpc_method_handler(
+            servicer.GetFeature,
+            request_deserializer=Point.FromString,
+            response_serializer=Feature.SerializeToString,
+        ),
+        'ListFeatures': grpc.unary_stream_rpc_method_handler(
+            servicer.ListFeatures,
+            request_deserializer=Rectangle.FromString,
+            response_serializer=Feature.SerializeToString,
+        ),
+        'RecordRoute': grpc.stream_unary_rpc_method_handler(
+            servicer.RecordRoute,
+            request_deserializer=Point.FromString,
+            response_serializer=RouteSummary.SerializeToString,
+        ),
+        'RouteChat': grpc.stream_stream_rpc_method_handler(
+            servicer.RouteChat,
+            request_deserializer=RouteNote.FromString,
+            response_serializer=RouteNote.SerializeToString,
+        ),
+    }
+    generic_handler = grpc.method_handlers_generic_handler(
+        'routeguide.RouteGuide', rpc_method_handlers)
+    server.add_generic_rpc_handlers((generic_handler,))
+
+
+  class BetaRouteGuideServicer(object):
+    """The Beta API is deprecated for 0.15.0 and later.
+
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This class was generated
+    only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0."""
+    """Interface exported by the server.
     """
-    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
-    context.set_details('Method not implemented!')
-    raise NotImplementedError('Method not implemented!')
-
-  def RouteChat(self, request_iterator, context):
-    """A Bidirectional streaming RPC.
-
-    Accepts a stream of RouteNotes sent while a route is being traversed,
-    while receiving other RouteNotes (e.g. from other users).
-    """
-    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
-    context.set_details('Method not implemented!')
-    raise NotImplementedError('Method not implemented!')
-
-
-def add_RouteGuideServicer_to_server(servicer, server):
-  rpc_method_handlers = {
-      'GetFeature': grpc.unary_unary_rpc_method_handler(
-          servicer.GetFeature,
-          request_deserializer=Point.FromString,
-          response_serializer=Feature.SerializeToString,
-      ),
-      'ListFeatures': grpc.unary_stream_rpc_method_handler(
-          servicer.ListFeatures,
-          request_deserializer=Rectangle.FromString,
-          response_serializer=Feature.SerializeToString,
-      ),
-      'RecordRoute': grpc.stream_unary_rpc_method_handler(
-          servicer.RecordRoute,
-          request_deserializer=Point.FromString,
-          response_serializer=RouteSummary.SerializeToString,
-      ),
-      'RouteChat': grpc.stream_stream_rpc_method_handler(
-          servicer.RouteChat,
-          request_deserializer=RouteNote.FromString,
-          response_serializer=RouteNote.SerializeToString,
-      ),
-  }
-  generic_handler = grpc.method_handlers_generic_handler(
-      'routeguide.RouteGuide', rpc_method_handlers)
-  server.add_generic_rpc_handlers((generic_handler,))
-
-
-class BetaRouteGuideServicer(object):
-  """Interface exported by the server.
-  """
-  def GetFeature(self, request, context):
-    """A simple RPC.
-
-    Obtains the feature at a given position.
-
-    A feature with an empty name is returned if there's no feature at the given
-    position.
-    """
-    context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
-  def ListFeatures(self, request, context):
-    """A server-to-client streaming RPC.
-
-    Obtains the Features available within the given Rectangle.  Results are
-    streamed rather than returned at once (e.g. in a response message with a
-    repeated field), as the rectangle may cover a large area and contain a
-    huge number of features.
-    """
-    context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
-  def RecordRoute(self, request_iterator, context):
-    """A client-to-server streaming RPC.
-
-    Accepts a stream of Points on a route being traversed, returning a
-    RouteSummary when traversal is completed.
-    """
-    context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
-  def RouteChat(self, request_iterator, context):
-    """A Bidirectional streaming RPC.
-
-    Accepts a stream of RouteNotes sent while a route is being traversed,
-    while receiving other RouteNotes (e.g. from other users).
-    """
-    context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
-
-
-class BetaRouteGuideStub(object):
-  """Interface exported by the server.
-  """
-  def GetFeature(self, request, timeout, metadata=None, with_call=False, protocol_options=None):
-    """A simple RPC.
-
-    Obtains the feature at a given position.
-
-    A feature with an empty name is returned if there's no feature at the given
-    position.
-    """
-    raise NotImplementedError()
-  GetFeature.future = None
-  def ListFeatures(self, request, timeout, metadata=None, with_call=False, protocol_options=None):
-    """A server-to-client streaming RPC.
-
-    Obtains the Features available within the given Rectangle.  Results are
-    streamed rather than returned at once (e.g. in a response message with a
-    repeated field), as the rectangle may cover a large area and contain a
-    huge number of features.
-    """
-    raise NotImplementedError()
-  def RecordRoute(self, request_iterator, timeout, metadata=None, with_call=False, protocol_options=None):
-    """A client-to-server streaming RPC.
-
-    Accepts a stream of Points on a route being traversed, returning a
-    RouteSummary when traversal is completed.
-    """
-    raise NotImplementedError()
-  RecordRoute.future = None
-  def RouteChat(self, request_iterator, timeout, metadata=None, with_call=False, protocol_options=None):
-    """A Bidirectional streaming RPC.
-
-    Accepts a stream of RouteNotes sent while a route is being traversed,
-    while receiving other RouteNotes (e.g. from other users).
+    def GetFeature(self, request, context):
+      """A simple RPC.
+
+      Obtains the feature at a given position.
+
+      A feature with an empty name is returned if there's no feature at the given
+      position.
+      """
+      context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
+    def ListFeatures(self, request, context):
+      """A server-to-client streaming RPC.
+
+      Obtains the Features available within the given Rectangle.  Results are
+      streamed rather than returned at once (e.g. in a response message with a
+      repeated field), as the rectangle may cover a large area and contain a
+      huge number of features.
+      """
+      context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
+    def RecordRoute(self, request_iterator, context):
+      """A client-to-server streaming RPC.
+
+      Accepts a stream of Points on a route being traversed, returning a
+      RouteSummary when traversal is completed.
+      """
+      context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
+    def RouteChat(self, request_iterator, context):
+      """A Bidirectional streaming RPC.
+
+      Accepts a stream of RouteNotes sent while a route is being traversed,
+      while receiving other RouteNotes (e.g. from other users).
+      """
+      context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
+
+
+  class BetaRouteGuideStub(object):
+    """The Beta API is deprecated for 0.15.0 and later.
+
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This class was generated
+    only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0."""
+    """Interface exported by the server.
     """
-    raise NotImplementedError()
-
-
-def beta_create_RouteGuide_server(servicer, pool=None, pool_size=None, default_timeout=None, maximum_timeout=None):
-  request_deserializers = {
-    ('routeguide.RouteGuide', 'GetFeature'): Point.FromString,
-    ('routeguide.RouteGuide', 'ListFeatures'): Rectangle.FromString,
-    ('routeguide.RouteGuide', 'RecordRoute'): Point.FromString,
-    ('routeguide.RouteGuide', 'RouteChat'): RouteNote.FromString,
-  }
-  response_serializers = {
-    ('routeguide.RouteGuide', 'GetFeature'): Feature.SerializeToString,
-    ('routeguide.RouteGuide', 'ListFeatures'): Feature.SerializeToString,
-    ('routeguide.RouteGuide', 'RecordRoute'): RouteSummary.SerializeToString,
-    ('routeguide.RouteGuide', 'RouteChat'): RouteNote.SerializeToString,
-  }
-  method_implementations = {
-    ('routeguide.RouteGuide', 'GetFeature'): face_utilities.unary_unary_inline(servicer.GetFeature),
-    ('routeguide.RouteGuide', 'ListFeatures'): face_utilities.unary_stream_inline(servicer.ListFeatures),
-    ('routeguide.RouteGuide', 'RecordRoute'): face_utilities.stream_unary_inline(servicer.RecordRoute),
-    ('routeguide.RouteGuide', 'RouteChat'): face_utilities.stream_stream_inline(servicer.RouteChat),
-  }
-  server_options = beta_implementations.server_options(request_deserializers=request_deserializers, response_serializers=response_serializers, thread_pool=pool, thread_pool_size=pool_size, default_timeout=default_timeout, maximum_timeout=maximum_timeout)
-  return beta_implementations.server(method_implementations, options=server_options)
-
-
-def beta_create_RouteGuide_stub(channel, host=None, metadata_transformer=None, pool=None, pool_size=None):
-  request_serializers = {
-    ('routeguide.RouteGuide', 'GetFeature'): Point.SerializeToString,
-    ('routeguide.RouteGuide', 'ListFeatures'): Rectangle.SerializeToString,
-    ('routeguide.RouteGuide', 'RecordRoute'): Point.SerializeToString,
-    ('routeguide.RouteGuide', 'RouteChat'): RouteNote.SerializeToString,
-  }
-  response_deserializers = {
-    ('routeguide.RouteGuide', 'GetFeature'): Feature.FromString,
-    ('routeguide.RouteGuide', 'ListFeatures'): Feature.FromString,
-    ('routeguide.RouteGuide', 'RecordRoute'): RouteSummary.FromString,
-    ('routeguide.RouteGuide', 'RouteChat'): RouteNote.FromString,
-  }
-  cardinalities = {
-    'GetFeature': cardinality.Cardinality.UNARY_UNARY,
-    'ListFeatures': cardinality.Cardinality.UNARY_STREAM,
-    'RecordRoute': cardinality.Cardinality.STREAM_UNARY,
-    'RouteChat': cardinality.Cardinality.STREAM_STREAM,
-  }
-  stub_options = beta_implementations.stub_options(host=host, metadata_transformer=metadata_transformer, request_serializers=request_serializers, response_deserializers=response_deserializers, thread_pool=pool, thread_pool_size=pool_size)
-  return beta_implementations.dynamic_stub(channel, 'routeguide.RouteGuide', cardinalities, options=stub_options)
+    def GetFeature(self, request, timeout, metadata=None, with_call=False, protocol_options=None):
+      """A simple RPC.
+
+      Obtains the feature at a given position.
+
+      A feature with an empty name is returned if there's no feature at the given
+      position.
+      """
+      raise NotImplementedError()
+    GetFeature.future = None
+    def ListFeatures(self, request, timeout, metadata=None, with_call=False, protocol_options=None):
+      """A server-to-client streaming RPC.
+
+      Obtains the Features available within the given Rectangle.  Results are
+      streamed rather than returned at once (e.g. in a response message with a
+      repeated field), as the rectangle may cover a large area and contain a
+      huge number of features.
+      """
+      raise NotImplementedError()
+    def RecordRoute(self, request_iterator, timeout, metadata=None, with_call=False, protocol_options=None):
+      """A client-to-server streaming RPC.
+
+      Accepts a stream of Points on a route being traversed, returning a
+      RouteSummary when traversal is completed.
+      """
+      raise NotImplementedError()
+    RecordRoute.future = None
+    def RouteChat(self, request_iterator, timeout, metadata=None, with_call=False, protocol_options=None):
+      """A Bidirectional streaming RPC.
+
+      Accepts a stream of RouteNotes sent while a route is being traversed,
+      while receiving other RouteNotes (e.g. from other users).
+      """
+      raise NotImplementedError()
+
+
+  def beta_create_RouteGuide_server(servicer, pool=None, pool_size=None, default_timeout=None, maximum_timeout=None):
+    """The Beta API is deprecated for 0.15.0 and later.
+
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This function was
+    generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"""
+    request_deserializers = {
+      ('routeguide.RouteGuide', 'GetFeature'): Point.FromString,
+      ('routeguide.RouteGuide', 'ListFeatures'): Rectangle.FromString,
+      ('routeguide.RouteGuide', 'RecordRoute'): Point.FromString,
+      ('routeguide.RouteGuide', 'RouteChat'): RouteNote.FromString,
+    }
+    response_serializers = {
+      ('routeguide.RouteGuide', 'GetFeature'): Feature.SerializeToString,
+      ('routeguide.RouteGuide', 'ListFeatures'): Feature.SerializeToString,
+      ('routeguide.RouteGuide', 'RecordRoute'): RouteSummary.SerializeToString,
+      ('routeguide.RouteGuide', 'RouteChat'): RouteNote.SerializeToString,
+    }
+    method_implementations = {
+      ('routeguide.RouteGuide', 'GetFeature'): face_utilities.unary_unary_inline(servicer.GetFeature),
+      ('routeguide.RouteGuide', 'ListFeatures'): face_utilities.unary_stream_inline(servicer.ListFeatures),
+      ('routeguide.RouteGuide', 'RecordRoute'): face_utilities.stream_unary_inline(servicer.RecordRoute),
+      ('routeguide.RouteGuide', 'RouteChat'): face_utilities.stream_stream_inline(servicer.RouteChat),
+    }
+    server_options = beta_implementations.server_options(request_deserializers=request_deserializers, response_serializers=response_serializers, thread_pool=pool, thread_pool_size=pool_size, default_timeout=default_timeout, maximum_timeout=maximum_timeout)
+    return beta_implementations.server(method_implementations, options=server_options)
+
+
+  def beta_create_RouteGuide_stub(channel, host=None, metadata_transformer=None, pool=None, pool_size=None):
+    """The Beta API is deprecated for 0.15.0 and later.
+
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This function was
+    generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"""
+    request_serializers = {
+      ('routeguide.RouteGuide', 'GetFeature'): Point.SerializeToString,
+      ('routeguide.RouteGuide', 'ListFeatures'): Rectangle.SerializeToString,
+      ('routeguide.RouteGuide', 'RecordRoute'): Point.SerializeToString,
+      ('routeguide.RouteGuide', 'RouteChat'): RouteNote.SerializeToString,
+    }
+    response_deserializers = {
+      ('routeguide.RouteGuide', 'GetFeature'): Feature.FromString,
+      ('routeguide.RouteGuide', 'ListFeatures'): Feature.FromString,
+      ('routeguide.RouteGuide', 'RecordRoute'): RouteSummary.FromString,
+      ('routeguide.RouteGuide', 'RouteChat'): RouteNote.FromString,
+    }
+    cardinalities = {
+      'GetFeature': cardinality.Cardinality.UNARY_UNARY,
+      'ListFeatures': cardinality.Cardinality.UNARY_STREAM,
+      'RecordRoute': cardinality.Cardinality.STREAM_UNARY,
+      'RouteChat': cardinality.Cardinality.STREAM_STREAM,
+    }
+    stub_options = beta_implementations.stub_options(host=host, metadata_transformer=metadata_transformer, request_serializers=request_serializers, response_deserializers=response_deserializers, thread_pool=pool, thread_pool_size=pool_size)
+    return beta_implementations.dynamic_stub(channel, 'routeguide.RouteGuide', cardinalities, options=stub_options)
+except ImportError:
+  pass
 # @@protoc_insertion_point(module_scope)
diff --git a/examples/python/multiplex/route_guide_pb2_grpc.py b/examples/python/multiplex/route_guide_pb2_grpc.py
new file mode 100644
index 0000000000000000000000000000000000000000..27b24c747dbd1ec383e70b71ca6230502822cb63
--- /dev/null
+++ b/examples/python/multiplex/route_guide_pb2_grpc.py
@@ -0,0 +1,114 @@
+import grpc
+from grpc.framework.common import cardinality
+from grpc.framework.interfaces.face import utilities as face_utilities
+
+import route_guide_pb2 as route__guide__pb2
+
+
+class RouteGuideStub(object):
+  """Interface exported by the server.
+  """
+
+  def __init__(self, channel):
+    """Constructor.
+
+    Args:
+      channel: A grpc.Channel.
+    """
+    self.GetFeature = channel.unary_unary(
+        '/routeguide.RouteGuide/GetFeature',
+        request_serializer=route__guide__pb2.Point.SerializeToString,
+        response_deserializer=route__guide__pb2.Feature.FromString,
+        )
+    self.ListFeatures = channel.unary_stream(
+        '/routeguide.RouteGuide/ListFeatures',
+        request_serializer=route__guide__pb2.Rectangle.SerializeToString,
+        response_deserializer=route__guide__pb2.Feature.FromString,
+        )
+    self.RecordRoute = channel.stream_unary(
+        '/routeguide.RouteGuide/RecordRoute',
+        request_serializer=route__guide__pb2.Point.SerializeToString,
+        response_deserializer=route__guide__pb2.RouteSummary.FromString,
+        )
+    self.RouteChat = channel.stream_stream(
+        '/routeguide.RouteGuide/RouteChat',
+        request_serializer=route__guide__pb2.RouteNote.SerializeToString,
+        response_deserializer=route__guide__pb2.RouteNote.FromString,
+        )
+
+
+class RouteGuideServicer(object):
+  """Interface exported by the server.
+  """
+
+  def GetFeature(self, request, context):
+    """A simple RPC.
+
+    Obtains the feature at a given position.
+
+    A feature with an empty name is returned if there's no feature at the given
+    position.
+    """
+    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+    context.set_details('Method not implemented!')
+    raise NotImplementedError('Method not implemented!')
+
+  def ListFeatures(self, request, context):
+    """A server-to-client streaming RPC.
+
+    Obtains the Features available within the given Rectangle.  Results are
+    streamed rather than returned at once (e.g. in a response message with a
+    repeated field), as the rectangle may cover a large area and contain a
+    huge number of features.
+    """
+    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+    context.set_details('Method not implemented!')
+    raise NotImplementedError('Method not implemented!')
+
+  def RecordRoute(self, request_iterator, context):
+    """A client-to-server streaming RPC.
+
+    Accepts a stream of Points on a route being traversed, returning a
+    RouteSummary when traversal is completed.
+    """
+    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+    context.set_details('Method not implemented!')
+    raise NotImplementedError('Method not implemented!')
+
+  def RouteChat(self, request_iterator, context):
+    """A Bidirectional streaming RPC.
+
+    Accepts a stream of RouteNotes sent while a route is being traversed,
+    while receiving other RouteNotes (e.g. from other users).
+    """
+    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+    context.set_details('Method not implemented!')
+    raise NotImplementedError('Method not implemented!')
+
+
+def add_RouteGuideServicer_to_server(servicer, server):
+  rpc_method_handlers = {
+      'GetFeature': grpc.unary_unary_rpc_method_handler(
+          servicer.GetFeature,
+          request_deserializer=route__guide__pb2.Point.FromString,
+          response_serializer=route__guide__pb2.Feature.SerializeToString,
+      ),
+      'ListFeatures': grpc.unary_stream_rpc_method_handler(
+          servicer.ListFeatures,
+          request_deserializer=route__guide__pb2.Rectangle.FromString,
+          response_serializer=route__guide__pb2.Feature.SerializeToString,
+      ),
+      'RecordRoute': grpc.stream_unary_rpc_method_handler(
+          servicer.RecordRoute,
+          request_deserializer=route__guide__pb2.Point.FromString,
+          response_serializer=route__guide__pb2.RouteSummary.SerializeToString,
+      ),
+      'RouteChat': grpc.stream_stream_rpc_method_handler(
+          servicer.RouteChat,
+          request_deserializer=route__guide__pb2.RouteNote.FromString,
+          response_serializer=route__guide__pb2.RouteNote.SerializeToString,
+      ),
+  }
+  generic_handler = grpc.method_handlers_generic_handler(
+      'routeguide.RouteGuide', rpc_method_handlers)
+  server.add_generic_rpc_handlers((generic_handler,))
diff --git a/examples/python/multiplex/run_codegen.py b/examples/python/multiplex/run_codegen.py
old mode 100755
new mode 100644
index 7922a0f5c7f5109671b2b0a96d4e9cdc00d29401..89ac9c8fae50a75560f70dfa6258e1c241c9c3be
--- a/examples/python/multiplex/run_codegen.py
+++ b/examples/python/multiplex/run_codegen.py
@@ -29,7 +29,7 @@
 
 """Generates protocol messages and gRPC stubs."""
 
-from grpc.tools import protoc
+from grpc_tools import protoc
 
 protoc.main(
     (
diff --git a/examples/python/route_guide/route_guide_client.py b/examples/python/route_guide/route_guide_client.py
index 8a80ed892dee70b24869ada8bec3e3220093d408..d2955231c3583e8b55439e33f13952bc4b294519 100644
--- a/examples/python/route_guide/route_guide_client.py
+++ b/examples/python/route_guide/route_guide_client.py
@@ -37,6 +37,7 @@ import time
 import grpc
 
 import route_guide_pb2
+import route_guide_pb2_grpc
 import route_guide_resources
 
 
@@ -116,7 +117,7 @@ def guide_route_chat(stub):
 
 def run():
   channel = grpc.insecure_channel('localhost:50051')
-  stub = route_guide_pb2.RouteGuideStub(channel)
+  stub = route_guide_pb2_grpc.RouteGuideStub(channel)
   print("-------------- GetFeature --------------")
   guide_get_feature(stub)
   print("-------------- ListFeatures --------------")
diff --git a/examples/python/route_guide/route_guide_pb2.py b/examples/python/route_guide/route_guide_pb2.py
index 924e186e06e4ca1094a3c648bf3748f79393464c..e6775eb8140f9eae5c7d466b1d1a88248373a4ab 100644
--- a/examples/python/route_guide/route_guide_pb2.py
+++ b/examples/python/route_guide/route_guide_pb2.py
@@ -277,240 +277,265 @@ _sym_db.RegisterMessage(RouteSummary)
 
 DESCRIPTOR.has_options = True
 DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\033io.grpc.examples.routeguideB\017RouteGuideProtoP\001\242\002\003RTG'))
-import grpc
-from grpc.beta import implementations as beta_implementations
-from grpc.beta import interfaces as beta_interfaces
-from grpc.framework.common import cardinality
-from grpc.framework.interfaces.face import utilities as face_utilities
-
-
-class RouteGuideStub(object):
-  """Interface exported by the server.
-  """
-
-  def __init__(self, channel):
-    """Constructor.
-
-    Args:
-      channel: A grpc.Channel.
+try:
+  # THESE ELEMENTS WILL BE DEPRECATED.
+  # Please use the generated *_pb2_grpc.py files instead.
+  import grpc
+  from grpc.framework.common import cardinality
+  from grpc.framework.interfaces.face import utilities as face_utilities
+  from grpc.beta import implementations as beta_implementations
+  from grpc.beta import interfaces as beta_interfaces
+
+
+  class RouteGuideStub(object):
+    """Interface exported by the server.
     """
-    self.GetFeature = channel.unary_unary(
-        '/routeguide.RouteGuide/GetFeature',
-        request_serializer=Point.SerializeToString,
-        response_deserializer=Feature.FromString,
-        )
-    self.ListFeatures = channel.unary_stream(
-        '/routeguide.RouteGuide/ListFeatures',
-        request_serializer=Rectangle.SerializeToString,
-        response_deserializer=Feature.FromString,
-        )
-    self.RecordRoute = channel.stream_unary(
-        '/routeguide.RouteGuide/RecordRoute',
-        request_serializer=Point.SerializeToString,
-        response_deserializer=RouteSummary.FromString,
-        )
-    self.RouteChat = channel.stream_stream(
-        '/routeguide.RouteGuide/RouteChat',
-        request_serializer=RouteNote.SerializeToString,
-        response_deserializer=RouteNote.FromString,
-        )
-
-
-class RouteGuideServicer(object):
-  """Interface exported by the server.
-  """
-
-  def GetFeature(self, request, context):
-    """A simple RPC.
-
-    Obtains the feature at a given position.
-
-    A feature with an empty name is returned if there's no feature at the given
-    position.
-    """
-    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
-    context.set_details('Method not implemented!')
-    raise NotImplementedError('Method not implemented!')
-
-  def ListFeatures(self, request, context):
-    """A server-to-client streaming RPC.
 
-    Obtains the Features available within the given Rectangle.  Results are
-    streamed rather than returned at once (e.g. in a response message with a
-    repeated field), as the rectangle may cover a large area and contain a
-    huge number of features.
+    def __init__(self, channel):
+      """Constructor.
+
+      Args:
+        channel: A grpc.Channel.
+      """
+      self.GetFeature = channel.unary_unary(
+          '/routeguide.RouteGuide/GetFeature',
+          request_serializer=Point.SerializeToString,
+          response_deserializer=Feature.FromString,
+          )
+      self.ListFeatures = channel.unary_stream(
+          '/routeguide.RouteGuide/ListFeatures',
+          request_serializer=Rectangle.SerializeToString,
+          response_deserializer=Feature.FromString,
+          )
+      self.RecordRoute = channel.stream_unary(
+          '/routeguide.RouteGuide/RecordRoute',
+          request_serializer=Point.SerializeToString,
+          response_deserializer=RouteSummary.FromString,
+          )
+      self.RouteChat = channel.stream_stream(
+          '/routeguide.RouteGuide/RouteChat',
+          request_serializer=RouteNote.SerializeToString,
+          response_deserializer=RouteNote.FromString,
+          )
+
+
+  class RouteGuideServicer(object):
+    """Interface exported by the server.
     """
-    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
-    context.set_details('Method not implemented!')
-    raise NotImplementedError('Method not implemented!')
-
-  def RecordRoute(self, request_iterator, context):
-    """A client-to-server streaming RPC.
 
-    Accepts a stream of Points on a route being traversed, returning a
-    RouteSummary when traversal is completed.
+    def GetFeature(self, request, context):
+      """A simple RPC.
+
+      Obtains the feature at a given position.
+
+      A feature with an empty name is returned if there's no feature at the given
+      position.
+      """
+      context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+      context.set_details('Method not implemented!')
+      raise NotImplementedError('Method not implemented!')
+
+    def ListFeatures(self, request, context):
+      """A server-to-client streaming RPC.
+
+      Obtains the Features available within the given Rectangle.  Results are
+      streamed rather than returned at once (e.g. in a response message with a
+      repeated field), as the rectangle may cover a large area and contain a
+      huge number of features.
+      """
+      context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+      context.set_details('Method not implemented!')
+      raise NotImplementedError('Method not implemented!')
+
+    def RecordRoute(self, request_iterator, context):
+      """A client-to-server streaming RPC.
+
+      Accepts a stream of Points on a route being traversed, returning a
+      RouteSummary when traversal is completed.
+      """
+      context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+      context.set_details('Method not implemented!')
+      raise NotImplementedError('Method not implemented!')
+
+    def RouteChat(self, request_iterator, context):
+      """A Bidirectional streaming RPC.
+
+      Accepts a stream of RouteNotes sent while a route is being traversed,
+      while receiving other RouteNotes (e.g. from other users).
+      """
+      context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+      context.set_details('Method not implemented!')
+      raise NotImplementedError('Method not implemented!')
+
+
+  def add_RouteGuideServicer_to_server(servicer, server):
+    rpc_method_handlers = {
+        'GetFeature': grpc.unary_unary_rpc_method_handler(
+            servicer.GetFeature,
+            request_deserializer=Point.FromString,
+            response_serializer=Feature.SerializeToString,
+        ),
+        'ListFeatures': grpc.unary_stream_rpc_method_handler(
+            servicer.ListFeatures,
+            request_deserializer=Rectangle.FromString,
+            response_serializer=Feature.SerializeToString,
+        ),
+        'RecordRoute': grpc.stream_unary_rpc_method_handler(
+            servicer.RecordRoute,
+            request_deserializer=Point.FromString,
+            response_serializer=RouteSummary.SerializeToString,
+        ),
+        'RouteChat': grpc.stream_stream_rpc_method_handler(
+            servicer.RouteChat,
+            request_deserializer=RouteNote.FromString,
+            response_serializer=RouteNote.SerializeToString,
+        ),
+    }
+    generic_handler = grpc.method_handlers_generic_handler(
+        'routeguide.RouteGuide', rpc_method_handlers)
+    server.add_generic_rpc_handlers((generic_handler,))
+
+
+  class BetaRouteGuideServicer(object):
+    """The Beta API is deprecated for 0.15.0 and later.
+
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This class was generated
+    only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0."""
+    """Interface exported by the server.
     """
-    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
-    context.set_details('Method not implemented!')
-    raise NotImplementedError('Method not implemented!')
-
-  def RouteChat(self, request_iterator, context):
-    """A Bidirectional streaming RPC.
-
-    Accepts a stream of RouteNotes sent while a route is being traversed,
-    while receiving other RouteNotes (e.g. from other users).
-    """
-    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
-    context.set_details('Method not implemented!')
-    raise NotImplementedError('Method not implemented!')
-
-
-def add_RouteGuideServicer_to_server(servicer, server):
-  rpc_method_handlers = {
-      'GetFeature': grpc.unary_unary_rpc_method_handler(
-          servicer.GetFeature,
-          request_deserializer=Point.FromString,
-          response_serializer=Feature.SerializeToString,
-      ),
-      'ListFeatures': grpc.unary_stream_rpc_method_handler(
-          servicer.ListFeatures,
-          request_deserializer=Rectangle.FromString,
-          response_serializer=Feature.SerializeToString,
-      ),
-      'RecordRoute': grpc.stream_unary_rpc_method_handler(
-          servicer.RecordRoute,
-          request_deserializer=Point.FromString,
-          response_serializer=RouteSummary.SerializeToString,
-      ),
-      'RouteChat': grpc.stream_stream_rpc_method_handler(
-          servicer.RouteChat,
-          request_deserializer=RouteNote.FromString,
-          response_serializer=RouteNote.SerializeToString,
-      ),
-  }
-  generic_handler = grpc.method_handlers_generic_handler(
-      'routeguide.RouteGuide', rpc_method_handlers)
-  server.add_generic_rpc_handlers((generic_handler,))
-
-
-class BetaRouteGuideServicer(object):
-  """Interface exported by the server.
-  """
-  def GetFeature(self, request, context):
-    """A simple RPC.
-
-    Obtains the feature at a given position.
-
-    A feature with an empty name is returned if there's no feature at the given
-    position.
-    """
-    context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
-  def ListFeatures(self, request, context):
-    """A server-to-client streaming RPC.
-
-    Obtains the Features available within the given Rectangle.  Results are
-    streamed rather than returned at once (e.g. in a response message with a
-    repeated field), as the rectangle may cover a large area and contain a
-    huge number of features.
-    """
-    context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
-  def RecordRoute(self, request_iterator, context):
-    """A client-to-server streaming RPC.
-
-    Accepts a stream of Points on a route being traversed, returning a
-    RouteSummary when traversal is completed.
-    """
-    context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
-  def RouteChat(self, request_iterator, context):
-    """A Bidirectional streaming RPC.
-
-    Accepts a stream of RouteNotes sent while a route is being traversed,
-    while receiving other RouteNotes (e.g. from other users).
-    """
-    context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
-
-
-class BetaRouteGuideStub(object):
-  """Interface exported by the server.
-  """
-  def GetFeature(self, request, timeout, metadata=None, with_call=False, protocol_options=None):
-    """A simple RPC.
-
-    Obtains the feature at a given position.
-
-    A feature with an empty name is returned if there's no feature at the given
-    position.
-    """
-    raise NotImplementedError()
-  GetFeature.future = None
-  def ListFeatures(self, request, timeout, metadata=None, with_call=False, protocol_options=None):
-    """A server-to-client streaming RPC.
-
-    Obtains the Features available within the given Rectangle.  Results are
-    streamed rather than returned at once (e.g. in a response message with a
-    repeated field), as the rectangle may cover a large area and contain a
-    huge number of features.
-    """
-    raise NotImplementedError()
-  def RecordRoute(self, request_iterator, timeout, metadata=None, with_call=False, protocol_options=None):
-    """A client-to-server streaming RPC.
-
-    Accepts a stream of Points on a route being traversed, returning a
-    RouteSummary when traversal is completed.
-    """
-    raise NotImplementedError()
-  RecordRoute.future = None
-  def RouteChat(self, request_iterator, timeout, metadata=None, with_call=False, protocol_options=None):
-    """A Bidirectional streaming RPC.
-
-    Accepts a stream of RouteNotes sent while a route is being traversed,
-    while receiving other RouteNotes (e.g. from other users).
+    def GetFeature(self, request, context):
+      """A simple RPC.
+
+      Obtains the feature at a given position.
+
+      A feature with an empty name is returned if there's no feature at the given
+      position.
+      """
+      context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
+    def ListFeatures(self, request, context):
+      """A server-to-client streaming RPC.
+
+      Obtains the Features available within the given Rectangle.  Results are
+      streamed rather than returned at once (e.g. in a response message with a
+      repeated field), as the rectangle may cover a large area and contain a
+      huge number of features.
+      """
+      context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
+    def RecordRoute(self, request_iterator, context):
+      """A client-to-server streaming RPC.
+
+      Accepts a stream of Points on a route being traversed, returning a
+      RouteSummary when traversal is completed.
+      """
+      context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
+    def RouteChat(self, request_iterator, context):
+      """A Bidirectional streaming RPC.
+
+      Accepts a stream of RouteNotes sent while a route is being traversed,
+      while receiving other RouteNotes (e.g. from other users).
+      """
+      context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
+
+
+  class BetaRouteGuideStub(object):
+    """The Beta API is deprecated for 0.15.0 and later.
+
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This class was generated
+    only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0."""
+    """Interface exported by the server.
     """
-    raise NotImplementedError()
-
-
-def beta_create_RouteGuide_server(servicer, pool=None, pool_size=None, default_timeout=None, maximum_timeout=None):
-  request_deserializers = {
-    ('routeguide.RouteGuide', 'GetFeature'): Point.FromString,
-    ('routeguide.RouteGuide', 'ListFeatures'): Rectangle.FromString,
-    ('routeguide.RouteGuide', 'RecordRoute'): Point.FromString,
-    ('routeguide.RouteGuide', 'RouteChat'): RouteNote.FromString,
-  }
-  response_serializers = {
-    ('routeguide.RouteGuide', 'GetFeature'): Feature.SerializeToString,
-    ('routeguide.RouteGuide', 'ListFeatures'): Feature.SerializeToString,
-    ('routeguide.RouteGuide', 'RecordRoute'): RouteSummary.SerializeToString,
-    ('routeguide.RouteGuide', 'RouteChat'): RouteNote.SerializeToString,
-  }
-  method_implementations = {
-    ('routeguide.RouteGuide', 'GetFeature'): face_utilities.unary_unary_inline(servicer.GetFeature),
-    ('routeguide.RouteGuide', 'ListFeatures'): face_utilities.unary_stream_inline(servicer.ListFeatures),
-    ('routeguide.RouteGuide', 'RecordRoute'): face_utilities.stream_unary_inline(servicer.RecordRoute),
-    ('routeguide.RouteGuide', 'RouteChat'): face_utilities.stream_stream_inline(servicer.RouteChat),
-  }
-  server_options = beta_implementations.server_options(request_deserializers=request_deserializers, response_serializers=response_serializers, thread_pool=pool, thread_pool_size=pool_size, default_timeout=default_timeout, maximum_timeout=maximum_timeout)
-  return beta_implementations.server(method_implementations, options=server_options)
-
-
-def beta_create_RouteGuide_stub(channel, host=None, metadata_transformer=None, pool=None, pool_size=None):
-  request_serializers = {
-    ('routeguide.RouteGuide', 'GetFeature'): Point.SerializeToString,
-    ('routeguide.RouteGuide', 'ListFeatures'): Rectangle.SerializeToString,
-    ('routeguide.RouteGuide', 'RecordRoute'): Point.SerializeToString,
-    ('routeguide.RouteGuide', 'RouteChat'): RouteNote.SerializeToString,
-  }
-  response_deserializers = {
-    ('routeguide.RouteGuide', 'GetFeature'): Feature.FromString,
-    ('routeguide.RouteGuide', 'ListFeatures'): Feature.FromString,
-    ('routeguide.RouteGuide', 'RecordRoute'): RouteSummary.FromString,
-    ('routeguide.RouteGuide', 'RouteChat'): RouteNote.FromString,
-  }
-  cardinalities = {
-    'GetFeature': cardinality.Cardinality.UNARY_UNARY,
-    'ListFeatures': cardinality.Cardinality.UNARY_STREAM,
-    'RecordRoute': cardinality.Cardinality.STREAM_UNARY,
-    'RouteChat': cardinality.Cardinality.STREAM_STREAM,
-  }
-  stub_options = beta_implementations.stub_options(host=host, metadata_transformer=metadata_transformer, request_serializers=request_serializers, response_deserializers=response_deserializers, thread_pool=pool, thread_pool_size=pool_size)
-  return beta_implementations.dynamic_stub(channel, 'routeguide.RouteGuide', cardinalities, options=stub_options)
+    def GetFeature(self, request, timeout, metadata=None, with_call=False, protocol_options=None):
+      """A simple RPC.
+
+      Obtains the feature at a given position.
+
+      A feature with an empty name is returned if there's no feature at the given
+      position.
+      """
+      raise NotImplementedError()
+    GetFeature.future = None
+    def ListFeatures(self, request, timeout, metadata=None, with_call=False, protocol_options=None):
+      """A server-to-client streaming RPC.
+
+      Obtains the Features available within the given Rectangle.  Results are
+      streamed rather than returned at once (e.g. in a response message with a
+      repeated field), as the rectangle may cover a large area and contain a
+      huge number of features.
+      """
+      raise NotImplementedError()
+    def RecordRoute(self, request_iterator, timeout, metadata=None, with_call=False, protocol_options=None):
+      """A client-to-server streaming RPC.
+
+      Accepts a stream of Points on a route being traversed, returning a
+      RouteSummary when traversal is completed.
+      """
+      raise NotImplementedError()
+    RecordRoute.future = None
+    def RouteChat(self, request_iterator, timeout, metadata=None, with_call=False, protocol_options=None):
+      """A Bidirectional streaming RPC.
+
+      Accepts a stream of RouteNotes sent while a route is being traversed,
+      while receiving other RouteNotes (e.g. from other users).
+      """
+      raise NotImplementedError()
+
+
+  def beta_create_RouteGuide_server(servicer, pool=None, pool_size=None, default_timeout=None, maximum_timeout=None):
+    """The Beta API is deprecated for 0.15.0 and later.
+
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This function was
+    generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"""
+    request_deserializers = {
+      ('routeguide.RouteGuide', 'GetFeature'): Point.FromString,
+      ('routeguide.RouteGuide', 'ListFeatures'): Rectangle.FromString,
+      ('routeguide.RouteGuide', 'RecordRoute'): Point.FromString,
+      ('routeguide.RouteGuide', 'RouteChat'): RouteNote.FromString,
+    }
+    response_serializers = {
+      ('routeguide.RouteGuide', 'GetFeature'): Feature.SerializeToString,
+      ('routeguide.RouteGuide', 'ListFeatures'): Feature.SerializeToString,
+      ('routeguide.RouteGuide', 'RecordRoute'): RouteSummary.SerializeToString,
+      ('routeguide.RouteGuide', 'RouteChat'): RouteNote.SerializeToString,
+    }
+    method_implementations = {
+      ('routeguide.RouteGuide', 'GetFeature'): face_utilities.unary_unary_inline(servicer.GetFeature),
+      ('routeguide.RouteGuide', 'ListFeatures'): face_utilities.unary_stream_inline(servicer.ListFeatures),
+      ('routeguide.RouteGuide', 'RecordRoute'): face_utilities.stream_unary_inline(servicer.RecordRoute),
+      ('routeguide.RouteGuide', 'RouteChat'): face_utilities.stream_stream_inline(servicer.RouteChat),
+    }
+    server_options = beta_implementations.server_options(request_deserializers=request_deserializers, response_serializers=response_serializers, thread_pool=pool, thread_pool_size=pool_size, default_timeout=default_timeout, maximum_timeout=maximum_timeout)
+    return beta_implementations.server(method_implementations, options=server_options)
+
+
+  def beta_create_RouteGuide_stub(channel, host=None, metadata_transformer=None, pool=None, pool_size=None):
+    """The Beta API is deprecated for 0.15.0 and later.
+
+    It is recommended to use the GA API (classes and functions in this
+    file not marked beta) for all further purposes. This function was
+    generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"""
+    request_serializers = {
+      ('routeguide.RouteGuide', 'GetFeature'): Point.SerializeToString,
+      ('routeguide.RouteGuide', 'ListFeatures'): Rectangle.SerializeToString,
+      ('routeguide.RouteGuide', 'RecordRoute'): Point.SerializeToString,
+      ('routeguide.RouteGuide', 'RouteChat'): RouteNote.SerializeToString,
+    }
+    response_deserializers = {
+      ('routeguide.RouteGuide', 'GetFeature'): Feature.FromString,
+      ('routeguide.RouteGuide', 'ListFeatures'): Feature.FromString,
+      ('routeguide.RouteGuide', 'RecordRoute'): RouteSummary.FromString,
+      ('routeguide.RouteGuide', 'RouteChat'): RouteNote.FromString,
+    }
+    cardinalities = {
+      'GetFeature': cardinality.Cardinality.UNARY_UNARY,
+      'ListFeatures': cardinality.Cardinality.UNARY_STREAM,
+      'RecordRoute': cardinality.Cardinality.STREAM_UNARY,
+      'RouteChat': cardinality.Cardinality.STREAM_STREAM,
+    }
+    stub_options = beta_implementations.stub_options(host=host, metadata_transformer=metadata_transformer, request_serializers=request_serializers, response_deserializers=response_deserializers, thread_pool=pool, thread_pool_size=pool_size)
+    return beta_implementations.dynamic_stub(channel, 'routeguide.RouteGuide', cardinalities, options=stub_options)
+except ImportError:
+  pass
 # @@protoc_insertion_point(module_scope)
diff --git a/examples/python/route_guide/route_guide_pb2_grpc.py b/examples/python/route_guide/route_guide_pb2_grpc.py
new file mode 100644
index 0000000000000000000000000000000000000000..27b24c747dbd1ec383e70b71ca6230502822cb63
--- /dev/null
+++ b/examples/python/route_guide/route_guide_pb2_grpc.py
@@ -0,0 +1,114 @@
+import grpc
+from grpc.framework.common import cardinality
+from grpc.framework.interfaces.face import utilities as face_utilities
+
+import route_guide_pb2 as route__guide__pb2
+
+
+class RouteGuideStub(object):
+  """Interface exported by the server.
+  """
+
+  def __init__(self, channel):
+    """Constructor.
+
+    Args:
+      channel: A grpc.Channel.
+    """
+    self.GetFeature = channel.unary_unary(
+        '/routeguide.RouteGuide/GetFeature',
+        request_serializer=route__guide__pb2.Point.SerializeToString,
+        response_deserializer=route__guide__pb2.Feature.FromString,
+        )
+    self.ListFeatures = channel.unary_stream(
+        '/routeguide.RouteGuide/ListFeatures',
+        request_serializer=route__guide__pb2.Rectangle.SerializeToString,
+        response_deserializer=route__guide__pb2.Feature.FromString,
+        )
+    self.RecordRoute = channel.stream_unary(
+        '/routeguide.RouteGuide/RecordRoute',
+        request_serializer=route__guide__pb2.Point.SerializeToString,
+        response_deserializer=route__guide__pb2.RouteSummary.FromString,
+        )
+    self.RouteChat = channel.stream_stream(
+        '/routeguide.RouteGuide/RouteChat',
+        request_serializer=route__guide__pb2.RouteNote.SerializeToString,
+        response_deserializer=route__guide__pb2.RouteNote.FromString,
+        )
+
+
+class RouteGuideServicer(object):
+  """Interface exported by the server.
+  """
+
+  def GetFeature(self, request, context):
+    """A simple RPC.
+
+    Obtains the feature at a given position.
+
+    A feature with an empty name is returned if there's no feature at the given
+    position.
+    """
+    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+    context.set_details('Method not implemented!')
+    raise NotImplementedError('Method not implemented!')
+
+  def ListFeatures(self, request, context):
+    """A server-to-client streaming RPC.
+
+    Obtains the Features available within the given Rectangle.  Results are
+    streamed rather than returned at once (e.g. in a response message with a
+    repeated field), as the rectangle may cover a large area and contain a
+    huge number of features.
+    """
+    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+    context.set_details('Method not implemented!')
+    raise NotImplementedError('Method not implemented!')
+
+  def RecordRoute(self, request_iterator, context):
+    """A client-to-server streaming RPC.
+
+    Accepts a stream of Points on a route being traversed, returning a
+    RouteSummary when traversal is completed.
+    """
+    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+    context.set_details('Method not implemented!')
+    raise NotImplementedError('Method not implemented!')
+
+  def RouteChat(self, request_iterator, context):
+    """A Bidirectional streaming RPC.
+
+    Accepts a stream of RouteNotes sent while a route is being traversed,
+    while receiving other RouteNotes (e.g. from other users).
+    """
+    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+    context.set_details('Method not implemented!')
+    raise NotImplementedError('Method not implemented!')
+
+
+def add_RouteGuideServicer_to_server(servicer, server):
+  rpc_method_handlers = {
+      'GetFeature': grpc.unary_unary_rpc_method_handler(
+          servicer.GetFeature,
+          request_deserializer=route__guide__pb2.Point.FromString,
+          response_serializer=route__guide__pb2.Feature.SerializeToString,
+      ),
+      'ListFeatures': grpc.unary_stream_rpc_method_handler(
+          servicer.ListFeatures,
+          request_deserializer=route__guide__pb2.Rectangle.FromString,
+          response_serializer=route__guide__pb2.Feature.SerializeToString,
+      ),
+      'RecordRoute': grpc.stream_unary_rpc_method_handler(
+          servicer.RecordRoute,
+          request_deserializer=route__guide__pb2.Point.FromString,
+          response_serializer=route__guide__pb2.RouteSummary.SerializeToString,
+      ),
+      'RouteChat': grpc.stream_stream_rpc_method_handler(
+          servicer.RouteChat,
+          request_deserializer=route__guide__pb2.RouteNote.FromString,
+          response_serializer=route__guide__pb2.RouteNote.SerializeToString,
+      ),
+  }
+  generic_handler = grpc.method_handlers_generic_handler(
+      'routeguide.RouteGuide', rpc_method_handlers)
+  server.add_generic_rpc_handlers((generic_handler,))
diff --git a/examples/python/route_guide/route_guide_server.py b/examples/python/route_guide/route_guide_server.py
index 3ffe6784768e1e0973c6540711b71df640dca0b5..bf4921793286f089f81789b08bfaf496497a274a 100644
--- a/examples/python/route_guide/route_guide_server.py
+++ b/examples/python/route_guide/route_guide_server.py
@@ -36,6 +36,7 @@ import math
 import grpc
 
 import route_guide_pb2
+import route_guide_pb2_grpc
 import route_guide_resources
 
 _ONE_DAY_IN_SECONDS = 60 * 60 * 24
@@ -68,7 +69,7 @@ def get_distance(start, end):
   R = 6371000; # metres
   return R * c;
 
-class RouteGuideServicer(route_guide_pb2.RouteGuideServicer):
+class RouteGuideServicer(route_guide_pb2_grpc.RouteGuideServicer):
   """Provides methods that implement functionality of route guide server."""
 
   def __init__(self):
@@ -125,7 +126,7 @@ class RouteGuideServicer(route_guide_pb2.RouteGuideServicer):
 
 def serve():
   server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
-  route_guide_pb2.add_RouteGuideServicer_to_server(
+  route_guide_pb2_grpc.add_RouteGuideServicer_to_server(
       RouteGuideServicer(), server)
   server.add_insecure_port('[::]:50051')
   server.start()
diff --git a/examples/python/route_guide/run_codegen.py b/examples/python/route_guide/run_codegen.py
index c7c60085809a98ee26bf13242fdb85bdd5a2fc31..3751e019c978d1a7817d4dd680a96971b99b89f3 100644
--- a/examples/python/route_guide/run_codegen.py
+++ b/examples/python/route_guide/run_codegen.py
@@ -29,7 +29,7 @@
 
 """Runs protoc with the gRPC plugin to generate messages and gRPC stubs."""
 
-from grpc.tools import protoc
+from grpc_tools import protoc
 
 protoc.main(
     (
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index c4ec01da931931de00cdf157fc9491aeba8d224a..e56eedd3a6b166d542ee4ca41d6875433012c21c 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -35,7 +35,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-Core'
-  version = '1.0.1'
+  version = '1.0.2'
   s.version  = version
   s.summary  = 'Core cross-platform gRPC library, written in C'
   s.homepage = 'http://www.grpc.io'
@@ -44,7 +44,9 @@ Pod::Spec.new do |s|
 
   s.source = {
     :git => 'https://github.com/grpc/grpc.git',
-    :tag => "v#{version}",
+    # TODO(mxyan): Change back to "v#{version}" for next release
+    #:tag => "v#{version}",
+    :tag => "objective-c-v#{version}",
     # TODO(jcanizales): Depend explicitly on the nanopb pod, and disable submodules.
     :submodules => true,
   }
@@ -165,6 +167,7 @@ Pod::Spec.new do |s|
                       'include/grpc/impl/codegen/byte_buffer_reader.h',
                       'include/grpc/impl/codegen/compression_types.h',
                       'include/grpc/impl/codegen/connectivity_state.h',
+                      'include/grpc/impl/codegen/exec_ctx_fwd.h',
                       'include/grpc/impl/codegen/grpc_types.h',
                       'include/grpc/impl/codegen/propagation_bits.h',
                       'include/grpc/impl/codegen/status.h',
@@ -253,6 +256,8 @@ Pod::Spec.new do |s|
                       'src/core/lib/channel/context.h',
                       'src/core/lib/channel/deadline_filter.h',
                       'src/core/lib/channel/handshaker.h',
+                      'src/core/lib/channel/handshaker_factory.h',
+                      'src/core/lib/channel/handshaker_registry.h',
                       'src/core/lib/channel/http_client_filter.h',
                       'src/core/lib/channel/http_server_filter.h',
                       'src/core/lib/channel/message_size_filter.h',
@@ -319,6 +324,7 @@ Pod::Spec.new do |s|
                       'src/core/lib/json/json_reader.h',
                       'src/core/lib/json/json_writer.h',
                       'src/core/lib/slice/percent_encoding.h',
+                      'src/core/lib/slice/slice_internal.h',
                       'src/core/lib/slice/slice_string_helpers.h',
                       'src/core/lib/surface/api_trace.h',
                       'src/core/lib/surface/call.h',
@@ -376,9 +382,9 @@ Pod::Spec.new do |s|
                       'src/core/lib/security/credentials/plugin/plugin_credentials.h',
                       'src/core/lib/security/credentials/ssl/ssl_credentials.h',
                       'src/core/lib/security/transport/auth_filters.h',
-                      'src/core/lib/security/transport/handshake.h',
                       'src/core/lib/security/transport/secure_endpoint.h',
                       'src/core/lib/security/transport/security_connector.h',
+                      'src/core/lib/security/transport/security_handshaker.h',
                       'src/core/lib/security/transport/tsi_error.h',
                       'src/core/lib/security/util/b64.h',
                       'src/core/lib/security/util/json_util.h',
@@ -387,6 +393,7 @@ Pod::Spec.new do |s|
                       'src/core/lib/tsi/ssl_types.h',
                       'src/core/lib/tsi/transport_security.h',
                       'src/core/lib/tsi/transport_security_interface.h',
+                      'src/core/ext/transport/chttp2/server/chttp2_server.h',
                       'src/core/ext/client_channel/client_channel.h',
                       'src/core/ext/client_channel/client_channel_factory.h',
                       'src/core/ext/client_channel/connector.h',
@@ -402,6 +409,7 @@ Pod::Spec.new do |s|
                       'src/core/ext/client_channel/subchannel.h',
                       'src/core/ext/client_channel/subchannel_index.h',
                       'src/core/ext/client_channel/uri_parser.h',
+                      'src/core/ext/transport/chttp2/client/chttp2_connector.h',
                       'src/core/ext/lb_policy/grpclb/grpclb.h',
                       'src/core/ext/lb_policy/grpclb/load_balancer_api.h',
                       'src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h',
@@ -430,6 +438,8 @@ Pod::Spec.new do |s|
                       'src/core/lib/channel/connected_channel.c',
                       'src/core/lib/channel/deadline_filter.c',
                       'src/core/lib/channel/handshaker.c',
+                      'src/core/lib/channel/handshaker_factory.c',
+                      'src/core/lib/channel/handshaker_registry.c',
                       'src/core/lib/channel/http_client_filter.c',
                       'src/core/lib/channel/http_server_filter.c',
                       'src/core/lib/channel/message_size_filter.c',
@@ -574,9 +584,9 @@ Pod::Spec.new do |s|
                       'src/core/lib/security/credentials/plugin/plugin_credentials.c',
                       'src/core/lib/security/credentials/ssl/ssl_credentials.c',
                       'src/core/lib/security/transport/client_auth_filter.c',
-                      'src/core/lib/security/transport/handshake.c',
                       'src/core/lib/security/transport/secure_endpoint.c',
                       'src/core/lib/security/transport/security_connector.c',
+                      'src/core/lib/security/transport/security_handshaker.c',
                       'src/core/lib/security/transport/server_auth_filter.c',
                       'src/core/lib/security/transport/tsi_error.c',
                       'src/core/lib/security/util/b64.c',
@@ -585,6 +595,7 @@ Pod::Spec.new do |s|
                       'src/core/lib/tsi/fake_transport_security.c',
                       'src/core/lib/tsi/ssl_transport_security.c',
                       'src/core/lib/tsi/transport_security.c',
+                      'src/core/ext/transport/chttp2/server/chttp2_server.c',
                       'src/core/ext/transport/chttp2/client/secure/secure_channel_create.c',
                       'src/core/ext/client_channel/channel_connectivity.c',
                       'src/core/ext/client_channel/client_channel.c',
@@ -604,6 +615,7 @@ Pod::Spec.new do |s|
                       'src/core/ext/client_channel/subchannel.c',
                       'src/core/ext/client_channel/subchannel_index.c',
                       'src/core/ext/client_channel/uri_parser.c',
+                      'src/core/ext/transport/chttp2/client/chttp2_connector.c',
                       'src/core/ext/transport/chttp2/server/insecure/server_chttp2.c',
                       'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c',
                       'src/core/ext/transport/chttp2/client/insecure/channel_create.c',
@@ -656,6 +668,8 @@ Pod::Spec.new do |s|
                               'src/core/lib/channel/context.h',
                               'src/core/lib/channel/deadline_filter.h',
                               'src/core/lib/channel/handshaker.h',
+                              'src/core/lib/channel/handshaker_factory.h',
+                              'src/core/lib/channel/handshaker_registry.h',
                               'src/core/lib/channel/http_client_filter.h',
                               'src/core/lib/channel/http_server_filter.h',
                               'src/core/lib/channel/message_size_filter.h',
@@ -722,6 +736,7 @@ Pod::Spec.new do |s|
                               'src/core/lib/json/json_reader.h',
                               'src/core/lib/json/json_writer.h',
                               'src/core/lib/slice/percent_encoding.h',
+                              'src/core/lib/slice/slice_internal.h',
                               'src/core/lib/slice/slice_string_helpers.h',
                               'src/core/lib/surface/api_trace.h',
                               'src/core/lib/surface/call.h',
@@ -779,9 +794,9 @@ Pod::Spec.new do |s|
                               'src/core/lib/security/credentials/plugin/plugin_credentials.h',
                               'src/core/lib/security/credentials/ssl/ssl_credentials.h',
                               'src/core/lib/security/transport/auth_filters.h',
-                              'src/core/lib/security/transport/handshake.h',
                               'src/core/lib/security/transport/secure_endpoint.h',
                               'src/core/lib/security/transport/security_connector.h',
+                              'src/core/lib/security/transport/security_handshaker.h',
                               'src/core/lib/security/transport/tsi_error.h',
                               'src/core/lib/security/util/b64.h',
                               'src/core/lib/security/util/json_util.h',
@@ -790,6 +805,7 @@ Pod::Spec.new do |s|
                               'src/core/lib/tsi/ssl_types.h',
                               'src/core/lib/tsi/transport_security.h',
                               'src/core/lib/tsi/transport_security_interface.h',
+                              'src/core/ext/transport/chttp2/server/chttp2_server.h',
                               'src/core/ext/client_channel/client_channel.h',
                               'src/core/ext/client_channel/client_channel_factory.h',
                               'src/core/ext/client_channel/connector.h',
@@ -805,6 +821,7 @@ Pod::Spec.new do |s|
                               'src/core/ext/client_channel/subchannel.h',
                               'src/core/ext/client_channel/subchannel_index.h',
                               'src/core/ext/client_channel/uri_parser.h',
+                              'src/core/ext/transport/chttp2/client/chttp2_connector.h',
                               'src/core/ext/lb_policy/grpclb/grpclb.h',
                               'src/core/ext/lb_policy/grpclb/load_balancer_api.h',
                               'src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h',
diff --git a/gRPC-ProtoRPC.podspec b/gRPC-ProtoRPC.podspec
index 61d4b62d391f2b30bc8fec54cd62587fb43691a5..62eaa2aaf7ffa5bfc4475621f18d2312726077d2 100644
--- a/gRPC-ProtoRPC.podspec
+++ b/gRPC-ProtoRPC.podspec
@@ -30,7 +30,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-ProtoRPC'
-  version = '1.0.1'
+  version = '1.0.2'
   s.version  = version
   s.summary  = 'RPC library for Protocol Buffers, based on gRPC'
   s.homepage = 'http://www.grpc.io'
@@ -39,7 +39,7 @@ Pod::Spec.new do |s|
 
   s.source = {
     :git => 'https://github.com/grpc/grpc.git',
-    :tag => "v#{version}",
+    :tag => "objective-c-v#{version}",
   }
 
   s.ios.deployment_target = '7.1'
diff --git a/gRPC-RxLibrary.podspec b/gRPC-RxLibrary.podspec
index d59385c039a182f419b28a9d2fd40190585bc27d..2e8fffd2f1158d66278cf0cc6d9859c32cad7b11 100644
--- a/gRPC-RxLibrary.podspec
+++ b/gRPC-RxLibrary.podspec
@@ -30,7 +30,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-RxLibrary'
-  version = '1.0.1'
+  version = '1.0.2'
   s.version  = version
   s.summary  = 'Reactive Extensions library for iOS/OSX.'
   s.homepage = 'http://www.grpc.io'
@@ -39,7 +39,7 @@ Pod::Spec.new do |s|
 
   s.source = {
     :git => 'https://github.com/grpc/grpc.git',
-    :tag => "v#{version}",
+    :tag => "objective-c-v#{version}",
   }
 
   s.ios.deployment_target = '7.1'
diff --git a/gRPC.podspec b/gRPC.podspec
index 76410b17d28996b12b280c1ba1483e0cd5328e99..e8b770944911cd43b37cc33c65ec875151b85ef7 100644
--- a/gRPC.podspec
+++ b/gRPC.podspec
@@ -30,7 +30,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC'
-  version = '1.0.1'
+  version = '1.0.2'
   s.version  = version
   s.summary  = 'gRPC client library for iOS/OSX'
   s.homepage = 'http://www.grpc.io'
@@ -39,7 +39,7 @@ Pod::Spec.new do |s|
 
   s.source = {
     :git => 'https://github.com/grpc/grpc.git',
-    :tag => "v#{version}",
+    :tag => "objective-c-v#{version}",
   }
 
   s.ios.deployment_target = '7.1'
diff --git a/grpc.gemspec b/grpc.gemspec
index cce6f2cfcccd2add5d390126cb0b9b274e5023d5..d1dcb1de020c59313068b56fa4a98e22b2bfceea 100755
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -27,7 +27,7 @@ Gem::Specification.new do |s|
   s.require_paths = %w( src/ruby/bin src/ruby/lib src/ruby/pb )
   s.platform      = Gem::Platform::RUBY
 
-  s.add_dependency 'google-protobuf', '~> 3.0.2'
+  s.add_dependency 'google-protobuf', '~> 3.1.0'
   s.add_dependency 'googleauth',      '~> 0.5.1'
 
   s.add_development_dependency 'bundler',            '~> 1.9'
@@ -148,6 +148,7 @@ Gem::Specification.new do |s|
   s.files += %w( include/grpc/impl/codegen/byte_buffer_reader.h )
   s.files += %w( include/grpc/impl/codegen/compression_types.h )
   s.files += %w( include/grpc/impl/codegen/connectivity_state.h )
+  s.files += %w( include/grpc/impl/codegen/exec_ctx_fwd.h )
   s.files += %w( include/grpc/impl/codegen/grpc_types.h )
   s.files += %w( include/grpc/impl/codegen/propagation_bits.h )
   s.files += %w( include/grpc/impl/codegen/status.h )
@@ -172,6 +173,8 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/channel/context.h )
   s.files += %w( src/core/lib/channel/deadline_filter.h )
   s.files += %w( src/core/lib/channel/handshaker.h )
+  s.files += %w( src/core/lib/channel/handshaker_factory.h )
+  s.files += %w( src/core/lib/channel/handshaker_registry.h )
   s.files += %w( src/core/lib/channel/http_client_filter.h )
   s.files += %w( src/core/lib/channel/http_server_filter.h )
   s.files += %w( src/core/lib/channel/message_size_filter.h )
@@ -238,6 +241,7 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/json/json_reader.h )
   s.files += %w( src/core/lib/json/json_writer.h )
   s.files += %w( src/core/lib/slice/percent_encoding.h )
+  s.files += %w( src/core/lib/slice/slice_internal.h )
   s.files += %w( src/core/lib/slice/slice_string_helpers.h )
   s.files += %w( src/core/lib/surface/api_trace.h )
   s.files += %w( src/core/lib/surface/call.h )
@@ -295,9 +299,9 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/security/credentials/plugin/plugin_credentials.h )
   s.files += %w( src/core/lib/security/credentials/ssl/ssl_credentials.h )
   s.files += %w( src/core/lib/security/transport/auth_filters.h )
-  s.files += %w( src/core/lib/security/transport/handshake.h )
   s.files += %w( src/core/lib/security/transport/secure_endpoint.h )
   s.files += %w( src/core/lib/security/transport/security_connector.h )
+  s.files += %w( src/core/lib/security/transport/security_handshaker.h )
   s.files += %w( src/core/lib/security/transport/tsi_error.h )
   s.files += %w( src/core/lib/security/util/b64.h )
   s.files += %w( src/core/lib/security/util/json_util.h )
@@ -306,6 +310,7 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/tsi/ssl_types.h )
   s.files += %w( src/core/lib/tsi/transport_security.h )
   s.files += %w( src/core/lib/tsi/transport_security_interface.h )
+  s.files += %w( src/core/ext/transport/chttp2/server/chttp2_server.h )
   s.files += %w( src/core/ext/client_channel/client_channel.h )
   s.files += %w( src/core/ext/client_channel/client_channel_factory.h )
   s.files += %w( src/core/ext/client_channel/connector.h )
@@ -321,6 +326,7 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/ext/client_channel/subchannel.h )
   s.files += %w( src/core/ext/client_channel/subchannel_index.h )
   s.files += %w( src/core/ext/client_channel/uri_parser.h )
+  s.files += %w( src/core/ext/transport/chttp2/client/chttp2_connector.h )
   s.files += %w( src/core/ext/lb_policy/grpclb/grpclb.h )
   s.files += %w( src/core/ext/lb_policy/grpclb/load_balancer_api.h )
   s.files += %w( src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h )
@@ -349,6 +355,8 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/channel/connected_channel.c )
   s.files += %w( src/core/lib/channel/deadline_filter.c )
   s.files += %w( src/core/lib/channel/handshaker.c )
+  s.files += %w( src/core/lib/channel/handshaker_factory.c )
+  s.files += %w( src/core/lib/channel/handshaker_registry.c )
   s.files += %w( src/core/lib/channel/http_client_filter.c )
   s.files += %w( src/core/lib/channel/http_server_filter.c )
   s.files += %w( src/core/lib/channel/message_size_filter.c )
@@ -493,9 +501,9 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/security/credentials/plugin/plugin_credentials.c )
   s.files += %w( src/core/lib/security/credentials/ssl/ssl_credentials.c )
   s.files += %w( src/core/lib/security/transport/client_auth_filter.c )
-  s.files += %w( src/core/lib/security/transport/handshake.c )
   s.files += %w( src/core/lib/security/transport/secure_endpoint.c )
   s.files += %w( src/core/lib/security/transport/security_connector.c )
+  s.files += %w( src/core/lib/security/transport/security_handshaker.c )
   s.files += %w( src/core/lib/security/transport/server_auth_filter.c )
   s.files += %w( src/core/lib/security/transport/tsi_error.c )
   s.files += %w( src/core/lib/security/util/b64.c )
@@ -504,6 +512,7 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/tsi/fake_transport_security.c )
   s.files += %w( src/core/lib/tsi/ssl_transport_security.c )
   s.files += %w( src/core/lib/tsi/transport_security.c )
+  s.files += %w( src/core/ext/transport/chttp2/server/chttp2_server.c )
   s.files += %w( src/core/ext/transport/chttp2/client/secure/secure_channel_create.c )
   s.files += %w( src/core/ext/client_channel/channel_connectivity.c )
   s.files += %w( src/core/ext/client_channel/client_channel.c )
@@ -523,6 +532,7 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/ext/client_channel/subchannel.c )
   s.files += %w( src/core/ext/client_channel/subchannel_index.c )
   s.files += %w( src/core/ext/client_channel/uri_parser.c )
+  s.files += %w( src/core/ext/transport/chttp2/client/chttp2_connector.c )
   s.files += %w( src/core/ext/transport/chttp2/server/insecure/server_chttp2.c )
   s.files += %w( src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c )
   s.files += %w( src/core/ext/transport/chttp2/client/insecure/channel_create.c )
diff --git a/include/grpc++/grpc++.h b/include/grpc++/grpc++.h
index 36d65d6ee1eb691098d8019feecde9bc11768cba..daecfea40696600a89958bbc89cb9c206967a1c2 100644
--- a/include/grpc++/grpc++.h
+++ b/include/grpc++/grpc++.h
@@ -44,6 +44,10 @@
 /// peer, compression settings, authentication, etc.
 /// - grpc::Server, representing a gRPC server, created by grpc::ServerBuilder.
 ///
+/// Streaming calls are handled with the streaming classes in
+/// \ref sync_stream.h and
+/// \ref async_stream.h.
+///
 /// Refer to the
 /// [examples](https://github.com/grpc/grpc/blob/master/examples/cpp)
 /// for code putting these pieces into play.
diff --git a/include/grpc++/impl/codegen/completion_queue.h b/include/grpc++/impl/codegen/completion_queue.h
index ef00163b7eceadd822c2e4398e0d59588310ff41..03cecdc21c61e0c51b9fceacc38e1715b3c78c21 100644
--- a/include/grpc++/impl/codegen/completion_queue.h
+++ b/include/grpc++/impl/codegen/completion_queue.h
@@ -52,6 +52,7 @@
 #include <grpc++/impl/codegen/grpc_library.h>
 #include <grpc++/impl/codegen/status.h>
 #include <grpc++/impl/codegen/time.h>
+#include <grpc/impl/codegen/atm.h>
 
 struct grpc_completion_queue;
 
@@ -93,14 +94,17 @@ class ServerContext;
 
 extern CoreCodegenInterface* g_core_codegen_interface;
 
-/// A thin wrapper around \a grpc_completion_queue (see / \a
-/// src/core/surface/completion_queue.h).
+/// A thin wrapper around \ref grpc_completion_queue (see \ref
+/// src/core/lib/surface/completion_queue.h).
+/// See \ref doc/cpp/perf_notes.md for notes on best practices for high
+/// performance servers.
 class CompletionQueue : private GrpcLibraryCodegen {
  public:
   /// Default constructor. Implicitly creates a \a grpc_completion_queue
   /// instance.
   CompletionQueue() {
     cq_ = g_core_codegen_interface->grpc_completion_queue_create(nullptr);
+    InitialAvalanching();  // reserve this for the future shutdown
   }
 
   /// Wrap \a take, taking ownership of the instance.
@@ -151,7 +155,8 @@ class CompletionQueue : private GrpcLibraryCodegen {
 
   /// Request the shutdown of the queue.
   ///
-  /// \warning This method must be called at some point. Once invoked, \a Next
+  /// \warning This method must be called at some point if this completion queue
+  /// is accessed with Next or AsyncNext. Once invoked, \a Next
   /// will start to return false and \a AsyncNext will return \a
   /// NextStatus::SHUTDOWN. Only once either one of these methods does that
   /// (that is, once the queue has been \em drained) can an instance of this
@@ -165,6 +170,21 @@ class CompletionQueue : private GrpcLibraryCodegen {
   /// owership is performed.
   grpc_completion_queue* cq() { return cq_; }
 
+  /// Manage state of avalanching operations : completion queue tags that
+  /// trigger other completion queue operations. The underlying core completion
+  /// queue should not really shutdown until all avalanching operations have
+  /// been finalized. Note that we maintain the requirement that an avalanche
+  /// registration must take place before CQ shutdown (which must be maintained
+  /// elsehwere)
+  void InitialAvalanching() {
+    gpr_atm_rel_store(&avalanches_in_flight_, static_cast<gpr_atm>(1));
+  }
+  void RegisterAvalanching() {
+    gpr_atm_no_barrier_fetch_add(&avalanches_in_flight_,
+                                 static_cast<gpr_atm>(1));
+  };
+  void CompleteAvalanching();
+
  private:
   // Friend synchronous wrappers so that they can access Pluck(), which is
   // a semi-private API geared towards the synchronous implementation.
@@ -229,6 +249,8 @@ class CompletionQueue : private GrpcLibraryCodegen {
   }
 
   grpc_completion_queue* cq_;  // owned
+
+  gpr_atm avalanches_in_flight_;
 };
 
 /// A specific type of completion queue used by the processing of notifications
diff --git a/include/grpc++/impl/codegen/server_interface.h b/include/grpc++/impl/codegen/server_interface.h
index 41a64bead039fec7a3b7d57acb8c82533015f50f..666b9ff66eb4f792384f240d1db7b77e05757278 100644
--- a/include/grpc++/impl/codegen/server_interface.h
+++ b/include/grpc++/impl/codegen/server_interface.h
@@ -140,7 +140,7 @@ class ServerInterface : public CallHook {
                      ServerAsyncStreamingInterface* stream,
                      CompletionQueue* call_cq, void* tag,
                      bool delete_on_finalize);
-    virtual ~BaseAsyncRequest() {}
+    virtual ~BaseAsyncRequest();
 
     bool FinalizeResult(void** tag, bool* status) override;
 
diff --git a/include/grpc++/resource_quota.h b/include/grpc++/resource_quota.h
index 75e04d4e2ffe451795a6cc32ee25c8828951a4d9..68a514658d958aeece3f560eddc3aec4e7565ad9 100644
--- a/include/grpc++/resource_quota.h
+++ b/include/grpc++/resource_quota.h
@@ -37,6 +37,7 @@
 struct grpc_resource_quota;
 
 #include <grpc++/impl/codegen/config.h>
+#include <grpc++/impl/codegen/grpc_library.h>
 
 namespace grpc {
 
@@ -44,7 +45,7 @@ namespace grpc {
 /// A ResourceQuota can be attached to a server (via ServerBuilder), or a client
 /// channel (via ChannelArguments). gRPC will attempt to keep memory used by
 /// all attached entities below the ResourceQuota bound.
-class ResourceQuota final {
+class ResourceQuota final : private GrpcLibraryCodegen {
  public:
   explicit ResourceQuota(const grpc::string& name);
   ResourceQuota();
diff --git a/include/grpc++/server_builder.h b/include/grpc++/server_builder.h
index 9252c6a63a8b9d0700ec62f01b4f3960eda84f94..2ac2f0a1ef485c2a1a6bf6184016407d73f22ac7 100644
--- a/include/grpc++/server_builder.h
+++ b/include/grpc++/server_builder.h
@@ -187,7 +187,7 @@ class ServerBuilder {
 
   struct SyncServerSettings {
     SyncServerSettings()
-        : num_cqs(GPR_MAX(gpr_cpu_num_cores(), 4)),
+        : num_cqs(1),
           min_pollers(1),
           max_pollers(INT_MAX),
           cq_timeout_msec(1000) {}
diff --git a/include/grpc++/support/channel_arguments.h b/include/grpc++/support/channel_arguments.h
index 571ab5d53054489084c3c620b5c2bb40ec5ebb87..d43f7c61bddbef307afab7f9d929e30d0b4355be 100644
--- a/include/grpc++/support/channel_arguments.h
+++ b/include/grpc++/support/channel_arguments.h
@@ -127,7 +127,7 @@ class ChannelArguments {
   /// Default pointer argument operations.
   struct PointerVtableMembers {
     static void* Copy(void* in) { return in; }
-    static void Destroy(void* in) {}
+    static void Destroy(grpc_exec_ctx* exec_ctx, void* in) {}
     static int Compare(void* a, void* b) {
       if (a < b) return -1;
       if (a > b) return 1;
diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h
index 5e486215e01c5d79921ce7968ae031c7bb251a7c..898f4d533bcff5d222b805fdebe6a82ceeb4e0f2 100644
--- a/include/grpc/grpc.h
+++ b/include/grpc/grpc.h
@@ -202,9 +202,15 @@ GRPCAPI grpc_call *grpc_channel_create_registered_call(
     completion of type 'tag' to the completion queue bound to the call.
     The order of ops specified in the batch has no significance.
     Only one operation of each type can be active at once in any given
-    batch. You must call grpc_completion_queue_next or
-    grpc_completion_queue_pluck on the completion queue associated with 'call'
-    for work to be performed.
+    batch.
+    If a call to grpc_call_start_batch returns GRPC_CALL_OK you must call
+    grpc_completion_queue_next or grpc_completion_queue_pluck on the completion
+    queue associated with 'call' for work to be performed. If a call to
+    grpc_call_start_batch returns any value other than GRPC_CALL_OK it is
+    guaranteed that no state associated with 'call' is changed and it is not
+    appropriate to call grpc_completion_queue_next or
+    grpc_completion_queue_pluck consequent to the failed grpc_call_start_batch
+    call.
     THREAD SAFETY: access to grpc_call_start_batch in multi-threaded environment
     needs to be synchronized. As an optimization, you may synchronize batches
     containing just send operations independently from batches containing just
diff --git a/include/grpc/impl/codegen/exec_ctx_fwd.h b/include/grpc/impl/codegen/exec_ctx_fwd.h
new file mode 100644
index 0000000000000000000000000000000000000000..32edcedbb31d0dd0068d9fc34c7cf273aa619271
--- /dev/null
+++ b/include/grpc/impl/codegen/exec_ctx_fwd.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_EXEC_CTX_FWD_H
+#define GRPC_IMPL_CODEGEN_EXEC_CTX_FWD_H
+
+/* forward declaration for exec_ctx.h */
+struct grpc_exec_ctx;
+typedef struct grpc_exec_ctx grpc_exec_ctx;
+
+#endif /* GRPC_IMPL_CODEGEN_EXEC_CTX_FWD_H */
diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h
index da1bd9dcbf0415463f2b5b8168fc1e48f6998866..42825b6878ce8fe2af054040893184eb2b394e4c 100644
--- a/include/grpc/impl/codegen/grpc_types.h
+++ b/include/grpc/impl/codegen/grpc_types.h
@@ -34,10 +34,10 @@
 #ifndef GRPC_IMPL_CODEGEN_GRPC_TYPES_H
 #define GRPC_IMPL_CODEGEN_GRPC_TYPES_H
 
+#include <grpc/impl/codegen/compression_types.h>
+#include <grpc/impl/codegen/exec_ctx_fwd.h>
 #include <grpc/impl/codegen/gpr_types.h>
 #include <grpc/impl/codegen/slice.h>
-
-#include <grpc/impl/codegen/compression_types.h>
 #include <grpc/impl/codegen/status.h>
 
 #include <stddef.h>
@@ -96,7 +96,7 @@ typedef enum {
 
 typedef struct grpc_arg_pointer_vtable {
   void *(*copy)(void *p);
-  void (*destroy)(void *p);
+  void (*destroy)(grpc_exec_ctx *exec_ctx, void *p);
   int (*cmp)(void *p, void *q);
 } grpc_arg_pointer_vtable;
 
@@ -206,18 +206,12 @@ typedef struct {
 /** If non-zero, allow the use of SO_REUSEPORT if it's available (default 1) */
 #define GRPC_ARG_ALLOW_REUSEPORT "grpc.so_reuseport"
 /** If non-zero, a pointer to a buffer pool (use grpc_resource_quota_arg_vtable
-   to fetch an appropriate pointer arg vtable */
+   to fetch an appropriate pointer arg vtable) */
 #define GRPC_ARG_RESOURCE_QUOTA "grpc.resource_quota"
-/** Service config data, to be passed to subchannels.
-    Not intended for external use. */
+/** Service config data in JSON form. Not intended for use outside of tests. */
 #define GRPC_ARG_SERVICE_CONFIG "grpc.service_config"
 /** LB policy name. */
 #define GRPC_ARG_LB_POLICY_NAME "grpc.lb_policy_name"
-/** Server name. Not intended for external use. */
-#define GRPC_ARG_SERVER_NAME "grpc.server_name"
-/** Resolved addresses in a form used by the LB policy.
-    Not intended for external use. */
-#define GRPC_ARG_LB_ADDRESSES "grpc.lb_addresses"
 /** The grpc_socket_mutator instance that set the socket options. A pointer. */
 #define GRPC_ARG_SOCKET_MUTATOR "grpc.socket_mutator"
 /** \} */
diff --git a/include/grpc/impl/codegen/port_platform.h b/include/grpc/impl/codegen/port_platform.h
index cd989d664ca0c7bbb219a79879fb7ca307336c72..4633fa12e84767d836b53b245024387759579992 100644
--- a/include/grpc/impl/codegen/port_platform.h
+++ b/include/grpc/impl/codegen/port_platform.h
@@ -361,7 +361,7 @@ typedef unsigned __int64 uint64_t;
 #define GPR_MAX_ALIGNMENT 16
 
 #ifndef GRPC_MUST_USE_RESULT
-#ifdef __GNUC__
+#if defined(__GNUC__) && !defined(__MINGW32__)
 #define GRPC_MUST_USE_RESULT __attribute__((warn_unused_result))
 #else
 #define GRPC_MUST_USE_RESULT
diff --git a/include/grpc/impl/codegen/slice.h b/include/grpc/impl/codegen/slice.h
index 774ba0e95d49f5ea0fbad74f9ca9b9a845d264a9..4d4a86fa22e40ebf2f60739fc76ca6c02a58a804 100644
--- a/include/grpc/impl/codegen/slice.h
+++ b/include/grpc/impl/codegen/slice.h
@@ -37,6 +37,8 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <grpc/impl/codegen/exec_ctx_fwd.h>
+
 /* Slice API
 
    A slice represents a contiguous reference counted array of bytes.
@@ -57,7 +59,7 @@
    grpc_slice_new, or grpc_slice_new_with_len instead. */
 typedef struct grpc_slice_refcount {
   void (*ref)(void *);
-  void (*unref)(void *);
+  void (*unref)(grpc_exec_ctx *exec_ctx, void *);
 } grpc_slice_refcount;
 
 #define GRPC_SLICE_INLINED_SIZE (sizeof(size_t) + sizeof(uint8_t *) - 1)
diff --git a/include/grpc/support/log_windows.h b/include/grpc/support/log_windows.h
index 12bf8cc1f5ecea5c1d88e8ce60906c34ad51c9f2..943a8e908b3eea1a4d121d148e30eec041303a3f 100644
--- a/include/grpc/support/log_windows.h
+++ b/include/grpc/support/log_windows.h
@@ -34,6 +34,8 @@
 #ifndef GRPC_SUPPORT_LOG_WINDOWS_H
 #define GRPC_SUPPORT_LOG_WINDOWS_H
 
+#include <grpc/impl/codegen/port_platform.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/package.json b/package.json
index 5506e9bbdf8deaba326801fb5c635bae7608ea71..375db74849f8f047278a5d7597ce5b89f200ae29 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
   "scripts": {
     "lint": "node ./node_modules/jshint/bin/jshint src/node/src src/node/test src/node/interop src/node/index.js --exclude-path=src/node/.jshintignore",
     "test": "./node_modules/.bin/mocha src/node/test && npm run-script lint",
+    "electron-build": "./node_modules/.bin/node-pre-gyp configure build --runtime=electron --disturl=https://atom.io/download/atom-shell",
     "gen_docs": "./node_modules/.bin/jsdoc -c src/node/jsdoc_conf.json",
     "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha src/node/test",
     "install": "./node_modules/.bin/node-pre-gyp install --fallback-to-build"
@@ -38,6 +39,7 @@
   "devDependencies": {
     "async": "^2.0.1",
     "body-parser": "^1.15.2",
+    "electron-mocha": "^3.1.1",
     "express": "^4.14.0",
     "google-auth-library": "^0.9.2",
     "google-protobuf": "^3.0.0",
@@ -50,7 +52,7 @@
     "poisson-process": "^0.2.1"
   },
   "engines": {
-    "node": ">=0.12.0"
+    "node": ">=1.1.0"
   },
   "binary": {
     "module_name": "grpc_node",
diff --git a/package.xml b/package.xml
index 2822ac624d7b4f8f63d95460f844602c25a1fcd4..4df15258275440d4b006d7579ea5e46ada3cbcfa 100644
--- a/package.xml
+++ b/package.xml
@@ -156,6 +156,7 @@
     <file baseinstalldir="/" name="include/grpc/impl/codegen/byte_buffer_reader.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/compression_types.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/connectivity_state.h" role="src" />
+    <file baseinstalldir="/" name="include/grpc/impl/codegen/exec_ctx_fwd.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/grpc_types.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/propagation_bits.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/status.h" role="src" />
@@ -180,6 +181,8 @@
     <file baseinstalldir="/" name="src/core/lib/channel/context.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/deadline_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/handshaker.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/channel/handshaker_factory.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/channel/handshaker_registry.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/http_client_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/http_server_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/message_size_filter.h" role="src" />
@@ -246,6 +249,7 @@
     <file baseinstalldir="/" name="src/core/lib/json/json_reader.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/json/json_writer.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/slice/percent_encoding.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/slice/slice_internal.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/slice/slice_string_helpers.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/surface/api_trace.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/surface/call.h" role="src" />
@@ -303,9 +307,9 @@
     <file baseinstalldir="/" name="src/core/lib/security/credentials/plugin/plugin_credentials.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/credentials/ssl/ssl_credentials.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/transport/auth_filters.h" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/transport/handshake.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/transport/secure_endpoint.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/transport/security_connector.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/transport/security_handshaker.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/transport/tsi_error.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/util/b64.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/util/json_util.h" role="src" />
@@ -314,6 +318,7 @@
     <file baseinstalldir="/" name="src/core/lib/tsi/ssl_types.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/tsi/transport_security.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/tsi/transport_security_interface.h" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/server/chttp2_server.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_channel/client_channel.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_channel/client_channel_factory.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_channel/connector.h" role="src" />
@@ -329,6 +334,7 @@
     <file baseinstalldir="/" name="src/core/ext/client_channel/subchannel.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_channel/subchannel_index.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_channel/uri_parser.h" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/chttp2_connector.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/lb_policy/grpclb/grpclb.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/lb_policy/grpclb/load_balancer_api.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h" role="src" />
@@ -357,6 +363,8 @@
     <file baseinstalldir="/" name="src/core/lib/channel/connected_channel.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/deadline_filter.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/handshaker.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/channel/handshaker_factory.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/channel/handshaker_registry.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/http_client_filter.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/http_server_filter.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/message_size_filter.c" role="src" />
@@ -501,9 +509,9 @@
     <file baseinstalldir="/" name="src/core/lib/security/credentials/plugin/plugin_credentials.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/credentials/ssl/ssl_credentials.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/transport/client_auth_filter.c" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/transport/handshake.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/transport/secure_endpoint.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/transport/security_connector.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/transport/security_handshaker.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/transport/server_auth_filter.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/transport/tsi_error.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/util/b64.c" role="src" />
@@ -512,6 +520,7 @@
     <file baseinstalldir="/" name="src/core/lib/tsi/fake_transport_security.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/tsi/ssl_transport_security.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/tsi/transport_security.c" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/server/chttp2_server.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/secure/secure_channel_create.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_channel/channel_connectivity.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_channel/client_channel.c" role="src" />
@@ -531,6 +540,7 @@
     <file baseinstalldir="/" name="src/core/ext/client_channel/subchannel.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_channel/subchannel_index.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_channel/uri_parser.c" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/chttp2_connector.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/server/insecure/server_chttp2.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/insecure/channel_create.c" role="src" />
diff --git a/setup.py b/setup.py
index 559a75f674257e387c89621e9efb159bb84fe4f2..ab217433a3b0d37c89e5284ed0d7dd970869f87c 100644
--- a/setup.py
+++ b/setup.py
@@ -218,15 +218,18 @@ SETUP_REQUIRES = INSTALL_REQUIRES + (
     'six>=1.10',
   ) if ENABLE_DOCUMENTATION_BUILD else ()
 
-if BUILD_WITH_CYTHON:
-  sys.stderr.write(
-    "You requested a Cython build via GRPC_PYTHON_BUILD_WITH_CYTHON, "
-    "but do not have Cython installed. We won't stop you from using "
-    "other commands, but the extension files will fail to build.\n")
-elif need_cython:
-  sys.stderr.write(
-      'We could not find Cython. Setup may take 10-20 minutes.\n')
-  SETUP_REQUIRES += ('cython>=0.23',)
+try:
+  import Cython
+except ImportError:
+  if BUILD_WITH_CYTHON:
+    sys.stderr.write(
+      "You requested a Cython build via GRPC_PYTHON_BUILD_WITH_CYTHON, "
+      "but do not have Cython installed. We won't stop you from using "
+      "other commands, but the extension files will fail to build.\n")
+  elif need_cython:
+    sys.stderr.write(
+        'We could not find Cython. Setup may take 10-20 minutes.\n')
+    SETUP_REQUIRES += ('cython>=0.23',)
 
 COMMAND_CLASS = {
     'doc': commands.SphinxDocumentation,
diff --git a/src/google_benchmark/gen_build_yaml.py b/src/benchmark/gen_build_yaml.py
similarity index 86%
rename from src/google_benchmark/gen_build_yaml.py
rename to src/benchmark/gen_build_yaml.py
index 302e08737aff92192d2b9f3e32d52d8d23b29ec4..09b76115a8662e110e9a30a23fa28370372f51f4 100755
--- a/src/google_benchmark/gen_build_yaml.py
+++ b/src/benchmark/gen_build_yaml.py
@@ -39,15 +39,15 @@ os.chdir(os.path.dirname(sys.argv[0])+'/../..')
 out = {}
 
 out['libs'] = [{
-    'name': 'google_benchmark',
+    'name': 'benchmark',
     'build': 'private',
     'language': 'c++',
     'secure': 'no',
-    'defaults': 'google_benchmark',
-    'src': sorted(glob.glob('third_party/google_benchmark/src/*.cc')),
+    'defaults': 'benchmark',
+    'src': sorted(glob.glob('third_party/benchmark/src/*.cc')),
     'headers': sorted(
-        glob.glob('third_party/google_benchmark/src/*.h') +
-        glob.glob('third_party/google_benchmark/include/benchmark/*.h')),
+        glob.glob('third_party/benchmark/src/*.h') +
+        glob.glob('third_party/benchmark/include/benchmark/*.h')),
 }]
 
 print yaml.dump(out)
diff --git a/src/compiler/csharp_generator.cc b/src/compiler/csharp_generator.cc
index a3af258d9c36b20bd1e36c21538cd48618046660..cc7a7a96aeab5c31256a2891c362d91446fb8d96 100644
--- a/src/compiler/csharp_generator.cc
+++ b/src/compiler/csharp_generator.cc
@@ -68,13 +68,13 @@ namespace {
 // Currently, we cannot easily reuse the functionality as
 // google/protobuf/compiler/csharp/csharp_doc_comment.h is not a public header.
 // TODO(jtattermusch): reuse the functionality from google/protobuf.
-void GenerateDocCommentBodyImpl(grpc::protobuf::io::Printer *printer,
+bool GenerateDocCommentBodyImpl(grpc::protobuf::io::Printer *printer,
                                 grpc::protobuf::SourceLocation location) {
   grpc::string comments = location.leading_comments.empty()
                               ? location.trailing_comments
                               : location.leading_comments;
   if (comments.empty()) {
-    return;
+    return false;
   }
   // XML escaping... no need for apostrophes etc as the whole text is going to
   // be a child
@@ -107,18 +107,84 @@ void GenerateDocCommentBodyImpl(grpc::protobuf::io::Printer *printer,
         printer->Print("///\n");
       }
       last_was_empty = false;
-      printer->Print("/// $line$\n", "line", *it);
+      printer->Print("///$line$\n", "line", *it);
     }
   }
   printer->Print("/// </summary>\n");
+  return true;
 }
 
 template <typename DescriptorType>
-void GenerateDocCommentBody(grpc::protobuf::io::Printer *printer,
+bool GenerateDocCommentBody(grpc::protobuf::io::Printer *printer,
                             const DescriptorType *descriptor) {
   grpc::protobuf::SourceLocation location;
-  if (descriptor->GetSourceLocation(&location)) {
-    GenerateDocCommentBodyImpl(printer, location);
+  if (!descriptor->GetSourceLocation(&location)) {
+    return false;
+  }
+  return GenerateDocCommentBodyImpl(printer, location);
+}
+
+void GenerateDocCommentServerMethod(grpc::protobuf::io::Printer *printer,
+                                    const MethodDescriptor *method) {
+  if (GenerateDocCommentBody(printer, method)) {
+    if (method->client_streaming()) {
+      printer->Print(
+          "/// <param name=\"requestStream\">Used for reading requests from "
+          "the client.</param>\n");
+    } else {
+      printer->Print(
+          "/// <param name=\"request\">The request received from the "
+          "client.</param>\n");
+    }
+    if (method->server_streaming()) {
+      printer->Print(
+          "/// <param name=\"responseStream\">Used for sending responses back "
+          "to the client.</param>\n");
+    }
+    printer->Print(
+        "/// <param name=\"context\">The context of the server-side call "
+        "handler being invoked.</param>\n");
+    if (method->server_streaming()) {
+      printer->Print(
+          "/// <returns>A task indicating completion of the "
+          "handler.</returns>\n");
+    } else {
+      printer->Print(
+          "/// <returns>The response to send back to the client (wrapped by a "
+          "task).</returns>\n");
+    }
+  }
+}
+
+void GenerateDocCommentClientMethod(grpc::protobuf::io::Printer *printer,
+                                    const MethodDescriptor *method,
+                                    bool is_sync, bool use_call_options) {
+  if (GenerateDocCommentBody(printer, method)) {
+    if (!method->client_streaming()) {
+      printer->Print(
+          "/// <param name=\"request\">The request to send to the "
+          "server.</param>\n");
+    }
+    if (!use_call_options) {
+      printer->Print(
+          "/// <param name=\"headers\">The initial metadata to send with the "
+          "call. This parameter is optional.</param>\n");
+      printer->Print(
+          "/// <param name=\"deadline\">An optional deadline for the call. The "
+          "call will be cancelled if deadline is hit.</param>\n");
+      printer->Print(
+          "/// <param name=\"cancellationToken\">An optional token for "
+          "canceling the call.</param>\n");
+    } else {
+      printer->Print(
+          "/// <param name=\"options\">The options for the call.</param>\n");
+    }
+    if (is_sync) {
+      printer->Print(
+          "/// <returns>The response received from the server.</returns>\n");
+    } else {
+      printer->Print("/// <returns>The call object.</returns>\n");
+    }
   }
 }
 
@@ -319,7 +385,7 @@ void GenerateServerClass(Printer *out, const ServiceDescriptor *service) {
   out->Indent();
   for (int i = 0; i < service->method_count(); i++) {
     const MethodDescriptor *method = service->method(i);
-    GenerateDocCommentBody(out, method);
+    GenerateDocCommentServerMethod(out, method);
     out->Print(
         "public virtual $returntype$ "
         "$methodname$($request$$response_stream_maybe$, "
@@ -393,7 +459,7 @@ void GenerateClientStub(Printer *out, const ServiceDescriptor *service) {
 
     if (method_type == METHODTYPE_NO_STREAMING) {
       // unary calls have an extra synchronous stub method
-      GenerateDocCommentBody(out, method);
+      GenerateDocCommentClientMethod(out, method, true, false);
       out->Print(
           "public virtual $response$ $methodname$($request$ request, Metadata "
           "headers = null, DateTime? deadline = null, CancellationToken "
@@ -411,7 +477,7 @@ void GenerateClientStub(Printer *out, const ServiceDescriptor *service) {
       out->Print("}\n");
 
       // overload taking CallOptions as a param
-      GenerateDocCommentBody(out, method);
+      GenerateDocCommentClientMethod(out, method, true, true);
       out->Print(
           "public virtual $response$ $methodname$($request$ request, "
           "CallOptions options)\n",
@@ -432,7 +498,7 @@ void GenerateClientStub(Printer *out, const ServiceDescriptor *service) {
     if (method_type == METHODTYPE_NO_STREAMING) {
       method_name += "Async";  // prevent name clash with synchronous method.
     }
-    GenerateDocCommentBody(out, method);
+    GenerateDocCommentClientMethod(out, method, false, false);
     out->Print(
         "public virtual $returntype$ $methodname$($request_maybe$Metadata "
         "headers = null, DateTime? deadline = null, CancellationToken "
@@ -452,7 +518,7 @@ void GenerateClientStub(Printer *out, const ServiceDescriptor *service) {
     out->Print("}\n");
 
     // overload taking CallOptions as a param
-    GenerateDocCommentBody(out, method);
+    GenerateDocCommentClientMethod(out, method, false, true);
     out->Print(
         "public virtual $returntype$ $methodname$($request_maybe$CallOptions "
         "options)\n",
@@ -517,6 +583,9 @@ void GenerateBindServiceMethod(Printer *out, const ServiceDescriptor *service) {
   out->Print(
       "/// <summary>Creates service definition that can be registered with a "
       "server</summary>\n");
+  out->Print(
+      "/// <param name=\"serviceImpl\">An object implementing the server-side"
+      " handling logic.</param>\n");
   out->Print(
       "public static ServerServiceDefinition BindService($implclass$ "
       "serviceImpl)\n",
diff --git a/src/compiler/python_generator.cc b/src/compiler/python_generator.cc
index febaf135b6fd4906149da49e2897ca8c9e5ec0bf..4841da8da8e8529f85966081e9d193b1b8bc2451 100644
--- a/src/compiler/python_generator.cc
+++ b/src/compiler/python_generator.cc
@@ -40,6 +40,7 @@
 #include <map>
 #include <memory>
 #include <ostream>
+#include <set>
 #include <sstream>
 #include <tuple>
 #include <vector>
@@ -64,7 +65,9 @@ using std::make_pair;
 using std::map;
 using std::pair;
 using std::replace;
+using std::tuple;
 using std::vector;
+using std::set;
 
 namespace grpc_python_generator {
 
@@ -73,6 +76,8 @@ namespace {
 typedef vector<const Descriptor*> DescriptorVector;
 typedef map<grpc::string, grpc::string> StringMap;
 typedef vector<grpc::string> StringVector;
+typedef tuple<grpc::string, grpc::string> StringPair;
+typedef set<StringPair> StringPairSet;
 
 // Provides RAII indentation handling. Use as:
 // {
@@ -651,6 +656,7 @@ bool PrivateGenerator::PrintPreamble() {
       "face_utilities\n");
   if (generate_in_pb2_grpc) {
     out->Print("\n");
+    StringPairSet imports_set;
     for (int i = 0; i < file->service_count(); ++i) {
       const ServiceDescriptor* service = file->service(i);
       for (int j = 0; j < service->method_count(); ++j) {
@@ -662,11 +668,15 @@ bool PrivateGenerator::PrintPreamble() {
           grpc::string type_file_name = type->file()->name();
           grpc::string module_name = ModuleName(type_file_name);
           grpc::string module_alias = ModuleAlias(type_file_name);
-          out->Print("import $ModuleName$ as $ModuleAlias$\n", "ModuleName",
-                     module_name, "ModuleAlias", module_alias);
+          imports_set.insert(std::make_tuple(module_name, module_alias));
         }
       }
     }
+    for (StringPairSet::iterator it = imports_set.begin();
+         it != imports_set.end(); ++it) {
+      out->Print("import $ModuleName$ as $ModuleAlias$\n", "ModuleName",
+                 std::get<0>(*it), "ModuleAlias", std::get<1>(*it));
+    }
   }
   return true;
 }
@@ -714,6 +724,9 @@ pair<bool, grpc::string> PrivateGenerator::GetGrpcServices() {
     out = &out_printer;
 
     if (generate_in_pb2_grpc) {
+      out->Print(
+          "# Generated by the gRPC Python protocol compiler plugin. "
+          "DO NOT EDIT!\n");
       if (!PrintPreamble()) {
         return make_pair(false, "");
       }
@@ -760,6 +773,32 @@ PythonGrpcGenerator::PythonGrpcGenerator(const GeneratorConfiguration& config)
 
 PythonGrpcGenerator::~PythonGrpcGenerator() {}
 
+static bool GenerateGrpc(GeneratorContext* context, PrivateGenerator& generator,
+                         grpc::string file_name, bool generate_in_pb2_grpc) {
+  bool success;
+  std::unique_ptr<ZeroCopyOutputStream> output;
+  std::unique_ptr<CodedOutputStream> coded_output;
+  grpc::string grpc_code;
+
+  if (generate_in_pb2_grpc) {
+    output.reset(context->Open(file_name));
+    generator.generate_in_pb2_grpc = true;
+  } else {
+    output.reset(context->OpenForInsert(file_name, "module_scope"));
+    generator.generate_in_pb2_grpc = false;
+  }
+
+  coded_output.reset(new CodedOutputStream(output.get()));
+  tie(success, grpc_code) = generator.GetGrpcServices();
+
+  if (success) {
+    coded_output->WriteRaw(grpc_code.data(), grpc_code.size());
+    return true;
+  } else {
+    return false;
+  }
+}
+
 bool PythonGrpcGenerator::Generate(const FileDescriptor* file,
                                    const grpc::string& parameter,
                                    GeneratorContext* context,
@@ -780,28 +819,15 @@ bool PythonGrpcGenerator::Generate(const FileDescriptor* file,
   }
 
   PrivateGenerator generator(config_, file);
-
-  std::unique_ptr<ZeroCopyOutputStream> pb2_output(
-      context->OpenForAppend(pb2_file_name));
-  std::unique_ptr<ZeroCopyOutputStream> grpc_output(
-      context->Open(pb2_grpc_file_name));
-  CodedOutputStream pb2_coded_out(pb2_output.get());
-  CodedOutputStream grpc_coded_out(grpc_output.get());
-  bool success = false;
-  grpc::string pb2_code;
-  grpc::string grpc_code;
-  generator.generate_in_pb2_grpc = false;
-  tie(success, pb2_code) = generator.GetGrpcServices();
-  if (success) {
-    generator.generate_in_pb2_grpc = true;
-    tie(success, grpc_code) = generator.GetGrpcServices();
-    if (success) {
-      pb2_coded_out.WriteRaw(pb2_code.data(), pb2_code.size());
-      grpc_coded_out.WriteRaw(grpc_code.data(), grpc_code.size());
-      return true;
-    }
+  if (parameter == "grpc_2_0") {
+    return GenerateGrpc(context, generator, pb2_grpc_file_name, true);
+  } else if (parameter == "") {
+    return GenerateGrpc(context, generator, pb2_grpc_file_name, true) &&
+           GenerateGrpc(context, generator, pb2_file_name, false);
+  } else {
+    *error = "Invalid parameter '" + parameter + "'.";
+    return false;
   }
-  return false;
 }
 
 }  // namespace grpc_python_generator
diff --git a/src/core/ext/README.md b/src/core/ext/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..0812b20823c55edd47657e8e37356097ec4c37ce
--- /dev/null
+++ b/src/core/ext/README.md
@@ -0,0 +1,5 @@
+Optional plugins for gRPC Core: Modules in this directory extend gRPC Core in
+useful ways. All optional code belongs here.
+
+NOTE: The movement of code between lib and ext is an ongoing effort, so this
+directory currently contains too much of the core library.
diff --git a/src/core/ext/census/grpc_filter.c b/src/core/ext/census/grpc_filter.c
index 397dbc40a871fa5b24bd984cf5223a38f481960b..8e4d4732b828de056b11c23ed2af9e7c7603a7b7 100644
--- a/src/core/ext/census/grpc_filter.c
+++ b/src/core/ext/census/grpc_filter.c
@@ -154,7 +154,8 @@ static grpc_error *server_init_call_elem(grpc_exec_ctx *exec_ctx,
   memset(d, 0, sizeof(*d));
   d->start_ts = args->start_time;
   /* TODO(hongyu): call census_tracing_start_op here. */
-  grpc_closure_init(&d->finish_recv, server_on_done_recv, elem);
+  grpc_closure_init(&d->finish_recv, server_on_done_recv, elem,
+                    grpc_schedule_on_exec_ctx);
   return GRPC_ERROR_NONE;
 }
 
@@ -167,11 +168,12 @@ static void server_destroy_call_elem(grpc_exec_ctx *exec_ctx,
   /* TODO(hongyu): record rpc server stats and census_tracing_end_op here */
 }
 
-static void init_channel_elem(grpc_exec_ctx *exec_ctx,
-                              grpc_channel_element *elem,
-                              grpc_channel_element_args *args) {
+static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx,
+                                     grpc_channel_element *elem,
+                                     grpc_channel_element_args *args) {
   channel_data *chand = elem->channel_data;
   GPR_ASSERT(chand != NULL);
+  return GRPC_ERROR_NONE;
 }
 
 static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/ext/census/grpc_plugin.c b/src/core/ext/census/grpc_plugin.c
index e43ceafd0c9483bcf1b09618a78d4fc2ff23c491..c9fe453af867e533d6c925a9d6fa1ec3eaae1fee 100644
--- a/src/core/ext/census/grpc_plugin.c
+++ b/src/core/ext/census/grpc_plugin.c
@@ -51,7 +51,8 @@ static bool is_census_enabled(const grpc_channel_args *a) {
   return census_enabled();
 }
 
-static bool maybe_add_census_filter(grpc_channel_stack_builder *builder,
+static bool maybe_add_census_filter(grpc_exec_ctx *exec_ctx,
+                                    grpc_channel_stack_builder *builder,
                                     void *arg) {
   const grpc_channel_args *args =
       grpc_channel_stack_builder_get_channel_arguments(builder);
diff --git a/src/core/ext/census/tracing.c b/src/core/ext/census/tracing.c
index 51508b00efea25af9517cad128c1ce63487a0ef0..afb70441d28b359b43b9338915752165a6be9862 100644
--- a/src/core/ext/census/tracing.c
+++ b/src/core/ext/census/tracing.c
@@ -34,7 +34,6 @@
 //#include "src/core/ext/census/tracing.h"
 
 #include <stdlib.h>
-
 #include <grpc/census.h>
 
 /* TODO(aveitch): These are all placeholder implementations. */
@@ -50,5 +49,3 @@ void census_trace_print(census_context *context, uint32_t type,
                         const char *buffer, size_t n) {
   abort();
 }
-
-// void SetTracerParams(const Params& params);
diff --git a/src/core/ext/client_channel/channel_connectivity.c b/src/core/ext/client_channel/channel_connectivity.c
index 9797e665643b24aa9e1b67d343cbdbaf30233588..dd70bc2c6c8997b075514f9641b37fa151daa6c1 100644
--- a/src/core/ext/client_channel/channel_connectivity.c
+++ b/src/core/ext/client_channel/channel_connectivity.c
@@ -76,6 +76,7 @@ typedef struct {
   gpr_mu mu;
   callback_phase phase;
   grpc_closure on_complete;
+  grpc_closure on_timeout;
   grpc_timer alarm;
   grpc_connectivity_state state;
   grpc_completion_queue *cq;
@@ -198,7 +199,10 @@ void grpc_channel_watch_connectivity_state(
   grpc_cq_begin_op(cq, tag);
 
   gpr_mu_init(&w->mu);
-  grpc_closure_init(&w->on_complete, watch_complete, w);
+  grpc_closure_init(&w->on_complete, watch_complete, w,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&w->on_timeout, timeout_complete, w,
+                    grpc_schedule_on_exec_ctx);
   w->phase = WAITING;
   w->state = last_observed_state;
   w->cq = cq;
@@ -207,7 +211,7 @@ void grpc_channel_watch_connectivity_state(
 
   grpc_timer_init(&exec_ctx, &w->alarm,
                   gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC),
-                  timeout_complete, w, gpr_now(GPR_CLOCK_MONOTONIC));
+                  &w->on_timeout, gpr_now(GPR_CLOCK_MONOTONIC));
 
   if (client_channel_elem->filter == &grpc_client_channel_filter) {
     GRPC_CHANNEL_INTERNAL_REF(channel, "watch_channel_connectivity");
diff --git a/src/core/ext/client_channel/client_channel.c b/src/core/ext/client_channel/client_channel.c
index 1fcff4388a55b582fea7549ae92f20cf90c6d6ec..2f25fef9a741bd44e3a51f1b81087836de304a5f 100644
--- a/src/core/ext/client_channel/client_channel.c
+++ b/src/core/ext/client_channel/client_channel.c
@@ -44,6 +44,7 @@
 #include <grpc/support/useful.h>
 
 #include "src/core/ext/client_channel/lb_policy_registry.h"
+#include "src/core/ext/client_channel/resolver_registry.h"
 #include "src/core/ext/client_channel/subchannel.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/connected_channel.h"
@@ -82,8 +83,12 @@ static void *method_parameters_copy(void *value) {
   return new_value;
 }
 
+static void method_parameters_free(grpc_exec_ctx *exec_ctx, void *p) {
+  gpr_free(p);
+}
+
 static const grpc_mdstr_hash_table_vtable method_parameters_vtable = {
-    gpr_free, method_parameters_copy};
+    method_parameters_free, method_parameters_copy};
 
 static void *method_parameters_create_from_json(const grpc_json *json) {
   wait_for_ready_value wait_for_ready = WAIT_FOR_READY_UNSET;
@@ -248,7 +253,8 @@ static void watch_lb_policy(grpc_exec_ctx *exec_ctx, channel_data *chand,
   GRPC_CHANNEL_STACK_REF(chand->owning_stack, "watch_lb_policy");
 
   w->chand = chand;
-  grpc_closure_init(&w->on_changed, on_lb_policy_state_changed, w);
+  grpc_closure_init(&w->on_changed, on_lb_policy_state_changed, w,
+                    grpc_schedule_on_exec_ctx);
   w->state = current_state;
   w->lb_policy = lb_policy;
   grpc_lb_policy_notify_on_state_change(exec_ctx, lb_policy, &w->state,
@@ -326,7 +332,7 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg,
           grpc_service_config_create(service_config_json);
       if (service_config != NULL) {
         method_params_table = grpc_service_config_create_method_config_table(
-            service_config, method_parameters_create_from_json,
+            exec_ctx, service_config, method_parameters_create_from_json,
             &method_parameters_vtable);
         grpc_service_config_destroy(service_config);
       }
@@ -335,7 +341,7 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg,
     // be pointing to data inside chand->resolver_result.
     // The copy will be saved in chand->lb_policy_name below.
     lb_policy_name = gpr_strdup(lb_policy_name);
-    grpc_channel_args_destroy(chand->resolver_result);
+    grpc_channel_args_destroy(exec_ctx, chand->resolver_result);
     chand->resolver_result = NULL;
   }
 
@@ -356,18 +362,16 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg,
     chand->service_config_json = service_config_json;
   }
   if (chand->method_params_table != NULL) {
-    grpc_mdstr_hash_table_unref(chand->method_params_table);
+    grpc_mdstr_hash_table_unref(exec_ctx, chand->method_params_table);
   }
   chand->method_params_table = method_params_table;
   if (lb_policy != NULL) {
-    grpc_exec_ctx_enqueue_list(exec_ctx, &chand->waiting_for_config_closures,
-                               NULL);
+    grpc_closure_list_sched(exec_ctx, &chand->waiting_for_config_closures);
   } else if (chand->resolver == NULL /* disconnected */) {
     grpc_closure_list_fail_all(
         &chand->waiting_for_config_closures,
         GRPC_ERROR_CREATE_REFERENCING("Channel disconnected", &error, 1));
-    grpc_exec_ctx_enqueue_list(exec_ctx, &chand->waiting_for_config_closures,
-                               NULL);
+    grpc_closure_list_sched(exec_ctx, &chand->waiting_for_config_closures);
   }
   if (lb_policy != NULL && chand->exit_idle_when_lb_policy_arrives) {
     GRPC_LB_POLICY_REF(lb_policy, "exit_idle");
@@ -424,7 +428,7 @@ static void cc_start_transport_op(grpc_exec_ctx *exec_ctx,
                                   grpc_transport_op *op) {
   channel_data *chand = elem->channel_data;
 
-  grpc_exec_ctx_sched(exec_ctx, op->on_consumed, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, op->on_consumed, GRPC_ERROR_NONE);
 
   GPR_ASSERT(op->set_accept_stream == false);
   if (op->bind_pollset != NULL) {
@@ -443,9 +447,8 @@ static void cc_start_transport_op(grpc_exec_ctx *exec_ctx,
 
   if (op->send_ping != NULL) {
     if (chand->lb_policy == NULL) {
-      grpc_exec_ctx_sched(exec_ctx, op->send_ping,
-                          GRPC_ERROR_CREATE("Ping with no load balancing"),
-                          NULL);
+      grpc_closure_sched(exec_ctx, op->send_ping,
+                         GRPC_ERROR_CREATE("Ping with no load balancing"));
     } else {
       grpc_lb_policy_ping_one(exec_ctx, chand->lb_policy, op->send_ping);
       op->bind_pollset = NULL;
@@ -464,8 +467,7 @@ static void cc_start_transport_op(grpc_exec_ctx *exec_ctx,
       if (!chand->started_resolving) {
         grpc_closure_list_fail_all(&chand->waiting_for_config_closures,
                                    GRPC_ERROR_REF(op->disconnect_with_error));
-        grpc_exec_ctx_enqueue_list(exec_ctx,
-                                   &chand->waiting_for_config_closures, NULL);
+        grpc_closure_list_sched(exec_ctx, &chand->waiting_for_config_closures);
       }
       if (chand->lb_policy != NULL) {
         grpc_pollset_set_del_pollset_set(exec_ctx,
@@ -499,24 +501,40 @@ static void cc_get_channel_info(grpc_exec_ctx *exec_ctx,
 }
 
 /* Constructor for channel_data */
-static void cc_init_channel_elem(grpc_exec_ctx *exec_ctx,
-                                 grpc_channel_element *elem,
-                                 grpc_channel_element_args *args) {
+static grpc_error *cc_init_channel_elem(grpc_exec_ctx *exec_ctx,
+                                        grpc_channel_element *elem,
+                                        grpc_channel_element_args *args) {
   channel_data *chand = elem->channel_data;
-
   memset(chand, 0, sizeof(*chand));
-
   GPR_ASSERT(args->is_last);
   GPR_ASSERT(elem->filter == &grpc_client_channel_filter);
-
+  // Initialize data members.
   gpr_mu_init(&chand->mu);
-  grpc_closure_init(&chand->on_resolver_result_changed,
-                    on_resolver_result_changed, chand);
   chand->owning_stack = args->channel_stack;
-
+  grpc_closure_init(&chand->on_resolver_result_changed,
+                    on_resolver_result_changed, chand,
+                    grpc_schedule_on_exec_ctx);
+  chand->interested_parties = grpc_pollset_set_create();
   grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE,
                                "client_channel");
-  chand->interested_parties = grpc_pollset_set_create();
+  // Record client channel factory.
+  const grpc_arg *arg = grpc_channel_args_find(args->channel_args,
+                                               GRPC_ARG_CLIENT_CHANNEL_FACTORY);
+  GPR_ASSERT(arg != NULL);
+  GPR_ASSERT(arg->type == GRPC_ARG_POINTER);
+  grpc_client_channel_factory_ref(arg->value.pointer.p);
+  chand->client_channel_factory = arg->value.pointer.p;
+  // Instantiate resolver.
+  arg = grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVER_URI);
+  GPR_ASSERT(arg != NULL);
+  GPR_ASSERT(arg->type == GRPC_ARG_STRING);
+  chand->resolver =
+      grpc_resolver_create(exec_ctx, arg->value.string, args->channel_args,
+                           chand->interested_parties);
+  if (chand->resolver == NULL) {
+    return GRPC_ERROR_CREATE("resolver creation failed");
+  }
+  return GRPC_ERROR_NONE;
 }
 
 /* Destructor for channel_data */
@@ -540,7 +558,7 @@ static void cc_destroy_channel_elem(grpc_exec_ctx *exec_ctx,
   gpr_free(chand->lb_policy_name);
   gpr_free(chand->service_config_json);
   if (chand->method_params_table != NULL) {
-    grpc_mdstr_hash_table_unref(chand->method_params_table);
+    grpc_mdstr_hash_table_unref(exec_ctx, chand->method_params_table);
   }
   grpc_connectivity_state_destroy(exec_ctx, &chand->state_tracker);
   grpc_pollset_set_destroy(chand->interested_parties);
@@ -662,8 +680,9 @@ static void retry_waiting_locked(grpc_exec_ctx *exec_ctx, call_data *calld) {
   calld->waiting_ops_count = 0;
   calld->waiting_ops_capacity = 0;
   GRPC_SUBCHANNEL_CALL_REF(a->call, "retry_ops");
-  grpc_exec_ctx_sched(exec_ctx, grpc_closure_create(retry_ops, a),
-                      GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(
+      exec_ctx, grpc_closure_create(retry_ops, a, grpc_schedule_on_exec_ctx),
+      GRPC_ERROR_NONE);
 }
 
 static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *arg,
@@ -683,9 +702,15 @@ static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *arg,
                                      "Failed to create subchannel", &error, 1));
   } else if (GET_CALL(calld) == CANCELLED_CALL) {
     /* already cancelled before subchannel became ready */
-    fail_locked(exec_ctx, calld,
-                GRPC_ERROR_CREATE_REFERENCING(
-                    "Cancelled before creating subchannel", &error, 1));
+    grpc_error *cancellation_error = GRPC_ERROR_CREATE_REFERENCING(
+        "Cancelled before creating subchannel", &error, 1);
+    /* if due to deadline, attach the deadline exceeded status to the error */
+    if (gpr_time_cmp(calld->deadline, gpr_now(GPR_CLOCK_MONOTONIC)) < 0) {
+      cancellation_error =
+          grpc_error_set_int(cancellation_error, GRPC_ERROR_INT_GRPC_STATUS,
+                             GRPC_STATUS_DEADLINE_EXCEEDED);
+    }
+    fail_locked(exec_ctx, calld, cancellation_error);
   } else {
     /* Create call on subchannel. */
     grpc_subchannel_call *subchannel_call = NULL;
@@ -739,14 +764,14 @@ static void continue_picking(grpc_exec_ctx *exec_ctx, void *arg,
   if (cpa->connected_subchannel == NULL) {
     /* cancelled, do nothing */
   } else if (error != GRPC_ERROR_NONE) {
-    grpc_exec_ctx_sched(exec_ctx, cpa->on_ready, GRPC_ERROR_REF(error), NULL);
+    grpc_closure_sched(exec_ctx, cpa->on_ready, GRPC_ERROR_REF(error));
   } else {
     call_data *calld = cpa->elem->call_data;
     gpr_mu_lock(&calld->mu);
     if (pick_subchannel(exec_ctx, cpa->elem, cpa->initial_metadata,
                         cpa->initial_metadata_flags, cpa->connected_subchannel,
                         cpa->on_ready, GRPC_ERROR_NONE)) {
-      grpc_exec_ctx_sched(exec_ctx, cpa->on_ready, GRPC_ERROR_NONE, NULL);
+      grpc_closure_sched(exec_ctx, cpa->on_ready, GRPC_ERROR_NONE);
     }
     gpr_mu_unlock(&calld->mu);
   }
@@ -778,9 +803,9 @@ static bool pick_subchannel(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
       cpa = closure->cb_arg;
       if (cpa->connected_subchannel == connected_subchannel) {
         cpa->connected_subchannel = NULL;
-        grpc_exec_ctx_sched(
+        grpc_closure_sched(
             exec_ctx, cpa->on_ready,
-            GRPC_ERROR_CREATE_REFERENCING("Pick cancelled", &error, 1), NULL);
+            GRPC_ERROR_CREATE_REFERENCING("Pick cancelled", &error, 1));
       }
     }
     gpr_mu_unlock(&chand->mu);
@@ -809,7 +834,6 @@ static bool pick_subchannel(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
         initial_metadata_flags &= ~GRPC_INITIAL_METADATA_WAIT_FOR_READY;
       }
     }
-    // TODO(dgq): make this deadline configurable somehow.
     const grpc_lb_policy_pick_args inputs = {
         initial_metadata, initial_metadata_flags, &calld->lb_token_mdelem,
         gpr_inf_future(GPR_CLOCK_MONOTONIC)};
@@ -832,12 +856,12 @@ static bool pick_subchannel(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
     cpa->connected_subchannel = connected_subchannel;
     cpa->on_ready = on_ready;
     cpa->elem = elem;
-    grpc_closure_init(&cpa->closure, continue_picking, cpa);
+    grpc_closure_init(&cpa->closure, continue_picking, cpa,
+                      grpc_schedule_on_exec_ctx);
     grpc_closure_list_append(&chand->waiting_for_config_closures, &cpa->closure,
                              GRPC_ERROR_NONE);
   } else {
-    grpc_exec_ctx_sched(exec_ctx, on_ready, GRPC_ERROR_CREATE("Disconnected"),
-                        NULL);
+    grpc_closure_sched(exec_ctx, on_ready, GRPC_ERROR_CREATE("Disconnected"));
   }
   gpr_mu_unlock(&chand->mu);
 
@@ -922,7 +946,8 @@ retry:
       calld->connected_subchannel == NULL &&
       op->send_initial_metadata != NULL) {
     calld->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL;
-    grpc_closure_init(&calld->next_step, subchannel_ready, elem);
+    grpc_closure_init(&calld->next_step, subchannel_ready, elem,
+                      grpc_schedule_on_exec_ctx);
     GRPC_CALL_STACK_REF(calld->owning_call, "pick_subchannel");
     /* If a subchannel is not available immediately, the polling entity from
        call_data should be provided to channel_data's interested_parties, so
@@ -980,8 +1005,8 @@ static void read_service_config(grpc_exec_ctx *exec_ctx, void *arg,
     gpr_mu_unlock(&chand->mu);
     // If the method config table was present, use it.
     if (method_params_table != NULL) {
-      const method_parameters *method_params =
-          grpc_method_config_table_get(method_params_table, calld->path);
+      const method_parameters *method_params = grpc_method_config_table_get(
+          exec_ctx, method_params_table, calld->path);
       if (method_params != NULL) {
         const bool have_method_timeout =
             gpr_time_cmp(method_params->timeout, gpr_time_0(GPR_TIMESPAN)) != 0;
@@ -1004,7 +1029,7 @@ static void read_service_config(grpc_exec_ctx *exec_ctx, void *arg,
           gpr_mu_unlock(&calld->mu);
         }
       }
-      grpc_mdstr_hash_table_unref(method_params_table);
+      grpc_mdstr_hash_table_unref(exec_ctx, method_params_table);
     }
   }
   GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "read_service_config");
@@ -1045,8 +1070,8 @@ static grpc_error *cc_init_call_elem(grpc_exec_ctx *exec_ctx,
       grpc_mdstr_hash_table *method_params_table =
           grpc_mdstr_hash_table_ref(chand->method_params_table);
       gpr_mu_unlock(&chand->mu);
-      method_parameters *method_params =
-          grpc_method_config_table_get(method_params_table, args->path);
+      method_parameters *method_params = grpc_method_config_table_get(
+          exec_ctx, method_params_table, args->path);
       if (method_params != NULL) {
         if (gpr_time_cmp(method_params->timeout,
                          gpr_time_0(GPR_CLOCK_MONOTONIC)) != 0) {
@@ -1059,7 +1084,7 @@ static grpc_error *cc_init_call_elem(grpc_exec_ctx *exec_ctx,
               method_params->wait_for_ready;
         }
       }
-      grpc_mdstr_hash_table_unref(method_params_table);
+      grpc_mdstr_hash_table_unref(exec_ctx, method_params_table);
     } else {
       gpr_mu_unlock(&chand->mu);
     }
@@ -1068,7 +1093,8 @@ static grpc_error *cc_init_call_elem(grpc_exec_ctx *exec_ctx,
     // get the service config data once the resolver returns.
     // Take a reference to the call stack to be owned by the callback.
     GRPC_CALL_STACK_REF(calld->owning_call, "read_service_config");
-    grpc_closure_init(&calld->read_service_config, read_service_config, elem);
+    grpc_closure_init(&calld->read_service_config, read_service_config, elem,
+                      grpc_schedule_on_exec_ctx);
     grpc_closure_list_append(&chand->waiting_for_config_closures,
                              &calld->read_service_config, GRPC_ERROR_NONE);
     gpr_mu_unlock(&chand->mu);
@@ -1087,7 +1113,7 @@ static void cc_destroy_call_elem(grpc_exec_ctx *exec_ctx,
                                  void *and_free_memory) {
   call_data *calld = elem->call_data;
   grpc_deadline_state_destroy(exec_ctx, elem);
-  GRPC_MDSTR_UNREF(calld->path);
+  GRPC_MDSTR_UNREF(exec_ctx, calld->path);
   GRPC_ERROR_UNREF(calld->cancel_error);
   grpc_subchannel_call *call = GET_CALL(calld);
   if (call != NULL && call != CANCELLED_CALL) {
@@ -1130,30 +1156,6 @@ const grpc_channel_filter grpc_client_channel_filter = {
     "client-channel",
 };
 
-void grpc_client_channel_finish_initialization(
-    grpc_exec_ctx *exec_ctx, grpc_channel_stack *channel_stack,
-    grpc_resolver *resolver,
-    grpc_client_channel_factory *client_channel_factory) {
-  /* post construction initialization: set the transport setup pointer */
-  GPR_ASSERT(client_channel_factory != NULL);
-  grpc_channel_element *elem = grpc_channel_stack_last_element(channel_stack);
-  channel_data *chand = elem->channel_data;
-  gpr_mu_lock(&chand->mu);
-  GPR_ASSERT(!chand->resolver);
-  chand->resolver = resolver;
-  GRPC_RESOLVER_REF(resolver, "channel");
-  if (!grpc_closure_list_empty(chand->waiting_for_config_closures) ||
-      chand->exit_idle_when_lb_policy_arrives) {
-    chand->started_resolving = true;
-    GRPC_CHANNEL_STACK_REF(chand->owning_stack, "resolver");
-    grpc_resolver_next(exec_ctx, resolver, &chand->resolver_result,
-                       &chand->on_resolver_result_changed);
-  }
-  chand->client_channel_factory = client_channel_factory;
-  grpc_client_channel_factory_ref(client_channel_factory);
-  gpr_mu_unlock(&chand->mu);
-}
-
 grpc_connectivity_state grpc_client_channel_check_connectivity_state(
     grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect) {
   channel_data *chand = elem->channel_data;
@@ -1205,7 +1207,8 @@ void grpc_client_channel_watch_connectivity_state(
   w->pollset = pollset;
   w->on_complete = on_complete;
   grpc_pollset_set_add_pollset(exec_ctx, chand->interested_parties, pollset);
-  grpc_closure_init(&w->my_closure, on_external_watch_complete, w);
+  grpc_closure_init(&w->my_closure, on_external_watch_complete, w,
+                    grpc_schedule_on_exec_ctx);
   GRPC_CHANNEL_STACK_REF(w->chand->owning_stack,
                          "external_connectivity_watcher");
   gpr_mu_lock(&chand->mu);
diff --git a/src/core/ext/client_channel/client_channel.h b/src/core/ext/client_channel/client_channel.h
index ab5a84fdfbe1319ccc222279989bebe9487dd1e0..f02587d0c1e69658ba80cb2b2979bd63a8db99d5 100644
--- a/src/core/ext/client_channel/client_channel.h
+++ b/src/core/ext/client_channel/client_channel.h
@@ -38,6 +38,9 @@
 #include "src/core/ext/client_channel/resolver.h"
 #include "src/core/lib/channel/channel_stack.h"
 
+// Channel arg key for server URI string.
+#define GRPC_ARG_SERVER_URI "grpc.server_uri"
+
 /* A client channel is a channel that begins disconnected, and can connect
    to some endpoint on demand. If that endpoint disconnects, it will be
    connected to again later.
@@ -47,13 +50,6 @@
 
 extern const grpc_channel_filter grpc_client_channel_filter;
 
-/* Post-construction initializer to give the client channel its resolver
-   and factory. */
-void grpc_client_channel_finish_initialization(
-    grpc_exec_ctx *exec_ctx, grpc_channel_stack *channel_stack,
-    grpc_resolver *resolver,
-    grpc_client_channel_factory *client_channel_factory);
-
 grpc_connectivity_state grpc_client_channel_check_connectivity_state(
     grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect);
 
diff --git a/src/core/ext/client_channel/client_channel_factory.c b/src/core/ext/client_channel/client_channel_factory.c
index 4900832d5734e813a9c491fd12572d18640006fe..d2707a155668de891a765eca024198fc26cb1553 100644
--- a/src/core/ext/client_channel/client_channel_factory.c
+++ b/src/core/ext/client_channel/client_channel_factory.c
@@ -55,3 +55,33 @@ grpc_channel* grpc_client_channel_factory_create_channel(
   return factory->vtable->create_client_channel(exec_ctx, factory, target, type,
                                                 args);
 }
+
+static void* factory_arg_copy(void* factory) {
+  grpc_client_channel_factory_ref(factory);
+  return factory;
+}
+
+static void factory_arg_destroy(grpc_exec_ctx* exec_ctx, void* factory) {
+  // TODO(roth): Remove local exec_ctx when
+  // https://github.com/grpc/grpc/pull/8705 is merged.
+  grpc_client_channel_factory_unref(exec_ctx, factory);
+}
+
+static int factory_arg_cmp(void* factory1, void* factory2) {
+  if (factory1 < factory2) return -1;
+  if (factory1 > factory2) return 1;
+  return 0;
+}
+
+static const grpc_arg_pointer_vtable factory_arg_vtable = {
+    factory_arg_copy, factory_arg_destroy, factory_arg_cmp};
+
+grpc_arg grpc_client_channel_factory_create_channel_arg(
+    grpc_client_channel_factory* factory) {
+  grpc_arg arg;
+  arg.type = GRPC_ARG_POINTER;
+  arg.key = GRPC_ARG_CLIENT_CHANNEL_FACTORY;
+  arg.value.pointer.p = factory;
+  arg.value.pointer.vtable = &factory_arg_vtable;
+  return arg;
+}
diff --git a/src/core/ext/client_channel/client_channel_factory.h b/src/core/ext/client_channel/client_channel_factory.h
index 2b8fc577b3b67f3873d2c3fb0034a460b7a9bb4b..bf2764b5370f8d2e0b2540a4580145aaf9f47923 100644
--- a/src/core/ext/client_channel/client_channel_factory.h
+++ b/src/core/ext/client_channel/client_channel_factory.h
@@ -39,6 +39,9 @@
 #include "src/core/ext/client_channel/subchannel.h"
 #include "src/core/lib/channel/channel_stack.h"
 
+// Channel arg key for client channel factory.
+#define GRPC_ARG_CLIENT_CHANNEL_FACTORY "grpc.client_channel_factory"
+
 typedef struct grpc_client_channel_factory grpc_client_channel_factory;
 typedef struct grpc_client_channel_factory_vtable
     grpc_client_channel_factory_vtable;
@@ -83,4 +86,7 @@ grpc_channel *grpc_client_channel_factory_create_channel(
     const char *target, grpc_client_channel_type type,
     const grpc_channel_args *args);
 
+grpc_arg grpc_client_channel_factory_create_channel_arg(
+    grpc_client_channel_factory *factory);
+
 #endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_CLIENT_CHANNEL_FACTORY_H */
diff --git a/src/core/ext/client_channel/client_channel_plugin.c b/src/core/ext/client_channel/client_channel_plugin.c
index a3e5079843906f6aa77f659b7ba7950000b20122..d50bba60f6a28a0cb201d00013c2c60b708d15f1 100644
--- a/src/core/ext/client_channel/client_channel_plugin.c
+++ b/src/core/ext/client_channel/client_channel_plugin.c
@@ -38,17 +38,20 @@
 #include <grpc/support/alloc.h>
 
 #include "src/core/ext/client_channel/client_channel.h"
+#include "src/core/ext/client_channel/http_connect_handshaker.h"
 #include "src/core/ext/client_channel/lb_policy_registry.h"
 #include "src/core/ext/client_channel/resolver_registry.h"
 #include "src/core/ext/client_channel/subchannel_index.h"
 #include "src/core/lib/surface/channel_init.h"
 
-static bool append_filter(grpc_channel_stack_builder *builder, void *arg) {
+static bool append_filter(grpc_exec_ctx *exec_ctx,
+                          grpc_channel_stack_builder *builder, void *arg) {
   return grpc_channel_stack_builder_append_filter(
       builder, (const grpc_channel_filter *)arg, NULL, NULL);
 }
 
-static bool set_default_host_if_unset(grpc_channel_stack_builder *builder,
+static bool set_default_host_if_unset(grpc_exec_ctx *exec_ctx,
+                                      grpc_channel_stack_builder *builder,
                                       void *unused) {
   const grpc_channel_args *args =
       grpc_channel_stack_builder_get_channel_arguments(builder);
@@ -66,9 +69,10 @@ static bool set_default_host_if_unset(grpc_channel_stack_builder *builder,
     arg.key = GRPC_ARG_DEFAULT_AUTHORITY;
     arg.value.string = default_authority;
     grpc_channel_args *new_args = grpc_channel_args_copy_and_add(args, &arg, 1);
-    grpc_channel_stack_builder_set_channel_arguments(builder, new_args);
+    grpc_channel_stack_builder_set_channel_arguments(exec_ctx, builder,
+                                                     new_args);
     gpr_free(default_authority);
-    grpc_channel_args_destroy(new_args);
+    grpc_channel_args_destroy(exec_ctx, new_args);
   }
   return true;
 }
@@ -81,6 +85,7 @@ void grpc_client_channel_init(void) {
                                    set_default_host_if_unset, NULL);
   grpc_channel_init_register_stage(GRPC_CLIENT_CHANNEL, INT_MAX, append_filter,
                                    (void *)&grpc_client_channel_filter);
+  grpc_http_connect_register_handshaker_factory();
 }
 
 void grpc_client_channel_shutdown(void) {
diff --git a/src/core/ext/client_channel/http_connect_handshaker.c b/src/core/ext/client_channel/http_connect_handshaker.c
index 82042897b2a6fe093d4a6ae296ec925a715bcbf4..fba32561acf822b04c776ced5815df6c8a98d0cc 100644
--- a/src/core/ext/client_channel/http_connect_handshaker.c
+++ b/src/core/ext/client_channel/http_connect_handshaker.c
@@ -40,10 +40,14 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
+#include "src/core/ext/client_channel/client_channel.h"
+#include "src/core/ext/client_channel/resolver_registry.h"
 #include "src/core/ext/client_channel/uri_parser.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/handshaker_registry.h"
 #include "src/core/lib/http/format_request.h"
 #include "src/core/lib/http/parser.h"
-#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/support/env.h"
 
 typedef struct http_connect_handshaker {
@@ -51,60 +55,113 @@ typedef struct http_connect_handshaker {
   grpc_handshaker base;
 
   char* proxy_server;
-  char* server_name;
+  grpc_http_header* headers;
+  size_t num_headers;
+
+  gpr_refcount refcount;
+  gpr_mu mu;
+
+  bool shutdown;
+  // Endpoint and read buffer to destroy after a shutdown.
+  grpc_endpoint* endpoint_to_destroy;
+  grpc_slice_buffer* read_buffer_to_destroy;
 
   // State saved while performing the handshake.
-  grpc_endpoint* endpoint;
-  grpc_channel_args* args;
-  grpc_handshaker_done_cb cb;
-  void* user_data;
+  grpc_handshaker_args* args;
+  grpc_closure* on_handshake_done;
 
   // Objects for processing the HTTP CONNECT request and response.
   grpc_slice_buffer write_buffer;
-  grpc_slice_buffer* read_buffer;  // Ownership passes through this object.
   grpc_closure request_done_closure;
   grpc_closure response_read_closure;
   grpc_http_parser http_parser;
   grpc_http_response http_response;
-  grpc_timer timeout_timer;
-
-  gpr_refcount refcount;
 } http_connect_handshaker;
 
 // Unref and clean up handshaker.
-static void http_connect_handshaker_unref(http_connect_handshaker* handshaker) {
+static void http_connect_handshaker_unref(grpc_exec_ctx* exec_ctx,
+                                          http_connect_handshaker* handshaker) {
   if (gpr_unref(&handshaker->refcount)) {
+    gpr_mu_destroy(&handshaker->mu);
+    if (handshaker->endpoint_to_destroy != NULL) {
+      grpc_endpoint_destroy(exec_ctx, handshaker->endpoint_to_destroy);
+    }
+    if (handshaker->read_buffer_to_destroy != NULL) {
+      grpc_slice_buffer_destroy_internal(exec_ctx,
+                                         handshaker->read_buffer_to_destroy);
+      gpr_free(handshaker->read_buffer_to_destroy);
+    }
     gpr_free(handshaker->proxy_server);
-    gpr_free(handshaker->server_name);
-    grpc_slice_buffer_destroy(&handshaker->write_buffer);
+    for (size_t i = 0; i < handshaker->num_headers; ++i) {
+      gpr_free(handshaker->headers[i].key);
+      gpr_free(handshaker->headers[i].value);
+    }
+    gpr_free(handshaker->headers);
+    grpc_slice_buffer_destroy_internal(exec_ctx, &handshaker->write_buffer);
     grpc_http_parser_destroy(&handshaker->http_parser);
     grpc_http_response_destroy(&handshaker->http_response);
     gpr_free(handshaker);
   }
 }
 
-// Callback invoked when deadline is exceeded.
-static void on_timeout(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) {
-  http_connect_handshaker* handshaker = arg;
-  if (error == GRPC_ERROR_NONE) {  // Timer fired, rather than being cancelled.
-    grpc_endpoint_shutdown(exec_ctx, handshaker->endpoint);
+// Set args fields to NULL, saving the endpoint and read buffer for
+// later destruction.
+static void cleanup_args_for_failure_locked(
+    grpc_exec_ctx* exec_ctx, http_connect_handshaker* handshaker) {
+  handshaker->endpoint_to_destroy = handshaker->args->endpoint;
+  handshaker->args->endpoint = NULL;
+  handshaker->read_buffer_to_destroy = handshaker->args->read_buffer;
+  handshaker->args->read_buffer = NULL;
+  grpc_channel_args_destroy(exec_ctx, handshaker->args->args);
+  handshaker->args->args = NULL;
+}
+
+// If the handshake failed or we're shutting down, clean up and invoke the
+// callback with the error.
+static void handshake_failed_locked(grpc_exec_ctx* exec_ctx,
+                                    http_connect_handshaker* handshaker,
+                                    grpc_error* error) {
+  if (error == GRPC_ERROR_NONE) {
+    // If we were shut down after an endpoint operation succeeded but
+    // before the endpoint callback was invoked, we need to generate our
+    // own error.
+    error = GRPC_ERROR_CREATE("Handshaker shutdown");
+  }
+  if (!handshaker->shutdown) {
+    // TODO(ctiller): It is currently necessary to shutdown endpoints
+    // before destroying them, even if we know that there are no
+    // pending read/write callbacks.  This should be fixed, at which
+    // point this can be removed.
+    grpc_endpoint_shutdown(exec_ctx, handshaker->args->endpoint);
+    // Not shutting down, so the handshake failed.  Clean up before
+    // invoking the callback.
+    cleanup_args_for_failure_locked(exec_ctx, handshaker);
+    // Set shutdown to true so that subsequent calls to
+    // http_connect_handshaker_shutdown() do nothing.
+    handshaker->shutdown = true;
   }
-  http_connect_handshaker_unref(handshaker);
+  // Invoke callback.
+  grpc_closure_sched(exec_ctx, handshaker->on_handshake_done, error);
 }
 
 // Callback invoked when finished writing HTTP CONNECT request.
 static void on_write_done(grpc_exec_ctx* exec_ctx, void* arg,
                           grpc_error* error) {
   http_connect_handshaker* handshaker = arg;
-  if (error != GRPC_ERROR_NONE) {
-    // If the write failed, invoke the callback immediately with the error.
-    handshaker->cb(exec_ctx, handshaker->endpoint, handshaker->args,
-                   handshaker->read_buffer, handshaker->user_data,
-                   GRPC_ERROR_REF(error));
+  gpr_mu_lock(&handshaker->mu);
+  if (error != GRPC_ERROR_NONE || handshaker->shutdown) {
+    // If the write failed or we're shutting down, clean up and invoke the
+    // callback with the error.
+    handshake_failed_locked(exec_ctx, handshaker, GRPC_ERROR_REF(error));
+    gpr_mu_unlock(&handshaker->mu);
+    http_connect_handshaker_unref(exec_ctx, handshaker);
   } else {
     // Otherwise, read the response.
-    grpc_endpoint_read(exec_ctx, handshaker->endpoint, handshaker->read_buffer,
+    // The read callback inherits our ref to the handshaker.
+    grpc_endpoint_read(exec_ctx, handshaker->args->endpoint,
+                       handshaker->args->read_buffer,
                        &handshaker->response_read_closure);
+    gpr_mu_unlock(&handshaker->mu);
   }
 }
 
@@ -112,37 +169,41 @@ static void on_write_done(grpc_exec_ctx* exec_ctx, void* arg,
 static void on_read_done(grpc_exec_ctx* exec_ctx, void* arg,
                          grpc_error* error) {
   http_connect_handshaker* handshaker = arg;
-  if (error != GRPC_ERROR_NONE) {
-    GRPC_ERROR_REF(error);  // Take ref to pass to the handshake-done callback.
+  gpr_mu_lock(&handshaker->mu);
+  if (error != GRPC_ERROR_NONE || handshaker->shutdown) {
+    // If the read failed or we're shutting down, clean up and invoke the
+    // callback with the error.
+    handshake_failed_locked(exec_ctx, handshaker, GRPC_ERROR_REF(error));
     goto done;
   }
   // Add buffer to parser.
-  for (size_t i = 0; i < handshaker->read_buffer->count; ++i) {
-    if (GRPC_SLICE_LENGTH(handshaker->read_buffer->slices[i]) > 0) {
+  for (size_t i = 0; i < handshaker->args->read_buffer->count; ++i) {
+    if (GRPC_SLICE_LENGTH(handshaker->args->read_buffer->slices[i]) > 0) {
       size_t body_start_offset = 0;
       error = grpc_http_parser_parse(&handshaker->http_parser,
-                                     handshaker->read_buffer->slices[i],
+                                     handshaker->args->read_buffer->slices[i],
                                      &body_start_offset);
-      if (error != GRPC_ERROR_NONE) goto done;
+      if (error != GRPC_ERROR_NONE) {
+        handshake_failed_locked(exec_ctx, handshaker, error);
+        goto done;
+      }
       if (handshaker->http_parser.state == GRPC_HTTP_BODY) {
-        // We've gotten back a successul response, so stop the timeout timer.
-        grpc_timer_cancel(exec_ctx, &handshaker->timeout_timer);
         // Remove the data we've already read from the read buffer,
         // leaving only the leftover bytes (if any).
         grpc_slice_buffer tmp_buffer;
         grpc_slice_buffer_init(&tmp_buffer);
         if (body_start_offset <
-            GRPC_SLICE_LENGTH(handshaker->read_buffer->slices[i])) {
+            GRPC_SLICE_LENGTH(handshaker->args->read_buffer->slices[i])) {
           grpc_slice_buffer_add(
               &tmp_buffer,
-              grpc_slice_split_tail(&handshaker->read_buffer->slices[i],
+              grpc_slice_split_tail(&handshaker->args->read_buffer->slices[i],
                                     body_start_offset));
         }
         grpc_slice_buffer_addn(&tmp_buffer,
-                               &handshaker->read_buffer->slices[i + 1],
-                               handshaker->read_buffer->count - i - 1);
-        grpc_slice_buffer_swap(handshaker->read_buffer, &tmp_buffer);
-        grpc_slice_buffer_destroy(&tmp_buffer);
+                               &handshaker->args->read_buffer->slices[i + 1],
+                               handshaker->args->read_buffer->count - i - 1);
+        grpc_slice_buffer_swap(handshaker->args->read_buffer, &tmp_buffer);
+        grpc_slice_buffer_destroy_internal(exec_ctx, &tmp_buffer);
         break;
       }
     }
@@ -159,9 +220,12 @@ static void on_read_done(grpc_exec_ctx* exec_ctx, void* arg,
   // complete (e.g., handling chunked transfer encoding or looking
   // at the Content-Length: header).
   if (handshaker->http_parser.state != GRPC_HTTP_BODY) {
-    grpc_slice_buffer_reset_and_unref(handshaker->read_buffer);
-    grpc_endpoint_read(exec_ctx, handshaker->endpoint, handshaker->read_buffer,
+    grpc_slice_buffer_reset_and_unref_internal(exec_ctx,
+                                               handshaker->args->read_buffer);
+    grpc_endpoint_read(exec_ctx, handshaker->args->endpoint,
+                       handshaker->args->read_buffer,
                        &handshaker->response_read_closure);
+    gpr_mu_unlock(&handshaker->mu);
     return;
   }
   // Make sure we got a 2xx response.
@@ -172,11 +236,17 @@ static void on_read_done(grpc_exec_ctx* exec_ctx, void* arg,
                  handshaker->http_response.status);
     error = GRPC_ERROR_CREATE(msg);
     gpr_free(msg);
+    handshake_failed_locked(exec_ctx, handshaker, error);
+    goto done;
   }
+  // Success.  Invoke handshake-done callback.
+  grpc_closure_sched(exec_ctx, handshaker->on_handshake_done, error);
 done:
-  // Invoke handshake-done callback.
-  handshaker->cb(exec_ctx, handshaker->endpoint, handshaker->args,
-                 handshaker->read_buffer, handshaker->user_data, error);
+  // Set shutdown to true so that subsequent calls to
+  // http_connect_handshaker_shutdown() do nothing.
+  handshaker->shutdown = true;
+  gpr_mu_unlock(&handshaker->mu);
+  http_connect_handshaker_unref(exec_ctx, handshaker);
 }
 
 //
@@ -186,67 +256,91 @@ done:
 static void http_connect_handshaker_destroy(grpc_exec_ctx* exec_ctx,
                                             grpc_handshaker* handshaker_in) {
   http_connect_handshaker* handshaker = (http_connect_handshaker*)handshaker_in;
-  http_connect_handshaker_unref(handshaker);
+  http_connect_handshaker_unref(exec_ctx, handshaker);
 }
 
 static void http_connect_handshaker_shutdown(grpc_exec_ctx* exec_ctx,
-                                             grpc_handshaker* handshaker) {}
+                                             grpc_handshaker* handshaker_in) {
+  http_connect_handshaker* handshaker = (http_connect_handshaker*)handshaker_in;
+  gpr_mu_lock(&handshaker->mu);
+  if (!handshaker->shutdown) {
+    handshaker->shutdown = true;
+    grpc_endpoint_shutdown(exec_ctx, handshaker->args->endpoint);
+    cleanup_args_for_failure_locked(exec_ctx, handshaker);
+  }
+  gpr_mu_unlock(&handshaker->mu);
+}
 
 static void http_connect_handshaker_do_handshake(
     grpc_exec_ctx* exec_ctx, grpc_handshaker* handshaker_in,
-    grpc_endpoint* endpoint, grpc_channel_args* args,
-    grpc_slice_buffer* read_buffer, gpr_timespec deadline,
-    grpc_tcp_server_acceptor* acceptor, grpc_handshaker_done_cb cb,
-    void* user_data) {
+    grpc_tcp_server_acceptor* acceptor, grpc_closure* on_handshake_done,
+    grpc_handshaker_args* args) {
   http_connect_handshaker* handshaker = (http_connect_handshaker*)handshaker_in;
+  // Get server name from channel args.
+  const grpc_arg* arg = grpc_channel_args_find(args->args, GRPC_ARG_SERVER_URI);
+  GPR_ASSERT(arg != NULL);
+  GPR_ASSERT(arg->type == GRPC_ARG_STRING);
+  char* canonical_uri =
+      grpc_resolver_factory_add_default_prefix_if_needed(arg->value.string);
+  grpc_uri* uri = grpc_uri_parse(canonical_uri, 1);
+  char* server_name = uri->path;
+  if (server_name[0] == '/') ++server_name;
   // Save state in the handshaker object.
-  handshaker->endpoint = endpoint;
+  gpr_mu_lock(&handshaker->mu);
   handshaker->args = args;
-  handshaker->cb = cb;
-  handshaker->user_data = user_data;
-  handshaker->read_buffer = read_buffer;
+  handshaker->on_handshake_done = on_handshake_done;
   // Send HTTP CONNECT request.
-  gpr_log(GPR_INFO, "Connecting to server %s via HTTP proxy %s",
-          handshaker->server_name, handshaker->proxy_server);
+  gpr_log(GPR_INFO, "Connecting to server %s via HTTP proxy %s", server_name,
+          handshaker->proxy_server);
   grpc_httpcli_request request;
   memset(&request, 0, sizeof(request));
-  request.host = handshaker->proxy_server;
+  request.host = server_name;
   request.http.method = "CONNECT";
-  request.http.path = handshaker->server_name;
+  request.http.path = server_name;
+  request.http.hdrs = handshaker->headers;
+  request.http.hdr_count = handshaker->num_headers;
   request.handshaker = &grpc_httpcli_plaintext;
   grpc_slice request_slice = grpc_httpcli_format_connect_request(&request);
   grpc_slice_buffer_add(&handshaker->write_buffer, request_slice);
-  grpc_endpoint_write(exec_ctx, endpoint, &handshaker->write_buffer,
-                      &handshaker->request_done_closure);
-  // Set timeout timer.  The timer gets a reference to the handshaker.
+  // Take a new ref to be held by the write callback.
   gpr_ref(&handshaker->refcount);
-  grpc_timer_init(exec_ctx, &handshaker->timeout_timer,
-                  gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC),
-                  on_timeout, handshaker, gpr_now(GPR_CLOCK_MONOTONIC));
+  grpc_endpoint_write(exec_ctx, args->endpoint, &handshaker->write_buffer,
+                      &handshaker->request_done_closure);
+  gpr_mu_unlock(&handshaker->mu);
+  // Clean up.
+  gpr_free(canonical_uri);
+  grpc_uri_destroy(uri);
 }
 
-static const struct grpc_handshaker_vtable http_connect_handshaker_vtable = {
+static const grpc_handshaker_vtable http_connect_handshaker_vtable = {
     http_connect_handshaker_destroy, http_connect_handshaker_shutdown,
     http_connect_handshaker_do_handshake};
 
 grpc_handshaker* grpc_http_connect_handshaker_create(const char* proxy_server,
-                                                     const char* server_name) {
+                                                     grpc_http_header* headers,
+                                                     size_t num_headers) {
   GPR_ASSERT(proxy_server != NULL);
-  GPR_ASSERT(server_name != NULL);
-  http_connect_handshaker* handshaker =
-      gpr_malloc(sizeof(http_connect_handshaker));
+  http_connect_handshaker* handshaker = gpr_malloc(sizeof(*handshaker));
   memset(handshaker, 0, sizeof(*handshaker));
   grpc_handshaker_init(&http_connect_handshaker_vtable, &handshaker->base);
+  gpr_mu_init(&handshaker->mu);
+  gpr_ref_init(&handshaker->refcount, 1);
   handshaker->proxy_server = gpr_strdup(proxy_server);
-  handshaker->server_name = gpr_strdup(server_name);
+  if (num_headers > 0) {
+    handshaker->headers = gpr_malloc(sizeof(grpc_http_header) * num_headers);
+    for (size_t i = 0; i < num_headers; ++i) {
+      handshaker->headers[i].key = gpr_strdup(headers[i].key);
+      handshaker->headers[i].value = gpr_strdup(headers[i].value);
+    }
+    handshaker->num_headers = num_headers;
+  }
   grpc_slice_buffer_init(&handshaker->write_buffer);
   grpc_closure_init(&handshaker->request_done_closure, on_write_done,
-                    handshaker);
+                    handshaker, grpc_schedule_on_exec_ctx);
   grpc_closure_init(&handshaker->response_read_closure, on_read_done,
-                    handshaker);
+                    handshaker, grpc_schedule_on_exec_ctx);
   grpc_http_parser_init(&handshaker->http_parser, GRPC_HTTP_RESPONSE,
                         &handshaker->http_response);
-  gpr_ref_init(&handshaker->refcount, 1);
   return &handshaker->base;
 }
 
@@ -273,3 +367,33 @@ done:
   grpc_uri_destroy(uri);
   return proxy_name;
 }
+
+//
+// handshaker factory
+//
+
+static void handshaker_factory_add_handshakers(
+    grpc_exec_ctx* exec_ctx, grpc_handshaker_factory* factory,
+    const grpc_channel_args* args, grpc_handshake_manager* handshake_mgr) {
+  char* proxy_name = grpc_get_http_proxy_server();
+  if (proxy_name != NULL) {
+    grpc_handshake_manager_add(
+        handshake_mgr,
+        grpc_http_connect_handshaker_create(proxy_name, NULL, 0));
+    gpr_free(proxy_name);
+  }
+}
+
+static void handshaker_factory_destroy(grpc_exec_ctx* exec_ctx,
+                                       grpc_handshaker_factory* factory) {}
+
+static const grpc_handshaker_factory_vtable handshaker_factory_vtable = {
+    handshaker_factory_add_handshakers, handshaker_factory_destroy};
+
+static grpc_handshaker_factory handshaker_factory = {
+    &handshaker_factory_vtable};
+
+void grpc_http_connect_register_handshaker_factory() {
+  grpc_handshaker_factory_register(true /* at_start */, HANDSHAKER_CLIENT,
+                                   &handshaker_factory);
+}
diff --git a/src/core/ext/client_channel/http_connect_handshaker.h b/src/core/ext/client_channel/http_connect_handshaker.h
index c689df2b2b52ec6a5dbf7b98351ba9e7a7e9cad2..c2e68de7167bff20cb65b033268a519886f5cc74 100644
--- a/src/core/ext/client_channel/http_connect_handshaker.h
+++ b/src/core/ext/client_channel/http_connect_handshaker.h
@@ -35,13 +35,18 @@
 #define GRPC_CORE_EXT_CLIENT_CHANNEL_HTTP_CONNECT_HANDSHAKER_H
 
 #include "src/core/lib/channel/handshaker.h"
+#include "src/core/lib/http/parser.h"
 
-/// Does NOT take ownership of \a proxy_server or \a server_name.
+/// Creates a new HTTP CONNECT handshaker.
 grpc_handshaker* grpc_http_connect_handshaker_create(const char* proxy_server,
-                                                     const char* server_name);
+                                                     grpc_http_header* headers,
+                                                     size_t num_headers);
 
 /// Returns the name of the proxy to use, or NULL if no proxy is configured.
 /// Caller takes ownership of result.
 char* grpc_get_http_proxy_server();
 
+/// Registers handshaker factory.
+void grpc_http_connect_register_handshaker_factory();
+
 #endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_HTTP_CONNECT_HANDSHAKER_H */
diff --git a/src/core/ext/client_channel/lb_policy_factory.c b/src/core/ext/client_channel/lb_policy_factory.c
index 8a474c881854fb968a68f5140b4e57f1fdd2b007..7af9bb04112818bf74a72591fede31ac97f886a0 100644
--- a/src/core/ext/client_channel/lb_policy_factory.c
+++ b/src/core/ext/client_channel/lb_policy_factory.c
@@ -112,11 +112,13 @@ int grpc_lb_addresses_cmp(const grpc_lb_addresses* addresses1,
   return 0;
 }
 
-void grpc_lb_addresses_destroy(grpc_lb_addresses* addresses) {
+void grpc_lb_addresses_destroy(grpc_exec_ctx* exec_ctx,
+                               grpc_lb_addresses* addresses) {
   for (size_t i = 0; i < addresses->num_addresses; ++i) {
     gpr_free(addresses->addresses[i].balancer_name);
     if (addresses->addresses[i].user_data != NULL) {
-      addresses->user_data_vtable->destroy(addresses->addresses[i].user_data);
+      addresses->user_data_vtable->destroy(exec_ctx,
+                                           addresses->addresses[i].user_data);
     }
   }
   gpr_free(addresses->addresses);
@@ -126,8 +128,8 @@ void grpc_lb_addresses_destroy(grpc_lb_addresses* addresses) {
 static void* lb_addresses_copy(void* addresses) {
   return grpc_lb_addresses_copy(addresses);
 }
-static void lb_addresses_destroy(void* addresses) {
-  grpc_lb_addresses_destroy(addresses);
+static void lb_addresses_destroy(grpc_exec_ctx* exec_ctx, void* addresses) {
+  grpc_lb_addresses_destroy(exec_ctx, addresses);
 }
 static int lb_addresses_cmp(void* addresses1, void* addresses2) {
   return grpc_lb_addresses_cmp(addresses1, addresses2);
diff --git a/src/core/ext/client_channel/lb_policy_factory.h b/src/core/ext/client_channel/lb_policy_factory.h
index e2b8080a32917461c003b73930b55df6d669ab64..9b8b03f982b0b1e9973bc0f8026b8ac9b62708f4 100644
--- a/src/core/ext/client_channel/lb_policy_factory.h
+++ b/src/core/ext/client_channel/lb_policy_factory.h
@@ -40,6 +40,9 @@
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/iomgr/resolve_address.h"
 
+// Channel arg key for grpc_lb_addresses.
+#define GRPC_ARG_LB_ADDRESSES "grpc.lb_addresses"
+
 typedef struct grpc_lb_policy_factory grpc_lb_policy_factory;
 typedef struct grpc_lb_policy_factory_vtable grpc_lb_policy_factory_vtable;
 
@@ -61,7 +64,7 @@ typedef struct grpc_lb_address {
 
 typedef struct grpc_lb_user_data_vtable {
   void *(*copy)(void *);
-  void (*destroy)(void *);
+  void (*destroy)(grpc_exec_ctx *exec_ctx, void *);
   int (*cmp)(void *, void *);
 } grpc_lb_user_data_vtable;
 
@@ -93,7 +96,8 @@ int grpc_lb_addresses_cmp(const grpc_lb_addresses *addresses1,
                           const grpc_lb_addresses *addresses2);
 
 /** Destroys \a addresses. */
-void grpc_lb_addresses_destroy(grpc_lb_addresses *addresses);
+void grpc_lb_addresses_destroy(grpc_exec_ctx *exec_ctx,
+                               grpc_lb_addresses *addresses);
 
 /** Returns a channel arg containing \a addresses. */
 grpc_arg grpc_lb_addresses_create_channel_arg(
diff --git a/src/core/ext/client_channel/resolver_factory.c b/src/core/ext/client_channel/resolver_factory.c
index 7c3d644257ca0d28469e8b39745a5f0f707f3ae4..00bbb92dd06adb8da427c9af3f1490dd1310ebbb 100644
--- a/src/core/ext/client_channel/resolver_factory.c
+++ b/src/core/ext/client_channel/resolver_factory.c
@@ -43,9 +43,10 @@ void grpc_resolver_factory_unref(grpc_resolver_factory* factory) {
 
 /** Create a resolver instance for a name */
 grpc_resolver* grpc_resolver_factory_create_resolver(
-    grpc_resolver_factory* factory, grpc_resolver_args* args) {
+    grpc_exec_ctx* exec_ctx, grpc_resolver_factory* factory,
+    grpc_resolver_args* args) {
   if (factory == NULL) return NULL;
-  return factory->vtable->create_resolver(factory, args);
+  return factory->vtable->create_resolver(exec_ctx, factory, args);
 }
 
 char* grpc_resolver_factory_get_default_authority(
diff --git a/src/core/ext/client_channel/resolver_factory.h b/src/core/ext/client_channel/resolver_factory.h
index 4da42e84d2f23c2887984b177dfe0282360dd2ce..3792ddca18c07ce91e10d6654f815af6506929dc 100644
--- a/src/core/ext/client_channel/resolver_factory.h
+++ b/src/core/ext/client_channel/resolver_factory.h
@@ -37,6 +37,7 @@
 #include "src/core/ext/client_channel/client_channel_factory.h"
 #include "src/core/ext/client_channel/resolver.h"
 #include "src/core/ext/client_channel/uri_parser.h"
+#include "src/core/lib/iomgr/pollset_set.h"
 
 typedef struct grpc_resolver_factory grpc_resolver_factory;
 typedef struct grpc_resolver_factory_vtable grpc_resolver_factory_vtable;
@@ -48,6 +49,7 @@ struct grpc_resolver_factory {
 typedef struct grpc_resolver_args {
   grpc_uri *uri;
   const grpc_channel_args *args;
+  grpc_pollset_set *pollset_set;
 } grpc_resolver_args;
 
 struct grpc_resolver_factory_vtable {
@@ -55,7 +57,8 @@ struct grpc_resolver_factory_vtable {
   void (*unref)(grpc_resolver_factory *factory);
 
   /** Implementation of grpc_resolver_factory_create_resolver */
-  grpc_resolver *(*create_resolver)(grpc_resolver_factory *factory,
+  grpc_resolver *(*create_resolver)(grpc_exec_ctx *exec_ctx,
+                                    grpc_resolver_factory *factory,
                                     grpc_resolver_args *args);
 
   /** Implementation of grpc_resolver_factory_get_default_authority */
@@ -70,7 +73,8 @@ void grpc_resolver_factory_unref(grpc_resolver_factory *resolver);
 
 /** Create a resolver instance for a name */
 grpc_resolver *grpc_resolver_factory_create_resolver(
-    grpc_resolver_factory *factory, grpc_resolver_args *args);
+    grpc_exec_ctx *exec_ctx, grpc_resolver_factory *factory,
+    grpc_resolver_args *args);
 
 /** Return a (freshly allocated with gpr_malloc) string representing
     the default authority to use for this scheme. */
diff --git a/src/core/ext/client_channel/resolver_registry.c b/src/core/ext/client_channel/resolver_registry.c
index d0f0fc3f332f012582490d57049dde65f79cd188..5110a7cad9e828675ff2373a2629ecb8d9ca7edd 100644
--- a/src/core/ext/client_channel/resolver_registry.c
+++ b/src/core/ext/client_channel/resolver_registry.c
@@ -109,8 +109,8 @@ static grpc_resolver_factory *lookup_factory_by_uri(grpc_uri *uri) {
 }
 
 static grpc_resolver_factory *resolve_factory(const char *target,
-                                              grpc_uri **uri) {
-  char *tmp;
+                                              grpc_uri **uri,
+                                              char **canonical_target) {
   grpc_resolver_factory *factory = NULL;
 
   GPR_ASSERT(uri != NULL);
@@ -118,37 +118,54 @@ static grpc_resolver_factory *resolve_factory(const char *target,
   factory = lookup_factory_by_uri(*uri);
   if (factory == NULL) {
     grpc_uri_destroy(*uri);
-    gpr_asprintf(&tmp, "%s%s", g_default_resolver_prefix, target);
-    *uri = grpc_uri_parse(tmp, 1);
+    gpr_asprintf(canonical_target, "%s%s", g_default_resolver_prefix, target);
+    *uri = grpc_uri_parse(*canonical_target, 1);
     factory = lookup_factory_by_uri(*uri);
     if (factory == NULL) {
       grpc_uri_destroy(grpc_uri_parse(target, 0));
-      grpc_uri_destroy(grpc_uri_parse(tmp, 0));
-      gpr_log(GPR_ERROR, "don't know how to resolve '%s' or '%s'", target, tmp);
+      grpc_uri_destroy(grpc_uri_parse(*canonical_target, 0));
+      gpr_log(GPR_ERROR, "don't know how to resolve '%s' or '%s'", target,
+              *canonical_target);
     }
-    gpr_free(tmp);
   }
   return factory;
 }
 
-grpc_resolver *grpc_resolver_create(const char *target,
-                                    const grpc_channel_args *args) {
+grpc_resolver *grpc_resolver_create(grpc_exec_ctx *exec_ctx, const char *target,
+                                    const grpc_channel_args *args,
+                                    grpc_pollset_set *pollset_set) {
   grpc_uri *uri = NULL;
-  grpc_resolver_factory *factory = resolve_factory(target, &uri);
+  char *canonical_target = NULL;
+  grpc_resolver_factory *factory =
+      resolve_factory(target, &uri, &canonical_target);
   grpc_resolver *resolver;
   grpc_resolver_args resolver_args;
   memset(&resolver_args, 0, sizeof(resolver_args));
   resolver_args.uri = uri;
   resolver_args.args = args;
-  resolver = grpc_resolver_factory_create_resolver(factory, &resolver_args);
+  resolver_args.pollset_set = pollset_set;
+  resolver =
+      grpc_resolver_factory_create_resolver(exec_ctx, factory, &resolver_args);
   grpc_uri_destroy(uri);
+  gpr_free(canonical_target);
   return resolver;
 }
 
 char *grpc_get_default_authority(const char *target) {
   grpc_uri *uri = NULL;
-  grpc_resolver_factory *factory = resolve_factory(target, &uri);
+  char *canonical_target = NULL;
+  grpc_resolver_factory *factory =
+      resolve_factory(target, &uri, &canonical_target);
   char *authority = grpc_resolver_factory_get_default_authority(factory, uri);
   grpc_uri_destroy(uri);
+  gpr_free(canonical_target);
   return authority;
 }
+
+char *grpc_resolver_factory_add_default_prefix_if_needed(const char *target) {
+  grpc_uri *uri = NULL;
+  char *canonical_target = NULL;
+  resolve_factory(target, &uri, &canonical_target);
+  grpc_uri_destroy(uri);
+  return canonical_target == NULL ? gpr_strdup(target) : canonical_target;
+}
diff --git a/src/core/ext/client_channel/resolver_registry.h b/src/core/ext/client_channel/resolver_registry.h
index 2a95a669f0d47a988b2ca8e5ec7b79b0d232bd21..4fb16131db978a8cef2ad7873bbfd534013fff04 100644
--- a/src/core/ext/client_channel/resolver_registry.h
+++ b/src/core/ext/client_channel/resolver_registry.h
@@ -35,6 +35,7 @@
 #define GRPC_CORE_EXT_CLIENT_CHANNEL_RESOLVER_REGISTRY_H
 
 #include "src/core/ext/client_channel/resolver_factory.h"
+#include "src/core/lib/iomgr/pollset_set.h"
 
 void grpc_resolver_registry_init();
 void grpc_resolver_registry_shutdown(void);
@@ -60,8 +61,9 @@ void grpc_register_resolver_type(grpc_resolver_factory *factory);
     If a resolver factory was not found, return NULL.
     \a args is a set of channel arguments to be included in the result
     (typically the set of arguments passed in from the client API). */
-grpc_resolver *grpc_resolver_create(const char *target,
-                                    const grpc_channel_args *args);
+grpc_resolver *grpc_resolver_create(grpc_exec_ctx *exec_ctx, const char *target,
+                                    const grpc_channel_args *args,
+                                    grpc_pollset_set *pollset_set);
 
 /** Find a resolver factory given a name and return an (owned-by-the-caller)
  *  reference to it */
@@ -71,4 +73,8 @@ grpc_resolver_factory *grpc_resolver_factory_lookup(const char *name);
     representing the default authority to pass from a client. */
 char *grpc_get_default_authority(const char *target);
 
+/** Returns a newly allocated string containing \a target, adding the
+    default prefix if needed. */
+char *grpc_resolver_factory_add_default_prefix_if_needed(const char *target);
+
 #endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_RESOLVER_REGISTRY_H */
diff --git a/src/core/ext/client_channel/subchannel.c b/src/core/ext/client_channel/subchannel.c
index b83c6882dc0dadb2377ede0c6ff63bee71b09ef4..1bac82b4512cf32f96769ab2639a5944036294fe 100644
--- a/src/core/ext/client_channel/subchannel.c
+++ b/src/core/ext/client_channel/subchannel.c
@@ -46,6 +46,7 @@
 #include "src/core/lib/channel/connected_channel.h"
 #include "src/core/lib/iomgr/timer.h"
 #include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/support/backoff.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/channel_init.h"
@@ -108,6 +109,9 @@ struct grpc_subchannel {
   /** callback for connection finishing */
   grpc_closure connected;
 
+  /** callback for our alarm */
+  grpc_closure on_alarm;
+
   /** pollset_set tracking who's interested in a connection
       being setup */
   grpc_pollset_set *pollset_set;
@@ -206,9 +210,9 @@ static void subchannel_destroy(grpc_exec_ctx *exec_ctx, void *arg,
                                grpc_error *error) {
   grpc_subchannel *c = arg;
   gpr_free((void *)c->filters);
-  grpc_channel_args_destroy(c->args);
+  grpc_channel_args_destroy(exec_ctx, c->args);
   gpr_free(c->addr);
-  grpc_slice_unref(c->initial_connect_string);
+  grpc_slice_unref_internal(exec_ctx, c->initial_connect_string);
   grpc_connectivity_state_destroy(exec_ctx, &c->state_tracker);
   grpc_connector_unref(exec_ctx, c->connector);
   grpc_pollset_set_destroy(c->pollset_set);
@@ -293,8 +297,9 @@ void grpc_subchannel_weak_unref(grpc_exec_ctx *exec_ctx,
   gpr_atm old_refs;
   old_refs = ref_mutate(c, -(gpr_atm)1, 1 REF_MUTATE_PURPOSE("WEAK_UNREF"));
   if (old_refs == 1) {
-    grpc_exec_ctx_sched(exec_ctx, grpc_closure_create(subchannel_destroy, c),
-                        GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, grpc_closure_create(subchannel_destroy, c,
+                                                     grpc_schedule_on_exec_ctx),
+                       GRPC_ERROR_NONE);
   }
 }
 
@@ -330,22 +335,25 @@ grpc_subchannel *grpc_subchannel_create(grpc_exec_ctx *exec_ctx,
   c->args = grpc_channel_args_copy(args->args);
   c->root_external_state_watcher.next = c->root_external_state_watcher.prev =
       &c->root_external_state_watcher;
-  grpc_closure_init(&c->connected, subchannel_connected, c);
+  grpc_closure_init(&c->connected, subchannel_connected, c,
+                    grpc_schedule_on_exec_ctx);
   grpc_connectivity_state_init(&c->state_tracker, GRPC_CHANNEL_IDLE,
                                "subchannel");
   int initial_backoff_ms =
       GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS * 1000;
   int max_backoff_ms = GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS * 1000;
+  int min_backoff_ms = GRPC_SUBCHANNEL_MIN_CONNECT_TIMEOUT_SECONDS * 1000;
   bool fixed_reconnect_backoff = false;
   if (c->args) {
     for (size_t i = 0; i < c->args->num_args; i++) {
       if (0 == strcmp(c->args->args[i].key,
-                      "grpc.testing.fixed_reconnect_backoff")) {
+                      "grpc.testing.fixed_reconnect_backoff_ms")) {
         GPR_ASSERT(c->args->args[i].type == GRPC_ARG_INTEGER);
         fixed_reconnect_backoff = true;
-        initial_backoff_ms = max_backoff_ms = grpc_channel_arg_get_integer(
-            &c->args->args[i],
-            (grpc_integer_options){initial_backoff_ms, 100, INT_MAX});
+        initial_backoff_ms = min_backoff_ms = max_backoff_ms =
+            grpc_channel_arg_get_integer(
+                &c->args->args[i],
+                (grpc_integer_options){initial_backoff_ms, 100, INT_MAX});
       } else if (0 == strcmp(c->args->args[i].key,
                              GRPC_ARG_MAX_RECONNECT_BACKOFF_MS)) {
         fixed_reconnect_backoff = false;
@@ -362,11 +370,11 @@ grpc_subchannel *grpc_subchannel_create(grpc_exec_ctx *exec_ctx,
     }
   }
   gpr_backoff_init(
-      &c->backoff_state,
+      &c->backoff_state, initial_backoff_ms,
       fixed_reconnect_backoff ? 1.0
                               : GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER,
       fixed_reconnect_backoff ? 0.0 : GRPC_SUBCHANNEL_RECONNECT_JITTER,
-      initial_backoff_ms, max_backoff_ms);
+      min_backoff_ms, max_backoff_ms);
   gpr_mu_init(&c->mu);
 
   return grpc_subchannel_index_register(exec_ctx, key, c);
@@ -478,7 +486,8 @@ static void maybe_start_connecting_locked(grpc_exec_ctx *exec_ctx,
       gpr_log(GPR_INFO, "Retry in %" PRId64 ".%09d seconds",
               time_til_next.tv_sec, time_til_next.tv_nsec);
     }
-    grpc_timer_init(exec_ctx, &c->alarm, c->next_attempt, on_alarm, c, now);
+    grpc_closure_init(&c->on_alarm, on_alarm, c, grpc_schedule_on_exec_ctx);
+    grpc_timer_init(exec_ctx, &c->alarm, c->next_attempt, &c->on_alarm, now);
   }
 }
 
@@ -503,7 +512,8 @@ void grpc_subchannel_notify_on_state_change(
     w->subchannel = c;
     w->pollset_set = interested_parties;
     w->notify = notify;
-    grpc_closure_init(&w->closure, on_external_state_watcher_done, w);
+    grpc_closure_init(&w->closure, on_external_state_watcher_done, w,
+                      grpc_schedule_on_exec_ctx);
     if (interested_parties != NULL) {
       grpc_pollset_set_add_pollset_set(exec_ctx, c->pollset_set,
                                        interested_parties);
@@ -598,18 +608,24 @@ static void publish_transport_locked(grpc_exec_ctx *exec_ctx,
   /* construct channel stack */
   grpc_channel_stack_builder *builder = grpc_channel_stack_builder_create();
   grpc_channel_stack_builder_set_channel_arguments(
-      builder, c->connecting_result.channel_args);
+      exec_ctx, builder, c->connecting_result.channel_args);
   grpc_channel_stack_builder_set_transport(builder,
                                            c->connecting_result.transport);
 
-  if (grpc_channel_init_create_stack(exec_ctx, builder,
-                                     GRPC_CLIENT_SUBCHANNEL)) {
-    con = grpc_channel_stack_builder_finish(exec_ctx, builder, 0, 1,
-                                            connection_destroy, NULL);
-  } else {
-    grpc_channel_stack_builder_destroy(builder);
+  if (!grpc_channel_init_create_stack(exec_ctx, builder,
+                                      GRPC_CLIENT_SUBCHANNEL)) {
+    grpc_channel_stack_builder_destroy(exec_ctx, builder);
     abort(); /* TODO(ctiller): what to do here (previously we just crashed) */
   }
+  grpc_error *error = grpc_channel_stack_builder_finish(
+      exec_ctx, builder, 0, 1, connection_destroy, NULL, (void **)&con);
+  if (error != GRPC_ERROR_NONE) {
+    const char *msg = grpc_error_string(error);
+    gpr_log(GPR_ERROR, "error initializing subchannel stack: %s", msg);
+    grpc_error_free_string(msg);
+    GRPC_ERROR_UNREF(error);
+    abort(); /* TODO(ctiller): what to do here? */
+  }
   stk = CHANNEL_STACK_FROM_CONNECTION(con);
   memset(&c->connecting_result, 0, sizeof(c->connecting_result));
 
@@ -618,7 +634,7 @@ static void publish_transport_locked(grpc_exec_ctx *exec_ctx,
   sw_subchannel->subchannel = c;
   sw_subchannel->connectivity_state = GRPC_CHANNEL_READY;
   grpc_closure_init(&sw_subchannel->closure, subchannel_on_child_state_changed,
-                    sw_subchannel);
+                    sw_subchannel, grpc_schedule_on_exec_ctx);
 
   if (c->disconnected) {
     gpr_free(sw_subchannel);
@@ -637,9 +653,7 @@ static void publish_transport_locked(grpc_exec_ctx *exec_ctx,
   GPR_ASSERT(gpr_atm_rel_cas(&c->connected_subchannel, 0, (gpr_atm)con));
 
   /* setup subchannel watching connected subchannel for changes; subchannel
-     ref
-     for connecting is donated
-     to the state watcher */
+     ref for connecting is donated to the state watcher */
   GRPC_SUBCHANNEL_WEAK_REF(c, "state_watcher");
   GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting");
   grpc_connected_subchannel_notify_on_state_change(
@@ -680,7 +694,7 @@ static void subchannel_connected(grpc_exec_ctx *exec_ctx, void *arg,
   }
   gpr_mu_unlock(&c->mu);
   GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connected");
-  grpc_channel_args_destroy(delete_channel_args);
+  grpc_channel_args_destroy(exec_ctx, delete_channel_args);
 }
 
 /*
diff --git a/src/core/ext/client_channel/subchannel.h b/src/core/ext/client_channel/subchannel.h
index 10bae620dfa6e196eb1bc3f72a9806ed79e7a83a..24aa9f73dca8002cef80144e81737bbd44a4fc6b 100644
--- a/src/core/ext/client_channel/subchannel.h
+++ b/src/core/ext/client_channel/subchannel.h
@@ -164,8 +164,6 @@ struct grpc_subchannel_args {
   size_t filter_count;
   /** Channel arguments to be supplied to the newly created channel */
   const grpc_channel_args *args;
-  /** Server name */
-  const char *server_name;
   /** Address to connect to */
   grpc_resolved_address *addr;
 };
diff --git a/src/core/ext/client_channel/subchannel_index.c b/src/core/ext/client_channel/subchannel_index.c
index 227013a7d78e144e704f4c1ab1fd7525e2a38c81..1ebe03ef1190ab59caab47fb2a5ba65a7d14da2f 100644
--- a/src/core/ext/client_channel/subchannel_index.c
+++ b/src/core/ext/client_channel/subchannel_index.c
@@ -86,7 +86,6 @@ static grpc_subchannel_key *create_key(
   } else {
     k->args.filters = NULL;
   }
-  k->args.server_name = gpr_strdup(args->server_name);
   k->args.addr = gpr_malloc(sizeof(grpc_resolved_address));
   k->args.addr->len = args->addr->len;
   if (k->args.addr->len > 0) {
@@ -113,8 +112,6 @@ static int subchannel_key_compare(grpc_subchannel_key *a,
   if (c != 0) return c;
   c = GPR_ICMP(a->args.filter_count, b->args.filter_count);
   if (c != 0) return c;
-  c = strcmp(a->args.server_name, b->args.server_name);
-  if (c != 0) return c;
   if (a->args.addr->len) {
     c = memcmp(a->args.addr->addr, b->args.addr->addr, a->args.addr->len);
     if (c != 0) return c;
@@ -131,8 +128,7 @@ void grpc_subchannel_key_destroy(grpc_exec_ctx *exec_ctx,
                                  grpc_subchannel_key *k) {
   grpc_connector_unref(exec_ctx, k->connector);
   gpr_free((grpc_channel_args *)k->args.filters);
-  grpc_channel_args_destroy((grpc_channel_args *)k->args.args);
-  gpr_free((void *)k->args.server_name);
+  grpc_channel_args_destroy(exec_ctx, (grpc_channel_args *)k->args.args);
   gpr_free(k->args.addr);
   gpr_free(k);
 }
diff --git a/src/core/ext/client_channel/uri_parser.c b/src/core/ext/client_channel/uri_parser.c
index 0fbc542ef8a960939ea6ba8226b00869d868f798..f8c946b2757c0d5c7d25d1452ffcb11a0405ded4 100644
--- a/src/core/ext/client_channel/uri_parser.c
+++ b/src/core/ext/client_channel/uri_parser.c
@@ -42,7 +42,6 @@
 #include <grpc/support/port_platform.h>
 #include <grpc/support/string_util.h>
 
-#include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/support/string.h"
 
 /** a size_t default value... maps to all 1's */
@@ -138,7 +137,6 @@ static int parse_fragment_or_query(const char *uri_text, size_t *i) {
   return 1;
 }
 
-static void do_nothing(void *ignored) {}
 static void parse_query_parts(grpc_uri *uri) {
   static const char *QUERY_PARTS_SEPARATOR = "&";
   static const char *QUERY_PARTS_VALUE_SEPARATOR = "=";
@@ -149,38 +147,32 @@ static void parse_query_parts(grpc_uri *uri) {
     uri->num_query_parts = 0;
     return;
   }
-  grpc_slice query_slice =
-      grpc_slice_new(uri->query, strlen(uri->query), do_nothing);
-  grpc_slice_buffer query_parts; /* the &-separated elements of the query */
-  grpc_slice_buffer query_param_parts; /* the =-separated subelements */
 
-  grpc_slice_buffer_init(&query_parts);
-  grpc_slice_buffer_init(&query_param_parts);
-
-  grpc_slice_split(query_slice, QUERY_PARTS_SEPARATOR, &query_parts);
-  uri->query_parts = gpr_malloc(query_parts.count * sizeof(char *));
-  uri->query_parts_values = gpr_malloc(query_parts.count * sizeof(char *));
-  uri->num_query_parts = query_parts.count;
-  for (size_t i = 0; i < query_parts.count; i++) {
-    grpc_slice_split(query_parts.slices[i], QUERY_PARTS_VALUE_SEPARATOR,
-                     &query_param_parts);
-    GPR_ASSERT(query_param_parts.count > 0);
-    uri->query_parts[i] =
-        grpc_dump_slice(query_param_parts.slices[0], GPR_DUMP_ASCII);
-    if (query_param_parts.count > 1) {
+  gpr_string_split(uri->query, QUERY_PARTS_SEPARATOR, &uri->query_parts,
+                   &uri->num_query_parts);
+  uri->query_parts_values = gpr_malloc(uri->num_query_parts * sizeof(char **));
+  for (size_t i = 0; i < uri->num_query_parts; i++) {
+    char **query_param_parts;
+    size_t num_query_param_parts;
+    char *full = uri->query_parts[i];
+    gpr_string_split(full, QUERY_PARTS_VALUE_SEPARATOR, &query_param_parts,
+                     &num_query_param_parts);
+    GPR_ASSERT(num_query_param_parts > 0);
+    uri->query_parts[i] = query_param_parts[0];
+    if (num_query_param_parts > 1) {
       /* TODO(dgq): only the first value after the separator is considered.
        * Perhaps all chars after the first separator for the query part should
        * be included, even if they include the separator. */
-      uri->query_parts_values[i] =
-          grpc_dump_slice(query_param_parts.slices[1], GPR_DUMP_ASCII);
+      uri->query_parts_values[i] = query_param_parts[1];
     } else {
       uri->query_parts_values[i] = NULL;
     }
-    grpc_slice_buffer_reset_and_unref(&query_param_parts);
+    for (size_t j = 2; j < num_query_param_parts; j++) {
+      gpr_free(query_param_parts[j]);
+    }
+    gpr_free(query_param_parts);
+    gpr_free(full);
   }
-  grpc_slice_buffer_destroy(&query_parts);
-  grpc_slice_buffer_destroy(&query_param_parts);
-  grpc_slice_unref(query_slice);
 }
 
 grpc_uri *grpc_uri_parse(const char *uri_text, int suppress_errors) {
diff --git a/src/core/ext/lb_policy/grpclb/grpclb.c b/src/core/ext/lb_policy/grpclb/grpclb.c
index 4262d2b9a462c55a80d34e31dd52c4cf9fcac0da..97f98df03af131c735e2718d2002a74c7f5affdf 100644
--- a/src/core/ext/lb_policy/grpclb/grpclb.c
+++ b/src/core/ext/lb_policy/grpclb/grpclb.c
@@ -106,6 +106,7 @@
 #include <grpc/support/string_util.h>
 #include <grpc/support/time.h>
 
+#include "src/core/ext/client_channel/client_channel.h"
 #include "src/core/ext/client_channel/client_channel_factory.h"
 #include "src/core/ext/client_channel/lb_policy_factory.h"
 #include "src/core/ext/client_channel/lb_policy_registry.h"
@@ -116,6 +117,7 @@
 #include "src/core/lib/iomgr/sockaddr.h"
 #include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/support/backoff.h"
 #include "src/core/lib/support/string.h"
@@ -123,10 +125,11 @@
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/transport/static_metadata.h"
 
-#define BACKOFF_MULTIPLIER 1.6
-#define BACKOFF_JITTER 0.2
-#define BACKOFF_MIN_SECONDS 10
-#define BACKOFF_MAX_SECONDS 60
+#define GRPC_GRPCLB_MIN_CONNECT_TIMEOUT_SECONDS 20
+#define GRPC_GRPCLB_INITIAL_CONNECT_BACKOFF_SECONDS 1
+#define GRPC_GRPCLB_RECONNECT_BACKOFF_MULTIPLIER 1.6
+#define GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS 120
+#define GRPC_GRPCLB_RECONNECT_JITTER 0.2
 
 int grpc_lb_glb_trace = 0;
 
@@ -178,8 +181,7 @@ static void wrapped_rr_closure(grpc_exec_ctx *exec_ctx, void *arg,
   wrapped_rr_closure_arg *wc_arg = arg;
 
   GPR_ASSERT(wc_arg->wrapped_closure != NULL);
-  grpc_exec_ctx_sched(exec_ctx, wc_arg->wrapped_closure, GRPC_ERROR_REF(error),
-                      NULL);
+  grpc_closure_sched(exec_ctx, wc_arg->wrapped_closure, GRPC_ERROR_REF(error));
 
   if (wc_arg->rr_policy != NULL) {
     /* if *target is NULL, no pick has been made by the RR policy (eg, all
@@ -246,7 +248,8 @@ static void add_pending_pick(pending_pick **root,
       pick_args->lb_token_mdelem_storage;
   pp->wrapped_on_complete_arg.free_when_done = pp;
   grpc_closure_init(&pp->wrapped_on_complete_arg.wrapper_closure,
-                    wrapped_rr_closure, &pp->wrapped_on_complete_arg);
+                    wrapped_rr_closure, &pp->wrapped_on_complete_arg,
+                    grpc_schedule_on_exec_ctx);
   *root = pp;
 }
 
@@ -266,7 +269,8 @@ static void add_pending_ping(pending_ping **root, grpc_closure *notify) {
   pping->wrapped_notify_arg.free_when_done = pping;
   pping->next = *root;
   grpc_closure_init(&pping->wrapped_notify_arg.wrapper_closure,
-                    wrapped_rr_closure, &pping->wrapped_notify_arg);
+                    wrapped_rr_closure, &pping->wrapped_notify_arg,
+                    grpc_schedule_on_exec_ctx);
   *root = pping;
 }
 
@@ -323,6 +327,9 @@ typedef struct glb_lb_policy {
   /* A response from the LB server has been received. Process it */
   grpc_closure lb_on_response_received;
 
+  /* LB call retry timer callback. */
+  grpc_closure lb_on_call_retry;
+
   grpc_call *lb_call; /* streaming call to the LB server, */
 
   grpc_metadata_array lb_initial_metadata_recv; /* initial MD from LB server */
@@ -383,8 +390,8 @@ static bool is_server_valid(const grpc_grpclb_server *server, size_t idx,
 static void *lb_token_copy(void *token) {
   return token == NULL ? NULL : GRPC_MDELEM_REF(token);
 }
-static void lb_token_destroy(void *token) {
-  if (token != NULL) GRPC_MDELEM_UNREF(token);
+static void lb_token_destroy(grpc_exec_ctx *exec_ctx, void *token) {
+  if (token != NULL) GRPC_MDELEM_UNREF(exec_ctx, token);
 }
 static int lb_token_cmp(void *token1, void *token2) {
   if (token1 > token2) return 1;
@@ -418,7 +425,7 @@ static void parse_server(const grpc_grpclb_server *server,
 
 /* Returns addresses extracted from \a serverlist. */
 static grpc_lb_addresses *process_serverlist_locked(
-    const grpc_grpclb_serverlist *serverlist) {
+    grpc_exec_ctx *exec_ctx, const grpc_grpclb_serverlist *serverlist) {
   size_t num_valid = 0;
   /* first pass: count how many are valid in order to allocate the necessary
    * memory in a single block */
@@ -454,8 +461,8 @@ static grpc_lb_addresses *process_serverlist_locked(
           strnlen(server->load_balance_token, lb_token_max_length);
       grpc_mdstr *lb_token_mdstr = grpc_mdstr_from_buffer(
           (uint8_t *)server->load_balance_token, lb_token_length);
-      user_data = grpc_mdelem_from_metadata_strings(GRPC_MDSTR_LB_TOKEN,
-                                                    lb_token_mdstr);
+      user_data = grpc_mdelem_from_metadata_strings(
+          exec_ctx, GRPC_MDSTR_LB_TOKEN, lb_token_mdstr);
     } else {
       char *uri = grpc_sockaddr_to_uri(&addr);
       gpr_log(GPR_INFO,
@@ -578,7 +585,8 @@ static grpc_lb_policy *create_rr_locked(
   grpc_lb_policy_args args;
   memset(&args, 0, sizeof(args));
   args.client_channel_factory = glb_policy->cc_factory;
-  grpc_lb_addresses *addresses = process_serverlist_locked(serverlist);
+  grpc_lb_addresses *addresses =
+      process_serverlist_locked(exec_ctx, serverlist);
 
   // Replace the LB addresses in the channel args that we pass down to
   // the subchannel.
@@ -590,8 +598,8 @@ static grpc_lb_policy *create_rr_locked(
 
   grpc_lb_policy *rr = grpc_lb_policy_create(exec_ctx, "round_robin", &args);
   GPR_ASSERT(rr != NULL);
-  grpc_lb_addresses_destroy(addresses);
-  grpc_channel_args_destroy(args.args);
+  grpc_lb_addresses_destroy(exec_ctx, addresses);
+  grpc_channel_args_destroy(exec_ctx, args.args);
   return rr;
 }
 
@@ -665,7 +673,7 @@ static void rr_handover_locked(grpc_exec_ctx *exec_ctx,
       gpr_malloc(sizeof(rr_connectivity_data));
   memset(rr_connectivity, 0, sizeof(rr_connectivity_data));
   grpc_closure_init(&rr_connectivity->on_change, glb_rr_connectivity_changed,
-                    rr_connectivity);
+                    rr_connectivity, grpc_schedule_on_exec_ctx);
   rr_connectivity->glb_policy = glb_policy;
   rr_connectivity->state = new_rr_state;
 
@@ -742,12 +750,6 @@ static void glb_rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg,
 static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx,
                                   grpc_lb_policy_factory *factory,
                                   grpc_lb_policy_args *args) {
-  /* Get server name. */
-  const grpc_arg *arg =
-      grpc_channel_args_find(args->args, GRPC_ARG_SERVER_NAME);
-  const char *server_name =
-      arg != NULL && arg->type == GRPC_ARG_STRING ? arg->value.string : NULL;
-
   /* Count the number of gRPC-LB addresses. There must be at least one.
    * TODO(roth): For now, we ignore non-balancer addresses, but in the
    * future, we may change the behavior such that we fall back to using
@@ -755,7 +757,8 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx,
    * time, this should be changed to allow a list with no balancer addresses,
    * since the resolver might fail to return a balancer address even when
    * this is the right LB policy to use. */
-  arg = grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES);
+  const grpc_arg *arg =
+      grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES);
   GPR_ASSERT(arg != NULL && arg->type == GRPC_ARG_POINTER);
   grpc_lb_addresses *addresses = arg->value.pointer.p;
   size_t num_grpclb_addrs = 0;
@@ -767,13 +770,25 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx,
   glb_lb_policy *glb_policy = gpr_malloc(sizeof(*glb_policy));
   memset(glb_policy, 0, sizeof(*glb_policy));
 
+  /* Get server name. */
+  arg = grpc_channel_args_find(args->args, GRPC_ARG_SERVER_URI);
+  GPR_ASSERT(arg != NULL);
+  GPR_ASSERT(arg->type == GRPC_ARG_STRING);
+  grpc_uri *uri = grpc_uri_parse(arg->value.string, true);
+  GPR_ASSERT(uri->path[0] != '\0');
+  glb_policy->server_name =
+      gpr_strdup(uri->path[0] == '/' ? uri->path + 1 : uri->path);
+  if (grpc_lb_glb_trace) {
+    gpr_log(GPR_INFO, "Will use '%s' as the server name for LB request.",
+            glb_policy->server_name);
+  }
+  grpc_uri_destroy(uri);
+
   /* All input addresses in addresses come from a resolver that claims
    * they are LB services. It's the resolver's responsibility to make sure
-   * this
-   * policy is only instantiated and used in that case.
+   * this policy is only instantiated and used in that case.
    *
    * Create a client channel over them to communicate with a LB service */
-  glb_policy->server_name = gpr_strdup(server_name);
   glb_policy->cc_factory = args->client_channel_factory;
   glb_policy->args = grpc_channel_args_copy(args->args);
   GPR_ASSERT(glb_policy->cc_factory != NULL);
@@ -817,15 +832,20 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx,
    * We need the LB channel to return addresses with is_balancer=false
    * so that it does not wind up recursively using the grpclb LB policy,
    * as per the special case logic in client_channel.c.
+   *
+   * Finally, we also strip out the channel arg for the server URI,
+   * since that will be different for the LB channel than for the parent
+   * channel.  (The client channel factory will re-add this arg with
+   * the right value.)
    */
-  static const char *keys_to_remove[] = {GRPC_ARG_LB_POLICY_NAME,
-                                         GRPC_ARG_LB_ADDRESSES};
+  static const char *keys_to_remove[] = {
+      GRPC_ARG_LB_POLICY_NAME, GRPC_ARG_LB_ADDRESSES, GRPC_ARG_SERVER_URI};
   grpc_channel_args *new_args = grpc_channel_args_copy_and_remove(
       args->args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove));
   glb_policy->lb_channel = grpc_client_channel_factory_create_channel(
       exec_ctx, glb_policy->cc_factory, target_uri_str,
       GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, new_args);
-  grpc_channel_args_destroy(new_args);
+  grpc_channel_args_destroy(exec_ctx, new_args);
 
   gpr_free(target_uri_str);
   for (size_t i = 0; i < num_grpclb_addrs; i++) {
@@ -851,7 +871,7 @@ static void glb_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
   GPR_ASSERT(glb_policy->pending_picks == NULL);
   GPR_ASSERT(glb_policy->pending_pings == NULL);
   gpr_free((void *)glb_policy->server_name);
-  grpc_channel_args_destroy(glb_policy->args);
+  grpc_channel_args_destroy(exec_ctx, glb_policy->args);
   grpc_channel_destroy(glb_policy->lb_channel);
   glb_policy->lb_channel = NULL;
   grpc_connectivity_state_destroy(exec_ctx, &glb_policy->state_tracker);
@@ -894,15 +914,15 @@ static void glb_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
   while (pp != NULL) {
     pending_pick *next = pp->next;
     *pp->target = NULL;
-    grpc_exec_ctx_sched(exec_ctx, &pp->wrapped_on_complete_arg.wrapper_closure,
-                        GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, &pp->wrapped_on_complete_arg.wrapper_closure,
+                       GRPC_ERROR_NONE);
     pp = next;
   }
 
   while (pping != NULL) {
     pending_ping *next = pping->next;
-    grpc_exec_ctx_sched(exec_ctx, &pping->wrapped_notify_arg.wrapper_closure,
-                        GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, &pping->wrapped_notify_arg.wrapper_closure,
+                       GRPC_ERROR_NONE);
     pping = next;
   }
 }
@@ -918,9 +938,9 @@ static void glb_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
     pending_pick *next = pp->next;
     if (pp->target == target) {
       *target = NULL;
-      grpc_exec_ctx_sched(
+      grpc_closure_sched(
           exec_ctx, &pp->wrapped_on_complete_arg.wrapper_closure,
-          GRPC_ERROR_CREATE_REFERENCING("Pick Cancelled", &error, 1), NULL);
+          GRPC_ERROR_CREATE_REFERENCING("Pick Cancelled", &error, 1));
     } else {
       pp->next = glb_policy->pending_picks;
       glb_policy->pending_picks = pp;
@@ -943,9 +963,9 @@ static void glb_cancel_picks(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
     pending_pick *next = pp->next;
     if ((pp->pick_args.initial_metadata_flags & initial_metadata_flags_mask) ==
         initial_metadata_flags_eq) {
-      grpc_exec_ctx_sched(
+      grpc_closure_sched(
           exec_ctx, &pp->wrapped_on_complete_arg.wrapper_closure,
-          GRPC_ERROR_CREATE_REFERENCING("Pick Cancelled", &error, 1), NULL);
+          GRPC_ERROR_CREATE_REFERENCING("Pick Cancelled", &error, 1));
     } else {
       pp->next = glb_policy->pending_picks;
       glb_policy->pending_picks = pp;
@@ -980,11 +1000,10 @@ static int glb_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
                     grpc_closure *on_complete) {
   if (pick_args->lb_token_mdelem_storage == NULL) {
     *target = NULL;
-    grpc_exec_ctx_sched(
+    grpc_closure_sched(
         exec_ctx, on_complete,
         GRPC_ERROR_CREATE("No mdelem storage for the LB token. Load reporting "
-                          "won't work without it. Failing"),
-        NULL);
+                          "won't work without it. Failing"));
     return 0;
   }
 
@@ -1003,7 +1022,8 @@ static int glb_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
     wrapped_rr_closure_arg *wc_arg = gpr_malloc(sizeof(wrapped_rr_closure_arg));
     memset(wc_arg, 0, sizeof(wrapped_rr_closure_arg));
 
-    grpc_closure_init(&wc_arg->wrapper_closure, wrapped_rr_closure, wc_arg);
+    grpc_closure_init(&wc_arg->wrapper_closure, wrapped_rr_closure, wc_arg,
+                      grpc_schedule_on_exec_ctx);
     wc_arg->rr_policy = glb_policy->rr_policy;
     wc_arg->target = target;
     wc_arg->wrapped_closure = on_complete;
@@ -1074,7 +1094,8 @@ static void lb_on_server_status_received(grpc_exec_ctx *exec_ctx, void *arg,
                                          grpc_error *error);
 static void lb_on_response_received(grpc_exec_ctx *exec_ctx, void *arg,
                                     grpc_error *error);
-static void lb_call_init_locked(glb_lb_policy *glb_policy) {
+static void lb_call_init_locked(grpc_exec_ctx *exec_ctx,
+                                glb_lb_policy *glb_policy) {
   GPR_ASSERT(glb_policy->server_name != NULL);
   GPR_ASSERT(glb_policy->server_name[0] != '\0');
   GPR_ASSERT(!glb_policy->shutting_down);
@@ -1083,7 +1104,7 @@ static void lb_call_init_locked(glb_lb_policy *glb_policy) {
    * glb_policy->base.interested_parties, which is comprised of the polling
    * entities from \a client_channel. */
   glb_policy->lb_call = grpc_channel_create_pollset_set_call(
-      glb_policy->lb_channel, NULL, GRPC_PROPAGATE_DEFAULTS,
+      exec_ctx, glb_policy->lb_channel, NULL, GRPC_PROPAGATE_DEFAULTS,
       glb_policy->base.interested_parties,
       "/grpc.lb.v1.LoadBalancer/BalanceLoad", glb_policy->server_name,
       glb_policy->deadline, NULL);
@@ -1096,20 +1117,25 @@ static void lb_call_init_locked(glb_lb_policy *glb_policy) {
   grpc_slice request_payload_slice = grpc_grpclb_request_encode(request);
   glb_policy->lb_request_payload =
       grpc_raw_byte_buffer_create(&request_payload_slice, 1);
-  grpc_slice_unref(request_payload_slice);
+  grpc_slice_unref_internal(exec_ctx, request_payload_slice);
   grpc_grpclb_request_destroy(request);
 
   glb_policy->lb_call_status_details = NULL;
   glb_policy->lb_call_status_details_capacity = 0;
 
   grpc_closure_init(&glb_policy->lb_on_server_status_received,
-                    lb_on_server_status_received, glb_policy);
+                    lb_on_server_status_received, glb_policy,
+                    grpc_schedule_on_exec_ctx);
   grpc_closure_init(&glb_policy->lb_on_response_received,
-                    lb_on_response_received, glb_policy);
-
-  gpr_backoff_init(&glb_policy->lb_call_backoff_state, BACKOFF_MULTIPLIER,
-                   BACKOFF_JITTER, BACKOFF_MIN_SECONDS * 1000,
-                   BACKOFF_MAX_SECONDS * 1000);
+                    lb_on_response_received, glb_policy,
+                    grpc_schedule_on_exec_ctx);
+
+  gpr_backoff_init(&glb_policy->lb_call_backoff_state,
+                   GRPC_GRPCLB_INITIAL_CONNECT_BACKOFF_SECONDS,
+                   GRPC_GRPCLB_RECONNECT_BACKOFF_MULTIPLIER,
+                   GRPC_GRPCLB_RECONNECT_JITTER,
+                   GRPC_GRPCLB_MIN_CONNECT_TIMEOUT_SECONDS * 1000,
+                   GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS * 1000);
 }
 
 static void lb_call_destroy_locked(glb_lb_policy *glb_policy) {
@@ -1132,7 +1158,7 @@ static void query_for_backends_locked(grpc_exec_ctx *exec_ctx,
   GPR_ASSERT(glb_policy->lb_channel != NULL);
   if (glb_policy->shutting_down) return;
 
-  lb_call_init_locked(glb_policy);
+  lb_call_init_locked(exec_ctx, glb_policy);
 
   if (grpc_lb_glb_trace) {
     gpr_log(GPR_INFO, "Query for backends (grpclb: %p, lb_call: %p)",
@@ -1217,7 +1243,7 @@ static void lb_on_response_received(grpc_exec_ctx *exec_ctx, void *arg,
         grpc_grpclb_response_parse_serverlist(response_slice);
     if (serverlist != NULL) {
       GPR_ASSERT(glb_policy->lb_call != NULL);
-      grpc_slice_unref(response_slice);
+      grpc_slice_unref_internal(exec_ctx, response_slice);
       if (grpc_lb_glb_trace) {
         gpr_log(GPR_INFO, "Serverlist with %lu servers received",
                 (unsigned long)serverlist->num_servers);
@@ -1261,7 +1287,7 @@ static void lb_on_response_received(grpc_exec_ctx *exec_ctx, void *arg,
     } else { /* serverlist == NULL */
       gpr_log(GPR_ERROR, "Invalid LB response received: '%s'. Ignoring.",
               grpc_dump_slice(response_slice, GPR_DUMP_ASCII | GPR_DUMP_HEX));
-      grpc_slice_unref(response_slice);
+      grpc_slice_unref_internal(exec_ctx, response_slice);
     }
 
     if (!glb_policy->shutting_down) {
@@ -1341,8 +1367,10 @@ static void lb_on_server_status_received(grpc_exec_ctx *exec_ctx, void *arg,
       }
     }
     GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "grpclb_retry_timer");
+    grpc_closure_init(&glb_policy->lb_on_call_retry, lb_call_on_retry_timer,
+                      glb_policy, grpc_schedule_on_exec_ctx);
     grpc_timer_init(exec_ctx, &glb_policy->lb_call_retry_timer, next_try,
-                    lb_call_on_retry_timer, glb_policy, now);
+                    &glb_policy->lb_on_call_retry, now);
   }
   gpr_mu_unlock(&glb_policy->mu);
   GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base,
diff --git a/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c b/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c
index afecb716fb0a405ea792a29e89bd4498284b8337..e352e0396f8f5de3abd72e0f2b66418599f1ee95 100644
--- a/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c
+++ b/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c
@@ -1,35 +1,3 @@
-/*
- *
- * Copyright 2016, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
 /* Automatically generated nanopb constant definitions */
 /* Generated by nanopb-0.3.7-dev */
 
diff --git a/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h b/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h
index e36d0966f85aabc0a1fcf2864c23177ffc672625..725aa7e386f9384732df22e49811c12c37581a67 100644
--- a/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h
+++ b/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h
@@ -1,35 +1,3 @@
-/*
- *
- * Copyright 2016, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
 /* Automatically generated nanopb header */
 /* Generated by nanopb-0.3.7-dev */
 
diff --git a/src/core/ext/lb_policy/pick_first/pick_first.c b/src/core/ext/lb_policy/pick_first/pick_first.c
index c69f773e7828a509d4a9880ffcfaf13470f9683b..821becff69d66e910fa077eb8c54535ec5fba9d2 100644
--- a/src/core/ext/lb_policy/pick_first/pick_first.c
+++ b/src/core/ext/lb_policy/pick_first/pick_first.c
@@ -120,7 +120,7 @@ static void pf_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
   while (pp != NULL) {
     pending_pick *next = pp->next;
     *pp->target = NULL;
-    grpc_exec_ctx_sched(exec_ctx, pp->on_complete, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, pp->on_complete, GRPC_ERROR_NONE);
     gpr_free(pp);
     pp = next;
   }
@@ -138,9 +138,9 @@ static void pf_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
     pending_pick *next = pp->next;
     if (pp->target == target) {
       *target = NULL;
-      grpc_exec_ctx_sched(
+      grpc_closure_sched(
           exec_ctx, pp->on_complete,
-          GRPC_ERROR_CREATE_REFERENCING("Pick Cancelled", &error, 1), NULL);
+          GRPC_ERROR_CREATE_REFERENCING("Pick Cancelled", &error, 1));
       gpr_free(pp);
     } else {
       pp->next = p->pending_picks;
@@ -165,9 +165,9 @@ static void pf_cancel_picks(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
     pending_pick *next = pp->next;
     if ((pp->initial_metadata_flags & initial_metadata_flags_mask) ==
         initial_metadata_flags_eq) {
-      grpc_exec_ctx_sched(
+      grpc_closure_sched(
           exec_ctx, pp->on_complete,
-          GRPC_ERROR_CREATE_REFERENCING("Pick Cancelled", &error, 1), NULL);
+          GRPC_ERROR_CREATE_REFERENCING("Pick Cancelled", &error, 1));
       gpr_free(pp);
     } else {
       pp->next = p->pending_picks;
@@ -306,14 +306,15 @@ static void pf_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg,
         /* drop the pick list: we are connected now */
         GRPC_LB_POLICY_WEAK_REF(&p->base, "destroy_subchannels");
         gpr_atm_rel_store(&p->selected, (gpr_atm)selected);
-        grpc_exec_ctx_sched(exec_ctx,
-                            grpc_closure_create(destroy_subchannels, p),
-                            GRPC_ERROR_NONE, NULL);
+        grpc_closure_sched(exec_ctx,
+                           grpc_closure_create(destroy_subchannels, p,
+                                               grpc_schedule_on_exec_ctx),
+                           GRPC_ERROR_NONE);
         /* update any calls that were waiting for a pick */
         while ((pp = p->pending_picks)) {
           p->pending_picks = pp->next;
           *pp->target = GRPC_CONNECTED_SUBCHANNEL_REF(selected, "picked");
-          grpc_exec_ctx_sched(exec_ctx, pp->on_complete, GRPC_ERROR_NONE, NULL);
+          grpc_closure_sched(exec_ctx, pp->on_complete, GRPC_ERROR_NONE);
           gpr_free(pp);
         }
         grpc_connected_subchannel_notify_on_state_change(
@@ -366,8 +367,7 @@ static void pf_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg,
           while ((pp = p->pending_picks)) {
             p->pending_picks = pp->next;
             *pp->target = NULL;
-            grpc_exec_ctx_sched(exec_ctx, pp->on_complete, GRPC_ERROR_NONE,
-                                NULL);
+            grpc_closure_sched(exec_ctx, pp->on_complete, GRPC_ERROR_NONE);
             gpr_free(pp);
           }
           GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base,
@@ -419,8 +419,7 @@ static void pf_ping_one(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
   if (selected) {
     grpc_connected_subchannel_ping(exec_ctx, selected, closure);
   } else {
-    grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_CREATE("Not connected"),
-                        NULL);
+    grpc_closure_sched(exec_ctx, closure, GRPC_ERROR_CREATE("Not connected"));
   }
 }
 
@@ -438,15 +437,10 @@ static grpc_lb_policy *create_pick_first(grpc_exec_ctx *exec_ctx,
                                          grpc_lb_policy_args *args) {
   GPR_ASSERT(args->client_channel_factory != NULL);
 
-  /* Get server name. */
-  const grpc_arg *arg =
-      grpc_channel_args_find(args->args, GRPC_ARG_SERVER_NAME);
-  const char *server_name =
-      arg != NULL && arg->type == GRPC_ARG_STRING ? arg->value.string : NULL;
-
   /* Find the number of backend addresses. We ignore balancer
    * addresses, since we don't know how to handle them. */
-  arg = grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES);
+  const grpc_arg *arg =
+      grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES);
   GPR_ASSERT(arg != NULL && arg->type == GRPC_ARG_POINTER);
   grpc_lb_addresses *addresses = arg->value.pointer.p;
   size_t num_addrs = 0;
@@ -472,9 +466,6 @@ static grpc_lb_policy *create_pick_first(grpc_exec_ctx *exec_ctx,
     }
 
     memset(&sc_args, 0, sizeof(grpc_subchannel_args));
-    /* server_name will be copied as part of the subchannel creation. This makes
-     * the copying of server_name (a borrowed pointer) OK. */
-    sc_args.server_name = server_name;
     sc_args.addr = &addresses->addresses[i].address;
     sc_args.args = args->args;
 
@@ -493,7 +484,8 @@ static grpc_lb_policy *create_pick_first(grpc_exec_ctx *exec_ctx,
   p->num_subchannels = subchannel_idx;
 
   grpc_lb_policy_init(&p->base, &pick_first_lb_policy_vtable);
-  grpc_closure_init(&p->connectivity_changed, pf_connectivity_changed, p);
+  grpc_closure_init(&p->connectivity_changed, pf_connectivity_changed, p,
+                    grpc_schedule_on_exec_ctx);
   gpr_mu_init(&p->mu);
   return &p->base;
 }
diff --git a/src/core/ext/lb_policy/round_robin/round_robin.c b/src/core/ext/lb_policy/round_robin/round_robin.c
index 59f84054c40739474b7cf5e9b498aaf016d90dbe..cb679489c3e019ce1d303cc86ef8e5436f1504ea 100644
--- a/src/core/ext/lb_policy/round_robin/round_robin.c
+++ b/src/core/ext/lb_policy/round_robin/round_robin.c
@@ -284,7 +284,7 @@ static void rr_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
     GRPC_SUBCHANNEL_UNREF(exec_ctx, sd->subchannel, "rr_destroy");
     if (sd->user_data != NULL) {
       GPR_ASSERT(sd->user_data_vtable != NULL);
-      sd->user_data_vtable->destroy(sd->user_data);
+      sd->user_data_vtable->destroy(exec_ctx, sd->user_data);
     }
     gpr_free(sd);
   }
@@ -321,8 +321,8 @@ static void rr_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
   while ((pp = p->pending_picks)) {
     p->pending_picks = pp->next;
     *pp->target = NULL;
-    grpc_exec_ctx_sched(exec_ctx, pp->on_complete,
-                        GRPC_ERROR_CREATE("Channel Shutdown"), NULL);
+    grpc_closure_sched(exec_ctx, pp->on_complete,
+                       GRPC_ERROR_CREATE("Channel Shutdown"));
     gpr_free(pp);
   }
   grpc_connectivity_state_set(
@@ -348,9 +348,9 @@ static void rr_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
     pending_pick *next = pp->next;
     if (pp->target == target) {
       *target = NULL;
-      grpc_exec_ctx_sched(
+      grpc_closure_sched(
           exec_ctx, pp->on_complete,
-          GRPC_ERROR_CREATE_REFERENCING("Pick cancelled", &error, 1), NULL);
+          GRPC_ERROR_CREATE_REFERENCING("Pick cancelled", &error, 1));
       gpr_free(pp);
     } else {
       pp->next = p->pending_picks;
@@ -376,9 +376,9 @@ static void rr_cancel_picks(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
     if ((pp->initial_metadata_flags & initial_metadata_flags_mask) ==
         initial_metadata_flags_eq) {
       *pp->target = NULL;
-      grpc_exec_ctx_sched(
+      grpc_closure_sched(
           exec_ctx, pp->on_complete,
-          GRPC_ERROR_CREATE_REFERENCING("Pick cancelled", &error, 1), NULL);
+          GRPC_ERROR_CREATE_REFERENCING("Pick cancelled", &error, 1));
       gpr_free(pp);
     } else {
       pp->next = p->pending_picks;
@@ -581,7 +581,7 @@ static void rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg,
                   "[RR CONN CHANGED] TARGET <-- SUBCHANNEL %p (NODE %p)",
                   (void *)selected->subchannel, (void *)selected);
         }
-        grpc_exec_ctx_sched(exec_ctx, pp->on_complete, GRPC_ERROR_NONE, NULL);
+        grpc_closure_sched(exec_ctx, pp->on_complete, GRPC_ERROR_NONE);
         gpr_free(pp);
       }
       update_lb_connectivity_status(exec_ctx, sd, error);
@@ -634,7 +634,7 @@ static void rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg,
         while ((pp = p->pending_picks)) {
           p->pending_picks = pp->next;
           *pp->target = NULL;
-          grpc_exec_ctx_sched(exec_ctx, pp->on_complete, GRPC_ERROR_NONE, NULL);
+          grpc_closure_sched(exec_ctx, pp->on_complete, GRPC_ERROR_NONE);
           gpr_free(pp);
         }
       }
@@ -684,8 +684,8 @@ static void rr_ping_one(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
     GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, target, "rr_picked");
   } else {
     gpr_mu_unlock(&p->mu);
-    grpc_exec_ctx_sched(exec_ctx, closure,
-                        GRPC_ERROR_CREATE("Round Robin not connected"), NULL);
+    grpc_closure_sched(exec_ctx, closure,
+                       GRPC_ERROR_CREATE("Round Robin not connected"));
   }
 }
 
@@ -703,15 +703,10 @@ static grpc_lb_policy *round_robin_create(grpc_exec_ctx *exec_ctx,
                                           grpc_lb_policy_args *args) {
   GPR_ASSERT(args->client_channel_factory != NULL);
 
-  /* Get server name. */
-  const grpc_arg *arg =
-      grpc_channel_args_find(args->args, GRPC_ARG_SERVER_NAME);
-  const char *server_name =
-      arg != NULL && arg->type == GRPC_ARG_STRING ? arg->value.string : NULL;
-
   /* Find the number of backend addresses. We ignore balancer
    * addresses, since we don't know how to handle them. */
-  arg = grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES);
+  const grpc_arg *arg =
+      grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES);
   GPR_ASSERT(arg != NULL && arg->type == GRPC_ARG_POINTER);
   grpc_lb_addresses *addresses = arg->value.pointer.p;
   size_t num_addrs = 0;
@@ -734,9 +729,6 @@ static grpc_lb_policy *round_robin_create(grpc_exec_ctx *exec_ctx,
     if (addresses->addresses[i].is_balancer) continue;
 
     memset(&sc_args, 0, sizeof(grpc_subchannel_args));
-    /* server_name will be copied as part of the subchannel creation. This makes
-     * the copying of server_name (a borrowed pointer) OK. */
-    sc_args.server_name = server_name;
     sc_args.addr = &addresses->addresses[i].address;
     sc_args.args = args->args;
 
@@ -757,7 +749,7 @@ static grpc_lb_policy *round_robin_create(grpc_exec_ctx *exec_ctx,
       }
       ++subchannel_idx;
       grpc_closure_init(&sd->connectivity_changed_closure,
-                        rr_connectivity_changed, sd);
+                        rr_connectivity_changed, sd, grpc_schedule_on_exec_ctx);
     }
   }
   if (subchannel_idx == 0) {
diff --git a/src/core/ext/load_reporting/load_reporting.c b/src/core/ext/load_reporting/load_reporting.c
index df1ea0ec9ac682cc66ec829ce769fffeeb12f32b..37b06a737fb025c755f3a8df98ac86453d286a34 100644
--- a/src/core/ext/load_reporting/load_reporting.c
+++ b/src/core/ext/load_reporting/load_reporting.c
@@ -53,7 +53,8 @@ static bool is_load_reporting_enabled(const grpc_channel_args *a) {
   return false;
 }
 
-static bool maybe_add_load_reporting_filter(grpc_channel_stack_builder *builder,
+static bool maybe_add_load_reporting_filter(grpc_exec_ctx *exec_ctx,
+                                            grpc_channel_stack_builder *builder,
                                             void *arg) {
   const grpc_channel_args *args =
       grpc_channel_stack_builder_get_channel_arguments(builder);
diff --git a/src/core/ext/load_reporting/load_reporting_filter.c b/src/core/ext/load_reporting/load_reporting_filter.c
index b810e20bb98d56e6e70034a95f99b6a4731346a3..07ef10e6a836b6ea250c73f9f3fc57ba9f95b111 100644
--- a/src/core/ext/load_reporting/load_reporting_filter.c
+++ b/src/core/ext/load_reporting/load_reporting_filter.c
@@ -68,7 +68,8 @@ typedef struct {
   grpc_exec_ctx *exec_ctx;
 } recv_md_filter_args;
 
-static grpc_mdelem *recv_md_filter(void *user_data, grpc_mdelem *md) {
+static grpc_mdelem *recv_md_filter(grpc_exec_ctx *exec_ctx, void *user_data,
+                                   grpc_mdelem *md) {
   recv_md_filter_args *a = user_data;
   grpc_call_element *elem = a->elem;
   call_data *calld = elem->call_data;
@@ -92,8 +93,8 @@ static void on_initial_md_ready(grpc_exec_ctx *exec_ctx, void *user_data,
     recv_md_filter_args a;
     a.elem = elem;
     a.exec_ctx = exec_ctx;
-    grpc_metadata_batch_filter(calld->recv_initial_metadata, recv_md_filter,
-                               &a);
+    grpc_metadata_batch_filter(exec_ctx, calld->recv_initial_metadata,
+                               recv_md_filter, &a);
     if (calld->service_method == NULL) {
       err =
           grpc_error_add_child(err, GRPC_ERROR_CREATE("Missing :path header"));
@@ -114,7 +115,8 @@ static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
   memset(calld, 0, sizeof(call_data));
 
   calld->id = (intptr_t)args->call_stack;
-  grpc_closure_init(&calld->on_initial_md_ready, on_initial_md_ready, elem);
+  grpc_closure_init(&calld->on_initial_md_ready, on_initial_md_ready, elem,
+                    grpc_schedule_on_exec_ctx);
 
   /* TODO(dgq): do something with the data
   channel_data *chand = elem->channel_data;
@@ -152,9 +154,9 @@ static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
 }
 
 /* Constructor for channel_data */
-static void init_channel_elem(grpc_exec_ctx *exec_ctx,
-                              grpc_channel_element *elem,
-                              grpc_channel_element_args *args) {
+static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx,
+                                     grpc_channel_element *elem,
+                                     grpc_channel_element_args *args) {
   GPR_ASSERT(!args->is_last);
 
   channel_data *chand = elem->channel_data;
@@ -171,6 +173,8 @@ static void init_channel_elem(grpc_exec_ctx *exec_ctx,
                                                 NULL,
                                                 NULL};
                                                 */
+
+  return GRPC_ERROR_NONE;
 }
 
 /* Destructor for channel data */
@@ -189,7 +193,8 @@ static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
   */
 }
 
-static grpc_mdelem *lr_trailing_md_filter(void *user_data, grpc_mdelem *md) {
+static grpc_mdelem *lr_trailing_md_filter(grpc_exec_ctx *exec_ctx,
+                                          void *user_data, grpc_mdelem *md) {
   grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
 
@@ -213,7 +218,7 @@ static void lr_start_transport_stream_op(grpc_exec_ctx *exec_ctx,
     calld->ops_recv_initial_metadata_ready = op->recv_initial_metadata_ready;
     op->recv_initial_metadata_ready = &calld->on_initial_md_ready;
   } else if (op->send_trailing_metadata) {
-    grpc_metadata_batch_filter(op->send_trailing_metadata,
+    grpc_metadata_batch_filter(exec_ctx, op->send_trailing_metadata,
                                lr_trailing_md_filter, elem);
   }
   grpc_call_next_op(exec_ctx, elem, op);
diff --git a/src/core/ext/resolver/README.md b/src/core/ext/resolver/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..b0e234e96a561460707c9afed27e80b92390862a
--- /dev/null
+++ b/src/core/ext/resolver/README.md
@@ -0,0 +1,4 @@
+# Resolver
+
+Implementations of various name resolution schemes.
+See the [naming spec](/doc/naming.md).
diff --git a/src/core/ext/resolver/dns/native/dns_resolver.c b/src/core/ext/resolver/dns/native/dns_resolver.c
index 665439f360636bd0714639b90f01389592410f3f..655d9dc586c2182631b4c2263522875538059f2f 100644
--- a/src/core/ext/resolver/dns/native/dns_resolver.c
+++ b/src/core/ext/resolver/dns/native/dns_resolver.c
@@ -46,10 +46,11 @@
 #include "src/core/lib/support/backoff.h"
 #include "src/core/lib/support/string.h"
 
-#define BACKOFF_MULTIPLIER 1.6
-#define BACKOFF_JITTER 0.2
-#define BACKOFF_MIN_SECONDS 1
-#define BACKOFF_MAX_SECONDS 120
+#define GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS 1
+#define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1
+#define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6
+#define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120
+#define GRPC_DNS_RECONNECT_JITTER 0.2
 
 typedef struct {
   /** base class: must be first */
@@ -60,6 +61,8 @@ typedef struct {
   char *default_port;
   /** channel args. */
   grpc_channel_args *channel_args;
+  /** pollset_set to drive the name resolution process */
+  grpc_pollset_set *interested_parties;
 
   /** mutex guarding the rest of the state */
   gpr_mu mu;
@@ -78,6 +81,7 @@ typedef struct {
   /** retry timer */
   bool have_retry_timer;
   grpc_timer retry_timer;
+  grpc_closure on_retry;
   /** retry backoff state */
   gpr_backoff backoff_state;
 
@@ -109,8 +113,8 @@ static void dns_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver) {
   }
   if (r->next_completion != NULL) {
     *r->target_result = NULL;
-    grpc_exec_ctx_sched(exec_ctx, r->next_completion,
-                        GRPC_ERROR_CREATE("Resolver Shutdown"), NULL);
+    grpc_closure_sched(exec_ctx, r->next_completion,
+                       GRPC_ERROR_CREATE("Resolver Shutdown"));
     r->next_completion = NULL;
   }
   gpr_mu_unlock(&r->mu);
@@ -179,13 +183,13 @@ static void dns_on_resolved(grpc_exec_ctx *exec_ctx, void *arg,
     grpc_arg new_arg = grpc_lb_addresses_create_channel_arg(addresses);
     result = grpc_channel_args_copy_and_add(r->channel_args, &new_arg, 1);
     grpc_resolved_addresses_destroy(r->addresses);
-    grpc_lb_addresses_destroy(addresses);
+    grpc_lb_addresses_destroy(exec_ctx, addresses);
   } else {
     gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
     gpr_timespec next_try = gpr_backoff_step(&r->backoff_state, now);
     gpr_timespec timeout = gpr_time_sub(next_try, now);
     const char *msg = grpc_error_string(error);
-    gpr_log(GPR_DEBUG, "dns resolution failed: %s", msg);
+    gpr_log(GPR_INFO, "dns resolution failed (will retry): %s", msg);
     grpc_error_free_string(msg);
     GPR_ASSERT(!r->have_retry_timer);
     r->have_retry_timer = true;
@@ -196,11 +200,12 @@ static void dns_on_resolved(grpc_exec_ctx *exec_ctx, void *arg,
     } else {
       gpr_log(GPR_DEBUG, "retrying immediately");
     }
-    grpc_timer_init(exec_ctx, &r->retry_timer, next_try, dns_on_retry_timer, r,
-                    now);
+    grpc_closure_init(&r->on_retry, dns_on_retry_timer, r,
+                      grpc_schedule_on_exec_ctx);
+    grpc_timer_init(exec_ctx, &r->retry_timer, next_try, &r->on_retry, now);
   }
   if (r->resolved_result != NULL) {
-    grpc_channel_args_destroy(r->resolved_result);
+    grpc_channel_args_destroy(exec_ctx, r->resolved_result);
   }
   r->resolved_result = result;
   r->resolved_version++;
@@ -216,8 +221,10 @@ static void dns_start_resolving_locked(grpc_exec_ctx *exec_ctx,
   GPR_ASSERT(!r->resolving);
   r->resolving = true;
   r->addresses = NULL;
-  grpc_resolve_address(exec_ctx, r->name_to_resolve, r->default_port,
-                       grpc_closure_create(dns_on_resolved, r), &r->addresses);
+  grpc_resolve_address(
+      exec_ctx, r->name_to_resolve, r->default_port, r->interested_parties,
+      grpc_closure_create(dns_on_resolved, r, grpc_schedule_on_exec_ctx),
+      &r->addresses);
 }
 
 static void dns_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx,
@@ -227,7 +234,7 @@ static void dns_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx,
     *r->target_result = r->resolved_result == NULL
                             ? NULL
                             : grpc_channel_args_copy(r->resolved_result);
-    grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE);
     r->next_completion = NULL;
     r->published_version = r->resolved_version;
   }
@@ -237,15 +244,17 @@ static void dns_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) {
   dns_resolver *r = (dns_resolver *)gr;
   gpr_mu_destroy(&r->mu);
   if (r->resolved_result != NULL) {
-    grpc_channel_args_destroy(r->resolved_result);
+    grpc_channel_args_destroy(exec_ctx, r->resolved_result);
   }
+  grpc_pollset_set_destroy(r->interested_parties);
   gpr_free(r->name_to_resolve);
   gpr_free(r->default_port);
-  grpc_channel_args_destroy(r->channel_args);
+  grpc_channel_args_destroy(exec_ctx, r->channel_args);
   gpr_free(r);
 }
 
-static grpc_resolver *dns_create(grpc_resolver_args *args,
+static grpc_resolver *dns_create(grpc_exec_ctx *exec_ctx,
+                                 grpc_resolver_args *args,
                                  const char *default_port) {
   if (0 != strcmp(args->uri->authority, "")) {
     gpr_log(GPR_ERROR, "authority based dns uri's not supported");
@@ -263,14 +272,17 @@ static grpc_resolver *dns_create(grpc_resolver_args *args,
   grpc_resolver_init(&r->base, &dns_resolver_vtable);
   r->name_to_resolve = proxy_name == NULL ? gpr_strdup(path) : proxy_name;
   r->default_port = gpr_strdup(default_port);
-  grpc_arg server_name_arg;
-  server_name_arg.type = GRPC_ARG_STRING;
-  server_name_arg.key = GRPC_ARG_SERVER_NAME;
-  server_name_arg.value.string = (char *)path;
-  r->channel_args =
-      grpc_channel_args_copy_and_add(args->args, &server_name_arg, 1);
-  gpr_backoff_init(&r->backoff_state, BACKOFF_MULTIPLIER, BACKOFF_JITTER,
-                   BACKOFF_MIN_SECONDS * 1000, BACKOFF_MAX_SECONDS * 1000);
+  r->channel_args = grpc_channel_args_copy(args->args);
+  r->interested_parties = grpc_pollset_set_create();
+  if (args->pollset_set != NULL) {
+    grpc_pollset_set_add_pollset_set(exec_ctx, r->interested_parties,
+                                     args->pollset_set);
+  }
+  gpr_backoff_init(&r->backoff_state, GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS,
+                   GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER,
+                   GRPC_DNS_RECONNECT_JITTER,
+                   GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS * 1000,
+                   GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000);
   return &r->base;
 }
 
@@ -283,8 +295,9 @@ static void dns_factory_ref(grpc_resolver_factory *factory) {}
 static void dns_factory_unref(grpc_resolver_factory *factory) {}
 
 static grpc_resolver *dns_factory_create_resolver(
-    grpc_resolver_factory *factory, grpc_resolver_args *args) {
-  return dns_create(args, "https");
+    grpc_exec_ctx *exec_ctx, grpc_resolver_factory *factory,
+    grpc_resolver_args *args) {
+  return dns_create(exec_ctx, args, "https");
 }
 
 static char *dns_factory_get_default_host_name(grpc_resolver_factory *factory,
diff --git a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c
index 26a650aaddeeceb8f3fea5b0dfc1b488545aa4c8..c146a627cb5b05b7de6b4d578407471541c20e7e 100644
--- a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c
+++ b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c
@@ -47,6 +47,7 @@
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/iomgr/resolve_address.h"
 #include "src/core/lib/iomgr/unix_sockets_posix.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/support/string.h"
 
@@ -89,7 +90,7 @@ static void sockaddr_shutdown(grpc_exec_ctx *exec_ctx,
   gpr_mu_lock(&r->mu);
   if (r->next_completion != NULL) {
     *r->target_result = NULL;
-    grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE);
     r->next_completion = NULL;
   }
   gpr_mu_unlock(&r->mu);
@@ -123,7 +124,7 @@ static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx,
     grpc_arg arg = grpc_lb_addresses_create_channel_arg(r->addresses);
     *r->target_result =
         grpc_channel_args_copy_and_add(r->channel_args, &arg, 1);
-    grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE);
     r->next_completion = NULL;
   }
 }
@@ -131,8 +132,8 @@ static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx,
 static void sockaddr_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) {
   sockaddr_resolver *r = (sockaddr_resolver *)gr;
   gpr_mu_destroy(&r->mu);
-  grpc_lb_addresses_destroy(r->addresses);
-  grpc_channel_args_destroy(r->channel_args);
+  grpc_lb_addresses_destroy(exec_ctx, r->addresses);
+  grpc_channel_args_destroy(exec_ctx, r->channel_args);
   gpr_free(r);
 }
 
@@ -161,7 +162,8 @@ char *unix_get_default_authority(grpc_resolver_factory *factory,
 
 static void do_nothing(void *ignored) {}
 
-static grpc_resolver *sockaddr_create(grpc_resolver_args *args,
+static grpc_resolver *sockaddr_create(grpc_exec_ctx *exec_ctx,
+                                      grpc_resolver_args *args,
                                       int parse(grpc_uri *uri,
                                                 grpc_resolved_address *dst)) {
   if (0 != strcmp(args->uri->authority, "")) {
@@ -188,22 +190,17 @@ static grpc_resolver *sockaddr_create(grpc_resolver_args *args,
     gpr_free(part_str);
     if (errors_found) break;
   }
-  grpc_slice_buffer_destroy(&path_parts);
-  grpc_slice_unref(path_slice);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &path_parts);
+  grpc_slice_unref_internal(exec_ctx, path_slice);
   if (errors_found) {
-    grpc_lb_addresses_destroy(addresses);
+    grpc_lb_addresses_destroy(exec_ctx, addresses);
     return NULL;
   }
   /* Instantiate resolver. */
   sockaddr_resolver *r = gpr_malloc(sizeof(sockaddr_resolver));
   memset(r, 0, sizeof(*r));
   r->addresses = addresses;
-  grpc_arg server_name_arg;
-  server_name_arg.type = GRPC_ARG_STRING;
-  server_name_arg.key = GRPC_ARG_SERVER_NAME;
-  server_name_arg.value.string = args->uri->path;
-  r->channel_args =
-      grpc_channel_args_copy_and_add(args->args, &server_name_arg, 1);
+  r->channel_args = grpc_channel_args_copy(args->args);
   gpr_mu_init(&r->mu);
   grpc_resolver_init(&r->base, &sockaddr_resolver_vtable);
   return &r->base;
@@ -219,8 +216,9 @@ static void sockaddr_factory_unref(grpc_resolver_factory *factory) {}
 
 #define DECL_FACTORY(name)                                                  \
   static grpc_resolver *name##_factory_create_resolver(                     \
-      grpc_resolver_factory *factory, grpc_resolver_args *args) {           \
-    return sockaddr_create(args, parse_##name);                             \
+      grpc_exec_ctx *exec_ctx, grpc_resolver_factory *factory,              \
+      grpc_resolver_args *args) {                                           \
+    return sockaddr_create(exec_ctx, args, parse_##name);                   \
   }                                                                         \
   static const grpc_resolver_factory_vtable name##_factory_vtable = {       \
       sockaddr_factory_ref, sockaddr_factory_unref,                         \
diff --git a/src/core/ext/transport/README.md b/src/core/ext/transport/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..2290568784d5e403257ddb9ed3ddcfcc1868dc87
--- /dev/null
+++ b/src/core/ext/transport/README.md
@@ -0,0 +1 @@
+Transports for gRPC
diff --git a/src/core/ext/transport/chttp2/README.md b/src/core/ext/transport/chttp2/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..8880a47460a13ae10bc8a562cb0cf829d57998af
--- /dev/null
+++ b/src/core/ext/transport/chttp2/README.md
@@ -0,0 +1 @@
+CHTTP2 - gRPC's implementation of a HTTP2 based transport
diff --git a/src/core/ext/transport/chttp2/client/chttp2_connector.c b/src/core/ext/transport/chttp2/client/chttp2_connector.c
new file mode 100644
index 0000000000000000000000000000000000000000..2c5dfaea601c064e83792f576d740c4327bd0463
--- /dev/null
+++ b/src/core/ext/transport/chttp2/client/chttp2_connector.c
@@ -0,0 +1,250 @@
+/*
+ *
+ * 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/ext/transport/chttp2/client/chttp2_connector.h"
+
+#include <grpc/grpc.h>
+
+#include <string.h>
+
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/client_channel/connector.h"
+#include "src/core/ext/client_channel/http_connect_handshaker.h"
+#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/handshaker.h"
+#include "src/core/lib/channel/handshaker_registry.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+typedef struct {
+  grpc_connector base;
+
+  gpr_mu mu;
+  gpr_refcount refs;
+
+  bool shutdown;
+  bool connecting;
+
+  grpc_closure *notify;
+  grpc_connect_in_args args;
+  grpc_connect_out_args *result;
+  grpc_closure initial_string_sent;
+  grpc_slice_buffer initial_string_buffer;
+
+  grpc_endpoint *endpoint;  // Non-NULL until handshaking starts.
+
+  grpc_closure connected;
+
+  grpc_handshake_manager *handshake_mgr;
+} chttp2_connector;
+
+static void chttp2_connector_ref(grpc_connector *con) {
+  chttp2_connector *c = (chttp2_connector *)con;
+  gpr_ref(&c->refs);
+}
+
+static void chttp2_connector_unref(grpc_exec_ctx *exec_ctx,
+                                   grpc_connector *con) {
+  chttp2_connector *c = (chttp2_connector *)con;
+  if (gpr_unref(&c->refs)) {
+    /* c->initial_string_buffer does not need to be destroyed */
+    gpr_mu_destroy(&c->mu);
+    // If handshaking is not yet in progress, destroy the endpoint.
+    // Otherwise, the handshaker will do this for us.
+    if (c->endpoint != NULL) grpc_endpoint_destroy(exec_ctx, c->endpoint);
+    gpr_free(c);
+  }
+}
+
+static void chttp2_connector_shutdown(grpc_exec_ctx *exec_ctx,
+                                      grpc_connector *con) {
+  chttp2_connector *c = (chttp2_connector *)con;
+  gpr_mu_lock(&c->mu);
+  c->shutdown = true;
+  if (c->handshake_mgr != NULL) {
+    grpc_handshake_manager_shutdown(exec_ctx, c->handshake_mgr);
+  }
+  // If handshaking is not yet in progress, shutdown the endpoint.
+  // Otherwise, the handshaker will do this for us.
+  if (!c->connecting && c->endpoint != NULL) {
+    grpc_endpoint_shutdown(exec_ctx, c->endpoint);
+  }
+  gpr_mu_unlock(&c->mu);
+}
+
+static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg,
+                              grpc_error *error) {
+  grpc_handshaker_args *args = arg;
+  chttp2_connector *c = args->user_data;
+  gpr_mu_lock(&c->mu);
+  if (error != GRPC_ERROR_NONE || c->shutdown) {
+    if (error == GRPC_ERROR_NONE) {
+      error = GRPC_ERROR_CREATE("connector shutdown");
+      // We were shut down after handshaking completed successfully, so
+      // destroy the endpoint here.
+      // TODO(ctiller): It is currently necessary to shutdown endpoints
+      // before destroying them, even if we know that there are no
+      // pending read/write callbacks.  This should be fixed, at which
+      // point this can be removed.
+      grpc_endpoint_shutdown(exec_ctx, args->endpoint);
+      grpc_endpoint_destroy(exec_ctx, args->endpoint);
+      grpc_channel_args_destroy(exec_ctx, args->args);
+      grpc_slice_buffer_destroy_internal(exec_ctx, args->read_buffer);
+      gpr_free(args->read_buffer);
+    } else {
+      error = GRPC_ERROR_REF(error);
+    }
+    memset(c->result, 0, sizeof(*c->result));
+  } else {
+    c->result->transport =
+        grpc_create_chttp2_transport(exec_ctx, args->args, args->endpoint, 1);
+    GPR_ASSERT(c->result->transport);
+    grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport,
+                                        args->read_buffer);
+    c->result->channel_args = args->args;
+  }
+  grpc_closure *notify = c->notify;
+  c->notify = NULL;
+  grpc_closure_sched(exec_ctx, notify, error);
+  grpc_handshake_manager_destroy(exec_ctx, c->handshake_mgr);
+  c->handshake_mgr = NULL;
+  gpr_mu_unlock(&c->mu);
+  chttp2_connector_unref(exec_ctx, (grpc_connector *)c);
+}
+
+static void start_handshake_locked(grpc_exec_ctx *exec_ctx,
+                                   chttp2_connector *c) {
+  c->handshake_mgr = grpc_handshake_manager_create();
+  grpc_handshakers_add(exec_ctx, HANDSHAKER_CLIENT, c->args.channel_args,
+                       c->handshake_mgr);
+  grpc_handshake_manager_do_handshake(
+      exec_ctx, c->handshake_mgr, c->endpoint, c->args.channel_args,
+      c->args.deadline, NULL /* acceptor */, on_handshake_done, c);
+  c->endpoint = NULL;  // Endpoint handed off to handshake manager.
+}
+
+static void on_initial_connect_string_sent(grpc_exec_ctx *exec_ctx, void *arg,
+                                           grpc_error *error) {
+  chttp2_connector *c = arg;
+  gpr_mu_lock(&c->mu);
+  if (error != GRPC_ERROR_NONE || c->shutdown) {
+    if (error == GRPC_ERROR_NONE) {
+      error = GRPC_ERROR_CREATE("connector shutdown");
+    } else {
+      error = GRPC_ERROR_REF(error);
+    }
+    memset(c->result, 0, sizeof(*c->result));
+    grpc_closure *notify = c->notify;
+    c->notify = NULL;
+    grpc_closure_sched(exec_ctx, notify, error);
+    gpr_mu_unlock(&c->mu);
+    chttp2_connector_unref(exec_ctx, arg);
+  } else {
+    start_handshake_locked(exec_ctx, c);
+    gpr_mu_unlock(&c->mu);
+  }
+}
+
+static void connected(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
+  chttp2_connector *c = arg;
+  gpr_mu_lock(&c->mu);
+  GPR_ASSERT(c->connecting);
+  c->connecting = false;
+  if (error != GRPC_ERROR_NONE || c->shutdown) {
+    if (error == GRPC_ERROR_NONE) {
+      error = GRPC_ERROR_CREATE("connector shutdown");
+    } else {
+      error = GRPC_ERROR_REF(error);
+    }
+    memset(c->result, 0, sizeof(*c->result));
+    grpc_closure *notify = c->notify;
+    c->notify = NULL;
+    grpc_closure_sched(exec_ctx, notify, error);
+    if (c->endpoint != NULL) grpc_endpoint_shutdown(exec_ctx, c->endpoint);
+    gpr_mu_unlock(&c->mu);
+    chttp2_connector_unref(exec_ctx, arg);
+  } else {
+    GPR_ASSERT(c->endpoint != NULL);
+    if (!GRPC_SLICE_IS_EMPTY(c->args.initial_connect_string)) {
+      grpc_closure_init(&c->initial_string_sent, on_initial_connect_string_sent,
+                        c, grpc_schedule_on_exec_ctx);
+      grpc_slice_buffer_init(&c->initial_string_buffer);
+      grpc_slice_buffer_add(&c->initial_string_buffer,
+                            c->args.initial_connect_string);
+      grpc_endpoint_write(exec_ctx, c->endpoint, &c->initial_string_buffer,
+                          &c->initial_string_sent);
+    } else {
+      start_handshake_locked(exec_ctx, c);
+    }
+    gpr_mu_unlock(&c->mu);
+  }
+}
+
+static void chttp2_connector_connect(grpc_exec_ctx *exec_ctx,
+                                     grpc_connector *con,
+                                     const grpc_connect_in_args *args,
+                                     grpc_connect_out_args *result,
+                                     grpc_closure *notify) {
+  chttp2_connector *c = (chttp2_connector *)con;
+  gpr_mu_lock(&c->mu);
+  GPR_ASSERT(c->notify == NULL);
+  c->notify = notify;
+  c->args = *args;
+  c->result = result;
+  GPR_ASSERT(c->endpoint == NULL);
+  chttp2_connector_ref(con);  // Ref taken for callback.
+  grpc_closure_init(&c->connected, connected, c, grpc_schedule_on_exec_ctx);
+  GPR_ASSERT(!c->connecting);
+  c->connecting = true;
+  grpc_tcp_client_connect(exec_ctx, &c->connected, &c->endpoint,
+                          args->interested_parties, args->channel_args,
+                          args->addr, args->deadline);
+  gpr_mu_unlock(&c->mu);
+}
+
+static const grpc_connector_vtable chttp2_connector_vtable = {
+    chttp2_connector_ref, chttp2_connector_unref, chttp2_connector_shutdown,
+    chttp2_connector_connect};
+
+grpc_connector *grpc_chttp2_connector_create() {
+  chttp2_connector *c = gpr_malloc(sizeof(*c));
+  memset(c, 0, sizeof(*c));
+  c->base.vtable = &chttp2_connector_vtable;
+  gpr_mu_init(&c->mu);
+  gpr_ref_init(&c->refs, 1);
+  return &c->base;
+}
diff --git a/src/core/ext/transport/chttp2/client/chttp2_connector.h b/src/core/ext/transport/chttp2/client/chttp2_connector.h
new file mode 100644
index 0000000000000000000000000000000000000000..f5d10254320e4f2a1d7fd70f79efa8f2aabfddc8
--- /dev/null
+++ b/src/core/ext/transport/chttp2/client/chttp2_connector.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H
+
+#include "src/core/ext/client_channel/connector.h"
+
+grpc_connector* grpc_chttp2_connector_create();
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H */
diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create.c b/src/core/ext/transport/chttp2/client/insecure/channel_create.c
index 8e03fd82c11d26d788f643d1c7eb98bbf91371dd..c9f4021216e1eb5c60ae54f7d90be8f61464ad6f 100644
--- a/src/core/ext/transport/chttp2/client/insecure/channel_create.c
+++ b/src/core/ext/transport/chttp2/client/insecure/channel_create.c
@@ -33,138 +33,17 @@
 
 #include <grpc/grpc.h>
 
-#include <stdlib.h>
 #include <string.h>
 
-#include <grpc/slice.h>
-#include <grpc/slice_buffer.h>
 #include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
 
 #include "src/core/ext/client_channel/client_channel.h"
-#include "src/core/ext/client_channel/http_connect_handshaker.h"
-#include "src/core/ext/client_channel/resolver_registry.h"
-#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/ext/transport/chttp2/client/chttp2_connector.h"
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/channel/compress_filter.h"
-#include "src/core/lib/channel/handshaker.h"
-#include "src/core/lib/channel/http_client_filter.h"
-#include "src/core/lib/iomgr/tcp_client.h"
 #include "src/core/lib/surface/api_trace.h"
 #include "src/core/lib/surface/channel.h"
 
-//
-// connector
-//
-
-typedef struct {
-  grpc_connector base;
-  gpr_refcount refs;
-
-  grpc_closure *notify;
-  grpc_connect_in_args args;
-  grpc_connect_out_args *result;
-  grpc_closure initial_string_sent;
-  grpc_slice_buffer initial_string_buffer;
-
-  grpc_endpoint *tcp;
-
-  grpc_closure connected;
-
-  grpc_handshake_manager *handshake_mgr;
-} connector;
-
-static void connector_ref(grpc_connector *con) {
-  connector *c = (connector *)con;
-  gpr_ref(&c->refs);
-}
-
-static void connector_unref(grpc_exec_ctx *exec_ctx, grpc_connector *con) {
-  connector *c = (connector *)con;
-  if (gpr_unref(&c->refs)) {
-    /* c->initial_string_buffer does not need to be destroyed */
-    grpc_handshake_manager_destroy(exec_ctx, c->handshake_mgr);
-    gpr_free(c);
-  }
-}
-
-static void on_initial_connect_string_sent(grpc_exec_ctx *exec_ctx, void *arg,
-                                           grpc_error *error) {
-  connector_unref(exec_ctx, arg);
-}
-
-static void on_handshake_done(grpc_exec_ctx *exec_ctx, grpc_endpoint *endpoint,
-                              grpc_channel_args *args,
-                              grpc_slice_buffer *read_buffer, void *user_data,
-                              grpc_error *error) {
-  connector *c = user_data;
-  if (error != GRPC_ERROR_NONE) {
-    grpc_channel_args_destroy(args);
-    gpr_free(read_buffer);
-  } else {
-    c->result->transport =
-        grpc_create_chttp2_transport(exec_ctx, args, endpoint, 1);
-    GPR_ASSERT(c->result->transport);
-    grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport,
-                                        read_buffer);
-    c->result->channel_args = args;
-  }
-  grpc_closure *notify = c->notify;
-  c->notify = NULL;
-  grpc_exec_ctx_sched(exec_ctx, notify, error, NULL);
-}
-
-static void connected(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
-  connector *c = arg;
-  grpc_endpoint *tcp = c->tcp;
-  if (tcp != NULL) {
-    if (!GRPC_SLICE_IS_EMPTY(c->args.initial_connect_string)) {
-      grpc_closure_init(&c->initial_string_sent, on_initial_connect_string_sent,
-                        c);
-      grpc_slice_buffer_init(&c->initial_string_buffer);
-      grpc_slice_buffer_add(&c->initial_string_buffer,
-                            c->args.initial_connect_string);
-      connector_ref(arg);
-      grpc_endpoint_write(exec_ctx, tcp, &c->initial_string_buffer,
-                          &c->initial_string_sent);
-    } else {
-      grpc_handshake_manager_do_handshake(
-          exec_ctx, c->handshake_mgr, tcp, c->args.channel_args,
-          c->args.deadline, NULL /* acceptor */, on_handshake_done, c);
-    }
-  } else {
-    memset(c->result, 0, sizeof(*c->result));
-    grpc_closure *notify = c->notify;
-    c->notify = NULL;
-    grpc_exec_ctx_sched(exec_ctx, notify, GRPC_ERROR_REF(error), NULL);
-  }
-}
-
-static void connector_shutdown(grpc_exec_ctx *exec_ctx, grpc_connector *con) {}
-
-static void connector_connect(grpc_exec_ctx *exec_ctx, grpc_connector *con,
-                              const grpc_connect_in_args *args,
-                              grpc_connect_out_args *result,
-                              grpc_closure *notify) {
-  connector *c = (connector *)con;
-  GPR_ASSERT(c->notify == NULL);
-  GPR_ASSERT(notify->cb);
-  c->notify = notify;
-  c->args = *args;
-  c->result = result;
-  c->tcp = NULL;
-  grpc_closure_init(&c->connected, connected, c);
-  grpc_tcp_client_connect(exec_ctx, &c->connected, &c->tcp,
-                          args->interested_parties, args->channel_args,
-                          args->addr, args->deadline);
-}
-
-static const grpc_connector_vtable connector_vtable = {
-    connector_ref, connector_unref, connector_shutdown, connector_connect};
-
-//
-// client_channel_factory
-//
-
 static void client_channel_factory_ref(
     grpc_client_channel_factory *cc_factory) {}
 
@@ -174,20 +53,9 @@ static void client_channel_factory_unref(
 static grpc_subchannel *client_channel_factory_create_subchannel(
     grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory,
     const grpc_subchannel_args *args) {
-  connector *c = gpr_malloc(sizeof(*c));
-  memset(c, 0, sizeof(*c));
-  c->base.vtable = &connector_vtable;
-  gpr_ref_init(&c->refs, 1);
-  c->handshake_mgr = grpc_handshake_manager_create();
-  char *proxy_name = grpc_get_http_proxy_server();
-  if (proxy_name != NULL) {
-    grpc_handshake_manager_add(
-        c->handshake_mgr,
-        grpc_http_connect_handshaker_create(proxy_name, args->server_name));
-    gpr_free(proxy_name);
-  }
-  grpc_subchannel *s = grpc_subchannel_create(exec_ctx, &c->base, args);
-  grpc_connector_unref(exec_ctx, &c->base);
+  grpc_connector *connector = grpc_chttp2_connector_create();
+  grpc_subchannel *s = grpc_subchannel_create(exec_ctx, connector, args);
+  grpc_connector_unref(exec_ctx, connector);
   return s;
 }
 
@@ -195,19 +63,15 @@ static grpc_channel *client_channel_factory_create_channel(
     grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory,
     const char *target, grpc_client_channel_type type,
     const grpc_channel_args *args) {
-  grpc_channel *channel =
-      grpc_channel_create(exec_ctx, target, args, GRPC_CLIENT_CHANNEL, NULL);
-  grpc_resolver *resolver = grpc_resolver_create(target, args);
-  if (!resolver) {
-    GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, channel,
-                                "client_channel_factory_create_channel");
-    return NULL;
-  }
-
-  grpc_client_channel_finish_initialization(
-      exec_ctx, grpc_channel_get_channel_stack(channel), resolver, cc_factory);
-  GRPC_RESOLVER_UNREF(exec_ctx, resolver, "create_channel");
-
+  // Add channel arg containing the server URI.
+  grpc_arg arg;
+  arg.type = GRPC_ARG_STRING;
+  arg.key = GRPC_ARG_SERVER_URI;
+  arg.value.string = (char *)target;
+  grpc_channel_args *new_args = grpc_channel_args_copy_and_add(args, &arg, 1);
+  grpc_channel *channel = grpc_channel_create(exec_ctx, target, new_args,
+                                              GRPC_CLIENT_CHANNEL, NULL);
+  grpc_channel_args_destroy(exec_ctx, new_args);
   return channel;
 }
 
@@ -230,16 +94,18 @@ grpc_channel *grpc_insecure_channel_create(const char *target,
   GRPC_API_TRACE(
       "grpc_insecure_channel_create(target=%p, args=%p, reserved=%p)", 3,
       (target, args, reserved));
-  GPR_ASSERT(!reserved);
-
-  grpc_client_channel_factory *factory =
-      (grpc_client_channel_factory *)&client_channel_factory;
+  GPR_ASSERT(reserved == NULL);
+  // Add channel arg containing the client channel factory.
+  grpc_arg arg =
+      grpc_client_channel_factory_create_channel_arg(&client_channel_factory);
+  grpc_channel_args *new_args = grpc_channel_args_copy_and_add(args, &arg, 1);
+  // Create channel.
   grpc_channel *channel = client_channel_factory_create_channel(
-      &exec_ctx, factory, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR, args);
-
-  grpc_client_channel_factory_unref(&exec_ctx, factory);
+      &exec_ctx, &client_channel_factory, target,
+      GRPC_CLIENT_CHANNEL_TYPE_REGULAR, new_args);
+  // Clean up.
+  grpc_channel_args_destroy(&exec_ctx, new_args);
   grpc_exec_ctx_finish(&exec_ctx);
-
   return channel != NULL ? channel : grpc_lame_client_channel_create(
                                          target, GRPC_STATUS_INTERNAL,
                                          "Failed to create client channel");
diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c b/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c
index 1e5b1c22e39093d28b9b7c1892be4ca4a55e8106..f1069e2c576c49e0b726ed342f4c4016f196166d 100644
--- a/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c
+++ b/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c
@@ -74,7 +74,7 @@ grpc_channel *grpc_insecure_channel_create_from_fd(
   GPR_ASSERT(transport);
   grpc_channel *channel = grpc_channel_create(
       &exec_ctx, target, final_args, GRPC_CLIENT_DIRECT_CHANNEL, transport);
-  grpc_channel_args_destroy(final_args);
+  grpc_channel_args_destroy(&exec_ctx, final_args);
   grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
 
   grpc_exec_ctx_finish(&exec_ctx);
diff --git a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c
index 04c88a2d368791dc4910784315494f79f69e8e95..f979d9bad5cd6d0f7b6b3995eb8678baed681185 100644
--- a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c
+++ b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c
@@ -33,239 +33,31 @@
 
 #include <grpc/grpc.h>
 
-#include <stdlib.h>
 #include <string.h>
 
-#include <grpc/slice.h>
-#include <grpc/slice_buffer.h>
 #include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
 
 #include "src/core/ext/client_channel/client_channel.h"
-#include "src/core/ext/client_channel/http_connect_handshaker.h"
-#include "src/core/ext/client_channel/resolver_registry.h"
-#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/ext/transport/chttp2/client/chttp2_connector.h"
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/channel/handshaker.h"
-#include "src/core/lib/iomgr/tcp_client.h"
-#include "src/core/lib/security/context/security_context.h"
 #include "src/core/lib/security/credentials/credentials.h"
-#include "src/core/lib/security/transport/auth_filters.h"
+#include "src/core/lib/security/transport/security_connector.h"
 #include "src/core/lib/surface/api_trace.h"
 #include "src/core/lib/surface/channel.h"
-#include "src/core/lib/tsi/transport_security_interface.h"
-
-//
-// connector
-//
-
-typedef struct {
-  grpc_connector base;
-  gpr_refcount refs;
-
-  grpc_channel_security_connector *security_connector;
-
-  grpc_closure *notify;
-  grpc_connect_in_args args;
-  grpc_connect_out_args *result;
-  grpc_closure initial_string_sent;
-  grpc_slice_buffer initial_string_buffer;
-
-  gpr_mu mu;
-  grpc_endpoint *connecting_endpoint;
-  grpc_endpoint *newly_connecting_endpoint;
-
-  grpc_closure connected_closure;
-
-  grpc_handshake_manager *handshake_mgr;
-
-  // TODO(roth): Remove once we eliminate on_secure_handshake_done().
-  grpc_channel_args *tmp_args;
-} connector;
-
-static void connector_ref(grpc_connector *con) {
-  connector *c = (connector *)con;
-  gpr_ref(&c->refs);
-}
-
-static void connector_unref(grpc_exec_ctx *exec_ctx, grpc_connector *con) {
-  connector *c = (connector *)con;
-  if (gpr_unref(&c->refs)) {
-    /* c->initial_string_buffer does not need to be destroyed */
-    grpc_channel_args_destroy(c->tmp_args);
-    grpc_handshake_manager_destroy(exec_ctx, c->handshake_mgr);
-    gpr_free(c);
-  }
-}
-
-static void on_secure_handshake_done(grpc_exec_ctx *exec_ctx, void *arg,
-                                     grpc_security_status status,
-                                     grpc_endpoint *secure_endpoint,
-                                     grpc_auth_context *auth_context) {
-  connector *c = arg;
-  gpr_mu_lock(&c->mu);
-  grpc_error *error = GRPC_ERROR_NONE;
-  if (c->connecting_endpoint == NULL) {
-    memset(c->result, 0, sizeof(*c->result));
-    gpr_mu_unlock(&c->mu);
-  } else if (status != GRPC_SECURITY_OK) {
-    error = grpc_error_set_int(GRPC_ERROR_CREATE("Secure handshake failed"),
-                               GRPC_ERROR_INT_SECURITY_STATUS, status);
-    memset(c->result, 0, sizeof(*c->result));
-    c->connecting_endpoint = NULL;
-    gpr_mu_unlock(&c->mu);
-  } else {
-    grpc_arg auth_context_arg;
-    c->connecting_endpoint = NULL;
-    gpr_mu_unlock(&c->mu);
-    c->result->transport = grpc_create_chttp2_transport(
-        exec_ctx, c->args.channel_args, secure_endpoint, 1);
-    grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, NULL);
-    auth_context_arg = grpc_auth_context_to_arg(auth_context);
-    c->result->channel_args =
-        grpc_channel_args_copy_and_add(c->tmp_args, &auth_context_arg, 1);
-  }
-  grpc_closure *notify = c->notify;
-  c->notify = NULL;
-  grpc_exec_ctx_sched(exec_ctx, notify, error, NULL);
-}
-
-static void on_handshake_done(grpc_exec_ctx *exec_ctx, grpc_endpoint *endpoint,
-                              grpc_channel_args *args,
-                              grpc_slice_buffer *read_buffer, void *user_data,
-                              grpc_error *error) {
-  connector *c = user_data;
-  c->tmp_args = args;
-  if (error != GRPC_ERROR_NONE) {
-    gpr_free(read_buffer);
-    grpc_closure *notify = c->notify;
-    c->notify = NULL;
-    grpc_exec_ctx_sched(exec_ctx, notify, error, NULL);
-  } else {
-    // TODO(roth, jboeuf): Convert security connector handshaking to use new
-    // handshake API, and then move the code from on_secure_handshake_done()
-    // into this function.
-    grpc_channel_security_connector_do_handshake(
-        exec_ctx, c->security_connector, endpoint, read_buffer,
-        c->args.deadline, on_secure_handshake_done, c);
-  }
-}
-
-static void on_initial_connect_string_sent(grpc_exec_ctx *exec_ctx, void *arg,
-                                           grpc_error *error) {
-  connector *c = arg;
-  grpc_handshake_manager_do_handshake(
-      exec_ctx, c->handshake_mgr, c->connecting_endpoint, c->args.channel_args,
-      c->args.deadline, NULL /* acceptor */, on_handshake_done, c);
-}
-
-static void connected(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
-  connector *c = arg;
-  grpc_endpoint *tcp = c->newly_connecting_endpoint;
-  if (tcp != NULL) {
-    gpr_mu_lock(&c->mu);
-    GPR_ASSERT(c->connecting_endpoint == NULL);
-    c->connecting_endpoint = tcp;
-    gpr_mu_unlock(&c->mu);
-    if (!GRPC_SLICE_IS_EMPTY(c->args.initial_connect_string)) {
-      grpc_closure_init(&c->initial_string_sent, on_initial_connect_string_sent,
-                        c);
-      grpc_slice_buffer_init(&c->initial_string_buffer);
-      grpc_slice_buffer_add(&c->initial_string_buffer,
-                            c->args.initial_connect_string);
-      grpc_endpoint_write(exec_ctx, tcp, &c->initial_string_buffer,
-                          &c->initial_string_sent);
-    } else {
-      grpc_handshake_manager_do_handshake(
-          exec_ctx, c->handshake_mgr, tcp, c->args.channel_args,
-          c->args.deadline, NULL /* acceptor */, on_handshake_done, c);
-    }
-  } else {
-    memset(c->result, 0, sizeof(*c->result));
-    grpc_closure *notify = c->notify;
-    c->notify = NULL;
-    grpc_exec_ctx_sched(exec_ctx, notify, GRPC_ERROR_REF(error), NULL);
-  }
-}
-
-static void connector_shutdown(grpc_exec_ctx *exec_ctx, grpc_connector *con) {
-  connector *c = (connector *)con;
-  grpc_endpoint *ep;
-  gpr_mu_lock(&c->mu);
-  ep = c->connecting_endpoint;
-  c->connecting_endpoint = NULL;
-  gpr_mu_unlock(&c->mu);
-  if (ep) {
-    grpc_endpoint_shutdown(exec_ctx, ep);
-  }
-}
-
-static void connector_connect(grpc_exec_ctx *exec_ctx, grpc_connector *con,
-                              const grpc_connect_in_args *args,
-                              grpc_connect_out_args *result,
-                              grpc_closure *notify) {
-  connector *c = (connector *)con;
-  GPR_ASSERT(c->notify == NULL);
-  c->notify = notify;
-  c->args = *args;
-  c->result = result;
-  gpr_mu_lock(&c->mu);
-  GPR_ASSERT(c->connecting_endpoint == NULL);
-  gpr_mu_unlock(&c->mu);
-  grpc_closure_init(&c->connected_closure, connected, c);
-  grpc_tcp_client_connect(
-      exec_ctx, &c->connected_closure, &c->newly_connecting_endpoint,
-      args->interested_parties, args->channel_args, args->addr, args->deadline);
-}
-
-static const grpc_connector_vtable connector_vtable = {
-    connector_ref, connector_unref, connector_shutdown, connector_connect};
-
-//
-// client_channel_factory
-//
-
-typedef struct {
-  grpc_client_channel_factory base;
-  gpr_refcount refs;
-  grpc_channel_security_connector *security_connector;
-} client_channel_factory;
 
 static void client_channel_factory_ref(
-    grpc_client_channel_factory *cc_factory) {
-  client_channel_factory *f = (client_channel_factory *)cc_factory;
-  gpr_ref(&f->refs);
-}
+    grpc_client_channel_factory *cc_factory) {}
 
 static void client_channel_factory_unref(
-    grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory) {
-  client_channel_factory *f = (client_channel_factory *)cc_factory;
-  if (gpr_unref(&f->refs)) {
-    GRPC_SECURITY_CONNECTOR_UNREF(&f->security_connector->base,
-                                  "client_channel_factory");
-    gpr_free(f);
-  }
-}
+    grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory) {}
 
 static grpc_subchannel *client_channel_factory_create_subchannel(
     grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory,
     const grpc_subchannel_args *args) {
-  client_channel_factory *f = (client_channel_factory *)cc_factory;
-  connector *c = gpr_malloc(sizeof(*c));
-  memset(c, 0, sizeof(*c));
-  c->base.vtable = &connector_vtable;
-  c->security_connector = f->security_connector;
-  c->handshake_mgr = grpc_handshake_manager_create();
-  char *proxy_name = grpc_get_http_proxy_server();
-  if (proxy_name != NULL) {
-    grpc_handshake_manager_add(
-        c->handshake_mgr,
-        grpc_http_connect_handshaker_create(proxy_name, args->server_name));
-    gpr_free(proxy_name);
-  }
-  gpr_mu_init(&c->mu);
-  gpr_ref_init(&c->refs, 1);
-  grpc_subchannel *s = grpc_subchannel_create(exec_ctx, &c->base, args);
-  grpc_connector_unref(exec_ctx, &c->base);
+  grpc_connector *connector = grpc_chttp2_connector_create();
+  grpc_subchannel *s = grpc_subchannel_create(exec_ctx, connector, args);
+  grpc_connector_unref(exec_ctx, connector);
   return s;
 }
 
@@ -273,19 +65,15 @@ static grpc_channel *client_channel_factory_create_channel(
     grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory,
     const char *target, grpc_client_channel_type type,
     const grpc_channel_args *args) {
-  client_channel_factory *f = (client_channel_factory *)cc_factory;
-  grpc_channel *channel =
-      grpc_channel_create(exec_ctx, target, args, GRPC_CLIENT_CHANNEL, NULL);
-  grpc_resolver *resolver = grpc_resolver_create(target, args);
-  if (resolver != NULL) {
-    grpc_client_channel_finish_initialization(
-        exec_ctx, grpc_channel_get_channel_stack(channel), resolver, &f->base);
-    GRPC_RESOLVER_UNREF(exec_ctx, resolver, "create");
-  } else {
-    GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, channel,
-                                "client_channel_factory_create_channel");
-    channel = NULL;
-  }
+  // Add channel arg containing the server URI.
+  grpc_arg arg;
+  arg.type = GRPC_ARG_STRING;
+  arg.key = GRPC_ARG_SERVER_URI;
+  arg.value.string = (char *)target;
+  grpc_channel_args *new_args = grpc_channel_args_copy_and_add(args, &arg, 1);
+  grpc_channel *channel = grpc_channel_create(exec_ctx, target, new_args,
+                                              GRPC_CLIENT_CHANNEL, NULL);
+  grpc_channel_args_destroy(exec_ctx, new_args);
   return channel;
 }
 
@@ -294,6 +82,9 @@ static const grpc_client_channel_factory_vtable client_channel_factory_vtable =
      client_channel_factory_create_subchannel,
      client_channel_factory_create_channel};
 
+static grpc_client_channel_factory client_channel_factory = {
+    &client_channel_factory_vtable};
+
 /* Create a secure client channel:
    Asynchronously: - resolve target
                    - connect to it (trying alternatives as presented)
@@ -320,36 +111,32 @@ grpc_channel *grpc_secure_channel_create(grpc_channel_credentials *creds,
   grpc_channel_security_connector *security_connector;
   grpc_channel_args *new_args_from_connector;
   if (grpc_channel_credentials_create_security_connector(
-          creds, target, args, &security_connector, &new_args_from_connector) !=
-      GRPC_SECURITY_OK) {
+          &exec_ctx, creds, target, args, &security_connector,
+          &new_args_from_connector) != GRPC_SECURITY_OK) {
     grpc_exec_ctx_finish(&exec_ctx);
     return grpc_lame_client_channel_create(
         target, GRPC_STATUS_INTERNAL, "Failed to create security connector.");
   }
-  grpc_arg connector_arg =
-      grpc_security_connector_to_arg(&security_connector->base);
+  // Add channel args containing the client channel factory and security
+  // connector.
+  grpc_arg args_to_add[2];
+  args_to_add[0] =
+      grpc_client_channel_factory_create_channel_arg(&client_channel_factory);
+  args_to_add[1] = grpc_security_connector_to_arg(&security_connector->base);
   grpc_channel_args *new_args = grpc_channel_args_copy_and_add(
       new_args_from_connector != NULL ? new_args_from_connector : args,
-      &connector_arg, 1);
+      args_to_add, GPR_ARRAY_SIZE(args_to_add));
   if (new_args_from_connector != NULL) {
-    grpc_channel_args_destroy(new_args_from_connector);
+    grpc_channel_args_destroy(&exec_ctx, new_args_from_connector);
   }
-  // Create client channel factory.
-  client_channel_factory *f = gpr_malloc(sizeof(*f));
-  memset(f, 0, sizeof(*f));
-  f->base.vtable = &client_channel_factory_vtable;
-  gpr_ref_init(&f->refs, 1);
-  GRPC_SECURITY_CONNECTOR_REF(&security_connector->base,
-                              "grpc_secure_channel_create");
-  f->security_connector = security_connector;
   // Create channel.
   grpc_channel *channel = client_channel_factory_create_channel(
-      &exec_ctx, &f->base, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR, new_args);
+      &exec_ctx, &client_channel_factory, target,
+      GRPC_CLIENT_CHANNEL_TYPE_REGULAR, new_args);
   // Clean up.
-  GRPC_SECURITY_CONNECTOR_UNREF(&f->security_connector->base,
+  GRPC_SECURITY_CONNECTOR_UNREF(&exec_ctx, &security_connector->base,
                                 "secure_client_channel_factory_create_channel");
-  grpc_channel_args_destroy(new_args);
-  grpc_client_channel_factory_unref(&exec_ctx, &f->base);
+  grpc_channel_args_destroy(&exec_ctx, new_args);
   grpc_exec_ctx_finish(&exec_ctx);
   return channel; /* may be NULL */
 }
diff --git a/src/core/ext/transport/chttp2/server/chttp2_server.c b/src/core/ext/transport/chttp2/server/chttp2_server.c
new file mode 100644
index 0000000000000000000000000000000000000000..574d1a7710195a4c58a0bbd71653755288df2372
--- /dev/null
+++ b/src/core/ext/transport/chttp2/server/chttp2_server.c
@@ -0,0 +1,342 @@
+/*
+ *
+ * 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/ext/transport/chttp2/server/chttp2_server.h"
+
+#include <grpc/grpc.h>
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/handshaker.h"
+#include "src/core/lib/channel/handshaker_registry.h"
+#include "src/core/lib/channel/http_server_filter.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/tcp_server.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/server.h"
+
+typedef struct pending_handshake_manager_node {
+  grpc_handshake_manager *handshake_mgr;
+  struct pending_handshake_manager_node *next;
+} pending_handshake_manager_node;
+
+typedef struct {
+  grpc_server *server;
+  grpc_tcp_server *tcp_server;
+  grpc_channel_args *args;
+  gpr_mu mu;
+  bool shutdown;
+  grpc_closure tcp_server_shutdown_complete;
+  grpc_closure *server_destroy_listener_done;
+  pending_handshake_manager_node *pending_handshake_mgrs;
+} server_state;
+
+typedef struct {
+  server_state *server_state;
+  grpc_pollset *accepting_pollset;
+  grpc_tcp_server_acceptor *acceptor;
+  grpc_handshake_manager *handshake_mgr;
+} server_connection_state;
+
+static void pending_handshake_manager_add_locked(
+    server_state *state, grpc_handshake_manager *handshake_mgr) {
+  pending_handshake_manager_node *node = gpr_malloc(sizeof(*node));
+  node->handshake_mgr = handshake_mgr;
+  node->next = state->pending_handshake_mgrs;
+  state->pending_handshake_mgrs = node;
+}
+
+static void pending_handshake_manager_remove_locked(
+    server_state *state, grpc_handshake_manager *handshake_mgr) {
+  pending_handshake_manager_node **prev_node = &state->pending_handshake_mgrs;
+  for (pending_handshake_manager_node *node = state->pending_handshake_mgrs;
+       node != NULL; node = node->next) {
+    if (node->handshake_mgr == handshake_mgr) {
+      *prev_node = node->next;
+      gpr_free(node);
+      break;
+    }
+    prev_node = &node->next;
+  }
+}
+
+static void pending_handshake_manager_shutdown_locked(grpc_exec_ctx *exec_ctx,
+                                                      server_state *state) {
+  pending_handshake_manager_node *prev_node = NULL;
+  for (pending_handshake_manager_node *node = state->pending_handshake_mgrs;
+       node != NULL; node = node->next) {
+    grpc_handshake_manager_shutdown(exec_ctx, node->handshake_mgr);
+    gpr_free(prev_node);
+    prev_node = node;
+  }
+  gpr_free(prev_node);
+  state->pending_handshake_mgrs = NULL;
+}
+
+static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg,
+                              grpc_error *error) {
+  grpc_handshaker_args *args = arg;
+  server_connection_state *connection_state = args->user_data;
+  gpr_mu_lock(&connection_state->server_state->mu);
+  if (error != GRPC_ERROR_NONE || connection_state->server_state->shutdown) {
+    const char *error_str = grpc_error_string(error);
+    gpr_log(GPR_ERROR, "Handshaking failed: %s", error_str);
+    grpc_error_free_string(error_str);
+    if (error == GRPC_ERROR_NONE && args->endpoint != NULL) {
+      // We were shut down after handshaking completed successfully, so
+      // destroy the endpoint here.
+      // TODO(ctiller): It is currently necessary to shutdown endpoints
+      // before destroying them, even if we know that there are no
+      // pending read/write callbacks.  This should be fixed, at which
+      // point this can be removed.
+      grpc_endpoint_shutdown(exec_ctx, args->endpoint);
+      grpc_endpoint_destroy(exec_ctx, args->endpoint);
+      grpc_channel_args_destroy(exec_ctx, args->args);
+      grpc_slice_buffer_destroy_internal(exec_ctx, args->read_buffer);
+      gpr_free(args->read_buffer);
+    }
+  } else {
+    // If the handshaking succeeded but there is no endpoint, then the
+    // handshaker may have handed off the connection to some external
+    // code, so we can just clean up here without creating a transport.
+    if (args->endpoint != NULL) {
+      grpc_transport *transport =
+          grpc_create_chttp2_transport(exec_ctx, args->args, args->endpoint, 0);
+      grpc_server_setup_transport(
+          exec_ctx, connection_state->server_state->server, transport,
+          connection_state->accepting_pollset, args->args);
+      grpc_chttp2_transport_start_reading(exec_ctx, transport,
+                                          args->read_buffer);
+      grpc_channel_args_destroy(exec_ctx, args->args);
+    }
+  }
+  pending_handshake_manager_remove_locked(connection_state->server_state,
+                                          connection_state->handshake_mgr);
+  gpr_mu_unlock(&connection_state->server_state->mu);
+  grpc_handshake_manager_destroy(exec_ctx, connection_state->handshake_mgr);
+  grpc_tcp_server_unref(exec_ctx, connection_state->server_state->tcp_server);
+  gpr_free(connection_state->acceptor);
+  gpr_free(connection_state);
+}
+
+static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *tcp,
+                      grpc_pollset *accepting_pollset,
+                      grpc_tcp_server_acceptor *acceptor) {
+  server_state *state = arg;
+  gpr_mu_lock(&state->mu);
+  if (state->shutdown) {
+    gpr_mu_unlock(&state->mu);
+    grpc_endpoint_destroy(exec_ctx, tcp);
+    gpr_free(acceptor);
+    return;
+  }
+  grpc_handshake_manager *handshake_mgr = grpc_handshake_manager_create();
+  pending_handshake_manager_add_locked(state, handshake_mgr);
+  gpr_mu_unlock(&state->mu);
+  grpc_tcp_server_ref(state->tcp_server);
+  server_connection_state *connection_state =
+      gpr_malloc(sizeof(*connection_state));
+  connection_state->server_state = state;
+  connection_state->accepting_pollset = accepting_pollset;
+  connection_state->acceptor = acceptor;
+  connection_state->handshake_mgr = handshake_mgr;
+  grpc_handshakers_add(exec_ctx, HANDSHAKER_SERVER, state->args,
+                       connection_state->handshake_mgr);
+  // TODO(roth): We should really get this timeout value from channel
+  // args instead of hard-coding it.
+  const gpr_timespec deadline = gpr_time_add(
+      gpr_now(GPR_CLOCK_MONOTONIC), gpr_time_from_seconds(120, GPR_TIMESPAN));
+  grpc_handshake_manager_do_handshake(exec_ctx, connection_state->handshake_mgr,
+                                      tcp, state->args, deadline, acceptor,
+                                      on_handshake_done, connection_state);
+}
+
+/* Server callback: start listening on our ports */
+static void server_start_listener(grpc_exec_ctx *exec_ctx, grpc_server *server,
+                                  void *arg, grpc_pollset **pollsets,
+                                  size_t pollset_count) {
+  server_state *state = arg;
+  gpr_mu_lock(&state->mu);
+  state->shutdown = false;
+  gpr_mu_unlock(&state->mu);
+  grpc_tcp_server_start(exec_ctx, state->tcp_server, pollsets, pollset_count,
+                        on_accept, state);
+}
+
+static void tcp_server_shutdown_complete(grpc_exec_ctx *exec_ctx, void *arg,
+                                         grpc_error *error) {
+  server_state *state = arg;
+  /* ensure all threads have unlocked */
+  gpr_mu_lock(&state->mu);
+  grpc_closure *destroy_done = state->server_destroy_listener_done;
+  GPR_ASSERT(state->shutdown);
+  pending_handshake_manager_shutdown_locked(exec_ctx, state);
+  gpr_mu_unlock(&state->mu);
+  // Flush queued work before destroying handshaker factory, since that
+  // may do a synchronous unref.
+  grpc_exec_ctx_flush(exec_ctx);
+  if (destroy_done != NULL) {
+    destroy_done->cb(exec_ctx, destroy_done->cb_arg, GRPC_ERROR_REF(error));
+    grpc_exec_ctx_flush(exec_ctx);
+  }
+  grpc_channel_args_destroy(exec_ctx, state->args);
+  gpr_mu_destroy(&state->mu);
+  gpr_free(state);
+}
+
+/* Server callback: destroy the tcp listener (so we don't generate further
+   callbacks) */
+static void server_destroy_listener(grpc_exec_ctx *exec_ctx,
+                                    grpc_server *server, void *arg,
+                                    grpc_closure *destroy_done) {
+  server_state *state = arg;
+  gpr_mu_lock(&state->mu);
+  state->shutdown = true;
+  state->server_destroy_listener_done = destroy_done;
+  grpc_tcp_server *tcp_server = state->tcp_server;
+  gpr_mu_unlock(&state->mu);
+  grpc_tcp_server_shutdown_listeners(exec_ctx, tcp_server);
+  grpc_tcp_server_unref(exec_ctx, tcp_server);
+}
+
+grpc_error *grpc_chttp2_server_add_port(grpc_exec_ctx *exec_ctx,
+                                        grpc_server *server, const char *addr,
+                                        grpc_channel_args *args,
+                                        int *port_num) {
+  grpc_resolved_addresses *resolved = NULL;
+  grpc_tcp_server *tcp_server = NULL;
+  size_t i;
+  size_t count = 0;
+  int port_temp;
+  grpc_error *err = GRPC_ERROR_NONE;
+  server_state *state = NULL;
+  grpc_error **errors = NULL;
+
+  *port_num = -1;
+
+  /* resolve address */
+  err = grpc_blocking_resolve_address(addr, "https", &resolved);
+  if (err != GRPC_ERROR_NONE) {
+    goto error;
+  }
+  state = gpr_malloc(sizeof(*state));
+  memset(state, 0, sizeof(*state));
+  grpc_closure_init(&state->tcp_server_shutdown_complete,
+                    tcp_server_shutdown_complete, state,
+                    grpc_schedule_on_exec_ctx);
+  err = grpc_tcp_server_create(exec_ctx, &state->tcp_server_shutdown_complete,
+                               args, &tcp_server);
+  if (err != GRPC_ERROR_NONE) {
+    goto error;
+  }
+
+  state->server = server;
+  state->tcp_server = tcp_server;
+  state->args = args;
+  state->shutdown = true;
+  gpr_mu_init(&state->mu);
+
+  const size_t naddrs = resolved->naddrs;
+  errors = gpr_malloc(sizeof(*errors) * naddrs);
+  for (i = 0; i < naddrs; i++) {
+    errors[i] =
+        grpc_tcp_server_add_port(tcp_server, &resolved->addrs[i], &port_temp);
+    if (errors[i] == GRPC_ERROR_NONE) {
+      if (*port_num == -1) {
+        *port_num = port_temp;
+      } else {
+        GPR_ASSERT(*port_num == port_temp);
+      }
+      count++;
+    }
+  }
+  if (count == 0) {
+    char *msg;
+    gpr_asprintf(&msg, "No address added out of total %" PRIuPTR " resolved",
+                 naddrs);
+    err = GRPC_ERROR_CREATE_REFERENCING(msg, errors, naddrs);
+    gpr_free(msg);
+    goto error;
+  } else if (count != naddrs) {
+    char *msg;
+    gpr_asprintf(&msg, "Only %" PRIuPTR
+                       " addresses added out of total %" PRIuPTR " resolved",
+                 count, naddrs);
+    err = GRPC_ERROR_CREATE_REFERENCING(msg, errors, naddrs);
+    gpr_free(msg);
+
+    const char *warning_message = grpc_error_string(err);
+    gpr_log(GPR_INFO, "WARNING: %s", warning_message);
+    grpc_error_free_string(warning_message);
+    /* we managed to bind some addresses: continue */
+  }
+  grpc_resolved_addresses_destroy(resolved);
+
+  /* Register with the server only upon success */
+  grpc_server_add_listener(exec_ctx, server, state, server_start_listener,
+                           server_destroy_listener);
+  goto done;
+
+/* Error path: cleanup and return */
+error:
+  GPR_ASSERT(err != GRPC_ERROR_NONE);
+  if (resolved) {
+    grpc_resolved_addresses_destroy(resolved);
+  }
+  if (tcp_server) {
+    grpc_tcp_server_unref(exec_ctx, tcp_server);
+  } else {
+    grpc_channel_args_destroy(exec_ctx, args);
+    gpr_free(state);
+  }
+  *port_num = 0;
+
+done:
+  if (errors != NULL) {
+    for (i = 0; i < naddrs; i++) {
+      GRPC_ERROR_UNREF(errors[i]);
+    }
+    gpr_free(errors);
+  }
+  return err;
+}
diff --git a/src/core/ext/transport/chttp2/server/chttp2_server.h b/src/core/ext/transport/chttp2/server/chttp2_server.h
new file mode 100644
index 0000000000000000000000000000000000000000..2581ebaae9673c429241517630015e7011abcfa4
--- /dev/null
+++ b/src/core/ext/transport/chttp2/server/chttp2_server.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+/// Adds a port to \a server.  Sets \a port_num to the port number.
+/// Takes ownership of \a args.
+grpc_error *grpc_chttp2_server_add_port(grpc_exec_ctx *exec_ctx,
+                                        grpc_server *server, const char *addr,
+                                        grpc_channel_args *args, int *port_num);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H */
diff --git a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
index c18d618f96bfceab229b7fe520b3f4521a231b22..bf5026bea643c55bf2f2a49eae2aa32a78390109 100644
--- a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
+++ b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
@@ -33,180 +33,27 @@
 
 #include <grpc/grpc.h>
 
-#include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
-#include <grpc/support/string_util.h>
-#include <grpc/support/useful.h>
 
-#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/ext/transport/chttp2/server/chttp2_server.h"
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/channel/handshaker.h"
-#include "src/core/lib/channel/http_server_filter.h"
-#include "src/core/lib/iomgr/resolve_address.h"
-#include "src/core/lib/iomgr/tcp_server.h"
 #include "src/core/lib/surface/api_trace.h"
 #include "src/core/lib/surface/server.h"
 
-typedef struct server_connect_state {
-  grpc_server *server;
-  grpc_pollset *accepting_pollset;
-  grpc_tcp_server_acceptor *acceptor;
-  grpc_handshake_manager *handshake_mgr;
-} server_connect_state;
-
-static void on_handshake_done(grpc_exec_ctx *exec_ctx, grpc_endpoint *endpoint,
-                              grpc_channel_args *args,
-                              grpc_slice_buffer *read_buffer, void *user_data,
-                              grpc_error *error) {
-  server_connect_state *state = user_data;
-  if (error != GRPC_ERROR_NONE) {
-    const char *error_str = grpc_error_string(error);
-    gpr_log(GPR_ERROR, "Handshaking failed: %s", error_str);
-    grpc_error_free_string(error_str);
-    GRPC_ERROR_UNREF(error);
-    grpc_handshake_manager_shutdown(exec_ctx, state->handshake_mgr);
-    gpr_free(read_buffer);
-  } else {
-    // Beware that the call to grpc_create_chttp2_transport() has to happen
-    // before grpc_tcp_server_destroy(). This is fine here, but similar code
-    // asynchronously doing a handshake instead of calling
-    // grpc_tcp_server_start() (as in server_secure_chttp2.c) needs to add
-    // synchronization to avoid this case.
-    grpc_transport *transport =
-        grpc_create_chttp2_transport(exec_ctx, args, endpoint, 0);
-    grpc_server_setup_transport(exec_ctx, state->server, transport,
-                                state->accepting_pollset,
-                                grpc_server_get_channel_args(state->server));
-    grpc_chttp2_transport_start_reading(exec_ctx, transport, read_buffer);
-  }
-  // Clean up.
-  grpc_channel_args_destroy(args);
-  grpc_handshake_manager_destroy(exec_ctx, state->handshake_mgr);
-  gpr_free(state);
-}
-
-static void on_accept(grpc_exec_ctx *exec_ctx, void *server, grpc_endpoint *tcp,
-                      grpc_pollset *accepting_pollset,
-                      grpc_tcp_server_acceptor *acceptor) {
-  server_connect_state *state = gpr_malloc(sizeof(server_connect_state));
-  state->server = server;
-  state->accepting_pollset = accepting_pollset;
-  state->acceptor = acceptor;
-  state->handshake_mgr = grpc_handshake_manager_create();
-  // TODO(roth): We should really get this timeout value from channel
-  // args instead of hard-coding it.
-  const gpr_timespec deadline = gpr_time_add(
-      gpr_now(GPR_CLOCK_MONOTONIC), gpr_time_from_seconds(120, GPR_TIMESPAN));
-  grpc_handshake_manager_do_handshake(
-      exec_ctx, state->handshake_mgr, tcp, grpc_server_get_channel_args(server),
-      deadline, acceptor, on_handshake_done, state);
-}
-
-/* Server callback: start listening on our ports */
-static void start(grpc_exec_ctx *exec_ctx, grpc_server *server, void *tcpp,
-                  grpc_pollset **pollsets, size_t pollset_count) {
-  grpc_tcp_server *tcp = tcpp;
-  grpc_tcp_server_start(exec_ctx, tcp, pollsets, pollset_count, on_accept,
-                        server);
-}
-
-/* Server callback: destroy the tcp listener (so we don't generate further
-   callbacks) */
-static void destroy(grpc_exec_ctx *exec_ctx, grpc_server *server, void *tcpp,
-                    grpc_closure *destroy_done) {
-  grpc_tcp_server *tcp = tcpp;
-  grpc_tcp_server_shutdown_listeners(exec_ctx, tcp);
-  grpc_tcp_server_unref(exec_ctx, tcp);
-  grpc_exec_ctx_sched(exec_ctx, destroy_done, GRPC_ERROR_NONE, NULL);
-}
-
 int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr) {
-  grpc_resolved_addresses *resolved = NULL;
-  grpc_tcp_server *tcp = NULL;
-  size_t i;
-  size_t count = 0;
-  int port_num = -1;
-  int port_temp;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_error *err = GRPC_ERROR_NONE;
-
+  int port_num = 0;
   GRPC_API_TRACE("grpc_server_add_insecure_http2_port(server=%p, addr=%s)", 2,
                  (server, addr));
-
-  grpc_error **errors = NULL;
-  err = grpc_blocking_resolve_address(addr, "https", &resolved);
-  if (err != GRPC_ERROR_NONE) {
-    goto error;
-  }
-
-  err = grpc_tcp_server_create(&exec_ctx, NULL,
-                               grpc_server_get_channel_args(server), &tcp);
+  grpc_error *err = grpc_chttp2_server_add_port(
+      &exec_ctx, server, addr,
+      grpc_channel_args_copy(grpc_server_get_channel_args(server)), &port_num);
   if (err != GRPC_ERROR_NONE) {
-    goto error;
-  }
-
-  const size_t naddrs = resolved->naddrs;
-  errors = gpr_malloc(sizeof(*errors) * naddrs);
-  for (i = 0; i < naddrs; i++) {
-    errors[i] = grpc_tcp_server_add_port(tcp, &resolved->addrs[i], &port_temp);
-    if (errors[i] == GRPC_ERROR_NONE) {
-      if (port_num == -1) {
-        port_num = port_temp;
-      } else {
-        GPR_ASSERT(port_num == port_temp);
-      }
-      count++;
-    }
+    const char *msg = grpc_error_string(err);
+    gpr_log(GPR_ERROR, "%s", msg);
+    grpc_error_free_string(msg);
+    GRPC_ERROR_UNREF(err);
   }
-  if (count == 0) {
-    char *msg;
-    gpr_asprintf(&msg, "No address added out of total %" PRIuPTR " resolved",
-                 naddrs);
-    err = GRPC_ERROR_CREATE_REFERENCING(msg, errors, naddrs);
-    gpr_free(msg);
-    goto error;
-  } else if (count != naddrs) {
-    char *msg;
-    gpr_asprintf(&msg, "Only %" PRIuPTR
-                       " addresses added out of total %" PRIuPTR " resolved",
-                 count, naddrs);
-    err = GRPC_ERROR_CREATE_REFERENCING(msg, errors, naddrs);
-    gpr_free(msg);
-
-    const char *warning_message = grpc_error_string(err);
-    gpr_log(GPR_INFO, "WARNING: %s", warning_message);
-    grpc_error_free_string(warning_message);
-    /* we managed to bind some addresses: continue */
-  }
-  grpc_resolved_addresses_destroy(resolved);
-
-  /* Register with the server only upon success */
-  grpc_server_add_listener(&exec_ctx, server, tcp, start, destroy);
-  goto done;
-
-/* Error path: cleanup and return */
-error:
-  GPR_ASSERT(err != GRPC_ERROR_NONE);
-  if (resolved) {
-    grpc_resolved_addresses_destroy(resolved);
-  }
-  if (tcp) {
-    grpc_tcp_server_unref(&exec_ctx, tcp);
-  }
-  port_num = 0;
-
-  const char *msg = grpc_error_string(err);
-  gpr_log(GPR_ERROR, "%s", msg);
-  grpc_error_free_string(msg);
-  GRPC_ERROR_UNREF(err);
-
-done:
   grpc_exec_ctx_finish(&exec_ctx);
-  if (errors != NULL) {
-    for (i = 0; i < naddrs; i++) {
-      GRPC_ERROR_UNREF(errors[i]);
-    }
-  }
-  gpr_free(errors);
   return port_num;
 }
diff --git a/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c b/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c
index aa2ecf5743d9e349168e3ad89b402865552cde4a..f46e8499326d5a0ecea74368fa0fdc605581edf5 100644
--- a/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c
+++ b/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c
@@ -62,7 +62,7 @@ void grpc_server_add_insecure_channel_from_fd(grpc_server *server,
   grpc_endpoint *server_endpoint =
       grpc_tcp_create(grpc_fd_create(fd, name), resource_quota,
                       GRPC_TCP_DEFAULT_READ_SLICE_SIZE, name);
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+  grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
 
   gpr_free(name);
 
diff --git a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
index 942638ad7fc09e427742498d057eb777d2b990ad..395c79a71d13af47df569e8218cae30b0ba253e0 100644
--- a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
+++ b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
@@ -38,218 +38,35 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
-#include <grpc/support/sync.h>
-#include <grpc/support/useful.h>
+
+#include "src/core/ext/transport/chttp2/server/chttp2_server.h"
+
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/handshaker.h"
-#include "src/core/lib/channel/http_server_filter.h"
-#include "src/core/lib/iomgr/endpoint.h"
-#include "src/core/lib/iomgr/resolve_address.h"
-#include "src/core/lib/iomgr/tcp_server.h"
 #include "src/core/lib/security/context/security_context.h"
 #include "src/core/lib/security/credentials/credentials.h"
-#include "src/core/lib/security/transport/auth_filters.h"
-#include "src/core/lib/security/transport/security_connector.h"
 #include "src/core/lib/surface/api_trace.h"
 #include "src/core/lib/surface/server.h"
 
-typedef struct server_secure_state {
-  grpc_server *server;
-  grpc_tcp_server *tcp;
-  grpc_server_security_connector *sc;
-  grpc_server_credentials *creds;
-  bool is_shutdown;
-  gpr_mu mu;
-  grpc_closure tcp_server_shutdown_complete;
-  grpc_closure *server_destroy_listener_done;
-} server_secure_state;
-
-typedef struct server_secure_connect {
-  server_secure_state *server_state;
-  grpc_pollset *accepting_pollset;
-  grpc_tcp_server_acceptor *acceptor;
-  grpc_handshake_manager *handshake_mgr;
-  // TODO(roth): Remove the following two fields when we eliminate
-  // grpc_server_security_connector_do_handshake().
-  gpr_timespec deadline;
-  grpc_channel_args *args;
-} server_secure_connect;
-
-static void on_secure_handshake_done(grpc_exec_ctx *exec_ctx, void *statep,
-                                     grpc_security_status status,
-                                     grpc_endpoint *secure_endpoint,
-                                     grpc_auth_context *auth_context) {
-  server_secure_connect *connection_state = statep;
-  if (status == GRPC_SECURITY_OK) {
-    if (secure_endpoint) {
-      gpr_mu_lock(&connection_state->server_state->mu);
-      if (!connection_state->server_state->is_shutdown) {
-        grpc_transport *transport = grpc_create_chttp2_transport(
-            exec_ctx, grpc_server_get_channel_args(
-                          connection_state->server_state->server),
-            secure_endpoint, 0);
-        grpc_arg args_to_add[2];
-        args_to_add[0] = grpc_server_credentials_to_arg(
-            connection_state->server_state->creds);
-        args_to_add[1] = grpc_auth_context_to_arg(auth_context);
-        grpc_channel_args *args_copy = grpc_channel_args_copy_and_add(
-            connection_state->args, args_to_add, GPR_ARRAY_SIZE(args_to_add));
-        grpc_server_setup_transport(
-            exec_ctx, connection_state->server_state->server, transport,
-            connection_state->accepting_pollset, args_copy);
-        grpc_channel_args_destroy(args_copy);
-        grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL);
-      } else {
-        /* We need to consume this here, because the server may already have
-         * gone away. */
-        grpc_endpoint_destroy(exec_ctx, secure_endpoint);
-      }
-      gpr_mu_unlock(&connection_state->server_state->mu);
-    }
-  } else {
-    gpr_log(GPR_ERROR, "Secure transport failed with error %d", status);
-  }
-  grpc_channel_args_destroy(connection_state->args);
-  grpc_tcp_server_unref(exec_ctx, connection_state->server_state->tcp);
-  gpr_free(connection_state);
-}
-
-static void on_handshake_done(grpc_exec_ctx *exec_ctx, grpc_endpoint *endpoint,
-                              grpc_channel_args *args,
-                              grpc_slice_buffer *read_buffer, void *user_data,
-                              grpc_error *error) {
-  server_secure_connect *connection_state = user_data;
-  if (error != GRPC_ERROR_NONE) {
-    const char *error_str = grpc_error_string(error);
-    gpr_log(GPR_ERROR, "Handshaking failed: %s", error_str);
-    grpc_error_free_string(error_str);
-    GRPC_ERROR_UNREF(error);
-    grpc_channel_args_destroy(args);
-    gpr_free(read_buffer);
-    grpc_handshake_manager_shutdown(exec_ctx, connection_state->handshake_mgr);
-    grpc_handshake_manager_destroy(exec_ctx, connection_state->handshake_mgr);
-    grpc_tcp_server_unref(exec_ctx, connection_state->server_state->tcp);
-    gpr_free(connection_state);
-    return;
-  }
-  grpc_handshake_manager_destroy(exec_ctx, connection_state->handshake_mgr);
-  connection_state->handshake_mgr = NULL;
-  // TODO(roth, jboeuf): Convert security connector handshaking to use new
-  // handshake API, and then move the code from on_secure_handshake_done()
-  // into this function.
-  connection_state->args = args;
-  grpc_server_security_connector_do_handshake(
-      exec_ctx, connection_state->server_state->sc, connection_state->acceptor,
-      endpoint, read_buffer, connection_state->deadline,
-      on_secure_handshake_done, connection_state);
-}
-
-static void on_accept(grpc_exec_ctx *exec_ctx, void *statep, grpc_endpoint *tcp,
-                      grpc_pollset *accepting_pollset,
-                      grpc_tcp_server_acceptor *acceptor) {
-  server_secure_state *server_state = statep;
-  server_secure_connect *connection_state = NULL;
-  gpr_mu_lock(&server_state->mu);
-  if (server_state->is_shutdown) {
-    gpr_mu_unlock(&server_state->mu);
-    grpc_endpoint_destroy(exec_ctx, tcp);
-    return;
-  }
-  gpr_mu_unlock(&server_state->mu);
-  grpc_tcp_server_ref(server_state->tcp);
-  connection_state = gpr_malloc(sizeof(*connection_state));
-  connection_state->server_state = server_state;
-  connection_state->accepting_pollset = accepting_pollset;
-  connection_state->acceptor = acceptor;
-  connection_state->handshake_mgr = grpc_handshake_manager_create();
-  // TODO(roth): We should really get this timeout value from channel
-  // args instead of hard-coding it.
-  connection_state->deadline = gpr_time_add(
-      gpr_now(GPR_CLOCK_MONOTONIC), gpr_time_from_seconds(120, GPR_TIMESPAN));
-  grpc_handshake_manager_do_handshake(
-      exec_ctx, connection_state->handshake_mgr, tcp,
-      grpc_server_get_channel_args(connection_state->server_state->server),
-      connection_state->deadline, acceptor, on_handshake_done,
-      connection_state);
-}
-
-/* Server callback: start listening on our ports */
-static void server_start_listener(grpc_exec_ctx *exec_ctx, grpc_server *server,
-                                  void *statep, grpc_pollset **pollsets,
-                                  size_t pollset_count) {
-  server_secure_state *server_state = statep;
-  gpr_mu_lock(&server_state->mu);
-  server_state->is_shutdown = false;
-  gpr_mu_unlock(&server_state->mu);
-  grpc_tcp_server_start(exec_ctx, server_state->tcp, pollsets, pollset_count,
-                        on_accept, server_state);
-}
-
-static void tcp_server_shutdown_complete(grpc_exec_ctx *exec_ctx, void *statep,
-                                         grpc_error *error) {
-  server_secure_state *server_state = statep;
-  /* ensure all threads have unlocked */
-  gpr_mu_lock(&server_state->mu);
-  grpc_closure *destroy_done = server_state->server_destroy_listener_done;
-  GPR_ASSERT(server_state->is_shutdown);
-  gpr_mu_unlock(&server_state->mu);
-  /* clean up */
-  grpc_server_security_connector_shutdown(exec_ctx, server_state->sc);
-
-  /* Flush queued work before a synchronous unref. */
-  grpc_exec_ctx_flush(exec_ctx);
-  GRPC_SECURITY_CONNECTOR_UNREF(&server_state->sc->base, "server");
-  grpc_server_credentials_unref(server_state->creds);
-
-  if (destroy_done != NULL) {
-    destroy_done->cb(exec_ctx, destroy_done->cb_arg, GRPC_ERROR_REF(error));
-    grpc_exec_ctx_flush(exec_ctx);
-  }
-  gpr_free(server_state);
-}
-
-static void server_destroy_listener(grpc_exec_ctx *exec_ctx,
-                                    grpc_server *server, void *statep,
-                                    grpc_closure *callback) {
-  server_secure_state *server_state = statep;
-  grpc_tcp_server *tcp;
-  gpr_mu_lock(&server_state->mu);
-  server_state->is_shutdown = true;
-  server_state->server_destroy_listener_done = callback;
-  tcp = server_state->tcp;
-  gpr_mu_unlock(&server_state->mu);
-  grpc_tcp_server_shutdown_listeners(exec_ctx, tcp);
-  grpc_tcp_server_unref(exec_ctx, server_state->tcp);
-}
-
 int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr,
                                       grpc_server_credentials *creds) {
-  grpc_resolved_addresses *resolved = NULL;
-  grpc_tcp_server *tcp = NULL;
-  server_secure_state *server_state = NULL;
-  size_t i;
-  size_t count = 0;
-  int port_num = -1;
-  int port_temp;
-  grpc_security_status status = GRPC_SECURITY_ERROR;
-  grpc_server_security_connector *sc = NULL;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_error *err = GRPC_ERROR_NONE;
-  grpc_error **errors = NULL;
-
+  grpc_server_security_connector *sc = NULL;
+  int port_num = 0;
   GRPC_API_TRACE(
       "grpc_server_add_secure_http2_port("
       "server=%p, addr=%s, creds=%p)",
       3, (server, addr, creds));
-
-  /* create security context */
+  // Create security context.
   if (creds == NULL) {
     err = GRPC_ERROR_CREATE(
         "No credentials specified for secure server port (creds==NULL)");
-    goto error;
+    goto done;
   }
-  status = grpc_server_credentials_create_security_connector(creds, &sc);
+  grpc_security_status status =
+      grpc_server_credentials_create_security_connector(&exec_ctx, creds, &sc);
   if (status != GRPC_SECURITY_OK) {
     char *msg;
     gpr_asprintf(&msg,
@@ -258,107 +75,27 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr,
     err = grpc_error_set_int(GRPC_ERROR_CREATE(msg),
                              GRPC_ERROR_INT_SECURITY_STATUS, status);
     gpr_free(msg);
-    goto error;
+    goto done;
   }
-  sc->channel_args = grpc_server_get_channel_args(server);
-
-  /* resolve address */
-  err = grpc_blocking_resolve_address(addr, "https", &resolved);
-  if (err != GRPC_ERROR_NONE) {
-    goto error;
+  // Create channel args.
+  grpc_arg args_to_add[2];
+  args_to_add[0] = grpc_server_credentials_to_arg(creds);
+  args_to_add[1] = grpc_security_connector_to_arg(&sc->base);
+  grpc_channel_args *args =
+      grpc_channel_args_copy_and_add(grpc_server_get_channel_args(server),
+                                     args_to_add, GPR_ARRAY_SIZE(args_to_add));
+  // Add server port.
+  err = grpc_chttp2_server_add_port(&exec_ctx, server, addr, args, &port_num);
+done:
+  if (sc != NULL) {
+    GRPC_SECURITY_CONNECTOR_UNREF(&exec_ctx, &sc->base, "server");
   }
-  server_state = gpr_malloc(sizeof(*server_state));
-  memset(server_state, 0, sizeof(*server_state));
-  grpc_closure_init(&server_state->tcp_server_shutdown_complete,
-                    tcp_server_shutdown_complete, server_state);
-  err = grpc_tcp_server_create(&exec_ctx,
-                               &server_state->tcp_server_shutdown_complete,
-                               grpc_server_get_channel_args(server), &tcp);
+  grpc_exec_ctx_finish(&exec_ctx);
   if (err != GRPC_ERROR_NONE) {
-    goto error;
+    const char *msg = grpc_error_string(err);
+    gpr_log(GPR_ERROR, "%s", msg);
+    grpc_error_free_string(msg);
+    GRPC_ERROR_UNREF(err);
   }
-
-  server_state->server = server;
-  server_state->tcp = tcp;
-  server_state->sc = sc;
-  server_state->creds = grpc_server_credentials_ref(creds);
-  server_state->is_shutdown = true;
-  gpr_mu_init(&server_state->mu);
-
-  errors = gpr_malloc(sizeof(*errors) * resolved->naddrs);
-  for (i = 0; i < resolved->naddrs; i++) {
-    errors[i] = grpc_tcp_server_add_port(tcp, &resolved->addrs[i], &port_temp);
-    if (errors[i] == GRPC_ERROR_NONE) {
-      if (port_num == -1) {
-        port_num = port_temp;
-      } else {
-        GPR_ASSERT(port_num == port_temp);
-      }
-      count++;
-    }
-  }
-  if (count == 0) {
-    char *msg;
-    gpr_asprintf(&msg, "No address added out of total %" PRIuPTR " resolved",
-                 resolved->naddrs);
-    err = GRPC_ERROR_CREATE_REFERENCING(msg, errors, resolved->naddrs);
-    gpr_free(msg);
-    goto error;
-  } else if (count != resolved->naddrs) {
-    char *msg;
-    gpr_asprintf(&msg, "Only %" PRIuPTR
-                       " addresses added out of total %" PRIuPTR " resolved",
-                 count, resolved->naddrs);
-    err = GRPC_ERROR_CREATE_REFERENCING(msg, errors, resolved->naddrs);
-    gpr_free(msg);
-
-    const char *warning_message = grpc_error_string(err);
-    gpr_log(GPR_INFO, "WARNING: %s", warning_message);
-    grpc_error_free_string(warning_message);
-    /* we managed to bind some addresses: continue */
-  } else {
-    for (i = 0; i < resolved->naddrs; i++) {
-      GRPC_ERROR_UNREF(errors[i]);
-    }
-  }
-  gpr_free(errors);
-  errors = NULL;
-  grpc_resolved_addresses_destroy(resolved);
-
-  /* Register with the server only upon success */
-  grpc_server_add_listener(&exec_ctx, server, server_state,
-                           server_start_listener, server_destroy_listener);
-
-  grpc_exec_ctx_finish(&exec_ctx);
   return port_num;
-
-/* Error path: cleanup and return */
-error:
-  GPR_ASSERT(err != GRPC_ERROR_NONE);
-  if (errors != NULL) {
-    for (i = 0; i < resolved->naddrs; i++) {
-      GRPC_ERROR_UNREF(errors[i]);
-    }
-    gpr_free(errors);
-  }
-  if (resolved) {
-    grpc_resolved_addresses_destroy(resolved);
-  }
-  if (tcp) {
-    grpc_tcp_server_unref(&exec_ctx, tcp);
-  } else {
-    if (sc) {
-      grpc_exec_ctx_flush(&exec_ctx);
-      GRPC_SECURITY_CONNECTOR_UNREF(&sc->base, "server");
-    }
-    if (server_state) {
-      gpr_free(server_state);
-    }
-  }
-  grpc_exec_ctx_finish(&exec_ctx);
-  const char *msg = grpc_error_string(err);
-  GRPC_ERROR_UNREF(err);
-  gpr_log(GPR_ERROR, "%s", msg);
-  grpc_error_free_string(msg);
-  return 0;
 }
diff --git a/src/core/ext/transport/chttp2/transport/bin_decoder.c b/src/core/ext/transport/chttp2/transport/bin_decoder.c
index 3eef80b5575c7c149d0771c45b2e0508d5294d1f..8db36e4a7f0d9eee6a704d5a9bae03804aa39c73 100644
--- a/src/core/ext/transport/chttp2/transport/bin_decoder.c
+++ b/src/core/ext/transport/chttp2/transport/bin_decoder.c
@@ -34,6 +34,7 @@
 #include "src/core/ext/transport/chttp2/transport/bin_decoder.h"
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/support/string.h"
 
@@ -143,7 +144,8 @@ bool grpc_base64_decode_partial(struct grpc_base64_decode_context *ctx) {
   return true;
 }
 
-grpc_slice grpc_chttp2_base64_decode(grpc_slice input) {
+grpc_slice grpc_chttp2_base64_decode(grpc_exec_ctx *exec_ctx,
+                                     grpc_slice input) {
   size_t input_length = GRPC_SLICE_LENGTH(input);
   size_t output_length = input_length / 4 * 3;
   struct grpc_base64_decode_context ctx;
@@ -179,7 +181,7 @@ grpc_slice grpc_chttp2_base64_decode(grpc_slice input) {
     char *s = grpc_dump_slice(input, GPR_DUMP_ASCII);
     gpr_log(GPR_ERROR, "Base64 decoding failed, input string:\n%s\n", s);
     gpr_free(s);
-    grpc_slice_unref(output);
+    grpc_slice_unref_internal(exec_ctx, output);
     return gpr_empty_slice();
   }
   GPR_ASSERT(ctx.output_cur == GRPC_SLICE_END_PTR(output));
@@ -187,7 +189,8 @@ grpc_slice grpc_chttp2_base64_decode(grpc_slice input) {
   return output;
 }
 
-grpc_slice grpc_chttp2_base64_decode_with_length(grpc_slice input,
+grpc_slice grpc_chttp2_base64_decode_with_length(grpc_exec_ctx *exec_ctx,
+                                                 grpc_slice input,
                                                  size_t output_length) {
   size_t input_length = GRPC_SLICE_LENGTH(input);
   grpc_slice output = grpc_slice_malloc(output_length);
@@ -200,7 +203,7 @@ grpc_slice grpc_chttp2_base64_decode_with_length(grpc_slice input,
             "grpc_chttp2_base64_decode_with_length has a length of %d, which "
             "has a tail of 1 byte.\n",
             (int)input_length);
-    grpc_slice_unref(output);
+    grpc_slice_unref_internal(exec_ctx, output);
     return gpr_empty_slice();
   }
 
@@ -210,7 +213,7 @@ grpc_slice grpc_chttp2_base64_decode_with_length(grpc_slice input,
             "than the max possible output length %d.\n",
             (int)output_length,
             (int)(input_length / 4 * 3 + tail_xtra[input_length % 4]));
-    grpc_slice_unref(output);
+    grpc_slice_unref_internal(exec_ctx, output);
     return gpr_empty_slice();
   }
 
@@ -224,7 +227,7 @@ grpc_slice grpc_chttp2_base64_decode_with_length(grpc_slice input,
     char *s = grpc_dump_slice(input, GPR_DUMP_ASCII);
     gpr_log(GPR_ERROR, "Base64 decoding failed, input string:\n%s\n", s);
     gpr_free(s);
-    grpc_slice_unref(output);
+    grpc_slice_unref_internal(exec_ctx, output);
     return gpr_empty_slice();
   }
   GPR_ASSERT(ctx.output_cur == GRPC_SLICE_END_PTR(output));
diff --git a/src/core/ext/transport/chttp2/transport/bin_decoder.h b/src/core/ext/transport/chttp2/transport/bin_decoder.h
index 83a90be519aa4c7a24cf58cdc3db05ef995f3c8f..48ffb2ae3b3b721ea23fdeb99f861d594d30ebe8 100644
--- a/src/core/ext/transport/chttp2/transport/bin_decoder.h
+++ b/src/core/ext/transport/chttp2/transport/bin_decoder.h
@@ -55,12 +55,13 @@ bool grpc_base64_decode_partial(struct grpc_base64_decode_context *ctx);
 
 /* base64 decode a slice with pad chars. Returns a new slice, does not take
    ownership of the input. Returns an empty slice if decoding is failed. */
-grpc_slice grpc_chttp2_base64_decode(grpc_slice input);
+grpc_slice grpc_chttp2_base64_decode(grpc_exec_ctx *exec_ctx, grpc_slice input);
 
 /* base64 decode a slice without pad chars, data length is needed. Returns a new
    slice, does not take ownership of the input. Returns an empty slice if
    decoding is failed. */
-grpc_slice grpc_chttp2_base64_decode_with_length(grpc_slice input,
+grpc_slice grpc_chttp2_base64_decode_with_length(grpc_exec_ctx *exec_ctx,
+                                                 grpc_slice input,
                                                  size_t output_length);
 
 #endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_DECODER_H */
diff --git a/src/core/ext/transport/chttp2/transport/bin_encoder.h b/src/core/ext/transport/chttp2/transport/bin_encoder.h
index 9e143b46e20d0ef83b8cc019588269f33307e5c5..477559d0e2508fe130678d0e415a7f2d74bbe61b 100644
--- a/src/core/ext/transport/chttp2/transport/bin_encoder.h
+++ b/src/core/ext/transport/chttp2/transport/bin_encoder.h
@@ -47,7 +47,7 @@ grpc_slice grpc_chttp2_huffman_compress(grpc_slice input);
 /* equivalent to:
    grpc_slice x = grpc_chttp2_base64_encode(input);
    grpc_slice y = grpc_chttp2_huffman_compress(x);
-   grpc_slice_unref(x);
+   grpc_slice_unref_internal(exec_ctx, x);
    return y; */
 grpc_slice grpc_chttp2_base64_encode_and_huffman_compress_impl(
     grpc_slice input);
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.c b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
index 4e3c7ff681ff19356148b3a5ade04142947b4b9e..8fe12a2690345ee6825aed66b03fc85e8aa72b3d 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.c
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
@@ -47,10 +47,12 @@
 #include "src/core/ext/transport/chttp2/transport/http2_errors.h"
 #include "src/core/ext/transport/chttp2/transport/internal.h"
 #include "src/core/ext/transport/chttp2/transport/status_conversion.h"
+#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/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/string.h"
 #include "src/core/lib/transport/static_metadata.h"
@@ -73,20 +75,14 @@ static const grpc_transport_vtable vtable;
 static void write_action_begin_locked(grpc_exec_ctx *exec_ctx, void *t,
                                       grpc_error *error);
 static void write_action(grpc_exec_ctx *exec_ctx, void *t, grpc_error *error);
-static void write_action_end(grpc_exec_ctx *exec_ctx, void *t,
-                             grpc_error *error);
 static void write_action_end_locked(grpc_exec_ctx *exec_ctx, void *t,
                                     grpc_error *error);
 
-static void read_action_begin(grpc_exec_ctx *exec_ctx, void *t,
-                              grpc_error *error);
 static void read_action_locked(grpc_exec_ctx *exec_ctx, void *t,
                                grpc_error *error);
 
 static void complete_fetch_locked(grpc_exec_ctx *exec_ctx, void *gs,
                                   grpc_error *error);
-static void complete_fetch(grpc_exec_ctx *exec_ctx, void *gs,
-                           grpc_error *error);
 /** Set a transport level setting, and push it to our peer */
 static void push_setting(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
                          grpc_chttp2_setting_id id, uint32_t value);
@@ -111,16 +107,9 @@ static void incoming_byte_stream_update_flow_control(grpc_exec_ctx *exec_ctx,
 static void incoming_byte_stream_destroy_locked(grpc_exec_ctx *exec_ctx,
                                                 void *byte_stream,
                                                 grpc_error *error_ignored);
-static void fail_pending_writes(grpc_exec_ctx *exec_ctx,
-                                grpc_chttp2_transport *t, grpc_chttp2_stream *s,
-                                grpc_error *error);
 
-static void benign_reclaimer(grpc_exec_ctx *exec_ctx, void *t,
-                             grpc_error *error);
 static void benign_reclaimer_locked(grpc_exec_ctx *exec_ctx, void *t,
                                     grpc_error *error);
-static void destructive_reclaimer(grpc_exec_ctx *exec_ctx, void *t,
-                                  grpc_error *error);
 static void destructive_reclaimer_locked(grpc_exec_ctx *exec_ctx, void *t,
                                          grpc_error *error);
 
@@ -144,13 +133,13 @@ static void destruct_transport(grpc_exec_ctx *exec_ctx,
 
   grpc_endpoint_destroy(exec_ctx, t->ep);
 
-  grpc_slice_buffer_destroy(&t->qbuf);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &t->qbuf);
 
-  grpc_slice_buffer_destroy(&t->outbuf);
-  grpc_chttp2_hpack_compressor_destroy(&t->hpack_compressor);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &t->outbuf);
+  grpc_chttp2_hpack_compressor_destroy(exec_ctx, &t->hpack_compressor);
 
-  grpc_slice_buffer_destroy(&t->read_buffer);
-  grpc_chttp2_hpack_parser_destroy(&t->hpack_parser);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &t->read_buffer);
+  grpc_chttp2_hpack_parser_destroy(exec_ctx, &t->hpack_parser);
   grpc_chttp2_goaway_parser_destroy(&t->goaway_parser);
 
   for (i = 0; i < STREAM_LIST_COUNT; i++) {
@@ -169,8 +158,8 @@ static void destruct_transport(grpc_exec_ctx *exec_ctx,
      and maybe they hold resources that need to be freed */
   while (t->pings.next != &t->pings) {
     grpc_chttp2_outstanding_ping *ping = t->pings.next;
-    grpc_exec_ctx_sched(exec_ctx, ping->on_recv,
-                        GRPC_ERROR_CREATE("Transport closed"), NULL);
+    grpc_closure_sched(exec_ctx, ping->on_recv,
+                       GRPC_ERROR_CREATE("Transport closed"));
     ping->next->prev = ping->prev;
     ping->prev->next = ping->next;
     gpr_free(ping);
@@ -249,21 +238,18 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
   grpc_slice_buffer_init(&t->outbuf);
   grpc_chttp2_hpack_compressor_init(&t->hpack_compressor);
 
-  grpc_closure_init(&t->write_action_begin_locked, write_action_begin_locked,
-                    t);
-  grpc_closure_init(&t->write_action, write_action, t);
-  grpc_closure_init(&t->write_action_end, write_action_end, t);
-  grpc_closure_init(&t->write_action_end_locked, write_action_end_locked, t);
-  grpc_closure_init(&t->read_action_begin, read_action_begin, t);
-  grpc_closure_init(&t->read_action_locked, read_action_locked, t);
-  grpc_closure_init(&t->benign_reclaimer, benign_reclaimer, t);
-  grpc_closure_init(&t->destructive_reclaimer, destructive_reclaimer, t);
-  grpc_closure_init(&t->benign_reclaimer_locked, benign_reclaimer_locked, t);
+  grpc_closure_init(&t->write_action, write_action, t,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&t->read_action_locked, read_action_locked, t,
+                    grpc_combiner_scheduler(t->combiner, false));
+  grpc_closure_init(&t->benign_reclaimer_locked, benign_reclaimer_locked, t,
+                    grpc_combiner_scheduler(t->combiner, false));
   grpc_closure_init(&t->destructive_reclaimer_locked,
-                    destructive_reclaimer_locked, t);
+                    destructive_reclaimer_locked, t,
+                    grpc_combiner_scheduler(t->combiner, false));
 
   grpc_chttp2_goaway_parser_init(&t->goaway_parser);
-  grpc_chttp2_hpack_parser_init(&t->hpack_parser);
+  grpc_chttp2_hpack_parser_init(exec_ctx, &t->hpack_parser);
 
   grpc_slice_buffer_init(&t->read_buffer);
 
@@ -398,9 +384,10 @@ static void destroy_transport_locked(grpc_exec_ctx *exec_ctx, void *tp,
 
 static void destroy_transport(grpc_exec_ctx *exec_ctx, grpc_transport *gt) {
   grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt;
-  grpc_combiner_execute(exec_ctx, t->combiner,
-                        grpc_closure_create(destroy_transport_locked, t),
-                        GRPC_ERROR_NONE, false);
+  grpc_closure_sched(exec_ctx, grpc_closure_create(
+                                   destroy_transport_locked, t,
+                                   grpc_combiner_scheduler(t->combiner, false)),
+                     GRPC_ERROR_NONE);
 }
 
 static void close_transport_locked(grpc_exec_ctx *exec_ctx,
@@ -474,8 +461,8 @@ static int init_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
   grpc_chttp2_data_parser_init(&s->data_parser);
   grpc_slice_buffer_init(&s->flow_controlled_buffer);
   s->deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
-  grpc_closure_init(&s->complete_fetch, complete_fetch, s);
-  grpc_closure_init(&s->complete_fetch_locked, complete_fetch_locked, s);
+  grpc_closure_init(&s->complete_fetch_locked, complete_fetch_locked, s,
+                    grpc_schedule_on_exec_ctx);
 
   GRPC_CHTTP2_REF_TRANSPORT(t, "stream");
 
@@ -530,9 +517,11 @@ static void destroy_stream_locked(grpc_exec_ctx *exec_ctx, void *sp,
   GPR_ASSERT(s->recv_message_ready == NULL);
   GPR_ASSERT(s->recv_trailing_metadata_finished == NULL);
   grpc_chttp2_data_parser_destroy(exec_ctx, &s->data_parser);
-  grpc_chttp2_incoming_metadata_buffer_destroy(&s->metadata_buffer[0]);
-  grpc_chttp2_incoming_metadata_buffer_destroy(&s->metadata_buffer[1]);
-  grpc_slice_buffer_destroy(&s->flow_controlled_buffer);
+  grpc_chttp2_incoming_metadata_buffer_destroy(exec_ctx,
+                                               &s->metadata_buffer[0]);
+  grpc_chttp2_incoming_metadata_buffer_destroy(exec_ctx,
+                                               &s->metadata_buffer[1]);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &s->flow_controlled_buffer);
   GRPC_ERROR_UNREF(s->read_closed_error);
   GRPC_ERROR_UNREF(s->write_closed_error);
 
@@ -550,9 +539,10 @@ static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
   grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs;
 
   s->destroy_stream_arg = and_free_memory;
-  grpc_closure_init(&s->destroy_stream, destroy_stream_locked, s);
-  grpc_combiner_execute(exec_ctx, t->combiner, &s->destroy_stream,
-                        GRPC_ERROR_NONE, false);
+  grpc_closure_sched(
+      exec_ctx, grpc_closure_init(&s->destroy_stream, destroy_stream_locked, s,
+                                  grpc_combiner_scheduler(t->combiner, false)),
+      GRPC_ERROR_NONE);
   GPR_TIMER_END("destroy_stream", 0);
 }
 
@@ -602,11 +592,13 @@ static void set_write_state(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
                                  write_state_name(t->write_state),
                                  write_state_name(st), reason));
   t->write_state = st;
-  if (st == GRPC_CHTTP2_WRITE_STATE_IDLE &&
-      t->close_transport_on_writes_finished != NULL) {
-    grpc_error *err = t->close_transport_on_writes_finished;
-    t->close_transport_on_writes_finished = NULL;
-    close_transport_locked(exec_ctx, t, err);
+  if (st == GRPC_CHTTP2_WRITE_STATE_IDLE) {
+    grpc_closure_list_sched(exec_ctx, &t->run_after_write);
+    if (t->close_transport_on_writes_finished != NULL) {
+      grpc_error *err = t->close_transport_on_writes_finished;
+      t->close_transport_on_writes_finished = NULL;
+      close_transport_locked(exec_ctx, t, err);
+    }
   }
 }
 
@@ -619,9 +611,12 @@ void grpc_chttp2_initiate_write(grpc_exec_ctx *exec_ctx,
     case GRPC_CHTTP2_WRITE_STATE_IDLE:
       set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_WRITING, reason);
       GRPC_CHTTP2_REF_TRANSPORT(t, "writing");
-      grpc_combiner_execute_finally(exec_ctx, t->combiner,
-                                    &t->write_action_begin_locked,
-                                    GRPC_ERROR_NONE, covered_by_poller);
+      grpc_closure_sched(
+          exec_ctx,
+          grpc_closure_init(
+              &t->write_action_begin_locked, write_action_begin_locked, t,
+              grpc_combiner_finally_scheduler(t->combiner, covered_by_poller)),
+          GRPC_ERROR_NONE);
       break;
     case GRPC_CHTTP2_WRITE_STATE_WRITING:
       set_write_state(
@@ -663,7 +658,7 @@ static void write_action_begin_locked(grpc_exec_ctx *exec_ctx, void *gt,
   if (!t->closed && grpc_chttp2_begin_write(exec_ctx, t)) {
     set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_WRITING,
                     "begin writing");
-    grpc_exec_ctx_sched(exec_ctx, &t->write_action, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, &t->write_action, GRPC_ERROR_NONE);
   } else {
     set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_IDLE,
                     "begin writing nothing");
@@ -675,19 +670,13 @@ static void write_action_begin_locked(grpc_exec_ctx *exec_ctx, void *gt,
 static void write_action(grpc_exec_ctx *exec_ctx, void *gt, grpc_error *error) {
   grpc_chttp2_transport *t = gt;
   GPR_TIMER_BEGIN("write_action", 0);
-  grpc_endpoint_write(exec_ctx, t->ep, &t->outbuf, &t->write_action_end);
+  grpc_endpoint_write(
+      exec_ctx, t->ep, &t->outbuf,
+      grpc_closure_init(&t->write_action_end_locked, write_action_end_locked, t,
+                        grpc_combiner_scheduler(t->combiner, false)));
   GPR_TIMER_END("write_action", 0);
 }
 
-static void write_action_end(grpc_exec_ctx *exec_ctx, void *gt,
-                             grpc_error *error) {
-  grpc_chttp2_transport *t = gt;
-  GPR_TIMER_BEGIN("write_action_end", 0);
-  grpc_combiner_execute(exec_ctx, t->combiner, &t->write_action_end_locked,
-                        GRPC_ERROR_REF(error), false);
-  GPR_TIMER_END("write_action_end", 0);
-}
-
 static void write_action_end_locked(grpc_exec_ctx *exec_ctx, void *tp,
                                     grpc_error *error) {
   GPR_TIMER_BEGIN("terminate_writing_with_lock", 0);
@@ -704,8 +693,6 @@ static void write_action_end_locked(grpc_exec_ctx *exec_ctx, void *tp,
     }
   }
 
-  grpc_chttp2_end_write(exec_ctx, t, GRPC_ERROR_REF(error));
-
   switch (t->write_state) {
     case GRPC_CHTTP2_WRITE_STATE_IDLE:
       GPR_UNREACHABLE_CODE(break);
@@ -719,21 +706,29 @@ static void write_action_end_locked(grpc_exec_ctx *exec_ctx, void *tp,
       set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_WRITING,
                       "continue writing [!covered]");
       GRPC_CHTTP2_REF_TRANSPORT(t, "writing");
-      grpc_combiner_execute_finally(exec_ctx, t->combiner,
-                                    &t->write_action_begin_locked,
-                                    GRPC_ERROR_NONE, false);
+      grpc_closure_run(
+          exec_ctx,
+          grpc_closure_init(
+              &t->write_action_begin_locked, write_action_begin_locked, t,
+              grpc_combiner_finally_scheduler(t->combiner, false)),
+          GRPC_ERROR_NONE);
       break;
     case GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE_AND_COVERED_BY_POLLER:
       GPR_TIMER_MARK("state=writing_stale_with_poller", 0);
       set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_WRITING,
                       "continue writing [covered]");
       GRPC_CHTTP2_REF_TRANSPORT(t, "writing");
-      grpc_combiner_execute_finally(exec_ctx, t->combiner,
-                                    &t->write_action_begin_locked,
-                                    GRPC_ERROR_NONE, true);
+      grpc_closure_run(
+          exec_ctx,
+          grpc_closure_init(&t->write_action_begin_locked,
+                            write_action_begin_locked, t,
+                            grpc_combiner_finally_scheduler(t->combiner, true)),
+          GRPC_ERROR_NONE);
       break;
   }
 
+  grpc_chttp2_end_write(exec_ctx, t, GRPC_ERROR_REF(error));
+
   GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "writing");
   GPR_TIMER_END("terminate_writing_with_lock", 0);
 }
@@ -761,7 +756,7 @@ void grpc_chttp2_add_incoming_goaway(grpc_exec_ctx *exec_ctx,
   char *msg = grpc_dump_slice(goaway_text, GPR_DUMP_HEX | GPR_DUMP_ASCII);
   GRPC_CHTTP2_IF_TRACING(
       gpr_log(GPR_DEBUG, "got goaway [%d]: %s", goaway_error, msg));
-  grpc_slice_unref(goaway_text);
+  grpc_slice_unref_internal(exec_ctx, goaway_text);
   t->seen_goaway = 1;
   /* lie: use transient failure from the transport to indicate goaway has been
    * received */
@@ -823,7 +818,14 @@ static void maybe_start_some_streams(grpc_exec_ctx *exec_ctx,
   }
 }
 
+/* Flag that this closure barrier wants stats to be updated before finishing */
 #define CLOSURE_BARRIER_STATS_BIT (1 << 0)
+/* Flag that this closure barrier may be covering a write in a pollset, and so
+   we should not complete this closure until we can prove that the write got
+   scheduled */
+#define CLOSURE_BARRIER_MAY_COVER_WRITE (1 << 1)
+/* First bit of the reference count, stored in the high order bits (with the low
+   bits being used for flags defined above) */
 #define CLOSURE_BARRIER_FIRST_REF_BIT (1 << 16)
 
 static grpc_closure *add_closure_barrier(grpc_closure *closure) {
@@ -850,6 +852,16 @@ void grpc_chttp2_complete_closure_step(grpc_exec_ctx *exec_ctx,
     return;
   }
   closure->next_data.scratch -= CLOSURE_BARRIER_FIRST_REF_BIT;
+  if (grpc_http_trace) {
+    const char *errstr = grpc_error_string(error);
+    gpr_log(GPR_DEBUG,
+            "complete_closure_step: %p refs=%d flags=0x%04x desc=%s err=%s",
+            closure,
+            (int)(closure->next_data.scratch / CLOSURE_BARRIER_FIRST_REF_BIT),
+            (int)(closure->next_data.scratch % CLOSURE_BARRIER_FIRST_REF_BIT),
+            desc, errstr);
+    grpc_error_free_string(errstr);
+  }
   if (error != GRPC_ERROR_NONE) {
     if (closure->error_data.error == GRPC_ERROR_NONE) {
       closure->error_data.error =
@@ -866,7 +878,13 @@ void grpc_chttp2_complete_closure_step(grpc_exec_ctx *exec_ctx,
       grpc_transport_move_stats(&s->stats, s->collecting_stats);
       s->collecting_stats = NULL;
     }
-    grpc_closure_run(exec_ctx, closure, closure->error_data.error);
+    if ((t->write_state == GRPC_CHTTP2_WRITE_STATE_IDLE) ||
+        !(closure->next_data.scratch & CLOSURE_BARRIER_MAY_COVER_WRITE)) {
+      grpc_closure_run(exec_ctx, closure, closure->error_data.error);
+    } else {
+      grpc_closure_list_append(&t->run_after_write, closure,
+                               closure->error_data.error);
+    }
   }
 }
 
@@ -943,15 +961,6 @@ static void complete_fetch_locked(grpc_exec_ctx *exec_ctx, void *gs,
   }
 }
 
-static void complete_fetch(grpc_exec_ctx *exec_ctx, void *gs,
-                           grpc_error *error) {
-  grpc_chttp2_stream *s = gs;
-  grpc_chttp2_transport *t = s->t;
-  grpc_combiner_execute(exec_ctx, t->combiner, &s->complete_fetch_locked,
-                        GRPC_ERROR_REF(error),
-                        s->complete_fetch_covered_by_poller);
-}
-
 static void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {}
 
 static void log_metadata(const grpc_metadata_batch *md_batch, uint32_t id,
@@ -987,7 +996,8 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
 
   grpc_closure *on_complete = op->on_complete;
   if (on_complete == NULL) {
-    on_complete = grpc_closure_create(do_nothing, NULL);
+    on_complete =
+        grpc_closure_create(do_nothing, NULL, grpc_schedule_on_exec_ctx);
   }
 
   /* use final_data as a barrier until enqueue time; the inital counter is
@@ -1011,6 +1021,7 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
 
   if (op->send_initial_metadata != NULL) {
     GPR_ASSERT(s->send_initial_metadata_finished == NULL);
+    on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE;
     s->send_initial_metadata_finished = add_closure_barrier(on_complete);
     s->send_initial_metadata = op->send_initial_metadata;
     const size_t metadata_size =
@@ -1064,6 +1075,7 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
   }
 
   if (op->send_message != NULL) {
+    on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE;
     s->fetching_send_message_finished = add_closure_barrier(op->on_complete);
     if (s->write_closed) {
       grpc_chttp2_complete_closure_step(
@@ -1101,6 +1113,7 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
 
   if (op->send_trailing_metadata != NULL) {
     GPR_ASSERT(s->send_trailing_metadata_finished == NULL);
+    on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE;
     s->send_trailing_metadata_finished = add_closure_barrier(on_complete);
     s->send_trailing_metadata = op->send_trailing_metadata;
     const size_t metadata_size =
@@ -1187,13 +1200,15 @@ static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
     gpr_free(str);
   }
 
-  grpc_closure_init(&op->transport_private.closure, perform_stream_op_locked,
-                    op);
   op->transport_private.args[0] = gt;
   op->transport_private.args[1] = gs;
   GRPC_CHTTP2_STREAM_REF(s, "perform_stream_op");
-  grpc_combiner_execute(exec_ctx, t->combiner, &op->transport_private.closure,
-                        GRPC_ERROR_NONE, op->covered_by_poller);
+  grpc_closure_sched(
+      exec_ctx,
+      grpc_closure_init(
+          &op->transport_private.closure, perform_stream_op_locked, op,
+          grpc_combiner_scheduler(t->combiner, op->covered_by_poller)),
+      GRPC_ERROR_NONE);
   GPR_TIMER_END("perform_stream_op", 0);
 }
 
@@ -1222,7 +1237,7 @@ void grpc_chttp2_ack_ping(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
   grpc_chttp2_outstanding_ping *ping;
   for (ping = t->pings.next; ping != &t->pings; ping = ping->next) {
     if (0 == memcmp(opaque_8bytes, ping->id, 8)) {
-      grpc_exec_ctx_sched(exec_ctx, ping->on_recv, GRPC_ERROR_NONE, NULL);
+      grpc_closure_sched(exec_ctx, ping->on_recv, GRPC_ERROR_NONE);
       ping->next->prev = ping->prev;
       ping->prev->next = ping->next;
       gpr_free(ping);
@@ -1260,7 +1275,7 @@ static void perform_transport_op_locked(grpc_exec_ctx *exec_ctx,
   if (op->send_goaway) {
     send_goaway(exec_ctx, t,
                 grpc_chttp2_grpc_status_to_http2_error(op->goaway_status),
-                grpc_slice_ref(*op->goaway_message));
+                grpc_slice_ref_internal(*op->goaway_message));
   }
 
   if (op->set_accept_stream) {
@@ -1296,11 +1311,12 @@ static void perform_transport_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
   char *msg = grpc_transport_op_string(op);
   gpr_free(msg);
   op->transport_private.args[0] = gt;
-  grpc_closure_init(&op->transport_private.closure, perform_transport_op_locked,
-                    op);
   GRPC_CHTTP2_REF_TRANSPORT(t, "transport_op");
-  grpc_combiner_execute(exec_ctx, t->combiner, &op->transport_private.closure,
-                        GRPC_ERROR_NONE, false);
+  grpc_closure_sched(
+      exec_ctx, grpc_closure_init(&op->transport_private.closure,
+                                  perform_transport_op_locked, op,
+                                  grpc_combiner_scheduler(t->combiner, false)),
+      GRPC_ERROR_NONE);
 }
 
 /*******************************************************************************
@@ -1490,21 +1506,22 @@ void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
     char status_string[GPR_LTOA_MIN_BUFSIZE];
     gpr_ltoa(status, status_string);
     grpc_chttp2_incoming_metadata_buffer_add(
-        &s->metadata_buffer[1],
-        grpc_mdelem_from_metadata_strings(
-            GRPC_MDSTR_GRPC_STATUS, grpc_mdstr_from_string(status_string)));
+        &s->metadata_buffer[1], grpc_mdelem_from_metadata_strings(
+                                    exec_ctx, GRPC_MDSTR_GRPC_STATUS,
+                                    grpc_mdstr_from_string(status_string)));
     if (slice) {
       grpc_chttp2_incoming_metadata_buffer_add(
           &s->metadata_buffer[1],
           grpc_mdelem_from_metadata_strings(
-              GRPC_MDSTR_GRPC_MESSAGE,
-              grpc_mdstr_from_slice(grpc_slice_ref(*slice))));
+              exec_ctx, GRPC_MDSTR_GRPC_MESSAGE,
+              grpc_mdstr_from_slice(exec_ctx,
+                                    grpc_slice_ref_internal(*slice))));
     }
     s->published_metadata[1] = GRPC_METADATA_SYNTHESIZED_FROM_FAKE;
     grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s);
   }
   if (slice) {
-    grpc_slice_unref(*slice);
+    grpc_slice_unref_internal(exec_ctx, *slice);
   }
 }
 
@@ -1534,9 +1551,9 @@ static grpc_error *removal_error(grpc_error *extra_error, grpc_chttp2_stream *s,
   return error;
 }
 
-static void fail_pending_writes(grpc_exec_ctx *exec_ctx,
-                                grpc_chttp2_transport *t, grpc_chttp2_stream *s,
-                                grpc_error *error) {
+void grpc_chttp2_fail_pending_writes(grpc_exec_ctx *exec_ctx,
+                                     grpc_chttp2_transport *t,
+                                     grpc_chttp2_stream *s, grpc_error *error) {
   error =
       removal_error(error, s, "Pending writes failed due to stream closure");
   s->send_initial_metadata = NULL;
@@ -1590,13 +1607,16 @@ void grpc_chttp2_mark_stream_closed(grpc_exec_ctx *exec_ctx,
   if (close_writes && !s->write_closed) {
     s->write_closed_error = GRPC_ERROR_REF(error);
     s->write_closed = true;
-    fail_pending_writes(exec_ctx, t, s, GRPC_ERROR_REF(error));
+    grpc_chttp2_fail_pending_writes(exec_ctx, t, s, GRPC_ERROR_REF(error));
     grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s);
   }
   if (s->read_closed && s->write_closed) {
     if (s->id != 0) {
       remove_stream(exec_ctx, t, s->id,
                     removal_error(GRPC_ERROR_REF(error), s, "Stream removed"));
+    } else {
+      /* Purge streams waiting on concurrency still waiting for id assignment */
+      grpc_chttp2_list_remove_waiting_for_concurrency(t, s);
     }
     GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "chttp2");
   }
@@ -1657,8 +1677,9 @@ static void close_from_api(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
 
     if (optional_message != NULL) {
       size_t msg_len = strlen(optional_message);
-      GPR_ASSERT(msg_len < 127);
-      message_pfx = grpc_slice_malloc(15);
+      GPR_ASSERT(msg_len <= UINT32_MAX);
+      uint32_t msg_len_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)msg_len, 0);
+      message_pfx = grpc_slice_malloc(14 + msg_len_len);
       p = GRPC_SLICE_START_PTR(message_pfx);
       *p++ = 0x40;
       *p++ = 12; /* len(grpc-message) */
@@ -1674,7 +1695,9 @@ static void close_from_api(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
       *p++ = 'a';
       *p++ = 'g';
       *p++ = 'e';
-      *p++ = (uint8_t)msg_len;
+      GRPC_CHTTP2_WRITE_VARINT((uint32_t)msg_len, 0, 0, p,
+                               (uint32_t)msg_len_len);
+      p += msg_len_len;
       GPR_ASSERT(p == GRPC_SLICE_END_PTR(message_pfx));
       len += (uint32_t)GRPC_SLICE_LENGTH(message_pfx);
       len += (uint32_t)msg_len;
@@ -1773,19 +1796,6 @@ static void update_global_window(void *args, uint32_t id, void *stream) {
  * INPUT PROCESSING - PARSING
  */
 
-static void read_action_begin(grpc_exec_ctx *exec_ctx, void *tp,
-                              grpc_error *error) {
-  /* Control flow:
-     reading_action_locked ->
-       (parse_unlocked -> post_parse_locked)? ->
-       post_reading_action_locked */
-  GPR_TIMER_BEGIN("reading_action", 0);
-  grpc_chttp2_transport *t = tp;
-  grpc_combiner_execute(exec_ctx, t->combiner, &t->read_action_locked,
-                        GRPC_ERROR_REF(error), false);
-  GPR_TIMER_END("reading_action", 0);
-}
-
 static grpc_error *try_http_parsing(grpc_exec_ctx *exec_ctx,
                                     grpc_chttp2_transport *t) {
   grpc_http_parser parser;
@@ -1882,10 +1892,11 @@ static void read_action_locked(grpc_exec_ctx *exec_ctx, void *tp,
     keep_reading = true;
     GRPC_CHTTP2_REF_TRANSPORT(t, "keep_reading");
   }
-  grpc_slice_buffer_reset_and_unref(&t->read_buffer);
+  grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &t->read_buffer);
 
   if (keep_reading) {
-    grpc_endpoint_read(exec_ctx, t->ep, &t->read_buffer, &t->read_action_begin);
+    grpc_endpoint_read(exec_ctx, t->ep, &t->read_buffer,
+                       &t->read_action_locked);
     GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "keep_reading");
   } else {
     GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "reading_action");
@@ -1936,7 +1947,7 @@ static void incoming_byte_stream_unref(grpc_exec_ctx *exec_ctx,
                                        grpc_chttp2_incoming_byte_stream *bs) {
   if (gpr_unref(&bs->refs)) {
     GRPC_ERROR_UNREF(bs->error);
-    grpc_slice_buffer_destroy(&bs->slices);
+    grpc_slice_buffer_destroy_internal(exec_ctx, &bs->slices);
     gpr_mu_destroy(&bs->slice_mu);
     gpr_free(bs);
   }
@@ -2022,10 +2033,12 @@ static int incoming_byte_stream_next(grpc_exec_ctx *exec_ctx,
   bs->next_action.slice = slice;
   bs->next_action.max_size_hint = max_size_hint;
   bs->next_action.on_complete = on_complete;
-  grpc_closure_init(&bs->next_action.closure, incoming_byte_stream_next_locked,
-                    bs);
-  grpc_combiner_execute(exec_ctx, bs->transport->combiner,
-                        &bs->next_action.closure, GRPC_ERROR_NONE, false);
+  grpc_closure_sched(
+      exec_ctx,
+      grpc_closure_init(
+          &bs->next_action.closure, incoming_byte_stream_next_locked, bs,
+          grpc_combiner_scheduler(bs->transport->combiner, false)),
+      GRPC_ERROR_NONE);
   GPR_TIMER_END("incoming_byte_stream_next", 0);
   return 0;
 }
@@ -2047,10 +2060,12 @@ static void incoming_byte_stream_destroy(grpc_exec_ctx *exec_ctx,
   GPR_TIMER_BEGIN("incoming_byte_stream_destroy", 0);
   grpc_chttp2_incoming_byte_stream *bs =
       (grpc_chttp2_incoming_byte_stream *)byte_stream;
-  grpc_closure_init(&bs->destroy_action, incoming_byte_stream_destroy_locked,
-                    bs);
-  grpc_combiner_execute(exec_ctx, bs->transport->combiner, &bs->destroy_action,
-                        GRPC_ERROR_NONE, false);
+  grpc_closure_sched(
+      exec_ctx,
+      grpc_closure_init(
+          &bs->destroy_action, incoming_byte_stream_destroy_locked, bs,
+          grpc_combiner_scheduler(bs->transport->combiner, false)),
+      GRPC_ERROR_NONE);
   GPR_TIMER_END("incoming_byte_stream_destroy", 0);
 }
 
@@ -2058,7 +2073,7 @@ static void incoming_byte_stream_publish_error(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs,
     grpc_error *error) {
   GPR_ASSERT(error != GRPC_ERROR_NONE);
-  grpc_exec_ctx_sched(exec_ctx, bs->on_next, GRPC_ERROR_REF(error), NULL);
+  grpc_closure_sched(exec_ctx, bs->on_next, GRPC_ERROR_REF(error));
   bs->on_next = NULL;
   GRPC_ERROR_UNREF(bs->error);
   bs->error = error;
@@ -2075,7 +2090,7 @@ void grpc_chttp2_incoming_byte_stream_push(grpc_exec_ctx *exec_ctx,
     bs->remaining_bytes -= (uint32_t)GRPC_SLICE_LENGTH(slice);
     if (bs->on_next != NULL) {
       *bs->next = slice;
-      grpc_exec_ctx_sched(exec_ctx, bs->on_next, GRPC_ERROR_NONE, NULL);
+      grpc_closure_sched(exec_ctx, bs->on_next, GRPC_ERROR_NONE);
       bs->on_next = NULL;
     } else {
       grpc_slice_buffer_add(&bs->slices, slice);
@@ -2143,7 +2158,7 @@ static void post_benign_reclaimer(grpc_exec_ctx *exec_ctx,
     GRPC_CHTTP2_REF_TRANSPORT(t, "benign_reclaimer");
     grpc_resource_user_post_reclaimer(exec_ctx,
                                       grpc_endpoint_get_resource_user(t->ep),
-                                      false, &t->benign_reclaimer);
+                                      false, &t->benign_reclaimer_locked);
   }
 }
 
@@ -2154,24 +2169,10 @@ static void post_destructive_reclaimer(grpc_exec_ctx *exec_ctx,
     GRPC_CHTTP2_REF_TRANSPORT(t, "destructive_reclaimer");
     grpc_resource_user_post_reclaimer(exec_ctx,
                                       grpc_endpoint_get_resource_user(t->ep),
-                                      true, &t->destructive_reclaimer);
+                                      true, &t->destructive_reclaimer_locked);
   }
 }
 
-static void benign_reclaimer(grpc_exec_ctx *exec_ctx, void *arg,
-                             grpc_error *error) {
-  grpc_chttp2_transport *t = arg;
-  grpc_combiner_execute(exec_ctx, t->combiner, &t->benign_reclaimer_locked,
-                        GRPC_ERROR_REF(error), false);
-}
-
-static void destructive_reclaimer(grpc_exec_ctx *exec_ctx, void *arg,
-                                  grpc_error *error) {
-  grpc_chttp2_transport *t = arg;
-  grpc_combiner_execute(exec_ctx, t->combiner, &t->destructive_reclaimer_locked,
-                        GRPC_ERROR_REF(error), false);
-}
-
 static void benign_reclaimer_locked(grpc_exec_ctx *exec_ctx, void *arg,
                                     grpc_error *error) {
   grpc_chttp2_transport *t = arg;
@@ -2352,5 +2353,5 @@ void grpc_chttp2_transport_start_reading(grpc_exec_ctx *exec_ctx,
     grpc_slice_buffer_move_into(read_buffer, &t->read_buffer);
     gpr_free(read_buffer);
   }
-  read_action_begin(exec_ctx, t, GRPC_ERROR_NONE);
+  grpc_closure_sched(exec_ctx, &t->read_action_locked, GRPC_ERROR_NONE);
 }
diff --git a/src/core/ext/transport/chttp2/transport/frame_rst_stream.c b/src/core/ext/transport/chttp2/transport/frame_rst_stream.c
index b4c5ed769b9cf0cc689411454738125bfe17033c..20043f5fbf431cd9a275cf6e3ae782b6b7e4fc98 100644
--- a/src/core/ext/transport/chttp2/transport/frame_rst_stream.c
+++ b/src/core/ext/transport/chttp2/transport/frame_rst_stream.c
@@ -109,7 +109,7 @@ grpc_error *grpc_chttp2_rst_stream_parser_parse(grpc_exec_ctx *exec_ctx,
                       (((uint32_t)p->reason_bytes[2]) << 8) |
                       (((uint32_t)p->reason_bytes[3]));
     grpc_error *error = GRPC_ERROR_NONE;
-    if (reason != GRPC_CHTTP2_NO_ERROR) {
+    if (reason != GRPC_CHTTP2_NO_ERROR || s->header_frames_received < 2) {
       error = grpc_error_set_int(GRPC_ERROR_CREATE("RST_STREAM"),
                                  GRPC_ERROR_INT_HTTP2_ERROR, (intptr_t)reason);
       grpc_status_code status_code = grpc_chttp2_http2_error_to_grpc_status(
diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.c b/src/core/ext/transport/chttp2/transport/hpack_encoder.c
index eb68fe3138749256bf1f69d6e513c59f29078ef5..49a8326f627d61831f92f5dcea25f8904a489dd9 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_encoder.c
+++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.c
@@ -48,6 +48,7 @@
 #include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
 #include "src/core/ext/transport/chttp2/transport/hpack_table.h"
 #include "src/core/ext/transport/chttp2/transport/varint.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/transport/metadata.h"
 #include "src/core/lib/transport/static_metadata.h"
 #include "src/core/lib/transport/timeout_encoding.h"
@@ -183,7 +184,8 @@ static void evict_entry(grpc_chttp2_hpack_compressor *c) {
 }
 
 /* add an element to the decoder table */
-static void add_elem(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem) {
+static void add_elem(grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_compressor *c,
+                     grpc_mdelem *elem) {
   uint32_t key_hash = elem->key->hash;
   uint32_t elem_hash = GRPC_MDSTR_KV_HASH(key_hash, elem->value->hash);
   uint32_t new_index = c->tail_remote_index + c->table_elems + 1;
@@ -227,12 +229,12 @@ static void add_elem(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem) {
   } else if (c->indices_elems[HASH_FRAGMENT_2(elem_hash)] <
              c->indices_elems[HASH_FRAGMENT_3(elem_hash)]) {
     /* not there: replace oldest */
-    GRPC_MDELEM_UNREF(c->entries_elems[HASH_FRAGMENT_2(elem_hash)]);
+    GRPC_MDELEM_UNREF(exec_ctx, c->entries_elems[HASH_FRAGMENT_2(elem_hash)]);
     c->entries_elems[HASH_FRAGMENT_2(elem_hash)] = GRPC_MDELEM_REF(elem);
     c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index;
   } else {
     /* not there: replace oldest */
-    GRPC_MDELEM_UNREF(c->entries_elems[HASH_FRAGMENT_3(elem_hash)]);
+    GRPC_MDELEM_UNREF(exec_ctx, c->entries_elems[HASH_FRAGMENT_3(elem_hash)]);
     c->entries_elems[HASH_FRAGMENT_3(elem_hash)] = GRPC_MDELEM_REF(elem);
     c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index;
   }
@@ -251,11 +253,11 @@ static void add_elem(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem) {
     c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index;
   } else if (c->indices_keys[HASH_FRAGMENT_2(key_hash)] <
              c->indices_keys[HASH_FRAGMENT_3(key_hash)]) {
-    GRPC_MDSTR_UNREF(c->entries_keys[HASH_FRAGMENT_2(key_hash)]);
+    GRPC_MDSTR_UNREF(exec_ctx, c->entries_keys[HASH_FRAGMENT_2(key_hash)]);
     c->entries_keys[HASH_FRAGMENT_2(key_hash)] = GRPC_MDSTR_REF(elem->key);
     c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index;
   } else {
-    GRPC_MDSTR_UNREF(c->entries_keys[HASH_FRAGMENT_3(key_hash)]);
+    GRPC_MDSTR_UNREF(exec_ctx, c->entries_keys[HASH_FRAGMENT_3(key_hash)]);
     c->entries_keys[HASH_FRAGMENT_3(key_hash)] = GRPC_MDSTR_REF(elem->key);
     c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index;
   }
@@ -294,7 +296,7 @@ static void emit_lithdr_incidx(grpc_chttp2_hpack_compressor *c,
                            add_tiny_header_data(st, len_pfx), len_pfx);
   GRPC_CHTTP2_WRITE_VARINT((uint32_t)len_val, 1, huffman_prefix,
                            add_tiny_header_data(st, len_val_len), len_val_len);
-  add_header_data(st, grpc_slice_ref(value_slice));
+  add_header_data(st, grpc_slice_ref_internal(value_slice));
 }
 
 static void emit_lithdr_noidx(grpc_chttp2_hpack_compressor *c,
@@ -311,7 +313,7 @@ static void emit_lithdr_noidx(grpc_chttp2_hpack_compressor *c,
                            add_tiny_header_data(st, len_pfx), len_pfx);
   GRPC_CHTTP2_WRITE_VARINT((uint32_t)len_val, 1, huffman_prefix,
                            add_tiny_header_data(st, len_val_len), len_val_len);
-  add_header_data(st, grpc_slice_ref(value_slice));
+  add_header_data(st, grpc_slice_ref_internal(value_slice));
 }
 
 static void emit_lithdr_incidx_v(grpc_chttp2_hpack_compressor *c,
@@ -327,10 +329,10 @@ static void emit_lithdr_incidx_v(grpc_chttp2_hpack_compressor *c,
   *add_tiny_header_data(st, 1) = 0x40;
   GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00,
                            add_tiny_header_data(st, len_key_len), len_key_len);
-  add_header_data(st, grpc_slice_ref(elem->key->slice));
+  add_header_data(st, grpc_slice_ref_internal(elem->key->slice));
   GRPC_CHTTP2_WRITE_VARINT(len_val, 1, huffman_prefix,
                            add_tiny_header_data(st, len_val_len), len_val_len);
-  add_header_data(st, grpc_slice_ref(value_slice));
+  add_header_data(st, grpc_slice_ref_internal(value_slice));
 }
 
 static void emit_lithdr_noidx_v(grpc_chttp2_hpack_compressor *c,
@@ -346,10 +348,10 @@ static void emit_lithdr_noidx_v(grpc_chttp2_hpack_compressor *c,
   *add_tiny_header_data(st, 1) = 0x00;
   GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00,
                            add_tiny_header_data(st, len_key_len), len_key_len);
-  add_header_data(st, grpc_slice_ref(elem->key->slice));
+  add_header_data(st, grpc_slice_ref_internal(elem->key->slice));
   GRPC_CHTTP2_WRITE_VARINT(len_val, 1, huffman_prefix,
                            add_tiny_header_data(st, len_val_len), len_val_len);
-  add_header_data(st, grpc_slice_ref(value_slice));
+  add_header_data(st, grpc_slice_ref_internal(value_slice));
 }
 
 static void emit_advertise_table_size_change(grpc_chttp2_hpack_compressor *c,
@@ -366,8 +368,8 @@ static uint32_t dynidx(grpc_chttp2_hpack_compressor *c, uint32_t elem_index) {
 }
 
 /* encode an mdelem */
-static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem,
-                      framer_state *st) {
+static void hpack_enc(grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_compressor *c,
+                      grpc_mdelem *elem, framer_state *st) {
   uint32_t key_hash = elem->key->hash;
   uint32_t elem_hash = GRPC_MDSTR_KV_HASH(key_hash, elem->value->hash);
   size_t decoder_space_usage;
@@ -417,7 +419,7 @@ static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem,
     /* HIT: key (first cuckoo hash) */
     if (should_add_elem) {
       emit_lithdr_incidx(c, dynidx(c, indices_key), elem, st);
-      add_elem(c, elem);
+      add_elem(exec_ctx, c, elem);
       return;
     } else {
       emit_lithdr_noidx(c, dynidx(c, indices_key), elem, st);
@@ -432,7 +434,7 @@ static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem,
     /* HIT: key (first cuckoo hash) */
     if (should_add_elem) {
       emit_lithdr_incidx(c, dynidx(c, indices_key), elem, st);
-      add_elem(c, elem);
+      add_elem(exec_ctx, c, elem);
       return;
     } else {
       emit_lithdr_noidx(c, dynidx(c, indices_key), elem, st);
@@ -445,7 +447,7 @@ static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem,
 
   if (should_add_elem) {
     emit_lithdr_incidx_v(c, elem, st);
-    add_elem(c, elem);
+    add_elem(exec_ctx, c, elem);
     return;
   } else {
     emit_lithdr_noidx_v(c, elem, st);
@@ -457,16 +459,17 @@ static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem,
 #define STRLEN_LIT(x) (sizeof(x) - 1)
 #define TIMEOUT_KEY "grpc-timeout"
 
-static void deadline_enc(grpc_chttp2_hpack_compressor *c, gpr_timespec deadline,
+static void deadline_enc(grpc_exec_ctx *exec_ctx,
+                         grpc_chttp2_hpack_compressor *c, gpr_timespec deadline,
                          framer_state *st) {
   char timeout_str[GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE];
   grpc_mdelem *mdelem;
   grpc_http2_encode_timeout(
       gpr_time_sub(deadline, gpr_now(deadline.clock_type)), timeout_str);
   mdelem = grpc_mdelem_from_metadata_strings(
-      GRPC_MDSTR_GRPC_TIMEOUT, grpc_mdstr_from_string(timeout_str));
-  hpack_enc(c, mdelem, st);
-  GRPC_MDELEM_UNREF(mdelem);
+      exec_ctx, GRPC_MDSTR_GRPC_TIMEOUT, grpc_mdstr_from_string(timeout_str));
+  hpack_enc(exec_ctx, c, mdelem, st);
+  GRPC_MDELEM_UNREF(exec_ctx, mdelem);
 }
 
 static uint32_t elems_for_bytes(uint32_t bytes) { return (bytes + 31) / 32; }
@@ -483,11 +486,12 @@ void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c) {
          sizeof(*c->table_elem_size) * c->cap_table_elems);
 }
 
-void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c) {
+void grpc_chttp2_hpack_compressor_destroy(grpc_exec_ctx *exec_ctx,
+                                          grpc_chttp2_hpack_compressor *c) {
   int i;
   for (i = 0; i < GRPC_CHTTP2_HPACKC_NUM_VALUES; i++) {
-    if (c->entries_keys[i]) GRPC_MDSTR_UNREF(c->entries_keys[i]);
-    if (c->entries_elems[i]) GRPC_MDELEM_UNREF(c->entries_elems[i]);
+    if (c->entries_keys[i]) GRPC_MDSTR_UNREF(exec_ctx, c->entries_keys[i]);
+    if (c->entries_elems[i]) GRPC_MDELEM_UNREF(exec_ctx, c->entries_elems[i]);
   }
   gpr_free(c->table_elem_size);
 }
@@ -542,7 +546,8 @@ void grpc_chttp2_hpack_compressor_set_max_table_size(
   }
 }
 
-void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c,
+void grpc_chttp2_encode_header(grpc_exec_ctx *exec_ctx,
+                               grpc_chttp2_hpack_compressor *c,
                                uint32_t stream_id,
                                grpc_metadata_batch *metadata, int is_eof,
                                size_t max_frame_size,
@@ -571,11 +576,11 @@ void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c,
   }
   grpc_metadata_batch_assert_ok(metadata);
   for (l = metadata->list.head; l; l = l->next) {
-    hpack_enc(c, l->md, &st);
+    hpack_enc(exec_ctx, c, l->md, &st);
   }
   deadline = metadata->deadline;
   if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) != 0) {
-    deadline_enc(c, deadline, &st);
+    deadline_enc(exec_ctx, c, deadline, &st);
   }
 
   finish_frame(&st, 1, is_eof);
diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.h b/src/core/ext/transport/chttp2/transport/hpack_encoder.h
index bcbd675ca2ef3aa1c68b219c07762d67bc70a714..3a35496ec8a664f64e8793d052c23e9045112b61 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_encoder.h
+++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.h
@@ -83,13 +83,15 @@ typedef struct {
 } grpc_chttp2_hpack_compressor;
 
 void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c);
-void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c);
+void grpc_chttp2_hpack_compressor_destroy(grpc_exec_ctx *exec_ctx,
+                                          grpc_chttp2_hpack_compressor *c);
 void grpc_chttp2_hpack_compressor_set_max_table_size(
     grpc_chttp2_hpack_compressor *c, uint32_t max_table_size);
 void grpc_chttp2_hpack_compressor_set_max_usable_size(
     grpc_chttp2_hpack_compressor *c, uint32_t max_table_size);
 
-void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c, uint32_t id,
+void grpc_chttp2_encode_header(grpc_exec_ctx *exec_ctx,
+                               grpc_chttp2_hpack_compressor *c, uint32_t id,
                                grpc_metadata_batch *metadata, int is_eof,
                                size_t max_frame_size,
                                grpc_transport_one_way_stats *stats,
diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.c b/src/core/ext/transport/chttp2/transport/hpack_parser.c
index 6a9200b8dbdf13bd85666a9ec4d322c42c27d82b..8b91cc760b85c018a56e5d36ca98efc384a1cb6d 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_parser.c
+++ b/src/core/ext/transport/chttp2/transport/hpack_parser.c
@@ -670,11 +670,11 @@ static const uint8_t inverse_base64[256] = {
 static grpc_error *on_hdr(grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_parser *p,
                           grpc_mdelem *md, int add_to_table) {
   if (add_to_table) {
-    grpc_error *err = grpc_chttp2_hptbl_add(&p->table, md);
+    grpc_error *err = grpc_chttp2_hptbl_add(exec_ctx, &p->table, md);
     if (err != GRPC_ERROR_NONE) return err;
   }
   if (p->on_header == NULL) {
-    GRPC_MDELEM_UNREF(md);
+    GRPC_MDELEM_UNREF(exec_ctx, md);
     return GRPC_ERROR_CREATE("on_header callback not set");
   }
   p->on_header(exec_ctx, p->on_header_user_data, md);
@@ -815,10 +815,10 @@ static grpc_error *finish_lithdr_incidx(grpc_exec_ctx *exec_ctx,
                                         const uint8_t *end) {
   grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
   GPR_ASSERT(md != NULL); /* handled in string parsing */
-  grpc_error *err = on_hdr(
-      exec_ctx, p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
-                                                     take_string(p, &p->value)),
-      1);
+  grpc_error *err = on_hdr(exec_ctx, p, grpc_mdelem_from_metadata_strings(
+                                            exec_ctx, GRPC_MDSTR_REF(md->key),
+                                            take_string(p, &p->value)),
+                           1);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
 }
@@ -828,10 +828,10 @@ static grpc_error *finish_lithdr_incidx_v(grpc_exec_ctx *exec_ctx,
                                           grpc_chttp2_hpack_parser *p,
                                           const uint8_t *cur,
                                           const uint8_t *end) {
-  grpc_error *err = on_hdr(
-      exec_ctx, p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key),
-                                                     take_string(p, &p->value)),
-      1);
+  grpc_error *err = on_hdr(exec_ctx, p, grpc_mdelem_from_metadata_strings(
+                                            exec_ctx, take_string(p, &p->key),
+                                            take_string(p, &p->value)),
+                           1);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
 }
@@ -883,10 +883,10 @@ static grpc_error *finish_lithdr_notidx(grpc_exec_ctx *exec_ctx,
                                         const uint8_t *end) {
   grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
   GPR_ASSERT(md != NULL); /* handled in string parsing */
-  grpc_error *err = on_hdr(
-      exec_ctx, p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
-                                                     take_string(p, &p->value)),
-      0);
+  grpc_error *err = on_hdr(exec_ctx, p, grpc_mdelem_from_metadata_strings(
+                                            exec_ctx, GRPC_MDSTR_REF(md->key),
+                                            take_string(p, &p->value)),
+                           0);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
 }
@@ -896,10 +896,10 @@ static grpc_error *finish_lithdr_notidx_v(grpc_exec_ctx *exec_ctx,
                                           grpc_chttp2_hpack_parser *p,
                                           const uint8_t *cur,
                                           const uint8_t *end) {
-  grpc_error *err = on_hdr(
-      exec_ctx, p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key),
-                                                     take_string(p, &p->value)),
-      0);
+  grpc_error *err = on_hdr(exec_ctx, p, grpc_mdelem_from_metadata_strings(
+                                            exec_ctx, take_string(p, &p->key),
+                                            take_string(p, &p->value)),
+                           0);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
 }
@@ -951,10 +951,10 @@ static grpc_error *finish_lithdr_nvridx(grpc_exec_ctx *exec_ctx,
                                         const uint8_t *end) {
   grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
   GPR_ASSERT(md != NULL); /* handled in string parsing */
-  grpc_error *err = on_hdr(
-      exec_ctx, p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
-                                                     take_string(p, &p->value)),
-      0);
+  grpc_error *err = on_hdr(exec_ctx, p, grpc_mdelem_from_metadata_strings(
+                                            exec_ctx, GRPC_MDSTR_REF(md->key),
+                                            take_string(p, &p->value)),
+                           0);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
 }
@@ -964,10 +964,10 @@ static grpc_error *finish_lithdr_nvridx_v(grpc_exec_ctx *exec_ctx,
                                           grpc_chttp2_hpack_parser *p,
                                           const uint8_t *cur,
                                           const uint8_t *end) {
-  grpc_error *err = on_hdr(
-      exec_ctx, p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key),
-                                                     take_string(p, &p->value)),
-      0);
+  grpc_error *err = on_hdr(exec_ctx, p, grpc_mdelem_from_metadata_strings(
+                                            exec_ctx, take_string(p, &p->key),
+                                            take_string(p, &p->value)),
+                           0);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
 }
@@ -1020,7 +1020,7 @@ static grpc_error *finish_max_tbl_size(grpc_exec_ctx *exec_ctx,
     gpr_log(GPR_INFO, "MAX TABLE SIZE: %d", p->index);
   }
   grpc_error *err =
-      grpc_chttp2_hptbl_set_current_table_size(&p->table, p->index);
+      grpc_chttp2_hptbl_set_current_table_size(exec_ctx, &p->table, p->index);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
 }
@@ -1534,7 +1534,8 @@ static grpc_error *parse_value_string_with_literal_key(
 
 /* PUBLIC INTERFACE */
 
-void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p) {
+void grpc_chttp2_hpack_parser_init(grpc_exec_ctx *exec_ctx,
+                                   grpc_chttp2_hpack_parser *p) {
   p->on_header = NULL;
   p->on_header_user_data = NULL;
   p->state = parse_begin;
@@ -1546,7 +1547,7 @@ void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p) {
   p->value.length = 0;
   p->dynamic_table_update_allowed = 2;
   p->last_error = GRPC_ERROR_NONE;
-  grpc_chttp2_hptbl_init(&p->table);
+  grpc_chttp2_hptbl_init(exec_ctx, &p->table);
 }
 
 void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p) {
@@ -1554,8 +1555,9 @@ void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p) {
   p->state = parse_stream_dep0;
 }
 
-void grpc_chttp2_hpack_parser_destroy(grpc_chttp2_hpack_parser *p) {
-  grpc_chttp2_hptbl_destroy(&p->table);
+void grpc_chttp2_hpack_parser_destroy(grpc_exec_ctx *exec_ctx,
+                                      grpc_chttp2_hpack_parser *p) {
+  grpc_chttp2_hptbl_destroy(exec_ctx, &p->table);
   GRPC_ERROR_UNREF(p->last_error);
   gpr_free(p->key.str);
   gpr_free(p->value.str);
@@ -1634,10 +1636,11 @@ grpc_error *grpc_chttp2_header_parser_parse(grpc_exec_ctx *exec_ctx,
              however -- it might be that we receive a RST_STREAM following this
              and can avoid the extra write */
           GRPC_CHTTP2_STREAM_REF(s, "final_rst");
-          grpc_combiner_execute_finally(
-              exec_ctx, t->combiner,
-              grpc_closure_create(force_client_rst_stream, s), GRPC_ERROR_NONE,
-              false);
+          grpc_closure_sched(
+              exec_ctx, grpc_closure_create(force_client_rst_stream, s,
+                                            grpc_combiner_finally_scheduler(
+                                                t->combiner, false)),
+              GRPC_ERROR_NONE);
         }
         grpc_chttp2_mark_stream_closed(exec_ctx, t, s, true, false,
                                        GRPC_ERROR_NONE);
diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.h b/src/core/ext/transport/chttp2/transport/hpack_parser.h
index a39bf466cdfcfd28f600a29f47600e8b398a523d..52ccf1e7a7334c9fd93fea3c17bff96a8cd4e086 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_parser.h
+++ b/src/core/ext/transport/chttp2/transport/hpack_parser.h
@@ -99,8 +99,10 @@ struct grpc_chttp2_hpack_parser {
   grpc_chttp2_hptbl table;
 };
 
-void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p);
-void grpc_chttp2_hpack_parser_destroy(grpc_chttp2_hpack_parser *p);
+void grpc_chttp2_hpack_parser_init(grpc_exec_ctx *exec_ctx,
+                                   grpc_chttp2_hpack_parser *p);
+void grpc_chttp2_hpack_parser_destroy(grpc_exec_ctx *exec_ctx,
+                                      grpc_chttp2_hpack_parser *p);
 
 void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p);
 
diff --git a/src/core/ext/transport/chttp2/transport/hpack_table.c b/src/core/ext/transport/chttp2/transport/hpack_table.c
index 2dc793d304b6d08d89d7b68d93014969c0ab1703..26d4036d49f6272b84e3e13096ae82684a050a6f 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_table.c
+++ b/src/core/ext/transport/chttp2/transport/hpack_table.c
@@ -179,7 +179,7 @@ static uint32_t entries_for_bytes(uint32_t bytes) {
          GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
 }
 
-void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl) {
+void grpc_chttp2_hptbl_init(grpc_exec_ctx *exec_ctx, grpc_chttp2_hptbl *tbl) {
   size_t i;
 
   memset(tbl, 0, sizeof(*tbl));
@@ -190,18 +190,20 @@ void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl) {
   tbl->ents = gpr_malloc(sizeof(*tbl->ents) * tbl->cap_entries);
   memset(tbl->ents, 0, sizeof(*tbl->ents) * tbl->cap_entries);
   for (i = 1; i <= GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) {
-    tbl->static_ents[i - 1] =
-        grpc_mdelem_from_strings(static_table[i].key, static_table[i].value);
+    tbl->static_ents[i - 1] = grpc_mdelem_from_strings(
+        exec_ctx, static_table[i].key, static_table[i].value);
   }
 }
 
-void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl) {
+void grpc_chttp2_hptbl_destroy(grpc_exec_ctx *exec_ctx,
+                               grpc_chttp2_hptbl *tbl) {
   size_t i;
   for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) {
-    GRPC_MDELEM_UNREF(tbl->static_ents[i]);
+    GRPC_MDELEM_UNREF(exec_ctx, tbl->static_ents[i]);
   }
   for (i = 0; i < tbl->num_ents; i++) {
-    GRPC_MDELEM_UNREF(tbl->ents[(tbl->first_ent + i) % tbl->cap_entries]);
+    GRPC_MDELEM_UNREF(exec_ctx,
+                      tbl->ents[(tbl->first_ent + i) % tbl->cap_entries]);
   }
   gpr_free(tbl->ents);
 }
@@ -224,7 +226,7 @@ grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl,
 }
 
 /* Evict one element from the table */
-static void evict1(grpc_chttp2_hptbl *tbl) {
+static void evict1(grpc_exec_ctx *exec_ctx, grpc_chttp2_hptbl *tbl) {
   grpc_mdelem *first_ent = tbl->ents[tbl->first_ent];
   size_t elem_bytes = GRPC_SLICE_LENGTH(first_ent->key->slice) +
                       GRPC_SLICE_LENGTH(first_ent->value->slice) +
@@ -233,7 +235,7 @@ static void evict1(grpc_chttp2_hptbl *tbl) {
   tbl->mem_used -= (uint32_t)elem_bytes;
   tbl->first_ent = ((tbl->first_ent + 1) % tbl->cap_entries);
   tbl->num_ents--;
-  GRPC_MDELEM_UNREF(first_ent);
+  GRPC_MDELEM_UNREF(exec_ctx, first_ent);
 }
 
 static void rebuild_ents(grpc_chttp2_hptbl *tbl, uint32_t new_cap) {
@@ -249,7 +251,8 @@ static void rebuild_ents(grpc_chttp2_hptbl *tbl, uint32_t new_cap) {
   tbl->first_ent = 0;
 }
 
-void grpc_chttp2_hptbl_set_max_bytes(grpc_chttp2_hptbl *tbl,
+void grpc_chttp2_hptbl_set_max_bytes(grpc_exec_ctx *exec_ctx,
+                                     grpc_chttp2_hptbl *tbl,
                                      uint32_t max_bytes) {
   if (tbl->max_bytes == max_bytes) {
     return;
@@ -258,12 +261,13 @@ void grpc_chttp2_hptbl_set_max_bytes(grpc_chttp2_hptbl *tbl,
     gpr_log(GPR_DEBUG, "Update hpack parser max size to %d", max_bytes);
   }
   while (tbl->mem_used > max_bytes) {
-    evict1(tbl);
+    evict1(exec_ctx, tbl);
   }
   tbl->max_bytes = max_bytes;
 }
 
-grpc_error *grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl *tbl,
+grpc_error *grpc_chttp2_hptbl_set_current_table_size(grpc_exec_ctx *exec_ctx,
+                                                     grpc_chttp2_hptbl *tbl,
                                                      uint32_t bytes) {
   if (tbl->current_table_bytes == bytes) {
     return GRPC_ERROR_NONE;
@@ -281,7 +285,7 @@ grpc_error *grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl *tbl,
     gpr_log(GPR_DEBUG, "Update hpack parser table size to %d", bytes);
   }
   while (tbl->mem_used > bytes) {
-    evict1(tbl);
+    evict1(exec_ctx, tbl);
   }
   tbl->current_table_bytes = bytes;
   tbl->max_entries = entries_for_bytes(bytes);
@@ -296,7 +300,8 @@ grpc_error *grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl *tbl,
   return GRPC_ERROR_NONE;
 }
 
-grpc_error *grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {
+grpc_error *grpc_chttp2_hptbl_add(grpc_exec_ctx *exec_ctx,
+                                  grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {
   /* determine how many bytes of buffer this entry represents */
   size_t elem_bytes = GRPC_SLICE_LENGTH(md->key->slice) +
                       GRPC_SLICE_LENGTH(md->value->slice) +
@@ -326,14 +331,14 @@ grpc_error *grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {
      * empty table.
      */
     while (tbl->num_ents) {
-      evict1(tbl);
+      evict1(exec_ctx, tbl);
     }
     return GRPC_ERROR_NONE;
   }
 
   /* evict entries to ensure no overflow */
   while (elem_bytes > (size_t)tbl->current_table_bytes - tbl->mem_used) {
-    evict1(tbl);
+    evict1(exec_ctx, tbl);
   }
 
   /* copy the finalized entry in */
diff --git a/src/core/ext/transport/chttp2/transport/hpack_table.h b/src/core/ext/transport/chttp2/transport/hpack_table.h
index 2ca130e64b20f9095113edeb3203dbbc7bc44a9e..144574ef06ec73063be0a5cd40507c775d2a66d0 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_table.h
+++ b/src/core/ext/transport/chttp2/transport/hpack_table.h
@@ -84,18 +84,21 @@ typedef struct {
 } grpc_chttp2_hptbl;
 
 /* initialize a hpack table */
-void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl);
-void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl);
-void grpc_chttp2_hptbl_set_max_bytes(grpc_chttp2_hptbl *tbl,
+void grpc_chttp2_hptbl_init(grpc_exec_ctx *exec_ctx, grpc_chttp2_hptbl *tbl);
+void grpc_chttp2_hptbl_destroy(grpc_exec_ctx *exec_ctx, grpc_chttp2_hptbl *tbl);
+void grpc_chttp2_hptbl_set_max_bytes(grpc_exec_ctx *exec_ctx,
+                                     grpc_chttp2_hptbl *tbl,
                                      uint32_t max_bytes);
-grpc_error *grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl *tbl,
+grpc_error *grpc_chttp2_hptbl_set_current_table_size(grpc_exec_ctx *exec_ctx,
+                                                     grpc_chttp2_hptbl *tbl,
                                                      uint32_t bytes);
 
 /* lookup a table entry based on its hpack index */
 grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl,
                                       uint32_t index);
 /* add a table entry to the index */
-grpc_error *grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl,
+grpc_error *grpc_chttp2_hptbl_add(grpc_exec_ctx *exec_ctx,
+                                  grpc_chttp2_hptbl *tbl,
                                   grpc_mdelem *md) GRPC_MUST_USE_RESULT;
 /* Find a key/value pair in the table... returns the index in the table of the
    most similar entry, or 0 if the value was not found */
diff --git a/src/core/ext/transport/chttp2/transport/incoming_metadata.c b/src/core/ext/transport/chttp2/transport/incoming_metadata.c
index 3e463a79957b08c4765421809d2e674c7edb4450..5d1094999cb4772fe2a301d4d591336918175b93 100644
--- a/src/core/ext/transport/chttp2/transport/incoming_metadata.c
+++ b/src/core/ext/transport/chttp2/transport/incoming_metadata.c
@@ -46,11 +46,11 @@ void grpc_chttp2_incoming_metadata_buffer_init(
 }
 
 void grpc_chttp2_incoming_metadata_buffer_destroy(
-    grpc_chttp2_incoming_metadata_buffer *buffer) {
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_metadata_buffer *buffer) {
   size_t i;
   if (!buffer->published) {
     for (i = 0; i < buffer->count; i++) {
-      GRPC_MDELEM_UNREF(buffer->elems[i].md);
+      GRPC_MDELEM_UNREF(exec_ctx, buffer->elems[i].md);
     }
   }
   gpr_free(buffer->elems);
diff --git a/src/core/ext/transport/chttp2/transport/incoming_metadata.h b/src/core/ext/transport/chttp2/transport/incoming_metadata.h
index df4343b93ed681d05c569cfa9a2fdd18f4c5aa94..7a0c4da15f33bc5db0639f4d8bf289c10f884adb 100644
--- a/src/core/ext/transport/chttp2/transport/incoming_metadata.h
+++ b/src/core/ext/transport/chttp2/transport/incoming_metadata.h
@@ -49,7 +49,7 @@ typedef struct {
 void grpc_chttp2_incoming_metadata_buffer_init(
     grpc_chttp2_incoming_metadata_buffer *buffer);
 void grpc_chttp2_incoming_metadata_buffer_destroy(
-    grpc_chttp2_incoming_metadata_buffer *buffer);
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_metadata_buffer *buffer);
 void grpc_chttp2_incoming_metadata_buffer_publish(
     grpc_chttp2_incoming_metadata_buffer *buffer, grpc_metadata_batch *batch);
 
diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h
index b74233d9923d7c752168c41423bca642b530d7f9..a52acbacdb3e28a21ea99a10cb07870556b9f9cc 100644
--- a/src/core/ext/transport/chttp2/transport/internal.h
+++ b/src/core/ext/transport/chttp2/transport/internal.h
@@ -212,10 +212,8 @@ struct grpc_chttp2_transport {
 
   grpc_closure write_action_begin_locked;
   grpc_closure write_action;
-  grpc_closure write_action_end;
   grpc_closure write_action_end_locked;
 
-  grpc_closure read_action_begin;
   grpc_closure read_action_locked;
 
   /** incoming read bytes */
@@ -327,16 +325,17 @@ struct grpc_chttp2_transport {
    */
   grpc_error *close_transport_on_writes_finished;
 
+  /* a list of closures to run after writes are finished */
+  grpc_closure_list run_after_write;
+
   /* buffer pool state */
   /** have we scheduled a benign cleanup? */
   bool benign_reclaimer_registered;
   /** have we scheduled a destructive cleanup? */
   bool destructive_reclaimer_registered;
   /** benign cleanup closure */
-  grpc_closure benign_reclaimer;
   grpc_closure benign_reclaimer_locked;
   /** destructive cleanup closure */
-  grpc_closure destructive_reclaimer;
   grpc_closure destructive_reclaimer_locked;
 };
 
@@ -493,6 +492,8 @@ void grpc_chttp2_list_add_waiting_for_concurrency(grpc_chttp2_transport *t,
                                                   grpc_chttp2_stream *s);
 int grpc_chttp2_list_pop_waiting_for_concurrency(grpc_chttp2_transport *t,
                                                  grpc_chttp2_stream **s);
+void grpc_chttp2_list_remove_waiting_for_concurrency(grpc_chttp2_transport *t,
+                                                     grpc_chttp2_stream *s);
 
 void grpc_chttp2_list_add_stalled_by_transport(grpc_chttp2_transport *t,
                                                grpc_chttp2_stream *s);
@@ -689,4 +690,8 @@ void grpc_chttp2_maybe_complete_recv_trailing_metadata(grpc_exec_ctx *exec_ctx,
                                                        grpc_chttp2_transport *t,
                                                        grpc_chttp2_stream *s);
 
+void grpc_chttp2_fail_pending_writes(grpc_exec_ctx *exec_ctx,
+                                     grpc_chttp2_transport *t,
+                                     grpc_chttp2_stream *s, grpc_error *error);
+
 #endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INTERNAL_H */
diff --git a/src/core/ext/transport/chttp2/transport/parsing.c b/src/core/ext/transport/chttp2/transport/parsing.c
index 5efb49751c3affadb4ff2a3755a8dba387923fae..4fb5dc7bd2bec429c91f6c0249f8c2eb8dd820b7 100644
--- a/src/core/ext/transport/chttp2/transport/parsing.c
+++ b/src/core/ext/transport/chttp2/transport/parsing.c
@@ -336,7 +336,7 @@ static grpc_error *skip_parser(grpc_exec_ctx *exec_ctx, void *parser,
 }
 
 static void skip_header(grpc_exec_ctx *exec_ctx, void *tp, grpc_mdelem *md) {
-  GRPC_MDELEM_UNREF(md);
+  GRPC_MDELEM_UNREF(exec_ctx, md);
 }
 
 static grpc_error *init_skip_frame_parser(grpc_exec_ctx *exec_ctx,
@@ -477,7 +477,7 @@ static void on_initial_header(grpc_exec_ctx *exec_ctx, void *tp,
     grpc_chttp2_incoming_metadata_buffer_set_deadline(
         &s->metadata_buffer[0],
         gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), *cached_timeout));
-    GRPC_MDELEM_UNREF(md);
+    GRPC_MDELEM_UNREF(exec_ctx, md);
   } else {
     const size_t new_size = s->metadata_buffer[0].size + GRPC_MDELEM_LENGTH(md);
     const size_t metadata_size_limit =
@@ -495,7 +495,7 @@ static void on_initial_header(grpc_exec_ctx *exec_ctx, void *tp,
               GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED));
       grpc_chttp2_parsing_become_skip_parser(exec_ctx, t);
       s->seen_error = true;
-      GRPC_MDELEM_UNREF(md);
+      GRPC_MDELEM_UNREF(exec_ctx, md);
     } else {
       grpc_chttp2_incoming_metadata_buffer_add(&s->metadata_buffer[0], md);
     }
@@ -538,7 +538,7 @@ static void on_trailing_header(grpc_exec_ctx *exec_ctx, void *tp,
             GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED));
     grpc_chttp2_parsing_become_skip_parser(exec_ctx, t);
     s->seen_error = true;
-    GRPC_MDELEM_UNREF(md);
+    GRPC_MDELEM_UNREF(exec_ctx, md);
   } else {
     grpc_chttp2_incoming_metadata_buffer_add(&s->metadata_buffer[1], md);
   }
@@ -712,7 +712,7 @@ static grpc_error *init_settings_frame_parser(grpc_exec_ctx *exec_ctx,
     memcpy(t->settings[GRPC_ACKED_SETTINGS], t->settings[GRPC_SENT_SETTINGS],
            GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t));
     grpc_chttp2_hptbl_set_max_bytes(
-        &t->hpack_parser.table,
+        exec_ctx, &t->hpack_parser.table,
         t->settings[GRPC_ACKED_SETTINGS]
                    [GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]);
     t->sent_local_settings = 0;
diff --git a/src/core/ext/transport/chttp2/transport/stream_lists.c b/src/core/ext/transport/chttp2/transport/stream_lists.c
index 6d25b3ae57908804c84d01f3aee1ddbbc0a10192..a60264cc51f234d152ac9c3c711b9216ae0e7bf8 100644
--- a/src/core/ext/transport/chttp2/transport/stream_lists.c
+++ b/src/core/ext/transport/chttp2/transport/stream_lists.c
@@ -158,6 +158,11 @@ int grpc_chttp2_list_pop_waiting_for_concurrency(grpc_chttp2_transport *t,
   return stream_list_pop(t, s, GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY);
 }
 
+void grpc_chttp2_list_remove_waiting_for_concurrency(grpc_chttp2_transport *t,
+                                                     grpc_chttp2_stream *s) {
+  stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY);
+}
+
 void grpc_chttp2_list_add_stalled_by_transport(grpc_chttp2_transport *t,
                                                grpc_chttp2_stream *s) {
   stream_list_add(t, s, GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT);
diff --git a/src/core/ext/transport/chttp2/transport/writing.c b/src/core/ext/transport/chttp2/transport/writing.c
index 139e7387c4d9a4afebfa08d5e015dbee74f5b453..ef2010af7b9f8c3074c51637adc779cae48dd95d 100644
--- a/src/core/ext/transport/chttp2/transport/writing.c
+++ b/src/core/ext/transport/chttp2/transport/writing.c
@@ -39,6 +39,7 @@
 
 #include "src/core/ext/transport/chttp2/transport/http2_errors.h"
 #include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_internal.h"
 
 static void add_to_write_list(grpc_chttp2_write_cb **list,
                               grpc_chttp2_write_cb *cb) {
@@ -119,7 +120,7 @@ bool grpc_chttp2_begin_write(grpc_exec_ctx *exec_ctx,
     /* send initial metadata if it's available */
     if (!sent_initial_metadata && s->send_initial_metadata) {
       grpc_chttp2_encode_header(
-          &t->hpack_compressor, s->id, s->send_initial_metadata, 0,
+          exec_ctx, &t->hpack_compressor, s->id, s->send_initial_metadata, 0,
           t->settings[GRPC_ACKED_SETTINGS][GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE],
           &s->stats.outgoing, &t->outbuf);
       s->send_initial_metadata = NULL;
@@ -186,9 +187,9 @@ bool grpc_chttp2_begin_write(grpc_exec_ctx *exec_ctx,
                                   &s->stats.outgoing, &t->outbuf);
         } else {
           grpc_chttp2_encode_header(
-              &t->hpack_compressor, s->id, s->send_trailing_metadata, true,
-              t->settings[GRPC_ACKED_SETTINGS]
-                         [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE],
+              exec_ctx, &t->hpack_compressor, s->id, s->send_trailing_metadata,
+              true, t->settings[GRPC_ACKED_SETTINGS]
+                               [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE],
               &s->stats.outgoing, &t->outbuf);
         }
         s->send_trailing_metadata = NULL;
@@ -254,7 +255,7 @@ void grpc_chttp2_end_write(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
     }
     GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "chttp2_writing:end");
   }
-  grpc_slice_buffer_reset_and_unref(&t->outbuf);
+  grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &t->outbuf);
   GRPC_ERROR_UNREF(error);
   GPR_TIMER_END("grpc_chttp2_end_write", 0);
 }
diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.c b/src/core/ext/transport/cronet/transport/cronet_transport.c
index a4c110101ecf53f56870ac0d3e90f72920bb5bbc..296c40632498e09b52971cb0f3c13ff4f5def488 100644
--- a/src/core/ext/transport/cronet/transport/cronet_transport.c
+++ b/src/core/ext/transport/cronet/transport/cronet_transport.c
@@ -149,6 +149,9 @@ struct write_state {
 struct op_state {
   bool state_op_done[OP_NUM_OPS];
   bool state_callback_received[OP_NUM_OPS];
+  bool fail_state;
+  bool flush_read;
+  grpc_error *cancel_error;
   /* data structure for storing data coming from server */
   struct read_state rs;
   /* data structure for storing data going to the server */
@@ -248,6 +251,12 @@ static void free_read_buffer(stream_obj *s) {
   }
 }
 
+static grpc_error *make_error_with_desc(int error_code, const char *desc) {
+  grpc_error *error = GRPC_ERROR_CREATE(desc);
+  error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, error_code);
+  return error;
+}
+
 /*
   Add a new stream op to op storage.
 */
@@ -418,6 +427,7 @@ static void on_response_headers_received(
     cronet_bidirectional_stream *stream,
     const cronet_bidirectional_stream_header_array *headers,
     const char *negotiated_protocol) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   CRONET_LOG(GPR_DEBUG, "R: on_response_headers_received(%p, %p, %s)", stream,
              headers, negotiated_protocol);
   stream_obj *s = (stream_obj *)stream->annotation;
@@ -429,11 +439,24 @@ static void on_response_headers_received(
     grpc_chttp2_incoming_metadata_buffer_add(
         &s->state.rs.initial_metadata,
         grpc_mdelem_from_metadata_strings(
-            grpc_mdstr_from_string(headers->headers[i].key),
+            &exec_ctx, grpc_mdstr_from_string(headers->headers[i].key),
             grpc_mdstr_from_string(headers->headers[i].value)));
   }
   s->state.state_callback_received[OP_RECV_INITIAL_METADATA] = true;
+  if (!(s->state.state_op_done[OP_CANCEL_ERROR] ||
+        s->state.state_callback_received[OP_FAILED])) {
+    /* Do an extra read to trigger on_succeeded() callback in case connection
+     is closed */
+    GPR_ASSERT(s->state.rs.length_field_received == false);
+    s->state.rs.read_buffer = s->state.rs.grpc_header_bytes;
+    s->state.rs.received_bytes = 0;
+    s->state.rs.remaining_bytes = GRPC_HEADER_SIZE_IN_BYTES;
+    CRONET_LOG(GPR_DEBUG, "cronet_bidirectional_stream_read(%p)", s->cbs);
+    cronet_bidirectional_stream_read(s->cbs, s->state.rs.read_buffer,
+                                     s->state.rs.remaining_bytes);
+  }
   gpr_mu_unlock(&s->mu);
+  grpc_exec_ctx_finish(&exec_ctx);
   execute_from_storage(s);
 }
 
@@ -464,7 +487,11 @@ static void on_read_completed(cronet_bidirectional_stream *stream, char *data,
              count);
   gpr_mu_lock(&s->mu);
   s->state.state_callback_received[OP_RECV_MESSAGE] = true;
-  if (count > 0) {
+  if (count > 0 && s->state.flush_read) {
+    CRONET_LOG(GPR_DEBUG, "cronet_bidirectional_stream_read(%p)", s->cbs);
+    cronet_bidirectional_stream_read(s->cbs, s->state.rs.read_buffer, 4096);
+    gpr_mu_unlock(&s->mu);
+  } else if (count > 0) {
     s->state.rs.received_bytes += count;
     s->state.rs.remaining_bytes -= count;
     if (s->state.rs.remaining_bytes > 0) {
@@ -479,6 +506,10 @@ static void on_read_completed(cronet_bidirectional_stream *stream, char *data,
       execute_from_storage(s);
     }
   } else {
+    if (s->state.flush_read) {
+      gpr_free(s->state.rs.read_buffer);
+      s->state.rs.read_buffer = NULL;
+    }
     s->state.rs.read_stream_closed = true;
     gpr_mu_unlock(&s->mu);
     execute_from_storage(s);
@@ -491,6 +522,7 @@ static void on_read_completed(cronet_bidirectional_stream *stream, char *data,
 static void on_response_trailers_received(
     cronet_bidirectional_stream *stream,
     const cronet_bidirectional_stream_header_array *trailers) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   CRONET_LOG(GPR_DEBUG, "R: on_response_trailers_received(%p,%p)", stream,
              trailers);
   stream_obj *s = (stream_obj *)stream->annotation;
@@ -505,13 +537,32 @@ static void on_response_trailers_received(
     grpc_chttp2_incoming_metadata_buffer_add(
         &s->state.rs.trailing_metadata,
         grpc_mdelem_from_metadata_strings(
-            grpc_mdstr_from_string(trailers->headers[i].key),
+            &exec_ctx, grpc_mdstr_from_string(trailers->headers[i].key),
             grpc_mdstr_from_string(trailers->headers[i].value)));
     s->state.rs.trailing_metadata_valid = true;
+    if (0 == strcmp(trailers->headers[i].key, "grpc-status") &&
+        0 != strcmp(trailers->headers[i].value, "0")) {
+      s->state.fail_state = true;
+    }
   }
   s->state.state_callback_received[OP_RECV_TRAILING_METADATA] = true;
-  gpr_mu_unlock(&s->mu);
-  execute_from_storage(s);
+  /* Send a EOS when server terminates the stream (testServerFinishesRequest) to
+   * trigger on_succeeded */
+  if (!s->state.state_op_done[OP_SEND_TRAILING_METADATA] &&
+      !(s->state.state_op_done[OP_CANCEL_ERROR] ||
+        s->state.state_callback_received[OP_FAILED])) {
+    CRONET_LOG(GPR_DEBUG, "cronet_bidirectional_stream_write (%p, 0)", s->cbs);
+    s->state.state_callback_received[OP_SEND_MESSAGE] = false;
+    cronet_bidirectional_stream_write(s->cbs, "", 0, true);
+    s->state.state_op_done[OP_SEND_TRAILING_METADATA] = true;
+
+    gpr_mu_unlock(&s->mu);
+    grpc_exec_ctx_finish(&exec_ctx);
+  } else {
+    gpr_mu_unlock(&s->mu);
+    grpc_exec_ctx_finish(&exec_ctx);
+    execute_from_storage(s);
+  }
 }
 
 /*
@@ -632,9 +683,9 @@ static bool op_can_be_run(grpc_transport_stream_op *curr_op,
   /* When call is canceled, every op can be run, except under following
   conditions
   */
-  bool is_canceled_of_failed = stream_state->state_op_done[OP_CANCEL_ERROR] ||
+  bool is_canceled_or_failed = stream_state->state_op_done[OP_CANCEL_ERROR] ||
                                stream_state->state_callback_received[OP_FAILED];
-  if (is_canceled_of_failed) {
+  if (is_canceled_or_failed) {
     if (op_id == OP_SEND_INITIAL_METADATA) result = false;
     if (op_id == OP_SEND_MESSAGE) result = false;
     if (op_id == OP_SEND_TRAILING_METADATA) result = false;
@@ -778,16 +829,10 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
       op_can_be_run(stream_op, stream_state, &oas->state,
                     OP_SEND_INITIAL_METADATA)) {
     CRONET_LOG(GPR_DEBUG, "running: %p OP_SEND_INITIAL_METADATA", oas);
-    /* This OP is the beginning. Reset various states */
-    memset(&s->header_array, 0, sizeof(s->header_array));
-    memset(&stream_state->rs, 0, sizeof(stream_state->rs));
-    memset(&stream_state->ws, 0, sizeof(stream_state->ws));
-    memset(stream_state->state_op_done, 0, sizeof(stream_state->state_op_done));
-    memset(stream_state->state_callback_received, 0,
-           sizeof(stream_state->state_callback_received));
     /* Start new cronet stream. It is destroyed in on_succeeded, on_canceled,
      * on_failed */
     GPR_ASSERT(s->cbs == NULL);
+    GPR_ASSERT(!stream_state->state_op_done[OP_SEND_INITIAL_METADATA]);
     s->cbs = cronet_bidirectional_stream_create(s->curr_ct.engine, s->curr_gs,
                                                 &cronet_callbacks);
     CRONET_LOG(GPR_DEBUG, "%p = cronet_bidirectional_stream_create()", s->cbs);
@@ -808,15 +853,18 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
              op_can_be_run(stream_op, stream_state, &oas->state,
                            OP_RECV_INITIAL_METADATA)) {
     CRONET_LOG(GPR_DEBUG, "running: %p  OP_RECV_INITIAL_METADATA", oas);
-    if (stream_state->state_op_done[OP_CANCEL_ERROR] ||
-        stream_state->state_callback_received[OP_FAILED]) {
-      grpc_exec_ctx_sched(exec_ctx, stream_op->recv_initial_metadata_ready,
-                          GRPC_ERROR_CANCELLED, NULL);
+    if (stream_state->state_op_done[OP_CANCEL_ERROR]) {
+      grpc_closure_sched(exec_ctx, stream_op->recv_initial_metadata_ready,
+                         GRPC_ERROR_CANCELLED);
+    } else if (stream_state->state_callback_received[OP_FAILED]) {
+      grpc_closure_sched(
+          exec_ctx, stream_op->recv_initial_metadata_ready,
+          make_error_with_desc(GRPC_STATUS_UNAVAILABLE, "Unavailable."));
     } else {
       grpc_chttp2_incoming_metadata_buffer_publish(
           &oas->s->state.rs.initial_metadata, stream_op->recv_initial_metadata);
-      grpc_exec_ctx_sched(exec_ctx, stream_op->recv_initial_metadata_ready,
-                          GRPC_ERROR_NONE, NULL);
+      grpc_closure_sched(exec_ctx, stream_op->recv_initial_metadata_ready,
+                         GRPC_ERROR_NONE);
     }
     stream_state->state_op_done[OP_RECV_INITIAL_METADATA] = true;
     result = ACTION_TAKEN_NO_CALLBACK;
@@ -865,19 +913,27 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
              op_can_be_run(stream_op, stream_state, &oas->state,
                            OP_RECV_MESSAGE)) {
     CRONET_LOG(GPR_DEBUG, "running: %p  OP_RECV_MESSAGE", oas);
-    if (stream_state->state_op_done[OP_CANCEL_ERROR] ||
-        stream_state->state_callback_received[OP_FAILED]) {
-      CRONET_LOG(GPR_DEBUG, "Stream is either cancelled or failed.");
-      grpc_exec_ctx_sched(exec_ctx, stream_op->recv_message_ready,
-                          GRPC_ERROR_CANCELLED, NULL);
+    if (stream_state->state_op_done[OP_CANCEL_ERROR]) {
+      CRONET_LOG(GPR_DEBUG, "Stream is cancelled.");
+      grpc_closure_sched(exec_ctx, stream_op->recv_message_ready,
+                         GRPC_ERROR_CANCELLED);
+      stream_state->state_op_done[OP_RECV_MESSAGE] = true;
+      result = ACTION_TAKEN_NO_CALLBACK;
+    } else if (stream_state->state_callback_received[OP_FAILED]) {
+      CRONET_LOG(GPR_DEBUG, "Stream failed.");
+      grpc_closure_sched(
+          exec_ctx, stream_op->recv_message_ready,
+          make_error_with_desc(GRPC_STATUS_UNAVAILABLE, "Unavailable."));
       stream_state->state_op_done[OP_RECV_MESSAGE] = true;
+      result = ACTION_TAKEN_NO_CALLBACK;
     } else if (stream_state->rs.read_stream_closed == true) {
       /* No more data will be received */
       CRONET_LOG(GPR_DEBUG, "read stream closed");
-      grpc_exec_ctx_sched(exec_ctx, stream_op->recv_message_ready,
-                          GRPC_ERROR_NONE, NULL);
+      grpc_closure_sched(exec_ctx, stream_op->recv_message_ready,
+                         GRPC_ERROR_NONE);
       stream_state->state_op_done[OP_RECV_MESSAGE] = true;
       oas->state.state_op_done[OP_RECV_MESSAGE] = true;
+      result = ACTION_TAKEN_NO_CALLBACK;
     } else if (stream_state->rs.length_field_received == false) {
       if (stream_state->rs.received_bytes == GRPC_HEADER_SIZE_IN_BYTES &&
           stream_state->rs.remaining_bytes == 0) {
@@ -907,8 +963,8 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
                                         &stream_state->rs.read_slice_buffer, 0);
           *((grpc_byte_buffer **)stream_op->recv_message) =
               (grpc_byte_buffer *)&stream_state->rs.sbs;
-          grpc_exec_ctx_sched(exec_ctx, stream_op->recv_message_ready,
-                              GRPC_ERROR_NONE, NULL);
+          grpc_closure_sched(exec_ctx, stream_op->recv_message_ready,
+                             GRPC_ERROR_NONE);
           stream_state->state_op_done[OP_RECV_MESSAGE] = true;
           oas->state.state_op_done[OP_RECV_MESSAGE] = true;
           result = ACTION_TAKEN_NO_CALLBACK;
@@ -942,14 +998,19 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
                                     &stream_state->rs.read_slice_buffer, 0);
       *((grpc_byte_buffer **)stream_op->recv_message) =
           (grpc_byte_buffer *)&stream_state->rs.sbs;
-      grpc_exec_ctx_sched(exec_ctx, stream_op->recv_message_ready,
-                          GRPC_ERROR_NONE, NULL);
+      grpc_closure_sched(exec_ctx, stream_op->recv_message_ready,
+                         GRPC_ERROR_NONE);
       stream_state->state_op_done[OP_RECV_MESSAGE] = true;
       oas->state.state_op_done[OP_RECV_MESSAGE] = true;
-      /* Clear read state of the stream, so next read op (if it were to come)
-       * will work */
-      stream_state->rs.received_bytes = stream_state->rs.remaining_bytes =
-          stream_state->rs.length_field_received = 0;
+      /* Do an extra read to trigger on_succeeded() callback in case connection
+         is closed */
+      stream_state->rs.read_buffer = stream_state->rs.grpc_header_bytes;
+      stream_state->rs.received_bytes = 0;
+      stream_state->rs.remaining_bytes = GRPC_HEADER_SIZE_IN_BYTES;
+      stream_state->rs.length_field_received = false;
+      CRONET_LOG(GPR_DEBUG, "cronet_bidirectional_stream_read(%p)", s->cbs);
+      cronet_bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer,
+                                       stream_state->rs.remaining_bytes);
       result = ACTION_TAKEN_NO_CALLBACK;
     }
   } else if (stream_op->recv_trailing_metadata &&
@@ -986,23 +1047,30 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
     CRONET_LOG(GPR_DEBUG, "W: cronet_bidirectional_stream_cancel(%p)", s->cbs);
     if (s->cbs) {
       cronet_bidirectional_stream_cancel(s->cbs);
+      result = ACTION_TAKEN_WITH_CALLBACK;
+    } else {
+      result = ACTION_TAKEN_NO_CALLBACK;
     }
     stream_state->state_op_done[OP_CANCEL_ERROR] = true;
-    result = ACTION_TAKEN_WITH_CALLBACK;
+    if (!stream_state->cancel_error) {
+      stream_state->cancel_error = GRPC_ERROR_REF(stream_op->cancel_error);
+    }
   } else if (stream_op->on_complete &&
              op_can_be_run(stream_op, stream_state, &oas->state,
                            OP_ON_COMPLETE)) {
     CRONET_LOG(GPR_DEBUG, "running: %p  OP_ON_COMPLETE", oas);
-    if (stream_state->state_op_done[OP_CANCEL_ERROR] ||
-        stream_state->state_callback_received[OP_FAILED]) {
-      grpc_exec_ctx_sched(exec_ctx, stream_op->on_complete,
-                          GRPC_ERROR_CANCELLED, NULL);
+    if (stream_state->state_op_done[OP_CANCEL_ERROR]) {
+      grpc_closure_sched(exec_ctx, stream_op->on_complete,
+                         GRPC_ERROR_REF(stream_state->cancel_error));
+    } else if (stream_state->state_callback_received[OP_FAILED]) {
+      grpc_closure_sched(
+          exec_ctx, stream_op->on_complete,
+          make_error_with_desc(GRPC_STATUS_UNAVAILABLE, "Unavailable."));
     } else {
       /* All actions in this stream_op are complete. Call the on_complete
        * callback
        */
-      grpc_exec_ctx_sched(exec_ctx, stream_op->on_complete, GRPC_ERROR_NONE,
-                          NULL);
+      grpc_closure_sched(exec_ctx, stream_op->on_complete, GRPC_ERROR_NONE);
     }
     oas->state.state_op_done[OP_ON_COMPLETE] = true;
     oas->done = true;
@@ -1017,6 +1085,15 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
       make a note */
     if (stream_op->recv_message)
       stream_state->state_op_done[OP_RECV_MESSAGE_AND_ON_COMPLETE] = true;
+  } else if (stream_state->fail_state && !stream_state->flush_read) {
+    CRONET_LOG(GPR_DEBUG, "running: %p  flush read", oas);
+    if (stream_state->rs.read_buffer &&
+        stream_state->rs.read_buffer != stream_state->rs.grpc_header_bytes) {
+      gpr_free(stream_state->rs.read_buffer);
+      stream_state->rs.read_buffer = NULL;
+    }
+    stream_state->rs.read_buffer = gpr_malloc(4096);
+    stream_state->flush_read = true;
   } else {
     result = NO_ACTION_POSSIBLE;
   }
@@ -1042,6 +1119,8 @@ static int init_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
   memset(s->state.state_op_done, 0, sizeof(s->state.state_op_done));
   memset(s->state.state_callback_received, 0,
          sizeof(s->state.state_callback_received));
+  s->state.fail_state = s->state.flush_read = false;
+  s->state.cancel_error = NULL;
   gpr_mu_init(&s->mu);
   return 0;
 }
@@ -1088,7 +1167,10 @@ static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
 }
 
 static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
-                           grpc_stream *gs, void *and_free_memory) {}
+                           grpc_stream *gs, void *and_free_memory) {
+  stream_obj *s = (stream_obj *)gs;
+  GRPC_ERROR_UNREF(s->state.cancel_error);
+}
 
 static void destroy_transport(grpc_exec_ctx *exec_ctx, grpc_transport *gt) {}
 
diff --git a/src/core/lib/README.md b/src/core/lib/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..69b6bce2d9ff5d1ee1b238dc68e11f09a1673c95
--- /dev/null
+++ b/src/core/lib/README.md
@@ -0,0 +1,6 @@
+Required elements of gRPC Core: Each module in this directory is required to
+build gRPC. If it's possible to envisage a configuration where code is not
+required, then that code belongs in ext/ instead.
+
+NOTE: The movement of code between lib and ext is an ongoing effort, so this
+directory currently contains too much of the core library.
diff --git a/src/core/lib/channel/README.md b/src/core/lib/channel/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..2dfcfe6e662322fc13a246bab166a88925f85694
--- /dev/null
+++ b/src/core/lib/channel/README.md
@@ -0,0 +1,4 @@
+# Channel
+
+Provides channel/call stack implementation, and implementation of common filters
+for that implementation.
diff --git a/src/core/lib/channel/channel_args.c b/src/core/lib/channel/channel_args.c
index 401a2ad4fe551271e6a551fabdfbfee219ee4f67..1a099ac437a19abcc89dee19bf67309af9a8a2ea 100644
--- a/src/core/lib/channel/channel_args.c
+++ b/src/core/lib/channel/channel_args.c
@@ -184,7 +184,7 @@ grpc_channel_args *grpc_channel_args_normalize(const grpc_channel_args *a) {
   return b;
 }
 
-void grpc_channel_args_destroy(grpc_channel_args *a) {
+void grpc_channel_args_destroy(grpc_exec_ctx *exec_ctx, grpc_channel_args *a) {
   size_t i;
   if (!a) return;
   for (i = 0; i < a->num_args; i++) {
@@ -195,7 +195,8 @@ void grpc_channel_args_destroy(grpc_channel_args *a) {
       case GRPC_ARG_INTEGER:
         break;
       case GRPC_ARG_POINTER:
-        a->args[i].value.pointer.vtable->destroy(a->args[i].value.pointer.p);
+        a->args[i].value.pointer.vtable->destroy(exec_ctx,
+                                                 a->args[i].value.pointer.p);
         break;
     }
     gpr_free(a->args[i].key);
@@ -249,7 +250,8 @@ static int find_compression_algorithm_states_bitset(const grpc_channel_args *a,
 }
 
 grpc_channel_args *grpc_channel_args_compression_algorithm_set_state(
-    grpc_channel_args **a, grpc_compression_algorithm algorithm, int state) {
+    grpc_exec_ctx *exec_ctx, grpc_channel_args **a,
+    grpc_compression_algorithm algorithm, int state) {
   int *states_arg = NULL;
   grpc_channel_args *result = *a;
   const int states_arg_found =
@@ -282,7 +284,7 @@ grpc_channel_args *grpc_channel_args_compression_algorithm_set_state(
       GPR_BITCLEAR((unsigned *)&tmp.value.integer, algorithm);
     }
     result = grpc_channel_args_copy_and_add(*a, &tmp, 1);
-    grpc_channel_args_destroy(*a);
+    grpc_channel_args_destroy(exec_ctx, *a);
     *a = result;
   }
   return result;
diff --git a/src/core/lib/channel/channel_args.h b/src/core/lib/channel/channel_args.h
index 88fc0e37a35d76f2390f69c2740f11b3c4bea5c6..5c7d31f8bba0306ab841bc68ea7db81790ff0f3e 100644
--- a/src/core/lib/channel/channel_args.h
+++ b/src/core/lib/channel/channel_args.h
@@ -68,7 +68,7 @@ grpc_channel_args *grpc_channel_args_merge(const grpc_channel_args *a,
                                            const grpc_channel_args *b);
 
 /** Destroy arguments created by \a grpc_channel_args_copy */
-void grpc_channel_args_destroy(grpc_channel_args *a);
+void grpc_channel_args_destroy(grpc_exec_ctx *exec_ctx, grpc_channel_args *a);
 
 /** Returns the compression algorithm set in \a a. */
 grpc_compression_algorithm grpc_channel_args_get_compression_algorithm(
@@ -88,7 +88,8 @@ grpc_channel_args *grpc_channel_args_set_compression_algorithm(
  * modified to point to the returned instance (which may be different from the
  * input value of \a a). */
 grpc_channel_args *grpc_channel_args_compression_algorithm_set_state(
-    grpc_channel_args **a, grpc_compression_algorithm algorithm, int enabled);
+    grpc_exec_ctx *exec_ctx, grpc_channel_args **a,
+    grpc_compression_algorithm algorithm, int enabled);
 
 /** Returns the bitset representing the support state (true for enabled, false
  * for disabled) for compression algorithms.
diff --git a/src/core/lib/channel/channel_stack.c b/src/core/lib/channel/channel_stack.c
index 999ad5f50743f2eb8dd3cc70e35e01ec82fa86e0..8f08b427fb3d3922614bd01b1b4cf32f13c3c0c5 100644
--- a/src/core/lib/channel/channel_stack.c
+++ b/src/core/lib/channel/channel_stack.c
@@ -102,13 +102,11 @@ grpc_call_element *grpc_call_stack_element(grpc_call_stack *call_stack,
   return CALL_ELEMS_FROM_STACK(call_stack) + index;
 }
 
-void grpc_channel_stack_init(grpc_exec_ctx *exec_ctx, int initial_refs,
-                             grpc_iomgr_cb_func destroy, void *destroy_arg,
-                             const grpc_channel_filter **filters,
-                             size_t filter_count,
-                             const grpc_channel_args *channel_args,
-                             grpc_transport *optional_transport,
-                             const char *name, grpc_channel_stack *stack) {
+grpc_error *grpc_channel_stack_init(
+    grpc_exec_ctx *exec_ctx, int initial_refs, grpc_iomgr_cb_func destroy,
+    void *destroy_arg, const grpc_channel_filter **filters, size_t filter_count,
+    const grpc_channel_args *channel_args, grpc_transport *optional_transport,
+    const char *name, grpc_channel_stack *stack) {
   size_t call_size =
       ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call_stack)) +
       ROUND_UP_TO_ALIGNMENT_SIZE(filter_count * sizeof(grpc_call_element));
@@ -126,6 +124,7 @@ void grpc_channel_stack_init(grpc_exec_ctx *exec_ctx, int initial_refs,
       ROUND_UP_TO_ALIGNMENT_SIZE(filter_count * sizeof(grpc_channel_element));
 
   /* init per-filter data */
+  grpc_error *first_error = GRPC_ERROR_NONE;
   for (i = 0; i < filter_count; i++) {
     args.channel_stack = stack;
     args.channel_args = channel_args;
@@ -134,7 +133,15 @@ void grpc_channel_stack_init(grpc_exec_ctx *exec_ctx, int initial_refs,
     args.is_last = i == (filter_count - 1);
     elems[i].filter = filters[i];
     elems[i].channel_data = user_data;
-    elems[i].filter->init_channel_elem(exec_ctx, &elems[i], &args);
+    grpc_error *error =
+        elems[i].filter->init_channel_elem(exec_ctx, &elems[i], &args);
+    if (error != GRPC_ERROR_NONE) {
+      if (first_error == GRPC_ERROR_NONE) {
+        first_error = error;
+      } else {
+        GRPC_ERROR_UNREF(error);
+      }
+    }
     user_data += ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_channel_data);
     call_size += ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_call_data);
   }
@@ -144,6 +151,7 @@ void grpc_channel_stack_init(grpc_exec_ctx *exec_ctx, int initial_refs,
              grpc_channel_stack_size(filters, filter_count));
 
   stack->call_stack_size = call_size;
+  return first_error;
 }
 
 void grpc_channel_stack_destroy(grpc_exec_ctx *exec_ctx,
@@ -289,7 +297,8 @@ void grpc_call_element_send_cancel(grpc_exec_ctx *exec_ctx,
   grpc_transport_stream_op *op = gpr_malloc(sizeof(*op));
   memset(op, 0, sizeof(*op));
   op->cancel_error = GRPC_ERROR_CANCELLED;
-  op->on_complete = grpc_closure_create(destroy_op, op);
+  op->on_complete =
+      grpc_closure_create(destroy_op, op, grpc_schedule_on_exec_ctx);
   elem->filter->start_transport_stream_op(exec_ctx, elem, op);
 }
 
@@ -299,8 +308,9 @@ void grpc_call_element_send_cancel_with_message(grpc_exec_ctx *exec_ctx,
                                                 grpc_slice *optional_message) {
   grpc_transport_stream_op *op = gpr_malloc(sizeof(*op));
   memset(op, 0, sizeof(*op));
-  op->on_complete = grpc_closure_create(destroy_op, op);
-  grpc_transport_stream_op_add_cancellation_with_message(op, status,
+  op->on_complete =
+      grpc_closure_create(destroy_op, op, grpc_schedule_on_exec_ctx);
+  grpc_transport_stream_op_add_cancellation_with_message(exec_ctx, op, status,
                                                          optional_message);
   elem->filter->start_transport_stream_op(exec_ctx, elem, op);
 }
@@ -311,7 +321,8 @@ void grpc_call_element_send_close_with_message(grpc_exec_ctx *exec_ctx,
                                                grpc_slice *optional_message) {
   grpc_transport_stream_op *op = gpr_malloc(sizeof(*op));
   memset(op, 0, sizeof(*op));
-  op->on_complete = grpc_closure_create(destroy_op, op);
-  grpc_transport_stream_op_add_close(op, status, optional_message);
+  op->on_complete =
+      grpc_closure_create(destroy_op, op, grpc_schedule_on_exec_ctx);
+  grpc_transport_stream_op_add_close(exec_ctx, op, status, optional_message);
   elem->filter->start_transport_stream_op(exec_ctx, elem, op);
 }
diff --git a/src/core/lib/channel/channel_stack.h b/src/core/lib/channel/channel_stack.h
index 004643d45f6cb5b532c685d21fa04ae05b0319ce..d9d3a8523358aba2863878ddc23e853835953daa 100644
--- a/src/core/lib/channel/channel_stack.h
+++ b/src/core/lib/channel/channel_stack.h
@@ -34,6 +34,13 @@
 #ifndef GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_H
 #define GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_H
 
+//////////////////////////////////////////////////////////////////////////////
+// IMPORTANT NOTE:
+//
+// When you update this API, please make the corresponding changes to
+// the C++ API in src/cpp/common/channel_filter.{h,cc}
+//////////////////////////////////////////////////////////////////////////////
+
 /* A channel filter defines how operations on a channel are implemented.
    Channel filters are chained together to create full channels, and if those
    chains are linear, then channel stacks provide a mechanism to minimize
@@ -146,8 +153,9 @@ typedef struct {
      is_first, is_last designate this elements position in the stack, and are
      useful for asserting correct configuration by upper layer code.
      The filter does not need to do any chaining */
-  void (*init_channel_elem)(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem,
-                            grpc_channel_element_args *args);
+  grpc_error *(*init_channel_elem)(grpc_exec_ctx *exec_ctx,
+                                   grpc_channel_element *elem,
+                                   grpc_channel_element_args *args);
   /* Destroy per channel data.
      The filter does not need to do any chaining */
   void (*destroy_channel_elem)(grpc_exec_ctx *exec_ctx,
@@ -214,12 +222,11 @@ grpc_call_element *grpc_call_stack_element(grpc_call_stack *stack, size_t i);
 size_t grpc_channel_stack_size(const grpc_channel_filter **filters,
                                size_t filter_count);
 /* Initialize a channel stack given some filters */
-void grpc_channel_stack_init(grpc_exec_ctx *exec_ctx, int initial_refs,
-                             grpc_iomgr_cb_func destroy, void *destroy_arg,
-                             const grpc_channel_filter **filters,
-                             size_t filter_count, const grpc_channel_args *args,
-                             grpc_transport *optional_transport,
-                             const char *name, grpc_channel_stack *stack);
+grpc_error *grpc_channel_stack_init(
+    grpc_exec_ctx *exec_ctx, int initial_refs, grpc_iomgr_cb_func destroy,
+    void *destroy_arg, const grpc_channel_filter **filters, size_t filter_count,
+    const grpc_channel_args *args, grpc_transport *optional_transport,
+    const char *name, grpc_channel_stack *stack);
 /* Destroy a channel stack */
 void grpc_channel_stack_destroy(grpc_exec_ctx *exec_ctx,
                                 grpc_channel_stack *stack);
diff --git a/src/core/lib/channel/channel_stack_builder.c b/src/core/lib/channel/channel_stack_builder.c
index eda4968f486cc6a7682daf1d2eaacb928f8bdd6f..5f9e3b453980d8053256e22c0406e2ac703210c6 100644
--- a/src/core/lib/channel/channel_stack_builder.c
+++ b/src/core/lib/channel/channel_stack_builder.c
@@ -138,9 +138,10 @@ void grpc_channel_stack_builder_set_name(grpc_channel_stack_builder *builder,
 }
 
 void grpc_channel_stack_builder_set_channel_arguments(
-    grpc_channel_stack_builder *builder, const grpc_channel_args *args) {
+    grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder,
+    const grpc_channel_args *args) {
   if (builder->args != NULL) {
-    grpc_channel_args_destroy(builder->args);
+    grpc_channel_args_destroy(exec_ctx, builder->args);
   }
   builder->args = grpc_channel_args_copy(args);
 }
@@ -213,7 +214,8 @@ bool grpc_channel_stack_builder_add_filter_after(
   return true;
 }
 
-void grpc_channel_stack_builder_destroy(grpc_channel_stack_builder *builder) {
+void grpc_channel_stack_builder_destroy(grpc_exec_ctx *exec_ctx,
+                                        grpc_channel_stack_builder *builder) {
   filter_node *p = builder->begin.next;
   while (p != &builder->end) {
     filter_node *next = p->next;
@@ -221,17 +223,16 @@ void grpc_channel_stack_builder_destroy(grpc_channel_stack_builder *builder) {
     p = next;
   }
   if (builder->args != NULL) {
-    grpc_channel_args_destroy(builder->args);
+    grpc_channel_args_destroy(exec_ctx, builder->args);
   }
   gpr_free(builder->target);
   gpr_free(builder);
 }
 
-void *grpc_channel_stack_builder_finish(grpc_exec_ctx *exec_ctx,
-                                        grpc_channel_stack_builder *builder,
-                                        size_t prefix_bytes, int initial_refs,
-                                        grpc_iomgr_cb_func destroy,
-                                        void *destroy_arg) {
+grpc_error *grpc_channel_stack_builder_finish(
+    grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder,
+    size_t prefix_bytes, int initial_refs, grpc_iomgr_cb_func destroy,
+    void *destroy_arg, void **result) {
   // count the number of filters
   size_t num_filters = 0;
   for (filter_node *p = builder->begin.next; p != &builder->end; p = p->next) {
@@ -250,28 +251,35 @@ void *grpc_channel_stack_builder_finish(grpc_exec_ctx *exec_ctx,
   size_t channel_stack_size = grpc_channel_stack_size(filters, num_filters);
 
   // allocate memory, with prefix_bytes followed by channel_stack_size
-  char *result = gpr_malloc(prefix_bytes + channel_stack_size);
+  *result = gpr_malloc(prefix_bytes + channel_stack_size);
   // fetch a pointer to the channel stack
   grpc_channel_stack *channel_stack =
-      (grpc_channel_stack *)(result + prefix_bytes);
+      (grpc_channel_stack *)((char *)(*result) + prefix_bytes);
   // and initialize it
-  grpc_channel_stack_init(exec_ctx, initial_refs, destroy,
-                          destroy_arg == NULL ? result : destroy_arg, filters,
-                          num_filters, builder->args, builder->transport,
-                          builder->name, channel_stack);
-
-  // run post-initialization functions
-  i = 0;
-  for (filter_node *p = builder->begin.next; p != &builder->end; p = p->next) {
-    if (p->init != NULL) {
-      p->init(channel_stack, grpc_channel_stack_element(channel_stack, i),
-              p->init_arg);
+  grpc_error *error = grpc_channel_stack_init(
+      exec_ctx, initial_refs, destroy,
+      destroy_arg == NULL ? *result : destroy_arg, filters, num_filters,
+      builder->args, builder->transport, builder->name, channel_stack);
+
+  if (error != GRPC_ERROR_NONE) {
+    grpc_channel_stack_destroy(exec_ctx, channel_stack);
+    gpr_free(*result);
+    *result = NULL;
+  } else {
+    // run post-initialization functions
+    i = 0;
+    for (filter_node *p = builder->begin.next; p != &builder->end;
+         p = p->next) {
+      if (p->init != NULL) {
+        p->init(channel_stack, grpc_channel_stack_element(channel_stack, i),
+                p->init_arg);
+      }
+      i++;
     }
-    i++;
   }
 
-  grpc_channel_stack_builder_destroy(builder);
+  grpc_channel_stack_builder_destroy(exec_ctx, builder);
   gpr_free((grpc_channel_filter **)filters);
 
-  return result;
+  return error;
 }
diff --git a/src/core/lib/channel/channel_stack_builder.h b/src/core/lib/channel/channel_stack_builder.h
index 4a00f7bfdbdb89243263bc3db97265f69df96812..8adf38e27bff0a4a232cc7678b9b3908591e85fd 100644
--- a/src/core/lib/channel/channel_stack_builder.h
+++ b/src/core/lib/channel/channel_stack_builder.h
@@ -73,7 +73,8 @@ grpc_transport *grpc_channel_stack_builder_get_transport(
 
 /// Set channel arguments: copies args
 void grpc_channel_stack_builder_set_channel_arguments(
-    grpc_channel_stack_builder *builder, const grpc_channel_args *args);
+    grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder,
+    const grpc_channel_args *args);
 
 /// Return a borrowed pointer to the channel arguments
 const grpc_channel_args *grpc_channel_stack_builder_get_channel_arguments(
@@ -146,19 +147,19 @@ bool grpc_channel_stack_builder_append_filter(
 void grpc_channel_stack_builder_iterator_destroy(
     grpc_channel_stack_builder_iterator *iterator);
 
-/// Destroy the builder, return the freshly minted channel stack
+/// Destroy the builder, return the freshly minted channel stack in \a result.
 /// Allocates \a prefix_bytes bytes before the channel stack
 /// Returns the base pointer of the allocated block
 /// \a initial_refs, \a destroy, \a destroy_arg are as per
 /// grpc_channel_stack_init
-void *grpc_channel_stack_builder_finish(grpc_exec_ctx *exec_ctx,
-                                        grpc_channel_stack_builder *builder,
-                                        size_t prefix_bytes, int initial_refs,
-                                        grpc_iomgr_cb_func destroy,
-                                        void *destroy_arg);
+grpc_error *grpc_channel_stack_builder_finish(
+    grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder,
+    size_t prefix_bytes, int initial_refs, grpc_iomgr_cb_func destroy,
+    void *destroy_arg, void **result);
 
 /// Destroy the builder without creating a channel stack
-void grpc_channel_stack_builder_destroy(grpc_channel_stack_builder *builder);
+void grpc_channel_stack_builder_destroy(grpc_exec_ctx *exec_ctx,
+                                        grpc_channel_stack_builder *builder);
 
 extern int grpc_trace_channel_stack_builder;
 
diff --git a/src/core/lib/channel/compress_filter.c b/src/core/lib/channel/compress_filter.c
index 2874d63fc780a7acece94af4e3107725321e2916..337c194b798fc4531c18abb978a0ff0ab7031c53 100644
--- a/src/core/lib/channel/compress_filter.c
+++ b/src/core/lib/channel/compress_filter.c
@@ -44,6 +44,7 @@
 #include "src/core/lib/compression/algorithm_metadata.h"
 #include "src/core/lib/compression/message_compress.h"
 #include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/transport/static_metadata.h"
 
@@ -82,7 +83,8 @@ typedef struct channel_data {
 /** For each \a md element from the incoming metadata, filter out the entry for
  * "grpc-encoding", using its value to populate the call data's
  * compression_algorithm field. */
-static grpc_mdelem *compression_md_filter(void *user_data, grpc_mdelem *md) {
+static grpc_mdelem *compression_md_filter(grpc_exec_ctx *exec_ctx,
+                                          void *user_data, grpc_mdelem *md) {
   grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
   channel_data *channeld = elem->channel_data;
@@ -130,12 +132,14 @@ static int skip_compression(grpc_call_element *elem, uint32_t flags) {
 
 /** Filter initial metadata */
 static void process_send_initial_metadata(
-    grpc_call_element *elem, grpc_metadata_batch *initial_metadata) {
+    grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
+    grpc_metadata_batch *initial_metadata) {
   call_data *calld = elem->call_data;
   channel_data *channeld = elem->channel_data;
   /* Parse incoming request for compression. If any, it'll be available
    * at calld->compression_algorithm */
-  grpc_metadata_batch_filter(initial_metadata, compression_md_filter, elem);
+  grpc_metadata_batch_filter(exec_ctx, initial_metadata, compression_md_filter,
+                             elem);
   if (!calld->has_compression_algorithm) {
     /* If no algorithm was found in the metadata and we aren't
      * exceptionally skipping compression, fall back to the channel
@@ -161,7 +165,7 @@ static void continue_send_message(grpc_exec_ctx *exec_ctx,
 static void send_done(grpc_exec_ctx *exec_ctx, void *elemp, grpc_error *error) {
   grpc_call_element *elem = elemp;
   call_data *calld = elem->call_data;
-  grpc_slice_buffer_reset_and_unref(&calld->slices);
+  grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &calld->slices);
   calld->post_send->cb(exec_ctx, calld->post_send->cb_arg, error);
 }
 
@@ -171,8 +175,8 @@ static void finish_send_message(grpc_exec_ctx *exec_ctx,
   int did_compress;
   grpc_slice_buffer tmp;
   grpc_slice_buffer_init(&tmp);
-  did_compress =
-      grpc_msg_compress(calld->compression_algorithm, &calld->slices, &tmp);
+  did_compress = grpc_msg_compress(exec_ctx, calld->compression_algorithm,
+                                   &calld->slices, &tmp);
   if (did_compress) {
     if (grpc_compression_trace) {
       char *algo_name;
@@ -199,7 +203,7 @@ static void finish_send_message(grpc_exec_ctx *exec_ctx,
     }
   }
 
-  grpc_slice_buffer_destroy(&tmp);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &tmp);
 
   grpc_slice_buffer_stream_init(&calld->replacement_stream, &calld->slices,
                                 calld->send_flags);
@@ -243,7 +247,7 @@ static void compress_start_transport_stream_op(grpc_exec_ctx *exec_ctx,
   GPR_TIMER_BEGIN("compress_start_transport_stream_op", 0);
 
   if (op->send_initial_metadata) {
-    process_send_initial_metadata(elem, op->send_initial_metadata);
+    process_send_initial_metadata(exec_ctx, elem, op->send_initial_metadata);
   }
   if (op->send_message != NULL &&
       !skip_compression(elem, op->send_message->flags)) {
@@ -269,8 +273,10 @@ static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
   /* initialize members */
   grpc_slice_buffer_init(&calld->slices);
   calld->has_compression_algorithm = 0;
-  grpc_closure_init(&calld->got_slice, got_slice, elem);
-  grpc_closure_init(&calld->send_done, send_done, elem);
+  grpc_closure_init(&calld->got_slice, got_slice, elem,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&calld->send_done, send_done, elem,
+                    grpc_schedule_on_exec_ctx);
 
   return GRPC_ERROR_NONE;
 }
@@ -281,13 +287,13 @@ static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                               void *ignored) {
   /* grab pointers to our data from the call element */
   call_data *calld = elem->call_data;
-  grpc_slice_buffer_destroy(&calld->slices);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &calld->slices);
 }
 
 /* Constructor for channel_data */
-static void init_channel_elem(grpc_exec_ctx *exec_ctx,
-                              grpc_channel_element *elem,
-                              grpc_channel_element_args *args) {
+static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx,
+                                     grpc_channel_element *elem,
+                                     grpc_channel_element_args *args) {
   channel_data *channeld = elem->channel_data;
 
   channeld->enabled_algorithms_bitset =
@@ -315,6 +321,7 @@ static void init_channel_elem(grpc_exec_ctx *exec_ctx,
   }
 
   GPR_ASSERT(!args->is_last);
+  return GRPC_ERROR_NONE;
 }
 
 /* Destructor for channel data */
diff --git a/src/core/lib/channel/connected_channel.c b/src/core/lib/channel/connected_channel.c
index 038e819f7207cfac963ff380f672372a6d222fc7..ccc0619e1cbb14226298dcc636140c079c8d4531 100644
--- a/src/core/lib/channel/connected_channel.c
+++ b/src/core/lib/channel/connected_channel.c
@@ -114,12 +114,13 @@ static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
 }
 
 /* Constructor for channel_data */
-static void init_channel_elem(grpc_exec_ctx *exec_ctx,
-                              grpc_channel_element *elem,
-                              grpc_channel_element_args *args) {
+static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx,
+                                     grpc_channel_element *elem,
+                                     grpc_channel_element_args *args) {
   channel_data *cd = (channel_data *)elem->channel_data;
   GPR_ASSERT(args->is_last);
   cd->transport = NULL;
+  return GRPC_ERROR_NONE;
 }
 
 /* Destructor for channel_data */
@@ -170,7 +171,8 @@ static void bind_transport(grpc_channel_stack *channel_stack,
   channel_stack->call_stack_size += grpc_transport_stream_size(t);
 }
 
-bool grpc_add_connected_filter(grpc_channel_stack_builder *builder,
+bool grpc_add_connected_filter(grpc_exec_ctx *exec_ctx,
+                               grpc_channel_stack_builder *builder,
                                void *arg_must_be_null) {
   GPR_ASSERT(arg_must_be_null == NULL);
   grpc_transport *t = grpc_channel_stack_builder_get_transport(builder);
diff --git a/src/core/lib/channel/connected_channel.h b/src/core/lib/channel/connected_channel.h
index 3142d647b7e4257a14106a41f9ef7c34164739b0..3585c0ecbc1a992ca2acd01b06f10ba0c6efa2f4 100644
--- a/src/core/lib/channel/connected_channel.h
+++ b/src/core/lib/channel/connected_channel.h
@@ -36,7 +36,8 @@
 
 #include "src/core/lib/channel/channel_stack_builder.h"
 
-bool grpc_add_connected_filter(grpc_channel_stack_builder *builder,
+bool grpc_add_connected_filter(grpc_exec_ctx *exec_ctx,
+                               grpc_channel_stack_builder *builder,
                                void *arg_must_be_null);
 
 #endif /* GRPC_CORE_LIB_CHANNEL_CONNECTED_CHANNEL_H */
diff --git a/src/core/lib/channel/context.h b/src/core/lib/channel/context.h
index 071c5f695c56cdb19536b43ce04924eec1b37918..6c931ad28aec4464f7fcc55afdb68c2585b69e7d 100644
--- a/src/core/lib/channel/context.h
+++ b/src/core/lib/channel/context.h
@@ -47,6 +47,9 @@ typedef enum {
   /// Value is a \a census_context.
   GRPC_CONTEXT_TRACING,
 
+  /// Reserved for traffic_class_context.
+  GRPC_CONTEXT_TRAFFIC,
+
   GRPC_CONTEXT_COUNT
 } grpc_context_index;
 
diff --git a/src/core/lib/channel/deadline_filter.c b/src/core/lib/channel/deadline_filter.c
index 0e703d8d27969f0b94d17f87dd0b9ce2efd979eb..a45a4d4b8206787868a43a600c8502a31158fd93 100644
--- a/src/core/lib/channel/deadline_filter.c
+++ b/src/core/lib/channel/deadline_filter.c
@@ -41,6 +41,7 @@
 
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/slice/slice_internal.h"
 
 //
 // grpc_deadline_state
@@ -58,7 +59,7 @@ static void timer_callback(grpc_exec_ctx* exec_ctx, void* arg,
     grpc_slice msg = grpc_slice_from_static_string("Deadline Exceeded");
     grpc_call_element_send_cancel_with_message(
         exec_ctx, elem, GRPC_STATUS_DEADLINE_EXCEEDED, &msg);
-    grpc_slice_unref(msg);
+    grpc_slice_unref_internal(exec_ctx, msg);
   }
   GRPC_CALL_STACK_UNREF(exec_ctx, deadline_state->call_stack, "deadline_timer");
 }
@@ -82,8 +83,11 @@ static void start_timer_if_needed_locked(grpc_exec_ctx* exec_ctx,
     // Take a reference to the call stack, to be owned by the timer.
     GRPC_CALL_STACK_REF(deadline_state->call_stack, "deadline_timer");
     deadline_state->timer_pending = true;
-    grpc_timer_init(exec_ctx, &deadline_state->timer, deadline, timer_callback,
-                    elem, gpr_now(GPR_CLOCK_MONOTONIC));
+    grpc_closure_init(&deadline_state->timer_callback, timer_callback, elem,
+                      grpc_schedule_on_exec_ctx);
+    grpc_timer_init(exec_ctx, &deadline_state->timer, deadline,
+                    &deadline_state->timer_callback,
+                    gpr_now(GPR_CLOCK_MONOTONIC));
   }
 }
 static void start_timer_if_needed(grpc_exec_ctx* exec_ctx,
@@ -123,7 +127,8 @@ static void on_complete(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) {
 static void inject_on_complete_cb(grpc_deadline_state* deadline_state,
                                   grpc_transport_stream_op* op) {
   deadline_state->next_on_complete = op->on_complete;
-  grpc_closure_init(&deadline_state->on_complete, on_complete, deadline_state);
+  grpc_closure_init(&deadline_state->on_complete, on_complete, deadline_state,
+                    grpc_schedule_on_exec_ctx);
   op->on_complete = &deadline_state->on_complete;
 }
 
@@ -172,8 +177,9 @@ void grpc_deadline_state_start(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
     struct start_timer_after_init_state* state = gpr_malloc(sizeof(*state));
     state->elem = elem;
     state->deadline = deadline;
-    grpc_closure_init(&state->closure, start_timer_after_init, state);
-    grpc_exec_ctx_sched(exec_ctx, &state->closure, GRPC_ERROR_NONE, NULL);
+    grpc_closure_init(&state->closure, start_timer_after_init, state,
+                      grpc_schedule_on_exec_ctx);
+    grpc_closure_sched(exec_ctx, &state->closure, GRPC_ERROR_NONE);
   }
 }
 
@@ -207,10 +213,11 @@ void grpc_deadline_state_client_start_transport_stream_op(
 //
 
 // Constructor for channel_data.  Used for both client and server filters.
-static void init_channel_elem(grpc_exec_ctx* exec_ctx,
-                              grpc_channel_element* elem,
-                              grpc_channel_element_args* args) {
+static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx,
+                                     grpc_channel_element* elem,
+                                     grpc_channel_element_args* args) {
   GPR_ASSERT(!args->is_last);
+  return GRPC_ERROR_NONE;
 }
 
 // Destructor for channel_data.  Used for both client and server filters.
@@ -289,7 +296,8 @@ static void server_start_transport_stream_op(grpc_exec_ctx* exec_ctx,
       calld->next_recv_initial_metadata_ready = op->recv_initial_metadata_ready;
       calld->recv_initial_metadata = op->recv_initial_metadata;
       grpc_closure_init(&calld->recv_initial_metadata_ready,
-                        recv_initial_metadata_ready, elem);
+                        recv_initial_metadata_ready, elem,
+                        grpc_schedule_on_exec_ctx);
       op->recv_initial_metadata_ready = &calld->recv_initial_metadata_ready;
     }
     // Make sure we know when the call is complete, so that we can cancel
diff --git a/src/core/lib/channel/deadline_filter.h b/src/core/lib/channel/deadline_filter.h
index 716a8525653b8b76cb76227a64d41d5aabb16bfd..bd2b84f79e13a3fca4d03f65cd3aa1bd0173dfea 100644
--- a/src/core/lib/channel/deadline_filter.h
+++ b/src/core/lib/channel/deadline_filter.h
@@ -46,6 +46,7 @@ typedef struct grpc_deadline_state {
   bool timer_pending;
   // The deadline timer.
   grpc_timer timer;
+  grpc_closure timer_callback;
   // Closure to invoke when the call is complete.
   // We use this to cancel the timer.
   grpc_closure on_complete;
diff --git a/src/core/lib/channel/handshaker.c b/src/core/lib/channel/handshaker.c
index a45a39981c698348b5edec9f39dfa49ab9c10fa6..c052ca538526861df51bf56c5cd69cbeb41c7c72 100644
--- a/src/core/lib/channel/handshaker.c
+++ b/src/core/lib/channel/handshaker.c
@@ -38,12 +38,13 @@
 
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/handshaker.h"
+#include "src/core/lib/iomgr/timer.h"
 
 //
 // grpc_handshaker
 //
 
-void grpc_handshaker_init(const struct grpc_handshaker_vtable* vtable,
+void grpc_handshaker_init(const grpc_handshaker_vtable* vtable,
                           grpc_handshaker* handshaker) {
   handshaker->vtable = vtable;
 }
@@ -60,45 +61,44 @@ void grpc_handshaker_shutdown(grpc_exec_ctx* exec_ctx,
 
 void grpc_handshaker_do_handshake(grpc_exec_ctx* exec_ctx,
                                   grpc_handshaker* handshaker,
-                                  grpc_endpoint* endpoint,
-                                  grpc_channel_args* args,
-                                  grpc_slice_buffer* read_buffer,
-                                  gpr_timespec deadline,
                                   grpc_tcp_server_acceptor* acceptor,
-                                  grpc_handshaker_done_cb cb, void* user_data) {
-  handshaker->vtable->do_handshake(exec_ctx, handshaker, endpoint, args,
-                                   read_buffer, deadline, acceptor, cb,
-                                   user_data);
+                                  grpc_closure* on_handshake_done,
+                                  grpc_handshaker_args* args) {
+  handshaker->vtable->do_handshake(exec_ctx, handshaker, acceptor,
+                                   on_handshake_done, args);
 }
 
 //
 // grpc_handshake_manager
 //
 
-// State used while chaining handshakers.
-struct grpc_handshaker_state {
-  // The index of the handshaker to invoke next.
-  size_t index;
-  // The deadline for all handshakers.
-  gpr_timespec deadline;
-  // The acceptor to call the handshakers with.
-  grpc_tcp_server_acceptor* acceptor;
-  // The final callback and user_data to invoke after the last handshaker.
-  grpc_handshaker_done_cb final_cb;
-  void* final_user_data;
-};
-
 struct grpc_handshake_manager {
+  gpr_mu mu;
+  gpr_refcount refs;
+  bool shutdown;
   // An array of handshakers added via grpc_handshake_manager_add().
   size_t count;
   grpc_handshaker** handshakers;
-  // State used while chaining handshakers.
-  struct grpc_handshaker_state* state;
+  // The index of the handshaker to invoke next and closure to invoke it.
+  size_t index;
+  grpc_closure call_next_handshaker;
+  // The acceptor to call the handshakers with.
+  grpc_tcp_server_acceptor* acceptor;
+  // Deadline timer across all handshakers.
+  grpc_timer deadline_timer;
+  grpc_closure on_timeout;
+  // The final callback and user_data to invoke after the last handshaker.
+  grpc_closure on_handshake_done;
+  void* user_data;
+  // Handshaker args.
+  grpc_handshaker_args args;
 };
 
 grpc_handshake_manager* grpc_handshake_manager_create() {
   grpc_handshake_manager* mgr = gpr_malloc(sizeof(grpc_handshake_manager));
   memset(mgr, 0, sizeof(*mgr));
+  gpr_mu_init(&mgr->mu);
+  gpr_ref_init(&mgr->refs, 1);
   return mgr;
 }
 
@@ -106,6 +106,7 @@ static bool is_power_of_2(size_t n) { return (n & (n - 1)) == 0; }
 
 void grpc_handshake_manager_add(grpc_handshake_manager* mgr,
                                 grpc_handshaker* handshaker) {
+  gpr_mu_lock(&mgr->mu);
   // To avoid allocating memory for each handshaker we add, we double
   // the number of elements every time we need more.
   size_t realloc_count = 0;
@@ -119,85 +120,121 @@ void grpc_handshake_manager_add(grpc_handshake_manager* mgr,
         gpr_realloc(mgr->handshakers, realloc_count * sizeof(grpc_handshaker*));
   }
   mgr->handshakers[mgr->count++] = handshaker;
+  gpr_mu_unlock(&mgr->mu);
+}
+
+static void grpc_handshake_manager_unref(grpc_exec_ctx* exec_ctx,
+                                         grpc_handshake_manager* mgr) {
+  if (gpr_unref(&mgr->refs)) {
+    for (size_t i = 0; i < mgr->count; ++i) {
+      grpc_handshaker_destroy(exec_ctx, mgr->handshakers[i]);
+    }
+    gpr_free(mgr->handshakers);
+    gpr_mu_destroy(&mgr->mu);
+    gpr_free(mgr);
+  }
 }
 
 void grpc_handshake_manager_destroy(grpc_exec_ctx* exec_ctx,
                                     grpc_handshake_manager* mgr) {
-  for (size_t i = 0; i < mgr->count; ++i) {
-    grpc_handshaker_destroy(exec_ctx, mgr->handshakers[i]);
-  }
-  gpr_free(mgr->handshakers);
-  gpr_free(mgr);
+  grpc_handshake_manager_unref(exec_ctx, mgr);
 }
 
 void grpc_handshake_manager_shutdown(grpc_exec_ctx* exec_ctx,
                                      grpc_handshake_manager* mgr) {
-  for (size_t i = 0; i < mgr->count; ++i) {
-    grpc_handshaker_shutdown(exec_ctx, mgr->handshakers[i]);
+  gpr_mu_lock(&mgr->mu);
+  // Shutdown the handshaker that's currently in progress, if any.
+  if (!mgr->shutdown && mgr->index > 0) {
+    mgr->shutdown = true;
+    grpc_handshaker_shutdown(exec_ctx, mgr->handshakers[mgr->index - 1]);
   }
-  if (mgr->state != NULL) {
-    gpr_free(mgr->state);
-    mgr->state = NULL;
+  gpr_mu_unlock(&mgr->mu);
+}
+
+// Helper function to call either the next handshaker or the
+// on_handshake_done callback.
+// Returns true if we've scheduled the on_handshake_done callback.
+static bool call_next_handshaker_locked(grpc_exec_ctx* exec_ctx,
+                                        grpc_handshake_manager* mgr,
+                                        grpc_error* error) {
+  GPR_ASSERT(mgr->index <= mgr->count);
+  // If we got an error or we've been shut down or we're exiting early or
+  // we've finished the last handshaker, invoke the on_handshake_done
+  // callback.  Otherwise, call the next handshaker.
+  if (error != GRPC_ERROR_NONE || mgr->shutdown || mgr->args.exit_early ||
+      mgr->index == mgr->count) {
+    // Cancel deadline timer, since we're invoking the on_handshake_done
+    // callback now.
+    grpc_timer_cancel(exec_ctx, &mgr->deadline_timer);
+    grpc_closure_sched(exec_ctx, &mgr->on_handshake_done, error);
+    mgr->shutdown = true;
+  } else {
+    grpc_handshaker_do_handshake(exec_ctx, mgr->handshakers[mgr->index],
+                                 mgr->acceptor, &mgr->call_next_handshaker,
+                                 &mgr->args);
   }
+  ++mgr->index;
+  return mgr->shutdown;
 }
 
 // A function used as the handshaker-done callback when chaining
 // handshakers together.
-static void call_next_handshaker(grpc_exec_ctx* exec_ctx,
-                                 grpc_endpoint* endpoint,
-                                 grpc_channel_args* args,
-                                 grpc_slice_buffer* read_buffer,
-                                 void* user_data, grpc_error* error) {
-  grpc_handshake_manager* mgr = user_data;
-  GPR_ASSERT(mgr->state != NULL);
-  GPR_ASSERT(mgr->state->index < mgr->count);
-  // If we got an error, skip all remaining handshakers and invoke the
-  // caller-supplied callback immediately.
-  if (error != GRPC_ERROR_NONE) {
-    mgr->state->final_cb(exec_ctx, endpoint, args, read_buffer,
-                         mgr->state->final_user_data, error);
-    return;
+static void call_next_handshaker(grpc_exec_ctx* exec_ctx, void* arg,
+                                 grpc_error* error) {
+  grpc_handshake_manager* mgr = arg;
+  gpr_mu_lock(&mgr->mu);
+  bool done = call_next_handshaker_locked(exec_ctx, mgr, GRPC_ERROR_REF(error));
+  gpr_mu_unlock(&mgr->mu);
+  // If we're invoked the final callback, we won't be coming back
+  // to this function, so we can release our reference to the
+  // handshake manager.
+  if (done) {
+    grpc_handshake_manager_unref(exec_ctx, mgr);
   }
-  grpc_handshaker_done_cb cb = call_next_handshaker;
-  // If this is the last handshaker, use the caller-supplied callback
-  // and user_data instead of chaining back to this function again.
-  if (mgr->state->index == mgr->count - 1) {
-    cb = mgr->state->final_cb;
-    user_data = mgr->state->final_user_data;
-  }
-  // Invoke handshaker.
-  grpc_handshaker_do_handshake(
-      exec_ctx, mgr->handshakers[mgr->state->index], endpoint, args,
-      read_buffer, mgr->state->deadline, mgr->state->acceptor, cb, user_data);
-  ++mgr->state->index;
-  // If this is the last handshaker, clean up state.
-  if (mgr->state->index == mgr->count) {
-    gpr_free(mgr->state);
-    mgr->state = NULL;
+}
+
+// Callback invoked when deadline is exceeded.
+static void on_timeout(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) {
+  grpc_handshake_manager* mgr = arg;
+  if (error == GRPC_ERROR_NONE) {  // Timer fired, rather than being cancelled.
+    grpc_handshake_manager_shutdown(exec_ctx, mgr);
   }
+  grpc_handshake_manager_unref(exec_ctx, mgr);
 }
 
 void grpc_handshake_manager_do_handshake(
     grpc_exec_ctx* exec_ctx, grpc_handshake_manager* mgr,
-    grpc_endpoint* endpoint, const grpc_channel_args* args,
+    grpc_endpoint* endpoint, const grpc_channel_args* channel_args,
     gpr_timespec deadline, grpc_tcp_server_acceptor* acceptor,
-    grpc_handshaker_done_cb cb, void* user_data) {
-  grpc_channel_args* args_copy = grpc_channel_args_copy(args);
-  grpc_slice_buffer* read_buffer = gpr_malloc(sizeof(*read_buffer));
-  grpc_slice_buffer_init(read_buffer);
-  if (mgr->count == 0) {
-    // No handshakers registered, so we just immediately call the done
-    // callback with the passed-in endpoint.
-    cb(exec_ctx, endpoint, args_copy, read_buffer, user_data, GRPC_ERROR_NONE);
-  } else {
-    GPR_ASSERT(mgr->state == NULL);
-    mgr->state = gpr_malloc(sizeof(struct grpc_handshaker_state));
-    memset(mgr->state, 0, sizeof(*mgr->state));
-    mgr->state->deadline = deadline;
-    mgr->state->acceptor = acceptor;
-    mgr->state->final_cb = cb;
-    mgr->state->final_user_data = user_data;
-    call_next_handshaker(exec_ctx, endpoint, args_copy, read_buffer, mgr,
-                         GRPC_ERROR_NONE);
+    grpc_iomgr_cb_func on_handshake_done, void* user_data) {
+  gpr_mu_lock(&mgr->mu);
+  GPR_ASSERT(mgr->index == 0);
+  GPR_ASSERT(!mgr->shutdown);
+  // Construct handshaker args.  These will be passed through all
+  // handshakers and eventually be freed by the on_handshake_done callback.
+  mgr->args.endpoint = endpoint;
+  mgr->args.args = grpc_channel_args_copy(channel_args);
+  mgr->args.user_data = user_data;
+  mgr->args.read_buffer = gpr_malloc(sizeof(*mgr->args.read_buffer));
+  grpc_slice_buffer_init(mgr->args.read_buffer);
+  // Initialize state needed for calling handshakers.
+  mgr->acceptor = acceptor;
+  grpc_closure_init(&mgr->call_next_handshaker, call_next_handshaker, mgr,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&mgr->on_handshake_done, on_handshake_done, &mgr->args,
+                    grpc_schedule_on_exec_ctx);
+  // Start deadline timer, which owns a ref.
+  gpr_ref(&mgr->refs);
+  grpc_closure_init(&mgr->on_timeout, on_timeout, mgr,
+                    grpc_schedule_on_exec_ctx);
+  grpc_timer_init(exec_ctx, &mgr->deadline_timer,
+                  gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC),
+                  &mgr->on_timeout, gpr_now(GPR_CLOCK_MONOTONIC));
+  // Start first handshaker, which also owns a ref.
+  gpr_ref(&mgr->refs);
+  bool done = call_next_handshaker_locked(exec_ctx, mgr, GRPC_ERROR_NONE);
+  gpr_mu_unlock(&mgr->mu);
+  if (done) {
+    grpc_handshake_manager_unref(exec_ctx, mgr);
   }
 }
diff --git a/src/core/lib/channel/handshaker.h b/src/core/lib/channel/handshaker.h
index f8a36c64730475fd704aedaa09194a8a9c688ce1..450b7adaee0baa45a327ba6b666b9f979f41b6ab 100644
--- a/src/core/lib/channel/handshaker.h
+++ b/src/core/lib/channel/handshaker.h
@@ -54,15 +54,33 @@
 
 typedef struct grpc_handshaker grpc_handshaker;
 
-/// Callback type invoked when a handshaker is done.
-/// Takes ownership of \a args and \a read_buffer.
-typedef void (*grpc_handshaker_done_cb)(grpc_exec_ctx* exec_ctx,
-                                        grpc_endpoint* endpoint,
-                                        grpc_channel_args* args,
-                                        grpc_slice_buffer* read_buffer,
-                                        void* user_data, grpc_error* error);
-
-struct grpc_handshaker_vtable {
+/// Arguments passed through handshakers and to the on_handshake_done callback.
+///
+/// For handshakers, all members are input/output parameters; for
+/// example, a handshaker may read from or write to \a endpoint and
+/// then later replace it with a wrapped endpoint.  Similarly, a
+/// handshaker may modify \a args.
+///
+/// A handshaker takes ownership of the members while a handshake is in
+/// progress.  Upon failure or shutdown of an in-progress handshaker,
+/// the handshaker is responsible for destroying the members and setting
+/// them to NULL before invoking the on_handshake_done callback.
+///
+/// For the on_handshake_done callback, all members are input arguments,
+/// which the callback takes ownership of.
+typedef struct {
+  grpc_endpoint* endpoint;
+  grpc_channel_args* args;
+  grpc_slice_buffer* read_buffer;
+  // A handshaker may set this to true before invoking on_handshake_done
+  // to indicate that subsequent handshakers should be skipped.
+  bool exit_early;
+  // User data passed through the handshake manager.  Not used by
+  // individual handshakers.
+  void* user_data;
+} grpc_handshaker_args;
+
+typedef struct {
   /// Destroys the handshaker.
   void (*destroy)(grpc_exec_ctx* exec_ctx, grpc_handshaker* handshaker);
 
@@ -70,43 +88,35 @@ struct grpc_handshaker_vtable {
   /// aborted in the middle).
   void (*shutdown)(grpc_exec_ctx* exec_ctx, grpc_handshaker* handshaker);
 
-  /// Performs handshaking.  When finished, calls \a cb with \a user_data.
-  /// Takes ownership of \a args.
-  /// Takes ownership of \a read_buffer, which contains leftover bytes read
-  /// from the endpoint by the previous handshaker.
+  /// Performs handshaking, modifying \a args as needed (e.g., to
+  /// replace \a endpoint with a wrapped endpoint).
+  /// When finished, invokes \a on_handshake_done.
   /// \a acceptor will be NULL for client-side handshakers.
   void (*do_handshake)(grpc_exec_ctx* exec_ctx, grpc_handshaker* handshaker,
-                       grpc_endpoint* endpoint, grpc_channel_args* args,
-                       grpc_slice_buffer* read_buffer, gpr_timespec deadline,
                        grpc_tcp_server_acceptor* acceptor,
-                       grpc_handshaker_done_cb cb, void* user_data);
-};
+                       grpc_closure* on_handshake_done,
+                       grpc_handshaker_args* args);
+} grpc_handshaker_vtable;
 
 /// Base struct.  To subclass, make this the first member of the
 /// implementation struct.
 struct grpc_handshaker {
-  const struct grpc_handshaker_vtable* vtable;
+  const grpc_handshaker_vtable* vtable;
 };
 
 /// Called by concrete implementations to initialize the base struct.
-void grpc_handshaker_init(const struct grpc_handshaker_vtable* vtable,
+void grpc_handshaker_init(const grpc_handshaker_vtable* vtable,
                           grpc_handshaker* handshaker);
 
-/// Convenient wrappers for invoking methods via the vtable.
-/// These probably do not need to be called from anywhere but
-/// grpc_handshake_manager.
 void grpc_handshaker_destroy(grpc_exec_ctx* exec_ctx,
                              grpc_handshaker* handshaker);
 void grpc_handshaker_shutdown(grpc_exec_ctx* exec_ctx,
                               grpc_handshaker* handshaker);
 void grpc_handshaker_do_handshake(grpc_exec_ctx* exec_ctx,
                                   grpc_handshaker* handshaker,
-                                  grpc_endpoint* endpoint,
-                                  grpc_channel_args* args,
-                                  grpc_slice_buffer* read_buffer,
-                                  gpr_timespec deadline,
                                   grpc_tcp_server_acceptor* acceptor,
-                                  grpc_handshaker_done_cb cb, void* user_data);
+                                  grpc_closure* on_handshake_done,
+                                  grpc_handshaker_args* args);
 
 ///
 /// grpc_handshake_manager
@@ -134,15 +144,21 @@ void grpc_handshake_manager_shutdown(grpc_exec_ctx* exec_ctx,
                                      grpc_handshake_manager* mgr);
 
 /// Invokes handshakers in the order they were added.
-/// Does NOT take ownership of \a args.  Instead, makes a copy before
+/// Takes ownership of \a endpoint, and then passes that ownership to
+/// the \a on_handshake_done callback.
+/// Does NOT take ownership of \a channel_args.  Instead, makes a copy before
 /// invoking the first handshaker.
 /// \a acceptor will be NULL for client-side handshakers.
-/// Invokes \a cb with \a user_data after either a handshaker fails or
-/// all handshakers have completed successfully.
+///
+/// When done, invokes \a on_handshake_done with a grpc_handshaker_args
+/// object as its argument.  If the callback is invoked with error !=
+/// GRPC_ERROR_NONE, then handshaking failed and the handshaker has done
+/// the necessary clean-up.  Otherwise, the callback takes ownership of
+/// the arguments.
 void grpc_handshake_manager_do_handshake(
     grpc_exec_ctx* exec_ctx, grpc_handshake_manager* mgr,
-    grpc_endpoint* endpoint, const grpc_channel_args* args,
+    grpc_endpoint* endpoint, const grpc_channel_args* channel_args,
     gpr_timespec deadline, grpc_tcp_server_acceptor* acceptor,
-    grpc_handshaker_done_cb cb, void* user_data);
+    grpc_iomgr_cb_func on_handshake_done, void* user_data);
 
 #endif /* GRPC_CORE_LIB_CHANNEL_HANDSHAKER_H */
diff --git a/src/core/lib/channel/handshaker_factory.c b/src/core/lib/channel/handshaker_factory.c
new file mode 100644
index 0000000000000000000000000000000000000000..3c30a4e1d2effe1621d80126b1617130ada8e879
--- /dev/null
+++ b/src/core/lib/channel/handshaker_factory.c
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/channel/handshaker_factory.h"
+
+#include <grpc/support/log.h>
+
+void grpc_handshaker_factory_add_handshakers(
+    grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *handshaker_factory,
+    const grpc_channel_args *args, grpc_handshake_manager *handshake_mgr) {
+  if (handshaker_factory != NULL) {
+    GPR_ASSERT(handshaker_factory->vtable != NULL);
+    handshaker_factory->vtable->add_handshakers(exec_ctx, handshaker_factory,
+                                                args, handshake_mgr);
+  }
+}
+
+void grpc_handshaker_factory_destroy(
+    grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *handshaker_factory) {
+  if (handshaker_factory != NULL) {
+    GPR_ASSERT(handshaker_factory->vtable != NULL);
+    handshaker_factory->vtable->destroy(exec_ctx, handshaker_factory);
+  }
+}
diff --git a/src/core/lib/channel/handshaker_factory.h b/src/core/lib/channel/handshaker_factory.h
new file mode 100644
index 0000000000000000000000000000000000000000..1984546104d37596515e43faaae47ddb26491cbc
--- /dev/null
+++ b/src/core/lib/channel/handshaker_factory.h
@@ -0,0 +1,66 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_CHANNEL_HANDSHAKER_FACTORY_H
+#define GRPC_CORE_LIB_CHANNEL_HANDSHAKER_FACTORY_H
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+#include "src/core/lib/channel/handshaker.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+// A handshaker factory is used to create handshakers.
+
+typedef struct grpc_handshaker_factory grpc_handshaker_factory;
+
+typedef struct {
+  void (*add_handshakers)(grpc_exec_ctx *exec_ctx,
+                          grpc_handshaker_factory *handshaker_factory,
+                          const grpc_channel_args *args,
+                          grpc_handshake_manager *handshake_mgr);
+  void (*destroy)(grpc_exec_ctx *exec_ctx,
+                  grpc_handshaker_factory *handshaker_factory);
+} grpc_handshaker_factory_vtable;
+
+struct grpc_handshaker_factory {
+  const grpc_handshaker_factory_vtable *vtable;
+};
+
+void grpc_handshaker_factory_add_handshakers(
+    grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *handshaker_factory,
+    const grpc_channel_args *args, grpc_handshake_manager *handshake_mgr);
+
+void grpc_handshaker_factory_destroy(
+    grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *handshaker_factory);
+
+#endif /* GRPC_CORE_LIB_CHANNEL_HANDSHAKER_FACTORY_H */
diff --git a/src/core/lib/channel/handshaker_registry.c b/src/core/lib/channel/handshaker_registry.c
new file mode 100644
index 0000000000000000000000000000000000000000..2e5f04064c26d2c83d2e55d45129ddffb9f11275
--- /dev/null
+++ b/src/core/lib/channel/handshaker_registry.c
@@ -0,0 +1,113 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/channel/handshaker_registry.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+//
+// grpc_handshaker_factory_list
+//
+
+typedef struct {
+  grpc_handshaker_factory** list;
+  size_t num_factories;
+} grpc_handshaker_factory_list;
+
+static void grpc_handshaker_factory_list_register(
+    grpc_handshaker_factory_list* list, bool at_start,
+    grpc_handshaker_factory* factory) {
+  list->list = gpr_realloc(
+      list->list, (list->num_factories + 1) * sizeof(grpc_handshaker_factory*));
+  if (at_start) {
+    memmove(list->list + 1, list->list,
+            sizeof(grpc_handshaker_factory*) * list->num_factories);
+    list->list[0] = factory;
+  } else {
+    list->list[list->num_factories] = factory;
+  }
+  ++list->num_factories;
+}
+
+static void grpc_handshaker_factory_list_add_handshakers(
+    grpc_exec_ctx* exec_ctx, grpc_handshaker_factory_list* list,
+    const grpc_channel_args* args, grpc_handshake_manager* handshake_mgr) {
+  for (size_t i = 0; i < list->num_factories; ++i) {
+    grpc_handshaker_factory_add_handshakers(exec_ctx, list->list[i], args,
+                                            handshake_mgr);
+  }
+}
+
+static void grpc_handshaker_factory_list_destroy(
+    grpc_exec_ctx* exec_ctx, grpc_handshaker_factory_list* list) {
+  for (size_t i = 0; i < list->num_factories; ++i) {
+    grpc_handshaker_factory_destroy(exec_ctx, list->list[i]);
+  }
+  gpr_free(list->list);
+}
+
+//
+// plugin
+//
+
+static grpc_handshaker_factory_list
+    g_handshaker_factory_lists[NUM_HANDSHAKER_TYPES];
+
+void grpc_handshaker_factory_registry_init() {
+  memset(g_handshaker_factory_lists, 0, sizeof(g_handshaker_factory_lists));
+}
+
+void grpc_handshaker_factory_registry_shutdown(grpc_exec_ctx* exec_ctx) {
+  for (size_t i = 0; i < NUM_HANDSHAKER_TYPES; ++i) {
+    grpc_handshaker_factory_list_destroy(exec_ctx,
+                                         &g_handshaker_factory_lists[i]);
+  }
+}
+
+void grpc_handshaker_factory_register(bool at_start,
+                                      grpc_handshaker_type handshaker_type,
+                                      grpc_handshaker_factory* factory) {
+  grpc_handshaker_factory_list_register(
+      &g_handshaker_factory_lists[handshaker_type], at_start, factory);
+}
+
+void grpc_handshakers_add(grpc_exec_ctx* exec_ctx,
+                          grpc_handshaker_type handshaker_type,
+                          const grpc_channel_args* args,
+                          grpc_handshake_manager* handshake_mgr) {
+  grpc_handshaker_factory_list_add_handshakers(
+      exec_ctx, &g_handshaker_factory_lists[handshaker_type], args,
+      handshake_mgr);
+}
diff --git a/src/core/lib/channel/handshaker_registry.h b/src/core/lib/channel/handshaker_registry.h
new file mode 100644
index 0000000000000000000000000000000000000000..53c1b173af230a05d9e60e398f911b10563eed23
--- /dev/null
+++ b/src/core/lib/channel/handshaker_registry.h
@@ -0,0 +1,63 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_CHANNEL_HANDSHAKER_REGISTRY_H
+#define GRPC_CORE_LIB_CHANNEL_HANDSHAKER_REGISTRY_H
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+#include "src/core/lib/channel/handshaker_factory.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+typedef enum {
+  HANDSHAKER_CLIENT = 0,
+  HANDSHAKER_SERVER,
+  NUM_HANDSHAKER_TYPES,  // Must be last.
+} grpc_handshaker_type;
+
+void grpc_handshaker_factory_registry_init();
+void grpc_handshaker_factory_registry_shutdown(grpc_exec_ctx* exec_ctx);
+
+/// Registers a new handshaker factory.  Takes ownership.
+/// If \a at_start is true, the new handshaker will be at the beginning of
+/// the list.  Otherwise, it will be added to the end.
+void grpc_handshaker_factory_register(bool at_start,
+                                      grpc_handshaker_type handshaker_type,
+                                      grpc_handshaker_factory* factory);
+
+void grpc_handshakers_add(grpc_exec_ctx* exec_ctx,
+                          grpc_handshaker_type handshaker_type,
+                          const grpc_channel_args* args,
+                          grpc_handshake_manager* handshake_mgr);
+
+#endif /* GRPC_CORE_LIB_CHANNEL_HANDSHAKER_REGISTRY_H */
diff --git a/src/core/lib/channel/http_client_filter.c b/src/core/lib/channel/http_client_filter.c
index fd8b46afcbf12bd5c3710049f7e15f0083f851b2..d15445098885e812c4774cbefc34f8a4800cd1aa 100644
--- a/src/core/lib/channel/http_client_filter.c
+++ b/src/core/lib/channel/http_client_filter.c
@@ -37,6 +37,7 @@
 #include <string.h>
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/slice/percent_encoding.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/transport/static_metadata.h"
 #include "src/core/lib/transport/transport_impl.h"
@@ -92,13 +93,9 @@ typedef struct channel_data {
   size_t max_payload_size_for_get;
 } channel_data;
 
-typedef struct {
-  grpc_call_element *elem;
-  grpc_exec_ctx *exec_ctx;
-} client_recv_filter_args;
-
-static grpc_mdelem *client_recv_filter(void *user_data, grpc_mdelem *md) {
-  client_recv_filter_args *a = user_data;
+static grpc_mdelem *client_recv_filter(grpc_exec_ctx *exec_ctx, void *user_data,
+                                       grpc_mdelem *md) {
+  grpc_call_element *elem = user_data;
   if (md == GRPC_MDELEM_STATUS_200) {
     return NULL;
   } else if (md->key == GRPC_MDSTR_STATUS) {
@@ -107,18 +104,19 @@ static grpc_mdelem *client_recv_filter(void *user_data, grpc_mdelem *md) {
                  grpc_mdstr_as_c_string(md->value));
     grpc_slice message = grpc_slice_from_copied_string(message_string);
     gpr_free(message_string);
-    grpc_call_element_send_close_with_message(a->exec_ctx, a->elem,
+    grpc_call_element_send_close_with_message(exec_ctx, elem,
                                               GRPC_STATUS_CANCELLED, &message);
     return NULL;
   } else if (md->key == GRPC_MDSTR_GRPC_MESSAGE) {
     grpc_slice pct_decoded_msg =
         grpc_permissive_percent_decode_slice(md->value->slice);
     if (grpc_slice_is_equivalent(pct_decoded_msg, md->value->slice)) {
-      grpc_slice_unref(pct_decoded_msg);
+      grpc_slice_unref_internal(exec_ctx, pct_decoded_msg);
       return md;
     } else {
       return grpc_mdelem_from_metadata_strings(
-          GRPC_MDSTR_GRPC_MESSAGE, grpc_mdstr_from_slice(pct_decoded_msg));
+          exec_ctx, GRPC_MDSTR_GRPC_MESSAGE,
+          grpc_mdstr_from_slice(exec_ctx, pct_decoded_msg));
     }
   } else if (md == GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC) {
     return NULL;
@@ -147,11 +145,8 @@ static void hc_on_recv_initial_metadata(grpc_exec_ctx *exec_ctx,
                                         void *user_data, grpc_error *error) {
   grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
-  client_recv_filter_args a;
-  a.elem = elem;
-  a.exec_ctx = exec_ctx;
-  grpc_metadata_batch_filter(calld->recv_initial_metadata, client_recv_filter,
-                             &a);
+  grpc_metadata_batch_filter(exec_ctx, calld->recv_initial_metadata,
+                             client_recv_filter, elem);
   grpc_closure_run(exec_ctx, calld->on_done_recv_initial_metadata,
                    GRPC_ERROR_REF(error));
 }
@@ -160,11 +155,8 @@ static void hc_on_recv_trailing_metadata(grpc_exec_ctx *exec_ctx,
                                          void *user_data, grpc_error *error) {
   grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
-  client_recv_filter_args a;
-  a.elem = elem;
-  a.exec_ctx = exec_ctx;
-  grpc_metadata_batch_filter(calld->recv_trailing_metadata, client_recv_filter,
-                             &a);
+  grpc_metadata_batch_filter(exec_ctx, calld->recv_trailing_metadata,
+                             client_recv_filter, elem);
   grpc_closure_run(exec_ctx, calld->on_done_recv_trailing_metadata,
                    GRPC_ERROR_REF(error));
 }
@@ -183,11 +175,12 @@ static void hc_on_complete(grpc_exec_ctx *exec_ctx, void *user_data,
 static void send_done(grpc_exec_ctx *exec_ctx, void *elemp, grpc_error *error) {
   grpc_call_element *elem = elemp;
   call_data *calld = elem->call_data;
-  grpc_slice_buffer_reset_and_unref(&calld->slices);
+  grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &calld->slices);
   calld->post_send->cb(exec_ctx, calld->post_send->cb_arg, error);
 }
 
-static grpc_mdelem *client_strip_filter(void *user_data, grpc_mdelem *md) {
+static grpc_mdelem *client_strip_filter(grpc_exec_ctx *exec_ctx,
+                                        void *user_data, grpc_mdelem *md) {
   /* eat the things we'd like to set ourselves */
   if (md->key == GRPC_MDSTR_METHOD) return NULL;
   if (md->key == GRPC_MDSTR_SCHEME) return NULL;
@@ -275,7 +268,7 @@ static void hc_mutate_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
         /* when all the send_message data is available, then create a MDELEM and
         append to headers */
         grpc_mdelem *payload_bin = grpc_mdelem_from_metadata_strings(
-            GRPC_MDSTR_GRPC_PAYLOAD_BIN,
+            exec_ctx, GRPC_MDSTR_GRPC_PAYLOAD_BIN,
             grpc_mdstr_from_buffer(calld->payload_bytes,
                                    op->send_message->length));
         grpc_metadata_batch_add_tail(op->send_initial_metadata,
@@ -292,8 +285,8 @@ static void hc_mutate_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
       }
     }
 
-    grpc_metadata_batch_filter(op->send_initial_metadata, client_strip_filter,
-                               elem);
+    grpc_metadata_batch_filter(exec_ctx, op->send_initial_metadata,
+                               client_strip_filter, elem);
     /* Send : prefixed headers, which have to be before any application
        layer headers. */
     grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->method,
@@ -352,12 +345,17 @@ static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
   calld->send_message_blocked = false;
   grpc_slice_buffer_init(&calld->slices);
   grpc_closure_init(&calld->hc_on_recv_initial_metadata,
-                    hc_on_recv_initial_metadata, elem);
+                    hc_on_recv_initial_metadata, elem,
+                    grpc_schedule_on_exec_ctx);
   grpc_closure_init(&calld->hc_on_recv_trailing_metadata,
-                    hc_on_recv_trailing_metadata, elem);
-  grpc_closure_init(&calld->hc_on_complete, hc_on_complete, elem);
-  grpc_closure_init(&calld->got_slice, got_slice, elem);
-  grpc_closure_init(&calld->send_done, send_done, elem);
+                    hc_on_recv_trailing_metadata, elem,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&calld->hc_on_complete, hc_on_complete, elem,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&calld->got_slice, got_slice, elem,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&calld->send_done, send_done, elem,
+                    grpc_schedule_on_exec_ctx);
   return GRPC_ERROR_NONE;
 }
 
@@ -366,7 +364,7 @@ static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                               const grpc_call_final_info *final_info,
                               void *ignored) {
   call_data *calld = elem->call_data;
-  grpc_slice_buffer_destroy(&calld->slices);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &calld->slices);
 }
 
 static grpc_mdelem *scheme_from_args(const grpc_channel_args *args) {
@@ -457,9 +455,9 @@ static grpc_mdstr *user_agent_from_args(const grpc_channel_args *args,
 }
 
 /* Constructor for channel_data */
-static void init_channel_elem(grpc_exec_ctx *exec_ctx,
-                              grpc_channel_element *elem,
-                              grpc_channel_element_args *args) {
+static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx,
+                                     grpc_channel_element *elem,
+                                     grpc_channel_element_args *args) {
   channel_data *chand = elem->channel_data;
   GPR_ASSERT(!args->is_last);
   GPR_ASSERT(args->optional_transport != NULL);
@@ -467,16 +465,17 @@ static void init_channel_elem(grpc_exec_ctx *exec_ctx,
   chand->max_payload_size_for_get =
       max_payload_size_from_args(args->channel_args);
   chand->user_agent = grpc_mdelem_from_metadata_strings(
-      GRPC_MDSTR_USER_AGENT,
+      exec_ctx, GRPC_MDSTR_USER_AGENT,
       user_agent_from_args(args->channel_args,
                            args->optional_transport->vtable->name));
+  return GRPC_ERROR_NONE;
 }
 
 /* Destructor for channel data */
 static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
                                  grpc_channel_element *elem) {
   channel_data *chand = elem->channel_data;
-  GRPC_MDELEM_UNREF(chand->user_agent);
+  GRPC_MDELEM_UNREF(exec_ctx, chand->user_agent);
 }
 
 const grpc_channel_filter grpc_http_client_filter = {
diff --git a/src/core/lib/channel/http_server_filter.c b/src/core/lib/channel/http_server_filter.c
index b42ff06039c079c879c3d9c463e61d9cd536f068..f508231238374b4d4fbf46d5d3a4c2fccd42d070 100644
--- a/src/core/lib/channel/http_server_filter.c
+++ b/src/core/lib/channel/http_server_filter.c
@@ -38,6 +38,7 @@
 #include <string.h>
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/slice/percent_encoding.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/transport/static_metadata.h"
 
 #define EXPECTED_CONTENT_TYPE "application/grpc"
@@ -82,31 +83,28 @@ typedef struct call_data {
 
 typedef struct channel_data { uint8_t unused; } channel_data;
 
-typedef struct {
-  grpc_call_element *elem;
-  grpc_exec_ctx *exec_ctx;
-} server_filter_args;
-
-static grpc_mdelem *server_filter_outgoing_metadata(void *user_data,
+static grpc_mdelem *server_filter_outgoing_metadata(grpc_exec_ctx *exec_ctx,
+                                                    void *user_data,
                                                     grpc_mdelem *md) {
   if (md->key == GRPC_MDSTR_GRPC_MESSAGE) {
     grpc_slice pct_encoded_msg = grpc_percent_encode_slice(
         md->value->slice, grpc_compatible_percent_encoding_unreserved_bytes);
     if (grpc_slice_is_equivalent(pct_encoded_msg, md->value->slice)) {
-      grpc_slice_unref(pct_encoded_msg);
+      grpc_slice_unref_internal(exec_ctx, pct_encoded_msg);
       return md;
     } else {
       return grpc_mdelem_from_metadata_strings(
-          GRPC_MDSTR_GRPC_MESSAGE, grpc_mdstr_from_slice(pct_encoded_msg));
+          exec_ctx, GRPC_MDSTR_GRPC_MESSAGE,
+          grpc_mdstr_from_slice(exec_ctx, pct_encoded_msg));
     }
   } else {
     return md;
   }
 }
 
-static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
-  server_filter_args *a = user_data;
-  grpc_call_element *elem = a->elem;
+static grpc_mdelem *server_filter(grpc_exec_ctx *exec_ctx, void *user_data,
+                                  grpc_mdelem *md) {
+  grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
 
   /* Check if it is one of the headers we care about. */
@@ -157,7 +155,7 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
     /* swallow it and error everything out. */
     /* TODO(klempner): We ought to generate more descriptive error messages
        on the wire here. */
-    grpc_call_element_send_cancel(a->exec_ctx, elem);
+    grpc_call_element_send_cancel(exec_ctx, elem);
     return NULL;
   } else if (md->key == GRPC_MDSTR_PATH) {
     if (calld->seen_path) {
@@ -173,7 +171,7 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
     /* translate host to :authority since :authority may be
        omitted */
     grpc_mdelem *authority = grpc_mdelem_from_metadata_strings(
-        GRPC_MDSTR_AUTHORITY, GRPC_MDSTR_REF(md->value));
+        exec_ctx, GRPC_MDSTR_AUTHORITY, GRPC_MDSTR_REF(md->value));
     calld->seen_authority = 1;
     return authority;
   } else if (md->key == GRPC_MDSTR_GRPC_PAYLOAD_BIN) {
@@ -181,7 +179,7 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
        header field */
     calld->seen_payload_bin = 1;
     grpc_slice_buffer_add(&calld->read_slice_buffer,
-                          grpc_slice_ref(md->value->slice));
+                          grpc_slice_ref_internal(md->value->slice));
     grpc_slice_buffer_stream_init(&calld->read_stream,
                                   &calld->read_slice_buffer, 0);
     return NULL;
@@ -195,10 +193,8 @@ static void hs_on_recv(grpc_exec_ctx *exec_ctx, void *user_data,
   grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
   if (err == GRPC_ERROR_NONE) {
-    server_filter_args a;
-    a.elem = elem;
-    a.exec_ctx = exec_ctx;
-    grpc_metadata_batch_filter(calld->recv_initial_metadata, server_filter, &a);
+    grpc_metadata_batch_filter(exec_ctx, calld->recv_initial_metadata,
+                               server_filter, elem);
     /* Have we seen the required http2 transport headers?
        (:method, :scheme, content-type, with :path and :authority covered
        at the channel level right now) */
@@ -310,9 +306,8 @@ static void hs_mutate_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
   }
 
   if (op->send_trailing_metadata) {
-    server_filter_args a = {elem, exec_ctx};
-    grpc_metadata_batch_filter(op->send_trailing_metadata,
-                               server_filter_outgoing_metadata, &a);
+    grpc_metadata_batch_filter(exec_ctx, op->send_trailing_metadata,
+                               server_filter_outgoing_metadata, elem);
   }
 }
 
@@ -334,9 +329,12 @@ static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
   call_data *calld = elem->call_data;
   /* initialize members */
   memset(calld, 0, sizeof(*calld));
-  grpc_closure_init(&calld->hs_on_recv, hs_on_recv, elem);
-  grpc_closure_init(&calld->hs_on_complete, hs_on_complete, elem);
-  grpc_closure_init(&calld->hs_recv_message_ready, hs_recv_message_ready, elem);
+  grpc_closure_init(&calld->hs_on_recv, hs_on_recv, elem,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&calld->hs_on_complete, hs_on_complete, elem,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&calld->hs_recv_message_ready, hs_recv_message_ready, elem,
+                    grpc_schedule_on_exec_ctx);
   grpc_slice_buffer_init(&calld->read_slice_buffer);
   return GRPC_ERROR_NONE;
 }
@@ -346,14 +344,15 @@ static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                               const grpc_call_final_info *final_info,
                               void *ignored) {
   call_data *calld = elem->call_data;
-  grpc_slice_buffer_destroy(&calld->read_slice_buffer);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &calld->read_slice_buffer);
 }
 
 /* Constructor for channel_data */
-static void init_channel_elem(grpc_exec_ctx *exec_ctx,
-                              grpc_channel_element *elem,
-                              grpc_channel_element_args *args) {
+static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx,
+                                     grpc_channel_element *elem,
+                                     grpc_channel_element_args *args) {
   GPR_ASSERT(!args->is_last);
+  return GRPC_ERROR_NONE;
 }
 
 /* Destructor for channel data */
diff --git a/src/core/lib/channel/message_size_filter.c b/src/core/lib/channel/message_size_filter.c
index 1cf68d790d80e8c9a01da24b25ea2037376090ba..862090b371cf974456fa4d0ed05c3f55a9cb9b97 100644
--- a/src/core/lib/channel/message_size_filter.c
+++ b/src/core/lib/channel/message_size_filter.c
@@ -54,8 +54,12 @@ static void* message_size_limits_copy(void* value) {
   return new_value;
 }
 
+static void message_size_limits_free(grpc_exec_ctx* exec_ctx, void* value) {
+  gpr_free(value);
+}
+
 static const grpc_mdstr_hash_table_vtable message_size_limits_vtable = {
-    gpr_free, message_size_limits_copy};
+    message_size_limits_free, message_size_limits_copy};
 
 static void* message_size_limits_create_from_json(const grpc_json* json) {
   int max_request_message_bytes = -1;
@@ -124,7 +128,7 @@ static void recv_message_ready(grpc_exec_ctx* exec_ctx, void* user_data,
     gpr_free(message_string);
   }
   // Invoke the next callback.
-  grpc_exec_ctx_sched(exec_ctx, calld->next_recv_message_ready, error, NULL);
+  grpc_closure_sched(exec_ctx, calld->next_recv_message_ready, error);
 }
 
 // Start transport stream op.
@@ -160,7 +164,8 @@ static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx,
   channel_data* chand = elem->channel_data;
   call_data* calld = elem->call_data;
   calld->next_recv_message_ready = NULL;
-  grpc_closure_init(&calld->recv_message_ready, recv_message_ready, elem);
+  grpc_closure_init(&calld->recv_message_ready, recv_message_ready, elem,
+                    grpc_schedule_on_exec_ctx);
   // Get max sizes from channel data, then merge in per-method config values.
   // Note: Per-method config is only available on the client, so we
   // apply the max request size to the send limit and the max response
@@ -168,8 +173,8 @@ static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx,
   calld->max_send_size = chand->max_send_size;
   calld->max_recv_size = chand->max_recv_size;
   if (chand->method_limit_table != NULL) {
-    message_size_limits* limits =
-        grpc_method_config_table_get(chand->method_limit_table, args->path);
+    message_size_limits* limits = grpc_method_config_table_get(
+        exec_ctx, chand->method_limit_table, args->path);
     if (limits != NULL) {
       if (limits->max_send_size >= 0 &&
           (limits->max_send_size < calld->max_send_size ||
@@ -192,9 +197,9 @@ static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
                               void* ignored) {}
 
 // Constructor for channel_data.
-static void init_channel_elem(grpc_exec_ctx* exec_ctx,
-                              grpc_channel_element* elem,
-                              grpc_channel_element_args* args) {
+static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx,
+                                     grpc_channel_element* elem,
+                                     grpc_channel_element_args* args) {
   GPR_ASSERT(!args->is_last);
   channel_data* chand = elem->channel_data;
   memset(chand, 0, sizeof(*chand));
@@ -226,18 +231,19 @@ static void init_channel_elem(grpc_exec_ctx* exec_ctx,
     if (service_config != NULL) {
       chand->method_limit_table =
           grpc_service_config_create_method_config_table(
-              service_config, message_size_limits_create_from_json,
+              exec_ctx, service_config, message_size_limits_create_from_json,
               &message_size_limits_vtable);
       grpc_service_config_destroy(service_config);
     }
   }
+  return GRPC_ERROR_NONE;
 }
 
 // Destructor for channel_data.
 static void destroy_channel_elem(grpc_exec_ctx* exec_ctx,
                                  grpc_channel_element* elem) {
   channel_data* chand = elem->channel_data;
-  grpc_mdstr_hash_table_unref(chand->method_limit_table);
+  grpc_mdstr_hash_table_unref(exec_ctx, chand->method_limit_table);
 }
 
 const grpc_channel_filter grpc_message_size_filter = {
diff --git a/src/core/lib/compression/message_compress.c b/src/core/lib/compression/message_compress.c
index 6c245acf615683213f0d6b1a9bfcd1232eae8162..49beb953b00cb6ecf3cd0dc4e3df7ae5a0920190 100644
--- a/src/core/lib/compression/message_compress.c
+++ b/src/core/lib/compression/message_compress.c
@@ -40,10 +40,12 @@
 
 #include <zlib.h>
 
+#include "src/core/lib/slice/slice_internal.h"
+
 #define OUTPUT_BLOCK_SIZE 1024
 
-static int zlib_body(z_stream* zs, grpc_slice_buffer* input,
-                     grpc_slice_buffer* output,
+static int zlib_body(grpc_exec_ctx* exec_ctx, z_stream* zs,
+                     grpc_slice_buffer* input, grpc_slice_buffer* output,
                      int (*flate)(z_stream* zs, int flush)) {
   int r;
   int flush;
@@ -87,7 +89,7 @@ static int zlib_body(z_stream* zs, grpc_slice_buffer* input,
   return 1;
 
 error:
-  grpc_slice_unref(outbuf);
+  grpc_slice_unref_internal(exec_ctx, outbuf);
   return 0;
 }
 
@@ -97,8 +99,8 @@ static void* zalloc_gpr(void* opaque, unsigned int items, unsigned int size) {
 
 static void zfree_gpr(void* opaque, void* address) { gpr_free(address); }
 
-static int zlib_compress(grpc_slice_buffer* input, grpc_slice_buffer* output,
-                         int gzip) {
+static int zlib_compress(grpc_exec_ctx* exec_ctx, grpc_slice_buffer* input,
+                         grpc_slice_buffer* output, int gzip) {
   z_stream zs;
   int r;
   size_t i;
@@ -110,10 +112,11 @@ static int zlib_compress(grpc_slice_buffer* input, grpc_slice_buffer* output,
   r = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | (gzip ? 16 : 0),
                    8, Z_DEFAULT_STRATEGY);
   GPR_ASSERT(r == Z_OK);
-  r = zlib_body(&zs, input, output, deflate) && output->length < input->length;
+  r = zlib_body(exec_ctx, &zs, input, output, deflate) &&
+      output->length < input->length;
   if (!r) {
     for (i = count_before; i < output->count; i++) {
-      grpc_slice_unref(output->slices[i]);
+      grpc_slice_unref_internal(exec_ctx, output->slices[i]);
     }
     output->count = count_before;
     output->length = length_before;
@@ -122,8 +125,8 @@ static int zlib_compress(grpc_slice_buffer* input, grpc_slice_buffer* output,
   return r;
 }
 
-static int zlib_decompress(grpc_slice_buffer* input, grpc_slice_buffer* output,
-                           int gzip) {
+static int zlib_decompress(grpc_exec_ctx* exec_ctx, grpc_slice_buffer* input,
+                           grpc_slice_buffer* output, int gzip) {
   z_stream zs;
   int r;
   size_t i;
@@ -134,10 +137,10 @@ static int zlib_decompress(grpc_slice_buffer* input, grpc_slice_buffer* output,
   zs.zfree = zfree_gpr;
   r = inflateInit2(&zs, 15 | (gzip ? 16 : 0));
   GPR_ASSERT(r == Z_OK);
-  r = zlib_body(&zs, input, output, inflate);
+  r = zlib_body(exec_ctx, &zs, input, output, inflate);
   if (!r) {
     for (i = count_before; i < output->count; i++) {
-      grpc_slice_unref(output->slices[i]);
+      grpc_slice_unref_internal(exec_ctx, output->slices[i]);
     }
     output->count = count_before;
     output->length = length_before;
@@ -149,12 +152,13 @@ static int zlib_decompress(grpc_slice_buffer* input, grpc_slice_buffer* output,
 static int copy(grpc_slice_buffer* input, grpc_slice_buffer* output) {
   size_t i;
   for (i = 0; i < input->count; i++) {
-    grpc_slice_buffer_add(output, grpc_slice_ref(input->slices[i]));
+    grpc_slice_buffer_add(output, grpc_slice_ref_internal(input->slices[i]));
   }
   return 1;
 }
 
-static int compress_inner(grpc_compression_algorithm algorithm,
+static int compress_inner(grpc_exec_ctx* exec_ctx,
+                          grpc_compression_algorithm algorithm,
                           grpc_slice_buffer* input, grpc_slice_buffer* output) {
   switch (algorithm) {
     case GRPC_COMPRESS_NONE:
@@ -162,9 +166,9 @@ static int compress_inner(grpc_compression_algorithm algorithm,
          rely on that here */
       return 0;
     case GRPC_COMPRESS_DEFLATE:
-      return zlib_compress(input, output, 0);
+      return zlib_compress(exec_ctx, input, output, 0);
     case GRPC_COMPRESS_GZIP:
-      return zlib_compress(input, output, 1);
+      return zlib_compress(exec_ctx, input, output, 1);
     case GRPC_COMPRESS_ALGORITHMS_COUNT:
       break;
   }
@@ -172,24 +176,26 @@ static int compress_inner(grpc_compression_algorithm algorithm,
   return 0;
 }
 
-int grpc_msg_compress(grpc_compression_algorithm algorithm,
+int grpc_msg_compress(grpc_exec_ctx* exec_ctx,
+                      grpc_compression_algorithm algorithm,
                       grpc_slice_buffer* input, grpc_slice_buffer* output) {
-  if (!compress_inner(algorithm, input, output)) {
+  if (!compress_inner(exec_ctx, algorithm, input, output)) {
     copy(input, output);
     return 0;
   }
   return 1;
 }
 
-int grpc_msg_decompress(grpc_compression_algorithm algorithm,
+int grpc_msg_decompress(grpc_exec_ctx* exec_ctx,
+                        grpc_compression_algorithm algorithm,
                         grpc_slice_buffer* input, grpc_slice_buffer* output) {
   switch (algorithm) {
     case GRPC_COMPRESS_NONE:
       return copy(input, output);
     case GRPC_COMPRESS_DEFLATE:
-      return zlib_decompress(input, output, 0);
+      return zlib_decompress(exec_ctx, input, output, 0);
     case GRPC_COMPRESS_GZIP:
-      return zlib_decompress(input, output, 1);
+      return zlib_decompress(exec_ctx, input, output, 1);
     case GRPC_COMPRESS_ALGORITHMS_COUNT:
       break;
   }
diff --git a/src/core/lib/compression/message_compress.h b/src/core/lib/compression/message_compress.h
index 448d36a8635190ece28e0532f182aea28ad3542a..7bd3d98607cc8dd80d86be55d939e7b9eca30487 100644
--- a/src/core/lib/compression/message_compress.h
+++ b/src/core/lib/compression/message_compress.h
@@ -40,13 +40,15 @@
 /* compress 'input' to 'output' using 'algorithm'.
    On success, appends compressed slices to output and returns 1.
    On failure, appends uncompressed slices to output and returns 0. */
-int grpc_msg_compress(grpc_compression_algorithm algorithm,
+int grpc_msg_compress(grpc_exec_ctx* exec_ctx,
+                      grpc_compression_algorithm algorithm,
                       grpc_slice_buffer* input, grpc_slice_buffer* output);
 
 /* decompress 'input' to 'output' using 'algorithm'.
    On success, appends slices to output and returns 1.
    On failure, output is unchanged, and returns 0. */
-int grpc_msg_decompress(grpc_compression_algorithm algorithm,
+int grpc_msg_decompress(grpc_exec_ctx* exec_ctx,
+                        grpc_compression_algorithm algorithm,
                         grpc_slice_buffer* input, grpc_slice_buffer* output);
 
 #endif /* GRPC_CORE_LIB_COMPRESSION_MESSAGE_COMPRESS_H */
diff --git a/src/core/lib/http/httpcli.c b/src/core/lib/http/httpcli.c
index fdb8abaa2df1f67399b8042c27f4fdc3304ee172..fb2108987b15baaebd27b95be08dc6ee92daba39 100644
--- a/src/core/lib/http/httpcli.c
+++ b/src/core/lib/http/httpcli.c
@@ -47,6 +47,7 @@
 #include "src/core/lib/iomgr/resolve_address.h"
 #include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/iomgr/tcp_client.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/support/string.h"
 
 typedef struct {
@@ -103,7 +104,7 @@ static void finish(grpc_exec_ctx *exec_ctx, internal_request *req,
                    grpc_error *error) {
   grpc_polling_entity_del_from_pollset_set(exec_ctx, req->pollent,
                                            req->context->pollset_set);
-  grpc_exec_ctx_sched(exec_ctx, req->on_done, error, NULL);
+  grpc_closure_sched(exec_ctx, req->on_done, error);
   grpc_http_parser_destroy(&req->parser);
   if (req->addresses != NULL) {
     grpc_resolved_addresses_destroy(req->addresses);
@@ -111,14 +112,14 @@ static void finish(grpc_exec_ctx *exec_ctx, internal_request *req,
   if (req->ep != NULL) {
     grpc_endpoint_destroy(exec_ctx, req->ep);
   }
-  grpc_slice_unref(req->request_text);
+  grpc_slice_unref_internal(exec_ctx, req->request_text);
   gpr_free(req->host);
   gpr_free(req->ssl_host_override);
   grpc_iomgr_unregister_object(&req->iomgr_obj);
-  grpc_slice_buffer_destroy(&req->incoming);
-  grpc_slice_buffer_destroy(&req->outgoing);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &req->incoming);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &req->outgoing);
   GRPC_ERROR_UNREF(req->overall_error);
-  grpc_resource_quota_internal_unref(exec_ctx, req->resource_quota);
+  grpc_resource_quota_unref_internal(exec_ctx, req->resource_quota);
   gpr_free(req);
 }
 
@@ -178,7 +179,7 @@ static void done_write(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
 }
 
 static void start_write(grpc_exec_ctx *exec_ctx, internal_request *req) {
-  grpc_slice_ref(req->request_text);
+  grpc_slice_ref_internal(req->request_text);
   grpc_slice_buffer_add(&req->outgoing, req->request_text);
   grpc_endpoint_write(exec_ctx, req->ep, &req->outgoing, &req->done_write);
 }
@@ -224,7 +225,8 @@ static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req,
     return;
   }
   addr = &req->addresses->addrs[req->next_address++];
-  grpc_closure_init(&req->connected, on_connected, req);
+  grpc_closure_init(&req->connected, on_connected, req,
+                    grpc_schedule_on_exec_ctx);
   grpc_arg arg;
   arg.key = GRPC_ARG_RESOURCE_QUOTA;
   arg.type = GRPC_ARG_POINTER;
@@ -265,9 +267,10 @@ static void internal_request_begin(grpc_exec_ctx *exec_ctx,
   req->context = context;
   req->pollent = pollent;
   req->overall_error = GRPC_ERROR_NONE;
-  req->resource_quota = grpc_resource_quota_internal_ref(resource_quota);
-  grpc_closure_init(&req->on_read, on_read, req);
-  grpc_closure_init(&req->done_write, done_write, req);
+  req->resource_quota = grpc_resource_quota_ref_internal(resource_quota);
+  grpc_closure_init(&req->on_read, on_read, req, grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&req->done_write, done_write, req,
+                    grpc_schedule_on_exec_ctx);
   grpc_slice_buffer_init(&req->incoming);
   grpc_slice_buffer_init(&req->outgoing);
   grpc_iomgr_register_object(&req->iomgr_obj, name);
@@ -277,8 +280,11 @@ static void internal_request_begin(grpc_exec_ctx *exec_ctx,
   GPR_ASSERT(pollent);
   grpc_polling_entity_add_to_pollset_set(exec_ctx, req->pollent,
                                          req->context->pollset_set);
-  grpc_resolve_address(exec_ctx, request->host, req->handshaker->default_port,
-                       grpc_closure_create(on_resolved, req), &req->addresses);
+  grpc_resolve_address(
+      exec_ctx, request->host, req->handshaker->default_port,
+      req->context->pollset_set,
+      grpc_closure_create(on_resolved, req, grpc_schedule_on_exec_ctx),
+      &req->addresses);
 }
 
 void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
diff --git a/src/core/lib/http/httpcli_security_connector.c b/src/core/lib/http/httpcli_security_connector.c
index 24d264c32a5bbde0b7ab378122d2516bcb3b54ef..440817c5a6bfd24e66960f9820cd76fd4b568514 100644
--- a/src/core/lib/http/httpcli_security_connector.c
+++ b/src/core/lib/http/httpcli_security_connector.c
@@ -38,7 +38,10 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
-#include "src/core/lib/security/transport/handshake.h"
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/security/transport/security_handshaker.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/tsi/ssl_transport_security.h"
 
@@ -48,7 +51,8 @@ typedef struct {
   char *secure_peer_name;
 } grpc_httpcli_ssl_channel_security_connector;
 
-static void httpcli_ssl_destroy(grpc_security_connector *sc) {
+static void httpcli_ssl_destroy(grpc_exec_ctx *exec_ctx,
+                                grpc_security_connector *sc) {
   grpc_httpcli_ssl_channel_security_connector *c =
       (grpc_httpcli_ssl_channel_security_connector *)sc;
   if (c->handshaker_factory != NULL) {
@@ -58,52 +62,43 @@ static void httpcli_ssl_destroy(grpc_security_connector *sc) {
   gpr_free(sc);
 }
 
-static void httpcli_ssl_do_handshake(grpc_exec_ctx *exec_ctx,
-                                     grpc_channel_security_connector *sc,
-                                     grpc_endpoint *nonsecure_endpoint,
-                                     grpc_slice_buffer *read_buffer,
-                                     gpr_timespec deadline,
-                                     grpc_security_handshake_done_cb cb,
-                                     void *user_data) {
+static void httpcli_ssl_add_handshakers(grpc_exec_ctx *exec_ctx,
+                                        grpc_channel_security_connector *sc,
+                                        grpc_handshake_manager *handshake_mgr) {
   grpc_httpcli_ssl_channel_security_connector *c =
       (grpc_httpcli_ssl_channel_security_connector *)sc;
-  tsi_result result = TSI_OK;
-  tsi_handshaker *handshaker;
-  if (c->handshaker_factory == NULL) {
-    gpr_free(read_buffer);
-    cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL);
-    return;
-  }
-  result = tsi_ssl_handshaker_factory_create_handshaker(
-      c->handshaker_factory, c->secure_peer_name, &handshaker);
-  if (result != TSI_OK) {
-    gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.",
-            tsi_result_to_string(result));
-    gpr_free(read_buffer);
-    cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL);
-  } else {
-    grpc_do_security_handshake(exec_ctx, handshaker, &sc->base, true,
-                               nonsecure_endpoint, read_buffer, deadline, cb,
-                               user_data);
+  tsi_handshaker *handshaker = NULL;
+  if (c->handshaker_factory != NULL) {
+    tsi_result result = tsi_ssl_handshaker_factory_create_handshaker(
+        c->handshaker_factory, c->secure_peer_name, &handshaker);
+    if (result != TSI_OK) {
+      gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.",
+              tsi_result_to_string(result));
+    }
   }
+  grpc_handshake_manager_add(
+      handshake_mgr,
+      grpc_security_handshaker_create(exec_ctx, handshaker, &sc->base));
 }
 
 static void httpcli_ssl_check_peer(grpc_exec_ctx *exec_ctx,
                                    grpc_security_connector *sc, tsi_peer peer,
-                                   grpc_security_peer_check_cb cb,
-                                   void *user_data) {
+                                   grpc_auth_context **auth_context,
+                                   grpc_closure *on_peer_checked) {
   grpc_httpcli_ssl_channel_security_connector *c =
       (grpc_httpcli_ssl_channel_security_connector *)sc;
-  grpc_security_status status = GRPC_SECURITY_OK;
+  grpc_error *error = GRPC_ERROR_NONE;
 
   /* Check the peer name. */
   if (c->secure_peer_name != NULL &&
       !tsi_ssl_peer_matches_name(&peer, c->secure_peer_name)) {
-    gpr_log(GPR_ERROR, "Peer name %s is not in peer certificate",
-            c->secure_peer_name);
-    status = GRPC_SECURITY_ERROR;
+    char *msg;
+    gpr_asprintf(&msg, "Peer name %s is not in peer certificate",
+                 c->secure_peer_name);
+    error = GRPC_ERROR_CREATE(msg);
+    gpr_free(msg);
   }
-  cb(exec_ctx, user_data, status, NULL);
+  grpc_closure_sched(exec_ctx, on_peer_checked, error);
   tsi_peer_destruct(&peer);
 }
 
@@ -111,8 +106,9 @@ static grpc_security_connector_vtable httpcli_ssl_vtable = {
     httpcli_ssl_destroy, httpcli_ssl_check_peer};
 
 static grpc_security_status httpcli_ssl_channel_security_connector_create(
-    const unsigned char *pem_root_certs, size_t pem_root_certs_size,
-    const char *secure_peer_name, grpc_channel_security_connector **sc) {
+    grpc_exec_ctx *exec_ctx, const unsigned char *pem_root_certs,
+    size_t pem_root_certs_size, const char *secure_peer_name,
+    grpc_channel_security_connector **sc) {
   tsi_result result = TSI_OK;
   grpc_httpcli_ssl_channel_security_connector *c;
 
@@ -136,11 +132,11 @@ static grpc_security_status httpcli_ssl_channel_security_connector_create(
   if (result != TSI_OK) {
     gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.",
             tsi_result_to_string(result));
-    httpcli_ssl_destroy(&c->base.base);
+    httpcli_ssl_destroy(exec_ctx, &c->base.base);
     *sc = NULL;
     return GRPC_SECURITY_ERROR;
   }
-  c->base.do_handshake = httpcli_ssl_do_handshake;
+  c->base.add_handshakers = httpcli_ssl_add_handshakers;
   *sc = &c->base;
   return GRPC_SECURITY_OK;
 }
@@ -150,19 +146,25 @@ static grpc_security_status httpcli_ssl_channel_security_connector_create(
 typedef struct {
   void (*func)(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *endpoint);
   void *arg;
+  grpc_handshake_manager *handshake_mgr;
 } on_done_closure;
 
-static void on_secure_transport_setup_done(grpc_exec_ctx *exec_ctx, void *rp,
-                                           grpc_security_status status,
-                                           grpc_endpoint *secure_endpoint,
-                                           grpc_auth_context *auth_context) {
-  on_done_closure *c = rp;
-  if (status != GRPC_SECURITY_OK) {
-    gpr_log(GPR_ERROR, "Secure transport setup failed with error %d.", status);
+static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg,
+                              grpc_error *error) {
+  grpc_handshaker_args *args = arg;
+  on_done_closure *c = args->user_data;
+  if (error != GRPC_ERROR_NONE) {
+    const char *msg = grpc_error_string(error);
+    gpr_log(GPR_ERROR, "Secure transport setup failed: %s", msg);
+    grpc_error_free_string(msg);
     c->func(exec_ctx, c->arg, NULL);
   } else {
-    c->func(exec_ctx, c->arg, secure_endpoint);
+    grpc_channel_args_destroy(exec_ctx, args->args);
+    grpc_slice_buffer_destroy_internal(exec_ctx, args->read_buffer);
+    gpr_free(args->read_buffer);
+    c->func(exec_ctx, c->arg, args->endpoint);
   }
+  grpc_handshake_manager_destroy(exec_ctx, c->handshake_mgr);
   gpr_free(c);
 }
 
@@ -183,12 +185,16 @@ static void ssl_handshake(grpc_exec_ctx *exec_ctx, void *arg,
   }
   c->func = on_done;
   c->arg = arg;
+  c->handshake_mgr = grpc_handshake_manager_create();
   GPR_ASSERT(httpcli_ssl_channel_security_connector_create(
-                 pem_root_certs, pem_root_certs_size, host, &sc) ==
+                 exec_ctx, pem_root_certs, pem_root_certs_size, host, &sc) ==
              GRPC_SECURITY_OK);
-  grpc_channel_security_connector_do_handshake(
-      exec_ctx, sc, tcp, NULL, deadline, on_secure_transport_setup_done, c);
-  GRPC_SECURITY_CONNECTOR_UNREF(&sc->base, "httpcli");
+  grpc_channel_security_connector_add_handshakers(exec_ctx, sc,
+                                                  c->handshake_mgr);
+  grpc_handshake_manager_do_handshake(
+      exec_ctx, c->handshake_mgr, tcp, NULL /* channel_args */, deadline,
+      NULL /* acceptor */, on_handshake_done, c /* user_data */);
+  GRPC_SECURITY_CONNECTOR_UNREF(exec_ctx, &sc->base, "httpcli");
 }
 
 const grpc_httpcli_handshaker grpc_httpcli_ssl = {"https", ssl_handshake};
diff --git a/src/core/lib/iomgr/README.md b/src/core/lib/iomgr/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..9b22b76cebf96637ea7dbc793e53a69ce870ef6c
--- /dev/null
+++ b/src/core/lib/iomgr/README.md
@@ -0,0 +1,6 @@
+# iomgr
+
+Platform abstractions for I/O (mostly network).
+
+Provides abstractions over TCP/UDP I/O, file loading, polling, and concurrency
+management for various operating systems.
diff --git a/src/core/lib/iomgr/closure.c b/src/core/lib/iomgr/closure.c
index c6ddc76732a4ecb8d3af7bc4ef28b773a3cf590c..da0ec878a39818bc5b5fcfd5ec7935f09e8efa0d 100644
--- a/src/core/lib/iomgr/closure.c
+++ b/src/core/lib/iomgr/closure.c
@@ -37,10 +37,13 @@
 
 #include "src/core/lib/profiling/timers.h"
 
-void grpc_closure_init(grpc_closure *closure, grpc_iomgr_cb_func cb,
-                       void *cb_arg) {
+grpc_closure *grpc_closure_init(grpc_closure *closure, grpc_iomgr_cb_func cb,
+                                void *cb_arg,
+                                grpc_closure_scheduler *scheduler) {
   closure->cb = cb;
   closure->cb_arg = cb_arg;
+  closure->scheduler = scheduler;
+  return closure;
 }
 
 void grpc_closure_list_init(grpc_closure_list *closure_list) {
@@ -105,11 +108,12 @@ static void closure_wrapper(grpc_exec_ctx *exec_ctx, void *arg,
   cb(exec_ctx, cb_arg, error);
 }
 
-grpc_closure *grpc_closure_create(grpc_iomgr_cb_func cb, void *cb_arg) {
+grpc_closure *grpc_closure_create(grpc_iomgr_cb_func cb, void *cb_arg,
+                                  grpc_closure_scheduler *scheduler) {
   wrapped_closure *wc = gpr_malloc(sizeof(*wc));
   wc->cb = cb;
   wc->cb_arg = cb_arg;
-  grpc_closure_init(&wc->wrapper, closure_wrapper, wc);
+  grpc_closure_init(&wc->wrapper, closure_wrapper, wc, scheduler);
   return &wc->wrapper;
 }
 
@@ -117,8 +121,30 @@ void grpc_closure_run(grpc_exec_ctx *exec_ctx, grpc_closure *c,
                       grpc_error *error) {
   GPR_TIMER_BEGIN("grpc_closure_run", 0);
   if (c != NULL) {
-    c->cb(exec_ctx, c->cb_arg, error);
+    c->scheduler->vtable->run(exec_ctx, c, error);
+  } else {
+    GRPC_ERROR_UNREF(error);
   }
-  GRPC_ERROR_UNREF(error);
   GPR_TIMER_END("grpc_closure_run", 0);
 }
+
+void grpc_closure_sched(grpc_exec_ctx *exec_ctx, grpc_closure *c,
+                        grpc_error *error) {
+  GPR_TIMER_BEGIN("grpc_closure_sched", 0);
+  if (c != NULL) {
+    c->scheduler->vtable->sched(exec_ctx, c, error);
+  } else {
+    GRPC_ERROR_UNREF(error);
+  }
+  GPR_TIMER_END("grpc_closure_sched", 0);
+}
+
+void grpc_closure_list_sched(grpc_exec_ctx *exec_ctx, grpc_closure_list *list) {
+  grpc_closure *c = list->head;
+  while (c != NULL) {
+    grpc_closure *next = c->next_data.next;
+    c->scheduler->vtable->sched(exec_ctx, c, c->error_data.error);
+    c = next;
+  }
+  list->head = list->tail = NULL;
+}
diff --git a/src/core/lib/iomgr/closure.h b/src/core/lib/iomgr/closure.h
index 2b4b271eaab2fe8cf56c6495b09e0981cba872ec..ee386fbc7670c28b94c26b5d2cc8dab8f882fbe7 100644
--- a/src/core/lib/iomgr/closure.h
+++ b/src/core/lib/iomgr/closure.h
@@ -35,6 +35,8 @@
 #define GRPC_CORE_LIB_IOMGR_CLOSURE_H
 
 #include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/exec_ctx_fwd.h>
 #include <stdbool.h>
 #include "src/core/lib/iomgr/error.h"
 #include "src/core/lib/support/mpscq.h"
@@ -42,10 +44,6 @@
 struct grpc_closure;
 typedef struct grpc_closure grpc_closure;
 
-/* forward declaration for exec_ctx.h */
-struct grpc_exec_ctx;
-typedef struct grpc_exec_ctx grpc_exec_ctx;
-
 typedef struct grpc_closure_list {
   grpc_closure *head;
   grpc_closure *tail;
@@ -59,6 +57,22 @@ typedef struct grpc_closure_list {
 typedef void (*grpc_iomgr_cb_func)(grpc_exec_ctx *exec_ctx, void *arg,
                                    grpc_error *error);
 
+typedef struct grpc_closure_scheduler grpc_closure_scheduler;
+
+typedef struct grpc_closure_scheduler_vtable {
+  /* NOTE: for all these functions, closure->scheduler == the scheduler that was
+           used to find this vtable */
+  void (*run)(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
+              grpc_error *error);
+  void (*sched)(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
+                grpc_error *error);
+} grpc_closure_scheduler_vtable;
+
+/** Abstract type that can schedule closures for execution */
+struct grpc_closure_scheduler {
+  const grpc_closure_scheduler_vtable *vtable;
+};
+
 /** A closure over a grpc_iomgr_cb_func. */
 struct grpc_closure {
   /** Once queued, next indicates the next queued closure; before then, scratch
@@ -75,6 +89,10 @@ struct grpc_closure {
   /** Arguments to be passed to "cb". */
   void *cb_arg;
 
+  /** Scheduler to schedule against: NULL to schedule against current execution
+      context */
+  grpc_closure_scheduler *scheduler;
+
   /** Once queued, the result of the closure. Before then: scratch space */
   union {
     grpc_error *error;
@@ -82,12 +100,14 @@ struct grpc_closure {
   } error_data;
 };
 
-/** Initializes \a closure with \a cb and \a cb_arg. */
-void grpc_closure_init(grpc_closure *closure, grpc_iomgr_cb_func cb,
-                       void *cb_arg);
+/** Initializes \a closure with \a cb and \a cb_arg. Returns \a closure. */
+grpc_closure *grpc_closure_init(grpc_closure *closure, grpc_iomgr_cb_func cb,
+                                void *cb_arg,
+                                grpc_closure_scheduler *scheduler);
 
 /* Create a heap allocated closure: try to avoid except for very rare events */
-grpc_closure *grpc_closure_create(grpc_iomgr_cb_func cb, void *cb_arg);
+grpc_closure *grpc_closure_create(grpc_iomgr_cb_func cb, void *cb_arg,
+                                  grpc_closure_scheduler *scheduler);
 
 #define GRPC_CLOSURE_LIST_INIT \
   { NULL, NULL }
@@ -115,4 +135,13 @@ bool grpc_closure_list_empty(grpc_closure_list list);
 void grpc_closure_run(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
                       grpc_error *error);
 
+/** Schedule a closure to be run. Does not need to be run from a safe point. */
+void grpc_closure_sched(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
+                        grpc_error *error);
+
+/** Schedule all closures in a list to be run. Does not need to be run from a
+ * safe point. */
+void grpc_closure_list_sched(grpc_exec_ctx *exec_ctx,
+                             grpc_closure_list *closure_list);
+
 #endif /* GRPC_CORE_LIB_IOMGR_CLOSURE_H */
diff --git a/src/core/lib/iomgr/combiner.c b/src/core/lib/iomgr/combiner.c
index 60ee14eb232b072929052af0c8b7aec25d43172c..c26a73b2b716a0d6316703ad5410b55113dde78c 100644
--- a/src/core/lib/iomgr/combiner.c
+++ b/src/core/lib/iomgr/combiner.c
@@ -56,6 +56,10 @@ int grpc_combiner_trace = 0;
 struct grpc_combiner {
   grpc_combiner *next_combiner_on_this_exec_ctx;
   grpc_workqueue *optional_workqueue;
+  grpc_closure_scheduler uncovered_scheduler;
+  grpc_closure_scheduler covered_scheduler;
+  grpc_closure_scheduler uncovered_finally_scheduler;
+  grpc_closure_scheduler covered_finally_scheduler;
   gpr_mpscq queue;
   // state is:
   // lower bit - zero if orphaned (STATE_UNORPHANED)
@@ -70,6 +74,26 @@ struct grpc_combiner {
   grpc_closure offload;
 };
 
+static void combiner_exec_uncovered(grpc_exec_ctx *exec_ctx,
+                                    grpc_closure *closure, grpc_error *error);
+static void combiner_exec_covered(grpc_exec_ctx *exec_ctx,
+                                  grpc_closure *closure, grpc_error *error);
+static void combiner_finally_exec_uncovered(grpc_exec_ctx *exec_ctx,
+                                            grpc_closure *closure,
+                                            grpc_error *error);
+static void combiner_finally_exec_covered(grpc_exec_ctx *exec_ctx,
+                                          grpc_closure *closure,
+                                          grpc_error *error);
+
+static const grpc_closure_scheduler_vtable scheduler_uncovered = {
+    combiner_exec_uncovered, combiner_exec_uncovered};
+static const grpc_closure_scheduler_vtable scheduler_covered = {
+    combiner_exec_covered, combiner_exec_covered};
+static const grpc_closure_scheduler_vtable finally_scheduler_uncovered = {
+    combiner_finally_exec_uncovered, combiner_finally_exec_uncovered};
+static const grpc_closure_scheduler_vtable finally_scheduler_covered = {
+    combiner_finally_exec_covered, combiner_finally_exec_covered};
+
 static void offload(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error);
 
 typedef struct {
@@ -90,17 +114,28 @@ static bool is_covered_by_poller(grpc_combiner *lock) {
          gpr_atm_acq_load(&lock->elements_covered_by_poller) > 0;
 }
 
+#define IS_COVERED_BY_POLLER_FMT "(final=%d elems=%" PRIdPTR ")->%d"
+#define IS_COVERED_BY_POLLER_ARGS(lock)                      \
+  (lock)->final_list_covered_by_poller,                      \
+      gpr_atm_acq_load(&(lock)->elements_covered_by_poller), \
+      is_covered_by_poller((lock))
+
 grpc_combiner *grpc_combiner_create(grpc_workqueue *optional_workqueue) {
   grpc_combiner *lock = gpr_malloc(sizeof(*lock));
   lock->next_combiner_on_this_exec_ctx = NULL;
   lock->time_to_execute_final_list = false;
   lock->optional_workqueue = optional_workqueue;
   lock->final_list_covered_by_poller = false;
+  lock->uncovered_scheduler.vtable = &scheduler_uncovered;
+  lock->covered_scheduler.vtable = &scheduler_covered;
+  lock->uncovered_finally_scheduler.vtable = &finally_scheduler_uncovered;
+  lock->covered_finally_scheduler.vtable = &finally_scheduler_covered;
   gpr_atm_no_barrier_store(&lock->state, STATE_UNORPHANED);
   gpr_atm_no_barrier_store(&lock->elements_covered_by_poller, 0);
   gpr_mpscq_init(&lock->queue);
   grpc_closure_list_init(&lock->final_list);
-  grpc_closure_init(&lock->offload, offload, lock);
+  grpc_closure_init(&lock->offload, offload, lock,
+                    grpc_workqueue_scheduler(lock->optional_workqueue));
   GRPC_COMBINER_TRACE(gpr_log(GPR_DEBUG, "C:%p create", lock));
   return lock;
 }
@@ -142,9 +177,9 @@ static void push_first_on_exec_ctx(grpc_exec_ctx *exec_ctx,
   }
 }
 
-void grpc_combiner_execute(grpc_exec_ctx *exec_ctx, grpc_combiner *lock,
-                           grpc_closure *cl, grpc_error *error,
-                           bool covered_by_poller) {
+static void combiner_exec(grpc_exec_ctx *exec_ctx, grpc_combiner *lock,
+                          grpc_closure *cl, grpc_error *error,
+                          bool covered_by_poller) {
   GPR_TIMER_BEGIN("combiner.execute", 0);
   gpr_atm last = gpr_atm_full_fetch_add(&lock->state, STATE_ELEM_COUNT_LOW_BIT);
   GRPC_COMBINER_TRACE(gpr_log(
@@ -165,6 +200,24 @@ void grpc_combiner_execute(grpc_exec_ctx *exec_ctx, grpc_combiner *lock,
   GPR_TIMER_END("combiner.execute", 0);
 }
 
+#define COMBINER_FROM_CLOSURE_SCHEDULER(closure, scheduler_name) \
+  ((grpc_combiner *)(((char *)((closure)->scheduler)) -          \
+                     offsetof(grpc_combiner, scheduler_name)))
+
+static void combiner_exec_uncovered(grpc_exec_ctx *exec_ctx, grpc_closure *cl,
+                                    grpc_error *error) {
+  combiner_exec(exec_ctx,
+                COMBINER_FROM_CLOSURE_SCHEDULER(cl, uncovered_scheduler), cl,
+                error, false);
+}
+
+static void combiner_exec_covered(grpc_exec_ctx *exec_ctx, grpc_closure *cl,
+                                  grpc_error *error) {
+  combiner_exec(exec_ctx,
+                COMBINER_FROM_CLOSURE_SCHEDULER(cl, covered_scheduler), cl,
+                error, true);
+}
+
 static void move_next(grpc_exec_ctx *exec_ctx) {
   exec_ctx->active_combiner =
       exec_ctx->active_combiner->next_combiner_on_this_exec_ctx;
@@ -182,8 +235,7 @@ static void queue_offload(grpc_exec_ctx *exec_ctx, grpc_combiner *lock) {
   move_next(exec_ctx);
   GRPC_COMBINER_TRACE(gpr_log(GPR_DEBUG, "C:%p queue_offload --> %p", lock,
                               lock->optional_workqueue));
-  grpc_workqueue_enqueue(exec_ctx, lock->optional_workqueue, &lock->offload,
-                         GRPC_ERROR_NONE);
+  grpc_closure_sched(exec_ctx, &lock->offload, GRPC_ERROR_NONE);
 }
 
 bool grpc_combiner_continue_exec_ctx(grpc_exec_ctx *exec_ctx) {
@@ -197,9 +249,10 @@ bool grpc_combiner_continue_exec_ctx(grpc_exec_ctx *exec_ctx) {
   GRPC_COMBINER_TRACE(
       gpr_log(GPR_DEBUG,
               "C:%p grpc_combiner_continue_exec_ctx workqueue=%p "
-              "is_covered_by_poller=%d exec_ctx_ready_to_finish=%d "
+              "is_covered_by_poller=" IS_COVERED_BY_POLLER_FMT
+              " exec_ctx_ready_to_finish=%d "
               "time_to_execute_final_list=%d",
-              lock, lock->optional_workqueue, is_covered_by_poller(lock),
+              lock, lock->optional_workqueue, IS_COVERED_BY_POLLER_ARGS(lock),
               grpc_exec_ctx_ready_to_finish(exec_ctx),
               lock->time_to_execute_final_list));
 
@@ -305,23 +358,22 @@ bool grpc_combiner_continue_exec_ctx(grpc_exec_ctx *exec_ctx) {
 }
 
 static void enqueue_finally(grpc_exec_ctx *exec_ctx, void *closure,
-                            grpc_error *error) {
-  grpc_combiner_execute_finally(exec_ctx, exec_ctx->active_combiner, closure,
-                                GRPC_ERROR_REF(error), false);
-}
+                            grpc_error *error);
 
-void grpc_combiner_execute_finally(grpc_exec_ctx *exec_ctx, grpc_combiner *lock,
-                                   grpc_closure *closure, grpc_error *error,
-                                   bool covered_by_poller) {
+static void combiner_execute_finally(grpc_exec_ctx *exec_ctx,
+                                     grpc_combiner *lock, grpc_closure *closure,
+                                     grpc_error *error,
+                                     bool covered_by_poller) {
   GRPC_COMBINER_TRACE(gpr_log(
       GPR_DEBUG, "C:%p grpc_combiner_execute_finally c=%p; ac=%p; cov=%d", lock,
       closure, exec_ctx->active_combiner, covered_by_poller));
   GPR_TIMER_BEGIN("combiner.execute_finally", 0);
   if (exec_ctx->active_combiner != lock) {
     GPR_TIMER_MARK("slowpath", 0);
-    grpc_combiner_execute(exec_ctx, lock,
-                          grpc_closure_create(enqueue_finally, closure), error,
-                          false);
+    grpc_closure_sched(
+        exec_ctx, grpc_closure_create(enqueue_finally, closure,
+                                      grpc_combiner_scheduler(lock, false)),
+        error);
     GPR_TIMER_END("combiner.execute_finally", 0);
     return;
   }
@@ -335,3 +387,36 @@ void grpc_combiner_execute_finally(grpc_exec_ctx *exec_ctx, grpc_combiner *lock,
   grpc_closure_list_append(&lock->final_list, closure, error);
   GPR_TIMER_END("combiner.execute_finally", 0);
 }
+
+static void enqueue_finally(grpc_exec_ctx *exec_ctx, void *closure,
+                            grpc_error *error) {
+  combiner_execute_finally(exec_ctx, exec_ctx->active_combiner, closure,
+                           GRPC_ERROR_REF(error), false);
+}
+
+static void combiner_finally_exec_uncovered(grpc_exec_ctx *exec_ctx,
+                                            grpc_closure *cl,
+                                            grpc_error *error) {
+  combiner_execute_finally(exec_ctx, COMBINER_FROM_CLOSURE_SCHEDULER(
+                                         cl, uncovered_finally_scheduler),
+                           cl, error, false);
+}
+
+static void combiner_finally_exec_covered(grpc_exec_ctx *exec_ctx,
+                                          grpc_closure *cl, grpc_error *error) {
+  combiner_execute_finally(
+      exec_ctx, COMBINER_FROM_CLOSURE_SCHEDULER(cl, covered_finally_scheduler),
+      cl, error, true);
+}
+
+grpc_closure_scheduler *grpc_combiner_scheduler(grpc_combiner *combiner,
+                                                bool covered_by_poller) {
+  return covered_by_poller ? &combiner->covered_scheduler
+                           : &combiner->uncovered_scheduler;
+}
+
+grpc_closure_scheduler *grpc_combiner_finally_scheduler(
+    grpc_combiner *combiner, bool covered_by_poller) {
+  return covered_by_poller ? &combiner->covered_finally_scheduler
+                           : &combiner->uncovered_finally_scheduler;
+}
diff --git a/src/core/lib/iomgr/combiner.h b/src/core/lib/iomgr/combiner.h
index d04eeed83a0397fbedfc18828e5253567def1e04..81dff85d40268a1080cbd4446954f67e0df57970 100644
--- a/src/core/lib/iomgr/combiner.h
+++ b/src/core/lib/iomgr/combiner.h
@@ -50,14 +50,12 @@
 grpc_combiner *grpc_combiner_create(grpc_workqueue *optional_workqueue);
 // Destroy the lock
 void grpc_combiner_destroy(grpc_exec_ctx *exec_ctx, grpc_combiner *lock);
-// Execute \a action within the lock.
-void grpc_combiner_execute(grpc_exec_ctx *exec_ctx, grpc_combiner *lock,
-                           grpc_closure *closure, grpc_error *error,
-                           bool covered_by_poller);
-// Execute \a action within the lock just prior to unlocking.
-void grpc_combiner_execute_finally(grpc_exec_ctx *exec_ctx, grpc_combiner *lock,
-                                   grpc_closure *closure, grpc_error *error,
-                                   bool covered_by_poller);
+// Fetch a scheduler to schedule closures against
+grpc_closure_scheduler *grpc_combiner_scheduler(grpc_combiner *lock,
+                                                bool covered_by_poller);
+// Scheduler to execute \a action within the lock just prior to unlocking.
+grpc_closure_scheduler *grpc_combiner_finally_scheduler(grpc_combiner *lock,
+                                                        bool covered_by_poller);
 
 bool grpc_combiner_continue_exec_ctx(grpc_exec_ctx *exec_ctx);
 
diff --git a/src/core/lib/iomgr/ev_epoll_linux.c b/src/core/lib/iomgr/ev_epoll_linux.c
index 07fbfd849e2c8470d965c65466aa468a01e2433b..d6664aead2e56f13d27c2037f2178abc787f4578 100644
--- a/src/core/lib/iomgr/ev_epoll_linux.c
+++ b/src/core/lib/iomgr/ev_epoll_linux.c
@@ -31,7 +31,6 @@
  *
  */
 
-#include <grpc/grpc_posix.h>
 #include "src/core/lib/iomgr/port.h"
 
 /* This polling engine is only relevant on linux kernels supporting epoll() */
@@ -69,6 +68,9 @@ static int grpc_polling_trace = 0; /* Disabled by default */
     gpr_log(GPR_INFO, (fmt), __VA_ARGS__); \
   }
 
+/* Uncomment the following enable extra checks on poll_object operations */
+/* #define PO_DEBUG */
+
 static int grpc_wakeup_signal = -1;
 static bool is_grpc_wakeup_signal_initialized = false;
 
@@ -95,10 +97,42 @@ void grpc_use_signal(int signum) {
 
 struct polling_island;
 
+typedef enum {
+  POLL_OBJ_FD,
+  POLL_OBJ_POLLSET,
+  POLL_OBJ_POLLSET_SET
+} poll_obj_type;
+
+typedef struct poll_obj {
+#ifdef PO_DEBUG
+  poll_obj_type obj_type;
+#endif
+  gpr_mu mu;
+  struct polling_island *pi;
+} poll_obj;
+
+const char *poll_obj_string(poll_obj_type po_type) {
+  switch (po_type) {
+    case POLL_OBJ_FD:
+      return "fd";
+    case POLL_OBJ_POLLSET:
+      return "pollset";
+    case POLL_OBJ_POLLSET_SET:
+      return "pollset_set";
+  }
+
+  GPR_UNREACHABLE_CODE(return "UNKNOWN");
+}
+
 /*******************************************************************************
  * Fd Declarations
  */
+
+#define FD_FROM_PO(po) ((grpc_fd *)(po))
+
 struct grpc_fd {
+  poll_obj po;
+
   int fd;
   /* refst format:
        bit 0    : 1=Active / 0=Orphaned
@@ -106,8 +140,6 @@ struct grpc_fd {
      Ref/Unref by two to avoid altering the orphaned bit */
   gpr_atm refst;
 
-  gpr_mu mu;
-
   /* Indicates that the fd is shutdown and that any pending read/write closures
      should fail */
   bool shutdown;
@@ -120,9 +152,6 @@ struct grpc_fd {
   grpc_closure *read_closure;
   grpc_closure *write_closure;
 
-  /* The polling island to which this fd belongs to (protected by mu) */
-  struct polling_island *polling_island;
-
   struct grpc_fd *freelist_next;
   grpc_closure *on_done_closure;
 
@@ -172,6 +201,8 @@ static void fd_global_shutdown(void);
 
 /* This is also used as grpc_workqueue (by directly casing it) */
 typedef struct polling_island {
+  grpc_closure_scheduler workqueue_scheduler;
+
   gpr_mu mu;
   /* Ref count. Use PI_ADD_REF() and PI_UNREF() macros to increment/decrement
      the refcount.
@@ -225,41 +256,21 @@ struct grpc_pollset_worker {
 };
 
 struct grpc_pollset {
-  gpr_mu mu;
+  poll_obj po;
+
   grpc_pollset_worker root_worker;
   bool kicked_without_pollers;
 
   bool shutting_down;          /* Is the pollset shutting down ? */
   bool finish_shutdown_called; /* Is the 'finish_shutdown_locked()' called ? */
   grpc_closure *shutdown_done; /* Called after after shutdown is complete */
-
-  /* The polling island to which this pollset belongs to */
-  struct polling_island *polling_island;
 };
 
 /*******************************************************************************
  * Pollset-set Declarations
  */
-/* TODO: sreek - Change the pollset_set implementation such that a pollset_set
- * directly points to a polling_island (and adding an fd/pollset/pollset_set to
- * the current pollset_set would result in polling island merges. This would
- * remove the need to maintain fd_count here. This will also significantly
- * simplify the grpc_fd structure since we would no longer need to explicitly
- * maintain the orphaned state */
 struct grpc_pollset_set {
-  gpr_mu mu;
-
-  size_t pollset_count;
-  size_t pollset_capacity;
-  grpc_pollset **pollsets;
-
-  size_t pollset_set_count;
-  size_t pollset_set_capacity;
-  struct grpc_pollset_set **pollset_sets;
-
-  size_t fd_count;
-  size_t fd_capacity;
-  grpc_fd **fds;
+  poll_obj po;
 };
 
 /*******************************************************************************
@@ -295,6 +306,8 @@ static __thread polling_island *g_current_thread_polling_island;
 
 /* Forward declaration */
 static void polling_island_delete(grpc_exec_ctx *exec_ctx, polling_island *pi);
+static void workqueue_enqueue(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
+                              grpc_error *error);
 
 #ifdef GRPC_TSAN
 /* Currently TSAN may incorrectly flag data races between epoll_ctl and
@@ -307,6 +320,9 @@ static void polling_island_delete(grpc_exec_ctx *exec_ctx, polling_island *pi);
 gpr_atm g_epoll_sync;
 #endif /* defined(GRPC_TSAN) */
 
+static const grpc_closure_scheduler_vtable workqueue_scheduler_vtable = {
+    workqueue_enqueue, workqueue_enqueue};
+
 static void pi_add_ref(polling_island *pi);
 static void pi_unref(grpc_exec_ctx *exec_ctx, polling_island *pi);
 
@@ -519,6 +535,7 @@ static polling_island *polling_island_create(grpc_exec_ctx *exec_ctx,
   *error = GRPC_ERROR_NONE;
 
   pi = gpr_malloc(sizeof(*pi));
+  pi->workqueue_scheduler.vtable = &workqueue_scheduler_vtable;
   gpr_mu_init(&pi->mu);
   pi->fd_cnt = 0;
   pi->fd_capacity = 0;
@@ -790,10 +807,10 @@ static polling_island *polling_island_merge(polling_island *p,
   return q;
 }
 
-static void workqueue_enqueue(grpc_exec_ctx *exec_ctx,
-                              grpc_workqueue *workqueue, grpc_closure *closure,
+static void workqueue_enqueue(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
                               grpc_error *error) {
   GPR_TIMER_BEGIN("workqueue.enqueue", 0);
+  grpc_workqueue *workqueue = (grpc_workqueue *)closure->scheduler;
   /* take a ref to the workqueue: otherwise it can happen that whatever events
    * this kicks off ends up destroying the workqueue before this function
    * completes */
@@ -810,6 +827,12 @@ static void workqueue_enqueue(grpc_exec_ctx *exec_ctx,
   GPR_TIMER_END("workqueue.enqueue", 0);
 }
 
+static grpc_closure_scheduler *workqueue_scheduler(grpc_workqueue *workqueue) {
+  polling_island *pi = (polling_island *)workqueue;
+  return workqueue == NULL ? grpc_schedule_on_exec_ctx
+                           : &pi->workqueue_scheduler;
+}
+
 static grpc_error *polling_island_global_init() {
   grpc_error *error = GRPC_ERROR_NONE;
 
@@ -915,7 +938,7 @@ static void fd_global_shutdown(void) {
   while (fd_freelist != NULL) {
     grpc_fd *fd = fd_freelist;
     fd_freelist = fd_freelist->freelist_next;
-    gpr_mu_destroy(&fd->mu);
+    gpr_mu_destroy(&fd->po.mu);
     gpr_free(fd);
   }
   gpr_mu_destroy(&fd_freelist_mu);
@@ -933,13 +956,17 @@ static grpc_fd *fd_create(int fd, const char *name) {
 
   if (new_fd == NULL) {
     new_fd = gpr_malloc(sizeof(grpc_fd));
-    gpr_mu_init(&new_fd->mu);
+    gpr_mu_init(&new_fd->po.mu);
   }
 
-  /* Note: It is not really needed to get the new_fd->mu lock here. If this is a
-     newly created fd (or an fd we got from the freelist), no one else would be
-     holding a lock to it anyway. */
-  gpr_mu_lock(&new_fd->mu);
+  /* Note: It is not really needed to get the new_fd->po.mu lock here. If this
+   * is a newly created fd (or an fd we got from the freelist), no one else
+   * would be holding a lock to it anyway. */
+  gpr_mu_lock(&new_fd->po.mu);
+  new_fd->po.pi = NULL;
+#ifdef PO_DEBUG
+  new_fd->po.obj_type = POLL_OBJ_FD;
+#endif
 
   gpr_atm_rel_store(&new_fd->refst, (gpr_atm)1);
   new_fd->fd = fd;
@@ -947,12 +974,11 @@ static grpc_fd *fd_create(int fd, const char *name) {
   new_fd->orphaned = false;
   new_fd->read_closure = CLOSURE_NOT_READY;
   new_fd->write_closure = CLOSURE_NOT_READY;
-  new_fd->polling_island = NULL;
   new_fd->freelist_next = NULL;
   new_fd->on_done_closure = NULL;
   new_fd->read_notifier_pollset = NULL;
 
-  gpr_mu_unlock(&new_fd->mu);
+  gpr_mu_unlock(&new_fd->po.mu);
 
   char *fd_name;
   gpr_asprintf(&fd_name, "%s fd=%d", name, fd);
@@ -964,17 +990,13 @@ static grpc_fd *fd_create(int fd, const char *name) {
   return new_fd;
 }
 
-static bool fd_is_orphaned(grpc_fd *fd) {
-  return (gpr_atm_acq_load(&fd->refst) & 1) == 0;
-}
-
 static int fd_wrapped_fd(grpc_fd *fd) {
   int ret_fd = -1;
-  gpr_mu_lock(&fd->mu);
+  gpr_mu_lock(&fd->po.mu);
   if (!fd->orphaned) {
     ret_fd = fd->fd;
   }
-  gpr_mu_unlock(&fd->mu);
+  gpr_mu_unlock(&fd->po.mu);
 
   return ret_fd;
 }
@@ -986,7 +1008,7 @@ static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
   grpc_error *error = GRPC_ERROR_NONE;
   polling_island *unref_pi = NULL;
 
-  gpr_mu_lock(&fd->mu);
+  gpr_mu_lock(&fd->po.mu);
   fd->on_done_closure = on_done;
 
   /* If release_fd is not NULL, we should be relinquishing control of the file
@@ -1006,25 +1028,24 @@ static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
 
   /* Remove the fd from the polling island:
      - Get a lock on the latest polling island (i.e the last island in the
-       linked list pointed by fd->polling_island). This is the island that
+       linked list pointed by fd->po.pi). This is the island that
        would actually contain the fd
      - Remove the fd from the latest polling island
      - Unlock the latest polling island
-     - Set fd->polling_island to NULL (but remove the ref on the polling island
+     - Set fd->po.pi to NULL (but remove the ref on the polling island
        before doing this.) */
-  if (fd->polling_island != NULL) {
-    polling_island *pi_latest = polling_island_lock(fd->polling_island);
+  if (fd->po.pi != NULL) {
+    polling_island *pi_latest = polling_island_lock(fd->po.pi);
     polling_island_remove_fd_locked(pi_latest, fd, is_fd_closed, &error);
     gpr_mu_unlock(&pi_latest->mu);
 
-    unref_pi = fd->polling_island;
-    fd->polling_island = NULL;
+    unref_pi = fd->po.pi;
+    fd->po.pi = NULL;
   }
 
-  grpc_exec_ctx_sched(exec_ctx, fd->on_done_closure, GRPC_ERROR_REF(error),
-                      NULL);
+  grpc_closure_sched(exec_ctx, fd->on_done_closure, GRPC_ERROR_REF(error));
 
-  gpr_mu_unlock(&fd->mu);
+  gpr_mu_unlock(&fd->po.mu);
   UNREF_BY(fd, 2, reason); /* Drop the reference */
   if (unref_pi != NULL) {
     /* Unref stale polling island here, outside the fd lock above.
@@ -1048,16 +1069,14 @@ static grpc_error *fd_shutdown_error(bool shutdown) {
 static void notify_on_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
                              grpc_closure **st, grpc_closure *closure) {
   if (fd->shutdown) {
-    grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_CREATE("FD shutdown"),
-                        NULL);
+    grpc_closure_sched(exec_ctx, closure, GRPC_ERROR_CREATE("FD shutdown"));
   } else if (*st == CLOSURE_NOT_READY) {
     /* not ready ==> switch to a waiting state by setting the closure */
     *st = closure;
   } else if (*st == CLOSURE_READY) {
     /* already ready ==> queue the closure to run immediately */
     *st = CLOSURE_NOT_READY;
-    grpc_exec_ctx_sched(exec_ctx, closure, fd_shutdown_error(fd->shutdown),
-                        NULL);
+    grpc_closure_sched(exec_ctx, closure, fd_shutdown_error(fd->shutdown));
   } else {
     /* upcallptr was set to a different closure.  This is an error! */
     gpr_log(GPR_ERROR,
@@ -1079,7 +1098,7 @@ static int set_ready_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
     return 0;
   } else {
     /* waiting ==> queue closure */
-    grpc_exec_ctx_sched(exec_ctx, *st, fd_shutdown_error(fd->shutdown), NULL);
+    grpc_closure_sched(exec_ctx, *st, fd_shutdown_error(fd->shutdown));
     *st = CLOSURE_NOT_READY;
     return 1;
   }
@@ -1089,23 +1108,23 @@ static grpc_pollset *fd_get_read_notifier_pollset(grpc_exec_ctx *exec_ctx,
                                                   grpc_fd *fd) {
   grpc_pollset *notifier = NULL;
 
-  gpr_mu_lock(&fd->mu);
+  gpr_mu_lock(&fd->po.mu);
   notifier = fd->read_notifier_pollset;
-  gpr_mu_unlock(&fd->mu);
+  gpr_mu_unlock(&fd->po.mu);
 
   return notifier;
 }
 
 static bool fd_is_shutdown(grpc_fd *fd) {
-  gpr_mu_lock(&fd->mu);
+  gpr_mu_lock(&fd->po.mu);
   const bool r = fd->shutdown;
-  gpr_mu_unlock(&fd->mu);
+  gpr_mu_unlock(&fd->po.mu);
   return r;
 }
 
 /* Might be called multiple times */
 static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd) {
-  gpr_mu_lock(&fd->mu);
+  gpr_mu_lock(&fd->po.mu);
   /* Do the actual shutdown only once */
   if (!fd->shutdown) {
     fd->shutdown = true;
@@ -1116,28 +1135,28 @@ static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd) {
     set_ready_locked(exec_ctx, fd, &fd->read_closure);
     set_ready_locked(exec_ctx, fd, &fd->write_closure);
   }
-  gpr_mu_unlock(&fd->mu);
+  gpr_mu_unlock(&fd->po.mu);
 }
 
 static void fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
                               grpc_closure *closure) {
-  gpr_mu_lock(&fd->mu);
+  gpr_mu_lock(&fd->po.mu);
   notify_on_locked(exec_ctx, fd, &fd->read_closure, closure);
-  gpr_mu_unlock(&fd->mu);
+  gpr_mu_unlock(&fd->po.mu);
 }
 
 static void fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
                                grpc_closure *closure) {
-  gpr_mu_lock(&fd->mu);
+  gpr_mu_lock(&fd->po.mu);
   notify_on_locked(exec_ctx, fd, &fd->write_closure, closure);
-  gpr_mu_unlock(&fd->mu);
+  gpr_mu_unlock(&fd->po.mu);
 }
 
 static grpc_workqueue *fd_get_workqueue(grpc_fd *fd) {
-  gpr_mu_lock(&fd->mu);
-  grpc_workqueue *workqueue = GRPC_WORKQUEUE_REF(
-      (grpc_workqueue *)fd->polling_island, "fd_get_workqueue");
-  gpr_mu_unlock(&fd->mu);
+  gpr_mu_lock(&fd->po.mu);
+  grpc_workqueue *workqueue =
+      GRPC_WORKQUEUE_REF((grpc_workqueue *)fd->po.pi, "fd_get_workqueue");
+  gpr_mu_unlock(&fd->po.mu);
   return workqueue;
 }
 
@@ -1277,8 +1296,12 @@ static grpc_error *kick_poller(void) {
 }
 
 static void pollset_init(grpc_pollset *pollset, gpr_mu **mu) {
-  gpr_mu_init(&pollset->mu);
-  *mu = &pollset->mu;
+  gpr_mu_init(&pollset->po.mu);
+  *mu = &pollset->po.mu;
+  pollset->po.pi = NULL;
+#ifdef PO_DEBUG
+  pollset->po.obj_type = POLL_OBJ_POLLSET;
+#endif
 
   pollset->root_worker.next = pollset->root_worker.prev = &pollset->root_worker;
   pollset->kicked_without_pollers = false;
@@ -1286,8 +1309,6 @@ static void pollset_init(grpc_pollset *pollset, gpr_mu **mu) {
   pollset->shutting_down = false;
   pollset->finish_shutdown_called = false;
   pollset->shutdown_done = NULL;
-
-  pollset->polling_island = NULL;
 }
 
 /* Convert a timespec to milliseconds:
@@ -1317,26 +1338,26 @@ static int poll_deadline_to_millis_timeout(gpr_timespec deadline,
 
 static void fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
                                grpc_pollset *notifier) {
-  /* Need the fd->mu since we might be racing with fd_notify_on_read */
-  gpr_mu_lock(&fd->mu);
+  /* Need the fd->po.mu since we might be racing with fd_notify_on_read */
+  gpr_mu_lock(&fd->po.mu);
   set_ready_locked(exec_ctx, fd, &fd->read_closure);
   fd->read_notifier_pollset = notifier;
-  gpr_mu_unlock(&fd->mu);
+  gpr_mu_unlock(&fd->po.mu);
 }
 
 static void fd_become_writable(grpc_exec_ctx *exec_ctx, grpc_fd *fd) {
-  /* Need the fd->mu since we might be racing with fd_notify_on_write */
-  gpr_mu_lock(&fd->mu);
+  /* Need the fd->po.mu since we might be racing with fd_notify_on_write */
+  gpr_mu_lock(&fd->po.mu);
   set_ready_locked(exec_ctx, fd, &fd->write_closure);
-  gpr_mu_unlock(&fd->mu);
+  gpr_mu_unlock(&fd->po.mu);
 }
 
 static void pollset_release_polling_island(grpc_exec_ctx *exec_ctx,
                                            grpc_pollset *ps, char *reason) {
-  if (ps->polling_island != NULL) {
-    PI_UNREF(exec_ctx, ps->polling_island, reason);
+  if (ps->po.pi != NULL) {
+    PI_UNREF(exec_ctx, ps->po.pi, reason);
   }
-  ps->polling_island = NULL;
+  ps->po.pi = NULL;
 }
 
 static void finish_shutdown_locked(grpc_exec_ctx *exec_ctx,
@@ -1346,12 +1367,12 @@ static void finish_shutdown_locked(grpc_exec_ctx *exec_ctx,
 
   pollset->finish_shutdown_called = true;
 
-  /* Release the ref and set pollset->polling_island to NULL */
+  /* Release the ref and set pollset->po.pi to NULL */
   pollset_release_polling_island(exec_ctx, pollset, "ps_shutdown");
-  grpc_exec_ctx_sched(exec_ctx, pollset->shutdown_done, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, pollset->shutdown_done, GRPC_ERROR_NONE);
 }
 
-/* pollset->mu lock must be held by the caller before calling this */
+/* pollset->po.mu lock must be held by the caller before calling this */
 static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                              grpc_closure *closure) {
   GPR_TIMER_BEGIN("pollset_shutdown", 0);
@@ -1376,7 +1397,7 @@ static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
  * here */
 static void pollset_destroy(grpc_pollset *pollset) {
   GPR_ASSERT(!pollset_has_workers(pollset));
-  gpr_mu_destroy(&pollset->mu);
+  gpr_mu_destroy(&pollset->po.mu);
 }
 
 static void pollset_reset(grpc_pollset *pollset) {
@@ -1386,7 +1407,7 @@ static void pollset_reset(grpc_pollset *pollset) {
   pollset->finish_shutdown_called = false;
   pollset->kicked_without_pollers = false;
   pollset->shutdown_done = NULL;
-  GPR_ASSERT(pollset->polling_island == NULL);
+  GPR_ASSERT(pollset->po.pi == NULL);
 }
 
 static bool maybe_do_workqueue_work(grpc_exec_ctx *exec_ctx,
@@ -1399,7 +1420,9 @@ static bool maybe_do_workqueue_work(grpc_exec_ctx *exec_ctx,
         workqueue_maybe_wakeup(pi);
       }
       grpc_closure *c = (grpc_closure *)n;
-      grpc_closure_run(exec_ctx, c, c->error_data.error);
+      grpc_error *error = c->error_data.error;
+      c->cb(exec_ctx, c->cb_arg, error);
+      GRPC_ERROR_UNREF(error);
       return true;
     } else if (gpr_atm_no_barrier_load(&pi->workqueue_item_count) > 0) {
       /* n == NULL might mean there's work but it's not available to be popped
@@ -1426,7 +1449,7 @@ static void pollset_work_and_unlock(grpc_exec_ctx *exec_ctx,
   GPR_TIMER_BEGIN("pollset_work_and_unlock", 0);
 
   /* We need to get the epoll_fd to wait on. The epoll_fd is in inside the
-     latest polling island pointed by pollset->polling_island.
+     latest polling island pointed by pollset->po.pi
 
      Since epoll_fd is immutable, we can read it without obtaining the polling
      island lock. There is however a possibility that the polling island (from
@@ -1435,36 +1458,36 @@ static void pollset_work_and_unlock(grpc_exec_ctx *exec_ctx,
      right-away from epoll_wait() and pick up the latest polling_island the next
      this function (i.e pollset_work_and_unlock()) is called */
 
-  if (pollset->polling_island == NULL) {
-    pollset->polling_island = polling_island_create(exec_ctx, NULL, error);
-    if (pollset->polling_island == NULL) {
+  if (pollset->po.pi == NULL) {
+    pollset->po.pi = polling_island_create(exec_ctx, NULL, error);
+    if (pollset->po.pi == NULL) {
       GPR_TIMER_END("pollset_work_and_unlock", 0);
       return; /* Fatal error. We cannot continue */
     }
 
-    PI_ADD_REF(pollset->polling_island, "ps");
+    PI_ADD_REF(pollset->po.pi, "ps");
     GRPC_POLLING_TRACE("pollset_work: pollset: %p created new pi: %p",
-                       (void *)pollset, (void *)pollset->polling_island);
+                       (void *)pollset, (void *)pollset->po.pi);
   }
 
-  pi = polling_island_maybe_get_latest(pollset->polling_island);
+  pi = polling_island_maybe_get_latest(pollset->po.pi);
   epoll_fd = pi->epoll_fd;
 
-  /* Update the pollset->polling_island since the island being pointed by
-     pollset->polling_island maybe older than the one pointed by pi) */
-  if (pollset->polling_island != pi) {
+  /* Update the pollset->po.pi since the island being pointed by
+     pollset->po.pi maybe older than the one pointed by pi) */
+  if (pollset->po.pi != pi) {
     /* Always do PI_ADD_REF before PI_UNREF because PI_UNREF may cause the
        polling island to be deleted */
     PI_ADD_REF(pi, "ps");
-    PI_UNREF(exec_ctx, pollset->polling_island, "ps");
-    pollset->polling_island = pi;
+    PI_UNREF(exec_ctx, pollset->po.pi, "ps");
+    pollset->po.pi = pi;
   }
 
   /* Add an extra ref so that the island does not get destroyed (which means
      the epoll_fd won't be closed) while we are are doing an epoll_wait() on the
      epoll_fd */
   PI_ADD_REF(pi, "ps_work");
-  gpr_mu_unlock(&pollset->mu);
+  gpr_mu_unlock(&pollset->po.mu);
 
   /* If we get some workqueue work to do, it might end up completing an item on
      the completion queue, so there's no need to poll... so we skip that and
@@ -1537,17 +1560,17 @@ static void pollset_work_and_unlock(grpc_exec_ctx *exec_ctx,
   GPR_ASSERT(pi != NULL);
 
   /* Before leaving, release the extra ref we added to the polling island. It
-     is important to use "pi" here (i.e our old copy of pollset->polling_island
+     is important to use "pi" here (i.e our old copy of pollset->po.pi
      that we got before releasing the polling island lock). This is because
-     pollset->polling_island pointer might get udpated in other parts of the
+     pollset->po.pi pointer might get udpated in other parts of the
      code when there is an island merge while we are doing epoll_wait() above */
   PI_UNREF(exec_ctx, pi, "ps_work");
 
   GPR_TIMER_END("pollset_work_and_unlock", 0);
 }
 
-/* pollset->mu lock must be held by the caller before calling this.
-   The function pollset_work() may temporarily release the lock (pollset->mu)
+/* pollset->po.mu lock must be held by the caller before calling this.
+   The function pollset_work() may temporarily release the lock (pollset->po.mu)
    during the course of its execution but it will always re-acquire the lock and
    ensure that it is held by the time the function returns */
 static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
@@ -1617,7 +1640,7 @@ static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                             &g_orig_sigmask, &error);
     grpc_exec_ctx_flush(exec_ctx);
 
-    gpr_mu_lock(&pollset->mu);
+    gpr_mu_lock(&pollset->po.mu);
 
     /* Note: There is no need to reset worker.is_kicked to 0 since we are no
        longer going to use this worker */
@@ -1637,9 +1660,9 @@ static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
     GPR_TIMER_MARK("pollset_work.finish_shutdown_locked", 0);
     finish_shutdown_locked(exec_ctx, pollset);
 
-    gpr_mu_unlock(&pollset->mu);
+    gpr_mu_unlock(&pollset->po.mu);
     grpc_exec_ctx_flush(exec_ctx);
-    gpr_mu_lock(&pollset->mu);
+    gpr_mu_lock(&pollset->po.mu);
   }
 
   *worker_hdl = NULL;
@@ -1653,130 +1676,160 @@ static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
   return error;
 }
 
-static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
-                           grpc_fd *fd) {
-  GPR_TIMER_BEGIN("pollset_add_fd", 0);
-
-  grpc_error *error = GRPC_ERROR_NONE;
+static void add_poll_object(grpc_exec_ctx *exec_ctx, poll_obj *bag,
+                            poll_obj_type bag_type, poll_obj *item,
+                            poll_obj_type item_type) {
+  GPR_TIMER_BEGIN("add_poll_object", 0);
 
-  gpr_mu_lock(&pollset->mu);
-  gpr_mu_lock(&fd->mu);
+#ifdef PO_DEBUG
+  GPR_ASSERT(item->obj_type == item_type);
+  GPR_ASSERT(bag->obj_type == bag_type);
+#endif
 
+  grpc_error *error = GRPC_ERROR_NONE;
   polling_island *pi_new = NULL;
 
+  gpr_mu_lock(&bag->mu);
+  gpr_mu_lock(&item->mu);
+
 retry:
-  /* 1) If fd->polling_island and pollset->polling_island are both non-NULL and
-   *    equal, do nothing.
-   * 2) If fd->polling_island and pollset->polling_island are both NULL, create
-   *    a new polling island (with a refcount of 2) and make the polling_island
-   *    fields in both fd and pollset to point to the new island
-   * 3) If one of fd->polling_island or pollset->polling_island is NULL, update
-   *    the NULL polling_island field to point to the non-NULL polling_island
-   *    field (ensure that the refcount on the polling island is incremented by
-   *    1 to account for the newly added reference)
-   * 4) Finally, if fd->polling_island and pollset->polling_island are non-NULL
-   *    and different, merge both the polling islands and update the
-   *    polling_island fields in both fd and pollset to point to the merged
-   *    polling island.
+  /*
+   * 1) If item->pi and bag->pi are both non-NULL and equal, do nothing
+   * 2) If item->pi and bag->pi are both NULL, create a new polling island (with
+   *    a refcount of 2) and point item->pi and bag->pi to the new island
+   * 3) If exactly one of item->pi or bag->pi is NULL, update it to point to
+   *    the other's non-NULL pi
+   * 4) Finally if item->pi and bag-pi are non-NULL and not-equal, merge the
+   *    polling islands and update item->pi and bag->pi to point to the new
+   *    island
    */
 
-  if (fd->orphaned) {
-    gpr_mu_unlock(&fd->mu);
-    gpr_mu_unlock(&pollset->mu);
-    /* early out */
+  /* Early out if we are trying to add an 'fd' to a 'bag' but the fd is already
+   * orphaned */
+  if (item_type == POLL_OBJ_FD && (FD_FROM_PO(item))->orphaned) {
+    gpr_mu_unlock(&item->mu);
+    gpr_mu_unlock(&bag->mu);
     return;
   }
 
-  if (fd->polling_island == pollset->polling_island) {
-    pi_new = fd->polling_island;
+  if (item->pi == bag->pi) {
+    pi_new = item->pi;
     if (pi_new == NULL) {
-      /* Unlock before creating a new polling island: the polling island will
-         create a workqueue which creates a file descriptor, and holding an fd
-         lock here can eventually cause a loop to appear to TSAN (making it
-         unhappy). We don't think it's a real loop (there's an epoch point where
-         that loop possibility disappears), but the advantages of keeping TSAN
-         happy outweigh any performance advantage we might have by keeping the
-         lock held. */
-      gpr_mu_unlock(&fd->mu);
-      pi_new = polling_island_create(exec_ctx, fd, &error);
-      gpr_mu_lock(&fd->mu);
-      /* Need to reverify any assumptions made between the initial lock and
-         getting to this branch: if they've changed, we need to throw away our
-         work and figure things out again. */
-      if (fd->polling_island != NULL) {
-        GRPC_POLLING_TRACE(
-            "pollset_add_fd: Raced creating new polling island. pi_new: %p "
-            "(fd: %d, pollset: %p)",
-            (void *)pi_new, fd->fd, (void *)pollset);
-
-        /* No need to lock 'pi_new' here since this is a new polling island and
-         * no one has a reference to it yet */
-        polling_island_remove_all_fds_locked(pi_new, true, &error);
-
-        /* Ref and unref so that the polling island gets deleted during unref */
-        PI_ADD_REF(pi_new, "dance_of_destruction");
-        PI_UNREF(exec_ctx, pi_new, "dance_of_destruction");
-        goto retry;
+      /* GPR_ASSERT(item->pi == bag->pi == NULL) */
+
+      /* If we are adding an fd to a bag (i.e pollset or pollset_set), then
+       * we need to do some extra work to make TSAN happy */
+      if (item_type == POLL_OBJ_FD) {
+        /* Unlock before creating a new polling island: the polling island will
+           create a workqueue which creates a file descriptor, and holding an fd
+           lock here can eventually cause a loop to appear to TSAN (making it
+           unhappy). We don't think it's a real loop (there's an epoch point
+           where that loop possibility disappears), but the advantages of
+           keeping TSAN happy outweigh any performance advantage we might have
+           by keeping the lock held. */
+        gpr_mu_unlock(&item->mu);
+        pi_new = polling_island_create(exec_ctx, FD_FROM_PO(item), &error);
+        gpr_mu_lock(&item->mu);
+
+        /* Need to reverify any assumptions made between the initial lock and
+           getting to this branch: if they've changed, we need to throw away our
+           work and figure things out again. */
+        if (item->pi != NULL) {
+          GRPC_POLLING_TRACE(
+              "add_poll_object: Raced creating new polling island. pi_new: %p "
+              "(fd: %d, %s: %p)",
+              (void *)pi_new, FD_FROM_PO(item)->fd, poll_obj_string(bag_type),
+              (void *)bag);
+          /* No need to lock 'pi_new' here since this is a new polling island
+            * and no one has a reference to it yet */
+          polling_island_remove_all_fds_locked(pi_new, true, &error);
+
+          /* Ref and unref so that the polling island gets deleted during unref
+           */
+          PI_ADD_REF(pi_new, "dance_of_destruction");
+          PI_UNREF(exec_ctx, pi_new, "dance_of_destruction");
+          goto retry;
+        }
       } else {
-        GRPC_POLLING_TRACE(
-            "pollset_add_fd: Created new polling island. pi_new: %p (fd: %d, "
-            "pollset: %p)",
-            (void *)pi_new, fd->fd, (void *)pollset);
+        pi_new = polling_island_create(exec_ctx, NULL, &error);
       }
+
+      GRPC_POLLING_TRACE(
+          "add_poll_object: Created new polling island. pi_new: %p (%s: %p, "
+          "%s: %p)",
+          (void *)pi_new, poll_obj_string(item_type), (void *)item,
+          poll_obj_string(bag_type), (void *)bag);
+    } else {
+      GRPC_POLLING_TRACE(
+          "add_poll_object: Same polling island. pi: %p (%s, %s)",
+          (void *)pi_new, poll_obj_string(item_type),
+          poll_obj_string(bag_type));
+    }
+  } else if (item->pi == NULL) {
+    /* GPR_ASSERT(bag->pi != NULL) */
+    /* Make pi_new point to latest pi*/
+    pi_new = polling_island_lock(bag->pi);
+
+    if (item_type == POLL_OBJ_FD) {
+      grpc_fd *fd = FD_FROM_PO(item);
+      polling_island_add_fds_locked(pi_new, &fd, 1, true, &error);
     }
-  } else if (fd->polling_island == NULL) {
-    pi_new = polling_island_lock(pollset->polling_island);
-    polling_island_add_fds_locked(pi_new, &fd, 1, true, &error);
-    gpr_mu_unlock(&pi_new->mu);
 
+    gpr_mu_unlock(&pi_new->mu);
     GRPC_POLLING_TRACE(
-        "pollset_add_fd: fd->pi was NULL. pi_new: %p (fd: %d, pollset: %p, "
-        "pollset->pi: %p)",
-        (void *)pi_new, fd->fd, (void *)pollset,
-        (void *)pollset->polling_island);
-  } else if (pollset->polling_island == NULL) {
-    pi_new = polling_island_lock(fd->polling_island);
+        "add_poll_obj: item->pi was NULL. pi_new: %p (item(%s): %p, "
+        "bag(%s): %p)",
+        (void *)pi_new, poll_obj_string(item_type), (void *)item,
+        poll_obj_string(bag_type), (void *)bag);
+  } else if (bag->pi == NULL) {
+    /* GPR_ASSERT(item->pi != NULL) */
+    /* Make pi_new to point to latest pi */
+    pi_new = polling_island_lock(item->pi);
     gpr_mu_unlock(&pi_new->mu);
-
     GRPC_POLLING_TRACE(
-        "pollset_add_fd: pollset->pi was NULL. pi_new: %p (fd: %d, pollset: "
-        "%p, fd->pi: %p",
-        (void *)pi_new, fd->fd, (void *)pollset, (void *)fd->polling_island);
+        "add_poll_obj: bag->pi was NULL. pi_new: %p (item(%s): %p, "
+        "bag(%s): %p)",
+        (void *)pi_new, poll_obj_string(item_type), (void *)item,
+        poll_obj_string(bag_type), (void *)bag);
   } else {
-    pi_new = polling_island_merge(fd->polling_island, pollset->polling_island,
-                                  &error);
+    pi_new = polling_island_merge(item->pi, bag->pi, &error);
     GRPC_POLLING_TRACE(
-        "pollset_add_fd: polling islands merged. pi_new: %p (fd: %d, pollset: "
-        "%p, fd->pi: %p, pollset->pi: %p)",
-        (void *)pi_new, fd->fd, (void *)pollset, (void *)fd->polling_island,
-        (void *)pollset->polling_island);
+        "add_poll_obj: polling islands merged. pi_new: %p (item(%s): %p, "
+        "bag(%s): %p)",
+        (void *)pi_new, poll_obj_string(item_type), (void *)item,
+        poll_obj_string(bag_type), (void *)bag);
   }
 
-  /* At this point, pi_new is the polling island that both fd->polling_island
-     and pollset->polling_island must be pointing to */
+  /* At this point, pi_new is the polling island that both item->pi and bag->pi
+     MUST be pointing to */
 
-  if (fd->polling_island != pi_new) {
-    PI_ADD_REF(pi_new, "fd");
-    if (fd->polling_island != NULL) {
-      PI_UNREF(exec_ctx, fd->polling_island, "fd");
+  if (item->pi != pi_new) {
+    PI_ADD_REF(pi_new, poll_obj_string(item_type));
+    if (item->pi != NULL) {
+      PI_UNREF(exec_ctx, item->pi, poll_obj_string(item_type));
     }
-    fd->polling_island = pi_new;
+    item->pi = pi_new;
   }
 
-  if (pollset->polling_island != pi_new) {
-    PI_ADD_REF(pi_new, "ps");
-    if (pollset->polling_island != NULL) {
-      PI_UNREF(exec_ctx, pollset->polling_island, "ps");
+  if (bag->pi != pi_new) {
+    PI_ADD_REF(pi_new, poll_obj_string(bag_type));
+    if (bag->pi != NULL) {
+      PI_UNREF(exec_ctx, bag->pi, poll_obj_string(bag_type));
     }
-    pollset->polling_island = pi_new;
+    bag->pi = pi_new;
   }
 
-  gpr_mu_unlock(&fd->mu);
-  gpr_mu_unlock(&pollset->mu);
+  gpr_mu_unlock(&item->mu);
+  gpr_mu_unlock(&bag->mu);
 
-  GRPC_LOG_IF_ERROR("pollset_add_fd", error);
+  GRPC_LOG_IF_ERROR("add_poll_object", error);
+  GPR_TIMER_END("add_poll_object", 0);
+}
 
-  GPR_TIMER_END("pollset_add_fd", 0);
+static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+                           grpc_fd *fd) {
+  add_poll_object(exec_ctx, &pollset->po, POLL_OBJ_POLLSET, &fd->po,
+                  POLL_OBJ_FD);
 }
 
 /*******************************************************************************
@@ -1784,142 +1837,60 @@ retry:
  */
 
 static grpc_pollset_set *pollset_set_create(void) {
-  grpc_pollset_set *pollset_set = gpr_malloc(sizeof(*pollset_set));
-  memset(pollset_set, 0, sizeof(*pollset_set));
-  gpr_mu_init(&pollset_set->mu);
-  return pollset_set;
+  grpc_pollset_set *pss = gpr_malloc(sizeof(*pss));
+  gpr_mu_init(&pss->po.mu);
+  pss->po.pi = NULL;
+#ifdef PO_DEBUG
+  pss->po.obj_type = POLL_OBJ_POLLSET_SET;
+#endif
+  return pss;
 }
 
-static void pollset_set_destroy(grpc_pollset_set *pollset_set) {
-  size_t i;
-  gpr_mu_destroy(&pollset_set->mu);
-  for (i = 0; i < pollset_set->fd_count; i++) {
-    GRPC_FD_UNREF(pollset_set->fds[i], "pollset_set");
+static void pollset_set_destroy(grpc_pollset_set *pss) {
+  gpr_mu_destroy(&pss->po.mu);
+
+  if (pss->po.pi != NULL) {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    PI_UNREF(&exec_ctx, pss->po.pi, "pss_destroy");
+    grpc_exec_ctx_finish(&exec_ctx);
   }
-  gpr_free(pollset_set->pollsets);
-  gpr_free(pollset_set->pollset_sets);
-  gpr_free(pollset_set->fds);
-  gpr_free(pollset_set);
+
+  gpr_free(pss);
 }
 
-static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx,
-                               grpc_pollset_set *pollset_set, grpc_fd *fd) {
-  size_t i;
-  gpr_mu_lock(&pollset_set->mu);
-  if (pollset_set->fd_count == pollset_set->fd_capacity) {
-    pollset_set->fd_capacity = GPR_MAX(8, 2 * pollset_set->fd_capacity);
-    pollset_set->fds = gpr_realloc(
-        pollset_set->fds, pollset_set->fd_capacity * sizeof(*pollset_set->fds));
-  }
-  GRPC_FD_REF(fd, "pollset_set");
-  pollset_set->fds[pollset_set->fd_count++] = fd;
-  for (i = 0; i < pollset_set->pollset_count; i++) {
-    pollset_add_fd(exec_ctx, pollset_set->pollsets[i], fd);
-  }
-  for (i = 0; i < pollset_set->pollset_set_count; i++) {
-    pollset_set_add_fd(exec_ctx, pollset_set->pollset_sets[i], fd);
-  }
-  gpr_mu_unlock(&pollset_set->mu);
+static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss,
+                               grpc_fd *fd) {
+  add_poll_object(exec_ctx, &pss->po, POLL_OBJ_POLLSET_SET, &fd->po,
+                  POLL_OBJ_FD);
 }
 
-static void pollset_set_del_fd(grpc_exec_ctx *exec_ctx,
-                               grpc_pollset_set *pollset_set, grpc_fd *fd) {
-  size_t i;
-  gpr_mu_lock(&pollset_set->mu);
-  for (i = 0; i < pollset_set->fd_count; i++) {
-    if (pollset_set->fds[i] == fd) {
-      pollset_set->fd_count--;
-      GPR_SWAP(grpc_fd *, pollset_set->fds[i],
-               pollset_set->fds[pollset_set->fd_count]);
-      GRPC_FD_UNREF(fd, "pollset_set");
-      break;
-    }
-  }
-  for (i = 0; i < pollset_set->pollset_set_count; i++) {
-    pollset_set_del_fd(exec_ctx, pollset_set->pollset_sets[i], fd);
-  }
-  gpr_mu_unlock(&pollset_set->mu);
+static void pollset_set_del_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss,
+                               grpc_fd *fd) {
+  /* Nothing to do */
 }
 
 static void pollset_set_add_pollset(grpc_exec_ctx *exec_ctx,
-                                    grpc_pollset_set *pollset_set,
-                                    grpc_pollset *pollset) {
-  size_t i, j;
-  gpr_mu_lock(&pollset_set->mu);
-  if (pollset_set->pollset_count == pollset_set->pollset_capacity) {
-    pollset_set->pollset_capacity =
-        GPR_MAX(8, 2 * pollset_set->pollset_capacity);
-    pollset_set->pollsets =
-        gpr_realloc(pollset_set->pollsets, pollset_set->pollset_capacity *
-                                               sizeof(*pollset_set->pollsets));
-  }
-  pollset_set->pollsets[pollset_set->pollset_count++] = pollset;
-  for (i = 0, j = 0; i < pollset_set->fd_count; i++) {
-    if (fd_is_orphaned(pollset_set->fds[i])) {
-      GRPC_FD_UNREF(pollset_set->fds[i], "pollset_set");
-    } else {
-      pollset_add_fd(exec_ctx, pollset, pollset_set->fds[i]);
-      pollset_set->fds[j++] = pollset_set->fds[i];
-    }
-  }
-  pollset_set->fd_count = j;
-  gpr_mu_unlock(&pollset_set->mu);
+                                    grpc_pollset_set *pss, grpc_pollset *ps) {
+  add_poll_object(exec_ctx, &pss->po, POLL_OBJ_POLLSET_SET, &ps->po,
+                  POLL_OBJ_POLLSET);
 }
 
 static void pollset_set_del_pollset(grpc_exec_ctx *exec_ctx,
-                                    grpc_pollset_set *pollset_set,
-                                    grpc_pollset *pollset) {
-  size_t i;
-  gpr_mu_lock(&pollset_set->mu);
-  for (i = 0; i < pollset_set->pollset_count; i++) {
-    if (pollset_set->pollsets[i] == pollset) {
-      pollset_set->pollset_count--;
-      GPR_SWAP(grpc_pollset *, pollset_set->pollsets[i],
-               pollset_set->pollsets[pollset_set->pollset_count]);
-      break;
-    }
-  }
-  gpr_mu_unlock(&pollset_set->mu);
+                                    grpc_pollset_set *pss, grpc_pollset *ps) {
+  /* Nothing to do */
 }
 
 static void pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx,
                                         grpc_pollset_set *bag,
                                         grpc_pollset_set *item) {
-  size_t i, j;
-  gpr_mu_lock(&bag->mu);
-  if (bag->pollset_set_count == bag->pollset_set_capacity) {
-    bag->pollset_set_capacity = GPR_MAX(8, 2 * bag->pollset_set_capacity);
-    bag->pollset_sets =
-        gpr_realloc(bag->pollset_sets,
-                    bag->pollset_set_capacity * sizeof(*bag->pollset_sets));
-  }
-  bag->pollset_sets[bag->pollset_set_count++] = item;
-  for (i = 0, j = 0; i < bag->fd_count; i++) {
-    if (fd_is_orphaned(bag->fds[i])) {
-      GRPC_FD_UNREF(bag->fds[i], "pollset_set");
-    } else {
-      pollset_set_add_fd(exec_ctx, item, bag->fds[i]);
-      bag->fds[j++] = bag->fds[i];
-    }
-  }
-  bag->fd_count = j;
-  gpr_mu_unlock(&bag->mu);
+  add_poll_object(exec_ctx, &bag->po, POLL_OBJ_POLLSET_SET, &item->po,
+                  POLL_OBJ_POLLSET_SET);
 }
 
 static void pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx,
                                         grpc_pollset_set *bag,
                                         grpc_pollset_set *item) {
-  size_t i;
-  gpr_mu_lock(&bag->mu);
-  for (i = 0; i < bag->pollset_set_count; i++) {
-    if (bag->pollset_sets[i] == item) {
-      bag->pollset_set_count--;
-      GPR_SWAP(grpc_pollset_set *, bag->pollset_sets[i],
-               bag->pollset_sets[bag->pollset_set_count]);
-      break;
-    }
-  }
-  gpr_mu_unlock(&bag->mu);
+  /* Nothing to do */
 }
 
 /* Test helper functions
@@ -1927,9 +1898,9 @@ static void pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx,
 void *grpc_fd_get_polling_island(grpc_fd *fd) {
   polling_island *pi;
 
-  gpr_mu_lock(&fd->mu);
-  pi = fd->polling_island;
-  gpr_mu_unlock(&fd->mu);
+  gpr_mu_lock(&fd->po.mu);
+  pi = fd->po.pi;
+  gpr_mu_unlock(&fd->po.mu);
 
   return pi;
 }
@@ -1937,9 +1908,9 @@ void *grpc_fd_get_polling_island(grpc_fd *fd) {
 void *grpc_pollset_get_polling_island(grpc_pollset *ps) {
   polling_island *pi;
 
-  gpr_mu_lock(&ps->mu);
-  pi = ps->polling_island;
-  gpr_mu_unlock(&ps->mu);
+  gpr_mu_lock(&ps->po.mu);
+  pi = ps->po.pi;
+  gpr_mu_unlock(&ps->po.mu);
 
   return pi;
 }
@@ -2000,7 +1971,7 @@ static const grpc_event_engine_vtable vtable = {
 
     .workqueue_ref = workqueue_ref,
     .workqueue_unref = workqueue_unref,
-    .workqueue_enqueue = workqueue_enqueue,
+    .workqueue_scheduler = workqueue_scheduler,
 
     .shutdown_engine = shutdown_engine,
 };
diff --git a/src/core/lib/iomgr/ev_poll_posix.c b/src/core/lib/iomgr/ev_poll_posix.c
index 21b28e5554e9c23a3f87e0d29b5101fcf8e50294..5bc56214434e4c98ed67f6daeb69c2248a9685f7 100644
--- a/src/core/lib/iomgr/ev_poll_posix.c
+++ b/src/core/lib/iomgr/ev_poll_posix.c
@@ -397,7 +397,7 @@ static void close_fd_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd) {
   if (!fd->released) {
     close(fd->fd);
   }
-  grpc_exec_ctx_sched(exec_ctx, fd->on_done_closure, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, fd->on_done_closure, GRPC_ERROR_NONE);
 }
 
 static int fd_wrapped_fd(grpc_fd *fd) {
@@ -457,16 +457,14 @@ static grpc_error *fd_shutdown_error(bool shutdown) {
 static void notify_on_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
                              grpc_closure **st, grpc_closure *closure) {
   if (fd->shutdown) {
-    grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_CREATE("FD shutdown"),
-                        NULL);
+    grpc_closure_sched(exec_ctx, closure, GRPC_ERROR_CREATE("FD shutdown"));
   } else if (*st == CLOSURE_NOT_READY) {
     /* not ready ==> switch to a waiting state by setting the closure */
     *st = closure;
   } else if (*st == CLOSURE_READY) {
     /* already ready ==> queue the closure to run immediately */
     *st = CLOSURE_NOT_READY;
-    grpc_exec_ctx_sched(exec_ctx, closure, fd_shutdown_error(fd->shutdown),
-                        NULL);
+    grpc_closure_sched(exec_ctx, closure, fd_shutdown_error(fd->shutdown));
     maybe_wake_one_watcher_locked(fd);
   } else {
     /* upcallptr was set to a different closure.  This is an error! */
@@ -489,7 +487,7 @@ static int set_ready_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
     return 0;
   } else {
     /* waiting ==> queue closure */
-    grpc_exec_ctx_sched(exec_ctx, *st, fd_shutdown_error(fd->shutdown), NULL);
+    grpc_closure_sched(exec_ctx, *st, fd_shutdown_error(fd->shutdown));
     *st = CLOSURE_NOT_READY;
     return 1;
   }
@@ -852,7 +850,7 @@ static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) {
     GRPC_FD_UNREF(pollset->fds[i], "multipoller");
   }
   pollset->fd_count = 0;
-  grpc_exec_ctx_sched(exec_ctx, pollset->shutdown_done, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, pollset->shutdown_done, GRPC_ERROR_NONE);
 }
 
 static void work_combine_error(grpc_error **composite, grpc_error *error) {
@@ -901,7 +899,7 @@ static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
   if (!pollset_has_workers(pollset) &&
       !grpc_closure_list_empty(pollset->idle_jobs)) {
     GPR_TIMER_MARK("pollset_work.idle_jobs", 0);
-    grpc_exec_ctx_enqueue_list(exec_ctx, &pollset->idle_jobs, NULL);
+    grpc_closure_list_sched(exec_ctx, &pollset->idle_jobs);
     goto done;
   }
   /* If we're shutting down then we don't execute any extended work */
@@ -1081,7 +1079,7 @@ static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
        * TODO(dklempner): Can we refactor the shutdown logic to avoid this? */
       gpr_mu_lock(&pollset->mu);
     } else if (!grpc_closure_list_empty(pollset->idle_jobs)) {
-      grpc_exec_ctx_enqueue_list(exec_ctx, &pollset->idle_jobs, NULL);
+      grpc_closure_list_sched(exec_ctx, &pollset->idle_jobs);
       gpr_mu_unlock(&pollset->mu);
       grpc_exec_ctx_flush(exec_ctx);
       gpr_mu_lock(&pollset->mu);
@@ -1100,7 +1098,7 @@ static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
   pollset->shutdown_done = closure;
   pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST);
   if (!pollset_has_workers(pollset)) {
-    grpc_exec_ctx_enqueue_list(exec_ctx, &pollset->idle_jobs, NULL);
+    grpc_closure_list_sched(exec_ctx, &pollset->idle_jobs);
   }
   if (!pollset->called_shutdown && !pollset_has_workers(pollset)) {
     pollset->called_shutdown = 1;
@@ -1288,10 +1286,8 @@ static void workqueue_unref(grpc_exec_ctx *exec_ctx,
                             grpc_workqueue *workqueue) {}
 #endif
 
-static void workqueue_enqueue(grpc_exec_ctx *exec_ctx,
-                              grpc_workqueue *workqueue, grpc_closure *closure,
-                              grpc_error *error) {
-  grpc_exec_ctx_sched(exec_ctx, closure, error, NULL);
+static grpc_closure_scheduler *workqueue_scheduler(grpc_workqueue *workqueue) {
+  return grpc_schedule_on_exec_ctx;
 }
 
 /*******************************************************************************
@@ -1534,7 +1530,7 @@ static const grpc_event_engine_vtable vtable = {
 
     .workqueue_ref = workqueue_ref,
     .workqueue_unref = workqueue_unref,
-    .workqueue_enqueue = workqueue_enqueue,
+    .workqueue_scheduler = workqueue_scheduler,
 
     .shutdown_engine = shutdown_engine,
 };
diff --git a/src/core/lib/iomgr/ev_posix.c b/src/core/lib/iomgr/ev_posix.c
index ab139895fdc873a7271026a45c839cbf44adc171..2975d619e18cb1869b2c665d897ad8f7fa5f3674 100644
--- a/src/core/lib/iomgr/ev_posix.c
+++ b/src/core/lib/iomgr/ev_posix.c
@@ -275,9 +275,8 @@ void grpc_workqueue_unref(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue) {
 }
 #endif
 
-void grpc_workqueue_enqueue(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue,
-                            grpc_closure *closure, grpc_error *error) {
-  g_event_engine->workqueue_enqueue(exec_ctx, workqueue, closure, error);
+grpc_closure_scheduler *grpc_workqueue_scheduler(grpc_workqueue *workqueue) {
+  return g_event_engine->workqueue_scheduler(workqueue);
 }
 
 #endif  // GRPC_POSIX_SOCKET
diff --git a/src/core/lib/iomgr/ev_posix.h b/src/core/lib/iomgr/ev_posix.h
index cb5832539da421b8abe0810f4bc3c314c442b4ef..1068a4bad519f81cff6e3a17ecab2711527cc948 100644
--- a/src/core/lib/iomgr/ev_posix.h
+++ b/src/core/lib/iomgr/ev_posix.h
@@ -106,8 +106,7 @@ typedef struct grpc_event_engine_vtable {
   grpc_workqueue *(*workqueue_ref)(grpc_workqueue *workqueue);
   void (*workqueue_unref)(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue);
 #endif
-  void (*workqueue_enqueue)(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue,
-                            grpc_closure *closure, grpc_error *error);
+  grpc_closure_scheduler *(*workqueue_scheduler)(grpc_workqueue *workqueue);
 } grpc_event_engine_vtable;
 
 void grpc_event_engine_init(void);
diff --git a/src/core/lib/iomgr/exec_ctx.c b/src/core/lib/iomgr/exec_ctx.c
index 604713e578e6d77ed6327f5e414ad3194e3ef753..6aa788f8e5f981f4af6259b12843ca5f2925be2d 100644
--- a/src/core/lib/iomgr/exec_ctx.c
+++ b/src/core/lib/iomgr/exec_ctx.c
@@ -57,7 +57,6 @@ bool grpc_always_ready_to_finish(grpc_exec_ctx *exec_ctx, void *arg_ignored) {
   return true;
 }
 
-#ifndef GRPC_EXECUTION_CONTEXT_SANITIZER
 bool grpc_exec_ctx_flush(grpc_exec_ctx *exec_ctx) {
   bool did_something = 0;
   GPR_TIMER_BEGIN("grpc_exec_ctx_flush", 0);
@@ -67,8 +66,10 @@ bool grpc_exec_ctx_flush(grpc_exec_ctx *exec_ctx) {
       exec_ctx->closure_list.head = exec_ctx->closure_list.tail = NULL;
       while (c != NULL) {
         grpc_closure *next = c->next_data.next;
+        grpc_error *error = c->error_data.error;
         did_something = true;
-        grpc_closure_run(exec_ctx, c, c->error_data.error);
+        c->cb(exec_ctx, c->cb_arg, error);
+        GRPC_ERROR_UNREF(error);
         c = next;
       }
     } else if (!grpc_combiner_continue_exec_ctx(exec_ctx)) {
@@ -76,30 +77,6 @@ bool grpc_exec_ctx_flush(grpc_exec_ctx *exec_ctx) {
     }
   }
   GPR_ASSERT(exec_ctx->active_combiner == NULL);
-  if (exec_ctx->stealing_from_workqueue != NULL) {
-    if (grpc_exec_ctx_ready_to_finish(exec_ctx)) {
-      grpc_workqueue_enqueue(exec_ctx, exec_ctx->stealing_from_workqueue,
-                             exec_ctx->stolen_closure,
-                             exec_ctx->stolen_closure->error_data.error);
-      GRPC_WORKQUEUE_UNREF(exec_ctx, exec_ctx->stealing_from_workqueue,
-                           "exec_ctx_sched");
-      exec_ctx->stealing_from_workqueue = NULL;
-      exec_ctx->stolen_closure = NULL;
-    } else {
-      grpc_closure *c = exec_ctx->stolen_closure;
-      GRPC_WORKQUEUE_UNREF(exec_ctx, exec_ctx->stealing_from_workqueue,
-                           "exec_ctx_sched");
-      exec_ctx->stealing_from_workqueue = NULL;
-      exec_ctx->stolen_closure = NULL;
-      grpc_error *error = c->error_data.error;
-      GPR_TIMER_BEGIN("grpc_exec_ctx_flush.stolen_cb", 0);
-      c->cb(exec_ctx, c->cb_arg, error);
-      GRPC_ERROR_UNREF(error);
-      GPR_TIMER_END("grpc_exec_ctx_flush.stolen_cb", 0);
-      grpc_exec_ctx_flush(exec_ctx);
-      did_something = true;
-    }
-  }
   GPR_TIMER_END("grpc_exec_ctx_flush", 0);
   return did_something;
 }
@@ -109,104 +86,21 @@ void grpc_exec_ctx_finish(grpc_exec_ctx *exec_ctx) {
   grpc_exec_ctx_flush(exec_ctx);
 }
 
-void grpc_exec_ctx_sched(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
-                         grpc_error *error,
-                         grpc_workqueue *offload_target_or_null) {
-  GPR_TIMER_BEGIN("grpc_exec_ctx_sched", 0);
-  if (offload_target_or_null == NULL) {
-    grpc_closure_list_append(&exec_ctx->closure_list, closure, error);
-  } else if (exec_ctx->stealing_from_workqueue == NULL) {
-    exec_ctx->stealing_from_workqueue = offload_target_or_null;
-    closure->error_data.error = error;
-    exec_ctx->stolen_closure = closure;
-  } else if (exec_ctx->stealing_from_workqueue != offload_target_or_null) {
-    grpc_workqueue_enqueue(exec_ctx, offload_target_or_null, closure, error);
-    GRPC_WORKQUEUE_UNREF(exec_ctx, offload_target_or_null, "exec_ctx_sched");
-  } else { /* stealing_from_workqueue == offload_target_or_null */
-    grpc_workqueue_enqueue(exec_ctx, offload_target_or_null,
-                           exec_ctx->stolen_closure,
-                           exec_ctx->stolen_closure->error_data.error);
-    closure->error_data.error = error;
-    exec_ctx->stolen_closure = closure;
-    GRPC_WORKQUEUE_UNREF(exec_ctx, offload_target_or_null, "exec_ctx_sched");
-  }
-  GPR_TIMER_END("grpc_exec_ctx_sched", 0);
+static void exec_ctx_run(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
+                         grpc_error *error) {
+  closure->cb(exec_ctx, closure->cb_arg, error);
+  GRPC_ERROR_UNREF(error);
 }
 
-void grpc_exec_ctx_enqueue_list(grpc_exec_ctx *exec_ctx,
-                                grpc_closure_list *list,
-                                grpc_workqueue *offload_target_or_null) {
-  grpc_closure_list_move(list, &exec_ctx->closure_list);
+static void exec_ctx_sched(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
+                           grpc_error *error) {
+  grpc_closure_list_append(&exec_ctx->closure_list, closure, error);
 }
 
 void grpc_exec_ctx_global_init(void) {}
 void grpc_exec_ctx_global_shutdown(void) {}
-#else
-static gpr_mu g_mu;
-static gpr_cv g_cv;
-static int g_threads = 0;
-
-static void run_closure(void *arg) {
-  grpc_closure *closure = arg;
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  closure->cb(&exec_ctx, closure->cb_arg, (closure->final_data & 1) != 0);
-  grpc_exec_ctx_finish(&exec_ctx);
-  gpr_mu_lock(&g_mu);
-  if (--g_threads == 0) {
-    gpr_cv_signal(&g_cv);
-  }
-  gpr_mu_unlock(&g_mu);
-}
-
-static void start_closure(grpc_closure *closure) {
-  gpr_thd_id id;
-  gpr_mu_lock(&g_mu);
-  g_threads++;
-  gpr_mu_unlock(&g_mu);
-  gpr_thd_new(&id, run_closure, closure, NULL);
-}
-
-bool grpc_exec_ctx_flush(grpc_exec_ctx *exec_ctx) { return false; }
-
-void grpc_exec_ctx_finish(grpc_exec_ctx *exec_ctx) {}
 
-void grpc_exec_ctx_enqueue(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
-                           bool success,
-                           grpc_workqueue *offload_target_or_null) {
-  GPR_ASSERT(offload_target_or_null == NULL);
-  if (closure == NULL) return;
-  closure->final_data = success;
-  start_closure(closure);
-}
-
-void grpc_exec_ctx_enqueue_list(grpc_exec_ctx *exec_ctx,
-                                grpc_closure_list *list,
-                                grpc_workqueue *offload_target_or_null) {
-  GPR_ASSERT(offload_target_or_null == NULL);
-  if (list == NULL) return;
-  grpc_closure *p = list->head;
-  while (p) {
-    grpc_closure *start = p;
-    p = grpc_closure_next(start);
-    start_closure(start);
-  }
-  grpc_closure_list r = GRPC_CLOSURE_LIST_INIT;
-  *list = r;
-}
-
-void grpc_exec_ctx_global_init(void) {
-  gpr_mu_init(&g_mu);
-  gpr_cv_init(&g_cv);
-}
-
-void grpc_exec_ctx_global_shutdown(void) {
-  gpr_mu_lock(&g_mu);
-  while (g_threads != 0) {
-    gpr_cv_wait(&g_cv, &g_mu, gpr_inf_future(GPR_CLOCK_REALTIME));
-  }
-  gpr_mu_unlock(&g_mu);
-
-  gpr_mu_destroy(&g_mu);
-  gpr_cv_destroy(&g_cv);
-}
-#endif
+static const grpc_closure_scheduler_vtable exec_ctx_scheduler_vtable = {
+    exec_ctx_run, exec_ctx_sched};
+static grpc_closure_scheduler exec_ctx_scheduler = {&exec_ctx_scheduler_vtable};
+grpc_closure_scheduler *grpc_schedule_on_exec_ctx = &exec_ctx_scheduler;
diff --git a/src/core/lib/iomgr/exec_ctx.h b/src/core/lib/iomgr/exec_ctx.h
index 7e50cb9825d1497837ae1c12f52379062e958037..e566f1b3e89318059e20a2aefd6be085a5005df1 100644
--- a/src/core/lib/iomgr/exec_ctx.h
+++ b/src/core/lib/iomgr/exec_ctx.h
@@ -66,17 +66,6 @@ typedef struct grpc_combiner grpc_combiner;
 #ifndef GRPC_EXECUTION_CONTEXT_SANITIZER
 struct grpc_exec_ctx {
   grpc_closure_list closure_list;
-  /** The workqueue we're stealing work from.
-      As items are queued to the execution context, we try to steal one
-      workqueue item and execute it inline (assuming the exec_ctx is not
-      finished) - doing so does not invalidate the workqueue's contract, and
-      provides a small latency win in cases where we get a hit */
-  grpc_workqueue *stealing_from_workqueue;
-  /** The workqueue item that was stolen from the workqueue above. When new
-      items are scheduled to be offloaded to that workqueue, we need to update
-      this like a 1-deep fifo to maintain the invariant that workqueue items
-      queued by one thread are started in order */
-  grpc_closure *stolen_closure;
   /** currently active combiner: updated only via combiner.c */
   grpc_combiner *active_combiner;
   /** last active combiner in the active combiner list */
@@ -89,10 +78,7 @@ struct grpc_exec_ctx {
 /* initializer for grpc_exec_ctx:
    prefer to use GRPC_EXEC_CTX_INIT whenever possible */
 #define GRPC_EXEC_CTX_INIT_WITH_FINISH_CHECK(finish_check, finish_check_arg) \
-  {                                                                          \
-    GRPC_CLOSURE_LIST_INIT, NULL, NULL, NULL, NULL, false, finish_check_arg, \
-        finish_check                                                         \
-  }
+  { GRPC_CLOSURE_LIST_INIT, NULL, NULL, false, finish_check_arg, finish_check }
 #else
 struct grpc_exec_ctx {
   bool cached_ready_to_finish;
@@ -108,6 +94,8 @@ struct grpc_exec_ctx {
 #define GRPC_EXEC_CTX_INIT \
   GRPC_EXEC_CTX_INIT_WITH_FINISH_CHECK(grpc_always_ready_to_finish, NULL)
 
+extern grpc_closure_scheduler *grpc_schedule_on_exec_ctx;
+
 /** Flush any work that has been enqueued onto this grpc_exec_ctx.
  *  Caller must guarantee that no interfering locks are held.
  *  Returns true if work was performed, false otherwise. */
@@ -115,14 +103,6 @@ bool grpc_exec_ctx_flush(grpc_exec_ctx *exec_ctx);
 /** Finish any pending work for a grpc_exec_ctx. Must be called before
  *  the instance is destroyed, or work may be lost. */
 void grpc_exec_ctx_finish(grpc_exec_ctx *exec_ctx);
-/** Add a closure to be executed in the future.
-    If \a offload_target_or_null is NULL, the closure will be executed at the
-    next exec_ctx.{finish,flush} point.
-    If \a offload_target_or_null is non-NULL, the closure will be scheduled
-    against the workqueue, and a reference to the workqueue will be consumed. */
-void grpc_exec_ctx_sched(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
-                         grpc_error *error,
-                         grpc_workqueue *offload_target_or_null);
 /** Returns true if we'd like to leave this execution context as soon as
     possible: useful for deciding whether to do something more or not depending
     on outside context */
@@ -131,11 +111,6 @@ bool grpc_exec_ctx_ready_to_finish(grpc_exec_ctx *exec_ctx);
 bool grpc_never_ready_to_finish(grpc_exec_ctx *exec_ctx, void *arg_ignored);
 /** A finish check that is always ready to finish */
 bool grpc_always_ready_to_finish(grpc_exec_ctx *exec_ctx, void *arg_ignored);
-/** Add a list of closures to be executed at the next flush/finish point.
- *  Leaves \a list empty. */
-void grpc_exec_ctx_enqueue_list(grpc_exec_ctx *exec_ctx,
-                                grpc_closure_list *list,
-                                grpc_workqueue *offload_target_or_null);
 
 void grpc_exec_ctx_global_init(void);
 
diff --git a/src/core/lib/iomgr/executor.c b/src/core/lib/iomgr/executor.c
index 8d7535d6fe52bd30b095c641248de99d7a67465a..852775564f6c95bc8a77c659cbcad3fa28d2178c 100644
--- a/src/core/lib/iomgr/executor.c
+++ b/src/core/lib/iomgr/executor.c
@@ -77,10 +77,18 @@ static void closure_exec_thread_func(void *ignored) {
       gpr_mu_unlock(&g_executor.mu);
       break;
     } else {
-      grpc_exec_ctx_enqueue_list(&exec_ctx, &g_executor.closures, NULL);
+      grpc_closure *c = g_executor.closures.head;
+      grpc_closure_list_init(&g_executor.closures);
+      gpr_mu_unlock(&g_executor.mu);
+      while (c != NULL) {
+        grpc_closure *next = c->next_data.next;
+        grpc_error *error = c->error_data.error;
+        c->cb(&exec_ctx, c->cb_arg, error);
+        GRPC_ERROR_UNREF(error);
+        c = next;
+      }
+      grpc_exec_ctx_flush(&exec_ctx);
     }
-    gpr_mu_unlock(&g_executor.mu);
-    grpc_exec_ctx_flush(&exec_ctx);
   }
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -112,7 +120,8 @@ static void maybe_spawn_locked() {
   g_executor.pending_join = 1;
 }
 
-void grpc_executor_push(grpc_closure *closure, grpc_error *error) {
+static void executor_push(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
+                          grpc_error *error) {
   gpr_mu_lock(&g_executor.mu);
   if (g_executor.shutting_down == 0) {
     grpc_closure_list_append(&g_executor.closures, closure, error);
@@ -121,9 +130,8 @@ void grpc_executor_push(grpc_closure *closure, grpc_error *error) {
   gpr_mu_unlock(&g_executor.mu);
 }
 
-void grpc_executor_shutdown() {
+void grpc_executor_shutdown(grpc_exec_ctx *exec_ctx) {
   int pending_join;
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
   gpr_mu_lock(&g_executor.mu);
   pending_join = g_executor.pending_join;
@@ -133,11 +141,24 @@ void grpc_executor_shutdown() {
    * list below because we aren't accepting new work */
 
   /* Execute pending callbacks, some may be performing cleanups */
-  grpc_exec_ctx_enqueue_list(&exec_ctx, &g_executor.closures, NULL);
-  grpc_exec_ctx_finish(&exec_ctx);
+  grpc_closure *c = g_executor.closures.head;
+  grpc_closure_list_init(&g_executor.closures);
+  while (c != NULL) {
+    grpc_closure *next = c->next_data.next;
+    grpc_error *error = c->error_data.error;
+    c->cb(exec_ctx, c->cb_arg, error);
+    GRPC_ERROR_UNREF(error);
+    c = next;
+  }
+  grpc_exec_ctx_flush(exec_ctx);
   GPR_ASSERT(grpc_closure_list_empty(g_executor.closures));
   if (pending_join) {
     gpr_thd_join(g_executor.tid);
   }
   gpr_mu_destroy(&g_executor.mu);
 }
+
+static const grpc_closure_scheduler_vtable executor_vtable = {executor_push,
+                                                              executor_push};
+static grpc_closure_scheduler executor_scheduler = {&executor_vtable};
+grpc_closure_scheduler *grpc_executor_scheduler = &executor_scheduler;
diff --git a/src/core/lib/iomgr/executor.h b/src/core/lib/iomgr/executor.h
index da9dcd07d02aa1a24a1fc50d63da9c2aa38d01ed..1213016383efdf16c928117c575a6b08dfe83a66 100644
--- a/src/core/lib/iomgr/executor.h
+++ b/src/core/lib/iomgr/executor.h
@@ -43,11 +43,9 @@
  * non-blocking solution available. */
 void grpc_executor_init();
 
-/** Enqueue \a closure for its eventual execution of \a f(arg) on a separate
- * thread */
-void grpc_executor_push(grpc_closure *closure, grpc_error *error);
+extern grpc_closure_scheduler *grpc_executor_scheduler;
 
 /** Shutdown the executor, running all pending work as part of the call */
-void grpc_executor_shutdown();
+void grpc_executor_shutdown(grpc_exec_ctx *exec_ctx);
 
 #endif /* GRPC_CORE_LIB_IOMGR_EXECUTOR_H */
diff --git a/src/core/lib/iomgr/iocp_windows.c b/src/core/lib/iomgr/iocp_windows.c
index 60ebe43676dce3b8c4d2549a3003ec6700a91ecf..f0f4a6ff39096b7dcf19fea7d12010a166fa5537 100644
--- a/src/core/lib/iomgr/iocp_windows.c
+++ b/src/core/lib/iomgr/iocp_windows.c
@@ -80,7 +80,6 @@ grpc_iocp_work_status grpc_iocp_work(grpc_exec_ctx *exec_ctx,
   LPOVERLAPPED overlapped;
   grpc_winsocket *socket;
   grpc_winsocket_callback_info *info;
-  grpc_closure *closure = NULL;
   success = GetQueuedCompletionStatus(
       g_iocp, &bytes, &completion_key, &overlapped,
       deadline_to_millis_timeout(deadline, gpr_now(deadline.clock_type)));
diff --git a/src/core/lib/iomgr/iomgr.c b/src/core/lib/iomgr/iomgr.c
index 3470b5ac815e8e6af829a6265552a78a8028f697..001e528409978d562588e1cc2a2cba32294c5639 100644
--- a/src/core/lib/iomgr/iomgr.c
+++ b/src/core/lib/iomgr/iomgr.c
@@ -83,11 +83,10 @@ static void dump_objects(const char *kind) {
   }
 }
 
-void grpc_iomgr_shutdown(void) {
+void grpc_iomgr_shutdown(grpc_exec_ctx *exec_ctx) {
   gpr_timespec shutdown_deadline = gpr_time_add(
       gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_seconds(10, GPR_TIMESPAN));
   gpr_timespec last_warning_time = gpr_now(GPR_CLOCK_REALTIME);
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
   grpc_iomgr_platform_flush();
 
@@ -104,10 +103,9 @@ void grpc_iomgr_shutdown(void) {
       }
       last_warning_time = gpr_now(GPR_CLOCK_REALTIME);
     }
-    if (grpc_timer_check(&exec_ctx, gpr_inf_future(GPR_CLOCK_MONOTONIC),
-                         NULL)) {
+    if (grpc_timer_check(exec_ctx, gpr_inf_future(GPR_CLOCK_MONOTONIC), NULL)) {
       gpr_mu_unlock(&g_mu);
-      grpc_exec_ctx_flush(&exec_ctx);
+      grpc_exec_ctx_flush(exec_ctx);
       grpc_iomgr_platform_flush();
       gpr_mu_lock(&g_mu);
       continue;
@@ -139,8 +137,8 @@ void grpc_iomgr_shutdown(void) {
   }
   gpr_mu_unlock(&g_mu);
 
-  grpc_timer_list_shutdown(&exec_ctx);
-  grpc_exec_ctx_finish(&exec_ctx);
+  grpc_timer_list_shutdown(exec_ctx);
+  grpc_exec_ctx_flush(exec_ctx);
 
   /* ensure all threads have left g_mu */
   gpr_mu_lock(&g_mu);
diff --git a/src/core/lib/iomgr/iomgr.h b/src/core/lib/iomgr/iomgr.h
index c1cfaf302e872e4f73f90d2913c39c766bb2828a..245a1e08aa2e3e705feadc0fbf25a2e2e11de71b 100644
--- a/src/core/lib/iomgr/iomgr.h
+++ b/src/core/lib/iomgr/iomgr.h
@@ -34,12 +34,14 @@
 #ifndef GRPC_CORE_LIB_IOMGR_IOMGR_H
 #define GRPC_CORE_LIB_IOMGR_IOMGR_H
 
+#include <grpc/impl/codegen/exec_ctx_fwd.h>
 #include "src/core/lib/iomgr/port.h"
 
 /** Initializes the iomgr. */
 void grpc_iomgr_init(void);
 
-/** Signals the intention to shutdown the iomgr. */
-void grpc_iomgr_shutdown(void);
+/** Signals the intention to shutdown the iomgr. Expects to be able to flush
+ * exec_ctx. */
+void grpc_iomgr_shutdown(grpc_exec_ctx *exec_ctx);
 
 #endif /* GRPC_CORE_LIB_IOMGR_IOMGR_H */
diff --git a/src/core/lib/iomgr/pollset_uv.c b/src/core/lib/iomgr/pollset_uv.c
index 3a74b842b6c10780d5fa764786c62e0b6e03f868..ed3edeee9493ca2a8bc69a917b39f2ad818b580f 100644
--- a/src/core/lib/iomgr/pollset_uv.c
+++ b/src/core/lib/iomgr/pollset_uv.c
@@ -83,7 +83,7 @@ void grpc_pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
     // Drain any pending UV callbacks without blocking
     uv_run(uv_default_loop(), UV_RUN_NOWAIT);
   }
-  grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, closure, GRPC_ERROR_NONE);
 }
 
 void grpc_pollset_destroy(grpc_pollset *pollset) {
diff --git a/src/core/lib/iomgr/pollset_windows.c b/src/core/lib/iomgr/pollset_windows.c
index 5540303e49ba4413f67e30fa3476d10463fde25c..2a45e708df6a499c3b4fa9560cbff19b88e2df38 100644
--- a/src/core/lib/iomgr/pollset_windows.c
+++ b/src/core/lib/iomgr/pollset_windows.c
@@ -109,7 +109,7 @@ void grpc_pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
   pollset->shutting_down = 1;
   grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST);
   if (!pollset->is_iocp_worker) {
-    grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, closure, GRPC_ERROR_NONE);
   } else {
     pollset->on_shutdown = closure;
   }
@@ -167,8 +167,7 @@ grpc_error *grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
       }
 
       if (pollset->shutting_down && pollset->on_shutdown != NULL) {
-        grpc_exec_ctx_sched(exec_ctx, pollset->on_shutdown, GRPC_ERROR_NONE,
-                            NULL);
+        grpc_closure_sched(exec_ctx, pollset->on_shutdown, GRPC_ERROR_NONE);
         pollset->on_shutdown = NULL;
       }
       goto done;
diff --git a/src/core/lib/iomgr/resolve_address.h b/src/core/lib/iomgr/resolve_address.h
index 275924448a16f8b217f024b9bd0d559a7c5d334e..e03d16fa4ead33f357de901232fb0ca6dd0d5a85 100644
--- a/src/core/lib/iomgr/resolve_address.h
+++ b/src/core/lib/iomgr/resolve_address.h
@@ -36,6 +36,7 @@
 
 #include <stddef.h>
 #include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/pollset_set.h"
 
 #define GRPC_MAX_SOCKADDR_SIZE 128
 
@@ -54,6 +55,7 @@ typedef struct {
 /* TODO(ctiller): add a timeout here */
 extern void (*grpc_resolve_address)(grpc_exec_ctx *exec_ctx, const char *addr,
                                     const char *default_port,
+                                    grpc_pollset_set *interested_parties,
                                     grpc_closure *on_done,
                                     grpc_resolved_addresses **addresses);
 /* Destroy resolved addresses */
diff --git a/src/core/lib/iomgr/resolve_address_posix.c b/src/core/lib/iomgr/resolve_address_posix.c
index de791b2b67ceaf3a749b8f0c0d049a30628e8fc1..50e470d14916a23d5e2b5ac2f7c16d8b74c78a4b 100644
--- a/src/core/lib/iomgr/resolve_address_posix.c
+++ b/src/core/lib/iomgr/resolve_address_posix.c
@@ -163,10 +163,9 @@ typedef struct {
 static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp,
                               grpc_error *error) {
   request *r = rp;
-  grpc_exec_ctx_sched(
+  grpc_closure_sched(
       exec_ctx, r->on_done,
-      grpc_blocking_resolve_address(r->name, r->default_port, r->addrs_out),
-      NULL);
+      grpc_blocking_resolve_address(r->name, r->default_port, r->addrs_out));
   gpr_free(r->name);
   gpr_free(r->default_port);
   gpr_free(r);
@@ -181,20 +180,22 @@ void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) {
 
 static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name,
                                  const char *default_port,
+                                 grpc_pollset_set *interested_parties,
                                  grpc_closure *on_done,
                                  grpc_resolved_addresses **addrs) {
   request *r = gpr_malloc(sizeof(request));
-  grpc_closure_init(&r->request_closure, do_request_thread, r);
+  grpc_closure_init(&r->request_closure, do_request_thread, r,
+                    grpc_executor_scheduler);
   r->name = gpr_strdup(name);
   r->default_port = gpr_strdup(default_port);
   r->on_done = on_done;
   r->addrs_out = addrs;
-  grpc_executor_push(&r->request_closure, GRPC_ERROR_NONE);
+  grpc_closure_sched(exec_ctx, &r->request_closure, GRPC_ERROR_NONE);
 }
 
-void (*grpc_resolve_address)(grpc_exec_ctx *exec_ctx, const char *name,
-                             const char *default_port, grpc_closure *on_done,
-                             grpc_resolved_addresses **addrs) =
-    resolve_address_impl;
+void (*grpc_resolve_address)(
+    grpc_exec_ctx *exec_ctx, const char *name, const char *default_port,
+    grpc_pollset_set *interested_parties, grpc_closure *on_done,
+    grpc_resolved_addresses **addrs) = resolve_address_impl;
 
 #endif
diff --git a/src/core/lib/iomgr/resolve_address_uv.c b/src/core/lib/iomgr/resolve_address_uv.c
index b8295acfa14ba18128d584beb5262c51b8a82625..9b5f3209f05cf131cfe3247e643b03ba61525660 100644
--- a/src/core/lib/iomgr/resolve_address_uv.c
+++ b/src/core/lib/iomgr/resolve_address_uv.c
@@ -98,7 +98,7 @@ static void getaddrinfo_callback(uv_getaddrinfo_t *req, int status,
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_error *error;
   error = handle_addrinfo_result(status, res, r->addresses);
-  grpc_exec_ctx_sched(&exec_ctx, r->on_done, error, NULL);
+  grpc_closure_sched(&exec_ctx, r->on_done, error);
   grpc_exec_ctx_finish(&exec_ctx);
 
   gpr_free(r->hints);
@@ -181,6 +181,7 @@ void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) {
 
 static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name,
                                  const char *default_port,
+                                 grpc_pollset_set *interested_parties,
                                  grpc_closure *on_done,
                                  grpc_resolved_addresses **addrs) {
   uv_getaddrinfo_t *req;
@@ -192,7 +193,7 @@ static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name,
   int s;
   err = try_split_host_port(name, default_port, &host, &port);
   if (err != GRPC_ERROR_NONE) {
-    grpc_exec_ctx_sched(exec_ctx, on_done, err, NULL);
+    grpc_closure_sched(exec_ctx, on_done, err);
     return;
   }
   r = gpr_malloc(sizeof(request));
@@ -216,16 +217,16 @@ static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name,
     *addrs = NULL;
     err = GRPC_ERROR_CREATE("getaddrinfo failed");
     err = grpc_error_set_str(err, GRPC_ERROR_STR_OS_ERROR, uv_strerror(s));
-    grpc_exec_ctx_sched(exec_ctx, on_done, err, NULL);
+    grpc_closure_sched(exec_ctx, on_done, err);
     gpr_free(r);
     gpr_free(req);
     gpr_free(hints);
   }
 }
 
-void (*grpc_resolve_address)(grpc_exec_ctx *exec_ctx, const char *name,
-                             const char *default_port, grpc_closure *on_done,
-                             grpc_resolved_addresses **addrs) =
-    resolve_address_impl;
+void (*grpc_resolve_address)(
+    grpc_exec_ctx *exec_ctx, const char *name, const char *default_port,
+    grpc_pollset_set *interested_parties, grpc_closure *on_done,
+    grpc_resolved_addresses **addrs) = resolve_address_impl;
 
 #endif /* GRPC_UV */
diff --git a/src/core/lib/iomgr/resolve_address_windows.c b/src/core/lib/iomgr/resolve_address_windows.c
index e139293c035ec1c7c2a4c0d8f4b495ec79066535..2439ce3cb79964eeee7ecf8667f7abeff7894814 100644
--- a/src/core/lib/iomgr/resolve_address_windows.c
+++ b/src/core/lib/iomgr/resolve_address_windows.c
@@ -154,7 +154,7 @@ static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp,
   } else {
     GRPC_ERROR_REF(error);
   }
-  grpc_exec_ctx_sched(exec_ctx, r->on_done, error, NULL);
+  grpc_closure_sched(exec_ctx, r->on_done, error);
   gpr_free(r->name);
   gpr_free(r->default_port);
   gpr_free(r);
@@ -169,20 +169,22 @@ void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) {
 
 static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name,
                                  const char *default_port,
+                                 grpc_pollset_set *interested_parties,
                                  grpc_closure *on_done,
                                  grpc_resolved_addresses **addresses) {
   request *r = gpr_malloc(sizeof(request));
-  grpc_closure_init(&r->request_closure, do_request_thread, r);
+  grpc_closure_init(&r->request_closure, do_request_thread, r,
+                    grpc_executor_scheduler);
   r->name = gpr_strdup(name);
   r->default_port = gpr_strdup(default_port);
   r->on_done = on_done;
   r->addresses = addresses;
-  grpc_executor_push(&r->request_closure, GRPC_ERROR_NONE);
+  grpc_closure_sched(exec_ctx, &r->request_closure, GRPC_ERROR_NONE);
 }
 
-void (*grpc_resolve_address)(grpc_exec_ctx *exec_ctx, const char *name,
-                             const char *default_port, grpc_closure *on_done,
-                             grpc_resolved_addresses **addresses) =
-    resolve_address_impl;
+void (*grpc_resolve_address)(
+    grpc_exec_ctx *exec_ctx, const char *name, const char *default_port,
+    grpc_pollset_set *interested_parties, grpc_closure *on_done,
+    grpc_resolved_addresses **addresses) = resolve_address_impl;
 
 #endif
diff --git a/src/core/lib/iomgr/resource_quota.c b/src/core/lib/iomgr/resource_quota.c
index 379bf9bd23be1fd94142f208e2c082cc04046126..42a044df772c3d1ce6257efa7317715ef94c7fe5 100644
--- a/src/core/lib/iomgr/resource_quota.c
+++ b/src/core/lib/iomgr/resource_quota.c
@@ -144,6 +144,12 @@ struct grpc_resource_quota {
   /* Closure around rq_reclamation_done */
   grpc_closure rq_reclamation_done_closure;
 
+  /* This is only really usable for debugging: it's always a stale pointer, but
+     a stale pointer that might just be fresh enough to guide us to where the
+     reclamation system is stuck */
+  grpc_closure *debug_only_last_initiated_reclaimer;
+  grpc_resource_user *debug_only_last_reclaimer_resource_user;
+
   /* Roots of all resource user lists */
   grpc_resource_user *roots[GRPC_RULIST_COUNT];
 
@@ -225,6 +231,7 @@ static void rulist_remove(grpc_resource_user *resource_user, grpc_rulist list) {
       resource_user->links[list].prev;
   resource_user->links[list].prev->links[list].next =
       resource_user->links[list].next;
+  resource_user->links[list].next = resource_user->links[list].prev = NULL;
 }
 
 /*******************************************************************************
@@ -250,17 +257,16 @@ static void rq_step(grpc_exec_ctx *exec_ctx, void *rq, grpc_error *error) {
   }
 
 done:
-  grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
+  grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
 }
 
 static void rq_step_sched(grpc_exec_ctx *exec_ctx,
                           grpc_resource_quota *resource_quota) {
   if (resource_quota->step_scheduled) return;
   resource_quota->step_scheduled = true;
-  grpc_resource_quota_internal_ref(resource_quota);
-  grpc_combiner_execute_finally(exec_ctx, resource_quota->combiner,
-                                &resource_quota->rq_step_closure,
-                                GRPC_ERROR_NONE, false);
+  grpc_resource_quota_ref_internal(resource_quota);
+  grpc_closure_sched(exec_ctx, &resource_quota->rq_step_closure,
+                     GRPC_ERROR_NONE);
 }
 
 /* returns true if all allocations are completed */
@@ -287,7 +293,7 @@ static bool rq_alloc(grpc_exec_ctx *exec_ctx,
     }
     if (resource_user->free_pool >= 0) {
       resource_user->allocating = false;
-      grpc_exec_ctx_enqueue_list(exec_ctx, &resource_user->on_allocated, NULL);
+      grpc_closure_list_sched(exec_ctx, &resource_user->on_allocated);
       gpr_mu_unlock(&resource_user->mu);
     } else {
       rulist_add_head(resource_user, GRPC_RULIST_AWAITING_ALLOCATION);
@@ -338,8 +344,11 @@ static bool rq_reclaim(grpc_exec_ctx *exec_ctx,
             destructive ? "destructive" : "benign");
   }
   resource_quota->reclaiming = true;
-  grpc_resource_quota_internal_ref(resource_quota);
+  grpc_resource_quota_ref_internal(resource_quota);
   grpc_closure *c = resource_user->reclaimers[destructive];
+  GPR_ASSERT(c);
+  resource_quota->debug_only_last_reclaimer_resource_user = resource_user;
+  resource_quota->debug_only_last_initiated_reclaimer = c;
   resource_user->reclaimers[destructive] = NULL;
   grpc_closure_run(exec_ctx, c, GRPC_ERROR_NONE);
   return true;
@@ -361,21 +370,10 @@ static void ru_slice_ref(void *p) {
   gpr_ref(&rc->refs);
 }
 
-static void ru_slice_unref(void *p) {
+static void ru_slice_unref(grpc_exec_ctx *exec_ctx, void *p) {
   ru_slice_refcount *rc = p;
   if (gpr_unref(&rc->refs)) {
-    /* TODO(ctiller): this is dangerous, but I think safe for now:
-       we have no guarantee here that we're at a safe point for creating an
-       execution context, but we have no way of writing this code otherwise.
-       In the future: consider lifting grpc_slice to grpc, and offering an
-       internal_{ref,unref} pair that is execution context aware.
-       Alternatively,
-       make exec_ctx be thread local and 'do the right thing' (whatever that
-       is)
-       if NULL */
-    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-    grpc_resource_user_free(&exec_ctx, rc->resource_user, rc->size);
-    grpc_exec_ctx_finish(&exec_ctx);
+    grpc_resource_user_free(exec_ctx, rc->resource_user, rc->size);
     gpr_free(rc);
   }
 }
@@ -429,7 +427,7 @@ static bool ru_post_reclaimer(grpc_exec_ctx *exec_ctx,
   resource_user->new_reclaimers[destructive] = NULL;
   GPR_ASSERT(resource_user->reclaimers[destructive] == NULL);
   if (gpr_atm_acq_load(&resource_user->shutdown) > 0) {
-    grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_CANCELLED, NULL);
+    grpc_closure_sched(exec_ctx, closure, GRPC_ERROR_CANCELLED);
     return false;
   }
   resource_user->reclaimers[destructive] = closure;
@@ -470,12 +468,14 @@ static void ru_post_destructive_reclaimer(grpc_exec_ctx *exec_ctx, void *ru,
 
 static void ru_shutdown(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) {
   grpc_resource_user *resource_user = ru;
-  grpc_exec_ctx_sched(exec_ctx, resource_user->reclaimers[0],
-                      GRPC_ERROR_CANCELLED, NULL);
-  grpc_exec_ctx_sched(exec_ctx, resource_user->reclaimers[1],
-                      GRPC_ERROR_CANCELLED, NULL);
+  grpc_closure_sched(exec_ctx, resource_user->reclaimers[0],
+                     GRPC_ERROR_CANCELLED);
+  grpc_closure_sched(exec_ctx, resource_user->reclaimers[1],
+                     GRPC_ERROR_CANCELLED);
   resource_user->reclaimers[0] = NULL;
   resource_user->reclaimers[1] = NULL;
+  rulist_remove(resource_user, GRPC_RULIST_RECLAIMER_BENIGN);
+  rulist_remove(resource_user, GRPC_RULIST_RECLAIMER_DESTRUCTIVE);
 }
 
 static void ru_destroy(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) {
@@ -484,15 +484,15 @@ static void ru_destroy(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) {
   for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
     rulist_remove(resource_user, (grpc_rulist)i);
   }
-  grpc_exec_ctx_sched(exec_ctx, resource_user->reclaimers[0],
-                      GRPC_ERROR_CANCELLED, NULL);
-  grpc_exec_ctx_sched(exec_ctx, resource_user->reclaimers[1],
-                      GRPC_ERROR_CANCELLED, NULL);
+  grpc_closure_sched(exec_ctx, resource_user->reclaimers[0],
+                     GRPC_ERROR_CANCELLED);
+  grpc_closure_sched(exec_ctx, resource_user->reclaimers[1],
+                     GRPC_ERROR_CANCELLED);
   if (resource_user->free_pool != 0) {
     resource_user->resource_quota->free_pool += resource_user->free_pool;
     rq_step_sched(exec_ctx, resource_user->resource_quota);
   }
-  grpc_resource_quota_internal_unref(exec_ctx, resource_user->resource_quota);
+  grpc_resource_quota_unref_internal(exec_ctx, resource_user->resource_quota);
   gpr_mu_destroy(&resource_user->mu);
   gpr_free(resource_user->name);
   gpr_free(resource_user);
@@ -528,7 +528,7 @@ static void rq_resize(grpc_exec_ctx *exec_ctx, void *args, grpc_error *error) {
   a->resource_quota->size += delta;
   a->resource_quota->free_pool += delta;
   rq_step_sched(exec_ctx, a->resource_quota);
-  grpc_resource_quota_internal_unref(exec_ctx, a->resource_quota);
+  grpc_resource_quota_unref_internal(exec_ctx, a->resource_quota);
   gpr_free(a);
 }
 
@@ -537,7 +537,7 @@ static void rq_reclamation_done(grpc_exec_ctx *exec_ctx, void *rq,
   grpc_resource_quota *resource_quota = rq;
   resource_quota->reclaiming = false;
   rq_step_sched(exec_ctx, resource_quota);
-  grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
+  grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
 }
 
 /*******************************************************************************
@@ -559,16 +559,19 @@ grpc_resource_quota *grpc_resource_quota_create(const char *name) {
     gpr_asprintf(&resource_quota->name, "anonymous_pool_%" PRIxPTR,
                  (intptr_t)resource_quota);
   }
-  grpc_closure_init(&resource_quota->rq_step_closure, rq_step, resource_quota);
+  grpc_closure_init(
+      &resource_quota->rq_step_closure, rq_step, resource_quota,
+      grpc_combiner_finally_scheduler(resource_quota->combiner, true));
   grpc_closure_init(&resource_quota->rq_reclamation_done_closure,
-                    rq_reclamation_done, resource_quota);
+                    rq_reclamation_done, resource_quota,
+                    grpc_combiner_scheduler(resource_quota->combiner, false));
   for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
     resource_quota->roots[i] = NULL;
   }
   return resource_quota;
 }
 
-void grpc_resource_quota_internal_unref(grpc_exec_ctx *exec_ctx,
+void grpc_resource_quota_unref_internal(grpc_exec_ctx *exec_ctx,
                                         grpc_resource_quota *resource_quota) {
   if (gpr_unref(&resource_quota->refs)) {
     grpc_combiner_destroy(exec_ctx, resource_quota->combiner);
@@ -580,11 +583,11 @@ void grpc_resource_quota_internal_unref(grpc_exec_ctx *exec_ctx,
 /* Public API */
 void grpc_resource_quota_unref(grpc_resource_quota *resource_quota) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+  grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
-grpc_resource_quota *grpc_resource_quota_internal_ref(
+grpc_resource_quota *grpc_resource_quota_ref_internal(
     grpc_resource_quota *resource_quota) {
   gpr_ref(&resource_quota->refs);
   return resource_quota;
@@ -592,7 +595,7 @@ grpc_resource_quota *grpc_resource_quota_internal_ref(
 
 /* Public API */
 void grpc_resource_quota_ref(grpc_resource_quota *resource_quota) {
-  grpc_resource_quota_internal_ref(resource_quota);
+  grpc_resource_quota_ref_internal(resource_quota);
 }
 
 /* Public API */
@@ -600,11 +603,10 @@ void grpc_resource_quota_resize(grpc_resource_quota *resource_quota,
                                 size_t size) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   rq_resize_args *a = gpr_malloc(sizeof(*a));
-  a->resource_quota = grpc_resource_quota_internal_ref(resource_quota);
+  a->resource_quota = grpc_resource_quota_ref_internal(resource_quota);
   a->size = (int64_t)size;
-  grpc_closure_init(&a->closure, rq_resize, a);
-  grpc_combiner_execute(&exec_ctx, resource_quota->combiner, &a->closure,
-                        GRPC_ERROR_NONE, false);
+  grpc_closure_init(&a->closure, rq_resize, a, grpc_schedule_on_exec_ctx);
+  grpc_closure_sched(&exec_ctx, &a->closure, GRPC_ERROR_NONE);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
@@ -617,7 +619,7 @@ grpc_resource_quota *grpc_resource_quota_from_channel_args(
   for (size_t i = 0; i < channel_args->num_args; i++) {
     if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
       if (channel_args->args[i].type == GRPC_ARG_POINTER) {
-        return grpc_resource_quota_internal_ref(
+        return grpc_resource_quota_ref_internal(
             channel_args->args[i].value.pointer.p);
       } else {
         gpr_log(GPR_DEBUG, GRPC_ARG_RESOURCE_QUOTA " should be a pointer");
@@ -632,7 +634,9 @@ static void *rq_copy(void *rq) {
   return rq;
 }
 
-static void rq_destroy(void *rq) { grpc_resource_quota_unref(rq); }
+static void rq_destroy(grpc_exec_ctx *exec_ctx, void *rq) {
+  grpc_resource_quota_unref_internal(exec_ctx, rq);
+}
 
 static int rq_cmp(void *a, void *b) { return GPR_ICMP(a, b); }
 
@@ -649,17 +653,21 @@ grpc_resource_user *grpc_resource_user_create(
     grpc_resource_quota *resource_quota, const char *name) {
   grpc_resource_user *resource_user = gpr_malloc(sizeof(*resource_user));
   resource_user->resource_quota =
-      grpc_resource_quota_internal_ref(resource_quota);
+      grpc_resource_quota_ref_internal(resource_quota);
   grpc_closure_init(&resource_user->allocate_closure, &ru_allocate,
-                    resource_user);
+                    resource_user,
+                    grpc_combiner_scheduler(resource_quota->combiner, false));
   grpc_closure_init(&resource_user->add_to_free_pool_closure,
-                    &ru_add_to_free_pool, resource_user);
+                    &ru_add_to_free_pool, resource_user,
+                    grpc_combiner_scheduler(resource_quota->combiner, false));
   grpc_closure_init(&resource_user->post_reclaimer_closure[0],
-                    &ru_post_benign_reclaimer, resource_user);
+                    &ru_post_benign_reclaimer, resource_user,
+                    grpc_combiner_scheduler(resource_quota->combiner, false));
   grpc_closure_init(&resource_user->post_reclaimer_closure[1],
-                    &ru_post_destructive_reclaimer, resource_user);
-  grpc_closure_init(&resource_user->destroy_closure, &ru_destroy,
-                    resource_user);
+                    &ru_post_destructive_reclaimer, resource_user,
+                    grpc_combiner_scheduler(resource_quota->combiner, false));
+  grpc_closure_init(&resource_user->destroy_closure, &ru_destroy, resource_user,
+                    grpc_combiner_scheduler(resource_quota->combiner, false));
   gpr_mu_init(&resource_user->mu);
   gpr_atm_rel_store(&resource_user->refs, 1);
   gpr_atm_rel_store(&resource_user->shutdown, 0);
@@ -694,9 +702,8 @@ static void ru_unref_by(grpc_exec_ctx *exec_ctx,
   gpr_atm old = gpr_atm_full_fetch_add(&resource_user->refs, -amount);
   GPR_ASSERT(old >= amount);
   if (old == amount) {
-    grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
-                          &resource_user->destroy_closure, GRPC_ERROR_NONE,
-                          false);
+    grpc_closure_sched(exec_ctx, &resource_user->destroy_closure,
+                       GRPC_ERROR_NONE);
   }
 }
 
@@ -712,9 +719,12 @@ void grpc_resource_user_unref(grpc_exec_ctx *exec_ctx,
 void grpc_resource_user_shutdown(grpc_exec_ctx *exec_ctx,
                                  grpc_resource_user *resource_user) {
   if (gpr_atm_full_fetch_add(&resource_user->shutdown, 1) == 0) {
-    grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
-                          grpc_closure_create(ru_shutdown, resource_user),
-                          GRPC_ERROR_NONE, false);
+    grpc_closure_sched(exec_ctx,
+                       grpc_closure_create(
+                           ru_shutdown, resource_user,
+                           grpc_combiner_scheduler(
+                               resource_user->resource_quota->combiner, false)),
+                       GRPC_ERROR_NONE);
   }
 }
 
@@ -734,12 +744,11 @@ void grpc_resource_user_alloc(grpc_exec_ctx *exec_ctx,
                              GRPC_ERROR_NONE);
     if (!resource_user->allocating) {
       resource_user->allocating = true;
-      grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
-                            &resource_user->allocate_closure, GRPC_ERROR_NONE,
-                            false);
+      grpc_closure_sched(exec_ctx, &resource_user->allocate_closure,
+                         GRPC_ERROR_NONE);
     }
   } else {
-    grpc_exec_ctx_sched(exec_ctx, optional_on_done, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, optional_on_done, GRPC_ERROR_NONE);
   }
   gpr_mu_unlock(&resource_user->mu);
 }
@@ -758,9 +767,8 @@ void grpc_resource_user_free(grpc_exec_ctx *exec_ctx,
   if (is_bigger_than_zero && was_zero_or_negative &&
       !resource_user->added_to_free_pool) {
     resource_user->added_to_free_pool = true;
-    grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
-                          &resource_user->add_to_free_pool_closure,
-                          GRPC_ERROR_NONE, false);
+    grpc_closure_sched(exec_ctx, &resource_user->add_to_free_pool_closure,
+                       GRPC_ERROR_NONE);
   }
   gpr_mu_unlock(&resource_user->mu);
   ru_unref_by(exec_ctx, resource_user, (gpr_atm)size);
@@ -772,9 +780,9 @@ void grpc_resource_user_post_reclaimer(grpc_exec_ctx *exec_ctx,
                                        grpc_closure *closure) {
   GPR_ASSERT(resource_user->new_reclaimers[destructive] == NULL);
   resource_user->new_reclaimers[destructive] = closure;
-  grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
-                        &resource_user->post_reclaimer_closure[destructive],
-                        GRPC_ERROR_NONE, false);
+  grpc_closure_sched(exec_ctx,
+                     &resource_user->post_reclaimer_closure[destructive],
+                     GRPC_ERROR_NONE);
 }
 
 void grpc_resource_user_finish_reclamation(grpc_exec_ctx *exec_ctx,
@@ -783,18 +791,20 @@ void grpc_resource_user_finish_reclamation(grpc_exec_ctx *exec_ctx,
     gpr_log(GPR_DEBUG, "RQ %s %s: reclamation complete",
             resource_user->resource_quota->name, resource_user->name);
   }
-  grpc_combiner_execute(
-      exec_ctx, resource_user->resource_quota->combiner,
-      &resource_user->resource_quota->rq_reclamation_done_closure,
-      GRPC_ERROR_NONE, false);
+  grpc_closure_sched(
+      exec_ctx, &resource_user->resource_quota->rq_reclamation_done_closure,
+      GRPC_ERROR_NONE);
 }
 
 void grpc_resource_user_slice_allocator_init(
     grpc_resource_user_slice_allocator *slice_allocator,
     grpc_resource_user *resource_user, grpc_iomgr_cb_func cb, void *p) {
-  grpc_closure_init(&slice_allocator->on_allocated, ru_allocated_slices,
-                    slice_allocator);
-  grpc_closure_init(&slice_allocator->on_done, cb, p);
+  grpc_closure_init(
+      &slice_allocator->on_allocated, ru_allocated_slices, slice_allocator,
+      grpc_combiner_scheduler(resource_user->resource_quota->combiner, false));
+  grpc_closure_init(
+      &slice_allocator->on_done, cb, p,
+      grpc_combiner_scheduler(resource_user->resource_quota->combiner, false));
   slice_allocator->resource_user = resource_user;
 }
 
diff --git a/src/core/lib/iomgr/resource_quota.h b/src/core/lib/iomgr/resource_quota.h
index 0181fd978b47f1db6f0bed60f1a6c47715954ac5..ef286c2fceeac48ac5678b1f63ac83880344e91e 100644
--- a/src/core/lib/iomgr/resource_quota.h
+++ b/src/core/lib/iomgr/resource_quota.h
@@ -77,9 +77,9 @@
 
 extern int grpc_resource_quota_trace;
 
-grpc_resource_quota *grpc_resource_quota_internal_ref(
+grpc_resource_quota *grpc_resource_quota_ref_internal(
     grpc_resource_quota *resource_quota);
-void grpc_resource_quota_internal_unref(grpc_exec_ctx *exec_ctx,
+void grpc_resource_quota_unref_internal(grpc_exec_ctx *exec_ctx,
                                         grpc_resource_quota *resource_quota);
 grpc_resource_quota *grpc_resource_quota_from_channel_args(
     const grpc_channel_args *channel_args);
diff --git a/src/core/lib/iomgr/socket_mutator.c b/src/core/lib/iomgr/socket_mutator.c
index 8b1efb6bab0e6ff4982e9921101f25742b7bb584..34b61dcfe5a58aaaabea256587d41c72e754cdbc 100644
--- a/src/core/lib/iomgr/socket_mutator.c
+++ b/src/core/lib/iomgr/socket_mutator.c
@@ -76,7 +76,7 @@ static void *socket_mutator_arg_copy(void *p) {
   return grpc_socket_mutator_ref(p);
 }
 
-static void socket_mutator_arg_destroy(void *p) {
+static void socket_mutator_arg_destroy(grpc_exec_ctx *exec_ctx, void *p) {
   grpc_socket_mutator_unref(p);
 }
 
diff --git a/src/core/lib/iomgr/socket_windows.c b/src/core/lib/iomgr/socket_windows.c
index 54911e0e31f73c567102ebe9c169b7d6b9d02586..2f2e02f71572a77cce0432b180bfc31d53c87bd7 100644
--- a/src/core/lib/iomgr/socket_windows.c
+++ b/src/core/lib/iomgr/socket_windows.c
@@ -131,7 +131,7 @@ static void socket_notify_on_iocp(grpc_exec_ctx *exec_ctx,
   gpr_mu_lock(&socket->state_mu);
   if (info->has_pending_iocp) {
     info->has_pending_iocp = 0;
-    grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, closure, GRPC_ERROR_NONE);
   } else {
     info->closure = closure;
   }
@@ -154,7 +154,7 @@ void grpc_socket_become_ready(grpc_exec_ctx *exec_ctx, grpc_winsocket *socket,
   GPR_ASSERT(!info->has_pending_iocp);
   gpr_mu_lock(&socket->state_mu);
   if (info->closure) {
-    grpc_exec_ctx_sched(exec_ctx, info->closure, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, info->closure, GRPC_ERROR_NONE);
     info->closure = NULL;
   } else {
     info->has_pending_iocp = 1;
diff --git a/src/core/lib/iomgr/tcp_client_posix.c b/src/core/lib/iomgr/tcp_client_posix.c
index a3a70a8ed75fa07fc4bd8ef312f9d7d2ba2d198c..9a77c92016b7076840cd485355604a4fcbd0ba8a 100644
--- a/src/core/lib/iomgr/tcp_client_posix.c
+++ b/src/core/lib/iomgr/tcp_client_posix.c
@@ -65,6 +65,7 @@ typedef struct {
   grpc_fd *fd;
   gpr_timespec deadline;
   grpc_timer alarm;
+  grpc_closure on_alarm;
   int refs;
   grpc_closure write_closure;
   grpc_pollset_set *interested_parties;
@@ -128,7 +129,7 @@ static void tc_on_alarm(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) {
   if (done) {
     gpr_mu_destroy(&ac->mu);
     gpr_free(ac->addr_str);
-    grpc_channel_args_destroy(ac->channel_args);
+    grpc_channel_args_destroy(exec_ctx, ac->channel_args);
     gpr_free(ac);
   }
 }
@@ -148,8 +149,8 @@ grpc_endpoint *grpc_tcp_client_create_from_fd(
             &channel_args->args[i], options);
       } else if (0 ==
                  strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
-        grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
-        resource_quota = grpc_resource_quota_internal_ref(
+        grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
+        resource_quota = grpc_resource_quota_ref_internal(
             channel_args->args[i].value.pointer.p);
       }
     }
@@ -157,7 +158,7 @@ grpc_endpoint *grpc_tcp_client_create_from_fd(
 
   grpc_endpoint *ep =
       grpc_tcp_create(fd, resource_quota, tcp_read_chunk_size, addr_str);
-  grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
+  grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
   return ep;
 }
 
@@ -262,10 +263,10 @@ finish:
   if (done) {
     gpr_mu_destroy(&ac->mu);
     gpr_free(ac->addr_str);
-    grpc_channel_args_destroy(ac->channel_args);
+    grpc_channel_args_destroy(exec_ctx, ac->channel_args);
     gpr_free(ac);
   }
-  grpc_exec_ctx_sched(exec_ctx, closure, error, NULL);
+  grpc_closure_sched(exec_ctx, closure, error);
 }
 
 static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
@@ -294,7 +295,7 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
 
   error = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode, &fd);
   if (error != GRPC_ERROR_NONE) {
-    grpc_exec_ctx_sched(exec_ctx, closure, error, NULL);
+    grpc_closure_sched(exec_ctx, closure, error);
     return;
   }
   if (dsmode == GRPC_DSMODE_IPV4) {
@@ -303,7 +304,7 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
     addr = &addr4_copy;
   }
   if ((error = prepare_socket(addr, fd, channel_args)) != GRPC_ERROR_NONE) {
-    grpc_exec_ctx_sched(exec_ctx, closure, error, NULL);
+    grpc_closure_sched(exec_ctx, closure, error);
     return;
   }
 
@@ -321,14 +322,13 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
   if (err >= 0) {
     *ep =
         grpc_tcp_client_create_from_fd(exec_ctx, fdobj, channel_args, addr_str);
-    grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, closure, GRPC_ERROR_NONE);
     goto done;
   }
 
   if (errno != EWOULDBLOCK && errno != EINPROGRESS) {
     grpc_fd_orphan(exec_ctx, fdobj, NULL, NULL, "tcp_client_connect_error");
-    grpc_exec_ctx_sched(exec_ctx, closure, GRPC_OS_ERROR(errno, "connect"),
-                        NULL);
+    grpc_closure_sched(exec_ctx, closure, GRPC_OS_ERROR(errno, "connect"));
     goto done;
   }
 
@@ -343,8 +343,8 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
   addr_str = NULL;
   gpr_mu_init(&ac->mu);
   ac->refs = 2;
-  ac->write_closure.cb = on_writable;
-  ac->write_closure.cb_arg = ac;
+  grpc_closure_init(&ac->write_closure, on_writable, ac,
+                    grpc_schedule_on_exec_ctx);
   ac->channel_args = grpc_channel_args_copy(channel_args);
 
   if (grpc_tcp_trace) {
@@ -353,9 +353,10 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
   }
 
   gpr_mu_lock(&ac->mu);
+  grpc_closure_init(&ac->on_alarm, tc_on_alarm, ac, grpc_schedule_on_exec_ctx);
   grpc_timer_init(exec_ctx, &ac->alarm,
                   gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC),
-                  tc_on_alarm, ac, gpr_now(GPR_CLOCK_MONOTONIC));
+                  &ac->on_alarm, gpr_now(GPR_CLOCK_MONOTONIC));
   grpc_fd_notify_on_write(exec_ctx, ac->fd, &ac->write_closure);
   gpr_mu_unlock(&ac->mu);
 
diff --git a/src/core/lib/iomgr/tcp_client_uv.c b/src/core/lib/iomgr/tcp_client_uv.c
index b07f9ceffa359b4545ecbce9a4e3b18d2b8b7ac4..5225a5402b2c99dbcb76eb1d832f1edfcc457c08 100644
--- a/src/core/lib/iomgr/tcp_client_uv.c
+++ b/src/core/lib/iomgr/tcp_client_uv.c
@@ -49,6 +49,7 @@
 typedef struct grpc_uv_tcp_connect {
   uv_connect_t connect_req;
   grpc_timer alarm;
+  grpc_closure on_alarm;
   uv_tcp_t *tcp_handle;
   grpc_closure *closure;
   grpc_endpoint **endpoint;
@@ -59,7 +60,7 @@ typedef struct grpc_uv_tcp_connect {
 
 static void uv_tcp_connect_cleanup(grpc_exec_ctx *exec_ctx,
                                    grpc_uv_tcp_connect *connect) {
-  grpc_resource_quota_internal_unref(exec_ctx, connect->resource_quota);
+  grpc_resource_quota_unref_internal(exec_ctx, connect->resource_quota);
   gpr_free(connect);
 }
 
@@ -110,7 +111,7 @@ static void uv_tc_on_connect(uv_connect_t *req, int status) {
   if (done) {
     uv_tcp_connect_cleanup(&exec_ctx, connect);
   }
-  grpc_exec_ctx_sched(&exec_ctx, closure, error, NULL);
+  grpc_closure_sched(&exec_ctx, closure, error);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
@@ -128,8 +129,8 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
   if (channel_args != NULL) {
     for (size_t i = 0; i < channel_args->num_args; i++) {
       if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
-        grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
-        resource_quota = grpc_resource_quota_internal_ref(
+        grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
+        resource_quota = grpc_resource_quota_ref_internal(
             channel_args->args[i].value.pointer.p);
       }
     }
@@ -148,9 +149,11 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
   uv_tcp_connect(&connect->connect_req, connect->tcp_handle,
                  (const struct sockaddr *)resolved_addr->addr,
                  uv_tc_on_connect);
+  grpc_closure_init(&connect->on_alarm, uv_tc_on_alarm, connect,
+                    grpc_schedule_on_exec_ctx);
   grpc_timer_init(exec_ctx, &connect->alarm,
                   gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC),
-                  uv_tc_on_alarm, connect, gpr_now(GPR_CLOCK_MONOTONIC));
+                  &connect->on_alarm, gpr_now(GPR_CLOCK_MONOTONIC));
 }
 
 // overridden by api_fuzzer.c
diff --git a/src/core/lib/iomgr/tcp_client_windows.c b/src/core/lib/iomgr/tcp_client_windows.c
index 1127588ebc74dbc86f1377daf62648857b505e8d..1e84ec3a1ec39d91cb6fd4929eaa33db04cfe732 100644
--- a/src/core/lib/iomgr/tcp_client_windows.c
+++ b/src/core/lib/iomgr/tcp_client_windows.c
@@ -58,6 +58,7 @@ typedef struct {
   grpc_winsocket *socket;
   gpr_timespec deadline;
   grpc_timer alarm;
+  grpc_closure on_alarm;
   char *addr_name;
   int refs;
   grpc_closure on_connect;
@@ -71,7 +72,7 @@ static void async_connect_unlock_and_cleanup(grpc_exec_ctx *exec_ctx,
   int done = (--ac->refs == 0);
   gpr_mu_unlock(&ac->mu);
   if (done) {
-    grpc_resource_quota_internal_unref(exec_ctx, ac->resource_quota);
+    grpc_resource_quota_unref_internal(exec_ctx, ac->resource_quota);
     gpr_mu_destroy(&ac->mu);
     gpr_free(ac->addr_name);
     gpr_free(ac);
@@ -129,7 +130,7 @@ static void on_connect(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) {
   async_connect_unlock_and_cleanup(exec_ctx, ac, socket);
   /* If the connection was aborted, the callback was already called when
      the deadline was met. */
-  grpc_exec_ctx_sched(exec_ctx, on_done, error, NULL);
+  grpc_closure_sched(exec_ctx, on_done, error);
 }
 
 /* Tries to issue one async connection, then schedules both an IOCP
@@ -157,8 +158,8 @@ void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *on_done,
   if (channel_args != NULL) {
     for (size_t i = 0; i < channel_args->num_args; i++) {
       if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
-        grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
-        resource_quota = grpc_resource_quota_internal_ref(
+        grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
+        resource_quota = grpc_resource_quota_ref_internal(
             channel_args->args[i].value.pointer.p);
       }
     }
@@ -227,9 +228,10 @@ void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *on_done,
   ac->addr_name = grpc_sockaddr_to_uri(addr);
   ac->endpoint = endpoint;
   ac->resource_quota = resource_quota;
-  grpc_closure_init(&ac->on_connect, on_connect, ac);
+  grpc_closure_init(&ac->on_connect, on_connect, ac, grpc_schedule_on_exec_ctx);
 
-  grpc_timer_init(exec_ctx, &ac->alarm, deadline, on_alarm, ac,
+  grpc_closure_init(&ac->on_alarm, on_alarm, ac, grpc_schedule_on_exec_ctx);
+  grpc_timer_init(exec_ctx, &ac->alarm, deadline, &ac->on_alarm,
                   gpr_now(GPR_CLOCK_MONOTONIC));
   grpc_socket_notify_on_write(exec_ctx, socket, &ac->on_connect);
   return;
@@ -246,8 +248,8 @@ failure:
   } else if (sock != INVALID_SOCKET) {
     closesocket(sock);
   }
-  grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
-  grpc_exec_ctx_sched(exec_ctx, on_done, final_error, NULL);
+  grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
+  grpc_closure_sched(exec_ctx, on_done, final_error);
 }
 
 #endif /* GRPC_WINSOCK_SOCKET */
diff --git a/src/core/lib/iomgr/tcp_posix.c b/src/core/lib/iomgr/tcp_posix.c
index 12a4797e6f40baf80673349ac1f38f2a57dadd2b..ece44978b0f2a68abdcbc999ec77ca99a141df74 100644
--- a/src/core/lib/iomgr/tcp_posix.c
+++ b/src/core/lib/iomgr/tcp_posix.c
@@ -56,6 +56,7 @@
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/iomgr/ev_posix.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/string.h"
 
@@ -107,6 +108,12 @@ typedef struct {
   grpc_resource_user_slice_allocator slice_allocator;
 } grpc_tcp;
 
+static grpc_error *tcp_annotate_error(grpc_error *src_error, grpc_tcp *tcp) {
+  return grpc_error_set_str(
+      grpc_error_set_int(src_error, GRPC_ERROR_INT_FD, tcp->fd),
+      GRPC_ERROR_STR_TARGET_ADDRESS, tcp->peer_string);
+}
+
 static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */,
                             grpc_error *error);
 static void tcp_handle_write(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */,
@@ -121,7 +128,7 @@ static void tcp_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
 static void tcp_free(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) {
   grpc_fd_orphan(exec_ctx, tcp->em_fd, tcp->release_fd_cb, tcp->release_fd,
                  "tcp_unref_orphan");
-  grpc_slice_buffer_destroy(&tcp->last_read_buffer);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &tcp->last_read_buffer);
   grpc_resource_user_unref(exec_ctx, tcp->resource_user);
   gpr_free(tcp->peer_string);
   gpr_free(tcp);
@@ -162,7 +169,7 @@ static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); }
 static void tcp_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
   grpc_network_status_unregister_endpoint(ep);
   grpc_tcp *tcp = (grpc_tcp *)ep;
-  grpc_slice_buffer_reset_and_unref(&tcp->last_read_buffer);
+  grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &tcp->last_read_buffer);
   TCP_UNREF(exec_ctx, tcp, "destroy");
 }
 
@@ -229,14 +236,17 @@ static void tcp_do_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) {
       /* We've consumed the edge, request a new one */
       grpc_fd_notify_on_read(exec_ctx, tcp->em_fd, &tcp->read_closure);
     } else {
-      grpc_slice_buffer_reset_and_unref(tcp->incoming_buffer);
-      call_read_cb(exec_ctx, tcp, GRPC_OS_ERROR(errno, "recvmsg"));
+      grpc_slice_buffer_reset_and_unref_internal(exec_ctx,
+                                                 tcp->incoming_buffer);
+      call_read_cb(exec_ctx, tcp,
+                   tcp_annotate_error(GRPC_OS_ERROR(errno, "recvmsg"), tcp));
       TCP_UNREF(exec_ctx, tcp, "read");
     }
   } else if (read_bytes == 0) {
     /* 0 read size ==> end of stream */
-    grpc_slice_buffer_reset_and_unref(tcp->incoming_buffer);
-    call_read_cb(exec_ctx, tcp, GRPC_ERROR_CREATE("Socket closed"));
+    grpc_slice_buffer_reset_and_unref_internal(exec_ctx, tcp->incoming_buffer);
+    call_read_cb(exec_ctx, tcp,
+                 tcp_annotate_error(GRPC_ERROR_CREATE("Socket closed"), tcp));
     TCP_UNREF(exec_ctx, tcp, "read");
   } else {
     GPR_ASSERT((size_t)read_bytes <= tcp->incoming_buffer->length);
@@ -260,8 +270,9 @@ static void tcp_read_allocation_done(grpc_exec_ctx *exec_ctx, void *tcpp,
                                      grpc_error *error) {
   grpc_tcp *tcp = tcpp;
   if (error != GRPC_ERROR_NONE) {
-    grpc_slice_buffer_reset_and_unref(tcp->incoming_buffer);
-    grpc_slice_buffer_reset_and_unref(&tcp->last_read_buffer);
+    grpc_slice_buffer_reset_and_unref_internal(exec_ctx, tcp->incoming_buffer);
+    grpc_slice_buffer_reset_and_unref_internal(exec_ctx,
+                                               &tcp->last_read_buffer);
     call_read_cb(exec_ctx, tcp, GRPC_ERROR_REF(error));
     TCP_UNREF(exec_ctx, tcp, "read");
   } else {
@@ -286,8 +297,9 @@ static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */,
   GPR_ASSERT(!tcp->finished_edge);
 
   if (error != GRPC_ERROR_NONE) {
-    grpc_slice_buffer_reset_and_unref(tcp->incoming_buffer);
-    grpc_slice_buffer_reset_and_unref(&tcp->last_read_buffer);
+    grpc_slice_buffer_reset_and_unref_internal(exec_ctx, tcp->incoming_buffer);
+    grpc_slice_buffer_reset_and_unref_internal(exec_ctx,
+                                               &tcp->last_read_buffer);
     call_read_cb(exec_ctx, tcp, GRPC_ERROR_REF(error));
     TCP_UNREF(exec_ctx, tcp, "read");
   } else {
@@ -301,14 +313,14 @@ static void tcp_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
   GPR_ASSERT(tcp->read_cb == NULL);
   tcp->read_cb = cb;
   tcp->incoming_buffer = incoming_buffer;
-  grpc_slice_buffer_reset_and_unref(incoming_buffer);
+  grpc_slice_buffer_reset_and_unref_internal(exec_ctx, incoming_buffer);
   grpc_slice_buffer_swap(incoming_buffer, &tcp->last_read_buffer);
   TCP_REF(tcp, "read");
   if (tcp->finished_edge) {
     tcp->finished_edge = false;
     grpc_fd_notify_on_read(exec_ctx, tcp->em_fd, &tcp->read_closure);
   } else {
-    grpc_exec_ctx_sched(exec_ctx, &tcp->read_closure, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, &tcp->read_closure, GRPC_ERROR_NONE);
   }
 }
 
@@ -365,8 +377,13 @@ static bool tcp_flush(grpc_tcp *tcp, grpc_error **error) {
         tcp->outgoing_slice_idx = unwind_slice_idx;
         tcp->outgoing_byte_idx = unwind_byte_idx;
         return false;
+      } else if (errno == EPIPE) {
+        *error = grpc_error_set_int(GRPC_OS_ERROR(errno, "sendmsg"),
+                                    GRPC_ERROR_INT_GRPC_STATUS,
+                                    GRPC_STATUS_UNAVAILABLE);
+        return true;
       } else {
-        *error = GRPC_OS_ERROR(errno, "sendmsg");
+        *error = tcp_annotate_error(GRPC_OS_ERROR(errno, "sendmsg"), tcp);
         return true;
       }
     }
@@ -447,10 +464,10 @@ static void tcp_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
 
   if (buf->length == 0) {
     GPR_TIMER_END("tcp_write", 0);
-    grpc_exec_ctx_sched(exec_ctx, cb, grpc_fd_is_shutdown(tcp->em_fd)
-                                          ? GRPC_ERROR_CREATE("EOF")
-                                          : GRPC_ERROR_NONE,
-                        NULL);
+    grpc_closure_sched(exec_ctx, cb,
+                       grpc_fd_is_shutdown(tcp->em_fd)
+                           ? tcp_annotate_error(GRPC_ERROR_CREATE("EOF"), tcp)
+                           : GRPC_ERROR_NONE);
     return;
   }
   tcp->outgoing_buffer = buf;
@@ -470,7 +487,7 @@ static void tcp_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
       gpr_log(GPR_DEBUG, "write: %s", str);
       grpc_error_free_string(str);
     }
-    grpc_exec_ctx_sched(exec_ctx, cb, error, NULL);
+    grpc_closure_sched(exec_ctx, cb, error);
   }
 
   GPR_TIMER_END("tcp_write", 0);
@@ -538,10 +555,10 @@ grpc_endpoint *grpc_tcp_create(grpc_fd *em_fd,
   gpr_ref_init(&tcp->refcount, 1);
   gpr_atm_no_barrier_store(&tcp->shutdown_count, 0);
   tcp->em_fd = em_fd;
-  tcp->read_closure.cb = tcp_handle_read;
-  tcp->read_closure.cb_arg = tcp;
-  tcp->write_closure.cb = tcp_handle_write;
-  tcp->write_closure.cb_arg = tcp;
+  grpc_closure_init(&tcp->read_closure, tcp_handle_read, tcp,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&tcp->write_closure, tcp_handle_write, tcp,
+                    grpc_schedule_on_exec_ctx);
   grpc_slice_buffer_init(&tcp->last_read_buffer);
   tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string);
   grpc_resource_user_slice_allocator_init(
@@ -565,7 +582,7 @@ void grpc_tcp_destroy_and_release_fd(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
   GPR_ASSERT(ep->vtable == &vtable);
   tcp->release_fd = fd;
   tcp->release_fd_cb = done;
-  grpc_slice_buffer_reset_and_unref(&tcp->last_read_buffer);
+  grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &tcp->last_read_buffer);
   TCP_UNREF(exec_ctx, tcp, "destroy");
 }
 
diff --git a/src/core/lib/iomgr/tcp_server.h b/src/core/lib/iomgr/tcp_server.h
index 6eba8c40571612e0e5dca11b07e92d6390efb5fe..437a94beff85afb796e82ad5b139b231ddd8c118 100644
--- a/src/core/lib/iomgr/tcp_server.h
+++ b/src/core/lib/iomgr/tcp_server.h
@@ -52,7 +52,8 @@ typedef struct grpc_tcp_server_acceptor {
   unsigned fd_index;
 } grpc_tcp_server_acceptor;
 
-/* Called for newly connected TCP connections. */
+/* Called for newly connected TCP connections.
+   Takes ownership of acceptor. */
 typedef void (*grpc_tcp_server_cb)(grpc_exec_ctx *exec_ctx, void *arg,
                                    grpc_endpoint *ep,
                                    grpc_pollset *accepting_pollset,
diff --git a/src/core/lib/iomgr/tcp_server_posix.c b/src/core/lib/iomgr/tcp_server_posix.c
index 7e2fb0f1f9c831c82b3a328fea248e097e3d915a..20efb678b230b4e904681bb70c567f9a05df579b 100644
--- a/src/core/lib/iomgr/tcp_server_posix.c
+++ b/src/core/lib/iomgr/tcp_server_posix.c
@@ -167,18 +167,18 @@ grpc_error *grpc_tcp_server_create(grpc_exec_ctx *exec_ctx,
         s->so_reuseport =
             has_so_reuseport && (args->args[i].value.integer != 0);
       } else {
-        grpc_resource_quota_internal_unref(exec_ctx, s->resource_quota);
+        grpc_resource_quota_unref_internal(exec_ctx, s->resource_quota);
         gpr_free(s);
         return GRPC_ERROR_CREATE(GRPC_ARG_ALLOW_REUSEPORT
                                  " must be an integer");
       }
     } else if (0 == strcmp(GRPC_ARG_RESOURCE_QUOTA, args->args[i].key)) {
       if (args->args[i].type == GRPC_ARG_POINTER) {
-        grpc_resource_quota_internal_unref(exec_ctx, s->resource_quota);
+        grpc_resource_quota_unref_internal(exec_ctx, s->resource_quota);
         s->resource_quota =
-            grpc_resource_quota_internal_ref(args->args[i].value.pointer.p);
+            grpc_resource_quota_ref_internal(args->args[i].value.pointer.p);
       } else {
-        grpc_resource_quota_internal_unref(exec_ctx, s->resource_quota);
+        grpc_resource_quota_unref_internal(exec_ctx, s->resource_quota);
         gpr_free(s);
         return GRPC_ERROR_CREATE(GRPC_ARG_RESOURCE_QUOTA
                                  " must be a pointer to a buffer pool");
@@ -208,7 +208,7 @@ static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
   GPR_ASSERT(s->shutdown);
   gpr_mu_unlock(&s->mu);
   if (s->shutdown_complete != NULL) {
-    grpc_exec_ctx_sched(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE);
   }
 
   gpr_mu_destroy(&s->mu);
@@ -219,7 +219,7 @@ static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
     gpr_free(sp);
   }
 
-  grpc_resource_quota_internal_unref(exec_ctx, s->resource_quota);
+  grpc_resource_quota_unref_internal(exec_ctx, s->resource_quota);
 
   gpr_free(s);
 }
@@ -254,8 +254,8 @@ static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
     grpc_tcp_listener *sp;
     for (sp = s->head; sp; sp = sp->next) {
       grpc_unlink_if_unix_domain_socket(&sp->addr);
-      sp->destroyed_closure.cb = destroyed_port;
-      sp->destroyed_closure.cb_arg = s;
+      grpc_closure_init(&sp->destroyed_closure, destroyed_port, s,
+                        grpc_schedule_on_exec_ctx);
       grpc_fd_orphan(exec_ctx, sp->emfd, &sp->destroyed_closure, NULL,
                      "tcp_listener_shutdown");
     }
@@ -381,16 +381,12 @@ error:
 /* event manager callback when reads are ready */
 static void on_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *err) {
   grpc_tcp_listener *sp = arg;
-  grpc_tcp_server_acceptor acceptor = {sp->server, sp->port_index,
-                                       sp->fd_index};
-  grpc_pollset *read_notifier_pollset = NULL;
-  grpc_fd *fdobj;
 
   if (err != GRPC_ERROR_NONE) {
     goto error;
   }
 
-  read_notifier_pollset =
+  grpc_pollset *read_notifier_pollset =
       sp->server->pollsets[(size_t)gpr_atm_no_barrier_fetch_add(
                                &sp->server->next_pollset_to_assign, 1) %
                            sp->server->pollset_count];
@@ -426,7 +422,7 @@ static void on_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *err) {
       gpr_log(GPR_DEBUG, "SERVER_CONNECT: incoming connection: %s", addr_str);
     }
 
-    fdobj = grpc_fd_create(fd, name);
+    grpc_fd *fdobj = grpc_fd_create(fd, name);
 
     if (read_notifier_pollset == NULL) {
       gpr_log(GPR_ERROR, "Read notifier pollset is not set on the fd");
@@ -435,11 +431,17 @@ static void on_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *err) {
 
     grpc_pollset_add_fd(exec_ctx, read_notifier_pollset, fdobj);
 
+    // Create acceptor.
+    grpc_tcp_server_acceptor *acceptor = gpr_malloc(sizeof(*acceptor));
+    acceptor->from_server = sp->server;
+    acceptor->port_index = sp->port_index;
+    acceptor->fd_index = sp->fd_index;
+
     sp->server->on_accept_cb(
         exec_ctx, sp->server->on_accept_cb_arg,
         grpc_tcp_create(fdobj, sp->server->resource_quota,
                         GRPC_TCP_DEFAULT_READ_SLICE_SIZE, addr_str),
-        read_notifier_pollset, &acceptor);
+        read_notifier_pollset, acceptor);
 
     gpr_free(name);
     gpr_free(addr_str);
@@ -721,8 +723,8 @@ void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s,
           "clone_port", clone_port(sp, (unsigned)(pollset_count - 1))));
       for (i = 0; i < pollset_count; i++) {
         grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd);
-        sp->read_closure.cb = on_read;
-        sp->read_closure.cb_arg = sp;
+        grpc_closure_init(&sp->read_closure, on_read, sp,
+                          grpc_schedule_on_exec_ctx);
         grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure);
         s->active_ports++;
         sp = sp->next;
@@ -731,8 +733,8 @@ void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s,
       for (i = 0; i < pollset_count; i++) {
         grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd);
       }
-      sp->read_closure.cb = on_read;
-      sp->read_closure.cb_arg = sp;
+      grpc_closure_init(&sp->read_closure, on_read, sp,
+                        grpc_schedule_on_exec_ctx);
       grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure);
       s->active_ports++;
       sp = sp->next;
@@ -758,7 +760,7 @@ void grpc_tcp_server_unref(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
   if (gpr_unref(&s->refs)) {
     grpc_tcp_server_shutdown_listeners(exec_ctx, s);
     gpr_mu_lock(&s->mu);
-    grpc_exec_ctx_enqueue_list(exec_ctx, &s->shutdown_starting, NULL);
+    grpc_closure_list_sched(exec_ctx, &s->shutdown_starting);
     gpr_mu_unlock(&s->mu);
     tcp_server_destroy(exec_ctx, s);
   }
diff --git a/src/core/lib/iomgr/tcp_server_uv.c b/src/core/lib/iomgr/tcp_server_uv.c
index b5b9b92a20afc7f9f3360d8bf481126956b5f136..eed2773f8a0b4ef67a97c29ee2f4efc71b806c2e 100644
--- a/src/core/lib/iomgr/tcp_server_uv.c
+++ b/src/core/lib/iomgr/tcp_server_uv.c
@@ -89,11 +89,11 @@ grpc_error *grpc_tcp_server_create(grpc_exec_ctx *exec_ctx,
   for (size_t i = 0; i < (args == NULL ? 0 : args->num_args); i++) {
     if (0 == strcmp(GRPC_ARG_RESOURCE_QUOTA, args->args[i].key)) {
       if (args->args[i].type == GRPC_ARG_POINTER) {
-        grpc_resource_quota_internal_unref(exec_ctx, s->resource_quota);
+        grpc_resource_quota_unref_internal(exec_ctx, s->resource_quota);
         s->resource_quota =
-            grpc_resource_quota_internal_ref(args->args[i].value.pointer.p);
+            grpc_resource_quota_ref_internal(args->args[i].value.pointer.p);
       } else {
-        grpc_resource_quota_internal_unref(exec_ctx, s->resource_quota);
+        grpc_resource_quota_unref_internal(exec_ctx, s->resource_quota);
         gpr_free(s);
         return GRPC_ERROR_CREATE(GRPC_ARG_RESOURCE_QUOTA
                                  " must be a pointer to a buffer pool");
@@ -126,7 +126,7 @@ void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server *s,
 
 static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
   if (s->shutdown_complete != NULL) {
-    grpc_exec_ctx_sched(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE);
   }
 
   while (s->head) {
@@ -136,7 +136,7 @@ static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
     gpr_free(sp->handle);
     gpr_free(sp);
   }
-  grpc_resource_quota_internal_unref(exec_ctx, s->resource_quota);
+  grpc_resource_quota_unref_internal(exec_ctx, s->resource_quota);
   gpr_free(s);
 }
 
@@ -170,7 +170,7 @@ void grpc_tcp_server_unref(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
   if (gpr_unref(&s->refs)) {
     /* Complete shutdown_starting work before destroying. */
     grpc_exec_ctx local_exec_ctx = GRPC_EXEC_CTX_INIT;
-    grpc_exec_ctx_enqueue_list(&local_exec_ctx, &s->shutdown_starting, NULL);
+    grpc_closure_list_sched(&local_exec_ctx, &s->shutdown_starting);
     if (exec_ctx == NULL) {
       grpc_exec_ctx_flush(&local_exec_ctx);
       tcp_server_destroy(&local_exec_ctx, s);
@@ -188,7 +188,6 @@ static void accepted_connection_close_cb(uv_handle_t *handle) {
 
 static void on_connect(uv_stream_t *server, int status) {
   grpc_tcp_listener *sp = (grpc_tcp_listener *)server->data;
-  grpc_tcp_server_acceptor acceptor = {sp->server, sp->port_index, 0};
   uv_tcp_t *client;
   grpc_endpoint *ep = NULL;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
@@ -201,6 +200,7 @@ static void on_connect(uv_stream_t *server, int status) {
             uv_strerror(status));
     return;
   }
+
   client = gpr_malloc(sizeof(uv_tcp_t));
   uv_tcp_init(uv_default_loop(), client);
   // UV documentation says this is guaranteed to succeed
@@ -220,8 +220,13 @@ static void on_connect(uv_stream_t *server, int status) {
       gpr_log(GPR_INFO, "uv_tcp_getpeername error: %s", uv_strerror(status));
     }
     ep = grpc_tcp_create(client, sp->server->resource_quota, peer_name_string);
+    // Create acceptor.
+    grpc_tcp_server_acceptor *acceptor = gpr_malloc(sizeof(*acceptor));
+    acceptor->from_server = sp->server;
+    acceptor->port_index = sp->port_index;
+    acceptor->fd_index = 0;
     sp->server->on_accept_cb(&exec_ctx, sp->server->on_accept_cb_arg, ep, NULL,
-                             &acceptor);
+                             acceptor);
     grpc_exec_ctx_finish(&exec_ctx);
   }
 }
diff --git a/src/core/lib/iomgr/tcp_server_windows.c b/src/core/lib/iomgr/tcp_server_windows.c
index 40d1746730baa35ec6ca72cc55553e4e9a2df4ad..dafe851ce8b51c7d92fcf4a3773864a4f3c6c9a3 100644
--- a/src/core/lib/iomgr/tcp_server_windows.c
+++ b/src/core/lib/iomgr/tcp_server_windows.c
@@ -116,11 +116,11 @@ grpc_error *grpc_tcp_server_create(grpc_exec_ctx *exec_ctx,
   for (size_t i = 0; i < (args == NULL ? 0 : args->num_args); i++) {
     if (0 == strcmp(GRPC_ARG_RESOURCE_QUOTA, args->args[i].key)) {
       if (args->args[i].type == GRPC_ARG_POINTER) {
-        grpc_resource_quota_internal_unref(exec_ctx, s->resource_quota);
+        grpc_resource_quota_unref_internal(exec_ctx, s->resource_quota);
         s->resource_quota =
-            grpc_resource_quota_internal_ref(args->args[i].value.pointer.p);
+            grpc_resource_quota_ref_internal(args->args[i].value.pointer.p);
       } else {
-        grpc_resource_quota_internal_unref(exec_ctx, s->resource_quota);
+        grpc_resource_quota_unref_internal(exec_ctx, s->resource_quota);
         gpr_free(s);
         return GRPC_ERROR_CREATE(GRPC_ARG_RESOURCE_QUOTA
                                  " must be a pointer to a buffer pool");
@@ -155,18 +155,19 @@ static void destroy_server(grpc_exec_ctx *exec_ctx, void *arg,
     grpc_winsocket_destroy(sp->socket);
     gpr_free(sp);
   }
-  grpc_resource_quota_internal_unref(exec_ctx, s->resource_quota);
+  grpc_resource_quota_unref_internal(exec_ctx, s->resource_quota);
   gpr_free(s);
 }
 
 static void finish_shutdown_locked(grpc_exec_ctx *exec_ctx,
                                    grpc_tcp_server *s) {
   if (s->shutdown_complete != NULL) {
-    grpc_exec_ctx_sched(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE);
   }
 
-  grpc_exec_ctx_sched(exec_ctx, grpc_closure_create(destroy_server, s),
-                      GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, grpc_closure_create(destroy_server, s,
+                                                   grpc_schedule_on_exec_ctx),
+                     GRPC_ERROR_NONE);
 }
 
 grpc_tcp_server *grpc_tcp_server_ref(grpc_tcp_server *s) {
@@ -183,7 +184,6 @@ void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server *s,
 }
 
 static void tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
-  int immediately_done = 0;
   grpc_tcp_listener *sp;
   gpr_mu_lock(&s->mu);
 
@@ -204,7 +204,7 @@ void grpc_tcp_server_unref(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
   if (gpr_unref(&s->refs)) {
     grpc_tcp_server_shutdown_listeners(exec_ctx, s);
     gpr_mu_lock(&s->mu);
-    grpc_exec_ctx_enqueue_list(exec_ctx, &s->shutdown_starting, NULL);
+    grpc_closure_list_sched(exec_ctx, &s->shutdown_starting);
     gpr_mu_unlock(&s->mu);
     tcp_server_destroy(exec_ctx, s);
   }
@@ -247,7 +247,7 @@ static grpc_error *prepare_socket(SOCKET sock,
 failure:
   GPR_ASSERT(error != GRPC_ERROR_NONE);
   char *tgtaddr = grpc_sockaddr_to_uri(addr);
-  grpc_error *final_error = grpc_error_set_int(
+  grpc_error_set_int(
       grpc_error_set_str(GRPC_ERROR_CREATE_REFERENCING(
                              "Failed to prepare server socket", &error, 1),
                          GRPC_ERROR_STR_TARGET_ADDRESS, tgtaddr),
@@ -260,7 +260,6 @@ failure:
 
 static void decrement_active_ports_and_notify_locked(grpc_exec_ctx *exec_ctx,
                                                      grpc_tcp_listener *sp) {
-  int notify = 0;
   sp->shutting_down = 0;
   GPR_ASSERT(sp->server->active_ports > 0);
   if (0 == --sp->server->active_ports) {
@@ -323,7 +322,6 @@ failure:
 /* Event manager callback when reads are ready. */
 static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   grpc_tcp_listener *sp = arg;
-  grpc_tcp_server_acceptor acceptor = {sp->server, sp->port_index, 0};
   SOCKET sock = sp->new_socket;
   grpc_winsocket_callback_info *info = &sp->socket->read_info;
   grpc_endpoint *ep = NULL;
@@ -396,8 +394,13 @@ static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   /* The only time we should call our callback, is where we successfully
      managed to accept a connection, and created an endpoint. */
   if (ep) {
+    // Create acceptor.
+    grpc_tcp_server_acceptor *acceptor = gpr_malloc(sizeof(*acceptor));
+    acceptor->from_server = sp->server;
+    acceptor->port_index = sp->port_index;
+    acceptor->fd_index = 0;
     sp->server->on_accept_cb(exec_ctx, sp->server->on_accept_cb_arg, ep, NULL,
-                             &acceptor);
+                             acceptor);
   }
   /* As we were notified from the IOCP of one and exactly one accept,
      the former socked we created has now either been destroy or assigned
@@ -461,7 +464,7 @@ static grpc_error *add_socket_to_server(grpc_tcp_server *s, SOCKET sock,
   sp->new_socket = INVALID_SOCKET;
   sp->port = port;
   sp->port_index = port_index;
-  grpc_closure_init(&sp->on_accept, on_accept, sp);
+  grpc_closure_init(&sp->on_accept, on_accept, sp, grpc_schedule_on_exec_ctx);
   GPR_ASSERT(sp->socket);
   gpr_mu_unlock(&s->mu);
   *listener = sp;
diff --git a/src/core/lib/iomgr/tcp_uv.c b/src/core/lib/iomgr/tcp_uv.c
index 6e2ad1dbe923594dd4a4b3cf96e6b7ed393510a2..3ddc79706be2c1035c318a67c7c1a6ba8ef35e94 100644
--- a/src/core/lib/iomgr/tcp_uv.c
+++ b/src/core/lib/iomgr/tcp_uv.c
@@ -169,7 +169,7 @@ static void read_callback(uv_stream_t *stream, ssize_t nread,
     // nread < 0: Error
     error = GRPC_ERROR_CREATE("TCP Read failed");
   }
-  grpc_exec_ctx_sched(&exec_ctx, cb, error, NULL);
+  grpc_closure_sched(&exec_ctx, cb, error);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
@@ -181,7 +181,7 @@ static void uv_endpoint_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
   GPR_ASSERT(tcp->read_cb == NULL);
   tcp->read_cb = cb;
   tcp->read_slices = read_slices;
-  grpc_slice_buffer_reset_and_unref(read_slices);
+  grpc_slice_buffer_reset_and_unref_internal(exec_ctx, read_slices);
   TCP_REF(tcp, "read");
   // TODO(murgatroid99): figure out what the return value here means
   status =
@@ -190,7 +190,7 @@ static void uv_endpoint_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
     error = GRPC_ERROR_CREATE("TCP Read failed at start");
     error =
         grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, uv_strerror(status));
-    grpc_exec_ctx_sched(exec_ctx, cb, error, NULL);
+    grpc_closure_sched(exec_ctx, cb, error);
   }
   if (grpc_tcp_trace) {
     const char *str = grpc_error_string(error);
@@ -217,7 +217,7 @@ static void write_callback(uv_write_t *req, int status) {
   gpr_free(tcp->write_buffers);
   grpc_resource_user_free(&exec_ctx, tcp->resource_user,
                           sizeof(uv_buf_t) * tcp->write_slices->count);
-  grpc_exec_ctx_sched(&exec_ctx, cb, error, NULL);
+  grpc_closure_sched(&exec_ctx, cb, error);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
@@ -243,8 +243,8 @@ static void uv_endpoint_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
   }
 
   if (tcp->shutting_down) {
-    grpc_exec_ctx_sched(exec_ctx, cb,
-                        GRPC_ERROR_CREATE("TCP socket is shutting down"), NULL);
+    grpc_closure_sched(exec_ctx, cb,
+                       GRPC_ERROR_CREATE("TCP socket is shutting down"));
     return;
   }
 
@@ -254,7 +254,7 @@ static void uv_endpoint_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
   if (tcp->write_slices->count == 0) {
     // No slices means we don't have to do anything,
     // and libuv doesn't like empty writes
-    grpc_exec_ctx_sched(exec_ctx, cb, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, cb, GRPC_ERROR_NONE);
     return;
   }
 
diff --git a/src/core/lib/iomgr/tcp_windows.c b/src/core/lib/iomgr/tcp_windows.c
index d4613b674e53870c924fd66bedf9a50112a75eec..84f791ba07af7abf0851d0515addf8624d7e2c7c 100644
--- a/src/core/lib/iomgr/tcp_windows.c
+++ b/src/core/lib/iomgr/tcp_windows.c
@@ -53,6 +53,7 @@
 #include "src/core/lib/iomgr/socket_windows.h"
 #include "src/core/lib/iomgr/tcp_client.h"
 #include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/slice/slice_internal.h"
 
 #if defined(__MSYS__) && defined(GPR_ARCH_64)
 /* Nasty workaround for nasty bug when using the 64 bits msys compiler
@@ -174,13 +175,13 @@ static void on_read(grpc_exec_ctx *exec_ctx, void *tcpp, grpc_error *error) {
       char *utf8_message = gpr_format_message(info->wsa_error);
       error = GRPC_ERROR_CREATE(utf8_message);
       gpr_free(utf8_message);
-      grpc_slice_unref(tcp->read_slice);
+      grpc_slice_unref_internal(exec_ctx, tcp->read_slice);
     } else {
       if (info->bytes_transfered != 0 && !tcp->shutting_down) {
         sub = grpc_slice_sub_no_ref(tcp->read_slice, 0, info->bytes_transfered);
         grpc_slice_buffer_add(tcp->read_slices, sub);
       } else {
-        grpc_slice_unref(tcp->read_slice);
+        grpc_slice_unref_internal(exec_ctx, tcp->read_slice);
         error = GRPC_ERROR_CREATE("End of TCP stream");
       }
     }
@@ -188,7 +189,7 @@ static void on_read(grpc_exec_ctx *exec_ctx, void *tcpp, grpc_error *error) {
 
   tcp->read_cb = NULL;
   TCP_UNREF(exec_ctx, tcp, "read");
-  grpc_exec_ctx_sched(exec_ctx, cb, error, NULL);
+  grpc_closure_sched(exec_ctx, cb, error);
 }
 
 static void win_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
@@ -202,14 +203,14 @@ static void win_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
   WSABUF buffer;
 
   if (tcp->shutting_down) {
-    grpc_exec_ctx_sched(exec_ctx, cb,
-                        GRPC_ERROR_CREATE("TCP socket is shutting down"), NULL);
+    grpc_closure_sched(exec_ctx, cb,
+                       GRPC_ERROR_CREATE("TCP socket is shutting down"));
     return;
   }
 
   tcp->read_cb = cb;
   tcp->read_slices = read_slices;
-  grpc_slice_buffer_reset_and_unref(read_slices);
+  grpc_slice_buffer_reset_and_unref_internal(exec_ctx, read_slices);
 
   tcp->read_slice = grpc_slice_malloc(8192);
 
@@ -227,7 +228,7 @@ static void win_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
   /* Did we get data immediately ? Yay. */
   if (info->wsa_error != WSAEWOULDBLOCK) {
     info->bytes_transfered = bytes_read;
-    grpc_exec_ctx_sched(exec_ctx, &tcp->on_read, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, &tcp->on_read, GRPC_ERROR_NONE);
     return;
   }
 
@@ -240,8 +241,8 @@ static void win_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
     int wsa_error = WSAGetLastError();
     if (wsa_error != WSA_IO_PENDING) {
       info->wsa_error = wsa_error;
-      grpc_exec_ctx_sched(exec_ctx, &tcp->on_read,
-                          GRPC_WSA_ERROR(info->wsa_error, "WSARecv"), NULL);
+      grpc_closure_sched(exec_ctx, &tcp->on_read,
+                         GRPC_WSA_ERROR(info->wsa_error, "WSARecv"));
       return;
     }
   }
@@ -272,7 +273,7 @@ static void on_write(grpc_exec_ctx *exec_ctx, void *tcpp, grpc_error *error) {
   }
 
   TCP_UNREF(exec_ctx, tcp, "write");
-  grpc_exec_ctx_sched(exec_ctx, cb, error, NULL);
+  grpc_closure_sched(exec_ctx, cb, error);
 }
 
 /* Initiates a write. */
@@ -290,8 +291,8 @@ static void win_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
   size_t len;
 
   if (tcp->shutting_down) {
-    grpc_exec_ctx_sched(exec_ctx, cb,
-                        GRPC_ERROR_CREATE("TCP socket is shutting down"), NULL);
+    grpc_closure_sched(exec_ctx, cb,
+                       GRPC_ERROR_CREATE("TCP socket is shutting down"));
     return;
   }
 
@@ -322,7 +323,7 @@ static void win_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
     grpc_error *error = status == 0
                             ? GRPC_ERROR_NONE
                             : GRPC_WSA_ERROR(info->wsa_error, "WSASend");
-    grpc_exec_ctx_sched(exec_ctx, cb, error, NULL);
+    grpc_closure_sched(exec_ctx, cb, error);
     if (allocated) gpr_free(allocated);
     return;
   }
@@ -340,8 +341,7 @@ static void win_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
     int wsa_error = WSAGetLastError();
     if (wsa_error != WSA_IO_PENDING) {
       TCP_UNREF(exec_ctx, tcp, "write");
-      grpc_exec_ctx_sched(exec_ctx, cb, GRPC_WSA_ERROR(wsa_error, "WSASend"),
-                          NULL);
+      grpc_closure_sched(exec_ctx, cb, GRPC_WSA_ERROR(wsa_error, "WSASend"));
       return;
     }
   }
@@ -424,8 +424,8 @@ grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket,
   tcp->socket = socket;
   gpr_mu_init(&tcp->mu);
   gpr_ref_init(&tcp->refcount, 1);
-  grpc_closure_init(&tcp->on_read, on_read, tcp);
-  grpc_closure_init(&tcp->on_write, on_write, tcp);
+  grpc_closure_init(&tcp->on_read, on_read, tcp, grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&tcp->on_write, on_write, tcp, grpc_schedule_on_exec_ctx);
   tcp->peer_string = gpr_strdup(peer_string);
   tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string);
   /* Tell network status tracking code about the new endpoint */
diff --git a/src/core/lib/iomgr/timer.h b/src/core/lib/iomgr/timer.h
index 20fe98c4a7d6588f8b187e8d28cc7ae44e67604f..d84a278b183d2c2dc95af3faa7ab053e84c661ca 100644
--- a/src/core/lib/iomgr/timer.h
+++ b/src/core/lib/iomgr/timer.h
@@ -49,15 +49,15 @@
 
 typedef struct grpc_timer grpc_timer;
 
-/* Initialize *timer. When expired or canceled, timer_cb will be called with
-   *timer_cb_arg and error set to indicate if it expired (GRPC_ERROR_NONE) or
-   was canceled (GRPC_ERROR_CANCELLED). timer_cb is guaranteed to be called
-   exactly once, and application code should check the error to determine
-   how it was invoked. The application callback is also responsible for
-   maintaining information about when to free up any user-level state. */
+/* Initialize *timer. When expired or canceled, closure will be called with
+   error set to indicate if it expired (GRPC_ERROR_NONE) or was canceled
+   (GRPC_ERROR_CANCELLED). timer_cb is guaranteed to be called exactly once, and
+   application code should check the error to determine how it was invoked. The
+   application callback is also responsible for maintaining information about
+   when to free up any user-level state. */
 void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer,
-                     gpr_timespec deadline, grpc_iomgr_cb_func timer_cb,
-                     void *timer_cb_arg, gpr_timespec now);
+                     gpr_timespec deadline, grpc_closure *closure,
+                     gpr_timespec now);
 
 /* Note that there is no timer destroy function. This is because the
    timer is a one-time occurrence with a guarantee that the callback will
diff --git a/src/core/lib/iomgr/timer_generic.c b/src/core/lib/iomgr/timer_generic.c
index 00058f9d86bdbac941f818d99f777ab084771afc..40c83514724c61daef6521ee66540891236bb151 100644
--- a/src/core/lib/iomgr/timer_generic.c
+++ b/src/core/lib/iomgr/timer_generic.c
@@ -178,28 +178,27 @@ static void note_deadline_change(shard_type *shard) {
 }
 
 void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer,
-                     gpr_timespec deadline, grpc_iomgr_cb_func timer_cb,
-                     void *timer_cb_arg, gpr_timespec now) {
+                     gpr_timespec deadline, grpc_closure *closure,
+                     gpr_timespec now) {
   int is_first_timer = 0;
   shard_type *shard = &g_shards[shard_idx(timer)];
   GPR_ASSERT(deadline.clock_type == g_clock_type);
   GPR_ASSERT(now.clock_type == g_clock_type);
-  grpc_closure_init(&timer->closure, timer_cb, timer_cb_arg);
+  timer->closure = closure;
   timer->deadline = deadline;
   timer->triggered = 0;
 
   if (!g_initialized) {
     timer->triggered = 1;
-    grpc_exec_ctx_sched(
-        exec_ctx, &timer->closure,
-        GRPC_ERROR_CREATE("Attempt to create timer before initialization"),
-        NULL);
+    grpc_closure_sched(
+        exec_ctx, timer->closure,
+        GRPC_ERROR_CREATE("Attempt to create timer before initialization"));
     return;
   }
 
   if (gpr_time_cmp(deadline, now) <= 0) {
     timer->triggered = 1;
-    grpc_exec_ctx_sched(exec_ctx, &timer->closure, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, timer->closure, GRPC_ERROR_NONE);
     return;
   }
 
@@ -251,7 +250,7 @@ void grpc_timer_cancel(grpc_exec_ctx *exec_ctx, grpc_timer *timer) {
   shard_type *shard = &g_shards[shard_idx(timer)];
   gpr_mu_lock(&shard->mu);
   if (!timer->triggered) {
-    grpc_exec_ctx_sched(exec_ctx, &timer->closure, GRPC_ERROR_CANCELLED, NULL);
+    grpc_closure_sched(exec_ctx, timer->closure, GRPC_ERROR_CANCELLED);
     timer->triggered = 1;
     if (timer->heap_index == INVALID_HEAP_INDEX) {
       list_remove(timer);
@@ -317,7 +316,7 @@ static size_t pop_timers(grpc_exec_ctx *exec_ctx, shard_type *shard,
   grpc_timer *timer;
   gpr_mu_lock(&shard->mu);
   while ((timer = pop_one(shard, now))) {
-    grpc_exec_ctx_sched(exec_ctx, &timer->closure, GRPC_ERROR_REF(error), NULL);
+    grpc_closure_sched(exec_ctx, timer->closure, GRPC_ERROR_REF(error));
     n++;
   }
   *new_min_deadline = compute_min_deadline(shard);
diff --git a/src/core/lib/iomgr/timer_generic.h b/src/core/lib/iomgr/timer_generic.h
index e4494adb5fe18941e313a9b20881217934a89590..9d901c7e68ff96a272f7d723f052926336cc7663 100644
--- a/src/core/lib/iomgr/timer_generic.h
+++ b/src/core/lib/iomgr/timer_generic.h
@@ -43,7 +43,7 @@ struct grpc_timer {
   int triggered;
   struct grpc_timer *next;
   struct grpc_timer *prev;
-  grpc_closure closure;
+  grpc_closure *closure;
 };
 
 #endif /* GRPC_CORE_LIB_IOMGR_TIMER_GENERIC_H */
diff --git a/src/core/lib/iomgr/timer_uv.c b/src/core/lib/iomgr/timer_uv.c
index cfcb89268b1f644d55a26a718d119bc061e69f6d..fa2cdee9646afae7af224baa769e5665c6cf0a13 100644
--- a/src/core/lib/iomgr/timer_uv.c
+++ b/src/core/lib/iomgr/timer_uv.c
@@ -55,20 +55,20 @@ void run_expired_timer(uv_timer_t *handle) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   GPR_ASSERT(!timer->triggered);
   timer->triggered = 1;
-  grpc_exec_ctx_sched(&exec_ctx, &timer->closure, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(&exec_ctx, timer->closure, GRPC_ERROR_NONE);
   stop_uv_timer(handle);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
 void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer,
-                     gpr_timespec deadline, grpc_iomgr_cb_func timer_cb,
-                     void *timer_cb_arg, gpr_timespec now) {
+                     gpr_timespec deadline, grpc_closure *closure,
+                     gpr_timespec now) {
   uint64_t timeout;
   uv_timer_t *uv_timer;
-  grpc_closure_init(&timer->closure, timer_cb, timer_cb_arg);
+  timer->closure = closure;
   if (gpr_time_cmp(deadline, now) <= 0) {
     timer->triggered = 1;
-    grpc_exec_ctx_sched(exec_ctx, &timer->closure, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, timer->closure, GRPC_ERROR_NONE);
     return;
   }
   timer->triggered = 0;
@@ -83,7 +83,7 @@ void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer,
 void grpc_timer_cancel(grpc_exec_ctx *exec_ctx, grpc_timer *timer) {
   if (!timer->triggered) {
     timer->triggered = 1;
-    grpc_exec_ctx_sched(exec_ctx, &timer->closure, GRPC_ERROR_CANCELLED, NULL);
+    grpc_closure_sched(exec_ctx, timer->closure, GRPC_ERROR_CANCELLED);
     stop_uv_timer((uv_timer_t *)timer->uv_timer);
   }
 }
diff --git a/src/core/lib/iomgr/timer_uv.h b/src/core/lib/iomgr/timer_uv.h
index 3de383ebd5010e79c2f74c28828b8f8d730966f2..13cf8bd4fa0f593876a314f4920711013d712970 100644
--- a/src/core/lib/iomgr/timer_uv.h
+++ b/src/core/lib/iomgr/timer_uv.h
@@ -37,7 +37,7 @@
 #include "src/core/lib/iomgr/exec_ctx.h"
 
 struct grpc_timer {
-  grpc_closure closure;
+  grpc_closure *closure;
   /* This is actually a uv_timer_t*, but we want to keep platform-specific
      types out of headers */
   void *uv_timer;
diff --git a/src/core/lib/iomgr/udp_server.c b/src/core/lib/iomgr/udp_server.c
index 3c24ea9afab9a3e50b44098e02decf53df5ddb48..dfbd295c914df62753be1474018edea0b7e9cf36 100644
--- a/src/core/lib/iomgr/udp_server.c
+++ b/src/core/lib/iomgr/udp_server.c
@@ -126,7 +126,7 @@ grpc_udp_server *grpc_udp_server_create(void) {
 
 static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_udp_server *s) {
   if (s->shutdown_complete != NULL) {
-    grpc_exec_ctx_sched(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE);
   }
 
   gpr_mu_destroy(&s->mu);
@@ -170,8 +170,8 @@ static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_udp_server *s) {
     for (sp = s->head; sp; sp = sp->next) {
       grpc_unlink_if_unix_domain_socket(&sp->addr);
 
-      sp->destroyed_closure.cb = destroyed_port;
-      sp->destroyed_closure.cb_arg = s;
+      grpc_closure_init(&sp->destroyed_closure, destroyed_port, s,
+                        grpc_schedule_on_exec_ctx);
 
       /* Call the orphan_cb to signal that the FD is about to be closed and
        * should no longer be used. */
@@ -446,8 +446,8 @@ void grpc_udp_server_start(grpc_exec_ctx *exec_ctx, grpc_udp_server *s,
     for (i = 0; i < pollset_count; i++) {
       grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd);
     }
-    sp->read_closure.cb = on_read;
-    sp->read_closure.cb_arg = sp;
+    grpc_closure_init(&sp->read_closure, on_read, sp,
+                      grpc_schedule_on_exec_ctx);
     grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure);
 
     s->active_ports++;
diff --git a/src/core/lib/iomgr/workqueue.h b/src/core/lib/iomgr/workqueue.h
index 73d984984303b322cd57592bb488eacebf15eb9d..371b0f55dcae0e941a90e1bff562cfafa5831ade 100644
--- a/src/core/lib/iomgr/workqueue.h
+++ b/src/core/lib/iomgr/workqueue.h
@@ -72,17 +72,16 @@ grpc_workqueue *grpc_workqueue_ref(grpc_workqueue *workqueue);
 void grpc_workqueue_unref(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue);
 #endif
 
-/** Add a work item to a workqueue. Items added to a work queue will be started
-    in approximately the order they were enqueued, on some thread that may or
-    may not be the current thread. Successive closures enqueued onto a workqueue
-    MAY be executed concurrently.
+/** Fetch the workqueue closure scheduler. Items added to a work queue will be
+    started in approximately the order they were enqueued, on some thread that
+    may or may not be the current thread. Successive closures enqueued onto a
+    workqueue MAY be executed concurrently.
 
     It is generally more expensive to add a closure to a workqueue than to the
     execution context, both in terms of CPU work and in execution latency.
 
     Use work queues when it's important that other threads be given a chance to
     tackle some workload. */
-void grpc_workqueue_enqueue(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue,
-                            grpc_closure *closure, grpc_error *error);
+grpc_closure_scheduler *grpc_workqueue_scheduler(grpc_workqueue *workqueue);
 
 #endif /* GRPC_CORE_LIB_IOMGR_WORKQUEUE_H */
diff --git a/src/core/lib/iomgr/workqueue_uv.c b/src/core/lib/iomgr/workqueue_uv.c
index e58ca476ccc08e7670f9b39e61f10061dbfe5530..4d61b409124d4f923450f10f343d9ce7d5bcc13f 100644
--- a/src/core/lib/iomgr/workqueue_uv.c
+++ b/src/core/lib/iomgr/workqueue_uv.c
@@ -58,9 +58,8 @@ grpc_workqueue *grpc_workqueue_ref(grpc_workqueue *workqueue) {
 void grpc_workqueue_unref(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue) {}
 #endif
 
-void grpc_workqueue_enqueue(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue,
-                            grpc_closure *closure, grpc_error *error) {
-  grpc_exec_ctx_sched(exec_ctx, closure, error, NULL);
+grpc_closure_scheduler *grpc_workqueue_scheduler(grpc_workqueue *workqueue) {
+  return grpc_schedule_on_exec_ctx;
 }
 
 #endif /* GPR_UV */
diff --git a/src/core/lib/iomgr/workqueue_windows.c b/src/core/lib/iomgr/workqueue_windows.c
index 5c93d3c59e265b11e1801a8bbe198566ec0a70e6..234b47cdf54c5ca7a8918e9e98ead5bfcdc73219 100644
--- a/src/core/lib/iomgr/workqueue_windows.c
+++ b/src/core/lib/iomgr/workqueue_windows.c
@@ -56,9 +56,8 @@ grpc_workqueue *grpc_workqueue_ref(grpc_workqueue *workqueue) {
 void grpc_workqueue_unref(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue) {}
 #endif
 
-void grpc_workqueue_enqueue(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue,
-                            grpc_closure *closure, grpc_error *error) {
-  grpc_exec_ctx_sched(exec_ctx, closure, error, NULL);
+grpc_closure_scheduler *grpc_workqueue_scheduler(grpc_workqueue *workqueue) {
+  return grpc_schedule_on_exec_ctx;
 }
 
 #endif /* GPR_WINDOWS */
diff --git a/src/core/lib/security/context/security_context.c b/src/core/lib/security/context/security_context.c
index 2204fadf54cf495766027c0613cfd3512598cee9..fe82fabc31a408a41c42f8f9ada555506fa4e367 100644
--- a/src/core/lib/security/context/security_context.c
+++ b/src/core/lib/security/context/security_context.c
@@ -47,6 +47,7 @@
 
 grpc_call_error grpc_call_set_credentials(grpc_call *call,
                                           grpc_call_credentials *creds) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_client_security_context *ctx = NULL;
   GRPC_API_TRACE("grpc_call_set_credentials(call=%p, creds=%p)", 2,
                  (call, creds));
@@ -62,9 +63,10 @@ grpc_call_error grpc_call_set_credentials(grpc_call *call,
     grpc_call_context_set(call, GRPC_CONTEXT_SECURITY, ctx,
                           grpc_client_security_context_destroy);
   } else {
-    grpc_call_credentials_unref(ctx->creds);
+    grpc_call_credentials_unref(&exec_ctx, ctx->creds);
     ctx->creds = grpc_call_credentials_ref(creds);
   }
+  grpc_exec_ctx_finish(&exec_ctx);
   return GRPC_CALL_OK;
 }
 
@@ -96,13 +98,15 @@ grpc_client_security_context *grpc_client_security_context_create(void) {
 }
 
 void grpc_client_security_context_destroy(void *ctx) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_client_security_context *c = (grpc_client_security_context *)ctx;
-  grpc_call_credentials_unref(c->creds);
+  grpc_call_credentials_unref(&exec_ctx, c->creds);
   GRPC_AUTH_CONTEXT_UNREF(c->auth_context, "client_security_context");
   if (c->extension.instance != NULL && c->extension.destroy != NULL) {
     c->extension.destroy(c->extension.instance);
   }
   gpr_free(ctx);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 /* --- grpc_server_security_context --- */
@@ -307,7 +311,7 @@ void grpc_auth_property_reset(grpc_auth_property *property) {
   memset(property, 0, sizeof(grpc_auth_property));
 }
 
-static void auth_context_pointer_arg_destroy(void *p) {
+static void auth_context_pointer_arg_destroy(grpc_exec_ctx *exec_ctx, void *p) {
   GRPC_AUTH_CONTEXT_UNREF(p, "auth_context_pointer_arg");
 }
 
diff --git a/src/core/lib/security/credentials/composite/composite_credentials.c b/src/core/lib/security/credentials/composite/composite_credentials.c
index d55d00b7b6638c25fff68cd406c3c56aa0312382..be1588dd8b9cf82d560d9b505a1a468760fb73e5 100644
--- a/src/core/lib/security/credentials/composite/composite_credentials.c
+++ b/src/core/lib/security/credentials/composite/composite_credentials.c
@@ -54,18 +54,20 @@ typedef struct {
   grpc_credentials_metadata_cb cb;
 } grpc_composite_call_credentials_metadata_context;
 
-static void composite_call_destruct(grpc_call_credentials *creds) {
+static void composite_call_destruct(grpc_exec_ctx *exec_ctx,
+                                    grpc_call_credentials *creds) {
   grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds;
   size_t i;
   for (i = 0; i < c->inner.num_creds; i++) {
-    grpc_call_credentials_unref(c->inner.creds_array[i]);
+    grpc_call_credentials_unref(exec_ctx, c->inner.creds_array[i]);
   }
   gpr_free(c->inner.creds_array);
 }
 
 static void composite_call_md_context_destroy(
+    grpc_exec_ctx *exec_ctx,
     grpc_composite_call_credentials_metadata_context *ctx) {
-  grpc_credentials_md_store_unref(ctx->md_elems);
+  grpc_credentials_md_store_unref(exec_ctx, ctx->md_elems);
   gpr_free(ctx);
 }
 
@@ -103,7 +105,7 @@ static void composite_call_metadata_cb(grpc_exec_ctx *exec_ctx, void *user_data,
   /* We're done!. */
   ctx->cb(exec_ctx, ctx->user_data, ctx->md_elems->entries,
           ctx->md_elems->num_entries, GRPC_CREDENTIALS_OK, NULL);
-  composite_call_md_context_destroy(ctx);
+  composite_call_md_context_destroy(exec_ctx, ctx);
 }
 
 static void composite_call_get_request_metadata(
@@ -209,17 +211,19 @@ grpc_call_credentials *grpc_credentials_contains_type(
 
 /* -- Composite channel credentials. -- */
 
-static void composite_channel_destruct(grpc_channel_credentials *creds) {
+static void composite_channel_destruct(grpc_exec_ctx *exec_ctx,
+                                       grpc_channel_credentials *creds) {
   grpc_composite_channel_credentials *c =
       (grpc_composite_channel_credentials *)creds;
-  grpc_channel_credentials_unref(c->inner_creds);
-  grpc_call_credentials_unref(c->call_creds);
+  grpc_channel_credentials_unref(exec_ctx, c->inner_creds);
+  grpc_call_credentials_unref(exec_ctx, c->call_creds);
 }
 
 static grpc_security_status composite_channel_create_security_connector(
-    grpc_channel_credentials *creds, grpc_call_credentials *call_creds,
-    const char *target, const grpc_channel_args *args,
-    grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
+    grpc_exec_ctx *exec_ctx, grpc_channel_credentials *creds,
+    grpc_call_credentials *call_creds, const char *target,
+    const grpc_channel_args *args, grpc_channel_security_connector **sc,
+    grpc_channel_args **new_args) {
   grpc_composite_channel_credentials *c =
       (grpc_composite_channel_credentials *)creds;
   grpc_security_status status = GRPC_SECURITY_ERROR;
@@ -233,11 +237,12 @@ static grpc_security_status composite_channel_create_security_connector(
     grpc_call_credentials *composite_call_creds =
         grpc_composite_call_credentials_create(c->call_creds, call_creds, NULL);
     status = c->inner_creds->vtable->create_security_connector(
-        c->inner_creds, composite_call_creds, target, args, sc, new_args);
-    grpc_call_credentials_unref(composite_call_creds);
+        exec_ctx, c->inner_creds, composite_call_creds, target, args, sc,
+        new_args);
+    grpc_call_credentials_unref(exec_ctx, composite_call_creds);
   } else {
     status = c->inner_creds->vtable->create_security_connector(
-        c->inner_creds, c->call_creds, target, args, sc, new_args);
+        exec_ctx, c->inner_creds, c->call_creds, target, args, sc, new_args);
   }
   return status;
 }
diff --git a/src/core/lib/security/credentials/credentials.c b/src/core/lib/security/credentials/credentials.c
index 1149e5c2edb012b81205ce271a1115196c7775c0..9781a22a86d5606de98d189b833afd37f7bb1975 100644
--- a/src/core/lib/security/credentials/credentials.c
+++ b/src/core/lib/security/credentials/credentials.c
@@ -66,8 +66,8 @@ grpc_credentials_metadata_request *grpc_credentials_metadata_request_create(
 }
 
 void grpc_credentials_metadata_request_destroy(
-    grpc_credentials_metadata_request *r) {
-  grpc_call_credentials_unref(r->creds);
+    grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *r) {
+  grpc_call_credentials_unref(exec_ctx, r->creds);
   grpc_http_response_destroy(&r->response);
   gpr_free(r);
 }
@@ -79,17 +79,22 @@ grpc_channel_credentials *grpc_channel_credentials_ref(
   return creds;
 }
 
-void grpc_channel_credentials_unref(grpc_channel_credentials *creds) {
+void grpc_channel_credentials_unref(grpc_exec_ctx *exec_ctx,
+                                    grpc_channel_credentials *creds) {
   if (creds == NULL) return;
   if (gpr_unref(&creds->refcount)) {
-    if (creds->vtable->destruct != NULL) creds->vtable->destruct(creds);
+    if (creds->vtable->destruct != NULL) {
+      creds->vtable->destruct(exec_ctx, creds);
+    }
     gpr_free(creds);
   }
 }
 
 void grpc_channel_credentials_release(grpc_channel_credentials *creds) {
   GRPC_API_TRACE("grpc_channel_credentials_release(creds=%p)", 1, (creds));
-  grpc_channel_credentials_unref(creds);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_channel_credentials_unref(&exec_ctx, creds);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 grpc_call_credentials *grpc_call_credentials_ref(grpc_call_credentials *creds) {
@@ -98,17 +103,22 @@ grpc_call_credentials *grpc_call_credentials_ref(grpc_call_credentials *creds) {
   return creds;
 }
 
-void grpc_call_credentials_unref(grpc_call_credentials *creds) {
+void grpc_call_credentials_unref(grpc_exec_ctx *exec_ctx,
+                                 grpc_call_credentials *creds) {
   if (creds == NULL) return;
   if (gpr_unref(&creds->refcount)) {
-    if (creds->vtable->destruct != NULL) creds->vtable->destruct(creds);
+    if (creds->vtable->destruct != NULL) {
+      creds->vtable->destruct(exec_ctx, creds);
+    }
     gpr_free(creds);
   }
 }
 
 void grpc_call_credentials_release(grpc_call_credentials *creds) {
   GRPC_API_TRACE("grpc_call_credentials_release(creds=%p)", 1, (creds));
-  grpc_call_credentials_unref(creds);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_call_credentials_unref(&exec_ctx, creds);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 void grpc_call_credentials_get_request_metadata(
@@ -126,16 +136,16 @@ void grpc_call_credentials_get_request_metadata(
 }
 
 grpc_security_status grpc_channel_credentials_create_security_connector(
-    grpc_channel_credentials *channel_creds, const char *target,
-    const grpc_channel_args *args, grpc_channel_security_connector **sc,
-    grpc_channel_args **new_args) {
+    grpc_exec_ctx *exec_ctx, grpc_channel_credentials *channel_creds,
+    const char *target, const grpc_channel_args *args,
+    grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
   *new_args = NULL;
   if (channel_creds == NULL) {
     return GRPC_SECURITY_ERROR;
   }
   GPR_ASSERT(channel_creds->vtable->create_security_connector != NULL);
   return channel_creds->vtable->create_security_connector(
-      channel_creds, NULL, target, args, sc, new_args);
+      exec_ctx, channel_creds, NULL, target, args, sc, new_args);
 }
 
 grpc_channel_credentials *
@@ -157,10 +167,13 @@ grpc_server_credentials *grpc_server_credentials_ref(
   return creds;
 }
 
-void grpc_server_credentials_unref(grpc_server_credentials *creds) {
+void grpc_server_credentials_unref(grpc_exec_ctx *exec_ctx,
+                                   grpc_server_credentials *creds) {
   if (creds == NULL) return;
   if (gpr_unref(&creds->refcount)) {
-    if (creds->vtable->destruct != NULL) creds->vtable->destruct(creds);
+    if (creds->vtable->destruct != NULL) {
+      creds->vtable->destruct(exec_ctx, creds);
+    }
     if (creds->processor.destroy != NULL && creds->processor.state != NULL) {
       creds->processor.destroy(creds->processor.state);
     }
@@ -170,16 +183,19 @@ void grpc_server_credentials_unref(grpc_server_credentials *creds) {
 
 void grpc_server_credentials_release(grpc_server_credentials *creds) {
   GRPC_API_TRACE("grpc_server_credentials_release(creds=%p)", 1, (creds));
-  grpc_server_credentials_unref(creds);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_server_credentials_unref(&exec_ctx, creds);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 grpc_security_status grpc_server_credentials_create_security_connector(
-    grpc_server_credentials *creds, grpc_server_security_connector **sc) {
+    grpc_exec_ctx *exec_ctx, grpc_server_credentials *creds,
+    grpc_server_security_connector **sc) {
   if (creds == NULL || creds->vtable->create_security_connector == NULL) {
     gpr_log(GPR_ERROR, "Server credentials cannot create security context.");
     return GRPC_SECURITY_ERROR;
   }
-  return creds->vtable->create_security_connector(creds, sc);
+  return creds->vtable->create_security_connector(exec_ctx, creds, sc);
 }
 
 void grpc_server_credentials_set_auth_metadata_processor(
@@ -196,8 +212,9 @@ void grpc_server_credentials_set_auth_metadata_processor(
   creds->processor = processor;
 }
 
-static void server_credentials_pointer_arg_destroy(void *p) {
-  grpc_server_credentials_unref(p);
+static void server_credentials_pointer_arg_destroy(grpc_exec_ctx *exec_ctx,
+                                                   void *p) {
+  grpc_server_credentials_unref(exec_ctx, p);
 }
 
 static void *server_credentials_pointer_arg_copy(void *p) {
diff --git a/src/core/lib/security/credentials/credentials.h b/src/core/lib/security/credentials/credentials.h
index 85b3bc53503e28b1d9dd5da93cb64a85a6e98450..3011df6b8a2aa93400819a1a6a436d9d290f3c3a 100644
--- a/src/core/lib/security/credentials/credentials.h
+++ b/src/core/lib/security/credentials/credentials.h
@@ -101,12 +101,13 @@ void grpc_override_well_known_credentials_path_getter(
 /* --- grpc_channel_credentials. --- */
 
 typedef struct {
-  void (*destruct)(grpc_channel_credentials *c);
+  void (*destruct)(grpc_exec_ctx *exec_ctx, grpc_channel_credentials *c);
 
   grpc_security_status (*create_security_connector)(
-      grpc_channel_credentials *c, grpc_call_credentials *call_creds,
-      const char *target, const grpc_channel_args *args,
-      grpc_channel_security_connector **sc, grpc_channel_args **new_args);
+      grpc_exec_ctx *exec_ctx, grpc_channel_credentials *c,
+      grpc_call_credentials *call_creds, const char *target,
+      const grpc_channel_args *args, grpc_channel_security_connector **sc,
+      grpc_channel_args **new_args);
 
   grpc_channel_credentials *(*duplicate_without_call_credentials)(
       grpc_channel_credentials *c);
@@ -120,16 +121,17 @@ struct grpc_channel_credentials {
 
 grpc_channel_credentials *grpc_channel_credentials_ref(
     grpc_channel_credentials *creds);
-void grpc_channel_credentials_unref(grpc_channel_credentials *creds);
+void grpc_channel_credentials_unref(grpc_exec_ctx *exec_ctx,
+                                    grpc_channel_credentials *creds);
 
 /* Creates a security connector for the channel. May also create new channel
    args for the channel to be used in place of the passed in const args if
    returned non NULL. In that case the caller is responsible for destroying
    new_args after channel creation. */
 grpc_security_status grpc_channel_credentials_create_security_connector(
-    grpc_channel_credentials *creds, const char *target,
-    const grpc_channel_args *args, grpc_channel_security_connector **sc,
-    grpc_channel_args **new_args);
+    grpc_exec_ctx *exec_ctx, grpc_channel_credentials *creds,
+    const char *target, const grpc_channel_args *args,
+    grpc_channel_security_connector **sc, grpc_channel_args **new_args);
 
 /* Creates a version of the channel credentials without any attached call
    credentials. This can be used in order to open a channel to a non-trusted
@@ -162,7 +164,8 @@ void grpc_credentials_md_store_add_cstrings(grpc_credentials_md_store *store,
                                             const char *key, const char *value);
 grpc_credentials_md_store *grpc_credentials_md_store_ref(
     grpc_credentials_md_store *store);
-void grpc_credentials_md_store_unref(grpc_credentials_md_store *store);
+void grpc_credentials_md_store_unref(grpc_exec_ctx *exec_ctx,
+                                     grpc_credentials_md_store *store);
 
 /* --- grpc_call_credentials. --- */
 
@@ -172,7 +175,7 @@ typedef void (*grpc_credentials_metadata_cb)(
     size_t num_md, grpc_credentials_status status, const char *error_details);
 
 typedef struct {
-  void (*destruct)(grpc_call_credentials *c);
+  void (*destruct)(grpc_exec_ctx *exec_ctx, grpc_call_credentials *c);
   void (*get_request_metadata)(grpc_exec_ctx *exec_ctx,
                                grpc_call_credentials *c,
                                grpc_polling_entity *pollent,
@@ -188,7 +191,8 @@ struct grpc_call_credentials {
 };
 
 grpc_call_credentials *grpc_call_credentials_ref(grpc_call_credentials *creds);
-void grpc_call_credentials_unref(grpc_call_credentials *creds);
+void grpc_call_credentials_unref(grpc_exec_ctx *exec_ctx,
+                                 grpc_call_credentials *creds);
 void grpc_call_credentials_get_request_metadata(
     grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
     grpc_polling_entity *pollent, grpc_auth_metadata_context context,
@@ -202,9 +206,10 @@ grpc_call_credentials *grpc_md_only_test_credentials_create(
 /* --- grpc_server_credentials. --- */
 
 typedef struct {
-  void (*destruct)(grpc_server_credentials *c);
+  void (*destruct)(grpc_exec_ctx *exec_ctx, grpc_server_credentials *c);
   grpc_security_status (*create_security_connector)(
-      grpc_server_credentials *c, grpc_server_security_connector **sc);
+      grpc_exec_ctx *exec_ctx, grpc_server_credentials *c,
+      grpc_server_security_connector **sc);
 } grpc_server_credentials_vtable;
 
 struct grpc_server_credentials {
@@ -215,12 +220,14 @@ struct grpc_server_credentials {
 };
 
 grpc_security_status grpc_server_credentials_create_security_connector(
-    grpc_server_credentials *creds, grpc_server_security_connector **sc);
+    grpc_exec_ctx *exec_ctx, grpc_server_credentials *creds,
+    grpc_server_security_connector **sc);
 
 grpc_server_credentials *grpc_server_credentials_ref(
     grpc_server_credentials *creds);
 
-void grpc_server_credentials_unref(grpc_server_credentials *creds);
+void grpc_server_credentials_unref(grpc_exec_ctx *exec_ctx,
+                                   grpc_server_credentials *creds);
 
 #define GRPC_SERVER_CREDENTIALS_ARG "grpc.server_credentials"
 
@@ -243,6 +250,6 @@ grpc_credentials_metadata_request *grpc_credentials_metadata_request_create(
     void *user_data);
 
 void grpc_credentials_metadata_request_destroy(
-    grpc_credentials_metadata_request *r);
+    grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *r);
 
 #endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_CREDENTIALS_H */
diff --git a/src/core/lib/security/credentials/credentials_metadata.c b/src/core/lib/security/credentials/credentials_metadata.c
index e6cb56773477438efe1a2eefb7d28e26c1ef393e..68da5fb4a8bde28f7fa701a3f2097a8c400f6f04 100644
--- a/src/core/lib/security/credentials/credentials_metadata.c
+++ b/src/core/lib/security/credentials/credentials_metadata.c
@@ -37,6 +37,8 @@
 
 #include <string.h>
 
+#include "src/core/lib/slice/slice_internal.h"
+
 static void store_ensure_capacity(grpc_credentials_md_store *store) {
   if (store->num_entries == store->allocated) {
     store->allocated = (store->allocated == 0) ? 1 : store->allocated * 2;
@@ -62,8 +64,8 @@ void grpc_credentials_md_store_add(grpc_credentials_md_store *store,
                                    grpc_slice key, grpc_slice value) {
   if (store == NULL) return;
   store_ensure_capacity(store);
-  store->entries[store->num_entries].key = grpc_slice_ref(key);
-  store->entries[store->num_entries].value = grpc_slice_ref(value);
+  store->entries[store->num_entries].key = grpc_slice_ref_internal(key);
+  store->entries[store->num_entries].value = grpc_slice_ref_internal(value);
   store->num_entries++;
 }
 
@@ -85,14 +87,15 @@ grpc_credentials_md_store *grpc_credentials_md_store_ref(
   return store;
 }
 
-void grpc_credentials_md_store_unref(grpc_credentials_md_store *store) {
+void grpc_credentials_md_store_unref(grpc_exec_ctx *exec_ctx,
+                                     grpc_credentials_md_store *store) {
   if (store == NULL) return;
   if (gpr_unref(&store->refcount)) {
     if (store->entries != NULL) {
       size_t i;
       for (i = 0; i < store->num_entries; i++) {
-        grpc_slice_unref(store->entries[i].key);
-        grpc_slice_unref(store->entries[i].value);
+        grpc_slice_unref_internal(exec_ctx, store->entries[i].key);
+        grpc_slice_unref_internal(exec_ctx, store->entries[i].value);
       }
       gpr_free(store->entries);
     }
diff --git a/src/core/lib/security/credentials/fake/fake_credentials.c b/src/core/lib/security/credentials/fake/fake_credentials.c
index ea4cb76fb99f3fa3e464865bedd7dc899c0cc133..a8679d097d0fb985d3262f873657d8c787b95421 100644
--- a/src/core/lib/security/credentials/fake/fake_credentials.c
+++ b/src/core/lib/security/credentials/fake/fake_credentials.c
@@ -45,16 +45,18 @@
 /* -- Fake transport security credentials. -- */
 
 static grpc_security_status fake_transport_security_create_security_connector(
-    grpc_channel_credentials *c, grpc_call_credentials *call_creds,
-    const char *target, const grpc_channel_args *args,
-    grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
+    grpc_exec_ctx *exec_ctx, grpc_channel_credentials *c,
+    grpc_call_credentials *call_creds, const char *target,
+    const grpc_channel_args *args, grpc_channel_security_connector **sc,
+    grpc_channel_args **new_args) {
   *sc = grpc_fake_channel_security_connector_create(call_creds);
   return GRPC_SECURITY_OK;
 }
 
 static grpc_security_status
 fake_transport_security_server_create_security_connector(
-    grpc_server_credentials *c, grpc_server_security_connector **sc) {
+    grpc_exec_ctx *exec_ctx, grpc_server_credentials *c,
+    grpc_server_security_connector **sc) {
   *sc = grpc_fake_server_security_connector_create();
   return GRPC_SECURITY_OK;
 }
@@ -89,9 +91,10 @@ grpc_server_credentials *grpc_fake_transport_security_server_credentials_create(
 
 /* -- Metadata-only test credentials. -- */
 
-static void md_only_test_destruct(grpc_call_credentials *creds) {
+static void md_only_test_destruct(grpc_exec_ctx *exec_ctx,
+                                  grpc_call_credentials *creds) {
   grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds;
-  grpc_credentials_md_store_unref(c->md_store);
+  grpc_credentials_md_store_unref(exec_ctx, c->md_store);
 }
 
 static void on_simulated_token_fetch_done(grpc_exec_ctx *exec_ctx,
@@ -101,7 +104,7 @@ static void on_simulated_token_fetch_done(grpc_exec_ctx *exec_ctx,
   grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)r->creds;
   r->cb(exec_ctx, r->user_data, c->md_store->entries, c->md_store->num_entries,
         GRPC_CREDENTIALS_OK, NULL);
-  grpc_credentials_metadata_request_destroy(r);
+  grpc_credentials_metadata_request_destroy(exec_ctx, r);
 }
 
 static void md_only_test_get_request_metadata(
@@ -113,9 +116,10 @@ static void md_only_test_get_request_metadata(
   if (c->is_async) {
     grpc_credentials_metadata_request *cb_arg =
         grpc_credentials_metadata_request_create(creds, cb, user_data);
-    grpc_executor_push(
-        grpc_closure_create(on_simulated_token_fetch_done, cb_arg),
-        GRPC_ERROR_NONE);
+    grpc_closure_sched(exec_ctx,
+                       grpc_closure_create(on_simulated_token_fetch_done,
+                                           cb_arg, grpc_executor_scheduler),
+                       GRPC_ERROR_NONE);
   } else {
     cb(exec_ctx, user_data, c->md_store->entries, 1, GRPC_CREDENTIALS_OK, NULL);
   }
diff --git a/src/core/lib/security/credentials/google_default/google_default_credentials.c b/src/core/lib/security/credentials/google_default/google_default_credentials.c
index afe0e3d357b24ce25429eed6aba07aec6fa36cdc..d6e1fe3dcfc378a83158eac464eea7a44e222e8a 100644
--- a/src/core/lib/security/credentials/google_default/google_default_credentials.c
+++ b/src/core/lib/security/credentials/google_default/google_default_credentials.c
@@ -45,6 +45,7 @@
 #include "src/core/lib/iomgr/polling_entity.h"
 #include "src/core/lib/security/credentials/jwt/jwt_credentials.h"
 #include "src/core/lib/security/credentials/oauth2/oauth2_credentials.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"
@@ -101,11 +102,10 @@ static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, grpc_error *e) {
   grpc_pollset_destroy(p);
 }
 
-static int is_stack_running_on_compute_engine(void) {
+static int is_stack_running_on_compute_engine(grpc_exec_ctx *exec_ctx) {
   compute_engine_detector detector;
   grpc_httpcli_request request;
   grpc_httpcli_context context;
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_closure destroy_closure;
 
   /* The http call is local. If it takes more than one sec, it is for sure not
@@ -128,13 +128,14 @@ static int is_stack_running_on_compute_engine(void) {
   grpc_resource_quota *resource_quota =
       grpc_resource_quota_create("google_default_credentials");
   grpc_httpcli_get(
-      &exec_ctx, &context, &detector.pollent, resource_quota, &request,
+      exec_ctx, &context, &detector.pollent, resource_quota, &request,
       gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), max_detection_delay),
-      grpc_closure_create(on_compute_engine_detection_http_response, &detector),
+      grpc_closure_create(on_compute_engine_detection_http_response, &detector,
+                          grpc_schedule_on_exec_ctx),
       &detector.response);
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+  grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
 
-  grpc_exec_ctx_flush(&exec_ctx);
+  grpc_exec_ctx_flush(exec_ctx);
 
   /* Block until we get the response. This is not ideal but this should only be
      called once for the lifetime of the process by the default credentials. */
@@ -143,7 +144,7 @@ static int is_stack_running_on_compute_engine(void) {
     grpc_pollset_worker *worker = NULL;
     if (!GRPC_LOG_IF_ERROR(
             "pollset_work",
-            grpc_pollset_work(&exec_ctx,
+            grpc_pollset_work(exec_ctx,
                               grpc_polling_entity_pollset(&detector.pollent),
                               &worker, gpr_now(GPR_CLOCK_MONOTONIC),
                               gpr_inf_future(GPR_CLOCK_MONOTONIC)))) {
@@ -155,12 +156,13 @@ static int is_stack_running_on_compute_engine(void) {
 
   grpc_httpcli_context_destroy(&context);
   grpc_closure_init(&destroy_closure, destroy_pollset,
-                    grpc_polling_entity_pollset(&detector.pollent));
-  grpc_pollset_shutdown(&exec_ctx,
+                    grpc_polling_entity_pollset(&detector.pollent),
+                    grpc_schedule_on_exec_ctx);
+  grpc_pollset_shutdown(exec_ctx,
                         grpc_polling_entity_pollset(&detector.pollent),
                         &destroy_closure);
-  grpc_exec_ctx_finish(&exec_ctx);
   g_polling_mu = NULL;
+  grpc_exec_ctx_flush(exec_ctx);
 
   gpr_free(grpc_polling_entity_pollset(&detector.pollent));
   grpc_http_response_destroy(&detector.response);
@@ -170,7 +172,7 @@ static int is_stack_running_on_compute_engine(void) {
 
 /* Takes ownership of creds_path if not NULL. */
 static grpc_error *create_default_creds_from_path(
-    char *creds_path, grpc_call_credentials **creds) {
+    grpc_exec_ctx *exec_ctx, char *creds_path, grpc_call_credentials **creds) {
   grpc_json *json = NULL;
   grpc_auth_json_key key;
   grpc_auth_refresh_token token;
@@ -200,7 +202,7 @@ static grpc_error *create_default_creds_from_path(
   if (grpc_auth_json_key_is_valid(&key)) {
     result =
         grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
-            key, grpc_max_auth_token_lifetime());
+            exec_ctx, key, grpc_max_auth_token_lifetime());
     if (result == NULL) {
       error = GRPC_ERROR_CREATE(
           "grpc_service_account_jwt_access_credentials_create_from_auth_json_"
@@ -225,7 +227,7 @@ static grpc_error *create_default_creds_from_path(
 end:
   GPR_ASSERT((result == NULL) + (error == GRPC_ERROR_NONE) == 1);
   if (creds_path != NULL) gpr_free(creds_path);
-  grpc_slice_unref(creds_data);
+  grpc_slice_unref_internal(exec_ctx, creds_data);
   if (json != NULL) grpc_json_destroy(json);
   *creds = result;
   return error;
@@ -236,6 +238,7 @@ grpc_channel_credentials *grpc_google_default_credentials_create(void) {
   grpc_call_credentials *call_creds = NULL;
   grpc_error *error = GRPC_ERROR_CREATE("Failed to create Google credentials");
   grpc_error *err;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
   GRPC_API_TRACE("grpc_google_default_credentials_create(void)", 0, ());
 
@@ -250,20 +253,22 @@ grpc_channel_credentials *grpc_google_default_credentials_create(void) {
 
   /* First, try the environment variable. */
   err = create_default_creds_from_path(
-      gpr_getenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR), &call_creds);
+      &exec_ctx, gpr_getenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR), &call_creds);
   if (err == GRPC_ERROR_NONE) goto end;
   error = grpc_error_add_child(error, err);
 
   /* Then the well-known file. */
   err = create_default_creds_from_path(
-      grpc_get_well_known_google_credentials_file_path(), &call_creds);
+      &exec_ctx, grpc_get_well_known_google_credentials_file_path(),
+      &call_creds);
   if (err == GRPC_ERROR_NONE) goto end;
   error = grpc_error_add_child(error, err);
 
   /* At last try to see if we're on compute engine (do the detection only once
      since it requires a network test). */
   if (!compute_engine_detection_done) {
-    int need_compute_engine_creds = is_stack_running_on_compute_engine();
+    int need_compute_engine_creds =
+        is_stack_running_on_compute_engine(&exec_ctx);
     compute_engine_detection_done = 1;
     if (need_compute_engine_creds) {
       call_creds = grpc_google_compute_engine_credentials_create(NULL);
@@ -286,8 +291,8 @@ end:
           grpc_composite_channel_credentials_create(ssl_creds, call_creds,
                                                     NULL));
       GPR_ASSERT(default_credentials != NULL);
-      grpc_channel_credentials_unref(ssl_creds);
-      grpc_call_credentials_unref(call_creds);
+      grpc_channel_credentials_unref(&exec_ctx, ssl_creds);
+      grpc_call_credentials_unref(&exec_ctx, call_creds);
       result = default_credentials;
     } else {
       gpr_log(GPR_ERROR, "Could not create google default credentials.");
@@ -299,18 +304,21 @@ end:
   } else {
     GRPC_ERROR_UNREF(error);
   }
+  grpc_exec_ctx_finish(&exec_ctx);
   return result;
 }
 
 void grpc_flush_cached_google_default_credentials(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   gpr_once_init(&g_once, init_default_credentials);
   gpr_mu_lock(&g_state_mu);
   if (default_credentials != NULL) {
-    grpc_channel_credentials_unref(default_credentials);
+    grpc_channel_credentials_unref(&exec_ctx, default_credentials);
     default_credentials = NULL;
   }
   compute_engine_detection_done = 0;
   gpr_mu_unlock(&g_state_mu);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 /* -- Well known credentials path. -- */
diff --git a/src/core/lib/security/credentials/iam/iam_credentials.c b/src/core/lib/security/credentials/iam/iam_credentials.c
index 370a384d0e9127ab4a78bbd20bbce603995ce7cd..abd69a967038acbdd9e8354e3e08102e53a4e62f 100644
--- a/src/core/lib/security/credentials/iam/iam_credentials.c
+++ b/src/core/lib/security/credentials/iam/iam_credentials.c
@@ -42,9 +42,10 @@
 #include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
 
-static void iam_destruct(grpc_call_credentials *creds) {
+static void iam_destruct(grpc_exec_ctx *exec_ctx,
+                         grpc_call_credentials *creds) {
   grpc_google_iam_credentials *c = (grpc_google_iam_credentials *)creds;
-  grpc_credentials_md_store_unref(c->iam_md);
+  grpc_credentials_md_store_unref(exec_ctx, c->iam_md);
 }
 
 static void iam_get_request_metadata(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.c b/src/core/lib/security/credentials/jwt/jwt_credentials.c
index 3daf0f4ef704115898f317ac214754e0bce5ca3a..616be64a54b59610a69e9364c80d65c3d8c93077 100644
--- a/src/core/lib/security/credentials/jwt/jwt_credentials.c
+++ b/src/core/lib/security/credentials/jwt/jwt_credentials.c
@@ -42,9 +42,10 @@
 #include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
 
-static void jwt_reset_cache(grpc_service_account_jwt_access_credentials *c) {
+static void jwt_reset_cache(grpc_exec_ctx *exec_ctx,
+                            grpc_service_account_jwt_access_credentials *c) {
   if (c->cached.jwt_md != NULL) {
-    grpc_credentials_md_store_unref(c->cached.jwt_md);
+    grpc_credentials_md_store_unref(exec_ctx, c->cached.jwt_md);
     c->cached.jwt_md = NULL;
   }
   if (c->cached.service_url != NULL) {
@@ -54,11 +55,12 @@ static void jwt_reset_cache(grpc_service_account_jwt_access_credentials *c) {
   c->cached.jwt_expiration = gpr_inf_past(GPR_CLOCK_REALTIME);
 }
 
-static void jwt_destruct(grpc_call_credentials *creds) {
+static void jwt_destruct(grpc_exec_ctx *exec_ctx,
+                         grpc_call_credentials *creds) {
   grpc_service_account_jwt_access_credentials *c =
       (grpc_service_account_jwt_access_credentials *)creds;
   grpc_auth_json_key_destruct(&c->key);
-  jwt_reset_cache(c);
+  jwt_reset_cache(exec_ctx, c);
   gpr_mu_destroy(&c->cache_mu);
 }
 
@@ -92,7 +94,7 @@ static void jwt_get_request_metadata(grpc_exec_ctx *exec_ctx,
     char *jwt = NULL;
     /* Generate a new jwt. */
     gpr_mu_lock(&c->cache_mu);
-    jwt_reset_cache(c);
+    jwt_reset_cache(exec_ctx, c);
     jwt = grpc_jwt_encode_and_sign(&c->key, context.service_url,
                                    c->jwt_lifetime, NULL);
     if (jwt != NULL) {
@@ -114,7 +116,7 @@ static void jwt_get_request_metadata(grpc_exec_ctx *exec_ctx,
   if (jwt_md != NULL) {
     cb(exec_ctx, user_data, jwt_md->entries, jwt_md->num_entries,
        GRPC_CREDENTIALS_OK, NULL);
-    grpc_credentials_md_store_unref(jwt_md);
+    grpc_credentials_md_store_unref(exec_ctx, jwt_md);
   } else {
     cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_ERROR,
        "Could not generate JWT.");
@@ -126,7 +128,8 @@ static grpc_call_credentials_vtable jwt_vtable = {jwt_destruct,
 
 grpc_call_credentials *
 grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
-    grpc_auth_json_key key, gpr_timespec token_lifetime) {
+    grpc_exec_ctx *exec_ctx, grpc_auth_json_key key,
+    gpr_timespec token_lifetime) {
   grpc_service_account_jwt_access_credentials *c;
   if (!grpc_auth_json_key_is_valid(&key)) {
     gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation");
@@ -140,7 +143,7 @@ grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
   c->key = key;
   c->jwt_lifetime = token_lifetime;
   gpr_mu_init(&c->cache_mu);
-  jwt_reset_cache(c);
+  jwt_reset_cache(exec_ctx, c);
   return &c->base;
 }
 
@@ -183,6 +186,11 @@ grpc_call_credentials *grpc_service_account_jwt_access_credentials_create(
     gpr_free(clean_json);
   }
   GPR_ASSERT(reserved == NULL);
-  return grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
-      grpc_auth_json_key_create_from_string(json_key), token_lifetime);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_call_credentials *creds =
+      grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
+          &exec_ctx, grpc_auth_json_key_create_from_string(json_key),
+          token_lifetime);
+  grpc_exec_ctx_finish(&exec_ctx);
+  return creds;
 }
diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.h b/src/core/lib/security/credentials/jwt/jwt_credentials.h
index d5726061794dbf54bc8e31c4f306394cbc70dc12..39b7aeafe85464c6d00a46f5a235152598192fef 100644
--- a/src/core/lib/security/credentials/jwt/jwt_credentials.h
+++ b/src/core/lib/security/credentials/jwt/jwt_credentials.h
@@ -57,6 +57,7 @@ typedef struct {
 // Takes ownership of the key.
 grpc_call_credentials *
 grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
-    grpc_auth_json_key key, gpr_timespec token_lifetime);
+    grpc_exec_ctx *exec_ctx, grpc_auth_json_key key,
+    gpr_timespec token_lifetime);
 
 #endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_CREDENTIALS_H */
diff --git a/src/core/lib/security/credentials/jwt/jwt_verifier.c b/src/core/lib/security/credentials/jwt/jwt_verifier.c
index 42bd89dd0acd9e4c6d4a7dd2877a9e81989587e4..2270be8f4493d320daee8f15a4d2b06c77e964e9 100644
--- a/src/core/lib/security/credentials/jwt/jwt_verifier.c
+++ b/src/core/lib/security/credentials/jwt/jwt_verifier.c
@@ -36,11 +36,6 @@
 #include <limits.h>
 #include <string.h>
 
-#include "src/core/lib/http/httpcli.h"
-#include "src/core/lib/iomgr/polling_entity.h"
-#include "src/core/lib/security/util/b64.h"
-#include "src/core/lib/tsi/ssl_types.h"
-
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
@@ -48,6 +43,13 @@
 #include <grpc/support/useful.h>
 #include <openssl/pem.h>
 
+#include "src/core/lib/http/httpcli.h"
+#include "src/core/lib/iomgr/polling_entity.h"
+#include "src/core/lib/security/util/b64.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/tsi/ssl_types.h"
+
 /* --- Utils. --- */
 
 const char *grpc_jwt_verifier_status_to_string(
@@ -84,11 +86,12 @@ static const EVP_MD *evp_md_from_alg(const char *alg) {
   }
 }
 
-static grpc_json *parse_json_part_from_jwt(const char *str, size_t len,
+static grpc_json *parse_json_part_from_jwt(grpc_exec_ctx *exec_ctx,
+                                           const char *str, size_t len,
                                            grpc_slice *buffer) {
   grpc_json *json;
 
-  *buffer = grpc_base64_decode_with_len(str, len, 1);
+  *buffer = grpc_base64_decode_with_len(exec_ctx, str, len, 1);
   if (GRPC_SLICE_IS_EMPTY(*buffer)) {
     gpr_log(GPR_ERROR, "Invalid base64.");
     return NULL;
@@ -96,7 +99,7 @@ static grpc_json *parse_json_part_from_jwt(const char *str, size_t len,
   json = grpc_json_parse_string_with_len((char *)GRPC_SLICE_START_PTR(*buffer),
                                          GRPC_SLICE_LENGTH(*buffer));
   if (json == NULL) {
-    grpc_slice_unref(*buffer);
+    grpc_slice_unref_internal(exec_ctx, *buffer);
     gpr_log(GPR_ERROR, "JSON parsing error.");
   }
   return json;
@@ -132,13 +135,14 @@ typedef struct {
   grpc_slice buffer;
 } jose_header;
 
-static void jose_header_destroy(jose_header *h) {
-  grpc_slice_unref(h->buffer);
+static void jose_header_destroy(grpc_exec_ctx *exec_ctx, jose_header *h) {
+  grpc_slice_unref_internal(exec_ctx, h->buffer);
   gpr_free(h);
 }
 
 /* Takes ownership of json and buffer. */
-static jose_header *jose_header_from_json(grpc_json *json, grpc_slice buffer) {
+static jose_header *jose_header_from_json(grpc_exec_ctx *exec_ctx,
+                                          grpc_json *json, grpc_slice buffer) {
   grpc_json *cur;
   jose_header *h = gpr_malloc(sizeof(jose_header));
   memset(h, 0, sizeof(jose_header));
@@ -173,7 +177,7 @@ static jose_header *jose_header_from_json(grpc_json *json, grpc_slice buffer) {
 
 error:
   grpc_json_destroy(json);
-  jose_header_destroy(h);
+  jose_header_destroy(exec_ctx, h);
   return NULL;
 }
 
@@ -193,9 +197,9 @@ struct grpc_jwt_claims {
   grpc_slice buffer;
 };
 
-void grpc_jwt_claims_destroy(grpc_jwt_claims *claims) {
+void grpc_jwt_claims_destroy(grpc_exec_ctx *exec_ctx, grpc_jwt_claims *claims) {
   grpc_json_destroy(claims->json);
-  grpc_slice_unref(claims->buffer);
+  grpc_slice_unref_internal(exec_ctx, claims->buffer);
   gpr_free(claims);
 }
 
@@ -240,7 +244,8 @@ gpr_timespec grpc_jwt_claims_not_before(const grpc_jwt_claims *claims) {
 }
 
 /* Takes ownership of json and buffer even in case of failure. */
-grpc_jwt_claims *grpc_jwt_claims_from_json(grpc_json *json, grpc_slice buffer) {
+grpc_jwt_claims *grpc_jwt_claims_from_json(grpc_exec_ctx *exec_ctx,
+                                           grpc_json *json, grpc_slice buffer) {
   grpc_json *cur;
   grpc_jwt_claims *claims = gpr_malloc(sizeof(grpc_jwt_claims));
   memset(claims, 0, sizeof(grpc_jwt_claims));
@@ -281,7 +286,7 @@ grpc_jwt_claims *grpc_jwt_claims_from_json(grpc_json *json, grpc_slice buffer) {
   return claims;
 
 error:
-  grpc_jwt_claims_destroy(claims);
+  grpc_jwt_claims_destroy(exec_ctx, claims);
   return NULL;
 }
 
@@ -305,6 +310,17 @@ grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims *claims,
     return GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE;
   }
 
+  /* This should be probably up to the upper layer to decide but let's harcode
+     the 99% use case here for email issuers, where the JWT must be self
+     issued. */
+  if (grpc_jwt_issuer_email_domain(claims->iss) != NULL &&
+      claims->sub != NULL && strcmp(claims->iss, claims->sub) != 0) {
+    gpr_log(GPR_ERROR,
+            "Email issuer (%s) cannot assert another subject (%s) than itself.",
+            claims->iss, claims->sub);
+    return GRPC_JWT_VERIFIER_BAD_SUBJECT;
+  }
+
   if (audience == NULL) {
     audience_ok = claims->aud == NULL;
   } else {
@@ -362,12 +378,12 @@ static verifier_cb_ctx *verifier_cb_ctx_create(
   return ctx;
 }
 
-void verifier_cb_ctx_destroy(verifier_cb_ctx *ctx) {
+void verifier_cb_ctx_destroy(grpc_exec_ctx *exec_ctx, verifier_cb_ctx *ctx) {
   if (ctx->audience != NULL) gpr_free(ctx->audience);
-  if (ctx->claims != NULL) grpc_jwt_claims_destroy(ctx->claims);
-  grpc_slice_unref(ctx->signature);
-  grpc_slice_unref(ctx->signed_data);
-  jose_header_destroy(ctx->header);
+  if (ctx->claims != NULL) grpc_jwt_claims_destroy(exec_ctx, ctx->claims);
+  grpc_slice_unref_internal(exec_ctx, ctx->signature);
+  grpc_slice_unref_internal(exec_ctx, ctx->signed_data);
+  jose_header_destroy(exec_ctx, ctx->header);
   for (size_t i = 0; i < HTTP_RESPONSE_COUNT; i++) {
     grpc_http_response_destroy(&ctx->responses[i]);
   }
@@ -447,23 +463,24 @@ end:
   return result;
 }
 
-static BIGNUM *bignum_from_base64(const char *b64) {
+static BIGNUM *bignum_from_base64(grpc_exec_ctx *exec_ctx, const char *b64) {
   BIGNUM *result = NULL;
   grpc_slice bin;
 
   if (b64 == NULL) return NULL;
-  bin = grpc_base64_decode(b64, 1);
+  bin = grpc_base64_decode(exec_ctx, b64, 1);
   if (GRPC_SLICE_IS_EMPTY(bin)) {
     gpr_log(GPR_ERROR, "Invalid base64 for big num.");
     return NULL;
   }
   result = BN_bin2bn(GRPC_SLICE_START_PTR(bin),
                      TSI_SIZE_AS_SIZE(GRPC_SLICE_LENGTH(bin)), NULL);
-  grpc_slice_unref(bin);
+  grpc_slice_unref_internal(exec_ctx, bin);
   return result;
 }
 
-static EVP_PKEY *pkey_from_jwk(const grpc_json *json, const char *kty) {
+static EVP_PKEY *pkey_from_jwk(grpc_exec_ctx *exec_ctx, const grpc_json *json,
+                               const char *kty) {
   const grpc_json *key_prop;
   RSA *rsa = NULL;
   EVP_PKEY *result = NULL;
@@ -480,10 +497,12 @@ static EVP_PKEY *pkey_from_jwk(const grpc_json *json, const char *kty) {
   }
   for (key_prop = json->child; key_prop != NULL; key_prop = key_prop->next) {
     if (strcmp(key_prop->key, "n") == 0) {
-      rsa->n = bignum_from_base64(validate_string_field(key_prop, "n"));
+      rsa->n =
+          bignum_from_base64(exec_ctx, validate_string_field(key_prop, "n"));
       if (rsa->n == NULL) goto end;
     } else if (strcmp(key_prop->key, "e") == 0) {
-      rsa->e = bignum_from_base64(validate_string_field(key_prop, "e"));
+      rsa->e =
+          bignum_from_base64(exec_ctx, validate_string_field(key_prop, "e"));
       if (rsa->e == NULL) goto end;
     }
   }
@@ -499,7 +518,8 @@ end:
   return result;
 }
 
-static EVP_PKEY *find_verification_key(const grpc_json *json,
+static EVP_PKEY *find_verification_key(grpc_exec_ctx *exec_ctx,
+                                       const grpc_json *json,
                                        const char *header_alg,
                                        const char *header_kid) {
   const grpc_json *jkey;
@@ -543,7 +563,7 @@ static EVP_PKEY *find_verification_key(const grpc_json *json,
     }
     if (alg != NULL && kid != NULL && kty != NULL &&
         strcmp(kid, header_kid) == 0 && strcmp(alg, header_alg) == 0) {
-      return pkey_from_jwk(jkey, kty);
+      return pkey_from_jwk(exec_ctx, jkey, kty);
     }
   }
   gpr_log(GPR_ERROR,
@@ -597,7 +617,7 @@ static void on_keys_retrieved(grpc_exec_ctx *exec_ctx, void *user_data,
     goto end;
   }
   verification_key =
-      find_verification_key(json, ctx->header->alg, ctx->header->kid);
+      find_verification_key(exec_ctx, json, ctx->header->alg, ctx->header->kid);
   if (verification_key == NULL) {
     gpr_log(GPR_ERROR, "Could not find verification key with kid %s.",
             ctx->header->kid);
@@ -621,8 +641,8 @@ static void on_keys_retrieved(grpc_exec_ctx *exec_ctx, void *user_data,
 end:
   if (json != NULL) grpc_json_destroy(json);
   if (verification_key != NULL) EVP_PKEY_free(verification_key);
-  ctx->user_cb(ctx->user_data, status, claims);
-  verifier_cb_ctx_destroy(ctx);
+  ctx->user_cb(exec_ctx, ctx->user_data, status, claims);
+  verifier_cb_ctx_destroy(exec_ctx, ctx);
 }
 
 static void on_openid_config_retrieved(grpc_exec_ctx *exec_ctx, void *user_data,
@@ -665,17 +685,18 @@ static void on_openid_config_retrieved(grpc_exec_ctx *exec_ctx, void *user_data,
   grpc_httpcli_get(
       exec_ctx, &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req,
       gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay),
-      grpc_closure_create(on_keys_retrieved, ctx),
+      grpc_closure_create(on_keys_retrieved, ctx, grpc_schedule_on_exec_ctx),
       &ctx->responses[HTTP_RESPONSE_KEYS]);
-  grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
+  grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
   grpc_json_destroy(json);
   gpr_free(req.host);
   return;
 
 error:
   if (json != NULL) grpc_json_destroy(json);
-  ctx->user_cb(ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, NULL);
-  verifier_cb_ctx_destroy(ctx);
+  ctx->user_cb(exec_ctx, ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR,
+               NULL);
+  verifier_cb_ctx_destroy(exec_ctx, ctx);
 }
 
 static email_key_mapping *verifier_get_mapping(grpc_jwt_verifier *v,
@@ -705,10 +726,26 @@ static void verifier_put_mapping(grpc_jwt_verifier *v, const char *email_domain,
   GPR_ASSERT(v->num_mappings <= v->allocated_mappings);
 }
 
+/* Very non-sophisticated way to detect an email address. Should be good
+   enough for now... */
+const char *grpc_jwt_issuer_email_domain(const char *issuer) {
+  const char *at_sign = strchr(issuer, '@');
+  if (at_sign == NULL) return NULL;
+  const char *email_domain = at_sign + 1;
+  if (*email_domain == '\0') return NULL;
+  const char *dot = strrchr(email_domain, '.');
+  if (dot == NULL || dot == email_domain) return email_domain;
+  GPR_ASSERT(dot > email_domain);
+  /* There may be a subdomain, we just want the domain. */
+  dot = gpr_memrchr(email_domain, '.', (size_t)(dot - email_domain));
+  if (dot == NULL) return email_domain;
+  return dot + 1;
+}
+
 /* Takes ownership of ctx. */
 static void retrieve_key_and_verify(grpc_exec_ctx *exec_ctx,
                                     verifier_cb_ctx *ctx) {
-  const char *at_sign;
+  const char *email_domain;
   grpc_closure *http_cb;
   char *path_prefix = NULL;
   const char *iss;
@@ -733,13 +770,9 @@ static void retrieve_key_and_verify(grpc_exec_ctx *exec_ctx,
      Nobody seems to implement the account/email/webfinger part 2. of the spec
      so we will rely instead on email/url mappings if we detect such an issuer.
      Part 4, on the other hand is implemented by both google and salesforce. */
-
-  /* Very non-sophisticated way to detect an email address. Should be good
-     enough for now... */
-  at_sign = strchr(iss, '@');
-  if (at_sign != NULL) {
+  email_domain = grpc_jwt_issuer_email_domain(iss);
+  if (email_domain != NULL) {
     email_key_mapping *mapping;
-    const char *email_domain = at_sign + 1;
     GPR_ASSERT(ctx->verifier != NULL);
     mapping = verifier_get_mapping(ctx->verifier, email_domain);
     if (mapping == NULL) {
@@ -754,7 +787,8 @@ static void retrieve_key_and_verify(grpc_exec_ctx *exec_ctx,
       *(path_prefix++) = '\0';
       gpr_asprintf(&req.http.path, "/%s/%s", path_prefix, iss);
     }
-    http_cb = grpc_closure_create(on_keys_retrieved, ctx);
+    http_cb =
+        grpc_closure_create(on_keys_retrieved, ctx, grpc_schedule_on_exec_ctx);
     rsp_idx = HTTP_RESPONSE_KEYS;
   } else {
     req.host = gpr_strdup(strstr(iss, "https://") == iss ? iss + 8 : iss);
@@ -766,7 +800,8 @@ static void retrieve_key_and_verify(grpc_exec_ctx *exec_ctx,
       gpr_asprintf(&req.http.path, "/%s%s", path_prefix,
                    GRPC_OPENID_CONFIG_URL_SUFFIX);
     }
-    http_cb = grpc_closure_create(on_openid_config_retrieved, ctx);
+    http_cb = grpc_closure_create(on_openid_config_retrieved, ctx,
+                                  grpc_schedule_on_exec_ctx);
     rsp_idx = HTTP_RESPONSE_OPENID;
   }
 
@@ -779,14 +814,15 @@ static void retrieve_key_and_verify(grpc_exec_ctx *exec_ctx,
       exec_ctx, &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req,
       gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay),
       http_cb, &ctx->responses[rsp_idx]);
-  grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
+  grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
   gpr_free(req.host);
   gpr_free(req.http.path);
   return;
 
 error:
-  ctx->user_cb(ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, NULL);
-  verifier_cb_ctx_destroy(ctx);
+  ctx->user_cb(exec_ctx, ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR,
+               NULL);
+  verifier_cb_ctx_destroy(exec_ctx, ctx);
 }
 
 void grpc_jwt_verifier_verify(grpc_exec_ctx *exec_ctx,
@@ -808,22 +844,24 @@ void grpc_jwt_verifier_verify(grpc_exec_ctx *exec_ctx,
   GPR_ASSERT(verifier != NULL && jwt != NULL && audience != NULL && cb != NULL);
   dot = strchr(cur, '.');
   if (dot == NULL) goto error;
-  json = parse_json_part_from_jwt(cur, (size_t)(dot - cur), &header_buffer);
+  json = parse_json_part_from_jwt(exec_ctx, cur, (size_t)(dot - cur),
+                                  &header_buffer);
   if (json == NULL) goto error;
-  header = jose_header_from_json(json, header_buffer);
+  header = jose_header_from_json(exec_ctx, json, header_buffer);
   if (header == NULL) goto error;
 
   cur = dot + 1;
   dot = strchr(cur, '.');
   if (dot == NULL) goto error;
-  json = parse_json_part_from_jwt(cur, (size_t)(dot - cur), &claims_buffer);
+  json = parse_json_part_from_jwt(exec_ctx, cur, (size_t)(dot - cur),
+                                  &claims_buffer);
   if (json == NULL) goto error;
-  claims = grpc_jwt_claims_from_json(json, claims_buffer);
+  claims = grpc_jwt_claims_from_json(exec_ctx, json, claims_buffer);
   if (claims == NULL) goto error;
 
   signed_jwt_len = (size_t)(dot - jwt);
   cur = dot + 1;
-  signature = grpc_base64_decode(cur, 1);
+  signature = grpc_base64_decode(exec_ctx, cur, 1);
   if (GRPC_SLICE_IS_EMPTY(signature)) goto error;
   retrieve_key_and_verify(
       exec_ctx,
@@ -832,9 +870,9 @@ void grpc_jwt_verifier_verify(grpc_exec_ctx *exec_ctx,
   return;
 
 error:
-  if (header != NULL) jose_header_destroy(header);
-  if (claims != NULL) grpc_jwt_claims_destroy(claims);
-  cb(user_data, GRPC_JWT_VERIFIER_BAD_FORMAT, NULL);
+  if (header != NULL) jose_header_destroy(exec_ctx, header);
+  if (claims != NULL) grpc_jwt_claims_destroy(exec_ctx, claims);
+  cb(exec_ctx, user_data, GRPC_JWT_VERIFIER_BAD_FORMAT, NULL);
 }
 
 grpc_jwt_verifier *grpc_jwt_verifier_create(
diff --git a/src/core/lib/security/credentials/jwt/jwt_verifier.h b/src/core/lib/security/credentials/jwt/jwt_verifier.h
index f09f9d5d47c2e15b021c85e39f56d6328918a2ff..4fa320a415943dc5549f209f65478d5347405b4b 100644
--- a/src/core/lib/security/credentials/jwt/jwt_verifier.h
+++ b/src/core/lib/security/credentials/jwt/jwt_verifier.h
@@ -43,8 +43,7 @@
 /* --- Constants. --- */
 
 #define GRPC_OPENID_CONFIG_URL_SUFFIX "/.well-known/openid-configuration"
-#define GRPC_GOOGLE_SERVICE_ACCOUNTS_EMAIL_DOMAIN \
-  "developer.gserviceaccount.com"
+#define GRPC_GOOGLE_SERVICE_ACCOUNTS_EMAIL_DOMAIN "gserviceaccount.com"
 #define GRPC_GOOGLE_SERVICE_ACCOUNTS_KEY_URL_PREFIX \
   "www.googleapis.com/robot/v1/metadata/x509"
 
@@ -57,6 +56,7 @@ typedef enum {
   GRPC_JWT_VERIFIER_BAD_AUDIENCE,
   GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR,
   GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE,
+  GRPC_JWT_VERIFIER_BAD_SUBJECT,
   GRPC_JWT_VERIFIER_GENERIC_ERROR
 } grpc_jwt_verifier_status;
 
@@ -66,7 +66,7 @@ const char *grpc_jwt_verifier_status_to_string(grpc_jwt_verifier_status status);
 
 typedef struct grpc_jwt_claims grpc_jwt_claims;
 
-void grpc_jwt_claims_destroy(grpc_jwt_claims *claims);
+void grpc_jwt_claims_destroy(grpc_exec_ctx *exec_ctx, grpc_jwt_claims *claims);
 
 /* Returns the whole JSON tree of the claims. */
 const grpc_json *grpc_jwt_claims_json(const grpc_jwt_claims *claims);
@@ -115,7 +115,8 @@ void grpc_jwt_verifier_destroy(grpc_jwt_verifier *verifier);
    is done (maybe in another thread).
    It is the responsibility of the callee to call grpc_jwt_claims_destroy on
    the claims. */
-typedef void (*grpc_jwt_verification_done_cb)(void *user_data,
+typedef void (*grpc_jwt_verification_done_cb)(grpc_exec_ctx *exec_ctx,
+                                              void *user_data,
                                               grpc_jwt_verifier_status status,
                                               grpc_jwt_claims *claims);
 
@@ -129,8 +130,10 @@ void grpc_jwt_verifier_verify(grpc_exec_ctx *exec_ctx,
 
 /* --- TESTING ONLY exposed functions. --- */
 
-grpc_jwt_claims *grpc_jwt_claims_from_json(grpc_json *json, grpc_slice buffer);
+grpc_jwt_claims *grpc_jwt_claims_from_json(grpc_exec_ctx *exec_ctx,
+                                           grpc_json *json, grpc_slice buffer);
 grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims *claims,
                                                const char *audience);
+const char *grpc_jwt_issuer_email_domain(const char *issuer);
 
 #endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_VERIFIER_H */
diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.c b/src/core/lib/security/credentials/oauth2/oauth2_credentials.c
index b3625b22c02c44350c2c40bdec4813b24dbb4903..1b0e43a1e46e1479950a066198058c679e3eef1d 100644
--- a/src/core/lib/security/credentials/oauth2/oauth2_credentials.c
+++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.c
@@ -118,18 +118,19 @@ void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token *refresh_token) {
 // Oauth2 Token Fetcher credentials.
 //
 
-static void oauth2_token_fetcher_destruct(grpc_call_credentials *creds) {
+static void oauth2_token_fetcher_destruct(grpc_exec_ctx *exec_ctx,
+                                          grpc_call_credentials *creds) {
   grpc_oauth2_token_fetcher_credentials *c =
       (grpc_oauth2_token_fetcher_credentials *)creds;
-  grpc_credentials_md_store_unref(c->access_token_md);
+  grpc_credentials_md_store_unref(exec_ctx, c->access_token_md);
   gpr_mu_destroy(&c->mu);
   grpc_httpcli_context_destroy(&c->httpcli_context);
 }
 
 grpc_credentials_status
 grpc_oauth2_token_fetcher_credentials_parse_server_response(
-    const grpc_http_response *response, grpc_credentials_md_store **token_md,
-    gpr_timespec *token_lifetime) {
+    grpc_exec_ctx *exec_ctx, const grpc_http_response *response,
+    grpc_credentials_md_store **token_md, gpr_timespec *token_lifetime) {
   char *null_terminated_body = NULL;
   char *new_access_token = NULL;
   grpc_credentials_status status = GRPC_CREDENTIALS_OK;
@@ -198,7 +199,7 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response(
     token_lifetime->tv_sec = strtol(expires_in->value, NULL, 10);
     token_lifetime->tv_nsec = 0;
     token_lifetime->clock_type = GPR_TIMESPAN;
-    if (*token_md != NULL) grpc_credentials_md_store_unref(*token_md);
+    if (*token_md != NULL) grpc_credentials_md_store_unref(exec_ctx, *token_md);
     *token_md = grpc_credentials_md_store_create(1);
     grpc_credentials_md_store_add_cstrings(
         *token_md, GRPC_AUTHORIZATION_METADATA_KEY, new_access_token);
@@ -207,7 +208,7 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response(
 
 end:
   if (status != GRPC_CREDENTIALS_OK && (*token_md != NULL)) {
-    grpc_credentials_md_store_unref(*token_md);
+    grpc_credentials_md_store_unref(exec_ctx, *token_md);
     *token_md = NULL;
   }
   if (null_terminated_body != NULL) gpr_free(null_terminated_body);
@@ -230,7 +231,7 @@ static void on_oauth2_token_fetcher_http_response(grpc_exec_ctx *exec_ctx,
 
   gpr_mu_lock(&c->mu);
   status = grpc_oauth2_token_fetcher_credentials_parse_server_response(
-      &r->response, &c->access_token_md, &token_lifetime);
+      exec_ctx, &r->response, &c->access_token_md, &token_lifetime);
   if (status == GRPC_CREDENTIALS_OK) {
     c->token_expiration =
         gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), token_lifetime);
@@ -242,7 +243,7 @@ static void on_oauth2_token_fetcher_http_response(grpc_exec_ctx *exec_ctx,
           "Error occured when fetching oauth2 token.");
   }
   gpr_mu_unlock(&c->mu);
-  grpc_credentials_metadata_request_destroy(r);
+  grpc_credentials_metadata_request_destroy(exec_ctx, r);
 }
 
 static void oauth2_token_fetcher_get_request_metadata(
@@ -268,7 +269,7 @@ static void oauth2_token_fetcher_get_request_metadata(
   if (cached_access_token_md != NULL) {
     cb(exec_ctx, user_data, cached_access_token_md->entries,
        cached_access_token_md->num_entries, GRPC_CREDENTIALS_OK, NULL);
-    grpc_credentials_md_store_unref(cached_access_token_md);
+    grpc_credentials_md_store_unref(exec_ctx, cached_access_token_md);
   } else {
     c->fetch_func(
         exec_ctx,
@@ -312,10 +313,12 @@ static void compute_engine_fetch_oauth2(
      extreme memory pressure. */
   grpc_resource_quota *resource_quota =
       grpc_resource_quota_create("oauth2_credentials");
-  grpc_httpcli_get(exec_ctx, httpcli_context, pollent, resource_quota, &request,
-                   deadline, grpc_closure_create(response_cb, metadata_req),
-                   &metadata_req->response);
-  grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
+  grpc_httpcli_get(
+      exec_ctx, httpcli_context, pollent, resource_quota, &request, deadline,
+      grpc_closure_create(response_cb, metadata_req, grpc_schedule_on_exec_ctx),
+
+      &metadata_req->response);
+  grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
 }
 
 grpc_call_credentials *grpc_google_compute_engine_credentials_create(
@@ -334,11 +337,12 @@ grpc_call_credentials *grpc_google_compute_engine_credentials_create(
 // Google Refresh Token credentials.
 //
 
-static void refresh_token_destruct(grpc_call_credentials *creds) {
+static void refresh_token_destruct(grpc_exec_ctx *exec_ctx,
+                                   grpc_call_credentials *creds) {
   grpc_google_refresh_token_credentials *c =
       (grpc_google_refresh_token_credentials *)creds;
   grpc_auth_refresh_token_destruct(&c->refresh_token);
-  oauth2_token_fetcher_destruct(&c->base.base);
+  oauth2_token_fetcher_destruct(exec_ctx, &c->base.base);
 }
 
 static grpc_call_credentials_vtable refresh_token_vtable = {
@@ -368,11 +372,12 @@ static void refresh_token_fetch_oauth2(
      extreme memory pressure. */
   grpc_resource_quota *resource_quota =
       grpc_resource_quota_create("oauth2_credentials_refresh");
-  grpc_httpcli_post(exec_ctx, httpcli_context, pollent, resource_quota,
-                    &request, body, strlen(body), deadline,
-                    grpc_closure_create(response_cb, metadata_req),
-                    &metadata_req->response);
-  grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
+  grpc_httpcli_post(
+      exec_ctx, httpcli_context, pollent, resource_quota, &request, body,
+      strlen(body), deadline,
+      grpc_closure_create(response_cb, metadata_req, grpc_schedule_on_exec_ctx),
+      &metadata_req->response);
+  grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
   gpr_free(body);
 }
 
@@ -424,9 +429,10 @@ grpc_call_credentials *grpc_google_refresh_token_credentials_create(
 // Oauth2 Access Token credentials.
 //
 
-static void access_token_destruct(grpc_call_credentials *creds) {
+static void access_token_destruct(grpc_exec_ctx *exec_ctx,
+                                  grpc_call_credentials *creds) {
   grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds;
-  grpc_credentials_md_store_unref(c->access_token_md);
+  grpc_credentials_md_store_unref(exec_ctx, c->access_token_md);
 }
 
 static void access_token_get_request_metadata(
diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.h b/src/core/lib/security/credentials/oauth2/oauth2_credentials.h
index 7f6f205c221645b7b1472ef0b136c131f28fbb80..2d7c02ccf570c5bdc958302b44322304ac8e0695 100644
--- a/src/core/lib/security/credentials/oauth2/oauth2_credentials.h
+++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.h
@@ -103,7 +103,7 @@ grpc_refresh_token_credentials_create_from_auth_refresh_token(
 // Exposed for testing only.
 grpc_credentials_status
 grpc_oauth2_token_fetcher_credentials_parse_server_response(
-    const struct grpc_http_response *response,
+    grpc_exec_ctx *exec_ctx, const struct grpc_http_response *response,
     grpc_credentials_md_store **token_md, gpr_timespec *token_lifetime);
 
 #endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_OAUTH2_CREDENTIALS_H */
diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.c b/src/core/lib/security/credentials/plugin/plugin_credentials.c
index 5d950098a063b78f9440e75cb6d394279cffcd8d..f90d7dce83f6f4977dd608f14f4d0fc7959ea355 100644
--- a/src/core/lib/security/credentials/plugin/plugin_credentials.c
+++ b/src/core/lib/security/credentials/plugin/plugin_credentials.c
@@ -35,20 +35,22 @@
 
 #include <string.h>
 
-#include "src/core/lib/surface/api_trace.h"
-
 #include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
 
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/surface/api_trace.h"
+
 typedef struct {
   void *user_data;
   grpc_credentials_metadata_cb cb;
 } grpc_metadata_plugin_request;
 
-static void plugin_destruct(grpc_call_credentials *creds) {
+static void plugin_destruct(grpc_exec_ctx *exec_ctx,
+                            grpc_call_credentials *creds) {
   grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds;
   if (c->plugin.state != NULL && c->plugin.destroy != NULL) {
     c->plugin.destroy(c->plugin.state);
@@ -100,8 +102,8 @@ static void plugin_md_request_metadata_ready(void *request,
       r->cb(&exec_ctx, r->user_data, md_array, num_md, GRPC_CREDENTIALS_OK,
             NULL);
       for (i = 0; i < num_md; i++) {
-        grpc_slice_unref(md_array[i].key);
-        grpc_slice_unref(md_array[i].value);
+        grpc_slice_unref_internal(&exec_ctx, md_array[i].key);
+        grpc_slice_unref_internal(&exec_ctx, md_array[i].value);
       }
       gpr_free(md_array);
     } else if (num_md == 0) {
diff --git a/src/core/lib/security/credentials/ssl/ssl_credentials.c b/src/core/lib/security/credentials/ssl/ssl_credentials.c
index 0dc1fccec4a76b7f9cc772a8c2d6282ed634caaa..4eebb7d6136647359f754b1c471eb96b39a89345 100644
--- a/src/core/lib/security/credentials/ssl/ssl_credentials.c
+++ b/src/core/lib/security/credentials/ssl/ssl_credentials.c
@@ -57,7 +57,8 @@ static void ssl_copy_key_material(const char *input, unsigned char **output,
 // SSL Channel Credentials.
 //
 
-static void ssl_destruct(grpc_channel_credentials *creds) {
+static void ssl_destruct(grpc_exec_ctx *exec_ctx,
+                         grpc_channel_credentials *creds) {
   grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
   if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs);
   if (c->config.pem_private_key != NULL) gpr_free(c->config.pem_private_key);
@@ -65,9 +66,10 @@ static void ssl_destruct(grpc_channel_credentials *creds) {
 }
 
 static grpc_security_status ssl_create_security_connector(
-    grpc_channel_credentials *creds, grpc_call_credentials *call_creds,
-    const char *target, const grpc_channel_args *args,
-    grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
+    grpc_exec_ctx *exec_ctx, grpc_channel_credentials *creds,
+    grpc_call_credentials *call_creds, const char *target,
+    const grpc_channel_args *args, grpc_channel_security_connector **sc,
+    grpc_channel_args **new_args) {
   grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
   grpc_security_status status = GRPC_SECURITY_OK;
   size_t i = 0;
@@ -83,7 +85,7 @@ static grpc_security_status ssl_create_security_connector(
     }
   }
   status = grpc_ssl_channel_security_connector_create(
-      call_creds, &c->config, target, overridden_target_name, sc);
+      exec_ctx, call_creds, &c->config, target, overridden_target_name, sc);
   if (status != GRPC_SECURITY_OK) {
     return status;
   }
@@ -138,7 +140,8 @@ grpc_channel_credentials *grpc_ssl_credentials_create(
 // SSL Server Credentials.
 //
 
-static void ssl_server_destruct(grpc_server_credentials *creds) {
+static void ssl_server_destruct(grpc_exec_ctx *exec_ctx,
+                                grpc_server_credentials *creds) {
   grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
   size_t i;
   for (i = 0; i < c->config.num_key_cert_pairs; i++) {
@@ -161,9 +164,10 @@ static void ssl_server_destruct(grpc_server_credentials *creds) {
 }
 
 static grpc_security_status ssl_server_create_security_connector(
-    grpc_server_credentials *creds, grpc_server_security_connector **sc) {
+    grpc_exec_ctx *exec_ctx, grpc_server_credentials *creds,
+    grpc_server_security_connector **sc) {
   grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
-  return grpc_ssl_server_security_connector_create(&c->config, sc);
+  return grpc_ssl_server_security_connector_create(exec_ctx, &c->config, sc);
 }
 
 static grpc_server_credentials_vtable ssl_server_vtable = {
diff --git a/src/core/lib/security/transport/client_auth_filter.c b/src/core/lib/security/transport/client_auth_filter.c
index 053bf5972c2b41b72a26c6697c75fa284b23417a..b7f6fd23e3055dceb1cc7ff6ecdd4a517a9fd279 100644
--- a/src/core/lib/security/transport/client_auth_filter.c
+++ b/src/core/lib/security/transport/client_auth_filter.c
@@ -44,6 +44,7 @@
 #include "src/core/lib/security/context/security_context.h"
 #include "src/core/lib/security/credentials/credentials.h"
 #include "src/core/lib/security/transport/security_connector.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/surface/call.h"
 #include "src/core/lib/transport/static_metadata.h"
@@ -93,7 +94,8 @@ static void bubble_up_error(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
   call_data *calld = elem->call_data;
   gpr_log(GPR_ERROR, "Client side authentication failure: %s", error_msg);
   grpc_slice error_slice = grpc_slice_from_copied_string(error_msg);
-  grpc_transport_stream_op_add_close(&calld->op, status, &error_slice);
+  grpc_transport_stream_op_add_close(exec_ctx, &calld->op, status,
+                                     &error_slice);
   grpc_call_next_op(exec_ctx, elem, &calld->op);
 }
 
@@ -121,8 +123,9 @@ static void on_credentials_metadata(grpc_exec_ctx *exec_ctx, void *user_data,
   for (i = 0; i < num_md; i++) {
     grpc_metadata_batch_add_tail(
         mdb, &calld->md_links[i],
-        grpc_mdelem_from_slices(grpc_slice_ref(md_elems[i].key),
-                                grpc_slice_ref(md_elems[i].value)));
+        grpc_mdelem_from_slices(exec_ctx,
+                                grpc_slice_ref_internal(md_elems[i].key),
+                                grpc_slice_ref_internal(md_elems[i].value)));
   }
   grpc_call_next_op(exec_ctx, elem, op);
 }
@@ -248,10 +251,10 @@ static void auth_start_transport_op(grpc_exec_ctx *exec_ctx,
       /* Pointer comparison is OK for md_elems created from the same context.
        */
       if (md->key == GRPC_MDSTR_AUTHORITY) {
-        if (calld->host != NULL) GRPC_MDSTR_UNREF(calld->host);
+        if (calld->host != NULL) GRPC_MDSTR_UNREF(exec_ctx, calld->host);
         calld->host = GRPC_MDSTR_REF(md->value);
       } else if (md->key == GRPC_MDSTR_PATH) {
-        if (calld->method != NULL) GRPC_MDSTR_UNREF(calld->method);
+        if (calld->method != NULL) GRPC_MDSTR_UNREF(exec_ctx, calld->method);
         calld->method = GRPC_MDSTR_REF(md->value);
       }
     }
@@ -292,20 +295,20 @@ static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                               const grpc_call_final_info *final_info,
                               void *ignored) {
   call_data *calld = elem->call_data;
-  grpc_call_credentials_unref(calld->creds);
+  grpc_call_credentials_unref(exec_ctx, calld->creds);
   if (calld->host != NULL) {
-    GRPC_MDSTR_UNREF(calld->host);
+    GRPC_MDSTR_UNREF(exec_ctx, calld->host);
   }
   if (calld->method != NULL) {
-    GRPC_MDSTR_UNREF(calld->method);
+    GRPC_MDSTR_UNREF(exec_ctx, calld->method);
   }
   reset_auth_metadata_context(&calld->auth_md_context);
 }
 
 /* Constructor for channel_data */
-static void init_channel_elem(grpc_exec_ctx *exec_ctx,
-                              grpc_channel_element *elem,
-                              grpc_channel_element_args *args) {
+static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx,
+                                     grpc_channel_element *elem,
+                                     grpc_channel_element_args *args) {
   grpc_security_connector *sc =
       grpc_find_security_connector_in_args(args->channel_args);
   grpc_auth_context *auth_context =
@@ -327,6 +330,7 @@ static void init_channel_elem(grpc_exec_ctx *exec_ctx,
           sc, "client_auth_filter");
   chand->auth_context =
       GRPC_AUTH_CONTEXT_REF(auth_context, "client_auth_filter");
+  return GRPC_ERROR_NONE;
 }
 
 /* Destructor for channel data */
@@ -336,7 +340,7 @@ static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
   channel_data *chand = elem->channel_data;
   grpc_channel_security_connector *sc = chand->security_connector;
   if (sc != NULL) {
-    GRPC_SECURITY_CONNECTOR_UNREF(&sc->base, "client_auth_filter");
+    GRPC_SECURITY_CONNECTOR_UNREF(exec_ctx, &sc->base, "client_auth_filter");
   }
   GRPC_AUTH_CONTEXT_UNREF(chand->auth_context, "client_auth_filter");
 }
diff --git a/src/core/lib/security/transport/handshake.c b/src/core/lib/security/transport/handshake.c
deleted file mode 100644
index 96237976107cf92dd2e64a36ae7ae01c0942e973..0000000000000000000000000000000000000000
--- a/src/core/lib/security/transport/handshake.c
+++ /dev/null
@@ -1,374 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include "src/core/lib/security/transport/handshake.h"
-
-#include <stdbool.h>
-#include <string.h>
-
-#include <grpc/slice_buffer.h>
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
-#include "src/core/lib/iomgr/timer.h"
-#include "src/core/lib/security/context/security_context.h"
-#include "src/core/lib/security/transport/secure_endpoint.h"
-#include "src/core/lib/security/transport/tsi_error.h"
-
-#define GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE 256
-
-typedef struct {
-  grpc_security_connector *connector;
-  tsi_handshaker *handshaker;
-  bool is_client_side;
-  unsigned char *handshake_buffer;
-  size_t handshake_buffer_size;
-  grpc_endpoint *wrapped_endpoint;
-  grpc_endpoint *secure_endpoint;
-  grpc_slice_buffer left_overs;
-  grpc_slice_buffer incoming;
-  grpc_slice_buffer outgoing;
-  grpc_security_handshake_done_cb cb;
-  void *user_data;
-  grpc_closure on_handshake_data_sent_to_peer;
-  grpc_closure on_handshake_data_received_from_peer;
-  grpc_auth_context *auth_context;
-  grpc_timer timer;
-  gpr_refcount refs;
-} grpc_security_handshake;
-
-static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx,
-                                                 void *setup,
-                                                 grpc_error *error);
-
-static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx, void *setup,
-                                           grpc_error *error);
-
-static void security_connector_remove_handshake(grpc_security_handshake *h) {
-  GPR_ASSERT(!h->is_client_side);
-  grpc_security_connector_handshake_list *node;
-  grpc_security_connector_handshake_list *tmp;
-  grpc_server_security_connector *sc =
-      (grpc_server_security_connector *)h->connector;
-  gpr_mu_lock(&sc->mu);
-  node = sc->handshaking_handshakes;
-  if (node && node->handshake == h) {
-    sc->handshaking_handshakes = node->next;
-    gpr_free(node);
-    gpr_mu_unlock(&sc->mu);
-    return;
-  }
-  while (node) {
-    if (node->next->handshake == h) {
-      tmp = node->next;
-      node->next = node->next->next;
-      gpr_free(tmp);
-      gpr_mu_unlock(&sc->mu);
-      return;
-    }
-    node = node->next;
-  }
-  gpr_mu_unlock(&sc->mu);
-}
-
-static void unref_handshake(grpc_security_handshake *h) {
-  if (gpr_unref(&h->refs)) {
-    if (h->handshaker != NULL) tsi_handshaker_destroy(h->handshaker);
-    if (h->handshake_buffer != NULL) gpr_free(h->handshake_buffer);
-    grpc_slice_buffer_destroy(&h->left_overs);
-    grpc_slice_buffer_destroy(&h->outgoing);
-    grpc_slice_buffer_destroy(&h->incoming);
-    GRPC_AUTH_CONTEXT_UNREF(h->auth_context, "handshake");
-    GRPC_SECURITY_CONNECTOR_UNREF(h->connector, "handshake");
-    gpr_free(h);
-  }
-}
-
-static void security_handshake_done(grpc_exec_ctx *exec_ctx,
-                                    grpc_security_handshake *h,
-                                    grpc_error *error) {
-  grpc_timer_cancel(exec_ctx, &h->timer);
-  if (!h->is_client_side) {
-    security_connector_remove_handshake(h);
-  }
-  if (error == GRPC_ERROR_NONE) {
-    h->cb(exec_ctx, h->user_data, GRPC_SECURITY_OK, h->secure_endpoint,
-          h->auth_context);
-  } else {
-    const char *msg = grpc_error_string(error);
-    gpr_log(GPR_DEBUG, "Security handshake failed: %s", msg);
-    grpc_error_free_string(msg);
-
-    if (h->secure_endpoint != NULL) {
-      grpc_endpoint_shutdown(exec_ctx, h->secure_endpoint);
-      grpc_endpoint_destroy(exec_ctx, h->secure_endpoint);
-    } else {
-      grpc_endpoint_destroy(exec_ctx, h->wrapped_endpoint);
-    }
-    h->cb(exec_ctx, h->user_data, GRPC_SECURITY_ERROR, NULL, NULL);
-  }
-  unref_handshake(h);
-  GRPC_ERROR_UNREF(error);
-}
-
-static void on_peer_checked(grpc_exec_ctx *exec_ctx, void *user_data,
-                            grpc_security_status status,
-                            grpc_auth_context *auth_context) {
-  grpc_security_handshake *h = user_data;
-  tsi_frame_protector *protector;
-  tsi_result result;
-  if (status != GRPC_SECURITY_OK) {
-    security_handshake_done(
-        exec_ctx, h,
-        grpc_error_set_int(GRPC_ERROR_CREATE("Error checking peer."),
-                           GRPC_ERROR_INT_SECURITY_STATUS, status));
-    return;
-  }
-  h->auth_context = GRPC_AUTH_CONTEXT_REF(auth_context, "handshake");
-  result =
-      tsi_handshaker_create_frame_protector(h->handshaker, NULL, &protector);
-  if (result != TSI_OK) {
-    security_handshake_done(
-        exec_ctx, h,
-        grpc_set_tsi_error_result(
-            GRPC_ERROR_CREATE("Frame protector creation failed"), result));
-    return;
-  }
-  h->secure_endpoint =
-      grpc_secure_endpoint_create(protector, h->wrapped_endpoint,
-                                  h->left_overs.slices, h->left_overs.count);
-  h->left_overs.count = 0;
-  h->left_overs.length = 0;
-  security_handshake_done(exec_ctx, h, GRPC_ERROR_NONE);
-  return;
-}
-
-static void check_peer(grpc_exec_ctx *exec_ctx, grpc_security_handshake *h) {
-  tsi_peer peer;
-  tsi_result result = tsi_handshaker_extract_peer(h->handshaker, &peer);
-
-  if (result != TSI_OK) {
-    security_handshake_done(
-        exec_ctx, h, grpc_set_tsi_error_result(
-                         GRPC_ERROR_CREATE("Peer extraction failed"), result));
-    return;
-  }
-  grpc_security_connector_check_peer(exec_ctx, h->connector, peer,
-                                     on_peer_checked, h);
-}
-
-static void send_handshake_bytes_to_peer(grpc_exec_ctx *exec_ctx,
-                                         grpc_security_handshake *h) {
-  size_t offset = 0;
-  tsi_result result = TSI_OK;
-  grpc_slice to_send;
-
-  do {
-    size_t to_send_size = h->handshake_buffer_size - offset;
-    result = tsi_handshaker_get_bytes_to_send_to_peer(
-        h->handshaker, h->handshake_buffer + offset, &to_send_size);
-    offset += to_send_size;
-    if (result == TSI_INCOMPLETE_DATA) {
-      h->handshake_buffer_size *= 2;
-      h->handshake_buffer =
-          gpr_realloc(h->handshake_buffer, h->handshake_buffer_size);
-    }
-  } while (result == TSI_INCOMPLETE_DATA);
-
-  if (result != TSI_OK) {
-    security_handshake_done(exec_ctx, h,
-                            grpc_set_tsi_error_result(
-                                GRPC_ERROR_CREATE("Handshake failed"), result));
-    return;
-  }
-
-  to_send =
-      grpc_slice_from_copied_buffer((const char *)h->handshake_buffer, offset);
-  grpc_slice_buffer_reset_and_unref(&h->outgoing);
-  grpc_slice_buffer_add(&h->outgoing, to_send);
-  /* TODO(klempner,jboeuf): This should probably use the client setup
-     deadline */
-  grpc_endpoint_write(exec_ctx, h->wrapped_endpoint, &h->outgoing,
-                      &h->on_handshake_data_sent_to_peer);
-}
-
-static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx,
-                                                 void *handshake,
-                                                 grpc_error *error) {
-  grpc_security_handshake *h = handshake;
-  size_t consumed_slice_size = 0;
-  tsi_result result = TSI_OK;
-  size_t i;
-  size_t num_left_overs;
-  int has_left_overs_in_current_slice = 0;
-
-  if (error != GRPC_ERROR_NONE) {
-    security_handshake_done(
-        exec_ctx, h,
-        GRPC_ERROR_CREATE_REFERENCING("Handshake read failed", &error, 1));
-    return;
-  }
-
-  for (i = 0; i < h->incoming.count; i++) {
-    consumed_slice_size = GRPC_SLICE_LENGTH(h->incoming.slices[i]);
-    result = tsi_handshaker_process_bytes_from_peer(
-        h->handshaker, GRPC_SLICE_START_PTR(h->incoming.slices[i]),
-        &consumed_slice_size);
-    if (!tsi_handshaker_is_in_progress(h->handshaker)) break;
-  }
-
-  if (tsi_handshaker_is_in_progress(h->handshaker)) {
-    /* We may need more data. */
-    if (result == TSI_INCOMPLETE_DATA) {
-      grpc_endpoint_read(exec_ctx, h->wrapped_endpoint, &h->incoming,
-                         &h->on_handshake_data_received_from_peer);
-      return;
-    } else {
-      send_handshake_bytes_to_peer(exec_ctx, h);
-      return;
-    }
-  }
-
-  if (result != TSI_OK) {
-    security_handshake_done(exec_ctx, h,
-                            grpc_set_tsi_error_result(
-                                GRPC_ERROR_CREATE("Handshake failed"), result));
-    return;
-  }
-
-  /* Handshake is done and successful this point. */
-  has_left_overs_in_current_slice =
-      (consumed_slice_size < GRPC_SLICE_LENGTH(h->incoming.slices[i]));
-  num_left_overs =
-      (has_left_overs_in_current_slice ? 1 : 0) + h->incoming.count - i - 1;
-  if (num_left_overs == 0) {
-    check_peer(exec_ctx, h);
-    return;
-  }
-
-  /* Put the leftovers in our buffer (ownership transfered). */
-  if (has_left_overs_in_current_slice) {
-    grpc_slice_buffer_add(
-        &h->left_overs,
-        grpc_slice_split_tail(&h->incoming.slices[i], consumed_slice_size));
-    grpc_slice_unref(
-        h->incoming.slices[i]); /* split_tail above increments refcount. */
-  }
-  grpc_slice_buffer_addn(
-      &h->left_overs, &h->incoming.slices[i + 1],
-      num_left_overs - (size_t)has_left_overs_in_current_slice);
-  check_peer(exec_ctx, h);
-}
-
-/* If handshake is NULL, the handshake is done. */
-static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx,
-                                           void *handshake, grpc_error *error) {
-  grpc_security_handshake *h = handshake;
-
-  /* Make sure that write is OK. */
-  if (error != GRPC_ERROR_NONE) {
-    if (handshake != NULL)
-      security_handshake_done(
-          exec_ctx, h,
-          GRPC_ERROR_CREATE_REFERENCING("Handshake write failed", &error, 1));
-    return;
-  }
-
-  /* We may be done. */
-  if (tsi_handshaker_is_in_progress(h->handshaker)) {
-    /* TODO(klempner,jboeuf): This should probably use the client setup
-       deadline */
-    grpc_endpoint_read(exec_ctx, h->wrapped_endpoint, &h->incoming,
-                       &h->on_handshake_data_received_from_peer);
-  } else {
-    check_peer(exec_ctx, h);
-  }
-}
-
-static void on_timeout(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
-  grpc_security_handshake *h = arg;
-  if (error == GRPC_ERROR_NONE) {
-    grpc_endpoint_shutdown(exec_ctx, h->wrapped_endpoint);
-  }
-  unref_handshake(h);
-}
-
-void grpc_do_security_handshake(
-    grpc_exec_ctx *exec_ctx, tsi_handshaker *handshaker,
-    grpc_security_connector *connector, bool is_client_side,
-    grpc_endpoint *nonsecure_endpoint, grpc_slice_buffer *read_buffer,
-    gpr_timespec deadline, grpc_security_handshake_done_cb cb,
-    void *user_data) {
-  grpc_security_connector_handshake_list *handshake_node;
-  grpc_security_handshake *h = gpr_malloc(sizeof(grpc_security_handshake));
-  memset(h, 0, sizeof(grpc_security_handshake));
-  h->handshaker = handshaker;
-  h->connector = GRPC_SECURITY_CONNECTOR_REF(connector, "handshake");
-  h->is_client_side = is_client_side;
-  h->handshake_buffer_size = GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE;
-  h->handshake_buffer = gpr_malloc(h->handshake_buffer_size);
-  h->wrapped_endpoint = nonsecure_endpoint;
-  h->user_data = user_data;
-  h->cb = cb;
-  gpr_ref_init(&h->refs, 2); /* timer and handshake proper each get a ref */
-  grpc_closure_init(&h->on_handshake_data_sent_to_peer,
-                    on_handshake_data_sent_to_peer, h);
-  grpc_closure_init(&h->on_handshake_data_received_from_peer,
-                    on_handshake_data_received_from_peer, h);
-  grpc_slice_buffer_init(&h->left_overs);
-  grpc_slice_buffer_init(&h->outgoing);
-  grpc_slice_buffer_init(&h->incoming);
-  if (read_buffer != NULL) {
-    grpc_slice_buffer_move_into(read_buffer, &h->incoming);
-    gpr_free(read_buffer);
-  }
-  if (!is_client_side) {
-    grpc_server_security_connector *server_connector =
-        (grpc_server_security_connector *)connector;
-    handshake_node = gpr_malloc(sizeof(grpc_security_connector_handshake_list));
-    handshake_node->handshake = h;
-    gpr_mu_lock(&server_connector->mu);
-    handshake_node->next = server_connector->handshaking_handshakes;
-    server_connector->handshaking_handshakes = handshake_node;
-    gpr_mu_unlock(&server_connector->mu);
-  }
-  send_handshake_bytes_to_peer(exec_ctx, h);
-  grpc_timer_init(exec_ctx, &h->timer,
-                  gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC),
-                  on_timeout, h, gpr_now(GPR_CLOCK_MONOTONIC));
-}
-
-void grpc_security_handshake_shutdown(grpc_exec_ctx *exec_ctx,
-                                      void *handshake) {
-  grpc_security_handshake *h = handshake;
-  grpc_endpoint_shutdown(exec_ctx, h->wrapped_endpoint);
-}
diff --git a/src/core/lib/security/transport/secure_endpoint.c b/src/core/lib/security/transport/secure_endpoint.c
index 1b278410e807b7fbdffa6ffd065bc171c2a51617..18a7a6f7e7fefeb665c87eedbd924c8dd9cde02f 100644
--- a/src/core/lib/security/transport/secure_endpoint.c
+++ b/src/core/lib/security/transport/secure_endpoint.c
@@ -46,6 +46,7 @@
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/security/transport/secure_endpoint.h"
 #include "src/core/lib/security/transport/tsi_error.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/tsi/transport_security_interface.h"
@@ -80,11 +81,11 @@ static void destroy(grpc_exec_ctx *exec_ctx, secure_endpoint *secure_ep) {
   secure_endpoint *ep = secure_ep;
   grpc_endpoint_destroy(exec_ctx, ep->wrapped_ep);
   tsi_frame_protector_destroy(ep->protector);
-  grpc_slice_buffer_destroy(&ep->leftover_bytes);
-  grpc_slice_unref(ep->read_staging_buffer);
-  grpc_slice_unref(ep->write_staging_buffer);
-  grpc_slice_buffer_destroy(&ep->output_buffer);
-  grpc_slice_buffer_destroy(&ep->source_buffer);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &ep->leftover_bytes);
+  grpc_slice_unref_internal(exec_ctx, ep->read_staging_buffer);
+  grpc_slice_unref_internal(exec_ctx, ep->write_staging_buffer);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &ep->output_buffer);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &ep->source_buffer);
   gpr_mu_destroy(&ep->protector_mu);
   gpr_free(ep);
 }
@@ -146,7 +147,7 @@ static void call_read_cb(grpc_exec_ctx *exec_ctx, secure_endpoint *ep,
     }
   }
   ep->read_buffer = NULL;
-  grpc_exec_ctx_sched(exec_ctx, ep->read_cb, error, NULL);
+  grpc_closure_sched(exec_ctx, ep->read_cb, error);
   SECURE_ENDPOINT_UNREF(exec_ctx, ep, "read");
 }
 
@@ -160,7 +161,7 @@ static void on_read(grpc_exec_ctx *exec_ctx, void *user_data,
   uint8_t *end = GRPC_SLICE_END_PTR(ep->read_staging_buffer);
 
   if (error != GRPC_ERROR_NONE) {
-    grpc_slice_buffer_reset_and_unref(ep->read_buffer);
+    grpc_slice_buffer_reset_and_unref_internal(exec_ctx, ep->read_buffer);
     call_read_cb(exec_ctx, ep, GRPC_ERROR_CREATE_REFERENCING(
                                    "Secure read failed", &error, 1));
     return;
@@ -215,10 +216,10 @@ static void on_read(grpc_exec_ctx *exec_ctx, void *user_data,
 
   /* TODO(yangg) experiment with moving this block after read_cb to see if it
      helps latency */
-  grpc_slice_buffer_reset_and_unref(&ep->source_buffer);
+  grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &ep->source_buffer);
 
   if (result != TSI_OK) {
-    grpc_slice_buffer_reset_and_unref(ep->read_buffer);
+    grpc_slice_buffer_reset_and_unref_internal(exec_ctx, ep->read_buffer);
     call_read_cb(exec_ctx, ep, grpc_set_tsi_error_result(
                                    GRPC_ERROR_CREATE("Unwrap failed"), result));
     return;
@@ -232,7 +233,7 @@ static void endpoint_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep,
   secure_endpoint *ep = (secure_endpoint *)secure_ep;
   ep->read_cb = cb;
   ep->read_buffer = slices;
-  grpc_slice_buffer_reset_and_unref(ep->read_buffer);
+  grpc_slice_buffer_reset_and_unref_internal(exec_ctx, ep->read_buffer);
 
   SECURE_ENDPOINT_REF(ep, "read");
   if (ep->leftover_bytes.count) {
@@ -264,7 +265,7 @@ static void endpoint_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep,
   uint8_t *cur = GRPC_SLICE_START_PTR(ep->write_staging_buffer);
   uint8_t *end = GRPC_SLICE_END_PTR(ep->write_staging_buffer);
 
-  grpc_slice_buffer_reset_and_unref(&ep->output_buffer);
+  grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &ep->output_buffer);
 
   if (grpc_trace_secure_endpoint) {
     for (i = 0; i < slices->count; i++) {
@@ -328,11 +329,10 @@ static void endpoint_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep,
 
   if (result != TSI_OK) {
     /* TODO(yangg) do different things according to the error type? */
-    grpc_slice_buffer_reset_and_unref(&ep->output_buffer);
-    grpc_exec_ctx_sched(
+    grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &ep->output_buffer);
+    grpc_closure_sched(
         exec_ctx, cb,
-        grpc_set_tsi_error_result(GRPC_ERROR_CREATE("Wrap failed"), result),
-        NULL);
+        grpc_set_tsi_error_result(GRPC_ERROR_CREATE("Wrap failed"), result));
     GPR_TIMER_END("secure_endpoint.endpoint_write", 0);
     return;
   }
@@ -372,7 +372,10 @@ static char *endpoint_get_peer(grpc_endpoint *secure_ep) {
   return grpc_endpoint_get_peer(ep->wrapped_ep);
 }
 
-static int endpoint_get_fd(grpc_endpoint *secure_ep) { return -1; }
+static int endpoint_get_fd(grpc_endpoint *secure_ep) {
+  secure_endpoint *ep = (secure_endpoint *)secure_ep;
+  return grpc_endpoint_get_fd(ep->wrapped_ep);
+}
 
 static grpc_workqueue *endpoint_get_workqueue(grpc_endpoint *secure_ep) {
   secure_endpoint *ep = (secure_endpoint *)secure_ep;
@@ -407,14 +410,14 @@ grpc_endpoint *grpc_secure_endpoint_create(
   grpc_slice_buffer_init(&ep->leftover_bytes);
   for (i = 0; i < leftover_nslices; i++) {
     grpc_slice_buffer_add(&ep->leftover_bytes,
-                          grpc_slice_ref(leftover_slices[i]));
+                          grpc_slice_ref_internal(leftover_slices[i]));
   }
   ep->write_staging_buffer = grpc_slice_malloc(STAGING_BUFFER_SIZE);
   ep->read_staging_buffer = grpc_slice_malloc(STAGING_BUFFER_SIZE);
   grpc_slice_buffer_init(&ep->output_buffer);
   grpc_slice_buffer_init(&ep->source_buffer);
   ep->read_buffer = NULL;
-  grpc_closure_init(&ep->on_read, on_read, ep);
+  grpc_closure_init(&ep->on_read, on_read, ep, grpc_schedule_on_exec_ctx);
   gpr_mu_init(&ep->protector_mu);
   gpr_ref_init(&ep->ref, 1);
   return &ep->base;
diff --git a/src/core/lib/security/transport/security_connector.c b/src/core/lib/security/transport/security_connector.c
index 0fbd63a7e138a6893f0791900202599dc07b184f..5aa26e05771a0c0e795998aa81ee04b445153e34 100644
--- a/src/core/lib/security/transport/security_connector.c
+++ b/src/core/lib/security/transport/security_connector.c
@@ -43,11 +43,12 @@
 #include <grpc/support/string_util.h>
 
 #include "src/core/ext/transport/chttp2/alpn/alpn.h"
+#include "src/core/lib/channel/handshaker.h"
 #include "src/core/lib/iomgr/load_file.h"
 #include "src/core/lib/security/context/security_context.h"
 #include "src/core/lib/security/credentials/credentials.h"
-#include "src/core/lib/security/transport/handshake.h"
 #include "src/core/lib/security/transport/secure_endpoint.h"
+#include "src/core/lib/security/transport/security_handshaker.h"
 #include "src/core/lib/support/env.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/tsi/fake_transport_security.h"
@@ -111,58 +112,34 @@ const tsi_peer_property *tsi_peer_get_property_by_name(const tsi_peer *peer,
   return NULL;
 }
 
-void grpc_server_security_connector_shutdown(
-    grpc_exec_ctx *exec_ctx, grpc_server_security_connector *connector) {
-  grpc_security_connector_handshake_list *tmp;
-  gpr_mu_lock(&connector->mu);
-  while (connector->handshaking_handshakes) {
-    tmp = connector->handshaking_handshakes;
-    grpc_security_handshake_shutdown(
-        exec_ctx, connector->handshaking_handshakes->handshake);
-    connector->handshaking_handshakes = tmp->next;
-    gpr_free(tmp);
+void grpc_channel_security_connector_add_handshakers(
+    grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *connector,
+    grpc_handshake_manager *handshake_mgr) {
+  if (connector != NULL) {
+    connector->add_handshakers(exec_ctx, connector, handshake_mgr);
   }
-  gpr_mu_unlock(&connector->mu);
 }
 
-void grpc_channel_security_connector_do_handshake(
-    grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc,
-    grpc_endpoint *nonsecure_endpoint, grpc_slice_buffer *read_buffer,
-    gpr_timespec deadline, grpc_security_handshake_done_cb cb,
-    void *user_data) {
-  if (sc == NULL || nonsecure_endpoint == NULL) {
-    gpr_free(read_buffer);
-    cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL);
-  } else {
-    sc->do_handshake(exec_ctx, sc, nonsecure_endpoint, read_buffer, deadline,
-                     cb, user_data);
-  }
-}
-
-void grpc_server_security_connector_do_handshake(
-    grpc_exec_ctx *exec_ctx, grpc_server_security_connector *sc,
-    grpc_tcp_server_acceptor *acceptor, grpc_endpoint *nonsecure_endpoint,
-    grpc_slice_buffer *read_buffer, gpr_timespec deadline,
-    grpc_security_handshake_done_cb cb, void *user_data) {
-  if (sc == NULL || nonsecure_endpoint == NULL) {
-    gpr_free(read_buffer);
-    cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL);
-  } else {
-    sc->do_handshake(exec_ctx, sc, acceptor, nonsecure_endpoint, read_buffer,
-                     deadline, cb, user_data);
+void grpc_server_security_connector_add_handshakers(
+    grpc_exec_ctx *exec_ctx, grpc_server_security_connector *connector,
+    grpc_handshake_manager *handshake_mgr) {
+  if (connector != NULL) {
+    connector->add_handshakers(exec_ctx, connector, handshake_mgr);
   }
 }
 
 void grpc_security_connector_check_peer(grpc_exec_ctx *exec_ctx,
                                         grpc_security_connector *sc,
                                         tsi_peer peer,
-                                        grpc_security_peer_check_cb cb,
-                                        void *user_data) {
+                                        grpc_auth_context **auth_context,
+                                        grpc_closure *on_peer_checked) {
   if (sc == NULL) {
-    cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL);
+    grpc_closure_sched(
+        exec_ctx, on_peer_checked,
+        GRPC_ERROR_CREATE("cannot check peer -- no security connector"));
     tsi_peer_destruct(&peer);
   } else {
-    sc->vtable->check_peer(exec_ctx, sc, peer, cb, user_data);
+    sc->vtable->check_peer(exec_ctx, sc, peer, auth_context, on_peer_checked);
   }
 }
 
@@ -195,7 +172,8 @@ grpc_security_connector *grpc_security_connector_ref(
 }
 
 #ifdef GRPC_SECURITY_CONNECTOR_REFCOUNT_DEBUG
-void grpc_security_connector_unref(grpc_security_connector *sc,
+void grpc_security_connector_unref(grpc_exec_ctx *exec_ctx,
+                                   grpc_security_connector *sc,
                                    const char *file, int line,
                                    const char *reason) {
   if (sc == NULL) return;
@@ -203,14 +181,15 @@ void grpc_security_connector_unref(grpc_security_connector *sc,
           "SECURITY_CONNECTOR:%p unref %d -> %d %s", sc,
           (int)sc->refcount.count, (int)sc->refcount.count - 1, reason);
 #else
-void grpc_security_connector_unref(grpc_security_connector *sc) {
+void grpc_security_connector_unref(grpc_exec_ctx *exec_ctx,
+                                   grpc_security_connector *sc) {
   if (sc == NULL) return;
 #endif
-  if (gpr_unref(&sc->refcount)) sc->vtable->destroy(sc);
+  if (gpr_unref(&sc->refcount)) sc->vtable->destroy(exec_ctx, sc);
 }
 
-static void connector_pointer_arg_destroy(void *p) {
-  GRPC_SECURITY_CONNECTOR_UNREF(p, "connector_pointer_arg_destroy");
+static void connector_pointer_arg_destroy(grpc_exec_ctx *exec_ctx, void *p) {
+  GRPC_SECURITY_CONNECTOR_UNREF(exec_ctx, p, "connector_pointer_arg_destroy");
 }
 
 static void *connector_pointer_arg_copy(void *p) {
@@ -256,51 +235,51 @@ grpc_security_connector *grpc_find_security_connector_in_args(
 
 /* -- Fake implementation. -- */
 
-static void fake_channel_destroy(grpc_security_connector *sc) {
+static void fake_channel_destroy(grpc_exec_ctx *exec_ctx,
+                                 grpc_security_connector *sc) {
   grpc_channel_security_connector *c = (grpc_channel_security_connector *)sc;
-  grpc_call_credentials_unref(c->request_metadata_creds);
+  grpc_call_credentials_unref(exec_ctx, c->request_metadata_creds);
   gpr_free(sc);
 }
 
-static void fake_server_destroy(grpc_security_connector *sc) {
-  grpc_server_security_connector *c = (grpc_server_security_connector *)sc;
-  gpr_mu_destroy(&c->mu);
+static void fake_server_destroy(grpc_exec_ctx *exec_ctx,
+                                grpc_security_connector *sc) {
   gpr_free(sc);
 }
 
 static void fake_check_peer(grpc_exec_ctx *exec_ctx,
                             grpc_security_connector *sc, tsi_peer peer,
-                            grpc_security_peer_check_cb cb, void *user_data) {
+                            grpc_auth_context **auth_context,
+                            grpc_closure *on_peer_checked) {
   const char *prop_name;
-  grpc_security_status status = GRPC_SECURITY_OK;
-  grpc_auth_context *auth_context = NULL;
+  grpc_error *error = GRPC_ERROR_NONE;
+  *auth_context = NULL;
   if (peer.property_count != 1) {
-    gpr_log(GPR_ERROR, "Fake peers should only have 1 property.");
-    status = GRPC_SECURITY_ERROR;
+    error = GRPC_ERROR_CREATE("Fake peers should only have 1 property.");
     goto end;
   }
   prop_name = peer.properties[0].name;
   if (prop_name == NULL ||
       strcmp(prop_name, TSI_CERTIFICATE_TYPE_PEER_PROPERTY)) {
-    gpr_log(GPR_ERROR, "Unexpected property in fake peer: %s.",
-            prop_name == NULL ? "<EMPTY>" : prop_name);
-    status = GRPC_SECURITY_ERROR;
+    char *msg;
+    gpr_asprintf(&msg, "Unexpected property in fake peer: %s.",
+                 prop_name == NULL ? "<EMPTY>" : prop_name);
+    error = GRPC_ERROR_CREATE(msg);
+    gpr_free(msg);
     goto end;
   }
   if (strncmp(peer.properties[0].value.data, TSI_FAKE_CERTIFICATE_TYPE,
               peer.properties[0].value.length)) {
-    gpr_log(GPR_ERROR, "Invalid value for cert type property.");
-    status = GRPC_SECURITY_ERROR;
+    error = GRPC_ERROR_CREATE("Invalid value for cert type property.");
     goto end;
   }
-  auth_context = grpc_auth_context_create(NULL);
+  *auth_context = grpc_auth_context_create(NULL);
   grpc_auth_context_add_cstring_property(
-      auth_context, GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME,
+      *auth_context, GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME,
       GRPC_FAKE_TRANSPORT_SECURITY_TYPE);
 
 end:
-  cb(exec_ctx, user_data, status, auth_context);
-  grpc_auth_context_unref(auth_context);
+  grpc_closure_sched(exec_ctx, on_peer_checked, error);
   tsi_peer_destruct(&peer);
 }
 
@@ -313,26 +292,24 @@ static void fake_channel_check_call_host(grpc_exec_ctx *exec_ctx,
   cb(exec_ctx, user_data, GRPC_SECURITY_OK);
 }
 
-static void fake_channel_do_handshake(grpc_exec_ctx *exec_ctx,
-                                      grpc_channel_security_connector *sc,
-                                      grpc_endpoint *nonsecure_endpoint,
-                                      grpc_slice_buffer *read_buffer,
-                                      gpr_timespec deadline,
-                                      grpc_security_handshake_done_cb cb,
-                                      void *user_data) {
-  grpc_do_security_handshake(exec_ctx, tsi_create_fake_handshaker(1), &sc->base,
-                             true, nonsecure_endpoint, read_buffer, deadline,
-                             cb, user_data);
+static void fake_channel_add_handshakers(
+    grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc,
+    grpc_handshake_manager *handshake_mgr) {
+  grpc_handshake_manager_add(
+      handshake_mgr,
+      grpc_security_handshaker_create(
+          exec_ctx, tsi_create_fake_handshaker(true /* is_client */),
+          &sc->base));
 }
 
-static void fake_server_do_handshake(
-    grpc_exec_ctx *exec_ctx, grpc_server_security_connector *sc,
-    grpc_tcp_server_acceptor *acceptor, grpc_endpoint *nonsecure_endpoint,
-    grpc_slice_buffer *read_buffer, gpr_timespec deadline,
-    grpc_security_handshake_done_cb cb, void *user_data) {
-  grpc_do_security_handshake(exec_ctx, tsi_create_fake_handshaker(0), &sc->base,
-                             false, nonsecure_endpoint, read_buffer, deadline,
-                             cb, user_data);
+static void fake_server_add_handshakers(grpc_exec_ctx *exec_ctx,
+                                        grpc_server_security_connector *sc,
+                                        grpc_handshake_manager *handshake_mgr) {
+  grpc_handshake_manager_add(
+      handshake_mgr,
+      grpc_security_handshaker_create(
+          exec_ctx, tsi_create_fake_handshaker(false /* is_client */),
+          &sc->base));
 }
 
 static grpc_security_connector_vtable fake_channel_vtable = {
@@ -350,7 +327,7 @@ grpc_channel_security_connector *grpc_fake_channel_security_connector_create(
   c->base.vtable = &fake_channel_vtable;
   c->request_metadata_creds = grpc_call_credentials_ref(request_metadata_creds);
   c->check_call_host = fake_channel_check_call_host;
-  c->do_handshake = fake_channel_do_handshake;
+  c->add_handshakers = fake_channel_add_handshakers;
   return c;
 }
 
@@ -362,8 +339,7 @@ grpc_server_security_connector *grpc_fake_server_security_connector_create(
   gpr_ref_init(&c->base.refcount, 1);
   c->base.vtable = &fake_server_vtable;
   c->base.url_scheme = GRPC_FAKE_SECURITY_URL_SCHEME;
-  c->do_handshake = fake_server_do_handshake;
-  gpr_mu_init(&c->mu);
+  c->add_handshakers = fake_server_add_handshakers;
   return c;
 }
 
@@ -381,10 +357,11 @@ typedef struct {
   tsi_ssl_handshaker_factory *handshaker_factory;
 } grpc_ssl_server_security_connector;
 
-static void ssl_channel_destroy(grpc_security_connector *sc) {
+static void ssl_channel_destroy(grpc_exec_ctx *exec_ctx,
+                                grpc_security_connector *sc) {
   grpc_ssl_channel_security_connector *c =
       (grpc_ssl_channel_security_connector *)sc;
-  grpc_call_credentials_unref(c->base.request_metadata_creds);
+  grpc_call_credentials_unref(exec_ctx, c->base.request_metadata_creds);
   if (c->handshaker_factory != NULL) {
     tsi_ssl_handshaker_factory_destroy(c->handshaker_factory);
   }
@@ -393,14 +370,13 @@ static void ssl_channel_destroy(grpc_security_connector *sc) {
   gpr_free(sc);
 }
 
-static void ssl_server_destroy(grpc_security_connector *sc) {
+static void ssl_server_destroy(grpc_exec_ctx *exec_ctx,
+                               grpc_security_connector *sc) {
   grpc_ssl_server_security_connector *c =
       (grpc_ssl_server_security_connector *)sc;
-
   if (c->handshaker_factory != NULL) {
     tsi_ssl_handshaker_factory_destroy(c->handshaker_factory);
   }
-  gpr_mu_destroy(&c->base.mu);
   gpr_free(sc);
 }
 
@@ -419,49 +395,35 @@ static grpc_security_status ssl_create_handshaker(
   return GRPC_SECURITY_OK;
 }
 
-static void ssl_channel_do_handshake(grpc_exec_ctx *exec_ctx,
-                                     grpc_channel_security_connector *sc,
-                                     grpc_endpoint *nonsecure_endpoint,
-                                     grpc_slice_buffer *read_buffer,
-                                     gpr_timespec deadline,
-                                     grpc_security_handshake_done_cb cb,
-                                     void *user_data) {
+static void ssl_channel_add_handshakers(grpc_exec_ctx *exec_ctx,
+                                        grpc_channel_security_connector *sc,
+                                        grpc_handshake_manager *handshake_mgr) {
   grpc_ssl_channel_security_connector *c =
       (grpc_ssl_channel_security_connector *)sc;
-  tsi_handshaker *handshaker;
-  grpc_security_status status = ssl_create_handshaker(
-      c->handshaker_factory, true,
-      c->overridden_target_name != NULL ? c->overridden_target_name
-                                        : c->target_name,
-      &handshaker);
-  if (status != GRPC_SECURITY_OK) {
-    gpr_free(read_buffer);
-    cb(exec_ctx, user_data, status, NULL, NULL);
-  } else {
-    grpc_do_security_handshake(exec_ctx, handshaker, &sc->base, true,
-                               nonsecure_endpoint, read_buffer, deadline, cb,
-                               user_data);
-  }
-}
-
-static void ssl_server_do_handshake(
-    grpc_exec_ctx *exec_ctx, grpc_server_security_connector *sc,
-    grpc_tcp_server_acceptor *acceptor, grpc_endpoint *nonsecure_endpoint,
-    grpc_slice_buffer *read_buffer, gpr_timespec deadline,
-    grpc_security_handshake_done_cb cb, void *user_data) {
+  // Instantiate TSI handshaker.
+  tsi_handshaker *tsi_hs = NULL;
+  ssl_create_handshaker(c->handshaker_factory, true /* is_client */,
+                        c->overridden_target_name != NULL
+                            ? c->overridden_target_name
+                            : c->target_name,
+                        &tsi_hs);
+  // Create handshakers.
+  grpc_handshake_manager_add(handshake_mgr, grpc_security_handshaker_create(
+                                                exec_ctx, tsi_hs, &sc->base));
+}
+
+static void ssl_server_add_handshakers(grpc_exec_ctx *exec_ctx,
+                                       grpc_server_security_connector *sc,
+                                       grpc_handshake_manager *handshake_mgr) {
   grpc_ssl_server_security_connector *c =
       (grpc_ssl_server_security_connector *)sc;
-  tsi_handshaker *handshaker;
-  grpc_security_status status =
-      ssl_create_handshaker(c->handshaker_factory, false, NULL, &handshaker);
-  if (status != GRPC_SECURITY_OK) {
-    gpr_free(read_buffer);
-    cb(exec_ctx, user_data, status, NULL, NULL);
-  } else {
-    grpc_do_security_handshake(exec_ctx, handshaker, &sc->base, false,
-                               nonsecure_endpoint, read_buffer, deadline, cb,
-                               user_data);
-  }
+  // Instantiate TSI handshaker.
+  tsi_handshaker *tsi_hs = NULL;
+  ssl_create_handshaker(c->handshaker_factory, false /* is_client */,
+                        NULL /* peer_name */, &tsi_hs);
+  // Create handshakers.
+  grpc_handshake_manager_add(handshake_mgr, grpc_security_handshaker_create(
+                                                exec_ctx, tsi_hs, &sc->base));
 }
 
 static int ssl_host_matches_name(const tsi_peer *peer, const char *peer_name) {
@@ -518,57 +480,53 @@ grpc_auth_context *tsi_ssl_peer_to_auth_context(const tsi_peer *peer) {
   return ctx;
 }
 
-static grpc_security_status ssl_check_peer(grpc_security_connector *sc,
-                                           const char *peer_name,
-                                           const tsi_peer *peer,
-                                           grpc_auth_context **auth_context) {
+static grpc_error *ssl_check_peer(grpc_security_connector *sc,
+                                  const char *peer_name, const tsi_peer *peer,
+                                  grpc_auth_context **auth_context) {
   /* Check the ALPN. */
   const tsi_peer_property *p =
       tsi_peer_get_property_by_name(peer, TSI_SSL_ALPN_SELECTED_PROTOCOL);
   if (p == NULL) {
-    gpr_log(GPR_ERROR, "Missing selected ALPN property.");
-    return GRPC_SECURITY_ERROR;
+    return GRPC_ERROR_CREATE(
+        "Cannot check peer: missing selected ALPN property.");
   }
   if (!grpc_chttp2_is_alpn_version_supported(p->value.data, p->value.length)) {
-    gpr_log(GPR_ERROR, "Invalid ALPN value.");
-    return GRPC_SECURITY_ERROR;
+    return GRPC_ERROR_CREATE("Cannot check peer: invalid ALPN value.");
   }
 
   /* Check the peer name if specified. */
   if (peer_name != NULL && !ssl_host_matches_name(peer, peer_name)) {
-    gpr_log(GPR_ERROR, "Peer name %s is not in peer certificate", peer_name);
-    return GRPC_SECURITY_ERROR;
+    char *msg;
+    gpr_asprintf(&msg, "Peer name %s is not in peer certificate", peer_name);
+    grpc_error *error = GRPC_ERROR_CREATE(msg);
+    gpr_free(msg);
+    return error;
   }
   *auth_context = tsi_ssl_peer_to_auth_context(peer);
-  return GRPC_SECURITY_OK;
+  return GRPC_ERROR_NONE;
 }
 
 static void ssl_channel_check_peer(grpc_exec_ctx *exec_ctx,
                                    grpc_security_connector *sc, tsi_peer peer,
-                                   grpc_security_peer_check_cb cb,
-                                   void *user_data) {
+                                   grpc_auth_context **auth_context,
+                                   grpc_closure *on_peer_checked) {
   grpc_ssl_channel_security_connector *c =
       (grpc_ssl_channel_security_connector *)sc;
-  grpc_security_status status;
-  grpc_auth_context *auth_context = NULL;
-  status = ssl_check_peer(sc, c->overridden_target_name != NULL
-                                  ? c->overridden_target_name
-                                  : c->target_name,
-                          &peer, &auth_context);
-  cb(exec_ctx, user_data, status, auth_context);
-  grpc_auth_context_unref(auth_context);
+  grpc_error *error = ssl_check_peer(sc, c->overridden_target_name != NULL
+                                             ? c->overridden_target_name
+                                             : c->target_name,
+                                     &peer, auth_context);
+  grpc_closure_sched(exec_ctx, on_peer_checked, error);
   tsi_peer_destruct(&peer);
 }
 
 static void ssl_server_check_peer(grpc_exec_ctx *exec_ctx,
                                   grpc_security_connector *sc, tsi_peer peer,
-                                  grpc_security_peer_check_cb cb,
-                                  void *user_data) {
-  grpc_auth_context *auth_context = NULL;
-  grpc_security_status status = ssl_check_peer(sc, NULL, &peer, &auth_context);
+                                  grpc_auth_context **auth_context,
+                                  grpc_closure *on_peer_checked) {
+  grpc_error *error = ssl_check_peer(sc, NULL, &peer, auth_context);
   tsi_peer_destruct(&peer);
-  cb(exec_ctx, user_data, status, auth_context);
-  grpc_auth_context_unref(auth_context);
+  grpc_closure_sched(exec_ctx, on_peer_checked, error);
 }
 
 static void add_shallow_auth_property_to_peer(tsi_peer *peer,
@@ -719,7 +677,7 @@ size_t grpc_get_default_ssl_roots(const unsigned char **pem_root_certs) {
 }
 
 grpc_security_status grpc_ssl_channel_security_connector_create(
-    grpc_call_credentials *request_metadata_creds,
+    grpc_exec_ctx *exec_ctx, grpc_call_credentials *request_metadata_creds,
     const grpc_ssl_config *config, const char *target_name,
     const char *overridden_target_name, grpc_channel_security_connector **sc) {
   size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions();
@@ -765,7 +723,7 @@ grpc_security_status grpc_ssl_channel_security_connector_create(
   c->base.request_metadata_creds =
       grpc_call_credentials_ref(request_metadata_creds);
   c->base.check_call_host = ssl_channel_check_call_host;
-  c->base.do_handshake = ssl_channel_do_handshake;
+  c->base.add_handshakers = ssl_channel_add_handshakers;
   gpr_split_host_port(target_name, &c->target_name, &port);
   gpr_free(port);
   if (overridden_target_name != NULL) {
@@ -780,7 +738,7 @@ grpc_security_status grpc_ssl_channel_security_connector_create(
   if (result != TSI_OK) {
     gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.",
             tsi_result_to_string(result));
-    ssl_channel_destroy(&c->base.base);
+    ssl_channel_destroy(exec_ctx, &c->base.base);
     *sc = NULL;
     goto error;
   }
@@ -796,7 +754,8 @@ error:
 }
 
 grpc_security_status grpc_ssl_server_security_connector_create(
-    const grpc_ssl_server_config *config, grpc_server_security_connector **sc) {
+    grpc_exec_ctx *exec_ctx, const grpc_ssl_server_config *config,
+    grpc_server_security_connector **sc) {
   size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions();
   const unsigned char **alpn_protocol_strings =
       gpr_malloc(sizeof(const char *) * num_alpn_protocols);
@@ -836,12 +795,11 @@ grpc_security_status grpc_ssl_server_security_connector_create(
   if (result != TSI_OK) {
     gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.",
             tsi_result_to_string(result));
-    ssl_server_destroy(&c->base.base);
+    ssl_server_destroy(exec_ctx, &c->base.base);
     *sc = NULL;
     goto error;
   }
-  gpr_mu_init(&c->base.mu);
-  c->base.do_handshake = ssl_server_do_handshake;
+  c->base.add_handshakers = ssl_server_add_handshakers;
   *sc = &c->base;
   gpr_free((void *)alpn_protocol_strings);
   gpr_free(alpn_protocol_string_lengths);
diff --git a/src/core/lib/security/transport/security_connector.h b/src/core/lib/security/transport/security_connector.h
index dc02692b01817e06e96c8bc4baf296c3e77e40e3..eba4e6d1d7baf83102917bd8e2536bc31ed3bba2 100644
--- a/src/core/lib/security/transport/security_connector.h
+++ b/src/core/lib/security/transport/security_connector.h
@@ -35,6 +35,8 @@
 #define GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_CONNECTOR_H
 
 #include <grpc/grpc_security.h>
+
+#include "src/core/lib/channel/handshaker.h"
 #include "src/core/lib/iomgr/endpoint.h"
 #include "src/core/lib/iomgr/tcp_server.h"
 #include "src/core/lib/tsi/transport_security_interface.h"
@@ -57,21 +59,11 @@ typedef struct grpc_security_connector grpc_security_connector;
 
 #define GRPC_SECURITY_CONNECTOR_ARG "grpc.security_connector"
 
-typedef void (*grpc_security_peer_check_cb)(grpc_exec_ctx *exec_ctx,
-                                            void *user_data,
-                                            grpc_security_status status,
-                                            grpc_auth_context *auth_context);
-
-/* Ownership of the secure_endpoint is transfered. */
-typedef void (*grpc_security_handshake_done_cb)(
-    grpc_exec_ctx *exec_ctx, void *user_data, grpc_security_status status,
-    grpc_endpoint *secure_endpoint, grpc_auth_context *auth_context);
-
 typedef struct {
-  void (*destroy)(grpc_security_connector *sc);
+  void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_security_connector *sc);
   void (*check_peer)(grpc_exec_ctx *exec_ctx, grpc_security_connector *sc,
-                     tsi_peer peer, grpc_security_peer_check_cb cb,
-                     void *user_data);
+                     tsi_peer peer, grpc_auth_context **auth_context,
+                     grpc_closure *on_peer_checked);
 } grpc_security_connector_vtable;
 
 typedef struct grpc_security_connector_handshake_list {
@@ -89,29 +81,32 @@ struct grpc_security_connector {
 #ifdef GRPC_SECURITY_CONNECTOR_REFCOUNT_DEBUG
 #define GRPC_SECURITY_CONNECTOR_REF(p, r) \
   grpc_security_connector_ref((p), __FILE__, __LINE__, (r))
-#define GRPC_SECURITY_CONNECTOR_UNREF(p, r) \
-  grpc_security_connector_unref((p), __FILE__, __LINE__, (r))
+#define GRPC_SECURITY_CONNECTOR_UNREF(exec_ctx, p, r) \
+  grpc_security_connector_unref((exec_ctx), (p), __FILE__, __LINE__, (r))
 grpc_security_connector *grpc_security_connector_ref(
     grpc_security_connector *policy, const char *file, int line,
     const char *reason);
-void grpc_security_connector_unref(grpc_security_connector *policy,
+void grpc_security_connector_unref(grpc_exec_ctx *exec_ctx,
+                                   grpc_security_connector *policy,
                                    const char *file, int line,
                                    const char *reason);
 #else
 #define GRPC_SECURITY_CONNECTOR_REF(p, r) grpc_security_connector_ref((p))
-#define GRPC_SECURITY_CONNECTOR_UNREF(p, r) grpc_security_connector_unref((p))
+#define GRPC_SECURITY_CONNECTOR_UNREF(exec_ctx, p, r) \
+  grpc_security_connector_unref((exec_ctx), (p))
 grpc_security_connector *grpc_security_connector_ref(
     grpc_security_connector *policy);
-void grpc_security_connector_unref(grpc_security_connector *policy);
+void grpc_security_connector_unref(grpc_exec_ctx *exec_ctx,
+                                   grpc_security_connector *policy);
 #endif
 
 /* Check the peer. Callee takes ownership of the peer object.
-   The callback will include the resulting auth_context. */
+   When done, sets *auth_context and invokes on_peer_checked. */
 void grpc_security_connector_check_peer(grpc_exec_ctx *exec_ctx,
                                         grpc_security_connector *sc,
                                         tsi_peer peer,
-                                        grpc_security_peer_check_cb cb,
-                                        void *user_data);
+                                        grpc_auth_context **auth_context,
+                                        grpc_closure *on_peer_checked);
 
 /* Util to encapsulate the connector in a channel arg. */
 grpc_arg grpc_security_connector_to_arg(grpc_security_connector *sc);
@@ -141,11 +136,9 @@ struct grpc_channel_security_connector {
                           grpc_channel_security_connector *sc, const char *host,
                           grpc_auth_context *auth_context,
                           grpc_security_call_host_check_cb cb, void *user_data);
-  void (*do_handshake)(grpc_exec_ctx *exec_ctx,
-                       grpc_channel_security_connector *sc,
-                       grpc_endpoint *nonsecure_endpoint,
-                       grpc_slice_buffer *read_buffer, gpr_timespec deadline,
-                       grpc_security_handshake_done_cb cb, void *user_data);
+  void (*add_handshakers)(grpc_exec_ctx *exec_ctx,
+                          grpc_channel_security_connector *sc,
+                          grpc_handshake_manager *handshake_mgr);
 };
 
 /* Checks that the host that will be set for a call is acceptable. */
@@ -154,11 +147,10 @@ void grpc_channel_security_connector_check_call_host(
     const char *host, grpc_auth_context *auth_context,
     grpc_security_call_host_check_cb cb, void *user_data);
 
-/* Handshake. */
-void grpc_channel_security_connector_do_handshake(
+/* Registers handshakers with \a handshake_mgr. */
+void grpc_channel_security_connector_add_handshakers(
     grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *connector,
-    grpc_endpoint *nonsecure_endpoint, grpc_slice_buffer *read_buffer,
-    gpr_timespec deadline, grpc_security_handshake_done_cb cb, void *user_data);
+    grpc_handshake_manager *handshake_mgr);
 
 /* --- server_security_connector object. ---
 
@@ -169,25 +161,14 @@ typedef struct grpc_server_security_connector grpc_server_security_connector;
 
 struct grpc_server_security_connector {
   grpc_security_connector base;
-  gpr_mu mu;
-  grpc_security_connector_handshake_list *handshaking_handshakes;
-  const grpc_channel_args *channel_args;
-  void (*do_handshake)(grpc_exec_ctx *exec_ctx,
-                       grpc_server_security_connector *sc,
-                       grpc_tcp_server_acceptor *acceptor,
-                       grpc_endpoint *nonsecure_endpoint,
-                       grpc_slice_buffer *read_buffer, gpr_timespec deadline,
-                       grpc_security_handshake_done_cb cb, void *user_data);
+  void (*add_handshakers)(grpc_exec_ctx *exec_ctx,
+                          grpc_server_security_connector *sc,
+                          grpc_handshake_manager *handshake_mgr);
 };
 
-void grpc_server_security_connector_do_handshake(
+void grpc_server_security_connector_add_handshakers(
     grpc_exec_ctx *exec_ctx, grpc_server_security_connector *sc,
-    grpc_tcp_server_acceptor *acceptor, grpc_endpoint *nonsecure_endpoint,
-    grpc_slice_buffer *read_buffer, gpr_timespec deadline,
-    grpc_security_handshake_done_cb cb, void *user_data);
-
-void grpc_server_security_connector_shutdown(
-    grpc_exec_ctx *exec_ctx, grpc_server_security_connector *connector);
+    grpc_handshake_manager *handshake_mgr);
 
 /* --- Creation security connectors. --- */
 
@@ -225,7 +206,7 @@ typedef struct {
   specific error code otherwise.
 */
 grpc_security_status grpc_ssl_channel_security_connector_create(
-    grpc_call_credentials *request_metadata_creds,
+    grpc_exec_ctx *exec_ctx, grpc_call_credentials *request_metadata_creds,
     const grpc_ssl_config *config, const char *target_name,
     const char *overridden_target_name, grpc_channel_security_connector **sc);
 
@@ -254,7 +235,8 @@ typedef struct {
   specific error code otherwise.
 */
 grpc_security_status grpc_ssl_server_security_connector_create(
-    const grpc_ssl_server_config *config, grpc_server_security_connector **sc);
+    grpc_exec_ctx *exec_ctx, const grpc_ssl_server_config *config,
+    grpc_server_security_connector **sc);
 
 /* Util. */
 const tsi_peer_property *tsi_peer_get_property_by_name(const tsi_peer *peer,
diff --git a/src/core/lib/security/transport/security_handshaker.c b/src/core/lib/security/transport/security_handshaker.c
new file mode 100644
index 0000000000000000000000000000000000000000..5e75856c7a456b1c37e31436614a581b341041cb
--- /dev/null
+++ b/src/core/lib/security/transport/security_handshaker.c
@@ -0,0 +1,501 @@
+/*
+ *
+ * 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/security/transport/security_handshaker.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/handshaker.h"
+#include "src/core/lib/channel/handshaker_registry.h"
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/security/transport/secure_endpoint.h"
+#include "src/core/lib/security/transport/tsi_error.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+#define GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE 256
+
+typedef struct {
+  grpc_handshaker base;
+
+  // State set at creation time.
+  tsi_handshaker *handshaker;
+  grpc_security_connector *connector;
+
+  gpr_mu mu;
+  gpr_refcount refs;
+
+  bool shutdown;
+  // Endpoint and read buffer to destroy after a shutdown.
+  grpc_endpoint *endpoint_to_destroy;
+  grpc_slice_buffer *read_buffer_to_destroy;
+
+  // State saved while performing the handshake.
+  grpc_handshaker_args *args;
+  grpc_closure *on_handshake_done;
+
+  unsigned char *handshake_buffer;
+  size_t handshake_buffer_size;
+  grpc_slice_buffer left_overs;
+  grpc_slice_buffer outgoing;
+  grpc_closure on_handshake_data_sent_to_peer;
+  grpc_closure on_handshake_data_received_from_peer;
+  grpc_closure on_peer_checked;
+  grpc_auth_context *auth_context;
+} security_handshaker;
+
+static void security_handshaker_unref(grpc_exec_ctx *exec_ctx,
+                                      security_handshaker *h) {
+  if (gpr_unref(&h->refs)) {
+    gpr_mu_destroy(&h->mu);
+    tsi_handshaker_destroy(h->handshaker);
+    if (h->endpoint_to_destroy != NULL) {
+      grpc_endpoint_destroy(exec_ctx, h->endpoint_to_destroy);
+    }
+    if (h->read_buffer_to_destroy != NULL) {
+      grpc_slice_buffer_destroy_internal(exec_ctx, h->read_buffer_to_destroy);
+      gpr_free(h->read_buffer_to_destroy);
+    }
+    gpr_free(h->handshake_buffer);
+    grpc_slice_buffer_destroy_internal(exec_ctx, &h->left_overs);
+    grpc_slice_buffer_destroy_internal(exec_ctx, &h->outgoing);
+    GRPC_AUTH_CONTEXT_UNREF(h->auth_context, "handshake");
+    GRPC_SECURITY_CONNECTOR_UNREF(exec_ctx, h->connector, "handshake");
+    gpr_free(h);
+  }
+}
+
+// Set args fields to NULL, saving the endpoint and read buffer for
+// later destruction.
+static void cleanup_args_for_failure_locked(grpc_exec_ctx *exec_ctx,
+                                            security_handshaker *h) {
+  h->endpoint_to_destroy = h->args->endpoint;
+  h->args->endpoint = NULL;
+  h->read_buffer_to_destroy = h->args->read_buffer;
+  h->args->read_buffer = NULL;
+  grpc_channel_args_destroy(exec_ctx, h->args->args);
+  h->args->args = NULL;
+}
+
+// If the handshake failed or we're shutting down, clean up and invoke the
+// callback with the error.
+static void security_handshake_failed_locked(grpc_exec_ctx *exec_ctx,
+                                             security_handshaker *h,
+                                             grpc_error *error) {
+  if (error == GRPC_ERROR_NONE) {
+    // If we were shut down after the handshake succeeded but before an
+    // endpoint callback was invoked, we need to generate our own error.
+    error = GRPC_ERROR_CREATE("Handshaker shutdown");
+  }
+  const char *msg = grpc_error_string(error);
+  gpr_log(GPR_DEBUG, "Security handshake failed: %s", msg);
+  grpc_error_free_string(msg);
+  if (!h->shutdown) {
+    // TODO(ctiller): It is currently necessary to shutdown endpoints
+    // before destroying them, even if we know that there are no
+    // pending read/write callbacks.  This should be fixed, at which
+    // point this can be removed.
+    grpc_endpoint_shutdown(exec_ctx, h->args->endpoint);
+    // Not shutting down, so the write failed.  Clean up before
+    // invoking the callback.
+    cleanup_args_for_failure_locked(exec_ctx, h);
+    // Set shutdown to true so that subsequent calls to
+    // security_handshaker_shutdown() do nothing.
+    h->shutdown = true;
+  }
+  // Invoke callback.
+  grpc_closure_sched(exec_ctx, h->on_handshake_done, error);
+}
+
+static void on_peer_checked(grpc_exec_ctx *exec_ctx, void *arg,
+                            grpc_error *error) {
+  security_handshaker *h = arg;
+  gpr_mu_lock(&h->mu);
+  if (error != GRPC_ERROR_NONE || h->shutdown) {
+    security_handshake_failed_locked(exec_ctx, h, GRPC_ERROR_REF(error));
+    goto done;
+  }
+  // Get frame protector.
+  tsi_frame_protector *protector;
+  tsi_result result =
+      tsi_handshaker_create_frame_protector(h->handshaker, NULL, &protector);
+  if (result != TSI_OK) {
+    error = grpc_set_tsi_error_result(
+        GRPC_ERROR_CREATE("Frame protector creation failed"), result);
+    security_handshake_failed_locked(exec_ctx, h, error);
+    goto done;
+  }
+  // Success.
+  // Create secure endpoint.
+  h->args->endpoint = grpc_secure_endpoint_create(
+      protector, h->args->endpoint, h->left_overs.slices, h->left_overs.count);
+  h->left_overs.count = 0;
+  h->left_overs.length = 0;
+  // Clear out the read buffer before it gets passed to the transport,
+  // since any excess bytes were already copied to h->left_overs.
+  grpc_slice_buffer_reset_and_unref_internal(exec_ctx, h->args->read_buffer);
+  // Add auth context to channel args.
+  grpc_arg auth_context_arg = grpc_auth_context_to_arg(h->auth_context);
+  grpc_channel_args *tmp_args = h->args->args;
+  h->args->args =
+      grpc_channel_args_copy_and_add(tmp_args, &auth_context_arg, 1);
+  grpc_channel_args_destroy(exec_ctx, tmp_args);
+  // Invoke callback.
+  grpc_closure_sched(exec_ctx, h->on_handshake_done, GRPC_ERROR_NONE);
+  // Set shutdown to true so that subsequent calls to
+  // security_handshaker_shutdown() do nothing.
+  h->shutdown = true;
+done:
+  gpr_mu_unlock(&h->mu);
+  security_handshaker_unref(exec_ctx, h);
+}
+
+static grpc_error *check_peer_locked(grpc_exec_ctx *exec_ctx,
+                                     security_handshaker *h) {
+  tsi_peer peer;
+  tsi_result result = tsi_handshaker_extract_peer(h->handshaker, &peer);
+  if (result != TSI_OK) {
+    return grpc_set_tsi_error_result(
+        GRPC_ERROR_CREATE("Peer extraction failed"), result);
+  }
+  grpc_security_connector_check_peer(exec_ctx, h->connector, peer,
+                                     &h->auth_context, &h->on_peer_checked);
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error *send_handshake_bytes_to_peer_locked(grpc_exec_ctx *exec_ctx,
+                                                       security_handshaker *h) {
+  // Get data to send.
+  tsi_result result = TSI_OK;
+  size_t offset = 0;
+  do {
+    size_t to_send_size = h->handshake_buffer_size - offset;
+    result = tsi_handshaker_get_bytes_to_send_to_peer(
+        h->handshaker, h->handshake_buffer + offset, &to_send_size);
+    offset += to_send_size;
+    if (result == TSI_INCOMPLETE_DATA) {
+      h->handshake_buffer_size *= 2;
+      h->handshake_buffer =
+          gpr_realloc(h->handshake_buffer, h->handshake_buffer_size);
+    }
+  } while (result == TSI_INCOMPLETE_DATA);
+  if (result != TSI_OK) {
+    return grpc_set_tsi_error_result(GRPC_ERROR_CREATE("Handshake failed"),
+                                     result);
+  }
+  // Send data.
+  grpc_slice to_send =
+      grpc_slice_from_copied_buffer((const char *)h->handshake_buffer, offset);
+  grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &h->outgoing);
+  grpc_slice_buffer_add(&h->outgoing, to_send);
+  grpc_endpoint_write(exec_ctx, h->args->endpoint, &h->outgoing,
+                      &h->on_handshake_data_sent_to_peer);
+  return GRPC_ERROR_NONE;
+}
+
+static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx,
+                                                 void *arg, grpc_error *error) {
+  security_handshaker *h = arg;
+  gpr_mu_lock(&h->mu);
+  if (error != GRPC_ERROR_NONE || h->shutdown) {
+    security_handshake_failed_locked(
+        exec_ctx, h,
+        GRPC_ERROR_CREATE_REFERENCING("Handshake read failed", &error, 1));
+    gpr_mu_unlock(&h->mu);
+    security_handshaker_unref(exec_ctx, h);
+    return;
+  }
+  // Process received data.
+  tsi_result result = TSI_OK;
+  size_t consumed_slice_size = 0;
+  size_t i;
+  for (i = 0; i < h->args->read_buffer->count; i++) {
+    consumed_slice_size = GRPC_SLICE_LENGTH(h->args->read_buffer->slices[i]);
+    result = tsi_handshaker_process_bytes_from_peer(
+        h->handshaker, GRPC_SLICE_START_PTR(h->args->read_buffer->slices[i]),
+        &consumed_slice_size);
+    if (!tsi_handshaker_is_in_progress(h->handshaker)) break;
+  }
+  if (tsi_handshaker_is_in_progress(h->handshaker)) {
+    /* We may need more data. */
+    if (result == TSI_INCOMPLETE_DATA) {
+      grpc_endpoint_read(exec_ctx, h->args->endpoint, h->args->read_buffer,
+                         &h->on_handshake_data_received_from_peer);
+      goto done;
+    } else {
+      error = send_handshake_bytes_to_peer_locked(exec_ctx, h);
+      if (error != GRPC_ERROR_NONE) {
+        security_handshake_failed_locked(exec_ctx, h, error);
+        gpr_mu_unlock(&h->mu);
+        security_handshaker_unref(exec_ctx, h);
+        return;
+      }
+      goto done;
+    }
+  }
+  if (result != TSI_OK) {
+    security_handshake_failed_locked(
+        exec_ctx, h, grpc_set_tsi_error_result(
+                         GRPC_ERROR_CREATE("Handshake failed"), result));
+    gpr_mu_unlock(&h->mu);
+    security_handshaker_unref(exec_ctx, h);
+    return;
+  }
+  /* Handshake is done and successful this point. */
+  bool has_left_overs_in_current_slice =
+      (consumed_slice_size <
+       GRPC_SLICE_LENGTH(h->args->read_buffer->slices[i]));
+  size_t num_left_overs = (has_left_overs_in_current_slice ? 1 : 0) +
+                          h->args->read_buffer->count - i - 1;
+  if (num_left_overs > 0) {
+    /* Put the leftovers in our buffer (ownership transfered). */
+    if (has_left_overs_in_current_slice) {
+      grpc_slice_buffer_add(
+          &h->left_overs,
+          grpc_slice_split_tail(&h->args->read_buffer->slices[i],
+                                consumed_slice_size));
+      /* split_tail above increments refcount. */
+      grpc_slice_unref_internal(exec_ctx, h->args->read_buffer->slices[i]);
+    }
+    grpc_slice_buffer_addn(
+        &h->left_overs, &h->args->read_buffer->slices[i + 1],
+        num_left_overs - (size_t)has_left_overs_in_current_slice);
+  }
+  // Check peer.
+  error = check_peer_locked(exec_ctx, h);
+  if (error != GRPC_ERROR_NONE) {
+    security_handshake_failed_locked(exec_ctx, h, error);
+    gpr_mu_unlock(&h->mu);
+    security_handshaker_unref(exec_ctx, h);
+    return;
+  }
+done:
+  gpr_mu_unlock(&h->mu);
+}
+
+static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx, void *arg,
+                                           grpc_error *error) {
+  security_handshaker *h = arg;
+  gpr_mu_lock(&h->mu);
+  if (error != GRPC_ERROR_NONE || h->shutdown) {
+    security_handshake_failed_locked(
+        exec_ctx, h,
+        GRPC_ERROR_CREATE_REFERENCING("Handshake write failed", &error, 1));
+    gpr_mu_unlock(&h->mu);
+    security_handshaker_unref(exec_ctx, h);
+    return;
+  }
+  /* We may be done. */
+  if (tsi_handshaker_is_in_progress(h->handshaker)) {
+    grpc_endpoint_read(exec_ctx, h->args->endpoint, h->args->read_buffer,
+                       &h->on_handshake_data_received_from_peer);
+  } else {
+    error = check_peer_locked(exec_ctx, h);
+    if (error != GRPC_ERROR_NONE) {
+      security_handshake_failed_locked(exec_ctx, h, error);
+      gpr_mu_unlock(&h->mu);
+      security_handshaker_unref(exec_ctx, h);
+      return;
+    }
+  }
+  gpr_mu_unlock(&h->mu);
+}
+
+//
+// public handshaker API
+//
+
+static void security_handshaker_destroy(grpc_exec_ctx *exec_ctx,
+                                        grpc_handshaker *handshaker) {
+  security_handshaker *h = (security_handshaker *)handshaker;
+  security_handshaker_unref(exec_ctx, h);
+}
+
+static void security_handshaker_shutdown(grpc_exec_ctx *exec_ctx,
+                                         grpc_handshaker *handshaker) {
+  security_handshaker *h = (security_handshaker *)handshaker;
+  gpr_mu_lock(&h->mu);
+  if (!h->shutdown) {
+    h->shutdown = true;
+    grpc_endpoint_shutdown(exec_ctx, h->args->endpoint);
+    cleanup_args_for_failure_locked(exec_ctx, h);
+  }
+  gpr_mu_unlock(&h->mu);
+}
+
+static void security_handshaker_do_handshake(grpc_exec_ctx *exec_ctx,
+                                             grpc_handshaker *handshaker,
+                                             grpc_tcp_server_acceptor *acceptor,
+                                             grpc_closure *on_handshake_done,
+                                             grpc_handshaker_args *args) {
+  security_handshaker *h = (security_handshaker *)handshaker;
+  gpr_mu_lock(&h->mu);
+  h->args = args;
+  h->on_handshake_done = on_handshake_done;
+  gpr_ref(&h->refs);
+  grpc_error *error = send_handshake_bytes_to_peer_locked(exec_ctx, h);
+  if (error != GRPC_ERROR_NONE) {
+    security_handshake_failed_locked(exec_ctx, h, error);
+    gpr_mu_unlock(&h->mu);
+    security_handshaker_unref(exec_ctx, h);
+    return;
+  }
+  gpr_mu_unlock(&h->mu);
+}
+
+static const grpc_handshaker_vtable security_handshaker_vtable = {
+    security_handshaker_destroy, security_handshaker_shutdown,
+    security_handshaker_do_handshake};
+
+static grpc_handshaker *security_handshaker_create(
+    grpc_exec_ctx *exec_ctx, tsi_handshaker *handshaker,
+    grpc_security_connector *connector) {
+  security_handshaker *h = gpr_malloc(sizeof(security_handshaker));
+  memset(h, 0, sizeof(security_handshaker));
+  grpc_handshaker_init(&security_handshaker_vtable, &h->base);
+  h->handshaker = handshaker;
+  h->connector = GRPC_SECURITY_CONNECTOR_REF(connector, "handshake");
+  gpr_mu_init(&h->mu);
+  gpr_ref_init(&h->refs, 1);
+  h->handshake_buffer_size = GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE;
+  h->handshake_buffer = gpr_malloc(h->handshake_buffer_size);
+  grpc_closure_init(&h->on_handshake_data_sent_to_peer,
+                    on_handshake_data_sent_to_peer, h,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&h->on_handshake_data_received_from_peer,
+                    on_handshake_data_received_from_peer, h,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&h->on_peer_checked, on_peer_checked, h,
+                    grpc_schedule_on_exec_ctx);
+  grpc_slice_buffer_init(&h->left_overs);
+  grpc_slice_buffer_init(&h->outgoing);
+  return &h->base;
+}
+
+//
+// fail_handshaker
+//
+
+static void fail_handshaker_destroy(grpc_exec_ctx *exec_ctx,
+                                    grpc_handshaker *handshaker) {
+  gpr_free(handshaker);
+}
+
+static void fail_handshaker_shutdown(grpc_exec_ctx *exec_ctx,
+                                     grpc_handshaker *handshaker) {}
+
+static void fail_handshaker_do_handshake(grpc_exec_ctx *exec_ctx,
+                                         grpc_handshaker *handshaker,
+                                         grpc_tcp_server_acceptor *acceptor,
+                                         grpc_closure *on_handshake_done,
+                                         grpc_handshaker_args *args) {
+  grpc_closure_sched(exec_ctx, on_handshake_done,
+                     GRPC_ERROR_CREATE("Failed to create security handshaker"));
+}
+
+static const grpc_handshaker_vtable fail_handshaker_vtable = {
+    fail_handshaker_destroy, fail_handshaker_shutdown,
+    fail_handshaker_do_handshake};
+
+static grpc_handshaker *fail_handshaker_create() {
+  grpc_handshaker *h = gpr_malloc(sizeof(*h));
+  grpc_handshaker_init(&fail_handshaker_vtable, h);
+  return h;
+}
+
+//
+// handshaker factories
+//
+
+static void client_handshaker_factory_add_handshakers(
+    grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *handshaker_factory,
+    const grpc_channel_args *args, grpc_handshake_manager *handshake_mgr) {
+  grpc_channel_security_connector *security_connector =
+      (grpc_channel_security_connector *)grpc_find_security_connector_in_args(
+          args);
+  grpc_channel_security_connector_add_handshakers(exec_ctx, security_connector,
+                                                  handshake_mgr);
+}
+
+static void server_handshaker_factory_add_handshakers(
+    grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *hf,
+    const grpc_channel_args *args, grpc_handshake_manager *handshake_mgr) {
+  grpc_server_security_connector *security_connector =
+      (grpc_server_security_connector *)grpc_find_security_connector_in_args(
+          args);
+  grpc_server_security_connector_add_handshakers(exec_ctx, security_connector,
+                                                 handshake_mgr);
+}
+
+static void handshaker_factory_destroy(
+    grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *handshaker_factory) {}
+
+static const grpc_handshaker_factory_vtable client_handshaker_factory_vtable = {
+    client_handshaker_factory_add_handshakers, handshaker_factory_destroy};
+
+static grpc_handshaker_factory client_handshaker_factory = {
+    &client_handshaker_factory_vtable};
+
+static const grpc_handshaker_factory_vtable server_handshaker_factory_vtable = {
+    server_handshaker_factory_add_handshakers, handshaker_factory_destroy};
+
+static grpc_handshaker_factory server_handshaker_factory = {
+    &server_handshaker_factory_vtable};
+
+//
+// exported functions
+//
+
+grpc_handshaker *grpc_security_handshaker_create(
+    grpc_exec_ctx *exec_ctx, tsi_handshaker *handshaker,
+    grpc_security_connector *connector) {
+  // If no TSI handshaker was created, return a handshaker that always fails.
+  // Otherwise, return a real security handshaker.
+  if (handshaker == NULL) {
+    return fail_handshaker_create();
+  } else {
+    return security_handshaker_create(exec_ctx, handshaker, connector);
+  }
+}
+
+void grpc_security_register_handshaker_factories() {
+  grpc_handshaker_factory_register(false /* at_start */, HANDSHAKER_CLIENT,
+                                   &client_handshaker_factory);
+  grpc_handshaker_factory_register(false /* at_start */, HANDSHAKER_SERVER,
+                                   &server_handshaker_factory);
+}
diff --git a/src/core/lib/security/transport/handshake.h b/src/core/lib/security/transport/security_handshaker.h
similarity index 73%
rename from src/core/lib/security/transport/handshake.h
rename to src/core/lib/security/transport/security_handshaker.h
index f8945405154bcb695ff3bb034d866aa2d81e333e..0b9eda178fdfe8e6b50d1a37808089b283822419 100644
--- a/src/core/lib/security/transport/handshake.h
+++ b/src/core/lib/security/transport/security_handshaker.h
@@ -31,20 +31,19 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_HANDSHAKE_H
-#define GRPC_CORE_LIB_SECURITY_TRANSPORT_HANDSHAKE_H
+#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_HANDSHAKER_H
+#define GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_HANDSHAKER_H
 
-#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/channel/handshaker.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/security/transport/security_connector.h"
 
-/* Calls the callback upon completion. Takes owership of handshaker and
- * read_buffer. */
-void grpc_do_security_handshake(
+/// Creates a security handshaker using \a handshaker.
+grpc_handshaker *grpc_security_handshaker_create(
     grpc_exec_ctx *exec_ctx, tsi_handshaker *handshaker,
-    grpc_security_connector *connector, bool is_client_side,
-    grpc_endpoint *nonsecure_endpoint, grpc_slice_buffer *read_buffer,
-    gpr_timespec deadline, grpc_security_handshake_done_cb cb, void *user_data);
+    grpc_security_connector *connector);
 
-void grpc_security_handshake_shutdown(grpc_exec_ctx *exec_ctx, void *handshake);
+/// Registers security handshaker factories.
+void grpc_security_register_handshaker_factories();
 
-#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_HANDSHAKE_H */
+#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_HANDSHAKER_H */
diff --git a/src/core/lib/security/transport/server_auth_filter.c b/src/core/lib/security/transport/server_auth_filter.c
index eaa1d0720b25b8effa18c9775a27876f55e5ce85..5e98ba895dcac977f2b106c4971dfdf4c75a09ce 100644
--- a/src/core/lib/security/transport/server_auth_filter.c
+++ b/src/core/lib/security/transport/server_auth_filter.c
@@ -83,7 +83,8 @@ static grpc_metadata_array metadata_batch_to_md_array(
   return result;
 }
 
-static grpc_mdelem *remove_consumed_md(void *user_data, grpc_mdelem *md) {
+static grpc_mdelem *remove_consumed_md(grpc_exec_ctx *exec_ctx, void *user_data,
+                                       grpc_mdelem *md) {
   grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
   size_t i;
@@ -129,10 +130,10 @@ static void on_md_processing_done(
   if (status == GRPC_STATUS_OK) {
     calld->consumed_md = consumed_md;
     calld->num_consumed_md = num_consumed_md;
-    grpc_metadata_batch_filter(calld->recv_initial_metadata, remove_consumed_md,
-                               elem);
+    grpc_metadata_batch_filter(&exec_ctx, calld->recv_initial_metadata,
+                               remove_consumed_md, elem);
     grpc_metadata_array_destroy(&calld->md);
-    grpc_exec_ctx_sched(&exec_ctx, calld->on_done_recv, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(&exec_ctx, calld->on_done_recv, GRPC_ERROR_NONE);
   } else {
     grpc_slice message;
     grpc_transport_stream_op *close_op = gpr_malloc(sizeof(*close_op));
@@ -148,13 +149,13 @@ static void on_md_processing_done(
       calld->transport_op->send_message = NULL;
     }
     calld->transport_op->send_trailing_metadata = NULL;
-    close_op->on_complete = grpc_closure_create(destroy_op, close_op);
-    grpc_transport_stream_op_add_close(close_op, status, &message);
+    close_op->on_complete =
+        grpc_closure_create(destroy_op, close_op, grpc_schedule_on_exec_ctx);
+    grpc_transport_stream_op_add_close(&exec_ctx, close_op, status, &message);
     grpc_call_next_op(&exec_ctx, elem, close_op);
-    grpc_exec_ctx_sched(&exec_ctx, calld->on_done_recv,
-                        grpc_error_set_int(GRPC_ERROR_CREATE(error_details),
-                                           GRPC_ERROR_INT_GRPC_STATUS, status),
-                        NULL);
+    grpc_closure_sched(&exec_ctx, calld->on_done_recv,
+                       grpc_error_set_int(GRPC_ERROR_CREATE(error_details),
+                                          GRPC_ERROR_INT_GRPC_STATUS, status));
   }
 
   grpc_exec_ctx_finish(&exec_ctx);
@@ -174,8 +175,7 @@ static void auth_on_recv(grpc_exec_ctx *exec_ctx, void *user_data,
       return;
     }
   }
-  grpc_exec_ctx_sched(exec_ctx, calld->on_done_recv, GRPC_ERROR_REF(error),
-                      NULL);
+  grpc_closure_sched(exec_ctx, calld->on_done_recv, GRPC_ERROR_REF(error));
 }
 
 static void set_recv_ops_md_callbacks(grpc_call_element *elem,
@@ -214,7 +214,8 @@ static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
 
   /* initialize members */
   memset(calld, 0, sizeof(*calld));
-  grpc_closure_init(&calld->auth_on_recv, auth_on_recv, elem);
+  grpc_closure_init(&calld->auth_on_recv, auth_on_recv, elem,
+                    grpc_schedule_on_exec_ctx);
 
   if (args->context[GRPC_CONTEXT_SECURITY].value != NULL) {
     args->context[GRPC_CONTEXT_SECURITY].destroy(
@@ -238,9 +239,9 @@ static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                               void *ignored) {}
 
 /* Constructor for channel_data */
-static void init_channel_elem(grpc_exec_ctx *exec_ctx,
-                              grpc_channel_element *elem,
-                              grpc_channel_element_args *args) {
+static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx,
+                                     grpc_channel_element *elem,
+                                     grpc_channel_element_args *args) {
   grpc_auth_context *auth_context =
       grpc_find_auth_context_in_args(args->channel_args);
   grpc_server_credentials *creds =
@@ -256,6 +257,7 @@ static void init_channel_elem(grpc_exec_ctx *exec_ctx,
   chand->auth_context =
       GRPC_AUTH_CONTEXT_REF(auth_context, "server_auth_filter");
   chand->creds = grpc_server_credentials_ref(creds);
+  return GRPC_ERROR_NONE;
 }
 
 /* Destructor for channel data */
@@ -264,7 +266,7 @@ static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
   /* grab pointers to our data from the channel element */
   channel_data *chand = elem->channel_data;
   GRPC_AUTH_CONTEXT_UNREF(chand->auth_context, "server_auth_filter");
-  grpc_server_credentials_unref(chand->creds);
+  grpc_server_credentials_unref(exec_ctx, chand->creds);
 }
 
 const grpc_channel_filter grpc_server_auth_filter = {
diff --git a/src/core/lib/security/util/b64.c b/src/core/lib/security/util/b64.c
index 4892e8e6213f0eb29e6f6fc95cc8d0cecb168f7a..bbd7e335a67d51a98552983bdfc6535ff4421f54 100644
--- a/src/core/lib/security/util/b64.c
+++ b/src/core/lib/security/util/b64.c
@@ -40,6 +40,8 @@
 #include <grpc/support/log.h>
 #include <grpc/support/useful.h>
 
+#include "src/core/lib/slice/slice_internal.h"
+
 /* --- Constants. --- */
 
 static const int8_t base64_bytes[] = {
@@ -120,8 +122,9 @@ char *grpc_base64_encode(const void *vdata, size_t data_size, int url_safe,
   return result;
 }
 
-grpc_slice grpc_base64_decode(const char *b64, int url_safe) {
-  return grpc_base64_decode_with_len(b64, strlen(b64), url_safe);
+grpc_slice grpc_base64_decode(grpc_exec_ctx *exec_ctx, const char *b64,
+                              int url_safe) {
+  return grpc_base64_decode_with_len(exec_ctx, b64, strlen(b64), url_safe);
 }
 
 static void decode_one_char(const unsigned char *codes, unsigned char *result,
@@ -182,8 +185,8 @@ static int decode_group(const unsigned char *codes, size_t num_codes,
   return 1;
 }
 
-grpc_slice grpc_base64_decode_with_len(const char *b64, size_t b64_len,
-                                       int url_safe) {
+grpc_slice grpc_base64_decode_with_len(grpc_exec_ctx *exec_ctx, const char *b64,
+                                       size_t b64_len, int url_safe) {
   grpc_slice result = grpc_slice_malloc(b64_len);
   unsigned char *current = GRPC_SLICE_START_PTR(result);
   size_t result_size = 0;
@@ -228,6 +231,6 @@ grpc_slice grpc_base64_decode_with_len(const char *b64, size_t b64_len,
   return result;
 
 fail:
-  grpc_slice_unref(result);
+  grpc_slice_unref_internal(exec_ctx, result);
   return gpr_empty_slice();
 }
diff --git a/src/core/lib/security/util/b64.h b/src/core/lib/security/util/b64.h
index 6ea0b5365be4a1a02a7f5492c747d0118f015898..d42a136f61a2a85e4e02b3c316feb6caf3a4ca45 100644
--- a/src/core/lib/security/util/b64.h
+++ b/src/core/lib/security/util/b64.h
@@ -43,10 +43,11 @@ char *grpc_base64_encode(const void *data, size_t data_size, int url_safe,
 
 /* Decodes data according to the base64 specification. Returns an empty
    slice in case of failure. */
-grpc_slice grpc_base64_decode(const char *b64, int url_safe);
+grpc_slice grpc_base64_decode(grpc_exec_ctx *exec_ctx, const char *b64,
+                              int url_safe);
 
 /* Same as above except that the length is provided by the caller. */
-grpc_slice grpc_base64_decode_with_len(const char *b64, size_t b64_len,
-                                       int url_safe);
+grpc_slice grpc_base64_decode_with_len(grpc_exec_ctx *exec_ctx, const char *b64,
+                                       size_t b64_len, int url_safe);
 
 #endif /* GRPC_CORE_LIB_SECURITY_UTIL_B64_H */
diff --git a/src/core/lib/slice/percent_encoding.c b/src/core/lib/slice/percent_encoding.c
index b9e35f1c71ecb37b19c6bf48eaf496d0e7eb7c33..c76c58d371e95e379a2cb50625eefcd81df7b971 100644
--- a/src/core/lib/slice/percent_encoding.c
+++ b/src/core/lib/slice/percent_encoding.c
@@ -35,6 +35,8 @@
 
 #include <grpc/support/log.h>
 
+#include "src/core/lib/slice/slice_internal.h"
+
 const uint8_t grpc_url_percent_encoding_unreserved_bytes[256 / 8] = {
     0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xff, 0x03, 0xfe, 0xff, 0xff,
     0x87, 0xfe, 0xff, 0xff, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -66,7 +68,7 @@ grpc_slice grpc_percent_encode_slice(grpc_slice slice,
   }
   // no unreserved bytes: return the string unmodified
   if (!any_reserved_bytes) {
-    return grpc_slice_ref(slice);
+    return grpc_slice_ref_internal(slice);
   }
   // second pass: actually encode
   grpc_slice out = grpc_slice_malloc(output_length);
@@ -119,7 +121,7 @@ bool grpc_strict_percent_decode_slice(grpc_slice slice_in,
     }
   }
   if (!any_percent_encoded_stuff) {
-    *slice_out = grpc_slice_ref(slice_in);
+    *slice_out = grpc_slice_ref_internal(slice_in);
     return true;
   }
   p = GRPC_SLICE_START_PTR(slice_in);
@@ -158,7 +160,7 @@ grpc_slice grpc_permissive_percent_decode_slice(grpc_slice slice_in) {
     }
   }
   if (!any_percent_encoded_stuff) {
-    return grpc_slice_ref(slice_in);
+    return grpc_slice_ref_internal(slice_in);
   }
   p = GRPC_SLICE_START_PTR(slice_in);
   grpc_slice out = grpc_slice_malloc(out_length);
diff --git a/src/core/lib/slice/slice.c b/src/core/lib/slice/slice.c
index 52977e6d9a6d8ca338f33fab2d83a129e0b7920e..76118102ece44cc2dd0a71ced563233efb828ac4 100644
--- a/src/core/lib/slice/slice.c
+++ b/src/core/lib/slice/slice.c
@@ -31,12 +31,16 @@
  *
  */
 
+#include "src/core/lib/slice/slice_internal.h"
+
 #include <grpc/slice.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
 #include <string.h>
 
+#include "src/core/lib/iomgr/exec_ctx.h"
+
 grpc_slice gpr_empty_slice(void) {
   grpc_slice out;
   out.refcount = 0;
@@ -44,25 +48,37 @@ grpc_slice gpr_empty_slice(void) {
   return out;
 }
 
-grpc_slice grpc_slice_ref(grpc_slice slice) {
+grpc_slice grpc_slice_ref_internal(grpc_slice slice) {
   if (slice.refcount) {
     slice.refcount->ref(slice.refcount);
   }
   return slice;
 }
 
-void grpc_slice_unref(grpc_slice slice) {
+void grpc_slice_unref_internal(grpc_exec_ctx *exec_ctx, grpc_slice slice) {
   if (slice.refcount) {
-    slice.refcount->unref(slice.refcount);
+    slice.refcount->unref(exec_ctx, slice.refcount);
   }
 }
 
+/* Public API */
+grpc_slice grpc_slice_ref(grpc_slice slice) {
+  return grpc_slice_ref_internal(slice);
+}
+
+/* Public API */
+void grpc_slice_unref(grpc_slice slice) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_slice_unref_internal(&exec_ctx, slice);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
 /* grpc_slice_from_static_string support structure - a refcount that does
    nothing */
-static void noop_ref_or_unref(void *unused) {}
+static void noop_ref(void *unused) {}
+static void noop_unref(grpc_exec_ctx *exec_ctx, void *unused) {}
 
-static grpc_slice_refcount noop_refcount = {noop_ref_or_unref,
-                                            noop_ref_or_unref};
+static grpc_slice_refcount noop_refcount = {noop_ref, noop_unref};
 
 grpc_slice grpc_slice_from_static_string(const char *s) {
   grpc_slice slice;
@@ -86,7 +102,7 @@ static void new_slice_ref(void *p) {
   gpr_ref(&r->refs);
 }
 
-static void new_slice_unref(void *p) {
+static void new_slice_unref(grpc_exec_ctx *exec_ctx, void *p) {
   new_slice_refcount *r = p;
   if (gpr_unref(&r->refs)) {
     r->user_destroy(r->user_data);
@@ -131,7 +147,7 @@ static void new_with_len_ref(void *p) {
   gpr_ref(&r->refs);
 }
 
-static void new_with_len_unref(void *p) {
+static void new_with_len_unref(grpc_exec_ctx *exec_ctx, void *p) {
   new_with_len_slice_refcount *r = p;
   if (gpr_unref(&r->refs)) {
     r->user_destroy(r->user_data, r->user_length);
@@ -177,7 +193,7 @@ static void malloc_ref(void *p) {
   gpr_ref(&r->refs);
 }
 
-static void malloc_unref(void *p) {
+static void malloc_unref(grpc_exec_ctx *exec_ctx, void *p) {
   malloc_refcount *r = p;
   if (gpr_unref(&r->refs)) {
     gpr_free(r);
diff --git a/src/core/lib/slice/slice_buffer.c b/src/core/lib/slice/slice_buffer.c
index 990ef128bdfce66de23d5205559a7013f7bfeba8..08eaf4963a6e406482bf2e05b7e7de9eec230fed 100644
--- a/src/core/lib/slice/slice_buffer.c
+++ b/src/core/lib/slice/slice_buffer.c
@@ -40,6 +40,8 @@
 #include <grpc/support/log.h>
 #include <grpc/support/useful.h>
 
+#include "src/core/lib/slice/slice_internal.h"
+
 /* grow a buffer; requires GRPC_SLICE_BUFFER_INLINE_ELEMENTS > 1 */
 #define GROW(x) (3 * (x) / 2)
 
@@ -63,13 +65,20 @@ void grpc_slice_buffer_init(grpc_slice_buffer *sb) {
   sb->slices = sb->inlined;
 }
 
-void grpc_slice_buffer_destroy(grpc_slice_buffer *sb) {
-  grpc_slice_buffer_reset_and_unref(sb);
+void grpc_slice_buffer_destroy_internal(grpc_exec_ctx *exec_ctx,
+                                        grpc_slice_buffer *sb) {
+  grpc_slice_buffer_reset_and_unref_internal(exec_ctx, sb);
   if (sb->slices != sb->inlined) {
     gpr_free(sb->slices);
   }
 }
 
+void grpc_slice_buffer_destroy(grpc_slice_buffer *sb) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_slice_buffer_destroy_internal(&exec_ctx, sb);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
 uint8_t *grpc_slice_buffer_tiny_add(grpc_slice_buffer *sb, size_t n) {
   grpc_slice *back;
   uint8_t *out;
@@ -154,17 +163,24 @@ void grpc_slice_buffer_pop(grpc_slice_buffer *sb) {
   }
 }
 
-void grpc_slice_buffer_reset_and_unref(grpc_slice_buffer *sb) {
+void grpc_slice_buffer_reset_and_unref_internal(grpc_exec_ctx *exec_ctx,
+                                                grpc_slice_buffer *sb) {
   size_t i;
 
   for (i = 0; i < sb->count; i++) {
-    grpc_slice_unref(sb->slices[i]);
+    grpc_slice_unref_internal(exec_ctx, sb->slices[i]);
   }
 
   sb->count = 0;
   sb->length = 0;
 }
 
+void grpc_slice_buffer_reset_and_unref(grpc_slice_buffer *sb) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_slice_buffer_reset_and_unref_internal(&exec_ctx, sb);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
 void grpc_slice_buffer_swap(grpc_slice_buffer *a, grpc_slice_buffer *b) {
   GPR_SWAP(size_t, a->count, b->count);
   GPR_SWAP(size_t, a->capacity, b->capacity);
diff --git a/src/core/lib/slice/slice_internal.h b/src/core/lib/slice/slice_internal.h
new file mode 100644
index 0000000000000000000000000000000000000000..6185333ca7218582fcf2e3425b31a53d374d48b9
--- /dev/null
+++ b/src/core/lib/slice/slice_internal.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SLICE_SLICE_INTERNAL_H
+#define GRPC_CORE_LIB_SLICE_SLICE_INTERNAL_H
+
+#include <grpc/slice.h>
+#include <grpc/slice_buffer.h>
+
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+grpc_slice grpc_slice_ref_internal(grpc_slice slice);
+void grpc_slice_unref_internal(grpc_exec_ctx *exec_ctx, grpc_slice slice);
+void grpc_slice_buffer_reset_and_unref_internal(grpc_exec_ctx *exec_ctx,
+                                                grpc_slice_buffer *sb);
+void grpc_slice_buffer_destroy_internal(grpc_exec_ctx *exec_ctx,
+                                        grpc_slice_buffer *sb);
+
+#endif /* GRPC_CORE_LIB_SLICE_SLICE_INTERNAL_H */
diff --git a/src/core/lib/slice/slice_string_helpers.c b/src/core/lib/slice/slice_string_helpers.c
index 4731762eceb284b1fcb6f92ab284cda37bf58e61..839c366b32d0dcf29ffa2bdef85fbbc59c08ecb7 100644
--- a/src/core/lib/slice/slice_string_helpers.c
+++ b/src/core/lib/slice/slice_string_helpers.c
@@ -37,6 +37,7 @@
 
 #include <grpc/support/log.h>
 
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/support/string.h"
 
 char *grpc_dump_slice(grpc_slice s, uint32_t flags) {
@@ -84,6 +85,6 @@ void grpc_slice_split(grpc_slice str, const char *sep, grpc_slice_buffer *dst) {
     grpc_slice_buffer_add_indexed(
         dst, grpc_slice_sub(str, end + sep_len, GRPC_SLICE_LENGTH(str)));
   } else { /* no sep found, add whole input */
-    grpc_slice_buffer_add_indexed(dst, grpc_slice_ref(str));
+    grpc_slice_buffer_add_indexed(dst, grpc_slice_ref_internal(str));
   }
 }
diff --git a/src/core/lib/support/backoff.c b/src/core/lib/support/backoff.c
index e89ef4722072c396040241281623524740ff3afa..061247271281269081a6f4bc2c2f3494c2c75d4a 100644
--- a/src/core/lib/support/backoff.c
+++ b/src/core/lib/support/backoff.c
@@ -35,8 +35,10 @@
 
 #include <grpc/support/useful.h>
 
-void gpr_backoff_init(gpr_backoff *backoff, double multiplier, double jitter,
+void gpr_backoff_init(gpr_backoff *backoff, int64_t initial_connect_timeout,
+                      double multiplier, double jitter,
                       int64_t min_timeout_millis, int64_t max_timeout_millis) {
+  backoff->initial_connect_timeout = initial_connect_timeout;
   backoff->multiplier = multiplier;
   backoff->jitter = jitter;
   backoff->min_timeout_millis = min_timeout_millis;
@@ -45,9 +47,10 @@ void gpr_backoff_init(gpr_backoff *backoff, double multiplier, double jitter,
 }
 
 gpr_timespec gpr_backoff_begin(gpr_backoff *backoff, gpr_timespec now) {
-  backoff->current_timeout_millis = backoff->min_timeout_millis;
-  return gpr_time_add(
-      now, gpr_time_from_millis(backoff->current_timeout_millis, GPR_TIMESPAN));
+  backoff->current_timeout_millis = backoff->initial_connect_timeout;
+  const int64_t first_timeout =
+      GPR_MAX(backoff->current_timeout_millis, backoff->min_timeout_millis);
+  return gpr_time_add(now, gpr_time_from_millis(first_timeout, GPR_TIMESPAN));
 }
 
 /* Generate a random number between 0 and 1. */
@@ -57,20 +60,28 @@ static double generate_uniform_random_number(uint32_t *rng_state) {
 }
 
 gpr_timespec gpr_backoff_step(gpr_backoff *backoff, gpr_timespec now) {
-  double new_timeout_millis =
+  const double new_timeout_millis =
       backoff->multiplier * (double)backoff->current_timeout_millis;
-  double jitter_range = backoff->jitter * new_timeout_millis;
-  double jitter =
+  backoff->current_timeout_millis =
+      GPR_MIN((int64_t)new_timeout_millis, backoff->max_timeout_millis);
+
+  const double jitter_range_width = backoff->jitter * new_timeout_millis;
+  const double jitter =
       (2 * generate_uniform_random_number(&backoff->rng_state) - 1) *
-      jitter_range;
+      jitter_range_width;
+
   backoff->current_timeout_millis =
-      GPR_CLAMP((int64_t)(new_timeout_millis + jitter),
-                backoff->min_timeout_millis, backoff->max_timeout_millis);
-  return gpr_time_add(
+      (int64_t)((double)(backoff->current_timeout_millis) + jitter);
+
+  const gpr_timespec current_deadline = gpr_time_add(
       now, gpr_time_from_millis(backoff->current_timeout_millis, GPR_TIMESPAN));
+
+  const gpr_timespec min_deadline = gpr_time_add(
+      now, gpr_time_from_millis(backoff->min_timeout_millis, GPR_TIMESPAN));
+
+  return gpr_time_max(current_deadline, min_deadline);
 }
 
 void gpr_backoff_reset(gpr_backoff *backoff) {
-  // forces step() to return a timeout of min_timeout_millis
-  backoff->current_timeout_millis = 0;
+  backoff->current_timeout_millis = backoff->initial_connect_timeout;
 }
diff --git a/src/core/lib/support/backoff.h b/src/core/lib/support/backoff.h
index 6d40c15546521d3f4e8f9d35b5f5de76cb86bb81..5e9b74082444c4f6e417062f42f34bcaee09cd1d 100644
--- a/src/core/lib/support/backoff.h
+++ b/src/core/lib/support/backoff.h
@@ -37,7 +37,9 @@
 #include <grpc/support/time.h>
 
 typedef struct {
-  /// const: multiplier between retry attempts
+  /// const:  how long to wait after the first failure before retrying
+  int64_t initial_connect_timeout;
+  /// const: factor with which to multiply backoff after a failed retry
   double multiplier;
   /// const: amount to randomize backoffs
   double jitter;
@@ -54,7 +56,8 @@ typedef struct {
 } gpr_backoff;
 
 /// Initialize backoff machinery - does not need to be destroyed
-void gpr_backoff_init(gpr_backoff *backoff, double multiplier, double jitter,
+void gpr_backoff_init(gpr_backoff *backoff, int64_t initial_connect_timeout,
+                      double multiplier, double jitter,
                       int64_t min_timeout_millis, int64_t max_timeout_millis);
 
 /// Begin retry loop: returns a timespec for the NEXT retry
diff --git a/src/core/lib/support/string.c b/src/core/lib/support/string.c
index f10a30f0fd5f7dd14d86e52011cf976ecb017a94..d20b86f7cf71baa948195b6e325e282da902c77e 100644
--- a/src/core/lib/support/string.c
+++ b/src/core/lib/support/string.c
@@ -275,3 +275,41 @@ int gpr_stricmp(const char *a, const char *b) {
   } while (ca == cb && ca && cb);
   return ca - cb;
 }
+
+static void add_string_to_split(const char *beg, const char *end, char ***strs,
+                                size_t *nstrs, size_t *capstrs) {
+  char *out = gpr_malloc((size_t)(end - beg) + 1);
+  memcpy(out, beg, (size_t)(end - beg));
+  out[end - beg] = 0;
+  if (*nstrs == *capstrs) {
+    *capstrs = GPR_MAX(8, 2 * *capstrs);
+    *strs = gpr_realloc(*strs, sizeof(*strs) * *capstrs);
+  }
+  (*strs)[*nstrs] = out;
+  ++*nstrs;
+}
+
+void gpr_string_split(const char *input, const char *sep, char ***strs,
+                      size_t *nstrs) {
+  char *next;
+  *strs = NULL;
+  *nstrs = 0;
+  size_t capstrs = 0;
+  while ((next = strstr(input, sep))) {
+    add_string_to_split(input, next, strs, nstrs, &capstrs);
+    input = next + strlen(sep);
+  }
+  add_string_to_split(input, input + strlen(input), strs, nstrs, &capstrs);
+}
+
+void *gpr_memrchr(const void *s, int c, size_t n) {
+  if (s == NULL) return NULL;
+  char *b = (char *)s;
+  size_t i;
+  for (i = 0; i < n; i++) {
+    if (b[n - i - 1] == c) {
+      return &b[n - i - 1];
+    }
+  }
+  return NULL;
+}
diff --git a/src/core/lib/support/string.h b/src/core/lib/support/string.h
index e933e2eb468c6b9818ceaf0d1b9901bd5cbe4f37..d1c52faed4adb500f8768bb67bb3972a794c984e 100644
--- a/src/core/lib/support/string.h
+++ b/src/core/lib/support/string.h
@@ -36,8 +36,6 @@
 
 #include <stddef.h>
 
-#include <grpc/slice.h>
-#include <grpc/slice_buffer.h>
 #include <grpc/support/port_platform.h>
 
 #ifdef __cplusplus
@@ -98,6 +96,9 @@ char *gpr_strjoin(const char **strs, size_t nstrs, size_t *total_length);
 char *gpr_strjoin_sep(const char **strs, size_t nstrs, const char *sep,
                       size_t *total_length);
 
+void gpr_string_split(const char *input, const char *sep, char ***strs,
+                      size_t *nstrs);
+
 /* A vector of strings... for building up a final string one piece at a time */
 typedef struct {
   char **strs;
@@ -118,6 +119,8 @@ char *gpr_strvec_flatten(gpr_strvec *strs, size_t *total_length);
     lower(a)==lower(b), >0 if lower(a)>lower(b) */
 int gpr_stricmp(const char *a, const char *b);
 
+void *gpr_memrchr(const void *s, int c, size_t n);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/core/lib/support/tmpfile.h b/src/core/lib/support/tmpfile.h
index 8952e5ec3da764589fa3e78ed4e2f9dc88a14771..f613cf9bc8d9ee4a7eaa5891165b41a755144789 100644
--- a/src/core/lib/support/tmpfile.h
+++ b/src/core/lib/support/tmpfile.h
@@ -36,8 +36,6 @@
 
 #include <stdio.h>
 
-#include <grpc/slice.h>
-
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/src/core/lib/surface/README.md b/src/core/lib/surface/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..74cbd7113150738e06d2d81fb10836176e308ec7
--- /dev/null
+++ b/src/core/lib/surface/README.md
@@ -0,0 +1,4 @@
+# Surface
+
+Surface provides the bulk of the gRPC Core public API, and translates it into
+calls against core components.
diff --git a/src/core/lib/surface/alarm.c b/src/core/lib/surface/alarm.c
index aa9d60ee6a899442572ce9362d4023a8343018c2..e71c0ebfc57c557854098774251af31560c925c2 100644
--- a/src/core/lib/surface/alarm.c
+++ b/src/core/lib/surface/alarm.c
@@ -38,6 +38,7 @@
 
 struct grpc_alarm {
   grpc_timer alarm;
+  grpc_closure on_alarm;
   grpc_cq_completion completion;
   /** completion queue where events about this alarm will be posted */
   grpc_completion_queue *cq;
@@ -64,9 +65,11 @@ grpc_alarm *grpc_alarm_create(grpc_completion_queue *cq, gpr_timespec deadline,
   alarm->tag = tag;
 
   grpc_cq_begin_op(cq, tag);
+  grpc_closure_init(&alarm->on_alarm, alarm_cb, alarm,
+                    grpc_schedule_on_exec_ctx);
   grpc_timer_init(&exec_ctx, &alarm->alarm,
                   gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC),
-                  alarm_cb, alarm, gpr_now(GPR_CLOCK_MONOTONIC));
+                  &alarm->on_alarm, gpr_now(GPR_CLOCK_MONOTONIC));
   grpc_exec_ctx_finish(&exec_ctx);
   return alarm;
 }
diff --git a/src/core/lib/surface/byte_buffer.c b/src/core/lib/surface/byte_buffer.c
index d646591a65511a9db41f3836644da3ec21700fc0..c8e2fdfdad935e37d7e6da7e1ee7b04cda6ac143 100644
--- a/src/core/lib/surface/byte_buffer.c
+++ b/src/core/lib/surface/byte_buffer.c
@@ -35,6 +35,8 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
+#include "src/core/lib/slice/slice_internal.h"
+
 grpc_byte_buffer *grpc_raw_byte_buffer_create(grpc_slice *slices,
                                               size_t nslices) {
   return grpc_raw_compressed_byte_buffer_create(slices, nslices,
@@ -50,7 +52,7 @@ grpc_byte_buffer *grpc_raw_compressed_byte_buffer_create(
   bb->data.raw.compression = compression;
   grpc_slice_buffer_init(&bb->data.raw.slice_buffer);
   for (i = 0; i < nslices; i++) {
-    grpc_slice_ref(slices[i]);
+    grpc_slice_ref_internal(slices[i]);
     grpc_slice_buffer_add(&bb->data.raw.slice_buffer, slices[i]);
   }
   return bb;
@@ -82,12 +84,14 @@ grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb) {
 
 void grpc_byte_buffer_destroy(grpc_byte_buffer *bb) {
   if (!bb) return;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   switch (bb->type) {
     case GRPC_BB_RAW:
-      grpc_slice_buffer_destroy(&bb->data.raw.slice_buffer);
+      grpc_slice_buffer_destroy_internal(&exec_ctx, &bb->data.raw.slice_buffer);
       break;
   }
   gpr_free(bb);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 size_t grpc_byte_buffer_length(grpc_byte_buffer *bb) {
diff --git a/src/core/lib/surface/byte_buffer_reader.c b/src/core/lib/surface/byte_buffer_reader.c
index 0089959fbb10c2a1ca4057904b4ad06800827f29..1a6ccdaddb6144d31bfcb4d3be9381eae897da48 100644
--- a/src/core/lib/surface/byte_buffer_reader.c
+++ b/src/core/lib/surface/byte_buffer_reader.c
@@ -42,6 +42,7 @@
 #include <grpc/support/log.h>
 
 #include "src/core/lib/compression/message_compress.h"
+#include "src/core/lib/slice/slice_internal.h"
 
 static int is_compressed(grpc_byte_buffer *buffer) {
   switch (buffer->type) {
@@ -56,13 +57,15 @@ static int is_compressed(grpc_byte_buffer *buffer) {
 
 int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader,
                                  grpc_byte_buffer *buffer) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_slice_buffer decompressed_slices_buffer;
   reader->buffer_in = buffer;
   switch (reader->buffer_in->type) {
     case GRPC_BB_RAW:
       grpc_slice_buffer_init(&decompressed_slices_buffer);
       if (is_compressed(reader->buffer_in)) {
-        if (grpc_msg_decompress(reader->buffer_in->data.raw.compression,
+        if (grpc_msg_decompress(&exec_ctx,
+                                reader->buffer_in->data.raw.compression,
                                 &reader->buffer_in->data.raw.slice_buffer,
                                 &decompressed_slices_buffer) == 0) {
           gpr_log(GPR_ERROR,
@@ -76,13 +79,15 @@ int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader,
               grpc_raw_byte_buffer_create(decompressed_slices_buffer.slices,
                                           decompressed_slices_buffer.count);
         }
-        grpc_slice_buffer_destroy(&decompressed_slices_buffer);
+        grpc_slice_buffer_destroy_internal(&exec_ctx,
+                                           &decompressed_slices_buffer);
       } else { /* not compressed, use the input buffer as output */
         reader->buffer_out = reader->buffer_in;
       }
       reader->current.index = 0;
       break;
   }
+  grpc_exec_ctx_finish(&exec_ctx);
   return 1;
 }
 
@@ -104,7 +109,8 @@ int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader,
       grpc_slice_buffer *slice_buffer;
       slice_buffer = &reader->buffer_out->data.raw.slice_buffer;
       if (reader->current.index < slice_buffer->count) {
-        *slice = grpc_slice_ref(slice_buffer->slices[reader->current.index]);
+        *slice = grpc_slice_ref_internal(
+            slice_buffer->slices[reader->current.index]);
         reader->current.index += 1;
         return 1;
       }
@@ -121,12 +127,14 @@ grpc_slice grpc_byte_buffer_reader_readall(grpc_byte_buffer_reader *reader) {
   grpc_slice out_slice = grpc_slice_malloc(input_size);
   uint8_t *const outbuf = GRPC_SLICE_START_PTR(out_slice); /* just an alias */
 
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   while (grpc_byte_buffer_reader_next(reader, &in_slice) != 0) {
     const size_t slice_length = GRPC_SLICE_LENGTH(in_slice);
     memcpy(&(outbuf[bytes_read]), GRPC_SLICE_START_PTR(in_slice), slice_length);
     bytes_read += slice_length;
-    grpc_slice_unref(in_slice);
+    grpc_slice_unref_internal(&exec_ctx, in_slice);
     GPR_ASSERT(bytes_read <= input_size);
   }
+  grpc_exec_ctx_finish(&exec_ctx);
   return out_slice;
 }
diff --git a/src/core/lib/surface/call.c b/src/core/lib/surface/call.c
index 1e0f3eeca52ff18cb4f76e494c5203ba693aad80..899e8fab3f60f35614abfeab0c86116939f9379a 100644
--- a/src/core/lib/surface/call.c
+++ b/src/core/lib/surface/call.c
@@ -49,6 +49,7 @@
 #include "src/core/lib/compression/algorithm_metadata.h"
 #include "src/core/lib/iomgr/timer.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/string.h"
 #include "src/core/lib/surface/api_trace.h"
@@ -226,12 +227,12 @@ static void destroy_call(grpc_exec_ctx *exec_ctx, void *call_stack,
 static void receiving_slice_ready(grpc_exec_ctx *exec_ctx, void *bctlp,
                                   grpc_error *error);
 
-grpc_error *grpc_call_create(const grpc_call_create_args *args,
+grpc_error *grpc_call_create(grpc_exec_ctx *exec_ctx,
+                             const grpc_call_create_args *args,
                              grpc_call **out_call) {
   size_t i, j;
   grpc_channel_stack *channel_stack =
       grpc_channel_get_channel_stack(args->channel);
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_call *call;
   GPR_TIMER_BEGIN("grpc_call_create", 0);
   call = gpr_malloc(sizeof(grpc_call) + channel_stack->call_stack_size);
@@ -315,14 +316,14 @@ grpc_error *grpc_call_create(const grpc_call_create_args *args,
   GRPC_CHANNEL_INTERNAL_REF(args->channel, "call");
   /* initial refcount dropped by grpc_call_destroy */
   grpc_error *error = grpc_call_stack_init(
-      &exec_ctx, channel_stack, 1, destroy_call, call, call->context,
+      exec_ctx, channel_stack, 1, destroy_call, call, call->context,
       args->server_transport_data, path, call->start_time, send_deadline,
       CALL_STACK_FROM_CALL(call));
   if (error != GRPC_ERROR_NONE) {
     grpc_status_code status;
     const char *error_str;
     grpc_error_get_status(error, &status, &error_str);
-    close_with_status(&exec_ctx, call, status, error_str);
+    close_with_status(exec_ctx, call, status, error_str);
   }
   if (args->cq != NULL) {
     GPR_ASSERT(
@@ -338,12 +339,11 @@ grpc_error *grpc_call_create(const grpc_call_create_args *args,
   }
   if (!grpc_polling_entity_is_empty(&call->pollent)) {
     grpc_call_stack_set_pollset_or_pollset_set(
-        &exec_ctx, CALL_STACK_FROM_CALL(call), &call->pollent);
+        exec_ctx, CALL_STACK_FROM_CALL(call), &call->pollent);
   }
 
-  if (path != NULL) GRPC_MDSTR_UNREF(path);
+  if (path != NULL) GRPC_MDSTR_UNREF(exec_ctx, path);
 
-  grpc_exec_ctx_finish(&exec_ctx);
   GPR_TIMER_END("grpc_call_create", 0);
   return error;
 }
@@ -404,7 +404,7 @@ static void destroy_call(grpc_exec_ctx *exec_ctx, void *call,
   GPR_TIMER_BEGIN("destroy_call", 0);
   for (i = 0; i < 2; i++) {
     grpc_metadata_batch_destroy(
-        &c->metadata_batch[1 /* is_receiving */][i /* is_initial */]);
+        exec_ctx, &c->metadata_batch[1 /* is_receiving */][i /* is_initial */]);
   }
   if (c->receiving_stream != NULL) {
     grpc_byte_stream_destroy(exec_ctx, c->receiving_stream);
@@ -412,11 +412,11 @@ static void destroy_call(grpc_exec_ctx *exec_ctx, void *call,
   gpr_mu_destroy(&c->mu);
   for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
     if (c->status[i].details) {
-      GRPC_MDSTR_UNREF(c->status[i].details);
+      GRPC_MDSTR_UNREF(exec_ctx, c->status[i].details);
     }
   }
   for (ii = 0; ii < c->send_extra_metadata_count; ii++) {
-    GRPC_MDELEM_UNREF(c->send_extra_metadata[ii].md);
+    GRPC_MDELEM_UNREF(exec_ctx, c->send_extra_metadata[ii].md);
   }
   for (i = 0; i < GRPC_CONTEXT_COUNT; i++) {
     if (c->context[i].destroy) {
@@ -446,22 +446,22 @@ static void set_status_code(grpc_call *call, status_source source,
   call->status[source].code = (grpc_status_code)status;
 }
 
-static void set_status_details(grpc_call *call, status_source source,
-                               grpc_mdstr *status) {
+static void set_status_details(grpc_exec_ctx *exec_ctx, grpc_call *call,
+                               status_source source, grpc_mdstr *status) {
   if (call->status[source].details != NULL) {
-    GRPC_MDSTR_UNREF(status);
+    GRPC_MDSTR_UNREF(exec_ctx, status);
   } else {
     call->status[source].details = status;
   }
 }
 
-static void set_status_from_error(grpc_call *call, status_source source,
-                                  grpc_error *error) {
+static void set_status_from_error(grpc_exec_ctx *exec_ctx, grpc_call *call,
+                                  status_source source, grpc_error *error) {
   grpc_status_code status;
   const char *msg;
   grpc_error_get_status(error, &status, &msg);
   set_status_code(call, source, (uint32_t)status);
-  set_status_details(call, source, grpc_mdstr_from_string(msg));
+  set_status_details(exec_ctx, call, source, grpc_mdstr_from_string(msg));
 }
 
 static void set_incoming_compression_algorithm(
@@ -495,7 +495,8 @@ uint32_t grpc_call_test_only_get_message_flags(grpc_call *call) {
 
 static void destroy_encodings_accepted_by_peer(void *p) { return; }
 
-static void set_encodings_accepted_by_peer(grpc_call *call, grpc_mdelem *mdel) {
+static void set_encodings_accepted_by_peer(grpc_exec_ctx *exec_ctx,
+                                           grpc_call *call, grpc_mdelem *mdel) {
   size_t i;
   grpc_compression_algorithm algorithm;
   grpc_slice_buffer accept_encoding_parts;
@@ -535,7 +536,7 @@ static void set_encodings_accepted_by_peer(grpc_call *call, grpc_mdelem *mdel) {
     }
   }
 
-  grpc_slice_buffer_destroy(&accept_encoding_parts);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &accept_encoding_parts);
 
   grpc_mdelem_set_user_data(
       mdel, destroy_encodings_accepted_by_peer,
@@ -593,12 +594,10 @@ static grpc_metadata *get_md_elem(grpc_metadata *metadata,
   return res;
 }
 
-static int prepare_application_metadata(grpc_call *call, int count,
-                                        grpc_metadata *metadata,
-                                        int is_trailing,
-                                        int prepend_extra_metadata,
-                                        grpc_metadata *additional_metadata,
-                                        int additional_metadata_count) {
+static int prepare_application_metadata(
+    grpc_exec_ctx *exec_ctx, grpc_call *call, int count,
+    grpc_metadata *metadata, int is_trailing, int prepend_extra_metadata,
+    grpc_metadata *additional_metadata, int additional_metadata_count) {
   int total_count = count + additional_metadata_count;
   int i;
   grpc_metadata_batch *batch =
@@ -609,7 +608,7 @@ static int prepare_application_metadata(grpc_call *call, int count,
     grpc_linked_mdelem *l = (grpc_linked_mdelem *)&md->internal_data;
     GPR_ASSERT(sizeof(grpc_linked_mdelem) == sizeof(md->internal_data));
     l->md = grpc_mdelem_from_string_and_buffer(
-        md->key, (const uint8_t *)md->value, md->value_length);
+        exec_ctx, md->key, (const uint8_t *)md->value, md->value_length);
     if (!grpc_header_key_is_legal(grpc_mdstr_as_c_string(l->md->key),
                                   GRPC_MDSTR_LENGTH(l->md->key))) {
       gpr_log(GPR_ERROR, "attempt to send invalid metadata key: %s",
@@ -629,7 +628,7 @@ static int prepare_application_metadata(grpc_call *call, int count,
       const grpc_metadata *md =
           get_md_elem(metadata, additional_metadata, j, count);
       grpc_linked_mdelem *l = (grpc_linked_mdelem *)&md->internal_data;
-      GRPC_MDELEM_UNREF(l->md);
+      GRPC_MDELEM_UNREF(exec_ctx, l->md);
     }
     return 0;
   }
@@ -794,7 +793,8 @@ static void send_cancel(grpc_exec_ctx *exec_ctx, void *tcp, grpc_error *error) {
   memset(&tc->op, 0, sizeof(tc->op));
   tc->op.cancel_error = tc->error;
   /* reuse closure to catch completion */
-  grpc_closure_init(&tc->closure, done_termination, tc);
+  grpc_closure_init(&tc->closure, done_termination, tc,
+                    grpc_schedule_on_exec_ctx);
   tc->op.on_complete = &tc->closure;
   execute_op(exec_ctx, tc->call, &tc->op);
 }
@@ -804,23 +804,25 @@ static void send_close(grpc_exec_ctx *exec_ctx, void *tcp, grpc_error *error) {
   memset(&tc->op, 0, sizeof(tc->op));
   tc->op.close_error = tc->error;
   /* reuse closure to catch completion */
-  grpc_closure_init(&tc->closure, done_termination, tc);
+  grpc_closure_init(&tc->closure, done_termination, tc,
+                    grpc_schedule_on_exec_ctx);
   tc->op.on_complete = &tc->closure;
   execute_op(exec_ctx, tc->call, &tc->op);
 }
 
 static grpc_call_error terminate_with_status(grpc_exec_ctx *exec_ctx,
                                              termination_closure *tc) {
-  set_status_from_error(tc->call, STATUS_FROM_API_OVERRIDE, tc->error);
+  set_status_from_error(exec_ctx, tc->call, STATUS_FROM_API_OVERRIDE,
+                        tc->error);
 
   if (tc->type == TC_CANCEL) {
-    grpc_closure_init(&tc->closure, send_cancel, tc);
+    grpc_closure_init(&tc->closure, send_cancel, tc, grpc_schedule_on_exec_ctx);
     GRPC_CALL_INTERNAL_REF(tc->call, "cancel");
   } else if (tc->type == TC_CLOSE) {
-    grpc_closure_init(&tc->closure, send_close, tc);
+    grpc_closure_init(&tc->closure, send_close, tc, grpc_schedule_on_exec_ctx);
     GRPC_CALL_INTERNAL_REF(tc->call, "close");
   }
-  grpc_exec_ctx_sched(exec_ctx, &tc->closure, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, &tc->closure, GRPC_ERROR_NONE);
   return GRPC_CALL_OK;
 }
 
@@ -928,7 +930,8 @@ static grpc_compression_algorithm decode_compression(grpc_mdelem *md) {
   return algorithm;
 }
 
-static grpc_mdelem *recv_common_filter(grpc_call *call, grpc_mdelem *elem) {
+static grpc_mdelem *recv_common_filter(grpc_exec_ctx *exec_ctx, grpc_call *call,
+                                       grpc_mdelem *elem) {
   if (elem->key == GRPC_MDSTR_GRPC_STATUS) {
     GPR_TIMER_BEGIN("status", 0);
     set_status_code(call, STATUS_FROM_WIRE, decode_status(elem));
@@ -936,7 +939,8 @@ static grpc_mdelem *recv_common_filter(grpc_call *call, grpc_mdelem *elem) {
     return NULL;
   } else if (elem->key == GRPC_MDSTR_GRPC_MESSAGE) {
     GPR_TIMER_BEGIN("status-details", 0);
-    set_status_details(call, STATUS_FROM_WIRE, GRPC_MDSTR_REF(elem->value));
+    set_status_details(exec_ctx, call, STATUS_FROM_WIRE,
+                       GRPC_MDSTR_REF(elem->value));
     GPR_TIMER_END("status-details", 0);
     return NULL;
   }
@@ -962,9 +966,10 @@ static grpc_mdelem *publish_app_metadata(grpc_call *call, grpc_mdelem *elem,
   return elem;
 }
 
-static grpc_mdelem *recv_initial_filter(void *callp, grpc_mdelem *elem) {
-  grpc_call *call = callp;
-  elem = recv_common_filter(call, elem);
+static grpc_mdelem *recv_initial_filter(grpc_exec_ctx *exec_ctx, void *args,
+                                        grpc_mdelem *elem) {
+  grpc_call *call = args;
+  elem = recv_common_filter(exec_ctx, call, elem);
   if (elem == NULL) {
     return NULL;
   } else if (elem->key == GRPC_MDSTR_GRPC_ENCODING) {
@@ -974,7 +979,7 @@ static grpc_mdelem *recv_initial_filter(void *callp, grpc_mdelem *elem) {
     return NULL;
   } else if (elem->key == GRPC_MDSTR_GRPC_ACCEPT_ENCODING) {
     GPR_TIMER_BEGIN("encodings_accepted_by_peer", 0);
-    set_encodings_accepted_by_peer(call, elem);
+    set_encodings_accepted_by_peer(exec_ctx, call, elem);
     GPR_TIMER_END("encodings_accepted_by_peer", 0);
     return NULL;
   } else {
@@ -982,9 +987,10 @@ static grpc_mdelem *recv_initial_filter(void *callp, grpc_mdelem *elem) {
   }
 }
 
-static grpc_mdelem *recv_trailing_filter(void *callp, grpc_mdelem *elem) {
-  grpc_call *call = callp;
-  elem = recv_common_filter(call, elem);
+static grpc_mdelem *recv_trailing_filter(grpc_exec_ctx *exec_ctx, void *args,
+                                         grpc_mdelem *elem) {
+  grpc_call *call = args;
+  elem = recv_common_filter(exec_ctx, call, elem);
   if (elem == NULL) {
     return NULL;
   } else {
@@ -1138,8 +1144,8 @@ static void process_data_after_md(grpc_exec_ctx *exec_ctx,
     } else {
       *call->receiving_buffer = grpc_raw_byte_buffer_create(NULL, 0);
     }
-    grpc_closure_init(&call->receiving_slice_ready, receiving_slice_ready,
-                      bctl);
+    grpc_closure_init(&call->receiving_slice_ready, receiving_slice_ready, bctl,
+                      grpc_schedule_on_exec_ctx);
     continue_receiving_slices(exec_ctx, bctl);
   }
 }
@@ -1234,7 +1240,7 @@ static void receiving_initial_metadata_ready(grpc_exec_ctx *exec_ctx,
   if (error == GRPC_ERROR_NONE) {
     grpc_metadata_batch *md =
         &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */];
-    grpc_metadata_batch_filter(md, recv_initial_filter, call);
+    grpc_metadata_batch_filter(exec_ctx, md, recv_initial_filter, call);
 
     GPR_TIMER_BEGIN("validate_filtered_metadata", 0);
     validate_filtered_metadata(exec_ctx, bctl);
@@ -1251,9 +1257,10 @@ static void receiving_initial_metadata_ready(grpc_exec_ctx *exec_ctx,
   call->has_initial_md_been_received = true;
   if (call->saved_receiving_stream_ready_bctlp != NULL) {
     grpc_closure *saved_rsr_closure = grpc_closure_create(
-        receiving_stream_ready, call->saved_receiving_stream_ready_bctlp);
+        receiving_stream_ready, call->saved_receiving_stream_ready_bctlp,
+        grpc_schedule_on_exec_ctx);
     call->saved_receiving_stream_ready_bctlp = NULL;
-    grpc_exec_ctx_sched(exec_ctx, saved_rsr_closure, error, NULL);
+    grpc_closure_sched(exec_ctx, saved_rsr_closure, error);
   }
 
   gpr_mu_unlock(&call->mu);
@@ -1278,14 +1285,15 @@ static void finish_batch(grpc_exec_ctx *exec_ctx, void *bctlp,
   intptr_t status;
   if (error != GRPC_ERROR_NONE &&
       grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, &status)) {
-    set_status_from_error(call, STATUS_FROM_CORE, error);
+    set_status_from_error(exec_ctx, call, STATUS_FROM_CORE, error);
   }
 
   if (bctl->send_initial_metadata) {
     if (error != GRPC_ERROR_NONE) {
-      set_status_from_error(call, STATUS_FROM_CORE, error);
+      set_status_from_error(exec_ctx, call, STATUS_FROM_CORE, error);
     }
     grpc_metadata_batch_destroy(
+        exec_ctx,
         &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]);
   }
   if (bctl->send_message) {
@@ -1293,12 +1301,13 @@ static void finish_batch(grpc_exec_ctx *exec_ctx, void *bctlp,
   }
   if (bctl->send_final_op) {
     grpc_metadata_batch_destroy(
+        exec_ctx,
         &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]);
   }
   if (bctl->recv_final_op) {
     grpc_metadata_batch *md =
         &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
-    grpc_metadata_batch_filter(md, recv_trailing_filter, call);
+    grpc_metadata_batch_filter(exec_ctx, md, recv_trailing_filter, call);
 
     call->received_final_op = true;
     /* propagate cancellation to any interested children */
@@ -1435,7 +1444,7 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
         bctl->send_initial_metadata = 1;
         call->sent_initial_metadata = 1;
         if (!prepare_application_metadata(
-                call, (int)op->data.send_initial_metadata.count,
+                exec_ctx, call, (int)op->data.send_initial_metadata.count,
                 op->data.send_initial_metadata.metadata, 0, call->is_client,
                 &compression_md, (int)additional_metadata_count)) {
           error = GRPC_CALL_ERROR_INVALID_METADATA;
@@ -1515,15 +1524,15 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
         call->sent_final_op = 1;
         call->send_extra_metadata_count = 1;
         call->send_extra_metadata[0].md = grpc_channel_get_reffed_status_elem(
-            call->channel, op->data.send_status_from_server.status);
+            exec_ctx, call->channel, op->data.send_status_from_server.status);
         if (op->data.send_status_from_server.status_details != NULL) {
           call->send_extra_metadata[1].md = grpc_mdelem_from_metadata_strings(
-              GRPC_MDSTR_GRPC_MESSAGE,
+              exec_ctx, GRPC_MDSTR_GRPC_MESSAGE,
               grpc_mdstr_from_string(
                   op->data.send_status_from_server.status_details));
           call->send_extra_metadata_count++;
           set_status_details(
-              call, STATUS_FROM_API_OVERRIDE,
+              exec_ctx, call, STATUS_FROM_API_OVERRIDE,
               GRPC_MDSTR_REF(call->send_extra_metadata[1].md->value));
         }
         if (op->data.send_status_from_server.status != GRPC_STATUS_OK) {
@@ -1531,7 +1540,7 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
                           (uint32_t)op->data.send_status_from_server.status);
         }
         if (!prepare_application_metadata(
-                call,
+                exec_ctx, call,
                 (int)op->data.send_status_from_server.trailing_metadata_count,
                 op->data.send_status_from_server.trailing_metadata, 1, 1, NULL,
                 0)) {
@@ -1551,10 +1560,15 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
           error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
           goto done_with_error;
         }
+        /* IF this is a server, then GRPC_OP_RECV_INITIAL_METADATA *must* come
+           from server.c. In that case, it's coming from accept_stream, and in
+           that case we're not necessarily covered by a poller. */
+        stream_op->covered_by_poller = call->is_client;
         call->received_initial_metadata = 1;
         call->buffered_metadata[0] = op->data.recv_initial_metadata;
         grpc_closure_init(&call->receiving_initial_metadata_ready,
-                          receiving_initial_metadata_ready, bctl);
+                          receiving_initial_metadata_ready, bctl,
+                          grpc_schedule_on_exec_ctx);
         bctl->recv_initial_metadata = 1;
         stream_op->recv_initial_metadata =
             &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */];
@@ -1577,7 +1591,7 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
         call->receiving_buffer = op->data.recv_message;
         stream_op->recv_message = &call->receiving_stream;
         grpc_closure_init(&call->receiving_stream_ready, receiving_stream_ready,
-                          bctl);
+                          bctl, grpc_schedule_on_exec_ctx);
         stream_op->recv_message_ready = &call->receiving_stream_ready;
         num_completion_callbacks_needed++;
         break;
@@ -1642,7 +1656,8 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
   gpr_ref_init(&bctl->steps_to_complete, num_completion_callbacks_needed);
 
   stream_op->context = call->context;
-  grpc_closure_init(&bctl->finish_batch, finish_batch, bctl);
+  grpc_closure_init(&bctl->finish_batch, finish_batch, bctl,
+                    grpc_schedule_on_exec_ctx);
   stream_op->on_complete = &bctl->finish_batch;
   gpr_mu_unlock(&call->mu);
 
@@ -1656,7 +1671,7 @@ done_with_error:
   /* reverse any mutations that occured */
   if (bctl->send_initial_metadata) {
     call->sent_initial_metadata = 0;
-    grpc_metadata_batch_clear(&call->metadata_batch[0][0]);
+    grpc_metadata_batch_clear(exec_ctx, &call->metadata_batch[0][0]);
   }
   if (bctl->send_message) {
     call->sending_message = 0;
@@ -1664,7 +1679,7 @@ done_with_error:
   }
   if (bctl->send_final_op) {
     call->sent_final_op = 0;
-    grpc_metadata_batch_clear(&call->metadata_batch[0][1]);
+    grpc_metadata_batch_clear(exec_ctx, &call->metadata_batch[0][1]);
   }
   if (bctl->recv_initial_metadata) {
     call->received_initial_metadata = 0;
diff --git a/src/core/lib/surface/call.h b/src/core/lib/surface/call.h
index 18af41b7fbb40dc83b53fa257f54fabe592117ba..233340c3298343982aea81d60e14530e0de1b8b1 100644
--- a/src/core/lib/surface/call.h
+++ b/src/core/lib/surface/call.h
@@ -70,7 +70,8 @@ typedef struct grpc_call_create_args {
 /* Create a new call based on \a args.
    Regardless of success or failure, always returns a valid new call into *call
    */
-grpc_error *grpc_call_create(const grpc_call_create_args *args,
+grpc_error *grpc_call_create(grpc_exec_ctx *exec_ctx,
+                             const grpc_call_create_args *args,
                              grpc_call **call);
 
 void grpc_call_set_completion_queue(grpc_exec_ctx *exec_ctx, grpc_call *call,
diff --git a/src/core/lib/surface/channel.c b/src/core/lib/surface/channel.c
index 1389df688625fdc49160730e9e5b6b5314de4664..b87295786e5dcdc18457b30706aa0e50c63028c6 100644
--- a/src/core/lib/surface/channel.c
+++ b/src/core/lib/surface/channel.c
@@ -86,87 +86,91 @@ grpc_channel *grpc_channel_create(grpc_exec_ctx *exec_ctx, const char *target,
                                   const grpc_channel_args *input_args,
                                   grpc_channel_stack_type channel_stack_type,
                                   grpc_transport *optional_transport) {
-  bool is_client = grpc_channel_stack_type_is_client(channel_stack_type);
-
   grpc_channel_stack_builder *builder = grpc_channel_stack_builder_create();
-  grpc_channel_stack_builder_set_channel_arguments(builder, input_args);
+  grpc_channel_stack_builder_set_channel_arguments(exec_ctx, builder,
+                                                   input_args);
   grpc_channel_stack_builder_set_target(builder, target);
   grpc_channel_stack_builder_set_transport(builder, optional_transport);
-  grpc_channel *channel;
-  grpc_channel_args *args;
   if (!grpc_channel_init_create_stack(exec_ctx, builder, channel_stack_type)) {
-    grpc_channel_stack_builder_destroy(builder);
+    grpc_channel_stack_builder_destroy(exec_ctx, builder);
     return NULL;
-  } else {
-    args = grpc_channel_args_copy(
-        grpc_channel_stack_builder_get_channel_arguments(builder));
-    channel = grpc_channel_stack_builder_finish(
-        exec_ctx, builder, sizeof(grpc_channel), 1, destroy_channel, NULL);
+  }
+  grpc_channel_args *args = grpc_channel_args_copy(
+      grpc_channel_stack_builder_get_channel_arguments(builder));
+  grpc_channel *channel;
+  grpc_error *error = grpc_channel_stack_builder_finish(
+      exec_ctx, builder, sizeof(grpc_channel), 1, destroy_channel, NULL,
+      (void **)&channel);
+  if (error != GRPC_ERROR_NONE) {
+    const char *msg = grpc_error_string(error);
+    gpr_log(GPR_ERROR, "channel stack builder failed: %s", msg);
+    grpc_error_free_string(msg);
+    GRPC_ERROR_UNREF(error);
+    goto done;
   }
 
   memset(channel, 0, sizeof(*channel));
   channel->target = gpr_strdup(target);
-  channel->is_client = is_client;
+  channel->is_client = grpc_channel_stack_type_is_client(channel_stack_type);
   gpr_mu_init(&channel->registered_call_mu);
   channel->registered_calls = NULL;
 
   grpc_compression_options_init(&channel->compression_options);
-  if (args) {
-    for (size_t i = 0; i < args->num_args; i++) {
-      if (0 == strcmp(args->args[i].key, GRPC_ARG_DEFAULT_AUTHORITY)) {
-        if (args->args[i].type != GRPC_ARG_STRING) {
-          gpr_log(GPR_ERROR, "%s ignored: it must be a string",
-                  GRPC_ARG_DEFAULT_AUTHORITY);
-        } else {
-          if (channel->default_authority) {
-            /* setting this takes precedence over anything else */
-            GRPC_MDELEM_UNREF(channel->default_authority);
-          }
-          channel->default_authority = grpc_mdelem_from_strings(
-              ":authority", args->args[i].value.string);
+
+  for (size_t i = 0; i < args->num_args; i++) {
+    if (0 == strcmp(args->args[i].key, GRPC_ARG_DEFAULT_AUTHORITY)) {
+      if (args->args[i].type != GRPC_ARG_STRING) {
+        gpr_log(GPR_ERROR, "%s ignored: it must be a string",
+                GRPC_ARG_DEFAULT_AUTHORITY);
+      } else {
+        if (channel->default_authority) {
+          /* setting this takes precedence over anything else */
+          GRPC_MDELEM_UNREF(exec_ctx, channel->default_authority);
         }
-      } else if (0 ==
-                 strcmp(args->args[i].key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG)) {
-        if (args->args[i].type != GRPC_ARG_STRING) {
-          gpr_log(GPR_ERROR, "%s ignored: it must be a string",
+        channel->default_authority = grpc_mdelem_from_strings(
+            exec_ctx, ":authority", args->args[i].value.string);
+      }
+    } else if (0 ==
+               strcmp(args->args[i].key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG)) {
+      if (args->args[i].type != GRPC_ARG_STRING) {
+        gpr_log(GPR_ERROR, "%s ignored: it must be a string",
+                GRPC_SSL_TARGET_NAME_OVERRIDE_ARG);
+      } else {
+        if (channel->default_authority) {
+          /* other ways of setting this (notably ssl) take precedence */
+          gpr_log(GPR_ERROR,
+                  "%s ignored: default host already set some other way",
                   GRPC_SSL_TARGET_NAME_OVERRIDE_ARG);
         } else {
-          if (channel->default_authority) {
-            /* other ways of setting this (notably ssl) take precedence */
-            gpr_log(GPR_ERROR,
-                    "%s ignored: default host already set some other way",
-                    GRPC_SSL_TARGET_NAME_OVERRIDE_ARG);
-          } else {
-            channel->default_authority = grpc_mdelem_from_strings(
-                ":authority", args->args[i].value.string);
-          }
+          channel->default_authority = grpc_mdelem_from_strings(
+              exec_ctx, ":authority", args->args[i].value.string);
         }
-      } else if (0 == strcmp(args->args[i].key,
-                             GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL)) {
-        channel->compression_options.default_level.is_set = true;
-        GPR_ASSERT(args->args[i].value.integer >= 0 &&
-                   args->args[i].value.integer < GRPC_COMPRESS_LEVEL_COUNT);
-        channel->compression_options.default_level.level =
-            (grpc_compression_level)args->args[i].value.integer;
-      } else if (0 == strcmp(args->args[i].key,
-                             GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM)) {
-        channel->compression_options.default_algorithm.is_set = true;
-        GPR_ASSERT(args->args[i].value.integer >= 0 &&
-                   args->args[i].value.integer <
-                       GRPC_COMPRESS_ALGORITHMS_COUNT);
-        channel->compression_options.default_algorithm.algorithm =
-            (grpc_compression_algorithm)args->args[i].value.integer;
-      } else if (0 ==
-                 strcmp(args->args[i].key,
-                        GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET)) {
-        channel->compression_options.enabled_algorithms_bitset =
-            (uint32_t)args->args[i].value.integer |
-            0x1; /* always support no compression */
       }
+    } else if (0 == strcmp(args->args[i].key,
+                           GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL)) {
+      channel->compression_options.default_level.is_set = true;
+      GPR_ASSERT(args->args[i].value.integer >= 0 &&
+                 args->args[i].value.integer < GRPC_COMPRESS_LEVEL_COUNT);
+      channel->compression_options.default_level.level =
+          (grpc_compression_level)args->args[i].value.integer;
+    } else if (0 == strcmp(args->args[i].key,
+                           GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM)) {
+      channel->compression_options.default_algorithm.is_set = true;
+      GPR_ASSERT(args->args[i].value.integer >= 0 &&
+                 args->args[i].value.integer < GRPC_COMPRESS_ALGORITHMS_COUNT);
+      channel->compression_options.default_algorithm.algorithm =
+          (grpc_compression_algorithm)args->args[i].value.integer;
+    } else if (0 ==
+               strcmp(args->args[i].key,
+                      GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET)) {
+      channel->compression_options.enabled_algorithms_bitset =
+          (uint32_t)args->args[i].value.integer |
+          0x1; /* always support no compression */
     }
-    grpc_channel_args_destroy(args);
   }
 
+done:
+  grpc_channel_args_destroy(exec_ctx, args);
   return channel;
 }
 
@@ -185,10 +189,10 @@ void grpc_channel_get_info(grpc_channel *channel,
 }
 
 static grpc_call *grpc_channel_create_call_internal(
-    grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask,
-    grpc_completion_queue *cq, grpc_pollset_set *pollset_set_alternative,
-    grpc_mdelem *path_mdelem, grpc_mdelem *authority_mdelem,
-    gpr_timespec deadline) {
+    grpc_exec_ctx *exec_ctx, grpc_channel *channel, grpc_call *parent_call,
+    uint32_t propagation_mask, grpc_completion_queue *cq,
+    grpc_pollset_set *pollset_set_alternative, grpc_mdelem *path_mdelem,
+    grpc_mdelem *authority_mdelem, gpr_timespec deadline) {
   grpc_mdelem *send_metadata[2];
   size_t num_metadata = 0;
 
@@ -215,7 +219,7 @@ static grpc_call *grpc_channel_create_call_internal(
   args.send_deadline = deadline;
 
   grpc_call *call;
-  GRPC_LOG_IF_ERROR("call_create", grpc_call_create(&args, &call));
+  GRPC_LOG_IF_ERROR("call_create", grpc_call_create(exec_ctx, &args, &call));
   return call;
 }
 
@@ -236,26 +240,30 @@ grpc_call *grpc_channel_create_call(grpc_channel *channel,
       (channel, parent_call, (unsigned)propagation_mask, cq, method, host,
        deadline.tv_sec, deadline.tv_nsec, (int)deadline.clock_type, reserved));
   GPR_ASSERT(!reserved);
-  return grpc_channel_create_call_internal(
-      channel, parent_call, propagation_mask, cq, NULL,
-      grpc_mdelem_from_metadata_strings(GRPC_MDSTR_PATH,
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_call *call = grpc_channel_create_call_internal(
+      &exec_ctx, channel, parent_call, propagation_mask, cq, NULL,
+      grpc_mdelem_from_metadata_strings(&exec_ctx, GRPC_MDSTR_PATH,
                                         grpc_mdstr_from_string(method)),
-      host ? grpc_mdelem_from_metadata_strings(GRPC_MDSTR_AUTHORITY,
+      host ? grpc_mdelem_from_metadata_strings(&exec_ctx, GRPC_MDSTR_AUTHORITY,
                                                grpc_mdstr_from_string(host))
            : NULL,
       deadline);
+  grpc_exec_ctx_finish(&exec_ctx);
+  return call;
 }
 
 grpc_call *grpc_channel_create_pollset_set_call(
-    grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask,
-    grpc_pollset_set *pollset_set, const char *method, const char *host,
-    gpr_timespec deadline, void *reserved) {
+    grpc_exec_ctx *exec_ctx, grpc_channel *channel, grpc_call *parent_call,
+    uint32_t propagation_mask, grpc_pollset_set *pollset_set,
+    const char *method, const char *host, gpr_timespec deadline,
+    void *reserved) {
   GPR_ASSERT(!reserved);
   return grpc_channel_create_call_internal(
-      channel, parent_call, propagation_mask, NULL, pollset_set,
-      grpc_mdelem_from_metadata_strings(GRPC_MDSTR_PATH,
+      exec_ctx, channel, parent_call, propagation_mask, NULL, pollset_set,
+      grpc_mdelem_from_metadata_strings(exec_ctx, GRPC_MDSTR_PATH,
                                         grpc_mdstr_from_string(method)),
-      host ? grpc_mdelem_from_metadata_strings(GRPC_MDSTR_AUTHORITY,
+      host ? grpc_mdelem_from_metadata_strings(exec_ctx, GRPC_MDSTR_AUTHORITY,
                                                grpc_mdstr_from_string(host))
            : NULL,
       deadline);
@@ -268,15 +276,18 @@ void *grpc_channel_register_call(grpc_channel *channel, const char *method,
       "grpc_channel_register_call(channel=%p, method=%s, host=%s, reserved=%p)",
       4, (channel, method, host, reserved));
   GPR_ASSERT(!reserved);
-  rc->path = grpc_mdelem_from_metadata_strings(GRPC_MDSTR_PATH,
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  rc->path = grpc_mdelem_from_metadata_strings(&exec_ctx, GRPC_MDSTR_PATH,
                                                grpc_mdstr_from_string(method));
-  rc->authority = host ? grpc_mdelem_from_metadata_strings(
-                             GRPC_MDSTR_AUTHORITY, grpc_mdstr_from_string(host))
-                       : NULL;
+  rc->authority =
+      host ? grpc_mdelem_from_metadata_strings(&exec_ctx, GRPC_MDSTR_AUTHORITY,
+                                               grpc_mdstr_from_string(host))
+           : NULL;
   gpr_mu_lock(&channel->registered_call_mu);
   rc->next = channel->registered_calls;
   channel->registered_calls = rc;
   gpr_mu_unlock(&channel->registered_call_mu);
+  grpc_exec_ctx_finish(&exec_ctx);
   return rc;
 }
 
@@ -296,10 +307,13 @@ grpc_call *grpc_channel_create_registered_call(
           registered_call_handle, deadline.tv_sec, deadline.tv_nsec,
           (int)deadline.clock_type, reserved));
   GPR_ASSERT(!reserved);
-  return grpc_channel_create_call_internal(
-      channel, parent_call, propagation_mask, completion_queue, NULL,
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_call *call = grpc_channel_create_call_internal(
+      &exec_ctx, channel, parent_call, propagation_mask, completion_queue, NULL,
       GRPC_MDELEM_REF(rc->path),
       rc->authority ? GRPC_MDELEM_REF(rc->authority) : NULL, deadline);
+  grpc_exec_ctx_finish(&exec_ctx);
+  return call;
 }
 
 #ifdef GRPC_STREAM_REFCOUNT_DEBUG
@@ -325,14 +339,14 @@ static void destroy_channel(grpc_exec_ctx *exec_ctx, void *arg,
   while (channel->registered_calls) {
     registered_call *rc = channel->registered_calls;
     channel->registered_calls = rc->next;
-    GRPC_MDELEM_UNREF(rc->path);
+    GRPC_MDELEM_UNREF(exec_ctx, rc->path);
     if (rc->authority) {
-      GRPC_MDELEM_UNREF(rc->authority);
+      GRPC_MDELEM_UNREF(exec_ctx, rc->authority);
     }
     gpr_free(rc);
   }
   if (channel->default_authority != NULL) {
-    GRPC_MDELEM_UNREF(channel->default_authority);
+    GRPC_MDELEM_UNREF(exec_ctx, channel->default_authority);
   }
   gpr_mu_destroy(&channel->registered_call_mu);
   gpr_free(channel->target);
@@ -362,7 +376,8 @@ grpc_compression_options grpc_channel_compression_options(
   return channel->compression_options;
 }
 
-grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel, int i) {
+grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_exec_ctx *exec_ctx,
+                                                 grpc_channel *channel, int i) {
   char tmp[GPR_LTOA_MIN_BUFSIZE];
   switch (i) {
     case 0:
@@ -373,6 +388,6 @@ grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel, int i) {
       return GRPC_MDELEM_GRPC_STATUS_2;
   }
   gpr_ltoa(i, tmp);
-  return grpc_mdelem_from_metadata_strings(GRPC_MDSTR_GRPC_STATUS,
+  return grpc_mdelem_from_metadata_strings(exec_ctx, GRPC_MDSTR_GRPC_STATUS,
                                            grpc_mdstr_from_string(tmp));
 }
diff --git a/src/core/lib/surface/channel.h b/src/core/lib/surface/channel.h
index 23cc8656cabe419821e470fc22e7f3ca70f4ff8b..2ebadb7a150db4045470559f3043dc3577226407 100644
--- a/src/core/lib/surface/channel.h
+++ b/src/core/lib/surface/channel.h
@@ -51,9 +51,10 @@ grpc_channel *grpc_channel_create(grpc_exec_ctx *exec_ctx, const char *target,
     properties from the server call to this new client call, depending on the
     value of \a propagation_mask (see propagation_bits.h for possible values) */
 grpc_call *grpc_channel_create_pollset_set_call(
-    grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask,
-    grpc_pollset_set *pollset_set, const char *method, const char *host,
-    gpr_timespec deadline, void *reserved);
+    grpc_exec_ctx *exec_ctx, grpc_channel *channel, grpc_call *parent_call,
+    uint32_t propagation_mask, grpc_pollset_set *pollset_set,
+    const char *method, const char *host, gpr_timespec deadline,
+    void *reserved);
 
 /** Get a (borrowed) pointer to this channels underlying channel stack */
 grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel);
@@ -62,7 +63,8 @@ grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel);
     status_code.
 
     The returned elem is owned by the caller. */
-grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel,
+grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_exec_ctx *exec_ctx,
+                                                 grpc_channel *channel,
                                                  int status_code);
 
 #ifdef GRPC_STREAM_REFCOUNT_DEBUG
diff --git a/src/core/lib/surface/channel_init.c b/src/core/lib/surface/channel_init.c
index 0627b3447918bdbad62db02b6cda026a26e0a266..7acb444d9b1792728eeeb47664325a2cb1cc2341 100644
--- a/src/core/lib/surface/channel_init.c
+++ b/src/core/lib/surface/channel_init.c
@@ -131,7 +131,7 @@ bool grpc_channel_init_create_stack(grpc_exec_ctx *exec_ctx,
 
   for (size_t i = 0; i < g_slots[type].num_slots; i++) {
     const stage_slot *slot = &g_slots[type].slots[i];
-    if (!slot->fn(builder, slot->arg)) {
+    if (!slot->fn(exec_ctx, builder, slot->arg)) {
       return false;
     }
   }
diff --git a/src/core/lib/surface/channel_init.h b/src/core/lib/surface/channel_init.h
index b53f2aefb92dbe1be2d35ccf3dbb37880191b7c6..411f5eae18cc61a410ea5c15eab8909b5bbf18fe 100644
--- a/src/core/lib/surface/channel_init.h
+++ b/src/core/lib/surface/channel_init.h
@@ -51,7 +51,8 @@ extern "C" {
 
 /// One stage of mutation: call functions against \a builder to influence the
 /// finally constructed channel stack
-typedef bool (*grpc_channel_init_stage)(grpc_channel_stack_builder *builder,
+typedef bool (*grpc_channel_init_stage)(grpc_exec_ctx *exec_ctx,
+                                        grpc_channel_stack_builder *builder,
                                         void *arg);
 
 /// Global initialization of the system
diff --git a/src/core/lib/surface/channel_ping.c b/src/core/lib/surface/channel_ping.c
index 0d2f01a6492cf4e712473b65cda74c6c83387f8e..e68febdddf6887f05cf7ed743e79379de350d35e 100644
--- a/src/core/lib/surface/channel_ping.c
+++ b/src/core/lib/surface/channel_ping.c
@@ -71,7 +71,7 @@ void grpc_channel_ping(grpc_channel *channel, grpc_completion_queue *cq,
   GPR_ASSERT(reserved == NULL);
   pr->tag = tag;
   pr->cq = cq;
-  grpc_closure_init(&pr->closure, ping_done, pr);
+  grpc_closure_init(&pr->closure, ping_done, pr, grpc_schedule_on_exec_ctx);
   op->send_ping = &pr->closure;
   op->bind_pollset = grpc_cq_pollset(cq);
   grpc_cq_begin_op(cq, tag);
diff --git a/src/core/lib/surface/completion_queue.c b/src/core/lib/surface/completion_queue.c
index 4e0feb56acc585b134f0acdd9b3eabe1a2667b19..4613c9021ee91044a721d191bc11ccc87e2229ec 100644
--- a/src/core/lib/surface/completion_queue.c
+++ b/src/core/lib/surface/completion_queue.c
@@ -168,7 +168,8 @@ grpc_completion_queue *grpc_completion_queue_create(void *reserved) {
 #ifndef NDEBUG
   cc->outstanding_tag_count = 0;
 #endif
-  grpc_closure_init(&cc->pollset_shutdown_done, on_pollset_shutdown_done, cc);
+  grpc_closure_init(&cc->pollset_shutdown_done, on_pollset_shutdown_done, cc,
+                    grpc_schedule_on_exec_ctx);
 
   GPR_TIMER_END("grpc_completion_queue_create", 0);
 
@@ -354,11 +355,13 @@ static void dump_pending_tags(grpc_completion_queue *cc) {
   gpr_strvec v;
   gpr_strvec_init(&v);
   gpr_strvec_add(&v, gpr_strdup("PENDING TAGS:"));
+  gpr_mu_lock(cc->mu);
   for (size_t i = 0; i < cc->outstanding_tag_count; i++) {
     char *s;
     gpr_asprintf(&s, " %p", cc->outstanding_tags[i]);
     gpr_strvec_add(&v, s);
   }
+  gpr_mu_unlock(cc->mu);
   char *out = gpr_strvec_flatten(&v, NULL);
   gpr_strvec_destroy(&v);
   gpr_log(GPR_DEBUG, "%s", out);
diff --git a/src/core/lib/surface/init.c b/src/core/lib/surface/init.c
index 7903f57a68c95f9513f27a8c47011544f5902177..f61bf1582e56728b22f4998f31fa088e92a7d458 100644
--- a/src/core/lib/surface/init.c
+++ b/src/core/lib/surface/init.c
@@ -44,6 +44,7 @@
 #include "src/core/lib/channel/compress_filter.h"
 #include "src/core/lib/channel/connected_channel.h"
 #include "src/core/lib/channel/deadline_filter.h"
+#include "src/core/lib/channel/handshaker_registry.h"
 #include "src/core/lib/channel/http_client_filter.h"
 #include "src/core/lib/channel/http_server_filter.h"
 #include "src/core/lib/channel/message_size_filter.h"
@@ -80,17 +81,20 @@ static void do_basic_init(void) {
   g_initializations = 0;
 }
 
-static bool append_filter(grpc_channel_stack_builder *builder, void *arg) {
+static bool append_filter(grpc_exec_ctx *exec_ctx,
+                          grpc_channel_stack_builder *builder, void *arg) {
   return grpc_channel_stack_builder_append_filter(
       builder, (const grpc_channel_filter *)arg, NULL, NULL);
 }
 
-static bool prepend_filter(grpc_channel_stack_builder *builder, void *arg) {
+static bool prepend_filter(grpc_exec_ctx *exec_ctx,
+                           grpc_channel_stack_builder *builder, void *arg) {
   return grpc_channel_stack_builder_prepend_filter(
       builder, (const grpc_channel_filter *)arg, NULL, NULL);
 }
 
-static bool maybe_add_http_filter(grpc_channel_stack_builder *builder,
+static bool maybe_add_http_filter(grpc_exec_ctx *exec_ctx,
+                                  grpc_channel_stack_builder *builder,
                                   void *arg) {
   grpc_transport *t = grpc_channel_stack_builder_get_transport(builder);
   if (t && strstr(t->vtable->name, "http")) {
@@ -201,6 +205,8 @@ void grpc_init(void) {
     grpc_executor_init();
     gpr_timers_global_init();
     grpc_cq_global_init();
+    grpc_handshaker_factory_registry_init();
+    grpc_security_init();
     for (i = 0; i < g_number_of_plugins; i++) {
       if (g_all_of_the_plugins[i].init != NULL) {
         g_all_of_the_plugins[i].init();
@@ -221,11 +227,12 @@ void grpc_init(void) {
 void grpc_shutdown(void) {
   int i;
   GRPC_API_TRACE("grpc_shutdown(void)", 0, ());
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   gpr_mu_lock(&g_init_mu);
   if (--g_initializations == 0) {
-    grpc_executor_shutdown();
+    grpc_executor_shutdown(&exec_ctx);
     grpc_cq_global_shutdown();
-    grpc_iomgr_shutdown();
+    grpc_iomgr_shutdown(&exec_ctx);
     gpr_timers_global_destroy();
     grpc_tracer_shutdown();
     for (i = g_number_of_plugins; i >= 0; i--) {
@@ -233,9 +240,11 @@ void grpc_shutdown(void) {
         g_all_of_the_plugins[i].destroy();
       }
     }
-    grpc_mdctx_global_shutdown();
+    grpc_mdctx_global_shutdown(&exec_ctx);
+    grpc_handshaker_factory_registry_shutdown(&exec_ctx);
   }
   gpr_mu_unlock(&g_init_mu);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 int grpc_is_initialized(void) {
diff --git a/src/core/lib/surface/init.h b/src/core/lib/surface/init.h
index b1bf57c10d197e8ef06cb377d0707cfeb1e683b0..3d1c528be043ea495160de3fe6a00a8107dcb25c 100644
--- a/src/core/lib/surface/init.h
+++ b/src/core/lib/surface/init.h
@@ -36,6 +36,7 @@
 
 void grpc_register_security_filters(void);
 void grpc_security_pre_init(void);
+void grpc_security_init(void);
 int grpc_is_initialized(void);
 
 #endif /* GRPC_CORE_LIB_SURFACE_INIT_H */
diff --git a/src/core/lib/surface/init_secure.c b/src/core/lib/surface/init_secure.c
index 7ee7b51568ede51b16913f0564cbc0e7a72b64eb..a44407d3bb4637559f77f8eddf216ac7ffb27289 100644
--- a/src/core/lib/surface/init_secure.c
+++ b/src/core/lib/surface/init_secure.c
@@ -41,6 +41,7 @@
 #include "src/core/lib/security/transport/auth_filters.h"
 #include "src/core/lib/security/transport/secure_endpoint.h"
 #include "src/core/lib/security/transport/security_connector.h"
+#include "src/core/lib/security/transport/security_handshaker.h"
 #include "src/core/lib/surface/channel_init.h"
 #include "src/core/lib/tsi/transport_security_interface.h"
 
@@ -50,7 +51,7 @@ void grpc_security_pre_init(void) {
 }
 
 static bool maybe_prepend_client_auth_filter(
-    grpc_channel_stack_builder *builder, void *arg) {
+    grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder, void *arg) {
   const grpc_channel_args *args =
       grpc_channel_stack_builder_get_channel_arguments(builder);
   if (args) {
@@ -65,7 +66,7 @@ static bool maybe_prepend_client_auth_filter(
 }
 
 static bool maybe_prepend_server_auth_filter(
-    grpc_channel_stack_builder *builder, void *arg) {
+    grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder, void *arg) {
   const grpc_channel_args *args =
       grpc_channel_stack_builder_get_channel_arguments(builder);
   if (args) {
@@ -87,3 +88,5 @@ void grpc_register_security_filters(void) {
   grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX,
                                    maybe_prepend_server_auth_filter, NULL);
 }
+
+void grpc_security_init() { grpc_security_register_handshaker_factories(); }
diff --git a/src/core/lib/surface/init_unsecure.c b/src/core/lib/surface/init_unsecure.c
index f952739e0a9d7360fb6820287b3ce6062da5c3d4..dbc9223ae0d6bcc0e11d40543df1a41d57e4d6e7 100644
--- a/src/core/lib/surface/init_unsecure.c
+++ b/src/core/lib/surface/init_unsecure.c
@@ -36,3 +36,5 @@
 void grpc_security_pre_init(void) {}
 
 void grpc_register_security_filters(void) {}
+
+void grpc_security_init(void) {}
diff --git a/src/core/lib/surface/lame_client.c b/src/core/lib/surface/lame_client.c
index d0df8e7e1743682e3594e21d33a355e84f2504e2..ae1eac09a96c5d9808a9864806ad7c582907f2a4 100644
--- a/src/core/lib/surface/lame_client.c
+++ b/src/core/lib/surface/lame_client.c
@@ -55,14 +55,15 @@ typedef struct {
   const char *error_message;
 } channel_data;
 
-static void fill_metadata(grpc_call_element *elem, grpc_metadata_batch *mdb) {
+static void fill_metadata(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
+                          grpc_metadata_batch *mdb) {
   call_data *calld = elem->call_data;
   channel_data *chand = elem->channel_data;
   char tmp[GPR_LTOA_MIN_BUFSIZE];
   gpr_ltoa(chand->error_code, tmp);
-  calld->status.md = grpc_mdelem_from_strings("grpc-status", tmp);
+  calld->status.md = grpc_mdelem_from_strings(exec_ctx, "grpc-status", tmp);
   calld->details.md =
-      grpc_mdelem_from_strings("grpc-message", chand->error_message);
+      grpc_mdelem_from_strings(exec_ctx, "grpc-message", chand->error_message);
   calld->status.prev = calld->details.next = NULL;
   calld->status.next = &calld->details;
   calld->details.prev = &calld->status;
@@ -76,9 +77,9 @@ static void lame_start_transport_stream_op(grpc_exec_ctx *exec_ctx,
                                            grpc_transport_stream_op *op) {
   GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
   if (op->recv_initial_metadata != NULL) {
-    fill_metadata(elem, op->recv_initial_metadata);
+    fill_metadata(exec_ctx, elem, op->recv_initial_metadata);
   } else if (op->recv_trailing_metadata != NULL) {
-    fill_metadata(elem, op->recv_trailing_metadata);
+    fill_metadata(exec_ctx, elem, op->recv_trailing_metadata);
   }
   grpc_transport_stream_op_finish_with_failure(
       exec_ctx, op, GRPC_ERROR_CREATE("lame client channel"));
@@ -98,16 +99,16 @@ static void lame_start_transport_op(grpc_exec_ctx *exec_ctx,
   if (op->on_connectivity_state_change) {
     GPR_ASSERT(*op->connectivity_state != GRPC_CHANNEL_SHUTDOWN);
     *op->connectivity_state = GRPC_CHANNEL_SHUTDOWN;
-    grpc_exec_ctx_sched(exec_ctx, op->on_connectivity_state_change,
-                        GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, op->on_connectivity_state_change,
+                       GRPC_ERROR_NONE);
   }
   if (op->send_ping != NULL) {
-    grpc_exec_ctx_sched(exec_ctx, op->send_ping,
-                        GRPC_ERROR_CREATE("lame client channel"), NULL);
+    grpc_closure_sched(exec_ctx, op->send_ping,
+                       GRPC_ERROR_CREATE("lame client channel"));
   }
   GRPC_ERROR_UNREF(op->disconnect_with_error);
   if (op->on_consumed != NULL) {
-    grpc_exec_ctx_sched(exec_ctx, op->on_consumed, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, op->on_consumed, GRPC_ERROR_NONE);
   }
 }
 
@@ -123,11 +124,12 @@ static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
   gpr_free(and_free_memory);
 }
 
-static void init_channel_elem(grpc_exec_ctx *exec_ctx,
-                              grpc_channel_element *elem,
-                              grpc_channel_element_args *args) {
+static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx,
+                                     grpc_channel_element *elem,
+                                     grpc_channel_element_args *args) {
   GPR_ASSERT(args->is_first);
   GPR_ASSERT(args->is_last);
+  return GRPC_ERROR_NONE;
 }
 
 static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/lib/surface/server.c b/src/core/lib/surface/server.c
index fe73aa375cb2a20f363901e11f96a2149ce05184..addb7c4fbc039667d35487d4cdab73a2ef810103 100644
--- a/src/core/lib/surface/server.c
+++ b/src/core/lib/surface/server.c
@@ -45,6 +45,7 @@
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/connected_channel.h"
 #include "src/core/lib/iomgr/iomgr.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/support/stack_lockfree.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/surface/api_trace.h"
@@ -271,14 +272,15 @@ struct shutdown_cleanup_args {
 static void shutdown_cleanup(grpc_exec_ctx *exec_ctx, void *arg,
                              grpc_error *error) {
   struct shutdown_cleanup_args *a = arg;
-  grpc_slice_unref(a->slice);
+  grpc_slice_unref_internal(exec_ctx, a->slice);
   gpr_free(a);
 }
 
 static void send_shutdown(grpc_exec_ctx *exec_ctx, grpc_channel *channel,
                           int send_goaway, grpc_error *send_disconnect) {
   struct shutdown_cleanup_args *sc = gpr_malloc(sizeof(*sc));
-  grpc_closure_init(&sc->closure, shutdown_cleanup, sc);
+  grpc_closure_init(&sc->closure, shutdown_cleanup, sc,
+                    grpc_schedule_on_exec_ctx);
   grpc_transport_op *op = grpc_make_transport_op(&sc->closure);
   grpc_channel_element *elem;
 
@@ -346,9 +348,9 @@ static void request_matcher_zombify_all_pending_calls(grpc_exec_ctx *exec_ctx,
     gpr_mu_unlock(&calld->mu_state);
     grpc_closure_init(
         &calld->kill_zombie_closure, kill_zombie,
-        grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0));
-    grpc_exec_ctx_sched(exec_ctx, &calld->kill_zombie_closure, GRPC_ERROR_NONE,
-                        NULL);
+        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, GRPC_ERROR_NONE);
   }
 }
 
@@ -379,7 +381,7 @@ static void server_ref(grpc_server *server) {
 static void server_delete(grpc_exec_ctx *exec_ctx, grpc_server *server) {
   registered_method *rm;
   size_t i;
-  grpc_channel_args_destroy(server->channel_args);
+  grpc_channel_args_destroy(exec_ctx, server->channel_args);
   gpr_mu_destroy(&server->mu_global);
   gpr_mu_destroy(&server->mu_call);
   while ((rm = server->registered_methods) != NULL) {
@@ -440,8 +442,8 @@ static void destroy_channel(grpc_exec_ctx *exec_ctx, channel_data *chand,
   orphan_channel(chand);
   server_ref(chand->server);
   maybe_finish_shutdown(exec_ctx, chand->server);
-  chand->finish_destroy_channel_closure.cb = finish_destroy_channel;
-  chand->finish_destroy_channel_closure.cb_arg = chand;
+  grpc_closure_init(&chand->finish_destroy_channel_closure,
+                    finish_destroy_channel, chand, grpc_schedule_on_exec_ctx);
 
   if (grpc_server_channel_trace && error != GRPC_ERROR_NONE) {
     const char *msg = grpc_error_string(error);
@@ -545,8 +547,9 @@ static void publish_new_rpc(grpc_exec_ctx *exec_ctx, void *arg,
     gpr_mu_unlock(&calld->mu_state);
     grpc_closure_init(
         &calld->kill_zombie_closure, kill_zombie,
-        grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0));
-    grpc_exec_ctx_sched(exec_ctx, &calld->kill_zombie_closure, error, NULL);
+        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);
     return;
   }
 
@@ -590,9 +593,9 @@ static void finish_start_new_rpc(
     gpr_mu_lock(&calld->mu_state);
     calld->state = ZOMBIED;
     gpr_mu_unlock(&calld->mu_state);
-    grpc_closure_init(&calld->kill_zombie_closure, kill_zombie, elem);
-    grpc_exec_ctx_sched(exec_ctx, &calld->kill_zombie_closure, GRPC_ERROR_NONE,
-                        NULL);
+    grpc_closure_init(&calld->kill_zombie_closure, kill_zombie, elem,
+                      grpc_schedule_on_exec_ctx);
+    grpc_closure_sched(exec_ctx, &calld->kill_zombie_closure, GRPC_ERROR_NONE);
     return;
   }
 
@@ -607,7 +610,8 @@ static void finish_start_new_rpc(
       memset(&op, 0, sizeof(op));
       op.op = GRPC_OP_RECV_MESSAGE;
       op.data.recv_message = &calld->payload;
-      grpc_closure_init(&calld->publish, publish_new_rpc, elem);
+      grpc_closure_init(&calld->publish, publish_new_rpc, elem,
+                        grpc_schedule_on_exec_ctx);
       grpc_call_start_batch_and_execute(exec_ctx, calld->call, &op, 1,
                                         &calld->publish);
       break;
@@ -740,7 +744,8 @@ static void maybe_finish_shutdown(grpc_exec_ctx *exec_ctx,
   }
 }
 
-static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
+static grpc_mdelem *server_filter(grpc_exec_ctx *exec_ctx, void *user_data,
+                                  grpc_mdelem *md) {
   grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
   if (md->key == GRPC_MDSTR_PATH) {
@@ -764,7 +769,8 @@ static void server_on_recv_initial_metadata(grpc_exec_ctx *exec_ctx, void *ptr,
   gpr_timespec op_deadline;
 
   GRPC_ERROR_REF(error);
-  grpc_metadata_batch_filter(calld->recv_initial_metadata, server_filter, elem);
+  grpc_metadata_batch_filter(exec_ctx, calld->recv_initial_metadata,
+                             server_filter, elem);
   op_deadline = calld->recv_initial_metadata->deadline;
   if (0 != gpr_time_cmp(op_deadline, gpr_inf_future(op_deadline.clock_type))) {
     calld->deadline = op_deadline;
@@ -813,9 +819,10 @@ static void got_initial_metadata(grpc_exec_ctx *exec_ctx, void *ptr,
     if (calld->state == NOT_STARTED) {
       calld->state = ZOMBIED;
       gpr_mu_unlock(&calld->mu_state);
-      grpc_closure_init(&calld->kill_zombie_closure, kill_zombie, elem);
-      grpc_exec_ctx_sched(exec_ctx, &calld->kill_zombie_closure,
-                          GRPC_ERROR_NONE, NULL);
+      grpc_closure_init(&calld->kill_zombie_closure, kill_zombie, elem,
+                        grpc_schedule_on_exec_ctx);
+      grpc_closure_sched(exec_ctx, &calld->kill_zombie_closure,
+                         GRPC_ERROR_NONE);
     } else if (calld->state == PENDING) {
       calld->state = ZOMBIED;
       gpr_mu_unlock(&calld->mu_state);
@@ -838,7 +845,7 @@ static void accept_stream(grpc_exec_ctx *exec_ctx, void *cd,
   args.server_transport_data = transport_server_data;
   args.send_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
   grpc_call *call;
-  grpc_error *error = grpc_call_create(&args, &call);
+  grpc_error *error = grpc_call_create(exec_ctx, &args, &call);
   grpc_call_element *elem =
       grpc_call_stack_element(grpc_call_get_call_stack(call), 0);
   if (error != GRPC_ERROR_NONE) {
@@ -851,7 +858,8 @@ static void accept_stream(grpc_exec_ctx *exec_ctx, void *cd,
   memset(&op, 0, sizeof(op));
   op.op = GRPC_OP_RECV_INITIAL_METADATA;
   op.data.recv_initial_metadata = &calld->initial_metadata;
-  grpc_closure_init(&calld->got_initial_metadata, got_initial_metadata, elem);
+  grpc_closure_init(&calld->got_initial_metadata, got_initial_metadata, elem,
+                    grpc_schedule_on_exec_ctx);
   grpc_call_start_batch_and_execute(exec_ctx, call, &op, 1,
                                     &calld->got_initial_metadata);
 }
@@ -887,7 +895,8 @@ static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
   gpr_mu_init(&calld->mu_state);
 
   grpc_closure_init(&calld->server_on_recv_initial_metadata,
-                    server_on_recv_initial_metadata, elem);
+                    server_on_recv_initial_metadata, elem,
+                    grpc_schedule_on_exec_ctx);
 
   server_ref(chand->server);
   return GRPC_ERROR_NONE;
@@ -902,10 +911,10 @@ static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
   GPR_ASSERT(calld->state != PENDING);
 
   if (calld->host) {
-    GRPC_MDSTR_UNREF(calld->host);
+    GRPC_MDSTR_UNREF(exec_ctx, calld->host);
   }
   if (calld->path) {
-    GRPC_MDSTR_UNREF(calld->path);
+    GRPC_MDSTR_UNREF(exec_ctx, calld->path);
   }
   grpc_metadata_array_destroy(&calld->initial_metadata);
 
@@ -914,9 +923,9 @@ static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
   server_unref(exec_ctx, chand->server);
 }
 
-static void init_channel_elem(grpc_exec_ctx *exec_ctx,
-                              grpc_channel_element *elem,
-                              grpc_channel_element_args *args) {
+static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx,
+                                     grpc_channel_element *elem,
+                                     grpc_channel_element_args *args) {
   channel_data *chand = elem->channel_data;
   GPR_ASSERT(args->is_first);
   GPR_ASSERT(!args->is_last);
@@ -926,7 +935,9 @@ static void init_channel_elem(grpc_exec_ctx *exec_ctx,
   chand->registered_methods = NULL;
   chand->connectivity_state = GRPC_CHANNEL_IDLE;
   grpc_closure_init(&chand->channel_connectivity_changed,
-                    channel_connectivity_changed, chand);
+                    channel_connectivity_changed, chand,
+                    grpc_schedule_on_exec_ctx);
+  return GRPC_ERROR_NONE;
 }
 
 static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
@@ -936,10 +947,10 @@ static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
   if (chand->registered_methods) {
     for (i = 0; i < chand->registered_method_slots; i++) {
       if (chand->registered_methods[i].method) {
-        GRPC_MDSTR_UNREF(chand->registered_methods[i].method);
+        GRPC_MDSTR_UNREF(exec_ctx, chand->registered_methods[i].method);
       }
       if (chand->registered_methods[i].host) {
-        GRPC_MDSTR_UNREF(chand->registered_methods[i].host);
+        GRPC_MDSTR_UNREF(exec_ctx, chand->registered_methods[i].host);
       }
     }
     gpr_free(chand->registered_methods);
@@ -1277,7 +1288,8 @@ void grpc_server_shutdown_and_notify(grpc_server *server,
 
   /* Shutdown listeners */
   for (l = server->listeners; l; l = l->next) {
-    grpc_closure_init(&l->destroy_done, listener_destroy_done, server);
+    grpc_closure_init(&l->destroy_done, listener_destroy_done, server,
+                      grpc_schedule_on_exec_ctx);
     l->destroy(&exec_ctx, server, l->arg, &l->destroy_done);
   }
 
@@ -1383,9 +1395,10 @@ static grpc_call_error queue_call_request(grpc_exec_ctx *exec_ctx,
         gpr_mu_unlock(&calld->mu_state);
         grpc_closure_init(
             &calld->kill_zombie_closure, kill_zombie,
-            grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0));
-        grpc_exec_ctx_sched(exec_ctx, &calld->kill_zombie_closure,
-                            GRPC_ERROR_NONE, NULL);
+            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,
+                           GRPC_ERROR_NONE);
       } else {
         GPR_ASSERT(calld->state == PENDING);
         calld->state = ACTIVATED;
diff --git a/src/core/lib/surface/validate_metadata.c b/src/core/lib/surface/validate_metadata.c
index 84f0a083bc165fa79d21aba70a340295fb413005..f49dd8584b0dbeed2c81f58cce7b1d692cce36d0 100644
--- a/src/core/lib/surface/validate_metadata.c
+++ b/src/core/lib/surface/validate_metadata.c
@@ -53,7 +53,7 @@ int grpc_header_key_is_legal(const char *key, size_t length) {
       0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xff, 0x03, 0x00, 0x00, 0x00,
       0x80, 0xfe, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-  if (length == 0) {
+  if (length == 0 || key[0] == ':') {
     return 0;
   }
   return conforms_to(key, length, legal_header_bits);
diff --git a/src/core/lib/transport/README.md b/src/core/lib/transport/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..e7e135edeb134db20477618c05be90b672abe57b
--- /dev/null
+++ b/src/core/lib/transport/README.md
@@ -0,0 +1,7 @@
+# Transport
+
+Common implementation details for gRPC Transports.
+
+Transports multiplex messages across some single connection. In ext/ there are
+implementations atop [a custom http2 implementation](/src/core/ext/transport/chttp2/README.md)
+and atop [cronet](/src/core/ext/transport/cronet/README.md).
diff --git a/src/core/lib/transport/byte_stream.c b/src/core/lib/transport/byte_stream.c
index 2f1d7b7c60c8ee4cfee73bddf353fa2de64378d9..4d4206189e7be0243148d9cebb9b8962f2bd4701 100644
--- a/src/core/lib/transport/byte_stream.c
+++ b/src/core/lib/transport/byte_stream.c
@@ -37,6 +37,8 @@
 
 #include <grpc/support/log.h>
 
+#include "src/core/lib/slice/slice_internal.h"
+
 int grpc_byte_stream_next(grpc_exec_ctx *exec_ctx,
                           grpc_byte_stream *byte_stream, grpc_slice *slice,
                           size_t max_size_hint, grpc_closure *on_complete) {
@@ -57,7 +59,8 @@ static int slice_buffer_stream_next(grpc_exec_ctx *exec_ctx,
                                     grpc_closure *on_complete) {
   grpc_slice_buffer_stream *stream = (grpc_slice_buffer_stream *)byte_stream;
   GPR_ASSERT(stream->cursor < stream->backing_buffer->count);
-  *slice = grpc_slice_ref(stream->backing_buffer->slices[stream->cursor]);
+  *slice =
+      grpc_slice_ref_internal(stream->backing_buffer->slices[stream->cursor]);
   stream->cursor++;
   return 1;
 }
diff --git a/src/core/lib/transport/connectivity_state.c b/src/core/lib/transport/connectivity_state.c
index 4f49d7cf7d769a93dc2639b979c232026a0b9728..c656d93740e86bfd465a0fd2e021e52b4890b1e5 100644
--- a/src/core/lib/transport/connectivity_state.c
+++ b/src/core/lib/transport/connectivity_state.c
@@ -81,7 +81,7 @@ void grpc_connectivity_state_destroy(grpc_exec_ctx *exec_ctx,
     } else {
       error = GRPC_ERROR_CREATE("Shutdown connectivity owner");
     }
-    grpc_exec_ctx_sched(exec_ctx, w->notify, error, NULL);
+    grpc_closure_sched(exec_ctx, w->notify, error);
     gpr_free(w);
   }
   GRPC_ERROR_UNREF(tracker->current_error);
@@ -121,7 +121,7 @@ bool grpc_connectivity_state_notify_on_state_change(
   if (current == NULL) {
     grpc_connectivity_state_watcher *w = tracker->watchers;
     if (w != NULL && w->notify == notify) {
-      grpc_exec_ctx_sched(exec_ctx, notify, GRPC_ERROR_CANCELLED, NULL);
+      grpc_closure_sched(exec_ctx, notify, GRPC_ERROR_CANCELLED);
       tracker->watchers = w->next;
       gpr_free(w);
       return false;
@@ -129,7 +129,7 @@ bool grpc_connectivity_state_notify_on_state_change(
     while (w != NULL) {
       grpc_connectivity_state_watcher *rm_candidate = w->next;
       if (rm_candidate != NULL && rm_candidate->notify == notify) {
-        grpc_exec_ctx_sched(exec_ctx, notify, GRPC_ERROR_CANCELLED, NULL);
+        grpc_closure_sched(exec_ctx, notify, GRPC_ERROR_CANCELLED);
         w->next = w->next->next;
         gpr_free(rm_candidate);
         return false;
@@ -140,8 +140,8 @@ bool grpc_connectivity_state_notify_on_state_change(
   } else {
     if (tracker->current_state != *current) {
       *current = tracker->current_state;
-      grpc_exec_ctx_sched(exec_ctx, notify,
-                          GRPC_ERROR_REF(tracker->current_error), NULL);
+      grpc_closure_sched(exec_ctx, notify,
+                         GRPC_ERROR_REF(tracker->current_error));
     } else {
       grpc_connectivity_state_watcher *w = gpr_malloc(sizeof(*w));
       w->current = current;
@@ -191,8 +191,8 @@ void grpc_connectivity_state_set(grpc_exec_ctx *exec_ctx,
       gpr_log(GPR_DEBUG, "NOTIFY: %p %s: %p", tracker, tracker->name,
               w->notify);
     }
-    grpc_exec_ctx_sched(exec_ctx, w->notify,
-                        GRPC_ERROR_REF(tracker->current_error), NULL);
+    grpc_closure_sched(exec_ctx, w->notify,
+                       GRPC_ERROR_REF(tracker->current_error));
     gpr_free(w);
   }
 }
diff --git a/src/core/lib/transport/mdstr_hash_table.c b/src/core/lib/transport/mdstr_hash_table.c
index 3d01e56df7e7883cfc6d54d5573225ea54a483b9..2791bf653b89797cb9b04cc854cdd0ae7c312213 100644
--- a/src/core/lib/transport/mdstr_hash_table.c
+++ b/src/core/lib/transport/mdstr_hash_table.c
@@ -94,13 +94,14 @@ grpc_mdstr_hash_table* grpc_mdstr_hash_table_ref(grpc_mdstr_hash_table* table) {
   return table;
 }
 
-void grpc_mdstr_hash_table_unref(grpc_mdstr_hash_table* table) {
+void grpc_mdstr_hash_table_unref(grpc_exec_ctx* exec_ctx,
+                                 grpc_mdstr_hash_table* table) {
   if (table != NULL && gpr_unref(&table->refs)) {
     for (size_t i = 0; i < table->size; ++i) {
       grpc_mdstr_hash_table_entry* entry = &table->entries[i];
       if (entry->key != NULL) {
-        GRPC_MDSTR_UNREF(entry->key);
-        entry->vtable->destroy_value(entry->value);
+        GRPC_MDSTR_UNREF(exec_ctx, entry->key);
+        entry->vtable->destroy_value(exec_ctx, entry->value);
       }
     }
     gpr_free(table->entries);
diff --git a/src/core/lib/transport/mdstr_hash_table.h b/src/core/lib/transport/mdstr_hash_table.h
index 8982ec3a8dc01f305bc3849eff736f2cf47b56f6..57f497ee278dcba766b090b9dce942fad0a0f6c0 100644
--- a/src/core/lib/transport/mdstr_hash_table.h
+++ b/src/core/lib/transport/mdstr_hash_table.h
@@ -49,7 +49,7 @@
 typedef struct grpc_mdstr_hash_table grpc_mdstr_hash_table;
 
 typedef struct grpc_mdstr_hash_table_vtable {
-  void (*destroy_value)(void* value);
+  void (*destroy_value)(grpc_exec_ctx* exec_ctx, void* value);
   void* (*copy_value)(void* value);
 } grpc_mdstr_hash_table_vtable;
 
@@ -66,7 +66,8 @@ grpc_mdstr_hash_table* grpc_mdstr_hash_table_create(
     size_t num_entries, grpc_mdstr_hash_table_entry* entries);
 
 grpc_mdstr_hash_table* grpc_mdstr_hash_table_ref(grpc_mdstr_hash_table* table);
-void grpc_mdstr_hash_table_unref(grpc_mdstr_hash_table* table);
+void grpc_mdstr_hash_table_unref(grpc_exec_ctx* exec_ctx,
+                                 grpc_mdstr_hash_table* table);
 
 /** Returns the value from \a table associated with \a key.
     Returns NULL if \a key is not found. */
diff --git a/src/core/lib/transport/metadata.c b/src/core/lib/transport/metadata.c
index fac19b91d9e8d775125855d920a4fea9397810e7..54c39df6a7f44722d4a05f780c0532514c288682 100644
--- a/src/core/lib/transport/metadata.c
+++ b/src/core/lib/transport/metadata.c
@@ -47,6 +47,7 @@
 
 #include "src/core/lib/iomgr/iomgr_internal.h"
 #include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/support/murmur_hash.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/transport/static_metadata.h"
@@ -153,7 +154,7 @@ static size_t g_static_mdtab_maxprobe;
 static strtab_shard g_strtab_shard[STRTAB_SHARD_COUNT];
 static mdtab_shard g_mdtab_shard[MDTAB_SHARD_COUNT];
 
-static void gc_mdtab(mdtab_shard *shard);
+static void gc_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard);
 
 void grpc_test_only_set_metadata_hash_seed(uint32_t seed) {
   g_hash_seed = seed;
@@ -227,12 +228,12 @@ void grpc_mdctx_global_init(void) {
   }
 }
 
-void grpc_mdctx_global_shutdown(void) {
+void grpc_mdctx_global_shutdown(grpc_exec_ctx *exec_ctx) {
   size_t i;
   for (i = 0; i < MDTAB_SHARD_COUNT; i++) {
     mdtab_shard *shard = &g_mdtab_shard[i];
     gpr_mu_destroy(&shard->mu);
-    gc_mdtab(shard);
+    gc_mdtab(exec_ctx, shard);
     /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */
     if (shard->count != 0) {
       gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata elements were leaked",
@@ -316,12 +317,13 @@ static void grow_strtab(strtab_shard *shard) {
   GPR_TIMER_END("grow_strtab", 0);
 }
 
-static void internal_destroy_string(strtab_shard *shard, internal_string *is) {
+static void internal_destroy_string(grpc_exec_ctx *exec_ctx,
+                                    strtab_shard *shard, internal_string *is) {
   internal_string **prev_next;
   internal_string *cur;
   GPR_TIMER_BEGIN("internal_destroy_string", 0);
   if (is->has_base64_and_huffman_encoded) {
-    grpc_slice_unref(is->base64_and_huffman);
+    grpc_slice_unref_internal(exec_ctx, is->base64_and_huffman);
   }
   for (prev_next = &shard->strs[TABLE_IDX(is->hash, LOG2_STRTAB_SHARD_COUNT,
                                           shard->capacity)],
@@ -340,20 +342,20 @@ static void slice_ref(void *p) {
   GRPC_MDSTR_REF((grpc_mdstr *)(is));
 }
 
-static void slice_unref(void *p) {
+static void slice_unref(grpc_exec_ctx *exec_ctx, void *p) {
   internal_string *is =
       (internal_string *)((char *)p - offsetof(internal_string, refcount));
-  GRPC_MDSTR_UNREF((grpc_mdstr *)(is));
+  GRPC_MDSTR_UNREF(exec_ctx, (grpc_mdstr *)(is));
 }
 
 grpc_mdstr *grpc_mdstr_from_string(const char *str) {
   return grpc_mdstr_from_buffer((const uint8_t *)str, strlen(str));
 }
 
-grpc_mdstr *grpc_mdstr_from_slice(grpc_slice slice) {
+grpc_mdstr *grpc_mdstr_from_slice(grpc_exec_ctx *exec_ctx, grpc_slice slice) {
   grpc_mdstr *result = grpc_mdstr_from_buffer(GRPC_SLICE_START_PTR(slice),
                                               GRPC_SLICE_LENGTH(slice));
-  grpc_slice_unref(slice);
+  grpc_slice_unref_internal(exec_ctx, slice);
   return result;
 }
 
@@ -444,7 +446,7 @@ grpc_mdstr *grpc_mdstr_from_buffer(const uint8_t *buf, size_t length) {
   return (grpc_mdstr *)s;
 }
 
-static void gc_mdtab(mdtab_shard *shard) {
+static void gc_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard) {
   size_t i;
   internal_metadata **prev_next;
   internal_metadata *md, *next;
@@ -457,8 +459,8 @@ static void gc_mdtab(mdtab_shard *shard) {
       void *user_data = (void *)gpr_atm_no_barrier_load(&md->user_data);
       next = md->bucket_next;
       if (gpr_atm_acq_load(&md->refcnt) == 0) {
-        GRPC_MDSTR_UNREF((grpc_mdstr *)md->key);
-        GRPC_MDSTR_UNREF((grpc_mdstr *)md->value);
+        GRPC_MDSTR_UNREF(exec_ctx, (grpc_mdstr *)md->key);
+        GRPC_MDSTR_UNREF(exec_ctx, (grpc_mdstr *)md->value);
         if (md->user_data) {
           ((destroy_user_data_func)gpr_atm_no_barrier_load(
               &md->destroy_user_data))(user_data);
@@ -506,16 +508,17 @@ static void grow_mdtab(mdtab_shard *shard) {
   GPR_TIMER_END("grow_mdtab", 0);
 }
 
-static void rehash_mdtab(mdtab_shard *shard) {
+static void rehash_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard) {
   if (gpr_atm_no_barrier_load(&shard->free_estimate) >
       (gpr_atm)(shard->capacity / 4)) {
-    gc_mdtab(shard);
+    gc_mdtab(exec_ctx, shard);
   } else {
     grow_mdtab(shard);
   }
 }
 
-grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdstr *mkey,
+grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_exec_ctx *exec_ctx,
+                                               grpc_mdstr *mkey,
                                                grpc_mdstr *mvalue) {
   internal_string *key = (internal_string *)mkey;
   internal_string *value = (internal_string *)mvalue;
@@ -547,8 +550,8 @@ grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdstr *mkey,
   for (md = shard->elems[idx]; md; md = md->bucket_next) {
     if (md->key == key && md->value == value) {
       REF_MD_LOCKED(shard, md);
-      GRPC_MDSTR_UNREF((grpc_mdstr *)key);
-      GRPC_MDSTR_UNREF((grpc_mdstr *)value);
+      GRPC_MDSTR_UNREF(exec_ctx, (grpc_mdstr *)key);
+      GRPC_MDSTR_UNREF(exec_ctx, (grpc_mdstr *)value);
       gpr_mu_unlock(&shard->mu);
       GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
       return (grpc_mdelem *)md;
@@ -574,7 +577,7 @@ grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdstr *mkey,
   shard->count++;
 
   if (shard->count > shard->capacity * 2) {
-    rehash_mdtab(shard);
+    rehash_mdtab(exec_ctx, shard);
   }
 
   gpr_mu_unlock(&shard->mu);
@@ -584,21 +587,26 @@ grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdstr *mkey,
   return (grpc_mdelem *)md;
 }
 
-grpc_mdelem *grpc_mdelem_from_strings(const char *key, const char *value) {
-  return grpc_mdelem_from_metadata_strings(grpc_mdstr_from_string(key),
-                                           grpc_mdstr_from_string(value));
+grpc_mdelem *grpc_mdelem_from_strings(grpc_exec_ctx *exec_ctx, const char *key,
+                                      const char *value) {
+  return grpc_mdelem_from_metadata_strings(
+      exec_ctx, grpc_mdstr_from_string(key), grpc_mdstr_from_string(value));
 }
 
-grpc_mdelem *grpc_mdelem_from_slices(grpc_slice key, grpc_slice value) {
-  return grpc_mdelem_from_metadata_strings(grpc_mdstr_from_slice(key),
-                                           grpc_mdstr_from_slice(value));
+grpc_mdelem *grpc_mdelem_from_slices(grpc_exec_ctx *exec_ctx, grpc_slice key,
+                                     grpc_slice value) {
+  return grpc_mdelem_from_metadata_strings(
+      exec_ctx, grpc_mdstr_from_slice(exec_ctx, key),
+      grpc_mdstr_from_slice(exec_ctx, value));
 }
 
-grpc_mdelem *grpc_mdelem_from_string_and_buffer(const char *key,
+grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_exec_ctx *exec_ctx,
+                                                const char *key,
                                                 const uint8_t *value,
                                                 size_t value_length) {
   return grpc_mdelem_from_metadata_strings(
-      grpc_mdstr_from_string(key), grpc_mdstr_from_buffer(value, value_length));
+      exec_ctx, grpc_mdstr_from_string(key),
+      grpc_mdstr_from_buffer(value, value_length));
 }
 
 static size_t get_base64_encoded_size(size_t raw_length) {
@@ -654,7 +662,7 @@ grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd DEBUG_ARGS) {
   return gmd;
 }
 
-void grpc_mdelem_unref(grpc_mdelem *gmd DEBUG_ARGS) {
+void grpc_mdelem_unref(grpc_exec_ctx *exec_ctx, grpc_mdelem *gmd DEBUG_ARGS) {
   internal_metadata *md = (internal_metadata *)gmd;
   if (!md) return;
   if (is_mdelem_static(gmd)) return;
@@ -696,7 +704,7 @@ grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *gs DEBUG_ARGS) {
   return gs;
 }
 
-void grpc_mdstr_unref(grpc_mdstr *gs DEBUG_ARGS) {
+void grpc_mdstr_unref(grpc_exec_ctx *exec_ctx, grpc_mdstr *gs DEBUG_ARGS) {
   internal_string *s = (internal_string *)gs;
   if (is_mdstr_static(gs)) return;
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
@@ -709,7 +717,7 @@ void grpc_mdstr_unref(grpc_mdstr *gs DEBUG_ARGS) {
         &g_strtab_shard[SHARD_IDX(s->hash, LOG2_STRTAB_SHARD_COUNT)];
     gpr_mu_lock(&shard->mu);
     GPR_ASSERT(0 == gpr_atm_no_barrier_load(&s->refcnt));
-    internal_destroy_string(shard, s);
+    internal_destroy_string(exec_ctx, shard, s);
     gpr_mu_unlock(&shard->mu);
   }
 }
diff --git a/src/core/lib/transport/metadata.h b/src/core/lib/transport/metadata.h
index a4955a1ea7440e506e91efba88816e5b02afd67f..991eee96f1c1df7189252ace3be719af8109103a 100644
--- a/src/core/lib/transport/metadata.h
+++ b/src/core/lib/transport/metadata.h
@@ -37,6 +37,8 @@
 #include <grpc/slice.h>
 #include <grpc/support/useful.h>
 
+#include "src/core/lib/iomgr/exec_ctx.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -96,7 +98,7 @@ void grpc_test_only_set_metadata_hash_seed(uint32_t seed);
    clients may have handy */
 grpc_mdstr *grpc_mdstr_from_string(const char *str);
 /* Unrefs the slice. */
-grpc_mdstr *grpc_mdstr_from_slice(grpc_slice slice);
+grpc_mdstr *grpc_mdstr_from_slice(grpc_exec_ctx *exec_ctx, grpc_slice slice);
 grpc_mdstr *grpc_mdstr_from_buffer(const uint8_t *str, size_t length);
 
 /* Returns a borrowed slice from the mdstr with its contents base64 encoded
@@ -105,12 +107,16 @@ grpc_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *str);
 
 /* Constructors for grpc_mdelem instances; take a variety of data types that
    clients may have handy */
-grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdstr *key,
+grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_exec_ctx *exec_ctx,
+                                               grpc_mdstr *key,
                                                grpc_mdstr *value);
-grpc_mdelem *grpc_mdelem_from_strings(const char *key, const char *value);
+grpc_mdelem *grpc_mdelem_from_strings(grpc_exec_ctx *exec_ctx, const char *key,
+                                      const char *value);
 /* Unrefs the slices. */
-grpc_mdelem *grpc_mdelem_from_slices(grpc_slice key, grpc_slice value);
-grpc_mdelem *grpc_mdelem_from_string_and_buffer(const char *key,
+grpc_mdelem *grpc_mdelem_from_slices(grpc_exec_ctx *exec_ctx, grpc_slice key,
+                                     grpc_slice value);
+grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_exec_ctx *exec_ctx,
+                                                const char *key,
                                                 const uint8_t *value,
                                                 size_t value_length);
 
@@ -127,22 +133,26 @@ void *grpc_mdelem_set_user_data(grpc_mdelem *md, void (*destroy_func)(void *),
 //#define GRPC_METADATA_REFCOUNT_DEBUG
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
 #define GRPC_MDSTR_REF(s) grpc_mdstr_ref((s), __FILE__, __LINE__)
-#define GRPC_MDSTR_UNREF(s) grpc_mdstr_unref((s), __FILE__, __LINE__)
+#define GRPC_MDSTR_UNREF(exec_ctx, s) \
+  grpc_mdstr_unref((exec_ctx), (s), __FILE__, __LINE__)
 #define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s), __FILE__, __LINE__)
-#define GRPC_MDELEM_UNREF(s) grpc_mdelem_unref((s), __FILE__, __LINE__)
+#define GRPC_MDELEM_UNREF(exec_ctx, s) \
+  grpc_mdelem_unref((exec_ctx), (s), __FILE__, __LINE__)
 grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *s, const char *file, int line);
-void grpc_mdstr_unref(grpc_mdstr *s, const char *file, int line);
+void grpc_mdstr_unref(grpc_exec_ctx *exec_ctx, grpc_mdstr *s, const char *file,
+                      int line);
 grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *md, const char *file, int line);
-void grpc_mdelem_unref(grpc_mdelem *md, const char *file, int line);
+void grpc_mdelem_unref(grpc_exec_ctx *exec_ctx, grpc_mdelem *md,
+                       const char *file, int line);
 #else
 #define GRPC_MDSTR_REF(s) grpc_mdstr_ref((s))
-#define GRPC_MDSTR_UNREF(s) grpc_mdstr_unref((s))
+#define GRPC_MDSTR_UNREF(exec_ctx, s) grpc_mdstr_unref((exec_ctx), (s))
 #define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s))
-#define GRPC_MDELEM_UNREF(s) grpc_mdelem_unref((s))
+#define GRPC_MDELEM_UNREF(exec_ctx, s) grpc_mdelem_unref((exec_ctx), (s))
 grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *s);
-void grpc_mdstr_unref(grpc_mdstr *s);
+void grpc_mdstr_unref(grpc_exec_ctx *exec_ctx, grpc_mdstr *s);
 grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *md);
-void grpc_mdelem_unref(grpc_mdelem *md);
+void grpc_mdelem_unref(grpc_exec_ctx *exec_ctx, grpc_mdelem *md);
 #endif
 
 /* Recover a char* from a grpc_mdstr. The returned string is null terminated.
@@ -162,7 +172,7 @@ int grpc_mdstr_is_bin_suffixed(grpc_mdstr *s);
 #define GRPC_MDSTR_KV_HASH(k_hash, v_hash) (GPR_ROTL((k_hash), 2) ^ (v_hash))
 
 void grpc_mdctx_global_init(void);
-void grpc_mdctx_global_shutdown(void);
+void grpc_mdctx_global_shutdown(grpc_exec_ctx *exec_ctx);
 
 /* Implementation provided by chttp2_transport */
 extern grpc_slice (*grpc_chttp2_base64_encode_and_huffman_compress)(
diff --git a/src/core/lib/transport/metadata_batch.c b/src/core/lib/transport/metadata_batch.c
index 84b5a74d513709dcdb2093b48dc793fab91756e5..b62ecc3aa6f86aa9b17741e11ed751d1af80058c 100644
--- a/src/core/lib/transport/metadata_batch.c
+++ b/src/core/lib/transport/metadata_batch.c
@@ -72,10 +72,11 @@ void grpc_metadata_batch_init(grpc_metadata_batch *batch) {
   batch->deadline = gpr_inf_future(GPR_CLOCK_REALTIME);
 }
 
-void grpc_metadata_batch_destroy(grpc_metadata_batch *batch) {
+void grpc_metadata_batch_destroy(grpc_exec_ctx *exec_ctx,
+                                 grpc_metadata_batch *batch) {
   grpc_linked_mdelem *l;
   for (l = batch->list.head; l; l = l->next) {
-    GRPC_MDELEM_UNREF(l->md);
+    GRPC_MDELEM_UNREF(exec_ctx, l->md);
   }
 }
 
@@ -140,8 +141,10 @@ void grpc_metadata_batch_move(grpc_metadata_batch *dst,
   memset(src, 0, sizeof(grpc_metadata_batch));
 }
 
-void grpc_metadata_batch_filter(grpc_metadata_batch *batch,
-                                grpc_mdelem *(*filter)(void *user_data,
+void grpc_metadata_batch_filter(grpc_exec_ctx *exec_ctx,
+                                grpc_metadata_batch *batch,
+                                grpc_mdelem *(*filter)(grpc_exec_ctx *exec_ctx,
+                                                       void *user_data,
                                                        grpc_mdelem *elem),
                                 void *user_data) {
   grpc_linked_mdelem *l;
@@ -152,7 +155,7 @@ void grpc_metadata_batch_filter(grpc_metadata_batch *batch,
   assert_valid_list(&batch->list);
   for (l = batch->list.head; l; l = next) {
     grpc_mdelem *orig = l->md;
-    grpc_mdelem *filt = filter(user_data, orig);
+    grpc_mdelem *filt = filter(exec_ctx, user_data, orig);
     next = l->next;
     if (filt == NULL) {
       if (l->prev) {
@@ -168,9 +171,9 @@ void grpc_metadata_batch_filter(grpc_metadata_batch *batch,
         batch->list.tail = l->prev;
       }
       assert_valid_list(&batch->list);
-      GRPC_MDELEM_UNREF(l->md);
+      GRPC_MDELEM_UNREF(exec_ctx, l->md);
     } else if (filt != orig) {
-      GRPC_MDELEM_UNREF(orig);
+      GRPC_MDELEM_UNREF(exec_ctx, orig);
       l->md = filt;
     }
   }
@@ -179,13 +182,15 @@ void grpc_metadata_batch_filter(grpc_metadata_batch *batch,
   GPR_TIMER_END("grpc_metadata_batch_filter", 0);
 }
 
-static grpc_mdelem *no_metadata_for_you(void *user_data, grpc_mdelem *elem) {
+static grpc_mdelem *no_metadata_for_you(grpc_exec_ctx *exec_ctx,
+                                        void *user_data, grpc_mdelem *elem) {
   return NULL;
 }
 
-void grpc_metadata_batch_clear(grpc_metadata_batch *batch) {
+void grpc_metadata_batch_clear(grpc_exec_ctx *exec_ctx,
+                               grpc_metadata_batch *batch) {
   batch->deadline = gpr_inf_future(GPR_CLOCK_REALTIME);
-  grpc_metadata_batch_filter(batch, no_metadata_for_you, NULL);
+  grpc_metadata_batch_filter(exec_ctx, batch, no_metadata_for_you, NULL);
 }
 
 bool grpc_metadata_batch_is_empty(grpc_metadata_batch *batch) {
diff --git a/src/core/lib/transport/metadata_batch.h b/src/core/lib/transport/metadata_batch.h
index 7a9ccb4bc854b21238784e4eb7cdeb759ff8868f..c0bd5174abb97fe19efb7fa0697b2bccb2656780 100644
--- a/src/core/lib/transport/metadata_batch.h
+++ b/src/core/lib/transport/metadata_batch.h
@@ -68,8 +68,10 @@ typedef struct grpc_metadata_batch {
 } grpc_metadata_batch;
 
 void grpc_metadata_batch_init(grpc_metadata_batch *batch);
-void grpc_metadata_batch_destroy(grpc_metadata_batch *batch);
-void grpc_metadata_batch_clear(grpc_metadata_batch *batch);
+void grpc_metadata_batch_destroy(grpc_exec_ctx *exec_ctx,
+                                 grpc_metadata_batch *batch);
+void grpc_metadata_batch_clear(grpc_exec_ctx *exec_ctx,
+                               grpc_metadata_batch *batch);
 bool grpc_metadata_batch_is_empty(grpc_metadata_batch *batch);
 
 /* Returns the transport size of the batch. */
@@ -118,8 +120,10 @@ void grpc_metadata_batch_add_tail(grpc_metadata_batch *batch,
     The return value from \a filter will be substituted for the
     grpc_mdelem passed to \a filter. If \a filter returns NULL,
     the element will be moved to the garbage list. */
-void grpc_metadata_batch_filter(grpc_metadata_batch *batch,
-                                grpc_mdelem *(*filter)(void *user_data,
+void grpc_metadata_batch_filter(grpc_exec_ctx *exec_ctx,
+                                grpc_metadata_batch *batch,
+                                grpc_mdelem *(*filter)(grpc_exec_ctx *exec_ctx,
+                                                       void *user_data,
                                                        grpc_mdelem *elem),
                                 void *user_data);
 
diff --git a/src/core/lib/transport/method_config.c b/src/core/lib/transport/method_config.c
new file mode 100644
index 0000000000000000000000000000000000000000..25fb54b37dbfe8f61b65388de356a78cb5bded7a
--- /dev/null
+++ b/src/core/lib/transport/method_config.c
@@ -0,0 +1,347 @@
+//
+// 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/transport/method_config.h"
+
+#include <string.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/transport/mdstr_hash_table.h"
+#include "src/core/lib/transport/metadata.h"
+
+//
+// grpc_method_config
+//
+
+// bool vtable
+
+static void* bool_copy(void* valuep) {
+  bool value = *(bool*)valuep;
+  bool* new_value = gpr_malloc(sizeof(bool));
+  *new_value = value;
+  return new_value;
+}
+
+static int bool_cmp(void* v1, void* v2) {
+  bool b1 = *(bool*)v1;
+  bool b2 = *(bool*)v2;
+  if (!b1 && b2) return -1;
+  if (b1 && !b2) return 1;
+  return 0;
+}
+
+static void free_mem(grpc_exec_ctx* exec_ctx, void* p) { gpr_free(p); }
+
+static grpc_mdstr_hash_table_vtable bool_vtable = {free_mem, bool_copy,
+                                                   bool_cmp};
+
+// timespec vtable
+
+static void* timespec_copy(void* valuep) {
+  gpr_timespec value = *(gpr_timespec*)valuep;
+  gpr_timespec* new_value = gpr_malloc(sizeof(gpr_timespec));
+  *new_value = value;
+  return new_value;
+}
+
+static int timespec_cmp(void* v1, void* v2) {
+  return gpr_time_cmp(*(gpr_timespec*)v1, *(gpr_timespec*)v2);
+}
+
+static grpc_mdstr_hash_table_vtable timespec_vtable = {free_mem, timespec_copy,
+                                                       timespec_cmp};
+
+// int32 vtable
+
+static void* int32_copy(void* valuep) {
+  int32_t value = *(int32_t*)valuep;
+  int32_t* new_value = gpr_malloc(sizeof(int32_t));
+  *new_value = value;
+  return new_value;
+}
+
+static int int32_cmp(void* v1, void* v2) {
+  int32_t i1 = *(int32_t*)v1;
+  int32_t i2 = *(int32_t*)v2;
+  if (i1 < i2) return -1;
+  if (i1 > i2) return 1;
+  return 0;
+}
+
+static grpc_mdstr_hash_table_vtable int32_vtable = {free_mem, int32_copy,
+                                                    int32_cmp};
+
+// Hash table keys.
+#define GRPC_METHOD_CONFIG_WAIT_FOR_READY "grpc.wait_for_ready"  // bool
+#define GRPC_METHOD_CONFIG_TIMEOUT "grpc.timeout"                // gpr_timespec
+#define GRPC_METHOD_CONFIG_MAX_REQUEST_MESSAGE_BYTES \
+  "grpc.max_request_message_bytes"  // int32
+#define GRPC_METHOD_CONFIG_MAX_RESPONSE_MESSAGE_BYTES \
+  "grpc.max_response_message_bytes"  // int32
+
+struct grpc_method_config {
+  grpc_mdstr_hash_table* table;
+  grpc_mdstr* wait_for_ready_key;
+  grpc_mdstr* timeout_key;
+  grpc_mdstr* max_request_message_bytes_key;
+  grpc_mdstr* max_response_message_bytes_key;
+};
+
+grpc_method_config* grpc_method_config_create(
+    bool* wait_for_ready, gpr_timespec* timeout,
+    int32_t* max_request_message_bytes, int32_t* max_response_message_bytes) {
+  grpc_method_config* method_config = gpr_malloc(sizeof(grpc_method_config));
+  memset(method_config, 0, sizeof(grpc_method_config));
+  method_config->wait_for_ready_key =
+      grpc_mdstr_from_string(GRPC_METHOD_CONFIG_WAIT_FOR_READY);
+  method_config->timeout_key =
+      grpc_mdstr_from_string(GRPC_METHOD_CONFIG_TIMEOUT);
+  method_config->max_request_message_bytes_key =
+      grpc_mdstr_from_string(GRPC_METHOD_CONFIG_MAX_REQUEST_MESSAGE_BYTES);
+  method_config->max_response_message_bytes_key =
+      grpc_mdstr_from_string(GRPC_METHOD_CONFIG_MAX_RESPONSE_MESSAGE_BYTES);
+  grpc_mdstr_hash_table_entry entries[4];
+  size_t num_entries = 0;
+  if (wait_for_ready != NULL) {
+    entries[num_entries].key = method_config->wait_for_ready_key;
+    entries[num_entries].value = wait_for_ready;
+    entries[num_entries].vtable = &bool_vtable;
+    ++num_entries;
+  }
+  if (timeout != NULL) {
+    entries[num_entries].key = method_config->timeout_key;
+    entries[num_entries].value = timeout;
+    entries[num_entries].vtable = &timespec_vtable;
+    ++num_entries;
+  }
+  if (max_request_message_bytes != NULL) {
+    entries[num_entries].key = method_config->max_request_message_bytes_key;
+    entries[num_entries].value = max_request_message_bytes;
+    entries[num_entries].vtable = &int32_vtable;
+    ++num_entries;
+  }
+  if (max_response_message_bytes != NULL) {
+    entries[num_entries].key = method_config->max_response_message_bytes_key;
+    entries[num_entries].value = max_response_message_bytes;
+    entries[num_entries].vtable = &int32_vtable;
+    ++num_entries;
+  }
+  method_config->table = grpc_mdstr_hash_table_create(num_entries, entries);
+  return method_config;
+}
+
+grpc_method_config* grpc_method_config_ref(grpc_method_config* method_config) {
+  grpc_mdstr_hash_table_ref(method_config->table);
+  return method_config;
+}
+
+void grpc_method_config_unref(grpc_exec_ctx* exec_ctx,
+                              grpc_method_config* method_config) {
+  if (grpc_mdstr_hash_table_unref(exec_ctx, method_config->table)) {
+    GRPC_MDSTR_UNREF(exec_ctx, method_config->wait_for_ready_key);
+    GRPC_MDSTR_UNREF(exec_ctx, method_config->timeout_key);
+    GRPC_MDSTR_UNREF(exec_ctx, method_config->max_request_message_bytes_key);
+    GRPC_MDSTR_UNREF(exec_ctx, method_config->max_response_message_bytes_key);
+    gpr_free(method_config);
+  }
+}
+
+int grpc_method_config_cmp(const grpc_method_config* method_config1,
+                           const grpc_method_config* method_config2) {
+  return grpc_mdstr_hash_table_cmp(method_config1->table,
+                                   method_config2->table);
+}
+
+const bool* grpc_method_config_get_wait_for_ready(
+    const grpc_method_config* method_config) {
+  return grpc_mdstr_hash_table_get(method_config->table,
+                                   method_config->wait_for_ready_key);
+}
+
+const gpr_timespec* grpc_method_config_get_timeout(
+    const grpc_method_config* method_config) {
+  return grpc_mdstr_hash_table_get(method_config->table,
+                                   method_config->timeout_key);
+}
+
+const int32_t* grpc_method_config_get_max_request_message_bytes(
+    const grpc_method_config* method_config) {
+  return grpc_mdstr_hash_table_get(
+      method_config->table, method_config->max_request_message_bytes_key);
+}
+
+const int32_t* grpc_method_config_get_max_response_message_bytes(
+    const grpc_method_config* method_config) {
+  return grpc_mdstr_hash_table_get(
+      method_config->table, method_config->max_response_message_bytes_key);
+}
+
+//
+// grpc_method_config_table
+//
+
+static void method_config_unref(grpc_exec_ctx* exec_ctx, void* valuep) {
+  grpc_method_config_unref(exec_ctx, valuep);
+}
+
+static void* method_config_ref(void* valuep) {
+  return grpc_method_config_ref(valuep);
+}
+
+static int method_config_cmp(void* valuep1, void* valuep2) {
+  return grpc_method_config_cmp(valuep1, valuep2);
+}
+
+static const grpc_mdstr_hash_table_vtable method_config_table_vtable = {
+    method_config_unref, method_config_ref, method_config_cmp};
+
+grpc_method_config_table* grpc_method_config_table_create(
+    size_t num_entries, grpc_method_config_table_entry* entries) {
+  grpc_mdstr_hash_table_entry* hash_table_entries =
+      gpr_malloc(sizeof(grpc_mdstr_hash_table_entry) * num_entries);
+  for (size_t i = 0; i < num_entries; ++i) {
+    hash_table_entries[i].key = entries[i].method_name;
+    hash_table_entries[i].value = entries[i].method_config;
+    hash_table_entries[i].vtable = &method_config_table_vtable;
+  }
+  grpc_method_config_table* method_config_table =
+      grpc_mdstr_hash_table_create(num_entries, hash_table_entries);
+  gpr_free(hash_table_entries);
+  return method_config_table;
+}
+
+grpc_method_config_table* grpc_method_config_table_ref(
+    grpc_method_config_table* table) {
+  return grpc_mdstr_hash_table_ref(table);
+}
+
+void grpc_method_config_table_unref(grpc_exec_ctx* exec_ctx,
+                                    grpc_method_config_table* table) {
+  grpc_mdstr_hash_table_unref(exec_ctx, table);
+}
+
+int grpc_method_config_table_cmp(const grpc_method_config_table* table1,
+                                 const grpc_method_config_table* table2) {
+  return grpc_mdstr_hash_table_cmp(table1, table2);
+}
+
+void* grpc_method_config_table_get(grpc_exec_ctx* exec_ctx,
+                                   const grpc_mdstr_hash_table* table,
+                                   const grpc_mdstr* path) {
+  void* value = grpc_mdstr_hash_table_get(table, path);
+  // If we didn't find a match for the path, try looking for a wildcard
+  // entry (i.e., change "/service/method" to "/service/*").
+  if (value == NULL) {
+    const char* path_str = grpc_mdstr_as_c_string(path);
+    const char* sep = strrchr(path_str, '/') + 1;
+    const size_t len = (size_t)(sep - path_str);
+    char* buf = gpr_malloc(len + 2);  // '*' and NUL
+    memcpy(buf, path_str, len);
+    buf[len] = '*';
+    buf[len + 1] = '\0';
+    grpc_mdstr* wildcard_path = grpc_mdstr_from_string(buf);
+    gpr_free(buf);
+    value = grpc_mdstr_hash_table_get(table, wildcard_path);
+    GRPC_MDSTR_UNREF(exec_ctx, wildcard_path);
+  }
+  return value;
+}
+
+static void* copy_arg(void* p) { return grpc_method_config_table_ref(p); }
+
+static void destroy_arg(grpc_exec_ctx* exec_ctx, void* p) {
+  grpc_method_config_table_unref(exec_ctx, p);
+}
+
+static int cmp_arg(void* p1, void* p2) {
+  return grpc_method_config_table_cmp(p1, p2);
+}
+
+static grpc_arg_pointer_vtable arg_vtable = {copy_arg, destroy_arg, cmp_arg};
+
+grpc_arg grpc_method_config_table_create_channel_arg(
+    grpc_method_config_table* table) {
+  grpc_arg arg;
+  arg.type = GRPC_ARG_POINTER;
+  arg.key = GRPC_ARG_SERVICE_CONFIG;
+  arg.value.pointer.p = table;
+  arg.value.pointer.vtable = &arg_vtable;
+  return arg;
+}
+
+// State used by convert_entry() below.
+typedef struct conversion_state {
+  void* (*convert_value)(const grpc_method_config* method_config);
+  const grpc_mdstr_hash_table_vtable* vtable;
+  size_t num_entries;
+  grpc_mdstr_hash_table_entry* entries;
+} conversion_state;
+
+// A function to be passed to grpc_mdstr_hash_table_iterate() to create
+// a copy of the entries.
+static void convert_entry(const grpc_mdstr_hash_table_entry* entry,
+                          void* user_data) {
+  conversion_state* state = user_data;
+  state->entries[state->num_entries].key = GRPC_MDSTR_REF(entry->key);
+  state->entries[state->num_entries].value = state->convert_value(entry->value);
+  state->entries[state->num_entries].vtable = state->vtable;
+  ++state->num_entries;
+}
+
+grpc_mdstr_hash_table* grpc_method_config_table_convert(
+    grpc_exec_ctx* exec_ctx, const grpc_method_config_table* table,
+    void* (*convert_value)(const grpc_method_config* method_config),
+    const grpc_mdstr_hash_table_vtable* vtable) {
+  // Create an array of the entries in the table with converted values.
+  conversion_state state;
+  state.convert_value = convert_value;
+  state.vtable = vtable;
+  state.num_entries = 0;
+  state.entries = gpr_malloc(sizeof(grpc_mdstr_hash_table_entry) *
+                             grpc_mdstr_hash_table_num_entries(table));
+  grpc_mdstr_hash_table_iterate(table, convert_entry, &state);
+  // Create a new table based on the array we just constructed.
+  grpc_mdstr_hash_table* new_table =
+      grpc_mdstr_hash_table_create(state.num_entries, state.entries);
+  // Clean up the array.
+  for (size_t i = 0; i < state.num_entries; ++i) {
+    GRPC_MDSTR_UNREF(exec_ctx, state.entries[i].key);
+    vtable->destroy_value(exec_ctx, state.entries[i].value);
+  }
+  gpr_free(state.entries);
+  // Return the new table.
+  return new_table;
+}
diff --git a/src/core/lib/transport/method_config.h b/src/core/lib/transport/method_config.h
new file mode 100644
index 0000000000000000000000000000000000000000..d17a493fd4d99cce28dac4723a71ee9713ee31c2
--- /dev/null
+++ b/src/core/lib/transport/method_config.h
@@ -0,0 +1,139 @@
+//
+// Copyright 2016, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H
+#define GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H
+
+#include <stdbool.h>
+
+#include <grpc/impl/codegen/gpr_types.h>
+#include <grpc/impl/codegen/grpc_types.h>
+
+#include "src/core/lib/transport/mdstr_hash_table.h"
+#include "src/core/lib/transport/metadata.h"
+
+/// Per-method configuration.
+typedef struct grpc_method_config grpc_method_config;
+
+/// Creates a grpc_method_config with the specified parameters.
+/// Any parameter may be NULL to indicate that the value is unset.
+///
+/// \a wait_for_ready indicates whether the client should wait until the
+/// request deadline for the channel to become ready, even if there is a
+/// temporary failure before the deadline while attempting to connect.
+///
+/// \a timeout indicates the timeout for calls.
+///
+/// \a max_request_message_bytes and \a max_response_message_bytes
+/// indicate the maximum sizes of the request (checked when sending) and
+/// response (checked when receiving) messages.
+grpc_method_config* grpc_method_config_create(
+    bool* wait_for_ready, gpr_timespec* timeout,
+    int32_t* max_request_message_bytes, int32_t* max_response_message_bytes);
+
+grpc_method_config* grpc_method_config_ref(grpc_method_config* method_config);
+void grpc_method_config_unref(grpc_exec_ctx* exec_ctx,
+                              grpc_method_config* method_config);
+
+/// Compares two grpc_method_configs.
+/// The sort order is stable but undefined.
+int grpc_method_config_cmp(const grpc_method_config* method_config1,
+                           const grpc_method_config* method_config2);
+
+/// These methods return NULL if the requested field is unset.
+/// The caller does NOT take ownership of the result.
+const bool* grpc_method_config_get_wait_for_ready(
+    const grpc_method_config* method_config);
+const gpr_timespec* grpc_method_config_get_timeout(
+    const grpc_method_config* method_config);
+const int32_t* grpc_method_config_get_max_request_message_bytes(
+    const grpc_method_config* method_config);
+const int32_t* grpc_method_config_get_max_response_message_bytes(
+    const grpc_method_config* method_config);
+
+/// A table of method configs.
+typedef grpc_mdstr_hash_table grpc_method_config_table;
+
+typedef struct grpc_method_config_table_entry {
+  /// The name is of one of the following forms:
+  ///   service/method -- specifies exact service and method name
+  ///   service/*      -- matches all methods for the specified service
+  grpc_mdstr* method_name;
+  grpc_method_config* method_config;
+} grpc_method_config_table_entry;
+
+/// Takes new references to all keys and values in \a entries.
+grpc_method_config_table* grpc_method_config_table_create(
+    size_t num_entries, grpc_method_config_table_entry* entries);
+
+grpc_method_config_table* grpc_method_config_table_ref(
+    grpc_method_config_table* table);
+void grpc_method_config_table_unref(grpc_exec_ctx* exec_ctx,
+                                    grpc_method_config_table* table);
+
+/// Compares two grpc_method_config_tables.
+/// The sort order is stable but undefined.
+int grpc_method_config_table_cmp(const grpc_method_config_table* table1,
+                                 const grpc_method_config_table* table2);
+
+/// Gets the method config for the specified \a path, which should be of
+/// the form "/service/method".
+/// Returns NULL if the method has no config.
+/// Caller does NOT own a reference to the result.
+///
+/// Note: This returns a void* instead of a grpc_method_config* so that
+/// it can also be used for tables constructed via
+/// grpc_method_config_table_convert().
+void* grpc_method_config_table_get(grpc_exec_ctx* exec_ctx,
+                                   const grpc_mdstr_hash_table* table,
+                                   const grpc_mdstr* path);
+
+/// Returns a channel arg containing \a table.
+grpc_arg grpc_method_config_table_create_channel_arg(
+    grpc_method_config_table* table);
+
+/// Generates a new table from \a table whose values are converted to a
+/// new form via the \a convert_value function.  The new table will use
+/// \a vtable for its values.
+///
+/// This is generally used to convert the table's value type from
+/// grpc_method_config to a simple struct containing only the parameters
+/// relevant to a particular filter, thus avoiding the need for a hash
+/// table lookup on the fast path.  In that scenario, \a convert_value
+/// will return a new instance of the struct containing the values from
+/// the grpc_method_config, and \a vtable provides the methods for
+/// operating on the struct type.
+grpc_mdstr_hash_table* grpc_method_config_table_convert(
+    grpc_exec_ctx* exec_ctx, const grpc_method_config_table* table,
+    void* (*convert_value)(const grpc_method_config* method_config),
+    const grpc_mdstr_hash_table_vtable* vtable);
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H */
diff --git a/src/core/lib/transport/pid_controller.h b/src/core/lib/transport/pid_controller.h
index 059b5b08346a48b1f9d13e9fb07584799e3a63f3..83c82d64713e8df6024d66f1cdbf5ea4b7bccc04 100644
--- a/src/core/lib/transport/pid_controller.h
+++ b/src/core/lib/transport/pid_controller.h
@@ -61,4 +61,4 @@ void grpc_pid_controller_reset(grpc_pid_controller *pid_controller);
 double grpc_pid_controller_update(grpc_pid_controller *pid_controller,
                                   double error, double dt);
 
-#endif
+#endif /* GRPC_CORE_LIB_TRANSPORT_PID_CONTROLLER_H */
diff --git a/src/core/lib/transport/service_config.c b/src/core/lib/transport/service_config.c
index 2e2b59e3f760c7b2fdd5103653bb945734248cb7..552d3ec85687dd20c5dd2d70a935e88705704bf9 100644
--- a/src/core/lib/transport/service_config.c
+++ b/src/core/lib/transport/service_config.c
@@ -146,7 +146,8 @@ static char* parse_json_method_name(grpc_json* json) {
 // each name found, incrementing \a idx for each entry added.
 // Returns false on error.
 static bool parse_json_method_config(
-    grpc_json* json, void* (*create_value)(const grpc_json* method_config_json),
+    grpc_exec_ctx* exec_ctx, grpc_json* json,
+    void* (*create_value)(const grpc_json* method_config_json),
     const grpc_mdstr_hash_table_vtable* vtable,
     grpc_mdstr_hash_table_entry* entries, size_t* idx) {
   // Construct value.
@@ -176,13 +177,13 @@ static bool parse_json_method_config(
   }
   success = true;
 done:
-  vtable->destroy_value(method_config);
+  vtable->destroy_value(exec_ctx, method_config);
   gpr_strvec_destroy(&paths);
   return success;
 }
 
 grpc_mdstr_hash_table* grpc_service_config_create_method_config_table(
-    const grpc_service_config* service_config,
+    grpc_exec_ctx* exec_ctx, const grpc_service_config* service_config,
     void* (*create_value)(const grpc_json* method_config_json),
     const grpc_mdstr_hash_table_vtable* vtable) {
   const grpc_json* json = service_config->json_tree;
@@ -205,8 +206,8 @@ grpc_mdstr_hash_table* grpc_service_config_create_method_config_table(
       size_t idx = 0;
       for (grpc_json* method = field->child; method != NULL;
            method = method->next) {
-        if (!parse_json_method_config(method, create_value, vtable, entries,
-                                      &idx)) {
+        if (!parse_json_method_config(exec_ctx, method, create_value, vtable,
+                                      entries, &idx)) {
           return NULL;
         }
       }
@@ -219,15 +220,16 @@ grpc_mdstr_hash_table* grpc_service_config_create_method_config_table(
     method_config_table = grpc_mdstr_hash_table_create(num_entries, entries);
     // Clean up.
     for (size_t i = 0; i < num_entries; ++i) {
-      GRPC_MDSTR_UNREF(entries[i].key);
-      vtable->destroy_value(entries[i].value);
+      GRPC_MDSTR_UNREF(exec_ctx, entries[i].key);
+      vtable->destroy_value(exec_ctx, entries[i].value);
     }
     gpr_free(entries);
   }
   return method_config_table;
 }
 
-void* grpc_method_config_table_get(const grpc_mdstr_hash_table* table,
+void* grpc_method_config_table_get(grpc_exec_ctx* exec_ctx,
+                                   const grpc_mdstr_hash_table* table,
                                    const grpc_mdstr* path) {
   void* value = grpc_mdstr_hash_table_get(table, path);
   // If we didn't find a match for the path, try looking for a wildcard
@@ -243,7 +245,7 @@ void* grpc_method_config_table_get(const grpc_mdstr_hash_table* table,
     grpc_mdstr* wildcard_path = grpc_mdstr_from_string(buf);
     gpr_free(buf);
     value = grpc_mdstr_hash_table_get(table, wildcard_path);
-    GRPC_MDSTR_UNREF(wildcard_path);
+    GRPC_MDSTR_UNREF(exec_ctx, wildcard_path);
   }
   return value;
 }
diff --git a/src/core/lib/transport/service_config.h b/src/core/lib/transport/service_config.h
index 2ffe4751938f7e14ba9843e5b38269388d100973..f0897170fa62466c564c0a4e803b87f3c8d74ed7 100644
--- a/src/core/lib/transport/service_config.h
+++ b/src/core/lib/transport/service_config.h
@@ -54,7 +54,7 @@ const char* grpc_service_config_get_lb_policy_name(
 /// \a vtable provides methods used to manage the values.
 /// Returns NULL on error.
 grpc_mdstr_hash_table* grpc_service_config_create_method_config_table(
-    const grpc_service_config* service_config,
+    grpc_exec_ctx* exec_ctx, const grpc_service_config* service_config,
     void* (*create_value)(const grpc_json* method_config_json),
     const grpc_mdstr_hash_table_vtable* vtable);
 
@@ -64,7 +64,8 @@ grpc_mdstr_hash_table* grpc_service_config_create_method_config_table(
 /// the form "/service/method".
 /// Returns NULL if the method has no config.
 /// Caller does NOT own a reference to the result.
-void* grpc_method_config_table_get(const grpc_mdstr_hash_table* table,
+void* grpc_method_config_table_get(grpc_exec_ctx* exec_ctx,
+                                   const grpc_mdstr_hash_table* table,
                                    const grpc_mdstr* path);
 
 #endif /* GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H */
diff --git a/src/core/lib/transport/transport.c b/src/core/lib/transport/transport.c
index b448126da8909f592b16c23264095ea3455c8b50..055edbb39fcc5526da8e3fc8fbc6153ff4c4c5cb 100644
--- a/src/core/lib/transport/transport.c
+++ b/src/core/lib/transport/transport.c
@@ -40,6 +40,7 @@
 #include <grpc/support/log.h>
 #include <grpc/support/sync.h>
 
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/transport/transport_impl.h"
@@ -68,7 +69,7 @@ void grpc_stream_unref(grpc_exec_ctx *exec_ctx,
                        grpc_stream_refcount *refcount) {
 #endif
   if (gpr_unref(&refcount->refs)) {
-    grpc_exec_ctx_sched(exec_ctx, &refcount->destroy, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, &refcount->destroy, GRPC_ERROR_NONE);
   }
 }
 
@@ -82,7 +83,7 @@ void grpc_stream_ref_init(grpc_stream_refcount *refcount, int initial_refs,
                           grpc_iomgr_cb_func cb, void *cb_arg) {
 #endif
   gpr_ref_init(&refcount->refs, initial_refs);
-  grpc_closure_init(&refcount->destroy, cb, cb_arg);
+  grpc_closure_init(&refcount->destroy, cb, cb_arg, grpc_schedule_on_exec_ctx);
 }
 
 static void move64(uint64_t *from, uint64_t *to) {
@@ -168,11 +169,10 @@ grpc_endpoint *grpc_transport_get_endpoint(grpc_exec_ctx *exec_ctx,
 void grpc_transport_stream_op_finish_with_failure(grpc_exec_ctx *exec_ctx,
                                                   grpc_transport_stream_op *op,
                                                   grpc_error *error) {
-  grpc_exec_ctx_sched(exec_ctx, op->recv_message_ready, GRPC_ERROR_REF(error),
-                      NULL);
-  grpc_exec_ctx_sched(exec_ctx, op->recv_initial_metadata_ready,
-                      GRPC_ERROR_REF(error), NULL);
-  grpc_exec_ctx_sched(exec_ctx, op->on_complete, error, NULL);
+  grpc_closure_sched(exec_ctx, op->recv_message_ready, GRPC_ERROR_REF(error));
+  grpc_closure_sched(exec_ctx, op->recv_initial_metadata_ready,
+                     GRPC_ERROR_REF(error));
+  grpc_closure_sched(exec_ctx, op->on_complete, error);
 }
 
 typedef struct {
@@ -196,7 +196,8 @@ static void add_error(grpc_transport_stream_op *op, grpc_error **which,
   cmd = gpr_malloc(sizeof(*cmd));
   cmd->error = error;
   cmd->then_call = op->on_complete;
-  grpc_closure_init(&cmd->closure, free_message, cmd);
+  grpc_closure_init(&cmd->closure, free_message, cmd,
+                    grpc_schedule_on_exec_ctx);
   op->on_complete = &cmd->closure;
   *which = error;
 }
@@ -212,12 +213,12 @@ void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op,
 }
 
 void grpc_transport_stream_op_add_cancellation_with_message(
-    grpc_transport_stream_op *op, grpc_status_code status,
-    grpc_slice *optional_message) {
+    grpc_exec_ctx *exec_ctx, grpc_transport_stream_op *op,
+    grpc_status_code status, grpc_slice *optional_message) {
   GPR_ASSERT(status != GRPC_STATUS_OK);
   if (op->cancel_error != GRPC_ERROR_NONE) {
     if (optional_message) {
-      grpc_slice_unref(*optional_message);
+      grpc_slice_unref_internal(exec_ctx, *optional_message);
     }
     return;
   }
@@ -227,7 +228,7 @@ void grpc_transport_stream_op_add_cancellation_with_message(
     error = grpc_error_set_str(GRPC_ERROR_CREATE(msg),
                                GRPC_ERROR_STR_GRPC_MESSAGE, msg);
     gpr_free(msg);
-    grpc_slice_unref(*optional_message);
+    grpc_slice_unref_internal(exec_ctx, *optional_message);
   } else {
     error = GRPC_ERROR_CREATE("Call cancelled");
   }
@@ -235,14 +236,15 @@ void grpc_transport_stream_op_add_cancellation_with_message(
   add_error(op, &op->cancel_error, error);
 }
 
-void grpc_transport_stream_op_add_close(grpc_transport_stream_op *op,
+void grpc_transport_stream_op_add_close(grpc_exec_ctx *exec_ctx,
+                                        grpc_transport_stream_op *op,
                                         grpc_status_code status,
                                         grpc_slice *optional_message) {
   GPR_ASSERT(status != GRPC_STATUS_OK);
   if (op->cancel_error != GRPC_ERROR_NONE ||
       op->close_error != GRPC_ERROR_NONE) {
     if (optional_message) {
-      grpc_slice_unref(*optional_message);
+      grpc_slice_unref_internal(exec_ctx, *optional_message);
     }
     return;
   }
@@ -252,7 +254,7 @@ void grpc_transport_stream_op_add_close(grpc_transport_stream_op *op,
     error = grpc_error_set_str(GRPC_ERROR_CREATE(msg),
                                GRPC_ERROR_STR_GRPC_MESSAGE, msg);
     gpr_free(msg);
-    grpc_slice_unref(*optional_message);
+    grpc_slice_unref_internal(exec_ctx, *optional_message);
   } else {
     error = GRPC_ERROR_CREATE("Call force closed");
   }
@@ -269,14 +271,14 @@ typedef struct {
 static void destroy_made_transport_op(grpc_exec_ctx *exec_ctx, void *arg,
                                       grpc_error *error) {
   made_transport_op *op = arg;
-  grpc_exec_ctx_sched(exec_ctx, op->inner_on_complete, GRPC_ERROR_REF(error),
-                      NULL);
+  grpc_closure_sched(exec_ctx, op->inner_on_complete, GRPC_ERROR_REF(error));
   gpr_free(op);
 }
 
 grpc_transport_op *grpc_make_transport_op(grpc_closure *on_complete) {
   made_transport_op *op = gpr_malloc(sizeof(*op));
-  grpc_closure_init(&op->outer_on_complete, destroy_made_transport_op, op);
+  grpc_closure_init(&op->outer_on_complete, destroy_made_transport_op, op,
+                    grpc_schedule_on_exec_ctx);
   op->inner_on_complete = on_complete;
   memset(&op->op, 0, sizeof(op->op));
   op->op.on_consumed = &op->outer_on_complete;
@@ -292,8 +294,7 @@ typedef struct {
 static void destroy_made_transport_stream_op(grpc_exec_ctx *exec_ctx, void *arg,
                                              grpc_error *error) {
   made_transport_stream_op *op = arg;
-  grpc_exec_ctx_sched(exec_ctx, op->inner_on_complete, GRPC_ERROR_REF(error),
-                      NULL);
+  grpc_closure_sched(exec_ctx, op->inner_on_complete, GRPC_ERROR_REF(error));
   gpr_free(op);
 }
 
@@ -301,7 +302,7 @@ grpc_transport_stream_op *grpc_make_transport_stream_op(
     grpc_closure *on_complete) {
   made_transport_stream_op *op = gpr_malloc(sizeof(*op));
   grpc_closure_init(&op->outer_on_complete, destroy_made_transport_stream_op,
-                    op);
+                    op, grpc_schedule_on_exec_ctx);
   op->inner_on_complete = on_complete;
   memset(&op->op, 0, sizeof(op->op));
   op->op.on_complete = &op->outer_on_complete;
diff --git a/src/core/lib/transport/transport.h b/src/core/lib/transport/transport.h
index 96c26749c878f7eeca4e4c07b8ab53c12127efd6..d1281830aa0394d594d54a02f18ea6656093c832 100644
--- a/src/core/lib/transport/transport.h
+++ b/src/core/lib/transport/transport.h
@@ -249,10 +249,11 @@ void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op,
                                                grpc_status_code status);
 
 void grpc_transport_stream_op_add_cancellation_with_message(
-    grpc_transport_stream_op *op, grpc_status_code status,
-    grpc_slice *optional_message);
+    grpc_exec_ctx *exec_ctx, grpc_transport_stream_op *op,
+    grpc_status_code status, grpc_slice *optional_message);
 
-void grpc_transport_stream_op_add_close(grpc_transport_stream_op *op,
+void grpc_transport_stream_op_add_close(grpc_exec_ctx *exec_ctx,
+                                        grpc_transport_stream_op *op,
                                         grpc_status_code status,
                                         grpc_slice *optional_message);
 
diff --git a/src/core/lib/tsi/README.md b/src/core/lib/tsi/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..3ca3c1ef38cc53080e2abf6e532203060ddf88fa
--- /dev/null
+++ b/src/core/lib/tsi/README.md
@@ -0,0 +1,2 @@
+# Transport Security Interface
+An abstraction library over crypto and auth modules (typically OpenSSL)
diff --git a/src/cpp/common/channel_arguments.cc b/src/cpp/common/channel_arguments.cc
index 0301b5b8ec813f8baa4d47b0d626a6f5955ceb7f..1fdd10613033f1f9208421f05bddc2121df28151 100644
--- a/src/cpp/common/channel_arguments.cc
+++ b/src/cpp/common/channel_arguments.cc
@@ -38,8 +38,11 @@
 #include <grpc++/resource_quota.h>
 #include <grpc/impl/codegen/grpc_types.h>
 #include <grpc/support/log.h>
+extern "C" {
 #include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/iomgr/socket_mutator.h"
+}
 namespace grpc {
 
 ChannelArguments::ChannelArguments() {
@@ -94,13 +97,15 @@ void ChannelArguments::SetSocketMutator(grpc_socket_mutator* mutator) {
   }
   grpc_arg mutator_arg = grpc_socket_mutator_to_arg(mutator);
   bool replaced = false;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   for (auto it = args_.begin(); it != args_.end(); ++it) {
     if (it->type == mutator_arg.type &&
         grpc::string(it->key) == grpc::string(mutator_arg.key)) {
-      it->value.pointer.vtable->destroy(it->value.pointer.p);
+      it->value.pointer.vtable->destroy(&exec_ctx, it->value.pointer.p);
       it->value.pointer = mutator_arg.value.pointer;
     }
   }
+  grpc_exec_ctx_finish(&exec_ctx);
   if (!replaced) {
     args_.push_back(mutator_arg);
   }
diff --git a/src/cpp/common/channel_filter.cc b/src/cpp/common/channel_filter.cc
index ad2c0f22950436aebc7c6f3ef779d1eb6e7b374c..c0dc9dd63e5a598a155f36b76ee4724974cafbd4 100644
--- a/src/cpp/common/channel_filter.cc
+++ b/src/cpp/common/channel_filter.cc
@@ -40,11 +40,12 @@ namespace grpc {
 
 // MetadataBatch
 
-grpc_linked_mdelem *MetadataBatch::AddMetadata(const string &key,
+grpc_linked_mdelem *MetadataBatch::AddMetadata(grpc_exec_ctx *exec_ctx,
+                                               const string &key,
                                                const string &value) {
   grpc_linked_mdelem *storage = new grpc_linked_mdelem;
   memset(storage, 0, sizeof(grpc_linked_mdelem));
-  storage->md = grpc_mdelem_from_strings(key.c_str(), value.c_str());
+  storage->md = grpc_mdelem_from_strings(exec_ctx, key.c_str(), value.c_str());
   grpc_metadata_batch_link_head(batch_, storage);
   return storage;
 }
@@ -89,7 +90,8 @@ std::vector<FilterRecord> *channel_filters;
 
 namespace {
 
-bool MaybeAddFilter(grpc_channel_stack_builder *builder, void *arg) {
+bool MaybeAddFilter(grpc_exec_ctx *exec_ctx,
+                    grpc_channel_stack_builder *builder, void *arg) {
   const FilterRecord &filter = *(FilterRecord *)arg;
   if (filter.include_filter) {
     const grpc_channel_args *args =
diff --git a/src/cpp/common/channel_filter.h b/src/cpp/common/channel_filter.h
index e420efc71c9b089701501789e1a070aa8de9fdf5..5de8f5e4631874018e74d31a1caa8a127d01d468 100644
--- a/src/cpp/common/channel_filter.h
+++ b/src/cpp/common/channel_filter.h
@@ -70,7 +70,8 @@ class MetadataBatch {
   /// Adds metadata and returns the newly allocated storage.
   /// The caller takes ownership of the result, which must exist for the
   /// lifetime of the gRPC call.
-  grpc_linked_mdelem *AddMetadata(const string &key, const string &value);
+  grpc_linked_mdelem *AddMetadata(grpc_exec_ctx *exec_ctx, const string &key,
+                                  const string &value);
 
   class const_iterator : public std::iterator<std::bidirectional_iterator_tag,
                                               const grpc_mdelem> {
@@ -216,12 +217,13 @@ class TransportStreamOp {
 /// Represents channel data.
 class ChannelData {
  public:
-  virtual ~ChannelData() {
-    if (peer_) gpr_free((void *)peer_);
-  }
+  virtual ~ChannelData() {}
 
-  /// Caller does NOT take ownership of result.
-  const char *peer() const { return peer_; }
+  /// Initializes the call data.
+  virtual grpc_error *Init(grpc_exec_ctx *exec_ctx,
+                           grpc_channel_element_args *args) {
+    return GRPC_ERROR_NONE;
+  }
 
   // TODO(roth): Find a way to avoid passing elem into these methods.
 
@@ -232,11 +234,7 @@ class ChannelData {
                        const grpc_channel_info *channel_info);
 
  protected:
-  /// Takes ownership of \a peer.
-  ChannelData(const grpc_channel_args &args, const char *peer) : peer_(peer) {}
-
- private:
-  const char *peer_;
+  ChannelData() {}
 };
 
 /// Represents call data.
@@ -245,7 +243,10 @@ class CallData {
   virtual ~CallData() {}
 
   /// Initializes the call data.
-  virtual grpc_error *Init() { return GRPC_ERROR_NONE; }
+  virtual grpc_error *Init(grpc_exec_ctx *exec_ctx, ChannelData *channel_data,
+                           grpc_call_element_args *args) {
+    return GRPC_ERROR_NONE;
+  }
 
   // TODO(roth): Find a way to avoid passing elem into these methods.
 
@@ -263,7 +264,7 @@ class CallData {
   virtual char *GetPeer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem);
 
  protected:
-  explicit CallData(const ChannelData &) {}
+  CallData() {}
 };
 
 namespace internal {
@@ -276,15 +277,11 @@ class ChannelFilter final {
  public:
   static const size_t channel_data_size = sizeof(ChannelDataType);
 
-  static void InitChannelElement(grpc_exec_ctx *exec_ctx,
-                                 grpc_channel_element *elem,
-                                 grpc_channel_element_args *args) {
-    const char *peer =
-        args->optional_transport
-            ? grpc_transport_get_peer(exec_ctx, args->optional_transport)
-            : nullptr;
-    // Construct the object in the already-allocated memory.
-    new (elem->channel_data) ChannelDataType(*args->channel_args, peer);
+  static grpc_error *InitChannelElement(grpc_exec_ctx *exec_ctx,
+                                        grpc_channel_element *elem,
+                                        grpc_channel_element_args *args) {
+    ChannelDataType *channel_data = new (elem->channel_data) ChannelDataType();
+    return channel_data->Init(exec_ctx, args);
   }
 
   static void DestroyChannelElement(grpc_exec_ctx *exec_ctx,
@@ -312,11 +309,10 @@ class ChannelFilter final {
   static grpc_error *InitCallElement(grpc_exec_ctx *exec_ctx,
                                      grpc_call_element *elem,
                                      grpc_call_element_args *args) {
-    const ChannelDataType &channel_data =
-        *(ChannelDataType *)elem->channel_data;
+    ChannelDataType *channel_data = (ChannelDataType *)elem->channel_data;
     // Construct the object in the already-allocated memory.
-    CallDataType *call_data = new (elem->call_data) CallDataType(channel_data);
-    return call_data->Init();
+    CallDataType *call_data = new (elem->call_data) CallDataType();
+    return call_data->Init(exec_ctx, channel_data, args);
   }
 
   static void DestroyCallElement(grpc_exec_ctx *exec_ctx,
diff --git a/src/cpp/common/completion_queue_cc.cc b/src/cpp/common/completion_queue_cc.cc
index 00cc102f9287b733637e4f43515e5fe261120746..0408a4108531b282663788c68349dd99a78d460c 100644
--- a/src/cpp/common/completion_queue_cc.cc
+++ b/src/cpp/common/completion_queue_cc.cc
@@ -43,11 +43,21 @@ namespace grpc {
 
 static internal::GrpcLibraryInitializer g_gli_initializer;
 
-CompletionQueue::CompletionQueue(grpc_completion_queue* take) : cq_(take) {}
+CompletionQueue::CompletionQueue(grpc_completion_queue* take) : cq_(take) {
+  InitialAvalanching();
+}
 
 void CompletionQueue::Shutdown() {
   g_gli_initializer.summon();
-  grpc_completion_queue_shutdown(cq_);
+  CompleteAvalanching();
+}
+
+void CompletionQueue::CompleteAvalanching() {
+  // Check if this was the last avalanching operation
+  if (gpr_atm_no_barrier_fetch_add(&avalanches_in_flight_,
+                                   static_cast<gpr_atm>(-1)) == 1) {
+    grpc_completion_queue_shutdown(cq_);
+  }
 }
 
 CompletionQueue::NextStatus CompletionQueue::AsyncNextInternal(
diff --git a/src/cpp/server/server_cc.cc b/src/cpp/server/server_cc.cc
index b7cfd6dbf11259e0e8bf7eeaa7a017235f84e24f..817d85a81ca2da1da19b198638d7c306ac817b2e 100644
--- a/src/cpp/server/server_cc.cc
+++ b/src/cpp/server/server_cc.cc
@@ -510,12 +510,6 @@ void Server::ShutdownInternal(gpr_timespec deadline) {
     ShutdownTag shutdown_tag;  // Dummy shutdown tag
     grpc_server_shutdown_and_notify(server_, shutdown_cq.cq(), &shutdown_tag);
 
-    // Shutdown all ThreadManagers. This will try to gracefully stop all the
-    // threads in the ThreadManagers (once they process any inflight requests)
-    for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) {
-      (*it)->Shutdown();  // ThreadManager's Shutdown()
-    }
-
     shutdown_cq.Shutdown();
 
     void* tag;
@@ -531,6 +525,12 @@ void Server::ShutdownInternal(gpr_timespec deadline) {
     // Else in case of SHUTDOWN or GOT_EVENT, it means that the server has
     // successfully shutdown
 
+    // Shutdown all ThreadManagers. This will try to gracefully stop all the
+    // threads in the ThreadManagers (once they process any inflight requests)
+    for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) {
+      (*it)->Shutdown();  // ThreadManager's Shutdown()
+    }
+
     // Wait for threads in all ThreadManagers to terminate
     for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) {
       (*it)->Wait();
@@ -575,9 +575,14 @@ ServerInterface::BaseAsyncRequest::BaseAsyncRequest(
       tag_(tag),
       delete_on_finalize_(delete_on_finalize),
       call_(nullptr) {
+  call_cq_->RegisterAvalanching();  // This op will trigger more ops
   memset(&initial_metadata_array_, 0, sizeof(initial_metadata_array_));
 }
 
+ServerInterface::BaseAsyncRequest::~BaseAsyncRequest() {
+  call_cq_->CompleteAvalanching();
+}
+
 bool ServerInterface::BaseAsyncRequest::FinalizeResult(void** tag,
                                                        bool* status) {
   if (*status) {
diff --git a/src/csharp/Grpc.Auth/Grpc.Auth.csproj b/src/csharp/Grpc.Auth/Grpc.Auth.csproj
index 99e8c1a3da164901d24e4967b54e044d75e1cadb..db55ed5a6c8a7a9bb05e4dcf301140e2d46dcfd9 100644
--- a/src/csharp/Grpc.Auth/Grpc.Auth.csproj
+++ b/src/csharp/Grpc.Auth/Grpc.Auth.csproj
@@ -29,15 +29,6 @@
     <WarningLevel>4</WarningLevel>
     <ConsolePause>false</ConsolePause>
   </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\ReleaseSigned</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <SignAssembly>True</SignAssembly>
-    <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
-  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Net" />
diff --git a/src/csharp/Grpc.Core.Tests/CallOptionsTest.cs b/src/csharp/Grpc.Core.Tests/CallOptionsTest.cs
index 99a2d47e6e904f165ec658c4177654bee224a91a..3c3b9f77458473c706522b63d415933a41f81d79 100644
--- a/src/csharp/Grpc.Core.Tests/CallOptionsTest.cs
+++ b/src/csharp/Grpc.Core.Tests/CallOptionsTest.cs
@@ -67,6 +67,9 @@ namespace Grpc.Core.Tests
             var credentials = new FakeCallCredentials();
             Assert.AreSame(credentials, options.WithCredentials(credentials).Credentials);
 
+            var flags = CallFlags.WaitForReady | CallFlags.CacheableRequest;
+            Assert.AreEqual(flags, options.WithFlags(flags).Flags);
+
             // Check that the original instance is unchanged.
             Assert.IsNull(options.Headers);
             Assert.IsNull(options.Deadline);
@@ -74,6 +77,7 @@ namespace Grpc.Core.Tests
             Assert.IsNull(options.WriteOptions);
             Assert.IsNull(options.PropagationToken);
             Assert.IsNull(options.Credentials);
+            Assert.AreEqual(default(CallFlags), options.Flags);
         }
 
         [Test]
@@ -94,5 +98,16 @@ namespace Grpc.Core.Tests
             Assert.AreEqual(token, new CallOptions(propagationToken: propagationToken2).Normalize().CancellationToken);
             Assert.Throws(typeof(ArgumentException), () => new CallOptions(cancellationToken: token, propagationToken: propagationToken2).Normalize());
         }
+
+        [Test]
+        public void WaitForReady()
+        {
+            var callOptions = new CallOptions();
+            Assert.IsFalse(callOptions.IsWaitForReady);
+
+            Assert.AreEqual(CallFlags.WaitForReady, callOptions.WithWaitForReady().Flags);
+            Assert.IsTrue(callOptions.WithWaitForReady().IsWaitForReady);
+            Assert.IsFalse(callOptions.WithWaitForReady(true).WithWaitForReady(false).IsWaitForReady);
+        }
     }
 }
diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
index 19a68ab9eaa3cf6c934553bc74e649f8afddd55a..646effe21a7c0eee42f48ff4bc0c70d749a05db7 100644
--- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
+++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
@@ -25,15 +25,6 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\ReleaseSigned</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <SignAssembly>True</SignAssembly>
-    <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
-  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="nunit.framework">
diff --git a/src/csharp/Grpc.Core.Tests/Internal/FakeNativeCall.cs b/src/csharp/Grpc.Core.Tests/Internal/FakeNativeCall.cs
index 909112a47c5effae1ccf2f729e4f3cb27a130b02..fe067fe8b0e403b0d3434471bb8cd03c45d93646 100644
--- a/src/csharp/Grpc.Core.Tests/Internal/FakeNativeCall.cs
+++ b/src/csharp/Grpc.Core.Tests/Internal/FakeNativeCall.cs
@@ -115,27 +115,27 @@ namespace Grpc.Core.Internal.Tests
             return "PEER";
         }
 
-        public void StartUnary(UnaryResponseClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags)
+        public void StartUnary(UnaryResponseClientHandler callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
         {
             UnaryResponseClientHandler = callback;
         }
 
-        public void StartUnary(BatchContextSafeHandle ctx, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags)
+        public void StartUnary(BatchContextSafeHandle ctx, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
         {
             throw new NotImplementedException();
         }
 
-        public void StartClientStreaming(UnaryResponseClientHandler callback, MetadataArraySafeHandle metadataArray)
+        public void StartClientStreaming(UnaryResponseClientHandler callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
         {
             UnaryResponseClientHandler = callback;
         }
 
-        public void StartServerStreaming(ReceivedStatusOnClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags)
+        public void StartServerStreaming(ReceivedStatusOnClientHandler callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
         {
             ReceivedStatusOnClientHandler = callback;
         }
 
-        public void StartDuplexStreaming(ReceivedStatusOnClientHandler callback, MetadataArraySafeHandle metadataArray)
+        public void StartDuplexStreaming(ReceivedStatusOnClientHandler callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
         {
             ReceivedStatusOnClientHandler = callback;
         }
diff --git a/src/csharp/Grpc.Core/CallOptions.cs b/src/csharp/Grpc.Core/CallOptions.cs
index 35548cfc96b5ffdaa4c1d491dc5f4961725d64b0..ce43dae17183afa8b749b3e2ebb8e1a7028d0fcf 100644
--- a/src/csharp/Grpc.Core/CallOptions.cs
+++ b/src/csharp/Grpc.Core/CallOptions.cs
@@ -50,6 +50,7 @@ namespace Grpc.Core
         WriteOptions writeOptions;
         ContextPropagationToken propagationToken;
         CallCredentials credentials;
+        CallFlags flags;
 
         /// <summary>
         /// Creates a new instance of <c>CallOptions</c> struct.
@@ -69,6 +70,7 @@ namespace Grpc.Core
             this.writeOptions = writeOptions;
             this.propagationToken = propagationToken;
             this.credentials = credentials;
+            this.flags = default(CallFlags);
         }
 
         /// <summary>
@@ -125,6 +127,24 @@ namespace Grpc.Core
             get { return this.credentials; }
         }
 
+        /// <summary>
+        /// If <c>true</c> and and channel is in <c>ChannelState.TransientFailure</c>, the call will attempt waiting for the channel to recover
+        /// instead of failing immediately (which is the default "FailFast" semantics).
+        /// Note: experimental API that can change or be removed without any prior notice.
+        /// </summary>
+        public bool IsWaitForReady
+        {
+            get { return (this.flags & CallFlags.WaitForReady) == CallFlags.WaitForReady; }
+        }
+
+        /// <summary>
+        /// Flags to use for this call.
+        /// </summary>
+        internal CallFlags Flags
+        {
+            get { return this.flags; }
+        }
+
         /// <summary>
         /// Returns new instance of <see cref="CallOptions"/> with
         /// <c>Headers</c> set to the value provided. Values of all other fields are preserved.
@@ -197,6 +217,32 @@ namespace Grpc.Core
             return newOptions;
         }
 
+        /// <summary>
+        /// Returns new instance of <see cref="CallOptions"/> with "WaitForReady" semantics enabled/disabled.
+        /// <see cref="IsWaitForReady"/>.
+        /// Note: experimental API that can change or be removed without any prior notice.
+        /// </summary>
+        public CallOptions WithWaitForReady(bool waitForReady = true)
+        {
+            if (waitForReady)
+            {
+                return WithFlags(this.flags | CallFlags.WaitForReady);
+            }
+            return WithFlags(this.flags & ~CallFlags.WaitForReady);
+        }
+
+        /// <summary>
+        /// Returns new instance of <see cref="CallOptions"/> with
+        /// <c>Flags</c> set to the value provided. Values of all other fields are preserved.
+        /// </summary>
+        /// <param name="flags">The call flags.</param>
+        internal CallOptions WithFlags(CallFlags flags)
+        {
+            var newOptions = this;
+            newOptions.flags = flags;
+            return newOptions;
+        }
+
         /// <summary>
         /// Returns a new instance of <see cref="CallOptions"/> with 
         /// all previously unset values set to their defaults and deadline and cancellation
diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj
index 51e627044facfa469098036a876b47b09d2a4abf..23e1ddcf7f70928c50af77ff7c77e975432a598f 100644
--- a/src/csharp/Grpc.Core/Grpc.Core.csproj
+++ b/src/csharp/Grpc.Core/Grpc.Core.csproj
@@ -27,16 +27,6 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\ReleaseSigned</OutputPath>
-    <DefineConstants>SIGNED</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <SignAssembly>True</SignAssembly>
-    <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
-  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Interactive.Async">
@@ -140,6 +130,7 @@
     <Compile Include="Logging\LogLevelFilterLogger.cs" />
     <Compile Include="Internal\RequestCallContextSafeHandle.cs" />
     <Compile Include="Utils\TaskUtils.cs" />
+    <Compile Include="Internal\CallFlags.cs" />
   </ItemGroup>
   <ItemGroup>
     <None Include="Grpc.Core.project.json" />
@@ -153,4 +144,4 @@
       <Link>roots.pem</Link>
     </EmbeddedResource>
   </ItemGroup>
-</Project>
+</Project>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
index da45c4829dff122ff5882edb3d7aeb7a5727b297..1f738a3b6f9995e5f236674718df7a2905f9c067 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
@@ -106,7 +106,7 @@ namespace Grpc.Core.Internal
                 using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers))
                 using (var ctx = BatchContextSafeHandle.Create())
                 {
-                    call.StartUnary(ctx, payload, metadataArray, GetWriteFlagsForCall());
+                    call.StartUnary(ctx, payload, GetWriteFlagsForCall(), metadataArray, details.Options.Flags);
 
                     var ev = cq.Pluck(ctx.Handle);
 
@@ -150,7 +150,7 @@ namespace Grpc.Core.Internal
                 unaryResponseTcs = new TaskCompletionSource<TResponse>();
                 using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers))
                 {
-                    call.StartUnary(HandleUnaryResponse, payload, metadataArray, GetWriteFlagsForCall());
+                    call.StartUnary(HandleUnaryResponse, payload, GetWriteFlagsForCall(), metadataArray, details.Options.Flags);
                 }
                 return unaryResponseTcs.Task;
             }
@@ -174,7 +174,7 @@ namespace Grpc.Core.Internal
                 unaryResponseTcs = new TaskCompletionSource<TResponse>();
                 using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers))
                 {
-                    call.StartClientStreaming(HandleUnaryResponse, metadataArray);
+                    call.StartClientStreaming(HandleUnaryResponse, metadataArray, details.Options.Flags);
                 }
 
                 return unaryResponseTcs.Task;
@@ -200,7 +200,7 @@ namespace Grpc.Core.Internal
                 streamingResponseCallFinishedTcs = new TaskCompletionSource<object>();
                 using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers))
                 {
-                    call.StartServerStreaming(HandleFinished, payload, metadataArray, GetWriteFlagsForCall());
+                    call.StartServerStreaming(HandleFinished, payload, GetWriteFlagsForCall(), metadataArray, details.Options.Flags);
                 }
                 call.StartReceiveInitialMetadata(HandleReceivedResponseHeaders);
             }
@@ -222,7 +222,7 @@ namespace Grpc.Core.Internal
                 streamingResponseCallFinishedTcs = new TaskCompletionSource<object>();
                 using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers))
                 {
-                    call.StartDuplexStreaming(HandleFinished, metadataArray);
+                    call.StartDuplexStreaming(HandleFinished, metadataArray, details.Options.Flags);
                 }
                 call.StartReceiveInitialMetadata(HandleReceivedResponseHeaders);
             }
diff --git a/src/csharp/Grpc.Core/Internal/CallFlags.cs b/src/csharp/Grpc.Core/Internal/CallFlags.cs
new file mode 100644
index 0000000000000000000000000000000000000000..454fa9b1f4500c62fc41a627886125090550506f
--- /dev/null
+++ b/src/csharp/Grpc.Core/Internal/CallFlags.cs
@@ -0,0 +1,60 @@
+#region Copyright notice and license
+
+// 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.
+
+#endregion
+
+using System;
+
+namespace Grpc.Core.Internal
+{
+    /// <summary>
+    /// Flags to enable special call behaviors (client-side only).
+    /// </summary>
+    [Flags]
+    internal enum CallFlags
+    {
+        /// <summary>
+        /// The call is idempotent (retrying the call doesn't change the outcome of the operation).
+        /// </summary>
+        IdempotentRequest = 0x10,
+
+        /// <summary>
+        /// If channel is in <c>ChannelState.TransientFailure</c>, attempt waiting for the channel to recover
+        /// instead of failing the call immediately.
+        /// </summary>
+        WaitForReady = 0x20,
+
+        /// <summary>
+        /// The call is cacheable. gRPC is free to use GET verb */
+        /// </summary>
+        CacheableRequest = 0x40
+    }
+}
diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
index f817a61bce832c951527cfdd7c725999dc2b8b49..6bfcc7fa74a5c0bfb0a6fd64b878e7abc67af8f1 100644
--- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
@@ -63,50 +63,50 @@ namespace Grpc.Core.Internal
             Native.grpcsharp_call_set_credentials(this, credentials).CheckOk();
         }
 
-        public void StartUnary(UnaryResponseClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags)
+        public void StartUnary(UnaryResponseClientHandler callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
         {
             using (completionQueue.NewScope())
             {
                 var ctx = BatchContextSafeHandle.Create();
                 completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient(), context.GetReceivedMessage(), context.GetReceivedInitialMetadata()));
-                Native.grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags)
+                Native.grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, metadataArray, callFlags)
                     .CheckOk();
             }
         }
 
-        public void StartUnary(BatchContextSafeHandle ctx, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags)
+        public void StartUnary(BatchContextSafeHandle ctx, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
         {
-            Native.grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags)
+            Native.grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, metadataArray, callFlags)
                 .CheckOk();
         }
 
-        public void StartClientStreaming(UnaryResponseClientHandler callback, MetadataArraySafeHandle metadataArray)
+        public void StartClientStreaming(UnaryResponseClientHandler callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
         {
             using (completionQueue.NewScope())
             {
                 var ctx = BatchContextSafeHandle.Create();
                 completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient(), context.GetReceivedMessage(), context.GetReceivedInitialMetadata()));
-                Native.grpcsharp_call_start_client_streaming(this, ctx, metadataArray).CheckOk();
+                Native.grpcsharp_call_start_client_streaming(this, ctx, metadataArray, callFlags).CheckOk();
             }
         }
 
-        public void StartServerStreaming(ReceivedStatusOnClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags)
+        public void StartServerStreaming(ReceivedStatusOnClientHandler callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
         {
             using (completionQueue.NewScope())
             {
                 var ctx = BatchContextSafeHandle.Create();
                 completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient()));
-                Native.grpcsharp_call_start_server_streaming(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags).CheckOk();
+                Native.grpcsharp_call_start_server_streaming(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, metadataArray, callFlags).CheckOk();
             }
         }
 
-        public void StartDuplexStreaming(ReceivedStatusOnClientHandler callback, MetadataArraySafeHandle metadataArray)
+        public void StartDuplexStreaming(ReceivedStatusOnClientHandler callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
         {
             using (completionQueue.NewScope())
             {
                 var ctx = BatchContextSafeHandle.Create();
                 completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient()));
-                Native.grpcsharp_call_start_duplex_streaming(this, ctx, metadataArray).CheckOk();
+                Native.grpcsharp_call_start_duplex_streaming(this, ctx, metadataArray, callFlags).CheckOk();
             }
         }
 
diff --git a/src/csharp/Grpc.Core/Internal/INativeCall.cs b/src/csharp/Grpc.Core/Internal/INativeCall.cs
index cd3719cb50c58d813de0faacd0e678173bdecd16..94fbb08feba03a5726cd3a1140c6202c029cc8fa 100644
--- a/src/csharp/Grpc.Core/Internal/INativeCall.cs
+++ b/src/csharp/Grpc.Core/Internal/INativeCall.cs
@@ -31,6 +31,7 @@
 #endregion
 
 using System;
+using Grpc.Core;
 
 namespace Grpc.Core.Internal
 {
@@ -54,19 +55,19 @@ namespace Grpc.Core.Internal
     {
         void Cancel();
 
-        void CancelWithStatus(Grpc.Core.Status status);
+        void CancelWithStatus(Status status);
 
         string GetPeer();
 
-        void StartUnary(UnaryResponseClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, Grpc.Core.WriteFlags writeFlags);
+        void StartUnary(UnaryResponseClientHandler callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags);
 
-        void StartUnary(BatchContextSafeHandle ctx, byte[] payload, MetadataArraySafeHandle metadataArray, Grpc.Core.WriteFlags writeFlags);
+        void StartUnary(BatchContextSafeHandle ctx, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags);
 
-        void StartClientStreaming(UnaryResponseClientHandler callback, MetadataArraySafeHandle metadataArray);
+        void StartClientStreaming(UnaryResponseClientHandler callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags);
 
-        void StartServerStreaming(ReceivedStatusOnClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, Grpc.Core.WriteFlags writeFlags);
+        void StartServerStreaming(ReceivedStatusOnClientHandler callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags);
 
-        void StartDuplexStreaming(ReceivedStatusOnClientHandler callback, MetadataArraySafeHandle metadataArray);
+        void StartDuplexStreaming(ReceivedStatusOnClientHandler callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags);
 
         void StartReceiveMessage(ReceivedMessageHandler callback);
 
@@ -74,11 +75,11 @@ namespace Grpc.Core.Internal
 
         void StartSendInitialMetadata(SendCompletionHandler callback, MetadataArraySafeHandle metadataArray);
 
-        void StartSendMessage(SendCompletionHandler callback, byte[] payload, Grpc.Core.WriteFlags writeFlags, bool sendEmptyInitialMetadata);
+        void StartSendMessage(SendCompletionHandler callback, byte[] payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata);
 
         void StartSendCloseFromClient(SendCompletionHandler callback);
 
-        void StartSendStatusFromServer(SendCompletionHandler callback, Grpc.Core.Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata, byte[] optionalPayload, Grpc.Core.WriteFlags writeFlags);
+        void StartSendStatusFromServer(SendCompletionHandler callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata, byte[] optionalPayload, WriteFlags writeFlags);
 
         void StartServerSide(ReceivedCloseOnServerHandler callback);
     }
diff --git a/src/csharp/Grpc.Core/Internal/NativeMethods.cs b/src/csharp/Grpc.Core/Internal/NativeMethods.cs
index 40ba7e30cb02b125ac016d34aa736aa917216b4a..ce38e3709369dc1e2529c472de8382e8e16cfb89 100644
--- a/src/csharp/Grpc.Core/Internal/NativeMethods.cs
+++ b/src/csharp/Grpc.Core/Internal/NativeMethods.cs
@@ -325,14 +325,14 @@ namespace Grpc.Core.Internal
             public delegate CallError grpcsharp_call_cancel_delegate(CallSafeHandle call);
             public delegate CallError grpcsharp_call_cancel_with_status_delegate(CallSafeHandle call, StatusCode status, string description);
             public delegate CallError grpcsharp_call_start_unary_delegate(CallSafeHandle call,
-                BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags);
+                BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
             public delegate CallError grpcsharp_call_start_client_streaming_delegate(CallSafeHandle call,
-                BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray);
+                BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
             public delegate CallError grpcsharp_call_start_server_streaming_delegate(CallSafeHandle call,
-                BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen,
-                MetadataArraySafeHandle metadataArray, WriteFlags writeFlags);
+                BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags,
+                MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
             public delegate CallError grpcsharp_call_start_duplex_streaming_delegate(CallSafeHandle call,
-                BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray);
+                BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
             public delegate CallError grpcsharp_call_send_message_delegate(CallSafeHandle call,
                 BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, bool sendEmptyInitialMetadata);
             public delegate CallError grpcsharp_call_send_close_from_client_delegate(CallSafeHandle call,
diff --git a/src/csharp/Grpc.Core/NativeDeps.targets b/src/csharp/Grpc.Core/NativeDeps.targets
index 66c5ec1292bbe2509128781598e2fa5b4e62574c..e187f72d266bd46d5f5ce0385926c7d1a53ee942 100644
--- a/src/csharp/Grpc.Core/NativeDeps.targets
+++ b/src/csharp/Grpc.Core/NativeDeps.targets
@@ -4,13 +4,11 @@
   <PropertyGroup Condition=" '$(NativeDependenciesConfiguration)' == '' ">
     <NativeDependenciesConfiguration Condition=" '$(Configuration)' == 'Debug' ">Debug</NativeDependenciesConfiguration>
     <NativeDependenciesConfiguration Condition=" '$(Configuration)' == 'Release' ">Release</NativeDependenciesConfiguration>
-    <NativeDependenciesConfiguration Condition=" '$(Configuration)' == 'ReleaseSigned' ">Release</NativeDependenciesConfiguration>
   </PropertyGroup>
 
   <PropertyGroup Condition=" '$(NativeDependenciesConfigurationUnix)' == '' ">
     <NativeDependenciesConfigurationUnix Condition=" '$(Configuration)' == 'Debug' ">dbg</NativeDependenciesConfigurationUnix>
     <NativeDependenciesConfigurationUnix Condition=" '$(Configuration)' == 'Release' ">opt</NativeDependenciesConfigurationUnix>
-    <NativeDependenciesConfigurationUnix Condition=" '$(Configuration)' == 'ReleaseSigned' ">opt</NativeDependenciesConfigurationUnix>
   </PropertyGroup>
 
   <!-- Autodetect platform -->
diff --git a/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj b/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj
index 65bf236def6b1207dd3820b7eb51b744bca02c42..de4005c2f660f587142335883661ae1409c57279 100644
--- a/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj
+++ b/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj
@@ -27,15 +27,6 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\ReleaseSigned</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <SignAssembly>True</SignAssembly>
-    <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
-  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
   </ItemGroup>
diff --git a/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj b/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj
index 26b42b6936da40bc20bacdfa52be316493be8bf3..3f38de2b714293834d76690835304fd5f8298f9d 100644
--- a/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj
+++ b/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj
@@ -27,15 +27,6 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\ReleaseSigned</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <SignAssembly>True</SignAssembly>
-    <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
-  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
   </ItemGroup>
diff --git a/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj b/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj
index 16d7a44f92f7c5cee3e759dff8ca995761a361e3..d22fe87825959451659af2988d3c0861ecf5fcca 100644
--- a/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj
+++ b/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj
@@ -25,15 +25,6 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\ReleaseSigned</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <SignAssembly>True</SignAssembly>
-    <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
-  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="nunit.framework">
diff --git a/src/csharp/Grpc.Examples/Grpc.Examples.csproj b/src/csharp/Grpc.Examples/Grpc.Examples.csproj
index c1ea2e283358a7a19ce16e57ba892411501031cc..44acb6c2e3a2a029b4eb335431a68d90671884a2 100644
--- a/src/csharp/Grpc.Examples/Grpc.Examples.csproj
+++ b/src/csharp/Grpc.Examples/Grpc.Examples.csproj
@@ -27,15 +27,6 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\ReleaseSigned</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <SignAssembly>True</SignAssembly>
-    <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
-  </PropertyGroup>
   <ItemGroup>
     <Reference Include="nunit.framework">
       <HintPath>..\packages\NUnit.3.2.0\lib\net45\nunit.framework.dll</HintPath>
diff --git a/src/csharp/Grpc.Examples/MathGrpc.cs b/src/csharp/Grpc.Examples/MathGrpc.cs
index 8b431c72184fb397c9f7c3a8f17615e37b161f26..3364b8ce8ecabcfb0d8404a5011f45d283f363a5 100644
--- a/src/csharp/Grpc.Examples/MathGrpc.cs
+++ b/src/csharp/Grpc.Examples/MathGrpc.cs
@@ -85,39 +85,53 @@ namespace Math {
     public abstract partial class MathBase
     {
       /// <summary>
-      ///  Div divides DivArgs.dividend by DivArgs.divisor and returns the quotient
-      ///  and remainder.
+      /// Div divides DivArgs.dividend by DivArgs.divisor and returns the quotient
+      /// and remainder.
       /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
       public virtual global::System.Threading.Tasks.Task<global::Math.DivReply> Div(global::Math.DivArgs request, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
       }
 
       /// <summary>
-      ///  DivMany accepts an arbitrary number of division args from the client stream
-      ///  and sends back the results in the reply stream.  The stream continues until
-      ///  the client closes its end; the server does the same after sending all the
-      ///  replies.  The stream ends immediately if either end aborts.
+      /// DivMany accepts an arbitrary number of division args from the client stream
+      /// and sends back the results in the reply stream.  The stream continues until
+      /// the client closes its end; the server does the same after sending all the
+      /// replies.  The stream ends immediately if either end aborts.
       /// </summary>
+      /// <param name="requestStream">Used for reading requests from the client.</param>
+      /// <param name="responseStream">Used for sending responses back to the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>A task indicating completion of the handler.</returns>
       public virtual global::System.Threading.Tasks.Task DivMany(IAsyncStreamReader<global::Math.DivArgs> requestStream, IServerStreamWriter<global::Math.DivReply> responseStream, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
       }
 
       /// <summary>
-      ///  Fib generates numbers in the Fibonacci sequence.  If FibArgs.limit > 0, Fib
-      ///  generates up to limit numbers; otherwise it continues until the call is
-      ///  canceled.  Unlike Fib above, Fib has no final FibReply.
+      /// Fib generates numbers in the Fibonacci sequence.  If FibArgs.limit > 0, Fib
+      /// generates up to limit numbers; otherwise it continues until the call is
+      /// canceled.  Unlike Fib above, Fib has no final FibReply.
       /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="responseStream">Used for sending responses back to the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>A task indicating completion of the handler.</returns>
       public virtual global::System.Threading.Tasks.Task Fib(global::Math.FibArgs request, IServerStreamWriter<global::Math.Num> responseStream, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
       }
 
       /// <summary>
-      ///  Sum sums a stream of numbers, returning the final result once the stream
-      ///  is closed.
+      /// Sum sums a stream of numbers, returning the final result once the stream
+      /// is closed.
       /// </summary>
+      /// <param name="requestStream">Used for reading requests from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
       public virtual global::System.Threading.Tasks.Task<global::Math.Num> Sum(IAsyncStreamReader<global::Math.Num> requestStream, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
@@ -149,87 +163,123 @@ namespace Math {
       }
 
       /// <summary>
-      ///  Div divides DivArgs.dividend by DivArgs.divisor and returns the quotient
-      ///  and remainder.
+      /// Div divides DivArgs.dividend by DivArgs.divisor and returns the quotient
+      /// and remainder.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Math.DivReply Div(global::Math.DivArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return Div(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  Div divides DivArgs.dividend by DivArgs.divisor and returns the quotient
-      ///  and remainder.
+      /// Div divides DivArgs.dividend by DivArgs.divisor and returns the quotient
+      /// and remainder.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Math.DivReply Div(global::Math.DivArgs request, CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_Div, null, options, request);
       }
       /// <summary>
-      ///  Div divides DivArgs.dividend by DivArgs.divisor and returns the quotient
-      ///  and remainder.
+      /// Div divides DivArgs.dividend by DivArgs.divisor and returns the quotient
+      /// and remainder.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Math.DivReply> DivAsync(global::Math.DivArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return DivAsync(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  Div divides DivArgs.dividend by DivArgs.divisor and returns the quotient
-      ///  and remainder.
+      /// Div divides DivArgs.dividend by DivArgs.divisor and returns the quotient
+      /// and remainder.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Math.DivReply> DivAsync(global::Math.DivArgs request, CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_Div, null, options, request);
       }
       /// <summary>
-      ///  DivMany accepts an arbitrary number of division args from the client stream
-      ///  and sends back the results in the reply stream.  The stream continues until
-      ///  the client closes its end; the server does the same after sending all the
-      ///  replies.  The stream ends immediately if either end aborts.
+      /// DivMany accepts an arbitrary number of division args from the client stream
+      /// and sends back the results in the reply stream.  The stream continues until
+      /// the client closes its end; the server does the same after sending all the
+      /// replies.  The stream ends immediately if either end aborts.
       /// </summary>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncDuplexStreamingCall<global::Math.DivArgs, global::Math.DivReply> DivMany(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return DivMany(new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  DivMany accepts an arbitrary number of division args from the client stream
-      ///  and sends back the results in the reply stream.  The stream continues until
-      ///  the client closes its end; the server does the same after sending all the
-      ///  replies.  The stream ends immediately if either end aborts.
+      /// DivMany accepts an arbitrary number of division args from the client stream
+      /// and sends back the results in the reply stream.  The stream continues until
+      /// the client closes its end; the server does the same after sending all the
+      /// replies.  The stream ends immediately if either end aborts.
       /// </summary>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncDuplexStreamingCall<global::Math.DivArgs, global::Math.DivReply> DivMany(CallOptions options)
       {
         return CallInvoker.AsyncDuplexStreamingCall(__Method_DivMany, null, options);
       }
       /// <summary>
-      ///  Fib generates numbers in the Fibonacci sequence.  If FibArgs.limit > 0, Fib
-      ///  generates up to limit numbers; otherwise it continues until the call is
-      ///  canceled.  Unlike Fib above, Fib has no final FibReply.
+      /// Fib generates numbers in the Fibonacci sequence.  If FibArgs.limit > 0, Fib
+      /// generates up to limit numbers; otherwise it continues until the call is
+      /// canceled.  Unlike Fib above, Fib has no final FibReply.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncServerStreamingCall<global::Math.Num> Fib(global::Math.FibArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return Fib(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  Fib generates numbers in the Fibonacci sequence.  If FibArgs.limit > 0, Fib
-      ///  generates up to limit numbers; otherwise it continues until the call is
-      ///  canceled.  Unlike Fib above, Fib has no final FibReply.
+      /// Fib generates numbers in the Fibonacci sequence.  If FibArgs.limit > 0, Fib
+      /// generates up to limit numbers; otherwise it continues until the call is
+      /// canceled.  Unlike Fib above, Fib has no final FibReply.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncServerStreamingCall<global::Math.Num> Fib(global::Math.FibArgs request, CallOptions options)
       {
         return CallInvoker.AsyncServerStreamingCall(__Method_Fib, null, options, request);
       }
       /// <summary>
-      ///  Sum sums a stream of numbers, returning the final result once the stream
-      ///  is closed.
+      /// Sum sums a stream of numbers, returning the final result once the stream
+      /// is closed.
       /// </summary>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncClientStreamingCall<global::Math.Num, global::Math.Num> Sum(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return Sum(new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  Sum sums a stream of numbers, returning the final result once the stream
-      ///  is closed.
+      /// Sum sums a stream of numbers, returning the final result once the stream
+      /// is closed.
       /// </summary>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncClientStreamingCall<global::Math.Num, global::Math.Num> Sum(CallOptions options)
       {
         return CallInvoker.AsyncClientStreamingCall(__Method_Sum, null, options);
@@ -242,6 +292,7 @@ namespace Math {
     }
 
     /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
     public static ServerServiceDefinition BindService(MathBase serviceImpl)
     {
       return ServerServiceDefinition.CreateBuilder()
diff --git a/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj b/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj
index 93c3b3a55fc7925ab2d56e34238e660e53056e0c..b82f9768612eefc211c4faa212d3a51920df599c 100644
--- a/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj
+++ b/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj
@@ -27,15 +27,6 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\ReleaseSigned</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <SignAssembly>True</SignAssembly>
-    <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
-  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Core" />
diff --git a/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj
index 74187683163e3b893fc7c35fa3a7cfb8a7d0a404..63aa18584d8d074ba72f8ffa9e823ce57b0728d7 100644
--- a/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj
+++ b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj
@@ -28,15 +28,6 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\ReleaseSigned</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <SignAssembly>True</SignAssembly>
-    <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
-  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Core" />
diff --git a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs
index ad5cf11b75ecbab3ce2455f476778c65472cef32..020c2df5657361428a974e9cb89578010078f39c 100644
--- a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs
+++ b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs
@@ -115,6 +115,7 @@ namespace Grpc.Health.V1 {
     }
 
     /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
     public static ServerServiceDefinition BindService(HealthBase serviceImpl)
     {
       return ServerServiceDefinition.CreateBuilder()
diff --git a/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj b/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj
index ae58073b52d5c2f2c9660a6454b9cec3c0ba4d64..6bb5f33966ea094e539d351aac21960673294475 100644
--- a/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj
+++ b/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj
@@ -29,15 +29,6 @@
     <WarningLevel>4</WarningLevel>
     <PlatformTarget>AnyCPU</PlatformTarget>
   </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\ReleaseSigned</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <SignAssembly>True</SignAssembly>
-    <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
-  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Net" />
diff --git a/src/csharp/Grpc.IntegrationTesting.QpsWorker/Grpc.IntegrationTesting.QpsWorker.csproj b/src/csharp/Grpc.IntegrationTesting.QpsWorker/Grpc.IntegrationTesting.QpsWorker.csproj
index 593bf0939db7ce342a497395aaa2bcdae40a6878..3b9587e31518a35e117e193d2bb769304311c1f1 100644
--- a/src/csharp/Grpc.IntegrationTesting.QpsWorker/Grpc.IntegrationTesting.QpsWorker.csproj
+++ b/src/csharp/Grpc.IntegrationTesting.QpsWorker/Grpc.IntegrationTesting.QpsWorker.csproj
@@ -27,15 +27,6 @@
     <WarningLevel>4</WarningLevel>
     <PlatformTarget>AnyCPU</PlatformTarget>
   </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\ReleaseSigned</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <SignAssembly>True</SignAssembly>
-    <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
-  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
   </ItemGroup>
diff --git a/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj b/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj
index d5c40ba9483b2615fc706493e85935350d632af1..081dc24fbf19ecaa07a82591b8e4d81e58cd6acd 100644
--- a/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj
+++ b/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj
@@ -29,15 +29,6 @@
     <WarningLevel>4</WarningLevel>
     <PlatformTarget>AnyCPU</PlatformTarget>
   </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\ReleaseSigned</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <SignAssembly>True</SignAssembly>
-    <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
-  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Net" />
diff --git a/src/csharp/Grpc.IntegrationTesting.StressClient/Grpc.IntegrationTesting.StressClient.csproj b/src/csharp/Grpc.IntegrationTesting.StressClient/Grpc.IntegrationTesting.StressClient.csproj
index 8bd3d789138a670e4e9e1ebf5c914721e89f42ad..0f283404504062a78ec89efae62f17661c826d65 100644
--- a/src/csharp/Grpc.IntegrationTesting.StressClient/Grpc.IntegrationTesting.StressClient.csproj
+++ b/src/csharp/Grpc.IntegrationTesting.StressClient/Grpc.IntegrationTesting.StressClient.csproj
@@ -27,15 +27,6 @@
     <WarningLevel>4</WarningLevel>
     <PlatformTarget>AnyCPU</PlatformTarget>
   </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\ReleaseSigned</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <SignAssembly>True</SignAssembly>
-    <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
-  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
   </ItemGroup>
diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
index 161b015300bfe2db27dba1bcabba763623f150b2..f7abcf8046997557a4e64e3a8147b899c81c7260 100644
--- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
+++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
@@ -28,15 +28,6 @@
     <WarningLevel>4</WarningLevel>
     <PlatformTarget>AnyCPU</PlatformTarget>
   </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\ReleaseSigned</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <SignAssembly>True</SignAssembly>
-    <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
-  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Net" />
diff --git a/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs b/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs
index d0bf0afc1d0715043d31bdfe5d137e2f25c49244..8b58622d5308860167c8dbc06709918e7ae84df6 100644
--- a/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs
+++ b/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs
@@ -76,17 +76,24 @@ namespace Grpc.Testing {
     public abstract partial class MetricsServiceBase
     {
       /// <summary>
-      ///  Returns the values of all the gauges that are currently being maintained by
-      ///  the service
+      /// Returns the values of all the gauges that are currently being maintained by
+      /// the service
       /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="responseStream">Used for sending responses back to the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>A task indicating completion of the handler.</returns>
       public virtual global::System.Threading.Tasks.Task GetAllGauges(global::Grpc.Testing.EmptyMessage request, IServerStreamWriter<global::Grpc.Testing.GaugeResponse> responseStream, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
       }
 
       /// <summary>
-      ///  Returns the value of one gauge
+      /// Returns the value of one gauge
       /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.GaugeResponse> GetGauge(global::Grpc.Testing.GaugeRequest request, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
@@ -118,45 +125,69 @@ namespace Grpc.Testing {
       }
 
       /// <summary>
-      ///  Returns the values of all the gauges that are currently being maintained by
-      ///  the service
+      /// Returns the values of all the gauges that are currently being maintained by
+      /// the service
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncServerStreamingCall<global::Grpc.Testing.GaugeResponse> GetAllGauges(global::Grpc.Testing.EmptyMessage request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return GetAllGauges(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  Returns the values of all the gauges that are currently being maintained by
-      ///  the service
+      /// Returns the values of all the gauges that are currently being maintained by
+      /// the service
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncServerStreamingCall<global::Grpc.Testing.GaugeResponse> GetAllGauges(global::Grpc.Testing.EmptyMessage request, CallOptions options)
       {
         return CallInvoker.AsyncServerStreamingCall(__Method_GetAllGauges, null, options, request);
       }
       /// <summary>
-      ///  Returns the value of one gauge
+      /// Returns the value of one gauge
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Grpc.Testing.GaugeResponse GetGauge(global::Grpc.Testing.GaugeRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return GetGauge(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  Returns the value of one gauge
+      /// Returns the value of one gauge
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Grpc.Testing.GaugeResponse GetGauge(global::Grpc.Testing.GaugeRequest request, CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_GetGauge, null, options, request);
       }
       /// <summary>
-      ///  Returns the value of one gauge
+      /// Returns the value of one gauge
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Grpc.Testing.GaugeResponse> GetGaugeAsync(global::Grpc.Testing.GaugeRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return GetGaugeAsync(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  Returns the value of one gauge
+      /// Returns the value of one gauge
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Grpc.Testing.GaugeResponse> GetGaugeAsync(global::Grpc.Testing.GaugeRequest request, CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_GetGauge, null, options, request);
@@ -169,6 +200,7 @@ namespace Grpc.Testing {
     }
 
     /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
     public static ServerServiceDefinition BindService(MetricsServiceBase serviceImpl)
     {
       return ServerServiceDefinition.CreateBuilder()
diff --git a/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs b/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs
index 3cc4ed9f3c3a489f642a9dbc906cb95615d83b26..5135d9ab66d18aa4abf151f9a493cba7258037f1 100644
--- a/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs
+++ b/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs
@@ -71,18 +71,25 @@ namespace Grpc.Testing {
     public abstract partial class BenchmarkServiceBase
     {
       /// <summary>
-      ///  One request followed by one response.
-      ///  The server returns the client payload as-is.
+      /// One request followed by one response.
+      /// The server returns the client payload as-is.
       /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.SimpleResponse> UnaryCall(global::Grpc.Testing.SimpleRequest request, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
       }
 
       /// <summary>
-      ///  One request followed by one response.
-      ///  The server returns the client payload as-is.
+      /// One request followed by one response.
+      /// The server returns the client payload as-is.
       /// </summary>
+      /// <param name="requestStream">Used for reading requests from the client.</param>
+      /// <param name="responseStream">Used for sending responses back to the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>A task indicating completion of the handler.</returns>
       public virtual global::System.Threading.Tasks.Task StreamingCall(IAsyncStreamReader<global::Grpc.Testing.SimpleRequest> requestStream, IServerStreamWriter<global::Grpc.Testing.SimpleResponse> responseStream, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
@@ -114,49 +121,71 @@ namespace Grpc.Testing {
       }
 
       /// <summary>
-      ///  One request followed by one response.
-      ///  The server returns the client payload as-is.
+      /// One request followed by one response.
+      /// The server returns the client payload as-is.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return UnaryCall(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  One request followed by one response.
-      ///  The server returns the client payload as-is.
+      /// One request followed by one response.
+      /// The server returns the client payload as-is.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_UnaryCall, null, options, request);
       }
       /// <summary>
-      ///  One request followed by one response.
-      ///  The server returns the client payload as-is.
+      /// One request followed by one response.
+      /// The server returns the client payload as-is.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return UnaryCallAsync(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  One request followed by one response.
-      ///  The server returns the client payload as-is.
+      /// One request followed by one response.
+      /// The server returns the client payload as-is.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_UnaryCall, null, options, request);
       }
       /// <summary>
-      ///  One request followed by one response.
-      ///  The server returns the client payload as-is.
+      /// One request followed by one response.
+      /// The server returns the client payload as-is.
       /// </summary>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return StreamingCall(new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  One request followed by one response.
-      ///  The server returns the client payload as-is.
+      /// One request followed by one response.
+      /// The server returns the client payload as-is.
       /// </summary>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingCall(CallOptions options)
       {
         return CallInvoker.AsyncDuplexStreamingCall(__Method_StreamingCall, null, options);
@@ -169,6 +198,7 @@ namespace Grpc.Testing {
     }
 
     /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
     public static ServerServiceDefinition BindService(BenchmarkServiceBase serviceImpl)
     {
       return ServerServiceDefinition.CreateBuilder()
@@ -227,42 +257,56 @@ namespace Grpc.Testing {
     public abstract partial class WorkerServiceBase
     {
       /// <summary>
-      ///  Start server with specified workload.
-      ///  First request sent specifies the ServerConfig followed by ServerStatus
-      ///  response. After that, a "Mark" can be sent anytime to request the latest
-      ///  stats. Closing the stream will initiate shutdown of the test server
-      ///  and once the shutdown has finished, the OK status is sent to terminate
-      ///  this RPC.
+      /// Start server with specified workload.
+      /// First request sent specifies the ServerConfig followed by ServerStatus
+      /// response. After that, a "Mark" can be sent anytime to request the latest
+      /// stats. Closing the stream will initiate shutdown of the test server
+      /// and once the shutdown has finished, the OK status is sent to terminate
+      /// this RPC.
       /// </summary>
+      /// <param name="requestStream">Used for reading requests from the client.</param>
+      /// <param name="responseStream">Used for sending responses back to the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>A task indicating completion of the handler.</returns>
       public virtual global::System.Threading.Tasks.Task RunServer(IAsyncStreamReader<global::Grpc.Testing.ServerArgs> requestStream, IServerStreamWriter<global::Grpc.Testing.ServerStatus> responseStream, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
       }
 
       /// <summary>
-      ///  Start client with specified workload.
-      ///  First request sent specifies the ClientConfig followed by ClientStatus
-      ///  response. After that, a "Mark" can be sent anytime to request the latest
-      ///  stats. Closing the stream will initiate shutdown of the test client
-      ///  and once the shutdown has finished, the OK status is sent to terminate
-      ///  this RPC.
+      /// Start client with specified workload.
+      /// First request sent specifies the ClientConfig followed by ClientStatus
+      /// response. After that, a "Mark" can be sent anytime to request the latest
+      /// stats. Closing the stream will initiate shutdown of the test client
+      /// and once the shutdown has finished, the OK status is sent to terminate
+      /// this RPC.
       /// </summary>
+      /// <param name="requestStream">Used for reading requests from the client.</param>
+      /// <param name="responseStream">Used for sending responses back to the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>A task indicating completion of the handler.</returns>
       public virtual global::System.Threading.Tasks.Task RunClient(IAsyncStreamReader<global::Grpc.Testing.ClientArgs> requestStream, IServerStreamWriter<global::Grpc.Testing.ClientStatus> responseStream, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
       }
 
       /// <summary>
-      ///  Just return the core count - unary call
+      /// Just return the core count - unary call
       /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.CoreResponse> CoreCount(global::Grpc.Testing.CoreRequest request, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
       }
 
       /// <summary>
-      ///  Quit this worker
+      /// Quit this worker
       /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.Void> QuitWorker(global::Grpc.Testing.Void request, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
@@ -294,105 +338,149 @@ namespace Grpc.Testing {
       }
 
       /// <summary>
-      ///  Start server with specified workload.
-      ///  First request sent specifies the ServerConfig followed by ServerStatus
-      ///  response. After that, a "Mark" can be sent anytime to request the latest
-      ///  stats. Closing the stream will initiate shutdown of the test server
-      ///  and once the shutdown has finished, the OK status is sent to terminate
-      ///  this RPC.
+      /// Start server with specified workload.
+      /// First request sent specifies the ServerConfig followed by ServerStatus
+      /// response. After that, a "Mark" can be sent anytime to request the latest
+      /// stats. Closing the stream will initiate shutdown of the test server
+      /// and once the shutdown has finished, the OK status is sent to terminate
+      /// this RPC.
       /// </summary>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus> RunServer(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return RunServer(new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  Start server with specified workload.
-      ///  First request sent specifies the ServerConfig followed by ServerStatus
-      ///  response. After that, a "Mark" can be sent anytime to request the latest
-      ///  stats. Closing the stream will initiate shutdown of the test server
-      ///  and once the shutdown has finished, the OK status is sent to terminate
-      ///  this RPC.
+      /// Start server with specified workload.
+      /// First request sent specifies the ServerConfig followed by ServerStatus
+      /// response. After that, a "Mark" can be sent anytime to request the latest
+      /// stats. Closing the stream will initiate shutdown of the test server
+      /// and once the shutdown has finished, the OK status is sent to terminate
+      /// this RPC.
       /// </summary>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus> RunServer(CallOptions options)
       {
         return CallInvoker.AsyncDuplexStreamingCall(__Method_RunServer, null, options);
       }
       /// <summary>
-      ///  Start client with specified workload.
-      ///  First request sent specifies the ClientConfig followed by ClientStatus
-      ///  response. After that, a "Mark" can be sent anytime to request the latest
-      ///  stats. Closing the stream will initiate shutdown of the test client
-      ///  and once the shutdown has finished, the OK status is sent to terminate
-      ///  this RPC.
+      /// Start client with specified workload.
+      /// First request sent specifies the ClientConfig followed by ClientStatus
+      /// response. After that, a "Mark" can be sent anytime to request the latest
+      /// stats. Closing the stream will initiate shutdown of the test client
+      /// and once the shutdown has finished, the OK status is sent to terminate
+      /// this RPC.
       /// </summary>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus> RunClient(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return RunClient(new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  Start client with specified workload.
-      ///  First request sent specifies the ClientConfig followed by ClientStatus
-      ///  response. After that, a "Mark" can be sent anytime to request the latest
-      ///  stats. Closing the stream will initiate shutdown of the test client
-      ///  and once the shutdown has finished, the OK status is sent to terminate
-      ///  this RPC.
+      /// Start client with specified workload.
+      /// First request sent specifies the ClientConfig followed by ClientStatus
+      /// response. After that, a "Mark" can be sent anytime to request the latest
+      /// stats. Closing the stream will initiate shutdown of the test client
+      /// and once the shutdown has finished, the OK status is sent to terminate
+      /// this RPC.
       /// </summary>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus> RunClient(CallOptions options)
       {
         return CallInvoker.AsyncDuplexStreamingCall(__Method_RunClient, null, options);
       }
       /// <summary>
-      ///  Just return the core count - unary call
+      /// Just return the core count - unary call
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Grpc.Testing.CoreResponse CoreCount(global::Grpc.Testing.CoreRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return CoreCount(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  Just return the core count - unary call
+      /// Just return the core count - unary call
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Grpc.Testing.CoreResponse CoreCount(global::Grpc.Testing.CoreRequest request, CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_CoreCount, null, options, request);
       }
       /// <summary>
-      ///  Just return the core count - unary call
+      /// Just return the core count - unary call
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Grpc.Testing.CoreResponse> CoreCountAsync(global::Grpc.Testing.CoreRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return CoreCountAsync(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  Just return the core count - unary call
+      /// Just return the core count - unary call
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Grpc.Testing.CoreResponse> CoreCountAsync(global::Grpc.Testing.CoreRequest request, CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_CoreCount, null, options, request);
       }
       /// <summary>
-      ///  Quit this worker
+      /// Quit this worker
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Grpc.Testing.Void QuitWorker(global::Grpc.Testing.Void request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return QuitWorker(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  Quit this worker
+      /// Quit this worker
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Grpc.Testing.Void QuitWorker(global::Grpc.Testing.Void request, CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_QuitWorker, null, options, request);
       }
       /// <summary>
-      ///  Quit this worker
+      /// Quit this worker
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Grpc.Testing.Void> QuitWorkerAsync(global::Grpc.Testing.Void request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return QuitWorkerAsync(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  Quit this worker
+      /// Quit this worker
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Grpc.Testing.Void> QuitWorkerAsync(global::Grpc.Testing.Void request, CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_QuitWorker, null, options, request);
@@ -405,6 +493,7 @@ namespace Grpc.Testing {
     }
 
     /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
     public static ServerServiceDefinition BindService(WorkerServiceBase serviceImpl)
     {
       return ServerServiceDefinition.CreateBuilder()
diff --git a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
index 43dbc2865f5b6c80933134e13a3cdac5c728edc0..0265f8e821ebe93fb62351562b26b21eaa55919d 100644
--- a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
+++ b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
@@ -42,8 +42,8 @@ using Grpc.Core;
 
 namespace Grpc.Testing {
   /// <summary>
-  ///  A simple service to test the various types of RPCs and experiment with
-  ///  performance with various types of payload.
+  /// A simple service to test the various types of RPCs and experiment with
+  /// performance with various types of payload.
   /// </summary>
   public static partial class TestService
   {
@@ -123,74 +123,101 @@ namespace Grpc.Testing {
     public abstract partial class TestServiceBase
     {
       /// <summary>
-      ///  One empty request followed by one empty response.
+      /// One empty request followed by one empty response.
       /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.Empty> EmptyCall(global::Grpc.Testing.Empty request, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
       }
 
       /// <summary>
-      ///  One request followed by one response.
+      /// One request followed by one response.
       /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.SimpleResponse> UnaryCall(global::Grpc.Testing.SimpleRequest request, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
       }
 
       /// <summary>
-      ///  One request followed by one response. Response has cache control
-      ///  headers set such that a caching HTTP proxy (such as GFE) can
-      ///  satisfy subsequent requests.
+      /// One request followed by one response. Response has cache control
+      /// headers set such that a caching HTTP proxy (such as GFE) can
+      /// satisfy subsequent requests.
       /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.SimpleResponse> CacheableUnaryCall(global::Grpc.Testing.SimpleRequest request, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
       }
 
       /// <summary>
-      ///  One request followed by a sequence of responses (streamed download).
-      ///  The server returns the payload with client desired type and sizes.
+      /// One request followed by a sequence of responses (streamed download).
+      /// The server returns the payload with client desired type and sizes.
       /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="responseStream">Used for sending responses back to the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>A task indicating completion of the handler.</returns>
       public virtual global::System.Threading.Tasks.Task StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, IServerStreamWriter<global::Grpc.Testing.StreamingOutputCallResponse> responseStream, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
       }
 
       /// <summary>
-      ///  A sequence of requests followed by one response (streamed upload).
-      ///  The server returns the aggregated size of client payload as the result.
+      /// A sequence of requests followed by one response (streamed upload).
+      /// The server returns the aggregated size of client payload as the result.
       /// </summary>
+      /// <param name="requestStream">Used for reading requests from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.StreamingInputCallResponse> StreamingInputCall(IAsyncStreamReader<global::Grpc.Testing.StreamingInputCallRequest> requestStream, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
       }
 
       /// <summary>
-      ///  A sequence of requests with each request served by the server immediately.
-      ///  As one request could lead to multiple responses, this interface
-      ///  demonstrates the idea of full duplexing.
+      /// A sequence of requests with each request served by the server immediately.
+      /// As one request could lead to multiple responses, this interface
+      /// demonstrates the idea of full duplexing.
       /// </summary>
+      /// <param name="requestStream">Used for reading requests from the client.</param>
+      /// <param name="responseStream">Used for sending responses back to the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>A task indicating completion of the handler.</returns>
       public virtual global::System.Threading.Tasks.Task FullDuplexCall(IAsyncStreamReader<global::Grpc.Testing.StreamingOutputCallRequest> requestStream, IServerStreamWriter<global::Grpc.Testing.StreamingOutputCallResponse> responseStream, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
       }
 
       /// <summary>
-      ///  A sequence of requests followed by a sequence of responses.
-      ///  The server buffers all the client requests and then serves them in order. A
-      ///  stream of responses are returned to the client when the server starts with
-      ///  first request.
+      /// A sequence of requests followed by a sequence of responses.
+      /// The server buffers all the client requests and then serves them in order. A
+      /// stream of responses are returned to the client when the server starts with
+      /// first request.
       /// </summary>
+      /// <param name="requestStream">Used for reading requests from the client.</param>
+      /// <param name="responseStream">Used for sending responses back to the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>A task indicating completion of the handler.</returns>
       public virtual global::System.Threading.Tasks.Task HalfDuplexCall(IAsyncStreamReader<global::Grpc.Testing.StreamingOutputCallRequest> requestStream, IServerStreamWriter<global::Grpc.Testing.StreamingOutputCallResponse> responseStream, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
       }
 
       /// <summary>
-      ///  The test server will not implement this method. It will be used
-      ///  to test the behavior when clients call unimplemented methods.
+      /// The test server will not implement this method. It will be used
+      /// to test the behavior when clients call unimplemented methods.
       /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.Empty> UnimplementedCall(global::Grpc.Testing.Empty request, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
@@ -222,195 +249,285 @@ namespace Grpc.Testing {
       }
 
       /// <summary>
-      ///  One empty request followed by one empty response.
+      /// One empty request followed by one empty response.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Grpc.Testing.Empty EmptyCall(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return EmptyCall(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  One empty request followed by one empty response.
+      /// One empty request followed by one empty response.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Grpc.Testing.Empty EmptyCall(global::Grpc.Testing.Empty request, CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_EmptyCall, null, options, request);
       }
       /// <summary>
-      ///  One empty request followed by one empty response.
+      /// One empty request followed by one empty response.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Grpc.Testing.Empty> EmptyCallAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return EmptyCallAsync(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  One empty request followed by one empty response.
+      /// One empty request followed by one empty response.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Grpc.Testing.Empty> EmptyCallAsync(global::Grpc.Testing.Empty request, CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_EmptyCall, null, options, request);
       }
       /// <summary>
-      ///  One request followed by one response.
+      /// One request followed by one response.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return UnaryCall(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  One request followed by one response.
+      /// One request followed by one response.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_UnaryCall, null, options, request);
       }
       /// <summary>
-      ///  One request followed by one response.
+      /// One request followed by one response.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return UnaryCallAsync(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  One request followed by one response.
+      /// One request followed by one response.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_UnaryCall, null, options, request);
       }
       /// <summary>
-      ///  One request followed by one response. Response has cache control
-      ///  headers set such that a caching HTTP proxy (such as GFE) can
-      ///  satisfy subsequent requests.
+      /// One request followed by one response. Response has cache control
+      /// headers set such that a caching HTTP proxy (such as GFE) can
+      /// satisfy subsequent requests.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Grpc.Testing.SimpleResponse CacheableUnaryCall(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return CacheableUnaryCall(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  One request followed by one response. Response has cache control
-      ///  headers set such that a caching HTTP proxy (such as GFE) can
-      ///  satisfy subsequent requests.
+      /// One request followed by one response. Response has cache control
+      /// headers set such that a caching HTTP proxy (such as GFE) can
+      /// satisfy subsequent requests.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Grpc.Testing.SimpleResponse CacheableUnaryCall(global::Grpc.Testing.SimpleRequest request, CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_CacheableUnaryCall, null, options, request);
       }
       /// <summary>
-      ///  One request followed by one response. Response has cache control
-      ///  headers set such that a caching HTTP proxy (such as GFE) can
-      ///  satisfy subsequent requests.
+      /// One request followed by one response. Response has cache control
+      /// headers set such that a caching HTTP proxy (such as GFE) can
+      /// satisfy subsequent requests.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> CacheableUnaryCallAsync(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return CacheableUnaryCallAsync(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  One request followed by one response. Response has cache control
-      ///  headers set such that a caching HTTP proxy (such as GFE) can
-      ///  satisfy subsequent requests.
+      /// One request followed by one response. Response has cache control
+      /// headers set such that a caching HTTP proxy (such as GFE) can
+      /// satisfy subsequent requests.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> CacheableUnaryCallAsync(global::Grpc.Testing.SimpleRequest request, CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_CacheableUnaryCall, null, options, request);
       }
       /// <summary>
-      ///  One request followed by a sequence of responses (streamed download).
-      ///  The server returns the payload with client desired type and sizes.
+      /// One request followed by a sequence of responses (streamed download).
+      /// The server returns the payload with client desired type and sizes.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncServerStreamingCall<global::Grpc.Testing.StreamingOutputCallResponse> StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return StreamingOutputCall(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  One request followed by a sequence of responses (streamed download).
-      ///  The server returns the payload with client desired type and sizes.
+      /// One request followed by a sequence of responses (streamed download).
+      /// The server returns the payload with client desired type and sizes.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncServerStreamingCall<global::Grpc.Testing.StreamingOutputCallResponse> StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, CallOptions options)
       {
         return CallInvoker.AsyncServerStreamingCall(__Method_StreamingOutputCall, null, options, request);
       }
       /// <summary>
-      ///  A sequence of requests followed by one response (streamed upload).
-      ///  The server returns the aggregated size of client payload as the result.
+      /// A sequence of requests followed by one response (streamed upload).
+      /// The server returns the aggregated size of client payload as the result.
       /// </summary>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncClientStreamingCall<global::Grpc.Testing.StreamingInputCallRequest, global::Grpc.Testing.StreamingInputCallResponse> StreamingInputCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return StreamingInputCall(new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  A sequence of requests followed by one response (streamed upload).
-      ///  The server returns the aggregated size of client payload as the result.
+      /// A sequence of requests followed by one response (streamed upload).
+      /// The server returns the aggregated size of client payload as the result.
       /// </summary>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncClientStreamingCall<global::Grpc.Testing.StreamingInputCallRequest, global::Grpc.Testing.StreamingInputCallResponse> StreamingInputCall(CallOptions options)
       {
         return CallInvoker.AsyncClientStreamingCall(__Method_StreamingInputCall, null, options);
       }
       /// <summary>
-      ///  A sequence of requests with each request served by the server immediately.
-      ///  As one request could lead to multiple responses, this interface
-      ///  demonstrates the idea of full duplexing.
+      /// A sequence of requests with each request served by the server immediately.
+      /// As one request could lead to multiple responses, this interface
+      /// demonstrates the idea of full duplexing.
       /// </summary>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> FullDuplexCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return FullDuplexCall(new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  A sequence of requests with each request served by the server immediately.
-      ///  As one request could lead to multiple responses, this interface
-      ///  demonstrates the idea of full duplexing.
+      /// A sequence of requests with each request served by the server immediately.
+      /// As one request could lead to multiple responses, this interface
+      /// demonstrates the idea of full duplexing.
       /// </summary>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> FullDuplexCall(CallOptions options)
       {
         return CallInvoker.AsyncDuplexStreamingCall(__Method_FullDuplexCall, null, options);
       }
       /// <summary>
-      ///  A sequence of requests followed by a sequence of responses.
-      ///  The server buffers all the client requests and then serves them in order. A
-      ///  stream of responses are returned to the client when the server starts with
-      ///  first request.
+      /// A sequence of requests followed by a sequence of responses.
+      /// The server buffers all the client requests and then serves them in order. A
+      /// stream of responses are returned to the client when the server starts with
+      /// first request.
       /// </summary>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> HalfDuplexCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return HalfDuplexCall(new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  A sequence of requests followed by a sequence of responses.
-      ///  The server buffers all the client requests and then serves them in order. A
-      ///  stream of responses are returned to the client when the server starts with
-      ///  first request.
+      /// A sequence of requests followed by a sequence of responses.
+      /// The server buffers all the client requests and then serves them in order. A
+      /// stream of responses are returned to the client when the server starts with
+      /// first request.
       /// </summary>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> HalfDuplexCall(CallOptions options)
       {
         return CallInvoker.AsyncDuplexStreamingCall(__Method_HalfDuplexCall, null, options);
       }
       /// <summary>
-      ///  The test server will not implement this method. It will be used
-      ///  to test the behavior when clients call unimplemented methods.
+      /// The test server will not implement this method. It will be used
+      /// to test the behavior when clients call unimplemented methods.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return UnimplementedCall(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  The test server will not implement this method. It will be used
-      ///  to test the behavior when clients call unimplemented methods.
+      /// The test server will not implement this method. It will be used
+      /// to test the behavior when clients call unimplemented methods.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_UnimplementedCall, null, options, request);
       }
       /// <summary>
-      ///  The test server will not implement this method. It will be used
-      ///  to test the behavior when clients call unimplemented methods.
+      /// The test server will not implement this method. It will be used
+      /// to test the behavior when clients call unimplemented methods.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return UnimplementedCallAsync(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  The test server will not implement this method. It will be used
-      ///  to test the behavior when clients call unimplemented methods.
+      /// The test server will not implement this method. It will be used
+      /// to test the behavior when clients call unimplemented methods.
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_UnimplementedCall, null, options, request);
@@ -423,6 +540,7 @@ namespace Grpc.Testing {
     }
 
     /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
     public static ServerServiceDefinition BindService(TestServiceBase serviceImpl)
     {
       return ServerServiceDefinition.CreateBuilder()
@@ -438,8 +556,8 @@ namespace Grpc.Testing {
 
   }
   /// <summary>
-  ///  A simple service NOT implemented at servers so clients can test for
-  ///  that case.
+  /// A simple service NOT implemented at servers so clients can test for
+  /// that case.
   /// </summary>
   public static partial class UnimplementedService
   {
@@ -464,8 +582,11 @@ namespace Grpc.Testing {
     public abstract partial class UnimplementedServiceBase
     {
       /// <summary>
-      ///  A call that no server should implement
+      /// A call that no server should implement
       /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.Empty> UnimplementedCall(global::Grpc.Testing.Empty request, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
@@ -497,29 +618,45 @@ namespace Grpc.Testing {
       }
 
       /// <summary>
-      ///  A call that no server should implement
+      /// A call that no server should implement
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return UnimplementedCall(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  A call that no server should implement
+      /// A call that no server should implement
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
       public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_UnimplementedCall, null, options, request);
       }
       /// <summary>
-      ///  A call that no server should implement
+      /// A call that no server should implement
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return UnimplementedCallAsync(request, new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  A call that no server should implement
+      /// A call that no server should implement
       /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_UnimplementedCall, null, options, request);
@@ -532,6 +669,7 @@ namespace Grpc.Testing {
     }
 
     /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
     public static ServerServiceDefinition BindService(UnimplementedServiceBase serviceImpl)
     {
       return ServerServiceDefinition.CreateBuilder()
@@ -540,7 +678,7 @@ namespace Grpc.Testing {
 
   }
   /// <summary>
-  ///  A service used to control reconnect server.
+  /// A service used to control reconnect server.
   /// </summary>
   public static partial class ReconnectService
   {
@@ -648,6 +786,7 @@ namespace Grpc.Testing {
     }
 
     /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
     public static ServerServiceDefinition BindService(ReconnectServiceBase serviceImpl)
     {
       return ServerServiceDefinition.CreateBuilder()
diff --git a/src/csharp/Grpc.Reflection.Tests/Grpc.Reflection.Tests.csproj b/src/csharp/Grpc.Reflection.Tests/Grpc.Reflection.Tests.csproj
index cebcf59ce847145174a33fbb3606d7f13c492d4f..c5918b194ef78b82784f08b4ef8648aedefb5dff 100644
--- a/src/csharp/Grpc.Reflection.Tests/Grpc.Reflection.Tests.csproj
+++ b/src/csharp/Grpc.Reflection.Tests/Grpc.Reflection.Tests.csproj
@@ -27,15 +27,6 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\ReleaseSigned</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <SignAssembly>True</SignAssembly>
-    <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
-  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Core" />
diff --git a/src/csharp/Grpc.Reflection/Grpc.Reflection.csproj b/src/csharp/Grpc.Reflection/Grpc.Reflection.csproj
index ea65998ce3e8a532d986a89e6c22c18d65f9877d..4e254a0b5397b15ddeb51ee8979476d4d6a9d131 100644
--- a/src/csharp/Grpc.Reflection/Grpc.Reflection.csproj
+++ b/src/csharp/Grpc.Reflection/Grpc.Reflection.csproj
@@ -28,15 +28,6 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\ReleaseSigned</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <SignAssembly>True</SignAssembly>
-    <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
-  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Core" />
diff --git a/src/csharp/Grpc.Reflection/ReflectionGrpc.cs b/src/csharp/Grpc.Reflection/ReflectionGrpc.cs
index 1b6f96ce7c7749bb6ecd0f74d1e4d2815fa60f09..5bd7558be58e2bad6f0877af7cb8f9d77966b944 100644
--- a/src/csharp/Grpc.Reflection/ReflectionGrpc.cs
+++ b/src/csharp/Grpc.Reflection/ReflectionGrpc.cs
@@ -64,9 +64,13 @@ namespace Grpc.Reflection.V1Alpha {
     public abstract partial class ServerReflectionBase
     {
       /// <summary>
-      ///  The reflection service is structured as a bidirectional stream, ensuring
-      ///  all related requests go to a single server.
+      /// The reflection service is structured as a bidirectional stream, ensuring
+      /// all related requests go to a single server.
       /// </summary>
+      /// <param name="requestStream">Used for reading requests from the client.</param>
+      /// <param name="responseStream">Used for sending responses back to the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>A task indicating completion of the handler.</returns>
       public virtual global::System.Threading.Tasks.Task ServerReflectionInfo(IAsyncStreamReader<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest> requestStream, IServerStreamWriter<global::Grpc.Reflection.V1Alpha.ServerReflectionResponse> responseStream, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
@@ -98,17 +102,23 @@ namespace Grpc.Reflection.V1Alpha {
       }
 
       /// <summary>
-      ///  The reflection service is structured as a bidirectional stream, ensuring
-      ///  all related requests go to a single server.
+      /// The reflection service is structured as a bidirectional stream, ensuring
+      /// all related requests go to a single server.
       /// </summary>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncDuplexStreamingCall<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest, global::Grpc.Reflection.V1Alpha.ServerReflectionResponse> ServerReflectionInfo(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return ServerReflectionInfo(new CallOptions(headers, deadline, cancellationToken));
       }
       /// <summary>
-      ///  The reflection service is structured as a bidirectional stream, ensuring
-      ///  all related requests go to a single server.
+      /// The reflection service is structured as a bidirectional stream, ensuring
+      /// all related requests go to a single server.
       /// </summary>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
       public virtual AsyncDuplexStreamingCall<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest, global::Grpc.Reflection.V1Alpha.ServerReflectionResponse> ServerReflectionInfo(CallOptions options)
       {
         return CallInvoker.AsyncDuplexStreamingCall(__Method_ServerReflectionInfo, null, options);
@@ -121,6 +131,7 @@ namespace Grpc.Reflection.V1Alpha {
     }
 
     /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
     public static ServerServiceDefinition BindService(ServerReflectionBase serviceImpl)
     {
       return ServerServiceDefinition.CreateBuilder()
diff --git a/src/csharp/Grpc.sln b/src/csharp/Grpc.sln
index 2e6a8fd4354c6da857757a685ccbd71659669e63..179e7313809bff76f92bd1130c93e0a206975938 100644
--- a/src/csharp/Grpc.sln
+++ b/src/csharp/Grpc.sln
@@ -44,105 +44,72 @@ Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
 		Release|Any CPU = Release|Any CPU
-		ReleaseSigned|Any CPU = ReleaseSigned|Any CPU
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
 		{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Release|Any CPU.Build.0 = Release|Any CPU
-		{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
-		{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
 		{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|Any CPU.Build.0 = Release|Any CPU
-		{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
-		{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
 		{4F18CF52-B3DB-4A77-97C5-7F7F4B6C1715}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{4F18CF52-B3DB-4A77-97C5-7F7F4B6C1715}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{4F18CF52-B3DB-4A77-97C5-7F7F4B6C1715}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{4F18CF52-B3DB-4A77-97C5-7F7F4B6C1715}.Release|Any CPU.Build.0 = Release|Any CPU
-		{4F18CF52-B3DB-4A77-97C5-7F7F4B6C1715}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
-		{4F18CF52-B3DB-4A77-97C5-7F7F4B6C1715}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
 		{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|Any CPU.Build.0 = Release|Any CPU
-		{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
-		{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
 		{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|Any CPU.Build.0 = Release|Any CPU
-		{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
-		{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
 		{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|Any CPU.Build.0 = Release|Any CPU
-		{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
-		{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
 		{A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Release|Any CPU.Build.0 = Release|Any CPU
-		{A654F3B8-E859-4E6A-B30D-227527DBEF0D}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
-		{A654F3B8-E859-4E6A-B30D-227527DBEF0D}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
 		{AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Release|Any CPU.Build.0 = Release|Any CPU
-		{AA5E328A-8835-49D7-98ED-C29F2B3049F0}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
-		{AA5E328A-8835-49D7-98ED-C29F2B3049F0}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
 		{ADEBA147-80AE-4710-82E9-5B7F93690266}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{ADEBA147-80AE-4710-82E9-5B7F93690266}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{ADEBA147-80AE-4710-82E9-5B7F93690266}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{ADEBA147-80AE-4710-82E9-5B7F93690266}.Release|Any CPU.Build.0 = Release|Any CPU
-		{ADEBA147-80AE-4710-82E9-5B7F93690266}.ReleaseSigned|Any CPU.ActiveCfg = Release|Any CPU
-		{ADEBA147-80AE-4710-82E9-5B7F93690266}.ReleaseSigned|Any CPU.Build.0 = Release|Any CPU
 		{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Release|Any CPU.Build.0 = Release|Any CPU
-		{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
-		{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
 		{B82B7DFE-7F7B-40EF-B3D6-064FF2B01294}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{B82B7DFE-7F7B-40EF-B3D6-064FF2B01294}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{B82B7DFE-7F7B-40EF-B3D6-064FF2B01294}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{B82B7DFE-7F7B-40EF-B3D6-064FF2B01294}.Release|Any CPU.Build.0 = Release|Any CPU
-		{B82B7DFE-7F7B-40EF-B3D6-064FF2B01294}.ReleaseSigned|Any CPU.ActiveCfg = Release|Any CPU
-		{B82B7DFE-7F7B-40EF-B3D6-064FF2B01294}.ReleaseSigned|Any CPU.Build.0 = Release|Any CPU
 		{B88F91D6-436D-4C78-8B99-47800FA8DE03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{B88F91D6-436D-4C78-8B99-47800FA8DE03}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{B88F91D6-436D-4C78-8B99-47800FA8DE03}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{B88F91D6-436D-4C78-8B99-47800FA8DE03}.Release|Any CPU.Build.0 = Release|Any CPU
-		{B88F91D6-436D-4C78-8B99-47800FA8DE03}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
-		{B88F91D6-436D-4C78-8B99-47800FA8DE03}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
 		{BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|Any CPU.Build.0 = Release|Any CPU
-		{BF62FE08-373A-43D6-9D73-41CAA38B7011}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
-		{BF62FE08-373A-43D6-9D73-41CAA38B7011}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
 		{C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{C61154BA-DD4A-4838-8420-0162A28925E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{C61154BA-DD4A-4838-8420-0162A28925E0}.Release|Any CPU.Build.0 = Release|Any CPU
-		{C61154BA-DD4A-4838-8420-0162A28925E0}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
-		{C61154BA-DD4A-4838-8420-0162A28925E0}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
 		{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|Any CPU.Build.0 = Release|Any CPU
-		{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
-		{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
 		{F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Release|Any CPU.Build.0 = Release|Any CPU
-		{F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
-		{F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
 	EndGlobalSection
 	GlobalSection(NestedProjects) = preSolution
 	EndGlobalSection
diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c
index 37d04eb10003adf778bee8d3e3ae084ea271870a..946f5872c0ef2a218545539959693d31817c8d24 100644
--- a/src/csharp/ext/grpc_csharp_ext.c
+++ b/src/csharp/ext/grpc_csharp_ext.c
@@ -521,8 +521,8 @@ GPR_EXPORT void GPR_CALLTYPE grpcsharp_call_destroy(grpc_call *call) {
 
 GPR_EXPORT grpc_call_error GPR_CALLTYPE
 grpcsharp_call_start_unary(grpc_call *call, grpcsharp_batch_context *ctx,
-                           const char *send_buffer, size_t send_buffer_len,
-                           grpc_metadata_array *initial_metadata, uint32_t write_flags) {
+                           const char *send_buffer, size_t send_buffer_len, uint32_t write_flags,
+                           grpc_metadata_array *initial_metadata, uint32_t initial_metadata_flags) {
   /* TODO: don't use magic number */
   grpc_op ops[6];
   memset(ops, 0, sizeof(ops));
@@ -532,7 +532,7 @@ grpcsharp_call_start_unary(grpc_call *call, grpcsharp_batch_context *ctx,
   ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
   ops[0].data.send_initial_metadata.metadata =
       ctx->send_initial_metadata.metadata;
-  ops[0].flags = 0;
+  ops[0].flags = initial_metadata_flags;
   ops[0].reserved = NULL;
 
   ops[1].op = GRPC_OP_SEND_MESSAGE;
@@ -575,7 +575,8 @@ grpcsharp_call_start_unary(grpc_call *call, grpcsharp_batch_context *ctx,
 GPR_EXPORT grpc_call_error GPR_CALLTYPE
 grpcsharp_call_start_client_streaming(grpc_call *call,
                                       grpcsharp_batch_context *ctx,
-                                      grpc_metadata_array *initial_metadata) {
+                                      grpc_metadata_array *initial_metadata,
+                                      uint32_t initial_metadata_flags) {
   /* TODO: don't use magic number */
   grpc_op ops[4];
   memset(ops, 0, sizeof(ops));
@@ -585,7 +586,7 @@ grpcsharp_call_start_client_streaming(grpc_call *call,
   ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
   ops[0].data.send_initial_metadata.metadata =
       ctx->send_initial_metadata.metadata;
-  ops[0].flags = 0;
+  ops[0].flags = initial_metadata_flags;
   ops[0].reserved = NULL;
 
   ops[1].op = GRPC_OP_RECV_INITIAL_METADATA;
@@ -617,7 +618,8 @@ grpcsharp_call_start_client_streaming(grpc_call *call,
 
 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_server_streaming(
     grpc_call *call, grpcsharp_batch_context *ctx, const char *send_buffer,
-    size_t send_buffer_len, grpc_metadata_array *initial_metadata, uint32_t write_flags) {
+    size_t send_buffer_len, uint32_t write_flags,
+    grpc_metadata_array *initial_metadata, uint32_t initial_metadata_flags) {
   /* TODO: don't use magic number */
   grpc_op ops[4];
   memset(ops, 0, sizeof(ops));
@@ -627,7 +629,7 @@ GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_server_streaming(
   ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
   ops[0].data.send_initial_metadata.metadata =
       ctx->send_initial_metadata.metadata;
-  ops[0].flags = 0;
+  ops[0].flags = initial_metadata_flags;
   ops[0].reserved = NULL;
 
   ops[1].op = GRPC_OP_SEND_MESSAGE;
@@ -660,7 +662,8 @@ GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_server_streaming(
 GPR_EXPORT grpc_call_error GPR_CALLTYPE
 grpcsharp_call_start_duplex_streaming(grpc_call *call,
                                       grpcsharp_batch_context *ctx,
-                                      grpc_metadata_array *initial_metadata) {
+                                      grpc_metadata_array *initial_metadata,
+                                      uint32_t initial_metadata_flags) {
   /* TODO: don't use magic number */
   grpc_op ops[2];
   memset(ops, 0, sizeof(ops));
@@ -670,7 +673,7 @@ grpcsharp_call_start_duplex_streaming(grpc_call *call,
   ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
   ops[0].data.send_initial_metadata.metadata =
       ctx->send_initial_metadata.metadata;
-  ops[0].flags = 0;
+  ops[0].flags = initial_metadata_flags;
   ops[0].reserved = NULL;
 
   ops[1].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
diff --git a/src/node/ext/completion_queue.h b/src/node/ext/completion_queue.h
index bf280f768b0680a652fdb36f1a74240719f17878..9b01028ef173a42c9c0e2eaef9352702c787647f 100644
--- a/src/node/ext/completion_queue.h
+++ b/src/node/ext/completion_queue.h
@@ -32,6 +32,7 @@
  */
 
 #include <v8.h>
+#include <grpc/grpc.h>
 
 namespace grpc {
 namespace node {
diff --git a/src/node/ext/completion_queue_async_worker.cc b/src/node/ext/completion_queue_threadpool.cc
similarity index 72%
rename from src/node/ext/completion_queue_async_worker.cc
rename to src/node/ext/completion_queue_threadpool.cc
index f5e03b277b36bda82f244f16de9041d856722e25..6302e7a103b112378b36d87fbacf0c67b5d56b8b 100644
--- a/src/node/ext/completion_queue_async_worker.cc
+++ b/src/node/ext/completion_queue_threadpool.cc
@@ -31,18 +31,63 @@
  *
  */
 
+/* I don't like using #ifndef, but I don't see a better way to do this */
+#ifndef GRPC_UV
+
 #include <node.h>
 #include <nan.h>
 
 #include "grpc/grpc.h"
 #include "grpc/support/log.h"
 #include "grpc/support/time.h"
-#include "completion_queue_async_worker.h"
+#include "completion_queue.h"
 #include "call.h"
 
 namespace grpc {
 namespace node {
 
+namespace {
+
+/* A worker that asynchronously calls completion_queue_next, and queues onto the
+   node event loop a call to the function stored in the event's tag. */
+class CompletionQueueAsyncWorker : public Nan::AsyncWorker {
+ public:
+  CompletionQueueAsyncWorker();
+
+  ~CompletionQueueAsyncWorker();
+  /* Calls completion_queue_next with the provided deadline, and stores the
+     event if there was one or sets an error message if there was not */
+  void Execute();
+
+  /* Returns the completion queue attached to this class */
+  static grpc_completion_queue *GetQueue();
+
+  /* Convenience function to create a worker with the given arguments and queue
+     it to run asynchronously */
+  static void Next();
+
+  /* Initialize the CompletionQueueAsyncWorker class */
+  static void Init(v8::Local<v8::Object> exports);
+
+ protected:
+  /* Called when Execute has succeeded (completed without setting an error
+     message). Calls the saved callback with the event that came from
+     completion_queue_next */
+  void HandleOKCallback();
+
+  void HandleErrorCallback();
+
+ private:
+  grpc_event result;
+
+  static grpc_completion_queue *queue;
+
+  // Number of grpc_completion_queue_next calls in the thread pool
+  static int current_threads;
+  // Number of grpc_completion_queue_next calls waiting to enter the thread pool
+  static int waiting_next_calls;
+};
+
 const int max_queue_threads = 2;
 
 using v8::Function;
@@ -137,5 +182,21 @@ void CompletionQueueAsyncWorker::HandleErrorCallback() {
   DestroyTag(result.tag);
 }
 
+}  // namespace
+
+grpc_completion_queue *GetCompletionQueue() {
+  return CompletionQueueAsyncWorker::GetQueue();
+}
+
+void CompletionQueueNext() {
+  CompletionQueueAsyncWorker::Next();
+}
+
+void CompletionQueueInit(Local<Object> exports) {
+  CompletionQueueAsyncWorker::Init(exports);
+}
+
 }  // namespace node
 }  // namespace grpc
+
+#endif  /* GRPC_UV */
diff --git a/src/node/ext/completion_queue.cc b/src/node/ext/completion_queue_uv.cc
similarity index 93%
rename from src/node/ext/completion_queue.cc
rename to src/node/ext/completion_queue_uv.cc
index fcfa77b39ce040b7a922c883aae350e908813fe0..615973a6c9b8f0e67fc02bc2a068bf74b4098906 100644
--- a/src/node/ext/completion_queue.cc
+++ b/src/node/ext/completion_queue_uv.cc
@@ -31,6 +31,8 @@
  *
  */
 
+#ifdef GRPC_UV
+
 #include <uv.h>
 #include <node.h>
 #include <v8.h>
@@ -38,7 +40,6 @@
 
 #include "call.h"
 #include "completion_queue.h"
-#include "completion_queue_async_worker.h"
 
 namespace grpc {
 namespace node {
@@ -81,34 +82,24 @@ void drain_completion_queue(uv_prepare_t *handle) {
 }
 
 grpc_completion_queue *GetCompletionQueue() {
-#ifdef GRPC_UV
   return queue;
-#else
-  return CompletionQueueAsyncWorker::GetQueue();
-#endif
 }
 
 void CompletionQueueNext() {
-#ifdef GRPC_UV
   if (pending_batches == 0) {
     GPR_ASSERT(!uv_is_active((uv_handle_t *)&prepare));
     uv_prepare_start(&prepare, drain_completion_queue);
   }
   pending_batches++;
-#else
-  CompletionQueueAsyncWorker::Next();
-#endif
 }
 
 void CompletionQueueInit(Local<Object> exports) {
-#ifdef GRPC_UV
   queue = grpc_completion_queue_create(NULL);
   uv_prepare_init(uv_default_loop(), &prepare);
   pending_batches = 0;
-#else
-  CompletionQueueAsyncWorker::Init(exports);
-#endif
 }
 
 }  // namespace node
 }  // namespace grpc
+
+#endif /* GRPC_UV */
diff --git a/src/node/interop/interop_client.js b/src/node/interop/interop_client.js
index 46ddecfb1f630711bffac1eac0240488784d6f57..75cfb41342faaa9cc50c91ee9d6b82f7a8540416 100644
--- a/src/node/interop/interop_client.js
+++ b/src/node/interop/interop_client.js
@@ -312,6 +312,9 @@ function customMetadata(client, done) {
     }
   };
   var streaming_arg = {
+    response_parameters: [
+     {size: 314159}
+    ],
     payload: {
       body: zeroBuffer(271828)
     }
diff --git a/src/node/performance/worker_service_impl.js b/src/node/performance/worker_service_impl.js
index 3f317f64294bdfaa4b69e8146dcc6e1e499a77a9..38888a7219a6ca8123709d0956c20162106dbb2c 100644
--- a/src/node/performance/worker_service_impl.js
+++ b/src/node/performance/worker_service_impl.js
@@ -55,9 +55,8 @@ module.exports = function WorkerServiceImpl(benchmark_impl, server) {
   }
 
   this.quitWorker = function quitWorker(call, callback) {
-    server.tryShutdown(function() {
-      callback(null, {});
-    });
+    callback(null, {});
+    server.tryShutdown(function() {});
   };
 
   this.runClient = function runClient(call) {
diff --git a/src/node/src/client.js b/src/node/src/client.js
index 9c1562e8b8cca1a135bc38001d288d5e9974bdcd..134ef239c2560e10ffaf382bcb5e4897c8c355ce 100644
--- a/src/node/src/client.js
+++ b/src/node/src/client.js
@@ -99,7 +99,18 @@ function ClientWritableStream(call, serialize) {
 function _write(chunk, encoding, callback) {
   /* jshint validthis: true */
   var batch = {};
-  var message = this.serialize(chunk);
+  var message;
+  try {
+    message = this.serialize(chunk);
+  } catch (e) {
+    /* Sending this error to the server and emitting it immediately on the
+       client may put the call in a slightly weird state on the client side,
+       but passing an object that causes a serialization failure is a misuse
+       of the API anyway, so that's OK. The primary purpose here is to give the
+       programmer a useful error and to stop the stream properly */
+    this.call.cancelWithStatus(grpc.status.INTERNAL, "Serialization failure");
+    callback(e);
+  }
   if (_.isFinite(encoding)) {
     /* Attach the encoding if it is a finite number. This is the closest we
      * can get to checking that it is valid flags */
@@ -184,14 +195,15 @@ function _emitStatusIfDone() {
     } else {
       status = this.received_status;
     }
-    this.emit('status', status);
-    if (status.code !== grpc.status.OK) {
+    if (status.code === grpc.status.OK) {
+      this.push(null);
+    } else {
       var error = new Error(status.details);
       error.code = status.code;
       error.metadata = status.metadata;
       this.emit('error', error);
-      return;
     }
+    this.emit('status', status);
   }
 }
 
@@ -224,9 +236,11 @@ function _read(size) {
     } catch (e) {
       self._readsDone({code: grpc.status.INTERNAL,
                        details: 'Failed to parse server response'});
+      return;
     }
     if (data === null) {
       self._readsDone();
+      return;
     }
     if (self.push(deserialized) && data !== null) {
       var read_batch = {};
@@ -396,6 +410,8 @@ function makeUnaryRequestFunction(method, serialize, deserialize) {
       var status = response.status;
       var error;
       var deserialized;
+      emitter.emit('metadata', Metadata._fromCoreRepresentation(
+          response.metadata));
       if (status.code === grpc.status.OK) {
         if (err) {
           // Got a batch error, but OK status. Something went wrong
@@ -423,8 +439,6 @@ function makeUnaryRequestFunction(method, serialize, deserialize) {
         args.callback(null, deserialized);
       }
       emitter.emit('status', status);
-      emitter.emit('metadata', Metadata._fromCoreRepresentation(
-          response.metadata));
     });
     return emitter;
   }
diff --git a/src/node/src/server.js b/src/node/src/server.js
index b3b414969ab84941ff950e4a33d0cbc9a86ae9c3..da9c6b2d7ff0983f09f1000e609f9f3d5275bc1a 100644
--- a/src/node/src/server.js
+++ b/src/node/src/server.js
@@ -127,7 +127,14 @@ function sendUnaryResponse(call, value, serialize, metadata, flags) {
         (new Metadata())._getCoreRepresentation();
     call.metadataSent = true;
   }
-  var message = serialize(value);
+  var message;
+  try {
+    message = serialize(value);
+  } catch (e) {
+    e.code = grpc.status.INTERNAL;
+    handleError(e);
+    return;
+  }
   message.grpcWriteFlags = flags;
   end_batch[grpc.opType.SEND_MESSAGE] = message;
   end_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = status;
@@ -278,7 +285,14 @@ function _write(chunk, encoding, callback) {
         (new Metadata())._getCoreRepresentation();
     this.call.metadataSent = true;
   }
-  var message = this.serialize(chunk);
+  var message;
+  try {
+    message = this.serialize(chunk);
+  } catch (e) {
+    e.code = grpc.status.INTERNAL;
+    callback(e);
+    return;
+  }
   if (_.isFinite(encoding)) {
     /* Attach the encoding if it is a finite number. This is the closest we
      * can get to checking that it is valid flags */
diff --git a/src/node/test/surface_test.js b/src/node/test/surface_test.js
index d8b36dc55cbaa0ee4b4d11d405b0115104e3ad85..e429a3648ba1d60023f2fdb057b5c0caba561bd1 100644
--- a/src/node/test/surface_test.js
+++ b/src/node/test/surface_test.js
@@ -179,20 +179,22 @@ describe('Server.prototype.addProtoService', function() {
       call.on('data', function(value) {
         assert.fail('No messages expected');
       });
-      call.on('status', function(status) {
-        assert.strictEqual(status.code, grpc.status.UNIMPLEMENTED);
+      call.on('error', function(err) {
+        assert.strictEqual(err.code, grpc.status.UNIMPLEMENTED);
         done();
       });
+      call.on('error', function(status) { /* Do nothing */ });
     });
     it('should respond to a bidi call with UNIMPLEMENTED', function(done) {
       var call = client.divMany();
       call.on('data', function(value) {
         assert.fail('No messages expected');
       });
-      call.on('status', function(status) {
-        assert.strictEqual(status.code, grpc.status.UNIMPLEMENTED);
+      call.on('error', function(err) {
+        assert.strictEqual(err.code, grpc.status.UNIMPLEMENTED);
         done();
       });
+      call.on('error', function(status) { /* Do nothing */ });
       call.end();
     });
   });
diff --git a/src/objective-c/!ProtoCompiler-gRPCPlugin.podspec b/src/objective-c/!ProtoCompiler-gRPCPlugin.podspec
index 6e594fd3edcdaad7f9f88504ead349809fc41524..bcc2bb612657759c77ac7a9285aff631b4f02bba 100644
--- a/src/objective-c/!ProtoCompiler-gRPCPlugin.podspec
+++ b/src/objective-c/!ProtoCompiler-gRPCPlugin.podspec
@@ -36,7 +36,7 @@ Pod::Spec.new do |s|
   # exclamation mark ensures that other "regular" pods will be able to find it as it'll be installed
   # before them.
   s.name     = '!ProtoCompiler-gRPCPlugin'
-  v = '1.0.1'
+  v = '1.0.2'
   s.version  = v
   s.summary  = 'The gRPC ProtoC plugin generates Objective-C files from .proto services.'
   s.description = <<-DESC
@@ -84,7 +84,10 @@ Pod::Spec.new do |s|
   repo = 'grpc/grpc'
   file = "grpc_objective_c_plugin-#{v}-macos-x86_64.zip"
   s.source = {
-    :http => "https://github.com/#{repo}/releases/download/v#{v}/#{file}",
+    # TODO(mxyan): Change back to "https://github.com/#{repo}/releases/download/v#{v}/#{file}" for
+    # next release
+    # :http => "https://github.com/#{repo}/releases/download/v#{v}/#{file}",
+    :http => "https://github.com/#{repo}/releases/download/objective-c-v#{v}/#{file}",
     # TODO(jcanizales): Add sha1 or sha256
     # :sha1 => '??',
   }
diff --git a/src/objective-c/GRPCClient/private/GRPCHost.m b/src/objective-c/GRPCClient/private/GRPCHost.m
index 31065cbf018ecced6ecb7283f4e85186fe66c8be..450bec36e088f9015c518ead48df9e6451fd1b29 100644
--- a/src/objective-c/GRPCClient/private/GRPCHost.m
+++ b/src/objective-c/GRPCClient/private/GRPCHost.m
@@ -50,7 +50,7 @@ NS_ASSUME_NONNULL_BEGIN
 
 // TODO(jcanizales): Generate the version in a standalone header, from templates. Like
 // templates/src/core/surface/version.c.template .
-#define GRPC_OBJC_VERSION_STRING @"1.0.1"
+#define GRPC_OBJC_VERSION_STRING @"1.0.2"
 
 static NSMutableDictionary *kHostCache;
 
diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
index 627b6aa86dd8474fe13231896ba27272d74c97c3..38fcae0299d943251cddc720ce7e478ad9513cf0 100644
--- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
+++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
@@ -112,7 +112,7 @@
 }
 
 - (void)dealloc {
-  gpr_free(_op.data.send_message);
+  grpc_byte_buffer_destroy(_op.data.send_message);
 }
 
 @end
diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m
index 4a92cc8e0d3ae93eb60526510cb8bfc0fd786692..4ba7badd866131c5c761949ea9e3c820c70988f3 100644
--- a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m
+++ b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m
@@ -316,7 +316,8 @@ static char *roots_filename;
 }
 
 - (void)testInvokeLargeRequest {
-  [self testIndividualCase:"invoke_large_request"];
+  // NOT SUPPORTED (frame size)
+  // [self testIndividualCase:"invoke_large_request"];
 }
 
 - (void)testLargeMetadata {
@@ -329,7 +330,8 @@ static char *roots_filename;
 }
 
 - (void)testMaxMessageLength {
-  [self testIndividualCase:"max_message_length"];
+  // NOT SUPPORTED (close_error)
+  // [self testIndividualCase:"max_message_length"];
 }
 
 - (void)testNegativeDeadline {
diff --git a/src/objective-c/tests/GRPCClientTests.m b/src/objective-c/tests/GRPCClientTests.m
index 77640525d58e674adc8f70b6198c439e22a1b46f..0b72a75f3d5b275cd5215a37d18b52548fcf775e 100644
--- a/src/objective-c/tests/GRPCClientTests.m
+++ b/src/objective-c/tests/GRPCClientTests.m
@@ -43,6 +43,8 @@
 #import <RxLibrary/GRXWriteable.h>
 #import <RxLibrary/GRXWriter+Immediate.h>
 
+#define TEST_TIMEOUT 16
+
 static NSString * const kHostAddress = @"localhost:5050";
 static NSString * const kPackage = @"grpc.testing";
 static NSString * const kService = @"TestService";
@@ -137,7 +139,7 @@ static GRPCProtoMethod *kUnaryCallMethod;
 
   [call startWithWriteable:responsesWriteable];
 
-  [self waitForExpectationsWithTimeout:4 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
 - (void)testEmptyRPC {
@@ -159,7 +161,7 @@ static GRPCProtoMethod *kUnaryCallMethod;
 
   [call startWithWriteable:responsesWriteable];
 
-  [self waitForExpectationsWithTimeout:8 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
 - (void)testSimpleProtoRPC {
@@ -191,7 +193,7 @@ static GRPCProtoMethod *kUnaryCallMethod;
 
   [call startWithWriteable:responsesWriteable];
 
-  [self waitForExpectationsWithTimeout:8 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
 - (void)testMetadata {
@@ -225,7 +227,7 @@ static GRPCProtoMethod *kUnaryCallMethod;
 
   [call startWithWriteable:responsesWriteable];
 
-  [self waitForExpectationsWithTimeout:4 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
 - (void)testResponseMetadataKVO {
@@ -256,7 +258,7 @@ static GRPCProtoMethod *kUnaryCallMethod;
   
   [call startWithWriteable:responsesWriteable];
   
-  [self waitForExpectationsWithTimeout:8 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
 - (void)testUserAgentPrefix {
@@ -287,7 +289,7 @@ static GRPCProtoMethod *kUnaryCallMethod;
 
   [call startWithWriteable:responsesWriteable];
 
-  [self waitForExpectationsWithTimeout:8 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
 // TODO(makarandd): Move to a different file that contains only unit tests
@@ -347,7 +349,7 @@ static GRPCProtoMethod *kUnaryCallMethod;
 
   [call startWithWriteable:responsesWriteable];
 
-  [self waitForExpectationsWithTimeout:8 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
 @end
diff --git a/src/objective-c/tests/InteropTests.m b/src/objective-c/tests/InteropTests.m
index 9804734d6aea330d826ac0e4019e8982942d2e7d..c3935ce1e094915eb017e130deea9d575e7fbcc2 100644
--- a/src/objective-c/tests/InteropTests.m
+++ b/src/objective-c/tests/InteropTests.m
@@ -46,6 +46,8 @@
 #import <RxLibrary/GRXBufferedPipe.h>
 #import <RxLibrary/GRXWriter+Immediate.h>
 
+#define TEST_TIMEOUT 32
+
 // Convenience constructors for the generated proto messages:
 
 @interface RMTStreamingOutputCallRequest (Constructors)
@@ -124,7 +126,7 @@
     [expectation fulfill];
   }];
 
-  [self waitForExpectationsWithTimeout:4 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
 - (void)testLargeUnaryRPC {
@@ -147,7 +149,7 @@
     [expectation fulfill];
   }];
 
-  [self waitForExpectationsWithTimeout:16 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
 - (void)test4MBResponsesAreAccepted {
@@ -164,7 +166,7 @@
     [expectation fulfill];
   }];
 
-  [self waitForExpectationsWithTimeout:16 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
 - (void)testResponsesOverMaxSizeFailWithActionableMessage {
@@ -185,7 +187,7 @@
     [expectation fulfill];
   }];
 
-  [self waitForExpectationsWithTimeout:16 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
 - (void)testResponsesOver4MBAreAcceptedIfOptedIn {
@@ -205,7 +207,7 @@
     [expectation fulfill];
   }];
 
-  [self waitForExpectationsWithTimeout:16 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
 - (void)testClientStreamingRPC {
@@ -238,7 +240,7 @@
     [expectation fulfill];
   }];
 
-  [self waitForExpectationsWithTimeout:8 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
 - (void)testServerStreamingRPC {
@@ -275,7 +277,7 @@
     }
   }];
 
-  [self waitForExpectationsWithTimeout:8 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
 - (void)testPingPongRPC {
@@ -319,7 +321,7 @@
       [expectation fulfill];
     }
   }];
-  [self waitForExpectationsWithTimeout:4 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
 #ifndef GRPC_COMPILE_WITH_CRONET
@@ -335,7 +337,7 @@
     XCTAssert(done, @"Unexpected response: %@", response);
     [expectation fulfill];
   }];
-  [self waitForExpectationsWithTimeout:2 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 #endif
 
@@ -361,7 +363,7 @@
   [call cancel];
   XCTAssertEqual(call.state, GRXWriterStateFinished);
 
-  [self waitForExpectationsWithTimeout:1 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
 - (void)testCancelAfterFirstResponseRPC {
@@ -396,7 +398,7 @@
     }
   }];
   [call start];
-  [self waitForExpectationsWithTimeout:8 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
 - (void)testRPCAfterClosingOpenConnections {
@@ -420,7 +422,7 @@
     }];
   }];
 
-  [self waitForExpectationsWithTimeout:4 handler:nil];
+  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
 @end
diff --git a/src/php/bin/generate_proto_php.sh b/src/php/bin/generate_proto_php.sh
index c558bc576981ac266bef54476ba7af931b8cf307..416fa9df97b246b3a95669ee7878b3991ad88f4a 100755
--- a/src/php/bin/generate_proto_php.sh
+++ b/src/php/bin/generate_proto_php.sh
@@ -39,10 +39,13 @@ protoc --proto_path=src/proto/math \
 
 # replace the Empty message with EmptyMessage
 # because Empty is a PHP reserved word
-sed -i 's/message Empty/message EmptyMessage/g' \
-    src/proto/grpc/testing/empty.proto
-sed -i 's/grpc\.testing\.Empty/grpc\.testing\.EmptyMessage/g' \
-    src/proto/grpc/testing/test.proto
+output_file=$(mktemp)
+sed 's/message Empty/message EmptyMessage/g' \
+  src/proto/grpc/testing/empty.proto > $output_file
+mv $output_file ./src/proto/grpc/testing/empty.proto
+sed 's/grpc\.testing\.Empty/grpc\.testing\.EmptyMessage/g' \
+  src/proto/grpc/testing/test.proto > $output_file
+mv $output_file ./src/proto/grpc/testing/test.proto
 
 protoc --proto_path=. \
        --php_out=src/php/tests/interop \
@@ -53,7 +56,10 @@ protoc --proto_path=. \
        src/proto/grpc/testing/test.proto
 
 # change it back
-sed -i 's/message EmptyMessage/message Empty/g' \
-    src/proto/grpc/testing/empty.proto
-sed -i 's/grpc\.testing\.EmptyMessage/grpc\.testing\.Empty/g' \
-    src/proto/grpc/testing/test.proto
+sed 's/message EmptyMessage/message Empty/g' \
+  src/proto/grpc/testing/empty.proto > $output_file
+mv $output_file ./src/proto/grpc/testing/empty.proto
+sed 's/grpc\.testing\.EmptyMessage/grpc\.testing\.Empty/g' \
+  src/proto/grpc/testing/test.proto > $output_file
+mv $output_file ./src/proto/grpc/testing/test.proto
+
diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c
index 3a49ea870813d55b06301cf0e7291f318be97a16..64b1137c2a454ba4bf2bbe782f0290b50787b200 100644
--- a/src/php/ext/grpc/call.c
+++ b/src/php/ext/grpc/call.c
@@ -478,6 +478,7 @@ PHP_METHOD(Call, startBatch) {
                                    true);
       add_property_zval(result, "status", recv_status);
       PHP_GRPC_DELREF(recv_status);
+      PHP_GRPC_FREE_STD_ZVAL(recv_status);
       break;
     case GRPC_OP_RECV_CLOSE_ON_SERVER:
       add_property_bool(result, "cancelled", cancelled);
@@ -501,6 +502,7 @@ cleanup:
     }
     if (ops[i].op == GRPC_OP_RECV_MESSAGE) {
       grpc_byte_buffer_destroy(message);
+      PHP_GRPC_FREE_STD_ZVAL(message_str);
     }
   }
   RETURN_DESTROY_ZVAL(result);
diff --git a/src/php/ext/grpc/call_credentials.c b/src/php/ext/grpc/call_credentials.c
index 3aafc3a19b667d761f188cb164a7b0f1c0907076..043817facd1c288f3c1bd6a2a461a6854ecfae89 100644
--- a/src/php/ext/grpc/call_credentials.c
+++ b/src/php/ext/grpc/call_credentials.c
@@ -111,9 +111,7 @@ PHP_METHOD(CallCredentials, createComposite) {
   grpc_call_credentials *creds =
       grpc_composite_call_credentials_create(cred1->wrapped, cred2->wrapped,
                                              NULL);
-  zval *creds_object;
-  PHP_GRPC_MAKE_STD_ZVAL(creds_object);
-  creds_object = grpc_php_wrap_call_credentials(creds TSRMLS_CC);
+  zval *creds_object = grpc_php_wrap_call_credentials(creds TSRMLS_CC);
   RETURN_DESTROY_ZVAL(creds_object);
 }
 
@@ -155,9 +153,7 @@ PHP_METHOD(CallCredentials, createFromPlugin) {
 
   grpc_call_credentials *creds =
     grpc_metadata_credentials_create_from_plugin(plugin, NULL);
-  zval *creds_object;
-  PHP_GRPC_MAKE_STD_ZVAL(creds_object);
-  creds_object = grpc_php_wrap_call_credentials(creds TSRMLS_CC);
+  zval *creds_object = grpc_php_wrap_call_credentials(creds TSRMLS_CC);
   RETURN_DESTROY_ZVAL(creds_object);
 }
 
@@ -211,6 +207,8 @@ void plugin_destroy_state(void *ptr) {
   plugin_state *state = (plugin_state *)ptr;
   efree(state->fci);
   efree(state->fci_cache);
+  PHP_GRPC_FREE_STD_ZVAL(state->fci->params);
+  PHP_GRPC_FREE_STD_ZVAL(state->fci->retval);
   efree(state);
 }
 
diff --git a/src/php/ext/grpc/channel_credentials.c b/src/php/ext/grpc/channel_credentials.c
index a32c4a4ea24272059c8b05e21ed256185acdf229..36a8223b8806fe124737f3eac8c14f9e993ddc63 100644
--- a/src/php/ext/grpc/channel_credentials.c
+++ b/src/php/ext/grpc/channel_credentials.c
@@ -121,9 +121,7 @@ PHP_METHOD(ChannelCredentials, setDefaultRootsPem) {
  */
 PHP_METHOD(ChannelCredentials, createDefault) {
   grpc_channel_credentials *creds = grpc_google_default_credentials_create();
-  zval *creds_object;
-  PHP_GRPC_MAKE_STD_ZVAL(creds_object);
-  creds_object = grpc_php_wrap_channel_credentials(creds TSRMLS_CC);
+  zval *creds_object = grpc_php_wrap_channel_credentials(creds TSRMLS_CC);
   RETURN_DESTROY_ZVAL(creds_object);
 }
 
@@ -160,9 +158,7 @@ PHP_METHOD(ChannelCredentials, createSsl) {
   grpc_channel_credentials *creds = grpc_ssl_credentials_create(
       pem_root_certs,
       pem_key_cert_pair.private_key == NULL ? NULL : &pem_key_cert_pair, NULL);
-  zval *creds_object;
-  PHP_GRPC_MAKE_STD_ZVAL(creds_object);
-  creds_object = grpc_php_wrap_channel_credentials(creds TSRMLS_CC);
+  zval *creds_object = grpc_php_wrap_channel_credentials(creds TSRMLS_CC);
   RETURN_DESTROY_ZVAL(creds_object);
 }
 
@@ -191,9 +187,7 @@ PHP_METHOD(ChannelCredentials, createComposite) {
   grpc_channel_credentials *creds =
       grpc_composite_channel_credentials_create(cred1->wrapped, cred2->wrapped,
                                                 NULL);
-  zval *creds_object;
-  PHP_GRPC_MAKE_STD_ZVAL(creds_object);
-  creds_object = grpc_php_wrap_channel_credentials(creds TSRMLS_CC);
+  zval *creds_object = grpc_php_wrap_channel_credentials(creds TSRMLS_CC);
   RETURN_DESTROY_ZVAL(creds_object);
 }
 
diff --git a/src/php/ext/grpc/php7_wrapper.h b/src/php/ext/grpc/php7_wrapper.h
index 1d7824113faf1be801726a73500e202607ca2669..72e982d6fc93aff101ae382dbad46b70f8c814cb 100644
--- a/src/php/ext/grpc/php7_wrapper.h
+++ b/src/php/ext/grpc/php7_wrapper.h
@@ -50,8 +50,13 @@
 
 #define PHP_GRPC_RETURN_STRING(val, dup) RETURN_STRING(val, dup)
 #define PHP_GRPC_MAKE_STD_ZVAL(pzv) MAKE_STD_ZVAL(pzv)
+#define PHP_GRPC_FREE_STD_ZVAL(pzv)
 #define PHP_GRPC_DELREF(zv) Z_DELREF_P(zv)
 
+#define RETURN_DESTROY_ZVAL(val) \
+  RETURN_ZVAL(val, false /* Don't execute copy constructor */, \
+              true /* Dealloc original before returning */)
+
 #define PHP_GRPC_WRAP_OBJECT_START(name) \
   typedef struct name { \
     zend_object std;
@@ -144,8 +149,15 @@ static inline int php_grpc_zend_hash_find(HashTable *ht, char *key, int len,
 #define PHP_GRPC_RETURN_STRING(val, dup) RETURN_STRING(val)
 #define PHP_GRPC_MAKE_STD_ZVAL(pzv) \
   pzv = (zval *)emalloc(sizeof(zval));
+#define PHP_GRPC_FREE_STD_ZVAL(pzv) efree(pzv);
 #define PHP_GRPC_DELREF(zv)
 
+#define RETURN_DESTROY_ZVAL(val) \
+  RETVAL_ZVAL(val, false /* Don't execute copy constructor */, \
+              true /* Dealloc original before returning */); \
+  efree(val); \
+  return
+
 #define PHP_GRPC_WRAP_OBJECT_START(name) \
   typedef struct name {
 #define PHP_GRPC_WRAP_OBJECT_END(name) \
diff --git a/src/php/ext/grpc/php_grpc.h b/src/php/ext/grpc/php_grpc.h
index e57a06545e6c3f3cbc83055899ceb1ed1d5f80aa..13083b0bc71cb28aadda4bb76a63cd85e1b2a31d 100644
--- a/src/php/ext/grpc/php_grpc.h
+++ b/src/php/ext/grpc/php_grpc.h
@@ -61,10 +61,6 @@ extern zend_module_entry grpc_module_entry;
 
 #include "grpc/grpc.h"
 
-#define RETURN_DESTROY_ZVAL(val)                               \
-  RETURN_ZVAL(val, false /* Don't execute copy constructor */, \
-              true /* Dealloc original before returning */)
-
 /* These are all function declarations */
 /* Code that runs at module initialization */
 PHP_MINIT_FUNCTION(grpc);
diff --git a/src/php/ext/grpc/server_credentials.c b/src/php/ext/grpc/server_credentials.c
index 1610c8ebb0982a6cdf6946289cd5e4b3a47a85d7..3e39fee246c4afe30f1e497ab57d9e2572568bac 100644
--- a/src/php/ext/grpc/server_credentials.c
+++ b/src/php/ext/grpc/server_credentials.c
@@ -113,9 +113,7 @@ PHP_METHOD(ServerCredentials, createSsl) {
   grpc_server_credentials *creds = grpc_ssl_server_credentials_create_ex(
       pem_root_certs, &pem_key_cert_pair, 1,
       GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE, NULL);
-  zval *creds_object;
-  PHP_GRPC_MAKE_STD_ZVAL(creds_object);
-  creds_object = grpc_php_wrap_server_credentials(creds TSRMLS_CC);
+  zval *creds_object = grpc_php_wrap_server_credentials(creds TSRMLS_CC);
   RETURN_DESTROY_ZVAL(creds_object);
 }
 
diff --git a/src/php/ext/grpc/timeval.c b/src/php/ext/grpc/timeval.c
index 945231b47feb5e26352aefa8d31c3ee9f4c9d38b..7ada915aaddbd113f01f807c9a3437af4d7619eb 100644
--- a/src/php/ext/grpc/timeval.c
+++ b/src/php/ext/grpc/timeval.c
@@ -115,11 +115,9 @@ PHP_METHOD(Timeval, add) {
   }
   wrapped_grpc_timeval *self = Z_WRAPPED_GRPC_TIMEVAL_P(getThis());
   wrapped_grpc_timeval *other = Z_WRAPPED_GRPC_TIMEVAL_P(other_obj);
-  zval *sum;
-  PHP_GRPC_MAKE_STD_ZVAL(sum);
-  sum =
-      grpc_php_wrap_timeval(gpr_time_add(self->wrapped, other->wrapped)
-                            TSRMLS_CC);
+  zval *sum =
+    grpc_php_wrap_timeval(gpr_time_add(self->wrapped, other->wrapped)
+                          TSRMLS_CC);
   RETURN_DESTROY_ZVAL(sum);
 }
 
@@ -141,11 +139,9 @@ PHP_METHOD(Timeval, subtract) {
   }
   wrapped_grpc_timeval *self = Z_WRAPPED_GRPC_TIMEVAL_P(getThis());
   wrapped_grpc_timeval *other = Z_WRAPPED_GRPC_TIMEVAL_P(other_obj);
-  zval *diff;
-  PHP_GRPC_MAKE_STD_ZVAL(diff);
-  diff =
-      grpc_php_wrap_timeval(gpr_time_sub(self->wrapped, other->wrapped)
-                            TSRMLS_CC);
+  zval *diff =
+    grpc_php_wrap_timeval(gpr_time_sub(self->wrapped, other->wrapped)
+                          TSRMLS_CC);
   RETURN_DESTROY_ZVAL(diff);
 }
 
@@ -206,9 +202,7 @@ PHP_METHOD(Timeval, similar) {
  * @return Timeval The current time
  */
 PHP_METHOD(Timeval, now) {
-  zval *now;
-  PHP_GRPC_MAKE_STD_ZVAL(now);
-  now = grpc_php_wrap_timeval(gpr_now(GPR_CLOCK_REALTIME) TSRMLS_CC);
+  zval *now = grpc_php_wrap_timeval(gpr_now(GPR_CLOCK_REALTIME) TSRMLS_CC);
   RETURN_DESTROY_ZVAL(now);
 }
 
@@ -217,13 +211,9 @@ PHP_METHOD(Timeval, now) {
  * @return Timeval Zero length time interval
  */
 PHP_METHOD(Timeval, zero) {
-  zval *grpc_php_timeval_zero;
-  PHP_GRPC_MAKE_STD_ZVAL(grpc_php_timeval_zero);
-  grpc_php_timeval_zero =
-      grpc_php_wrap_timeval(gpr_time_0(GPR_CLOCK_REALTIME) TSRMLS_CC);
-  RETURN_ZVAL(grpc_php_timeval_zero,
-              false, /* Copy original before returning? */
-              true /* Destroy original before returning */);
+  zval *grpc_php_timeval_zero =
+    grpc_php_wrap_timeval(gpr_time_0(GPR_CLOCK_REALTIME) TSRMLS_CC);
+  RETURN_DESTROY_ZVAL(grpc_php_timeval_zero);
 }
 
 /**
@@ -231,10 +221,8 @@ PHP_METHOD(Timeval, zero) {
  * @return Timeval Infinite future time value
  */
 PHP_METHOD(Timeval, infFuture) {
-  zval *grpc_php_timeval_inf_future;
-  PHP_GRPC_MAKE_STD_ZVAL(grpc_php_timeval_inf_future);
-  grpc_php_timeval_inf_future =
-      grpc_php_wrap_timeval(gpr_inf_future(GPR_CLOCK_REALTIME) TSRMLS_CC);
+  zval *grpc_php_timeval_inf_future =
+    grpc_php_wrap_timeval(gpr_inf_future(GPR_CLOCK_REALTIME) TSRMLS_CC);
   RETURN_DESTROY_ZVAL(grpc_php_timeval_inf_future);
 }
 
@@ -243,10 +231,8 @@ PHP_METHOD(Timeval, infFuture) {
  * @return Timeval Infinite past time value
  */
 PHP_METHOD(Timeval, infPast) {
-  zval *grpc_php_timeval_inf_past;
-  PHP_GRPC_MAKE_STD_ZVAL(grpc_php_timeval_inf_past);
-  grpc_php_timeval_inf_past =
-      grpc_php_wrap_timeval(gpr_inf_past(GPR_CLOCK_REALTIME) TSRMLS_CC);
+  zval *grpc_php_timeval_inf_past =
+    grpc_php_wrap_timeval(gpr_inf_past(GPR_CLOCK_REALTIME) TSRMLS_CC);
   RETURN_DESTROY_ZVAL(grpc_php_timeval_inf_past);
 }
 
diff --git a/src/php/lib/Grpc/AbstractCall.php b/src/php/lib/Grpc/AbstractCall.php
index c4d56790f7568e1d5c2355d2260e24a152f8a017..9f0b02b8bbcfb75044028aedeef3d709e5282a10 100644
--- a/src/php/lib/Grpc/AbstractCall.php
+++ b/src/php/lib/Grpc/AbstractCall.php
@@ -62,7 +62,7 @@ abstract class AbstractCall
         Channel $channel,
         $method,
         $deserialize,
-        $options = []
+        array $options = []
     ) {
         if (array_key_exists('timeout', $options) &&
             is_numeric($timeout = $options['timeout'])
@@ -89,7 +89,7 @@ abstract class AbstractCall
     }
 
     /**
-     * @return mixed The metadata sent by the server.
+     * @return mixed The metadata sent by the server
      */
     public function getMetadata()
     {
@@ -97,7 +97,7 @@ abstract class AbstractCall
     }
 
     /**
-     * @return mixed The trailing metadata sent by the server.
+     * @return mixed The trailing metadata sent by the server
      */
     public function getTrailingMetadata()
     {
@@ -105,7 +105,7 @@ abstract class AbstractCall
     }
 
     /**
-     * @return string The URI of the endpoint.
+     * @return string The URI of the endpoint
      */
     public function getPeer()
     {
@@ -167,8 +167,7 @@ abstract class AbstractCall
     /**
      * Set the CallCredentials for the underlying Call.
      *
-     * @param CallCredentials $call_credentials The CallCredentials
-     *                                          object
+     * @param CallCredentials $call_credentials The CallCredentials object
      */
     public function setCallCredentials($call_credentials)
     {
diff --git a/src/php/lib/Grpc/BaseStub.php b/src/php/lib/Grpc/BaseStub.php
index d0baeae955bb4a07a807861e8db1df054fe0be5e..a9e77b9396acf948512b0ac487a6901bea12dcac 100644
--- a/src/php/lib/Grpc/BaseStub.php
+++ b/src/php/lib/Grpc/BaseStub.php
@@ -48,14 +48,14 @@ class BaseStub
     private $update_metadata;
 
     /**
-     * @param $hostname string
-     * @param $opts array
+     * @param string  $hostname
+     * @param array   $opts
      *  - 'update_metadata': (optional) a callback function which takes in a
      * metadata array, and returns an updated metadata array
      *  - 'grpc.primary_user_agent': (optional) a user-agent string
-     * @param $channel Channel An already created Channel object
+     * @param Channel $channel An already created Channel object (optional)
      */
-    public function __construct($hostname, $opts, $channel = null)
+    public function __construct($hostname, $opts, Channel $channel = null)
     {
         $ssl_roots = file_get_contents(
             dirname(__FILE__).'/../../../../etc/roots.pem');
@@ -98,7 +98,7 @@ class BaseStub
     }
 
     /**
-     * @return string The URI of the endpoint.
+     * @return string The URI of the endpoint
      */
     public function getTarget()
     {
@@ -106,7 +106,7 @@ class BaseStub
     }
 
     /**
-     * @param $try_to_connect bool
+     * @param bool $try_to_connect (optional)
      *
      * @return int The grpc connectivity state
      */
@@ -145,6 +145,12 @@ class BaseStub
         return $this->_checkConnectivityState($new_state);
     }
 
+    /**
+     * @param $new_state Connect state
+     *
+     * @return bool true if state is CHANNEL_READY
+     * @throw Exception if state is CHANNEL_FATAL_FAILURE
+     */
     private function _checkConnectivityState($new_state)
     {
         if ($new_state == \Grpc\CHANNEL_READY) {
@@ -167,6 +173,10 @@ class BaseStub
 
     /**
      * constructs the auth uri for the jwt.
+     *
+     * @param string $method The method string
+     *
+     * @return string The URL string
      */
     private function _get_jwt_aud_uri($method)
     {
@@ -191,7 +201,7 @@ class BaseStub
      *
      * @param array $metadata The metadata map
      *
-     * @return $metadata Validated and key-normalized metadata map
+     * @return array $metadata Validated and key-normalized metadata map
      * @throw InvalidArgumentException if key contains invalid characters
      */
     private function _validate_and_normalize_metadata($metadata)
@@ -220,14 +230,16 @@ class BaseStub
      * @param mixed    $argument    The argument to the method
      * @param callable $deserialize A function that deserializes the response
      * @param array    $metadata    A metadata map to send to the server
+     *                              (optional)
+     * @param array    $options     An array of options (optional)
      *
      * @return SimpleSurfaceActiveCall The active call object
      */
     public function _simpleRequest($method,
                                    $argument,
                                    $deserialize,
-                                   $metadata = [],
-                                   $options = [])
+                                   array $metadata = [],
+                                   array $options = [])
     {
         $call = new UnaryCall($this->channel,
                               $method,
@@ -251,17 +263,17 @@ class BaseStub
      * output.
      *
      * @param string   $method      The name of the method to call
-     * @param array    $arguments   An array or Traversable of arguments to stream to the
-     *                              server
      * @param callable $deserialize A function that deserializes the response
      * @param array    $metadata    A metadata map to send to the server
+     *                              (optional)
+     * @param array    $options     An array of options (optional)
      *
      * @return ClientStreamingSurfaceActiveCall The active call object
      */
     public function _clientStreamRequest($method,
-                                         callable $deserialize,
-                                         $metadata = [],
-                                         $options = [])
+                                         $deserialize,
+                                         array $metadata = [],
+                                         array $options = [])
     {
         $call = new ClientStreamingCall($this->channel,
                                         $method,
@@ -281,21 +293,23 @@ class BaseStub
     }
 
     /**
-     * Call a remote method that takes a single argument and returns a stream of
-     * responses.
+     * Call a remote method that takes a single argument and returns a stream
+     * of responses.
      *
      * @param string   $method      The name of the method to call
      * @param mixed    $argument    The argument to the method
      * @param callable $deserialize A function that deserializes the responses
      * @param array    $metadata    A metadata map to send to the server
+     *                              (optional)
+     * @param array    $options     An array of options (optional)
      *
      * @return ServerStreamingSurfaceActiveCall The active call object
      */
     public function _serverStreamRequest($method,
                                          $argument,
-                                         callable $deserialize,
-                                         $metadata = [],
-                                         $options = [])
+                                         $deserialize,
+                                         array $metadata = [],
+                                         array $options = [])
     {
         $call = new ServerStreamingCall($this->channel,
                                         $method,
@@ -320,13 +334,15 @@ class BaseStub
      * @param string   $method      The name of the method to call
      * @param callable $deserialize A function that deserializes the responses
      * @param array    $metadata    A metadata map to send to the server
+     *                              (optional)
+     * @param array    $options     An array of options (optional)
      *
      * @return BidiStreamingSurfaceActiveCall The active call object
      */
     public function _bidiRequest($method,
-                                 callable $deserialize,
-                                 $metadata = [],
-                                 $options = [])
+                                 $deserialize,
+                                 array $metadata = [],
+                                 array $options = [])
     {
         $call = new BidiStreamingCall($this->channel,
                                       $method,
diff --git a/src/php/lib/Grpc/BidiStreamingCall.php b/src/php/lib/Grpc/BidiStreamingCall.php
index f0e1e811def73d4fb7e86f81846948b0dac0dabc..b03bbd204fb447b9efd90efcdf67f7506926093b 100644
--- a/src/php/lib/Grpc/BidiStreamingCall.php
+++ b/src/php/lib/Grpc/BidiStreamingCall.php
@@ -35,8 +35,8 @@
 namespace Grpc;
 
 /**
- * Represents an active call that allows for sending and recieving messages in
- * streams in any order.
+ * Represents an active call that allows for sending and recieving messages
+ * in streams in any order.
  */
 class BidiStreamingCall extends AbstractCall
 {
@@ -44,6 +44,7 @@ class BidiStreamingCall extends AbstractCall
      * Start the call.
      *
      * @param array $metadata Metadata to send with the call, if applicable
+     *                        (optional)
      */
     public function start(array $metadata = [])
     {
@@ -76,10 +77,10 @@ class BidiStreamingCall extends AbstractCall
      * writesDone is called.
      *
      * @param ByteBuffer $data    The data to write
-     * @param array      $options an array of options, possible keys:
-     *                            'flags' => a number
+     * @param array      $options An array of options, possible keys:
+     *                            'flags' => a number (optional)
      */
-    public function write($data, $options = [])
+    public function write($data, array $options = [])
     {
         $message_array = ['message' => $this->serializeMessage($data)];
         if (array_key_exists('flags', $options)) {
@@ -103,8 +104,8 @@ class BidiStreamingCall extends AbstractCall
     /**
      * Wait for the server to send the status, and return it.
      *
-     * @return \stdClass The status object, with integer $code, string $details,
-     *                   and array $metadata members
+     * @return \stdClass The status object, with integer $code, string
+     *                   $details, and array $metadata members
      */
     public function getStatus()
     {
diff --git a/src/php/lib/Grpc/ClientStreamingCall.php b/src/php/lib/Grpc/ClientStreamingCall.php
index 20db809ea3ca79e83b9eba827fec1904527ae1aa..c542f08872744214a3937b0045889df66be26add 100644
--- a/src/php/lib/Grpc/ClientStreamingCall.php
+++ b/src/php/lib/Grpc/ClientStreamingCall.php
@@ -35,8 +35,8 @@
 namespace Grpc;
 
 /**
- * Represents an active call that sends a stream of messages and then gets a
- * single response.
+ * Represents an active call that sends a stream of messages and then gets
+ * a single response.
  */
 class ClientStreamingCall extends AbstractCall
 {
@@ -44,8 +44,9 @@ class ClientStreamingCall extends AbstractCall
      * Start the call.
      *
      * @param array $metadata Metadata to send with the call, if applicable
+     *                        (optional)
      */
-    public function start($metadata = [])
+    public function start(array $metadata = [])
     {
         $this->call->startBatch([
             OP_SEND_INITIAL_METADATA => $metadata,
@@ -57,8 +58,8 @@ class ClientStreamingCall extends AbstractCall
      * wait is called.
      *
      * @param ByteBuffer $data    The data to write
-     * @param array      $options an array of options, possible keys:
-     *                            'flags' => a number
+     * @param array      $options An array of options, possible keys:
+     *                            'flags' => a number (optional)
      */
     public function write($data, array $options = [])
     {
diff --git a/src/php/lib/Grpc/ServerStreamingCall.php b/src/php/lib/Grpc/ServerStreamingCall.php
index 5aeeafa94adcfb805c1f1a71ddb4a0e76e170cde..406512bf579497e003eca148b0aaafeccce0c720 100644
--- a/src/php/lib/Grpc/ServerStreamingCall.php
+++ b/src/php/lib/Grpc/ServerStreamingCall.php
@@ -35,8 +35,8 @@
 namespace Grpc;
 
 /**
- * Represents an active call that sends a single message and then gets a stream
- * of responses.
+ * Represents an active call that sends a single message and then gets a
+ * stream of responses.
  */
 class ServerStreamingCall extends AbstractCall
 {
@@ -45,10 +45,11 @@ class ServerStreamingCall extends AbstractCall
      *
      * @param mixed $data     The data to send
      * @param array $metadata Metadata to send with the call, if applicable
-     * @param array $options  an array of options, possible keys:
-     *                        'flags' => a number
+     *                        (optional)
+     * @param array $options  An array of options, possible keys:
+     *                        'flags' => a number (optional)
      */
-    public function start($data, $metadata = [], $options = [])
+    public function start($data, array $metadata = [], array $options = [])
     {
         $message_array = ['message' => $this->serializeMessage($data)];
         if (array_key_exists('flags', $options)) {
@@ -82,8 +83,8 @@ class ServerStreamingCall extends AbstractCall
     /**
      * Wait for the server to send the status, and return it.
      *
-     * @return \stdClass The status object, with integer $code, string $details,
-     *                   and array $metadata members
+     * @return \stdClass The status object, with integer $code, string
+     *                   $details, and array $metadata members
      */
     public function getStatus()
     {
diff --git a/src/php/lib/Grpc/UnaryCall.php b/src/php/lib/Grpc/UnaryCall.php
index e8eb6487a8c0b36c6f4eb6c7363a09b6bceb1d00..3c1cb158ea563df9f8765fac891a955442508f57 100644
--- a/src/php/lib/Grpc/UnaryCall.php
+++ b/src/php/lib/Grpc/UnaryCall.php
@@ -35,8 +35,8 @@
 namespace Grpc;
 
 /**
- * Represents an active call that sends a single message and then gets a single
- * response.
+ * Represents an active call that sends a single message and then gets a
+ * single response.
  */
 class UnaryCall extends AbstractCall
 {
@@ -45,10 +45,11 @@ class UnaryCall extends AbstractCall
      *
      * @param mixed $data     The data to send
      * @param array $metadata Metadata to send with the call, if applicable
-     * @param array $options  an array of options, possible keys:
-     *                        'flags' => a number
+     *                        (optional)
+     * @param array $options  An array of options, possible keys:
+     *                        'flags' => a number (optional)
      */
-    public function start($data, $metadata = [], $options = [])
+    public function start($data, array $metadata = [], array $options = [])
     {
         $message_array = ['message' => $this->serializeMessage($data)];
         if (isset($options['flags'])) {
diff --git a/src/php/tests/interop/interop_client.php b/src/php/tests/interop/interop_client.php
index d201915d668e2f05560561836b1fd7915d45b0e0..2acf5612c7e3b1b50d20686fec73d56add6782da 100755
--- a/src/php/tests/interop/interop_client.php
+++ b/src/php/tests/interop/interop_client.php
@@ -451,11 +451,22 @@ function customMetadata($stub)
 
     $streaming_request = new grpc\testing\StreamingOutputCallRequest();
     $streaming_request->setPayload($payload);
+    $response_parameters = new grpc\testing\ResponseParameters();
+    $response_parameters->setSize($response_len);
+    $streaming_request->getResponseParameters()[] = $response_parameters;
     $streaming_call->write($streaming_request);
     $streaming_call->writesDone();
+    $result = $streaming_call->read();
 
     hardAssertIfStatusOk($streaming_call->getStatus());
 
+    $streaming_initial_metadata = $streaming_call->getMetadata();
+    hardAssert(array_key_exists($ECHO_INITIAL_KEY, $streaming_initial_metadata),
+               'Initial metadata does not contain expected key');
+    hardAssert(
+        $streaming_initial_metadata[$ECHO_INITIAL_KEY][0] === $ECHO_INITIAL_VALUE,
+        'Incorrect initial metadata value');
+
     $streaming_trailing_metadata = $streaming_call->getTrailingMetadata();
     hardAssert(array_key_exists($ECHO_TRAILING_KEY,
                                 $streaming_trailing_metadata),
diff --git a/src/proto/grpc/testing/BUILD b/src/proto/grpc/testing/BUILD
new file mode 100644
index 0000000000000000000000000000000000000000..f9f9cbceaf3f5473cddf35c4e23d7292a77f8505
--- /dev/null
+++ b/src/proto/grpc/testing/BUILD
@@ -0,0 +1,63 @@
+
+package(default_visibility = ["//visibility:public"])
+
+load("//bazel:grpc_build_system.bzl", "grpc_proto_library")
+
+grpc_proto_library(
+    name = "compiler_test_proto",
+    srcs = ["compiler_test.proto"],
+)
+
+grpc_proto_library(
+    name = "control_proto",
+    srcs = ["control.proto"],
+    deps = ["payloads_proto", "stats_proto"],
+)
+
+grpc_proto_library(
+    name = "echo_messages_proto",
+    srcs = ["echo_messages.proto"],
+)
+
+grpc_proto_library(
+    name = "echo_proto",
+    srcs = ["echo.proto"],
+    deps = ["echo_messages_proto"],
+)
+
+grpc_proto_library(
+    name = "empty_proto",
+    srcs = ["empty.proto"],
+)
+
+grpc_proto_library(
+    name = "messages_proto",
+    srcs = ["messages.proto"],
+)
+
+grpc_proto_library(
+    name = "metrics_proto",
+    srcs = ["metrics.proto"],
+)
+
+grpc_proto_library(
+    name = "payloads_proto",
+    srcs = ["payloads.proto"],
+)
+
+grpc_proto_library(
+    name = "services_proto",
+    srcs = ["services.proto"],
+    deps = ["control_proto", "messages_proto"],
+)
+
+grpc_proto_library(
+    name = "stats_proto",
+    srcs = ["stats.proto"],
+)
+
+grpc_proto_library(
+    name = "test_proto",
+    srcs = ["test.proto"],
+    deps = ["empty_proto", "messages_proto"],
+)
diff --git a/src/proto/grpc/testing/control.proto b/src/proto/grpc/testing/control.proto
index be387cf7869279cbd5d2de9d5f1690e8c1cc2098..529313cfb1ea5f38b0d9576838f2d1385acfe5f1 100644
--- a/src/proto/grpc/testing/control.proto
+++ b/src/proto/grpc/testing/control.proto
@@ -78,6 +78,14 @@ message SecurityParams {
   string server_host_override = 2;
 }
 
+message ChannelArg {
+  string name = 1;
+  oneof value {
+    string str_value = 2;
+    int32 int_value = 3;
+  }
+}
+
 message ClientConfig {
   // List of targets to connect to. At least one target needs to be specified.
   repeated string server_targets = 1;
@@ -103,6 +111,8 @@ message ClientConfig {
 
   // If we use an OTHER_CLIENT client_type, this string gives more detail
   string other_client_api = 15;
+
+  repeated ChannelArg channel_args = 16;
 }
 
 message ClientStatus { ClientStats stats = 1; }
diff --git a/src/proto/grpc/testing/duplicate/BUILD b/src/proto/grpc/testing/duplicate/BUILD
new file mode 100644
index 0000000000000000000000000000000000000000..255e699bec0f556fbf80aeea0d124c0b5b09148c
--- /dev/null
+++ b/src/proto/grpc/testing/duplicate/BUILD
@@ -0,0 +1,10 @@
+
+package(default_visibility = ["//visibility:public"])
+
+load("//bazel:grpc_build_system.bzl", "grpc_proto_library")
+
+grpc_proto_library(
+    name = "echo_duplicate_proto",
+    srcs = ["echo_duplicate.proto"],
+    deps = ["//src/proto/grpc/testing:echo_messages_proto"],
+)
diff --git a/src/python/grpcio/commands.py b/src/python/grpcio/commands.py
index ea3b6f3391debaa6d27e28f58c3db6a9734f52ce..701c6af0179300b52344a981f0a770202a7fcba2 100644
--- a/src/python/grpcio/commands.py
+++ b/src/python/grpcio/commands.py
@@ -62,6 +62,7 @@ napoleon_numpy_docstring = True
 napoleon_include_special_with_doc = True
 
 html_theme = 'sphinx_rtd_theme'
+copyright = "2016, The gRPC Authors"
 """
 
 API_GLOSSARY = """
diff --git a/src/python/grpcio/grpc/__init__.py b/src/python/grpcio/grpc/__init__.py
index 6087276d51ed0a11ef8dd42987f611a277711863..e3c10156d0fa959c8b7b8935b0789fa6eb22741b 100644
--- a/src/python/grpcio/grpc/__init__.py
+++ b/src/python/grpcio/grpc/__init__.py
@@ -31,6 +31,7 @@
 
 import abc
 import enum
+import sys
 
 import six
 
@@ -767,8 +768,8 @@ class ServicerContext(six.with_metaclass(abc.ABCMeta, RpcContext)):
     gRPC runtime to determine the status code of the RPC.
 
     Args:
-      code: The integer status code of the RPC to be transmitted to the
-        invocation side of the RPC.
+      code: A StatusCode value to be transmitted to the invocation side of the
+        RPC as the status code of the RPC.
     """
     raise NotImplementedError()
 
@@ -780,8 +781,8 @@ class ServicerContext(six.with_metaclass(abc.ABCMeta, RpcContext)):
     details to transmit.
 
     Args:
-      details: The details string of the RPC to be transmitted to
-        the invocation side of the RPC.
+      details: A string to be transmitted to the invocation side of the RPC as
+        the status details of the RPC.
     """
     raise NotImplementedError()
 
@@ -849,6 +850,26 @@ class GenericRpcHandler(six.with_metaclass(abc.ABCMeta)):
     raise NotImplementedError()
 
 
+class ServiceRpcHandler(six.with_metaclass(abc.ABCMeta, GenericRpcHandler)):
+  """An implementation of RPC methods belonging to a service.
+
+  A service handles RPC methods with structured names of the form
+  '/Service.Name/Service.MethodX', where 'Service.Name' is the value
+  returned by service_name(), and 'Service.MethodX' is the service method
+  name.  A service can have multiple service methods names, but only a single
+  service name.
+  """
+
+  @abc.abstractmethod
+  def service_name(self):
+    """Returns this services name.
+
+    Returns:
+      The service name.
+    """
+    raise NotImplementedError()
+
+
 #############################  Server Interface  ###############################
 
 
@@ -904,21 +925,6 @@ class Server(six.with_metaclass(abc.ABCMeta)):
     """
     raise NotImplementedError()
 
-  @abc.abstractmethod
-  def add_shutdown_handler(self, shutdown_handler):
-    """Adds a handler to be called on server shutdown.
-
-    Shutdown handlers are run on server stop() or in the event that a running
-    server is destroyed unexpectedly.  The handlers are run in series before
-    the stop grace period.
-
-    Args:
-      shutdown_handler:  A function taking a single arg, a time in seconds
-      within which the handler should complete.  None indicates the handler can
-      run for any duration.
-    """
-    raise NotImplementedError()
-
   @abc.abstractmethod
   def start(self):
     """Starts this Server's service of RPCs.
@@ -929,7 +935,7 @@ class Server(six.with_metaclass(abc.ABCMeta)):
     raise NotImplementedError()
 
   @abc.abstractmethod
-  def stop(self, grace, shutdown_handler_grace=None):
+  def stop(self, grace):
     """Stops this Server's service of RPCs.
 
     All calls to this method immediately stop service of new RPCs. When existing
@@ -952,8 +958,6 @@ class Server(six.with_metaclass(abc.ABCMeta)):
         aborted by this Server's stopping. If None, all RPCs will be aborted
         immediately and this method will block until this Server is completely
         stopped.
-      shutdown_handler_grace:  A duration of time in seconds or None.  This
-        value is passed to all shutdown handlers.
 
     Returns:
       A threading.Event that will be set when this Server has completely
@@ -1248,8 +1252,7 @@ def secure_channel(target, credentials, options=None):
                           credentials._credentials)
 
 
-def server(thread_pool, handlers=None, options=None, exit_grace=None,
-           exit_shutdown_handler_grace=None):
+def server(thread_pool, handlers=None, options=None):
   """Creates a Server with which RPCs can be serviced.
 
   Args:
@@ -1262,19 +1265,13 @@ def server(thread_pool, handlers=None, options=None, exit_grace=None,
       returned Server is started.
     options: A sequence of string-value pairs according to which to configure
       the created server.
-    exit_grace:  The grace period to use when terminating
-      running servers at interpreter exit.  None indicates unspecified.
-    exit_shutdown_handler_grace:  The shutdown handler grace to use when
-      terminating running servers at interpreter exit.  None indicates
-      unspecified.
 
   Returns:
     A Server with which RPCs can be serviced.
   """
   from grpc import _server
   return _server.Server(thread_pool, () if handlers is None else handlers,
-                        () if options is None else options, exit_grace,
-                        exit_shutdown_handler_grace)
+                        () if options is None else options)
 
 
 ###################################  __all__  #################################
@@ -1304,6 +1301,7 @@ __all__ = (
     'RpcMethodHandler',
     'HandlerCallDetails',
     'GenericRpcHandler',
+    'ServiceRpcHandler',
     'Server',
     'unary_unary_rpc_method_handler',
     'unary_stream_rpc_method_handler',
@@ -1321,3 +1319,24 @@ __all__ = (
     'secure_channel',
     'server',
 )
+
+
+############################### Extension Shims ################################
+
+
+# Here to maintain backwards compatibility; avoid using these in new code!
+try:
+  import grpc_tools
+  sys.modules.update({'grpc.tools': grpc_tools})
+except ImportError:
+  pass
+try:
+  import grpc_health
+  sys.modules.update({'grpc.health': grpc_health})
+except ImportError:
+  pass
+try:
+  import grpc_reflection
+  sys.modules.update({'grpc.reflection': grpc_reflection})
+except ImportError:
+  pass
diff --git a/src/python/grpcio/grpc/_channel.py b/src/python/grpcio/grpc/_channel.py
index 53a26727abed3f7c43612cc5fc097e08fea9ff2b..e8c6a99cb18843055fa987801be14433f52a37a1 100644
--- a/src/python/grpcio/grpc/_channel.py
+++ b/src/python/grpcio/grpc/_channel.py
@@ -32,12 +32,13 @@
 import sys
 import threading
 import time
+import logging
 
 import grpc
 from grpc import _common
 from grpc import _grpcio_metadata
-from grpc.framework.foundation import callable_util
 from grpc._cython import cygrpc
+from grpc.framework.foundation import callable_util
 
 _USER_AGENT = 'Python-gRPC-{}'.format(_grpcio_metadata.__version__)
 
@@ -99,6 +100,22 @@ def _wait_once_until(condition, until):
     else:
       condition.wait(timeout=remaining)
 
+_INTERNAL_CALL_ERROR_MESSAGE_FORMAT = (
+    'Internal gRPC call error %d. ' +
+    'Please report to https://github.com/grpc/grpc/issues')
+
+def _check_call_error(call_error, metadata):
+  if call_error == cygrpc.CallError.invalid_metadata:
+    raise ValueError('metadata was invalid: %s' % metadata)
+  elif call_error != cygrpc.CallError.ok:
+    raise ValueError(_INTERNAL_CALL_ERROR_MESSAGE_FORMAT % call_error)
+
+def _call_error_set_RPCstate(state, call_error, metadata):
+  if call_error == cygrpc.CallError.invalid_metadata:
+    _abort(state, grpc.StatusCode.INTERNAL, 'metadata was invalid: %s' % metadata)
+  else:
+    _abort(state, grpc.StatusCode.INTERNAL, 
+        _INTERNAL_CALL_ERROR_MESSAGE_FORMAT % call_error)
 
 class _RPCState(object):
 
@@ -181,7 +198,16 @@ def _consume_request_iterator(
   event_handler = _event_handler(state, call, None)
 
   def consume_request_iterator():
-    for request in request_iterator:
+    while True:
+      try:
+        request = next(request_iterator)
+      except StopIteration:
+        break
+      except Exception as e:
+        logging.exception("Exception iterating requests!")
+        call.cancel()
+        _abort(state, grpc.StatusCode.UNKNOWN, "Exception iterating requests!")
+        return
       serialized_request = _common.serialize(request, request_serializer)
       with state.condition:
         if state.code is None and not state.cancelled:
@@ -358,7 +384,7 @@ class _Rendezvous(grpc.RpcError, grpc.Future, grpc.Call):
       if self._state.callbacks is None:
         return False
       else:
-        self._state.callbacks.append(lambda: callback())
+        self._state.callbacks.append(callback)
         return True
 
   def initial_metadata(self):
@@ -435,10 +461,10 @@ def _end_unary_response_blocking(state, with_call, deadline):
 class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
 
   def __init__(
-      self, channel, create_managed_call, method, request_serializer,
+      self, channel, managed_call, method, request_serializer,
       response_deserializer):
     self._channel = channel
-    self._create_managed_call = create_managed_call
+    self._managed_call = managed_call
     self._method = method
     self._request_serializer = request_serializer
     self._response_deserializer = response_deserializer
@@ -472,7 +498,8 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
           None, 0, completion_queue, self._method, None, deadline_timespec)
       if credentials is not None:
         call.set_credentials(credentials._credentials)
-      call.start_client_batch(cygrpc.Operations(operations), None)
+      call_error = call.start_client_batch(cygrpc.Operations(operations), None)
+      _check_call_error(call_error, metadata)
       _handle_event(completion_queue.poll(), state, self._response_deserializer)
       return state, deadline
 
@@ -490,23 +517,28 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
     if rendezvous:
       return rendezvous
     else:
-      call = self._create_managed_call(
+      call, drive_call = self._managed_call(
           None, 0, self._method, None, deadline_timespec)
       if credentials is not None:
         call.set_credentials(credentials._credentials)
       event_handler = _event_handler(state, call, self._response_deserializer)
       with state.condition:
-        call.start_client_batch(cygrpc.Operations(operations), event_handler)
+        call_error = call.start_client_batch(cygrpc.Operations(operations),
+            event_handler)
+        if call_error != cygrpc.CallError.ok:
+          _call_error_set_RPCstate(state, call_error, metadata)
+          return _Rendezvous(state, None, None, deadline)
+        drive_call()
       return _Rendezvous(state, call, self._response_deserializer, deadline)
 
 
 class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
 
   def __init__(
-      self, channel, create_managed_call, method, request_serializer,
+      self, channel, managed_call, method, request_serializer,
       response_deserializer):
     self._channel = channel
-    self._create_managed_call = create_managed_call
+    self._managed_call = managed_call
     self._method = method
     self._request_serializer = request_serializer
     self._response_deserializer = response_deserializer
@@ -518,7 +550,7 @@ class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
       raise rendezvous
     else:
       state = _RPCState(_UNARY_STREAM_INITIAL_DUE, None, None, None, None)
-      call = self._create_managed_call(
+      call, drive_call = self._managed_call(
           None, 0, self._method, None, deadline_timespec)
       if credentials is not None:
         call.set_credentials(credentials._credentials)
@@ -535,17 +567,22 @@ class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
             cygrpc.operation_send_close_from_client(_EMPTY_FLAGS),
             cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS),
         )
-        call.start_client_batch(cygrpc.Operations(operations), event_handler)
+        call_error = call.start_client_batch(cygrpc.Operations(operations),
+            event_handler)
+        if call_error != cygrpc.CallError.ok:
+          _call_error_set_RPCstate(state, call_error, metadata)
+          return _Rendezvous(state, None, None, deadline)
+        drive_call()
       return _Rendezvous(state, call, self._response_deserializer, deadline)
 
 
 class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
 
   def __init__(
-      self, channel, create_managed_call, method, request_serializer,
+      self, channel, managed_call, method, request_serializer,
       response_deserializer):
     self._channel = channel
-    self._create_managed_call = create_managed_call
+    self._managed_call = managed_call
     self._method = method
     self._request_serializer = request_serializer
     self._response_deserializer = response_deserializer
@@ -569,7 +606,8 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
           cygrpc.operation_receive_message(_EMPTY_FLAGS),
           cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS),
       )
-      call.start_client_batch(cygrpc.Operations(operations), None)
+      call_error = call.start_client_batch(cygrpc.Operations(operations), None)
+      _check_call_error(call_error, metadata)
       _consume_request_iterator(
           request_iterator, state, call, self._request_serializer)
     while True:
@@ -597,7 +635,7 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
       self, request_iterator, timeout=None, metadata=None, credentials=None):
     deadline, deadline_timespec = _deadline(timeout)
     state = _RPCState(_STREAM_UNARY_INITIAL_DUE, None, None, None, None)
-    call = self._create_managed_call(
+    call, drive_call = self._managed_call(
         None, 0, self._method, None, deadline_timespec)
     if credentials is not None:
       call.set_credentials(credentials._credentials)
@@ -613,7 +651,12 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
           cygrpc.operation_receive_message(_EMPTY_FLAGS),
           cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS),
       )
-      call.start_client_batch(cygrpc.Operations(operations), event_handler)
+      call_error = call.start_client_batch(cygrpc.Operations(operations),
+          event_handler)
+      if call_error != cygrpc.CallError.ok:
+        _call_error_set_RPCstate(state, call_error, metadata)
+        return _Rendezvous(state, None, None, deadline)
+      drive_call()
       _consume_request_iterator(
           request_iterator, state, call, self._request_serializer)
     return _Rendezvous(state, call, self._response_deserializer, deadline)
@@ -622,10 +665,10 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
 class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable):
 
   def __init__(
-      self, channel, create_managed_call, method, request_serializer,
+      self, channel, managed_call, method, request_serializer,
       response_deserializer):
     self._channel = channel
-    self._create_managed_call = create_managed_call
+    self._managed_call = managed_call
     self._method = method
     self._request_serializer = request_serializer
     self._response_deserializer = response_deserializer
@@ -634,7 +677,7 @@ class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable):
       self, request_iterator, timeout=None, metadata=None, credentials=None):
     deadline, deadline_timespec = _deadline(timeout)
     state = _RPCState(_STREAM_STREAM_INITIAL_DUE, None, None, None, None)
-    call = self._create_managed_call(
+    call, drive_call = self._managed_call(
         None, 0, self._method, None, deadline_timespec)
     if credentials is not None:
       call.set_credentials(credentials._credentials)
@@ -649,7 +692,12 @@ class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable):
               _common.cygrpc_metadata(metadata), _EMPTY_FLAGS),
           cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS),
       )
-      call.start_client_batch(cygrpc.Operations(operations), event_handler)
+      call_error = call.start_client_batch(cygrpc.Operations(operations),
+          event_handler)
+      if call_error != cygrpc.CallError.ok:
+        _call_error_set_RPCstate(state, call_error, metadata)
+        return _Rendezvous(state, None, None, deadline)
+      drive_call()
       _consume_request_iterator(
           request_iterator, state, call, self._request_serializer)
     return _Rendezvous(state, call, self._response_deserializer, deadline)
@@ -687,16 +735,13 @@ def _run_channel_spin_thread(state):
   channel_spin_thread.start()
 
 
-def _create_channel_managed_call(state):
-  def create_channel_managed_call(parent, flags, method, host, deadline):
-    """Creates a managed cygrpc.Call.
+def _channel_managed_call_management(state):
+  def create(parent, flags, method, host, deadline):
+    """Creates a managed cygrpc.Call and a function to call to drive it.
 
-    Callers of this function must conduct at least one operation on the returned
-    call. The tags associated with operations conducted on the returned call
-    must be no-argument callables that return None to indicate that this channel
-    should continue polling for events associated with the call and return the
-    call itself to indicate that no more events associated with the call will be
-    generated.
+    If operations are successfully added to the returned cygrpc.Call, the
+    returned function must be called. If operations are not successfully added
+    to the returned cygrpc.Call, the returned function must not be called.
 
     Args:
       parent: A cygrpc.Call to be used as the parent of the created call.
@@ -706,18 +751,22 @@ def _create_channel_managed_call(state):
       deadline: A cygrpc.Timespec to be the deadline of the created call.
 
     Returns:
-      A cygrpc.Call with which to conduct an RPC.
+      A cygrpc.Call with which to conduct an RPC and a function to call if
+        operations are successfully started on the call.
     """
-    with state.lock:
-      call = state.channel.create_call(
-          parent, flags, state.completion_queue, method, host, deadline)
-      if state.managed_calls is None:
-        state.managed_calls = set((call,))
-        _run_channel_spin_thread(state)
-      else:
-        state.managed_calls.add(call)
-      return call
-  return create_channel_managed_call
+    call = state.channel.create_call(
+        parent, flags, state.completion_queue, method, host, deadline)
+
+    def drive():
+      with state.lock:
+        if state.managed_calls is None:
+          state.managed_calls = set((call,))
+          _run_channel_spin_thread(state)
+        else:
+          state.managed_calls.add(call)
+
+    return call, drive
+  return create
 
 
 class _ChannelConnectivityState(object):
@@ -847,6 +896,7 @@ def _options(options):
 
 
 class Channel(grpc.Channel):
+  """A cygrpc.Channel-backed implementation of grpc.Channel."""
 
   def __init__(self, target, options, credentials):
     """Constructor.
@@ -871,25 +921,25 @@ class Channel(grpc.Channel):
   def unary_unary(
       self, method, request_serializer=None, response_deserializer=None):
     return _UnaryUnaryMultiCallable(
-        self._channel, _create_channel_managed_call(self._call_state),
+        self._channel, _channel_managed_call_management(self._call_state),
         _common.encode(method), request_serializer, response_deserializer)
 
   def unary_stream(
       self, method, request_serializer=None, response_deserializer=None):
     return _UnaryStreamMultiCallable(
-        self._channel, _create_channel_managed_call(self._call_state),
+        self._channel, _channel_managed_call_management(self._call_state),
         _common.encode(method), request_serializer, response_deserializer)
 
   def stream_unary(
       self, method, request_serializer=None, response_deserializer=None):
     return _StreamUnaryMultiCallable(
-        self._channel, _create_channel_managed_call(self._call_state),
+        self._channel, _channel_managed_call_management(self._call_state),
         _common.encode(method), request_serializer, response_deserializer)
 
   def stream_stream(
       self, method, request_serializer=None, response_deserializer=None):
     return _StreamStreamMultiCallable(
-        self._channel, _create_channel_managed_call(self._call_state),
+        self._channel, _channel_managed_call_management(self._call_state),
         _common.encode(method), request_serializer, response_deserializer)
 
   def __del__(self):
diff --git a/src/python/grpcio/grpc/_server.py b/src/python/grpcio/grpc/_server.py
index d83a2e6dedaa9d9b3afe4c7096fda8156a9a6123..5223712dfa7d47d6ce69b9612565bc4615ed393d 100644
--- a/src/python/grpcio/grpc/_server.py
+++ b/src/python/grpcio/grpc/_server.py
@@ -60,8 +60,7 @@ _CANCELLED = 'cancelled'
 _EMPTY_FLAGS = 0
 _EMPTY_METADATA = cygrpc.Metadata(())
 
-_DEFAULT_EXIT_GRACE = 1.0
-_DEFAULT_EXIT_SHUTDOWN_HANDLER_GRACE = 5.0
+_UNEXPECTED_EXIT_SERVER_GRACE = 1.0
 
 
 def _serialized_request(request_event):
@@ -596,18 +595,14 @@ class _ServerStage(enum.Enum):
 
 class _ServerState(object):
 
-  def __init__(self, completion_queue, server, generic_handlers, thread_pool,
-               exit_grace, exit_shutdown_handler_grace):
+  def __init__(self, completion_queue, server, generic_handlers, thread_pool):
     self.lock = threading.Lock()
     self.completion_queue = completion_queue
     self.server = server
     self.generic_handlers = list(generic_handlers)
     self.thread_pool = thread_pool
-    self.exit_grace = exit_grace
-    self.exit_shutdown_handler_grace = exit_shutdown_handler_grace
     self.stage = _ServerStage.STOPPED
     self.shutdown_events = None
-    self.shutdown_handlers = []
 
     # TODO(https://github.com/grpc/grpc/issues/6597): eliminate these fields.
     self.rpc_states = set()
@@ -677,45 +672,41 @@ def _serve(state):
             return
 
 
-def _stop(state, grace, shutdown_handler_grace):
-  shutdown_event = threading.Event()
-
-  def cancel_all_calls_after_grace():
-    with state.lock:
-      if state.stage is _ServerStage.STOPPED:
-        shutdown_event.set()
-        return
-      elif state.stage is _ServerStage.STARTED:
-        do_shutdown = True
-        state.stage = _ServerStage.GRACE
-        state.shutdown_events = []
-      else:
-        do_shutdown = False
-      state.shutdown_events.append(shutdown_event)
-
-    if do_shutdown:
-      # Run Shutdown Handlers without the lock
-      for handler in state.shutdown_handlers:
-        handler(shutdown_handler_grace)
-      with state.lock:
+def _stop(state, grace):
+  with state.lock:
+    if state.stage is _ServerStage.STOPPED:
+      shutdown_event = threading.Event()
+      shutdown_event.set()
+      return shutdown_event
+    else:
+      if state.stage is _ServerStage.STARTED:
         state.server.shutdown(state.completion_queue, _SHUTDOWN_TAG)
         state.stage = _ServerStage.GRACE
+        state.shutdown_events = []
         state.due.add(_SHUTDOWN_TAG)
-
-    if not shutdown_event.wait(timeout=grace):
-      with state.lock:
+      shutdown_event = threading.Event()
+      state.shutdown_events.append(shutdown_event)
+      if grace is None:
         state.server.cancel_all_calls()
         # TODO(https://github.com/grpc/grpc/issues/6597): delete this loop.
         for rpc_state in state.rpc_states:
           with rpc_state.condition:
             rpc_state.client = _CANCELLED
             rpc_state.condition.notify_all()
-
-  if grace is None:
-    cancel_all_calls_after_grace()
-  else:
-    threading.Thread(target=cancel_all_calls_after_grace).start()
-
+      else:
+        def cancel_all_calls_after_grace():
+          shutdown_event.wait(timeout=grace)
+          with state.lock:
+            state.server.cancel_all_calls()
+            # TODO(https://github.com/grpc/grpc/issues/6597): delete this loop.
+            for rpc_state in state.rpc_states:
+              with rpc_state.condition:
+                rpc_state.client = _CANCELLED
+                rpc_state.condition.notify_all()
+        thread = threading.Thread(target=cancel_all_calls_after_grace)
+        thread.start()
+        return shutdown_event
+  shutdown_event.wait()
   return shutdown_event
 
 
@@ -725,12 +716,12 @@ def _start(state):
       raise ValueError('Cannot start already-started server!')
     state.server.start()
     state.stage = _ServerStage.STARTED
-    _request_call(state)
+    _request_call(state)    
     def cleanup_server(timeout):
       if timeout is None:
-        _stop(state, state.exit_grace, state.exit_shutdown_handler_grace).wait()
+        _stop(state, _UNEXPECTED_EXIT_SERVER_GRACE).wait()
       else:
-        _stop(state, timeout, 0).wait()
+        _stop(state, timeout).wait()
 
     thread = _common.CleanupThread(
         cleanup_server, target=_serve, args=(state,))
@@ -738,16 +729,12 @@ def _start(state):
 
 class Server(grpc.Server):
 
-  def __init__(self, thread_pool, generic_handlers, options, exit_grace,
-               exit_shutdown_handler_grace):
+  def __init__(self, thread_pool, generic_handlers, options):
     completion_queue = cygrpc.CompletionQueue()
     server = cygrpc.Server(_common.channel_args(options))
     server.register_completion_queue(completion_queue)
     self._state = _ServerState(
-        completion_queue, server, generic_handlers, thread_pool,
-        _DEFAULT_EXIT_GRACE if exit_grace is None else exit_grace,
-        _DEFAULT_EXIT_SHUTDOWN_HANDLER_GRACE if exit_shutdown_handler_grace
-        is None else exit_shutdown_handler_grace)
+        completion_queue, server, generic_handlers, thread_pool)
 
   def add_generic_rpc_handlers(self, generic_rpc_handlers):
     _add_generic_handlers(self._state, generic_rpc_handlers)
@@ -758,14 +745,11 @@ class Server(grpc.Server):
   def add_secure_port(self, address, server_credentials):
     return _add_secure_port(self._state, _common.encode(address), server_credentials)
 
-  def add_shutdown_handler(self, handler):
-    self._state.shutdown_handlers.append(handler)
-
   def start(self):
     _start(self._state)
 
-  def stop(self, grace, shutdown_handler_grace=None):
-    return _stop(self._state, grace, shutdown_handler_grace)
+  def stop(self, grace):
+    return _stop(self._state, grace)
 
   def __del__(self):
-    _stop(self._state, None, None)
+    _stop(self._state, None)
diff --git a/src/python/grpcio/grpc/_utilities.py b/src/python/grpcio/grpc/_utilities.py
index 4850967fbc7e84eb1276f886921d32b86e4e204f..a375896e6e6eb53da54b3b16f388bfe0e2791fb2 100644
--- a/src/python/grpcio/grpc/_utilities.py
+++ b/src/python/grpcio/grpc/_utilities.py
@@ -53,13 +53,17 @@ class RpcMethodHandler(
   pass
 
 
-class DictionaryGenericHandler(grpc.GenericRpcHandler):
+class DictionaryGenericHandler(grpc.ServiceRpcHandler):
 
   def __init__(self, service, method_handlers):
+    self._name = service
     self._method_handlers = {
         _common.fully_qualified_method(service, method): method_handler
         for method, method_handler in six.iteritems(method_handlers)}
 
+  def service_name(self):
+    return self._name
+
   def service(self, handler_call_details):
     return self._method_handlers.get(handler_call_details.method)
 
diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py
index bfd706a78bc978d3e65d977a6b477871dbaa2b04..e27e9e181d5158722efda4e616c77d9d92011fad 100644
--- a/src/python/grpcio/grpc_core_dependencies.py
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -82,6 +82,8 @@ CORE_SOURCE_FILES = [
   'src/core/lib/channel/connected_channel.c',
   'src/core/lib/channel/deadline_filter.c',
   'src/core/lib/channel/handshaker.c',
+  'src/core/lib/channel/handshaker_factory.c',
+  'src/core/lib/channel/handshaker_registry.c',
   'src/core/lib/channel/http_client_filter.c',
   'src/core/lib/channel/http_server_filter.c',
   'src/core/lib/channel/message_size_filter.c',
@@ -226,9 +228,9 @@ CORE_SOURCE_FILES = [
   'src/core/lib/security/credentials/plugin/plugin_credentials.c',
   'src/core/lib/security/credentials/ssl/ssl_credentials.c',
   'src/core/lib/security/transport/client_auth_filter.c',
-  'src/core/lib/security/transport/handshake.c',
   'src/core/lib/security/transport/secure_endpoint.c',
   'src/core/lib/security/transport/security_connector.c',
+  'src/core/lib/security/transport/security_handshaker.c',
   'src/core/lib/security/transport/server_auth_filter.c',
   'src/core/lib/security/transport/tsi_error.c',
   'src/core/lib/security/util/b64.c',
@@ -237,6 +239,7 @@ CORE_SOURCE_FILES = [
   'src/core/lib/tsi/fake_transport_security.c',
   'src/core/lib/tsi/ssl_transport_security.c',
   'src/core/lib/tsi/transport_security.c',
+  'src/core/ext/transport/chttp2/server/chttp2_server.c',
   'src/core/ext/transport/chttp2/client/secure/secure_channel_create.c',
   'src/core/ext/client_channel/channel_connectivity.c',
   'src/core/ext/client_channel/client_channel.c',
@@ -256,6 +259,7 @@ CORE_SOURCE_FILES = [
   'src/core/ext/client_channel/subchannel.c',
   'src/core/ext/client_channel/subchannel_index.c',
   'src/core/ext/client_channel/uri_parser.c',
+  'src/core/ext/transport/chttp2/client/chttp2_connector.c',
   'src/core/ext/transport/chttp2/server/insecure/server_chttp2.c',
   'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c',
   'src/core/ext/transport/chttp2/client/insecure/channel_create.c',
diff --git a/src/python/grpcio/support.py b/src/python/grpcio/support.py
index f363f5fdc5033400d213afc01c90f1f17f8d9586..b226e690fdd3b18a838ae69ca2df51bed5bd10c2 100644
--- a/src/python/grpcio/support.py
+++ b/src/python/grpcio/support.py
@@ -100,9 +100,15 @@ def diagnose_compile_error(build_ext, error):
             .format(source)
           )
 
+def diagnose_attribute_error(build_ext, error):
+  if any('_needs_stub' in arg for arg in error.args):
+    raise commands.CommandError(
+        "We expect a missing `_needs_stub` attribute from older versions of "
+        "setuptools. Consider upgrading setuptools.")
 
 _ERROR_DIAGNOSES = {
-    errors.CompileError: diagnose_compile_error
+    errors.CompileError: diagnose_compile_error,
+    AttributeError: diagnose_attribute_error
 }
 
 def diagnose_build_ext_error(build_ext, error, formatted):
diff --git a/src/python/grpcio_health_checking/MANIFEST.in b/src/python/grpcio_health_checking/MANIFEST.in
index 7407f646d161896255816060f1cd047228041ebe..5255e4c4036c9e3999d844238f7067d786940cbf 100644
--- a/src/python/grpcio_health_checking/MANIFEST.in
+++ b/src/python/grpcio_health_checking/MANIFEST.in
@@ -1,4 +1,4 @@
 include grpc_version.py
 include health_commands.py
-graft grpc
+graft grpc_health
 global-exclude *.pyc
diff --git a/src/python/grpcio_health_checking/grpc/health/__init__.py b/src/python/grpcio_health_checking/grpc_health/__init__.py
similarity index 100%
rename from src/python/grpcio_health_checking/grpc/health/__init__.py
rename to src/python/grpcio_health_checking/grpc_health/__init__.py
diff --git a/src/python/grpcio_health_checking/grpc/health/v1/__init__.py b/src/python/grpcio_health_checking/grpc_health/v1/__init__.py
similarity index 100%
rename from src/python/grpcio_health_checking/grpc/health/v1/__init__.py
rename to src/python/grpcio_health_checking/grpc_health/v1/__init__.py
diff --git a/src/python/grpcio_health_checking/grpc/health/v1/health.py b/src/python/grpcio_health_checking/grpc_health/v1/health.py
similarity index 98%
rename from src/python/grpcio_health_checking/grpc/health/v1/health.py
rename to src/python/grpcio_health_checking/grpc_health/v1/health.py
index 8108ac1096200b0a21df1de53daaa63a3a163c1b..0df679b0e22c3de249f2c6959a84a6c94ccd8209 100644
--- a/src/python/grpcio_health_checking/grpc/health/v1/health.py
+++ b/src/python/grpcio_health_checking/grpc_health/v1/health.py
@@ -33,7 +33,7 @@ import threading
 
 import grpc
 
-from grpc.health.v1 import health_pb2
+from grpc_health.v1 import health_pb2
 
 
 class HealthServicer(health_pb2.HealthServicer):
diff --git a/src/python/grpcio_health_checking/health_commands.py b/src/python/grpcio_health_checking/health_commands.py
index 66df25da63f7cd1463a0123d3de66ea2b4ba824a..0c420a655f58ecf16c41e03037acd18a3a415db7 100644
--- a/src/python/grpcio_health_checking/health_commands.py
+++ b/src/python/grpcio_health_checking/health_commands.py
@@ -54,7 +54,7 @@ class CopyProtoModules(setuptools.Command):
     if os.path.isfile(HEALTH_PROTO):
       shutil.copyfile(
           HEALTH_PROTO,
-          os.path.join(ROOT_DIR, 'grpc/health/v1/health.proto'))
+          os.path.join(ROOT_DIR, 'grpc_health/v1/health.proto'))
 
 
 class BuildPackageProtos(setuptools.Command):
@@ -74,5 +74,5 @@ class BuildPackageProtos(setuptools.Command):
     # directory is provided as an 'include' directory. We assume it's the '' key
     # to `self.distribution.package_dir` (and get a key error if it's not
     # there).
-    from grpc.tools import command
+    from grpc_tools import command
     command.build_package_protos(self.distribution.package_dir[''])
diff --git a/src/python/grpcio_health_checking/setup.py b/src/python/grpcio_health_checking/setup.py
index 8c92ee16a93d6365bf54de16b07f5143e1a90429..e88f389ba8a5fce962fdfa192d7046897ba9b786 100644
--- a/src/python/grpcio_health_checking/setup.py
+++ b/src/python/grpcio_health_checking/setup.py
@@ -66,7 +66,6 @@ setuptools.setup(
     license='3-clause BSD',
     package_dir=PACKAGE_DIRECTORIES,
     packages=setuptools.find_packages('.'),
-    namespace_packages=['grpc'],
     install_requires=INSTALL_REQUIRES,
     setup_requires=SETUP_REQUIRES,
     cmdclass=COMMAND_CLASS
diff --git a/src/python/grpcio_reflection/grpc/reflection/__init__.py b/src/python/grpcio_reflection/grpc_reflection/__init__.py
similarity index 100%
rename from src/python/grpcio_reflection/grpc/reflection/__init__.py
rename to src/python/grpcio_reflection/grpc_reflection/__init__.py
diff --git a/src/python/grpcio_reflection/grpc/reflection/v1alpha/__init__.py b/src/python/grpcio_reflection/grpc_reflection/v1alpha/__init__.py
similarity index 100%
rename from src/python/grpcio_reflection/grpc/reflection/v1alpha/__init__.py
rename to src/python/grpcio_reflection/grpc_reflection/v1alpha/__init__.py
diff --git a/src/python/grpcio_reflection/grpc/reflection/v1alpha/reflection.py b/src/python/grpcio_reflection/grpc_reflection/v1alpha/reflection.py
similarity index 99%
rename from src/python/grpcio_reflection/grpc/reflection/v1alpha/reflection.py
rename to src/python/grpcio_reflection/grpc_reflection/v1alpha/reflection.py
index 3c399b0d7996848ca93ab2fa99d7b2aa97a6eb96..bfcbce8e04123f343a44fe63fa5ca95b6c2c362d 100644
--- a/src/python/grpcio_reflection/grpc/reflection/v1alpha/reflection.py
+++ b/src/python/grpcio_reflection/grpc_reflection/v1alpha/reflection.py
@@ -35,7 +35,7 @@ import grpc
 from google.protobuf import descriptor_pb2
 from google.protobuf import descriptor_pool
 
-from grpc.reflection.v1alpha import reflection_pb2
+from grpc_reflection.v1alpha import reflection_pb2
 
 _POOL = descriptor_pool.Default()
 
diff --git a/src/python/grpcio_reflection/reflection_commands.py b/src/python/grpcio_reflection/reflection_commands.py
index d189aee57741177db08b6baaee4a923888f780cf..dee5491e0adc239b25790d97f956be5388109876 100644
--- a/src/python/grpcio_reflection/reflection_commands.py
+++ b/src/python/grpcio_reflection/reflection_commands.py
@@ -54,7 +54,7 @@ class CopyProtoModules(setuptools.Command):
     if os.path.isfile(HEALTH_PROTO):
       shutil.copyfile(
           HEALTH_PROTO,
-          os.path.join(ROOT_DIR, 'grpc/reflection/v1alpha/reflection.proto'))
+          os.path.join(ROOT_DIR, 'grpc_reflection/v1alpha/reflection.proto'))
 
 
 class BuildPackageProtos(setuptools.Command):
@@ -74,5 +74,5 @@ class BuildPackageProtos(setuptools.Command):
     # directory is provided as an 'include' directory. We assume it's the '' key
     # to `self.distribution.package_dir` (and get a key error if it's not
     # there).
-    from grpc.tools import command
+    from grpc_tools import command
     command.build_package_protos(self.distribution.package_dir[''])
diff --git a/src/python/grpcio_reflection/setup.py b/src/python/grpcio_reflection/setup.py
index df95af4de1648ffb47c7362f7d2f74034e08f5a4..cfc41f4fe74c2c9be203f06993002e21130ed83d 100644
--- a/src/python/grpcio_reflection/setup.py
+++ b/src/python/grpcio_reflection/setup.py
@@ -66,7 +66,6 @@ setuptools.setup(
     license='3-clause BSD',
     package_dir=PACKAGE_DIRECTORIES,
     packages=setuptools.find_packages('.'),
-    namespace_packages=['grpc'],
     install_requires=INSTALL_REQUIRES,
     setup_requires=SETUP_REQUIRES,
     cmdclass=COMMAND_CLASS
diff --git a/src/python/grpcio_tests/commands.py b/src/python/grpcio_tests/commands.py
index 5ee551cfe1eabb2c5286b9f1bfaea0eb2e07d9e6..e822971fe0983c2aeec750ba2191574e9d7534a8 100644
--- a/src/python/grpcio_tests/commands.py
+++ b/src/python/grpcio_tests/commands.py
@@ -100,7 +100,7 @@ class BuildProtoModules(setuptools.Command):
     pass
 
   def run(self):
-    import grpc.tools.protoc as protoc
+    import grpc_tools.protoc as protoc
 
     include_regex = re.compile(self.include)
     exclude_regex = re.compile(self.exclude) if self.exclude else None
@@ -116,7 +116,7 @@ class BuildProtoModules(setuptools.Command):
     # but we currently have name conflicts in src/proto
     for path in paths:
       command = [
-          'grpc.tools.protoc',
+          'grpc_tools.protoc',
           '-I {}'.format(PROTO_STEM),
           '--python_out={}'.format(PROTO_STEM),
           '--grpc_python_out={}'.format(PROTO_STEM),
diff --git a/src/python/grpcio_tests/setup.py b/src/python/grpcio_tests/setup.py
index 01d5fa875b9c71e1f5eb90ecab50c177e4e1922d..375fbd6c77decb7798fd1b2ade2a8d08dbe613f1 100644
--- a/src/python/grpcio_tests/setup.py
+++ b/src/python/grpcio_tests/setup.py
@@ -35,7 +35,7 @@ import sys
 
 import setuptools
 
-import grpc.tools.command
+import grpc_tools.command
 
 PY3 = sys.version_info.major == 3
 
@@ -68,7 +68,7 @@ COMMAND_CLASS = {
     # Run `preprocess` *before* doing any packaging!
     'preprocess': commands.GatherProto,
 
-    'build_package_protos': grpc.tools.command.BuildPackageProtos,
+    'build_package_protos': grpc_tools.command.BuildPackageProtos,
     'build_py': commands.BuildPy,
     'run_interop': commands.RunInterop,
     'test_lite': commands.TestLite
diff --git a/src/python/grpcio_tests/tests/health_check/_health_servicer_test.py b/src/python/grpcio_tests/tests/health_check/_health_servicer_test.py
index 80300d13df7cbe212118b35ef0eaae33c0fe5e61..5dde72b169874ce0dbac79c02457e6e5fa051ad9 100644
--- a/src/python/grpcio_tests/tests/health_check/_health_servicer_test.py
+++ b/src/python/grpcio_tests/tests/health_check/_health_servicer_test.py
@@ -27,14 +27,14 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-"""Tests of grpc.health.v1.health."""
+"""Tests of grpc_health.v1.health."""
 
 import unittest
 
 import grpc
 from grpc.framework.foundation import logging_pool
-from grpc.health.v1 import health
-from grpc.health.v1 import health_pb2
+from grpc_health.v1 import health
+from grpc_health.v1 import health_pb2
 
 from tests.unit.framework.common import test_constants
 
diff --git a/src/python/grpcio_tests/tests/http2/_negative_http2_client.py b/src/python/grpcio_tests/tests/http2/_negative_http2_client.py
new file mode 100644
index 0000000000000000000000000000000000000000..f8604683b3a9a07ac617bed6112dac9bacca3749
--- /dev/null
+++ b/src/python/grpcio_tests/tests/http2/_negative_http2_client.py
@@ -0,0 +1,153 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""The Python client used to test negative http2 conditions."""
+
+import argparse
+
+import grpc
+from src.proto.grpc.testing import test_pb2
+from src.proto.grpc.testing import messages_pb2
+
+def _validate_payload_type_and_length(response, expected_type, expected_length):
+  if response.payload.type is not expected_type:
+    raise ValueError(
+      'expected payload type %s, got %s' %
+          (expected_type, type(response.payload.type)))
+  elif len(response.payload.body) != expected_length:
+    raise ValueError(
+      'expected payload body size %d, got %d' %
+          (expected_length, len(response.payload.body)))
+
+def _expect_status_code(call, expected_code):
+  if call.code() != expected_code:
+    raise ValueError(
+      'expected code %s, got %s' % (expected_code, call.code()))
+
+def _expect_status_details(call, expected_details):
+  if call.details() != expected_details:
+    raise ValueError(
+      'expected message %s, got %s' % (expected_details, call.details()))
+
+def _validate_status_code_and_details(call, expected_code, expected_details):
+  _expect_status_code(call, expected_code)
+  _expect_status_details(call, expected_details)
+
+# common requests
+_REQUEST_SIZE = 314159
+_RESPONSE_SIZE = 271828
+
+_SIMPLE_REQUEST = messages_pb2.SimpleRequest(
+    response_type=messages_pb2.COMPRESSABLE,
+    response_size=_RESPONSE_SIZE,
+    payload=messages_pb2.Payload(body=b'\x00' * _REQUEST_SIZE))
+
+def _goaway(stub):
+  first_response = stub.UnaryCall(_SIMPLE_REQUEST)
+  _validate_payload_type_and_length(first_response, 
+      messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+  second_response = stub.UnaryCall(_SIMPLE_REQUEST)
+  _validate_payload_type_and_length(second_response, 
+      messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+
+def _rst_after_header(stub):
+  resp_future = stub.UnaryCall.future(_SIMPLE_REQUEST)
+  _validate_status_code_and_details(resp_future, grpc.StatusCode.UNAVAILABLE, "")
+
+def _rst_during_data(stub):
+  resp_future = stub.UnaryCall.future(_SIMPLE_REQUEST)
+  _validate_status_code_and_details(resp_future, grpc.StatusCode.UNKNOWN, "")
+
+def _rst_after_data(stub):
+  resp_future = stub.UnaryCall.future(_SIMPLE_REQUEST)
+  _validate_payload_type_and_length(next(resp_future),
+      messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+  _validate_status_code_and_details(resp_future, grpc.StatusCode.UNKNOWN, "")
+
+def _ping(stub):
+  response = stub.UnaryCall(_SIMPLE_REQUEST)
+  _validate_payload_type_and_length(response, 
+      messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+
+def _max_streams(stub):
+  # send one req to ensure server sets MAX_STREAMS
+  response = stub.UnaryCall(_SIMPLE_REQUEST)
+  _validate_payload_type_and_length(response, 
+      messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+
+  # give the streams a workout
+  futures = []
+  for _ in range(15):
+    futures.append(stub.UnaryCall.future(_SIMPLE_REQUEST))
+  for future in futures:
+    _validate_payload_type_and_length(future.result(),
+        messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+
+def _run_test_case(test_case, stub):
+  if test_case == 'goaway':
+    _goaway(stub)
+  elif test_case == 'rst_after_header':
+    _rst_after_header(stub)
+  elif test_case == 'rst_during_data':
+    _rst_during_data(stub)
+  elif test_case == 'rst_after_data':
+    _rst_after_data(stub)
+  elif test_case =='ping':
+    _ping(stub)
+  elif test_case == 'max_streams':
+    _max_streams(stub)
+  else:
+    raise ValueError("Invalid test case: %s" % test_case)
+
+def _args():
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      '--server_host', help='the host to which to connect', type=str,
+      default="127.0.0.1")
+  parser.add_argument(
+      '--server_port', help='the port to which to connect', type=int,
+      default="8080")
+  parser.add_argument(
+      '--test_case', help='the test case to execute', type=str,
+      default="goaway")
+  return parser.parse_args()
+
+def _stub(server_host, server_port):
+  target = '{}:{}'.format(server_host, server_port)
+  channel = grpc.insecure_channel(target)
+  return test_pb2.TestServiceStub(channel)
+
+def main():
+  args = _args()
+  stub = _stub(args.server_host, args.server_port)
+  _run_test_case(args.test_case, stub)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/src/python/grpcio_tests/tests/interop/_insecure_interop_test.py b/src/python/grpcio_tests/tests/interop/_insecure_intraop_test.py
similarity index 95%
rename from src/python/grpcio_tests/tests/interop/_insecure_interop_test.py
rename to src/python/grpcio_tests/tests/interop/_insecure_intraop_test.py
index 936c895bd2e73549925e73e858864ba75627a45b..4fb22b4d9dfb4fb0c6577d3c20848765adae580a 100644
--- a/src/python/grpcio_tests/tests/interop/_insecure_interop_test.py
+++ b/src/python/grpcio_tests/tests/interop/_insecure_intraop_test.py
@@ -35,13 +35,13 @@ import unittest
 import grpc
 from src.proto.grpc.testing import test_pb2
 
-from tests.interop import _interop_test_case
+from tests.interop import _intraop_test_case
 from tests.interop import methods
 from tests.interop import server
 
 
-class InsecureInteropTest(
-    _interop_test_case.InteropTestCase,
+class InsecureIntraopTest(
+    _intraop_test_case.IntraopTestCase,
     unittest.TestCase):
 
   def setUp(self):
diff --git a/src/python/grpcio_tests/tests/interop/_interop_test_case.py b/src/python/grpcio_tests/tests/interop/_intraop_test_case.py
similarity index 98%
rename from src/python/grpcio_tests/tests/interop/_interop_test_case.py
rename to src/python/grpcio_tests/tests/interop/_intraop_test_case.py
index ccea17a66da0a35f3a5cdfe09a6a942793bb5461..fe1c1739929e1f91519d81d63ebdd8b5e38c7926 100644
--- a/src/python/grpcio_tests/tests/interop/_interop_test_case.py
+++ b/src/python/grpcio_tests/tests/interop/_intraop_test_case.py
@@ -32,7 +32,7 @@
 from tests.interop import methods
 
 
-class InteropTestCase(object):
+class IntraopTestCase(object):
   """Unit test methods.
 
   This class must be mixed in with unittest.TestCase and a class that defines
diff --git a/src/python/grpcio_tests/tests/interop/_secure_interop_test.py b/src/python/grpcio_tests/tests/interop/_secure_intraop_test.py
similarity index 95%
rename from src/python/grpcio_tests/tests/interop/_secure_interop_test.py
rename to src/python/grpcio_tests/tests/interop/_secure_intraop_test.py
index eaca553e1b8e74116de473c914a77b70b018951c..3665c69726c772b87180265d0b9af113bdc962e6 100644
--- a/src/python/grpcio_tests/tests/interop/_secure_interop_test.py
+++ b/src/python/grpcio_tests/tests/interop/_secure_intraop_test.py
@@ -35,15 +35,15 @@ import unittest
 import grpc
 from src.proto.grpc.testing import test_pb2
 
-from tests.interop import _interop_test_case
+from tests.interop import _intraop_test_case
 from tests.interop import methods
 from tests.interop import resources
 
 _SERVER_HOST_OVERRIDE = 'foo.test.google.fr'
 
 
-class SecureInteropTest(
-    _interop_test_case.InteropTestCase,
+class SecureIntraopTest(
+    _intraop_test_case.IntraopTestCase,
     unittest.TestCase):
 
   def setUp(self):
diff --git a/src/python/grpcio_tests/tests/interop/methods.py b/src/python/grpcio_tests/tests/interop/methods.py
index 52e56f35022634a3ea2bf1d8b8f256e92514af2f..9038ae57515bc02cae68a038ff56ad4f62601dd8 100644
--- a/src/python/grpcio_tests/tests/interop/methods.py
+++ b/src/python/grpcio_tests/tests/interop/methods.py
@@ -33,7 +33,6 @@ import enum
 import json
 import os
 import threading
-import time
 
 from oauth2client import client as oauth2client_client
 
@@ -196,16 +195,6 @@ def _server_streaming(stub):
         response, messages_pb2.COMPRESSABLE, sizes[index])
 
 
-def _cancel_after_begin(stub):
-  sizes = (27182, 8, 1828, 45904,)
-  payloads = (messages_pb2.Payload(body=b'\x00' * size) for size in sizes)
-  requests = (messages_pb2.StreamingInputCallRequest(payload=payload)
-              for payload in payloads)
-  response_future = stub.StreamingInputCall.future(requests)
-  response_future.cancel()
-  if not response_future.cancelled():
-    raise ValueError('expected call to be cancelled')
-
 
 class _Pipe(object):
 
@@ -265,6 +254,16 @@ def _ping_pong(stub):
           response, messages_pb2.COMPRESSABLE, response_size)
 
 
+def _cancel_after_begin(stub):
+  with _Pipe() as pipe:
+    response_future = stub.StreamingInputCall.future(pipe)
+    response_future.cancel()
+    if not response_future.cancelled():
+      raise ValueError('expected cancelled method to return True')
+    if response_future.code() is not grpc.StatusCode.CANCELLED:
+      raise ValueError('expected status code CANCELLED')
+
+
 def _cancel_after_first_response(stub):
   request_response_sizes = (31415, 9, 2653, 58979,)
   request_payload_sizes = (27182, 8, 1828, 45904,)
@@ -302,7 +301,6 @@ def _timeout_on_sleeping_server(stub):
         response_type=messages_pb2.COMPRESSABLE,
         payload=messages_pb2.Payload(body=b'\x00' * request_payload_size))
     pipe.add(request)
-    time.sleep(0.1)
     try:
       next(response_iterator)
     except grpc.RpcError as rpc_error:
diff --git a/src/python/grpcio_tests/tests/protoc_plugin/_split_definitions_test.py b/src/python/grpcio_tests/tests/protoc_plugin/_split_definitions_test.py
index 089366a8c7260357dc14c4948a8904a2b3868db4..f8ae05bb7a97a3f102ec889e1dbe08b5f551612b 100644
--- a/src/python/grpcio_tests/tests/protoc_plugin/_split_definitions_test.py
+++ b/src/python/grpcio_tests/tests/protoc_plugin/_split_definitions_test.py
@@ -44,7 +44,7 @@ import threading
 import unittest
 
 import grpc
-from grpc.tools import protoc
+from grpc_tools import protoc
 from tests.unit.framework.common import test_constants
 
 _MESSAGES_IMPORT = b'import "messages.proto";'
@@ -167,7 +167,7 @@ class SameSeparateTest(unittest.TestCase, SeparateTestMixin):
         '',
         '--proto_path={}'.format(self.proto_directory),
         '--python_out={}'.format(self.python_out_directory),
-        '--grpc_python_out={}'.format(self.grpc_python_out_directory),
+        '--grpc_python_out=grpc_2_0:{}'.format(self.grpc_python_out_directory),
         same_proto_file,
     ])
     if protoc_result != 0:
@@ -241,7 +241,7 @@ class SplitCommonTest(unittest.TestCase, CommonTestMixin):
         '',
         '--proto_path={}'.format(self.proto_directory),
         '--python_out={}'.format(self.python_out_directory),
-        '--grpc_python_out={}'.format(self.python_out_directory),
+        '--grpc_python_out={}'.format(self.grpc_python_out_directory),
         services_proto_file,
         messages_proto_file,
     ])
@@ -285,7 +285,7 @@ class SplitSeparateTest(unittest.TestCase, SeparateTestMixin):
         '',
         '--proto_path={}'.format(self.proto_directory),
         '--python_out={}'.format(self.python_out_directory),
-        '--grpc_python_out={}'.format(self.grpc_python_out_directory),
+        '--grpc_python_out=grpc_2_0:{}'.format(self.grpc_python_out_directory),
         services_proto_file,
         messages_proto_file,
     ])
diff --git a/src/python/grpcio_tests/tests/qps/benchmark_client.py b/src/python/grpcio_tests/tests/qps/benchmark_client.py
index 83b46c914e7f89d8d8966c29d573d69b6f310413..650e4756e7295169bcea373c5a68f19a15baa913 100644
--- a/src/python/grpcio_tests/tests/qps/benchmark_client.py
+++ b/src/python/grpcio_tests/tests/qps/benchmark_client.py
@@ -68,12 +68,8 @@ class BenchmarkClient:
     else:
       channel = grpc.insecure_channel(server)
 
-    connected_event = threading.Event()
-    def wait_for_ready(connectivity):
-      if connectivity == grpc.ChannelConnectivity.READY:
-        connected_event.set()
-    channel.subscribe(wait_for_ready, try_to_connect=True)
-    connected_event.wait()
+    # waits for the channel to be ready before we start sending messages
+    grpc.channel_ready_future(channel).result()
 
     if config.payload_config.WhichOneof('payload') == 'simple_params':
       self._generic = False
diff --git a/src/python/grpcio_tests/tests/reflection/_reflection_servicer_test.py b/src/python/grpcio_tests/tests/reflection/_reflection_servicer_test.py
index 87264cf9ba91ab77d09ca74af98516687a208b97..43d6c971b5292ca1430e7e5198b0d3ad0640fbc6 100644
--- a/src/python/grpcio_tests/tests/reflection/_reflection_servicer_test.py
+++ b/src/python/grpcio_tests/tests/reflection/_reflection_servicer_test.py
@@ -27,14 +27,14 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-"""Tests of grpc.reflection.v1alpha.reflection."""
+"""Tests of grpc_reflection.v1alpha.reflection."""
 
 import unittest
 
 import grpc
 from grpc.framework.foundation import logging_pool
-from grpc.reflection.v1alpha import reflection
-from grpc.reflection.v1alpha import reflection_pb2
+from grpc_reflection.v1alpha import reflection
+from grpc_reflection.v1alpha import reflection_pb2
 
 from google.protobuf import descriptor_pool
 from google.protobuf import descriptor_pb2
@@ -75,7 +75,7 @@ class ReflectionServicerTest(unittest.TestCase):
         file_by_filename='i-donut-exist'
       ),
     )
-    responses = tuple(self._stub.ServerReflectionInfo(requests))
+    responses = tuple(self._stub.ServerReflectionInfo(iter(requests)))
     expected_responses = (
       reflection_pb2.ServerReflectionResponse(
         valid_host='',
@@ -93,7 +93,7 @@ class ReflectionServicerTest(unittest.TestCase):
         )
       ),
     )
-    self.assertEqual(expected_responses, responses)
+    self.assertSequenceEqual(expected_responses, responses)
 
   def testFileBySymbol(self):
     requests = (
@@ -104,7 +104,7 @@ class ReflectionServicerTest(unittest.TestCase):
         file_containing_symbol='i.donut.exist.co.uk.org.net.me.name.foo'
       ),
     )
-    responses = tuple(self._stub.ServerReflectionInfo(requests))
+    responses = tuple(self._stub.ServerReflectionInfo(iter(requests)))
     expected_responses = (
       reflection_pb2.ServerReflectionResponse(
         valid_host='',
@@ -122,7 +122,7 @@ class ReflectionServicerTest(unittest.TestCase):
         )
       ),
     )
-    self.assertEqual(expected_responses, responses)
+    self.assertSequenceEqual(expected_responses, responses)
 
   @unittest.skip('TODO(atash): implement file-containing-extension reflection '
                  '(see https://github.com/google/protobuf/issues/2248)')
@@ -141,7 +141,7 @@ class ReflectionServicerTest(unittest.TestCase):
         ),
       ),
     )
-    responses = tuple(self._stub.ServerReflectionInfo(requests))
+    responses = tuple(self._stub.ServerReflectionInfo(iter(requests)))
     expected_responses = (
       reflection_pb2.ServerReflectionResponse(
         valid_host='',
@@ -159,7 +159,7 @@ class ReflectionServicerTest(unittest.TestCase):
         )
       ),
     )
-    self.assertEqual(expected_responses, responses)
+    self.assertSequenceEqual(expected_responses, responses)
 
   def testListServices(self):
     requests = (
@@ -167,7 +167,7 @@ class ReflectionServicerTest(unittest.TestCase):
         list_services='',
       ),
     )
-    responses = tuple(self._stub.ServerReflectionInfo(requests))
+    responses = tuple(self._stub.ServerReflectionInfo(iter(requests)))
     expected_responses = (
       reflection_pb2.ServerReflectionResponse(
         valid_host='',
@@ -179,7 +179,7 @@ class ReflectionServicerTest(unittest.TestCase):
         )
       ),
     )
-    self.assertEqual(expected_responses, responses)
+    self.assertSequenceEqual(expected_responses, responses)
 
 if __name__ == '__main__':
   unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/stress/client.py b/src/python/grpcio_tests/tests/stress/client.py
index 390ea13021d1a37013230330c61b9dfd911a2e9a..b8116729b573ddc289392497850d8b3ce240789f 100644
--- a/src/python/grpcio_tests/tests/stress/client.py
+++ b/src/python/grpcio_tests/tests/stress/client.py
@@ -110,10 +110,13 @@ def _get_channel(target, args):
     channel_credentials = grpc.ssl_channel_credentials(
         root_certificates=root_certificates)
     options = (('grpc.ssl_target_name_override', args.server_host_override,),)
-    return grpc.secure_channel(
-        target, channel_credentials, options=options)
+    channel = grpc.secure_channel(target, channel_credentials, options=options)
   else:
-    return grpc.insecure_channel(target)
+    channel = grpc.insecure_channel(target)
+
+  # waits for the channel to be ready before we start sending messages
+  grpc.channel_ready_future(channel).result()
+  return channel
 
 def run_test(args):
   test_cases = _parse_weighted_test_cases(args.test_cases)
diff --git a/src/python/grpcio_tests/tests/tests.json b/src/python/grpcio_tests/tests/tests.json
index 04a2e4417847c2a07137c2a1970d6442f7eac039..70d965d3ca8e232ff58be15b82739f1aafe7d2ad 100644
--- a/src/python/grpcio_tests/tests/tests.json
+++ b/src/python/grpcio_tests/tests/tests.json
@@ -1,7 +1,7 @@
 [
   "health_check._health_servicer_test.HealthServicerTest",
-  "interop._insecure_interop_test.InsecureInteropTest",
-  "interop._secure_interop_test.SecureInteropTest",
+  "interop._insecure_intraop_test.InsecureIntraopTest",
+  "interop._secure_intraop_test.SecureIntraopTest",
   "protoc_plugin._python_plugin_test.PythonPluginTest",
   "protoc_plugin._split_definitions_test.SameCommonTest",
   "protoc_plugin._split_definitions_test.SameSeparateTest",
@@ -27,7 +27,8 @@
   "unit._cython.cygrpc_test.TypeSmokeTest",
   "unit._empty_message_test.EmptyMessageTest",
   "unit._exit_test.ExitTest",
-  "unit._exit_test.ShutdownHandlerTest",
+  "unit._invalid_metadata_test.InvalidMetadataTest",
+  "unit._invocation_defects_test.InvocationDefectsTest",
   "unit._metadata_code_details_test.MetadataCodeDetailsTest",
   "unit._metadata_test.MetadataTest",
   "unit._rpc_test.RPCTest",
diff --git a/src/python/grpcio_tests/tests/unit/_api_test.py b/src/python/grpcio_tests/tests/unit/_api_test.py
index 2fe89499f51c8fa59bfe55ef960a72ca7d71ab9d..51dc4254208e10f2e98bb8c3c326e4c2fcd01ad5 100644
--- a/src/python/grpcio_tests/tests/unit/_api_test.py
+++ b/src/python/grpcio_tests/tests/unit/_api_test.py
@@ -65,6 +65,7 @@ class AllTest(unittest.TestCase):
         'RpcMethodHandler',
         'HandlerCallDetails',
         'GenericRpcHandler',
+        'ServiceRpcHandler',
         'Server',
         'unary_unary_rpc_method_handler',
         'unary_stream_rpc_method_handler',
diff --git a/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py b/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py
index e0a7d15aa7636c77d9274e2df8de6ff04c97fe8c..46a964db8cfe416fe8c0b10bb3b1db3f19e7e13e 100644
--- a/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py
+++ b/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py
@@ -64,7 +64,7 @@ class ChannelReadyFutureTest(unittest.TestCase):
     ready_future = grpc.channel_ready_future(channel)
     ready_future.add_done_callback(callback.accept_value)
     with self.assertRaises(grpc.FutureTimeoutError):
-      ready_future.result(test_constants.SHORT_TIMEOUT)
+      ready_future.result(timeout=test_constants.SHORT_TIMEOUT)
     self.assertFalse(ready_future.cancelled())
     self.assertFalse(ready_future.done())
     self.assertTrue(ready_future.running())
@@ -85,7 +85,7 @@ class ChannelReadyFutureTest(unittest.TestCase):
 
     ready_future = grpc.channel_ready_future(channel)
     ready_future.add_done_callback(callback.accept_value)
-    self.assertIsNone(ready_future.result(test_constants.SHORT_TIMEOUT))
+    self.assertIsNone(ready_future.result(timeout=test_constants.LONG_TIMEOUT))
     value_passed_to_callback = callback.block_until_called()
     self.assertIs(ready_future, value_passed_to_callback)
     self.assertFalse(ready_future.cancelled())
diff --git a/src/python/grpcio_tests/tests/unit/_compression_test.py b/src/python/grpcio_tests/tests/unit/_compression_test.py
index 83b91094666cd5a0be5e879cf0f3309b622c4532..4d3f02e917551e0cca52e04153a677c4953ddace 100644
--- a/src/python/grpcio_tests/tests/unit/_compression_test.py
+++ b/src/python/grpcio_tests/tests/unit/_compression_test.py
@@ -125,7 +125,7 @@ class CompressionTest(unittest.TestCase):
     compressed_channel = grpc.insecure_channel('localhost:%d' % self._port,
         options=[('grpc.default_compression_algorithm', 1)])
     multi_callable = compressed_channel.stream_stream(_STREAM_STREAM)
-    call = multi_callable([request] * test_constants.STREAM_LENGTH)
+    call = multi_callable(iter([request] * test_constants.STREAM_LENGTH))
     for response in call:
       self.assertEqual(request, response)
 
diff --git a/src/python/grpcio_tests/tests/unit/_empty_message_test.py b/src/python/grpcio_tests/tests/unit/_empty_message_test.py
index 131f6e945253acae867894f34cedf1c6c60c5705..69f468927905a5d42e94c04e119760932e05dc78 100644
--- a/src/python/grpcio_tests/tests/unit/_empty_message_test.py
+++ b/src/python/grpcio_tests/tests/unit/_empty_message_test.py
@@ -123,12 +123,12 @@ class EmptyMessageTest(unittest.TestCase):
 
   def testStreamUnary(self):
     response = self._channel.stream_unary(_STREAM_UNARY)(
-        [_REQUEST] * test_constants.STREAM_LENGTH)
+        iter([_REQUEST] * test_constants.STREAM_LENGTH))
     self.assertEqual(_RESPONSE, response)
 
   def testStreamStream(self):
     response_iterator = self._channel.stream_stream(_STREAM_STREAM)(
-        [_REQUEST] * test_constants.STREAM_LENGTH)
+        iter([_REQUEST] * test_constants.STREAM_LENGTH))
     self.assertSequenceEqual(
         [_RESPONSE] * test_constants.STREAM_LENGTH, list(response_iterator))
 
diff --git a/src/python/grpcio_tests/tests/unit/_exit_scenarios.py b/src/python/grpcio_tests/tests/unit/_exit_scenarios.py
index b33802bf57294d7e45edbc5cf739cee43ad65b4d..777527137f0f29ba520570b9d1bb61a05d6d3a4c 100644
--- a/src/python/grpcio_tests/tests/unit/_exit_scenarios.py
+++ b/src/python/grpcio_tests/tests/unit/_exit_scenarios.py
@@ -240,7 +240,7 @@ if __name__ == '__main__':
       multi_callable = channel.stream_unary(method)
       future = multi_callable.future(infinite_request_iterator())
       result, call = multi_callable.with_call(
-          [REQUEST] * test_constants.STREAM_LENGTH)
+          iter([REQUEST] * test_constants.STREAM_LENGTH))
     elif (args.scenario == IN_FLIGHT_STREAM_STREAM_CALL or
           args.scenario == IN_FLIGHT_PARTIAL_STREAM_STREAM_CALL):
       multi_callable = channel.stream_stream(method)
diff --git a/src/python/grpcio_tests/tests/unit/_exit_test.py b/src/python/grpcio_tests/tests/unit/_exit_test.py
index 342f5fcc10cb980b73837b138aecac316609e2ee..5a4a32887c3634cdd263b65831c8e65fa88bb65b 100644
--- a/src/python/grpcio_tests/tests/unit/_exit_test.py
+++ b/src/python/grpcio_tests/tests/unit/_exit_test.py
@@ -43,8 +43,6 @@ import threading
 import time
 import unittest
 
-import grpc
-from grpc.framework.foundation import logging_pool
 from tests.unit import _exit_scenarios
 
 SCENARIO_FILE = os.path.abspath(os.path.join(
@@ -54,7 +52,7 @@ BASE_COMMAND = [INTERPRETER, SCENARIO_FILE]
 BASE_SIGTERM_COMMAND = BASE_COMMAND + ['--wait_for_interrupt']
 
 INIT_TIME = 1.0
-SHUTDOWN_GRACE = 5.0
+
 
 processes = []
 process_lock = threading.Lock()
@@ -184,24 +182,5 @@ class ExitTest(unittest.TestCase):
     interrupt_and_wait(process)
 
 
-class _ShutDownHandler(object):
-
-  def __init__(self):
-    self.seen_handler_grace = None
-
-  def shutdown_handler(self, handler_grace):
-    self.seen_handler_grace = handler_grace
-
-  
-class ShutdownHandlerTest(unittest.TestCase):
-
-  def test_shutdown_handler(self):
-    server = grpc.server(logging_pool.pool(1))
-    handler = _ShutDownHandler()
-    server.add_shutdown_handler(handler.shutdown_handler)
-    server.start()
-    server.stop(0, shutdown_handler_grace=SHUTDOWN_GRACE).wait()
-    self.assertEqual(SHUTDOWN_GRACE, handler.seen_handler_grace)
-
 if __name__ == '__main__':
   unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_invalid_metadata_test.py b/src/python/grpcio_tests/tests/unit/_invalid_metadata_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..2dc225de292d910b0d230d9ed5c36753999025c2
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/_invalid_metadata_test.py
@@ -0,0 +1,175 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Test of RPCs made against gRPC Python's application-layer API."""
+
+import unittest
+
+import grpc
+
+from tests.unit.framework.common import test_constants
+
+_SERIALIZE_REQUEST = lambda bytestring: bytestring * 2
+_DESERIALIZE_REQUEST = lambda bytestring: bytestring[len(bytestring) // 2:]
+_SERIALIZE_RESPONSE = lambda bytestring: bytestring * 3
+_DESERIALIZE_RESPONSE = lambda bytestring: bytestring[:len(bytestring) // 3]
+
+_UNARY_UNARY = '/test/UnaryUnary'
+_UNARY_STREAM = '/test/UnaryStream'
+_STREAM_UNARY = '/test/StreamUnary'
+_STREAM_STREAM = '/test/StreamStream'
+
+
+def _unary_unary_multi_callable(channel):
+  return channel.unary_unary(_UNARY_UNARY)
+
+
+def _unary_stream_multi_callable(channel):
+  return channel.unary_stream(
+      _UNARY_STREAM,
+      request_serializer=_SERIALIZE_REQUEST,
+      response_deserializer=_DESERIALIZE_RESPONSE)
+
+
+def _stream_unary_multi_callable(channel):
+  return channel.stream_unary(
+      _STREAM_UNARY,
+      request_serializer=_SERIALIZE_REQUEST,
+      response_deserializer=_DESERIALIZE_RESPONSE)
+
+
+def _stream_stream_multi_callable(channel):
+  return channel.stream_stream(_STREAM_STREAM)
+
+
+class InvalidMetadataTest(unittest.TestCase):
+
+  def setUp(self):
+    self._channel = grpc.insecure_channel('localhost:8080')
+    self._unary_unary = _unary_unary_multi_callable(self._channel)
+    self._unary_stream = _unary_stream_multi_callable(self._channel)
+    self._stream_unary = _stream_unary_multi_callable(self._channel)
+    self._stream_stream = _stream_stream_multi_callable(self._channel)
+
+  def testUnaryRequestBlockingUnaryResponse(self):
+    request = b'\x07\x08'
+    metadata = (('InVaLiD', 'UnaryRequestBlockingUnaryResponse'),)
+    expected_error_details = "metadata was invalid: %s" % metadata
+    with self.assertRaises(ValueError) as exception_context:
+      self._unary_unary(request, metadata=metadata)
+    self.assertIn(expected_error_details, str(exception_context.exception))
+
+  def testUnaryRequestBlockingUnaryResponseWithCall(self):
+    request = b'\x07\x08'
+    metadata = (('InVaLiD', 'UnaryRequestBlockingUnaryResponseWithCall'),)
+    expected_error_details = "metadata was invalid: %s" % metadata
+    with self.assertRaises(ValueError) as exception_context:
+      self._unary_unary.with_call(request, metadata=metadata)
+    self.assertIn(expected_error_details, str(exception_context.exception))
+
+  def testUnaryRequestFutureUnaryResponse(self):
+    request = b'\x07\x08'
+    metadata = (('InVaLiD', 'UnaryRequestFutureUnaryResponse'),)
+    expected_error_details = "metadata was invalid: %s" % metadata
+    response_future = self._unary_unary.future(request, metadata=metadata)
+    with self.assertRaises(grpc.RpcError) as exception_context:
+      response_future.result()
+    self.assertEqual(
+        exception_context.exception.details(), expected_error_details)
+    self.assertEqual(
+        exception_context.exception.code(), grpc.StatusCode.INTERNAL)
+    self.assertEqual(response_future.details(), expected_error_details)
+    self.assertEqual(response_future.code(), grpc.StatusCode.INTERNAL)
+
+  def testUnaryRequestStreamResponse(self):
+    request = b'\x37\x58'
+    metadata = (('InVaLiD', 'UnaryRequestStreamResponse'),)
+    expected_error_details = "metadata was invalid: %s" % metadata
+    response_iterator = self._unary_stream(request, metadata=metadata)
+    with self.assertRaises(grpc.RpcError) as exception_context:
+      next(response_iterator)
+    self.assertEqual(
+        exception_context.exception.details(), expected_error_details)
+    self.assertEqual(
+        exception_context.exception.code(), grpc.StatusCode.INTERNAL)
+    self.assertEqual(response_iterator.details(), expected_error_details)
+    self.assertEqual(response_iterator.code(), grpc.StatusCode.INTERNAL)
+
+  def testStreamRequestBlockingUnaryResponse(self):
+    request_iterator = (b'\x07\x08' for _ in range(test_constants.STREAM_LENGTH))
+    metadata = (('InVaLiD', 'StreamRequestBlockingUnaryResponse'),)
+    expected_error_details = "metadata was invalid: %s" % metadata
+    with self.assertRaises(ValueError) as exception_context:
+      self._stream_unary(request_iterator, metadata=metadata)
+    self.assertIn(expected_error_details, str(exception_context.exception))
+
+  def testStreamRequestBlockingUnaryResponseWithCall(self):
+    request_iterator = (
+        b'\x07\x08' for _ in range(test_constants.STREAM_LENGTH))
+    metadata = (('InVaLiD', 'StreamRequestBlockingUnaryResponseWithCall'),)
+    expected_error_details = "metadata was invalid: %s" % metadata
+    multi_callable = _stream_unary_multi_callable(self._channel)
+    with self.assertRaises(ValueError) as exception_context:
+      multi_callable.with_call(request_iterator, metadata=metadata)
+    self.assertIn(expected_error_details, str(exception_context.exception))
+
+  def testStreamRequestFutureUnaryResponse(self):
+    request_iterator = (
+        b'\x07\x08' for _ in range(test_constants.STREAM_LENGTH))
+    metadata = (('InVaLiD', 'StreamRequestFutureUnaryResponse'),)
+    expected_error_details = "metadata was invalid: %s" % metadata
+    response_future = self._stream_unary.future(
+        request_iterator, metadata=metadata)
+    with self.assertRaises(grpc.RpcError) as exception_context:
+      response_future.result()
+    self.assertEqual(
+        exception_context.exception.details(), expected_error_details)
+    self.assertEqual(
+        exception_context.exception.code(), grpc.StatusCode.INTERNAL)
+    self.assertEqual(response_future.details(), expected_error_details)
+    self.assertEqual(response_future.code(), grpc.StatusCode.INTERNAL)
+
+  def testStreamRequestStreamResponse(self):
+    request_iterator = (
+        b'\x07\x08' for _ in range(test_constants.STREAM_LENGTH))
+    metadata = (('InVaLiD', 'StreamRequestStreamResponse'),)
+    expected_error_details = "metadata was invalid: %s" % metadata
+    response_iterator = self._stream_stream(request_iterator, metadata=metadata)
+    with self.assertRaises(grpc.RpcError) as exception_context:
+      next(response_iterator)
+    self.assertEqual(
+        exception_context.exception.details(), expected_error_details)
+    self.assertEqual(
+        exception_context.exception.code(), grpc.StatusCode.INTERNAL)
+    self.assertEqual(response_iterator.details(), expected_error_details)
+    self.assertEqual(response_iterator.code(), grpc.StatusCode.INTERNAL)
+
+
+if __name__ == '__main__':
+  unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_invocation_defects_test.py b/src/python/grpcio_tests/tests/unit/_invocation_defects_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..4312679bb96640ee3d2d52c8c63661c7f9072720
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/_invocation_defects_test.py
@@ -0,0 +1,247 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import itertools
+import threading
+import unittest
+from concurrent import futures
+
+import grpc
+from grpc.framework.foundation import logging_pool
+
+from tests.unit.framework.common import test_constants
+from tests.unit.framework.common import test_control
+
+_SERIALIZE_REQUEST = lambda bytestring: bytestring * 2
+_DESERIALIZE_REQUEST = lambda bytestring: bytestring[len(bytestring) // 2:]
+_SERIALIZE_RESPONSE = lambda bytestring: bytestring * 3
+_DESERIALIZE_RESPONSE = lambda bytestring: bytestring[:len(bytestring) // 3]
+
+_UNARY_UNARY = '/test/UnaryUnary'
+_UNARY_STREAM = '/test/UnaryStream'
+_STREAM_UNARY = '/test/StreamUnary'
+_STREAM_STREAM = '/test/StreamStream'
+
+
+class _Callback(object):
+  def __init__(self):
+    self._condition = threading.Condition()
+    self._value = None
+    self._called = False
+
+  def __call__(self, value):
+    with self._condition:
+      self._value = value
+      self._called = True
+      self._condition.notify_all()
+
+  def value(self):
+    with self._condition:
+      while not self._called:
+        self._condition.wait()
+      return self._value
+
+
+class _Handler(object):
+  def __init__(self, control):
+    self._control = control
+
+  def handle_unary_unary(self, request, servicer_context):
+    self._control.control()
+    if servicer_context is not None:
+      servicer_context.set_trailing_metadata((('testkey', 'testvalue',),))
+    return request
+
+  def handle_unary_stream(self, request, servicer_context):
+    for _ in range(test_constants.STREAM_LENGTH):
+      self._control.control()
+      yield request
+    self._control.control()
+    if servicer_context is not None:
+      servicer_context.set_trailing_metadata((('testkey', 'testvalue',),))
+
+  def handle_stream_unary(self, request_iterator, servicer_context):
+    if servicer_context is not None:
+      servicer_context.invocation_metadata()
+    self._control.control()
+    response_elements = []
+    for request in request_iterator:
+      self._control.control()
+      response_elements.append(request)
+    self._control.control()
+    if servicer_context is not None:
+      servicer_context.set_trailing_metadata((('testkey', 'testvalue',),))
+    return b''.join(response_elements)
+
+  def handle_stream_stream(self, request_iterator, servicer_context):
+    self._control.control()
+    if servicer_context is not None:
+      servicer_context.set_trailing_metadata((('testkey', 'testvalue',),))
+    for request in request_iterator:
+      self._control.control()
+      yield request
+    self._control.control()
+
+
+class _MethodHandler(grpc.RpcMethodHandler):
+  def __init__(
+    self, request_streaming, response_streaming, request_deserializer,
+    response_serializer, unary_unary, unary_stream, stream_unary,
+    stream_stream):
+    self.request_streaming = request_streaming
+    self.response_streaming = response_streaming
+    self.request_deserializer = request_deserializer
+    self.response_serializer = response_serializer
+    self.unary_unary = unary_unary
+    self.unary_stream = unary_stream
+    self.stream_unary = stream_unary
+    self.stream_stream = stream_stream
+
+
+class _GenericHandler(grpc.GenericRpcHandler):
+  def __init__(self, handler):
+    self._handler = handler
+
+  def service(self, handler_call_details):
+    if handler_call_details.method == _UNARY_UNARY:
+      return _MethodHandler(
+        False, False, None, None, self._handler.handle_unary_unary, None,
+        None, None)
+    elif handler_call_details.method == _UNARY_STREAM:
+      return _MethodHandler(
+        False, True, _DESERIALIZE_REQUEST, _SERIALIZE_RESPONSE, None,
+        self._handler.handle_unary_stream, None, None)
+    elif handler_call_details.method == _STREAM_UNARY:
+      return _MethodHandler(
+        True, False, _DESERIALIZE_REQUEST, _SERIALIZE_RESPONSE, None, None,
+        self._handler.handle_stream_unary, None)
+    elif handler_call_details.method == _STREAM_STREAM:
+      return _MethodHandler(
+        True, True, None, None, None, None, None,
+        self._handler.handle_stream_stream)
+    else:
+      return None
+
+
+class FailAfterFewIterationsCounter(object):
+    def __init__(self, high, bytestring):
+        self._current = 0
+        self._high = high
+        self._bytestring = bytestring
+
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        if self._current >= self._high:
+            raise Exception("This is a deliberate failure in a unit test.")
+        else:
+            self._current += 1
+            return self._bytestring
+
+
+def _unary_unary_multi_callable(channel):
+  return channel.unary_unary(_UNARY_UNARY)
+
+
+def _unary_stream_multi_callable(channel):
+  return channel.unary_stream(
+    _UNARY_STREAM,
+    request_serializer=_SERIALIZE_REQUEST,
+    response_deserializer=_DESERIALIZE_RESPONSE)
+
+
+def _stream_unary_multi_callable(channel):
+  return channel.stream_unary(
+    _STREAM_UNARY,
+    request_serializer=_SERIALIZE_REQUEST,
+    response_deserializer=_DESERIALIZE_RESPONSE)
+
+
+def _stream_stream_multi_callable(channel):
+  return channel.stream_stream(_STREAM_STREAM)
+
+
+class InvocationDefectsTest(unittest.TestCase):
+  def setUp(self):
+    self._control = test_control.PauseFailControl()
+    self._handler = _Handler(self._control)
+    self._server_pool = logging_pool.pool(test_constants.THREAD_CONCURRENCY)
+
+    self._server = grpc.server(self._server_pool)
+    port = self._server.add_insecure_port('[::]:0')
+    self._server.add_generic_rpc_handlers((_GenericHandler(self._handler),))
+    self._server.start()
+
+    self._channel = grpc.insecure_channel('localhost:%d' % port)
+
+  def tearDown(self):
+    self._server.stop(0)
+
+  def testIterableStreamRequestBlockingUnaryResponse(self):
+    requests = [b'\x07\x08' for _ in range(test_constants.STREAM_LENGTH)]
+    multi_callable = _stream_unary_multi_callable(self._channel)
+
+    with self.assertRaises(grpc.RpcError):
+      response = multi_callable(
+        requests,
+        metadata=(('test', 'IterableStreamRequestBlockingUnaryResponse'),))
+
+  def testIterableStreamRequestFutureUnaryResponse(self):
+    requests = [b'\x07\x08' for _ in range(test_constants.STREAM_LENGTH)]
+    multi_callable = _stream_unary_multi_callable(self._channel)
+    response_future = multi_callable.future(
+      requests,
+      metadata=(
+        ('test', 'IterableStreamRequestFutureUnaryResponse'),))
+
+    with self.assertRaises(grpc.RpcError):
+      response = response_future.result()
+
+  def testIterableStreamRequestStreamResponse(self):
+    requests = [b'\x77\x58' for _ in range(test_constants.STREAM_LENGTH)]
+    multi_callable = _stream_stream_multi_callable(self._channel)
+    response_iterator = multi_callable(
+      requests,
+      metadata=(('test', 'IterableStreamRequestStreamResponse'),))
+
+    with self.assertRaises(grpc.RpcError):
+      next(response_iterator)
+
+  def testIteratorStreamRequestStreamResponse(self):
+    requests_iterator = FailAfterFewIterationsCounter(
+      test_constants.STREAM_LENGTH // 2, b'\x07\x08')
+    multi_callable = _stream_stream_multi_callable(self._channel)
+    response_iterator = multi_callable(
+      requests_iterator,
+      metadata=(('test', 'IteratorStreamRequestStreamResponse'),))
+
+    with self.assertRaises(grpc.RpcError):
+      for _ in range(test_constants.STREAM_LENGTH // 2 + 1):
+        next(response_iterator)
diff --git a/src/python/grpcio_tests/tests/unit/_metadata_test.py b/src/python/grpcio_tests/tests/unit/_metadata_test.py
index da7347692998d8e0fa35b5a04db790e23cde8850..caba53ffcc9030e7b5cfb48e5df9b62e855acea0 100644
--- a/src/python/grpcio_tests/tests/unit/_metadata_test.py
+++ b/src/python/grpcio_tests/tests/unit/_metadata_test.py
@@ -193,7 +193,7 @@ class MetadataTest(unittest.TestCase):
   def testStreamUnary(self):
     multi_callable = self._channel.stream_unary(_STREAM_UNARY)
     unused_response, call = multi_callable.with_call(
-        [_REQUEST] * test_constants.STREAM_LENGTH,
+        iter([_REQUEST] * test_constants.STREAM_LENGTH),
         metadata=_CLIENT_METADATA)
     self.assertTrue(test_common.metadata_transmitted(
         _SERVER_INITIAL_METADATA, call.initial_metadata()))
@@ -202,7 +202,7 @@ class MetadataTest(unittest.TestCase):
 
   def testStreamStream(self):
     multi_callable = self._channel.stream_stream(_STREAM_STREAM)
-    call = multi_callable([_REQUEST] * test_constants.STREAM_LENGTH,
+    call = multi_callable(iter([_REQUEST] * test_constants.STREAM_LENGTH),
                           metadata=_CLIENT_METADATA)
     self.assertTrue(test_common.metadata_transmitted(
         _SERVER_INITIAL_METADATA, call.initial_metadata()))
diff --git a/src/python/grpcio_tests/tests/unit/beta/_utilities_test.py b/src/python/grpcio_tests/tests/unit/beta/_utilities_test.py
index 90fe10c77ce8d4df6e3bdf8b727a6dbf492cfe76..9cce96cc85c43f6db017485d7f2b42627d1a267e 100644
--- a/src/python/grpcio_tests/tests/unit/beta/_utilities_test.py
+++ b/src/python/grpcio_tests/tests/unit/beta/_utilities_test.py
@@ -66,7 +66,7 @@ class ChannelConnectivityTest(unittest.TestCase):
     ready_future = utilities.channel_ready_future(channel)
     ready_future.add_done_callback(callback.accept_value)
     with self.assertRaises(future.TimeoutError):
-      ready_future.result(test_constants.SHORT_TIMEOUT)
+      ready_future.result(timeout=test_constants.SHORT_TIMEOUT)
     self.assertFalse(ready_future.cancelled())
     self.assertFalse(ready_future.done())
     self.assertTrue(ready_future.running())
@@ -88,7 +88,7 @@ class ChannelConnectivityTest(unittest.TestCase):
       ready_future = utilities.channel_ready_future(channel)
       ready_future.add_done_callback(callback.accept_value)
       self.assertIsNone(
-          ready_future.result(test_constants.SHORT_TIMEOUT))
+          ready_future.result(timeout=test_constants.LONG_TIMEOUT))
       value_passed_to_callback = callback.block_until_called()
       self.assertIs(ready_future, value_passed_to_callback)
       self.assertFalse(ready_future.cancelled())
diff --git a/src/ruby/ext/grpc/rb_byte_buffer.c b/src/ruby/ext/grpc/rb_byte_buffer.c
index f97890e4a2eef0c117d1d628f9696507d1fcd16b..47fd6d91200c36919498e2b9d78c40501ef4a23c 100644
--- a/src/ruby/ext/grpc/rb_byte_buffer.c
+++ b/src/ruby/ext/grpc/rb_byte_buffer.c
@@ -65,5 +65,6 @@ VALUE grpc_rb_byte_buffer_to_s(grpc_byte_buffer *buffer) {
                GRPC_SLICE_LENGTH(next));
     grpc_slice_unref(next);
   }
+  grpc_byte_buffer_reader_destroy(&reader);
   return rb_string;
 }
diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c
index 2a6a246e677321303fa2f36cd32346145c86f1ba..c7b112c94b446b3b0779bf65c2d1fd51f37c0061 100644
--- a/src/ruby/ext/grpc/rb_server.c
+++ b/src/ruby/ext/grpc/rb_server.c
@@ -37,6 +37,7 @@
 #include "rb_server.h"
 
 #include <grpc/grpc.h>
+#include <grpc/support/atm.h>
 #include <grpc/grpc_security.h>
 #include <grpc/support/log.h>
 #include "rb_call.h"
@@ -59,22 +60,26 @@ typedef struct grpc_rb_server {
   /* The actual server */
   grpc_server *wrapped;
   grpc_completion_queue *queue;
+  gpr_atm shutdown_started;
 } grpc_rb_server;
 
 static void destroy_server(grpc_rb_server *server, gpr_timespec deadline) {
   grpc_event ev;
-  if (server->wrapped != NULL) {
-    grpc_server_shutdown_and_notify(server->wrapped, server->queue, NULL);
-    ev = rb_completion_queue_pluck(server->queue, NULL, deadline, NULL);
-    if (ev.type == GRPC_QUEUE_TIMEOUT) {
-      grpc_server_cancel_all_calls(server->wrapped);
-      rb_completion_queue_pluck(server->queue, NULL,
-                                gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+  // This can be started by app or implicitly by GC. Avoid a race between these.
+  if (gpr_atm_full_fetch_add(&server->shutdown_started, (gpr_atm)1) == 0) {
+    if (server->wrapped != NULL) {
+      grpc_server_shutdown_and_notify(server->wrapped, server->queue, NULL);
+      ev = rb_completion_queue_pluck(server->queue, NULL, deadline, NULL);
+      if (ev.type == GRPC_QUEUE_TIMEOUT) {
+        grpc_server_cancel_all_calls(server->wrapped);
+        rb_completion_queue_pluck(server->queue, NULL,
+                                  gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+      }
+      grpc_server_destroy(server->wrapped);
+      grpc_rb_completion_queue_destroy(server->queue);
+      server->wrapped = NULL;
+      server->queue = NULL;
     }
-    grpc_server_destroy(server->wrapped);
-    grpc_rb_completion_queue_destroy(server->queue);
-    server->wrapped = NULL;
-    server->queue = NULL;
   }
 }
 
@@ -115,6 +120,7 @@ static const rb_data_type_t grpc_rb_server_data_type = {
 static VALUE grpc_rb_server_alloc(VALUE cls) {
   grpc_rb_server *wrapper = ALLOC(grpc_rb_server);
   wrapper->wrapped = NULL;
+  wrapper->shutdown_started = (gpr_atm)0;
   return TypedData_Wrap_Struct(cls, &grpc_rb_server_data_type, wrapper);
 }
 
diff --git a/src/ruby/lib/grpc/errors.rb b/src/ruby/lib/grpc/errors.rb
index 23b2bb7e127f707a645acaabfff19f3e9de24886..f6998e17c496bb6a94edde2da64567cb116b82a0 100644
--- a/src/ruby/lib/grpc/errors.rb
+++ b/src/ruby/lib/grpc/errors.rb
@@ -35,9 +35,18 @@ module GRPC
   # either end of a GRPC connection.  When raised, it indicates that a status
   # error should be returned to the other end of a GRPC connection; when
   # caught it means that this end received a status error.
+  #
+  # There is also subclass of BadStatus in this module for each GRPC status.
+  # E.g., the GRPC::Cancelled class corresponds to status CANCELLED.
+  #
+  # See
+  # https://github.com/grpc/grpc/blob/master/include/grpc/impl/codegen/status.h
+  # for detailed descriptions of each status code.
   class BadStatus < StandardError
     attr_reader :code, :details, :metadata
 
+    include GRPC::Core::StatusCodes
+
     # @param code [Numeric] the status code
     # @param details [String] the details of the exception
     # @param metadata [Hash] the error's metadata
@@ -55,9 +64,152 @@ module GRPC
     def to_status
       Struct::Status.new(code, details, @metadata)
     end
+
+    def self.new_status_exception(code, details = 'unkown cause', metadata = {})
+      codes = {}
+      codes[OK] = Ok
+      codes[CANCELLED] = Cancelled
+      codes[UNKNOWN] = Unknown
+      codes[INVALID_ARGUMENT] = InvalidArgument
+      codes[DEADLINE_EXCEEDED] = DeadlineExceeded
+      codes[NOT_FOUND] = NotFound
+      codes[ALREADY_EXISTS] = AlreadyExists
+      codes[PERMISSION_DENIED] =  PermissionDenied
+      codes[UNAUTHENTICATED] = Unauthenticated
+      codes[RESOURCE_EXHAUSTED] = ResourceExhausted
+      codes[FAILED_PRECONDITION] = FailedPrecondition
+      codes[ABORTED] = Aborted
+      codes[OUT_OF_RANGE] = OutOfRange
+      codes[UNIMPLEMENTED] =  Unimplemented
+      codes[INTERNAL] = Internal
+      codes[UNIMPLEMENTED] =  Unimplemented
+      codes[UNAVAILABLE] =  Unavailable
+      codes[DATA_LOSS] = DataLoss
+
+      if codes[code].nil?
+        BadStatus.new(code, details, metadata)
+      else
+        codes[code].new(details, metadata)
+      end
+    end
+  end
+
+  # GRPC status code corresponding to status OK
+  class Ok < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::OK, details, metadata)
+    end
   end
 
-  # Cancelled is an exception class that indicates that an rpc was cancelled.
-  class Cancelled < StandardError
+  # GRPC status code corresponding to status CANCELLED
+  class Cancelled < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::CANCELLED, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status UNKNOWN
+  class Unknown < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::UNKNOWN, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status INVALID_ARGUMENT
+  class InvalidArgument < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::INVALID_ARGUMENT, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status DEADLINE_EXCEEDED
+  class DeadlineExceeded < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::DEADLINE_EXCEEDED, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status NOT_FOUND
+  class NotFound < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::NOT_FOUND, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status ALREADY_EXISTS
+  class AlreadyExists < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::ALREADY_EXISTS, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status PERMISSION_DENIED
+  class PermissionDenied < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::PERMISSION_DENIED, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status UNAUTHENTICATED
+  class Unauthenticated < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::UNAUTHENTICATED, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status RESOURCE_EXHAUSTED
+  class ResourceExhausted < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::RESOURCE_EXHAUSTED, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status FAILED_PRECONDITION
+  class FailedPrecondition < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::FAILED_PRECONDITION, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status ABORTED
+  class Aborted < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::ABORTED, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status OUT_OF_RANGE
+  class OutOfRange < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::OUT_OF_RANGE, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status UNIMPLEMENTED
+  class Unimplemented < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::UNIMPLEMENTED, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status INTERNAL
+  class Internal < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::INTERNAL, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status UNAVAILABLE
+  class Unavailable < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::UNAVAILABLE, details, metadata)
+    end
+  end
+
+  # GRPC status code corresponding to status DATA_LOSS
+  class DataLoss < BadStatus
+    def initialize(details = 'unknown cause', metadata = {})
+      super(Core::StatusCodes::DATA_LOSS, details, metadata)
+    end
   end
 end
diff --git a/src/ruby/lib/grpc/generic/active_call.rb b/src/ruby/lib/grpc/generic/active_call.rb
index f5c426ebfc6e122f4ec3f7d6057679111908cb63..3b31f77ec088ca0a5738a7e193c24cd9347e0e9c 100644
--- a/src/ruby/lib/grpc/generic/active_call.rb
+++ b/src/ruby/lib/grpc/generic/active_call.rb
@@ -43,8 +43,8 @@ class Struct
         GRPC.logger.debug("Failing with status #{status}")
         # raise BadStatus, propagating the metadata if present.
         md = status.metadata
-        fail GRPC::BadStatus.new(status.code, status.details, md),
-             "status code: #{status.code}, details: #{status.details}"
+        fail GRPC::BadStatus.new_status_exception(
+          status.code, status.details, md)
       end
       status
     end
diff --git a/src/ruby/lib/grpc/generic/bidi_call.rb b/src/ruby/lib/grpc/generic/bidi_call.rb
index d7cd9e6df2b8ff14ab451e7403dc3b3bcba25e0b..adc77bbf5d515f7ef61113d4001daa3da6da210d 100644
--- a/src/ruby/lib/grpc/generic/bidi_call.rb
+++ b/src/ruby/lib/grpc/generic/bidi_call.rb
@@ -200,6 +200,7 @@ module GRPC
             if is_client
               batch_result = @call.run_batch(RECV_STATUS_ON_CLIENT => nil)
               @call.status = batch_result.status
+              @call.trailing_metadata = @call.status.metadata if @call.status
               batch_result.check_status
               GRPC.logger.debug("bidi-read-loop: done status #{@call.status}")
             end
@@ -219,6 +220,10 @@ module GRPC
       GRPC.logger.debug('bidi-read-loop: finished')
       @reads_complete = true
       finished
+      # Make sure that the write loop is done done before finishing the call.
+      # Note that blocking is ok at this point because we've already received
+      # a status
+      @enq_th.join if is_client
     end
   end
 end
diff --git a/src/ruby/lib/grpc/generic/rpc_desc.rb b/src/ruby/lib/grpc/generic/rpc_desc.rb
index cd17aed8e7c4c07bb42233485bbe2f2a85a6b4b4..d46c4a1b5c23a1fa4d05b67f906ae7a5d098dd77 100644
--- a/src/ruby/lib/grpc/generic/rpc_desc.rb
+++ b/src/ruby/lib/grpc/generic/rpc_desc.rb
@@ -119,7 +119,7 @@ module GRPC
       # Send back a UNKNOWN status to the client
       GRPC.logger.warn("failed handler: #{active_call}; sending status:UNKNOWN")
       GRPC.logger.warn(e)
-      send_status(active_call, UNKNOWN, 'unkown error handling call on server')
+      send_status(active_call, UNKNOWN, "#{e.class}: #{e.message}")
     end
 
     def assert_arity_matches(mth)
diff --git a/src/ruby/lib/grpc/generic/service.rb b/src/ruby/lib/grpc/generic/service.rb
index 7cb9f1cc99d95448d5728095d43f9bd478d15150..f5a6b49eb4471d038578c4be81ca769b4538c34d 100644
--- a/src/ruby/lib/grpc/generic/service.rb
+++ b/src/ruby/lib/grpc/generic/service.rb
@@ -110,8 +110,9 @@ module GRPC
         rpc_descs[name] = RpcDesc.new(name, input, output,
                                       marshal_class_method,
                                       unmarshal_class_method)
-        define_method(name) do
-          fail GRPC::BadStatus, GRPC::Core::StatusCodes::UNIMPLEMENTED
+        define_method(GenericService.underscore(name.to_s).to_sym) do |_, _|
+          fail GRPC::BadStatus.new_status_exception(
+            GRPC::Core::StatusCodes::UNIMPLEMENTED)
         end
       end
 
diff --git a/src/ruby/pb/grpc/health/checker.rb b/src/ruby/pb/grpc/health/checker.rb
index 4bce1744c48f2e32c20ea2cdbbfe2041aceadff7..6b2d852ebfd6ad23d08764f18a761b1bba80290e 100644
--- a/src/ruby/pb/grpc/health/checker.rb
+++ b/src/ruby/pb/grpc/health/checker.rb
@@ -52,7 +52,9 @@ module Grpc
         @status_mutex.synchronize do
           status = @statuses["#{req.service}"]
         end
-        fail GRPC::BadStatus, StatusCodes::NOT_FOUND if status.nil?
+	if status.nil?
+          fail GRPC::BadStatus.new_status_exception(StatusCodes::NOT_FOUND)
+	end
         HealthCheckResponse.new(status: status)
       end
 
diff --git a/src/ruby/pb/src/proto/grpc/testing/test_services_pb.rb b/src/ruby/pb/src/proto/grpc/testing/test_services_pb.rb
index fde328e4c5d2053cf3bc7f58683bb543098991bf..7d1072e512c51701b7793a614d05a0ea43af9031 100644
--- a/src/ruby/pb/src/proto/grpc/testing/test_services_pb.rb
+++ b/src/ruby/pb/src/proto/grpc/testing/test_services_pb.rb
@@ -54,6 +54,10 @@ module Grpc
         rpc :EmptyCall, Empty, Empty
         # One request followed by one response.
         rpc :UnaryCall, SimpleRequest, SimpleResponse
+        # One request followed by one response. Response has cache control
+        # headers set such that a caching HTTP proxy (such as GFE) can
+        # satisfy subsequent requests.
+        rpc :CacheableUnaryCall, SimpleRequest, SimpleResponse
         # One request followed by a sequence of responses (streamed download).
         # The server returns the payload with client desired type and sizes.
         rpc :StreamingOutputCall, StreamingOutputCallRequest, stream(StreamingOutputCallResponse)
@@ -69,6 +73,9 @@ module Grpc
         # stream of responses are returned to the client when the server starts with
         # first request.
         rpc :HalfDuplexCall, stream(StreamingOutputCallRequest), stream(StreamingOutputCallResponse)
+        # The test server will not implement this method. It will be used
+        # to test the behavior when clients call unimplemented methods.
+        rpc :UnimplementedCall, Empty, Empty
       end
 
       Stub = Service.rpc_stub_class
diff --git a/src/ruby/pb/test/client.rb b/src/ruby/pb/test/client.rb
index 1e3ae656302da8e8fd8ad1f58b4bf2cd8919e009..9c4ee9c6f2bb4b0ad3499951aa87fd87c734b666 100755
--- a/src/ruby/pb/test/client.rb
+++ b/src/ruby/pb/test/client.rb
@@ -158,14 +158,26 @@ def create_stub(opts)
 
     GRPC.logger.info("... connecting securely to #{address}")
     stub_opts[:channel_args].merge!(compression_channel_args)
-    Grpc::Testing::TestService::Stub.new(address, creds, **stub_opts)
+    if opts.test_case == "unimplemented_service"
+      Grpc::Testing::UnimplementedService::Stub.new(address, creds, **stub_opts)
+    else
+      Grpc::Testing::TestService::Stub.new(address, creds, **stub_opts)
+    end
   else
     GRPC.logger.info("... connecting insecurely to #{address}")
-    Grpc::Testing::TestService::Stub.new(
-      address,
-      :this_channel_is_insecure,
-      channel_args: compression_channel_args
-    )
+    if opts.test_case == "unimplemented_service"
+      Grpc::Testing::UnimplementedService::Stub.new(
+        address,
+        :this_channel_is_insecure,
+        channel_args: compression_channel_args
+      )
+    else
+      Grpc::Testing::TestService::Stub.new(
+        address,
+        :this_channel_is_insecure,
+        channel_args: compression_channel_args
+      )
+    end
   end
 end
 
@@ -459,11 +471,8 @@ class NamedTests
     deadline = GRPC::Core::TimeConsts::from_relative_time(1)
     resps = @stub.full_duplex_call(enum.each_item, deadline: deadline)
     resps.each { } # wait to receive each request (or timeout)
-    fail 'Should have raised GRPC::BadStatus(DEADLINE_EXCEEDED)'
-  rescue GRPC::BadStatus => e
-    assert("#{__callee__}: status was wrong") do
-      e.code == GRPC::Core::StatusCodes::DEADLINE_EXCEEDED
-    end
+    fail 'Should have raised GRPC::DeadlineExceeded'
+  rescue GRPC::DeadlineExceeded
   end
 
   def empty_stream
@@ -505,6 +514,153 @@ class NamedTests
     op.wait
   end
 
+  def unimplemented_method
+    begin
+      resp = @stub.unimplemented_call(Empty.new)
+    rescue GRPC::Unimplemented => e
+      return
+    rescue Exception => e
+      fail AssertionError, "Expected BadStatus. Received: #{e.inspect}"
+    end
+    fail AssertionError, "GRPC::Unimplemented should have been raised. Was not."
+  end
+
+  def unimplemented_service
+    begin
+      resp = @stub.unimplemented_call(Empty.new)
+    rescue GRPC::Unimplemented => e
+      return
+    rescue Exception => e
+      fail AssertionError, "Expected BadStatus. Received: #{e.inspect}"
+    end
+    fail AssertionError, "GRPC::Unimplemented should have been raised. Was not."
+  end
+
+  def status_code_and_message
+
+    # Function wide constants.
+    message = "test status method"
+    code = GRPC::Core::StatusCodes::UNKNOWN
+
+    # Testing with UnaryCall.
+    payload = Payload.new(type: :COMPRESSABLE, body: nulls(1))
+    echo_status = EchoStatus.new(code: code, message: message)
+    req = SimpleRequest.new(response_type: :COMPRESSABLE,
+			    response_size: 1,
+			    payload: payload,
+			    response_status: echo_status)
+    seen_correct_exception = false
+    begin
+      resp = @stub.unary_call(req)
+    rescue GRPC::Unknown => e
+      if e.details != message
+	      fail AssertionError,
+	        "Expected message #{message}. Received: #{e.details}"
+      end
+      seen_correct_exception = true
+    rescue Exception => e
+      fail AssertionError, "Expected BadStatus. Received: #{e.inspect}"
+    end
+
+    if not seen_correct_exception
+      fail AssertionError, "Did not see expected status from UnaryCall"
+    end
+
+    # testing with FullDuplex
+    req_cls, p_cls = StreamingOutputCallRequest, ResponseParameters
+    duplex_req = req_cls.new(payload: Payload.new(body: nulls(1)),
+                  response_type: :COMPRESSABLE,
+                  response_parameters: [p_cls.new(size: 1)],
+                  response_status: echo_status)
+    seen_correct_exception = false
+    begin
+      resp = @stub.full_duplex_call([duplex_req])
+      resp.each { |r| }
+    rescue GRPC::Unknown => e
+      if e.details != message
+        fail AssertionError,
+          "Expected message #{message}. Received: #{e.details}"
+      end
+      seen_correct_exception = true
+    rescue Exception => e
+      fail AssertionError, "Expected BadStatus. Received: #{e.inspect}"
+    end
+
+    if not seen_correct_exception
+      fail AssertionError, "Did not see expected status from FullDuplexCall"
+    end
+
+  end
+
+
+  def custom_metadata
+
+    # Function wide constants
+    req_size, wanted_response_size = 271_828, 314_159
+    initial_metadata_key = "x-grpc-test-echo-initial"
+    initial_metadata_value = "test_initial_metadata_value"
+    trailing_metadata_key = "x-grpc-test-echo-trailing-bin"
+    trailing_metadata_value = "\x0a\x0b\x0a\x0b\x0a\x0b"
+
+    metadata = {
+      initial_metadata_key => initial_metadata_value,
+      trailing_metadata_key => trailing_metadata_value
+    }
+
+    # Testing with UnaryCall
+    payload = Payload.new(type: :COMPRESSABLE, body: nulls(req_size))
+    req = SimpleRequest.new(response_type: :COMPRESSABLE,
+			    response_size: wanted_response_size,
+			    payload: payload)
+
+    op = @stub.unary_call(req, metadata: metadata, return_op: true)
+    op.execute
+    if not op.metadata.has_key?(initial_metadata_key)
+      fail AssertionError, "Expected initial metadata. None received"
+    elsif op.metadata[initial_metadata_key] != metadata[initial_metadata_key]
+      fail AssertionError, 
+             "Expected initial metadata: #{metadata[initial_metadata_key]}. "\
+             "Received: #{op.metadata[initial_metadata_key]}"
+    end
+    if not op.trailing_metadata.has_key?(trailing_metadata_key)
+      fail AssertionError, "Expected trailing metadata. None received"
+    elsif op.trailing_metadata[trailing_metadata_key] !=
+          metadata[trailing_metadata_key]
+      fail AssertionError, 
+            "Expected trailing metadata: #{metadata[trailing_metadata_key]}. "\
+            "Received: #{op.trailing_metadata[trailing_metadata_key]}"
+    end
+
+    # Testing with FullDuplex
+    req_cls, p_cls = StreamingOutputCallRequest, ResponseParameters
+    duplex_req = req_cls.new(payload: Payload.new(body: nulls(req_size)),
+                  response_type: :COMPRESSABLE,
+                  response_parameters: [p_cls.new(size: wanted_response_size)])
+
+    duplex_op = @stub.full_duplex_call([duplex_req], metadata: metadata,
+                                        return_op: true)
+    resp = duplex_op.execute
+    resp.each { |r| } # ensures that the server sends trailing data
+    duplex_op.wait
+    if not duplex_op.metadata.has_key?(initial_metadata_key)
+      fail AssertionError, "Expected initial metadata. None received"
+    elsif duplex_op.metadata[initial_metadata_key] !=
+          metadata[initial_metadata_key]
+      fail AssertionError,
+             "Expected initial metadata: #{metadata[initial_metadata_key]}. "\
+             "Received: #{duplex_op.metadata[initial_metadata_key]}"
+    end
+    if not duplex_op.trailing_metadata[trailing_metadata_key]
+      fail AssertionError, "Expected trailing metadata. None received"
+    elsif duplex_op.trailing_metadata[trailing_metadata_key] !=
+          metadata[trailing_metadata_key]
+      fail AssertionError, 
+          "Expected trailing metadata: #{metadata[trailing_metadata_key]}. "\
+          "Received: #{duplex_op.trailing_metadata[trailing_metadata_key]}"
+    end
+
+  end
+
   def all
     all_methods = NamedTests.instance_methods(false).map(&:to_s)
     all_methods.each do |m|
diff --git a/src/ruby/pb/test/server.rb b/src/ruby/pb/test/server.rb
index 3f1e0a1ccfc00f94bd21879265af866936bd7f88..cc9b8726481b9225e74c7c7a52dda72dc73123ec 100755
--- a/src/ruby/pb/test/server.rb
+++ b/src/ruby/pb/test/server.rb
@@ -129,6 +129,27 @@ def nulls(l)
   [].pack('x' * l).force_encoding('ascii-8bit')
 end
 
+def maybe_echo_metadata(_call)
+  
+  # these are consistent for all interop tests
+  initial_metadata_key = "x-grpc-test-echo-initial"
+  trailing_metadata_key = "x-grpc-test-echo-trailing-bin"
+
+  if _call.metadata.has_key?(initial_metadata_key)
+    _call.metadata_to_send[initial_metadata_key] = _call.metadata[initial_metadata_key]
+  end
+  if _call.metadata.has_key?(trailing_metadata_key)
+    _call.output_metadata[trailing_metadata_key] = _call.metadata[trailing_metadata_key]
+  end
+end
+
+def maybe_echo_status_and_message(req)
+  unless req.response_status.nil?
+    fail GRPC::BadStatus.new_status_exception(
+        req.response_status.code, req.response_status.message)
+  end
+end
+
 # A FullDuplexEnumerator passes requests to a block and yields generated responses
 class FullDuplexEnumerator
   include Grpc::Testing
@@ -143,6 +164,7 @@ class FullDuplexEnumerator
     begin
       cls = StreamingOutputCallResponse
       @requests.each do |req|
+        maybe_echo_status_and_message(req)
         req.response_parameters.each do |params|
           resp_size = params.size
           GRPC.logger.info("read a req, response size is #{resp_size}")
@@ -170,6 +192,8 @@ class TestTarget < Grpc::Testing::TestService::Service
   end
 
   def unary_call(simple_req, _call)
+    maybe_echo_metadata(_call)
+    maybe_echo_status_and_message(simple_req)
     req_size = simple_req.response_size
     SimpleResponse.new(payload: Payload.new(type: :COMPRESSABLE,
                                             body: nulls(req_size)))
@@ -189,7 +213,8 @@ class TestTarget < Grpc::Testing::TestService::Service
     end
   end
 
-  def full_duplex_call(reqs)
+  def full_duplex_call(reqs, _call)
+    maybe_echo_metadata(_call)
     # reqs is a lazy Enumerator of the requests sent by the client.
     FullDuplexEnumerator.new(reqs).each_item
   end
diff --git a/src/ruby/qps/client.rb b/src/ruby/qps/client.rb
index 8aed866da5d98c1dbd88b8728792879142d6acf9..817192626be0d0a86529f7f30037938752bc6783 100644
--- a/src/ruby/qps/client.rb
+++ b/src/ruby/qps/client.rb
@@ -134,6 +134,7 @@ class BenchmarkClient
     resp = stub.streaming_call(q.each_item)
     start = Time.now
     q.push(req)
+    pushed_sentinal = false
     resp.each do |r|
       @histogram.add((Time.now-start)*1e9)
       if !@done
@@ -141,8 +142,9 @@ class BenchmarkClient
         start = Time.now
         q.push(req)
       else
-        q.push(self)
-        break
+        q.push(self) unless pushed_sentinal
+	# Continue polling on the responses to consume and release resources
+        pushed_sentinal = true
       end
     end
   end
diff --git a/src/ruby/spec/error_sanity_spec.rb b/src/ruby/spec/error_sanity_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..77e94a88160b91a5f628acd4e1edb68ad89a899b
--- /dev/null
+++ b/src/ruby/spec/error_sanity_spec.rb
@@ -0,0 +1,64 @@
+# 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.
+
+require 'grpc'
+
+StatusCodes = GRPC::Core::StatusCodes
+
+describe StatusCodes do
+  # convert upper snake-case to camel case.
+  # e.g., DEADLINE_EXCEEDED -> DeadlineExceeded
+  def upper_snake_to_camel(name)
+    name.to_s.split('_').map(&:downcase).map(&:capitalize).join('')
+  end
+
+  StatusCodes.constants.each do |status_name|
+    it 'there is a subclass of BadStatus corresponding to StatusCode: ' \
+      "#{status_name} that has code: #{StatusCodes.const_get(status_name)}" do
+      camel_case = upper_snake_to_camel(status_name)
+      error_class = GRPC.const_get(camel_case)
+      # expect the error class to be a subclass of BadStatus
+      expect(error_class < GRPC::BadStatus)
+
+      error_object = error_class.new
+      # check that the code matches the int value of the error's constant
+      status_code = StatusCodes.const_get(status_name)
+      expect(error_object.code).to eq(status_code)
+
+      # check default parameters
+      expect(error_object.details).to eq('unknown cause')
+      expect(error_object.metadata).to eq({})
+
+      # check that the BadStatus factory for creates the correct
+      # exception too
+      from_factory = GRPC::BadStatus.new_status_exception(status_code)
+      expect(from_factory.is_a?(error_class)).to be(true)
+    end
+  end
+end
diff --git a/src/ruby/spec/generic/client_stub_spec.rb b/src/ruby/spec/generic/client_stub_spec.rb
index 607a4a3c5da980d936ff099bcf0075e90b0dd087..b51b291cbddd8ee4a96ffb11209f4a48c3647b02 100644
--- a/src/ruby/spec/generic/client_stub_spec.rb
+++ b/src/ruby/spec/generic/client_stub_spec.rb
@@ -190,15 +190,14 @@ describe 'ClientStub' do
         end
         creds = GRPC::Core::CallCredentials.new(failing_auth)
 
-        error_occured = false
+        unauth_error_occured = false
         begin
           get_response(stub, credentials: creds)
-        rescue GRPC::BadStatus => e
-          error_occured = true
-          expect(e.code).to eq(GRPC::Core::StatusCodes::UNAUTHENTICATED)
+        rescue GRPC::Unauthenticated => e
+          unauth_error_occured = true
           expect(e.details.include?(error_message)).to be true
         end
-        expect(error_occured).to eq(true)
+        expect(unauth_error_occured).to eq(true)
 
         # Kill the server thread so tests can complete
         th.kill
diff --git a/src/ruby/spec/generic/rpc_desc_spec.rb b/src/ruby/spec/generic/rpc_desc_spec.rb
index a3f0efa6036d27c8ac33e369573b9d5fde02293b..1ace7211e9cd7c2534e5b7ff9f59512291620354 100644
--- a/src/ruby/spec/generic/rpc_desc_spec.rb
+++ b/src/ruby/spec/generic/rpc_desc_spec.rb
@@ -48,7 +48,6 @@ describe GRPC::RpcDesc do
     @bidi_streamer = RpcDesc.new('ss', Stream.new(Object.new),
                                  Stream.new(Object.new), 'encode', 'decode')
     @bs_code = INTERNAL
-    @no_reason = 'unkown error handling call on server'
     @ok_response = Object.new
   end
 
@@ -62,8 +61,9 @@ describe GRPC::RpcDesc do
 
     it 'sends status UNKNOWN if other StandardErrors are raised' do
       expect(@call).to receive(:remote_read).once.and_return(Object.new)
-      expect(@call).to receive(:send_status) .once.with(UNKNOWN, @no_reason,
-                                                        false, metadata: {})
+      expect(@call).to receive(:send_status).once.with(UNKNOWN,
+                                                       arg_error_msg,
+                                                       false, metadata: {})
       this_desc.run_server_method(@call, method(:other_error))
     end
 
@@ -112,7 +112,7 @@ describe GRPC::RpcDesc do
       end
 
       it 'sends status UNKNOWN if other StandardErrors are raised' do
-        expect(@call).to receive(:send_status).once.with(UNKNOWN, @no_reason,
+        expect(@call).to receive(:send_status).once.with(UNKNOWN, arg_error_msg,
                                                          false, metadata: {})
         @client_streamer.run_server_method(@call, method(:other_error_alt))
       end
@@ -174,8 +174,9 @@ describe GRPC::RpcDesc do
       end
 
       it 'sends status UNKNOWN if other StandardErrors are raised' do
+        error_msg = arg_error_msg(StandardError.new)
         expect(@call).to receive(:run_server_bidi).and_raise(StandardError)
-        expect(@call).to receive(:send_status).once.with(UNKNOWN, @no_reason,
+        expect(@call).to receive(:send_status).once.with(UNKNOWN, error_msg,
                                                          false, metadata: {})
         @bidi_streamer.run_server_method(@call, method(:other_error_alt))
       end
@@ -342,4 +343,9 @@ describe GRPC::RpcDesc do
   def other_error_alt(_call)
     fail(ArgumentError, 'other error')
   end
+
+  def arg_error_msg(error = nil)
+    error ||= ArgumentError.new('other error')
+    "#{error.class}: #{error.message}"
+  end
 end
diff --git a/src/ruby/spec/generic/rpc_server_pool_spec.rb b/src/ruby/spec/generic/rpc_server_pool_spec.rb
index 48ccaee510156e69688a85c817f63db8a027c998..69e8222cb97f4bf0031e3efbc7b06597fbe8c179 100644
--- a/src/ruby/spec/generic/rpc_server_pool_spec.rb
+++ b/src/ruby/spec/generic/rpc_server_pool_spec.rb
@@ -94,18 +94,6 @@ describe GRPC::Pool do
       expect(q.pop).to be(o)
       p.stop
     end
-
-    it 'it throws an error if all of the workers have tasks to do' do
-      p = Pool.new(5)
-      p.start
-      job = proc {}
-      5.times do
-        expect(p.ready_for_work?).to be(true)
-        p.schedule(&job)
-      end
-      expect { p.schedule(&job) }.to raise_error
-      expect { p.schedule(&job) }.to raise_error
-    end
   end
 
   describe '#stop' do
diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb
index c5694790fdf0c6ef7772071360cab66de85f0189..806ea8ce9fd04ee50fbf832c26d25f70b47a828e 100644
--- a/src/ruby/spec/generic/rpc_server_spec.rb
+++ b/src/ruby/spec/generic/rpc_server_spec.rb
@@ -408,21 +408,21 @@ describe GRPC::RpcServer do
         req = EchoMsg.new
         n = 20 # arbitrary, use as many to ensure the server pool is exceeded
         threads = []
-        bad_status_code = nil
+        one_failed_as_unavailable = false
         n.times do
           threads << Thread.new do
             stub = SlowStub.new(alt_host, :this_channel_is_insecure)
             begin
               stub.an_rpc(req)
-            rescue GRPC::BadStatus => e
-              bad_status_code = e.code
+            rescue GRPC::ResourceExhausted
+              one_failed_as_unavailable = true
             end
           end
         end
         threads.each(&:join)
         alt_srv.stop
         t.join
-        expect(bad_status_code).to be(StatusCodes::RESOURCE_EXHAUSTED)
+        expect(one_failed_as_unavailable).to be(true)
       end
     end
 
diff --git a/src/ruby/spec/pb/health/checker_spec.rb b/src/ruby/spec/pb/health/checker_spec.rb
index 4711e09e88a7cb5b6a6abad4aa3193f319aa52dc..719510001c1a4e654d0f2868244c2d2570ef88a0 100644
--- a/src/ruby/spec/pb/health/checker_spec.rb
+++ b/src/ruby/spec/pb/health/checker_spec.rb
@@ -122,7 +122,7 @@ describe Grpc::Health::Checker do
           checker.check(HCReq.new(service: t[:service]), nil)
         end
         expected_msg = /#{StatusCodes::NOT_FOUND}/
-        expect(&blk).to raise_error GRPC::BadStatus, expected_msg
+        expect(&blk).to raise_error GRPC::NotFound, expected_msg
       end
     end
   end
@@ -141,7 +141,7 @@ describe Grpc::Health::Checker do
           checker.check(HCReq.new(service: t[:service]), nil)
         end
         expected_msg = /#{StatusCodes::NOT_FOUND}/
-        expect(&blk).to raise_error GRPC::BadStatus, expected_msg
+        expect(&blk).to raise_error GRPC::NotFound, expected_msg
       end
     end
   end
@@ -163,7 +163,7 @@ describe Grpc::Health::Checker do
           checker.check(HCReq.new(service: t[:service]), nil)
         end
         expected_msg = /#{StatusCodes::NOT_FOUND}/
-        expect(&blk).to raise_error GRPC::BadStatus, expected_msg
+        expect(&blk).to raise_error GRPC::NotFound, expected_msg
       end
     end
   end
@@ -214,7 +214,7 @@ describe Grpc::Health::Checker do
         stub.check(HCReq.new(service: 'unknown'))
       end
       expected_msg = /#{StatusCodes::NOT_FOUND}/
-      expect(&blk).to raise_error GRPC::BadStatus, expected_msg
+      expect(&blk).to raise_error GRPC::NotFound, expected_msg
       @srv.stop
       t.join
     end
diff --git a/src/ruby/spec/spec_helper.rb b/src/ruby/spec/spec_helper.rb
index c891c1bf5e45e32eaab4df10c094ef1bdc20bea1..c2be0afa726aba8fe8718714557ba77a3b8e084c 100644
--- a/src/ruby/spec/spec_helper.rb
+++ b/src/ruby/spec/spec_helper.rb
@@ -67,3 +67,5 @@ RSpec.configure do |config|
 end
 
 RSpec::Expectations.configuration.warn_about_potential_false_positives = false
+
+Thread.abort_on_exception = true
diff --git a/templates/BUILD.template b/templates/BUILD.template
deleted file mode 100644
index af23fb279945d560f542fa4784e85b5a56a49168..0000000000000000000000000000000000000000
--- a/templates/BUILD.template
+++ /dev/null
@@ -1,256 +0,0 @@
-%YAML 1.2
---- |
-  # GRPC Bazel BUILD file.
-  # This currently builds C, C++ and Objective-C code.
-  # This file has been automatically generated from a template file.
-  # Please look at the templates directory instead.
-  # This file can be regenerated from the template by running
-  # tools/buildgen/generate_projects.sh
-  
-  # 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.
-  
-  licenses(["notice"])  # 3-clause BSD
-  
-  exports_files(["LICENSE"])
-  
-  package(default_visibility = ["//visibility:public"])
-  
-  <%!
-  def get_deps(target_dict):
-    deps = []
-    if target_dict.get('secure', False):
-      deps = [
-        "//external:libssl",
-      ]
-    if target_dict.get('build', None) == 'protoc':
-      deps.append("//external:protobuf_compiler")
-    if (target_dict['name'] == 'grpc++_unsecure' or
-        target_dict['name'] == 'grpc++' or
-        target_dict['name'] == 'grpc++_codegen_lib'):
-      deps.append("//external:protobuf_clib")
-    elif target_dict['name'] == 'grpc':
-      deps.append("//external:zlib")
-    for d in target_dict.get('deps', []):
-      if d.find('//') == 0 or d[0] == ':':
-        deps.append(d)
-      else:
-        deps.append(':%s' % (d))
-    return deps
-  %>
-
-  % for lib in libs:
-  % if lib.build in ("all", "protoc"):
-  ${cc_library(lib)}
-  % endif
-  % endfor
-  
-  % for lib in libs:
-  % if lib.name in ("grpc", "gpr"):
-  ${objc_library(lib)}
-  % endif
-  % endfor
-  
-  % for tgt in targets:
-  % if tgt.build == 'protoc':
-  ${cc_binary(tgt)}
-  % endif
-  % endfor
-  
-  <%def name="cc_library(lib)">
-  <%
-    lib_hdrs = lib.get("headers", [])
-    hdrs = [h for h in lib_hdrs if not h.startswith('third_party/nanopb')]
-    srcs = [s for s in lib.src if not s.startswith('third_party/nanopb')]
-    uses_nanopb = len(lib_hdrs) != len(hdrs) or len(srcs) != len(lib.src)
-  %>
-  cc_library(
-    name = "${lib.name}",
-    srcs = [
-  % for hdr in hdrs:
-      "${hdr}",
-  % endfor
-  % for src in srcs:
-      "${src}",
-  % endfor
-    ],
-    hdrs = [
-  % for hdr in lib.get("public_headers", []):
-      "${hdr}",
-  % endfor
-    ],
-    includes = [
-      "include",
-      ".",
-    ],
-    deps = [
-  % for dep in get_deps(lib):
-      "${dep}",
-  % endfor
-  % if uses_nanopb:
-      "//external:nanopb",
-  % endif
-    ],
-  % if lib.name in ("grpc", "grpc_unsecure"):
-    copts = [
-      "-std=gnu99",
-    ],
-  % endif
-  )
-  </%def>
-  
-  <%def name="objc_library(lib)">
-  <%
-    lib_hdrs = lib.get("headers", [])
-    hdrs = [h for h in lib_hdrs if not h.startswith('third_party/nanopb')]
-    srcs = [s for s in lib.src if not s.startswith('third_party/nanopb')]
-    uses_nanopb = len(lib_hdrs) != len(hdrs) or len(srcs) != len(lib.src)
-  %>
-  objc_library(
-    name = "${lib.name}_objc",
-    srcs = [
-  % for src in srcs:
-      "${src}",
-  % endfor
-    ],
-    hdrs = [
-  % for hdr in lib.get("public_headers", []):
-      "${hdr}",
-  % endfor
-  % for hdr in hdrs:
-      "${hdr}",
-  % endfor
-    ],
-    includes = [
-      "include",
-      ".",
-    ],
-    deps = [
-  % for dep in lib.get("deps", []):
-      ":${dep}_objc",
-  % endfor
-  % if lib.get('secure', False):
-      "//external:libssl_objc",
-  % endif
-  % if uses_nanopb:
-      "//external:nanopb",
-  % endif
-    ],
-  % if lib.get("baselib", false):
-    sdk_dylibs = ["libz"],
-  % endif
-  )
-  </%def>
-  
-  <%def name="cc_binary(tgt)">
-  cc_binary(
-    name = "${tgt.name}",
-    srcs = [
-  % for src in tgt.src:
-      "${src}",
-  % endfor
-    ],
-    deps = [
-  % for dep in get_deps(tgt):
-      "${dep}",
-  % endfor
-    ],
-  )
-  </%def>
-  
-  objc_path = "src/objective-c"
-  
-  rx_library_path = objc_path + "/RxLibrary"
-  
-  objc_library(
-    name = "rx_library",
-    hdrs = glob([
-      rx_library_path + "/*.h",
-      rx_library_path + "/transformations/*.h",
-    ]),
-    srcs = glob([
-      rx_library_path + "/*.m",
-      rx_library_path + "/transformations/*.m",
-    ]),
-    includes = [objc_path],
-    deps = [
-      ":rx_library_private",
-    ],
-  )
-  
-  objc_library(
-    name = "rx_library_private",
-    hdrs = glob([rx_library_path + "/private/*.h"]),
-    srcs = glob([rx_library_path + "/private/*.m"]),
-    visibility = ["//visibility:private"],
-  )
-  
-  objc_client_path = objc_path + "/GRPCClient"
-  
-  objc_library(
-    name = "grpc_client",
-    hdrs = glob([
-      objc_client_path + "/*.h",
-      objc_client_path + "/private/*.h",
-    ]),
-    srcs = glob([
-      objc_client_path + "/*.m",
-      objc_client_path + "/private/*.m",
-    ]),
-    includes = [objc_path],
-    bundles = [":gRPCCertificates"],
-    deps = [
-      ":grpc_objc",
-      ":rx_library",
-    ],
-  )
-  
-  objc_bundle_library(
-      # The choice of name is signicant here, since it determines the bundle name.
-      name = "gRPCCertificates",
-      resources = ["etc/roots.pem"],
-  )
-  
-  proto_objc_rpc_path = objc_path + "/ProtoRPC"
-  
-  objc_library(
-    name = "proto_objc_rpc",
-    hdrs = glob([
-      proto_objc_rpc_path + "/*.h",
-    ]),
-    srcs = glob([
-      proto_objc_rpc_path + "/*.m",
-    ]),
-    includes = [objc_path],
-    deps = [
-      ":grpc_client",
-      ":rx_library",
-      "//external:protobuf_objc",
-    ],
-  )
diff --git a/templates/CMakeLists.txt.template b/templates/CMakeLists.txt.template
index c3c636c6af7db733bd1ec7e298735553ce7b8b38..028c1b8c32879bacb585e7e62605fb72b8e58940 100644
--- a/templates/CMakeLists.txt.template
+++ b/templates/CMakeLists.txt.template
@@ -84,7 +84,13 @@
   set(gRPC_USE_PROTO_LITE OFF CACHE BOOL "Use the protobuf-lite library")
 
   if (MSVC)
-    add_definitions( -D_WIN32_WINNT=0x600 )
+    add_definitions(-D_WIN32_WINNT=0x600 -D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_WINSOCK_DEPRECATED_NO_WARNINGS)
+    # needed to compile boringssl
+    add_definitions(/wd4464 /wd4623 /wd4668 /wd4701 /wd4702 /wd4777 /wd5027)
+    # needed to compile protobuf
+    add_definitions(/wd4065 /wd4506)
+    # TODO(jtattermusch): revisit C4267 occurrences throughout the code
+    add_definitions(/wd4267)
   endif()
 
   if (gRPC_USE_PROTO_LITE)
@@ -188,14 +194,19 @@
   endif()
 
   % for lib in libs:
-  % if lib.build in ["all", "protoc", "tool"]:
+  % if lib.build in ["all", "protoc", "tool"] and lib.language in ['c', 'c++']:
+  ## TODO(jtattermusch): grpc++_reflection includes .proto files
+  ## which is not yet supported and thus fails the entire build.
+  ## Re-enable once fixed.
+  % if lib.name != 'grpc++_reflection':
     ${cc_library(lib)}
     ${cc_install(lib)}
   % endif
+  % endif
   % endfor
 
   % for tgt in targets:
-  % if tgt.build in ["all", "protoc", "tool"]:
+  % if tgt.build in ["all", "protoc", "tool"] and tgt.language in ['c', 'c++']:
   ${cc_binary(tgt)}
   ${cc_install(tgt)}
   % endif
diff --git a/templates/Makefile.template b/templates/Makefile.template
index d58d4fce41ea1bfbc778dcea37889af210b3b652..6f4db26f2bf40b053f347776ed5a59abcd313625 100644
--- a/templates/Makefile.template
+++ b/templates/Makefile.template
@@ -182,12 +182,19 @@
   endif
   STRIP ?= strip -x
   else
+  ifeq ($(SYSTEM),MINGW32)
+  ifeq ($(origin AR), default)
+  AR = ar rcs
+  endif
+  STRIP ?= strip --strip-unneeded
+  else
   ifeq ($(origin AR), default)
   AR = ar rcs
   endif
   STRIP ?= strip
   endif
   endif
+  endif
   INSTALL ?= install
   RM ?= rm -f
   PKG_CONFIG ?= pkg-config
@@ -392,6 +399,7 @@
   Libs.private: $(PC_LIBS_PRIVATE)
 
   ifeq ($(SYSTEM),MINGW32)
+  EXECUTABLE_SUFFIX = .exe
   SHARED_EXT_CORE = dll
   SHARED_EXT_CPP = dll
   SHARED_EXT_CSHARP = dll
@@ -400,6 +408,7 @@
   SHARED_VERSION_CPP = -${settings.cpp_version.major}
   SHARED_VERSION_CSHARP = -${settings.csharp_version.major}
   else ifeq ($(SYSTEM),Darwin)
+  EXECUTABLE_SUFFIX = 
   SHARED_EXT_CORE = dylib
   SHARED_EXT_CPP = dylib
   SHARED_EXT_CSHARP = dylib
@@ -408,6 +417,7 @@
   SHARED_VERSION_CPP =
   SHARED_VERSION_CSHARP =
   else
+  EXECUTABLE_SUFFIX = 
   SHARED_EXT_CORE = so.$(CORE_VERSION)
   SHARED_EXT_CPP = so.$(CPP_VERSION)
   SHARED_EXT_CSHARP = so.$(CSHARP_VERSION)
@@ -1196,7 +1206,7 @@
   $(GENDIR)/${p}.grpc.pb.cc: ${p}.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) ${' '.join('$(GENDIR)/%s.pb.cc $(GENDIR)/%s.grpc.pb.cc' % (q,q) for q in proto_deps.get(p, []))}
   	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
   	$(Q) mkdir -p `dirname $@`
-  	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $<
+  	$(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $<
   endif
 
   % endfor
@@ -1295,7 +1305,7 @@
   	$(Q) $(INSTALL) -d $(prefix)/lib
   	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/$(SHARED_PREFIX)${lib.name}$(SHARED_VERSION_${lang_to_var[lib.language]}).$(SHARED_EXT_${lang_to_var[lib.language]}) $(prefix)/lib/$(SHARED_PREFIX)${lib.name}$(SHARED_VERSION_${lang_to_var[lib.language]}).$(SHARED_EXT_${lang_to_var[lib.language]})
   ifeq ($(SYSTEM),MINGW32)
-  	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/lib${lib.name}-imp.a $(prefix)/lib/lib${lib.name}-imp.a
+  	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/lib${lib.name}$(SHARED_VERSION_${lang_to_var[lib.language]})-dll.a $(prefix)/lib/lib${lib.name}.a
   else ifneq ($(SYSTEM),Darwin)
   	$(Q) ln -sf $(SHARED_PREFIX)${lib.name}$(SHARED_VERSION_${lang_to_var[lib.language]}).$(SHARED_EXT_${lang_to_var[lib.language]}) $(prefix)/lib/lib${lib.name}.so.${settings.core_version.major}
   	$(Q) ln -sf $(SHARED_PREFIX)${lib.name}$(SHARED_VERSION_${lang_to_var[lib.language]}).$(SHARED_EXT_${lang_to_var[lib.language]}) $(prefix)/lib/lib${lib.name}.so
@@ -1322,9 +1332,6 @@
   ${install_shared("csharp")}
 
   install-plugins: $(PROTOC_PLUGINS)
-  ifeq ($(SYSTEM),MINGW32)
-  	$(Q) false
-  else
   	$(E) "[INSTALL] Installing grpc protoc plugins"
   % for tgt in targets:
   % if tgt.build == 'protoc':
@@ -1332,7 +1339,6 @@
   	$(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/${tgt.name} $(prefix)/bin/${tgt.name}
   % endif
   % endfor
-  endif
 
   install-pkg-config_c: pc_c pc_c_unsecure
   	$(E) "[INSTALL] Installing C pkg-config files"
@@ -1513,8 +1519,8 @@
         assert dep_lib, 'lib %s not found (in %s)' % (dep, lib.name)
         link_libs = link_libs + ' -l' + dep
         lib_deps = lib_deps + ' $(LIBDIR)/$(CONFIG)/lib' + dep + '.$(SHARED_EXT_' + lang_to_var[dep_lib.language] + ')'
-        mingw_libs = mingw_libs + ' -l' + dep + '-imp'
-        mingw_lib_deps = mingw_lib_deps + ' $(LIBDIR)/$(CONFIG)/' + dep + '.$(SHARED_EXT_' + lang_to_var[dep_lib.language] + ')'
+        mingw_libs = mingw_libs + ' -l' + dep + '$(SHARED_VERSION_' + lang_to_var[dep_lib.language] + ')-dll'
+        mingw_lib_deps = mingw_lib_deps + ' $(LIBDIR)/$(CONFIG)/' + dep + '$(SHARED_VERSION_' + lang_to_var[dep_lib.language] + ').$(SHARED_EXT_' + lang_to_var[dep_lib.language] + ')'
 
     security = lib.get('secure', 'check')
     if security == True:
@@ -1546,7 +1552,7 @@
   ${out_mingbase}.$(SHARED_EXT_${lang_to_var[lib.language]}): $(LIB${lib.name.upper()}_OBJS) ${mingw_lib_deps}
   	$(E) "[LD]      Linking $@"
   	$(Q) mkdir -p `dirname $@`
-  	$(Q) ${ld} ${ldflags} -L$(LIBDIR)/$(CONFIG) -shared ${lib.name}.def -Wl,--output-def=${out_mingbase}.def -Wl,--out-implib=${out_libbase}-dll.a -o ${out_mingbase}.$(SHARED_EXT_${lang_to_var[lib.language]}) ${common}${mingw_libs}
+  	$(Q) ${ld} ${ldflags} -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=${out_mingbase}.def -Wl,--out-implib=${out_libbase}-dll.a -o ${out_mingbase}.$(SHARED_EXT_${lang_to_var[lib.language]}) ${common}${mingw_libs}
   else
   ${out_libbase}.$(SHARED_EXT_${lang_to_var[lib.language]}): $(LIB${lib.name.upper()}_OBJS) ${lib_deps}
   	$(E) "[LD]      Linking $@"
diff --git a/templates/binding.gyp.template b/templates/binding.gyp.template
index e6592acebbc520314277baea7f40af2186b506e2..851effc4f3dab98edf493cdb823f9babe4c22f32 100644
--- a/templates/binding.gyp.template
+++ b/templates/binding.gyp.template
@@ -39,18 +39,68 @@
   # Some of this file is built with the help of
   # https://n8.io/converting-a-c-library-to-gyp/
   {
+    'variables': {
+      'runtime%': 'node'
+    },
     'target_defaults': {
       'include_dirs': [
         '.',
         'include'
       ],
       'defines': [
-        'GRPC_UV'
+        'GPR_BACKWARDS_COMPATIBILITY_MODE'
       ],
       'conditions': [
+        ['runtime=="node"', {
+          'defines': [
+            'GRPC_UV'
+          ]
+        }],
+        ['OS!="win" and runtime=="electron"', {
+          "defines": [
+            'OPENSSL_NO_THREADS'
+          ]
+        }],
+        # This is the condition for using boringssl
+        ['OS=="win" or runtime=="electron"', {
+          "include_dirs": [
+            "third_party/boringssl/include"
+          ],
+          "defines": [
+            '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.
+          'defines': [
+            'TSI_OPENSSL_ALPN_SUPPORT=<!(echo <(target) | cut -d. -f1)'
+          ],
+          'include_dirs': [
+            '<(node_root_dir)/deps/openssl/openssl/include',
+          ],
+          'conditions': [
+           ["target_arch=='ia32'", {
+               "include_dirs": [ "<(node_root_dir)/deps/openssl/config/piii" ]
+           }],
+           ["target_arch=='x64'", {
+               "include_dirs": [ "<(node_root_dir)/deps/openssl/config/k8" ]
+           }],
+           ["target_arch=='arm'", {
+               "include_dirs": [ "<(node_root_dir)/deps/openssl/config/arm" ]
+           }]
+          ]
+        }],
         ['OS == "win"', {
           "include_dirs": [
-            "third_party/boringssl/include",
             "third_party/zlib"
           ],
           "defines": [
@@ -60,8 +110,6 @@
             'UNICODE',
             '_UNICODE',
             'NOMINMAX',
-            'OPENSSL_NO_ASM',
-            'GPR_BACKWARDS_COMPATIBILITY_MODE'
           ],
           "msvs_settings": {
             'VCCLCompilerTool': {
@@ -74,21 +122,8 @@
         }, { # OS != "win"
           'variables': {
             'config': '<!(echo $CONFIG)',
-            # 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.
-          'defines': [
-            'TSI_OPENSSL_ALPN_SUPPORT=<!(echo <(target) | cut -d. -f1)',
-            'GPR_BACKWARDS_COMPATIBILITY_MODE'
-          ],
           'include_dirs': [
-            '<(node_root_dir)/deps/openssl/openssl/include',
             '<(node_root_dir)/deps/zlib'
           ],
           'conditions': [
@@ -103,21 +138,42 @@
                 '-fprofile-arcs'
               ]
             }
-           ],
-           ["target_arch=='ia32'", {
-               "include_dirs": [ "<(node_root_dir)/deps/openssl/config/piii" ]
-           }],
-           ["target_arch=='x64'", {
-               "include_dirs": [ "<(node_root_dir)/deps/openssl/config/k8" ]
-           }],
-           ["target_arch=='arm'", {
-               "include_dirs": [ "<(node_root_dir)/deps/openssl/config/arm" ]
-           }]
+           ]
           ]
         }]
       ]
     },
     'conditions': [
+      ['OS=="win" or runtime=="electron"', {
+        'targets': [
+          % for module in node_modules:
+          % for lib in libs:
+          % if lib.name in module.transitive_deps and lib.name == 'boringssl':
+          {
+            'cflags': [
+              '-std=c99',
+              '-Wall',
+              '-Werror'
+            ],
+            'target_name': '${lib.name}',
+            'product_prefix': 'lib',
+            'type': 'static_library',
+            'dependencies': [
+              % for dep in getattr(lib, 'deps', []):
+              '${dep}',
+              % endfor
+            ],
+            'sources': [
+              % for source in lib.src:
+              '${source}',
+              % endfor
+            ]
+          },
+          % endif
+          % endfor
+          % endfor
+        ]
+      }],
       ['OS == "win"', {
         'targets': [
           {
@@ -143,10 +199,10 @@
               }
             ]
           },
-          # Only want to compile BoringSSL and zlib under Windows
+          # Only want to compile zlib under Windows
           % for module in node_modules:
           % for lib in libs:
-          % if lib.name in module.transitive_deps and lib.name in ('boringssl', 'z'):
+          % if lib.name in module.transitive_deps and lib.name == 'z':
           {
             'cflags': [
               '-std=c99',
@@ -223,6 +279,15 @@
           '-g'
         ],
         "conditions": [
+          ['OS=="win" or runtime=="electron"', {
+            'dependencies': [
+              % for dep in getattr(module, 'deps', []):
+              % if dep == 'boringssl':
+              "${dep}",
+              % endif
+              % endfor
+            ]
+          }],
           ['OS=="mac"', {
             'xcode_settings': {
               'MACOSX_DEPLOYMENT_TARGET': '10.9',
@@ -235,7 +300,7 @@
           ['OS=="win"', {
             'dependencies': [
               % for dep in getattr(module, 'deps', []):
-              % if dep in ('boringssl', 'z'):
+              % if dep == 'z':
               "${dep}",
               % endif
               % endfor
diff --git a/templates/config.m4.template b/templates/config.m4.template
index 5847d456f51c89c941065e04344f86ce0a2f4469..f5f1d23088dadeaedff737fc148349a366a6d484 100644
--- a/templates/config.m4.template
+++ b/templates/config.m4.template
@@ -7,9 +7,9 @@
     dnl Write more examples of tests here...
 
     dnl # --with-grpc -> add include path
-    PHP_ADD_INCLUDE(../../grpc/include)
-    PHP_ADD_INCLUDE(../../grpc/src/php/ext/grpc)
-    PHP_ADD_INCLUDE(../../grpc/third_party/boringssl/include)
+    PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/include)
+    PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/src/php/ext/grpc)
+    PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/third_party/boringssl/include)
 
     LIBS="-lpthread $LIBS"
 
diff --git a/templates/gRPC-Core.podspec.template b/templates/gRPC-Core.podspec.template
index fbad1a3f709d96155f662921ae48f82b1f66108e..1b97d18f1637472ccdc44ca08c21069e9619666b 100644
--- a/templates/gRPC-Core.podspec.template
+++ b/templates/gRPC-Core.podspec.template
@@ -62,7 +62,7 @@
   %>
   Pod::Spec.new do |s|
     s.name     = 'gRPC-Core'
-    version = '1.0.1'
+    version = '1.0.2'
     s.version  = version
     s.summary  = 'Core cross-platform gRPC library, written in C'
     s.homepage = 'http://www.grpc.io'
@@ -71,7 +71,9 @@
 
     s.source = {
       :git => 'https://github.com/grpc/grpc.git',
-      :tag => "v#{version}",
+      # TODO(mxyan): Change back to "v#{version}" for next release
+      #:tag => "v#{version}",
+      :tag => "objective-c-v#{version}",
       # TODO(jcanizales): Depend explicitly on the nanopb pod, and disable submodules.
       :submodules => true,
     }
diff --git a/templates/grpc.gemspec.template b/templates/grpc.gemspec.template
index 62d61b75c1df74fbbdfa7ad58f2c44c6d77b1c9e..82fbb69008853f1c19515182af266b6433a839a5 100644
--- a/templates/grpc.gemspec.template
+++ b/templates/grpc.gemspec.template
@@ -29,7 +29,7 @@
     s.require_paths = %w( src/ruby/bin src/ruby/lib src/ruby/pb )
     s.platform      = Gem::Platform::RUBY
 
-    s.add_dependency 'google-protobuf', '~> 3.0.2'
+    s.add_dependency 'google-protobuf', '~> 3.1.0'
     s.add_dependency 'googleauth',      '~> 0.5.1'
 
     s.add_development_dependency 'bundler',            '~> 1.9'
diff --git a/templates/package.json.template b/templates/package.json.template
index 81f39d27f518361064cc84642c857771b007758c..316c28e478efeaf4239dd5778851885459e35d78 100644
--- a/templates/package.json.template
+++ b/templates/package.json.template
@@ -23,6 +23,7 @@
     "scripts": {
       "lint": "node ./node_modules/jshint/bin/jshint src/node/src src/node/test src/node/interop src/node/index.js --exclude-path=src/node/.jshintignore",
       "test": "./node_modules/.bin/mocha src/node/test && npm run-script lint",
+      "electron-build": "./node_modules/.bin/node-pre-gyp configure build --runtime=electron --disturl=https://atom.io/download/atom-shell",
       "gen_docs": "./node_modules/.bin/jsdoc -c src/node/jsdoc_conf.json",
       "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha src/node/test",
       "install": "./node_modules/.bin/node-pre-gyp install --fallback-to-build"
@@ -40,6 +41,7 @@
     "devDependencies": {
       "async": "^2.0.1",
       "body-parser": "^1.15.2",
+      "electron-mocha": "^3.1.1",
       "express": "^4.14.0",
       "google-auth-library": "^0.9.2",
       "google-protobuf": "^3.0.0",
@@ -52,7 +54,7 @@
       "poisson-process": "^0.2.1"
     },
     "engines": {
-      "node": ">=0.12.0"
+      "node": ">=1.1.0"
     },
     "binary": {
       "module_name": "grpc_node",
diff --git a/templates/tools/dockerfile/clang_format.include b/templates/tools/dockerfile/clang_format.include
new file mode 100644
index 0000000000000000000000000000000000000000..9a2b60ba8c47bd47906d2279203d472e7ed4234c
--- /dev/null
+++ b/templates/tools/dockerfile/clang_format.include
@@ -0,0 +1,5 @@
+RUN apt-get update && apt-get -y install wget
+RUN echo deb http://llvm.org/apt/wily/ llvm-toolchain-wily-3.8 main >> /etc/apt/sources.list
+RUN echo deb-src http://llvm.org/apt/wily/ llvm-toolchain-wily-3.8 main >> /etc/apt/sources.list
+RUN wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key| apt-key add -
+RUN apt-get update && apt-get -y install clang-format-3.8
diff --git a/templates/tools/dockerfile/grpc_clang_format/Dockerfile.template b/templates/tools/dockerfile/grpc_clang_format/Dockerfile.template
new file mode 100644
index 0000000000000000000000000000000000000000..8360fc121c1803828ef96e1e24e3bb942ecd1020
--- /dev/null
+++ b/templates/tools/dockerfile/grpc_clang_format/Dockerfile.template
@@ -0,0 +1,37 @@
+%YAML 1.2
+--- |
+  # 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.
+  
+  FROM ubuntu:15.10
+  
+  <%include file="../clang_format.include"/>
+  ADD clang_format_all_the_things.sh /
+  CMD ["echo 'Run with tools/distrib/clang_format_code.sh'"]
+  
diff --git a/templates/tools/dockerfile/test/bazel/Dockerfile.template b/templates/tools/dockerfile/test/bazel/Dockerfile.template
new file mode 100644
index 0000000000000000000000000000000000000000..82f90bef68153074618c64516ee187421a0095db
--- /dev/null
+++ b/templates/tools/dockerfile/test/bazel/Dockerfile.template
@@ -0,0 +1,48 @@
+%YAML 1.2
+--- |
+  # 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.
+  
+  FROM ubuntu:15.10
+  
+  <%include file="../../apt_get_basic.include"/>
+
+  #========================
+  # Bazel installation
+  RUN apt-get install -y software-properties-common g++
+  RUN echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" > /etc/apt/sources.list.d/bazel.list
+  RUN curl https://bazel.build/bazel-release.pub.gpg | apt-key add -
+  RUN apt-get -y update
+  RUN apt-get -y install bazel
+
+  RUN mkdir -p /var/local/jenkins
+  
+  # Define the default command.
+  CMD ["bash"]
+  
diff --git a/templates/tools/dockerfile/test/node_jessie_x64/Dockerfile.template b/templates/tools/dockerfile/test/node_jessie_x64/Dockerfile.template
index 72b098f0c2048666ee5b153acf2040afa92a8b97..ceaa9aa5abccbe880db12cfd64a407870f39066b 100644
--- a/templates/tools/dockerfile/test/node_jessie_x64/Dockerfile.template
+++ b/templates/tools/dockerfile/test/node_jessie_x64/Dockerfile.template
@@ -28,10 +28,21 @@
   # 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.
-  
+
   FROM debian:jessie
-  
+
   <%include file="../../apt_get_basic.include"/>
+
+  # Install Electron apt dependencies
+  RUN apt-get update && apt-get install -y ${'\\'}
+    libasound2 ${'\\'}
+    libgconf-2-4 ${'\\'}
+    libgtk2.0-0 ${'\\'}
+    libnss3 ${'\\'}
+    libxss1 ${'\\'}
+    libxtst6 ${'\\'}
+    xvfb
+
   <%include file="../../python_deps.include"/>
   <%include file="../../node_deps.include"/>
   <%include file="../../run_tests_addons.include"/>
diff --git a/templates/tools/dockerfile/test/sanity/Dockerfile.template b/templates/tools/dockerfile/test/sanity/Dockerfile.template
index 12309b64d1606d7d46e15568a912f255214fcfa1..8617666b21eb2f36395794caf85fa90d4d330f5f 100644
--- a/templates/tools/dockerfile/test/sanity/Dockerfile.template
+++ b/templates/tools/dockerfile/test/sanity/Dockerfile.template
@@ -48,19 +48,16 @@
   #======================================
   # More sanity test dependencies (bazel)
   RUN apt-get install -y openjdk-8-jdk
-  # TOOD(jtattermusch): pin the bazel version
-  RUN git clone https://github.com/bazelbuild/bazel.git /bazel
-  RUN cd /bazel && ./compile.sh
+  # Check out Bazel version 0.4.1 since this version allows running
+  # ./compile.sh without a local protoc dependency
+  # TODO(mattkwong): install dependencies to support latest Bazel version if newer
+  # version is needed
+  RUN git clone https://github.com/bazelbuild/bazel.git /bazel && ${"\\"}
+    cd /bazel && git checkout tags/0.4.1 && ./compile.sh
   RUN ln -s /bazel/output/bazel /bin/
   
-  #===================
-  # Docker "inception"
-  # Note this is quite the ugly hack.
-  # This makes sure that the docker binary we inject has its dependencies.
-  RUN curl https://get.docker.com/ | sh
-  RUN apt-get remove --purge -y docker-engine
-  
-  RUN mkdir /var/local/jenkins
+  <%include file="../../clang_format.include"/>
+  <%include file="../../run_tests_addons.include"/>
   
   # Define the default command.
   CMD ["bash"]
diff --git a/templates/tools/doxygen/Doxyfile.include b/templates/tools/doxygen/Doxyfile.include
index 8b0c528c1f68f5eee14b6609d5cab25c6fe0fc92..e26ae2b948ae19f4c0e8e0028fd94a5e242170d3 100644
--- a/templates/tools/doxygen/Doxyfile.include
+++ b/templates/tools/doxygen/Doxyfile.include
@@ -8,7 +8,10 @@
 <%def name="gen_doxyfile(libnames, packagename, collection, internal)">
 <%
   import itertools
+  import glob
+  import os
   targets = []
+  docpackage = packagename.replace('+', 'p').lower()
   for libname in libnames:
     target = None
     for p in collection:
@@ -16,6 +19,11 @@
         target = p
     assert(target)
     targets.append(target)
+  srcdoc = []
+  for dirpath, dirnames, filenames in os.walk('src/%s' % docpackage):
+    for filename in filenames:
+      if os.path.splitext(filename)[1] == '.md':
+        srcdoc.append(os.path.join(dirpath, filename))
 %>
 # Doxyfile 1.8.9.1
 
@@ -778,13 +786,20 @@ WARN_LOGFILE           =
 # spaces.
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = ${' \\\n'.join(
-                               itertools.chain.from_iterable(
-			           target.public_headers +
-				   ([]
-				    if not internal
-				    else target.headers + target.src)
-			       for target in targets))}
+INPUT                  = ${
+    ' \\\n'.join(sorted(
+        itertools.chain(
+            itertools.chain.from_iterable(
+    			      target.public_headers +
+    				    ([]
+    				     if not internal
+    				     else target.headers + target.src)
+    			      for target in targets),
+            glob.glob('doc/*.md'),
+            glob.glob('doc/%s/*.md' % docpackage),
+            [] if not internal else srcdoc)
+    ))
+}
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/templates/tools/run_tests/configs.json.template b/templates/tools/run_tests/generated/configs.json.template
similarity index 100%
rename from templates/tools/run_tests/configs.json.template
rename to templates/tools/run_tests/generated/configs.json.template
diff --git a/templates/tools/run_tests/sources_and_headers.json.template b/templates/tools/run_tests/generated/sources_and_headers.json.template
similarity index 100%
rename from templates/tools/run_tests/sources_and_headers.json.template
rename to templates/tools/run_tests/generated/sources_and_headers.json.template
diff --git a/templates/tools/run_tests/tests.json.template b/templates/tools/run_tests/generated/tests.json.template
similarity index 100%
rename from templates/tools/run_tests/tests.json.template
rename to templates/tools/run_tests/generated/tests.json.template
diff --git a/templates/vsprojects/grpc.sln.template b/templates/vsprojects/grpc.sln.template
index ded98383dad0e24bd97069d66fc69bef74fd4811..0b28e936be2514d26e5a00157c92eadaf7370e1b 100644
--- a/templates/vsprojects/grpc.sln.template
+++ b/templates/vsprojects/grpc.sln.template
@@ -2,6 +2,6 @@
 --- |
   <%namespace file="sln_defs.include" import="gen_solution"/>\
   <%
-  solution_projects = [p for p in vsprojects if p.build not in ['protoc', 'test', 'fuzzer'] and p.language in ['c', 'c++'] and p.vs_proj_dir == '.' and not (p.build == 'private' and p.language == 'c++')]
+  solution_projects = [p for p in vsprojects if p.build not in ['protoc', 'test', 'fuzzer'] and p.language in ['c', 'c++'] and p.vs_proj_dir == '.' and not (p.build == 'private' and p.language == 'c++') and not p.name in ['z', 'boringssl', 'grpc++_reflection']]
   %>\
   ${gen_solution(solution_projects, use_dlls='yes')}
diff --git a/templates/vsprojects/protobuf.props.template b/templates/vsprojects/protobuf.props.template
index 48f9431c1cff1e0c2a8a9b4998397e5eef87d7f4..3ae7c745ded911bf8786952ff4768d556cee05b7 100644
--- a/templates/vsprojects/protobuf.props.template
+++ b/templates/vsprojects/protobuf.props.template
@@ -6,7 +6,7 @@
   <ItemDefinitionGroup>
     <Link>
       <AdditionalDependencies>libprotobuf.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>$(SolutionDir)\..\third_party\protobuf\cmake\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>$(SolutionDir)\..\third_party\protobuf\cmake\build\solution\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup />
diff --git a/templates/vsprojects/protoc.props.template b/templates/vsprojects/protoc.props.template
index ced6028a4be1338517536ebe2ee56e94db08185a..2c3844a9a4e9af2a40c76e01abc3e26dfffffda6 100644
--- a/templates/vsprojects/protoc.props.template
+++ b/templates/vsprojects/protoc.props.template
@@ -9,7 +9,7 @@
     </ClCompile>
     <Link>
       <AdditionalDependencies>libprotoc.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>$(SolutionDir)\..\third_party\protobuf\cmake\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>$(SolutionDir)\..\third_party\protobuf\cmake\build\solution\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup />
diff --git a/test/core/bad_client/BUILD b/test/core/bad_client/BUILD
new file mode 100644
index 0000000000000000000000000000000000000000..5406eb9a4b0886e921c6239db8b48b0b15d6775c
--- /dev/null
+++ b/test/core/bad_client/BUILD
@@ -0,0 +1,32 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+load(":generate_tests.bzl", "grpc_bad_client_tests")
+
+grpc_bad_client_tests()
diff --git a/test/core/bad_client/bad_client.c b/test/core/bad_client/bad_client.c
index 07fcd995d7bc79db4688565c63abad9d9f88bb86..a84086804cdb0e84f81032c50db1ccc2d9f616cf 100644
--- a/test/core/bad_client/bad_client.c
+++ b/test/core/bad_client/bad_client.c
@@ -33,15 +33,18 @@
 
 #include "test/core/bad_client/bad_client.h"
 
+#include <stdio.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
 #include <grpc/support/thd.h>
-#include <stdio.h>
+
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/core/lib/channel/http_server_filter.h"
 #include "src/core/lib/iomgr/endpoint_pair.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/support/murmur_hash.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/surface/completion_queue.h"
@@ -117,7 +120,7 @@ void grpc_run_bad_client_test(
   grpc_resource_quota *resource_quota =
       grpc_resource_quota_create("bad_client_test");
   sfd = grpc_iomgr_create_endpoint_pair("fixture", resource_quota, 65536);
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+  grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
 
   /* Create server, completion events */
   a.server = grpc_server_create(NULL, NULL);
@@ -148,7 +151,8 @@ void grpc_run_bad_client_test(
 
   grpc_slice_buffer_init(&outgoing);
   grpc_slice_buffer_add(&outgoing, slice);
-  grpc_closure_init(&done_write_closure, done_write, &a);
+  grpc_closure_init(&done_write_closure, done_write, &a,
+                    grpc_schedule_on_exec_ctx);
 
   /* Write data */
   grpc_endpoint_write(&exec_ctx, sfd.client, &outgoing, &done_write_closure);
@@ -175,13 +179,14 @@ void grpc_run_bad_client_test(
       grpc_slice_buffer_init(&args.incoming);
       gpr_event_init(&args.read_done);
       grpc_closure read_done_closure;
-      grpc_closure_init(&read_done_closure, read_done, &args);
+      grpc_closure_init(&read_done_closure, read_done, &args,
+                        grpc_schedule_on_exec_ctx);
       grpc_endpoint_read(&exec_ctx, sfd.client, &args.incoming,
                          &read_done_closure);
       grpc_exec_ctx_finish(&exec_ctx);
       GPR_ASSERT(
           gpr_event_wait(&args.read_done, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)));
-      grpc_slice_buffer_destroy(&args.incoming);
+      grpc_slice_buffer_destroy_internal(&exec_ctx, &args.incoming);
     }
     // Shutdown.
     grpc_endpoint_shutdown(&exec_ctx, sfd.client);
@@ -194,7 +199,7 @@ void grpc_run_bad_client_test(
                  .type == GRPC_OP_COMPLETE);
   grpc_server_destroy(a.server);
   grpc_completion_queue_destroy(a.cq);
-  grpc_slice_buffer_destroy(&outgoing);
+  grpc_slice_buffer_destroy_internal(&exec_ctx, &outgoing);
 
   grpc_exec_ctx_finish(&exec_ctx);
   grpc_shutdown();
diff --git a/test/core/bad_client/generate_tests.bzl b/test/core/bad_client/generate_tests.bzl
new file mode 100755
index 0000000000000000000000000000000000000000..694ddeeab615547ad3ce03e12b0dc18f716e96e8
--- /dev/null
+++ b/test/core/bad_client/generate_tests.bzl
@@ -0,0 +1,68 @@
+#!/usr/bin/env python2.7
+# 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.
+
+
+"""Generates the appropriate build.json data for all the bad_client tests."""
+
+
+def test_options():
+  return struct()
+
+
+# maps test names to options
+BAD_CLIENT_TESTS = {
+    'badreq': test_options(),
+    'connection_prefix': test_options(),
+    'headers': test_options(),
+    'initial_settings_frame': test_options(),
+    'head_of_line_blocking': test_options(),
+    'large_metadata': test_options(),
+    'server_registered_method': test_options(),
+    'simple_request': test_options(),
+    'window_overflow': test_options(),
+    'unknown_frame': test_options(),
+}
+
+def grpc_bad_client_tests():
+  native.cc_library(
+      name = 'bad_client_test',
+      srcs = ['bad_client.c'],
+      hdrs = ['bad_client.h'],
+      copts = ['-std=c99'],
+      deps = ['//test/core/util:grpc_test_util', '//:grpc', '//:gpr', '//test/core/end2end:cq_verifier']
+  )
+  for t, topt in BAD_CLIENT_TESTS.items():
+    native.cc_test(
+        name = '%s_bad_client_test' % t,
+        srcs = ['tests/%s.c' % t],
+        deps = [':bad_client_test'],
+        copts = ['-std=c99'],
+    )
+
diff --git a/test/core/bad_ssl/BUILD b/test/core/bad_ssl/BUILD
new file mode 100644
index 0000000000000000000000000000000000000000..630733dd4d7beda72c2f0562ca7e32fbac99ea81
--- /dev/null
+++ b/test/core/bad_ssl/BUILD
@@ -0,0 +1,32 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+load(":generate_tests.bzl", "grpc_bad_ssl_tests")
+
+grpc_bad_ssl_tests()
diff --git a/test/core/bad_ssl/generate_tests.bzl b/test/core/bad_ssl/generate_tests.bzl
new file mode 100755
index 0000000000000000000000000000000000000000..78474bc1cab42064a69f17a6b3e157ddbf6082af
--- /dev/null
+++ b/test/core/bad_ssl/generate_tests.bzl
@@ -0,0 +1,52 @@
+#!/usr/bin/env python2.7
+# 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.
+
+
+def test_options():
+  return struct()
+
+
+# maps test names to options
+BAD_SSL_TESTS = ['cert', 'alpn']
+
+def grpc_bad_ssl_tests():
+  native.cc_library(
+      name = 'bad_ssl_test_server',
+      srcs = ['server_common.c'],
+      hdrs = ['server_common.h'],
+      deps = ['//test/core/util:grpc_test_util', '//:grpc', '//test/core/end2end:ssl_test_data']
+  )
+  for t in BAD_SSL_TESTS:
+    native.cc_test(
+        name = 'bad_ssl_%s_server' % t,
+        srcs = ['servers/%s.c' % t],
+        deps = [':bad_ssl_test_server'],
+    )
+
diff --git a/test/core/channel/channel_args_test.c b/test/core/channel/channel_args_test.c
index d3eb969f0984030a65d33b967aaf5daba6c3d1a3..b2c41652a49a9e04d4b46d616681138cd37c6b1d 100644
--- a/test/core/channel/channel_args_test.c
+++ b/test/core/channel/channel_args_test.c
@@ -37,10 +37,12 @@
 #include <grpc/support/useful.h>
 
 #include "src/core/lib/channel/channel_args.h"
-
+#include "src/core/lib/iomgr/exec_ctx.h"
 #include "test/core/util/test_config.h"
 
 static void test_create(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+
   grpc_arg arg_int;
   grpc_arg arg_string;
   grpc_arg to_add[2];
@@ -68,10 +70,12 @@ static void test_create(void) {
   GPR_ASSERT(strcmp(ch_args->args[1].value.string, arg_string.value.string) ==
              0);
 
-  grpc_channel_args_destroy(ch_args);
+  grpc_channel_args_destroy(&exec_ctx, ch_args);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_set_compression_algorithm(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_channel_args *ch_args;
 
   ch_args =
@@ -81,10 +85,12 @@ static void test_set_compression_algorithm(void) {
                     GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM) == 0);
   GPR_ASSERT(ch_args->args[0].type == GRPC_ARG_INTEGER);
 
-  grpc_channel_args_destroy(ch_args);
+  grpc_channel_args_destroy(&exec_ctx, ch_args);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_compression_algorithm_states(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_channel_args *ch_args, *ch_args_wo_gzip, *ch_args_wo_gzip_deflate;
   unsigned states_bitset;
   size_t i;
@@ -100,10 +106,10 @@ static void test_compression_algorithm_states(void) {
 
   /* disable gzip and deflate */
   ch_args_wo_gzip = grpc_channel_args_compression_algorithm_set_state(
-      &ch_args, GRPC_COMPRESS_GZIP, 0);
+      &exec_ctx, &ch_args, GRPC_COMPRESS_GZIP, 0);
   GPR_ASSERT(ch_args == ch_args_wo_gzip);
   ch_args_wo_gzip_deflate = grpc_channel_args_compression_algorithm_set_state(
-      &ch_args_wo_gzip, GRPC_COMPRESS_DEFLATE, 0);
+      &exec_ctx, &ch_args_wo_gzip, GRPC_COMPRESS_DEFLATE, 0);
   GPR_ASSERT(ch_args_wo_gzip == ch_args_wo_gzip_deflate);
 
   states_bitset = (unsigned)grpc_channel_args_compression_algorithm_get_states(
@@ -118,7 +124,7 @@ static void test_compression_algorithm_states(void) {
 
   /* re-enabled gzip only */
   ch_args_wo_gzip = grpc_channel_args_compression_algorithm_set_state(
-      &ch_args_wo_gzip_deflate, GRPC_COMPRESS_GZIP, 1);
+      &exec_ctx, &ch_args_wo_gzip_deflate, GRPC_COMPRESS_GZIP, 1);
   GPR_ASSERT(ch_args_wo_gzip == ch_args_wo_gzip_deflate);
 
   states_bitset = (unsigned)grpc_channel_args_compression_algorithm_get_states(
@@ -131,7 +137,8 @@ static void test_compression_algorithm_states(void) {
     }
   }
 
-  grpc_channel_args_destroy(ch_args);
+  grpc_channel_args_destroy(&exec_ctx, ch_args);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_set_socket_mutator(void) {
@@ -144,7 +151,11 @@ static void test_set_socket_mutator(void) {
   GPR_ASSERT(strcmp(ch_args->args[0].key, GRPC_ARG_SOCKET_MUTATOR) == 0);
   GPR_ASSERT(ch_args->args[0].type == GRPC_ARG_POINTER);
 
-  grpc_channel_args_destroy(ch_args);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_channel_args_destroy(&exec_ctx, ch_args);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
 }
 
 int main(int argc, char **argv) {
diff --git a/test/core/channel/channel_stack_test.c b/test/core/channel/channel_stack_test.c
index 0840820cca96ed9774aabed323c1d8ced8ed64d1..080c56f1349380cf5364d14ccde37cc1609df961 100644
--- a/test/core/channel/channel_stack_test.c
+++ b/test/core/channel/channel_stack_test.c
@@ -41,9 +41,9 @@
 
 #include "test/core/util/test_config.h"
 
-static void channel_init_func(grpc_exec_ctx *exec_ctx,
-                              grpc_channel_element *elem,
-                              grpc_channel_element_args *args) {
+static grpc_error *channel_init_func(grpc_exec_ctx *exec_ctx,
+                                     grpc_channel_element *elem,
+                                     grpc_channel_element_args *args) {
   GPR_ASSERT(args->channel_args->num_args == 1);
   GPR_ASSERT(args->channel_args->args[0].type == GRPC_ARG_INTEGER);
   GPR_ASSERT(0 == strcmp(args->channel_args->args[0].key, "test_key"));
@@ -51,6 +51,7 @@ static void channel_init_func(grpc_exec_ctx *exec_ctx,
   GPR_ASSERT(args->is_first);
   GPR_ASSERT(args->is_last);
   *(int *)(elem->channel_data) = 0;
+  return GRPC_ERROR_NONE;
 }
 
 static grpc_error *call_init_func(grpc_exec_ctx *exec_ctx,
@@ -156,8 +157,8 @@ static void test_create_channel_stack(void) {
 
   GRPC_CHANNEL_STACK_UNREF(&exec_ctx, channel_stack, "done");
 
+  GRPC_MDSTR_UNREF(&exec_ctx, path);
   grpc_exec_ctx_finish(&exec_ctx);
-  GRPC_MDSTR_UNREF(path);
 }
 
 int main(int argc, char **argv) {
diff --git a/test/core/client_channel/lb_policies_test.c b/test/core/client_channel/lb_policies_test.c
index 95595d7c51c160540c20f5dec7d4c042e4c9f4e6..f46982a3879b945a1dfd2e812344c4494fa9a735 100644
--- a/test/core/client_channel/lb_policies_test.c
+++ b/test/core/client_channel/lb_policies_test.c
@@ -64,9 +64,11 @@ typedef struct servers_fixture {
 } servers_fixture;
 
 typedef struct request_sequences {
-  size_t n;
-  int *connections;
-  int *connectivity_states;
+  size_t n;         /* number of iterations */
+  int *connections; /* indexed by the interation number, value is the index of
+                       the server it connected to or -1 if none */
+  int *connectivity_states; /* indexed by the interation number, value is the
+                               client connectivity state */
 } request_sequences;
 
 typedef void (*verifier_fn)(const servers_fixture *, grpc_channel *,
@@ -239,6 +241,8 @@ static request_sequences request_sequences_create(size_t n) {
   res.n = n;
   res.connections = gpr_malloc(sizeof(*res.connections) * n);
   res.connectivity_states = gpr_malloc(sizeof(*res.connectivity_states) * n);
+  memset(res.connections, 0, sizeof(*res.connections) * n);
+  memset(res.connectivity_states, 0, sizeof(*res.connectivity_states) * n);
   return res;
 }
 
@@ -481,7 +485,7 @@ void run_spec(const test_spec *spec) {
   gpr_asprintf(&client_hostport, "ipv4:%s", servers_hostports_str);
 
   arg_array[0].type = GRPC_ARG_INTEGER;
-  arg_array[0].key = "grpc.testing.fixed_reconnect_backoff";
+  arg_array[0].key = "grpc.testing.fixed_reconnect_backoff_ms";
   arg_array[0].value.integer = RETRY_TIMEOUT;
   arg_array[1].type = GRPC_ARG_STRING;
   arg_array[1].key = GRPC_ARG_LB_POLICY_NAME;
@@ -519,7 +523,7 @@ static grpc_channel *create_client(const servers_fixture *f) {
   gpr_asprintf(&client_hostport, "ipv4:%s", servers_hostports_str);
 
   arg_array[0].type = GRPC_ARG_INTEGER;
-  arg_array[0].key = "grpc.testing.fixed_reconnect_backoff";
+  arg_array[0].key = "grpc.testing.fixed_reconnect_backoff_ms";
   arg_array[0].value.integer = RETRY_TIMEOUT;
   arg_array[1].type = GRPC_ARG_STRING;
   arg_array[1].key = GRPC_ARG_LB_POLICY_NAME;
@@ -641,7 +645,11 @@ static void test_get_channel_info() {
   arg.value.string = "{\"loadBalancingPolicy\": \"ROUND_ROBIN\"}";
   grpc_channel_args *args = grpc_channel_args_copy_and_add(NULL, &arg, 1);
   channel = grpc_insecure_channel_create("ipv4:127.0.0.1:1234", args, NULL);
-  grpc_channel_args_destroy(args);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_channel_args_destroy(&exec_ctx, args);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
   // Ensures that resolver returns.
   grpc_channel_check_connectivity_state(channel, true /* try_to_connect */);
   // Now request the service config again.
@@ -780,15 +788,15 @@ static void verify_total_carnage_round_robin(const servers_fixture *f,
     }
   }
 
-  /* no server is ever available. The persistent state is TRANSIENT_FAILURE */
+  /* No server is ever available. There should be no READY states (or SHUTDOWN).
+   * Note that all other states (IDLE, CONNECTING, TRANSIENT_FAILURE) are still
+   * possible, as the policy transitions while attempting to reconnect. */
   for (size_t i = 0; i < sequences->n; i++) {
     const grpc_connectivity_state actual = sequences->connectivity_states[i];
-    const grpc_connectivity_state expected = GRPC_CHANNEL_TRANSIENT_FAILURE;
-    if (actual != expected) {
+    if (actual == GRPC_CHANNEL_READY || actual == GRPC_CHANNEL_SHUTDOWN) {
       gpr_log(GPR_ERROR,
-              "CONNECTIVITY STATUS SEQUENCE FAILURE: expected '%s', got '%s' "
-              "at iteration #%d",
-              grpc_connectivity_state_name(expected),
+              "CONNECTIVITY STATUS SEQUENCE FAILURE: got unexpected state "
+              "'%s' at iteration #%d.",
               grpc_connectivity_state_name(actual), (int)i);
       abort();
     }
@@ -825,8 +833,7 @@ static void verify_partial_carnage_round_robin(
   }
 
   /* We can assert that the first client channel state should be READY, when all
-   * servers were available; and that the last one should be TRANSIENT_FAILURE,
-   * after all servers are gone. */
+   * servers were available */
   grpc_connectivity_state actual = sequences->connectivity_states[0];
   grpc_connectivity_state expected = GRPC_CHANNEL_READY;
   if (actual != expected) {
@@ -838,17 +845,19 @@ static void verify_partial_carnage_round_robin(
     abort();
   }
 
+  /* ... and that the last one shouldn't be READY (or SHUTDOWN): all servers are
+   * gone. It may be all other states (IDLE, CONNECTING, TRANSIENT_FAILURE), as
+   * the policy transitions while attempting to reconnect. */
   actual = sequences->connectivity_states[num_iters - 1];
-  expected = GRPC_CHANNEL_TRANSIENT_FAILURE;
-  if (actual != expected) {
-    gpr_log(GPR_ERROR,
-            "CONNECTIVITY STATUS SEQUENCE FAILURE: expected '%s', got '%s' "
-            "at iteration #%d",
-            grpc_connectivity_state_name(expected),
-            grpc_connectivity_state_name(actual), (int)num_iters - 1);
-    abort();
+  for (i = 0; i < sequences->n; i++) {
+    if (actual == GRPC_CHANNEL_READY || actual == GRPC_CHANNEL_SHUTDOWN) {
+      gpr_log(GPR_ERROR,
+              "CONNECTIVITY STATUS SEQUENCE FAILURE: got unexpected state "
+              "'%s' at iteration #%d.",
+              grpc_connectivity_state_name(actual), (int)i);
+      abort();
+    }
   }
-
   gpr_free(expected_connection_sequence);
 }
 
@@ -873,68 +882,21 @@ static void verify_rebirth_round_robin(const servers_fixture *f,
                                        grpc_channel *client,
                                        const request_sequences *sequences,
                                        const size_t num_iters) {
-  int *expected_connection_sequence;
-  size_t i, j, unique_seq_last_idx, unique_seq_first_idx;
-  const size_t expected_seq_length = f->num_servers;
-  int *seen_elements;
-
   dump_array("actual_connection_sequence", sequences->connections, num_iters);
 
-  /* verify conn. seq. expectation */
-  /* get the first unique run of length "num_servers". */
-  expected_connection_sequence = gpr_malloc(sizeof(int) * expected_seq_length);
-  seen_elements = gpr_malloc(sizeof(int) * expected_seq_length);
-
-  unique_seq_last_idx = ~(size_t)0;
-
-  memset(seen_elements, 0, sizeof(int) * expected_seq_length);
-  for (i = 0; i < num_iters; i++) {
-    if (sequences->connections[i] < 0 ||
-        seen_elements[sequences->connections[i]] != 0) {
-      /* if anything breaks the uniqueness of the run, back to square zero */
-      memset(seen_elements, 0, sizeof(int) * expected_seq_length);
-      continue;
-    }
-    seen_elements[sequences->connections[i]] = 1;
-    for (j = 0; j < expected_seq_length; j++) {
-      if (seen_elements[j] == 0) break;
-    }
-    if (j == expected_seq_length) { /* seen all the elements */
-      unique_seq_last_idx = i;
-      break;
-    }
-  }
-  /* make sure we found a valid run */
-  dump_array("seen_elements", seen_elements, expected_seq_length);
-  for (j = 0; j < expected_seq_length; j++) {
-    GPR_ASSERT(seen_elements[j] != 0);
-  }
-
-  GPR_ASSERT(unique_seq_last_idx != ~(size_t)0);
-
-  unique_seq_first_idx = (unique_seq_last_idx - expected_seq_length + 1);
-  memcpy(expected_connection_sequence,
-         sequences->connections + unique_seq_first_idx,
-         sizeof(int) * expected_seq_length);
-
   /* first iteration succeeds */
   GPR_ASSERT(sequences->connections[0] != -1);
   /* then we fail for a while... */
   GPR_ASSERT(sequences->connections[1] == -1);
-  /* ... but should be up at "unique_seq_first_idx" */
-  GPR_ASSERT(sequences->connections[unique_seq_first_idx] != -1);
-
-  for (j = 0, i = unique_seq_first_idx; i < num_iters; i++) {
-    const int actual = sequences->connections[i];
-    const int expected =
-        expected_connection_sequence[j++ % expected_seq_length];
-    if (actual != expected) {
-      print_failed_expectations(expected_connection_sequence,
-                                sequences->connections, expected_seq_length,
-                                num_iters);
-      abort();
+  /* ... but should be up eventually */
+  size_t first_iter_back_up = ~0ul;
+  for (size_t i = 2; i < sequences->n; ++i) {
+    if (sequences->connections[i] != -1) {
+      first_iter_back_up = i;
+      break;
     }
   }
+  GPR_ASSERT(first_iter_back_up != ~0ul);
 
   /* We can assert that the first client channel state should be READY, when all
    * servers were available; same thing for the last one. In the middle
@@ -962,7 +924,7 @@ static void verify_rebirth_round_robin(const servers_fixture *f,
   }
 
   bool found_failure_status = false;
-  for (i = 1; i < sequences->n - 1; i++) {
+  for (size_t i = 1; i < sequences->n - 1; i++) {
     if (sequences->connectivity_states[i] == GRPC_CHANNEL_TRANSIENT_FAILURE) {
       found_failure_status = true;
       break;
@@ -974,14 +936,11 @@ static void verify_rebirth_round_robin(const servers_fixture *f,
         "CONNECTIVITY STATUS SEQUENCE FAILURE: "
         "GRPC_CHANNEL_TRANSIENT_FAILURE status not found. Got the following "
         "instead:");
-    for (i = 0; i < num_iters; i++) {
+    for (size_t i = 0; i < num_iters; i++) {
       gpr_log(GPR_ERROR, "[%d]: %s", (int)i,
               grpc_connectivity_state_name(sequences->connectivity_states[i]));
     }
   }
-
-  gpr_free(expected_connection_sequence);
-  gpr_free(seen_elements);
 }
 
 int main(int argc, char **argv) {
@@ -991,8 +950,8 @@ int main(int argc, char **argv) {
   const size_t NUM_ITERS = 10;
   const size_t NUM_SERVERS = 4;
 
-  grpc_test_init(argc, argv);
   grpc_init();
+  grpc_test_init(argc, argv);
   grpc_tracer_set_enabled("round_robin", 1);
 
   GPR_ASSERT(grpc_lb_policy_create(&exec_ctx, "this-lb-policy-does-not-exist",
diff --git a/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c b/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c
index ffa167a0e7899a9850b56b0b2dbb88ab1d638cbe..f0c6843a688e31cfe1a202b690ad76329e2116a6 100644
--- a/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c
+++ b/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c
@@ -63,7 +63,8 @@ static grpc_error *my_resolve_address(const char *name, const char *addr,
   }
 }
 
-static grpc_resolver *create_resolver(const char *name) {
+static grpc_resolver *create_resolver(grpc_exec_ctx *exec_ctx,
+                                      const char *name) {
   grpc_resolver_factory *factory = grpc_resolver_factory_lookup("dns");
   grpc_uri *uri = grpc_uri_parse(name, 0);
   GPR_ASSERT(uri);
@@ -71,7 +72,7 @@ static grpc_resolver *create_resolver(const char *name) {
   memset(&args, 0, sizeof(args));
   args.uri = uri;
   grpc_resolver *resolver =
-      grpc_resolver_factory_create_resolver(factory, &args);
+      grpc_resolver_factory_create_resolver(exec_ctx, factory, &args);
   grpc_resolver_factory_unref(factory);
   grpc_uri_destroy(uri);
   return resolver;
@@ -101,29 +102,29 @@ int main(int argc, char **argv) {
   grpc_init();
   gpr_mu_init(&g_mu);
   grpc_blocking_resolve_address = my_resolve_address;
-
-  grpc_resolver *resolver = create_resolver("dns:test");
-
   grpc_channel_args *result = (grpc_channel_args *)1;
 
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_resolver *resolver = create_resolver(&exec_ctx, "dns:test");
   gpr_event ev1;
   gpr_event_init(&ev1);
-  grpc_resolver_next(&exec_ctx, resolver, &result,
-                     grpc_closure_create(on_done, &ev1));
+  grpc_resolver_next(
+      &exec_ctx, resolver, &result,
+      grpc_closure_create(on_done, &ev1, grpc_schedule_on_exec_ctx));
   grpc_exec_ctx_flush(&exec_ctx);
   GPR_ASSERT(wait_loop(5, &ev1));
   GPR_ASSERT(result == NULL);
 
   gpr_event ev2;
   gpr_event_init(&ev2);
-  grpc_resolver_next(&exec_ctx, resolver, &result,
-                     grpc_closure_create(on_done, &ev2));
+  grpc_resolver_next(
+      &exec_ctx, resolver, &result,
+      grpc_closure_create(on_done, &ev2, grpc_schedule_on_exec_ctx));
   grpc_exec_ctx_flush(&exec_ctx);
   GPR_ASSERT(wait_loop(30, &ev2));
   GPR_ASSERT(result != NULL);
 
-  grpc_channel_args_destroy(result);
+  grpc_channel_args_destroy(&exec_ctx, result);
   GRPC_RESOLVER_UNREF(&exec_ctx, resolver, "test");
   grpc_exec_ctx_finish(&exec_ctx);
 
diff --git a/test/core/client_channel/resolvers/dns_resolver_test.c b/test/core/client_channel/resolvers/dns_resolver_test.c
index 41a9125431f3f4fb37452337ec05d5d2f7b6956a..5603a57b5fc5e3bb07e7f57fac1ee99172ef53d7 100644
--- a/test/core/client_channel/resolvers/dns_resolver_test.c
+++ b/test/core/client_channel/resolvers/dns_resolver_test.c
@@ -48,7 +48,7 @@ static void test_succeeds(grpc_resolver_factory *factory, const char *string) {
   GPR_ASSERT(uri);
   memset(&args, 0, sizeof(args));
   args.uri = uri;
-  resolver = grpc_resolver_factory_create_resolver(factory, &args);
+  resolver = grpc_resolver_factory_create_resolver(&exec_ctx, factory, &args);
   GPR_ASSERT(resolver != NULL);
   GRPC_RESOLVER_UNREF(&exec_ctx, resolver, "test_succeeds");
   grpc_uri_destroy(uri);
@@ -65,7 +65,7 @@ static void test_fails(grpc_resolver_factory *factory, const char *string) {
   GPR_ASSERT(uri);
   memset(&args, 0, sizeof(args));
   args.uri = uri;
-  resolver = grpc_resolver_factory_create_resolver(factory, &args);
+  resolver = grpc_resolver_factory_create_resolver(&exec_ctx, factory, &args);
   GPR_ASSERT(resolver == NULL);
   grpc_uri_destroy(uri);
   grpc_exec_ctx_finish(&exec_ctx);
diff --git a/test/core/client_channel/resolvers/sockaddr_resolver_test.c b/test/core/client_channel/resolvers/sockaddr_resolver_test.c
index ebf311ab83f8eac76a14d19e40a02788ccf50b01..10df78537c8987ed70fbe3a19107a9872afd8924 100644
--- a/test/core/client_channel/resolvers/sockaddr_resolver_test.c
+++ b/test/core/client_channel/resolvers/sockaddr_resolver_test.c
@@ -49,12 +49,7 @@ typedef struct on_resolution_arg {
 
 void on_resolution_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   on_resolution_arg *res = arg;
-  const grpc_arg *channel_arg =
-      grpc_channel_args_find(res->resolver_result, GRPC_ARG_SERVER_NAME);
-  GPR_ASSERT(channel_arg != NULL);
-  GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING);
-  GPR_ASSERT(strcmp(res->expected_server_name, channel_arg->value.string) == 0);
-  grpc_channel_args_destroy(res->resolver_result);
+  grpc_channel_args_destroy(exec_ctx, res->resolver_result);
 }
 
 static void test_succeeds(grpc_resolver_factory *factory, const char *string) {
@@ -67,14 +62,14 @@ static void test_succeeds(grpc_resolver_factory *factory, const char *string) {
   GPR_ASSERT(uri);
   memset(&args, 0, sizeof(args));
   args.uri = uri;
-  resolver = grpc_resolver_factory_create_resolver(factory, &args);
+  resolver = grpc_resolver_factory_create_resolver(&exec_ctx, factory, &args);
   GPR_ASSERT(resolver != NULL);
 
   on_resolution_arg on_res_arg;
   memset(&on_res_arg, 0, sizeof(on_res_arg));
   on_res_arg.expected_server_name = uri->path;
-  grpc_closure *on_resolution =
-      grpc_closure_create(on_resolution_cb, &on_res_arg);
+  grpc_closure *on_resolution = grpc_closure_create(
+      on_resolution_cb, &on_res_arg, grpc_schedule_on_exec_ctx);
 
   grpc_resolver_next(&exec_ctx, resolver, &on_res_arg.resolver_result,
                      on_resolution);
@@ -93,7 +88,7 @@ static void test_fails(grpc_resolver_factory *factory, const char *string) {
   GPR_ASSERT(uri);
   memset(&args, 0, sizeof(args));
   args.uri = uri;
-  resolver = grpc_resolver_factory_create_resolver(factory, &args);
+  resolver = grpc_resolver_factory_create_resolver(&exec_ctx, factory, &args);
   GPR_ASSERT(resolver == NULL);
   grpc_uri_destroy(uri);
   grpc_exec_ctx_finish(&exec_ctx);
diff --git a/test/core/client_channel/set_initial_connect_string_test.c b/test/core/client_channel/set_initial_connect_string_test.c
index b16a3ebf4511633e766a17e11f98694915478bf7..b78ba98e0a9dd2c6f53115e86adfc1fccbb55f88 100644
--- a/test/core/client_channel/set_initial_connect_string_test.c
+++ b/test/core/client_channel/set_initial_connect_string_test.c
@@ -92,8 +92,9 @@ static void handle_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
 static void on_connect(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *tcp,
                        grpc_pollset *accepting_pollset,
                        grpc_tcp_server_acceptor *acceptor) {
+  gpr_free(acceptor);
   test_tcp_server *server = arg;
-  grpc_closure_init(&on_read, handle_read, NULL);
+  grpc_closure_init(&on_read, handle_read, NULL, grpc_schedule_on_exec_ctx);
   grpc_slice_buffer_init(&state.incoming_buffer);
   grpc_slice_buffer_init(&state.temp_incoming_buffer);
   state.tcp = tcp;
@@ -157,7 +158,7 @@ static void cleanup_rpc(void) {
   grpc_event ev;
   grpc_slice_buffer_destroy(&state.incoming_buffer);
   grpc_slice_buffer_destroy(&state.temp_incoming_buffer);
-  grpc_channel_credentials_unref(state.creds);
+  grpc_channel_credentials_release(state.creds);
   grpc_call_destroy(state.call);
   grpc_completion_queue_shutdown(state.cq);
   do {
diff --git a/test/core/compression/algorithm_test.c b/test/core/compression/algorithm_test.c
index bdee748ae6fa02c9cbe2ea68466dc19240a693b9..ff17667b9451efc43f2bc404944bdbc4abd9d94c 100644
--- a/test/core/compression/algorithm_test.c
+++ b/test/core/compression/algorithm_test.c
@@ -53,6 +53,7 @@ static void test_algorithm_mesh(void) {
     grpc_compression_algorithm parsed;
     grpc_mdstr *mdstr;
     grpc_mdelem *mdelem;
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
     GPR_ASSERT(
         grpc_compression_algorithm_name((grpc_compression_algorithm)i, &name));
     GPR_ASSERT(grpc_compression_algorithm_parse(name, strlen(name), &parsed));
@@ -63,8 +64,9 @@ static void test_algorithm_mesh(void) {
     mdelem = grpc_compression_encoding_mdelem(parsed);
     GPR_ASSERT(mdelem->value == mdstr);
     GPR_ASSERT(mdelem->key == GRPC_MDSTR_GRPC_ENCODING);
-    GRPC_MDSTR_UNREF(mdstr);
-    GRPC_MDELEM_UNREF(mdelem);
+    GRPC_MDSTR_UNREF(&exec_ctx, mdstr);
+    GRPC_MDELEM_UNREF(&exec_ctx, mdelem);
+    grpc_exec_ctx_finish(&exec_ctx);
   }
 
   /* test failure */
@@ -73,6 +75,7 @@ static void test_algorithm_mesh(void) {
 }
 
 static void test_algorithm_failure(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_mdstr *mdstr;
 
   gpr_log(GPR_DEBUG, "test_algorithm_failure");
@@ -88,7 +91,8 @@ static void test_algorithm_failure(void) {
              NULL);
   GPR_ASSERT(grpc_compression_algorithm_mdstr(GRPC_COMPRESS_ALGORITHMS_COUNT +
                                               1) == NULL);
-  GRPC_MDSTR_UNREF(mdstr);
+  GRPC_MDSTR_UNREF(&exec_ctx, mdstr);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 int main(int argc, char **argv) {
diff --git a/test/core/compression/message_compress_test.c b/test/core/compression/message_compress_test.c
index fc53cd9d36fdb5867c47c265948211697be0afae..2432ca768adb21ad73fe56a8c644961e9a7b2051 100644
--- a/test/core/compression/message_compress_test.c
+++ b/test/core/compression/message_compress_test.c
@@ -40,6 +40,7 @@
 #include <grpc/support/log.h>
 #include <grpc/support/useful.h>
 
+#include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/support/murmur_hash.h"
 #include "test/core/util/slice_splitter.h"
 #include "test/core/util/test_config.h"
@@ -82,7 +83,12 @@ static void assert_passthrough(grpc_slice value,
 
   grpc_split_slices_to_buffer(uncompressed_split_mode, &value, 1, &input);
 
-  was_compressed = grpc_msg_compress(algorithm, &input, &compressed_raw);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    was_compressed =
+        grpc_msg_compress(&exec_ctx, algorithm, &input, &compressed_raw);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
   GPR_ASSERT(input.count > 0);
 
   switch (compress_result_check) {
@@ -99,8 +105,13 @@ static void assert_passthrough(grpc_slice value,
 
   grpc_split_slice_buffer(compressed_split_mode, &compressed_raw, &compressed);
 
-  GPR_ASSERT(grpc_msg_decompress(
-      was_compressed ? algorithm : GRPC_COMPRESS_NONE, &compressed, &output));
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    GPR_ASSERT(grpc_msg_decompress(
+        &exec_ctx, was_compressed ? algorithm : GRPC_COMPRESS_NONE, &compressed,
+        &output));
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
 
   final = grpc_slice_merge(output.slices, output.count);
   GPR_ASSERT(0 == grpc_slice_cmp(value, final));
@@ -160,7 +171,9 @@ static void test_tiny_data_compress(void) {
 
   for (i = 0; i < GRPC_COMPRESS_ALGORITHMS_COUNT; i++) {
     if (i == GRPC_COMPRESS_NONE) continue;
-    GPR_ASSERT(0 == grpc_msg_compress(i, &input, &output));
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    GPR_ASSERT(0 == grpc_msg_compress(&exec_ctx, i, &input, &output));
+    grpc_exec_ctx_finish(&exec_ctx);
     GPR_ASSERT(1 == output.count);
   }
 
@@ -180,8 +193,9 @@ static void test_bad_decompression_data_crc(void) {
   grpc_slice_buffer_init(&output);
   grpc_slice_buffer_add(&input, create_test_value(ONE_MB_A));
 
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   /* compress it */
-  grpc_msg_compress(GRPC_COMPRESS_GZIP, &input, &corrupted);
+  grpc_msg_compress(&exec_ctx, GRPC_COMPRESS_GZIP, &input, &corrupted);
   /* corrupt the output by smashing the CRC */
   GPR_ASSERT(corrupted.count > 1);
   GPR_ASSERT(GRPC_SLICE_LENGTH(corrupted.slices[1]) > 8);
@@ -189,7 +203,9 @@ static void test_bad_decompression_data_crc(void) {
   memcpy(GRPC_SLICE_START_PTR(corrupted.slices[1]) + idx, &bad, 4);
 
   /* try (and fail) to decompress the corrupted compresed buffer */
-  GPR_ASSERT(0 == grpc_msg_decompress(GRPC_COMPRESS_GZIP, &corrupted, &output));
+  GPR_ASSERT(0 == grpc_msg_decompress(&exec_ctx, GRPC_COMPRESS_GZIP, &corrupted,
+                                      &output));
+  grpc_exec_ctx_finish(&exec_ctx);
 
   grpc_slice_buffer_destroy(&input);
   grpc_slice_buffer_destroy(&corrupted);
@@ -208,7 +224,10 @@ static void test_bad_decompression_data_trailing_garbage(void) {
                   "\x78\xda\x63\x60\x60\x60\x00\x00\x00\x04\x00\x01\x99", 13));
 
   /* try (and fail) to decompress the invalid compresed buffer */
-  GPR_ASSERT(0 == grpc_msg_decompress(GRPC_COMPRESS_DEFLATE, &input, &output));
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  GPR_ASSERT(0 == grpc_msg_decompress(&exec_ctx, GRPC_COMPRESS_DEFLATE, &input,
+                                      &output));
+  grpc_exec_ctx_finish(&exec_ctx);
 
   grpc_slice_buffer_destroy(&input);
   grpc_slice_buffer_destroy(&output);
@@ -224,7 +243,10 @@ static void test_bad_decompression_data_stream(void) {
                         grpc_slice_from_copied_buffer("\x78\xda\xff\xff", 4));
 
   /* try (and fail) to decompress the invalid compresed buffer */
-  GPR_ASSERT(0 == grpc_msg_decompress(GRPC_COMPRESS_DEFLATE, &input, &output));
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  GPR_ASSERT(0 == grpc_msg_decompress(&exec_ctx, GRPC_COMPRESS_DEFLATE, &input,
+                                      &output));
+  grpc_exec_ctx_finish(&exec_ctx);
 
   grpc_slice_buffer_destroy(&input);
   grpc_slice_buffer_destroy(&output);
@@ -239,13 +261,16 @@ static void test_bad_compression_algorithm(void) {
   grpc_slice_buffer_init(&output);
   grpc_slice_buffer_add(
       &input, grpc_slice_from_copied_string("Never gonna give you up"));
-  was_compressed =
-      grpc_msg_compress(GRPC_COMPRESS_ALGORITHMS_COUNT, &input, &output);
+
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  was_compressed = grpc_msg_compress(&exec_ctx, GRPC_COMPRESS_ALGORITHMS_COUNT,
+                                     &input, &output);
   GPR_ASSERT(0 == was_compressed);
 
-  was_compressed =
-      grpc_msg_compress(GRPC_COMPRESS_ALGORITHMS_COUNT + 123, &input, &output);
+  was_compressed = grpc_msg_compress(
+      &exec_ctx, GRPC_COMPRESS_ALGORITHMS_COUNT + 123, &input, &output);
   GPR_ASSERT(0 == was_compressed);
+  grpc_exec_ctx_finish(&exec_ctx);
 
   grpc_slice_buffer_destroy(&input);
   grpc_slice_buffer_destroy(&output);
@@ -261,13 +286,15 @@ static void test_bad_decompression_algorithm(void) {
   grpc_slice_buffer_add(&input,
                         grpc_slice_from_copied_string(
                             "I'm not really compressed but it doesn't matter"));
-  was_decompressed =
-      grpc_msg_decompress(GRPC_COMPRESS_ALGORITHMS_COUNT, &input, &output);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  was_decompressed = grpc_msg_decompress(
+      &exec_ctx, GRPC_COMPRESS_ALGORITHMS_COUNT, &input, &output);
   GPR_ASSERT(0 == was_decompressed);
 
-  was_decompressed = grpc_msg_decompress(GRPC_COMPRESS_ALGORITHMS_COUNT + 123,
-                                         &input, &output);
+  was_decompressed = grpc_msg_decompress(
+      &exec_ctx, GRPC_COMPRESS_ALGORITHMS_COUNT + 123, &input, &output);
   GPR_ASSERT(0 == was_decompressed);
+  grpc_exec_ctx_finish(&exec_ctx);
 
   grpc_slice_buffer_destroy(&input);
   grpc_slice_buffer_destroy(&output);
diff --git a/test/core/end2end/BUILD b/test/core/end2end/BUILD
new file mode 100644
index 0000000000000000000000000000000000000000..681cea1de74e1fce1ca37a8da493fc59533bcae2
--- /dev/null
+++ b/test/core/end2end/BUILD
@@ -0,0 +1,78 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+load(":generate_tests.bzl", "grpc_end2end_tests")
+
+cc_library(
+  name = 'cq_verifier',
+  srcs = ['cq_verifier.c'],
+  hdrs = ['cq_verifier.h'],
+  deps = ['//:gpr', '//:grpc', '//test/core/util:grpc_test_util'],
+  copts = ['-std=c99'],
+  visibility = ["//test:__subpackages__"],
+)
+
+cc_library(
+  name = 'ssl_test_data',
+  visibility = ["//test:__subpackages__"],
+  hdrs = ['data/ssl_test_data.h'],
+  copts = ['-std=c99'],
+  srcs = [
+    "data/client_certs.c",
+    "data/server1_cert.c",
+    "data/server1_key.c",
+    "data/test_root_cert.c",
+  ]
+)
+
+cc_library(
+  name = 'fake_resolver',
+  hdrs = ['fake_resolver.h'],
+  srcs = ['fake_resolver.c'],
+  copts = ['-std=c99'],
+  deps = ['//:gpr', '//:grpc', '//test/core/util:grpc_test_util']
+)
+
+cc_library(
+  name = 'http_proxy',
+  hdrs = ['fixtures/http_proxy.h'],
+  srcs = ['fixtures/http_proxy.c'],
+  copts = ['-std=c99'],
+  deps = ['//:gpr', '//:grpc', '//test/core/util:grpc_test_util']
+)
+
+cc_library(
+  name = 'proxy',
+  hdrs = ['fixtures/proxy.h'],
+  srcs = ['fixtures/proxy.c'],
+  copts = ['-std=c99'],
+  deps = ['//:gpr', '//:grpc', '//test/core/util:grpc_test_util']
+)
+
+grpc_end2end_tests()
diff --git a/test/core/end2end/bad_server_response_test.c b/test/core/end2end/bad_server_response_test.c
index 1c4a17fda84a668237f69b15b74bbf145d275abc..a05201688309f9cc4ede388ead33a2a0c30b50d4 100644
--- a/test/core/end2end/bad_server_response_test.c
+++ b/test/core/end2end/bad_server_response_test.c
@@ -47,6 +47,7 @@
 #include <grpc/support/thd.h>
 
 #include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/support/string.h"
 #include "test/core/end2end/cq_verifier.h"
@@ -145,9 +146,10 @@ static void handle_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
 static void on_connect(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *tcp,
                        grpc_pollset *accepting_pollset,
                        grpc_tcp_server_acceptor *acceptor) {
+  gpr_free(acceptor);
   test_tcp_server *server = arg;
-  grpc_closure_init(&on_read, handle_read, NULL);
-  grpc_closure_init(&on_write, done_write, NULL);
+  grpc_closure_init(&on_read, handle_read, NULL, grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&on_write, done_write, NULL, grpc_schedule_on_exec_ctx);
   grpc_slice_buffer_init(&state.temp_incoming_buffer);
   grpc_slice_buffer_init(&state.outgoing_buffer);
   state.tcp = tcp;
@@ -226,10 +228,10 @@ static void start_rpc(int target_port, grpc_status_code expected_status,
   cq_verifier_destroy(cqv);
 }
 
-static void cleanup_rpc(void) {
+static void cleanup_rpc(grpc_exec_ctx *exec_ctx) {
   grpc_event ev;
-  grpc_slice_buffer_destroy(&state.temp_incoming_buffer);
-  grpc_slice_buffer_destroy(&state.outgoing_buffer);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &state.temp_incoming_buffer);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &state.outgoing_buffer);
   grpc_call_destroy(state.call);
   grpc_completion_queue_shutdown(state.cq);
   do {
@@ -298,8 +300,8 @@ static void run_test(const char *response_payload,
   /* clean up */
   grpc_endpoint_shutdown(&exec_ctx, state.tcp);
   grpc_endpoint_destroy(&exec_ctx, state.tcp);
+  cleanup_rpc(&exec_ctx);
   grpc_exec_ctx_finish(&exec_ctx);
-  cleanup_rpc();
   test_tcp_server_destroy(&test_server);
 
   grpc_shutdown();
diff --git a/test/core/end2end/connection_refused_test.c b/test/core/end2end/connection_refused_test.c
index 728d6dca5900bf4fb098e3f401731479416b83db..44308043608053f108ed22eea598d72a6068bac1 100644
--- a/test/core/end2end/connection_refused_test.c
+++ b/test/core/end2end/connection_refused_test.c
@@ -145,7 +145,11 @@ static void run_test(bool wait_for_ready, bool use_service_config) {
   gpr_free(details);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
 
-  if (args != NULL) grpc_channel_args_destroy(args);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    if (args != NULL) grpc_channel_args_destroy(&exec_ctx, args);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
 
   grpc_shutdown();
 }
diff --git a/test/core/end2end/end2end_test.sh b/test/core/end2end/end2end_test.sh
new file mode 100755
index 0000000000000000000000000000000000000000..f2309acc881d6539a854a9c15a4fd3b28fb6a561
--- /dev/null
+++ b/test/core/end2end/end2end_test.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+# Test runner for end2end tests from bazel
+
+# 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.
+$1 $2
diff --git a/test/core/end2end/fake_resolver.c b/test/core/end2end/fake_resolver.c
index 865b55de4d659f1da143c3a9237f32b94273619e..966642f403c7f9e660929ec51aafbe1254ddd199 100644
--- a/test/core/end2end/fake_resolver.c
+++ b/test/core/end2end/fake_resolver.c
@@ -48,6 +48,7 @@
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/iomgr/resolve_address.h"
 #include "src/core/lib/iomgr/unix_sockets_posix.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/support/string.h"
 
@@ -76,8 +77,8 @@ typedef struct {
 static void fake_resolver_destroy(grpc_exec_ctx* exec_ctx, grpc_resolver* gr) {
   fake_resolver* r = (fake_resolver*)gr;
   gpr_mu_destroy(&r->mu);
-  grpc_channel_args_destroy(r->channel_args);
-  grpc_lb_addresses_destroy(r->addresses);
+  grpc_channel_args_destroy(exec_ctx, r->channel_args);
+  grpc_lb_addresses_destroy(exec_ctx, r->addresses);
   gpr_free(r);
 }
 
@@ -87,7 +88,7 @@ static void fake_resolver_shutdown(grpc_exec_ctx* exec_ctx,
   gpr_mu_lock(&r->mu);
   if (r->next_completion != NULL) {
     *r->target_result = NULL;
-    grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE);
     r->next_completion = NULL;
   }
   gpr_mu_unlock(&r->mu);
@@ -100,7 +101,7 @@ static void fake_resolver_maybe_finish_next_locked(grpc_exec_ctx* exec_ctx,
     grpc_arg arg = grpc_lb_addresses_create_channel_arg(r->addresses);
     *r->target_result =
         grpc_channel_args_copy_and_add(r->channel_args, &arg, 1);
-    grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE);
     r->next_completion = NULL;
   }
 }
@@ -140,7 +141,8 @@ static void fake_resolver_factory_unref(grpc_resolver_factory* factory) {}
 
 static void do_nothing(void* ignored) {}
 
-static grpc_resolver* fake_resolver_create(grpc_resolver_factory* factory,
+static grpc_resolver* fake_resolver_create(grpc_exec_ctx* exec_ctx,
+                                           grpc_resolver_factory* factory,
                                            grpc_resolver_args* args) {
   if (0 != strcmp(args->uri->authority, "")) {
     gpr_log(GPR_ERROR, "authority based uri's not supported by the %s scheme",
@@ -172,21 +174,16 @@ static grpc_resolver* fake_resolver_create(grpc_resolver_factory* factory,
     addresses->addresses[i].is_balancer = lb_enabled;
     if (errors_found) break;
   }
-  grpc_slice_buffer_destroy(&path_parts);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &path_parts);
   grpc_slice_unref(path_slice);
   if (errors_found) {
-    grpc_lb_addresses_destroy(addresses);
+    grpc_lb_addresses_destroy(exec_ctx, addresses);
     return NULL;
   }
   // Instantiate resolver.
   fake_resolver* r = gpr_malloc(sizeof(fake_resolver));
   memset(r, 0, sizeof(*r));
-  grpc_arg server_name_arg;
-  server_name_arg.type = GRPC_ARG_STRING;
-  server_name_arg.key = GRPC_ARG_SERVER_NAME;
-  server_name_arg.value.string = args->uri->path;
-  r->channel_args =
-      grpc_channel_args_copy_and_add(args->args, &server_name_arg, 1);
+  r->channel_args = grpc_channel_args_copy(args->args);
   r->addresses = addresses;
   gpr_mu_init(&r->mu);
   grpc_resolver_init(&r->base, &fake_resolver_vtable);
diff --git a/test/core/end2end/fixtures/h2_census.c b/test/core/end2end/fixtures/h2_census.c
index c52d7660f59850776dc54b390aede107b1c76d9d..8e60123ed6e5ebb5c67cb3088587e279e410c00d 100644
--- a/test/core/end2end/fixtures/h2_census.c
+++ b/test/core/end2end/fixtures/h2_census.c
@@ -85,7 +85,11 @@ void chttp2_init_client_fullstack(grpc_end2end_test_fixture *f,
   client_args = grpc_channel_args_copy_and_add(client_args, &arg, 1);
   f->client = grpc_insecure_channel_create(ffd->localaddr, client_args, NULL);
   GPR_ASSERT(f->client);
-  grpc_channel_args_destroy(client_args);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_channel_args_destroy(&exec_ctx, client_args);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
 }
 
 void chttp2_init_server_fullstack(grpc_end2end_test_fixture *f,
@@ -97,7 +101,11 @@ void chttp2_init_server_fullstack(grpc_end2end_test_fixture *f,
   }
   server_args = grpc_channel_args_copy_and_add(server_args, &arg, 1);
   f->server = grpc_server_create(server_args, NULL);
-  grpc_channel_args_destroy(server_args);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_channel_args_destroy(&exec_ctx, server_args);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
   grpc_server_register_completion_queue(f->server, f->cq, NULL);
   GPR_ASSERT(grpc_server_add_insecure_http2_port(f->server, ffd->localaddr));
   grpc_server_start(f->server);
diff --git a/test/core/end2end/fixtures/h2_compress.c b/test/core/end2end/fixtures/h2_compress.c
index fedd2ebc467d2bcaf04c0b33efde67a9ca7014a6..c01e45664bb3300945ec0abe75a2a21f6176cd48 100644
--- a/test/core/end2end/fixtures/h2_compress.c
+++ b/test/core/end2end/fixtures/h2_compress.c
@@ -78,7 +78,9 @@ void chttp2_init_client_fullstack_compression(grpc_end2end_test_fixture *f,
                                               grpc_channel_args *client_args) {
   fullstack_compression_fixture_data *ffd = f->fixture_data;
   if (ffd->client_args_compression != NULL) {
-    grpc_channel_args_destroy(ffd->client_args_compression);
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_channel_args_destroy(&exec_ctx, ffd->client_args_compression);
+    grpc_exec_ctx_finish(&exec_ctx);
   }
   ffd->client_args_compression = grpc_channel_args_set_compression_algorithm(
       client_args, GRPC_COMPRESS_GZIP);
@@ -90,7 +92,9 @@ void chttp2_init_server_fullstack_compression(grpc_end2end_test_fixture *f,
                                               grpc_channel_args *server_args) {
   fullstack_compression_fixture_data *ffd = f->fixture_data;
   if (ffd->server_args_compression != NULL) {
-    grpc_channel_args_destroy(ffd->server_args_compression);
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_channel_args_destroy(&exec_ctx, ffd->server_args_compression);
+    grpc_exec_ctx_finish(&exec_ctx);
   }
   ffd->server_args_compression = grpc_channel_args_set_compression_algorithm(
       server_args, GRPC_COMPRESS_GZIP);
@@ -104,11 +108,13 @@ void chttp2_init_server_fullstack_compression(grpc_end2end_test_fixture *f,
 }
 
 void chttp2_tear_down_fullstack_compression(grpc_end2end_test_fixture *f) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   fullstack_compression_fixture_data *ffd = f->fixture_data;
-  grpc_channel_args_destroy(ffd->client_args_compression);
-  grpc_channel_args_destroy(ffd->server_args_compression);
+  grpc_channel_args_destroy(&exec_ctx, ffd->client_args_compression);
+  grpc_channel_args_destroy(&exec_ctx, ffd->server_args_compression);
   gpr_free(ffd->localaddr);
   gpr_free(ffd);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 /* All test configurations */
diff --git a/test/core/end2end/fixtures/h2_load_reporting.c b/test/core/end2end/fixtures/h2_load_reporting.c
index 7a76489b440a8903740582c53fc971dcf87cce91..38321f34db73e77145992ab5d5e805be031d0dec 100644
--- a/test/core/end2end/fixtures/h2_load_reporting.c
+++ b/test/core/end2end/fixtures/h2_load_reporting.c
@@ -88,7 +88,11 @@ void chttp2_init_server_load_reporting(grpc_end2end_test_fixture *f,
   }
   server_args = grpc_channel_args_copy_and_add(server_args, &arg, 1);
   f->server = grpc_server_create(server_args, NULL);
-  grpc_channel_args_destroy(server_args);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_channel_args_destroy(&exec_ctx, server_args);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
   grpc_server_register_completion_queue(f->server, f->cq, NULL);
   GPR_ASSERT(grpc_server_add_insecure_http2_port(f->server, ffd->localaddr));
   grpc_server_start(f->server);
diff --git a/test/core/end2end/fixtures/h2_oauth2.c b/test/core/end2end/fixtures/h2_oauth2.c
index 6122f4f2f93c4e51806a48fc61f60b4ed869812a..83f759ce2dd6499053602d24db06d02af5297d75 100644
--- a/test/core/end2end/fixtures/h2_oauth2.c
+++ b/test/core/end2end/fixtures/h2_oauth2.c
@@ -163,7 +163,11 @@ static void chttp2_init_client_simple_ssl_with_oauth2_secure_fullstack(
   grpc_channel_args *new_client_args =
       grpc_channel_args_copy_and_add(client_args, &ssl_name_override, 1);
   chttp2_init_client_secure_fullstack(f, new_client_args, ssl_oauth2_creds);
-  grpc_channel_args_destroy(new_client_args);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_channel_args_destroy(&exec_ctx, new_client_args);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
   grpc_channel_credentials_release(ssl_creds);
   grpc_call_credentials_release(oauth2_creds);
 }
diff --git a/test/core/end2end/fixtures/h2_ssl.c b/test/core/end2end/fixtures/h2_ssl.c
index bbd522cf309719b4f8a38c886c0aceb4b61785bb..cf44cd093c93633add39c6b77939706ff57be041 100644
--- a/test/core/end2end/fixtures/h2_ssl.c
+++ b/test/core/end2end/fixtures/h2_ssl.c
@@ -118,7 +118,11 @@ static void chttp2_init_client_simple_ssl_secure_fullstack(
   grpc_channel_args *new_client_args =
       grpc_channel_args_copy_and_add(client_args, &ssl_name_override, 1);
   chttp2_init_client_secure_fullstack(f, new_client_args, ssl_creds);
-  grpc_channel_args_destroy(new_client_args);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_channel_args_destroy(&exec_ctx, new_client_args);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
 }
 
 static int fail_server_auth_check(grpc_channel_args *server_args) {
diff --git a/test/core/end2end/fixtures/h2_ssl_cert.c b/test/core/end2end/fixtures/h2_ssl_cert.c
index e39cb491de86af0831c53cde3ae644592313c7fe..ae49cc859aff1903b7029b5f552ae94747ac6ebc 100644
--- a/test/core/end2end/fixtures/h2_ssl_cert.c
+++ b/test/core/end2end/fixtures/h2_ssl_cert.c
@@ -186,7 +186,11 @@ typedef enum { NONE, SELF_SIGNED, SIGNED, BAD_CERT_PAIR } certtype;
     grpc_channel_args *new_client_args =                                     \
         grpc_channel_args_copy_and_add(client_args, &ssl_name_override, 1);  \
     chttp2_init_client_secure_fullstack(f, new_client_args, ssl_creds);      \
-    grpc_channel_args_destroy(new_client_args);                              \
+    {                                                                        \
+      grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;                           \
+      grpc_channel_args_destroy(&exec_ctx, new_client_args);                 \
+      grpc_exec_ctx_finish(&exec_ctx);                                       \
+    }                                                                        \
   }
 
 CLIENT_INIT(NONE)
diff --git a/test/core/end2end/fixtures/h2_ssl_proxy.c b/test/core/end2end/fixtures/h2_ssl_proxy.c
index 27cf3ebf326e326c9fd904c22bb23952d20c1649..740b075bf6d32abe3296f3ed9a7318247d623ffe 100644
--- a/test/core/end2end/fixtures/h2_ssl_proxy.c
+++ b/test/core/end2end/fixtures/h2_ssl_proxy.c
@@ -79,7 +79,11 @@ static grpc_channel *create_proxy_client(const char *target,
   channel =
       grpc_secure_channel_create(ssl_creds, target, new_client_args, NULL);
   grpc_channel_credentials_release(ssl_creds);
-  grpc_channel_args_destroy(new_client_args);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_channel_args_destroy(&exec_ctx, new_client_args);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
   return channel;
 }
 
@@ -151,7 +155,11 @@ static void chttp2_init_client_simple_ssl_secure_fullstack(
   grpc_channel_args *new_client_args =
       grpc_channel_args_copy_and_add(client_args, &ssl_name_override, 1);
   chttp2_init_client_secure_fullstack(f, new_client_args, ssl_creds);
-  grpc_channel_args_destroy(new_client_args);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_channel_args_destroy(&exec_ctx, new_client_args);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
 }
 
 static int fail_server_auth_check(grpc_channel_args *server_args) {
diff --git a/test/core/end2end/fixtures/http_proxy.c b/test/core/end2end/fixtures/http_proxy.c
index 57fc4a38f8bfb888bf36d5a94955a06a3d602cd7..b62082c6fb1d1552aca41593a4277376e41dc63b 100644
--- a/test/core/end2end/fixtures/http_proxy.c
+++ b/test/core/end2end/fixtures/http_proxy.c
@@ -59,6 +59,7 @@
 #include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/iomgr/tcp_client.h"
 #include "src/core/lib/iomgr/tcp_server.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "test/core/util/port.h"
 
 struct grpc_end2end_http_proxy {
@@ -110,12 +111,14 @@ static void proxy_connection_unref(grpc_exec_ctx* exec_ctx,
     if (conn->server_endpoint != NULL)
       grpc_endpoint_destroy(exec_ctx, conn->server_endpoint);
     grpc_pollset_set_destroy(conn->pollset_set);
-    grpc_slice_buffer_destroy(&conn->client_read_buffer);
-    grpc_slice_buffer_destroy(&conn->client_deferred_write_buffer);
-    grpc_slice_buffer_destroy(&conn->client_write_buffer);
-    grpc_slice_buffer_destroy(&conn->server_read_buffer);
-    grpc_slice_buffer_destroy(&conn->server_deferred_write_buffer);
-    grpc_slice_buffer_destroy(&conn->server_write_buffer);
+    grpc_slice_buffer_destroy_internal(exec_ctx, &conn->client_read_buffer);
+    grpc_slice_buffer_destroy_internal(exec_ctx,
+                                       &conn->client_deferred_write_buffer);
+    grpc_slice_buffer_destroy_internal(exec_ctx, &conn->client_write_buffer);
+    grpc_slice_buffer_destroy_internal(exec_ctx, &conn->server_read_buffer);
+    grpc_slice_buffer_destroy_internal(exec_ctx,
+                                       &conn->server_deferred_write_buffer);
+    grpc_slice_buffer_destroy_internal(exec_ctx, &conn->server_write_buffer);
     grpc_http_parser_destroy(&conn->http_parser);
     grpc_http_request_destroy(&conn->http_request);
     gpr_free(conn);
@@ -367,6 +370,7 @@ static void on_read_request_done(grpc_exec_ctx* exec_ctx, void* arg,
 static void on_accept(grpc_exec_ctx* exec_ctx, void* arg,
                       grpc_endpoint* endpoint, grpc_pollset* accepting_pollset,
                       grpc_tcp_server_acceptor* acceptor) {
+  gpr_free(acceptor);
   grpc_end2end_http_proxy* proxy = arg;
   // Instantiate proxy_connection.
   proxy_connection* conn = gpr_malloc(sizeof(*conn));
@@ -375,15 +379,20 @@ static void on_accept(grpc_exec_ctx* exec_ctx, void* arg,
   gpr_ref_init(&conn->refcount, 1);
   conn->pollset_set = grpc_pollset_set_create();
   grpc_pollset_set_add_pollset(exec_ctx, conn->pollset_set, proxy->pollset);
-  grpc_closure_init(&conn->on_read_request_done, on_read_request_done, conn);
-  grpc_closure_init(&conn->on_server_connect_done, on_server_connect_done,
-                    conn);
-  grpc_closure_init(&conn->on_write_response_done, on_write_response_done,
-                    conn);
-  grpc_closure_init(&conn->on_client_read_done, on_client_read_done, conn);
-  grpc_closure_init(&conn->on_client_write_done, on_client_write_done, conn);
-  grpc_closure_init(&conn->on_server_read_done, on_server_read_done, conn);
-  grpc_closure_init(&conn->on_server_write_done, on_server_write_done, conn);
+  grpc_closure_init(&conn->on_read_request_done, on_read_request_done, conn,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&conn->on_server_connect_done, on_server_connect_done, conn,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&conn->on_write_response_done, on_write_response_done, conn,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&conn->on_client_read_done, on_client_read_done, conn,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&conn->on_client_write_done, on_client_write_done, conn,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&conn->on_server_read_done, on_server_read_done, conn,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&conn->on_server_write_done, on_server_write_done, conn,
+                    grpc_schedule_on_exec_ctx);
   grpc_slice_buffer_init(&conn->client_read_buffer);
   grpc_slice_buffer_init(&conn->client_deferred_write_buffer);
   grpc_slice_buffer_init(&conn->client_write_buffer);
@@ -468,9 +477,10 @@ void grpc_end2end_http_proxy_destroy(grpc_end2end_http_proxy* proxy) {
   grpc_tcp_server_shutdown_listeners(&exec_ctx, proxy->server);
   grpc_tcp_server_unref(&exec_ctx, proxy->server);
   gpr_free(proxy->proxy_name);
-  grpc_channel_args_destroy(proxy->channel_args);
+  grpc_channel_args_destroy(&exec_ctx, proxy->channel_args);
   grpc_closure destroyed;
-  grpc_closure_init(&destroyed, destroy_pollset, proxy->pollset);
+  grpc_closure_init(&destroyed, destroy_pollset, proxy->pollset,
+                    grpc_schedule_on_exec_ctx);
   grpc_pollset_shutdown(&exec_ctx, proxy->pollset, &destroyed);
   gpr_free(proxy);
   grpc_exec_ctx_finish(&exec_ctx);
diff --git a/test/core/end2end/fuzzers/BUILD b/test/core/end2end/fuzzers/BUILD
new file mode 100644
index 0000000000000000000000000000000000000000..2adda560b401516a55f4ee8f863488181579c38a
--- /dev/null
+++ b/test/core/end2end/fuzzers/BUILD
@@ -0,0 +1,54 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+load("//test/core/util:grpc_fuzzer.bzl", "grpc_fuzzer")
+
+grpc_fuzzer(
+  name = "api_fuzzer",
+  srcs = ["api_fuzzer.c"],
+  deps = ["//:gpr", "//:grpc", "//test/core/util:grpc_test_util", "//test/core/end2end:ssl_test_data"],
+  corpus = "api_fuzzer_corpus",
+  copts = ["-std=c99"],
+)
+
+grpc_fuzzer(
+  name = "client_fuzzer",
+  srcs = ["client_fuzzer.c"],
+  deps = ["//:gpr", "//:grpc", "//test/core/util:grpc_test_util"],
+  corpus = "client_fuzzer_corpus",
+  copts = ["-std=c99"],
+)
+
+grpc_fuzzer(
+  name = "server_fuzzer",
+  srcs = ["server_fuzzer.c"],
+  deps = ["//:gpr", "//:grpc", "//test/core/util:grpc_test_util"],
+  corpus = "server_fuzzer_corpus",
+  copts = ["-std=c99"],
+)
diff --git a/test/core/end2end/fuzzers/api_fuzzer.c b/test/core/end2end/fuzzers/api_fuzzer.c
index 19ac6ced14b39a7c53dbbe2e2221e41a71e41d88..200a51858a02072fec588b626ed8e30db55ecaea 100644
--- a/test/core/end2end/fuzzers/api_fuzzer.c
+++ b/test/core/end2end/fuzzers/api_fuzzer.c
@@ -349,11 +349,11 @@ static void finish_resolve(grpc_exec_ctx *exec_ctx, void *arg,
     addrs->addrs = gpr_malloc(sizeof(*addrs->addrs));
     addrs->addrs[0].len = 0;
     *r->addrs = addrs;
-    grpc_exec_ctx_sched(exec_ctx, r->on_done, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, r->on_done, GRPC_ERROR_NONE);
   } else {
-    grpc_exec_ctx_sched(
+    grpc_closure_sched(
         exec_ctx, r->on_done,
-        GRPC_ERROR_CREATE_REFERENCING("Resolution failed", &error, 1), NULL);
+        GRPC_ERROR_CREATE_REFERENCING("Resolution failed", &error, 1));
   }
 
   gpr_free(r->addr);
@@ -361,16 +361,19 @@ static void finish_resolve(grpc_exec_ctx *exec_ctx, void *arg,
 }
 
 void my_resolve_address(grpc_exec_ctx *exec_ctx, const char *addr,
-                        const char *default_port, grpc_closure *on_done,
+                        const char *default_port,
+                        grpc_pollset_set *interested_parties,
+                        grpc_closure *on_done,
                         grpc_resolved_addresses **addresses) {
   addr_req *r = gpr_malloc(sizeof(*r));
   r->addr = gpr_strdup(addr);
   r->on_done = on_done;
   r->addrs = addresses;
-  grpc_timer_init(exec_ctx, &r->timer,
-                  gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
-                               gpr_time_from_seconds(1, GPR_TIMESPAN)),
-                  finish_resolve, r, gpr_now(GPR_CLOCK_MONOTONIC));
+  grpc_timer_init(
+      exec_ctx, &r->timer, gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+                                        gpr_time_from_seconds(1, GPR_TIMESPAN)),
+      grpc_closure_create(finish_resolve, r, grpc_schedule_on_exec_ctx),
+      gpr_now(GPR_CLOCK_MONOTONIC));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -396,7 +399,7 @@ static void do_connect(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   future_connect *fc = arg;
   if (error != GRPC_ERROR_NONE) {
     *fc->ep = NULL;
-    grpc_exec_ctx_sched(exec_ctx, fc->closure, GRPC_ERROR_REF(error), NULL);
+    grpc_closure_sched(exec_ctx, fc->closure, GRPC_ERROR_REF(error));
   } else if (g_server != NULL) {
     grpc_endpoint *client;
     grpc_endpoint *server;
@@ -408,7 +411,7 @@ static void do_connect(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
     grpc_server_setup_transport(exec_ctx, g_server, transport, NULL, NULL);
     grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL);
 
-    grpc_exec_ctx_sched(exec_ctx, fc->closure, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, fc->closure, GRPC_ERROR_NONE);
   } else {
     sched_connect(exec_ctx, fc->closure, fc->ep, fc->deadline);
   }
@@ -419,8 +422,8 @@ static void sched_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
                           grpc_endpoint **ep, gpr_timespec deadline) {
   if (gpr_time_cmp(deadline, gpr_now(deadline.clock_type)) < 0) {
     *ep = NULL;
-    grpc_exec_ctx_sched(exec_ctx, closure,
-                        GRPC_ERROR_CREATE("Connect deadline exceeded"), NULL);
+    grpc_closure_sched(exec_ctx, closure,
+                       GRPC_ERROR_CREATE("Connect deadline exceeded"));
     return;
   }
 
@@ -428,10 +431,11 @@ static void sched_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
   fc->closure = closure;
   fc->ep = ep;
   fc->deadline = deadline;
-  grpc_timer_init(exec_ctx, &fc->timer,
-                  gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
-                               gpr_time_from_millis(1, GPR_TIMESPAN)),
-                  do_connect, fc, gpr_now(GPR_CLOCK_MONOTONIC));
+  grpc_timer_init(
+      exec_ctx, &fc->timer, gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+                                         gpr_time_from_millis(1, GPR_TIMESPAN)),
+      grpc_closure_create(do_connect, fc, grpc_schedule_on_exec_ctx),
+      gpr_now(GPR_CLOCK_MONOTONIC));
 }
 
 static void my_tcp_client_connect(grpc_exec_ctx *exec_ctx,
@@ -744,7 +748,11 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
           grpc_channel_args *args = read_args(&inp);
           g_channel = grpc_insecure_channel_create(target_uri, args, NULL);
           GPR_ASSERT(g_channel != NULL);
-          grpc_channel_args_destroy(args);
+          {
+            grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+            grpc_channel_args_destroy(&exec_ctx, args);
+            grpc_exec_ctx_finish(&exec_ctx);
+          }
           gpr_free(target_uri);
           gpr_free(target);
         } else {
@@ -768,7 +776,11 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
           grpc_channel_args *args = read_args(&inp);
           g_server = grpc_server_create(args, NULL);
           GPR_ASSERT(g_server != NULL);
-          grpc_channel_args_destroy(args);
+          {
+            grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+            grpc_channel_args_destroy(&exec_ctx, args);
+            grpc_exec_ctx_finish(&exec_ctx);
+          }
           grpc_server_register_completion_queue(g_server, cq, NULL);
           grpc_server_start(g_server);
           server_shutdown = false;
@@ -1104,7 +1116,11 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
           grpc_channel_credentials *creds = read_channel_creds(&inp);
           g_channel = grpc_secure_channel_create(creds, target_uri, args, NULL);
           GPR_ASSERT(g_channel != NULL);
-          grpc_channel_args_destroy(args);
+          {
+            grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+            grpc_channel_args_destroy(&exec_ctx, args);
+            grpc_exec_ctx_finish(&exec_ctx);
+          }
           gpr_free(target_uri);
           gpr_free(target);
           grpc_channel_credentials_release(creds);
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/08455b3ef9d516deb8155d8db7d51c43ce0ff07a b/test/core/end2end/fuzzers/api_fuzzer_corpus/08455b3ef9d516deb8155d8db7d51c43ce0ff07a
new file mode 100644
index 0000000000000000000000000000000000000000..4b698ba9ee26c4cd200fcbdc446d61490dafbd70
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/08455b3ef9d516deb8155d8db7d51c43ce0ff07a differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/0d19e60f4eb1b729e272dd5dec68e8b67f03a5f4 b/test/core/end2end/fuzzers/api_fuzzer_corpus/0d19e60f4eb1b729e272dd5dec68e8b67f03a5f4
new file mode 100644
index 0000000000000000000000000000000000000000..2889e7040e8eaf3d989ac735a1f866b4fef66b9f
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/0d19e60f4eb1b729e272dd5dec68e8b67f03a5f4 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/14064ac4844c709b247a4345a92d8be9766785c4 b/test/core/end2end/fuzzers/api_fuzzer_corpus/14064ac4844c709b247a4345a92d8be9766785c4
new file mode 100644
index 0000000000000000000000000000000000000000..fa731d4a050800e0341e9f837510685aa95af9b4
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/14064ac4844c709b247a4345a92d8be9766785c4 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/1901234bd7c9cb6ee19ff8b17179c481bdc7c5f2 b/test/core/end2end/fuzzers/api_fuzzer_corpus/1901234bd7c9cb6ee19ff8b17179c481bdc7c5f2
new file mode 100644
index 0000000000000000000000000000000000000000..ef09a0eaf535368d3ce7552dc17f084a12700661
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/1901234bd7c9cb6ee19ff8b17179c481bdc7c5f2 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/20ff1f8d5d34cb01d58aa334dc46a6644e6e1d12 b/test/core/end2end/fuzzers/api_fuzzer_corpus/20ff1f8d5d34cb01d58aa334dc46a6644e6e1d12
new file mode 100644
index 0000000000000000000000000000000000000000..fc747d4a727b53e600bd04db9db113699e443c55
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/20ff1f8d5d34cb01d58aa334dc46a6644e6e1d12 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/2378c3f1206f20711468391ce739116ffe58374b b/test/core/end2end/fuzzers/api_fuzzer_corpus/2378c3f1206f20711468391ce739116ffe58374b
new file mode 100644
index 0000000000000000000000000000000000000000..ca3ce31cbe4d2f5cb8b0ac606ad2d411b042edc1
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/2378c3f1206f20711468391ce739116ffe58374b differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/40640a91fda4e4e42d3063a28b9ffbba1b8c3701 b/test/core/end2end/fuzzers/api_fuzzer_corpus/40640a91fda4e4e42d3063a28b9ffbba1b8c3701
new file mode 100644
index 0000000000000000000000000000000000000000..f11821acc9a961be2847021d76fabc0f07f5bea6
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/40640a91fda4e4e42d3063a28b9ffbba1b8c3701 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/49f564289c79de9e0342f8b0821a167bc8c5ec00 b/test/core/end2end/fuzzers/api_fuzzer_corpus/49f564289c79de9e0342f8b0821a167bc8c5ec00
new file mode 100644
index 0000000000000000000000000000000000000000..c846377f0ae42e68a1ee99755acf49c31f2ff973
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/49f564289c79de9e0342f8b0821a167bc8c5ec00 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/507865c4a5ce880b80400d93fa85def2682581cb b/test/core/end2end/fuzzers/api_fuzzer_corpus/507865c4a5ce880b80400d93fa85def2682581cb
new file mode 100644
index 0000000000000000000000000000000000000000..c4473eacbb20789a2fb08b49a3affb734ee8e81f
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/507865c4a5ce880b80400d93fa85def2682581cb differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/5f2fdb01d8ff632803ca2b732a7c088c6843d7d3 b/test/core/end2end/fuzzers/api_fuzzer_corpus/5f2fdb01d8ff632803ca2b732a7c088c6843d7d3
new file mode 100644
index 0000000000000000000000000000000000000000..b91a052534a42714136e08b8481e1cc6604783b1
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/5f2fdb01d8ff632803ca2b732a7c088c6843d7d3 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/61de97a9d6c4b082602c02277d8d763921f5f95b b/test/core/end2end/fuzzers/api_fuzzer_corpus/61de97a9d6c4b082602c02277d8d763921f5f95b
new file mode 100644
index 0000000000000000000000000000000000000000..a24008b1d47bcdfc98496cf9cf1243ae1791a920
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/61de97a9d6c4b082602c02277d8d763921f5f95b differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/62b039b8a318cc08471f13629da08c68c414d8e7 b/test/core/end2end/fuzzers/api_fuzzer_corpus/62b039b8a318cc08471f13629da08c68c414d8e7
new file mode 100644
index 0000000000000000000000000000000000000000..e5e218acc372b72f89d20710875377e0a1ff8da9
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/62b039b8a318cc08471f13629da08c68c414d8e7 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/641739453f7d4d3b55a1c7b79bed7da6dfd62ae0 b/test/core/end2end/fuzzers/api_fuzzer_corpus/641739453f7d4d3b55a1c7b79bed7da6dfd62ae0
new file mode 100644
index 0000000000000000000000000000000000000000..9dcc5c578cb20776ad4bfac86d5cc7a76bd13a14
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/641739453f7d4d3b55a1c7b79bed7da6dfd62ae0 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/6589505362ffb5164a3c7cb1b9feadcddfba44e9 b/test/core/end2end/fuzzers/api_fuzzer_corpus/6589505362ffb5164a3c7cb1b9feadcddfba44e9
new file mode 100644
index 0000000000000000000000000000000000000000..10625da2fffb65152c5a7f18ff8bbd67053deeeb
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/6589505362ffb5164a3c7cb1b9feadcddfba44e9 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/724f5400f19e5a0be97022341c39eeaaaffeb390 b/test/core/end2end/fuzzers/api_fuzzer_corpus/724f5400f19e5a0be97022341c39eeaaaffeb390
new file mode 100644
index 0000000000000000000000000000000000000000..1dbc5381926e4910170eb1d57ed0c71f72491987
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/724f5400f19e5a0be97022341c39eeaaaffeb390 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/7254b9ff59ab3fcf345effdabbc25ebd2e907b23 b/test/core/end2end/fuzzers/api_fuzzer_corpus/7254b9ff59ab3fcf345effdabbc25ebd2e907b23
new file mode 100644
index 0000000000000000000000000000000000000000..92371e031fd5a3a16eb92dcc275eff45ee322ad8
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/7254b9ff59ab3fcf345effdabbc25ebd2e907b23 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/784d6f5c093ab5360670173ce001e1a446f95822 b/test/core/end2end/fuzzers/api_fuzzer_corpus/784d6f5c093ab5360670173ce001e1a446f95822
new file mode 100644
index 0000000000000000000000000000000000000000..79885b7f2a89ad24923574be3e4f35e300028aee
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/784d6f5c093ab5360670173ce001e1a446f95822 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/79328fdc89d0af0e38da089dab062fd3ea5aae59 b/test/core/end2end/fuzzers/api_fuzzer_corpus/79328fdc89d0af0e38da089dab062fd3ea5aae59
new file mode 100644
index 0000000000000000000000000000000000000000..4a5cbfd65419fde6e67ac0ecfb636e9cfd70d253
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/79328fdc89d0af0e38da089dab062fd3ea5aae59 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/7a2569f4daf4480ad142cb4ee7c675bed82db74c b/test/core/end2end/fuzzers/api_fuzzer_corpus/7a2569f4daf4480ad142cb4ee7c675bed82db74c
new file mode 100644
index 0000000000000000000000000000000000000000..75b55e7e107328f55f696cc1053637e8f9b78ed8
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/7a2569f4daf4480ad142cb4ee7c675bed82db74c differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/87a300cd25d2e57745bd00499d4d2352a10a2fa1 b/test/core/end2end/fuzzers/api_fuzzer_corpus/87a300cd25d2e57745bd00499d4d2352a10a2fa1
new file mode 100644
index 0000000000000000000000000000000000000000..c8f79862bc3c1721fbcccce5198bfe48ea3d3eec
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/87a300cd25d2e57745bd00499d4d2352a10a2fa1 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/8a1c629910280f8beebb88687696de98da988ecc b/test/core/end2end/fuzzers/api_fuzzer_corpus/8a1c629910280f8beebb88687696de98da988ecc
new file mode 100644
index 0000000000000000000000000000000000000000..ce3377fdc1e06745926690b5e3dbc5d7a5020e28
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/8a1c629910280f8beebb88687696de98da988ecc differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/8ff7b1568d2da2e4196c2e28617e1911d62021e6 b/test/core/end2end/fuzzers/api_fuzzer_corpus/8ff7b1568d2da2e4196c2e28617e1911d62021e6
new file mode 100644
index 0000000000000000000000000000000000000000..321520b870744d2441f1883a61d3337178b91fc3
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/8ff7b1568d2da2e4196c2e28617e1911d62021e6 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/982f375b183d984958667461c7767206062eb4cb b/test/core/end2end/fuzzers/api_fuzzer_corpus/982f375b183d984958667461c7767206062eb4cb
new file mode 100644
index 0000000000000000000000000000000000000000..01d07d64087e8f4ad4c927b72adff6746ad91a4b
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/982f375b183d984958667461c7767206062eb4cb differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/98739af631223fa05ad88f1e63a52052a9bb4b1c b/test/core/end2end/fuzzers/api_fuzzer_corpus/98739af631223fa05ad88f1e63a52052a9bb4b1c
new file mode 100644
index 0000000000000000000000000000000000000000..a4adb7aee6aed697073e4b761c17810c122928c5
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/98739af631223fa05ad88f1e63a52052a9bb4b1c differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/a78bca1ef8829d720dd5dedac3a9d4d12684da34 b/test/core/end2end/fuzzers/api_fuzzer_corpus/a78bca1ef8829d720dd5dedac3a9d4d12684da34
new file mode 100644
index 0000000000000000000000000000000000000000..322d4ae019867b64b09253c957976c40ad9122cd
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/a78bca1ef8829d720dd5dedac3a9d4d12684da34 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/ac763d89466ebfad676e75be0076831c03fe2a5d b/test/core/end2end/fuzzers/api_fuzzer_corpus/ac763d89466ebfad676e75be0076831c03fe2a5d
new file mode 100644
index 0000000000000000000000000000000000000000..64c9720c9b9d11d6183076147266f1ea1cd0115f
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/ac763d89466ebfad676e75be0076831c03fe2a5d differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/b165523f699e6ae9b2ad15873b9d56465d1af546 b/test/core/end2end/fuzzers/api_fuzzer_corpus/b165523f699e6ae9b2ad15873b9d56465d1af546
new file mode 100644
index 0000000000000000000000000000000000000000..b0b7452924d35154493a18fe3db2c24b524d5e17
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/b165523f699e6ae9b2ad15873b9d56465d1af546 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/b2a5ec3ef40c68d594638218e3c943a479f82215 b/test/core/end2end/fuzzers/api_fuzzer_corpus/b2a5ec3ef40c68d594638218e3c943a479f82215
new file mode 100644
index 0000000000000000000000000000000000000000..ac2b15440728cb62704432ed9b39919cb62f8bce
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/b2a5ec3ef40c68d594638218e3c943a479f82215 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/b92b1c5e0dba009a9516e7185f5df019c62c5cc9 b/test/core/end2end/fuzzers/api_fuzzer_corpus/b92b1c5e0dba009a9516e7185f5df019c62c5cc9
new file mode 100644
index 0000000000000000000000000000000000000000..3d542f38ec941b8daf69e53a4efdc309ce989ba2
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/b92b1c5e0dba009a9516e7185f5df019c62c5cc9 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/c5cd7af16e7bc0049445d5a0b92a3d4b7e5e3533 b/test/core/end2end/fuzzers/api_fuzzer_corpus/c5cd7af16e7bc0049445d5a0b92a3d4b7e5e3533
new file mode 100644
index 0000000000000000000000000000000000000000..c8fca17ad38310b58ac412d25d5a72f963576097
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/c5cd7af16e7bc0049445d5a0b92a3d4b7e5e3533 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/crash-da39a3ee5e6b4b0d3255bfef95601890afd80709 b/test/core/end2end/fuzzers/api_fuzzer_corpus/crash-da39a3ee5e6b4b0d3255bfef95601890afd80709
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/e28ffd8c2816f12f6395805199c792d1718191df b/test/core/end2end/fuzzers/api_fuzzer_corpus/e28ffd8c2816f12f6395805199c792d1718191df
new file mode 100644
index 0000000000000000000000000000000000000000..a867a8b0be95755dba75d7a18e644d00086fbdc0
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/e28ffd8c2816f12f6395805199c792d1718191df differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/ec823f4018389e64a99f6580277fba28df6bd136 b/test/core/end2end/fuzzers/api_fuzzer_corpus/ec823f4018389e64a99f6580277fba28df6bd136
new file mode 100644
index 0000000000000000000000000000000000000000..2e6d409d39acb6f548711ddb81a46519a8061ab5
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/ec823f4018389e64a99f6580277fba28df6bd136 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/ee2c1ac1e668f22836cf25a59495e778b0e2c7a8 b/test/core/end2end/fuzzers/api_fuzzer_corpus/ee2c1ac1e668f22836cf25a59495e778b0e2c7a8
new file mode 100644
index 0000000000000000000000000000000000000000..f1990ac2315efdb5d9d0f2ee1dd090355d4811d6
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/ee2c1ac1e668f22836cf25a59495e778b0e2c7a8 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/ee6855178435d2046d8763ecae46e1e0a71a95f4 b/test/core/end2end/fuzzers/api_fuzzer_corpus/ee6855178435d2046d8763ecae46e1e0a71a95f4
new file mode 100644
index 0000000000000000000000000000000000000000..6badaf36dc1c14580d3f6e18eb7e69a02908d72a
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/ee6855178435d2046d8763ecae46e1e0a71a95f4 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/eeb310d91038cb02862e187e68c5d6578233485b b/test/core/end2end/fuzzers/api_fuzzer_corpus/eeb310d91038cb02862e187e68c5d6578233485b
new file mode 100644
index 0000000000000000000000000000000000000000..1b05cfb0367b6c63aa783bc2a6862382235d20df
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/eeb310d91038cb02862e187e68c5d6578233485b differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/f07bc2907c95f2aeb79ca60e2ad826e13b848f45 b/test/core/end2end/fuzzers/api_fuzzer_corpus/f07bc2907c95f2aeb79ca60e2ad826e13b848f45
new file mode 100644
index 0000000000000000000000000000000000000000..313d45626282eb3ad2035cad2187980293664282
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/f07bc2907c95f2aeb79ca60e2ad826e13b848f45 differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/fb655905396b768cf3ff015afdb0b564dae2cdfd b/test/core/end2end/fuzzers/api_fuzzer_corpus/fb655905396b768cf3ff015afdb0b564dae2cdfd
new file mode 100644
index 0000000000000000000000000000000000000000..b02dabe03a80f708a041d18d13df473b2d7a97b5
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/fb655905396b768cf3ff015afdb0b564dae2cdfd differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/fdb038087233cd176746558875932029f779221d b/test/core/end2end/fuzzers/api_fuzzer_corpus/fdb038087233cd176746558875932029f779221d
new file mode 100644
index 0000000000000000000000000000000000000000..017fdbf661af95f27f13ae0a13ef31711144b563
Binary files /dev/null and b/test/core/end2end/fuzzers/api_fuzzer_corpus/fdb038087233cd176746558875932029f779221d differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer.c b/test/core/end2end/fuzzers/client_fuzzer.c
index c5260cd2878e652012ca6e30e3e6c35bfb702a39..26b520885bb1fd6f4a74df77f64c08d5508df0f6 100644
--- a/test/core/end2end/fuzzers/client_fuzzer.c
+++ b/test/core/end2end/fuzzers/client_fuzzer.c
@@ -62,7 +62,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
       grpc_resource_quota_create("client_fuzzer");
   grpc_endpoint *mock_endpoint =
       grpc_mock_endpoint_create(discard_write, resource_quota);
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+  grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
 
   grpc_completion_queue *cq = grpc_completion_queue_create(NULL);
   grpc_transport *transport =
diff --git a/test/core/end2end/fuzzers/server_fuzzer.c b/test/core/end2end/fuzzers/server_fuzzer.c
index 164022ec79ed7533a23933699aabe43cf8a47977..115fb0692526c6a2f182b40a8caee59299b0922a 100644
--- a/test/core/end2end/fuzzers/server_fuzzer.c
+++ b/test/core/end2end/fuzzers/server_fuzzer.c
@@ -60,7 +60,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
       grpc_resource_quota_create("server_fuzzer");
   grpc_endpoint *mock_endpoint =
       grpc_mock_endpoint_create(discard_write, resource_quota);
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+  grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
   grpc_mock_endpoint_put_read(
       &exec_ctx, mock_endpoint,
       grpc_slice_from_copied_buffer((const char *)data, size));
diff --git a/test/core/end2end/generate_tests.bzl b/test/core/end2end/generate_tests.bzl
new file mode 100755
index 0000000000000000000000000000000000000000..ed1ba3eea9959a4b4e301c0f0478407c1482cdef
--- /dev/null
+++ b/test/core/end2end/generate_tests.bzl
@@ -0,0 +1,192 @@
+#!/usr/bin/env python2.7
+# 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.
+
+
+"""Generates the appropriate build.json data for all the end2end tests."""
+
+
+def fixture_options(fullstack=True, includes_proxy=False, dns_resolver=True,
+                    secure=True, tracing=False,
+                    platforms=['windows', 'linux', 'mac', 'posix']):
+  return struct(
+    fullstack=fullstack,
+    includes_proxy=includes_proxy,
+    dns_resolver=dns_resolver,
+    secure=secure,
+    tracing=tracing,
+    #platforms=platforms
+  )
+
+
+# maps fixture name to whether it requires the security library
+END2END_FIXTURES = {
+    'h2_compress': fixture_options(),
+    'h2_census': fixture_options(),
+    'h2_load_reporting': fixture_options(),
+    'h2_fakesec': fixture_options(),
+    'h2_fd': fixture_options(dns_resolver=False, fullstack=False,
+                             platforms=['linux', 'mac', 'posix']),
+    'h2_full': fixture_options(),
+    'h2_full+pipe': fixture_options(platforms=['linux']),
+    'h2_full+trace': fixture_options(tracing=True),
+    'h2_http_proxy': fixture_options(),
+    'h2_oauth2': fixture_options(),
+    'h2_proxy': fixture_options(includes_proxy=True),
+    'h2_sockpair_1byte': fixture_options(fullstack=False, dns_resolver=False),
+    'h2_sockpair': fixture_options(fullstack=False, dns_resolver=False),
+    'h2_sockpair+trace': fixture_options(fullstack=False, dns_resolver=False,
+                                         tracing=True),
+    'h2_ssl': fixture_options(secure=True),
+    'h2_ssl_cert': fixture_options(secure=True),
+    'h2_ssl_proxy': fixture_options(secure=True),
+    'h2_uds': fixture_options(dns_resolver=False,
+                              platforms=['linux', 'mac', 'posix']),
+}
+
+
+def test_options(needs_fullstack=False, needs_dns=False, proxyable=True,
+                 secure=False, traceable=False):
+  return struct(
+    needs_fullstack=needs_fullstack,
+    needs_dns=needs_dns,
+    proxyable=proxyable,
+    secure=secure,
+    traceable=traceable
+  )
+
+
+# maps test names to options
+END2END_TESTS = {
+    'bad_hostname': test_options(),
+    'binary_metadata': test_options(),
+    'resource_quota_server': test_options(proxyable=False),
+    'call_creds': test_options(secure=True),
+    'cancel_after_accept': test_options(),
+    'cancel_after_client_done': test_options(),
+    'cancel_after_invoke': test_options(),
+    'cancel_before_invoke': test_options(),
+    'cancel_in_a_vacuum': test_options(),
+    'cancel_with_status': test_options(),
+    'compressed_payload': test_options(),
+    'connectivity': test_options(needs_fullstack=True, proxyable=False),
+    'default_host': test_options(needs_fullstack=True, needs_dns=True),
+    'disappearing_server': test_options(needs_fullstack=True),
+    'empty_batch': test_options(),
+    'filter_causes_close': test_options(),
+    'filter_call_init_fails': test_options(),
+    'graceful_server_shutdown': test_options(),
+    'hpack_size': test_options(proxyable=False, traceable=False),
+    'high_initial_seqno': test_options(),
+    'idempotent_request': test_options(),
+    'invoke_large_request': test_options(),
+    'large_metadata': test_options(),
+    'max_concurrent_streams': test_options(proxyable=False),
+    'max_message_length': test_options(),
+    'negative_deadline': test_options(),
+    'network_status_change': test_options(),
+    'no_logging': test_options(traceable=False),
+    'no_op': test_options(),
+    'payload': test_options(),
+    'load_reporting_hook': test_options(),
+    'ping_pong_streaming': test_options(),
+    'ping': test_options(proxyable=False),
+    'registered_call': test_options(),
+    'request_with_flags': test_options(proxyable=False),
+    'request_with_payload': test_options(),
+    'server_finishes_request': test_options(),
+    'shutdown_finishes_calls': test_options(),
+    'shutdown_finishes_tags': test_options(),
+    'simple_cacheable_request': test_options(),
+    'simple_delayed_request': test_options(needs_fullstack=True),
+    'simple_metadata': test_options(),
+    'simple_request': test_options(),
+    'streaming_error_response': test_options(),
+    'trailing_metadata': test_options(),
+    'authority_not_supported': test_options(),
+    'filter_latency': test_options(),
+}
+
+
+def compatible(fopt, topt):
+  if topt.needs_fullstack:
+    if not fopt.fullstack:
+      return False
+  if topt.needs_dns:
+    if not fopt.dns_resolver:
+      return False
+  if not topt.proxyable:
+    if fopt.includes_proxy:
+      return False
+  if not topt.traceable:
+    if fopt.tracing:
+      return False
+  return True
+
+
+def grpc_end2end_tests():
+  native.cc_library(
+    name = 'end2end_tests',
+    srcs = ['end2end_tests.c', 'end2end_test_utils.c'] + [
+             'tests/%s.c' % t
+             for t in sorted(END2END_TESTS.keys())],
+    hdrs = [
+      'tests/cancel_test_helpers.h',
+      'end2end_tests.h'
+    ],
+    copts = ['-std=c99'],
+    deps = [
+      ':cq_verifier',
+      ':ssl_test_data',
+      ':fake_resolver',
+      ':http_proxy',
+      ':proxy',
+      '//test/core/util:grpc_test_util',
+      '//:grpc',
+      '//test/core/util:gpr_test_util',
+      '//:gpr',
+    ]
+  )
+
+  for f, fopt in END2END_FIXTURES.items():
+    native.cc_binary(
+      name = '%s_test' % f,
+      srcs = ['fixtures/%s.c' % f],
+      copts = ['-std=c99'],
+      deps = [':end2end_tests']
+    )
+    for t, topt in END2END_TESTS.items():
+      #print(compatible(fopt, topt), f, t, fopt, topt)
+      if not compatible(fopt, topt): continue
+      native.sh_test(
+        name = '%s_test@%s' % (f, t),
+        srcs = ['end2end_test.sh'],
+        args = ['$(location %s_test)' % f, t],
+        data = [':%s_test' % f],
+      )
diff --git a/test/core/end2end/goaway_server_test.c b/test/core/end2end/goaway_server_test.c
index a8c7b2be5ac41bb58adb2e2b8b2e1cfdd4691c7e..cd68b390bbb746e76c4b2ebef8cc940e644f8dd3 100644
--- a/test/core/end2end/goaway_server_test.c
+++ b/test/core/end2end/goaway_server_test.c
@@ -126,8 +126,16 @@ int main(int argc, char **argv) {
 
   char *addr;
 
+  grpc_channel_args client_args;
+  grpc_arg arg_array[1];
+  arg_array[0].type = GRPC_ARG_INTEGER;
+  arg_array[0].key = "grpc.testing.fixed_reconnect_backoff_ms";
+  arg_array[0].value.integer = 1000;
+  client_args.args = arg_array;
+  client_args.num_args = 1;
+
   /* create a channel that picks first amongst the servers */
-  grpc_channel *chan = grpc_insecure_channel_create("test", NULL, NULL);
+  grpc_channel *chan = grpc_insecure_channel_create("test", &client_args, NULL);
   /* and an initial call to them */
   grpc_call *call1 = grpc_channel_create_call(
       chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq, "/foo", "127.0.0.1",
diff --git a/test/core/end2end/invalid_call_argument_test.c b/test/core/end2end/invalid_call_argument_test.c
index 765b6ad1bee8ab67418a4086d13ad65f323ece01..d974d2c8ff9ab048963cffbdab941541e7992506 100644
--- a/test/core/end2end/invalid_call_argument_test.c
+++ b/test/core/end2end/invalid_call_argument_test.c
@@ -573,6 +573,29 @@ static void test_recv_close_on_server_twice() {
   cleanup_test();
 }
 
+static void test_invalid_initial_metadata_reserved_key() {
+  gpr_log(GPR_INFO, "test_invalid_initial_metadata_reserved_key");
+
+  grpc_metadata metadata;
+  metadata.key = ":start_with_colon";
+  metadata.value = "value";
+  metadata.value_length = 6;
+
+  grpc_op *op;
+  prepare_test(1);
+  op = g_state.ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 1;
+  op->data.send_initial_metadata.metadata = &metadata;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  GPR_ASSERT(GRPC_CALL_ERROR_INVALID_METADATA ==
+             grpc_call_start_batch(g_state.call, g_state.ops,
+                                   (size_t)(op - g_state.ops), tag(1), NULL));
+  cleanup_test();
+}
+
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
   grpc_init();
@@ -595,6 +618,7 @@ int main(int argc, char **argv) {
   test_send_server_status_twice();
   test_recv_close_on_server_with_invalid_flags();
   test_recv_close_on_server_twice();
+  test_invalid_initial_metadata_reserved_key();
   grpc_shutdown();
 
   return 0;
diff --git a/test/core/end2end/tests/cancel_after_accept.c b/test/core/end2end/tests/cancel_after_accept.c
index e582c59f2d37de76a1db72b79804befff7ca7a2f..a8e310c683540e4c7d5fff09345eb66e0767f6b5 100644
--- a/test/core/end2end/tests/cancel_after_accept.c
+++ b/test/core/end2end/tests/cancel_after_accept.c
@@ -251,7 +251,11 @@ static void test_cancel_after_accept(grpc_end2end_test_config config,
   grpc_call_destroy(c);
   grpc_call_destroy(s);
 
-  if (args != NULL) grpc_channel_args_destroy(args);
+  if (args != NULL) {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_channel_args_destroy(&exec_ctx, args);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
 
   cq_verifier_destroy(cqv);
   end_test(&f);
diff --git a/test/core/end2end/tests/compressed_payload.c b/test/core/end2end/tests/compressed_payload.c
index 81fc7091f80d31ee027acfd7094dd2f0f4981dd7..d7efe7747bfec71f314c1b979fcaf1700ce0c11a 100644
--- a/test/core/end2end/tests/compressed_payload.c
+++ b/test/core/end2end/tests/compressed_payload.c
@@ -140,8 +140,12 @@ static void request_for_disabled_algorithm(
       NULL, requested_client_compression_algorithm);
   server_args =
       grpc_channel_args_set_compression_algorithm(NULL, GRPC_COMPRESS_NONE);
-  server_args = grpc_channel_args_compression_algorithm_set_state(
-      &server_args, algorithm_to_disable, false);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    server_args = grpc_channel_args_compression_algorithm_set_state(
+        &exec_ctx, &server_args, algorithm_to_disable, false);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
 
   f = begin_test(config, test_name, client_args, server_args);
   cqv = cq_verifier_create(f.cq);
@@ -262,8 +266,12 @@ static void request_for_disabled_algorithm(
   grpc_byte_buffer_destroy(request_payload);
   grpc_byte_buffer_destroy(request_payload_recv);
 
-  grpc_channel_args_destroy(client_args);
-  grpc_channel_args_destroy(server_args);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_channel_args_destroy(&exec_ctx, client_args);
+    grpc_channel_args_destroy(&exec_ctx, server_args);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
 
   end_test(&f);
   config.tear_down_data(&f);
@@ -512,8 +520,12 @@ static void request_with_payload_template(
 
   cq_verifier_destroy(cqv);
 
-  grpc_channel_args_destroy(client_args);
-  grpc_channel_args_destroy(server_args);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_channel_args_destroy(&exec_ctx, client_args);
+    grpc_channel_args_destroy(&exec_ctx, server_args);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
 
   end_test(&f);
   config.tear_down_data(&f);
diff --git a/test/core/end2end/tests/connectivity.c b/test/core/end2end/tests/connectivity.c
index 260297ebd4d12a4791146d90b6025675d7377182..42475c7331d4a45cb808738aac586a8918b09333 100644
--- a/test/core/end2end/tests/connectivity.c
+++ b/test/core/end2end/tests/connectivity.c
@@ -68,7 +68,15 @@ static void test_connectivity(grpc_end2end_test_config config) {
   gpr_thd_options thdopt = gpr_thd_options_default();
   gpr_thd_id thdid;
 
-  config.init_client(&f, NULL);
+  grpc_channel_args client_args;
+  grpc_arg arg_array[1];
+  arg_array[0].type = GRPC_ARG_INTEGER;
+  arg_array[0].key = "grpc.testing.fixed_reconnect_backoff_ms";
+  arg_array[0].value.integer = 1000;
+  client_args.args = arg_array;
+  client_args.num_args = 1;
+
+  config.init_client(&f, &client_args);
 
   ce.channel = f.client;
   ce.cq = f.cq;
diff --git a/test/core/end2end/tests/filter_call_init_fails.c b/test/core/end2end/tests/filter_call_init_fails.c
index 41ae575fff455073b3bec6095d141eae8b209cdb..ac9d2dd2fe8eafcb5f6da88f49fee50c581eb2c1 100644
--- a/test/core/end2end/tests/filter_call_init_fails.c
+++ b/test/core/end2end/tests/filter_call_init_fails.c
@@ -216,9 +216,11 @@ static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                               const grpc_call_final_info *final_info,
                               void *and_free_memory) {}
 
-static void init_channel_elem(grpc_exec_ctx *exec_ctx,
-                              grpc_channel_element *elem,
-                              grpc_channel_element_args *args) {}
+static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx,
+                                     grpc_channel_element *elem,
+                                     grpc_channel_element_args *args) {
+  return GRPC_ERROR_NONE;
+}
 
 static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
                                  grpc_channel_element *elem) {}
@@ -241,7 +243,8 @@ static const grpc_channel_filter test_filter = {
  * Registration
  */
 
-static bool maybe_add_filter(grpc_channel_stack_builder *builder, void *arg) {
+static bool maybe_add_filter(grpc_exec_ctx *exec_ctx,
+                             grpc_channel_stack_builder *builder, void *arg) {
   if (g_enable_filter) {
     // Want to add the filter as close to the end as possible, to make
     // sure that all of the filters work well together.  However, we
diff --git a/test/core/end2end/tests/filter_causes_close.c b/test/core/end2end/tests/filter_causes_close.c
index bf9fd9073d17eb239798817c38cecfd5662be185..45f623c5c7b6d7a465566915186f3fb4d2055ca9 100644
--- a/test/core/end2end/tests/filter_causes_close.c
+++ b/test/core/end2end/tests/filter_causes_close.c
@@ -213,13 +213,13 @@ static void recv_im_ready(grpc_exec_ctx *exec_ctx, void *arg,
     grpc_slice message =
         grpc_slice_from_copied_string("Failure that's not preventable.");
     grpc_transport_stream_op *op = grpc_make_transport_stream_op(NULL);
-    grpc_transport_stream_op_add_close(op, GRPC_STATUS_PERMISSION_DENIED,
-                                       &message);
+    grpc_transport_stream_op_add_close(exec_ctx, op,
+                                       GRPC_STATUS_PERMISSION_DENIED, &message);
     grpc_call_next_op(exec_ctx, elem, op);
   }
-  grpc_exec_ctx_sched(
+  grpc_closure_sched(
       exec_ctx, calld->recv_im_ready,
-      GRPC_ERROR_CREATE_REFERENCING("Forced call to close", &error, 1), NULL);
+      GRPC_ERROR_CREATE_REFERENCING("Forced call to close", &error, 1));
 }
 
 static void start_transport_stream_op(grpc_exec_ctx *exec_ctx,
@@ -228,7 +228,8 @@ static void start_transport_stream_op(grpc_exec_ctx *exec_ctx,
   call_data *calld = elem->call_data;
   if (op->recv_initial_metadata != NULL) {
     calld->recv_im_ready = op->recv_initial_metadata_ready;
-    op->recv_initial_metadata_ready = grpc_closure_create(recv_im_ready, elem);
+    op->recv_initial_metadata_ready =
+        grpc_closure_create(recv_im_ready, elem, grpc_schedule_on_exec_ctx);
   }
   grpc_call_next_op(exec_ctx, elem, op);
 }
@@ -243,9 +244,11 @@ static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                               const grpc_call_final_info *final_info,
                               void *and_free_memory) {}
 
-static void init_channel_elem(grpc_exec_ctx *exec_ctx,
-                              grpc_channel_element *elem,
-                              grpc_channel_element_args *args) {}
+static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx,
+                                     grpc_channel_element *elem,
+                                     grpc_channel_element_args *args) {
+  return GRPC_ERROR_NONE;
+}
 
 static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
                                  grpc_channel_element *elem) {}
@@ -268,7 +271,8 @@ static const grpc_channel_filter test_filter = {
  * Registration
  */
 
-static bool maybe_add_filter(grpc_channel_stack_builder *builder, void *arg) {
+static bool maybe_add_filter(grpc_exec_ctx *exec_ctx,
+                             grpc_channel_stack_builder *builder, void *arg) {
   if (g_enable_filter) {
     return grpc_channel_stack_builder_prepend_filter(builder, &test_filter,
                                                      NULL, NULL);
diff --git a/test/core/end2end/tests/filter_latency.c b/test/core/end2end/tests/filter_latency.c
index ea63d4542085d7af2730a067eafd77667fddeea4..dbb5c8dcd9d82e4607b6a35d1668d433da74993d 100644
--- a/test/core/end2end/tests/filter_latency.c
+++ b/test/core/end2end/tests/filter_latency.c
@@ -281,9 +281,11 @@ static void server_destroy_call_elem(grpc_exec_ctx *exec_ctx,
   gpr_mu_unlock(&g_mu);
 }
 
-static void init_channel_elem(grpc_exec_ctx *exec_ctx,
-                              grpc_channel_element *elem,
-                              grpc_channel_element_args *args) {}
+static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx,
+                                     grpc_channel_element *elem,
+                                     grpc_channel_element_args *args) {
+  return GRPC_ERROR_NONE;
+}
 
 static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
                                  grpc_channel_element *elem) {}
@@ -320,7 +322,8 @@ static const grpc_channel_filter test_server_filter = {
  * Registration
  */
 
-static bool maybe_add_filter(grpc_channel_stack_builder *builder, void *arg) {
+static bool maybe_add_filter(grpc_exec_ctx *exec_ctx,
+                             grpc_channel_stack_builder *builder, void *arg) {
   grpc_channel_filter *filter = arg;
   if (g_enable_filter) {
     // Want to add the filter as close to the end as possible, to make
diff --git a/test/core/end2end/tests/load_reporting_hook.c b/test/core/end2end/tests/load_reporting_hook.c
index 5b75f26bd20eb8aa806f096443b7f2de7359d940..ae5c27062990d4ece37a356977528317dca7504d 100644
--- a/test/core/end2end/tests/load_reporting_hook.c
+++ b/test/core/end2end/tests/load_reporting_hook.c
@@ -311,7 +311,11 @@ static void test_load_reporting_hook(grpc_end2end_test_config config) {
                                 response_msg, &initial_lr_metadata,
                                 &trailing_lr_metadata);
   end_test(&f);
-  grpc_channel_args_destroy(lr_server_args);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_channel_args_destroy(&exec_ctx, lr_server_args);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
   config.tear_down_data(&f);
 }
 
diff --git a/test/core/end2end/tests/max_message_length.c b/test/core/end2end/tests/max_message_length.c
index c222d9dfae7053ae58b3fa32b5edcca614ab6fd7..4a6ef44bb323d69c4f5668f0c397d2b2d73b8a5c 100644
--- a/test/core/end2end/tests/max_message_length.c
+++ b/test/core/end2end/tests/max_message_length.c
@@ -168,8 +168,12 @@ static void test_max_message_length_on_request(grpc_end2end_test_config config,
 
   f = begin_test(config, "test_max_request_message_length", client_args,
                  server_args);
-  if (client_args != NULL) grpc_channel_args_destroy(client_args);
-  if (server_args != NULL) grpc_channel_args_destroy(server_args);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    if (client_args != NULL) grpc_channel_args_destroy(&exec_ctx, client_args);
+    if (server_args != NULL) grpc_channel_args_destroy(&exec_ctx, server_args);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
 
   cqv = cq_verifier_create(f.cq);
 
@@ -342,9 +346,12 @@ static void test_max_message_length_on_response(grpc_end2end_test_config config,
 
   f = begin_test(config, "test_max_response_message_length", client_args,
                  server_args);
-  if (client_args != NULL) grpc_channel_args_destroy(client_args);
-  if (server_args != NULL) grpc_channel_args_destroy(server_args);
-
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    if (client_args != NULL) grpc_channel_args_destroy(&exec_ctx, client_args);
+    if (server_args != NULL) grpc_channel_args_destroy(&exec_ctx, server_args);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
   cqv = cq_verifier_create(f.cq);
 
   c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
diff --git a/test/core/end2end/tests/payload.c b/test/core/end2end/tests/payload.c
index db2e5c83de69703938374f1154ebc9d17e8dc726..4a88c5f2a816e260c451e84d112a669fe3eafad0 100644
--- a/test/core/end2end/tests/payload.c
+++ b/test/core/end2end/tests/payload.c
@@ -126,7 +126,7 @@ static void request_response_with_payload(grpc_end2end_test_config config,
       grpc_raw_byte_buffer_create(&request_payload_slice, 1);
   grpc_byte_buffer *response_payload =
       grpc_raw_byte_buffer_create(&response_payload_slice, 1);
-  gpr_timespec deadline = five_seconds_time();
+  gpr_timespec deadline = n_seconds_time(60);
   cq_verifier *cqv = cq_verifier_create(f.cq);
   grpc_op ops[6];
   grpc_op *op;
diff --git a/test/core/end2end/tests/simple_delayed_request.c b/test/core/end2end/tests/simple_delayed_request.c
index ec40c8f22dba4d0a6792225ef5b9ce1e32867103..414a03d98bb839c6b098dd4bf68a7ff19bc3fccf 100644
--- a/test/core/end2end/tests/simple_delayed_request.c
+++ b/test/core/end2end/tests/simple_delayed_request.c
@@ -200,21 +200,36 @@ static void simple_delayed_request_body(grpc_end2end_test_config config,
 
 static void test_simple_delayed_request_short(grpc_end2end_test_config config) {
   grpc_end2end_test_fixture f;
+  grpc_channel_args client_args;
+  grpc_arg arg_array[1];
+  arg_array[0].type = GRPC_ARG_INTEGER;
+  arg_array[0].key = "grpc.testing.fixed_reconnect_backoff_ms";
+  arg_array[0].value.integer = 1000;
+  client_args.args = arg_array;
+  client_args.num_args = 1;
 
   gpr_log(GPR_INFO, "%s/%s", "test_simple_delayed_request_short", config.name);
   f = config.create_fixture(NULL, NULL);
-  simple_delayed_request_body(config, &f, NULL, NULL, 100000);
+
+  simple_delayed_request_body(config, &f, &client_args, NULL, 100000);
   end_test(&f);
   config.tear_down_data(&f);
 }
 
 static void test_simple_delayed_request_long(grpc_end2end_test_config config) {
   grpc_end2end_test_fixture f;
+  grpc_channel_args client_args;
+  grpc_arg arg_array[1];
+  arg_array[0].type = GRPC_ARG_INTEGER;
+  arg_array[0].key = "grpc.testing.fixed_reconnect_backoff_ms";
+  arg_array[0].value.integer = 1000;
+  client_args.args = arg_array;
+  client_args.num_args = 1;
 
   gpr_log(GPR_INFO, "%s/%s", "test_simple_delayed_request_long", config.name);
   f = config.create_fixture(NULL, NULL);
   /* This timeout should be longer than a single retry */
-  simple_delayed_request_body(config, &f, NULL, NULL, 1500000);
+  simple_delayed_request_body(config, &f, &client_args, NULL, 1500000);
   end_test(&f);
   config.tear_down_data(&f);
 }
diff --git a/test/core/http/BUILD b/test/core/http/BUILD
new file mode 100644
index 0000000000000000000000000000000000000000..58d265bd8fb67edac1e262711be2c2a65fa1d70d
--- /dev/null
+++ b/test/core/http/BUILD
@@ -0,0 +1,47 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+load("//test/core/util:grpc_fuzzer.bzl", "grpc_fuzzer")
+
+grpc_fuzzer(
+  name = "response_fuzzer",
+  srcs = ["response_fuzzer.c"],
+  deps = ["//:gpr", "//:grpc", "//test/core/util:grpc_test_util"],
+  corpus = "response_corpus",
+  copts = ["-std=c99"],
+)
+
+grpc_fuzzer(
+  name = "request_fuzzer",
+  srcs = ["request_fuzzer.c"],
+  deps = ["//:gpr", "//:grpc", "//test/core/util:grpc_test_util"],
+  corpus = "request_corpus",
+  copts = ["-std=c99"],
+)
+
diff --git a/test/core/http/httpcli_test.c b/test/core/http/httpcli_test.c
index 3e312c1ddeefc3ab7abb3e59d54f6562a23fd003..4cdbb5d6da6855bdd71b56beb0d93bc56d95b720 100644
--- a/test/core/http/httpcli_test.c
+++ b/test/core/http/httpcli_test.c
@@ -90,10 +90,11 @@ static void test_get(int port) {
   grpc_http_response response;
   memset(&response, 0, sizeof(response));
   grpc_resource_quota *resource_quota = grpc_resource_quota_create("test_get");
-  grpc_httpcli_get(&exec_ctx, &g_context, &g_pops, resource_quota, &req,
-                   n_seconds_time(15),
-                   grpc_closure_create(on_finish, &response), &response);
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+  grpc_httpcli_get(
+      &exec_ctx, &g_context, &g_pops, resource_quota, &req, n_seconds_time(15),
+      grpc_closure_create(on_finish, &response, grpc_schedule_on_exec_ctx),
+      &response);
+  grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
   gpr_mu_lock(g_mu);
   while (!g_done) {
     grpc_pollset_worker *worker = NULL;
@@ -130,10 +131,12 @@ static void test_post(int port) {
   grpc_http_response response;
   memset(&response, 0, sizeof(response));
   grpc_resource_quota *resource_quota = grpc_resource_quota_create("test_post");
-  grpc_httpcli_post(&exec_ctx, &g_context, &g_pops, resource_quota, &req,
-                    "hello", 5, n_seconds_time(15),
-                    grpc_closure_create(on_finish, &response), &response);
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+  grpc_httpcli_post(
+      &exec_ctx, &g_context, &g_pops, resource_quota, &req, "hello", 5,
+      n_seconds_time(15),
+      grpc_closure_create(on_finish, &response, grpc_schedule_on_exec_ctx),
+      &response);
+  grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
   gpr_mu_lock(g_mu);
   while (!g_done) {
     grpc_pollset_worker *worker = NULL;
@@ -207,7 +210,8 @@ int main(int argc, char **argv) {
   test_post(port);
 
   grpc_httpcli_context_destroy(&g_context);
-  grpc_closure_init(&destroyed, destroy_pops, &g_pops);
+  grpc_closure_init(&destroyed, destroy_pops, &g_pops,
+                    grpc_schedule_on_exec_ctx);
   grpc_pollset_shutdown(&exec_ctx, grpc_polling_entity_pollset(&g_pops),
                         &destroyed);
   grpc_exec_ctx_finish(&exec_ctx);
diff --git a/test/core/http/httpscli_test.c b/test/core/http/httpscli_test.c
index d06035149ef0379cc4fe9336ecf9a478e9274a9b..72017cfa54e29b02682708464b623466267e3235 100644
--- a/test/core/http/httpscli_test.c
+++ b/test/core/http/httpscli_test.c
@@ -91,10 +91,11 @@ static void test_get(int port) {
   grpc_http_response response;
   memset(&response, 0, sizeof(response));
   grpc_resource_quota *resource_quota = grpc_resource_quota_create("test_get");
-  grpc_httpcli_get(&exec_ctx, &g_context, &g_pops, resource_quota, &req,
-                   n_seconds_time(15),
-                   grpc_closure_create(on_finish, &response), &response);
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+  grpc_httpcli_get(
+      &exec_ctx, &g_context, &g_pops, resource_quota, &req, n_seconds_time(15),
+      grpc_closure_create(on_finish, &response, grpc_schedule_on_exec_ctx),
+      &response);
+  grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
   gpr_mu_lock(g_mu);
   while (!g_done) {
     grpc_pollset_worker *worker = NULL;
@@ -132,10 +133,12 @@ static void test_post(int port) {
   grpc_http_response response;
   memset(&response, 0, sizeof(response));
   grpc_resource_quota *resource_quota = grpc_resource_quota_create("test_post");
-  grpc_httpcli_post(&exec_ctx, &g_context, &g_pops, resource_quota, &req,
-                    "hello", 5, n_seconds_time(15),
-                    grpc_closure_create(on_finish, &response), &response);
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+  grpc_httpcli_post(
+      &exec_ctx, &g_context, &g_pops, resource_quota, &req, "hello", 5,
+      n_seconds_time(15),
+      grpc_closure_create(on_finish, &response, grpc_schedule_on_exec_ctx),
+      &response);
+  grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
   gpr_mu_lock(g_mu);
   while (!g_done) {
     grpc_pollset_worker *worker = NULL;
@@ -210,7 +213,8 @@ int main(int argc, char **argv) {
   test_post(port);
 
   grpc_httpcli_context_destroy(&g_context);
-  grpc_closure_init(&destroyed, destroy_pops, &g_pops);
+  grpc_closure_init(&destroyed, destroy_pops, &g_pops,
+                    grpc_schedule_on_exec_ctx);
   grpc_pollset_shutdown(&exec_ctx, grpc_polling_entity_pollset(&g_pops),
                         &destroyed);
   grpc_exec_ctx_finish(&exec_ctx);
diff --git a/test/core/internal_api_canaries/iomgr.c b/test/core/internal_api_canaries/iomgr.c
index de03c47c13eb1890cb46a1649b74517efd73474f..3225b9dc198f0bd79ca254c21a3a5776bb7e2603 100644
--- a/test/core/internal_api_canaries/iomgr.c
+++ b/test/core/internal_api_canaries/iomgr.c
@@ -48,7 +48,7 @@
 static void test_code(void) {
   /* iomgr.h */
   grpc_iomgr_init();
-  grpc_iomgr_shutdown();
+  grpc_iomgr_shutdown(NULL);
 
   /* closure.h */
   grpc_closure closure;
@@ -60,9 +60,9 @@ static void test_code(void) {
   closure_list.head = NULL;
   closure_list.tail = NULL;
 
-  grpc_closure_init(&closure, NULL, NULL);
+  grpc_closure_init(&closure, NULL, NULL, grpc_schedule_on_exec_ctx);
 
-  grpc_closure_create(NULL, NULL);
+  grpc_closure_create(NULL, NULL, grpc_schedule_on_exec_ctx);
 
   grpc_closure_list_move(NULL, NULL);
   grpc_closure_list_append(NULL, NULL, GRPC_ERROR_CREATE("Foo"));
@@ -72,8 +72,8 @@ static void test_code(void) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_exec_ctx_flush(&exec_ctx);
   grpc_exec_ctx_finish(&exec_ctx);
-  grpc_exec_ctx_sched(&exec_ctx, &closure, GRPC_ERROR_CREATE("Foo"), NULL);
-  grpc_exec_ctx_enqueue_list(&exec_ctx, &closure_list, NULL);
+  grpc_closure_sched(&exec_ctx, &closure, GRPC_ERROR_CREATE("Foo"));
+  grpc_closure_list_sched(&exec_ctx, &closure_list);
 
   /* endpoint.h */
   grpc_endpoint endpoint;
@@ -99,8 +99,7 @@ static void test_code(void) {
 
   /* executor.h */
   grpc_executor_init();
-  grpc_executor_push(&closure, GRPC_ERROR_CREATE("Phi"));
-  grpc_executor_shutdown();
+  grpc_executor_shutdown(NULL);
 
   /* pollset.h */
   grpc_pollset_size();
diff --git a/test/core/iomgr/combiner_test.c b/test/core/iomgr/combiner_test.c
index f7d5809be74785eebe654a154695d95384980639..9b6d6ff9b463024e4dae48130d6724816bfddca3 100644
--- a/test/core/iomgr/combiner_test.c
+++ b/test/core/iomgr/combiner_test.c
@@ -59,9 +59,10 @@ static void test_execute_one(void) {
   grpc_combiner *lock = grpc_combiner_create(NULL);
   bool done = false;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_combiner_execute(&exec_ctx, lock,
-                        grpc_closure_create(set_bool_to_true, &done),
-                        GRPC_ERROR_NONE, false);
+  grpc_closure_sched(&exec_ctx,
+                     grpc_closure_create(set_bool_to_true, &done,
+                                         grpc_combiner_scheduler(lock, false)),
+                     GRPC_ERROR_NONE);
   grpc_exec_ctx_flush(&exec_ctx);
   GPR_ASSERT(done);
   grpc_combiner_destroy(&exec_ctx, lock);
@@ -94,9 +95,10 @@ static void execute_many_loop(void *a) {
       ex_args *c = gpr_malloc(sizeof(*c));
       c->ctr = &args->ctr;
       c->value = n++;
-      grpc_combiner_execute(&exec_ctx, args->lock,
-                            grpc_closure_create(check_one, c), GRPC_ERROR_NONE,
-                            false);
+      grpc_closure_sched(
+          &exec_ctx, grpc_closure_create(check_one, c, grpc_combiner_scheduler(
+                                                           args->lock, false)),
+          GRPC_ERROR_NONE);
       grpc_exec_ctx_flush(&exec_ctx);
     }
     // sleep for a little bit, to test a combiner draining and another thread
@@ -134,9 +136,10 @@ static void in_finally(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
 }
 
 static void add_finally(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
-  grpc_combiner_execute_finally(exec_ctx, arg,
-                                grpc_closure_create(in_finally, NULL),
-                                GRPC_ERROR_NONE, false);
+  grpc_closure_sched(exec_ctx, grpc_closure_create(
+                                   in_finally, NULL,
+                                   grpc_combiner_finally_scheduler(arg, false)),
+                     GRPC_ERROR_NONE);
 }
 
 static void test_execute_finally(void) {
@@ -144,8 +147,10 @@ static void test_execute_finally(void) {
 
   grpc_combiner *lock = grpc_combiner_create(NULL);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_combiner_execute(&exec_ctx, lock, grpc_closure_create(add_finally, lock),
-                        GRPC_ERROR_NONE, false);
+  grpc_closure_sched(&exec_ctx,
+                     grpc_closure_create(add_finally, lock,
+                                         grpc_combiner_scheduler(lock, false)),
+                     GRPC_ERROR_NONE);
   grpc_exec_ctx_flush(&exec_ctx);
   GPR_ASSERT(got_in_finally);
   grpc_combiner_destroy(&exec_ctx, lock);
diff --git a/test/core/iomgr/endpoint_pair_test.c b/test/core/iomgr/endpoint_pair_test.c
index 2a257a7ceacf6a82116dab1e32daec809f19beda..f02171f2ef78fcf00a939079627b3cb572a73e03 100644
--- a/test/core/iomgr/endpoint_pair_test.c
+++ b/test/core/iomgr/endpoint_pair_test.c
@@ -81,7 +81,8 @@ int main(int argc, char **argv) {
   g_pollset = gpr_malloc(grpc_pollset_size());
   grpc_pollset_init(g_pollset, &g_mu);
   grpc_endpoint_tests(configs[0], g_pollset, g_mu);
-  grpc_closure_init(&destroyed, destroy_pollset, g_pollset);
+  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();
diff --git a/test/core/iomgr/endpoint_tests.c b/test/core/iomgr/endpoint_tests.c
index 8186ea7e85611d34cc753c309f7abc81f7f2b755..df5dd209031b98efa7e382a40a4405201766a712 100644
--- a/test/core/iomgr/endpoint_tests.c
+++ b/test/core/iomgr/endpoint_tests.c
@@ -41,6 +41,8 @@
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
 #include <grpc/support/useful.h>
+
+#include "src/core/lib/slice/slice_internal.h"
 #include "test/core/util/test_config.h"
 
 /*
@@ -211,9 +213,10 @@ static void read_and_write_test(grpc_endpoint_test_config config,
   state.write_done = 0;
   state.current_read_data = 0;
   state.current_write_data = 0;
-  grpc_closure_init(&state.done_read, read_and_write_test_read_handler, &state);
+  grpc_closure_init(&state.done_read, read_and_write_test_read_handler, &state,
+                    grpc_schedule_on_exec_ctx);
   grpc_closure_init(&state.done_write, read_and_write_test_write_handler,
-                    &state);
+                    &state, grpc_schedule_on_exec_ctx);
   grpc_slice_buffer_init(&state.outgoing);
   grpc_slice_buffer_init(&state.incoming);
 
@@ -249,8 +252,8 @@ static void read_and_write_test(grpc_endpoint_test_config config,
   grpc_exec_ctx_flush(&exec_ctx);
 
   end_test(config);
-  grpc_slice_buffer_destroy(&state.outgoing);
-  grpc_slice_buffer_destroy(&state.incoming);
+  grpc_slice_buffer_destroy_internal(&exec_ctx, &state.outgoing);
+  grpc_slice_buffer_destroy_internal(&exec_ctx, &state.incoming);
   grpc_endpoint_destroy(&exec_ctx, state.read_ep);
   grpc_endpoint_destroy(&exec_ctx, state.write_ep);
   grpc_exec_ctx_finish(&exec_ctx);
@@ -290,21 +293,24 @@ static void multiple_shutdown_test(grpc_endpoint_test_config config) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_endpoint_add_to_pollset(&exec_ctx, f.client_ep, g_pollset);
   grpc_endpoint_read(&exec_ctx, f.client_ep, &slice_buffer,
-                     grpc_closure_create(inc_on_failure, &fail_count));
+                     grpc_closure_create(inc_on_failure, &fail_count,
+                                         grpc_schedule_on_exec_ctx));
   wait_for_fail_count(&exec_ctx, &fail_count, 0);
   grpc_endpoint_shutdown(&exec_ctx, f.client_ep);
   wait_for_fail_count(&exec_ctx, &fail_count, 1);
   grpc_endpoint_read(&exec_ctx, f.client_ep, &slice_buffer,
-                     grpc_closure_create(inc_on_failure, &fail_count));
+                     grpc_closure_create(inc_on_failure, &fail_count,
+                                         grpc_schedule_on_exec_ctx));
   wait_for_fail_count(&exec_ctx, &fail_count, 2);
   grpc_slice_buffer_add(&slice_buffer, grpc_slice_from_copied_string("a"));
   grpc_endpoint_write(&exec_ctx, f.client_ep, &slice_buffer,
-                      grpc_closure_create(inc_on_failure, &fail_count));
+                      grpc_closure_create(inc_on_failure, &fail_count,
+                                          grpc_schedule_on_exec_ctx));
   wait_for_fail_count(&exec_ctx, &fail_count, 3);
   grpc_endpoint_shutdown(&exec_ctx, f.client_ep);
   wait_for_fail_count(&exec_ctx, &fail_count, 3);
 
-  grpc_slice_buffer_destroy(&slice_buffer);
+  grpc_slice_buffer_destroy_internal(&exec_ctx, &slice_buffer);
 
   grpc_endpoint_destroy(&exec_ctx, f.client_ep);
   grpc_endpoint_destroy(&exec_ctx, f.server_ep);
diff --git a/test/core/iomgr/ev_epoll_linux_test.c b/test/core/iomgr/ev_epoll_linux_test.c
index 564b05d7f4c727335dbdd53e1b01d79b53056b01..40ae91bc6de601faf5518f43b51bcfd798055808 100644
--- a/test/core/iomgr/ev_epoll_linux_test.c
+++ b/test/core/iomgr/ev_epoll_linux_test.c
@@ -102,7 +102,8 @@ static void test_pollset_cleanup(grpc_exec_ctx *exec_ctx,
   int i;
 
   for (i = 0; i < num_pollsets; i++) {
-    grpc_closure_init(&destroyed, destroy_pollset, pollsets[i].pollset);
+    grpc_closure_init(&destroyed, destroy_pollset, pollsets[i].pollset,
+                      grpc_schedule_on_exec_ctx);
     grpc_pollset_shutdown(exec_ctx, pollsets[i].pollset, &destroyed);
 
     grpc_exec_ctx_flush(exec_ctx);
@@ -236,7 +237,11 @@ int main(int argc, char **argv) {
             "strategy. and the current strategy is: '%s'",
             poll_strategy);
   }
-  grpc_iomgr_shutdown();
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_iomgr_shutdown(&exec_ctx);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
   return 0;
 }
 #else /* defined(GRPC_LINUX_EPOLL) */
diff --git a/test/core/iomgr/fd_conservation_posix_test.c b/test/core/iomgr/fd_conservation_posix_test.c
index 652b37eb6f461a54421f8bcefe3d8c748fe8d155..3dffa02c3c809b3790b375b5f28d92943c7e084a 100644
--- a/test/core/iomgr/fd_conservation_posix_test.c
+++ b/test/core/iomgr/fd_conservation_posix_test.c
@@ -65,6 +65,10 @@ int main(int argc, char **argv) {
 
   grpc_resource_quota_unref(resource_quota);
 
-  grpc_iomgr_shutdown();
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_iomgr_shutdown(&exec_ctx);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
   return 0;
 }
diff --git a/test/core/iomgr/fd_posix_test.c b/test/core/iomgr/fd_posix_test.c
index 6166699fe6286e2f4dd44bdff419aaefb4a71b83..a617bfc6469937a09e5fa60284eedceaecc22592 100644
--- a/test/core/iomgr/fd_posix_test.c
+++ b/test/core/iomgr/fd_posix_test.c
@@ -219,8 +219,8 @@ static void listen_cb(grpc_exec_ctx *exec_ctx, void *arg, /*=sv_arg*/
   se->sv = sv;
   se->em_fd = grpc_fd_create(fd, "listener");
   grpc_pollset_add_fd(exec_ctx, g_pollset, se->em_fd);
-  se->session_read_closure.cb = session_read_cb;
-  se->session_read_closure.cb_arg = se;
+  grpc_closure_init(&se->session_read_closure, session_read_cb, se,
+                    grpc_schedule_on_exec_ctx);
   grpc_fd_notify_on_read(exec_ctx, se->em_fd, &se->session_read_closure);
 
   grpc_fd_notify_on_read(exec_ctx, listen_em_fd, &sv->listen_closure);
@@ -249,8 +249,8 @@ static int server_start(grpc_exec_ctx *exec_ctx, server *sv) {
   sv->em_fd = grpc_fd_create(fd, "server");
   grpc_pollset_add_fd(exec_ctx, g_pollset, sv->em_fd);
   /* Register to be interested in reading from listen_fd. */
-  sv->listen_closure.cb = listen_cb;
-  sv->listen_closure.cb_arg = sv;
+  grpc_closure_init(&sv->listen_closure, listen_cb, sv,
+                    grpc_schedule_on_exec_ctx);
   grpc_fd_notify_on_read(exec_ctx, sv->em_fd, &sv->listen_closure);
 
   return port;
@@ -333,8 +333,8 @@ static void client_session_write(grpc_exec_ctx *exec_ctx, void *arg, /*client */
   if (errno == EAGAIN) {
     gpr_mu_lock(g_mu);
     if (cl->client_write_cnt < CLIENT_TOTAL_WRITE_CNT) {
-      cl->write_closure.cb = client_session_write;
-      cl->write_closure.cb_arg = cl;
+      grpc_closure_init(&cl->write_closure, client_session_write, cl,
+                        grpc_schedule_on_exec_ctx);
       grpc_fd_notify_on_write(exec_ctx, cl->em_fd, &cl->write_closure);
       cl->client_write_cnt++;
     } else {
@@ -459,10 +459,10 @@ static void test_grpc_fd_change(void) {
   grpc_closure second_closure;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
-  first_closure.cb = first_read_callback;
-  first_closure.cb_arg = &a;
-  second_closure.cb = second_read_callback;
-  second_closure.cb_arg = &b;
+  grpc_closure_init(&first_closure, first_read_callback, &a,
+                    grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&second_closure, second_read_callback, &b,
+                    grpc_schedule_on_exec_ctx);
 
   init_change_data(&a);
   init_change_data(&b);
@@ -546,11 +546,13 @@ int main(int argc, char **argv) {
   grpc_pollset_init(g_pollset, &g_mu);
   test_grpc_fd();
   test_grpc_fd_change();
-  grpc_closure_init(&destroyed, destroy_pollset, g_pollset);
+  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_exec_ctx_flush(&exec_ctx);
   gpr_free(g_pollset);
-  grpc_iomgr_shutdown();
+  grpc_iomgr_shutdown(&exec_ctx);
+  grpc_exec_ctx_finish(&exec_ctx);
   return 0;
 }
 
diff --git a/test/core/iomgr/resolve_address_test.c b/test/core/iomgr/resolve_address_test.c
index 2dd0d88b3f80b2cf82d08204e04388651d29060f..84f4ed4c90e8990f8dbd2adf6fc3458a5986d7bd 100644
--- a/test/core/iomgr/resolve_address_test.c
+++ b/test/core/iomgr/resolve_address_test.c
@@ -32,8 +32,10 @@
  */
 
 #include "src/core/lib/iomgr/resolve_address.h"
+#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"
@@ -46,16 +48,73 @@ static gpr_timespec test_deadline(void) {
 typedef struct args_struct {
   gpr_event ev;
   grpc_resolved_addresses *addrs;
+  gpr_atm done_atm;
+  gpr_mu *mu;
+  grpc_pollset *pollset;
+  grpc_pollset_set *pollset_set;
 } args_struct;
 
-void args_init(args_struct *args) {
+static void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {}
+
+void args_init(grpc_exec_ctx *exec_ctx, args_struct *args) {
   gpr_event_init(&args->ev);
+  args->pollset = gpr_malloc(grpc_pollset_size());
+  grpc_pollset_init(args->pollset, &args->mu);
+  args->pollset_set = grpc_pollset_set_create();
+  grpc_pollset_set_add_pollset(exec_ctx, args->pollset_set, args->pollset);
   args->addrs = NULL;
 }
 
-void args_finish(args_struct *args) {
+void args_finish(grpc_exec_ctx *exec_ctx, args_struct *args) {
   GPR_ASSERT(gpr_event_wait(&args->ev, test_deadline()));
   grpc_resolved_addresses_destroy(args->addrs);
+  grpc_pollset_set_del_pollset(exec_ctx, args->pollset_set, args->pollset);
+  grpc_pollset_set_destroy(args->pollset_set);
+  grpc_closure do_nothing_cb;
+  grpc_closure_init(&do_nothing_cb, do_nothing, NULL,
+                    grpc_schedule_on_exec_ctx);
+  grpc_pollset_shutdown(exec_ctx, args->pollset, &do_nothing_cb);
+  // exec_ctx needs to be flushed before calling grpc_pollset_destroy()
+  grpc_exec_ctx_flush(exec_ctx);
+  grpc_pollset_destroy(args->pollset);
+  gpr_free(args->pollset);
+}
+
+static gpr_timespec n_sec_deadline(int seconds) {
+  return gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
+                      gpr_time_from_seconds(seconds, GPR_TIMESPAN));
+}
+
+static void actually_poll(void *argsp) {
+  args_struct *args = argsp;
+  gpr_timespec deadline = n_sec_deadline(10);
+  while (true) {
+    bool done = gpr_atm_acq_load(&args->done_atm) != 0;
+    if (done) {
+      break;
+    }
+    gpr_timespec time_left =
+        gpr_time_sub(deadline, gpr_now(GPR_CLOCK_REALTIME));
+    gpr_log(GPR_DEBUG, "done=%d, time_left=%" PRId64 ".%09d", done,
+            time_left.tv_sec, time_left.tv_nsec);
+    GPR_ASSERT(gpr_time_cmp(time_left, gpr_time_0(GPR_TIMESPAN)) >= 0);
+    grpc_pollset_worker *worker = NULL;
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    gpr_mu_lock(args->mu);
+    GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, args->pollset, &worker,
+                          gpr_now(GPR_CLOCK_REALTIME), n_sec_deadline(1)));
+    gpr_mu_unlock(args->mu);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
+  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,
@@ -64,53 +123,65 @@ static void must_succeed(grpc_exec_ctx *exec_ctx, void *argsp,
   GPR_ASSERT(err == GRPC_ERROR_NONE);
   GPR_ASSERT(args->addrs != NULL);
   GPR_ASSERT(args->addrs->naddrs > 0);
-  gpr_event_set(&args->ev, (void *)1);
+  gpr_atm_rel_store(&args->done_atm, 1);
 }
 
 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_event_set(&args->ev, (void *)1);
+  gpr_atm_rel_store(&args->done_atm, 1);
 }
 
 static void test_localhost(void) {
-  args_struct args;
-  args_init(&args);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_resolve_address(&exec_ctx, "localhost:1", NULL,
-                       grpc_closure_create(must_succeed, &args), &args.addrs);
+  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);
+  args_finish(&exec_ctx, &args);
   grpc_exec_ctx_finish(&exec_ctx);
-  args_finish(&args);
 }
 
 static void test_default_port(void) {
-  args_struct args;
-  args_init(&args);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_resolve_address(&exec_ctx, "localhost", "1",
-                       grpc_closure_create(must_succeed, &args), &args.addrs);
+  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);
+  args_finish(&exec_ctx, &args);
   grpc_exec_ctx_finish(&exec_ctx);
-  args_finish(&args);
 }
 
 static void test_missing_default_port(void) {
-  args_struct args;
-  args_init(&args);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_resolve_address(&exec_ctx, "localhost", NULL,
-                       grpc_closure_create(must_fail, &args), &args.addrs);
+  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);
+  args_finish(&exec_ctx, &args);
   grpc_exec_ctx_finish(&exec_ctx);
-  args_finish(&args);
 }
 
 static void test_ipv6_with_port(void) {
-  args_struct args;
-  args_init(&args);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_resolve_address(&exec_ctx, "[2001:db8::1]:1", NULL,
-                       grpc_closure_create(must_succeed, &args), &args.addrs);
+  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);
+  args_finish(&exec_ctx, &args);
   grpc_exec_ctx_finish(&exec_ctx);
-  args_finish(&args);
 }
 
 static void test_ipv6_without_port(void) {
@@ -119,13 +190,16 @@ static void test_ipv6_without_port(void) {
   };
   unsigned i;
   for (i = 0; i < sizeof(kCases) / sizeof(*kCases); i++) {
-    args_struct args;
-    args_init(&args);
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-    grpc_resolve_address(&exec_ctx, kCases[i], "80",
-                         grpc_closure_create(must_succeed, &args), &args.addrs);
+    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);
+    args_finish(&exec_ctx, &args);
     grpc_exec_ctx_finish(&exec_ctx);
-    args_finish(&args);
   }
 }
 
@@ -135,13 +209,16 @@ static void test_invalid_ip_addresses(void) {
   };
   unsigned i;
   for (i = 0; i < sizeof(kCases) / sizeof(*kCases); i++) {
-    args_struct args;
-    args_init(&args);
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-    grpc_resolve_address(&exec_ctx, kCases[i], NULL,
-                         grpc_closure_create(must_fail, &args), &args.addrs);
+    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);
+    args_finish(&exec_ctx, &args);
     grpc_exec_ctx_finish(&exec_ctx);
-    args_finish(&args);
   }
 }
 
@@ -151,13 +228,16 @@ static void test_unparseable_hostports(void) {
   };
   unsigned i;
   for (i = 0; i < sizeof(kCases) / sizeof(*kCases); i++) {
-    args_struct args;
-    args_init(&args);
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-    grpc_resolve_address(&exec_ctx, kCases[i], "1",
-                         grpc_closure_create(must_fail, &args), &args.addrs);
+    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);
+    args_finish(&exec_ctx, &args);
     grpc_exec_ctx_finish(&exec_ctx);
-    args_finish(&args);
   }
 }
 
@@ -172,7 +252,11 @@ int main(int argc, char **argv) {
   test_ipv6_without_port();
   test_invalid_ip_addresses();
   test_unparseable_hostports();
-  grpc_iomgr_shutdown();
-  grpc_executor_shutdown();
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_executor_shutdown(&exec_ctx);
+    grpc_iomgr_shutdown(&exec_ctx);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
   return 0;
 }
diff --git a/test/core/iomgr/resource_quota_test.c b/test/core/iomgr/resource_quota_test.c
index a82d44f7f8558ee17a7fc05035a8dec36395d574..a5b28f210d8e761baf719165a4e077e974d47c7b 100644
--- a/test/core/iomgr/resource_quota_test.c
+++ b/test/core/iomgr/resource_quota_test.c
@@ -36,6 +36,7 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
+#include "src/core/lib/slice/slice_internal.h"
 #include "test/core/util/test_config.h"
 
 static void inc_int_cb(grpc_exec_ctx *exec_ctx, void *a, grpc_error *error) {
@@ -45,7 +46,9 @@ static void inc_int_cb(grpc_exec_ctx *exec_ctx, void *a, grpc_error *error) {
 static void set_bool_cb(grpc_exec_ctx *exec_ctx, void *a, grpc_error *error) {
   *(bool *)a = true;
 }
-grpc_closure *set_bool(bool *p) { return grpc_closure_create(set_bool_cb, p); }
+grpc_closure *set_bool(bool *p) {
+  return grpc_closure_create(set_bool_cb, p, grpc_schedule_on_exec_ctx);
+}
 
 typedef struct {
   size_t size;
@@ -67,7 +70,7 @@ grpc_closure *make_reclaimer(grpc_resource_user *resource_user, size_t size,
   a->size = size;
   a->resource_user = resource_user;
   a->then = then;
-  return grpc_closure_create(reclaimer_cb, a);
+  return grpc_closure_create(reclaimer_cb, a, grpc_schedule_on_exec_ctx);
 }
 
 static void unused_reclaimer_cb(grpc_exec_ctx *exec_ctx, void *arg,
@@ -76,7 +79,8 @@ static void unused_reclaimer_cb(grpc_exec_ctx *exec_ctx, void *arg,
   grpc_closure_run(exec_ctx, arg, GRPC_ERROR_NONE);
 }
 grpc_closure *make_unused_reclaimer(grpc_closure *then) {
-  return grpc_closure_create(unused_reclaimer_cb, then);
+  return grpc_closure_create(unused_reclaimer_cb, then,
+                             grpc_schedule_on_exec_ctx);
 }
 
 static void destroy_user(grpc_resource_user *usr) {
@@ -631,7 +635,11 @@ static void test_one_slice(void) {
     GPR_ASSERT(num_allocs == start_allocs + 1);
   }
 
-  grpc_slice_buffer_destroy(&buffer);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_slice_buffer_destroy_internal(&exec_ctx, &buffer);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
   destroy_user(usr);
   grpc_resource_quota_unref(q);
 }
@@ -667,7 +675,11 @@ static void test_one_slice_deleted_late(void) {
   }
 
   grpc_resource_quota_unref(q);
-  grpc_slice_buffer_destroy(&buffer);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_slice_buffer_destroy_internal(&exec_ctx, &buffer);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
 }
 
 int main(int argc, char **argv) {
diff --git a/test/core/iomgr/tcp_client_posix_test.c b/test/core/iomgr/tcp_client_posix_test.c
index 5fab826fb778ae60fa6a2001d77178cc7dcadfc1..0ea7a000eb430f074eb8e8723a6f3bf986162db1 100644
--- a/test/core/iomgr/tcp_client_posix_test.c
+++ b/test/core/iomgr/tcp_client_posix_test.c
@@ -113,7 +113,7 @@ void test_succeeds(void) {
   /* connect to it */
   GPR_ASSERT(getsockname(svr_fd, (struct sockaddr *)addr,
                          (socklen_t *)&resolved_addr.len) == 0);
-  grpc_closure_init(&done, must_succeed, NULL);
+  grpc_closure_init(&done, must_succeed, NULL, grpc_schedule_on_exec_ctx);
   grpc_tcp_client_connect(&exec_ctx, &done, &g_connecting, g_pollset_set, NULL,
                           &resolved_addr, gpr_inf_future(GPR_CLOCK_REALTIME));
 
@@ -163,7 +163,7 @@ void test_fails(void) {
   gpr_mu_unlock(g_mu);
 
   /* connect to a broken address */
-  grpc_closure_init(&done, must_fail, NULL);
+  grpc_closure_init(&done, must_fail, NULL, grpc_schedule_on_exec_ctx);
   grpc_tcp_client_connect(&exec_ctx, &done, &g_connecting, g_pollset_set, NULL,
                           &resolved_addr, gpr_inf_future(GPR_CLOCK_REALTIME));
 
@@ -207,7 +207,8 @@ int main(int argc, char **argv) {
   gpr_log(GPR_ERROR, "End of first test");
   test_fails();
   grpc_pollset_set_destroy(g_pollset_set);
-  grpc_closure_init(&destroyed, destroy_pollset, g_pollset);
+  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();
diff --git a/test/core/iomgr/tcp_posix_test.c b/test/core/iomgr/tcp_posix_test.c
index 5eafa570bbf43a48d80a8a0c89d6741de54207cd..cabb64eb8f1f00827347119367883887cd8da35d 100644
--- a/test/core/iomgr/tcp_posix_test.c
+++ b/test/core/iomgr/tcp_posix_test.c
@@ -50,6 +50,8 @@
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
 #include <grpc/support/useful.h>
+
+#include "src/core/lib/slice/slice_internal.h"
 #include "test/core/iomgr/endpoint_tests.h"
 #include "test/core/util/test_config.h"
 
@@ -184,7 +186,7 @@ static void read_test(size_t num_bytes, size_t slice_size) {
   grpc_resource_quota *resource_quota = grpc_resource_quota_create("read_test");
   ep = grpc_tcp_create(grpc_fd_create(sv[1], "read_test"), resource_quota,
                        slice_size, "test");
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+  grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
   grpc_endpoint_add_to_pollset(&exec_ctx, ep, g_pollset);
 
   written_bytes = fill_socket_partial(sv[0], num_bytes);
@@ -194,7 +196,7 @@ static void read_test(size_t num_bytes, size_t slice_size) {
   state.read_bytes = 0;
   state.target_read_bytes = written_bytes;
   grpc_slice_buffer_init(&state.incoming);
-  grpc_closure_init(&state.read_cb, read_cb, &state);
+  grpc_closure_init(&state.read_cb, read_cb, &state, grpc_schedule_on_exec_ctx);
 
   grpc_endpoint_read(&exec_ctx, ep, &state.incoming, &state.read_cb);
 
@@ -212,7 +214,7 @@ static void read_test(size_t num_bytes, size_t slice_size) {
   GPR_ASSERT(state.read_bytes == state.target_read_bytes);
   gpr_mu_unlock(g_mu);
 
-  grpc_slice_buffer_destroy(&state.incoming);
+  grpc_slice_buffer_destroy_internal(&exec_ctx, &state.incoming);
   grpc_endpoint_destroy(&exec_ctx, ep);
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -235,7 +237,7 @@ static void large_read_test(size_t slice_size) {
       grpc_resource_quota_create("large_read_test");
   ep = grpc_tcp_create(grpc_fd_create(sv[1], "large_read_test"), resource_quota,
                        slice_size, "test");
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+  grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
   grpc_endpoint_add_to_pollset(&exec_ctx, ep, g_pollset);
 
   written_bytes = fill_socket(sv[0]);
@@ -245,7 +247,7 @@ static void large_read_test(size_t slice_size) {
   state.read_bytes = 0;
   state.target_read_bytes = (size_t)written_bytes;
   grpc_slice_buffer_init(&state.incoming);
-  grpc_closure_init(&state.read_cb, read_cb, &state);
+  grpc_closure_init(&state.read_cb, read_cb, &state, grpc_schedule_on_exec_ctx);
 
   grpc_endpoint_read(&exec_ctx, ep, &state.incoming, &state.read_cb);
 
@@ -263,7 +265,7 @@ static void large_read_test(size_t slice_size) {
   GPR_ASSERT(state.read_bytes == state.target_read_bytes);
   gpr_mu_unlock(g_mu);
 
-  grpc_slice_buffer_destroy(&state.incoming);
+  grpc_slice_buffer_destroy_internal(&exec_ctx, &state.incoming);
   grpc_endpoint_destroy(&exec_ctx, ep);
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -374,7 +376,7 @@ static void write_test(size_t num_bytes, size_t slice_size) {
       grpc_resource_quota_create("write_test");
   ep = grpc_tcp_create(grpc_fd_create(sv[1], "write_test"), resource_quota,
                        GRPC_TCP_DEFAULT_READ_SLICE_SIZE, "test");
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+  grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
   grpc_endpoint_add_to_pollset(&exec_ctx, ep, g_pollset);
 
   state.ep = ep;
@@ -384,7 +386,8 @@ static void write_test(size_t num_bytes, size_t slice_size) {
 
   grpc_slice_buffer_init(&outgoing);
   grpc_slice_buffer_addn(&outgoing, slices, num_blocks);
-  grpc_closure_init(&write_done_closure, write_done, &state);
+  grpc_closure_init(&write_done_closure, write_done, &state,
+                    grpc_schedule_on_exec_ctx);
 
   grpc_endpoint_write(&exec_ctx, ep, &outgoing, &write_done_closure);
   drain_socket_blocking(sv[0], num_bytes, num_bytes);
@@ -404,7 +407,7 @@ static void write_test(size_t num_bytes, size_t slice_size) {
   }
   gpr_mu_unlock(g_mu);
 
-  grpc_slice_buffer_destroy(&outgoing);
+  grpc_slice_buffer_destroy_internal(&exec_ctx, &outgoing);
   grpc_endpoint_destroy(&exec_ctx, ep);
   gpr_free(slices);
   grpc_exec_ctx_finish(&exec_ctx);
@@ -429,7 +432,8 @@ static void release_fd_test(size_t num_bytes, size_t slice_size) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_closure fd_released_cb;
   int fd_released_done = 0;
-  grpc_closure_init(&fd_released_cb, &on_fd_released, &fd_released_done);
+  grpc_closure_init(&fd_released_cb, &on_fd_released, &fd_released_done,
+                    grpc_schedule_on_exec_ctx);
 
   gpr_log(GPR_INFO,
           "Release fd read_test of size %" PRIuPTR ", slice size %" PRIuPTR,
@@ -442,7 +446,7 @@ static void release_fd_test(size_t num_bytes, size_t slice_size) {
   ep = grpc_tcp_create(grpc_fd_create(sv[1], "read_test"), resource_quota,
                        slice_size, "test");
   GPR_ASSERT(grpc_tcp_fd(ep) == sv[1] && sv[1] >= 0);
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+  grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
   grpc_endpoint_add_to_pollset(&exec_ctx, ep, g_pollset);
 
   written_bytes = fill_socket_partial(sv[0], num_bytes);
@@ -452,7 +456,7 @@ static void release_fd_test(size_t num_bytes, size_t slice_size) {
   state.read_bytes = 0;
   state.target_read_bytes = written_bytes;
   grpc_slice_buffer_init(&state.incoming);
-  grpc_closure_init(&state.read_cb, read_cb, &state);
+  grpc_closure_init(&state.read_cb, read_cb, &state, grpc_schedule_on_exec_ctx);
 
   grpc_endpoint_read(&exec_ctx, ep, &state.incoming, &state.read_cb);
 
@@ -472,7 +476,7 @@ static void release_fd_test(size_t num_bytes, size_t slice_size) {
   GPR_ASSERT(state.read_bytes == state.target_read_bytes);
   gpr_mu_unlock(g_mu);
 
-  grpc_slice_buffer_destroy(&state.incoming);
+  grpc_slice_buffer_destroy_internal(&exec_ctx, &state.incoming);
   grpc_tcp_destroy_and_release_fd(&exec_ctx, ep, &fd, &fd_released_cb);
   grpc_exec_ctx_flush(&exec_ctx);
   gpr_mu_lock(g_mu);
@@ -534,7 +538,7 @@ static grpc_endpoint_test_fixture create_fixture_tcp_socketpair(
                                 resource_quota, slice_size, "test");
   f.server_ep = grpc_tcp_create(grpc_fd_create(sv[1], "fixture:server"),
                                 resource_quota, slice_size, "test");
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+  grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
   grpc_endpoint_add_to_pollset(&exec_ctx, f.client_ep, g_pollset);
   grpc_endpoint_add_to_pollset(&exec_ctx, f.server_ep, g_pollset);
 
@@ -561,7 +565,8 @@ int main(int argc, char **argv) {
   grpc_pollset_init(g_pollset, &g_mu);
   grpc_endpoint_tests(configs[0], g_pollset, g_mu);
   run_tests();
-  grpc_closure_init(&destroyed, destroy_pollset, g_pollset);
+  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();
diff --git a/test/core/iomgr/tcp_server_posix_test.c b/test/core/iomgr/tcp_server_posix_test.c
index 1b8a39c1beb36025a147f51001aa15ea2629574d..020f0059802e371e4d1fa7714e674609dcbd7740 100644
--- a/test/core/iomgr/tcp_server_posix_test.c
+++ b/test/core/iomgr/tcp_server_posix_test.c
@@ -104,7 +104,7 @@ static void server_weak_ref_shutdown(grpc_exec_ctx *exec_ctx, void *arg,
 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);
+                    weak_ref, grpc_schedule_on_exec_ctx);
 }
 
 /* Make weak_ref->server_shutdown a shutdown_starting cb on server.
@@ -126,6 +126,7 @@ static void on_connect(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *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;
@@ -365,7 +366,8 @@ int main(int argc, char **argv) {
   test_connect(1);
   test_connect(10);
 
-  grpc_closure_init(&destroyed, destroy_pollset, g_pollset);
+  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();
diff --git a/test/core/iomgr/timer_list_test.c b/test/core/iomgr/timer_list_test.c
index be8988ab752f2ee940e7517d6676e3464e18ec60..8d7ac3fdaa1be1548e61be90faf291ec159f4e8f 100644
--- a/test/core/iomgr/timer_list_test.c
+++ b/test/core/iomgr/timer_list_test.c
@@ -57,17 +57,20 @@ static void add_test(void) {
 
   /* 10 ms timers.  will expire in the current epoch */
   for (i = 0; i < 10; i++) {
-    grpc_timer_init(&exec_ctx, &timers[i],
-                    gpr_time_add(start, gpr_time_from_millis(10, GPR_TIMESPAN)),
-                    cb, (void *)(intptr_t)i, start);
+    grpc_timer_init(
+        &exec_ctx, &timers[i],
+        gpr_time_add(start, gpr_time_from_millis(10, GPR_TIMESPAN)),
+        grpc_closure_create(cb, (void *)(intptr_t)i, grpc_schedule_on_exec_ctx),
+        start);
   }
 
   /* 1010 ms timers.  will expire in the next epoch */
   for (i = 10; i < 20; i++) {
     grpc_timer_init(
         &exec_ctx, &timers[i],
-        gpr_time_add(start, gpr_time_from_millis(1010, GPR_TIMESPAN)), cb,
-        (void *)(intptr_t)i, start);
+        gpr_time_add(start, gpr_time_from_millis(1010, GPR_TIMESPAN)),
+        grpc_closure_create(cb, (void *)(intptr_t)i, grpc_schedule_on_exec_ctx),
+        start);
   }
 
   /* collect timers.  Only the first batch should be ready. */
@@ -125,16 +128,26 @@ void destruction_test(void) {
   grpc_timer_list_init(gpr_time_0(GPR_CLOCK_REALTIME));
   memset(cb_called, 0, sizeof(cb_called));
 
-  grpc_timer_init(&exec_ctx, &timers[0], tfm(100), cb, (void *)(intptr_t)0,
-                  gpr_time_0(GPR_CLOCK_REALTIME));
-  grpc_timer_init(&exec_ctx, &timers[1], tfm(3), cb, (void *)(intptr_t)1,
-                  gpr_time_0(GPR_CLOCK_REALTIME));
-  grpc_timer_init(&exec_ctx, &timers[2], tfm(100), cb, (void *)(intptr_t)2,
-                  gpr_time_0(GPR_CLOCK_REALTIME));
-  grpc_timer_init(&exec_ctx, &timers[3], tfm(3), cb, (void *)(intptr_t)3,
-                  gpr_time_0(GPR_CLOCK_REALTIME));
-  grpc_timer_init(&exec_ctx, &timers[4], tfm(1), cb, (void *)(intptr_t)4,
-                  gpr_time_0(GPR_CLOCK_REALTIME));
+  grpc_timer_init(
+      &exec_ctx, &timers[0], tfm(100),
+      grpc_closure_create(cb, (void *)(intptr_t)0, grpc_schedule_on_exec_ctx),
+      gpr_time_0(GPR_CLOCK_REALTIME));
+  grpc_timer_init(
+      &exec_ctx, &timers[1], tfm(3),
+      grpc_closure_create(cb, (void *)(intptr_t)1, grpc_schedule_on_exec_ctx),
+      gpr_time_0(GPR_CLOCK_REALTIME));
+  grpc_timer_init(
+      &exec_ctx, &timers[2], tfm(100),
+      grpc_closure_create(cb, (void *)(intptr_t)2, grpc_schedule_on_exec_ctx),
+      gpr_time_0(GPR_CLOCK_REALTIME));
+  grpc_timer_init(
+      &exec_ctx, &timers[3], tfm(3),
+      grpc_closure_create(cb, (void *)(intptr_t)3, grpc_schedule_on_exec_ctx),
+      gpr_time_0(GPR_CLOCK_REALTIME));
+  grpc_timer_init(
+      &exec_ctx, &timers[4], tfm(1),
+      grpc_closure_create(cb, (void *)(intptr_t)4, grpc_schedule_on_exec_ctx),
+      gpr_time_0(GPR_CLOCK_REALTIME));
   GPR_ASSERT(1 == grpc_timer_check(&exec_ctx, tfm(2), NULL));
   grpc_exec_ctx_finish(&exec_ctx);
   GPR_ASSERT(1 == cb_called[4][1]);
diff --git a/test/core/iomgr/udp_server_test.c b/test/core/iomgr/udp_server_test.c
index 9bea229466820402b5a9eebf4f6e3ef9666a9435..e1210401c816ec83dfc9a0f94fa41576910b682d 100644
--- a/test/core/iomgr/udp_server_test.c
+++ b/test/core/iomgr/udp_server_test.c
@@ -234,11 +234,12 @@ int main(int argc, char **argv) {
   test_receive(1);
   test_receive(10);
 
-  grpc_closure_init(&destroyed, destroy_pollset, g_pollset);
+  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);
   gpr_free(g_pollset);
-  grpc_iomgr_shutdown();
+  grpc_shutdown();
   return 0;
 }
 
diff --git a/test/core/json/BUILD b/test/core/json/BUILD
new file mode 100644
index 0000000000000000000000000000000000000000..4b3fbd6076834a28681fc54872c71eac4b7ecaed
--- /dev/null
+++ b/test/core/json/BUILD
@@ -0,0 +1,39 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+load("//test/core/util:grpc_fuzzer.bzl", "grpc_fuzzer")
+
+grpc_fuzzer(
+  name = "json_fuzzer",
+  srcs = ["fuzzer.c"],
+  deps = ["//:gpr", "//:grpc", "//test/core/util:grpc_test_util"],
+  corpus = "corpus",
+  copts = ["-std=c99"],
+)
+
diff --git a/test/core/memory_usage/client.c b/test/core/memory_usage/client.c
new file mode 100644
index 0000000000000000000000000000000000000000..9fc122b4c360d8bc2bcb6f05b8a4e0d0c9d4e3a0
--- /dev/null
+++ b/test/core/memory_usage/client.c
@@ -0,0 +1,314 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <grpc/grpc.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/byte_buffer_reader.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/cmdline.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "src/core/lib/support/string.h"
+#include "test/core/util/memory_counters.h"
+#include "test/core/util/test_config.h"
+
+static grpc_channel *channel;
+static grpc_completion_queue *cq;
+static grpc_op metadata_ops[2];
+static grpc_op status_ops[2];
+static grpc_op snapshot_ops[6];
+static grpc_op *op;
+
+typedef struct {
+  grpc_call *call;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_status_code status;
+  char *details;
+  size_t details_capacity;
+  grpc_metadata_array trailing_metadata_recv;
+} fling_call;
+
+// Statically allocate call data structs. Enough to accomodate 10000 ping-pong
+// calls and 1 extra for the snapshot calls.
+static fling_call calls[10001];
+
+static void *tag(intptr_t t) { return (void *)t; }
+
+// A call is intentionally divided into two steps. First step is to initiate a
+// call (i.e send and recv metadata). A call is outstanding after we initated,
+// so we can measure the call memory usage.
+static void init_ping_pong_request(int call_idx) {
+  grpc_metadata_array_init(&calls[call_idx].initial_metadata_recv);
+
+  memset(metadata_ops, 0, sizeof(metadata_ops));
+  op = metadata_ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata = &calls[call_idx].initial_metadata_recv;
+  op++;
+
+  calls[call_idx].call = grpc_channel_create_call(
+      channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq, "/Reflector/reflectUnary",
+      "localhost", gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+
+  GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(calls[call_idx].call,
+                                                   metadata_ops,
+                                                   (size_t)(op - metadata_ops),
+                                                   tag(call_idx), NULL));
+  grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+}
+
+// Second step is to finish the call (i.e recv status) and destroy the call.
+static void finish_ping_pong_request(int call_idx) {
+  grpc_metadata_array_init(&calls[call_idx].trailing_metadata_recv);
+
+  memset(status_ops, 0, sizeof(status_ops));
+  op = status_ops;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata =
+      &calls[call_idx].trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &calls[call_idx].status;
+  op->data.recv_status_on_client.status_details = &calls[call_idx].details;
+  op->data.recv_status_on_client.status_details_capacity =
+      &calls[call_idx].details_capacity;
+  op++;
+
+  GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(calls[call_idx].call,
+                                                   status_ops,
+                                                   (size_t)(op - status_ops),
+                                                   tag(call_idx), NULL));
+  grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+  grpc_metadata_array_destroy(&calls[call_idx].initial_metadata_recv);
+  grpc_metadata_array_destroy(&calls[call_idx].trailing_metadata_recv);
+  gpr_free(calls[call_idx].details);
+  grpc_call_destroy(calls[call_idx].call);
+  calls[call_idx].call = NULL;
+}
+
+static struct grpc_memory_counters send_snapshot_request(
+    int call_idx, const char *call_type) {
+  grpc_metadata_array_init(&calls[call_idx].initial_metadata_recv);
+  grpc_metadata_array_init(&calls[call_idx].trailing_metadata_recv);
+
+  grpc_byte_buffer *response_payload_recv = NULL;
+  memset(snapshot_ops, 0, sizeof(snapshot_ops));
+  op = snapshot_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 = &calls[call_idx].initial_metadata_recv;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message = &response_payload_recv;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata =
+      &calls[call_idx].trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &calls[call_idx].status;
+  op->data.recv_status_on_client.status_details = &calls[call_idx].details;
+  op->data.recv_status_on_client.status_details_capacity =
+      &calls[call_idx].details_capacity;
+  op++;
+
+  calls[call_idx].call = grpc_channel_create_call(
+      channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq, call_type, "localhost",
+      gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(
+                                 calls[call_idx].call, snapshot_ops,
+                                 (size_t)(op - snapshot_ops), (void *)0, NULL));
+  grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+
+  grpc_byte_buffer_reader reader;
+  grpc_byte_buffer_reader_init(&reader, response_payload_recv);
+  grpc_slice response = grpc_byte_buffer_reader_readall(&reader);
+
+  struct grpc_memory_counters snapshot;
+  snapshot.total_size_absolute =
+      ((struct grpc_memory_counters *)GRPC_SLICE_START_PTR(response))
+          ->total_size_absolute;
+  snapshot.total_allocs_absolute =
+      ((struct grpc_memory_counters *)GRPC_SLICE_START_PTR(response))
+          ->total_allocs_absolute;
+  snapshot.total_size_relative =
+      ((struct grpc_memory_counters *)GRPC_SLICE_START_PTR(response))
+          ->total_size_relative;
+  snapshot.total_allocs_relative =
+      ((struct grpc_memory_counters *)GRPC_SLICE_START_PTR(response))
+          ->total_allocs_relative;
+
+  grpc_metadata_array_destroy(&calls[call_idx].initial_metadata_recv);
+  grpc_metadata_array_destroy(&calls[call_idx].trailing_metadata_recv);
+  grpc_slice_unref(response);
+  grpc_byte_buffer_reader_destroy(&reader);
+  grpc_byte_buffer_destroy(response_payload_recv);
+  gpr_free(calls[call_idx].details);
+  calls[call_idx].details = NULL;
+  calls[call_idx].details_capacity = 0;
+  grpc_call_destroy(calls[call_idx].call);
+  calls[call_idx].call = NULL;
+
+  return snapshot;
+}
+
+int main(int argc, char **argv) {
+  grpc_memory_counters_init();
+  grpc_slice slice = grpc_slice_from_copied_string("x");
+  char *fake_argv[1];
+
+  char *target = "localhost:443";
+  gpr_cmdline *cl;
+  grpc_event event;
+
+  grpc_init();
+
+  GPR_ASSERT(argc >= 1);
+  fake_argv[0] = argv[0];
+  grpc_test_init(1, fake_argv);
+
+  int warmup_iterations = 100;
+  int benchmark_iterations = 1000;
+
+  cl = gpr_cmdline_create("memory profiling client");
+  gpr_cmdline_add_string(cl, "target", "Target host:port", &target);
+  gpr_cmdline_add_int(cl, "warmup", "Warmup iterations", &warmup_iterations);
+  gpr_cmdline_add_int(cl, "benchmark", "Benchmark iterations",
+                      &benchmark_iterations);
+  gpr_cmdline_parse(cl, argc, argv);
+  gpr_cmdline_destroy(cl);
+
+  for (int k = 0; k < (int)(sizeof(calls) / sizeof(fling_call)); k++) {
+    calls[k].details = NULL;
+    calls[k].details_capacity = 0;
+  }
+
+  cq = grpc_completion_queue_create(NULL);
+
+  struct grpc_memory_counters client_channel_start =
+      grpc_memory_counters_snapshot();
+  channel = grpc_insecure_channel_create(target, NULL, NULL);
+
+  int call_idx = 0;
+
+  struct grpc_memory_counters before_server_create =
+      send_snapshot_request(0, "Reflector/GetBeforeSvrCreation");
+  struct grpc_memory_counters after_server_create =
+      send_snapshot_request(0, "Reflector/GetAfterSvrCreation");
+
+  // warmup period
+  for (call_idx = 0; call_idx < warmup_iterations; ++call_idx) {
+    init_ping_pong_request(call_idx + 1);
+  }
+
+  struct grpc_memory_counters server_benchmark_calls_start =
+      send_snapshot_request(0, "Reflector/SimpleSnapshot");
+
+  struct grpc_memory_counters client_benchmark_calls_start =
+      grpc_memory_counters_snapshot();
+
+  // benchmark period
+  for (; call_idx < warmup_iterations + benchmark_iterations; ++call_idx) {
+    init_ping_pong_request(call_idx + 1);
+  }
+
+  struct grpc_memory_counters client_calls_inflight =
+      grpc_memory_counters_snapshot();
+
+  struct grpc_memory_counters server_calls_inflight =
+      send_snapshot_request(0, "Reflector/DestroyCalls");
+
+  do {
+    event = grpc_completion_queue_next(
+        cq, gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
+                         gpr_time_from_micros(10000, GPR_TIMESPAN)),
+        NULL);
+  } while (event.type != GRPC_QUEUE_TIMEOUT);
+
+  // second step - recv status and destroy call
+  for (call_idx = 0; call_idx < warmup_iterations + benchmark_iterations;
+       ++call_idx) {
+    finish_ping_pong_request(call_idx + 1);
+  }
+
+  struct grpc_memory_counters server_calls_end =
+      send_snapshot_request(0, "Reflector/SimpleSnapshot");
+
+  struct grpc_memory_counters client_channel_end =
+      grpc_memory_counters_snapshot();
+
+  grpc_channel_destroy(channel);
+  grpc_completion_queue_shutdown(cq);
+
+  do {
+    event = grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME),
+                                       NULL);
+  } while (event.type != GRPC_QUEUE_SHUTDOWN);
+  grpc_slice_unref(slice);
+
+  grpc_completion_queue_destroy(cq);
+  grpc_shutdown();
+
+  gpr_log(GPR_INFO, "---------client stats--------");
+  gpr_log(GPR_INFO, "client call memory usage: %f bytes per call",
+          (double)(client_calls_inflight.total_size_relative -
+                   client_benchmark_calls_start.total_size_relative) /
+              benchmark_iterations);
+  gpr_log(GPR_INFO, "client channel memory usage %zi bytes",
+          client_channel_end.total_size_relative -
+              client_channel_start.total_size_relative);
+
+  gpr_log(GPR_INFO, "---------server stats--------");
+  gpr_log(GPR_INFO, "server create: %zi bytes",
+          after_server_create.total_size_relative -
+              before_server_create.total_size_relative);
+  gpr_log(GPR_INFO, "server call memory usage: %f bytes per call",
+          (double)(server_calls_inflight.total_size_relative -
+                   server_benchmark_calls_start.total_size_relative) /
+              benchmark_iterations);
+  gpr_log(GPR_INFO, "server channel memory usage %zi bytes",
+          server_calls_end.total_size_relative -
+              after_server_create.total_size_relative);
+
+  grpc_memory_counters_destroy();
+  return 0;
+}
diff --git a/test/core/memory_usage/memory_usage_test.c b/test/core/memory_usage/memory_usage_test.c
new file mode 100644
index 0000000000000000000000000000000000000000..7e7a9d050da2f010d91e9848dd34205fdc04b250
--- /dev/null
+++ b/test/core/memory_usage/memory_usage_test.c
@@ -0,0 +1,93 @@
+/*
+ *
+ * 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 <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/subprocess.h>
+#include "src/core/lib/support/string.h"
+#include "test/core/util/port.h"
+
+int main(int argc, char **argv) {
+  char *me = argv[0];
+  char *lslash = strrchr(me, '/');
+  char root[1024];
+  int port = grpc_pick_unused_port_or_die();
+  char *args[10];
+  int status;
+  gpr_subprocess *svr, *cli;
+  /* figure out where we are */
+  if (lslash) {
+    memcpy(root, me, (size_t)(lslash - me));
+    root[lslash - me] = 0;
+  } else {
+    strcpy(root, ".");
+  }
+  /* start the server */
+  gpr_asprintf(&args[0], "%s/memory_profile_server%s", root,
+               gpr_subprocess_binary_extension());
+  args[1] = "--bind";
+  gpr_join_host_port(&args[2], "::", port);
+  args[3] = "--no-secure";
+  svr = gpr_subprocess_create(4, (const char **)args);
+  gpr_free(args[0]);
+  gpr_free(args[2]);
+
+  /* start the client */
+  gpr_asprintf(&args[0], "%s/memory_profile_client%s", root,
+               gpr_subprocess_binary_extension());
+  args[1] = "--target";
+  gpr_join_host_port(&args[2], "127.0.0.1", port);
+  args[3] = "--warmup=1000";
+  args[4] = "--benchmark=9000";
+  cli = gpr_subprocess_create(5, (const char **)args);
+  gpr_free(args[0]);
+  gpr_free(args[2]);
+
+  /* wait for completion */
+  printf("waiting for client\n");
+  if ((status = gpr_subprocess_join(cli))) {
+    gpr_subprocess_destroy(cli);
+    gpr_subprocess_destroy(svr);
+    return status;
+  }
+  gpr_subprocess_destroy(cli);
+
+  gpr_subprocess_interrupt(svr);
+  status = gpr_subprocess_join(svr);
+  gpr_subprocess_destroy(svr);
+  return status;
+}
diff --git a/test/core/memory_usage/server.c b/test/core/memory_usage/server.c
new file mode 100644
index 0000000000000000000000000000000000000000..c0710930b07b3eb90593958f923be82c4de95546
--- /dev/null
+++ b/test/core/memory_usage/server.c
@@ -0,0 +1,321 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <grpc/grpc.h>
+#include <grpc/grpc_security.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#ifndef _WIN32
+/* This is for _exit() below, which is temporary. */
+#include <unistd.h>
+#endif
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/cmdline.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include "test/core/end2end/data/ssl_test_data.h"
+#include "test/core/util/memory_counters.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+static grpc_completion_queue *cq;
+static grpc_server *server;
+static grpc_op metadata_ops[2];
+static grpc_op snapshot_ops[5];
+static grpc_op status_op;
+static int got_sigint = 0;
+static grpc_byte_buffer *payload_buffer = NULL;
+static grpc_byte_buffer *terminal_buffer = NULL;
+static int was_cancelled = 2;
+
+static void *tag(intptr_t t) { return (void *)t; }
+
+typedef enum {
+  FLING_SERVER_NEW_REQUEST = 1,
+  FLING_SERVER_SEND_INIT_METADATA,
+  FLING_SERVER_WAIT_FOR_DESTROY,
+  FLING_SERVER_SEND_STATUS_FLING_CALL,
+  FLING_SERVER_SEND_STATUS_SNAPSHOT,
+  FLING_SERVER_BATCH_SEND_STATUS_FLING_CALL
+} fling_server_tags;
+
+typedef struct {
+  fling_server_tags state;
+  grpc_call *call;
+  grpc_call_details call_details;
+  grpc_metadata_array request_metadata_recv;
+  grpc_metadata_array initial_metadata_send;
+} fling_call;
+
+// hold up to 10000 calls and 6 snaphost calls
+static fling_call calls[100006];
+
+static void request_call_unary(int call_idx) {
+  if (call_idx == (int)(sizeof(calls) / sizeof(fling_call))) {
+    gpr_log(GPR_INFO, "Used all call slots (10000) on server. Server exit.");
+    _exit(0);
+  }
+  grpc_metadata_array_init(&calls[call_idx].request_metadata_recv);
+  grpc_server_request_call(
+      server, &calls[call_idx].call, &calls[call_idx].call_details,
+      &calls[call_idx].request_metadata_recv, cq, cq, &calls[call_idx]);
+}
+
+static void send_initial_metadata_unary(void *tag) {
+  grpc_metadata_array_init(&(*(fling_call *)tag).initial_metadata_send);
+  metadata_ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
+  metadata_ops[0].data.send_initial_metadata.count = 0;
+
+  GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch((*(fling_call *)tag).call,
+                                                   metadata_ops, 1, tag, NULL));
+}
+
+static void send_status(void *tag) {
+  status_op.op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  status_op.data.send_status_from_server.status = GRPC_STATUS_OK;
+  status_op.data.send_status_from_server.trailing_metadata_count = 0;
+  status_op.data.send_status_from_server.status_details = "";
+
+  GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch((*(fling_call *)tag).call,
+                                                   &status_op, 1, tag, NULL));
+}
+
+static void send_snapshot(void *tag, struct grpc_memory_counters *snapshot) {
+  grpc_op *op;
+
+  grpc_slice snapshot_slice =
+      grpc_slice_new(snapshot, sizeof(*snapshot), gpr_free);
+  payload_buffer = grpc_raw_byte_buffer_create(&snapshot_slice, 1);
+  grpc_metadata_array_init(&(*(fling_call *)tag).initial_metadata_send);
+
+  op = snapshot_ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message = &terminal_buffer;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  if (payload_buffer == NULL) {
+    gpr_log(GPR_INFO, "NULL payload buffer !!!");
+  }
+  op->data.send_message = payload_buffer;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.status = GRPC_STATUS_OK;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status_details = "";
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+
+  GPR_ASSERT(GRPC_CALL_OK ==
+             grpc_call_start_batch((*(fling_call *)tag).call, snapshot_ops,
+                                   (size_t)(op - snapshot_ops), tag, NULL));
+}
+/* We have some sort of deadlock, so let's not exit gracefully for now.
+   When that is resolved, please remove the #include <unistd.h> above. */
+static void sigint_handler(int x) { _exit(0); }
+
+int main(int argc, char **argv) {
+  grpc_memory_counters_init();
+  grpc_event ev;
+  char *addr_buf = NULL;
+  gpr_cmdline *cl;
+  int shutdown_started = 0;
+  int shutdown_finished = 0;
+
+  int secure = 0;
+  char *addr = NULL;
+
+  char *fake_argv[1];
+
+  GPR_ASSERT(argc >= 1);
+  fake_argv[0] = argv[0];
+  grpc_test_init(1, fake_argv);
+
+  grpc_init();
+  srand((unsigned)clock());
+
+  cl = gpr_cmdline_create("fling server");
+  gpr_cmdline_add_string(cl, "bind", "Bind host:port", &addr);
+  gpr_cmdline_add_flag(cl, "secure", "Run with security?", &secure);
+  gpr_cmdline_parse(cl, argc, argv);
+  gpr_cmdline_destroy(cl);
+
+  if (addr == NULL) {
+    gpr_join_host_port(&addr_buf, "::", grpc_pick_unused_port_or_die());
+    addr = addr_buf;
+  }
+  gpr_log(GPR_INFO, "creating server on: %s", addr);
+
+  cq = grpc_completion_queue_create(NULL);
+
+  struct grpc_memory_counters before_server_create =
+      grpc_memory_counters_snapshot();
+  if (secure) {
+    grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {test_server1_key,
+                                                    test_server1_cert};
+    grpc_server_credentials *ssl_creds = grpc_ssl_server_credentials_create(
+        NULL, &pem_key_cert_pair, 1, 0, NULL);
+    server = grpc_server_create(NULL, NULL);
+    GPR_ASSERT(grpc_server_add_secure_http2_port(server, addr, ssl_creds));
+    grpc_server_credentials_release(ssl_creds);
+  } else {
+    server = grpc_server_create(NULL, NULL);
+    GPR_ASSERT(grpc_server_add_insecure_http2_port(server, addr));
+  }
+
+  grpc_server_register_completion_queue(server, cq, NULL);
+  grpc_server_start(server);
+
+  struct grpc_memory_counters after_server_create =
+      grpc_memory_counters_snapshot();
+
+  gpr_free(addr_buf);
+  addr = addr_buf = NULL;
+
+  // initialize call instances
+  for (int i = 0; i < (int)(sizeof(calls) / sizeof(fling_call)); i++) {
+    grpc_call_details_init(&calls[i].call_details);
+    calls[i].state = FLING_SERVER_NEW_REQUEST;
+  }
+
+  int next_call_idx = 0;
+  struct grpc_memory_counters current_snapshot;
+
+  request_call_unary(next_call_idx);
+
+  signal(SIGINT, sigint_handler);
+
+  while (!shutdown_finished) {
+    if (got_sigint && !shutdown_started) {
+      gpr_log(GPR_INFO, "Shutting down due to SIGINT");
+      grpc_server_shutdown_and_notify(server, cq, tag(1000));
+      GPR_ASSERT(grpc_completion_queue_pluck(
+                     cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5), NULL)
+                     .type == GRPC_OP_COMPLETE);
+      grpc_completion_queue_shutdown(cq);
+      shutdown_started = 1;
+    }
+    ev = grpc_completion_queue_next(
+        cq, gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
+                         gpr_time_from_micros(1000000, GPR_TIMESPAN)),
+        NULL);
+    fling_call *s = ev.tag;
+    switch (ev.type) {
+      case GRPC_OP_COMPLETE:
+        switch (s->state) {
+          case FLING_SERVER_NEW_REQUEST:
+            request_call_unary(++next_call_idx);
+            if (0 ==
+                strcmp(s->call_details.method, "/Reflector/reflectUnary")) {
+              s->state = FLING_SERVER_SEND_INIT_METADATA;
+              send_initial_metadata_unary(s);
+            } else if (0 == strcmp(s->call_details.method,
+                                   "Reflector/GetBeforeSvrCreation")) {
+              s->state = FLING_SERVER_SEND_STATUS_SNAPSHOT;
+              send_snapshot(s, &before_server_create);
+            } else if (0 == strcmp(s->call_details.method,
+                                   "Reflector/GetAfterSvrCreation")) {
+              s->state = FLING_SERVER_SEND_STATUS_SNAPSHOT;
+              send_snapshot(s, &after_server_create);
+            } else if (0 == strcmp(s->call_details.method,
+                                   "Reflector/SimpleSnapshot")) {
+              s->state = FLING_SERVER_SEND_STATUS_SNAPSHOT;
+              current_snapshot = grpc_memory_counters_snapshot();
+              send_snapshot(s, &current_snapshot);
+            } else if (0 == strcmp(s->call_details.method,
+                                   "Reflector/DestroyCalls")) {
+              s->state = FLING_SERVER_BATCH_SEND_STATUS_FLING_CALL;
+              current_snapshot = grpc_memory_counters_snapshot();
+              send_snapshot(s, &current_snapshot);
+            } else {
+              gpr_log(GPR_ERROR, "Wrong call method");
+            }
+            break;
+          case FLING_SERVER_SEND_INIT_METADATA:
+            s->state = FLING_SERVER_WAIT_FOR_DESTROY;
+            break;
+          case FLING_SERVER_WAIT_FOR_DESTROY:
+            break;
+          case FLING_SERVER_SEND_STATUS_FLING_CALL:
+            grpc_call_destroy(s->call);
+            grpc_call_details_destroy(&s->call_details);
+            grpc_metadata_array_destroy(&s->initial_metadata_send);
+            grpc_metadata_array_destroy(&s->request_metadata_recv);
+            break;
+          case FLING_SERVER_BATCH_SEND_STATUS_FLING_CALL:
+            for (int k = 0; k < (int)(sizeof(calls) / sizeof(fling_call));
+                 ++k) {
+              if (calls[k].state == FLING_SERVER_WAIT_FOR_DESTROY) {
+                calls[k].state = FLING_SERVER_SEND_STATUS_FLING_CALL;
+                send_status(&calls[k]);
+              }
+            }
+          // no break here since we want to continue to case
+          // FLING_SERVER_SEND_STATUS_SNAPSHOT to destroy the snapshot call
+          case FLING_SERVER_SEND_STATUS_SNAPSHOT:
+            grpc_byte_buffer_destroy(payload_buffer);
+            grpc_byte_buffer_destroy(terminal_buffer);
+            grpc_call_destroy(s->call);
+            grpc_call_details_destroy(&s->call_details);
+            grpc_metadata_array_destroy(&s->initial_metadata_send);
+            grpc_metadata_array_destroy(&s->request_metadata_recv);
+            terminal_buffer = NULL;
+            payload_buffer = NULL;
+            break;
+        }
+        break;
+      case GRPC_QUEUE_SHUTDOWN:
+        GPR_ASSERT(shutdown_started);
+        shutdown_finished = 1;
+        break;
+      case GRPC_QUEUE_TIMEOUT:
+        break;
+    }
+  }
+
+  grpc_server_destroy(server);
+  grpc_completion_queue_destroy(cq);
+  grpc_shutdown();
+  grpc_memory_counters_destroy();
+  return 0;
+}
diff --git a/test/core/nanopb/BUILD b/test/core/nanopb/BUILD
new file mode 100644
index 0000000000000000000000000000000000000000..bdf79b7fefa73284a4d9a08487da63b74ff96f29
--- /dev/null
+++ b/test/core/nanopb/BUILD
@@ -0,0 +1,47 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+load("//test/core/util:grpc_fuzzer.bzl", "grpc_fuzzer")
+
+grpc_fuzzer(
+  name = "fuzzer_response",
+  srcs = ["fuzzer_response.c"],
+  deps = ["//:gpr", "//:grpc", "//test/core/util:grpc_test_util"],
+  corpus = "corpus_response",
+  copts = ["-std=c99"],
+)
+
+grpc_fuzzer(
+  name = "fuzzer_serverlist",
+  srcs = ["fuzzer_serverlist.c"],
+  deps = ["//:gpr", "//:grpc", "//test/core/util:grpc_test_util"],
+  corpus = "corpus_serverlist",
+  copts = ["-std=c99"],
+)
+
diff --git a/test/core/security/b64_test.c b/test/core/security/b64_test.c
index af883f51e9e7a3bc288339eafcf6320c5e4e48ea..28af48075ebf51b13bd553455865ded94c48c172 100644
--- a/test/core/security/b64_test.c
+++ b/test/core/security/b64_test.c
@@ -38,6 +38,8 @@
 #include <grpc/slice.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "test/core/util/test_config.h"
 
 static int buffers_are_equal(const unsigned char *buf1,
@@ -57,12 +59,14 @@ static void test_simple_encode_decode_b64(int url_safe, int multiline) {
   const char *hello = "hello";
   char *hello_b64 =
       grpc_base64_encode(hello, strlen(hello), url_safe, multiline);
-  grpc_slice hello_slice = grpc_base64_decode(hello_b64, url_safe);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_slice hello_slice = grpc_base64_decode(&exec_ctx, hello_b64, url_safe);
   GPR_ASSERT(GRPC_SLICE_LENGTH(hello_slice) == strlen(hello));
   GPR_ASSERT(strncmp((const char *)GRPC_SLICE_START_PTR(hello_slice), hello,
                      GRPC_SLICE_LENGTH(hello_slice)) == 0);
 
-  grpc_slice_unref(hello_slice);
+  grpc_slice_unref_internal(&exec_ctx, hello_slice);
+  grpc_exec_ctx_finish(&exec_ctx);
   gpr_free(hello_b64);
 }
 
@@ -75,13 +79,15 @@ static void test_full_range_encode_decode_b64(int url_safe, int multiline) {
 
   /* Try all the different paddings. */
   for (i = 0; i < 3; i++) {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
     b64 = grpc_base64_encode(orig, sizeof(orig) - i, url_safe, multiline);
-    orig_decoded = grpc_base64_decode(b64, url_safe);
+    orig_decoded = grpc_base64_decode(&exec_ctx, b64, url_safe);
     GPR_ASSERT(GRPC_SLICE_LENGTH(orig_decoded) == (sizeof(orig) - i));
     GPR_ASSERT(buffers_are_equal(orig, GRPC_SLICE_START_PTR(orig_decoded),
                                  sizeof(orig) - i));
-    grpc_slice_unref(orig_decoded);
+    grpc_slice_unref_internal(&exec_ctx, orig_decoded);
     gpr_free(b64);
+    grpc_exec_ctx_finish(&exec_ctx);
   }
 }
 
@@ -117,7 +123,7 @@ static void test_full_range_encode_decode_b64_urlsafe_multiline(void) {
   test_full_range_encode_decode_b64(1, 1);
 }
 
-static void test_url_safe_unsafe_mismtach_failure(void) {
+static void test_url_safe_unsafe_mismatch_failure(void) {
   unsigned char orig[256];
   size_t i;
   char *b64;
@@ -125,17 +131,19 @@ static void test_url_safe_unsafe_mismtach_failure(void) {
   int url_safe = 1;
   for (i = 0; i < sizeof(orig); i++) orig[i] = (uint8_t)i;
 
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   b64 = grpc_base64_encode(orig, sizeof(orig), url_safe, 0);
-  orig_decoded = grpc_base64_decode(b64, !url_safe);
+  orig_decoded = grpc_base64_decode(&exec_ctx, b64, !url_safe);
   GPR_ASSERT(GRPC_SLICE_IS_EMPTY(orig_decoded));
   gpr_free(b64);
-  grpc_slice_unref(orig_decoded);
+  grpc_slice_unref_internal(&exec_ctx, orig_decoded);
 
   b64 = grpc_base64_encode(orig, sizeof(orig), !url_safe, 0);
-  orig_decoded = grpc_base64_decode(b64, url_safe);
+  orig_decoded = grpc_base64_decode(&exec_ctx, b64, url_safe);
   GPR_ASSERT(GRPC_SLICE_IS_EMPTY(orig_decoded));
   gpr_free(b64);
-  grpc_slice_unref(orig_decoded);
+  grpc_slice_unref_internal(&exec_ctx, orig_decoded);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_rfc4648_test_vectors(void) {
@@ -173,38 +181,40 @@ static void test_rfc4648_test_vectors(void) {
 static void test_unpadded_decode(void) {
   grpc_slice decoded;
 
-  decoded = grpc_base64_decode("Zm9vYmFy", 0);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  decoded = grpc_base64_decode(&exec_ctx, "Zm9vYmFy", 0);
   GPR_ASSERT(!GRPC_SLICE_IS_EMPTY(decoded));
   GPR_ASSERT(grpc_slice_str_cmp(decoded, "foobar") == 0);
   grpc_slice_unref(decoded);
 
-  decoded = grpc_base64_decode("Zm9vYmE", 0);
+  decoded = grpc_base64_decode(&exec_ctx, "Zm9vYmE", 0);
   GPR_ASSERT(!GRPC_SLICE_IS_EMPTY(decoded));
   GPR_ASSERT(grpc_slice_str_cmp(decoded, "fooba") == 0);
   grpc_slice_unref(decoded);
 
-  decoded = grpc_base64_decode("Zm9vYg", 0);
+  decoded = grpc_base64_decode(&exec_ctx, "Zm9vYg", 0);
   GPR_ASSERT(!GRPC_SLICE_IS_EMPTY(decoded));
   GPR_ASSERT(grpc_slice_str_cmp(decoded, "foob") == 0);
   grpc_slice_unref(decoded);
 
-  decoded = grpc_base64_decode("Zm9v", 0);
+  decoded = grpc_base64_decode(&exec_ctx, "Zm9v", 0);
   GPR_ASSERT(!GRPC_SLICE_IS_EMPTY(decoded));
   GPR_ASSERT(grpc_slice_str_cmp(decoded, "foo") == 0);
   grpc_slice_unref(decoded);
 
-  decoded = grpc_base64_decode("Zm8", 0);
+  decoded = grpc_base64_decode(&exec_ctx, "Zm8", 0);
   GPR_ASSERT(!GRPC_SLICE_IS_EMPTY(decoded));
   GPR_ASSERT(grpc_slice_str_cmp(decoded, "fo") == 0);
   grpc_slice_unref(decoded);
 
-  decoded = grpc_base64_decode("Zg", 0);
+  decoded = grpc_base64_decode(&exec_ctx, "Zg", 0);
   GPR_ASSERT(!GRPC_SLICE_IS_EMPTY(decoded));
   GPR_ASSERT(grpc_slice_str_cmp(decoded, "f") == 0);
   grpc_slice_unref(decoded);
 
-  decoded = grpc_base64_decode("", 0);
+  decoded = grpc_base64_decode(&exec_ctx, "", 0);
   GPR_ASSERT(GRPC_SLICE_IS_EMPTY(decoded));
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 int main(int argc, char **argv) {
@@ -217,7 +227,7 @@ int main(int argc, char **argv) {
   test_full_range_encode_decode_b64_multiline();
   test_full_range_encode_decode_b64_urlsafe_no_multiline();
   test_full_range_encode_decode_b64_urlsafe_multiline();
-  test_url_safe_unsafe_mismtach_failure();
+  test_url_safe_unsafe_mismatch_failure();
   test_rfc4648_test_vectors();
   test_unpadded_decode();
   return 0;
diff --git a/test/core/security/create_jwt.c b/test/core/security/create_jwt.c
index 741ace9bdda2fc7d009522e072d514f50c7498cb..ac795f29d225a32a41b1b0a9ed2f2d399f3ae834 100644
--- a/test/core/security/create_jwt.c
+++ b/test/core/security/create_jwt.c
@@ -72,6 +72,7 @@ int main(int argc, char **argv) {
   char *scope = NULL;
   char *json_key_file_path = NULL;
   char *service_url = NULL;
+  grpc_init();
   gpr_cmdline *cl = gpr_cmdline_create("create_jwt");
   gpr_cmdline_add_string(cl, "json_key", "File path of the json key.",
                          &json_key_file_path);
@@ -102,5 +103,6 @@ int main(int argc, char **argv) {
   create_jwt(json_key_file_path, service_url, scope);
 
   gpr_cmdline_destroy(cl);
+  grpc_shutdown();
   return 0;
 }
diff --git a/test/core/security/credentials_test.c b/test/core/security/credentials_test.c
index d4c755088df05bad845b9b0a69739b57b24ce371..0a79292b5b9a840a94823939a32e69bce669f5a3 100644
--- a/test/core/security/credentials_test.c
+++ b/test/core/security/credentials_test.c
@@ -166,24 +166,29 @@ static grpc_httpcli_response http_response(int status, const char *body) {
 /* -- Tests. -- */
 
 static void test_empty_md_store(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_credentials_md_store *store = grpc_credentials_md_store_create(0);
   GPR_ASSERT(store->num_entries == 0);
   GPR_ASSERT(store->allocated == 0);
-  grpc_credentials_md_store_unref(store);
+  grpc_credentials_md_store_unref(&exec_ctx, store);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_ref_unref_empty_md_store(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_credentials_md_store *store = grpc_credentials_md_store_create(0);
   grpc_credentials_md_store_ref(store);
   grpc_credentials_md_store_ref(store);
   GPR_ASSERT(store->num_entries == 0);
   GPR_ASSERT(store->allocated == 0);
-  grpc_credentials_md_store_unref(store);
-  grpc_credentials_md_store_unref(store);
-  grpc_credentials_md_store_unref(store);
+  grpc_credentials_md_store_unref(&exec_ctx, store);
+  grpc_credentials_md_store_unref(&exec_ctx, store);
+  grpc_credentials_md_store_unref(&exec_ctx, store);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_add_to_empty_md_store(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_credentials_md_store *store = grpc_credentials_md_store_create(0);
   const char *key_str = "hello";
   const char *value_str = "there blah blah blah blah blah blah blah";
@@ -195,10 +200,12 @@ static void test_add_to_empty_md_store(void) {
   GPR_ASSERT(grpc_slice_cmp(value, store->entries[0].value) == 0);
   grpc_slice_unref(key);
   grpc_slice_unref(value);
-  grpc_credentials_md_store_unref(store);
+  grpc_credentials_md_store_unref(&exec_ctx, store);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_add_cstrings_to_empty_md_store(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_credentials_md_store *store = grpc_credentials_md_store_create(0);
   const char *key_str = "hello";
   const char *value_str = "there blah blah blah blah blah blah blah";
@@ -206,18 +213,22 @@ static void test_add_cstrings_to_empty_md_store(void) {
   GPR_ASSERT(store->num_entries == 1);
   GPR_ASSERT(grpc_slice_str_cmp(store->entries[0].key, key_str) == 0);
   GPR_ASSERT(grpc_slice_str_cmp(store->entries[0].value, value_str) == 0);
-  grpc_credentials_md_store_unref(store);
+  grpc_credentials_md_store_unref(&exec_ctx, store);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_empty_preallocated_md_store(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_credentials_md_store *store = grpc_credentials_md_store_create(4);
   GPR_ASSERT(store->num_entries == 0);
   GPR_ASSERT(store->allocated == 4);
   GPR_ASSERT(store->entries != NULL);
-  grpc_credentials_md_store_unref(store);
+  grpc_credentials_md_store_unref(&exec_ctx, store);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_add_abunch_to_md_store(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_credentials_md_store *store = grpc_credentials_md_store_create(4);
   size_t num_entries = 1000;
   const char *key_str = "hello";
@@ -230,16 +241,19 @@ static void test_add_abunch_to_md_store(void) {
     GPR_ASSERT(grpc_slice_str_cmp(store->entries[i].key, key_str) == 0);
     GPR_ASSERT(grpc_slice_str_cmp(store->entries[i].value, value_str) == 0);
   }
-  grpc_credentials_md_store_unref(store);
+  grpc_credentials_md_store_unref(&exec_ctx, store);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_oauth2_token_fetcher_creds_parsing_ok(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_credentials_md_store *token_md = NULL;
   gpr_timespec token_lifetime;
   grpc_httpcli_response response =
       http_response(200, valid_oauth2_json_response);
   GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response(
-                 &response, &token_md, &token_lifetime) == GRPC_CREDENTIALS_OK);
+                 &exec_ctx, &response, &token_md, &token_lifetime) ==
+             GRPC_CREDENTIALS_OK);
   GPR_ASSERT(token_lifetime.tv_sec == 3599);
   GPR_ASSERT(token_lifetime.tv_nsec == 0);
   GPR_ASSERT(token_md->num_entries == 1);
@@ -248,32 +262,38 @@ static void test_oauth2_token_fetcher_creds_parsing_ok(void) {
   GPR_ASSERT(grpc_slice_str_cmp(token_md->entries[0].value,
                                 "Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_") ==
              0);
-  grpc_credentials_md_store_unref(token_md);
+  grpc_credentials_md_store_unref(&exec_ctx, token_md);
   grpc_http_response_destroy(&response);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_oauth2_token_fetcher_creds_parsing_bad_http_status(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_credentials_md_store *token_md = NULL;
   gpr_timespec token_lifetime;
   grpc_httpcli_response response =
       http_response(401, valid_oauth2_json_response);
   GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response(
-                 &response, &token_md, &token_lifetime) ==
+                 &exec_ctx, &response, &token_md, &token_lifetime) ==
              GRPC_CREDENTIALS_ERROR);
   grpc_http_response_destroy(&response);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_oauth2_token_fetcher_creds_parsing_empty_http_body(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_credentials_md_store *token_md = NULL;
   gpr_timespec token_lifetime;
   grpc_httpcli_response response = http_response(200, "");
   GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response(
-                 &response, &token_md, &token_lifetime) ==
+                 &exec_ctx, &response, &token_md, &token_lifetime) ==
              GRPC_CREDENTIALS_ERROR);
   grpc_http_response_destroy(&response);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_oauth2_token_fetcher_creds_parsing_invalid_json(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_credentials_md_store *token_md = NULL;
   gpr_timespec token_lifetime;
   grpc_httpcli_response response =
@@ -282,12 +302,14 @@ static void test_oauth2_token_fetcher_creds_parsing_invalid_json(void) {
                     " \"expires_in\":3599, "
                     " \"token_type\":\"Bearer\"");
   GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response(
-                 &response, &token_md, &token_lifetime) ==
+                 &exec_ctx, &response, &token_md, &token_lifetime) ==
              GRPC_CREDENTIALS_ERROR);
   grpc_http_response_destroy(&response);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_oauth2_token_fetcher_creds_parsing_missing_token(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_credentials_md_store *token_md = NULL;
   gpr_timespec token_lifetime;
   grpc_httpcli_response response = http_response(200,
@@ -295,12 +317,14 @@ static void test_oauth2_token_fetcher_creds_parsing_missing_token(void) {
                                                  " \"expires_in\":3599, "
                                                  " \"token_type\":\"Bearer\"}");
   GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response(
-                 &response, &token_md, &token_lifetime) ==
+                 &exec_ctx, &response, &token_md, &token_lifetime) ==
              GRPC_CREDENTIALS_ERROR);
   grpc_http_response_destroy(&response);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_oauth2_token_fetcher_creds_parsing_missing_token_type(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_credentials_md_store *token_md = NULL;
   gpr_timespec token_lifetime;
   grpc_httpcli_response response =
@@ -309,13 +333,15 @@ static void test_oauth2_token_fetcher_creds_parsing_missing_token_type(void) {
                     " \"expires_in\":3599, "
                     "}");
   GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response(
-                 &response, &token_md, &token_lifetime) ==
+                 &exec_ctx, &response, &token_md, &token_lifetime) ==
              GRPC_CREDENTIALS_ERROR);
   grpc_http_response_destroy(&response);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_oauth2_token_fetcher_creds_parsing_missing_token_lifetime(
     void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_credentials_md_store *token_md = NULL;
   gpr_timespec token_lifetime;
   grpc_httpcli_response response =
@@ -323,9 +349,10 @@ static void test_oauth2_token_fetcher_creds_parsing_missing_token_lifetime(
                     "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\","
                     " \"token_type\":\"Bearer\"}");
   GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response(
-                 &response, &token_md, &token_lifetime) ==
+                 &exec_ctx, &response, &token_md, &token_lifetime) ==
              GRPC_CREDENTIALS_ERROR);
   grpc_http_response_destroy(&response);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void check_metadata(expected_md *expected, grpc_credentials_md *md_elems,
@@ -361,7 +388,7 @@ static void check_google_iam_metadata(grpc_exec_ctx *exec_ctx, void *user_data,
   GPR_ASSERT(error_details == NULL);
   GPR_ASSERT(num_md == 2);
   check_metadata(emd, md_elems, num_md);
-  grpc_call_credentials_unref(c);
+  grpc_call_credentials_unref(exec_ctx, c);
 }
 
 static void test_google_iam_creds(void) {
@@ -385,7 +412,7 @@ static void check_access_token_metadata(
   GPR_ASSERT(error_details == NULL);
   GPR_ASSERT(num_md == 1);
   check_metadata(emd, md_elems, num_md);
-  grpc_call_credentials_unref(c);
+  grpc_call_credentials_unref(exec_ctx, c);
 }
 
 static void test_access_token_creds(void) {
@@ -401,9 +428,10 @@ static void test_access_token_creds(void) {
 }
 
 static grpc_security_status check_channel_oauth2_create_security_connector(
-    grpc_channel_credentials *c, grpc_call_credentials *call_creds,
-    const char *target, const grpc_channel_args *args,
-    grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
+    grpc_exec_ctx *exec_ctx, grpc_channel_credentials *c,
+    grpc_call_credentials *call_creds, const char *target,
+    const grpc_channel_args *args, grpc_channel_security_connector **sc,
+    grpc_channel_args **new_args) {
   GPR_ASSERT(strcmp(c->type, "mock") == 0);
   GPR_ASSERT(call_creds != NULL);
   GPR_ASSERT(strcmp(call_creds->type, GRPC_CALL_CREDENTIALS_TYPE_OAUTH2) == 0);
@@ -411,6 +439,7 @@ static grpc_security_status check_channel_oauth2_create_security_connector(
 }
 
 static void test_channel_oauth2_composite_creds(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_channel_args *new_args;
   grpc_channel_credentials_vtable vtable = {
       NULL, check_channel_oauth2_create_security_connector, NULL};
@@ -424,9 +453,10 @@ static void test_channel_oauth2_composite_creds(void) {
   grpc_channel_credentials_release(channel_creds);
   grpc_call_credentials_release(oauth2_creds);
   GPR_ASSERT(grpc_channel_credentials_create_security_connector(
-                 channel_oauth2_creds, NULL, NULL, NULL, &new_args) ==
-             GRPC_SECURITY_OK);
+                 &exec_ctx, channel_oauth2_creds, NULL, NULL, NULL,
+                 &new_args) == GRPC_SECURITY_OK);
   grpc_channel_credentials_release(channel_oauth2_creds);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void check_oauth2_google_iam_composite_metadata(
@@ -443,7 +473,7 @@ static void check_oauth2_google_iam_composite_metadata(
   GPR_ASSERT(error_details == NULL);
   GPR_ASSERT(num_md == 3);
   check_metadata(emd, md_elems, num_md);
-  grpc_call_credentials_unref(c);
+  grpc_call_credentials_unref(exec_ctx, c);
 }
 
 static void test_oauth2_google_iam_composite_creds(void) {
@@ -459,8 +489,8 @@ static void test_oauth2_google_iam_composite_creds(void) {
   grpc_call_credentials *composite_creds =
       grpc_composite_call_credentials_create(oauth2_creds, google_iam_creds,
                                              NULL);
-  grpc_call_credentials_unref(oauth2_creds);
-  grpc_call_credentials_unref(google_iam_creds);
+  grpc_call_credentials_unref(&exec_ctx, oauth2_creds);
+  grpc_call_credentials_unref(&exec_ctx, google_iam_creds);
   GPR_ASSERT(
       strcmp(composite_creds->type, GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0);
   creds_array =
@@ -478,9 +508,10 @@ static void test_oauth2_google_iam_composite_creds(void) {
 
 static grpc_security_status
 check_channel_oauth2_google_iam_create_security_connector(
-    grpc_channel_credentials *c, grpc_call_credentials *call_creds,
-    const char *target, const grpc_channel_args *args,
-    grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
+    grpc_exec_ctx *exec_ctx, grpc_channel_credentials *c,
+    grpc_call_credentials *call_creds, const char *target,
+    const grpc_channel_args *args, grpc_channel_security_connector **sc,
+    grpc_channel_args **new_args) {
   const grpc_call_credentials_array *creds_array;
   GPR_ASSERT(strcmp(c->type, "mock") == 0);
   GPR_ASSERT(call_creds != NULL);
@@ -495,6 +526,7 @@ check_channel_oauth2_google_iam_create_security_connector(
 }
 
 static void test_channel_oauth2_google_iam_composite_creds(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_channel_args *new_args;
   grpc_channel_credentials_vtable vtable = {
       NULL, check_channel_oauth2_google_iam_create_security_connector, NULL};
@@ -517,10 +549,11 @@ static void test_channel_oauth2_google_iam_composite_creds(void) {
   grpc_call_credentials_release(google_iam_creds);
 
   GPR_ASSERT(grpc_channel_credentials_create_security_connector(
-                 channel_oauth2_iam_creds, NULL, NULL, NULL, &new_args) ==
-             GRPC_SECURITY_OK);
+                 &exec_ctx, channel_oauth2_iam_creds, NULL, NULL, NULL,
+                 &new_args) == GRPC_SECURITY_OK);
 
   grpc_channel_credentials_release(channel_oauth2_iam_creds);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void on_oauth2_creds_get_metadata_success(
@@ -565,7 +598,7 @@ static int compute_engine_httpcli_get_success_override(
     grpc_httpcli_response *response) {
   validate_compute_engine_http_request(request);
   *response = http_response(200, valid_oauth2_json_response);
-  grpc_exec_ctx_sched(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, on_done, GRPC_ERROR_NONE);
   return 1;
 }
 
@@ -575,7 +608,7 @@ static int compute_engine_httpcli_get_failure_override(
     grpc_httpcli_response *response) {
   validate_compute_engine_http_request(request);
   *response = http_response(403, "Not Authorized.");
-  grpc_exec_ctx_sched(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, on_done, GRPC_ERROR_NONE);
   return 1;
 }
 
@@ -619,7 +652,7 @@ static void test_compute_engine_creds_success(void) {
       on_oauth2_creds_get_metadata_success, (void *)test_user_data);
   grpc_exec_ctx_finish(&exec_ctx);
 
-  grpc_call_credentials_unref(compute_engine_creds);
+  grpc_call_credentials_unref(&exec_ctx, compute_engine_creds);
   grpc_httpcli_set_override(NULL, NULL);
 }
 
@@ -634,7 +667,7 @@ static void test_compute_engine_creds_failure(void) {
   grpc_call_credentials_get_request_metadata(
       &exec_ctx, compute_engine_creds, NULL, auth_md_ctx,
       on_oauth2_creds_get_metadata_failure, (void *)test_user_data);
-  grpc_call_credentials_unref(compute_engine_creds);
+  grpc_call_credentials_unref(&exec_ctx, compute_engine_creds);
   grpc_httpcli_set_override(NULL, NULL);
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -668,7 +701,7 @@ static int refresh_token_httpcli_post_success(
     grpc_closure *on_done, grpc_httpcli_response *response) {
   validate_refresh_token_http_request(request, body, body_size);
   *response = http_response(200, valid_oauth2_json_response);
-  grpc_exec_ctx_sched(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, on_done, GRPC_ERROR_NONE);
   return 1;
 }
 
@@ -678,7 +711,7 @@ static int refresh_token_httpcli_post_failure(
     grpc_closure *on_done, grpc_httpcli_response *response) {
   validate_refresh_token_http_request(request, body, body_size);
   *response = http_response(403, "Not Authorized.");
-  grpc_exec_ctx_sched(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, on_done, GRPC_ERROR_NONE);
   return 1;
 }
 
@@ -706,7 +739,7 @@ static void test_refresh_token_creds_success(void) {
       on_oauth2_creds_get_metadata_success, (void *)test_user_data);
   grpc_exec_ctx_flush(&exec_ctx);
 
-  grpc_call_credentials_unref(refresh_token_creds);
+  grpc_call_credentials_unref(&exec_ctx, refresh_token_creds);
   grpc_httpcli_set_override(NULL, NULL);
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -723,7 +756,7 @@ static void test_refresh_token_creds_failure(void) {
   grpc_call_credentials_get_request_metadata(
       &exec_ctx, refresh_token_creds, NULL, auth_md_ctx,
       on_oauth2_creds_get_metadata_failure, (void *)test_user_data);
-  grpc_call_credentials_unref(refresh_token_creds);
+  grpc_call_credentials_unref(&exec_ctx, refresh_token_creds);
   grpc_httpcli_set_override(NULL, NULL);
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -832,7 +865,7 @@ static void test_jwt_creds_success(void) {
   grpc_exec_ctx_flush(&exec_ctx);
 
   gpr_free(json_key_string);
-  grpc_call_credentials_unref(jwt_creds);
+  grpc_call_credentials_unref(&exec_ctx, jwt_creds);
   grpc_jwt_encode_and_sign_set_override(NULL);
 }
 
@@ -851,7 +884,7 @@ static void test_jwt_creds_signing_failure(void) {
       on_jwt_creds_get_metadata_failure, (void *)test_user_data);
 
   gpr_free(json_key_string);
-  grpc_call_credentials_unref(jwt_creds);
+  grpc_call_credentials_unref(&exec_ctx, jwt_creds);
   grpc_jwt_encode_and_sign_set_override(NULL);
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -870,6 +903,7 @@ static void set_google_default_creds_env_var_with_file_contents(
 }
 
 static void test_google_default_creds_auth_key(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_service_account_jwt_access_credentials *jwt;
   grpc_composite_channel_credentials *creds;
   char *json_key = test_json_key_str();
@@ -885,11 +919,13 @@ static void test_google_default_creds_auth_key(void) {
       strcmp(jwt->key.client_id,
              "777-abaslkan11hlb6nmim3bpspl31ud.apps.googleusercontent.com") ==
       0);
-  grpc_channel_credentials_unref(&creds->base);
+  grpc_channel_credentials_unref(&exec_ctx, &creds->base);
   gpr_setenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, ""); /* Reset. */
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_google_default_creds_refresh_token(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_google_refresh_token_credentials *refresh;
   grpc_composite_channel_credentials *creds;
   grpc_flush_cached_google_default_credentials();
@@ -901,8 +937,9 @@ static void test_google_default_creds_refresh_token(void) {
   refresh = (grpc_google_refresh_token_credentials *)creds->call_creds;
   GPR_ASSERT(strcmp(refresh->refresh_token.client_id,
                     "32555999999.apps.googleusercontent.com") == 0);
-  grpc_channel_credentials_unref(&creds->base);
+  grpc_channel_credentials_unref(&exec_ctx, &creds->base);
   gpr_setenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, ""); /* Reset. */
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static int default_creds_gce_detection_httpcli_get_success_override(
@@ -917,7 +954,7 @@ static int default_creds_gce_detection_httpcli_get_success_override(
   response->hdrs = headers;
   GPR_ASSERT(strcmp(request->http.path, "/") == 0);
   GPR_ASSERT(strcmp(request->host, "metadata.google.internal") == 0);
-  grpc_exec_ctx_sched(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, on_done, GRPC_ERROR_NONE);
   return 1;
 }
 
@@ -975,7 +1012,7 @@ static int default_creds_gce_detection_httpcli_get_failure_override(
   GPR_ASSERT(strcmp(request->http.path, "/") == 0);
   GPR_ASSERT(strcmp(request->host, "metadata.google.internal") == 0);
   *response = http_response(200, "");
-  grpc_exec_ctx_sched(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, on_done, GRPC_ERROR_NONE);
   return 1;
 }
 
@@ -1142,6 +1179,8 @@ static void test_get_well_known_google_credentials_file_path(void) {
 }
 
 static void test_channel_creds_duplicate_without_call_creds(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+
   grpc_channel_credentials *channel_creds =
       grpc_fake_transport_security_credentials_create();
 
@@ -1149,21 +1188,23 @@ static void test_channel_creds_duplicate_without_call_creds(void) {
       grpc_channel_credentials_duplicate_without_call_credentials(
           channel_creds);
   GPR_ASSERT(dup == channel_creds);
-  grpc_channel_credentials_unref(dup);
+  grpc_channel_credentials_unref(&exec_ctx, dup);
 
   grpc_call_credentials *call_creds =
       grpc_access_token_credentials_create("blah", NULL);
   grpc_channel_credentials *composite_creds =
       grpc_composite_channel_credentials_create(channel_creds, call_creds,
                                                 NULL);
-  grpc_call_credentials_unref(call_creds);
+  grpc_call_credentials_unref(&exec_ctx, call_creds);
   dup = grpc_channel_credentials_duplicate_without_call_credentials(
       composite_creds);
   GPR_ASSERT(dup == channel_creds);
-  grpc_channel_credentials_unref(dup);
+  grpc_channel_credentials_unref(&exec_ctx, dup);
 
-  grpc_channel_credentials_unref(channel_creds);
-  grpc_channel_credentials_unref(composite_creds);
+  grpc_channel_credentials_unref(&exec_ctx, channel_creds);
+  grpc_channel_credentials_unref(&exec_ctx, composite_creds);
+
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 int main(int argc, char **argv) {
diff --git a/test/core/security/json_token_test.c b/test/core/security/json_token_test.c
index 201655881f242b683ad867f40621d3b9e0f8c234..5cebb09bb253301084423bedebb188342f79a835 100644
--- a/test/core/security/json_token_test.c
+++ b/test/core/security/json_token_test.c
@@ -44,6 +44,7 @@
 #include "src/core/lib/json/json.h"
 #include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
 #include "src/core/lib/security/util/b64.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "test/core/util/test_config.h"
 
 /* This JSON key was generated with the GCE console and revoked immediately.
@@ -220,6 +221,7 @@ static void test_parse_json_key_failure_no_private_key(void) {
 
 static grpc_json *parse_json_part_from_jwt(const char *str, size_t len,
                                            char **scratchpad) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   char *b64;
   char *decoded;
   grpc_json *json;
@@ -227,7 +229,7 @@ static grpc_json *parse_json_part_from_jwt(const char *str, size_t len,
   b64 = gpr_malloc(len + 1);
   strncpy(b64, str, len);
   b64[len] = '\0';
-  slice = grpc_base64_decode(b64, 1);
+  slice = grpc_base64_decode(&exec_ctx, b64, 1);
   GPR_ASSERT(!GRPC_SLICE_IS_EMPTY(slice));
   decoded = gpr_malloc(GRPC_SLICE_LENGTH(slice) + 1);
   strncpy(decoded, (const char *)GRPC_SLICE_START_PTR(slice),
@@ -237,6 +239,7 @@ static grpc_json *parse_json_part_from_jwt(const char *str, size_t len,
   gpr_free(b64);
   *scratchpad = decoded;
   grpc_slice_unref(slice);
+  grpc_exec_ctx_finish(&exec_ctx);
   return json;
 }
 
@@ -338,10 +341,12 @@ static void check_jwt_claim(grpc_json *claim, const char *expected_audience,
 static void check_jwt_signature(const char *b64_signature, RSA *rsa_key,
                                 const char *signed_data,
                                 size_t signed_data_size) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+
   EVP_MD_CTX *md_ctx = EVP_MD_CTX_create();
   EVP_PKEY *key = EVP_PKEY_new();
 
-  grpc_slice sig = grpc_base64_decode(b64_signature, 1);
+  grpc_slice sig = grpc_base64_decode(&exec_ctx, b64_signature, 1);
   GPR_ASSERT(!GRPC_SLICE_IS_EMPTY(sig));
   GPR_ASSERT(GRPC_SLICE_LENGTH(sig) == 128);
 
@@ -355,9 +360,11 @@ static void check_jwt_signature(const char *b64_signature, RSA *rsa_key,
   GPR_ASSERT(EVP_DigestVerifyFinal(md_ctx, GRPC_SLICE_START_PTR(sig),
                                    GRPC_SLICE_LENGTH(sig)) == 1);
 
-  grpc_slice_unref(sig);
+  grpc_slice_unref_internal(&exec_ctx, sig);
   if (key != NULL) EVP_PKEY_free(key);
   if (md_ctx != NULL) EVP_MD_CTX_destroy(md_ctx);
+
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static char *service_account_creds_jwt_encode_and_sign(
diff --git a/test/core/security/jwt_verifier_test.c b/test/core/security/jwt_verifier_test.c
index f8afba8d6d80eca71c23af0bb8ddde2a72a8ca48..a9bd976a39d1a1f2c5c114f80725a7c029890a8d 100644
--- a/test/core/security/jwt_verifier_test.c
+++ b/test/core/security/jwt_verifier_test.c
@@ -166,6 +166,13 @@ static const char claims_without_time_constraint[] =
     "  \"jti\": \"jwtuniqueid\","
     "  \"foo\": \"bar\"}";
 
+static const char claims_with_bad_subject[] =
+    "{ \"aud\": \"https://foo.com\","
+    "  \"iss\": \"evil@blah.foo.com\","
+    "  \"sub\": \"juju@blah.foo.com\","
+    "  \"jti\": \"jwtuniqueid\","
+    "  \"foo\": \"bar\"}";
+
 static const char invalid_claims[] =
     "{ \"aud\": \"https://foo.com\","
     "  \"iss\": 46," /* Issuer cannot be a number. */
@@ -179,13 +186,46 @@ typedef struct {
   const char *expected_subject;
 } verifier_test_config;
 
+static void test_jwt_issuer_email_domain(void) {
+  const char *d = grpc_jwt_issuer_email_domain("https://foo.com");
+  GPR_ASSERT(d == NULL);
+  d = grpc_jwt_issuer_email_domain("foo.com");
+  GPR_ASSERT(d == NULL);
+  d = grpc_jwt_issuer_email_domain("");
+  GPR_ASSERT(d == NULL);
+  d = grpc_jwt_issuer_email_domain("@");
+  GPR_ASSERT(d == NULL);
+  d = grpc_jwt_issuer_email_domain("bar@foo");
+  GPR_ASSERT(strcmp(d, "foo") == 0);
+  d = grpc_jwt_issuer_email_domain("bar@foo.com");
+  GPR_ASSERT(strcmp(d, "foo.com") == 0);
+  d = grpc_jwt_issuer_email_domain("bar@blah.foo.com");
+  GPR_ASSERT(strcmp(d, "foo.com") == 0);
+  d = grpc_jwt_issuer_email_domain("bar.blah@blah.foo.com");
+  GPR_ASSERT(strcmp(d, "foo.com") == 0);
+  d = grpc_jwt_issuer_email_domain("bar.blah@baz.blah.foo.com");
+  GPR_ASSERT(strcmp(d, "foo.com") == 0);
+
+  /* This is not a very good parser but make sure we do not crash on these weird
+     inputs. */
+  d = grpc_jwt_issuer_email_domain("@foo");
+  GPR_ASSERT(strcmp(d, "foo") == 0);
+  d = grpc_jwt_issuer_email_domain("bar@.");
+  GPR_ASSERT(d != NULL);
+  d = grpc_jwt_issuer_email_domain("bar@..");
+  GPR_ASSERT(d != NULL);
+  d = grpc_jwt_issuer_email_domain("bar@...");
+  GPR_ASSERT(d != NULL);
+}
+
 static void test_claims_success(void) {
   grpc_jwt_claims *claims;
   grpc_slice s = grpc_slice_from_copied_string(claims_without_time_constraint);
   grpc_json *json = grpc_json_parse_string_with_len(
       (char *)GRPC_SLICE_START_PTR(s), GRPC_SLICE_LENGTH(s));
   GPR_ASSERT(json != NULL);
-  claims = grpc_jwt_claims_from_json(json, s);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  claims = grpc_jwt_claims_from_json(&exec_ctx, json, s);
   GPR_ASSERT(claims != NULL);
   GPR_ASSERT(grpc_jwt_claims_json(claims) == json);
   GPR_ASSERT(strcmp(grpc_jwt_claims_audience(claims), "https://foo.com") == 0);
@@ -194,7 +234,8 @@ static void test_claims_success(void) {
   GPR_ASSERT(strcmp(grpc_jwt_claims_id(claims), "jwtuniqueid") == 0);
   GPR_ASSERT(grpc_jwt_claims_check(claims, "https://foo.com") ==
              GRPC_JWT_VERIFIER_OK);
-  grpc_jwt_claims_destroy(claims);
+  grpc_jwt_claims_destroy(&exec_ctx, claims);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_expired_claims_failure(void) {
@@ -206,7 +247,8 @@ static void test_expired_claims_failure(void) {
   gpr_timespec exp_exp = {120, 0, GPR_CLOCK_REALTIME};
   gpr_timespec exp_nbf = {60, 0, GPR_CLOCK_REALTIME};
   GPR_ASSERT(json != NULL);
-  claims = grpc_jwt_claims_from_json(json, s);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  claims = grpc_jwt_claims_from_json(&exec_ctx, json, s);
   GPR_ASSERT(claims != NULL);
   GPR_ASSERT(grpc_jwt_claims_json(claims) == json);
   GPR_ASSERT(strcmp(grpc_jwt_claims_audience(claims), "https://foo.com") == 0);
@@ -219,14 +261,17 @@ static void test_expired_claims_failure(void) {
 
   GPR_ASSERT(grpc_jwt_claims_check(claims, "https://foo.com") ==
              GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE);
-  grpc_jwt_claims_destroy(claims);
+  grpc_jwt_claims_destroy(&exec_ctx, claims);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_invalid_claims_failure(void) {
   grpc_slice s = grpc_slice_from_copied_string(invalid_claims);
   grpc_json *json = grpc_json_parse_string_with_len(
       (char *)GRPC_SLICE_START_PTR(s), GRPC_SLICE_LENGTH(s));
-  GPR_ASSERT(grpc_jwt_claims_from_json(json, s) == NULL);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  GPR_ASSERT(grpc_jwt_claims_from_json(&exec_ctx, json, s) == NULL);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_bad_audience_claims_failure(void) {
@@ -235,11 +280,28 @@ static void test_bad_audience_claims_failure(void) {
   grpc_json *json = grpc_json_parse_string_with_len(
       (char *)GRPC_SLICE_START_PTR(s), GRPC_SLICE_LENGTH(s));
   GPR_ASSERT(json != NULL);
-  claims = grpc_jwt_claims_from_json(json, s);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  claims = grpc_jwt_claims_from_json(&exec_ctx, json, s);
   GPR_ASSERT(claims != NULL);
   GPR_ASSERT(grpc_jwt_claims_check(claims, "https://bar.com") ==
              GRPC_JWT_VERIFIER_BAD_AUDIENCE);
-  grpc_jwt_claims_destroy(claims);
+  grpc_jwt_claims_destroy(&exec_ctx, claims);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+static void test_bad_subject_claims_failure(void) {
+  grpc_jwt_claims *claims;
+  grpc_slice s = grpc_slice_from_copied_string(claims_with_bad_subject);
+  grpc_json *json = grpc_json_parse_string_with_len(
+      (char *)GRPC_SLICE_START_PTR(s), GRPC_SLICE_LENGTH(s));
+  GPR_ASSERT(json != NULL);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  claims = grpc_jwt_claims_from_json(&exec_ctx, json, s);
+  GPR_ASSERT(claims != NULL);
+  GPR_ASSERT(grpc_jwt_claims_check(claims, "https://foo.com") ==
+             GRPC_JWT_VERIFIER_BAD_SUBJECT);
+  grpc_jwt_claims_destroy(&exec_ctx, claims);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static char *json_key_str(const char *last_part) {
@@ -294,18 +356,18 @@ static int httpcli_get_google_keys_for_email(
                     "/robot/v1/metadata/x509/"
                     "777-abaslkan11hlb6nmim3bpspl31ud@developer."
                     "gserviceaccount.com") == 0);
-  grpc_exec_ctx_sched(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, on_done, GRPC_ERROR_NONE);
   return 1;
 }
 
-static void on_verification_success(void *user_data,
+static void on_verification_success(grpc_exec_ctx *exec_ctx, void *user_data,
                                     grpc_jwt_verifier_status status,
                                     grpc_jwt_claims *claims) {
   GPR_ASSERT(status == GRPC_JWT_VERIFIER_OK);
   GPR_ASSERT(claims != NULL);
   GPR_ASSERT(user_data == (void *)expected_user_data);
   GPR_ASSERT(strcmp(grpc_jwt_claims_audience(claims), expected_audience) == 0);
-  grpc_jwt_claims_destroy(claims);
+  grpc_jwt_claims_destroy(exec_ctx, claims);
 }
 
 static void test_jwt_verifier_google_email_issuer_success(void) {
@@ -338,7 +400,7 @@ static int httpcli_get_custom_keys_for_email(
   GPR_ASSERT(request->handshaker == &grpc_httpcli_ssl);
   GPR_ASSERT(strcmp(request->host, "keys.bar.com") == 0);
   GPR_ASSERT(strcmp(request->http.path, "/jwk/foo@bar.com") == 0);
-  grpc_exec_ctx_sched(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, on_done, GRPC_ERROR_NONE);
   return 1;
 }
 
@@ -372,7 +434,7 @@ static int httpcli_get_jwk_set(grpc_exec_ctx *exec_ctx,
   GPR_ASSERT(request->handshaker == &grpc_httpcli_ssl);
   GPR_ASSERT(strcmp(request->host, "www.googleapis.com") == 0);
   GPR_ASSERT(strcmp(request->http.path, "/oauth2/v3/certs") == 0);
-  grpc_exec_ctx_sched(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, on_done, GRPC_ERROR_NONE);
   return 1;
 }
 
@@ -387,7 +449,7 @@ static int httpcli_get_openid_config(grpc_exec_ctx *exec_ctx,
   GPR_ASSERT(strcmp(request->http.path, GRPC_OPENID_CONFIG_URL_SUFFIX) == 0);
   grpc_httpcli_set_override(httpcli_get_jwk_set,
                             httpcli_post_should_not_be_called);
-  grpc_exec_ctx_sched(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, on_done, GRPC_ERROR_NONE);
   return 1;
 }
 
@@ -413,7 +475,8 @@ static void test_jwt_verifier_url_issuer_success(void) {
   grpc_httpcli_set_override(NULL, NULL);
 }
 
-static void on_verification_key_retrieval_error(void *user_data,
+static void on_verification_key_retrieval_error(grpc_exec_ctx *exec_ctx,
+                                                void *user_data,
                                                 grpc_jwt_verifier_status status,
                                                 grpc_jwt_claims *claims) {
   GPR_ASSERT(status == GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR);
@@ -427,7 +490,7 @@ static int httpcli_get_bad_json(grpc_exec_ctx *exec_ctx,
                                 grpc_httpcli_response *response) {
   *response = http_response(200, gpr_strdup("{\"bad\": \"stuff\"}"));
   GPR_ASSERT(request->handshaker == &grpc_httpcli_ssl);
-  grpc_exec_ctx_sched(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, on_done, GRPC_ERROR_NONE);
   return 1;
 }
 
@@ -483,7 +546,11 @@ static void corrupt_jwt_sig(char *jwt) {
   uint8_t *sig_bytes;
   char *last_dot = strrchr(jwt, '.');
   GPR_ASSERT(last_dot != NULL);
-  sig = grpc_base64_decode(last_dot + 1, 1);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    sig = grpc_base64_decode(&exec_ctx, last_dot + 1, 1);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
   GPR_ASSERT(!GRPC_SLICE_IS_EMPTY(sig));
   sig_bytes = GRPC_SLICE_START_PTR(sig);
   (*sig_bytes)++; /* Corrupt first byte. */
@@ -494,7 +561,8 @@ static void corrupt_jwt_sig(char *jwt) {
   grpc_slice_unref(sig);
 }
 
-static void on_verification_bad_signature(void *user_data,
+static void on_verification_bad_signature(grpc_exec_ctx *exec_ctx,
+                                          void *user_data,
                                           grpc_jwt_verifier_status status,
                                           grpc_jwt_claims *claims) {
   GPR_ASSERT(status == GRPC_JWT_VERIFIER_BAD_SIGNATURE);
@@ -535,7 +603,7 @@ static int httpcli_get_should_not_be_called(grpc_exec_ctx *exec_ctx,
   return 1;
 }
 
-static void on_verification_bad_format(void *user_data,
+static void on_verification_bad_format(grpc_exec_ctx *exec_ctx, void *user_data,
                                        grpc_jwt_verifier_status status,
                                        grpc_jwt_claims *claims) {
   GPR_ASSERT(status == GRPC_JWT_VERIFIER_BAD_FORMAT);
@@ -563,10 +631,12 @@ static void test_jwt_verifier_bad_format(void) {
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
   grpc_init();
+  test_jwt_issuer_email_domain();
   test_claims_success();
   test_expired_claims_failure();
   test_invalid_claims_failure();
   test_bad_audience_claims_failure();
+  test_bad_subject_claims_failure();
   test_jwt_verifier_google_email_issuer_success();
   test_jwt_verifier_custom_email_issuer_success();
   test_jwt_verifier_url_issuer_success();
diff --git a/test/core/security/oauth2_utils.c b/test/core/security/oauth2_utils.c
index 44a209258d8228c5703cd1c247d0e609139c8fa2..ff77af908a996fc1a802a390ae4b94ce52cf03e2 100644
--- a/test/core/security/oauth2_utils.c
+++ b/test/core/security/oauth2_utils.c
@@ -92,7 +92,8 @@ char *grpc_test_fetch_oauth2_token_with_credentials(
   request.pops = grpc_polling_entity_create_from_pollset(pollset);
   request.is_done = 0;
 
-  grpc_closure_init(&do_nothing_closure, do_nothing, NULL);
+  grpc_closure_init(&do_nothing_closure, do_nothing, NULL,
+                    grpc_schedule_on_exec_ctx);
 
   grpc_call_credentials_get_request_metadata(
       &exec_ctx, creds, &request.pops, null_ctx, on_oauth2_response, &request);
diff --git a/test/core/security/secure_endpoint_test.c b/test/core/security/secure_endpoint_test.c
index b5d95004fe450409cda8dabbcd118078da90fd55..e1d6ff9691fd1e4826538bfe464bc76a6481f4f0 100644
--- a/test/core/security/secure_endpoint_test.c
+++ b/test/core/security/secure_endpoint_test.c
@@ -42,6 +42,7 @@
 #include "src/core/lib/iomgr/endpoint_pair.h"
 #include "src/core/lib/iomgr/iomgr.h"
 #include "src/core/lib/security/transport/secure_endpoint.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/tsi/fake_transport_security.h"
 #include "test/core/util/test_config.h"
 
@@ -59,7 +60,7 @@ static grpc_endpoint_test_fixture secure_endpoint_create_fixture_tcp_socketpair(
   grpc_resource_quota *resource_quota =
       grpc_resource_quota_create("secure_endpoint_test");
   tcp = grpc_iomgr_create_endpoint_pair("fixture", resource_quota, slice_size);
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+  grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
   grpc_endpoint_add_to_pollset(&exec_ctx, tcp.client, g_pollset);
   grpc_endpoint_add_to_pollset(&exec_ctx, tcp.server, g_pollset);
 
@@ -158,7 +159,7 @@ static void test_leftover(grpc_endpoint_test_config config, size_t slice_size) {
   gpr_log(GPR_INFO, "Start test left over");
 
   grpc_slice_buffer_init(&incoming);
-  grpc_closure_init(&done_closure, inc_call_ctr, &n);
+  grpc_closure_init(&done_closure, inc_call_ctr, &n, grpc_schedule_on_exec_ctx);
   grpc_endpoint_read(&exec_ctx, f.client_ep, &incoming, &done_closure);
   grpc_exec_ctx_finish(&exec_ctx);
   GPR_ASSERT(n == 1);
@@ -170,8 +171,8 @@ static void test_leftover(grpc_endpoint_test_config config, size_t slice_size) {
   grpc_endpoint_destroy(&exec_ctx, f.client_ep);
   grpc_endpoint_destroy(&exec_ctx, f.server_ep);
   grpc_exec_ctx_finish(&exec_ctx);
-  grpc_slice_unref(s);
-  grpc_slice_buffer_destroy(&incoming);
+  grpc_slice_unref_internal(&exec_ctx, s);
+  grpc_slice_buffer_destroy_internal(&exec_ctx, &incoming);
 
   clean_up();
 }
@@ -191,7 +192,8 @@ int main(int argc, char **argv) {
   grpc_pollset_init(g_pollset, &g_mu);
   grpc_endpoint_tests(configs[0], g_pollset, g_mu);
   test_leftover(configs[1], 1);
-  grpc_closure_init(&destroyed, destroy_pollset, g_pollset);
+  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();
diff --git a/test/core/security/ssl_server_fuzzer.c b/test/core/security/ssl_server_fuzzer.c
index 04969765f59f59212db1c81f4ab9c9a4ae08acfb..55e8f5e78d23042b28cea890b008c66932ddba5c 100644
--- a/test/core/security/ssl_server_fuzzer.c
+++ b/test/core/security/ssl_server_fuzzer.c
@@ -58,17 +58,14 @@ struct handshake_state {
   bool done_callback_called;
 };
 
-static void on_secure_handshake_done(grpc_exec_ctx *exec_ctx, void *statep,
-                                     grpc_security_status status,
-                                     grpc_endpoint *secure_endpoint,
-                                     grpc_auth_context *auth_context) {
-  struct handshake_state *state = (struct handshake_state *)statep;
+static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg,
+                              grpc_error *error) {
+  grpc_handshaker_args *args = arg;
+  struct handshake_state *state = args->user_data;
   GPR_ASSERT(state->done_callback_called == false);
   state->done_callback_called = true;
   // The fuzzer should not pass the handshake.
-  GPR_ASSERT(status != GRPC_SECURITY_OK);
-  GPR_ASSERT(secure_endpoint == NULL);
-  GPR_ASSERT(auth_context == NULL);
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
 }
 
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
@@ -82,7 +79,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
       grpc_resource_quota_create("ssl_server_fuzzer");
   grpc_endpoint *mock_endpoint =
       grpc_mock_endpoint_create(discard_write, resource_quota);
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+  grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
 
   grpc_mock_endpoint_put_read(
       &exec_ctx, mock_endpoint,
@@ -106,17 +103,18 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
   // Create security connector
   grpc_server_security_connector *sc = NULL;
   grpc_security_status status =
-      grpc_server_credentials_create_security_connector(creds, &sc);
+      grpc_server_credentials_create_security_connector(&exec_ctx, creds, &sc);
   GPR_ASSERT(status == GRPC_SECURITY_OK);
-  sc->channel_args = NULL;
   gpr_timespec deadline = gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
                                        gpr_time_from_seconds(1, GPR_TIMESPAN));
 
   struct handshake_state state;
   state.done_callback_called = false;
-  grpc_server_security_connector_do_handshake(&exec_ctx, sc, NULL,
-                                              mock_endpoint, NULL, deadline,
-                                              on_secure_handshake_done, &state);
+  grpc_handshake_manager *handshake_mgr = grpc_handshake_manager_create();
+  grpc_server_security_connector_add_handshakers(&exec_ctx, sc, handshake_mgr);
+  grpc_handshake_manager_do_handshake(
+      &exec_ctx, handshake_mgr, mock_endpoint, NULL /* channel_args */,
+      deadline, NULL /* acceptor */, on_handshake_done, &state);
   grpc_exec_ctx_flush(&exec_ctx);
 
   // If the given string happens to be part of the correct client hello, the
@@ -129,7 +127,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
 
   GPR_ASSERT(state.done_callback_called);
 
-  GRPC_SECURITY_CONNECTOR_UNREF(&sc->base, "test");
+  grpc_handshake_manager_destroy(&exec_ctx, handshake_mgr);
+  GRPC_SECURITY_CONNECTOR_UNREF(&exec_ctx, &sc->base, "test");
   grpc_server_credentials_release(creds);
   grpc_slice_unref(cert_slice);
   grpc_slice_unref(key_slice);
diff --git a/test/core/security/verify_jwt.c b/test/core/security/verify_jwt.c
index 043d29e6bb94cf6b5115c0fde228d8f8ef70b058..bbd4a67ac1cba1cc80fb86869407ef1a22c91b7c 100644
--- a/test/core/security/verify_jwt.c
+++ b/test/core/security/verify_jwt.c
@@ -59,7 +59,7 @@ static void print_usage_and_exit(gpr_cmdline *cl, const char *argv0) {
   exit(1);
 }
 
-static void on_jwt_verification_done(void *user_data,
+static void on_jwt_verification_done(grpc_exec_ctx *exec_ctx, void *user_data,
                                      grpc_jwt_verifier_status status,
                                      grpc_jwt_claims *claims) {
   synchronizer *sync = user_data;
@@ -72,7 +72,7 @@ static void on_jwt_verification_done(void *user_data,
         grpc_json_dump_to_string((grpc_json *)grpc_jwt_claims_json(claims), 2);
     printf("Claims: \n\n%s\n", claims_str);
     gpr_free(claims_str);
-    grpc_jwt_claims_destroy(claims);
+    grpc_jwt_claims_destroy(exec_ctx, claims);
   } else {
     GPR_ASSERT(claims == NULL);
     fprintf(stderr, "Verification failed with error %s\n",
@@ -93,6 +93,7 @@ int main(int argc, char **argv) {
   char *aud = NULL;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
+  grpc_init();
   cl = gpr_cmdline_create("JWT verifier tool");
   gpr_cmdline_add_string(cl, "jwt", "JSON web token to verify", &jwt);
   gpr_cmdline_add_string(cl, "aud", "Audience for the JWT", &aud);
@@ -131,5 +132,6 @@ int main(int argc, char **argv) {
 
   grpc_jwt_verifier_destroy(verifier);
   gpr_cmdline_destroy(cl);
+  grpc_shutdown();
   return !sync.success;
 }
diff --git a/test/core/support/BUILD b/test/core/support/BUILD
new file mode 100644
index 0000000000000000000000000000000000000000..dfe952eb373e336c90cd6758ea78eb891d0399ff
--- /dev/null
+++ b/test/core/support/BUILD
@@ -0,0 +1,154 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+cc_test(
+    name = "alloc_test",
+    srcs = ["alloc_test.c"],
+    deps = ["//:gpr", "//test/core/util:gpr_test_util"],
+    copts = ['-std=c99']
+)
+
+cc_test(
+    name = "avl_test",
+    srcs = ["avl_test.c"],
+    deps = ["//:gpr", "//test/core/util:gpr_test_util"],
+    copts = ['-std=c99']
+)
+
+cc_test(
+    name = "backoff_test",
+    srcs = ["backoff_test.c"],
+    deps = ["//:gpr", "//test/core/util:gpr_test_util"],
+    copts = ['-std=c99']
+)
+
+cc_test(
+    name = "cmdline_test",
+    srcs = ["cmdline_test.c"],
+    deps = ["//:gpr", "//test/core/util:gpr_test_util"],
+    copts = ['-std=c99']
+)
+
+cc_test(
+    name = "cpu_test",
+    srcs = ["cpu_test.c"],
+    deps = ["//:gpr", "//test/core/util:gpr_test_util"],
+    copts = ['-std=c99']
+)
+
+cc_test(
+    name = "env_test",
+    srcs = ["env_test.c"],
+    deps = ["//:gpr", "//test/core/util:gpr_test_util"],
+    copts = ['-std=c99']
+)
+
+cc_test(
+    name = "histogram_test",
+    srcs = ["histogram_test.c"],
+    deps = ["//:gpr", "//test/core/util:gpr_test_util"],
+    copts = ['-std=c99']
+)
+
+cc_test(
+    name = "host_port_test",
+    srcs = ["host_port_test.c"],
+    deps = ["//:gpr", "//test/core/util:gpr_test_util"],
+    copts = ['-std=c99']
+)
+
+cc_test(
+    name = "log_test",
+    srcs = ["log_test.c"],
+    deps = ["//:gpr", "//test/core/util:gpr_test_util"],
+    copts = ['-std=c99']
+)
+
+cc_test(
+    name = "mpscq_test",
+    srcs = ["mpscq_test.c"],
+    deps = ["//:gpr", "//test/core/util:gpr_test_util"],
+    copts = ['-std=c99']
+)
+
+cc_test(
+    name = "murmur_hash_test",
+    srcs = ["murmur_hash_test.c"],
+    deps = ["//:gpr", "//test/core/util:gpr_test_util"],
+    copts = ['-std=c99']
+)
+
+cc_test(
+    name = "stack_lockfree_test",
+    srcs = ["stack_lockfree_test.c"],
+    deps = ["//:gpr", "//test/core/util:gpr_test_util"],
+    copts = ['-std=c99']
+)
+
+cc_test(
+    name = "string_test",
+    srcs = ["string_test.c"],
+    deps = ["//:gpr", "//test/core/util:gpr_test_util"],
+    copts = ['-std=c99']
+)
+
+cc_test(
+    name = "sync_test",
+    srcs = ["sync_test.c"],
+    deps = ["//:gpr", "//test/core/util:gpr_test_util"],
+    copts = ['-std=c99']
+)
+
+cc_test(
+    name = "thd_test",
+    srcs = ["thd_test.c"],
+    deps = ["//:gpr", "//test/core/util:gpr_test_util"],
+    copts = ['-std=c99']
+)
+
+cc_test(
+    name = "time_test",
+    srcs = ["time_test.c"],
+    deps = ["//:gpr", "//test/core/util:gpr_test_util"],
+    copts = ['-std=c99']
+)
+
+cc_test(
+    name = "tls_test",
+    srcs = ["tls_test.c"],
+    deps = ["//:gpr", "//test/core/util:gpr_test_util"],
+    copts = ['-std=c99']
+)
+
+cc_test(
+    name = "useful_test",
+    srcs = ["useful_test.c"],
+    deps = ["//:gpr", "//test/core/util:gpr_test_util"],
+    copts = ['-std=c99']
+)
diff --git a/test/core/support/backoff_test.c b/test/core/support/backoff_test.c
index 13cba7d750ea9c205cfb63c4f22cb92e733d838c..ad6e5a98f291918a9f46c582c3e7b13442aa45de 100644
--- a/test/core/support/backoff_test.c
+++ b/test/core/support/backoff_test.c
@@ -39,61 +39,110 @@
 
 static void test_constant_backoff(void) {
   gpr_backoff backoff;
-  gpr_backoff_init(&backoff, 1.0, 0.0, 1000, 1000);
+  gpr_backoff_init(&backoff, 200 /* initial timeout */, 1.0 /* multiplier */,
+                   0.0 /* jitter */, 100 /* min timeout */,
+                   1000 /* max timeout */);
 
   gpr_timespec now = gpr_time_0(GPR_TIMESPAN);
   gpr_timespec next = gpr_backoff_begin(&backoff, now);
-  GPR_ASSERT(gpr_time_to_millis(gpr_time_sub(next, now)) == 1000);
+  GPR_ASSERT(gpr_time_to_millis(gpr_time_sub(next, now)) == 200);
   for (int i = 0; i < 10000; i++) {
     next = gpr_backoff_step(&backoff, now);
-    GPR_ASSERT(gpr_time_to_millis(gpr_time_sub(next, now)) == 1000);
+    GPR_ASSERT(gpr_time_to_millis(gpr_time_sub(next, now)) == 200);
     now = next;
   }
 }
 
-static void test_no_jitter_backoff(void) {
+static void test_min_connect(void) {
   gpr_backoff backoff;
-  gpr_backoff_init(&backoff, 2.0, 0.0, 1, 513);
+  gpr_backoff_init(&backoff, 100 /* initial timeout */, 1.0 /* multiplier */,
+                   0.0 /* jitter */, 200 /* min timeout */,
+                   1000 /* max timeout */);
 
   gpr_timespec now = gpr_time_0(GPR_TIMESPAN);
   gpr_timespec next = gpr_backoff_begin(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(1, GPR_TIMESPAN), next) == 0);
-  now = next;
-  next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(3, GPR_TIMESPAN), next) == 0);
+  GPR_ASSERT(gpr_time_to_millis(gpr_time_sub(next, now)) == 200);
+}
+
+static void test_no_jitter_backoff(void) {
+  gpr_backoff backoff;
+  gpr_backoff_init(&backoff, 2 /* initial timeout */, 2.0 /* multiplier */,
+                   0.0 /* jitter */, 1 /* min timeout */,
+                   513 /* max timeout */);
+  // x_1 = 2
+  // x_n = 2**i + x_{i-1} ( = 2**(n+1) - 2 )
+  gpr_timespec now = gpr_time_0(GPR_TIMESPAN);
+  gpr_timespec next = gpr_backoff_begin(&backoff, now);
+  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(2, GPR_TIMESPAN), next) == 0);
   now = next;
   next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(7, GPR_TIMESPAN), next) == 0);
+  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(6, GPR_TIMESPAN), next) == 0);
   now = next;
   next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(15, GPR_TIMESPAN), next) == 0);
+  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(14, GPR_TIMESPAN), next) == 0);
   now = next;
   next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(31, GPR_TIMESPAN), next) == 0);
+  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(30, GPR_TIMESPAN), next) == 0);
   now = next;
   next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(63, GPR_TIMESPAN), next) == 0);
+  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(62, GPR_TIMESPAN), next) == 0);
   now = next;
   next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(127, GPR_TIMESPAN), next) == 0);
+  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(126, GPR_TIMESPAN), next) == 0);
   now = next;
   next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(255, GPR_TIMESPAN), next) == 0);
+  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(254, GPR_TIMESPAN), next) == 0);
   now = next;
   next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(511, GPR_TIMESPAN), next) == 0);
+  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(510, GPR_TIMESPAN), next) == 0);
   now = next;
   next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(1023, GPR_TIMESPAN), next) == 0);
+  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(1022, GPR_TIMESPAN), next) == 0);
   now = next;
   next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(1536, GPR_TIMESPAN), next) == 0);
+  // Hit the maximum timeout. From this point onwards, retries will increase
+  // only by max timeout.
+  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(1535, GPR_TIMESPAN), next) == 0);
   now = next;
   next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(2049, GPR_TIMESPAN), next) == 0);
+  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(2048, GPR_TIMESPAN), next) == 0);
   now = next;
   next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(2562, GPR_TIMESPAN), next) == 0);
+  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(2561, GPR_TIMESPAN), next) == 0);
+}
+
+static void test_jitter_backoff(void) {
+  const int64_t initial_timeout = 500;
+  const double jitter = 0.1;
+  gpr_backoff backoff;
+  gpr_backoff_init(&backoff, initial_timeout, 1.0 /* multiplier */, jitter,
+                   100 /* min timeout */, 1000 /* max timeout */);
+
+  backoff.rng_state = 0;  // force consistent PRNG
+
+  gpr_timespec now = gpr_time_0(GPR_TIMESPAN);
+  gpr_timespec next = gpr_backoff_begin(&backoff, now);
+  GPR_ASSERT(gpr_time_to_millis(gpr_time_sub(next, now)) == 500);
+
+  int64_t expected_next_lower_bound =
+      (int64_t)((double)initial_timeout * (1 - jitter));
+  int64_t expected_next_upper_bound =
+      (int64_t)((double)initial_timeout * (1 + jitter));
+
+  for (int i = 0; i < 10000; i++) {
+    next = gpr_backoff_step(&backoff, now);
+
+    // next-now must be within (jitter*100)% of the previous timeout.
+    const int64_t timeout_millis = gpr_time_to_millis(gpr_time_sub(next, now));
+    GPR_ASSERT(timeout_millis >= expected_next_lower_bound);
+    GPR_ASSERT(timeout_millis <= expected_next_upper_bound);
+
+    expected_next_lower_bound =
+        (int64_t)((double)timeout_millis * (1 - jitter));
+    expected_next_upper_bound =
+        (int64_t)((double)timeout_millis * (1 + jitter));
+    now = next;
+  }
 }
 
 int main(int argc, char **argv) {
@@ -101,7 +150,9 @@ int main(int argc, char **argv) {
   gpr_time_init();
 
   test_constant_backoff();
+  test_min_connect();
   test_no_jitter_backoff();
+  test_jitter_backoff();
 
   return 0;
 }
diff --git a/test/core/support/string_test.c b/test/core/support/string_test.c
index 78b77fad8e8791974af51ccf919c598e11bed6d4..af232db350d9b5740d43aedc54fee2803243b0ef 100644
--- a/test/core/support/string_test.c
+++ b/test/core/support/string_test.c
@@ -243,6 +243,8 @@ static void test_int64toa() {
 static void test_leftpad() {
   char *padded;
 
+  LOG_TEST_NAME("test_leftpad");
+
   padded = gpr_leftpad("foo", ' ', 5);
   GPR_ASSERT(0 == strcmp("  foo", padded));
   gpr_free(padded);
@@ -273,12 +275,25 @@ static void test_leftpad() {
 }
 
 static void test_stricmp(void) {
+  LOG_TEST_NAME("test_stricmp");
+
   GPR_ASSERT(0 == gpr_stricmp("hello", "hello"));
   GPR_ASSERT(0 == gpr_stricmp("HELLO", "hello"));
   GPR_ASSERT(gpr_stricmp("a", "b") < 0);
   GPR_ASSERT(gpr_stricmp("b", "a") > 0);
 }
 
+static void test_memrchr(void) {
+  LOG_TEST_NAME("test_memrchr");
+
+  GPR_ASSERT(NULL == gpr_memrchr(NULL, 'a', 0));
+  GPR_ASSERT(NULL == gpr_memrchr("", 'a', 0));
+  GPR_ASSERT(NULL == gpr_memrchr("hello", 'b', 5));
+  GPR_ASSERT(0 == strcmp((const char *)gpr_memrchr("hello", 'h', 5), "hello"));
+  GPR_ASSERT(0 == strcmp((const char *)gpr_memrchr("hello", 'o', 5), "o"));
+  GPR_ASSERT(0 == strcmp((const char *)gpr_memrchr("hello", 'l', 5), "lo"));
+}
+
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
   test_strdup();
@@ -291,5 +306,6 @@ int main(int argc, char **argv) {
   test_int64toa();
   test_leftpad();
   test_stricmp();
+  test_memrchr();
   return 0;
 }
diff --git a/test/core/surface/byte_buffer_reader_test.c b/test/core/surface/byte_buffer_reader_test.c
index d8d7a52d15a4c7c7ca755ae03be56151c7633be9..2f1f72f0e0a5c56373bd92e36e4977d24c4f52e7 100644
--- a/test/core/surface/byte_buffer_reader_test.c
+++ b/test/core/surface/byte_buffer_reader_test.c
@@ -40,9 +40,10 @@
 #include <grpc/support/log.h>
 #include <grpc/support/thd.h>
 #include <grpc/support/time.h>
-#include "test/core/util/test_config.h"
 
 #include "src/core/lib/compression/message_compress.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "test/core/util/test_config.h"
 
 #include <string.h>
 
@@ -145,7 +146,12 @@ static void read_compressed_slice(grpc_compression_algorithm algorithm,
   input_slice = grpc_slice_malloc(input_size);
   memset(GRPC_SLICE_START_PTR(input_slice), 'a', input_size);
   grpc_slice_buffer_add(&sliceb_in, input_slice); /* takes ownership */
-  GPR_ASSERT(grpc_msg_compress(algorithm, &sliceb_in, &sliceb_out));
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    GPR_ASSERT(
+        grpc_msg_compress(&exec_ctx, algorithm, &sliceb_in, &sliceb_out));
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
 
   buffer = grpc_raw_compressed_byte_buffer_create(sliceb_out.slices,
                                                   sliceb_out.count, algorithm);
diff --git a/test/core/surface/channel_create_test.c b/test/core/surface/channel_create_test.c
index ad7970aab9d220a4ec50c93a0a48b1dc107a7a4f..654e5324d980aa376a074913f755b3937f0cd2ad 100644
--- a/test/core/surface/channel_create_test.c
+++ b/test/core/surface/channel_create_test.c
@@ -31,9 +31,14 @@
  *
  */
 
+#include <string.h>
+
 #include <grpc/grpc.h>
 #include <grpc/support/log.h>
+
 #include "src/core/ext/client_channel/resolver_registry.h"
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/surface/channel.h"
 #include "test/core/util/test_config.h"
 
 void test_unknown_scheme_target(void) {
@@ -44,6 +49,13 @@ void test_unknown_scheme_target(void) {
 
   chan = grpc_insecure_channel_create("blah://blah", NULL, NULL);
   GPR_ASSERT(chan != NULL);
+
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_channel_element *elem =
+      grpc_channel_stack_element(grpc_channel_get_channel_stack(chan), 0);
+  GPR_ASSERT(0 == strcmp(elem->filter->name, "lame-client"));
+  grpc_exec_ctx_finish(&exec_ctx);
+
   grpc_channel_destroy(chan);
 }
 
diff --git a/test/core/surface/concurrent_connectivity_test.c b/test/core/surface/concurrent_connectivity_test.c
index f9f4675454635a9b48ac19d2b3852700e976d17b..8ebe8d07e4c6a7c6568dd4d7b35dee0789c286d8 100644
--- a/test/core/surface/concurrent_connectivity_test.c
+++ b/test/core/surface/concurrent_connectivity_test.c
@@ -105,8 +105,8 @@ void server_thread(void *vargs) {
 static void on_connect(grpc_exec_ctx *exec_ctx, void *vargs, grpc_endpoint *tcp,
                        grpc_pollset *accepting_pollset,
                        grpc_tcp_server_acceptor *acceptor) {
+  gpr_free(acceptor);
   struct server_thread_args *args = (struct server_thread_args *)vargs;
-  (void)acceptor;
   grpc_endpoint_shutdown(exec_ctx, tcp);
   grpc_endpoint_destroy(exec_ctx, tcp);
   GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(args->pollset, NULL));
@@ -229,9 +229,9 @@ int main(int argc, char **argv) {
   gpr_atm_rel_store(&args.stop, 1);
   gpr_thd_join(server);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_pollset_shutdown(
-      &exec_ctx, args.pollset,
-      grpc_closure_create(done_pollset_shutdown, args.pollset));
+  grpc_pollset_shutdown(&exec_ctx, args.pollset,
+                        grpc_closure_create(done_pollset_shutdown, args.pollset,
+                                            grpc_schedule_on_exec_ctx));
   grpc_exec_ctx_finish(&exec_ctx);
 
   grpc_shutdown();
diff --git a/test/core/surface/lame_client_test.c b/test/core/surface/lame_client_test.c
index 6afcefca9236c000e4482323fc96ab9ae9e2342f..b6db6a6b084578de5ad4077d200a9440953a77c2 100644
--- a/test/core/surface/lame_client_test.c
+++ b/test/core/surface/lame_client_test.c
@@ -62,7 +62,8 @@ void test_transport_op(grpc_channel *channel) {
   grpc_connectivity_state state = GRPC_CHANNEL_IDLE;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
-  grpc_closure_init(&transport_op_cb, verify_connectivity, &state);
+  grpc_closure_init(&transport_op_cb, verify_connectivity, &state,
+                    grpc_schedule_on_exec_ctx);
 
   op = grpc_make_transport_op(NULL);
   op->on_connectivity_state_change = &transport_op_cb;
@@ -71,7 +72,8 @@ void test_transport_op(grpc_channel *channel) {
   elem->filter->start_transport_op(&exec_ctx, elem, op);
   grpc_exec_ctx_finish(&exec_ctx);
 
-  grpc_closure_init(&transport_op_cb, do_nothing, NULL);
+  grpc_closure_init(&transport_op_cb, do_nothing, NULL,
+                    grpc_schedule_on_exec_ctx);
   op = grpc_make_transport_op(&transport_op_cb);
   elem->filter->start_transport_op(&exec_ctx, elem, op);
   grpc_exec_ctx_finish(&exec_ctx);
diff --git a/test/core/surface/public_headers_must_be_c89.c b/test/core/surface/public_headers_must_be_c89.c
index d4cfa25d44b8ec142dcfd4eb44139da4813ef9c6..610495377c97d4ec8e56a3a47effa2b1c93ac298 100644
--- a/test/core/surface/public_headers_must_be_c89.c
+++ b/test/core/surface/public_headers_must_be_c89.c
@@ -42,6 +42,7 @@
 #include <grpc/impl/codegen/byte_buffer_reader.h>
 #include <grpc/impl/codegen/compression_types.h>
 #include <grpc/impl/codegen/connectivity_state.h>
+#include <grpc/impl/codegen/exec_ctx_fwd.h>
 #include <grpc/impl/codegen/gpr_types.h>
 #include <grpc/impl/codegen/grpc_types.h>
 #include <grpc/impl/codegen/port_platform.h>
diff --git a/test/core/surface/secure_channel_create_test.c b/test/core/surface/secure_channel_create_test.c
index 444ebdc093816948c9aa829d2c9b489a4f631afe..ab4067dbe16737c93fa3e01005eb028c44b7fbd2 100644
--- a/test/core/surface/secure_channel_create_test.c
+++ b/test/core/surface/secure_channel_create_test.c
@@ -51,7 +51,9 @@ void test_unknown_scheme_target(void) {
   creds = grpc_fake_transport_security_credentials_create();
   chan = grpc_secure_channel_create(creds, "blah://blah", NULL, NULL);
   GPR_ASSERT(chan == NULL);
-  grpc_channel_credentials_unref(creds);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_channel_credentials_unref(&exec_ctx, creds);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 void test_security_connector_already_in_arg(void) {
diff --git a/test/core/surface/sequential_connectivity_test.c b/test/core/surface/sequential_connectivity_test.c
index fe87f119f2f1a9eaaa6226ad514f14f5cd6c694e..32927187626dff42b7218540ee4eeb9bb3bc709e 100644
--- a/test/core/surface/sequential_connectivity_test.c
+++ b/test/core/surface/sequential_connectivity_test.c
@@ -39,6 +39,7 @@
 #include <grpc/support/thd.h>
 
 #include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
 #include "test/core/end2end/data/ssl_test_data.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
@@ -162,7 +163,11 @@ static grpc_channel *secure_test_create_channel(const char *addr) {
       grpc_channel_args_copy_and_add(NULL, &ssl_name_override, 1);
   grpc_channel *channel =
       grpc_secure_channel_create(ssl_creds, addr, new_client_args, NULL);
-  grpc_channel_args_destroy(new_client_args);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_channel_args_destroy(&exec_ctx, new_client_args);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
   grpc_channel_credentials_release(ssl_creds);
   return channel;
 }
diff --git a/test/core/surface/server_chttp2_test.c b/test/core/surface/server_chttp2_test.c
index 6310b6f00b0100bcb9c9197a18cdf8f113fcec67..6c178abdad1987390206ee2f3fee55a7b01ba4ef 100644
--- a/test/core/surface/server_chttp2_test.c
+++ b/test/core/surface/server_chttp2_test.c
@@ -44,8 +44,11 @@
 #include "test/core/util/test_config.h"
 
 void test_unparsable_target(void) {
-  int port = grpc_server_add_insecure_http2_port(NULL, "[");
+  grpc_channel_args args = {0, NULL};
+  grpc_server *server = grpc_server_create(&args, NULL);
+  int port = grpc_server_add_insecure_http2_port(server, "[");
   GPR_ASSERT(port == 0);
+  grpc_server_destroy(server);
 }
 
 void test_add_same_port_twice() {
diff --git a/test/core/transport/chttp2/BUILD b/test/core/transport/chttp2/BUILD
new file mode 100644
index 0000000000000000000000000000000000000000..5dd205174f9a49b25015184d0f2b4db552304afc
--- /dev/null
+++ b/test/core/transport/chttp2/BUILD
@@ -0,0 +1,38 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+load("//test/core/util:grpc_fuzzer.bzl", "grpc_fuzzer")
+
+grpc_fuzzer(
+  name = "hpack_parser_fuzzer",
+  srcs = ["hpack_parser_fuzzer_test.c"],
+  deps = ["//:grpc", "//test/core/util:grpc_test_util"],
+  corpus = "hpack_parser_corpus"
+)
+
diff --git a/test/core/transport/chttp2/bin_decoder_test.c b/test/core/transport/chttp2/bin_decoder_test.c
index 7ddc30291add7703290f6a347a707e07c32b293e..221112ab21ba6519ccfb44b9a4ea03dd29fe8e16 100644
--- a/test/core/transport/chttp2/bin_decoder_test.c
+++ b/test/core/transport/chttp2/bin_decoder_test.c
@@ -38,13 +38,14 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/support/string.h"
 
 static int all_ok = 1;
 
-static void expect_slice_eq(grpc_slice expected, grpc_slice slice, char *debug,
-                            int line) {
+static void expect_slice_eq(grpc_exec_ctx *exec_ctx, grpc_slice expected,
+                            grpc_slice slice, char *debug, int line) {
   if (0 != grpc_slice_cmp(slice, expected)) {
     char *hs = grpc_dump_slice(slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
     char *he = grpc_dump_slice(expected, GPR_DUMP_HEX | GPR_DUMP_ASCII);
@@ -54,92 +55,104 @@ static void expect_slice_eq(grpc_slice expected, grpc_slice slice, char *debug,
     gpr_free(he);
     all_ok = 0;
   }
-  grpc_slice_unref(expected);
-  grpc_slice_unref(slice);
+  grpc_slice_unref_internal(exec_ctx, expected);
+  grpc_slice_unref_internal(exec_ctx, slice);
 }
 
-static grpc_slice base64_encode(const char *s) {
+static grpc_slice base64_encode(grpc_exec_ctx *exec_ctx, const char *s) {
   grpc_slice ss = grpc_slice_from_copied_string(s);
   grpc_slice out = grpc_chttp2_base64_encode(ss);
-  grpc_slice_unref(ss);
+  grpc_slice_unref_internal(exec_ctx, ss);
   return out;
 }
 
-static grpc_slice base64_decode(const char *s) {
+static grpc_slice base64_decode(grpc_exec_ctx *exec_ctx, const char *s) {
   grpc_slice ss = grpc_slice_from_copied_string(s);
-  grpc_slice out = grpc_chttp2_base64_decode(ss);
-  grpc_slice_unref(ss);
+  grpc_slice out = grpc_chttp2_base64_decode(exec_ctx, ss);
+  grpc_slice_unref_internal(exec_ctx, ss);
   return out;
 }
 
-static grpc_slice base64_decode_with_length(const char *s,
+static grpc_slice base64_decode_with_length(grpc_exec_ctx *exec_ctx,
+                                            const char *s,
                                             size_t output_length) {
   grpc_slice ss = grpc_slice_from_copied_string(s);
-  grpc_slice out = grpc_chttp2_base64_decode_with_length(ss, output_length);
-  grpc_slice_unref(ss);
+  grpc_slice out =
+      grpc_chttp2_base64_decode_with_length(exec_ctx, ss, output_length);
+  grpc_slice_unref_internal(exec_ctx, ss);
   return out;
 }
 
-#define EXPECT_SLICE_EQ(expected, slice)                                    \
-  expect_slice_eq(                                                          \
-      grpc_slice_from_copied_buffer(expected, sizeof(expected) - 1), slice, \
-      #slice, __LINE__);
+#define EXPECT_SLICE_EQ(exec_ctx, expected, slice)                             \
+  expect_slice_eq(                                                             \
+      exec_ctx, grpc_slice_from_copied_buffer(expected, sizeof(expected) - 1), \
+      slice, #slice, __LINE__);
 
-#define ENCODE_AND_DECODE(s) \
-  EXPECT_SLICE_EQ(           \
-      s, grpc_chttp2_base64_decode_with_length(base64_encode(s), strlen(s)));
+#define ENCODE_AND_DECODE(exec_ctx, s)                   \
+  EXPECT_SLICE_EQ(exec_ctx, s,                           \
+                  grpc_chttp2_base64_decode_with_length( \
+                      exec_ctx, base64_encode(exec_ctx, s), strlen(s)));
 
 int main(int argc, char **argv) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+
   /* ENCODE_AND_DECODE tests grpc_chttp2_base64_decode_with_length(), which
      takes encoded base64 strings without pad chars, but output length is
      required. */
   /* Base64 test vectors from RFC 4648 */
-  ENCODE_AND_DECODE("");
-  ENCODE_AND_DECODE("f");
-  ENCODE_AND_DECODE("foo");
-  ENCODE_AND_DECODE("fo");
-  ENCODE_AND_DECODE("foob");
-  ENCODE_AND_DECODE("fooba");
-  ENCODE_AND_DECODE("foobar");
+  ENCODE_AND_DECODE(&exec_ctx, "");
+  ENCODE_AND_DECODE(&exec_ctx, "f");
+  ENCODE_AND_DECODE(&exec_ctx, "foo");
+  ENCODE_AND_DECODE(&exec_ctx, "fo");
+  ENCODE_AND_DECODE(&exec_ctx, "foob");
+  ENCODE_AND_DECODE(&exec_ctx, "fooba");
+  ENCODE_AND_DECODE(&exec_ctx, "foobar");
 
-  ENCODE_AND_DECODE("\xc0\xc1\xc2\xc3\xc4\xc5");
+  ENCODE_AND_DECODE(&exec_ctx, "\xc0\xc1\xc2\xc3\xc4\xc5");
 
   /* Base64 test vectors from RFC 4648, with pad chars */
   /* BASE64("") = "" */
-  EXPECT_SLICE_EQ("", base64_decode(""));
+  EXPECT_SLICE_EQ(&exec_ctx, "", base64_decode(&exec_ctx, ""));
   /* BASE64("f") = "Zg==" */
-  EXPECT_SLICE_EQ("f", base64_decode("Zg=="));
+  EXPECT_SLICE_EQ(&exec_ctx, "f", base64_decode(&exec_ctx, "Zg=="));
   /* BASE64("fo") = "Zm8=" */
-  EXPECT_SLICE_EQ("fo", base64_decode("Zm8="));
+  EXPECT_SLICE_EQ(&exec_ctx, "fo", base64_decode(&exec_ctx, "Zm8="));
   /* BASE64("foo") = "Zm9v" */
-  EXPECT_SLICE_EQ("foo", base64_decode("Zm9v"));
+  EXPECT_SLICE_EQ(&exec_ctx, "foo", base64_decode(&exec_ctx, "Zm9v"));
   /* BASE64("foob") = "Zm9vYg==" */
-  EXPECT_SLICE_EQ("foob", base64_decode("Zm9vYg=="));
+  EXPECT_SLICE_EQ(&exec_ctx, "foob", base64_decode(&exec_ctx, "Zm9vYg=="));
   /* BASE64("fooba") = "Zm9vYmE=" */
-  EXPECT_SLICE_EQ("fooba", base64_decode("Zm9vYmE="));
+  EXPECT_SLICE_EQ(&exec_ctx, "fooba", base64_decode(&exec_ctx, "Zm9vYmE="));
   /* BASE64("foobar") = "Zm9vYmFy" */
-  EXPECT_SLICE_EQ("foobar", base64_decode("Zm9vYmFy"));
+  EXPECT_SLICE_EQ(&exec_ctx, "foobar", base64_decode(&exec_ctx, "Zm9vYmFy"));
 
-  EXPECT_SLICE_EQ("\xc0\xc1\xc2\xc3\xc4\xc5", base64_decode("wMHCw8TF"));
+  EXPECT_SLICE_EQ(&exec_ctx, "\xc0\xc1\xc2\xc3\xc4\xc5",
+                  base64_decode(&exec_ctx, "wMHCw8TF"));
 
   // Test illegal input length in grpc_chttp2_base64_decode
-  EXPECT_SLICE_EQ("", base64_decode("a"));
-  EXPECT_SLICE_EQ("", base64_decode("ab"));
-  EXPECT_SLICE_EQ("", base64_decode("abc"));
+  EXPECT_SLICE_EQ(&exec_ctx, "", base64_decode(&exec_ctx, "a"));
+  EXPECT_SLICE_EQ(&exec_ctx, "", base64_decode(&exec_ctx, "ab"));
+  EXPECT_SLICE_EQ(&exec_ctx, "", base64_decode(&exec_ctx, "abc"));
 
   // Test illegal charactors in grpc_chttp2_base64_decode
-  EXPECT_SLICE_EQ("", base64_decode("Zm:v"));
-  EXPECT_SLICE_EQ("", base64_decode("Zm=v"));
+  EXPECT_SLICE_EQ(&exec_ctx, "", base64_decode(&exec_ctx, "Zm:v"));
+  EXPECT_SLICE_EQ(&exec_ctx, "", base64_decode(&exec_ctx, "Zm=v"));
 
   // Test output_length longer than max possible output length in
   // grpc_chttp2_base64_decode_with_length
-  EXPECT_SLICE_EQ("", base64_decode_with_length("Zg", 2));
-  EXPECT_SLICE_EQ("", base64_decode_with_length("Zm8", 3));
-  EXPECT_SLICE_EQ("", base64_decode_with_length("Zm9v", 4));
+  EXPECT_SLICE_EQ(&exec_ctx, "", base64_decode_with_length(&exec_ctx, "Zg", 2));
+  EXPECT_SLICE_EQ(&exec_ctx, "",
+                  base64_decode_with_length(&exec_ctx, "Zm8", 3));
+  EXPECT_SLICE_EQ(&exec_ctx, "",
+                  base64_decode_with_length(&exec_ctx, "Zm9v", 4));
 
   // Test illegal charactors in grpc_chttp2_base64_decode_with_length
-  EXPECT_SLICE_EQ("", base64_decode_with_length("Zm:v", 3));
-  EXPECT_SLICE_EQ("", base64_decode_with_length("Zm=v", 3));
+  EXPECT_SLICE_EQ(&exec_ctx, "",
+                  base64_decode_with_length(&exec_ctx, "Zm:v", 3));
+  EXPECT_SLICE_EQ(&exec_ctx, "",
+                  base64_decode_with_length(&exec_ctx, "Zm=v", 3));
+
+  grpc_exec_ctx_finish(&exec_ctx);
 
   return all_ok ? 0 : 1;
 }
diff --git a/test/core/transport/chttp2/hpack_encoder_test.c b/test/core/transport/chttp2/hpack_encoder_test.c
index 91421e18f49856ae861fef61010465dfea04a0f1..1fd2540d652620114260fe8c686e94f49fd54f2a 100644
--- a/test/core/transport/chttp2/hpack_encoder_test.c
+++ b/test/core/transport/chttp2/hpack_encoder_test.c
@@ -41,6 +41,7 @@
 #include <grpc/support/string_util.h>
 
 #include "src/core/ext/transport/chttp2/transport/hpack_parser.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/transport/metadata.h"
@@ -59,8 +60,9 @@ size_t cap_to_delete = 0;
 
 /* verify that the output generated by encoding the stream matches the
    hexstring passed in */
-static void verify(size_t window_available, int eof, size_t expect_window_used,
-                   const char *expected, size_t nheaders, ...) {
+static void verify(grpc_exec_ctx *exec_ctx, size_t window_available, int eof,
+                   size_t expect_window_used, const char *expected,
+                   size_t nheaders, ...) {
   grpc_slice_buffer output;
   grpc_slice merged;
   grpc_slice expect = parse_hexstring(expected);
@@ -79,7 +81,7 @@ static void verify(size_t window_available, int eof, size_t expect_window_used,
       e[i - 1].next = &e[i];
       e[i].prev = &e[i - 1];
     }
-    e[i].md = grpc_mdelem_from_strings(key, value);
+    e[i].md = grpc_mdelem_from_strings(exec_ctx, key, value);
   }
   e[0].prev = NULL;
   e[nheaders - 1].next = NULL;
@@ -98,11 +100,11 @@ static void verify(size_t window_available, int eof, size_t expect_window_used,
 
   grpc_transport_one_way_stats stats;
   memset(&stats, 0, sizeof(stats));
-  grpc_chttp2_encode_header(&g_compressor, 0xdeadbeef, &b, eof, 16384, &stats,
-                            &output);
+  grpc_chttp2_encode_header(exec_ctx, &g_compressor, 0xdeadbeef, &b, eof, 16384,
+                            &stats, &output);
   merged = grpc_slice_merge(output.slices, output.count);
-  grpc_slice_buffer_destroy(&output);
-  grpc_metadata_batch_destroy(&b);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &output);
+  grpc_metadata_batch_destroy(exec_ctx, &b);
 
   if (0 != grpc_slice_cmp(merged, expect)) {
     char *expect_str = grpc_dump_slice(expect, GPR_DUMP_HEX | GPR_DUMP_ASCII);
@@ -115,31 +117,32 @@ static void verify(size_t window_available, int eof, size_t expect_window_used,
     g_failure = 1;
   }
 
-  grpc_slice_unref(merged);
-  grpc_slice_unref(expect);
+  grpc_slice_unref_internal(exec_ctx, merged);
+  grpc_slice_unref_internal(exec_ctx, expect);
 }
 
-static void test_basic_headers(void) {
+static void test_basic_headers(grpc_exec_ctx *exec_ctx) {
   int i;
 
-  verify(0, 0, 0, "000005 0104 deadbeef 40 0161 0161", 1, "a", "a");
-  verify(0, 0, 0, "000001 0104 deadbeef be", 1, "a", "a");
-  verify(0, 0, 0, "000001 0104 deadbeef be", 1, "a", "a");
-  verify(0, 0, 0, "000006 0104 deadbeef be 40 0162 0163", 2, "a", "a", "b",
+  verify(exec_ctx, 0, 0, 0, "000005 0104 deadbeef 40 0161 0161", 1, "a", "a");
+  verify(exec_ctx, 0, 0, 0, "000001 0104 deadbeef be", 1, "a", "a");
+  verify(exec_ctx, 0, 0, 0, "000001 0104 deadbeef be", 1, "a", "a");
+  verify(exec_ctx, 0, 0, 0, "000006 0104 deadbeef be 40 0162 0163", 2, "a", "a",
+         "b", "c");
+  verify(exec_ctx, 0, 0, 0, "000002 0104 deadbeef bf be", 2, "a", "a", "b",
          "c");
-  verify(0, 0, 0, "000002 0104 deadbeef bf be", 2, "a", "a", "b", "c");
-  verify(0, 0, 0, "000004 0104 deadbeef 7f 00 0164", 1, "a", "d");
+  verify(exec_ctx, 0, 0, 0, "000004 0104 deadbeef 7f 00 0164", 1, "a", "d");
 
   /* flush out what's there to make a few values look very popular */
   for (i = 0; i < 350; i++) {
-    verify(0, 0, 0, "000003 0104 deadbeef c0 bf be", 3, "a", "a", "b", "c", "a",
-           "d");
+    verify(exec_ctx, 0, 0, 0, "000003 0104 deadbeef c0 bf be", 3, "a", "a", "b",
+           "c", "a", "d");
   }
 
-  verify(0, 0, 0, "000006 0104 deadbeef c0 00 016b 0176", 2, "a", "a", "k",
-         "v");
+  verify(exec_ctx, 0, 0, 0, "000006 0104 deadbeef c0 00 016b 0176", 2, "a", "a",
+         "k", "v");
   /* this could be 000004 0104 deadbeef 0f 30 0176 also */
-  verify(0, 0, 0, "000004 0104 deadbeef 0f 2f 0176", 1, "a", "v");
+  verify(exec_ctx, 0, 0, 0, "000004 0104 deadbeef 0f 2f 0176", 1, "a", "v");
 }
 
 static void encode_int_to_str(int i, char *p) {
@@ -150,7 +153,7 @@ static void encode_int_to_str(int i, char *p) {
   p[2] = 0;
 }
 
-static void test_decode_table_overflow(void) {
+static void test_decode_table_overflow(grpc_exec_ctx *exec_ctx) {
   int i;
   char key[3], value[3];
   char *expect;
@@ -173,22 +176,24 @@ static void test_decode_table_overflow(void) {
     }
 
     if (i > 0) {
-      verify(0, 0, 0, expect, 2, "aa", "ba", key, value);
+      verify(exec_ctx, 0, 0, 0, expect, 2, "aa", "ba", key, value);
     } else {
-      verify(0, 0, 0, expect, 1, key, value);
+      verify(exec_ctx, 0, 0, 0, expect, 1, key, value);
     }
     gpr_free(expect);
   }
 
   /* if the above passes, then we must have just knocked this pair out of the
      decoder stack, and so we'll be forced to re-encode it */
-  verify(0, 0, 0, "000007 0104 deadbeef 40 026161 026261", 1, "aa", "ba");
+  verify(exec_ctx, 0, 0, 0, "000007 0104 deadbeef 40 026161 026261", 1, "aa",
+         "ba");
 }
 
-static void verify_table_size_change_match_elem_size(const char *key,
+static void verify_table_size_change_match_elem_size(grpc_exec_ctx *exec_ctx,
+                                                     const char *key,
                                                      const char *value) {
   grpc_slice_buffer output;
-  grpc_mdelem *elem = grpc_mdelem_from_strings(key, value);
+  grpc_mdelem *elem = grpc_mdelem_from_strings(exec_ctx, key, value);
   size_t elem_size = grpc_mdelem_get_size_in_hpack_table(elem);
   size_t initial_table_size = g_compressor.table_size;
   grpc_linked_mdelem *e = gpr_malloc(sizeof(*e));
@@ -203,25 +208,27 @@ static void verify_table_size_change_match_elem_size(const char *key,
 
   grpc_transport_one_way_stats stats;
   memset(&stats, 0, sizeof(stats));
-  grpc_chttp2_encode_header(&g_compressor, 0xdeadbeef, &b, 0, 16384, &stats,
-                            &output);
-  grpc_slice_buffer_destroy(&output);
-  grpc_metadata_batch_destroy(&b);
+  grpc_chttp2_encode_header(exec_ctx, &g_compressor, 0xdeadbeef, &b, 0, 16384,
+                            &stats, &output);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &output);
+  grpc_metadata_batch_destroy(exec_ctx, &b);
 
   GPR_ASSERT(g_compressor.table_size == elem_size + initial_table_size);
   gpr_free(e);
 }
 
-static void test_encode_header_size(void) {
-  verify_table_size_change_match_elem_size("hello", "world");
-  verify_table_size_change_match_elem_size("hello-bin", "world");
+static void test_encode_header_size(grpc_exec_ctx *exec_ctx) {
+  verify_table_size_change_match_elem_size(exec_ctx, "hello", "world");
+  verify_table_size_change_match_elem_size(exec_ctx, "hello-bin", "world");
 }
 
-static void run_test(void (*test)(), const char *name) {
+static void run_test(void (*test)(grpc_exec_ctx *exec_ctx), const char *name) {
   gpr_log(GPR_INFO, "RUN TEST: %s", name);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_chttp2_hpack_compressor_init(&g_compressor);
-  test();
-  grpc_chttp2_hpack_compressor_destroy(&g_compressor);
+  test(&exec_ctx);
+  grpc_chttp2_hpack_compressor_destroy(&exec_ctx, &g_compressor);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 int main(int argc, char **argv) {
diff --git a/test/core/transport/chttp2/hpack_parser_fuzzer_test.c b/test/core/transport/chttp2/hpack_parser_fuzzer_test.c
index 95acbf1a6813eee2372d2b5616378524e7b3849d..4e00f49b66466472b433e86a0f4d9424add130b5 100644
--- a/test/core/transport/chttp2/hpack_parser_fuzzer_test.c
+++ b/test/core/transport/chttp2/hpack_parser_fuzzer_test.c
@@ -44,7 +44,7 @@ bool squelch = true;
 bool leak_check = true;
 
 static void onhdr(grpc_exec_ctx *exec_ctx, void *ud, grpc_mdelem *md) {
-  GRPC_MDELEM_UNREF(md);
+  GRPC_MDELEM_UNREF(exec_ctx, md);
 }
 static void dont_log(gpr_log_func_args *args) {}
 
@@ -53,13 +53,13 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
   if (squelch) gpr_set_log_function(dont_log);
   grpc_init();
   grpc_chttp2_hpack_parser parser;
-  grpc_chttp2_hpack_parser_init(&parser);
-  parser.on_header = onhdr;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_chttp2_hpack_parser_init(&exec_ctx, &parser);
+  parser.on_header = onhdr;
   GRPC_ERROR_UNREF(
       grpc_chttp2_hpack_parser_parse(&exec_ctx, &parser, data, data + size));
+  grpc_chttp2_hpack_parser_destroy(&exec_ctx, &parser);
   grpc_exec_ctx_finish(&exec_ctx);
-  grpc_chttp2_hpack_parser_destroy(&parser);
   grpc_shutdown();
   return 0;
 }
diff --git a/test/core/transport/chttp2/hpack_parser_test.c b/test/core/transport/chttp2/hpack_parser_test.c
index e2813df70cf8708d1a5752001df22474180366f5..8f48849c974777ea28157d86400445965bb3307c 100644
--- a/test/core/transport/chttp2/hpack_parser_test.c
+++ b/test/core/transport/chttp2/hpack_parser_test.c
@@ -54,7 +54,7 @@ static void onhdr(grpc_exec_ctx *exec_ctx, void *ud, grpc_mdelem *md) {
   GPR_ASSERT(evalue);
   GPR_ASSERT(grpc_slice_str_cmp(md->key->slice, ekey) == 0);
   GPR_ASSERT(grpc_slice_str_cmp(md->value->slice, evalue) == 0);
-  GRPC_MDELEM_UNREF(md);
+  GRPC_MDELEM_UNREF(exec_ctx, md);
 }
 
 static void test_vector(grpc_chttp2_hpack_parser *parser,
@@ -94,8 +94,9 @@ static void test_vector(grpc_chttp2_hpack_parser *parser,
 
 static void test_vectors(grpc_slice_split_mode mode) {
   grpc_chttp2_hpack_parser parser;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
-  grpc_chttp2_hpack_parser_init(&parser);
+  grpc_chttp2_hpack_parser_init(&exec_ctx, &parser);
   /* D.2.1 */
   test_vector(&parser, mode,
               "400a 6375 7374 6f6d 2d6b 6579 0d63 7573"
@@ -111,9 +112,9 @@ static void test_vectors(grpc_slice_split_mode mode) {
               "password", "secret", NULL);
   /* D.2.4 */
   test_vector(&parser, mode, "82", ":method", "GET", NULL);
-  grpc_chttp2_hpack_parser_destroy(&parser);
+  grpc_chttp2_hpack_parser_destroy(&exec_ctx, &parser);
 
-  grpc_chttp2_hpack_parser_init(&parser);
+  grpc_chttp2_hpack_parser_init(&exec_ctx, &parser);
   /* D.3.1 */
   test_vector(&parser, mode,
               "8286 8441 0f77 7777 2e65 7861 6d70 6c65"
@@ -131,9 +132,9 @@ static void test_vectors(grpc_slice_split_mode mode) {
               ":method", "GET", ":scheme", "https", ":path", "/index.html",
               ":authority", "www.example.com", "custom-key", "custom-value",
               NULL);
-  grpc_chttp2_hpack_parser_destroy(&parser);
+  grpc_chttp2_hpack_parser_destroy(&exec_ctx, &parser);
 
-  grpc_chttp2_hpack_parser_init(&parser);
+  grpc_chttp2_hpack_parser_init(&exec_ctx, &parser);
   /* D.4.1 */
   test_vector(&parser, mode,
               "8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4"
@@ -151,11 +152,11 @@ static void test_vectors(grpc_slice_split_mode mode) {
               ":method", "GET", ":scheme", "https", ":path", "/index.html",
               ":authority", "www.example.com", "custom-key", "custom-value",
               NULL);
-  grpc_chttp2_hpack_parser_destroy(&parser);
+  grpc_chttp2_hpack_parser_destroy(&exec_ctx, &parser);
 
-  grpc_chttp2_hpack_parser_init(&parser);
-  grpc_chttp2_hptbl_set_max_bytes(&parser.table, 256);
-  grpc_chttp2_hptbl_set_current_table_size(&parser.table, 256);
+  grpc_chttp2_hpack_parser_init(&exec_ctx, &parser);
+  grpc_chttp2_hptbl_set_max_bytes(&exec_ctx, &parser.table, 256);
+  grpc_chttp2_hptbl_set_current_table_size(&exec_ctx, &parser.table, 256);
   /* D.5.1 */
   test_vector(&parser, mode,
               "4803 3330 3258 0770 7269 7661 7465 611d"
@@ -185,11 +186,11 @@ static void test_vectors(grpc_slice_split_mode mode) {
               "https://www.example.com", "content-encoding", "gzip",
               "set-cookie",
               "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", NULL);
-  grpc_chttp2_hpack_parser_destroy(&parser);
+  grpc_chttp2_hpack_parser_destroy(&exec_ctx, &parser);
 
-  grpc_chttp2_hpack_parser_init(&parser);
-  grpc_chttp2_hptbl_set_max_bytes(&parser.table, 256);
-  grpc_chttp2_hptbl_set_current_table_size(&parser.table, 256);
+  grpc_chttp2_hpack_parser_init(&exec_ctx, &parser);
+  grpc_chttp2_hptbl_set_max_bytes(&exec_ctx, &parser.table, 256);
+  grpc_chttp2_hptbl_set_current_table_size(&exec_ctx, &parser.table, 256);
   /* D.6.1 */
   test_vector(&parser, mode,
               "4882 6402 5885 aec3 771a 4b61 96d0 7abe"
@@ -216,7 +217,9 @@ static void test_vectors(grpc_slice_split_mode mode) {
               "https://www.example.com", "content-encoding", "gzip",
               "set-cookie",
               "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", NULL);
-  grpc_chttp2_hpack_parser_destroy(&parser);
+  grpc_chttp2_hpack_parser_destroy(&exec_ctx, &parser);
+
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 int main(int argc, char **argv) {
diff --git a/test/core/transport/chttp2/hpack_table_test.c b/test/core/transport/chttp2/hpack_table_test.c
index 1a7e2442caa39e66ac84b40f644d06f967d4637a..ef2ad66b5ca84c3958ed483405561c13b5290c73 100644
--- a/test/core/transport/chttp2/hpack_table_test.c
+++ b/test/core/transport/chttp2/hpack_table_test.c
@@ -59,9 +59,10 @@ static void assert_index(const grpc_chttp2_hptbl *tbl, uint32_t idx,
 }
 
 static void test_static_lookup(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_chttp2_hptbl tbl;
 
-  grpc_chttp2_hptbl_init(&tbl);
+  grpc_chttp2_hptbl_init(&exec_ctx, &tbl);
 
   LOG_TEST("test_static_lookup");
   assert_index(&tbl, 1, ":authority", "");
@@ -126,7 +127,8 @@ static void test_static_lookup(void) {
   assert_index(&tbl, 60, "via", "");
   assert_index(&tbl, 61, "www-authenticate", "");
 
-  grpc_chttp2_hptbl_destroy(&tbl);
+  grpc_chttp2_hptbl_destroy(&exec_ctx, &tbl);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_many_additions(void) {
@@ -137,15 +139,16 @@ static void test_many_additions(void) {
 
   LOG_TEST("test_many_additions");
 
-  grpc_chttp2_hptbl_init(&tbl);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_chttp2_hptbl_init(&exec_ctx, &tbl);
 
   for (i = 0; i < 100000; i++) {
     grpc_mdelem *elem;
     gpr_asprintf(&key, "K:%d", i);
     gpr_asprintf(&value, "VALUE:%d", i);
-    elem = grpc_mdelem_from_strings(key, value);
-    GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem) == GRPC_ERROR_NONE);
-    GRPC_MDELEM_UNREF(elem);
+    elem = grpc_mdelem_from_strings(&exec_ctx, key, value);
+    GPR_ASSERT(grpc_chttp2_hptbl_add(&exec_ctx, &tbl, elem) == GRPC_ERROR_NONE);
+    GRPC_MDELEM_UNREF(&exec_ctx, elem);
     assert_index(&tbl, 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY, key, value);
     gpr_free(key);
     gpr_free(value);
@@ -158,19 +161,23 @@ static void test_many_additions(void) {
     }
   }
 
-  grpc_chttp2_hptbl_destroy(&tbl);
+  grpc_chttp2_hptbl_destroy(&exec_ctx, &tbl);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static grpc_chttp2_hptbl_find_result find_simple(grpc_chttp2_hptbl *tbl,
                                                  const char *key,
                                                  const char *value) {
-  grpc_mdelem *md = grpc_mdelem_from_strings(key, value);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_mdelem *md = grpc_mdelem_from_strings(&exec_ctx, key, value);
   grpc_chttp2_hptbl_find_result r = grpc_chttp2_hptbl_find(tbl, md);
-  GRPC_MDELEM_UNREF(md);
+  GRPC_MDELEM_UNREF(&exec_ctx, md);
+  grpc_exec_ctx_finish(&exec_ctx);
   return r;
 }
 
 static void test_find(void) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_chttp2_hptbl tbl;
   uint32_t i;
   char buffer[32];
@@ -179,16 +186,16 @@ static void test_find(void) {
 
   LOG_TEST("test_find");
 
-  grpc_chttp2_hptbl_init(&tbl);
-  elem = grpc_mdelem_from_strings("abc", "xyz");
-  GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem) == GRPC_ERROR_NONE);
-  GRPC_MDELEM_UNREF(elem);
-  elem = grpc_mdelem_from_strings("abc", "123");
-  GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem) == GRPC_ERROR_NONE);
-  GRPC_MDELEM_UNREF(elem);
-  elem = grpc_mdelem_from_strings("x", "1");
-  GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem) == GRPC_ERROR_NONE);
-  GRPC_MDELEM_UNREF(elem);
+  grpc_chttp2_hptbl_init(&exec_ctx, &tbl);
+  elem = grpc_mdelem_from_strings(&exec_ctx, "abc", "xyz");
+  GPR_ASSERT(grpc_chttp2_hptbl_add(&exec_ctx, &tbl, elem) == GRPC_ERROR_NONE);
+  GRPC_MDELEM_UNREF(&exec_ctx, elem);
+  elem = grpc_mdelem_from_strings(&exec_ctx, "abc", "123");
+  GPR_ASSERT(grpc_chttp2_hptbl_add(&exec_ctx, &tbl, elem) == GRPC_ERROR_NONE);
+  GRPC_MDELEM_UNREF(&exec_ctx, elem);
+  elem = grpc_mdelem_from_strings(&exec_ctx, "x", "1");
+  GPR_ASSERT(grpc_chttp2_hptbl_add(&exec_ctx, &tbl, elem) == GRPC_ERROR_NONE);
+  GRPC_MDELEM_UNREF(&exec_ctx, elem);
 
   r = find_simple(&tbl, "abc", "123");
   GPR_ASSERT(r.index == 2 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
@@ -237,9 +244,9 @@ static void test_find(void) {
   /* overflow the string buffer, check find still works */
   for (i = 0; i < 10000; i++) {
     int64_ttoa(i, buffer);
-    elem = grpc_mdelem_from_strings("test", buffer);
-    GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem) == GRPC_ERROR_NONE);
-    GRPC_MDELEM_UNREF(elem);
+    elem = grpc_mdelem_from_strings(&exec_ctx, "test", buffer);
+    GPR_ASSERT(grpc_chttp2_hptbl_add(&exec_ctx, &tbl, elem) == GRPC_ERROR_NONE);
+    GRPC_MDELEM_UNREF(&exec_ctx, elem);
   }
 
   r = find_simple(&tbl, "abc", "123");
@@ -267,7 +274,8 @@ static void test_find(void) {
   GPR_ASSERT(r.index != 0);
   GPR_ASSERT(r.has_value == 0);
 
-  grpc_chttp2_hptbl_destroy(&tbl);
+  grpc_chttp2_hptbl_destroy(&exec_ctx, &tbl);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 int main(int argc, char **argv) {
diff --git a/test/core/transport/connectivity_state_test.c b/test/core/transport/connectivity_state_test.c
index 1050059eff7b9dcae984e53f0a13b87bacfaf50b..3520ef0a80de24fab332da568e507f11e3954fcc 100644
--- a/test/core/transport/connectivity_state_test.c
+++ b/test/core/transport/connectivity_state_test.c
@@ -86,7 +86,8 @@ static void test_check(void) {
 
 static void test_subscribe_then_unsubscribe(void) {
   grpc_connectivity_state_tracker tracker;
-  grpc_closure *closure = grpc_closure_create(must_fail, THE_ARG);
+  grpc_closure *closure =
+      grpc_closure_create(must_fail, THE_ARG, grpc_schedule_on_exec_ctx);
   grpc_connectivity_state state = GRPC_CHANNEL_IDLE;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   gpr_log(GPR_DEBUG, "test_subscribe_then_unsubscribe");
@@ -109,7 +110,8 @@ static void test_subscribe_then_unsubscribe(void) {
 
 static void test_subscribe_then_destroy(void) {
   grpc_connectivity_state_tracker tracker;
-  grpc_closure *closure = grpc_closure_create(must_succeed, THE_ARG);
+  grpc_closure *closure =
+      grpc_closure_create(must_succeed, THE_ARG, grpc_schedule_on_exec_ctx);
   grpc_connectivity_state state = GRPC_CHANNEL_IDLE;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   gpr_log(GPR_DEBUG, "test_subscribe_then_destroy");
@@ -128,7 +130,8 @@ static void test_subscribe_then_destroy(void) {
 
 static void test_subscribe_with_failure_then_destroy(void) {
   grpc_connectivity_state_tracker tracker;
-  grpc_closure *closure = grpc_closure_create(must_fail, THE_ARG);
+  grpc_closure *closure =
+      grpc_closure_create(must_fail, THE_ARG, grpc_schedule_on_exec_ctx);
   grpc_connectivity_state state = GRPC_CHANNEL_SHUTDOWN;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   gpr_log(GPR_DEBUG, "test_subscribe_with_failure_then_destroy");
diff --git a/test/core/transport/metadata_test.c b/test/core/transport/metadata_test.c
index 5c89d8530ad6c4e7c25493f4055929dedb2ad81c..3625043d0774e986d0f1b6f9a81e174f28c62e7f 100644
--- a/test/core/transport/metadata_test.c
+++ b/test/core/transport/metadata_test.c
@@ -42,6 +42,7 @@
 #include <grpc/support/string_util.h>
 
 #include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/transport/static_metadata.h"
 #include "test/core/util/test_config.h"
@@ -63,6 +64,7 @@ static void test_create_string(void) {
   LOG_TEST("test_create_string");
 
   grpc_init();
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   s1 = grpc_mdstr_from_string("hello");
   s2 = grpc_mdstr_from_string("hello");
   s3 = grpc_mdstr_from_string("very much not hello");
@@ -70,9 +72,10 @@ static void test_create_string(void) {
   GPR_ASSERT(s3 != s1);
   GPR_ASSERT(grpc_slice_str_cmp(s1->slice, "hello") == 0);
   GPR_ASSERT(grpc_slice_str_cmp(s3->slice, "very much not hello") == 0);
-  GRPC_MDSTR_UNREF(s1);
-  GRPC_MDSTR_UNREF(s2);
-  GRPC_MDSTR_UNREF(s3);
+  GRPC_MDSTR_UNREF(&exec_ctx, s1);
+  GRPC_MDSTR_UNREF(&exec_ctx, s2);
+  GRPC_MDSTR_UNREF(&exec_ctx, s3);
+  grpc_exec_ctx_finish(&exec_ctx);
   grpc_shutdown();
 }
 
@@ -82,9 +85,10 @@ static void test_create_metadata(void) {
   LOG_TEST("test_create_metadata");
 
   grpc_init();
-  m1 = grpc_mdelem_from_strings("a", "b");
-  m2 = grpc_mdelem_from_strings("a", "b");
-  m3 = grpc_mdelem_from_strings("a", "c");
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  m1 = grpc_mdelem_from_strings(&exec_ctx, "a", "b");
+  m2 = grpc_mdelem_from_strings(&exec_ctx, "a", "b");
+  m3 = grpc_mdelem_from_strings(&exec_ctx, "a", "c");
   GPR_ASSERT(m1 == m2);
   GPR_ASSERT(m3 != m1);
   GPR_ASSERT(m3->key == m1->key);
@@ -92,9 +96,10 @@ static void test_create_metadata(void) {
   GPR_ASSERT(grpc_slice_str_cmp(m1->key->slice, "a") == 0);
   GPR_ASSERT(grpc_slice_str_cmp(m1->value->slice, "b") == 0);
   GPR_ASSERT(grpc_slice_str_cmp(m3->value->slice, "c") == 0);
-  GRPC_MDELEM_UNREF(m1);
-  GRPC_MDELEM_UNREF(m2);
-  GRPC_MDELEM_UNREF(m3);
+  GRPC_MDELEM_UNREF(&exec_ctx, m1);
+  GRPC_MDELEM_UNREF(&exec_ctx, m2);
+  GRPC_MDELEM_UNREF(&exec_ctx, m3);
+  grpc_exec_ctx_finish(&exec_ctx);
   grpc_shutdown();
 }
 
@@ -105,11 +110,14 @@ static void test_create_many_ephemeral_metadata(void) {
   LOG_TEST("test_create_many_ephemeral_metadata");
 
   grpc_init();
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   /* add, and immediately delete a bunch of different elements */
   for (i = 0; i < MANY; i++) {
     gpr_ltoa(i, buffer);
-    GRPC_MDELEM_UNREF(grpc_mdelem_from_strings("a", buffer));
+    GRPC_MDELEM_UNREF(&exec_ctx,
+                      grpc_mdelem_from_strings(&exec_ctx, "a", buffer));
   }
+  grpc_exec_ctx_finish(&exec_ctx);
   grpc_shutdown();
 }
 
@@ -122,22 +130,24 @@ static void test_create_many_persistant_metadata(void) {
   LOG_TEST("test_create_many_persistant_metadata");
 
   grpc_init();
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   /* add phase */
   for (i = 0; i < MANY; i++) {
     gpr_ltoa(i, buffer);
-    created[i] = grpc_mdelem_from_strings("a", buffer);
+    created[i] = grpc_mdelem_from_strings(&exec_ctx, "a", buffer);
   }
   /* verify phase */
   for (i = 0; i < MANY; i++) {
     gpr_ltoa(i, buffer);
-    md = grpc_mdelem_from_strings("a", buffer);
+    md = grpc_mdelem_from_strings(&exec_ctx, "a", buffer);
     GPR_ASSERT(md == created[i]);
-    GRPC_MDELEM_UNREF(md);
+    GRPC_MDELEM_UNREF(&exec_ctx, md);
   }
   /* cleanup phase */
   for (i = 0; i < MANY; i++) {
-    GRPC_MDELEM_UNREF(created[i]);
+    GRPC_MDELEM_UNREF(&exec_ctx, created[i]);
   }
+  grpc_exec_ctx_finish(&exec_ctx);
   grpc_shutdown();
 
   gpr_free(created);
@@ -147,9 +157,11 @@ static void test_spin_creating_the_same_thing(void) {
   LOG_TEST("test_spin_creating_the_same_thing");
 
   grpc_init();
-  GRPC_MDELEM_UNREF(grpc_mdelem_from_strings("a", "b"));
-  GRPC_MDELEM_UNREF(grpc_mdelem_from_strings("a", "b"));
-  GRPC_MDELEM_UNREF(grpc_mdelem_from_strings("a", "b"));
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_from_strings(&exec_ctx, "a", "b"));
+  GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_from_strings(&exec_ctx, "a", "b"));
+  GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_from_strings(&exec_ctx, "a", "b"));
+  grpc_exec_ctx_finish(&exec_ctx);
   grpc_shutdown();
 }
 
@@ -164,6 +176,7 @@ static void test_things_stick_around(void) {
   LOG_TEST("test_things_stick_around");
 
   grpc_init();
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
   for (i = 0; i < nstrs; i++) {
     gpr_asprintf(&buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%" PRIuPTR "x", i);
@@ -174,7 +187,7 @@ static void test_things_stick_around(void) {
 
   for (i = 0; i < nstrs; i++) {
     GRPC_MDSTR_REF(strs[i]);
-    GRPC_MDSTR_UNREF(strs[i]);
+    GRPC_MDSTR_UNREF(&exec_ctx, strs[i]);
   }
 
   for (i = 0; i < nstrs; i++) {
@@ -186,17 +199,18 @@ static void test_things_stick_around(void) {
   }
 
   for (i = 0; i < nstrs; i++) {
-    GRPC_MDSTR_UNREF(strs[shuf[i]]);
+    GRPC_MDSTR_UNREF(&exec_ctx, strs[shuf[i]]);
     for (j = i + 1; j < nstrs; j++) {
       gpr_asprintf(&buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%" PRIuPTR "x",
                    shuf[j]);
       test = grpc_mdstr_from_string(buffer);
       GPR_ASSERT(test == strs[shuf[j]]);
-      GRPC_MDSTR_UNREF(test);
+      GRPC_MDSTR_UNREF(&exec_ctx, test);
       gpr_free(buffer);
     }
   }
 
+  grpc_exec_ctx_finish(&exec_ctx);
   grpc_shutdown();
   gpr_free(strs);
   gpr_free(shuf);
@@ -210,19 +224,21 @@ static void test_slices_work(void) {
   LOG_TEST("test_slices_work");
 
   grpc_init();
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
   str = grpc_mdstr_from_string(
       "123456789012345678901234567890123456789012345678901234567890");
   slice = grpc_slice_ref(str->slice);
-  GRPC_MDSTR_UNREF(str);
-  grpc_slice_unref(slice);
+  GRPC_MDSTR_UNREF(&exec_ctx, str);
+  grpc_slice_unref_internal(&exec_ctx, slice);
 
   str = grpc_mdstr_from_string(
       "123456789012345678901234567890123456789012345678901234567890");
   slice = grpc_slice_ref(str->slice);
-  grpc_slice_unref(slice);
-  GRPC_MDSTR_UNREF(str);
+  grpc_slice_unref_internal(&exec_ctx, slice);
+  GRPC_MDSTR_UNREF(&exec_ctx, str);
 
+  grpc_exec_ctx_finish(&exec_ctx);
   grpc_shutdown();
 }
 
@@ -234,13 +250,15 @@ static void test_base64_and_huffman_works(void) {
   LOG_TEST("test_base64_and_huffman_works");
 
   grpc_init();
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   str = grpc_mdstr_from_string("abcdefg");
   slice1 = grpc_mdstr_as_base64_encoded_and_huffman_compressed(str);
   slice2 = grpc_chttp2_base64_encode_and_huffman_compress(str->slice);
   GPR_ASSERT(0 == grpc_slice_cmp(slice1, slice2));
 
-  grpc_slice_unref(slice2);
-  GRPC_MDSTR_UNREF(str);
+  grpc_slice_unref_internal(&exec_ctx, slice2);
+  GRPC_MDSTR_UNREF(&exec_ctx, str);
+  grpc_exec_ctx_finish(&exec_ctx);
   grpc_shutdown();
 }
 
@@ -251,29 +269,33 @@ static void test_user_data_works(void) {
   LOG_TEST("test_user_data_works");
 
   grpc_init();
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   ud1 = gpr_malloc(sizeof(int));
   *ud1 = 1;
   ud2 = gpr_malloc(sizeof(int));
   *ud2 = 2;
-  md = grpc_mdelem_from_strings("abc", "123");
+  md = grpc_mdelem_from_strings(&exec_ctx, "abc", "123");
   grpc_mdelem_set_user_data(md, gpr_free, ud1);
   grpc_mdelem_set_user_data(md, gpr_free, ud2);
   GPR_ASSERT(grpc_mdelem_get_user_data(md, gpr_free) == ud1);
-  GRPC_MDELEM_UNREF(md);
+  GRPC_MDELEM_UNREF(&exec_ctx, md);
+  grpc_exec_ctx_finish(&exec_ctx);
   grpc_shutdown();
 }
 
-static void verify_ascii_header_size(const char *key, const char *value) {
-  grpc_mdelem *elem = grpc_mdelem_from_strings(key, value);
+static void verify_ascii_header_size(grpc_exec_ctx *exec_ctx, const char *key,
+                                     const char *value) {
+  grpc_mdelem *elem = grpc_mdelem_from_strings(exec_ctx, key, value);
   size_t elem_size = grpc_mdelem_get_size_in_hpack_table(elem);
   size_t expected_size = 32 + strlen(key) + strlen(value);
   GPR_ASSERT(expected_size == elem_size);
-  GRPC_MDELEM_UNREF(elem);
+  GRPC_MDELEM_UNREF(exec_ctx, elem);
 }
 
-static void verify_binary_header_size(const char *key, const uint8_t *value,
-                                      size_t value_len) {
-  grpc_mdelem *elem = grpc_mdelem_from_string_and_buffer(key, value, value_len);
+static void verify_binary_header_size(grpc_exec_ctx *exec_ctx, const char *key,
+                                      const uint8_t *value, size_t value_len) {
+  grpc_mdelem *elem =
+      grpc_mdelem_from_string_and_buffer(exec_ctx, key, value, value_len);
   GPR_ASSERT(grpc_is_binary_header(key, strlen(key)));
   size_t elem_size = grpc_mdelem_get_size_in_hpack_table(elem);
   grpc_slice value_slice =
@@ -281,33 +303,37 @@ static void verify_binary_header_size(const char *key, const uint8_t *value,
   grpc_slice base64_encoded = grpc_chttp2_base64_encode(value_slice);
   size_t expected_size = 32 + strlen(key) + GRPC_SLICE_LENGTH(base64_encoded);
   GPR_ASSERT(expected_size == elem_size);
-  grpc_slice_unref(value_slice);
-  grpc_slice_unref(base64_encoded);
-  GRPC_MDELEM_UNREF(elem);
+  grpc_slice_unref_internal(exec_ctx, value_slice);
+  grpc_slice_unref_internal(exec_ctx, base64_encoded);
+  GRPC_MDELEM_UNREF(exec_ctx, elem);
 }
 
 #define BUFFER_SIZE 64
 static void test_mdelem_sizes_in_hpack(void) {
   LOG_TEST("test_mdelem_size");
   grpc_init();
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
   uint8_t binary_value[BUFFER_SIZE] = {0};
   for (uint8_t i = 0; i < BUFFER_SIZE; i++) {
     binary_value[i] = i;
   }
 
-  verify_ascii_header_size("hello", "world");
-  verify_ascii_header_size("hello", "worldxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
-  verify_ascii_header_size(":scheme", "http");
+  verify_ascii_header_size(&exec_ctx, "hello", "world");
+  verify_ascii_header_size(&exec_ctx, "hello",
+                           "worldxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+  verify_ascii_header_size(&exec_ctx, ":scheme", "http");
 
   for (uint8_t i = 0; i < BUFFER_SIZE; i++) {
-    verify_binary_header_size("hello-bin", binary_value, i);
+    verify_binary_header_size(&exec_ctx, "hello-bin", binary_value, i);
   }
 
   const char *static_metadata = grpc_static_metadata_strings[0];
   memcpy(binary_value, static_metadata, strlen(static_metadata));
-  verify_binary_header_size("hello-bin", binary_value, strlen(static_metadata));
+  verify_binary_header_size(&exec_ctx, "hello-bin", binary_value,
+                            strlen(static_metadata));
 
+  grpc_exec_ctx_finish(&exec_ctx);
   grpc_shutdown();
 }
 
diff --git a/test/core/util/BUILD b/test/core/util/BUILD
new file mode 100644
index 0000000000000000000000000000000000000000..e50e595d039b4ad5aa603432b679282862c0d0ef
--- /dev/null
+++ b/test/core/util/BUILD
@@ -0,0 +1,58 @@
+
+
+cc_library(
+    name = "gpr_test_util",
+    srcs = [
+        "test_config.c",
+        "memory_counters.c",
+    ],
+    hdrs = [
+        "test_config.h",
+        "memory_counters.h",
+    ],
+    deps = ["//:gpr"],
+    visibility = ["//:__subpackages__"],
+)
+
+cc_library(
+    name = "grpc_test_util",
+    srcs = [
+        "grpc_profiler.c",
+        "mock_endpoint.c",
+        "parse_hexstring.c",
+        "passthru_endpoint.c",
+        "port_posix.c",
+        "port_server_client.c",
+        "port_windows.c",
+        "reconnect_server.c",
+        "slice_splitter.c",
+        "test_tcp_server.c",
+    ],
+    hdrs = [
+        "grpc_profiler.h",
+        "mock_endpoint.h",
+        "parse_hexstring.h",
+        "passthru_endpoint.h",
+        "port.h",
+        "port_server_client.h",
+        "reconnect_server.h",
+        "slice_splitter.h",
+        "test_tcp_server.h",
+    ],
+    deps = [":gpr_test_util", "//:grpc"],
+    visibility = ["//test:__subpackages__"],
+    copts = ["-std=c99"],
+)
+
+cc_library(
+  name = "one_corpus_entry_fuzzer",
+  srcs = ["one_corpus_entry_fuzzer.c"],
+  deps = [":gpr_test_util", "//:grpc"],
+  visibility = ["//test:__subpackages__"],
+)
+
+sh_library(
+  name = "fuzzer_one_entry_runner",
+  srcs = ["fuzzer_one_entry_runner.sh"],
+  visibility = ["//test:__subpackages__"],
+)
diff --git a/src/python/grpcio_health_checking/grpc/__init__.py b/test/core/util/fuzzer_one_entry_runner.sh
old mode 100644
new mode 100755
similarity index 94%
rename from src/python/grpcio_health_checking/grpc/__init__.py
rename to test/core/util/fuzzer_one_entry_runner.sh
index fcc7048815f6b99f1a6d19a86484d172fa4cd4d1..a0558a9c09e2b52c2943476f5658c9834228cec0
--- a/src/python/grpcio_health_checking/grpc/__init__.py
+++ b/test/core/util/fuzzer_one_entry_runner.sh
@@ -1,4 +1,8 @@
-# Copyright 2015, Google Inc.
+#!/bin/sh
+
+# Test runner for fuzzer tests from bazel
+
+# Copyright 2017, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -26,5 +30,4 @@
 # 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.
-
-__import__('pkg_resources').declare_namespace(__name__)
+$1 $2
diff --git a/test/core/util/grpc_fuzzer.bzl b/test/core/util/grpc_fuzzer.bzl
new file mode 100644
index 0000000000000000000000000000000000000000..2f552a9fdb093c146c360f3bfb286c9de6cebcde
--- /dev/null
+++ b/test/core/util/grpc_fuzzer.bzl
@@ -0,0 +1,43 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+def grpc_fuzzer(name, corpus, srcs = [], deps = [], **kwargs):
+  native.cc_binary(
+    name = '%s/one_entry.bin' % name,
+    srcs = srcs,
+    deps = deps + ["//test/core/util:one_corpus_entry_fuzzer"],
+    **kwargs
+  )
+  for entry in native.glob(['%s/*' % corpus]):
+    native.sh_test(
+      name = '%s/one_entry/%s' % (name, entry),
+      data = [':%s/one_entry.bin' % name, entry],
+      srcs = ['//test/core/util:fuzzer_one_entry_runner'],
+      args = ['$(location :%s/one_entry.bin)' % name, '$(location %s)' % entry]
+    )
diff --git a/test/core/util/memory_counters.h b/test/core/util/memory_counters.h
index f33281650147fc77d7ec64191dc819c705c906b9..b9b2b3adda5dcad3140d87640b557830013cd648 100644
--- a/test/core/util/memory_counters.h
+++ b/test/core/util/memory_counters.h
@@ -34,6 +34,8 @@
 #ifndef GRPC_TEST_CORE_UTIL_MEMORY_COUNTERS_H
 #define GRPC_TEST_CORE_UTIL_MEMORY_COUNTERS_H
 
+#include <stddef.h>
+
 struct grpc_memory_counters {
   size_t total_size_relative;
   size_t total_size_absolute;
diff --git a/test/core/util/mock_endpoint.c b/test/core/util/mock_endpoint.c
index bf6d85252a97ebdeafecb88ee81dc8caa4ab9847..04793bceabe28aa1882db5ca9fceff81b1648474 100644
--- a/test/core/util/mock_endpoint.c
+++ b/test/core/util/mock_endpoint.c
@@ -55,7 +55,7 @@ static void me_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
   gpr_mu_lock(&m->mu);
   if (m->read_buffer.count > 0) {
     grpc_slice_buffer_swap(&m->read_buffer, slices);
-    grpc_exec_ctx_sched(exec_ctx, cb, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, cb, GRPC_ERROR_NONE);
   } else {
     m->on_read = cb;
     m->on_read_out = slices;
@@ -69,7 +69,7 @@ static void me_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
   for (size_t i = 0; i < slices->count; i++) {
     m->on_write(slices->slices[i]);
   }
-  grpc_exec_ctx_sched(exec_ctx, cb, GRPC_ERROR_NONE, NULL);
+  grpc_closure_sched(exec_ctx, cb, GRPC_ERROR_NONE);
 }
 
 static void me_add_to_pollset(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
@@ -82,8 +82,8 @@ static void me_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
   grpc_mock_endpoint *m = (grpc_mock_endpoint *)ep;
   gpr_mu_lock(&m->mu);
   if (m->on_read) {
-    grpc_exec_ctx_sched(exec_ctx, m->on_read,
-                        GRPC_ERROR_CREATE("Endpoint Shutdown"), NULL);
+    grpc_closure_sched(exec_ctx, m->on_read,
+                       GRPC_ERROR_CREATE("Endpoint Shutdown"));
     m->on_read = NULL;
   }
   gpr_mu_unlock(&m->mu);
@@ -144,7 +144,7 @@ void grpc_mock_endpoint_put_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
   gpr_mu_lock(&m->mu);
   if (m->on_read != NULL) {
     grpc_slice_buffer_add(m->on_read_out, slice);
-    grpc_exec_ctx_sched(exec_ctx, m->on_read, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, m->on_read, GRPC_ERROR_NONE);
     m->on_read = NULL;
   } else {
     grpc_slice_buffer_add(&m->read_buffer, slice);
diff --git a/test/core/util/passthru_endpoint.c b/test/core/util/passthru_endpoint.c
index b3405f02e9f89d63b9ed7de71d88676b874c63c4..d42ec7f9e8cb3e46be8a2665ceee8e497747d71e 100644
--- a/test/core/util/passthru_endpoint.c
+++ b/test/core/util/passthru_endpoint.c
@@ -39,6 +39,8 @@
 #include <grpc/support/string_util.h>
 #include "src/core/lib/iomgr/sockaddr.h"
 
+#include "src/core/lib/slice/slice_internal.h"
+
 typedef struct passthru_endpoint passthru_endpoint;
 
 typedef struct {
@@ -63,11 +65,10 @@ static void me_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
   half *m = (half *)ep;
   gpr_mu_lock(&m->parent->mu);
   if (m->parent->shutdown) {
-    grpc_exec_ctx_sched(exec_ctx, cb, GRPC_ERROR_CREATE("Already shutdown"),
-                        NULL);
+    grpc_closure_sched(exec_ctx, cb, GRPC_ERROR_CREATE("Already shutdown"));
   } else if (m->read_buffer.count > 0) {
     grpc_slice_buffer_swap(&m->read_buffer, slices);
-    grpc_exec_ctx_sched(exec_ctx, cb, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, cb, GRPC_ERROR_NONE);
   } else {
     m->on_read = cb;
     m->on_read_out = slices;
@@ -91,7 +92,7 @@ static void me_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
     for (size_t i = 0; i < slices->count; i++) {
       grpc_slice_buffer_add(m->on_read_out, grpc_slice_ref(slices->slices[i]));
     }
-    grpc_exec_ctx_sched(exec_ctx, m->on_read, GRPC_ERROR_NONE, NULL);
+    grpc_closure_sched(exec_ctx, m->on_read, GRPC_ERROR_NONE);
     m->on_read = NULL;
   } else {
     for (size_t i = 0; i < slices->count; i++) {
@@ -99,7 +100,7 @@ static void me_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
     }
   }
   gpr_mu_unlock(&m->parent->mu);
-  grpc_exec_ctx_sched(exec_ctx, cb, error, NULL);
+  grpc_closure_sched(exec_ctx, cb, error);
 }
 
 static void me_add_to_pollset(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
@@ -113,14 +114,12 @@ static void me_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
   gpr_mu_lock(&m->parent->mu);
   m->parent->shutdown = true;
   if (m->on_read) {
-    grpc_exec_ctx_sched(exec_ctx, m->on_read, GRPC_ERROR_CREATE("Shutdown"),
-                        NULL);
+    grpc_closure_sched(exec_ctx, m->on_read, GRPC_ERROR_CREATE("Shutdown"));
     m->on_read = NULL;
   }
   m = other_half(m);
   if (m->on_read) {
-    grpc_exec_ctx_sched(exec_ctx, m->on_read, GRPC_ERROR_CREATE("Shutdown"),
-                        NULL);
+    grpc_closure_sched(exec_ctx, m->on_read, GRPC_ERROR_CREATE("Shutdown"));
     m->on_read = NULL;
   }
   gpr_mu_unlock(&m->parent->mu);
@@ -133,8 +132,8 @@ static void me_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
   if (0 == --p->halves) {
     gpr_mu_unlock(&p->mu);
     gpr_mu_destroy(&p->mu);
-    grpc_slice_buffer_destroy(&p->client.read_buffer);
-    grpc_slice_buffer_destroy(&p->server.read_buffer);
+    grpc_slice_buffer_destroy_internal(exec_ctx, &p->client.read_buffer);
+    grpc_slice_buffer_destroy_internal(exec_ctx, &p->server.read_buffer);
     grpc_resource_user_unref(exec_ctx, p->client.resource_user);
     grpc_resource_user_unref(exec_ctx, p->server.resource_user);
     gpr_free(p);
diff --git a/test/core/util/port_server_client.c b/test/core/util/port_server_client.c
index b2342feeb4097158aa8f000bbce568bdef6a2324..4baf63dcd7c27ef2c073ab3edd91fe0a39d08143 100644
--- a/test/core/util/port_server_client.c
+++ b/test/core/util/port_server_client.c
@@ -92,7 +92,8 @@ void grpc_free_port_using_server(char *server, int port) {
   grpc_pollset *pollset = gpr_malloc(grpc_pollset_size());
   grpc_pollset_init(pollset, &pr.mu);
   pr.pops = grpc_polling_entity_create_from_pollset(pollset);
-  shutdown_closure = grpc_closure_create(destroy_pops_and_shutdown, &pr.pops);
+  shutdown_closure = grpc_closure_create(destroy_pops_and_shutdown, &pr.pops,
+                                         grpc_schedule_on_exec_ctx);
 
   req.host = server;
   gpr_asprintf(&path, "/drop/%d", port);
@@ -103,8 +104,10 @@ void grpc_free_port_using_server(char *server, int port) {
       grpc_resource_quota_create("port_server_client/free");
   grpc_httpcli_get(&exec_ctx, &context, &pr.pops, resource_quota, &req,
                    GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10),
-                   grpc_closure_create(freed_port_from_server, &pr), &rsp);
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+                   grpc_closure_create(freed_port_from_server, &pr,
+                                       grpc_schedule_on_exec_ctx),
+                   &rsp);
+  grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
   gpr_mu_lock(pr.mu);
   while (!pr.done) {
     grpc_pollset_worker *worker = NULL;
@@ -174,9 +177,10 @@ static void got_port_from_server(grpc_exec_ctx *exec_ctx, void *arg,
         grpc_resource_quota_create("port_server_client/pick_retry");
     grpc_httpcli_get(exec_ctx, pr->ctx, &pr->pops, resource_quota, &req,
                      GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10),
-                     grpc_closure_create(got_port_from_server, pr),
+                     grpc_closure_create(got_port_from_server, pr,
+                                         grpc_schedule_on_exec_ctx),
                      &pr->response);
-    grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
+    grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
     return;
   }
   GPR_ASSERT(response);
@@ -208,7 +212,8 @@ int grpc_pick_port_using_server(char *server) {
   grpc_pollset *pollset = gpr_malloc(grpc_pollset_size());
   grpc_pollset_init(pollset, &pr.mu);
   pr.pops = grpc_polling_entity_create_from_pollset(pollset);
-  shutdown_closure = grpc_closure_create(destroy_pops_and_shutdown, &pr.pops);
+  shutdown_closure = grpc_closure_create(destroy_pops_and_shutdown, &pr.pops,
+                                         grpc_schedule_on_exec_ctx);
   pr.port = -1;
   pr.server = server;
   pr.ctx = &context;
@@ -219,11 +224,12 @@ int grpc_pick_port_using_server(char *server) {
   grpc_httpcli_context_init(&context);
   grpc_resource_quota *resource_quota =
       grpc_resource_quota_create("port_server_client/pick");
-  grpc_httpcli_get(&exec_ctx, &context, &pr.pops, resource_quota, &req,
-                   GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10),
-                   grpc_closure_create(got_port_from_server, &pr),
-                   &pr.response);
-  grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
+  grpc_httpcli_get(
+      &exec_ctx, &context, &pr.pops, resource_quota, &req,
+      GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10),
+      grpc_closure_create(got_port_from_server, &pr, grpc_schedule_on_exec_ctx),
+      &pr.response);
+  grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
   grpc_exec_ctx_finish(&exec_ctx);
   gpr_mu_lock(pr.mu);
   while (pr.port == -1) {
diff --git a/test/core/util/reconnect_server.c b/test/core/util/reconnect_server.c
index 6509cc5b685e0a446b903fcc73dd8ff390024e3f..7bf83a74a1fda08295947980a337365c26ddda49 100644
--- a/test/core/util/reconnect_server.c
+++ b/test/core/util/reconnect_server.c
@@ -73,6 +73,7 @@ static void pretty_print_backoffs(reconnect_server *server) {
 static void on_connect(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *tcp,
                        grpc_pollset *accepting_pollset,
                        grpc_tcp_server_acceptor *acceptor) {
+  gpr_free(acceptor);
   char *peer;
   char *last_colon;
   reconnect_server *server = (reconnect_server *)arg;
diff --git a/test/core/util/test_tcp_server.c b/test/core/util/test_tcp_server.c
index 16df91d96882d11174faa3c9370af186abf44e96..2338b81ab18364414d705e1ab051a471c3a410e7 100644
--- a/test/core/util/test_tcp_server.c
+++ b/test/core/util/test_tcp_server.c
@@ -57,7 +57,8 @@ void test_tcp_server_init(test_tcp_server *server,
                           grpc_tcp_server_cb on_connect, void *user_data) {
   grpc_init();
   server->tcp_server = NULL;
-  grpc_closure_init(&server->shutdown_complete, on_server_destroyed, server);
+  grpc_closure_init(&server->shutdown_complete, on_server_destroyed, server,
+                    grpc_schedule_on_exec_ctx);
   server->shutdown = 0;
   server->pollset = gpr_malloc(grpc_pollset_size());
   grpc_pollset_init(server->pollset, &server->mu);
@@ -111,7 +112,8 @@ void test_tcp_server_destroy(test_tcp_server *server) {
   gpr_timespec shutdown_deadline;
   grpc_closure do_nothing_cb;
   grpc_tcp_server_unref(&exec_ctx, server->tcp_server);
-  grpc_closure_init(&do_nothing_cb, do_nothing, NULL);
+  grpc_closure_init(&do_nothing_cb, do_nothing, NULL,
+                    grpc_schedule_on_exec_ctx);
   shutdown_deadline = gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
                                    gpr_time_from_seconds(5, GPR_TIMESPAN));
   while (!server->shutdown &&
diff --git a/test/cpp/common/channel_arguments_test.cc b/test/cpp/common/channel_arguments_test.cc
index 60d3215265f075b1a72e45a19acea901092a54f1..190d32ce060b4c9c6af0d10c82d12df8362b54d8 100644
--- a/test/cpp/common/channel_arguments_test.cc
+++ b/test/cpp/common/channel_arguments_test.cc
@@ -37,7 +37,11 @@
 #include <grpc/grpc.h>
 #include <grpc/support/useful.h>
 #include <gtest/gtest.h>
+
+extern "C" {
+#include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/iomgr/socket_mutator.h"
+}
 
 namespace grpc {
 namespace testing {
@@ -228,7 +232,11 @@ TEST_F(ChannelArgumentsTest, SetSocketMutator) {
   EXPECT_FALSE(HasArg(arg0));
 
   // arg0 is destroyed by grpc_socket_mutator_to_arg(mutator1)
-  arg1.value.pointer.vtable->destroy(arg1.value.pointer.p);
+  {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    arg1.value.pointer.vtable->destroy(&exec_ctx, arg1.value.pointer.p);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
 }
 
 TEST_F(ChannelArgumentsTest, SetUserAgentPrefix) {
diff --git a/test/cpp/common/channel_filter_test.cc b/test/cpp/common/channel_filter_test.cc
index 600a953d828fb04fee7340497616124a46719b09..32246a4b765df895501d5728fd8b08f791a4978f 100644
--- a/test/cpp/common/channel_filter_test.cc
+++ b/test/cpp/common/channel_filter_test.cc
@@ -41,14 +41,24 @@ namespace testing {
 
 class MyChannelData : public ChannelData {
  public:
-  MyChannelData(const grpc_channel_args& args, const char* peer)
-      : ChannelData(args, peer) {}
+  MyChannelData() {}
+
+  grpc_error* Init(grpc_exec_ctx* exec_ctx,
+                   grpc_channel_element_args* args) override {
+    (void)args->channel_args;  // Make sure field is available.
+    return GRPC_ERROR_NONE;
+  }
 };
 
 class MyCallData : public CallData {
  public:
-  explicit MyCallData(const ChannelData& channel_data)
-      : CallData(channel_data) {}
+  MyCallData() {}
+
+  grpc_error* Init(grpc_exec_ctx* exec_ctx, ChannelData* channel_data,
+                   grpc_call_element_args* args) override {
+    (void)args->path;  // Make sure field is available.
+    return GRPC_ERROR_NONE;
+  }
 };
 
 // This test ensures that when we make changes to the filter API in
diff --git a/test/cpp/end2end/async_end2end_test.cc b/test/cpp/end2end/async_end2end_test.cc
index 8e385d100c16f37b770e120149e8a6401c64ff19..2ce3f2f7bd9e3e07215ae3c05a3d3f9e23699dbe 100644
--- a/test/cpp/end2end/async_end2end_test.cc
+++ b/test/cpp/end2end/async_end2end_test.cc
@@ -254,7 +254,8 @@ class AsyncEnd2endTest : public ::testing::TestWithParam<TestScenario> {
 
     // Setup server
     ServerBuilder builder;
-    auto server_creds = GetServerCredentials(GetParam().credentials_type);
+    auto server_creds = GetCredentialsProvider()->GetServerCredentials(
+        GetParam().credentials_type);
     builder.AddListeningPort(server_address_.str(), server_creds);
     builder.RegisterService(&service_);
     cq_ = builder.AddCompletionQueue();
@@ -283,8 +284,8 @@ class AsyncEnd2endTest : public ::testing::TestWithParam<TestScenario> {
 
   void ResetStub() {
     ChannelArguments args;
-    auto channel_creds =
-        GetChannelCredentials(GetParam().credentials_type, &args);
+    auto channel_creds = GetCredentialsProvider()->GetChannelCredentials(
+        GetParam().credentials_type, &args);
     std::shared_ptr<Channel> channel =
         CreateCustomChannel(server_address_.str(), channel_creds, args);
     stub_ = grpc::testing::EchoTestService::NewStub(channel);
@@ -892,8 +893,8 @@ TEST_P(AsyncEnd2endTest, ServerCheckDone) {
 
 TEST_P(AsyncEnd2endTest, UnimplementedRpc) {
   ChannelArguments args;
-  auto channel_creds =
-      GetChannelCredentials(GetParam().credentials_type, &args);
+  auto channel_creds = GetCredentialsProvider()->GetChannelCredentials(
+      GetParam().credentials_type, &args);
   std::shared_ptr<Channel> channel =
       CreateCustomChannel(server_address_.str(), channel_creds, args);
   std::unique_ptr<grpc::testing::UnimplementedEchoService::Stub> stub;
@@ -1404,11 +1405,15 @@ std::vector<TestScenario> CreateTestScenarios(bool test_disable_blocking,
   std::vector<grpc::string> credentials_types;
   std::vector<grpc::string> messages;
 
-  credentials_types.push_back(kInsecureCredentialsType);
-  auto sec_list = GetSecureCredentialsTypeList();
+  if (GetCredentialsProvider()->GetChannelCredentials(kInsecureCredentialsType,
+                                                      nullptr) != nullptr) {
+    credentials_types.push_back(kInsecureCredentialsType);
+  }
+  auto sec_list = GetCredentialsProvider()->GetSecureCredentialsTypeList();
   for (auto sec = sec_list.begin(); sec != sec_list.end(); sec++) {
     credentials_types.push_back(*sec);
   }
+  GPR_ASSERT(!credentials_types.empty());
 
   messages.push_back("Hello");
   for (int sz = 1; sz < test_big_limit; sz *= 2) {
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index 9bb892c694bbd809b97eb80e51627b91f2dbea60..1a1a94e87c74ffe43eaaad71cb7b71a83d80ea5f 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -242,7 +242,8 @@ class End2endTest : public ::testing::TestWithParam<TestScenario> {
     // Setup server
     ServerBuilder builder;
     ConfigureServerBuilder(&builder);
-    auto server_creds = GetServerCredentials(GetParam().credentials_type);
+    auto server_creds = GetCredentialsProvider()->GetServerCredentials(
+        GetParam().credentials_type);
     if (GetParam().credentials_type != kInsecureCredentialsType) {
       server_creds->SetAuthMetadataProcessor(processor);
     }
@@ -270,8 +271,8 @@ class End2endTest : public ::testing::TestWithParam<TestScenario> {
     }
     EXPECT_TRUE(is_server_started_);
     ChannelArguments args;
-    auto channel_creds =
-        GetChannelCredentials(GetParam().credentials_type, &args);
+    auto channel_creds = GetCredentialsProvider()->GetChannelCredentials(
+        GetParam().credentials_type, &args);
     if (!user_agent_prefix_.empty()) {
       args.SetUserAgentPrefix(user_agent_prefix_);
     }
@@ -1520,11 +1521,18 @@ std::vector<TestScenario> CreateTestScenarios(bool use_proxy,
   std::vector<TestScenario> scenarios;
   std::vector<grpc::string> credentials_types;
   if (test_secure) {
-    credentials_types = GetSecureCredentialsTypeList();
+    credentials_types =
+        GetCredentialsProvider()->GetSecureCredentialsTypeList();
   }
   if (test_insecure) {
-    credentials_types.push_back(kInsecureCredentialsType);
+    // Only add insecure credentials type when it is registered with the
+    // provider. User may create providers that do not have insecure.
+    if (GetCredentialsProvider()->GetChannelCredentials(
+            kInsecureCredentialsType, nullptr) != nullptr) {
+      credentials_types.push_back(kInsecureCredentialsType);
+    }
   }
+  GPR_ASSERT(!credentials_types.empty());
   for (auto it = credentials_types.begin(); it != credentials_types.end();
        ++it) {
     scenarios.emplace_back(false, *it);
@@ -1541,7 +1549,7 @@ INSTANTIATE_TEST_CASE_P(End2end, End2endTest,
 
 INSTANTIATE_TEST_CASE_P(End2endServerTryCancel, End2endServerTryCancelTest,
                         ::testing::ValuesIn(CreateTestScenarios(false, true,
-                                                                false)));
+                                                                true)));
 
 INSTANTIATE_TEST_CASE_P(ProxyEnd2end, ProxyEnd2endTest,
                         ::testing::ValuesIn(CreateTestScenarios(true, true,
diff --git a/test/cpp/end2end/filter_end2end_test.cc b/test/cpp/end2end/filter_end2end_test.cc
index ab6ed46de564446b85f59c9d40455206d43990cf..bd384f68b40724e3dc996ccf03be4c5606aa75cd 100644
--- a/test/cpp/end2end/filter_end2end_test.cc
+++ b/test/cpp/end2end/filter_end2end_test.cc
@@ -114,20 +114,17 @@ int GetCallCounterValue() {
 
 class ChannelDataImpl : public ChannelData {
  public:
-  ChannelDataImpl(const grpc_channel_args& args, const char* peer)
-      : ChannelData(args, peer) {
+  grpc_error* Init(grpc_exec_ctx* exec_ctx, grpc_channel_element_args* args) {
     IncrementConnectionCounter();
+    return GRPC_ERROR_NONE;
   }
 };
 
 class CallDataImpl : public CallData {
  public:
-  explicit CallDataImpl(const ChannelDataImpl& channel_data)
-      : CallData(channel_data) {}
-
   void StartTransportStreamOp(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
                               TransportStreamOp* op) override {
-    // Incrementing the counter could be done from the ctor, but we want
+    // Incrementing the counter could be done from Init(), but we want
     // to test that the individual methods are actually called correctly.
     if (op->recv_initial_metadata() != nullptr) IncrementCallCounter();
     grpc_call_next_op(exec_ctx, elem, op->op());
diff --git a/test/cpp/end2end/server_crash_test.cc b/test/cpp/end2end/server_crash_test.cc
index 8cee1403dcf4b1864609e21d85edb2df7b54fcc9..b1f9216055f8b488e81eef7abc64fdf18960818a 100644
--- a/test/cpp/end2end/server_crash_test.cc
+++ b/test/cpp/end2end/server_crash_test.cc
@@ -138,7 +138,7 @@ TEST_F(CrashTest, ResponseStream) {
   auto server = CreateServerAndClient("response");
 
   gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
-                               gpr_time_from_seconds(5, GPR_TIMESPAN)));
+                               gpr_time_from_seconds(60, GPR_TIMESPAN)));
   KillClient();
   server->Shutdown();
   GPR_ASSERT(HadOneResponseStream());
@@ -148,7 +148,7 @@ TEST_F(CrashTest, BidiStream) {
   auto server = CreateServerAndClient("bidi");
 
   gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
-                               gpr_time_from_seconds(5, GPR_TIMESPAN)));
+                               gpr_time_from_seconds(60, GPR_TIMESPAN)));
   KillClient();
   server->Shutdown();
   GPR_ASSERT(HadOneBidiStream());
diff --git a/test/cpp/grpclb/grpclb_test.cc b/test/cpp/grpclb/grpclb_test.cc
index fcdcaba6a23ddf6d7a4d0b25e251fef6c01bdfa0..de304b9f89ce246a3d8952f6079a76eada79b321 100644
--- a/test/cpp/grpclb/grpclb_test.cc
+++ b/test/cpp/grpclb/grpclb_test.cc
@@ -659,7 +659,7 @@ static test_fixture setup_test_fixture(int lb_server_update_delay_ms) {
   char *server_uri;
   // The grpclb LB policy will be automatically selected by virtue of
   // the fact that the returned addresses are balancer addresses.
-  gpr_asprintf(&server_uri, "test:%s?lb_enabled=1",
+  gpr_asprintf(&server_uri, "test:///%s?lb_enabled=1",
                tf.lb_server.servers_hostport);
   setup_client(server_uri, &tf.client);
   gpr_free(server_uri);
diff --git a/test/cpp/interop/client.cc b/test/cpp/interop/client.cc
index c58910abc3fb5a3e5827be04ee90b435ae02ea9c..3265554444b9af68986713abeb5287f0dc9744f5 100644
--- a/test/cpp/interop/client.cc
+++ b/test/cpp/interop/client.cc
@@ -49,6 +49,7 @@
 #include "test/cpp/util/test_config.h"
 
 DEFINE_bool(use_tls, false, "Whether to use tls.");
+DEFINE_string(custom_credentials_type, "", "User provided credentials type.");
 DEFINE_bool(use_test_ca, false, "False to use SSL roots for google");
 DEFINE_int32(server_port, 0, "Server port.");
 DEFINE_string(server_host, "127.0.0.1", "Server host to connect to");
diff --git a/test/cpp/interop/client_helper.cc b/test/cpp/interop/client_helper.cc
index c171969e147d35665fe5dd95d14f38db8cb8019b..91564e5dcefbb71c1f50a9d6f6deb97715f5ff5a 100644
--- a/test/cpp/interop/client_helper.cc
+++ b/test/cpp/interop/client_helper.cc
@@ -50,8 +50,10 @@
 #include "src/cpp/client/secure_credentials.h"
 #include "test/core/security/oauth2_utils.h"
 #include "test/cpp/util/create_test_channel.h"
+#include "test/cpp/util/test_credentials_provider.h"
 
 DECLARE_bool(use_tls);
+DECLARE_string(custom_credentials_type);
 DECLARE_bool(use_test_ca);
 DECLARE_int32(server_port);
 DECLARE_string(server_host);
@@ -114,8 +116,12 @@ std::shared_ptr<Channel> CreateChannelForTestCase(
     creds = AccessTokenCredentials(raw_token);
     GPR_ASSERT(creds);
   }
-  return CreateTestChannel(host_port, FLAGS_server_host_override, FLAGS_use_tls,
-                           !FLAGS_use_test_ca, creds);
+  if (FLAGS_custom_credentials_type.empty()) {
+    return CreateTestChannel(host_port, FLAGS_server_host_override,
+                             FLAGS_use_tls, !FLAGS_use_test_ca, creds);
+  } else {
+    return CreateTestChannel(host_port, FLAGS_custom_credentials_type, creds);
+  }
 }
 
 }  // namespace testing
diff --git a/test/cpp/interop/http2_client.cc b/test/cpp/interop/http2_client.cc
new file mode 100644
index 0000000000000000000000000000000000000000..38aee43b26a947b9ed39b9e071e3d8ba194b6973
--- /dev/null
+++ b/test/cpp/interop/http2_client.cc
@@ -0,0 +1,272 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <thread>
+
+#include <gflags/gflags.h>
+#include <grpc++/channel.h>
+#include <grpc++/client_context.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/transport/byte_stream.h"
+#include "src/proto/grpc/testing/messages.grpc.pb.h"
+#include "src/proto/grpc/testing/test.grpc.pb.h"
+#include "test/cpp/interop/http2_client.h"
+
+#include "src/core/lib/support/string.h"
+#include "test/cpp/util/create_test_channel.h"
+#include "test/cpp/util/test_config.h"
+
+namespace grpc {
+namespace testing {
+
+namespace {
+const int kLargeRequestSize = 271828;
+const int kLargeResponseSize = 314159;
+}  // namespace
+
+Http2Client::ServiceStub::ServiceStub(std::shared_ptr<Channel> channel)
+    : channel_(channel) {
+  stub_ = TestService::NewStub(channel);
+}
+
+TestService::Stub* Http2Client::ServiceStub::Get() { return stub_.get(); }
+
+Http2Client::Http2Client(std::shared_ptr<Channel> channel)
+    : serviceStub_(channel), channel_(channel) {}
+
+bool Http2Client::AssertStatusCode(const Status& s, StatusCode expected_code) {
+  if (s.error_code() == expected_code) {
+    return true;
+  }
+
+  gpr_log(GPR_ERROR, "Error status code: %d (expected: %d), message: %s",
+          s.error_code(), expected_code, s.error_message().c_str());
+  abort();
+}
+
+bool Http2Client::DoRstAfterHeader() {
+  gpr_log(GPR_DEBUG, "Sending RPC and expecting reset stream after header");
+
+  ClientContext context;
+  SimpleRequest request;
+  SimpleResponse response;
+  request.set_response_size(kLargeResponseSize);
+  grpc::string payload(kLargeRequestSize, '\0');
+  request.mutable_payload()->set_body(payload.c_str(), kLargeRequestSize);
+
+  Status s = serviceStub_.Get()->UnaryCall(&context, request, &response);
+  AssertStatusCode(s, grpc::StatusCode::UNKNOWN);
+  GPR_ASSERT(!response.has_payload());  // no data should be received
+
+  gpr_log(GPR_DEBUG, "Done testing reset stream after header");
+  return true;
+}
+
+bool Http2Client::DoRstAfterData() {
+  gpr_log(GPR_DEBUG, "Sending RPC and expecting reset stream after data");
+
+  ClientContext context;
+  SimpleRequest request;
+  SimpleResponse response;
+  request.set_response_size(kLargeResponseSize);
+  grpc::string payload(kLargeRequestSize, '\0');
+  request.mutable_payload()->set_body(payload.c_str(), kLargeRequestSize);
+
+  Status s = serviceStub_.Get()->UnaryCall(&context, request, &response);
+  AssertStatusCode(s, grpc::StatusCode::UNKNOWN);
+  GPR_ASSERT(response.has_payload());  // data should be received
+
+  gpr_log(GPR_DEBUG, "Done testing reset stream after data");
+  return true;
+}
+
+bool Http2Client::DoRstDuringData() {
+  gpr_log(GPR_DEBUG, "Sending RPC and expecting reset stream during data");
+
+  ClientContext context;
+  SimpleRequest request;
+  SimpleResponse response;
+  request.set_response_size(kLargeResponseSize);
+  grpc::string payload(kLargeRequestSize, '\0');
+  request.mutable_payload()->set_body(payload.c_str(), kLargeRequestSize);
+
+  Status s = serviceStub_.Get()->UnaryCall(&context, request, &response);
+  AssertStatusCode(s, grpc::StatusCode::UNKNOWN);
+  GPR_ASSERT(!response.has_payload());  // no data should be received
+
+  gpr_log(GPR_DEBUG, "Done testing reset stream during data");
+  return true;
+}
+
+bool Http2Client::DoGoaway() {
+  gpr_log(GPR_DEBUG, "Sending two RPCs and expecting goaway");
+
+  int numCalls = 2;
+  for (int i = 0; i < numCalls; i++) {
+    ClientContext context;
+    SimpleRequest request;
+    SimpleResponse response;
+    request.set_response_size(kLargeResponseSize);
+    grpc::string payload(kLargeRequestSize, '\0');
+    request.mutable_payload()->set_body(payload.c_str(), kLargeRequestSize);
+
+    Status s = serviceStub_.Get()->UnaryCall(&context, request, &response);
+    AssertStatusCode(s, grpc::StatusCode::OK);
+    GPR_ASSERT(response.payload().body() ==
+               grpc::string(kLargeResponseSize, '\0'));
+  }
+
+  gpr_log(GPR_DEBUG, "Done testing goaway");
+  return true;
+}
+
+bool Http2Client::DoPing() {
+  gpr_log(GPR_DEBUG, "Sending RPC and expecting ping");
+
+  ClientContext context;
+  SimpleRequest request;
+  SimpleResponse response;
+  request.set_response_size(kLargeResponseSize);
+  grpc::string payload(kLargeRequestSize, '\0');
+  request.mutable_payload()->set_body(payload.c_str(), kLargeRequestSize);
+
+  Status s = serviceStub_.Get()->UnaryCall(&context, request, &response);
+  AssertStatusCode(s, grpc::StatusCode::OK);
+  GPR_ASSERT(response.payload().body() ==
+             grpc::string(kLargeResponseSize, '\0'));
+
+  gpr_log(GPR_DEBUG, "Done testing ping");
+  return true;
+}
+
+void Http2Client::MaxStreamsWorker(std::shared_ptr<grpc::Channel> channel) {
+  ClientContext context;
+  SimpleRequest request;
+  SimpleResponse response;
+  request.set_response_size(kLargeResponseSize);
+  grpc::string payload(kLargeRequestSize, '\0');
+  request.mutable_payload()->set_body(payload.c_str(), kLargeRequestSize);
+
+  Status s =
+      TestService::NewStub(channel)->UnaryCall(&context, request, &response);
+  AssertStatusCode(s, grpc::StatusCode::OK);
+  GPR_ASSERT(response.payload().body() ==
+             grpc::string(kLargeResponseSize, '\0'));
+}
+
+bool Http2Client::DoMaxStreams() {
+  gpr_log(GPR_DEBUG, "Testing max streams");
+
+  // Make an initial call on the channel to ensure the server's max streams
+  // setting is received
+  ClientContext context;
+  SimpleRequest request;
+  SimpleResponse response;
+  request.set_response_size(kLargeResponseSize);
+  grpc::string payload(kLargeRequestSize, '\0');
+  request.mutable_payload()->set_body(payload.c_str(), kLargeRequestSize);
+  Status s =
+      TestService::NewStub(channel_)->UnaryCall(&context, request, &response);
+  AssertStatusCode(s, grpc::StatusCode::OK);
+  GPR_ASSERT(response.payload().body() ==
+             grpc::string(kLargeResponseSize, '\0'));
+
+  std::vector<std::thread> test_threads;
+
+  for (int i = 0; i < 10; i++) {
+    test_threads.emplace_back(
+        std::thread(&Http2Client::MaxStreamsWorker, this, channel_));
+  }
+
+  for (auto it = test_threads.begin(); it != test_threads.end(); it++) {
+    it->join();
+  }
+
+  gpr_log(GPR_DEBUG, "Done testing max streams");
+  return true;
+}
+
+}  // namespace testing
+}  // namespace grpc
+
+DEFINE_int32(server_port, 0, "Server port.");
+DEFINE_string(server_host, "127.0.0.1", "Server host to connect to");
+DEFINE_string(test_case, "rst_after_header",
+              "Configure different test cases. Valid options are:\n\n"
+              "goaway\n"
+              "max_streams\n"
+              "ping\n"
+              "rst_after_data\n"
+              "rst_after_header\n"
+              "rst_during_data\n");
+
+int main(int argc, char** argv) {
+  grpc::testing::InitTest(&argc, &argv, true);
+  GPR_ASSERT(FLAGS_server_port);
+  const int host_port_buf_size = 1024;
+  char host_port[host_port_buf_size];
+  snprintf(host_port, host_port_buf_size, "%s:%d", FLAGS_server_host.c_str(),
+           FLAGS_server_port);
+  grpc::testing::Http2Client client(grpc::CreateTestChannel(host_port, false));
+  gpr_log(GPR_INFO, "Testing case: %s", FLAGS_test_case.c_str());
+  int ret = 0;
+  if (FLAGS_test_case == "rst_after_header") {
+    client.DoRstAfterHeader();
+  } else if (FLAGS_test_case == "rst_after_data") {
+    client.DoRstAfterData();
+  } else if (FLAGS_test_case == "rst_during_data") {
+    client.DoRstDuringData();
+  } else if (FLAGS_test_case == "goaway") {
+    client.DoGoaway();
+  } else if (FLAGS_test_case == "ping") {
+    client.DoPing();
+  } else if (FLAGS_test_case == "max_streams") {
+    client.DoMaxStreams();
+  } else {
+    const char* testcases[] = {
+        "goaway",         "max_streams",      "ping",
+        "rst_after_data", "rst_after_header", "rst_during_data"};
+    char* joined_testcases =
+        gpr_strjoin_sep(testcases, GPR_ARRAY_SIZE(testcases), "\n", NULL);
+
+    gpr_log(GPR_ERROR, "Unsupported test case %s. Valid options are\n%s",
+            FLAGS_test_case.c_str(), joined_testcases);
+    gpr_free(joined_testcases);
+    ret = 1;
+  }
+
+  return ret;
+}
diff --git a/test/cpp/interop/http2_client.h b/test/cpp/interop/http2_client.h
new file mode 100644
index 0000000000000000000000000000000000000000..6a315f5abbab8b38e4ebcacd56c205f2d9bdf55f
--- /dev/null
+++ b/test/cpp/interop/http2_client.h
@@ -0,0 +1,80 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_TEST_CPP_INTEROP_HTTP2_CLIENT_H
+#define GRPC_TEST_CPP_INTEROP_HTTP2_CLIENT_H
+
+#include <memory>
+
+#include <grpc++/channel.h>
+#include <grpc/grpc.h>
+#include "src/proto/grpc/testing/messages.grpc.pb.h"
+#include "src/proto/grpc/testing/test.grpc.pb.h"
+
+namespace grpc {
+namespace testing {
+
+class Http2Client {
+ public:
+  explicit Http2Client(std::shared_ptr<Channel> channel);
+  ~Http2Client() {}
+
+  bool DoRstAfterHeader();
+  bool DoRstAfterData();
+  bool DoRstDuringData();
+  bool DoGoaway();
+  bool DoPing();
+  bool DoMaxStreams();
+
+ private:
+  class ServiceStub {
+   public:
+    ServiceStub(std::shared_ptr<Channel> channel);
+
+    TestService::Stub* Get();
+
+   private:
+    std::unique_ptr<TestService::Stub> stub_;
+    std::shared_ptr<Channel> channel_;
+  };
+
+  void MaxStreamsWorker(std::shared_ptr<grpc::Channel> channel);
+  bool AssertStatusCode(const Status& s, StatusCode expected_code);
+  ServiceStub serviceStub_;
+  std::shared_ptr<Channel> channel_;
+};
+
+}  // namespace testing
+}  // namespace grpc
+
+#endif  // GRPC_TEST_CPP_INTEROP_HTTP2_CLIENT_H
diff --git a/test/cpp/interop/interop_server.cc b/test/cpp/interop/interop_server.cc
index 8b50ae8c05a67da4c436d9baa79bbed09384a6ee..956840ba7090d5bc374dbc49f196e0fb480bb1a4 100644
--- a/test/cpp/interop/interop_server.cc
+++ b/test/cpp/interop/interop_server.cc
@@ -56,6 +56,7 @@
 #include "test/cpp/util/test_config.h"
 
 DEFINE_bool(use_tls, false, "Whether to use tls.");
+DEFINE_string(custom_credentials_type, "", "User provided credentials type.");
 DEFINE_int32(port, 0, "Server port.");
 DEFINE_int32(max_send_message_size, -1, "The maximum send message size.");
 
@@ -344,7 +345,7 @@ void grpc::testing::interop::RunServer(
   }
   std::unique_ptr<Server> server(builder.BuildAndStart());
   gpr_log(GPR_INFO, "Server listening on %s", server_address.str().c_str());
-  while (!g_got_sigint) {
+  while (!gpr_atm_no_barrier_load(&g_got_sigint)) {
     sleep(5);
   }
 }
diff --git a/test/cpp/interop/interop_server_bootstrap.cc b/test/cpp/interop/interop_server_bootstrap.cc
index 424f7ca7f0df7a1c6b49fe08960871c48aaf8901..99518c6943fee1ac2b589487376db988c859878f 100644
--- a/test/cpp/interop/interop_server_bootstrap.cc
+++ b/test/cpp/interop/interop_server_bootstrap.cc
@@ -37,10 +37,10 @@
 #include "test/cpp/interop/server_helper.h"
 #include "test/cpp/util/test_config.h"
 
-bool grpc::testing::interop::g_got_sigint = false;
+gpr_atm grpc::testing::interop::g_got_sigint;
 
 static void sigint_handler(int x) {
-  grpc::testing::interop::g_got_sigint = true;
+  gpr_atm_no_barrier_store(&grpc::testing::interop::g_got_sigint, true);
 }
 
 int main(int argc, char** argv) {
diff --git a/test/cpp/interop/interop_test.cc b/test/cpp/interop/interop_test.cc
index c066598d363d2e57398aa700a11ff144762f0538..d4004740a41a575a069b98d8a18d0d55daa76401 100644
--- a/test/cpp/interop/interop_test.cc
+++ b/test/cpp/interop/interop_test.cc
@@ -126,7 +126,7 @@ int main(int argc, char** argv) {
     return 1;
   }
   /* wait a little */
-  sleep(2);
+  sleep(10);
   /* start the clients */
   ret = test_client(root, "127.0.0.1", port);
   if (ret != 0) return ret;
diff --git a/test/cpp/interop/server_helper.cc b/test/cpp/interop/server_helper.cc
index 8b0b511bcb89d62d4e5642266b8aaaa83bf9e6b2..d395f50fa59297f86d7154cead138e50a67945e4 100644
--- a/test/cpp/interop/server_helper.cc
+++ b/test/cpp/interop/server_helper.cc
@@ -39,23 +39,23 @@
 #include <grpc++/security/server_credentials.h>
 
 #include "src/core/lib/surface/call_test_only.h"
-#include "test/core/end2end/data/ssl_test_data.h"
+#include "test/cpp/util/test_credentials_provider.h"
 
 DECLARE_bool(use_tls);
+DECLARE_string(custom_credentials_type);
 
 namespace grpc {
 namespace testing {
 
 std::shared_ptr<ServerCredentials> CreateInteropServerCredentials() {
-  if (FLAGS_use_tls) {
-    SslServerCredentialsOptions::PemKeyCertPair pkcp = {test_server1_key,
-                                                        test_server1_cert};
-    SslServerCredentialsOptions ssl_opts;
-    ssl_opts.pem_root_certs = "";
-    ssl_opts.pem_key_cert_pairs.push_back(pkcp);
-    return SslServerCredentials(ssl_opts);
+  if (!FLAGS_custom_credentials_type.empty()) {
+    return GetCredentialsProvider()->GetServerCredentials(
+        FLAGS_custom_credentials_type);
+  } else if (FLAGS_use_tls) {
+    return GetCredentialsProvider()->GetServerCredentials(kTlsCredentialsType);
   } else {
-    return InsecureServerCredentials();
+    return GetCredentialsProvider()->GetServerCredentials(
+        kInsecureCredentialsType);
   }
 }
 
diff --git a/test/cpp/interop/server_helper.h b/test/cpp/interop/server_helper.h
index fc4ea8b3e81730b42bc81eb6da853de6c80f00f3..99539adee557dd972231a3ebc2288f30d431fa69 100644
--- a/test/cpp/interop/server_helper.h
+++ b/test/cpp/interop/server_helper.h
@@ -36,9 +36,11 @@
 
 #include <memory>
 
+#include <grpc/compression.h>
+#include <grpc/impl/codegen/atm.h>
+
 #include <grpc++/security/server_credentials.h>
 #include <grpc++/server_context.h>
-#include <grpc/compression.h>
 
 namespace grpc {
 namespace testing {
@@ -62,7 +64,7 @@ class InteropServerContextInspector {
 
 namespace interop {
 
-extern bool g_got_sigint;
+extern gpr_atm g_got_sigint;
 void RunServer(std::shared_ptr<ServerCredentials> creds);
 
 }  // namespace interop
diff --git a/test/cpp/interop/stress_test.cc b/test/cpp/interop/stress_test.cc
index fc35db5233ab0b07296df15f3433e372bad5e5d2..562522de779c34c530299048b913207861ded048 100644
--- a/test/cpp/interop/stress_test.cc
+++ b/test/cpp/interop/stress_test.cc
@@ -147,6 +147,7 @@ DEFINE_bool(do_not_abort_on_transient_failures, true,
 // Options from client.cc (for compatibility with interop test).
 // TODO(sreek): Consolidate overlapping options
 DEFINE_bool(use_tls, false, "Whether to use tls.");
+DEFINE_string(custom_credentials_type, "", "User provided credentials type.");
 DEFINE_bool(use_test_ca, false, "False to use SSL roots for google");
 DEFINE_int32(server_port, 0, "Server port.");
 DEFINE_string(server_host, "127.0.0.1", "Server host to connect to");
@@ -371,9 +372,9 @@ int main(int argc, char** argv) {
   }
 
   // Start metrics server before waiting for the stress test threads
+  std::unique_ptr<grpc::Server> metrics_server;
   if (FLAGS_metrics_port > 0) {
-    std::unique_ptr<grpc::Server> metrics_server =
-        metrics_service.StartServer(FLAGS_metrics_port);
+    metrics_server = metrics_service.StartServer(FLAGS_metrics_port);
   }
 
   // Wait for the stress test threads to complete
diff --git a/test/cpp/microbenchmarks/bm_fullstack.cc b/test/cpp/microbenchmarks/bm_fullstack.cc
index 6cc780d44af36169292d4b5c8fcb41d9a7be68c0..6c0bf804885a21e8b24d70e8deeede7b217f011b 100644
--- a/test/cpp/microbenchmarks/bm_fullstack.cc
+++ b/test/cpp/microbenchmarks/bm_fullstack.cc
@@ -59,7 +59,7 @@ extern "C" {
 }
 #include "src/cpp/client/create_channel_internal.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
-#include "third_party/google_benchmark/include/benchmark/benchmark.h"
+#include "third_party/benchmark/include/benchmark/benchmark.h"
 
 namespace grpc {
 namespace testing {
diff --git a/test/cpp/microbenchmarks/noop-benchmark.cc b/test/cpp/microbenchmarks/noop-benchmark.cc
index 6b06c69c6e3efdb5fb5fef0afb7b407588da61ac..99fa6d5f6e539c19bf7e6c7f70642666c55f92eb 100644
--- a/test/cpp/microbenchmarks/noop-benchmark.cc
+++ b/test/cpp/microbenchmarks/noop-benchmark.cc
@@ -31,10 +31,10 @@
  *
  */
 
-/* This benchmark exists to ensure that the google_benchmark integration is
+/* This benchmark exists to ensure that the benchmark integration is
  * working */
 
-#include "third_party/google_benchmark/include/benchmark/benchmark.h"
+#include "third_party/benchmark/include/benchmark/benchmark.h"
 
 static void BM_NoOp(benchmark::State& state) {
   while (state.KeepRunning()) {
diff --git a/test/cpp/qps/client.h b/test/cpp/qps/client.h
index fdd78ebb899d2626278e4263674a6acbb8f978f4..18f9778fc6f90444d9842dd5e19ab442de881b6b 100644
--- a/test/cpp/qps/client.h
+++ b/test/cpp/qps/client.h
@@ -409,6 +409,7 @@ class ClientImpl : public Client {
       // old compilers happy with using this in std::vector
       ChannelArguments args;
       args.SetInt("shard_to_ensure_no_subchannel_merges", shard);
+      set_channel_args(config, &args);
       channel_ = CreateTestChannel(
           target, config.security_params().server_host_override(),
           config.has_security_params(), !config.security_params().use_test_ca(),
@@ -423,6 +424,18 @@ class ClientImpl : public Client {
     StubType* get_stub() { return stub_.get(); }
 
    private:
+    void set_channel_args(const ClientConfig& config, ChannelArguments* args) {
+      for (auto channel_arg : config.channel_args()) {
+        if (channel_arg.value_case() == ChannelArg::kStrValue) {
+          args->SetString(channel_arg.name(), channel_arg.str_value());
+        } else if (channel_arg.value_case() == ChannelArg::kIntValue) {
+          args->SetInt(channel_arg.name(), channel_arg.int_value());
+        } else {
+          gpr_log(GPR_ERROR, "Empty channel arg value.");
+        }
+      }
+    }
+
     std::shared_ptr<Channel> channel_;
     std::unique_ptr<StubType> stub_;
   };
diff --git a/test/cpp/qps/driver.cc b/test/cpp/qps/driver.cc
index ea0b38e8ad040dda84c8e6d068ee2a1259229519..93ef32db7700df73cfb5e5f9c3525995971f7acb 100644
--- a/test/cpp/qps/driver.cc
+++ b/test/cpp/qps/driver.cc
@@ -44,6 +44,7 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/host_port.h>
 #include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
 
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/support/env.h"
@@ -99,23 +100,36 @@ static std::unordered_map<string, std::deque<int>> get_hosts_and_cores(
   return hosts;
 }
 
-static deque<string> get_workers(const string& name) {
-  char* env = gpr_getenv(name.c_str());
-  if (!env) return deque<string>();
-
+static deque<string> get_workers(const string& env_name) {
+  char* env = gpr_getenv(env_name.c_str());
+  if (!env) {
+    env = gpr_strdup("");
+  }
   deque<string> out;
   char* p = env;
-  for (;;) {
-    char* comma = strchr(p, ',');
-    if (comma) {
-      out.emplace_back(p, comma);
-      p = comma + 1;
-    } else {
-      out.emplace_back(p);
-      gpr_free(env);
-      return out;
+  if (strlen(env) != 0) {
+    for (;;) {
+      char* comma = strchr(p, ',');
+      if (comma) {
+        out.emplace_back(p, comma);
+        p = comma + 1;
+      } else {
+        out.emplace_back(p);
+        break;
+      }
     }
   }
+  if (out.size() == 0) {
+    gpr_log(GPR_ERROR,
+            "Environment variable \"%s\" does not contain a list of QPS "
+            "workers to use. Set it to a comma-separated list of "
+            "hostname:port pairs, starting with hosts that should act as "
+            "servers. E.g. export "
+            "%s=\"serverhost1:1234,clienthost1:1234,clienthost2:1234\"",
+            env_name.c_str(), env_name.c_str());
+  }
+  gpr_free(env);
+  return out;
 }
 
 // helpers for postprocess_scenario_result
@@ -195,7 +209,8 @@ static void postprocess_scenario_result(ScenarioResult* result) {
 std::unique_ptr<ScenarioResult> RunScenario(
     const ClientConfig& initial_client_config, size_t num_clients,
     const ServerConfig& initial_server_config, size_t num_servers,
-    int warmup_seconds, int benchmark_seconds, int spawn_local_worker_count) {
+    int warmup_seconds, int benchmark_seconds, int spawn_local_worker_count,
+    const char* qps_server_target_override, bool configure_core_lists) {
   // Log everything from the driver
   gpr_set_log_verbosity(GPR_LOG_SEVERITY_DEBUG);
 
@@ -240,9 +255,7 @@ std::unique_ptr<ScenarioResult> RunScenario(
       workers.push_back(addr);
     }
   }
-
-  // Setup the hosts and core counts
-  auto hosts_cores = get_hosts_and_cores(workers);
+  GPR_ASSERT(workers.size() != 0);
 
   // if num_clients is set to <=0, do dynamic sizing: all workers
   // except for servers are clients
@@ -264,6 +277,11 @@ std::unique_ptr<ScenarioResult> RunScenario(
     unique_ptr<ClientReaderWriter<ServerArgs, ServerStatus>> stream;
   };
   std::vector<ServerData> servers(num_servers);
+  std::unordered_map<string, std::deque<int>> hosts_cores;
+
+  if (configure_core_lists) {
+    hosts_cores = get_hosts_and_cores(workers);
+  }
   for (size_t i = 0; i < num_servers; i++) {
     gpr_log(GPR_INFO, "Starting server on %s (worker #%" PRIuPTR ")",
             workers[i].c_str(), i);
@@ -271,37 +289,36 @@ std::unique_ptr<ScenarioResult> RunScenario(
         CreateChannel(workers[i], InsecureChannelCredentials()));
 
     ServerConfig server_config = initial_server_config;
-    char* host;
-    char* driver_port;
-    char* cli_target;
-    gpr_split_host_port(workers[i].c_str(), &host, &driver_port);
-    string host_str(host);
     int server_core_limit = initial_server_config.core_limit();
     int client_core_limit = initial_client_config.core_limit();
 
-    if (server_core_limit == 0 && client_core_limit > 0) {
-      // In this case, limit the server cores if it matches the
-      // same host as one or more clients
-      const auto& dq = hosts_cores.at(host_str);
-      bool match = false;
-      int limit = dq.size();
-      for (size_t cli = 0; cli < num_clients; cli++) {
-        if (host_str == get_host(workers[cli + num_servers])) {
-          limit -= client_core_limit;
-          match = true;
+    if (configure_core_lists) {
+      string host_str(get_host(workers[i]));
+      if (server_core_limit == 0 && client_core_limit > 0) {
+        // In this case, limit the server cores if it matches the
+        // same host as one or more clients
+        const auto& dq = hosts_cores.at(host_str);
+        bool match = false;
+        int limit = dq.size();
+        for (size_t cli = 0; cli < num_clients; cli++) {
+          if (host_str == get_host(workers[cli + num_servers])) {
+            limit -= client_core_limit;
+            match = true;
+          }
+        }
+        if (match) {
+          GPR_ASSERT(limit > 0);
+          server_core_limit = limit;
         }
       }
-      if (match) {
-        GPR_ASSERT(limit > 0);
-        server_core_limit = limit;
-      }
-    }
-    if (server_core_limit > 0) {
-      auto& dq = hosts_cores.at(host_str);
-      GPR_ASSERT(dq.size() >= static_cast<size_t>(server_core_limit));
-      for (int core = 0; core < server_core_limit; core++) {
-        server_config.add_core_list(dq.front());
-        dq.pop_front();
+      if (server_core_limit > 0) {
+        auto& dq = hosts_cores.at(host_str);
+        GPR_ASSERT(dq.size() >= static_cast<size_t>(server_core_limit));
+        gpr_log(GPR_INFO, "Setting server core_list");
+        for (int core = 0; core < server_core_limit; core++) {
+          server_config.add_core_list(dq.front());
+          dq.pop_front();
+        }
       }
     }
 
@@ -315,11 +332,19 @@ std::unique_ptr<ScenarioResult> RunScenario(
     if (!servers[i].stream->Read(&init_status)) {
       gpr_log(GPR_ERROR, "Server %zu did not yield initial status", i);
     }
-    gpr_join_host_port(&cli_target, host, init_status.port());
-    client_config.add_server_targets(cli_target);
-    gpr_free(host);
-    gpr_free(driver_port);
-    gpr_free(cli_target);
+    if (qps_server_target_override != NULL &&
+        strlen(qps_server_target_override) > 0) {
+      // overriding the qps server target only works if there is 1 server
+      GPR_ASSERT(num_servers == 1);
+      client_config.add_server_targets(qps_server_target_override);
+    } else {
+      std::string host;
+      char* cli_target;
+      host = get_host(workers[i]);
+      gpr_join_host_port(&cli_target, host.c_str(), init_status.port());
+      client_config.add_server_targets(cli_target);
+      gpr_free(cli_target);
+    }
   }
 
   // Targets are all set by now
@@ -341,7 +366,8 @@ std::unique_ptr<ScenarioResult> RunScenario(
 
     int server_core_limit = initial_server_config.core_limit();
     int client_core_limit = initial_client_config.core_limit();
-    if ((server_core_limit > 0) || (client_core_limit > 0)) {
+    if (configure_core_lists &&
+        ((server_core_limit > 0) || (client_core_limit > 0))) {
       auto& dq = hosts_cores.at(get_host(worker));
       if (client_core_limit == 0) {
         // limit client cores if it matches a server host
@@ -359,6 +385,7 @@ std::unique_ptr<ScenarioResult> RunScenario(
       }
       if (client_core_limit > 0) {
         GPR_ASSERT(dq.size() >= static_cast<size_t>(client_core_limit));
+        gpr_log(GPR_INFO, "Setting client core_list");
         for (int core = 0; core < client_core_limit; core++) {
           per_client_config.add_core_list(dq.front());
           dq.pop_front();
@@ -548,6 +575,9 @@ bool RunQuit() {
   // Get client, server lists
   bool result = true;
   auto workers = get_workers("QPS_WORKERS");
+  if (workers.size() == 0) {
+    return false;
+  }
   for (size_t i = 0; i < workers.size(); i++) {
     auto stub = WorkerService::NewStub(
         CreateChannel(workers[i], InsecureChannelCredentials()));
diff --git a/test/cpp/qps/driver.h b/test/cpp/qps/driver.h
index 93f4370cafa51787051e509f50b1100dd0c1dd6f..b5c8152e1bc945d9aaf9be5aadf6edfea2e70e96 100644
--- a/test/cpp/qps/driver.h
+++ b/test/cpp/qps/driver.h
@@ -45,7 +45,9 @@ namespace testing {
 std::unique_ptr<ScenarioResult> RunScenario(
     const grpc::testing::ClientConfig& client_config, size_t num_clients,
     const grpc::testing::ServerConfig& server_config, size_t num_servers,
-    int warmup_seconds, int benchmark_seconds, int spawn_local_worker_count);
+    int warmup_seconds, int benchmark_seconds, int spawn_local_worker_count,
+    const char* qps_server_target_override = "",
+    bool configure_core_lists = true);
 
 bool RunQuit();
 }  // namespace testing
diff --git a/test/cpp/qps/gen_build_yaml.py b/test/cpp/qps/gen_build_yaml.py
index 4aa58d2737c1600aaa92dada6a178eaa341e015d..188d6196e5aca0790ce40ec3e7e8428b5c5e9ef0 100755
--- a/test/cpp/qps/gen_build_yaml.py
+++ b/test/cpp/qps/gen_build_yaml.py
@@ -91,7 +91,7 @@ print yaml.dump({
       'boringssl': True,
       'defaults': 'boringssl',
       'cpu_cost': guess_cpu(scenario_json, False),
-      'exclude_configs': ['tsan'],
+      'exclude_configs': ['tsan', 'asan'],
       'timeout_seconds': 6*60
     }
     for scenario_json in scenario_config.CXXLanguage().scenarios()
@@ -99,7 +99,7 @@ print yaml.dump({
   ] + [
     {
       'name': 'json_run_localhost',
-      'shortname': 'json_run_localhost:%s' % scenario_json['name'],
+      'shortname': 'json_run_localhost:%s_low_thread_count' % scenario_json['name'],
       'args': ['--scenarios_json', _scenario_json_string(scenario_json, True)],
       'ci_platforms': ['linux'],
       'platforms': ['linux'],
@@ -108,7 +108,7 @@ print yaml.dump({
       'boringssl': True,
       'defaults': 'boringssl',
       'cpu_cost': guess_cpu(scenario_json, True),
-      'exclude_configs': sorted(c for c in configs_from_yaml if c != 'tsan'),
+      'exclude_configs': sorted(c for c in configs_from_yaml if c not in ('tsan', 'asan')),
       'timeout_seconds': 6*60
     }
     for scenario_json in scenario_config.CXXLanguage().scenarios()
diff --git a/test/cpp/qps/qps_json_driver.cc b/test/cpp/qps/qps_json_driver.cc
index 31b5917fb74d58c66562ddadc83fa104e235384d..57ee5ef63cd1eacbb94db2b9cfa239c5426d4bf6 100644
--- a/test/cpp/qps/qps_json_driver.cc
+++ b/test/cpp/qps/qps_json_driver.cc
@@ -67,17 +67,25 @@ DEFINE_double(error_tolerance, 0.01,
               "range is narrower than the error_tolerance computed range, we "
               "stop the search.");
 
+DEFINE_string(qps_server_target_override, "",
+              "Override QPS server target to configure in client configs."
+              "Only applicable if there is a single benchmark server.");
+DEFINE_bool(configure_core_lists, true,
+            "Provide 'core_list' parameters to workers. Value determined "
+            "by cores available and 'core_limit' parameters of the scenarios.");
+
 namespace grpc {
 namespace testing {
 
 static std::unique_ptr<ScenarioResult> RunAndReport(const Scenario& scenario,
                                                     bool* success) {
   std::cerr << "RUNNING SCENARIO: " << scenario.name() << "\n";
-  auto result =
-      RunScenario(scenario.client_config(), scenario.num_clients(),
-                  scenario.server_config(), scenario.num_servers(),
-                  scenario.warmup_seconds(), scenario.benchmark_seconds(),
-                  scenario.spawn_local_worker_count());
+  auto result = RunScenario(
+      scenario.client_config(), scenario.num_clients(),
+      scenario.server_config(), scenario.num_servers(),
+      scenario.warmup_seconds(), scenario.benchmark_seconds(),
+      scenario.spawn_local_worker_count(),
+      FLAGS_qps_server_target_override.c_str(), FLAGS_configure_core_lists);
 
   // Amend the result with scenario config. Eventually we should adjust
   // RunScenario contract so we don't need to touch the result here.
@@ -204,6 +212,7 @@ static bool QpsDriver() {
             SearchOfferedLoad(FLAGS_initial_search_value,
                               FLAGS_targeted_cpu_load, scenario, &success);
         gpr_log(GPR_INFO, "targeted_offered_load %f", targeted_offered_load);
+        GetCpuLoad(scenario, targeted_offered_load, &success);
       } else {
         gpr_log(GPR_ERROR, "Unimplemented search param");
       }
diff --git a/test/cpp/util/create_test_channel.cc b/test/cpp/util/create_test_channel.cc
index fe8b5d54235b1bd2fe64c24e4bf63db290ec95a3..ad62e03490e359501e2fa1a1dab1662ba34f5391 100644
--- a/test/cpp/util/create_test_channel.cc
+++ b/test/cpp/util/create_test_channel.cc
@@ -35,11 +35,37 @@
 
 #include <grpc++/create_channel.h>
 #include <grpc++/security/credentials.h>
+#include <grpc/support/log.h>
 
-#include "test/core/end2end/data/ssl_test_data.h"
+#include "test/cpp/util/test_credentials_provider.h"
 
 namespace grpc {
 
+namespace {
+
+const char kProdTlsCredentialsType[] = "prod_ssl";
+
+class SslCredentialProvider : public testing::CredentialTypeProvider {
+ public:
+  std::shared_ptr<ChannelCredentials> GetChannelCredentials(
+      grpc::ChannelArguments* args) override {
+    return SslCredentials(SslCredentialsOptions());
+  }
+  std::shared_ptr<ServerCredentials> GetServerCredentials() override {
+    return nullptr;
+  }
+};
+
+gpr_once g_once_init_add_prod_ssl_provider = GPR_ONCE_INIT;
+// Register ssl with non-test roots type to the credentials provider.
+void AddProdSslType() {
+  testing::GetCredentialsProvider()->AddSecureType(
+      kProdTlsCredentialsType, std::unique_ptr<testing::CredentialTypeProvider>(
+                                   new SslCredentialProvider));
+}
+
+}  // namespace
+
 // When ssl is enabled, if server is empty, override_hostname is used to
 // create channel. Otherwise, connect to server and override hostname if
 // override_hostname is provided.
@@ -61,16 +87,22 @@ std::shared_ptr<Channel> CreateTestChannel(
     const std::shared_ptr<CallCredentials>& creds,
     const ChannelArguments& args) {
   ChannelArguments channel_args(args);
+  std::shared_ptr<ChannelCredentials> channel_creds;
   if (enable_ssl) {
-    const char* roots_certs = use_prod_roots ? "" : test_root_cert;
-    SslCredentialsOptions ssl_opts = {roots_certs, "", ""};
-
-    std::shared_ptr<ChannelCredentials> channel_creds =
-        SslCredentials(ssl_opts);
-
-    if (!server.empty() && !override_hostname.empty()) {
-      channel_args.SetSslTargetNameOverride(override_hostname);
+    if (use_prod_roots) {
+      gpr_once_init(&g_once_init_add_prod_ssl_provider, &AddProdSslType);
+      channel_creds = testing::GetCredentialsProvider()->GetChannelCredentials(
+          kProdTlsCredentialsType, &channel_args);
+      if (!server.empty() && !override_hostname.empty()) {
+        channel_args.SetSslTargetNameOverride(override_hostname);
+      }
+    } else {
+      // override_hostname is discarded as the provider handles it.
+      channel_creds = testing::GetCredentialsProvider()->GetChannelCredentials(
+          testing::kTlsCredentialsType, &channel_args);
     }
+    GPR_ASSERT(channel_creds != nullptr);
+
     const grpc::string& connect_to =
         server.empty() ? override_hostname : server;
     if (creds.get()) {
@@ -103,4 +135,18 @@ std::shared_ptr<Channel> CreateTestChannel(const grpc::string& server,
   return CreateTestChannel(server, "foo.test.google.fr", enable_ssl, false);
 }
 
+std::shared_ptr<Channel> CreateTestChannel(
+    const grpc::string& server, const grpc::string& credential_type,
+    const std::shared_ptr<CallCredentials>& creds) {
+  ChannelArguments channel_args;
+  std::shared_ptr<ChannelCredentials> channel_creds =
+      testing::GetCredentialsProvider()->GetChannelCredentials(credential_type,
+                                                               &channel_args);
+  GPR_ASSERT(channel_creds != nullptr);
+  if (creds.get()) {
+    channel_creds = CompositeChannelCredentials(channel_creds, creds);
+  }
+  return CreateCustomChannel(server, channel_creds, channel_args);
+}
+
 }  // namespace grpc
diff --git a/test/cpp/util/create_test_channel.h b/test/cpp/util/create_test_channel.h
index 4ff666dc1bc43b48cf6eb75ca55e7684d86a0319..ce71a97edbbf6b08d3bd5beb024a5dd20230b132 100644
--- a/test/cpp/util/create_test_channel.h
+++ b/test/cpp/util/create_test_channel.h
@@ -59,6 +59,10 @@ std::shared_ptr<Channel> CreateTestChannel(
     const std::shared_ptr<CallCredentials>& creds,
     const ChannelArguments& args);
 
+std::shared_ptr<Channel> CreateTestChannel(
+    const grpc::string& server, const grpc::string& credential_type,
+    const std::shared_ptr<CallCredentials>& creds);
+
 }  // namespace grpc
 
 #endif  // GRPC_TEST_CPP_UTIL_CREATE_TEST_CHANNEL_H
diff --git a/test/cpp/util/grpc_tool.cc b/test/cpp/util/grpc_tool.cc
index 03c33abe9f5cab20d7d3c78b48e48c829e2a6a15..b9900ca1b7a74c140e68ef91fbb349998a372cf5 100644
--- a/test/cpp/util/grpc_tool.cc
+++ b/test/cpp/util/grpc_tool.cc
@@ -86,11 +86,12 @@ class GrpcTool {
   // callback);
   // bool PrintTypeId(int argc, const char** argv, GrpcToolOutputCallback
   // callback);
-  // bool ParseMessage(int argc, const char** argv, GrpcToolOutputCallback
-  // callback);
-  // bool ToText(int argc, const char** argv, GrpcToolOutputCallback callback);
-  // bool ToBinary(int argc, const char** argv, GrpcToolOutputCallback
-  // callback);
+  bool ParseMessage(int argc, const char** argv, const CliCredentials& cred,
+                    GrpcToolOutputCallback callback);
+  bool ToText(int argc, const char** argv, const CliCredentials& cred,
+              GrpcToolOutputCallback callback);
+  bool ToBinary(int argc, const char** argv, const CliCredentials& cred,
+                GrpcToolOutputCallback callback);
 
   void SetPrintCommandMode(int exit_status) {
     print_command_usage_ = true;
@@ -173,9 +174,9 @@ const Command ops[] = {
     {"list", BindWith5Args(&GrpcTool::ListServices), 1, 3},
     {"call", BindWith5Args(&GrpcTool::CallMethod), 2, 3},
     {"type", BindWith5Args(&GrpcTool::PrintType), 2, 2},
-    // {"parse", BindWith5Args(&GrpcTool::ParseMessage), 2, 3},
-    // {"totext", BindWith5Args(&GrpcTool::ToText), 2, 3},
-    // {"tobinary", BindWith5Args(&GrpcTool::ToBinary), 2, 3},
+    {"parse", BindWith5Args(&GrpcTool::ParseMessage), 2, 3},
+    {"totext", BindWith5Args(&GrpcTool::ToText), 2, 3},
+    {"tobinary", BindWith5Args(&GrpcTool::ToBinary), 2, 3},
 };
 
 void Usage(const grpc::string& msg) {
@@ -185,9 +186,9 @@ void Usage(const grpc::string& msg) {
       "  grpc_cli ls ...         ; List services\n"
       "  grpc_cli call ...       ; Call method\n"
       "  grpc_cli type ...       ; Print type\n"
-      // "  grpc_cli parse ...      ; Parse message\n"
-      // "  grpc_cli totext ...     ; Convert binary message to text\n"
-      // "  grpc_cli tobinary ...   ; Convert text message to binary\n"
+      "  grpc_cli parse ...      ; Parse message\n"
+      "  grpc_cli totext ...     ; Convert binary message to text\n"
+      "  grpc_cli tobinary ...   ; Convert text message to binary\n"
       "  grpc_cli help ...       ; Print this message, or per-command usage\n"
       "\n",
       msg.c_str());
@@ -414,6 +415,7 @@ bool GrpcTool::CallMethod(int argc, const char** argv,
   grpc::string request_text;
   grpc::string server_address(argv[0]);
   grpc::string method_name(argv[1]);
+  grpc::string formatted_method_name;
   std::unique_ptr<grpc::testing::ProtoFileParser> parser;
   grpc::string serialized_request_proto;
 
@@ -450,7 +452,9 @@ bool GrpcTool::CallMethod(int argc, const char** argv,
 
   if (FLAGS_binary_input) {
     serialized_request_proto = request_text;
+    formatted_method_name = method_name;
   } else {
+    formatted_method_name = parser->GetFormattedMethodName(method_name);
     serialized_request_proto = parser->GetSerializedProtoFromMethod(
         method_name, request_text, true /* is_request */);
     if (parser->HasError()) {
@@ -466,9 +470,9 @@ bool GrpcTool::CallMethod(int argc, const char** argv,
   ParseMetadataFlag(&client_metadata);
   PrintMetadata(client_metadata, "Sending client initial metadata:");
   grpc::Status status = grpc::testing::CliCall::Call(
-      channel, parser->GetFormatedMethodName(method_name),
-      serialized_request_proto, &serialized_response_proto, client_metadata,
-      &server_initial_metadata, &server_trailing_metadata);
+      channel, formatted_method_name, serialized_request_proto,
+      &serialized_response_proto, client_metadata, &server_initial_metadata,
+      &server_trailing_metadata);
   PrintMetadata(server_initial_metadata,
                 "Received initial metadata from server:");
   PrintMetadata(server_trailing_metadata,
@@ -493,5 +497,122 @@ bool GrpcTool::CallMethod(int argc, const char** argv,
   return callback(output_ss.str());
 }
 
+bool GrpcTool::ParseMessage(int argc, const char** argv,
+                            const CliCredentials& cred,
+                            GrpcToolOutputCallback callback) {
+  CommandUsage(
+      "Parse message\n"
+      "  grpc_cli parse <address> <type> [<message>]\n"
+      "    <address>                ; host:port\n"
+      "    <type>                   ; Protocol buffer type name\n"
+      "    <message>                ; Text protobuffer (overrides --infile)\n"
+      "    --protofiles             ; Comma separated proto files used as a"
+      " fallback when parsing request/response\n"
+      "    --proto_path             ; The search path of proto files, valid"
+      " only when --protofiles is given\n"
+      "    --infile                 ; Input filename (defaults to stdin)\n"
+      "    --outfile                ; Output filename (defaults to stdout)\n"
+      "    --binary_input           ; Input in binary format\n"
+      "    --binary_output          ; Output in binary format\n" +
+      cred.GetCredentialUsage());
+
+  std::stringstream output_ss;
+  grpc::string message_text;
+  grpc::string server_address(argv[0]);
+  grpc::string type_name(argv[1]);
+  std::unique_ptr<grpc::testing::ProtoFileParser> parser;
+  grpc::string serialized_request_proto;
+
+  if (argc == 3) {
+    message_text = argv[2];
+    if (!FLAGS_infile.empty()) {
+      fprintf(stderr, "warning: message given in argv, ignoring --infile.\n");
+    }
+  } else {
+    std::stringstream input_stream;
+    if (FLAGS_infile.empty()) {
+      if (isatty(STDIN_FILENO)) {
+        fprintf(stderr, "reading request message from stdin...\n");
+      }
+      input_stream << std::cin.rdbuf();
+    } else {
+      std::ifstream input_file(FLAGS_infile, std::ios::in | std::ios::binary);
+      input_stream << input_file.rdbuf();
+      input_file.close();
+    }
+    message_text = input_stream.str();
+  }
+
+  if (!FLAGS_binary_input || !FLAGS_binary_output) {
+    std::shared_ptr<grpc::Channel> channel =
+        grpc::CreateChannel(server_address, cred.GetCredentials());
+    parser.reset(
+        new grpc::testing::ProtoFileParser(FLAGS_remotedb ? channel : nullptr,
+                                           FLAGS_proto_path, FLAGS_protofiles));
+    if (parser->HasError()) {
+      return false;
+    }
+  }
+
+  if (FLAGS_binary_input) {
+    serialized_request_proto = message_text;
+  } else {
+    serialized_request_proto =
+        parser->GetSerializedProtoFromMessageType(type_name, message_text);
+    if (parser->HasError()) {
+      return false;
+    }
+  }
+
+  if (FLAGS_binary_output) {
+    output_ss << serialized_request_proto;
+  } else {
+    grpc::string output_text = parser->GetTextFormatFromMessageType(
+        type_name, serialized_request_proto);
+    if (parser->HasError()) {
+      return false;
+    }
+    output_ss << output_text << std::endl;
+  }
+
+  return callback(output_ss.str());
+}
+
+bool GrpcTool::ToText(int argc, const char** argv, const CliCredentials& cred,
+                      GrpcToolOutputCallback callback) {
+  CommandUsage(
+      "Convert binary message to text\n"
+      "  grpc_cli totext <protofiles> <type>\n"
+      "    <protofiles>             ; Comma separated list of proto files\n"
+      "    <type>                   ; Protocol buffer type name\n"
+      "    --proto_path             ; The search path of proto files\n"
+      "    --infile                 ; Input filename (defaults to stdin)\n"
+      "    --outfile                ; Output filename (defaults to stdout)\n");
+
+  FLAGS_protofiles = argv[0];
+  FLAGS_remotedb = false;
+  FLAGS_binary_input = true;
+  FLAGS_binary_output = false;
+  return ParseMessage(argc, argv, cred, callback);
+}
+
+bool GrpcTool::ToBinary(int argc, const char** argv, const CliCredentials& cred,
+                        GrpcToolOutputCallback callback) {
+  CommandUsage(
+      "Convert text message to binary\n"
+      "  grpc_cli tobinary <protofiles> <type> [<message>]\n"
+      "    <protofiles>             ; Comma separated list of proto files\n"
+      "    <type>                   ; Protocol buffer type name\n"
+      "    --proto_path             ; The search path of proto files\n"
+      "    --infile                 ; Input filename (defaults to stdin)\n"
+      "    --outfile                ; Output filename (defaults to stdout)\n");
+
+  FLAGS_protofiles = argv[0];
+  FLAGS_remotedb = false;
+  FLAGS_binary_input = false;
+  FLAGS_binary_output = true;
+  return ParseMessage(argc, argv, cred, callback);
+}
+
 }  // namespace testing
 }  // namespace grpc
diff --git a/test/cpp/util/grpc_tool_test.cc b/test/cpp/util/grpc_tool_test.cc
index 1ff8172306f69c9e57ae2623285d345c0359194e..33ce611a604d16bc18c117059cd535d2eff66cf4 100644
--- a/test/cpp/util/grpc_tool_test.cc
+++ b/test/cpp/util/grpc_tool_test.cc
@@ -86,9 +86,18 @@ using grpc::testing::EchoResponse;
   "  rpc Echo(grpc.testing.EchoRequest) returns (grpc.testing.EchoResponse) " \
   "{}\n"
 
+#define ECHO_RESPONSE_MESSAGE \
+  "message: \"echo\"\n"       \
+  "param {\n"                 \
+  "  host: \"localhost\"\n"   \
+  "  peer: \"peer\"\n"        \
+  "}\n\n"
+
 namespace grpc {
 namespace testing {
 
+DECLARE_bool(binary_input);
+DECLARE_bool(binary_output);
 DECLARE_bool(l);
 
 namespace {
@@ -338,6 +347,47 @@ TEST_F(GrpcToolTest, CallCommand) {
   ShutdownServer();
 }
 
+TEST_F(GrpcToolTest, ParseCommand) {
+  // Test input "grpc_cli parse localhost:<port> grpc.testing.EchoResponse
+  // ECHO_RESPONSE_MESSAGE"
+  std::stringstream output_stream;
+  std::stringstream binary_output_stream;
+
+  const grpc::string server_address = SetUpServer();
+  const char* argv[] = {"grpc_cli", "parse", server_address.c_str(),
+                        "grpc.testing.EchoResponse", ECHO_RESPONSE_MESSAGE};
+
+  FLAGS_binary_input = false;
+  FLAGS_binary_output = false;
+  EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(),
+                                   std::bind(PrintStream, &output_stream,
+                                             std::placeholders::_1)));
+  // Expected output: ECHO_RESPONSE_MESSAGE
+  EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), ECHO_RESPONSE_MESSAGE));
+
+  // Parse text message to binary message and then parse it back to text message
+  output_stream.str(grpc::string());
+  output_stream.clear();
+  FLAGS_binary_output = true;
+  EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(),
+                                   std::bind(PrintStream, &output_stream,
+                                             std::placeholders::_1)));
+  grpc::string binary_data = output_stream.str();
+  output_stream.str(grpc::string());
+  output_stream.clear();
+  argv[4] = binary_data.c_str();
+  FLAGS_binary_input = true;
+  FLAGS_binary_output = false;
+  EXPECT_TRUE(0 == GrpcToolMainLib(5, argv, TestCliCredentials(),
+                                   std::bind(PrintStream, &output_stream,
+                                             std::placeholders::_1)));
+
+  // Expected output: ECHO_RESPONSE_MESSAGE
+  EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), ECHO_RESPONSE_MESSAGE));
+
+  ShutdownServer();
+}
+
 TEST_F(GrpcToolTest, TooFewArguments) {
   // Test input "grpc_cli call Echo"
   std::stringstream output_stream;
diff --git a/test/cpp/util/proto_file_parser.cc b/test/cpp/util/proto_file_parser.cc
index 3e524227e5ca1e3451c7cc05c9a9ff4a83f2b874..bc8a6083f416c51972114403598b9f06b97aa5cc 100644
--- a/test/cpp/util/proto_file_parser.cc
+++ b/test/cpp/util/proto_file_parser.cc
@@ -172,19 +172,19 @@ grpc::string ProtoFileParser::GetFullMethodName(const grpc::string& method) {
   return method_descriptor->full_name();
 }
 
-grpc::string ProtoFileParser::GetFormatedMethodName(
+grpc::string ProtoFileParser::GetFormattedMethodName(
     const grpc::string& method) {
   has_error_ = false;
-  grpc::string formated_method_name = GetFullMethodName(method);
+  grpc::string formatted_method_name = GetFullMethodName(method);
   if (has_error_) {
     return "";
   }
-  size_t last_dot = formated_method_name.find_last_of('.');
+  size_t last_dot = formatted_method_name.find_last_of('.');
   if (last_dot != grpc::string::npos) {
-    formated_method_name[last_dot] = '/';
+    formatted_method_name[last_dot] = '/';
   }
-  formated_method_name.insert(formated_method_name.begin(), '/');
-  return formated_method_name;
+  formatted_method_name.insert(formatted_method_name.begin(), '/');
+  return formatted_method_name;
 }
 
 grpc::string ProtoFileParser::GetMessageTypeFromMethod(
diff --git a/test/cpp/util/proto_file_parser.h b/test/cpp/util/proto_file_parser.h
index eda3991e7270552bd95ea843544f27f64a7ec2f0..c1070a37b565c37ea1186d123e0bfce6dfd6de25 100644
--- a/test/cpp/util/proto_file_parser.h
+++ b/test/cpp/util/proto_file_parser.h
@@ -64,9 +64,9 @@ class ProtoFileParser {
   // descriptor database queries.
   grpc::string GetFullMethodName(const grpc::string& method);
 
-  // Formated method name is in the form of /Service/Method, it's good to be
+  // Formatted method name is in the form of /Service/Method, it's good to be
   // used as the argument of Stub::Call()
-  grpc::string GetFormatedMethodName(const grpc::string& method);
+  grpc::string GetFormattedMethodName(const grpc::string& method);
 
   grpc::string GetSerializedProtoFromMethod(
       const grpc::string& method, const grpc::string& text_format_proto,
diff --git a/test/cpp/util/test_credentials_provider.cc b/test/cpp/util/test_credentials_provider.cc
index 0456b9666710ceb0e4fb13ce1929d8653ef78cd9..909b02a7019fe426733f140fbb4b06c5c3ca9576 100644
--- a/test/cpp/util/test_credentials_provider.cc
+++ b/test/cpp/util/test_credentials_provider.cc
@@ -43,25 +43,9 @@
 #include "test/core/end2end/data/ssl_test_data.h"
 
 namespace grpc {
+namespace testing {
 namespace {
 
-using grpc::testing::CredentialTypeProvider;
-
-// Provide test credentials. Thread-safe.
-class CredentialsProvider {
- public:
-  virtual ~CredentialsProvider() {}
-
-  virtual void AddSecureType(
-      const grpc::string& type,
-      std::unique_ptr<CredentialTypeProvider> type_provider) = 0;
-  virtual std::shared_ptr<ChannelCredentials> GetChannelCredentials(
-      const grpc::string& type, ChannelArguments* args) = 0;
-  virtual std::shared_ptr<ServerCredentials> GetServerCredentials(
-      const grpc::string& type) = 0;
-  virtual std::vector<grpc::string> GetSecureCredentialsTypeList() = 0;
-};
-
 class DefaultCredentialsProvider : public CredentialsProvider {
  public:
   ~DefaultCredentialsProvider() override {}
@@ -145,37 +129,21 @@ class DefaultCredentialsProvider : public CredentialsProvider {
       added_secure_type_providers_;
 };
 
-gpr_once g_once_init_provider = GPR_ONCE_INIT;
 CredentialsProvider* g_provider = nullptr;
 
-void CreateDefaultProvider() { g_provider = new DefaultCredentialsProvider; }
-
-CredentialsProvider* GetProvider() {
-  gpr_once_init(&g_once_init_provider, &CreateDefaultProvider);
-  return g_provider;
-}
-
 }  // namespace
 
-namespace testing {
-
-void AddSecureType(const grpc::string& type,
-                   std::unique_ptr<CredentialTypeProvider> type_provider) {
-  GetProvider()->AddSecureType(type, std::move(type_provider));
-}
-
-std::shared_ptr<ChannelCredentials> GetChannelCredentials(
-    const grpc::string& type, ChannelArguments* args) {
-  return GetProvider()->GetChannelCredentials(type, args);
-}
-
-std::shared_ptr<ServerCredentials> GetServerCredentials(
-    const grpc::string& type) {
-  return GetProvider()->GetServerCredentials(type);
+CredentialsProvider* GetCredentialsProvider() {
+  if (g_provider == nullptr) {
+    g_provider = new DefaultCredentialsProvider;
+  }
+  return g_provider;
 }
 
-std::vector<grpc::string> GetSecureCredentialsTypeList() {
-  return GetProvider()->GetSecureCredentialsTypeList();
+void SetCredentialsProvider(CredentialsProvider* provider) {
+  // For now, forbids overriding provider.
+  GPR_ASSERT(g_provider == nullptr);
+  g_provider = provider;
 }
 
 }  // namespace testing
diff --git a/test/cpp/util/test_credentials_provider.h b/test/cpp/util/test_credentials_provider.h
index 1fb311e556e8c9ac6dc99b463a6a71fe56286244..0bc52ebe4dfa2d433ee14b6603ef33c4cedbe7d8 100644
--- a/test/cpp/util/test_credentials_provider.h
+++ b/test/cpp/util/test_credentials_provider.h
@@ -59,23 +59,39 @@ class CredentialTypeProvider {
   virtual std::shared_ptr<ServerCredentials> GetServerCredentials() = 0;
 };
 
-// Add a secure type in addition to the defaults above
-// (kInsecureCredentialsType, kTlsCredentialsType) that can be returned from the
-// functions below.
-void AddSecureType(const grpc::string& type,
-                   std::unique_ptr<CredentialTypeProvider> type_provider);
-
-// Provide channel credentials according to the given type. Alter the channel
-// arguments if needed.
-std::shared_ptr<ChannelCredentials> GetChannelCredentials(
-    const grpc::string& type, ChannelArguments* args);
-
-// Provide server credentials according to the given type.
-std::shared_ptr<ServerCredentials> GetServerCredentials(
-    const grpc::string& type);
-
-// Provide a list of secure credentials type.
-std::vector<grpc::string> GetSecureCredentialsTypeList();
+// Provide test credentials. Thread-safe.
+class CredentialsProvider {
+ public:
+  virtual ~CredentialsProvider() {}
+
+  // Add a secure type in addition to the defaults. The default provider has
+  // (kInsecureCredentialsType, kTlsCredentialsType).
+  virtual void AddSecureType(
+      const grpc::string& type,
+      std::unique_ptr<CredentialTypeProvider> type_provider) = 0;
+
+  // Provide channel credentials according to the given type. Alter the channel
+  // arguments if needed. Return nullptr if type is not registered.
+  virtual std::shared_ptr<ChannelCredentials> GetChannelCredentials(
+      const grpc::string& type, ChannelArguments* args) = 0;
+
+  // Provide server credentials according to the given type.
+  // Return nullptr if type is not registered.
+  virtual std::shared_ptr<ServerCredentials> GetServerCredentials(
+      const grpc::string& type) = 0;
+
+  // Provide a list of secure credentials type.
+  virtual std::vector<grpc::string> GetSecureCredentialsTypeList() = 0;
+};
+
+// Get the current provider. Create a default one if not set.
+// Not thread-safe.
+CredentialsProvider* GetCredentialsProvider();
+
+// Set the global provider. Takes ownership. The previous set provider will be
+// destroyed.
+// Not thread-safe.
+void SetCredentialsProvider(CredentialsProvider* provider);
 
 }  // namespace testing
 }  // namespace grpc
diff --git a/test/http2_test/http2_base_server.py b/test/http2_test/http2_base_server.py
new file mode 100644
index 0000000000000000000000000000000000000000..8de028ceb1b85a90c7e563eaf05fdff6088891f5
--- /dev/null
+++ b/test/http2_test/http2_base_server.py
@@ -0,0 +1,204 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import logging
+import messages_pb2
+import struct
+
+import h2
+import h2.connection
+import twisted
+import twisted.internet
+import twisted.internet.protocol
+
+_READ_CHUNK_SIZE = 16384
+_GRPC_HEADER_SIZE = 5
+
+class H2ProtocolBaseServer(twisted.internet.protocol.Protocol):
+  def __init__(self):
+    self._conn = h2.connection.H2Connection(client_side=False)
+    self._recv_buffer = {}
+    self._handlers = {}
+    self._handlers['ConnectionMade'] = self.on_connection_made_default
+    self._handlers['DataReceived'] = self.on_data_received_default
+    self._handlers['WindowUpdated'] = self.on_window_update_default
+    self._handlers['RequestReceived'] = self.on_request_received_default
+    self._handlers['SendDone'] = self.on_send_done_default
+    self._handlers['ConnectionLost'] = self.on_connection_lost
+    self._handlers['PingAcknowledged'] = self.on_ping_acknowledged_default
+    self._stream_status = {}
+    self._send_remaining = {}
+    self._outstanding_pings = 0
+
+  def set_handlers(self, handlers):
+    self._handlers = handlers
+
+  def connectionMade(self):
+    self._handlers['ConnectionMade']()
+
+  def connectionLost(self, reason):
+    self._handlers['ConnectionLost'](reason)
+
+  def on_connection_made_default(self):
+    logging.info('Connection Made')
+    self._conn.initiate_connection()
+    self.transport.setTcpNoDelay(True)
+    self.transport.write(self._conn.data_to_send())
+
+  def on_connection_lost(self, reason):
+    logging.info('Disconnected %s' % reason)
+
+  def dataReceived(self, data):
+    try:
+      events = self._conn.receive_data(data)
+    except h2.exceptions.ProtocolError:
+      # this try/except block catches exceptions due to race between sending
+      # GOAWAY and processing a response in flight.
+      return
+    if self._conn.data_to_send:
+      self.transport.write(self._conn.data_to_send())
+    for event in events:
+      if isinstance(event, h2.events.RequestReceived) and self._handlers.has_key('RequestReceived'):
+        logging.info('RequestReceived Event for stream: %d' % event.stream_id)
+        self._handlers['RequestReceived'](event)
+      elif isinstance(event, h2.events.DataReceived) and self._handlers.has_key('DataReceived'):
+        logging.info('DataReceived Event for stream: %d' % event.stream_id)
+        self._handlers['DataReceived'](event)
+      elif isinstance(event, h2.events.WindowUpdated) and self._handlers.has_key('WindowUpdated'):
+        logging.info('WindowUpdated Event for stream: %d' % event.stream_id)
+        self._handlers['WindowUpdated'](event)
+      elif isinstance(event, h2.events.PingAcknowledged) and self._handlers.has_key('PingAcknowledged'):
+        logging.info('PingAcknowledged Event')
+        self._handlers['PingAcknowledged'](event)
+    self.transport.write(self._conn.data_to_send())
+
+  def on_ping_acknowledged_default(self, event):
+    logging.info('ping acknowledged')
+    self._outstanding_pings -= 1
+
+  def on_data_received_default(self, event):
+    self._conn.acknowledge_received_data(len(event.data), event.stream_id)
+    self._recv_buffer[event.stream_id] += event.data
+
+  def on_request_received_default(self, event):
+    self._recv_buffer[event.stream_id] = ''
+    self._stream_id = event.stream_id
+    self._stream_status[event.stream_id] = True
+    self._conn.send_headers(
+      stream_id=event.stream_id,
+      headers=[
+          (':status', '200'),
+          ('content-type', 'application/grpc'),
+          ('grpc-encoding', 'identity'),
+          ('grpc-accept-encoding', 'identity,deflate,gzip'),
+      ],
+    )
+    self.transport.write(self._conn.data_to_send())
+
+  def on_window_update_default(self, event):
+    # send pending data, if any
+    self.default_send(event.stream_id)
+
+  def send_reset_stream(self):
+    self._conn.reset_stream(self._stream_id)
+    self.transport.write(self._conn.data_to_send())
+
+  def setup_send(self, data_to_send, stream_id):
+    logging.info('Setting up data to send for stream_id: %d' % stream_id)
+    self._send_remaining[stream_id] = len(data_to_send)
+    self._send_offset = 0
+    self._data_to_send = data_to_send
+    self.default_send(stream_id)
+
+  def default_send(self, stream_id):
+    if not self._send_remaining.has_key(stream_id):
+      # not setup to send data yet
+      return
+
+    while self._send_remaining[stream_id] > 0:
+      lfcw = self._conn.local_flow_control_window(stream_id)
+      if lfcw == 0:
+        break
+      chunk_size = min(lfcw, _READ_CHUNK_SIZE)
+      bytes_to_send = min(chunk_size, self._send_remaining[stream_id])
+      logging.info('flow_control_window = %d. sending [%d:%d] stream_id %d' %
+                    (lfcw, self._send_offset, self._send_offset + bytes_to_send,
+                    stream_id))
+      data = self._data_to_send[self._send_offset : self._send_offset + bytes_to_send]
+      try:
+        self._conn.send_data(stream_id, data, False)
+      except h2.exceptions.ProtocolError:
+        logging.info('Stream %d is closed' % stream_id)
+        break
+      self._send_remaining[stream_id] -= bytes_to_send
+      self._send_offset += bytes_to_send
+      if self._send_remaining[stream_id] == 0:
+        self._handlers['SendDone'](stream_id)
+
+  def default_ping(self):
+    logging.info('sending ping')
+    self._outstanding_pings += 1
+    self._conn.ping(b'\x00'*8)
+    self.transport.write(self._conn.data_to_send())
+
+  def on_send_done_default(self, stream_id):
+    if self._stream_status[stream_id]:
+      self._stream_status[stream_id] = False
+      self.default_send_trailer(stream_id)
+    else:
+      logging.error('Stream %d is already closed' % stream_id)
+
+  def default_send_trailer(self, stream_id):
+    logging.info('Sending trailer for stream id %d' % stream_id)
+    self._conn.send_headers(stream_id,
+      headers=[ ('grpc-status', '0') ],
+      end_stream=True
+    )
+    self.transport.write(self._conn.data_to_send())
+
+  @staticmethod
+  def default_response_data(response_size):
+    sresp = messages_pb2.SimpleResponse()
+    sresp.payload.body = b'\x00'*response_size
+    serialized_resp_proto = sresp.SerializeToString()
+    response_data = b'\x00' + struct.pack('i', len(serialized_resp_proto))[::-1] + serialized_resp_proto
+    return response_data
+
+  def parse_received_data(self, stream_id):
+    """ returns a grpc framed string of bytes containing response proto of the size
+    asked in request """
+    recv_buffer = self._recv_buffer[stream_id]
+    grpc_msg_size = struct.unpack('i',recv_buffer[1:5][::-1])[0]
+    if len(recv_buffer) != _GRPC_HEADER_SIZE + grpc_msg_size:
+      return None
+    req_proto_str = recv_buffer[5:5+grpc_msg_size]
+    sr = messages_pb2.SimpleRequest()
+    sr.ParseFromString(req_proto_str)
+    logging.info('Parsed request for stream %d: response_size=%s' % (stream_id, sr.response_size))
+    return sr
diff --git a/test/http2_test/http2_test_server.py b/test/http2_test/http2_test_server.py
new file mode 100644
index 0000000000000000000000000000000000000000..abde3433ad22782ab1757c52650e6575f4e8d014
--- /dev/null
+++ b/test/http2_test/http2_test_server.py
@@ -0,0 +1,104 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""HTTP2 Test Server"""
+
+import argparse
+import logging
+import twisted
+import twisted.internet
+import twisted.internet.endpoints
+import twisted.internet.reactor
+
+import http2_base_server
+import test_goaway
+import test_max_streams
+import test_ping
+import test_rst_after_data
+import test_rst_after_header
+import test_rst_during_data
+
+_TEST_CASE_MAPPING = {
+  'rst_after_header': test_rst_after_header.TestcaseRstStreamAfterHeader,
+  'rst_after_data': test_rst_after_data.TestcaseRstStreamAfterData,
+  'rst_during_data': test_rst_during_data.TestcaseRstStreamDuringData,
+  'goaway': test_goaway.TestcaseGoaway,
+  'ping': test_ping.TestcasePing,
+  'max_streams': test_max_streams.TestcaseSettingsMaxStreams,
+}
+
+class H2Factory(twisted.internet.protocol.Factory):
+  def __init__(self, testcase):
+    logging.info('Creating H2Factory for new connection.')
+    self._num_streams = 0
+    self._testcase = testcase
+
+  def buildProtocol(self, addr):
+    self._num_streams += 1
+    logging.info('New Connection: %d' % self._num_streams)
+    if not _TEST_CASE_MAPPING.has_key(self._testcase):
+      logging.error('Unknown test case: %s' % self._testcase)
+      assert(0)
+    else:
+      t = _TEST_CASE_MAPPING[self._testcase]
+
+    if self._testcase == 'goaway':
+      return t(self._num_streams).get_base_server()
+    else:
+      return t().get_base_server()
+
+def parse_arguments():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--base_port', type=int, default=8080,
+    help='base port to run the servers (default: 8080). One test server is '
+    'started on each incrementing port, beginning with base_port, in the '
+    'following order: goaway,max_streams,ping,rst_after_data,rst_after_header,'
+    'rst_during_data'
+    )
+  return parser.parse_args()
+
+def start_test_servers(base_port):
+  """ Start one server per test case on incrementing port numbers
+  beginning with base_port """
+  index = 0
+  for test_case in sorted(_TEST_CASE_MAPPING.keys()):
+    portnum = base_port + index
+    logging.warning('serving on port %d : %s'%(portnum, test_case))
+    endpoint = twisted.internet.endpoints.TCP4ServerEndpoint(
+      twisted.internet.reactor, portnum, backlog=128)
+    endpoint.listen(H2Factory(test_case))
+    index += 1
+
+if __name__ == '__main__':
+  logging.basicConfig(
+    format='%(levelname) -10s %(asctime)s %(module)s:%(lineno)s | %(message)s',
+    level=logging.INFO)
+  args = parse_arguments()
+  start_test_servers(args.base_port)
+  twisted.internet.reactor.run()
diff --git a/test/http2_test/messages_pb2.py b/test/http2_test/messages_pb2.py
new file mode 100644
index 0000000000000000000000000000000000000000..86cf5a8970fc670640e0ba29c3c0035be4a43feb
--- /dev/null
+++ b/test/http2_test/messages_pb2.py
@@ -0,0 +1,661 @@
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: messages.proto
+
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+from google.protobuf.internal import enum_type_wrapper
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf import descriptor_pb2
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='messages.proto',
+  package='grpc.testing',
+  syntax='proto3',
+  serialized_pb=_b('\n\x0emessages.proto\x12\x0cgrpc.testing\"\x1a\n\tBoolValue\x12\r\n\x05value\x18\x01 \x01(\x08\"@\n\x07Payload\x12\'\n\x04type\x18\x01 \x01(\x0e\x32\x19.grpc.testing.PayloadType\x12\x0c\n\x04\x62ody\x18\x02 \x01(\x0c\"+\n\nEchoStatus\x12\x0c\n\x04\x63ode\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xce\x02\n\rSimpleRequest\x12\x30\n\rresponse_type\x18\x01 \x01(\x0e\x32\x19.grpc.testing.PayloadType\x12\x15\n\rresponse_size\x18\x02 \x01(\x05\x12&\n\x07payload\x18\x03 \x01(\x0b\x32\x15.grpc.testing.Payload\x12\x15\n\rfill_username\x18\x04 \x01(\x08\x12\x18\n\x10\x66ill_oauth_scope\x18\x05 \x01(\x08\x12\x34\n\x13response_compressed\x18\x06 \x01(\x0b\x32\x17.grpc.testing.BoolValue\x12\x31\n\x0fresponse_status\x18\x07 \x01(\x0b\x32\x18.grpc.testing.EchoStatus\x12\x32\n\x11\x65xpect_compressed\x18\x08 \x01(\x0b\x32\x17.grpc.testing.BoolValue\"_\n\x0eSimpleResponse\x12&\n\x07payload\x18\x01 \x01(\x0b\x32\x15.grpc.testing.Payload\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x13\n\x0boauth_scope\x18\x03 \x01(\t\"w\n\x19StreamingInputCallRequest\x12&\n\x07payload\x18\x01 \x01(\x0b\x32\x15.grpc.testing.Payload\x12\x32\n\x11\x65xpect_compressed\x18\x02 \x01(\x0b\x32\x17.grpc.testing.BoolValue\"=\n\x1aStreamingInputCallResponse\x12\x1f\n\x17\x61ggregated_payload_size\x18\x01 \x01(\x05\"d\n\x12ResponseParameters\x12\x0c\n\x04size\x18\x01 \x01(\x05\x12\x13\n\x0binterval_us\x18\x02 \x01(\x05\x12+\n\ncompressed\x18\x03 \x01(\x0b\x32\x17.grpc.testing.BoolValue\"\xe8\x01\n\x1aStreamingOutputCallRequest\x12\x30\n\rresponse_type\x18\x01 \x01(\x0e\x32\x19.grpc.testing.PayloadType\x12=\n\x13response_parameters\x18\x02 \x03(\x0b\x32 .grpc.testing.ResponseParameters\x12&\n\x07payload\x18\x03 \x01(\x0b\x32\x15.grpc.testing.Payload\x12\x31\n\x0fresponse_status\x18\x07 \x01(\x0b\x32\x18.grpc.testing.EchoStatus\"E\n\x1bStreamingOutputCallResponse\x12&\n\x07payload\x18\x01 \x01(\x0b\x32\x15.grpc.testing.Payload\"3\n\x0fReconnectParams\x12 \n\x18max_reconnect_backoff_ms\x18\x01 \x01(\x05\"3\n\rReconnectInfo\x12\x0e\n\x06passed\x18\x01 \x01(\x08\x12\x12\n\nbackoff_ms\x18\x02 \x03(\x05*\x1f\n\x0bPayloadType\x12\x10\n\x0c\x43OMPRESSABLE\x10\x00\x62\x06proto3')
+)
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+_PAYLOADTYPE = _descriptor.EnumDescriptor(
+  name='PayloadType',
+  full_name='grpc.testing.PayloadType',
+  filename=None,
+  file=DESCRIPTOR,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='COMPRESSABLE', index=0, number=0,
+      options=None,
+      type=None),
+  ],
+  containing_type=None,
+  options=None,
+  serialized_start=1303,
+  serialized_end=1334,
+)
+_sym_db.RegisterEnumDescriptor(_PAYLOADTYPE)
+
+PayloadType = enum_type_wrapper.EnumTypeWrapper(_PAYLOADTYPE)
+COMPRESSABLE = 0
+
+
+
+_BOOLVALUE = _descriptor.Descriptor(
+  name='BoolValue',
+  full_name='grpc.testing.BoolValue',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='value', full_name='grpc.testing.BoolValue.value', index=0,
+      number=1, type=8, cpp_type=7, label=1,
+      has_default_value=False, default_value=False,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=32,
+  serialized_end=58,
+)
+
+
+_PAYLOAD = _descriptor.Descriptor(
+  name='Payload',
+  full_name='grpc.testing.Payload',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='type', full_name='grpc.testing.Payload.type', index=0,
+      number=1, type=14, cpp_type=8, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='body', full_name='grpc.testing.Payload.body', index=1,
+      number=2, type=12, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b(""),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=60,
+  serialized_end=124,
+)
+
+
+_ECHOSTATUS = _descriptor.Descriptor(
+  name='EchoStatus',
+  full_name='grpc.testing.EchoStatus',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='code', full_name='grpc.testing.EchoStatus.code', index=0,
+      number=1, type=5, cpp_type=1, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='message', full_name='grpc.testing.EchoStatus.message', index=1,
+      number=2, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=126,
+  serialized_end=169,
+)
+
+
+_SIMPLEREQUEST = _descriptor.Descriptor(
+  name='SimpleRequest',
+  full_name='grpc.testing.SimpleRequest',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='response_type', full_name='grpc.testing.SimpleRequest.response_type', index=0,
+      number=1, type=14, cpp_type=8, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='response_size', full_name='grpc.testing.SimpleRequest.response_size', index=1,
+      number=2, type=5, cpp_type=1, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='payload', full_name='grpc.testing.SimpleRequest.payload', index=2,
+      number=3, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='fill_username', full_name='grpc.testing.SimpleRequest.fill_username', index=3,
+      number=4, type=8, cpp_type=7, label=1,
+      has_default_value=False, default_value=False,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='fill_oauth_scope', full_name='grpc.testing.SimpleRequest.fill_oauth_scope', index=4,
+      number=5, type=8, cpp_type=7, label=1,
+      has_default_value=False, default_value=False,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='response_compressed', full_name='grpc.testing.SimpleRequest.response_compressed', index=5,
+      number=6, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='response_status', full_name='grpc.testing.SimpleRequest.response_status', index=6,
+      number=7, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='expect_compressed', full_name='grpc.testing.SimpleRequest.expect_compressed', index=7,
+      number=8, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=172,
+  serialized_end=506,
+)
+
+
+_SIMPLERESPONSE = _descriptor.Descriptor(
+  name='SimpleResponse',
+  full_name='grpc.testing.SimpleResponse',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='payload', full_name='grpc.testing.SimpleResponse.payload', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='username', full_name='grpc.testing.SimpleResponse.username', index=1,
+      number=2, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='oauth_scope', full_name='grpc.testing.SimpleResponse.oauth_scope', index=2,
+      number=3, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=508,
+  serialized_end=603,
+)
+
+
+_STREAMINGINPUTCALLREQUEST = _descriptor.Descriptor(
+  name='StreamingInputCallRequest',
+  full_name='grpc.testing.StreamingInputCallRequest',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='payload', full_name='grpc.testing.StreamingInputCallRequest.payload', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='expect_compressed', full_name='grpc.testing.StreamingInputCallRequest.expect_compressed', index=1,
+      number=2, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=605,
+  serialized_end=724,
+)
+
+
+_STREAMINGINPUTCALLRESPONSE = _descriptor.Descriptor(
+  name='StreamingInputCallResponse',
+  full_name='grpc.testing.StreamingInputCallResponse',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='aggregated_payload_size', full_name='grpc.testing.StreamingInputCallResponse.aggregated_payload_size', index=0,
+      number=1, type=5, cpp_type=1, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=726,
+  serialized_end=787,
+)
+
+
+_RESPONSEPARAMETERS = _descriptor.Descriptor(
+  name='ResponseParameters',
+  full_name='grpc.testing.ResponseParameters',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='size', full_name='grpc.testing.ResponseParameters.size', index=0,
+      number=1, type=5, cpp_type=1, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='interval_us', full_name='grpc.testing.ResponseParameters.interval_us', index=1,
+      number=2, type=5, cpp_type=1, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='compressed', full_name='grpc.testing.ResponseParameters.compressed', index=2,
+      number=3, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=789,
+  serialized_end=889,
+)
+
+
+_STREAMINGOUTPUTCALLREQUEST = _descriptor.Descriptor(
+  name='StreamingOutputCallRequest',
+  full_name='grpc.testing.StreamingOutputCallRequest',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='response_type', full_name='grpc.testing.StreamingOutputCallRequest.response_type', index=0,
+      number=1, type=14, cpp_type=8, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='response_parameters', full_name='grpc.testing.StreamingOutputCallRequest.response_parameters', index=1,
+      number=2, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='payload', full_name='grpc.testing.StreamingOutputCallRequest.payload', index=2,
+      number=3, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='response_status', full_name='grpc.testing.StreamingOutputCallRequest.response_status', index=3,
+      number=7, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=892,
+  serialized_end=1124,
+)
+
+
+_STREAMINGOUTPUTCALLRESPONSE = _descriptor.Descriptor(
+  name='StreamingOutputCallResponse',
+  full_name='grpc.testing.StreamingOutputCallResponse',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='payload', full_name='grpc.testing.StreamingOutputCallResponse.payload', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1126,
+  serialized_end=1195,
+)
+
+
+_RECONNECTPARAMS = _descriptor.Descriptor(
+  name='ReconnectParams',
+  full_name='grpc.testing.ReconnectParams',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='max_reconnect_backoff_ms', full_name='grpc.testing.ReconnectParams.max_reconnect_backoff_ms', index=0,
+      number=1, type=5, cpp_type=1, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1197,
+  serialized_end=1248,
+)
+
+
+_RECONNECTINFO = _descriptor.Descriptor(
+  name='ReconnectInfo',
+  full_name='grpc.testing.ReconnectInfo',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='passed', full_name='grpc.testing.ReconnectInfo.passed', index=0,
+      number=1, type=8, cpp_type=7, label=1,
+      has_default_value=False, default_value=False,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='backoff_ms', full_name='grpc.testing.ReconnectInfo.backoff_ms', index=1,
+      number=2, type=5, cpp_type=1, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=1250,
+  serialized_end=1301,
+)
+
+_PAYLOAD.fields_by_name['type'].enum_type = _PAYLOADTYPE
+_SIMPLEREQUEST.fields_by_name['response_type'].enum_type = _PAYLOADTYPE
+_SIMPLEREQUEST.fields_by_name['payload'].message_type = _PAYLOAD
+_SIMPLEREQUEST.fields_by_name['response_compressed'].message_type = _BOOLVALUE
+_SIMPLEREQUEST.fields_by_name['response_status'].message_type = _ECHOSTATUS
+_SIMPLEREQUEST.fields_by_name['expect_compressed'].message_type = _BOOLVALUE
+_SIMPLERESPONSE.fields_by_name['payload'].message_type = _PAYLOAD
+_STREAMINGINPUTCALLREQUEST.fields_by_name['payload'].message_type = _PAYLOAD
+_STREAMINGINPUTCALLREQUEST.fields_by_name['expect_compressed'].message_type = _BOOLVALUE
+_RESPONSEPARAMETERS.fields_by_name['compressed'].message_type = _BOOLVALUE
+_STREAMINGOUTPUTCALLREQUEST.fields_by_name['response_type'].enum_type = _PAYLOADTYPE
+_STREAMINGOUTPUTCALLREQUEST.fields_by_name['response_parameters'].message_type = _RESPONSEPARAMETERS
+_STREAMINGOUTPUTCALLREQUEST.fields_by_name['payload'].message_type = _PAYLOAD
+_STREAMINGOUTPUTCALLREQUEST.fields_by_name['response_status'].message_type = _ECHOSTATUS
+_STREAMINGOUTPUTCALLRESPONSE.fields_by_name['payload'].message_type = _PAYLOAD
+DESCRIPTOR.message_types_by_name['BoolValue'] = _BOOLVALUE
+DESCRIPTOR.message_types_by_name['Payload'] = _PAYLOAD
+DESCRIPTOR.message_types_by_name['EchoStatus'] = _ECHOSTATUS
+DESCRIPTOR.message_types_by_name['SimpleRequest'] = _SIMPLEREQUEST
+DESCRIPTOR.message_types_by_name['SimpleResponse'] = _SIMPLERESPONSE
+DESCRIPTOR.message_types_by_name['StreamingInputCallRequest'] = _STREAMINGINPUTCALLREQUEST
+DESCRIPTOR.message_types_by_name['StreamingInputCallResponse'] = _STREAMINGINPUTCALLRESPONSE
+DESCRIPTOR.message_types_by_name['ResponseParameters'] = _RESPONSEPARAMETERS
+DESCRIPTOR.message_types_by_name['StreamingOutputCallRequest'] = _STREAMINGOUTPUTCALLREQUEST
+DESCRIPTOR.message_types_by_name['StreamingOutputCallResponse'] = _STREAMINGOUTPUTCALLRESPONSE
+DESCRIPTOR.message_types_by_name['ReconnectParams'] = _RECONNECTPARAMS
+DESCRIPTOR.message_types_by_name['ReconnectInfo'] = _RECONNECTINFO
+DESCRIPTOR.enum_types_by_name['PayloadType'] = _PAYLOADTYPE
+
+BoolValue = _reflection.GeneratedProtocolMessageType('BoolValue', (_message.Message,), dict(
+  DESCRIPTOR = _BOOLVALUE,
+  __module__ = 'messages_pb2'
+  # @@protoc_insertion_point(class_scope:grpc.testing.BoolValue)
+  ))
+_sym_db.RegisterMessage(BoolValue)
+
+Payload = _reflection.GeneratedProtocolMessageType('Payload', (_message.Message,), dict(
+  DESCRIPTOR = _PAYLOAD,
+  __module__ = 'messages_pb2'
+  # @@protoc_insertion_point(class_scope:grpc.testing.Payload)
+  ))
+_sym_db.RegisterMessage(Payload)
+
+EchoStatus = _reflection.GeneratedProtocolMessageType('EchoStatus', (_message.Message,), dict(
+  DESCRIPTOR = _ECHOSTATUS,
+  __module__ = 'messages_pb2'
+  # @@protoc_insertion_point(class_scope:grpc.testing.EchoStatus)
+  ))
+_sym_db.RegisterMessage(EchoStatus)
+
+SimpleRequest = _reflection.GeneratedProtocolMessageType('SimpleRequest', (_message.Message,), dict(
+  DESCRIPTOR = _SIMPLEREQUEST,
+  __module__ = 'messages_pb2'
+  # @@protoc_insertion_point(class_scope:grpc.testing.SimpleRequest)
+  ))
+_sym_db.RegisterMessage(SimpleRequest)
+
+SimpleResponse = _reflection.GeneratedProtocolMessageType('SimpleResponse', (_message.Message,), dict(
+  DESCRIPTOR = _SIMPLERESPONSE,
+  __module__ = 'messages_pb2'
+  # @@protoc_insertion_point(class_scope:grpc.testing.SimpleResponse)
+  ))
+_sym_db.RegisterMessage(SimpleResponse)
+
+StreamingInputCallRequest = _reflection.GeneratedProtocolMessageType('StreamingInputCallRequest', (_message.Message,), dict(
+  DESCRIPTOR = _STREAMINGINPUTCALLREQUEST,
+  __module__ = 'messages_pb2'
+  # @@protoc_insertion_point(class_scope:grpc.testing.StreamingInputCallRequest)
+  ))
+_sym_db.RegisterMessage(StreamingInputCallRequest)
+
+StreamingInputCallResponse = _reflection.GeneratedProtocolMessageType('StreamingInputCallResponse', (_message.Message,), dict(
+  DESCRIPTOR = _STREAMINGINPUTCALLRESPONSE,
+  __module__ = 'messages_pb2'
+  # @@protoc_insertion_point(class_scope:grpc.testing.StreamingInputCallResponse)
+  ))
+_sym_db.RegisterMessage(StreamingInputCallResponse)
+
+ResponseParameters = _reflection.GeneratedProtocolMessageType('ResponseParameters', (_message.Message,), dict(
+  DESCRIPTOR = _RESPONSEPARAMETERS,
+  __module__ = 'messages_pb2'
+  # @@protoc_insertion_point(class_scope:grpc.testing.ResponseParameters)
+  ))
+_sym_db.RegisterMessage(ResponseParameters)
+
+StreamingOutputCallRequest = _reflection.GeneratedProtocolMessageType('StreamingOutputCallRequest', (_message.Message,), dict(
+  DESCRIPTOR = _STREAMINGOUTPUTCALLREQUEST,
+  __module__ = 'messages_pb2'
+  # @@protoc_insertion_point(class_scope:grpc.testing.StreamingOutputCallRequest)
+  ))
+_sym_db.RegisterMessage(StreamingOutputCallRequest)
+
+StreamingOutputCallResponse = _reflection.GeneratedProtocolMessageType('StreamingOutputCallResponse', (_message.Message,), dict(
+  DESCRIPTOR = _STREAMINGOUTPUTCALLRESPONSE,
+  __module__ = 'messages_pb2'
+  # @@protoc_insertion_point(class_scope:grpc.testing.StreamingOutputCallResponse)
+  ))
+_sym_db.RegisterMessage(StreamingOutputCallResponse)
+
+ReconnectParams = _reflection.GeneratedProtocolMessageType('ReconnectParams', (_message.Message,), dict(
+  DESCRIPTOR = _RECONNECTPARAMS,
+  __module__ = 'messages_pb2'
+  # @@protoc_insertion_point(class_scope:grpc.testing.ReconnectParams)
+  ))
+_sym_db.RegisterMessage(ReconnectParams)
+
+ReconnectInfo = _reflection.GeneratedProtocolMessageType('ReconnectInfo', (_message.Message,), dict(
+  DESCRIPTOR = _RECONNECTINFO,
+  __module__ = 'messages_pb2'
+  # @@protoc_insertion_point(class_scope:grpc.testing.ReconnectInfo)
+  ))
+_sym_db.RegisterMessage(ReconnectInfo)
+
+
+# @@protoc_insertion_point(module_scope)
diff --git a/test/http2_test/test_goaway.py b/test/http2_test/test_goaway.py
new file mode 100644
index 0000000000000000000000000000000000000000..61f4beb74aa5f02036289106d0fd3b2f3cb0e994
--- /dev/null
+++ b/test/http2_test/test_goaway.py
@@ -0,0 +1,77 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import logging
+import time
+
+import http2_base_server
+
+class TestcaseGoaway(object):
+  """ 
+    This test does the following:
+      Process incoming request normally, i.e. send headers, data and trailers.
+      Then send a GOAWAY frame with the stream id of the processed request.
+      It checks that the next request is made on a different TCP connection.
+  """
+  def __init__(self, iteration):
+    self._base_server = http2_base_server.H2ProtocolBaseServer()
+    self._base_server._handlers['RequestReceived'] = self.on_request_received
+    self._base_server._handlers['DataReceived'] = self.on_data_received
+    self._base_server._handlers['SendDone'] = self.on_send_done
+    self._base_server._handlers['ConnectionLost'] = self.on_connection_lost
+    self._ready_to_send = False
+    self._iteration = iteration
+
+  def get_base_server(self):
+    return self._base_server
+
+  def on_connection_lost(self, reason):
+    logging.info('Disconnect received. Count %d' % self._iteration)
+    # _iteration == 2 => Two different connections have been used.
+    if self._iteration == 2:
+      self._base_server.on_connection_lost(reason)
+
+  def on_send_done(self, stream_id):
+    self._base_server.on_send_done_default(stream_id)
+    logging.info('Sending GOAWAY for stream %d:' % stream_id)
+    self._base_server._conn.close_connection(error_code=0, additional_data=None, last_stream_id=stream_id)
+    self._base_server._stream_status[stream_id] = False
+
+  def on_request_received(self, event):
+    self._ready_to_send = False
+    self._base_server.on_request_received_default(event)
+
+  def on_data_received(self, event):
+    self._base_server.on_data_received_default(event)
+    sr = self._base_server.parse_received_data(event.stream_id)
+    if sr:
+      logging.info('Creating response size = %s' % sr.response_size)
+      response_data = self._base_server.default_response_data(sr.response_size)
+      self._ready_to_send = True
+      self._base_server.setup_send(response_data, event.stream_id)
diff --git a/test/http2_test/test_max_streams.py b/test/http2_test/test_max_streams.py
new file mode 100644
index 0000000000000000000000000000000000000000..9942b1bb9aaa168e38555fa8f6ec72a60e9888e3
--- /dev/null
+++ b/test/http2_test/test_max_streams.py
@@ -0,0 +1,63 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import hyperframe.frame
+import logging
+
+import http2_base_server
+
+class TestcaseSettingsMaxStreams(object):
+  """
+    This test sets MAX_CONCURRENT_STREAMS to 1 and asserts that at any point
+    only 1 stream is active.
+  """
+  def __init__(self):
+    self._base_server = http2_base_server.H2ProtocolBaseServer()
+    self._base_server._handlers['DataReceived'] = self.on_data_received
+    self._base_server._handlers['ConnectionMade'] = self.on_connection_made
+
+  def get_base_server(self):
+    return self._base_server
+
+  def on_connection_made(self):
+    logging.info('Connection Made')
+    self._base_server._conn.initiate_connection()
+    self._base_server._conn.update_settings(
+                  {hyperframe.frame.SettingsFrame.MAX_CONCURRENT_STREAMS: 1})
+    self._base_server.transport.setTcpNoDelay(True)
+    self._base_server.transport.write(self._base_server._conn.data_to_send())
+
+  def on_data_received(self, event):
+    self._base_server.on_data_received_default(event)
+    sr = self._base_server.parse_received_data(event.stream_id)
+    if sr:
+      logging.info('Creating response of size = %s' % sr.response_size)
+      response_data = self._base_server.default_response_data(sr.response_size)
+      self._base_server.setup_send(response_data, event.stream_id)
+    # TODO (makdharma): Add assertion to check number of live streams
diff --git a/test/http2_test/test_ping.py b/test/http2_test/test_ping.py
new file mode 100644
index 0000000000000000000000000000000000000000..da41fd01bbe332447ebec26e86517c6eddac235f
--- /dev/null
+++ b/test/http2_test/test_ping.py
@@ -0,0 +1,67 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import logging
+
+import http2_base_server
+
+class TestcasePing(object):
+  """
+    This test injects PING frames before and after header and data. Keeps count
+    of outstanding ping response and asserts when the count is non-zero at the
+    end of the test.
+  """
+  def __init__(self):
+    self._base_server = http2_base_server.H2ProtocolBaseServer()
+    self._base_server._handlers['RequestReceived'] = self.on_request_received
+    self._base_server._handlers['DataReceived'] = self.on_data_received
+    self._base_server._handlers['ConnectionLost'] = self.on_connection_lost
+
+  def get_base_server(self):
+    return self._base_server
+
+  def on_request_received(self, event):
+    self._base_server.default_ping()
+    self._base_server.on_request_received_default(event)
+    self._base_server.default_ping()
+
+  def on_data_received(self, event):
+    self._base_server.on_data_received_default(event)
+    sr = self._base_server.parse_received_data(event.stream_id)
+    if sr:
+      logging.info('Creating response size = %s' % sr.response_size)
+      response_data = self._base_server.default_response_data(sr.response_size)
+      self._base_server.default_ping()
+      self._base_server.setup_send(response_data, event.stream_id)
+      self._base_server.default_ping()
+
+  def on_connection_lost(self, reason):
+    logging.info('Disconnect received. Ping Count %d' % self._base_server._outstanding_pings)
+    assert(self._base_server._outstanding_pings == 0)
+    self._base_server.on_connection_lost(reason)
diff --git a/test/http2_test/test_rst_after_data.py b/test/http2_test/test_rst_after_data.py
new file mode 100644
index 0000000000000000000000000000000000000000..92360253951769e9387b7183656427830cc85f82
--- /dev/null
+++ b/test/http2_test/test_rst_after_data.py
@@ -0,0 +1,57 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import http2_base_server
+
+class TestcaseRstStreamAfterData(object):
+  """
+    In response to an incoming request, this test sends headers, followed by
+    data, followed by a reset stream frame. Client asserts that the RPC failed.
+    Client needs to deliver the complete message to the application layer.
+  """
+  def __init__(self):
+    self._base_server = http2_base_server.H2ProtocolBaseServer()
+    self._base_server._handlers['DataReceived'] = self.on_data_received
+    self._base_server._handlers['SendDone'] = self.on_send_done
+
+  def get_base_server(self):
+    return self._base_server
+
+  def on_data_received(self, event):
+    self._base_server.on_data_received_default(event)
+    sr = self._base_server.parse_received_data(event.stream_id)
+    if sr:
+      response_data = self._base_server.default_response_data(sr.response_size)
+      self._ready_to_send = True
+      self._base_server.setup_send(response_data, event.stream_id)
+      # send reset stream
+
+  def on_send_done(self, stream_id):
+    self._base_server.send_reset_stream()
+    self._base_server._stream_status[stream_id] = False
diff --git a/test/http2_test/test_rst_after_header.py b/test/http2_test/test_rst_after_header.py
new file mode 100644
index 0000000000000000000000000000000000000000..41e1adb8adff60da1116eece48ad43a6cfa6e9a4
--- /dev/null
+++ b/test/http2_test/test_rst_after_header.py
@@ -0,0 +1,48 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import http2_base_server
+
+class TestcaseRstStreamAfterHeader(object):
+  """
+    In response to an incoming request, this test sends headers, followed by
+    a reset stream frame. Client asserts that the RPC failed.
+  """
+  def __init__(self):
+    self._base_server = http2_base_server.H2ProtocolBaseServer()
+    self._base_server._handlers['RequestReceived'] = self.on_request_received
+
+  def get_base_server(self):
+    return self._base_server
+
+  def on_request_received(self, event):
+    # send initial headers
+    self._base_server.on_request_received_default(event)
+    # send reset stream
+    self._base_server.send_reset_stream()
diff --git a/test/http2_test/test_rst_during_data.py b/test/http2_test/test_rst_during_data.py
new file mode 100644
index 0000000000000000000000000000000000000000..7c859db267af3c2c7316c3689ad152d2edd35e99
--- /dev/null
+++ b/test/http2_test/test_rst_during_data.py
@@ -0,0 +1,58 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import http2_base_server
+
+class TestcaseRstStreamDuringData(object):
+  """
+    In response to an incoming request, this test sends headers, followed by
+    some data, followed by a reset stream frame. Client asserts that the RPC
+    failed and does not deliver the message to the application.
+  """
+  def __init__(self):
+    self._base_server = http2_base_server.H2ProtocolBaseServer()
+    self._base_server._handlers['DataReceived'] = self.on_data_received
+    self._base_server._handlers['SendDone'] = self.on_send_done
+
+  def get_base_server(self):
+    return self._base_server
+
+  def on_data_received(self, event):
+    self._base_server.on_data_received_default(event)
+    sr = self._base_server.parse_received_data(event.stream_id)
+    if sr:
+      response_data = self._base_server.default_response_data(sr.response_size)
+      self._ready_to_send = True
+      response_len = len(response_data)
+      truncated_response_data = response_data[0:response_len/2]
+      self._base_server.setup_send(truncated_response_data, event.stream_id)
+
+  def on_send_done(self, stream_id):
+    self._base_server.send_reset_stream()
+    self._base_server._stream_status[stream_id] = False
diff --git a/third_party/google_benchmark b/third_party/benchmark
similarity index 100%
rename from third_party/google_benchmark
rename to third_party/benchmark
diff --git a/third_party/boringssl-with-bazel b/third_party/boringssl-with-bazel
new file mode 160000
index 0000000000000000000000000000000000000000..886e7d75368e3f4fab3f4d0d3584e4abfc557755
--- /dev/null
+++ b/third_party/boringssl-with-bazel
@@ -0,0 +1 @@
+Subproject commit 886e7d75368e3f4fab3f4d0d3584e4abfc557755
diff --git a/third_party/zlib.BUILD b/third_party/zlib.BUILD
new file mode 100644
index 0000000000000000000000000000000000000000..7879a81c680874974e52fe023954744abfcd3277
--- /dev/null
+++ b/third_party/zlib.BUILD
@@ -0,0 +1,36 @@
+cc_library(
+    name = "z",
+    srcs = [
+        "adler32.c",
+        "compress.c",
+        "crc32.c",
+        "deflate.c",
+        "infback.c",
+        "inffast.c",
+        "inflate.c",
+        "inftrees.c",
+        "trees.c",
+        "uncompr.c",
+        "zutil.c",
+    ],
+    hdrs = [
+        "crc32.h",
+        "deflate.h",
+        "gzguts.h",
+        "inffast.h",
+        "inffixed.h",
+        "inflate.h",
+        "inftrees.h",
+        "trees.h",
+        "zconf.h",
+        "zlib.h",
+        "zutil.h",
+    ],
+    includes = [
+        "include",
+    ],
+    linkstatic = 1,
+    visibility = [
+        "//visibility:public",
+    ],
+)
diff --git a/tools/buildgen/generate_build_additions.sh b/tools/buildgen/generate_build_additions.sh
index 1ea47042f4490e788393bdffc38fbbe774a15c11..a4373ed350319f4e221bb0eac7fc7ea376b2f843 100644
--- a/tools/buildgen/generate_build_additions.sh
+++ b/tools/buildgen/generate_build_additions.sh
@@ -28,9 +28,11 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+set -e
+
 gen_build_yaml_dirs="  \
   src/boringssl        \
-  src/google_benchmark \
+  src/benchmark \
   src/proto            \
   src/zlib             \
   test/core/bad_client \
diff --git a/tools/buildgen/generate_projects.py b/tools/buildgen/generate_projects.py
index 5e78ad52d6bfdb3bb77d12cb60d085d410601dd8..f8ddaf4963c8234ef1a88d085f439d7ee522d59c 100755
--- a/tools/buildgen/generate_projects.py
+++ b/tools/buildgen/generate_projects.py
@@ -36,7 +36,7 @@ import shutil
 import sys
 import tempfile
 import multiprocessing
-sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), '..', 'run_tests'))
+sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), '..', 'run_tests', 'python_utils'))
 
 assert sys.argv[1:], 'run generate_projects.sh instead of this directly'
 
diff --git a/tools/codegen/core/gen_nano_proto.sh b/tools/codegen/core/gen_nano_proto.sh
index df107c208f670f90dd4a369236a6cf0427c130c6..99e49814b833f6d043d1f7bcc109f2e38b38d35d 100755
--- a/tools/codegen/core/gen_nano_proto.sh
+++ b/tools/codegen/core/gen_nano_proto.sh
@@ -42,46 +42,6 @@
 # 4: Output dir not an absolute path.
 # 5: Couldn't create output directory (2nd argument).
 
-read -r -d '' COPYRIGHT <<'EOF'
-/*
- *
- * Copyright <YEAR>, 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.
- *
- */
-
-EOF
-
-CURRENT_YEAR=$(date +%Y)
-COPYRIGHT_FILE=$(mktemp)
-echo "${COPYRIGHT/<YEAR>/$CURRENT_YEAR}" > $COPYRIGHT_FILE
-
 set -ex
 if [ $# -lt 2 ] || [ $# -gt 3 ]; then
   echo "Usage: $0 <input.proto> <absolute path to output dir> [grpc path]"
@@ -143,13 +103,6 @@ readonly UC_PROTO_BASENAME=`echo $PROTO_BASENAME | tr [a-z] [A-Z]`
 sed -i "s:PB_${UC_PROTO_BASENAME}_PB_H_INCLUDED:GRPC_${INCLUDE_GUARD_BASE}_${UC_PROTO_BASENAME}_PB_H:g" \
   "$OUTPUT_DIR/$PROTO_BASENAME.pb.h"
 
-# prepend copyright
-TMPFILE=$(mktemp)
-cat $COPYRIGHT_FILE "$OUTPUT_DIR/$PROTO_BASENAME.pb.c" > $TMPFILE
-mv -v $TMPFILE "$OUTPUT_DIR/$PROTO_BASENAME.pb.c"
-cat $COPYRIGHT_FILE "$OUTPUT_DIR/$PROTO_BASENAME.pb.h" > $TMPFILE
-mv -v $TMPFILE "$OUTPUT_DIR/$PROTO_BASENAME.pb.h"
-
 deactivate
 rm -rf $VENV_DIR
 
diff --git a/tools/distrib/check_copyright.py b/tools/distrib/check_copyright.py
index f06e5f1d1a15c0747b9b19fe1df4903d0a3b1aaf..718bb563f3afff4b9db800c575ef001bfd633ce6 100755
--- a/tools/distrib/check_copyright.py
+++ b/tools/distrib/check_copyright.py
@@ -92,9 +92,23 @@ LICENSE_PREFIX = {
   'LICENSE':    '',
 }
 
-KNOWN_BAD = set([
+_EXEMPT = frozenset((
+  # Generated protocol compiler output.
+  'examples/python/helloworld/helloworld_pb2.py',
+  'examples/python/helloworld/helloworld_pb2_grpc.py',
+  'examples/python/multiplex/helloworld_pb2.py',
+  'examples/python/multiplex/helloworld_pb2_grpc.py',
+  'examples/python/multiplex/route_guide_pb2.py',
+  'examples/python/multiplex/route_guide_pb2_grpc.py',
+  'examples/python/route_guide/route_guide_pb2.py',
+  'examples/python/route_guide/route_guide_pb2_grpc.py',
+
+  'src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h',
+  'src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c',
+
+  # An older file originally from outside gRPC.
   'src/php/tests/bootstrap.php',
-])
+))
 
 
 RE_YEAR = r'Copyright (?P<first_year>[0-9]+\-)?(?P<last_year>[0-9]+), Google Inc\.'
@@ -140,7 +154,8 @@ except subprocess.CalledProcessError:
   sys.exit(0)
 
 for filename in filename_list:
-  if filename in KNOWN_BAD: continue
+  if filename in _EXEMPT:
+    continue
   ext = os.path.splitext(filename)[1]
   base = os.path.basename(filename)
   if ext in RE_LICENSE:
diff --git a/tools/distrib/check_nanopb_output.sh b/tools/distrib/check_nanopb_output.sh
index c0707051a60a81ffd08b15eb5919fa1c127e9efe..eb64e23daf760dc5b93603d6208a3e3bc86d162a 100755
--- a/tools/distrib/check_nanopb_output.sh
+++ b/tools/distrib/check_nanopb_output.sh
@@ -37,7 +37,7 @@ readonly PROTOBUF_INSTALL_PREFIX="$(mktemp -d)"
 pushd third_party/protobuf
 ./autogen.sh
 ./configure --prefix="$PROTOBUF_INSTALL_PREFIX"
-make
+make -j 8
 make install
 #ldconfig
 popd
@@ -51,7 +51,7 @@ fi
 # stack up and change to nanopb's proto generator directory
 pushd third_party/nanopb/generator/proto
 export PATH="$PROTOC_BIN_PATH:$PATH"
-make
+make -j 8
 # back to the root directory
 popd
 
diff --git a/tools/distrib/clang_format_code.sh b/tools/distrib/clang_format_code.sh
index 858e07489816a7f59f273e058c9d7310db2df319..13e018709fe93a06f2f86fbc83ef40e9a8931aee 100755
--- a/tools/distrib/clang_format_code.sh
+++ b/tools/distrib/clang_format_code.sh
@@ -32,9 +32,15 @@ set -ex
 
 # change to root directory
 cd $(dirname $0)/../..
+REPO_ROOT=$(pwd)
 
-# build clang-format docker image
-docker build -t grpc_clang_format tools/dockerfile/grpc_clang_format
+if [ "$CLANG_FORMAT_SKIP_DOCKER" == "" ]
+then
+  # build clang-format docker image
+  docker build -t grpc_clang_format tools/dockerfile/grpc_clang_format
 
-# run clang-format against the checked out codebase
-docker run -e TEST=$TEST -e CHANGED_FILES="$CHANGED_FILES" --rm=true -v ${HOST_GIT_ROOT:-`pwd`}:/local-code -t grpc_clang_format /clang_format_all_the_things.sh
+  # run clang-format against the checked out codebase
+  docker run -e TEST=$TEST -e CHANGED_FILES="$CHANGED_FILES" -e CLANG_FORMAT_ROOT="/local-code" --rm=true -v "${REPO_ROOT}":/local-code -t grpc_clang_format /clang_format_all_the_things.sh
+else
+  CLANG_FORMAT_ROOT="${REPO_ROOT}" tools/dockerfile/grpc_clang_format/clang_format_all_the_things.sh
+fi
diff --git a/tools/distrib/python/docgen.py b/tools/distrib/python/docgen.py
index 622317920d4f9c7228df872e20ddc3056adae4a7..38ffcd6e0e3d52e56099a9895acb52e7f99c805d 100755
--- a/tools/distrib/python/docgen.py
+++ b/tools/distrib/python/docgen.py
@@ -94,6 +94,7 @@ if args.submit:
   # specified repository, edit it, and push it. It's up to the user to then go
   # onto GitHub and make a PR against grpc/grpc:gh-pages.
   repo_parent_dir = tempfile.mkdtemp()
+  print('Documentation parent directory: {}'.format(repo_parent_dir))
   repo_dir = os.path.join(repo_parent_dir, 'grpc')
   python_doc_dir = os.path.join(repo_dir, 'python')
   doc_branch = args.doc_branch
diff --git a/tools/distrib/python/grpcio_tools/MANIFEST.in b/tools/distrib/python/grpcio_tools/MANIFEST.in
index 7712834d642ad549cf2e1ff40dd13bb998e3e698..11ce367747aa1db4f98f7d285956f34f31e71df8 100644
--- a/tools/distrib/python/grpcio_tools/MANIFEST.in
+++ b/tools/distrib/python/grpcio_tools/MANIFEST.in
@@ -2,6 +2,6 @@ include grpc_version.py
 include protoc_deps.py
 include protoc_lib_deps.py
 include README.rst
-graft grpc
+graft grpc_tools
 graft grpc_root
 graft third_party
diff --git a/tools/distrib/python/grpcio_tools/grpc/tools/__init__.py b/tools/distrib/python/grpcio_tools/grpc_tools/__init__.py
similarity index 100%
rename from tools/distrib/python/grpcio_tools/grpc/tools/__init__.py
rename to tools/distrib/python/grpcio_tools/grpc_tools/__init__.py
diff --git a/tools/distrib/python/grpcio_tools/grpc/tools/_protoc_compiler.pyx b/tools/distrib/python/grpcio_tools/grpc_tools/_protoc_compiler.pyx
similarity index 97%
rename from tools/distrib/python/grpcio_tools/grpc/tools/_protoc_compiler.pyx
rename to tools/distrib/python/grpcio_tools/grpc_tools/_protoc_compiler.pyx
index a6530127c0474970c38d0bb35684a11375fd725d..81034fad5e0d9e9ef4d943fc2cdadafd1d15232d 100644
--- a/tools/distrib/python/grpcio_tools/grpc/tools/_protoc_compiler.pyx
+++ b/tools/distrib/python/grpcio_tools/grpc_tools/_protoc_compiler.pyx
@@ -29,7 +29,7 @@
 
 from libc cimport stdlib
 
-cdef extern from "grpc/tools/main.h":
+cdef extern from "grpc_tools/main.h":
   int protoc_main(int argc, char *argv[])
 
 def run_main(list args not None):
diff --git a/tools/distrib/python/grpcio_tools/grpc/tools/command.py b/tools/distrib/python/grpcio_tools/grpc_tools/command.py
similarity index 97%
rename from tools/distrib/python/grpcio_tools/grpc/tools/command.py
rename to tools/distrib/python/grpcio_tools/grpc_tools/command.py
index 424fd904113465d7011b036979224c166331dbf0..31b3331a66f917b67c9dc4bc40ef39baac3c4ce0 100644
--- a/tools/distrib/python/grpcio_tools/grpc/tools/command.py
+++ b/tools/distrib/python/grpcio_tools/grpc_tools/command.py
@@ -33,7 +33,7 @@ import sys
 
 import setuptools
 
-from grpc.tools import protoc
+from grpc_tools import protoc
 
 
 def build_package_protos(package_root):
@@ -45,11 +45,11 @@ def build_package_protos(package_root):
         proto_files.append(os.path.abspath(os.path.join(root, filename)))
 
   well_known_protos_include = pkg_resources.resource_filename(
-      'grpc.tools', '_proto')
+      'grpc_tools', '_proto')
 
   for proto_file in proto_files:
     command = [
-        'grpc.tools.protoc',
+        'grpc_tools.protoc',
         '--proto_path={}'.format(inclusion_root),
         '--proto_path={}'.format(well_known_protos_include),
         '--python_out={}'.format(inclusion_root),
diff --git a/tools/distrib/python/grpcio_tools/grpc/tools/main.cc b/tools/distrib/python/grpcio_tools/grpc_tools/main.cc
similarity index 98%
rename from tools/distrib/python/grpcio_tools/grpc/tools/main.cc
rename to tools/distrib/python/grpcio_tools/grpc_tools/main.cc
index 83918395135d5090676f12973ca1b11e74ccdc1e..0c2fa3180a952187f0d9dc0867cb7e3d15e3fd3b 100644
--- a/tools/distrib/python/grpcio_tools/grpc/tools/main.cc
+++ b/tools/distrib/python/grpcio_tools/grpc_tools/main.cc
@@ -32,7 +32,7 @@
 
 #include "src/compiler/python_generator.h"
 
-#include "grpc/tools/main.h"
+#include "grpc_tools/main.h"
 
 int protoc_main(int argc, char* argv[]) {
   google::protobuf::compiler::CommandLineInterface cli;
diff --git a/tools/distrib/python/grpcio_tools/grpc/tools/main.h b/tools/distrib/python/grpcio_tools/grpc_tools/main.h
similarity index 100%
rename from tools/distrib/python/grpcio_tools/grpc/tools/main.h
rename to tools/distrib/python/grpcio_tools/grpc_tools/main.h
diff --git a/tools/distrib/python/grpcio_tools/grpc/tools/protoc.py b/tools/distrib/python/grpcio_tools/grpc_tools/protoc.py
similarity index 95%
rename from tools/distrib/python/grpcio_tools/grpc/tools/protoc.py
rename to tools/distrib/python/grpcio_tools/grpc_tools/protoc.py
index e1256a7dd9bd86542fb155755ee83aa09d3d68e5..63fddb2f064640df544a9b9725585e4ac39c3681 100644
--- a/tools/distrib/python/grpcio_tools/grpc/tools/protoc.py
+++ b/tools/distrib/python/grpcio_tools/grpc_tools/protoc.py
@@ -32,7 +32,7 @@
 import pkg_resources
 import sys
 
-from grpc.tools import _protoc_compiler
+from grpc_tools import _protoc_compiler
 
 def main(command_arguments):
   """Run the protocol buffer compiler with the given command-line arguments.
@@ -45,5 +45,5 @@ def main(command_arguments):
   return _protoc_compiler.run_main(command_arguments)
 
 if __name__ == '__main__':
-  proto_include = pkg_resources.resource_filename('grpc.tools', '_proto')
+  proto_include = pkg_resources.resource_filename('grpc_tools', '_proto')
   sys.exit(main(sys.argv + ['-I{}'.format(proto_include)]))
diff --git a/tools/distrib/python/grpcio_tools/setup.py b/tools/distrib/python/grpcio_tools/setup.py
index a07a586fb2d09bd4ebc22f722b87ab993ebeeab4..502d7ef27b81d54d7e9568b6285bf98f4cee2baf 100644
--- a/tools/distrib/python/grpcio_tools/setup.py
+++ b/tools/distrib/python/grpcio_tools/setup.py
@@ -108,7 +108,7 @@ PROTO_FILES = [
 CC_INCLUDE = os.path.normpath(protoc_lib_deps.CC_INCLUDE)
 PROTO_INCLUDE = os.path.normpath(protoc_lib_deps.PROTO_INCLUDE)
 
-GRPC_PYTHON_TOOLS_PACKAGE = 'grpc.tools'
+GRPC_PYTHON_TOOLS_PACKAGE = 'grpc_tools'
 GRPC_PYTHON_PROTO_RESOURCES_NAME = '_proto'
 
 DEFINE_MACROS = ()
@@ -154,16 +154,16 @@ def package_data():
 
 def extension_modules():
   if BUILD_WITH_CYTHON:
-    plugin_sources = [os.path.join('grpc', 'tools', '_protoc_compiler.pyx')]
+    plugin_sources = [os.path.join('grpc_tools', '_protoc_compiler.pyx')]
   else:
-    plugin_sources = [os.path.join('grpc', 'tools', '_protoc_compiler.cpp')]
+    plugin_sources = [os.path.join('grpc_tools', '_protoc_compiler.cpp')]
   plugin_sources += [
-    os.path.join('grpc', 'tools', 'main.cc'),
+    os.path.join('grpc_tools', 'main.cc'),
     os.path.join('grpc_root', 'src', 'compiler', 'python_generator.cc')] + [
     os.path.join(CC_INCLUDE, cc_file)
     for cc_file in CC_FILES]
   plugin_ext = extension.Extension(
-      name='grpc.tools._protoc_compiler',
+      name='grpc_tools._protoc_compiler',
       sources=plugin_sources,
       include_dirs=[
           '.',
@@ -184,12 +184,11 @@ def extension_modules():
     return extensions
 
 setuptools.setup(
-  name='grpcio_tools',
+  name='grpcio-tools',
   version=grpc_version.VERSION,
   license='3-clause BSD',
   ext_modules=extension_modules(),
   packages=setuptools.find_packages('.'),
-  namespace_packages=['grpc'],
   install_requires=[
     'protobuf>=3.0.0',
     'grpcio>={version}'.format(version=grpc_version.VERSION),
diff --git a/tools/dockerfile/grpc_artifact_protoc/Dockerfile b/tools/dockerfile/grpc_artifact_protoc/Dockerfile
index 1bbc6e021bc67a6d2419bef498485704f86bcda3..2904a8fa51ff710f66475ed9631dfc0628c300bb 100644
--- a/tools/dockerfile/grpc_artifact_protoc/Dockerfile
+++ b/tools/dockerfile/grpc_artifact_protoc/Dockerfile
@@ -59,5 +59,11 @@ RUN yum install -y devtoolset-1.1 \
                    devtoolset-1.1-libstdc++-devel \
                    devtoolset-1.1-libstdc++-devel.i686 || true
 
+# Update Git to version >1.7 to allow cloning submodules with --reference arg.
+RUN yum remove -y git
+RUN yum install -y epel-release
+RUN yum install -y https://centos6.iuscommunity.org/ius-release.rpm
+RUN yum install -y git2u
+
 # Start in devtoolset environment that uses GCC 4.7
 CMD ["scl", "enable", "devtoolset-1.1", "bash"]
diff --git a/tools/dockerfile/grpc_artifact_python_manylinux_x64/Dockerfile b/tools/dockerfile/grpc_artifact_python_manylinux_x64/Dockerfile
index 1d4e8e1a4a4b27da4b57250a229bc8f883caa266..69e624aa41a18244eed8d6c4ce85d1e174d5ad04 100644
--- a/tools/dockerfile/grpc_artifact_python_manylinux_x64/Dockerfile
+++ b/tools/dockerfile/grpc_artifact_python_manylinux_x64/Dockerfile
@@ -34,6 +34,19 @@ FROM quay.io/pypa/manylinux1_x86_64
 # Update the package manager
 RUN yum update -y
 
+#############################################################
+# Update Git to allow cloning submodules with --reference arg
+RUN yum remove -y git
+RUN yum install -y curl-devel expat-devel gettext-devel openssl-devel zlib-devel gcc
+RUN cd /usr/src && \
+  wget https://kernel.org/pub/software/scm/git/git-2.0.5.tar.gz && \
+  tar xzf git-2.0.5.tar.gz
+RUN cd /usr/src/git-2.0.5 && \
+  make prefix=/usr/local/git all && \
+  make prefix=/usr/local/git install
+ENV PATH /usr/local/git/bin:$PATH
+RUN source /etc/bashrc
+
 ###################################
 # Install Python build requirements
 RUN /opt/python/cp27-cp27m/bin/pip install cython
diff --git a/tools/dockerfile/grpc_artifact_python_manylinux_x86/Dockerfile b/tools/dockerfile/grpc_artifact_python_manylinux_x86/Dockerfile
index 810499695ec45a34a69336edba21e70cebecfb82..9af80078edd51b6d023d30aeca5e2e16047ebb12 100644
--- a/tools/dockerfile/grpc_artifact_python_manylinux_x86/Dockerfile
+++ b/tools/dockerfile/grpc_artifact_python_manylinux_x86/Dockerfile
@@ -34,6 +34,19 @@ FROM quay.io/pypa/manylinux1_i686
 # Update the package manager
 RUN yum update -y
 
+#############################################################
+# Update Git to allow cloning submodules with --reference arg
+RUN yum remove -y git
+RUN yum install -y curl-devel expat-devel gettext-devel openssl-devel zlib-devel gcc
+RUN cd /usr/src && \
+  wget https://kernel.org/pub/software/scm/git/git-2.0.5.tar.gz && \
+  tar xzf git-2.0.5.tar.gz
+RUN cd /usr/src/git-2.0.5 && \
+  make prefix=/usr/local/git all && \
+  make prefix=/usr/local/git install
+ENV PATH /usr/local/git/bin:$PATH
+RUN source /etc/bashrc
+
 ###################################
 # Install Python build requirements
 RUN /opt/python/cp27-cp27m/bin/pip install cython
diff --git a/tools/dockerfile/grpc_clang_format/Dockerfile b/tools/dockerfile/grpc_clang_format/Dockerfile
index ab58017a02af8937b6b1c393e16032c6250618b6..85f5e4db74a639388fb9458f986e45d9aea44ead 100644
--- a/tools/dockerfile/grpc_clang_format/Dockerfile
+++ b/tools/dockerfile/grpc_clang_format/Dockerfile
@@ -27,13 +27,13 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-FROM ubuntu:wily
-RUN apt-get update
-RUN apt-get -y install wget
+FROM ubuntu:15.10
+
+RUN apt-get update && apt-get -y install wget
 RUN echo deb http://llvm.org/apt/wily/ llvm-toolchain-wily-3.8 main >> /etc/apt/sources.list
 RUN echo deb-src http://llvm.org/apt/wily/ llvm-toolchain-wily-3.8 main >> /etc/apt/sources.list
 RUN wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key| apt-key add -
-RUN apt-get update
-RUN apt-get -y install clang-format-3.8
+RUN apt-get update && apt-get -y install clang-format-3.8
+
 ADD clang_format_all_the_things.sh /
 CMD ["echo 'Run with tools/distrib/clang_format_code.sh'"]
diff --git a/tools/dockerfile/grpc_clang_format/clang_format_all_the_things.sh b/tools/dockerfile/grpc_clang_format/clang_format_all_the_things.sh
index 462c65ab5e9be20dcc582c7916b3391020b83cb7..c6e4aabfe63fcb88f4e961451699275f753c1bb5 100755
--- a/tools/dockerfile/grpc_clang_format/clang_format_all_the_things.sh
+++ b/tools/dockerfile/grpc_clang_format/clang_format_all_the_things.sh
@@ -44,7 +44,7 @@ for dir in $DIRS
 do
   for glob in $GLOB
   do
-    files="$files `find /local-code/$dir -name $glob -and -not -name *.generated.* -and -not -name *.pb.h -and -not -name *.pb.c -and -not -name *.pb.cc`"
+    files="$files `find ${CLANG_FORMAT_ROOT}/$dir -name $glob -and -not -name *.generated.* -and -not -name *.pb.h -and -not -name *.pb.c -and -not -name *.pb.cc`"
   done
 done
 
@@ -54,7 +54,7 @@ if [ -n "$CHANGED_FILES" ]; then
   files=$(comm -12 <(echo $files | tr ' ' '\n' | sort -u) <(echo $CHANGED_FILES | tr ' ' '\n' | sort -u))
 fi
 
-if [ "x$TEST" = "x" ]
+if [ "$TEST" == "" ]
 then
   echo $files | xargs $CLANG_FORMAT -i
 else
diff --git a/tools/dockerfile/push_testing_images.sh b/tools/dockerfile/push_testing_images.sh
new file mode 100755
index 0000000000000000000000000000000000000000..f1ee8d59dd7e2db4651df7748c079fea96f26bae
--- /dev/null
+++ b/tools/dockerfile/push_testing_images.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Builds selected testing docker images and pushes them to dockerhub.
+# Useful for testing environments where it's impractical (or impossible)
+# to rely on docker images being cached locally after they've been built
+# for the first time (which might be costly especially for some images).
+# NOTE: gRPC docker images intended to be used by end users are NOT
+# pushed using this script (they're built automatically by dockerhub).
+# This script is only for "internal" images we use when testing gRPC.  
+
+set -ex
+
+cd $(dirname $0)/../..
+git_root=$(pwd)
+cd -
+
+DOCKERHUB_ORGANIZATION=grpctesting
+
+for DOCKERFILE_DIR in tools/dockerfile/test/fuzzer
+do
+  # Generate image name based on Dockerfile checksum. That works well as long
+  # as can count on dockerfiles being written in a way that changing the logical 
+  # contents of the docker image always changes the SHA (e.g. using "ADD file" 
+  # cmd in the dockerfile in not ok as contents of the added file will not be
+  # reflected in the SHA).
+  DOCKER_IMAGE_NAME=$(basename $DOCKERFILE_DIR)_$(sha1sum $DOCKERFILE_DIR/Dockerfile | cut -f1 -d\ )
+
+  # skip the image if it already exists in the repo 
+  curl --silent -f -lSL https://registry.hub.docker.com/v2/repositories/${DOCKERHUB_ORGANIZATION}/${DOCKER_IMAGE_NAME}/tags/latest > /dev/null \
+      && continue
+
+  docker build -t ${DOCKERHUB_ORGANIZATION}/${DOCKER_IMAGE_NAME} ${DOCKERFILE_DIR}
+      
+  # "docker login" needs to be run in advance
+  docker push ${DOCKERHUB_ORGANIZATION}/${DOCKER_IMAGE_NAME}
+done
diff --git a/tools/run_tests/prepare_travis.sh b/tools/dockerfile/test/bazel/Dockerfile
old mode 100755
new mode 100644
similarity index 61%
rename from tools/run_tests/prepare_travis.sh
rename to tools/dockerfile/test/bazel/Dockerfile
index 10546535e8ea10387f5545a309a58424b027dd48..cc41384833796c76186f054d842cbea4f02a2d9a
--- a/tools/run_tests/prepare_travis.sh
+++ b/tools/dockerfile/test/bazel/Dockerfile
@@ -1,4 +1,3 @@
-#!/bin/bash
 # Copyright 2015, Google Inc.
 # All rights reserved.
 #
@@ -28,40 +27,52 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-cd `dirname $0`/../..
-grpc_dir=`pwd`
+FROM ubuntu:15.10
 
-distrib=`md5sum /etc/issue | cut -f1 -d\ `
-echo "Configuring for distribution $distrib"
-git submodule | while read sha path extra ; do
-  cd /tmp
-  name=`basename $path`
-  file=$name-$sha-$CONFIG-prebuilt-$distrib.tar.gz
-  echo -n "Looking for $file ..."
-  url=http://storage.googleapis.com/grpc-prebuilt-packages/$file
-  wget -q $url && (
-    echo " Found."
-    tar xfz $file
-  ) || echo " Not found."
-done
+# Install Git and basic packages.
+RUN apt-get update && apt-get install -y \
+  autoconf \
+  autotools-dev \
+  build-essential \
+  bzip2 \
+  ccache \
+  curl \
+  gcc \
+  gcc-multilib \
+  git \
+  golang \
+  gyp \
+  lcov \
+  libc6 \
+  libc6-dbg \
+  libc6-dev \
+  libgtest-dev \
+  libtool \
+  make \
+  perl \
+  strace \
+  python-dev \
+  python-setuptools \
+  python-yaml \
+  telnet \
+  unzip \
+  wget \
+  zip && apt-get clean
 
-mkdir -p bins/$CONFIG/protobuf
-mkdir -p libs/$CONFIG/protobuf
-mkdir -p libs/$CONFIG/openssl
+#================
+# Build profiling
+RUN apt-get update && apt-get install -y time && apt-get clean
 
-function cpt {
-  cp /tmp/prebuilt/$1 $2/$CONFIG/$3
-  touch $2/$CONFIG/$3/`basename $1`
-}
 
-if [ -e /tmp/prebuilt/bin/protoc ] ; then
-  touch third_party/protobuf/configure
-  cpt bin/protoc bins protobuf
-  cpt lib/libprotoc.a libs protobuf
-  cpt lib/libprotobuf.a libs protobuf
-fi
+#========================
+# Bazel installation
+RUN apt-get install -y software-properties-common g++
+RUN echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" > /etc/apt/sources.list.d/bazel.list
+RUN curl https://bazel.build/bazel-release.pub.gpg | apt-key add -
+RUN apt-get -y update
+RUN apt-get -y install bazel
 
-if [ -e /tmp/prebuilt/lib/libssl.a ] ; then
-  cpt lib/libcrypto.a libs openssl
-  cpt lib/libssl.a libs openssl
-fi
+RUN mkdir -p /var/local/jenkins
+
+# Define the default command.
+CMD ["bash"]
diff --git a/tools/dockerfile/test/node_jessie_x64/Dockerfile b/tools/dockerfile/test/node_jessie_x64/Dockerfile
index d9a75018295323351de516df0149f8f838e50013..7f93933ecacf7562847dab5688c97e98b27d6ff3 100644
--- a/tools/dockerfile/test/node_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/node_jessie_x64/Dockerfile
@@ -63,6 +63,17 @@ RUN apt-get update && apt-get install -y \
 # Build profiling
 RUN apt-get update && apt-get install -y time && apt-get clean
 
+
+# Install Electron apt dependencies
+RUN apt-get update && apt-get install -y \
+  libasound2 \
+  libgconf-2-4 \
+  libgtk2.0-0 \
+  libnss3 \
+  libxss1 \
+  libxtst6 \
+  xvfb
+
 #====================
 # Python dependencies
 
diff --git a/tools/dockerfile/test/sanity/Dockerfile b/tools/dockerfile/test/sanity/Dockerfile
index f4b4831a649ee512460088b63657db06bd39f74d..811384fda14f996fae5606e83af4cf852e6064d2 100644
--- a/tools/dockerfile/test/sanity/Dockerfile
+++ b/tools/dockerfile/test/sanity/Dockerfile
@@ -93,19 +93,31 @@ RUN pip install simplejson mako
 #======================================
 # More sanity test dependencies (bazel)
 RUN apt-get install -y openjdk-8-jdk
-# TOOD(jtattermusch): pin the bazel version
-RUN git clone https://github.com/bazelbuild/bazel.git /bazel
-RUN cd /bazel && ./compile.sh
+# Check out Bazel version 0.4.1 since this version allows running
+# ./compile.sh without a local protoc dependency
+# TODO(mattkwong): install dependencies to support latest Bazel version if newer
+# version is needed
+RUN git clone https://github.com/bazelbuild/bazel.git /bazel && \
+  cd /bazel && git checkout tags/0.4.1 && ./compile.sh
 RUN ln -s /bazel/output/bazel /bin/
 
-#===================
-# Docker "inception"
-# Note this is quite the ugly hack.
-# This makes sure that the docker binary we inject has its dependencies.
-RUN curl https://get.docker.com/ | sh
-RUN apt-get remove --purge -y docker-engine
+RUN apt-get update && apt-get -y install wget
+RUN echo deb http://llvm.org/apt/wily/ llvm-toolchain-wily-3.8 main >> /etc/apt/sources.list
+RUN echo deb-src http://llvm.org/apt/wily/ llvm-toolchain-wily-3.8 main >> /etc/apt/sources.list
+RUN wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key| apt-key add -
+RUN apt-get update && apt-get -y install clang-format-3.8
+
+# Prepare ccache
+RUN ln -s /usr/bin/ccache /usr/local/bin/gcc
+RUN ln -s /usr/bin/ccache /usr/local/bin/g++
+RUN ln -s /usr/bin/ccache /usr/local/bin/cc
+RUN ln -s /usr/bin/ccache /usr/local/bin/c++
+RUN ln -s /usr/bin/ccache /usr/local/bin/clang
+RUN ln -s /usr/bin/ccache /usr/local/bin/clang++
+
 
 RUN mkdir /var/local/jenkins
 
+
 # Define the default command.
 CMD ["bash"]
diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++
index ff3a0e381daafd113f1b600a5d8256d3320f9a98..fa9b7057c576c4d0a82fb20e85a726d12228a378 100644
--- a/tools/doxygen/Doxyfile.c++
+++ b/tools/doxygen/Doxyfile.c++
@@ -760,7 +760,35 @@ WARN_LOGFILE           =
 # spaces.
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = include/grpc++/alarm.h \
+INPUT                  = doc/PROTOCOL-HTTP2.md \
+doc/PROTOCOL-WEB.md \
+doc/binary-logging.md \
+doc/c-style-guide.md \
+doc/command_line_tool.md \
+doc/compression.md \
+doc/compression_cookbook.md \
+doc/connection-backoff-interop-test-description.md \
+doc/connection-backoff.md \
+doc/connectivity-semantics-and-api.md \
+doc/cpp-style-guide.md \
+doc/cpp/pending_api_cleanups.md \
+doc/cpp/perf_notes.md \
+doc/environment_variables.md \
+doc/epoll-polling-engine.md \
+doc/fail_fast.md \
+doc/g_stands_for.md \
+doc/health-checking.md \
+doc/http-grpc-status-mapping.md \
+doc/interop-test-descriptions.md \
+doc/load-balancing.md \
+doc/naming.md \
+doc/negative-http2-interop-test-descriptions.md \
+doc/server-reflection.md \
+doc/server_reflection_tutorial.md \
+doc/statuscodes.md \
+doc/stress_test_framework.md \
+doc/wait-for-ready.md \
+include/grpc++/alarm.h \
 include/grpc++/channel.h \
 include/grpc++/client_context.h \
 include/grpc++/completion_queue.h \
@@ -771,7 +799,35 @@ include/grpc++/generic/generic_stub.h \
 include/grpc++/grpc++.h \
 include/grpc++/impl/call.h \
 include/grpc++/impl/client_unary_call.h \
+include/grpc++/impl/codegen/async_stream.h \
+include/grpc++/impl/codegen/async_unary_call.h \
+include/grpc++/impl/codegen/call.h \
+include/grpc++/impl/codegen/call_hook.h \
+include/grpc++/impl/codegen/channel_interface.h \
+include/grpc++/impl/codegen/client_context.h \
+include/grpc++/impl/codegen/client_unary_call.h \
+include/grpc++/impl/codegen/completion_queue.h \
+include/grpc++/impl/codegen/completion_queue_tag.h \
+include/grpc++/impl/codegen/config.h \
 include/grpc++/impl/codegen/core_codegen.h \
+include/grpc++/impl/codegen/core_codegen_interface.h \
+include/grpc++/impl/codegen/create_auth_context.h \
+include/grpc++/impl/codegen/grpc_library.h \
+include/grpc++/impl/codegen/method_handler_impl.h \
+include/grpc++/impl/codegen/rpc_method.h \
+include/grpc++/impl/codegen/rpc_service_method.h \
+include/grpc++/impl/codegen/security/auth_context.h \
+include/grpc++/impl/codegen/serialization_traits.h \
+include/grpc++/impl/codegen/server_context.h \
+include/grpc++/impl/codegen/server_interface.h \
+include/grpc++/impl/codegen/service_type.h \
+include/grpc++/impl/codegen/status.h \
+include/grpc++/impl/codegen/status_code_enum.h \
+include/grpc++/impl/codegen/status_helper.h \
+include/grpc++/impl/codegen/string_ref.h \
+include/grpc++/impl/codegen/stub_options.h \
+include/grpc++/impl/codegen/sync_stream.h \
+include/grpc++/impl/codegen/time.h \
 include/grpc++/impl/grpc_library.h \
 include/grpc++/impl/method_handler_impl.h \
 include/grpc++/impl/rpc_method.h \
@@ -802,47 +858,20 @@ include/grpc++/support/string_ref.h \
 include/grpc++/support/stub_options.h \
 include/grpc++/support/sync_stream.h \
 include/grpc++/support/time.h \
-include/grpc++/impl/codegen/async_stream.h \
-include/grpc++/impl/codegen/async_unary_call.h \
-include/grpc++/impl/codegen/call.h \
-include/grpc++/impl/codegen/call_hook.h \
-include/grpc++/impl/codegen/channel_interface.h \
-include/grpc++/impl/codegen/client_context.h \
-include/grpc++/impl/codegen/client_unary_call.h \
-include/grpc++/impl/codegen/completion_queue.h \
-include/grpc++/impl/codegen/completion_queue_tag.h \
-include/grpc++/impl/codegen/config.h \
-include/grpc++/impl/codegen/core_codegen_interface.h \
-include/grpc++/impl/codegen/create_auth_context.h \
-include/grpc++/impl/codegen/grpc_library.h \
-include/grpc++/impl/codegen/method_handler_impl.h \
-include/grpc++/impl/codegen/rpc_method.h \
-include/grpc++/impl/codegen/rpc_service_method.h \
-include/grpc++/impl/codegen/security/auth_context.h \
-include/grpc++/impl/codegen/serialization_traits.h \
-include/grpc++/impl/codegen/server_context.h \
-include/grpc++/impl/codegen/server_interface.h \
-include/grpc++/impl/codegen/service_type.h \
-include/grpc++/impl/codegen/status.h \
-include/grpc++/impl/codegen/status_code_enum.h \
-include/grpc++/impl/codegen/status_helper.h \
-include/grpc++/impl/codegen/string_ref.h \
-include/grpc++/impl/codegen/stub_options.h \
-include/grpc++/impl/codegen/sync_stream.h \
-include/grpc++/impl/codegen/time.h \
-include/grpc/impl/codegen/byte_buffer_reader.h \
-include/grpc/impl/codegen/compression_types.h \
-include/grpc/impl/codegen/connectivity_state.h \
-include/grpc/impl/codegen/grpc_types.h \
-include/grpc/impl/codegen/propagation_bits.h \
-include/grpc/impl/codegen/status.h \
 include/grpc/impl/codegen/atm.h \
 include/grpc/impl/codegen/atm_gcc_atomic.h \
 include/grpc/impl/codegen/atm_gcc_sync.h \
 include/grpc/impl/codegen/atm_windows.h \
+include/grpc/impl/codegen/byte_buffer_reader.h \
+include/grpc/impl/codegen/compression_types.h \
+include/grpc/impl/codegen/connectivity_state.h \
+include/grpc/impl/codegen/exec_ctx_fwd.h \
 include/grpc/impl/codegen/gpr_types.h \
+include/grpc/impl/codegen/grpc_types.h \
 include/grpc/impl/codegen/port_platform.h \
+include/grpc/impl/codegen/propagation_bits.h \
 include/grpc/impl/codegen/slice.h \
+include/grpc/impl/codegen/status.h \
 include/grpc/impl/codegen/sync.h \
 include/grpc/impl/codegen/sync_generic.h \
 include/grpc/impl/codegen/sync_posix.h \
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index 04e8f4e7f20d36abacfc6c0ebf016ca604207530..bca5652a463fcee4d18fc11e01a149d3f6a85ccc 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -760,7 +760,35 @@ WARN_LOGFILE           =
 # spaces.
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = include/grpc++/alarm.h \
+INPUT                  = doc/PROTOCOL-HTTP2.md \
+doc/PROTOCOL-WEB.md \
+doc/binary-logging.md \
+doc/c-style-guide.md \
+doc/command_line_tool.md \
+doc/compression.md \
+doc/compression_cookbook.md \
+doc/connection-backoff-interop-test-description.md \
+doc/connection-backoff.md \
+doc/connectivity-semantics-and-api.md \
+doc/cpp-style-guide.md \
+doc/cpp/pending_api_cleanups.md \
+doc/cpp/perf_notes.md \
+doc/environment_variables.md \
+doc/epoll-polling-engine.md \
+doc/fail_fast.md \
+doc/g_stands_for.md \
+doc/health-checking.md \
+doc/http-grpc-status-mapping.md \
+doc/interop-test-descriptions.md \
+doc/load-balancing.md \
+doc/naming.md \
+doc/negative-http2-interop-test-descriptions.md \
+doc/server-reflection.md \
+doc/server_reflection_tutorial.md \
+doc/statuscodes.md \
+doc/stress_test_framework.md \
+doc/wait-for-ready.md \
+include/grpc++/alarm.h \
 include/grpc++/channel.h \
 include/grpc++/client_context.h \
 include/grpc++/completion_queue.h \
@@ -771,7 +799,36 @@ include/grpc++/generic/generic_stub.h \
 include/grpc++/grpc++.h \
 include/grpc++/impl/call.h \
 include/grpc++/impl/client_unary_call.h \
+include/grpc++/impl/codegen/async_stream.h \
+include/grpc++/impl/codegen/async_unary_call.h \
+include/grpc++/impl/codegen/call.h \
+include/grpc++/impl/codegen/call_hook.h \
+include/grpc++/impl/codegen/channel_interface.h \
+include/grpc++/impl/codegen/client_context.h \
+include/grpc++/impl/codegen/client_unary_call.h \
+include/grpc++/impl/codegen/completion_queue.h \
+include/grpc++/impl/codegen/completion_queue_tag.h \
+include/grpc++/impl/codegen/config.h \
 include/grpc++/impl/codegen/core_codegen.h \
+include/grpc++/impl/codegen/core_codegen.h \
+include/grpc++/impl/codegen/core_codegen_interface.h \
+include/grpc++/impl/codegen/create_auth_context.h \
+include/grpc++/impl/codegen/grpc_library.h \
+include/grpc++/impl/codegen/method_handler_impl.h \
+include/grpc++/impl/codegen/rpc_method.h \
+include/grpc++/impl/codegen/rpc_service_method.h \
+include/grpc++/impl/codegen/security/auth_context.h \
+include/grpc++/impl/codegen/serialization_traits.h \
+include/grpc++/impl/codegen/server_context.h \
+include/grpc++/impl/codegen/server_interface.h \
+include/grpc++/impl/codegen/service_type.h \
+include/grpc++/impl/codegen/status.h \
+include/grpc++/impl/codegen/status_code_enum.h \
+include/grpc++/impl/codegen/status_helper.h \
+include/grpc++/impl/codegen/string_ref.h \
+include/grpc++/impl/codegen/stub_options.h \
+include/grpc++/impl/codegen/sync_stream.h \
+include/grpc++/impl/codegen/time.h \
 include/grpc++/impl/grpc_library.h \
 include/grpc++/impl/method_handler_impl.h \
 include/grpc++/impl/rpc_method.h \
@@ -802,97 +859,70 @@ include/grpc++/support/string_ref.h \
 include/grpc++/support/stub_options.h \
 include/grpc++/support/sync_stream.h \
 include/grpc++/support/time.h \
-include/grpc++/impl/codegen/async_stream.h \
-include/grpc++/impl/codegen/async_unary_call.h \
-include/grpc++/impl/codegen/call.h \
-include/grpc++/impl/codegen/call_hook.h \
-include/grpc++/impl/codegen/channel_interface.h \
-include/grpc++/impl/codegen/client_context.h \
-include/grpc++/impl/codegen/client_unary_call.h \
-include/grpc++/impl/codegen/completion_queue.h \
-include/grpc++/impl/codegen/completion_queue_tag.h \
-include/grpc++/impl/codegen/config.h \
-include/grpc++/impl/codegen/core_codegen_interface.h \
-include/grpc++/impl/codegen/create_auth_context.h \
-include/grpc++/impl/codegen/grpc_library.h \
-include/grpc++/impl/codegen/method_handler_impl.h \
-include/grpc++/impl/codegen/rpc_method.h \
-include/grpc++/impl/codegen/rpc_service_method.h \
-include/grpc++/impl/codegen/security/auth_context.h \
-include/grpc++/impl/codegen/serialization_traits.h \
-include/grpc++/impl/codegen/server_context.h \
-include/grpc++/impl/codegen/server_interface.h \
-include/grpc++/impl/codegen/service_type.h \
-include/grpc++/impl/codegen/status.h \
-include/grpc++/impl/codegen/status_code_enum.h \
-include/grpc++/impl/codegen/status_helper.h \
-include/grpc++/impl/codegen/string_ref.h \
-include/grpc++/impl/codegen/stub_options.h \
-include/grpc++/impl/codegen/sync_stream.h \
-include/grpc++/impl/codegen/time.h \
-include/grpc/impl/codegen/byte_buffer_reader.h \
-include/grpc/impl/codegen/compression_types.h \
-include/grpc/impl/codegen/connectivity_state.h \
-include/grpc/impl/codegen/grpc_types.h \
-include/grpc/impl/codegen/propagation_bits.h \
-include/grpc/impl/codegen/status.h \
 include/grpc/impl/codegen/atm.h \
 include/grpc/impl/codegen/atm_gcc_atomic.h \
 include/grpc/impl/codegen/atm_gcc_sync.h \
 include/grpc/impl/codegen/atm_windows.h \
+include/grpc/impl/codegen/byte_buffer_reader.h \
+include/grpc/impl/codegen/compression_types.h \
+include/grpc/impl/codegen/connectivity_state.h \
+include/grpc/impl/codegen/exec_ctx_fwd.h \
 include/grpc/impl/codegen/gpr_types.h \
+include/grpc/impl/codegen/grpc_types.h \
 include/grpc/impl/codegen/port_platform.h \
+include/grpc/impl/codegen/propagation_bits.h \
 include/grpc/impl/codegen/slice.h \
+include/grpc/impl/codegen/status.h \
 include/grpc/impl/codegen/sync.h \
 include/grpc/impl/codegen/sync_generic.h \
 include/grpc/impl/codegen/sync_posix.h \
 include/grpc/impl/codegen/sync_windows.h \
-include/grpc++/impl/codegen/core_codegen.h \
-src/cpp/client/secure_credentials.h \
-src/cpp/common/secure_auth_context.h \
-src/cpp/server/secure_server_credentials.h \
-src/cpp/client/create_channel_internal.h \
-src/cpp/common/channel_filter.h \
-src/cpp/server/dynamic_thread_pool.h \
-src/cpp/server/thread_pool_interface.h \
-src/cpp/thread_manager/thread_manager.h \
-src/cpp/client/insecure_credentials.cc \
-src/cpp/client/secure_credentials.cc \
-src/cpp/common/auth_property_iterator.cc \
-src/cpp/common/secure_auth_context.cc \
-src/cpp/common/secure_channel_arguments.cc \
-src/cpp/common/secure_create_auth_context.cc \
-src/cpp/server/insecure_server_credentials.cc \
-src/cpp/server/secure_server_credentials.cc \
+src/cpp/README.md \
 src/cpp/client/channel_cc.cc \
 src/cpp/client/client_context.cc \
 src/cpp/client/create_channel.cc \
 src/cpp/client/create_channel_internal.cc \
+src/cpp/client/create_channel_internal.h \
 src/cpp/client/create_channel_posix.cc \
 src/cpp/client/credentials_cc.cc \
 src/cpp/client/generic_stub.cc \
+src/cpp/client/insecure_credentials.cc \
+src/cpp/client/secure_credentials.cc \
+src/cpp/client/secure_credentials.h \
+src/cpp/codegen/codegen_init.cc \
+src/cpp/common/auth_property_iterator.cc \
 src/cpp/common/channel_arguments.cc \
 src/cpp/common/channel_filter.cc \
+src/cpp/common/channel_filter.h \
 src/cpp/common/completion_queue_cc.cc \
 src/cpp/common/core_codegen.cc \
 src/cpp/common/resource_quota_cc.cc \
 src/cpp/common/rpc_method.cc \
+src/cpp/common/secure_auth_context.cc \
+src/cpp/common/secure_auth_context.h \
+src/cpp/common/secure_channel_arguments.cc \
+src/cpp/common/secure_create_auth_context.cc \
 src/cpp/common/version_cc.cc \
 src/cpp/server/async_generic_service.cc \
 src/cpp/server/create_default_thread_pool.cc \
 src/cpp/server/dynamic_thread_pool.cc \
+src/cpp/server/dynamic_thread_pool.h \
+src/cpp/server/insecure_server_credentials.cc \
+src/cpp/server/secure_server_credentials.cc \
+src/cpp/server/secure_server_credentials.h \
 src/cpp/server/server_builder.cc \
 src/cpp/server/server_cc.cc \
 src/cpp/server/server_context.cc \
 src/cpp/server/server_credentials.cc \
 src/cpp/server/server_posix.cc \
+src/cpp/server/thread_pool_interface.h \
 src/cpp/thread_manager/thread_manager.cc \
+src/cpp/thread_manager/thread_manager.h \
 src/cpp/util/byte_buffer_cc.cc \
 src/cpp/util/slice_cc.cc \
 src/cpp/util/status.cc \
 src/cpp/util/string_ref.cc \
-src/cpp/util/time_cc.cc \
-src/cpp/codegen/codegen_init.cc
+src/cpp/util/time_cc.cc
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/tools/doxygen/Doxyfile.core b/tools/doxygen/Doxyfile.core
index 1e748ba4a8969d550cb59cf76b5bf0171e97fec1..ccbfe3a4e66ccfffcacf1062f24989e1016e4574 100644
--- a/tools/doxygen/Doxyfile.core
+++ b/tools/doxygen/Doxyfile.core
@@ -760,34 +760,73 @@ WARN_LOGFILE           =
 # spaces.
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = include/grpc/byte_buffer.h \
+INPUT                  = doc/PROTOCOL-HTTP2.md \
+doc/PROTOCOL-WEB.md \
+doc/binary-logging.md \
+doc/c-style-guide.md \
+doc/command_line_tool.md \
+doc/compression.md \
+doc/compression_cookbook.md \
+doc/connection-backoff-interop-test-description.md \
+doc/connection-backoff.md \
+doc/connectivity-semantics-and-api.md \
+doc/core/pending_api_cleanups.md \
+doc/cpp-style-guide.md \
+doc/environment_variables.md \
+doc/epoll-polling-engine.md \
+doc/fail_fast.md \
+doc/g_stands_for.md \
+doc/health-checking.md \
+doc/http-grpc-status-mapping.md \
+doc/interop-test-descriptions.md \
+doc/load-balancing.md \
+doc/naming.md \
+doc/negative-http2-interop-test-descriptions.md \
+doc/server-reflection.md \
+doc/server_reflection_tutorial.md \
+doc/statuscodes.md \
+doc/stress_test_framework.md \
+doc/wait-for-ready.md \
+include/grpc/byte_buffer.h \
 include/grpc/byte_buffer_reader.h \
+include/grpc/census.h \
 include/grpc/compression.h \
 include/grpc/grpc.h \
 include/grpc/grpc_posix.h \
+include/grpc/grpc_security.h \
 include/grpc/grpc_security_constants.h \
-include/grpc/slice.h \
-include/grpc/slice_buffer.h \
-include/grpc/status.h \
-include/grpc/impl/codegen/byte_buffer_reader.h \
-include/grpc/impl/codegen/compression_types.h \
-include/grpc/impl/codegen/connectivity_state.h \
-include/grpc/impl/codegen/grpc_types.h \
-include/grpc/impl/codegen/propagation_bits.h \
-include/grpc/impl/codegen/status.h \
 include/grpc/impl/codegen/atm.h \
+include/grpc/impl/codegen/atm.h \
+include/grpc/impl/codegen/atm_gcc_atomic.h \
 include/grpc/impl/codegen/atm_gcc_atomic.h \
 include/grpc/impl/codegen/atm_gcc_sync.h \
+include/grpc/impl/codegen/atm_gcc_sync.h \
 include/grpc/impl/codegen/atm_windows.h \
+include/grpc/impl/codegen/atm_windows.h \
+include/grpc/impl/codegen/byte_buffer_reader.h \
+include/grpc/impl/codegen/compression_types.h \
+include/grpc/impl/codegen/connectivity_state.h \
+include/grpc/impl/codegen/exec_ctx_fwd.h \
+include/grpc/impl/codegen/gpr_types.h \
 include/grpc/impl/codegen/gpr_types.h \
+include/grpc/impl/codegen/grpc_types.h \
 include/grpc/impl/codegen/port_platform.h \
+include/grpc/impl/codegen/port_platform.h \
+include/grpc/impl/codegen/propagation_bits.h \
 include/grpc/impl/codegen/slice.h \
+include/grpc/impl/codegen/slice.h \
+include/grpc/impl/codegen/status.h \
+include/grpc/impl/codegen/sync.h \
 include/grpc/impl/codegen/sync.h \
 include/grpc/impl/codegen/sync_generic.h \
+include/grpc/impl/codegen/sync_generic.h \
+include/grpc/impl/codegen/sync_posix.h \
 include/grpc/impl/codegen/sync_posix.h \
 include/grpc/impl/codegen/sync_windows.h \
-include/grpc/grpc_security.h \
-include/grpc/census.h \
+include/grpc/impl/codegen/sync_windows.h \
+include/grpc/slice.h \
+include/grpc/slice_buffer.h \
+include/grpc/status.h \
 include/grpc/support/alloc.h \
 include/grpc/support/atm.h \
 include/grpc/support/atm_gcc_atomic.h \
@@ -813,18 +852,7 @@ include/grpc/support/tls.h \
 include/grpc/support/tls_gcc.h \
 include/grpc/support/tls_msvc.h \
 include/grpc/support/tls_pthread.h \
-include/grpc/support/useful.h \
-include/grpc/impl/codegen/atm.h \
-include/grpc/impl/codegen/atm_gcc_atomic.h \
-include/grpc/impl/codegen/atm_gcc_sync.h \
-include/grpc/impl/codegen/atm_windows.h \
-include/grpc/impl/codegen/gpr_types.h \
-include/grpc/impl/codegen/port_platform.h \
-include/grpc/impl/codegen/slice.h \
-include/grpc/impl/codegen/sync.h \
-include/grpc/impl/codegen/sync_generic.h \
-include/grpc/impl/codegen/sync_posix.h \
-include/grpc/impl/codegen/sync_windows.h
+include/grpc/support/useful.h
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index a629bdc542123c7da3b93e4f17287f0f2511d1bf..fc8fac32f0dc2b7338959705d6de3eb71808f59a 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -760,483 +760,461 @@ WARN_LOGFILE           =
 # spaces.
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = include/grpc/byte_buffer.h \
+INPUT                  = doc/PROTOCOL-HTTP2.md \
+doc/PROTOCOL-WEB.md \
+doc/binary-logging.md \
+doc/c-style-guide.md \
+doc/command_line_tool.md \
+doc/compression.md \
+doc/compression_cookbook.md \
+doc/connection-backoff-interop-test-description.md \
+doc/connection-backoff.md \
+doc/connectivity-semantics-and-api.md \
+doc/core/pending_api_cleanups.md \
+doc/cpp-style-guide.md \
+doc/environment_variables.md \
+doc/epoll-polling-engine.md \
+doc/fail_fast.md \
+doc/g_stands_for.md \
+doc/health-checking.md \
+doc/http-grpc-status-mapping.md \
+doc/interop-test-descriptions.md \
+doc/load-balancing.md \
+doc/naming.md \
+doc/negative-http2-interop-test-descriptions.md \
+doc/server-reflection.md \
+doc/server_reflection_tutorial.md \
+doc/statuscodes.md \
+doc/stress_test_framework.md \
+doc/wait-for-ready.md \
+include/grpc/byte_buffer.h \
 include/grpc/byte_buffer_reader.h \
+include/grpc/census.h \
 include/grpc/compression.h \
 include/grpc/grpc.h \
 include/grpc/grpc_posix.h \
+include/grpc/grpc_security.h \
 include/grpc/grpc_security_constants.h \
-include/grpc/slice.h \
-include/grpc/slice_buffer.h \
-include/grpc/status.h \
-include/grpc/impl/codegen/byte_buffer_reader.h \
-include/grpc/impl/codegen/compression_types.h \
-include/grpc/impl/codegen/connectivity_state.h \
-include/grpc/impl/codegen/grpc_types.h \
-include/grpc/impl/codegen/propagation_bits.h \
-include/grpc/impl/codegen/status.h \
+include/grpc/impl/codegen/atm.h \
 include/grpc/impl/codegen/atm.h \
 include/grpc/impl/codegen/atm_gcc_atomic.h \
+include/grpc/impl/codegen/atm_gcc_atomic.h \
+include/grpc/impl/codegen/atm_gcc_sync.h \
 include/grpc/impl/codegen/atm_gcc_sync.h \
 include/grpc/impl/codegen/atm_windows.h \
+include/grpc/impl/codegen/atm_windows.h \
+include/grpc/impl/codegen/byte_buffer_reader.h \
+include/grpc/impl/codegen/compression_types.h \
+include/grpc/impl/codegen/connectivity_state.h \
+include/grpc/impl/codegen/exec_ctx_fwd.h \
 include/grpc/impl/codegen/gpr_types.h \
+include/grpc/impl/codegen/gpr_types.h \
+include/grpc/impl/codegen/grpc_types.h \
 include/grpc/impl/codegen/port_platform.h \
+include/grpc/impl/codegen/port_platform.h \
+include/grpc/impl/codegen/propagation_bits.h \
 include/grpc/impl/codegen/slice.h \
+include/grpc/impl/codegen/slice.h \
+include/grpc/impl/codegen/status.h \
 include/grpc/impl/codegen/sync.h \
+include/grpc/impl/codegen/sync.h \
+include/grpc/impl/codegen/sync_generic.h \
 include/grpc/impl/codegen/sync_generic.h \
 include/grpc/impl/codegen/sync_posix.h \
+include/grpc/impl/codegen/sync_posix.h \
 include/grpc/impl/codegen/sync_windows.h \
-include/grpc/grpc_security.h \
-include/grpc/census.h \
+include/grpc/impl/codegen/sync_windows.h \
+include/grpc/slice.h \
+include/grpc/slice_buffer.h \
+include/grpc/status.h \
+include/grpc/support/alloc.h \
+include/grpc/support/atm.h \
+include/grpc/support/atm_gcc_atomic.h \
+include/grpc/support/atm_gcc_sync.h \
+include/grpc/support/atm_windows.h \
+include/grpc/support/avl.h \
+include/grpc/support/cmdline.h \
+include/grpc/support/cpu.h \
+include/grpc/support/histogram.h \
+include/grpc/support/host_port.h \
+include/grpc/support/log.h \
+include/grpc/support/log_windows.h \
+include/grpc/support/port_platform.h \
+include/grpc/support/string_util.h \
+include/grpc/support/subprocess.h \
+include/grpc/support/sync.h \
+include/grpc/support/sync_generic.h \
+include/grpc/support/sync_posix.h \
+include/grpc/support/sync_windows.h \
+include/grpc/support/thd.h \
+include/grpc/support/time.h \
+include/grpc/support/tls.h \
+include/grpc/support/tls_gcc.h \
+include/grpc/support/tls_msvc.h \
+include/grpc/support/tls_pthread.h \
+include/grpc/support/useful.h \
+src/core/README.md \
+src/core/ext/README.md \
+src/core/ext/census/README.md \
+src/core/ext/census/aggregation.h \
+src/core/ext/census/base_resources.c \
+src/core/ext/census/base_resources.h \
+src/core/ext/census/census_interface.h \
+src/core/ext/census/census_rpc_stats.h \
+src/core/ext/census/context.c \
+src/core/ext/census/gen/README.md \
+src/core/ext/census/gen/census.pb.c \
+src/core/ext/census/gen/census.pb.h \
+src/core/ext/census/gen/trace_context.pb.c \
+src/core/ext/census/gen/trace_context.pb.h \
+src/core/ext/census/grpc_context.c \
+src/core/ext/census/grpc_filter.c \
+src/core/ext/census/grpc_filter.h \
+src/core/ext/census/grpc_plugin.c \
+src/core/ext/census/initialize.c \
+src/core/ext/census/mlog.c \
+src/core/ext/census/mlog.h \
+src/core/ext/census/operation.c \
+src/core/ext/census/placeholders.c \
+src/core/ext/census/resource.c \
+src/core/ext/census/resource.h \
+src/core/ext/census/rpc_metric_id.h \
+src/core/ext/census/trace_context.c \
+src/core/ext/census/trace_context.h \
+src/core/ext/census/tracing.c \
+src/core/ext/client_channel/README.md \
+src/core/ext/client_channel/channel_connectivity.c \
+src/core/ext/client_channel/client_channel.c \
+src/core/ext/client_channel/client_channel.h \
+src/core/ext/client_channel/client_channel_factory.c \
+src/core/ext/client_channel/client_channel_factory.h \
+src/core/ext/client_channel/client_channel_plugin.c \
+src/core/ext/client_channel/connector.c \
+src/core/ext/client_channel/connector.h \
+src/core/ext/client_channel/default_initial_connect_string.c \
+src/core/ext/client_channel/http_connect_handshaker.c \
+src/core/ext/client_channel/http_connect_handshaker.h \
+src/core/ext/client_channel/initial_connect_string.c \
+src/core/ext/client_channel/initial_connect_string.h \
+src/core/ext/client_channel/lb_policy.c \
+src/core/ext/client_channel/lb_policy.h \
+src/core/ext/client_channel/lb_policy_factory.c \
+src/core/ext/client_channel/lb_policy_factory.h \
+src/core/ext/client_channel/lb_policy_registry.c \
+src/core/ext/client_channel/lb_policy_registry.h \
+src/core/ext/client_channel/parse_address.c \
+src/core/ext/client_channel/parse_address.h \
+src/core/ext/client_channel/resolver.c \
+src/core/ext/client_channel/resolver.h \
+src/core/ext/client_channel/resolver_factory.c \
+src/core/ext/client_channel/resolver_factory.h \
+src/core/ext/client_channel/resolver_registry.c \
+src/core/ext/client_channel/resolver_registry.h \
+src/core/ext/client_channel/subchannel.c \
+src/core/ext/client_channel/subchannel.h \
+src/core/ext/client_channel/subchannel_index.c \
+src/core/ext/client_channel/subchannel_index.h \
+src/core/ext/client_channel/uri_parser.c \
+src/core/ext/client_channel/uri_parser.h \
+src/core/ext/lb_policy/grpclb/grpclb.c \
+src/core/ext/lb_policy/grpclb/grpclb.h \
+src/core/ext/lb_policy/grpclb/load_balancer_api.c \
+src/core/ext/lb_policy/grpclb/load_balancer_api.h \
+src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \
+src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h \
+src/core/ext/lb_policy/pick_first/pick_first.c \
+src/core/ext/lb_policy/round_robin/round_robin.c \
+src/core/ext/load_reporting/load_reporting.c \
+src/core/ext/load_reporting/load_reporting.h \
+src/core/ext/load_reporting/load_reporting_filter.c \
+src/core/ext/load_reporting/load_reporting_filter.h \
+src/core/ext/resolver/README.md \
+src/core/ext/resolver/dns/native/README.md \
+src/core/ext/resolver/dns/native/dns_resolver.c \
+src/core/ext/resolver/sockaddr/README.md \
+src/core/ext/resolver/sockaddr/sockaddr_resolver.c \
+src/core/ext/transport/README.md \
+src/core/ext/transport/chttp2/README.md \
+src/core/ext/transport/chttp2/alpn/alpn.c \
+src/core/ext/transport/chttp2/alpn/alpn.h \
+src/core/ext/transport/chttp2/client/chttp2_connector.c \
+src/core/ext/transport/chttp2/client/chttp2_connector.h \
+src/core/ext/transport/chttp2/client/insecure/README.md \
+src/core/ext/transport/chttp2/client/insecure/channel_create.c \
+src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c \
+src/core/ext/transport/chttp2/client/secure/README.md \
+src/core/ext/transport/chttp2/client/secure/secure_channel_create.c \
+src/core/ext/transport/chttp2/server/chttp2_server.c \
+src/core/ext/transport/chttp2/server/chttp2_server.h \
+src/core/ext/transport/chttp2/server/insecure/README.md \
+src/core/ext/transport/chttp2/server/insecure/server_chttp2.c \
+src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c \
+src/core/ext/transport/chttp2/server/secure/README.md \
+src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c \
+src/core/ext/transport/chttp2/transport/README.md \
+src/core/ext/transport/chttp2/transport/bin_decoder.c \
+src/core/ext/transport/chttp2/transport/bin_decoder.h \
+src/core/ext/transport/chttp2/transport/bin_encoder.c \
+src/core/ext/transport/chttp2/transport/bin_encoder.h \
+src/core/ext/transport/chttp2/transport/chttp2_plugin.c \
+src/core/ext/transport/chttp2/transport/chttp2_transport.c \
+src/core/ext/transport/chttp2/transport/chttp2_transport.h \
+src/core/ext/transport/chttp2/transport/frame.h \
+src/core/ext/transport/chttp2/transport/frame_data.c \
+src/core/ext/transport/chttp2/transport/frame_data.h \
+src/core/ext/transport/chttp2/transport/frame_goaway.c \
+src/core/ext/transport/chttp2/transport/frame_goaway.h \
+src/core/ext/transport/chttp2/transport/frame_ping.c \
+src/core/ext/transport/chttp2/transport/frame_ping.h \
+src/core/ext/transport/chttp2/transport/frame_rst_stream.c \
+src/core/ext/transport/chttp2/transport/frame_rst_stream.h \
+src/core/ext/transport/chttp2/transport/frame_settings.c \
+src/core/ext/transport/chttp2/transport/frame_settings.h \
+src/core/ext/transport/chttp2/transport/frame_window_update.c \
+src/core/ext/transport/chttp2/transport/frame_window_update.h \
+src/core/ext/transport/chttp2/transport/hpack_encoder.c \
+src/core/ext/transport/chttp2/transport/hpack_encoder.h \
+src/core/ext/transport/chttp2/transport/hpack_parser.c \
+src/core/ext/transport/chttp2/transport/hpack_parser.h \
+src/core/ext/transport/chttp2/transport/hpack_table.c \
+src/core/ext/transport/chttp2/transport/hpack_table.h \
+src/core/ext/transport/chttp2/transport/http2_errors.h \
+src/core/ext/transport/chttp2/transport/huffsyms.c \
+src/core/ext/transport/chttp2/transport/huffsyms.h \
+src/core/ext/transport/chttp2/transport/incoming_metadata.c \
+src/core/ext/transport/chttp2/transport/incoming_metadata.h \
+src/core/ext/transport/chttp2/transport/internal.h \
+src/core/ext/transport/chttp2/transport/parsing.c \
+src/core/ext/transport/chttp2/transport/status_conversion.c \
+src/core/ext/transport/chttp2/transport/status_conversion.h \
+src/core/ext/transport/chttp2/transport/stream_lists.c \
+src/core/ext/transport/chttp2/transport/stream_map.c \
+src/core/ext/transport/chttp2/transport/stream_map.h \
+src/core/ext/transport/chttp2/transport/varint.c \
+src/core/ext/transport/chttp2/transport/varint.h \
+src/core/ext/transport/chttp2/transport/writing.c \
+src/core/lib/README.md \
+src/core/lib/channel/README.md \
+src/core/lib/channel/channel_args.c \
 src/core/lib/channel/channel_args.h \
+src/core/lib/channel/channel_stack.c \
 src/core/lib/channel/channel_stack.h \
+src/core/lib/channel/channel_stack_builder.c \
 src/core/lib/channel/channel_stack_builder.h \
+src/core/lib/channel/compress_filter.c \
 src/core/lib/channel/compress_filter.h \
+src/core/lib/channel/connected_channel.c \
 src/core/lib/channel/connected_channel.h \
 src/core/lib/channel/context.h \
+src/core/lib/channel/deadline_filter.c \
 src/core/lib/channel/deadline_filter.h \
+src/core/lib/channel/handshaker.c \
 src/core/lib/channel/handshaker.h \
+src/core/lib/channel/handshaker_factory.c \
+src/core/lib/channel/handshaker_factory.h \
+src/core/lib/channel/handshaker_registry.c \
+src/core/lib/channel/handshaker_registry.h \
+src/core/lib/channel/http_client_filter.c \
 src/core/lib/channel/http_client_filter.h \
+src/core/lib/channel/http_server_filter.c \
 src/core/lib/channel/http_server_filter.h \
+src/core/lib/channel/message_size_filter.c \
 src/core/lib/channel/message_size_filter.h \
 src/core/lib/compression/algorithm_metadata.h \
+src/core/lib/compression/compression.c \
+src/core/lib/compression/message_compress.c \
 src/core/lib/compression/message_compress.h \
+src/core/lib/debug/trace.c \
 src/core/lib/debug/trace.h \
+src/core/lib/http/format_request.c \
 src/core/lib/http/format_request.h \
+src/core/lib/http/httpcli.c \
 src/core/lib/http/httpcli.h \
+src/core/lib/http/httpcli_security_connector.c \
+src/core/lib/http/parser.c \
 src/core/lib/http/parser.h \
+src/core/lib/iomgr/README.md \
+src/core/lib/iomgr/closure.c \
 src/core/lib/iomgr/closure.h \
+src/core/lib/iomgr/combiner.c \
 src/core/lib/iomgr/combiner.h \
+src/core/lib/iomgr/endpoint.c \
 src/core/lib/iomgr/endpoint.h \
 src/core/lib/iomgr/endpoint_pair.h \
+src/core/lib/iomgr/endpoint_pair_posix.c \
+src/core/lib/iomgr/endpoint_pair_uv.c \
+src/core/lib/iomgr/endpoint_pair_windows.c \
+src/core/lib/iomgr/error.c \
 src/core/lib/iomgr/error.h \
+src/core/lib/iomgr/ev_epoll_linux.c \
 src/core/lib/iomgr/ev_epoll_linux.h \
+src/core/lib/iomgr/ev_poll_posix.c \
 src/core/lib/iomgr/ev_poll_posix.h \
+src/core/lib/iomgr/ev_posix.c \
 src/core/lib/iomgr/ev_posix.h \
+src/core/lib/iomgr/exec_ctx.c \
 src/core/lib/iomgr/exec_ctx.h \
+src/core/lib/iomgr/executor.c \
 src/core/lib/iomgr/executor.h \
+src/core/lib/iomgr/iocp_windows.c \
 src/core/lib/iomgr/iocp_windows.h \
+src/core/lib/iomgr/iomgr.c \
 src/core/lib/iomgr/iomgr.h \
 src/core/lib/iomgr/iomgr_internal.h \
+src/core/lib/iomgr/iomgr_posix.c \
 src/core/lib/iomgr/iomgr_posix.h \
+src/core/lib/iomgr/iomgr_uv.c \
+src/core/lib/iomgr/iomgr_windows.c \
+src/core/lib/iomgr/load_file.c \
 src/core/lib/iomgr/load_file.h \
+src/core/lib/iomgr/network_status_tracker.c \
 src/core/lib/iomgr/network_status_tracker.h \
+src/core/lib/iomgr/polling_entity.c \
 src/core/lib/iomgr/polling_entity.h \
 src/core/lib/iomgr/pollset.h \
 src/core/lib/iomgr/pollset_set.h \
+src/core/lib/iomgr/pollset_set_uv.c \
+src/core/lib/iomgr/pollset_set_windows.c \
 src/core/lib/iomgr/pollset_set_windows.h \
+src/core/lib/iomgr/pollset_uv.c \
 src/core/lib/iomgr/pollset_uv.h \
+src/core/lib/iomgr/pollset_windows.c \
 src/core/lib/iomgr/pollset_windows.h \
 src/core/lib/iomgr/port.h \
 src/core/lib/iomgr/resolve_address.h \
+src/core/lib/iomgr/resolve_address_posix.c \
+src/core/lib/iomgr/resolve_address_uv.c \
+src/core/lib/iomgr/resolve_address_windows.c \
+src/core/lib/iomgr/resource_quota.c \
 src/core/lib/iomgr/resource_quota.h \
 src/core/lib/iomgr/sockaddr.h \
 src/core/lib/iomgr/sockaddr_posix.h \
+src/core/lib/iomgr/sockaddr_utils.c \
 src/core/lib/iomgr/sockaddr_utils.h \
 src/core/lib/iomgr/sockaddr_windows.h \
+src/core/lib/iomgr/socket_mutator.c \
 src/core/lib/iomgr/socket_mutator.h \
 src/core/lib/iomgr/socket_utils.h \
+src/core/lib/iomgr/socket_utils_common_posix.c \
+src/core/lib/iomgr/socket_utils_linux.c \
+src/core/lib/iomgr/socket_utils_posix.c \
 src/core/lib/iomgr/socket_utils_posix.h \
+src/core/lib/iomgr/socket_utils_uv.c \
+src/core/lib/iomgr/socket_utils_windows.c \
+src/core/lib/iomgr/socket_windows.c \
 src/core/lib/iomgr/socket_windows.h \
 src/core/lib/iomgr/tcp_client.h \
+src/core/lib/iomgr/tcp_client_posix.c \
 src/core/lib/iomgr/tcp_client_posix.h \
+src/core/lib/iomgr/tcp_client_uv.c \
+src/core/lib/iomgr/tcp_client_windows.c \
+src/core/lib/iomgr/tcp_posix.c \
 src/core/lib/iomgr/tcp_posix.h \
 src/core/lib/iomgr/tcp_server.h \
+src/core/lib/iomgr/tcp_server_posix.c \
+src/core/lib/iomgr/tcp_server_uv.c \
+src/core/lib/iomgr/tcp_server_windows.c \
+src/core/lib/iomgr/tcp_uv.c \
 src/core/lib/iomgr/tcp_uv.h \
+src/core/lib/iomgr/tcp_windows.c \
 src/core/lib/iomgr/tcp_windows.h \
+src/core/lib/iomgr/time_averaged_stats.c \
 src/core/lib/iomgr/time_averaged_stats.h \
 src/core/lib/iomgr/timer.h \
+src/core/lib/iomgr/timer_generic.c \
 src/core/lib/iomgr/timer_generic.h \
+src/core/lib/iomgr/timer_heap.c \
 src/core/lib/iomgr/timer_heap.h \
+src/core/lib/iomgr/timer_uv.c \
 src/core/lib/iomgr/timer_uv.h \
+src/core/lib/iomgr/udp_server.c \
 src/core/lib/iomgr/udp_server.h \
+src/core/lib/iomgr/unix_sockets_posix.c \
 src/core/lib/iomgr/unix_sockets_posix.h \
+src/core/lib/iomgr/unix_sockets_posix_noop.c \
+src/core/lib/iomgr/wakeup_fd_cv.c \
 src/core/lib/iomgr/wakeup_fd_cv.h \
+src/core/lib/iomgr/wakeup_fd_eventfd.c \
+src/core/lib/iomgr/wakeup_fd_nospecial.c \
+src/core/lib/iomgr/wakeup_fd_pipe.c \
 src/core/lib/iomgr/wakeup_fd_pipe.h \
+src/core/lib/iomgr/wakeup_fd_posix.c \
 src/core/lib/iomgr/wakeup_fd_posix.h \
 src/core/lib/iomgr/workqueue.h \
+src/core/lib/iomgr/workqueue_uv.c \
 src/core/lib/iomgr/workqueue_uv.h \
+src/core/lib/iomgr/workqueue_windows.c \
 src/core/lib/iomgr/workqueue_windows.h \
+src/core/lib/json/json.c \
 src/core/lib/json/json.h \
 src/core/lib/json/json_common.h \
-src/core/lib/json/json_reader.h \
-src/core/lib/json/json_writer.h \
-src/core/lib/slice/percent_encoding.h \
-src/core/lib/slice/slice_string_helpers.h \
-src/core/lib/surface/api_trace.h \
-src/core/lib/surface/call.h \
-src/core/lib/surface/call_test_only.h \
-src/core/lib/surface/channel.h \
-src/core/lib/surface/channel_init.h \
-src/core/lib/surface/channel_stack_type.h \
-src/core/lib/surface/completion_queue.h \
-src/core/lib/surface/event_string.h \
-src/core/lib/surface/init.h \
-src/core/lib/surface/lame_client.h \
-src/core/lib/surface/server.h \
-src/core/lib/transport/byte_stream.h \
-src/core/lib/transport/connectivity_state.h \
-src/core/lib/transport/mdstr_hash_table.h \
-src/core/lib/transport/metadata.h \
-src/core/lib/transport/metadata_batch.h \
-src/core/lib/transport/pid_controller.h \
-src/core/lib/transport/service_config.h \
-src/core/lib/transport/static_metadata.h \
-src/core/lib/transport/timeout_encoding.h \
-src/core/lib/transport/transport.h \
-src/core/lib/transport/transport_impl.h \
-src/core/ext/transport/chttp2/transport/bin_decoder.h \
-src/core/ext/transport/chttp2/transport/bin_encoder.h \
-src/core/ext/transport/chttp2/transport/chttp2_transport.h \
-src/core/ext/transport/chttp2/transport/frame.h \
-src/core/ext/transport/chttp2/transport/frame_data.h \
-src/core/ext/transport/chttp2/transport/frame_goaway.h \
-src/core/ext/transport/chttp2/transport/frame_ping.h \
-src/core/ext/transport/chttp2/transport/frame_rst_stream.h \
-src/core/ext/transport/chttp2/transport/frame_settings.h \
-src/core/ext/transport/chttp2/transport/frame_window_update.h \
-src/core/ext/transport/chttp2/transport/hpack_encoder.h \
-src/core/ext/transport/chttp2/transport/hpack_parser.h \
-src/core/ext/transport/chttp2/transport/hpack_table.h \
-src/core/ext/transport/chttp2/transport/http2_errors.h \
-src/core/ext/transport/chttp2/transport/huffsyms.h \
-src/core/ext/transport/chttp2/transport/incoming_metadata.h \
-src/core/ext/transport/chttp2/transport/internal.h \
-src/core/ext/transport/chttp2/transport/status_conversion.h \
-src/core/ext/transport/chttp2/transport/stream_map.h \
-src/core/ext/transport/chttp2/transport/varint.h \
-src/core/ext/transport/chttp2/alpn/alpn.h \
-src/core/lib/security/context/security_context.h \
-src/core/lib/security/credentials/composite/composite_credentials.h \
-src/core/lib/security/credentials/credentials.h \
-src/core/lib/security/credentials/fake/fake_credentials.h \
-src/core/lib/security/credentials/google_default/google_default_credentials.h \
-src/core/lib/security/credentials/iam/iam_credentials.h \
-src/core/lib/security/credentials/jwt/json_token.h \
-src/core/lib/security/credentials/jwt/jwt_credentials.h \
-src/core/lib/security/credentials/jwt/jwt_verifier.h \
-src/core/lib/security/credentials/oauth2/oauth2_credentials.h \
-src/core/lib/security/credentials/plugin/plugin_credentials.h \
-src/core/lib/security/credentials/ssl/ssl_credentials.h \
-src/core/lib/security/transport/auth_filters.h \
-src/core/lib/security/transport/handshake.h \
-src/core/lib/security/transport/secure_endpoint.h \
-src/core/lib/security/transport/security_connector.h \
-src/core/lib/security/transport/tsi_error.h \
-src/core/lib/security/util/b64.h \
-src/core/lib/security/util/json_util.h \
-src/core/lib/tsi/fake_transport_security.h \
-src/core/lib/tsi/ssl_transport_security.h \
-src/core/lib/tsi/ssl_types.h \
-src/core/lib/tsi/transport_security.h \
-src/core/lib/tsi/transport_security_interface.h \
-src/core/ext/client_channel/client_channel.h \
-src/core/ext/client_channel/client_channel_factory.h \
-src/core/ext/client_channel/connector.h \
-src/core/ext/client_channel/http_connect_handshaker.h \
-src/core/ext/client_channel/initial_connect_string.h \
-src/core/ext/client_channel/lb_policy.h \
-src/core/ext/client_channel/lb_policy_factory.h \
-src/core/ext/client_channel/lb_policy_registry.h \
-src/core/ext/client_channel/parse_address.h \
-src/core/ext/client_channel/resolver.h \
-src/core/ext/client_channel/resolver_factory.h \
-src/core/ext/client_channel/resolver_registry.h \
-src/core/ext/client_channel/subchannel.h \
-src/core/ext/client_channel/subchannel_index.h \
-src/core/ext/client_channel/uri_parser.h \
-src/core/ext/lb_policy/grpclb/grpclb.h \
-src/core/ext/lb_policy/grpclb/load_balancer_api.h \
-src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h \
-third_party/nanopb/pb.h \
-third_party/nanopb/pb_common.h \
-third_party/nanopb/pb_decode.h \
-third_party/nanopb/pb_encode.h \
-src/core/ext/load_reporting/load_reporting.h \
-src/core/ext/load_reporting/load_reporting_filter.h \
-src/core/ext/census/aggregation.h \
-src/core/ext/census/base_resources.h \
-src/core/ext/census/census_interface.h \
-src/core/ext/census/census_rpc_stats.h \
-src/core/ext/census/gen/census.pb.h \
-src/core/ext/census/gen/trace_context.pb.h \
-src/core/ext/census/grpc_filter.h \
-src/core/ext/census/mlog.h \
-src/core/ext/census/resource.h \
-src/core/ext/census/rpc_metric_id.h \
-src/core/ext/census/trace_context.h \
-src/core/lib/surface/init.c \
-src/core/lib/channel/channel_args.c \
-src/core/lib/channel/channel_stack.c \
-src/core/lib/channel/channel_stack_builder.c \
-src/core/lib/channel/compress_filter.c \
-src/core/lib/channel/connected_channel.c \
-src/core/lib/channel/deadline_filter.c \
-src/core/lib/channel/handshaker.c \
-src/core/lib/channel/http_client_filter.c \
-src/core/lib/channel/http_server_filter.c \
-src/core/lib/channel/message_size_filter.c \
-src/core/lib/compression/compression.c \
-src/core/lib/compression/message_compress.c \
-src/core/lib/debug/trace.c \
-src/core/lib/http/format_request.c \
-src/core/lib/http/httpcli.c \
-src/core/lib/http/parser.c \
-src/core/lib/iomgr/closure.c \
-src/core/lib/iomgr/combiner.c \
-src/core/lib/iomgr/endpoint.c \
-src/core/lib/iomgr/endpoint_pair_posix.c \
-src/core/lib/iomgr/endpoint_pair_uv.c \
-src/core/lib/iomgr/endpoint_pair_windows.c \
-src/core/lib/iomgr/error.c \
-src/core/lib/iomgr/ev_epoll_linux.c \
-src/core/lib/iomgr/ev_poll_posix.c \
-src/core/lib/iomgr/ev_posix.c \
-src/core/lib/iomgr/exec_ctx.c \
-src/core/lib/iomgr/executor.c \
-src/core/lib/iomgr/iocp_windows.c \
-src/core/lib/iomgr/iomgr.c \
-src/core/lib/iomgr/iomgr_posix.c \
-src/core/lib/iomgr/iomgr_uv.c \
-src/core/lib/iomgr/iomgr_windows.c \
-src/core/lib/iomgr/load_file.c \
-src/core/lib/iomgr/network_status_tracker.c \
-src/core/lib/iomgr/polling_entity.c \
-src/core/lib/iomgr/pollset_set_uv.c \
-src/core/lib/iomgr/pollset_set_windows.c \
-src/core/lib/iomgr/pollset_uv.c \
-src/core/lib/iomgr/pollset_windows.c \
-src/core/lib/iomgr/resolve_address_posix.c \
-src/core/lib/iomgr/resolve_address_uv.c \
-src/core/lib/iomgr/resolve_address_windows.c \
-src/core/lib/iomgr/resource_quota.c \
-src/core/lib/iomgr/sockaddr_utils.c \
-src/core/lib/iomgr/socket_mutator.c \
-src/core/lib/iomgr/socket_utils_common_posix.c \
-src/core/lib/iomgr/socket_utils_linux.c \
-src/core/lib/iomgr/socket_utils_posix.c \
-src/core/lib/iomgr/socket_utils_uv.c \
-src/core/lib/iomgr/socket_utils_windows.c \
-src/core/lib/iomgr/socket_windows.c \
-src/core/lib/iomgr/tcp_client_posix.c \
-src/core/lib/iomgr/tcp_client_uv.c \
-src/core/lib/iomgr/tcp_client_windows.c \
-src/core/lib/iomgr/tcp_posix.c \
-src/core/lib/iomgr/tcp_server_posix.c \
-src/core/lib/iomgr/tcp_server_uv.c \
-src/core/lib/iomgr/tcp_server_windows.c \
-src/core/lib/iomgr/tcp_uv.c \
-src/core/lib/iomgr/tcp_windows.c \
-src/core/lib/iomgr/time_averaged_stats.c \
-src/core/lib/iomgr/timer_generic.c \
-src/core/lib/iomgr/timer_heap.c \
-src/core/lib/iomgr/timer_uv.c \
-src/core/lib/iomgr/udp_server.c \
-src/core/lib/iomgr/unix_sockets_posix.c \
-src/core/lib/iomgr/unix_sockets_posix_noop.c \
-src/core/lib/iomgr/wakeup_fd_cv.c \
-src/core/lib/iomgr/wakeup_fd_eventfd.c \
-src/core/lib/iomgr/wakeup_fd_nospecial.c \
-src/core/lib/iomgr/wakeup_fd_pipe.c \
-src/core/lib/iomgr/wakeup_fd_posix.c \
-src/core/lib/iomgr/workqueue_uv.c \
-src/core/lib/iomgr/workqueue_windows.c \
-src/core/lib/json/json.c \
 src/core/lib/json/json_reader.c \
+src/core/lib/json/json_reader.h \
 src/core/lib/json/json_string.c \
 src/core/lib/json/json_writer.c \
-src/core/lib/slice/percent_encoding.c \
-src/core/lib/slice/slice.c \
-src/core/lib/slice/slice_buffer.c \
-src/core/lib/slice/slice_string_helpers.c \
-src/core/lib/surface/alarm.c \
-src/core/lib/surface/api_trace.c \
-src/core/lib/surface/byte_buffer.c \
-src/core/lib/surface/byte_buffer_reader.c \
-src/core/lib/surface/call.c \
-src/core/lib/surface/call_details.c \
-src/core/lib/surface/call_log_batch.c \
-src/core/lib/surface/channel.c \
-src/core/lib/surface/channel_init.c \
-src/core/lib/surface/channel_ping.c \
-src/core/lib/surface/channel_stack_type.c \
-src/core/lib/surface/completion_queue.c \
-src/core/lib/surface/event_string.c \
-src/core/lib/surface/lame_client.c \
-src/core/lib/surface/metadata_array.c \
-src/core/lib/surface/server.c \
-src/core/lib/surface/validate_metadata.c \
-src/core/lib/surface/version.c \
-src/core/lib/transport/byte_stream.c \
-src/core/lib/transport/connectivity_state.c \
-src/core/lib/transport/mdstr_hash_table.c \
-src/core/lib/transport/metadata.c \
-src/core/lib/transport/metadata_batch.c \
-src/core/lib/transport/pid_controller.c \
-src/core/lib/transport/service_config.c \
-src/core/lib/transport/static_metadata.c \
-src/core/lib/transport/timeout_encoding.c \
-src/core/lib/transport/transport.c \
-src/core/lib/transport/transport_op_string.c \
-src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c \
-src/core/ext/transport/chttp2/transport/bin_decoder.c \
-src/core/ext/transport/chttp2/transport/bin_encoder.c \
-src/core/ext/transport/chttp2/transport/chttp2_plugin.c \
-src/core/ext/transport/chttp2/transport/chttp2_transport.c \
-src/core/ext/transport/chttp2/transport/frame_data.c \
-src/core/ext/transport/chttp2/transport/frame_goaway.c \
-src/core/ext/transport/chttp2/transport/frame_ping.c \
-src/core/ext/transport/chttp2/transport/frame_rst_stream.c \
-src/core/ext/transport/chttp2/transport/frame_settings.c \
-src/core/ext/transport/chttp2/transport/frame_window_update.c \
-src/core/ext/transport/chttp2/transport/hpack_encoder.c \
-src/core/ext/transport/chttp2/transport/hpack_parser.c \
-src/core/ext/transport/chttp2/transport/hpack_table.c \
-src/core/ext/transport/chttp2/transport/huffsyms.c \
-src/core/ext/transport/chttp2/transport/incoming_metadata.c \
-src/core/ext/transport/chttp2/transport/parsing.c \
-src/core/ext/transport/chttp2/transport/status_conversion.c \
-src/core/ext/transport/chttp2/transport/stream_lists.c \
-src/core/ext/transport/chttp2/transport/stream_map.c \
-src/core/ext/transport/chttp2/transport/varint.c \
-src/core/ext/transport/chttp2/transport/writing.c \
-src/core/ext/transport/chttp2/alpn/alpn.c \
-src/core/lib/http/httpcli_security_connector.c \
+src/core/lib/json/json_writer.h \
+src/core/lib/profiling/basic_timers.c \
+src/core/lib/profiling/stap_timers.c \
+src/core/lib/profiling/timers.h \
 src/core/lib/security/context/security_context.c \
+src/core/lib/security/context/security_context.h \
 src/core/lib/security/credentials/composite/composite_credentials.c \
+src/core/lib/security/credentials/composite/composite_credentials.h \
 src/core/lib/security/credentials/credentials.c \
+src/core/lib/security/credentials/credentials.h \
 src/core/lib/security/credentials/credentials_metadata.c \
 src/core/lib/security/credentials/fake/fake_credentials.c \
+src/core/lib/security/credentials/fake/fake_credentials.h \
 src/core/lib/security/credentials/google_default/credentials_generic.c \
 src/core/lib/security/credentials/google_default/google_default_credentials.c \
+src/core/lib/security/credentials/google_default/google_default_credentials.h \
 src/core/lib/security/credentials/iam/iam_credentials.c \
+src/core/lib/security/credentials/iam/iam_credentials.h \
 src/core/lib/security/credentials/jwt/json_token.c \
+src/core/lib/security/credentials/jwt/json_token.h \
 src/core/lib/security/credentials/jwt/jwt_credentials.c \
+src/core/lib/security/credentials/jwt/jwt_credentials.h \
 src/core/lib/security/credentials/jwt/jwt_verifier.c \
+src/core/lib/security/credentials/jwt/jwt_verifier.h \
 src/core/lib/security/credentials/oauth2/oauth2_credentials.c \
+src/core/lib/security/credentials/oauth2/oauth2_credentials.h \
 src/core/lib/security/credentials/plugin/plugin_credentials.c \
+src/core/lib/security/credentials/plugin/plugin_credentials.h \
 src/core/lib/security/credentials/ssl/ssl_credentials.c \
+src/core/lib/security/credentials/ssl/ssl_credentials.h \
+src/core/lib/security/transport/auth_filters.h \
 src/core/lib/security/transport/client_auth_filter.c \
-src/core/lib/security/transport/handshake.c \
 src/core/lib/security/transport/secure_endpoint.c \
+src/core/lib/security/transport/secure_endpoint.h \
 src/core/lib/security/transport/security_connector.c \
+src/core/lib/security/transport/security_connector.h \
+src/core/lib/security/transport/security_handshaker.c \
+src/core/lib/security/transport/security_handshaker.h \
 src/core/lib/security/transport/server_auth_filter.c \
 src/core/lib/security/transport/tsi_error.c \
+src/core/lib/security/transport/tsi_error.h \
 src/core/lib/security/util/b64.c \
+src/core/lib/security/util/b64.h \
 src/core/lib/security/util/json_util.c \
-src/core/lib/surface/init_secure.c \
-src/core/lib/tsi/fake_transport_security.c \
-src/core/lib/tsi/ssl_transport_security.c \
-src/core/lib/tsi/transport_security.c \
-src/core/ext/transport/chttp2/client/secure/secure_channel_create.c \
-src/core/ext/client_channel/channel_connectivity.c \
-src/core/ext/client_channel/client_channel.c \
-src/core/ext/client_channel/client_channel_factory.c \
-src/core/ext/client_channel/client_channel_plugin.c \
-src/core/ext/client_channel/connector.c \
-src/core/ext/client_channel/default_initial_connect_string.c \
-src/core/ext/client_channel/http_connect_handshaker.c \
-src/core/ext/client_channel/initial_connect_string.c \
-src/core/ext/client_channel/lb_policy.c \
-src/core/ext/client_channel/lb_policy_factory.c \
-src/core/ext/client_channel/lb_policy_registry.c \
-src/core/ext/client_channel/parse_address.c \
-src/core/ext/client_channel/resolver.c \
-src/core/ext/client_channel/resolver_factory.c \
-src/core/ext/client_channel/resolver_registry.c \
-src/core/ext/client_channel/subchannel.c \
-src/core/ext/client_channel/subchannel_index.c \
-src/core/ext/client_channel/uri_parser.c \
-src/core/ext/transport/chttp2/server/insecure/server_chttp2.c \
-src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c \
-src/core/ext/transport/chttp2/client/insecure/channel_create.c \
-src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c \
-src/core/ext/lb_policy/grpclb/grpclb.c \
-src/core/ext/lb_policy/grpclb/load_balancer_api.c \
-src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \
-third_party/nanopb/pb_common.c \
-third_party/nanopb/pb_decode.c \
-third_party/nanopb/pb_encode.c \
-src/core/ext/lb_policy/pick_first/pick_first.c \
-src/core/ext/lb_policy/round_robin/round_robin.c \
-src/core/ext/resolver/dns/native/dns_resolver.c \
-src/core/ext/resolver/sockaddr/sockaddr_resolver.c \
-src/core/ext/load_reporting/load_reporting.c \
-src/core/ext/load_reporting/load_reporting_filter.c \
-src/core/ext/census/base_resources.c \
-src/core/ext/census/context.c \
-src/core/ext/census/gen/census.pb.c \
-src/core/ext/census/gen/trace_context.pb.c \
-src/core/ext/census/grpc_context.c \
-src/core/ext/census/grpc_filter.c \
-src/core/ext/census/grpc_plugin.c \
-src/core/ext/census/initialize.c \
-src/core/ext/census/mlog.c \
-src/core/ext/census/operation.c \
-src/core/ext/census/placeholders.c \
-src/core/ext/census/resource.c \
-src/core/ext/census/trace_context.c \
-src/core/ext/census/tracing.c \
-src/core/plugin_registry/grpc_plugin_registry.c \
-include/grpc/support/alloc.h \
-include/grpc/support/atm.h \
-include/grpc/support/atm_gcc_atomic.h \
-include/grpc/support/atm_gcc_sync.h \
-include/grpc/support/atm_windows.h \
-include/grpc/support/avl.h \
-include/grpc/support/cmdline.h \
-include/grpc/support/cpu.h \
-include/grpc/support/histogram.h \
-include/grpc/support/host_port.h \
-include/grpc/support/log.h \
-include/grpc/support/log_windows.h \
-include/grpc/support/port_platform.h \
-include/grpc/support/string_util.h \
-include/grpc/support/subprocess.h \
-include/grpc/support/sync.h \
-include/grpc/support/sync_generic.h \
-include/grpc/support/sync_posix.h \
-include/grpc/support/sync_windows.h \
-include/grpc/support/thd.h \
-include/grpc/support/time.h \
-include/grpc/support/tls.h \
-include/grpc/support/tls_gcc.h \
-include/grpc/support/tls_msvc.h \
-include/grpc/support/tls_pthread.h \
-include/grpc/support/useful.h \
-include/grpc/impl/codegen/atm.h \
-include/grpc/impl/codegen/atm_gcc_atomic.h \
-include/grpc/impl/codegen/atm_gcc_sync.h \
-include/grpc/impl/codegen/atm_windows.h \
-include/grpc/impl/codegen/gpr_types.h \
-include/grpc/impl/codegen/port_platform.h \
-include/grpc/impl/codegen/slice.h \
-include/grpc/impl/codegen/sync.h \
-include/grpc/impl/codegen/sync_generic.h \
-include/grpc/impl/codegen/sync_posix.h \
-include/grpc/impl/codegen/sync_windows.h \
-src/core/lib/profiling/timers.h \
-src/core/lib/support/backoff.h \
-src/core/lib/support/block_annotate.h \
-src/core/lib/support/env.h \
-src/core/lib/support/mpscq.h \
-src/core/lib/support/murmur_hash.h \
-src/core/lib/support/stack_lockfree.h \
-src/core/lib/support/string.h \
-src/core/lib/support/string_windows.h \
-src/core/lib/support/thd_internal.h \
-src/core/lib/support/time_precise.h \
-src/core/lib/support/tmpfile.h \
-src/core/lib/profiling/basic_timers.c \
-src/core/lib/profiling/stap_timers.c \
+src/core/lib/security/util/json_util.h \
+src/core/lib/slice/percent_encoding.c \
+src/core/lib/slice/percent_encoding.h \
+src/core/lib/slice/slice.c \
+src/core/lib/slice/slice_buffer.c \
+src/core/lib/slice/slice_internal.h \
+src/core/lib/slice/slice_string_helpers.c \
+src/core/lib/slice/slice_string_helpers.h \
 src/core/lib/support/alloc.c \
 src/core/lib/support/avl.c \
 src/core/lib/support/backoff.c \
+src/core/lib/support/backoff.h \
+src/core/lib/support/block_annotate.h \
 src/core/lib/support/cmdline.c \
 src/core/lib/support/cpu_iphone.c \
 src/core/lib/support/cpu_linux.c \
 src/core/lib/support/cpu_posix.c \
 src/core/lib/support/cpu_windows.c \
+src/core/lib/support/env.h \
 src/core/lib/support/env_linux.c \
 src/core/lib/support/env_posix.c \
 src/core/lib/support/env_windows.c \
@@ -1248,29 +1226,109 @@ src/core/lib/support/log_linux.c \
 src/core/lib/support/log_posix.c \
 src/core/lib/support/log_windows.c \
 src/core/lib/support/mpscq.c \
+src/core/lib/support/mpscq.h \
 src/core/lib/support/murmur_hash.c \
+src/core/lib/support/murmur_hash.h \
 src/core/lib/support/stack_lockfree.c \
+src/core/lib/support/stack_lockfree.h \
 src/core/lib/support/string.c \
+src/core/lib/support/string.h \
 src/core/lib/support/string_posix.c \
 src/core/lib/support/string_util_windows.c \
 src/core/lib/support/string_windows.c \
+src/core/lib/support/string_windows.h \
 src/core/lib/support/subprocess_posix.c \
 src/core/lib/support/subprocess_windows.c \
 src/core/lib/support/sync.c \
 src/core/lib/support/sync_posix.c \
 src/core/lib/support/sync_windows.c \
 src/core/lib/support/thd.c \
+src/core/lib/support/thd_internal.h \
 src/core/lib/support/thd_posix.c \
 src/core/lib/support/thd_windows.c \
 src/core/lib/support/time.c \
 src/core/lib/support/time_posix.c \
 src/core/lib/support/time_precise.c \
+src/core/lib/support/time_precise.h \
 src/core/lib/support/time_windows.c \
 src/core/lib/support/tls_pthread.c \
+src/core/lib/support/tmpfile.h \
 src/core/lib/support/tmpfile_msys.c \
 src/core/lib/support/tmpfile_posix.c \
 src/core/lib/support/tmpfile_windows.c \
-src/core/lib/support/wrap_memcpy.c
+src/core/lib/support/wrap_memcpy.c \
+src/core/lib/surface/README.md \
+src/core/lib/surface/alarm.c \
+src/core/lib/surface/api_trace.c \
+src/core/lib/surface/api_trace.h \
+src/core/lib/surface/byte_buffer.c \
+src/core/lib/surface/byte_buffer_reader.c \
+src/core/lib/surface/call.c \
+src/core/lib/surface/call.h \
+src/core/lib/surface/call_details.c \
+src/core/lib/surface/call_log_batch.c \
+src/core/lib/surface/call_test_only.h \
+src/core/lib/surface/channel.c \
+src/core/lib/surface/channel.h \
+src/core/lib/surface/channel_init.c \
+src/core/lib/surface/channel_init.h \
+src/core/lib/surface/channel_ping.c \
+src/core/lib/surface/channel_stack_type.c \
+src/core/lib/surface/channel_stack_type.h \
+src/core/lib/surface/completion_queue.c \
+src/core/lib/surface/completion_queue.h \
+src/core/lib/surface/event_string.c \
+src/core/lib/surface/event_string.h \
+src/core/lib/surface/init.c \
+src/core/lib/surface/init.h \
+src/core/lib/surface/init_secure.c \
+src/core/lib/surface/lame_client.c \
+src/core/lib/surface/lame_client.h \
+src/core/lib/surface/metadata_array.c \
+src/core/lib/surface/server.c \
+src/core/lib/surface/server.h \
+src/core/lib/surface/validate_metadata.c \
+src/core/lib/surface/version.c \
+src/core/lib/transport/README.md \
+src/core/lib/transport/byte_stream.c \
+src/core/lib/transport/byte_stream.h \
+src/core/lib/transport/connectivity_state.c \
+src/core/lib/transport/connectivity_state.h \
+src/core/lib/transport/mdstr_hash_table.c \
+src/core/lib/transport/mdstr_hash_table.h \
+src/core/lib/transport/metadata.c \
+src/core/lib/transport/metadata.h \
+src/core/lib/transport/metadata_batch.c \
+src/core/lib/transport/metadata_batch.h \
+src/core/lib/transport/pid_controller.c \
+src/core/lib/transport/pid_controller.h \
+src/core/lib/transport/service_config.c \
+src/core/lib/transport/service_config.h \
+src/core/lib/transport/static_metadata.c \
+src/core/lib/transport/static_metadata.h \
+src/core/lib/transport/timeout_encoding.c \
+src/core/lib/transport/timeout_encoding.h \
+src/core/lib/transport/transport.c \
+src/core/lib/transport/transport.h \
+src/core/lib/transport/transport_impl.h \
+src/core/lib/transport/transport_op_string.c \
+src/core/lib/tsi/README.md \
+src/core/lib/tsi/fake_transport_security.c \
+src/core/lib/tsi/fake_transport_security.h \
+src/core/lib/tsi/ssl_transport_security.c \
+src/core/lib/tsi/ssl_transport_security.h \
+src/core/lib/tsi/ssl_types.h \
+src/core/lib/tsi/transport_security.c \
+src/core/lib/tsi/transport_security.h \
+src/core/lib/tsi/transport_security_interface.h \
+src/core/plugin_registry/grpc_plugin_registry.c \
+third_party/nanopb/pb.h \
+third_party/nanopb/pb_common.c \
+third_party/nanopb/pb_common.h \
+third_party/nanopb/pb_decode.c \
+third_party/nanopb/pb_decode.h \
+third_party/nanopb/pb_encode.c \
+third_party/nanopb/pb_encode.h
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/tools/gce/linux_performance_worker_init.sh b/tools/gce/linux_performance_worker_init.sh
index 523749ee81457a28fd7f980b34b7b53176add14f..ab29e015e0444d61419796f6784e1b46046c1aac 100755
--- a/tools/gce/linux_performance_worker_init.sh
+++ b/tools/gce/linux_performance_worker_init.sh
@@ -150,3 +150,19 @@ sudo tar -C /usr/local -xzf go$GO_VERSION.$OS-$ARCH.tar.gz
 # Put go on the PATH, keep the usual installation dir
 sudo ln -s /usr/local/go/bin/go /usr/bin/go
 rm go$GO_VERSION.$OS-$ARCH.tar.gz
+
+# Install perf, to profile benchmarks. (need to get the right linux-tools-<> for kernel version)
+sudo apt-get install -y linux-tools-common linux-tools-generic linux-tools-`uname -r`
+# see http://unix.stackexchange.com/questions/14227/do-i-need-root-admin-permissions-to-run-userspace-perf-tool-perf-events-ar
+echo 0 | sudo tee /proc/sys/kernel/perf_event_paranoid
+# see http://stackoverflow.com/questions/21284906/perf-couldnt-record-kernel-reference-relocation-symbol
+echo 0 | sudo tee /proc/sys/kernel/kptr_restrict
+
+# qps workers under perf appear to need a lot of mmap pages under certain scenarios and perf args in
+# order to not lose perf events or time out
+echo 4096 | sudo tee /proc/sys/kernel/perf_event_mlock_kb
+
+# Fetch scripts to generate flame graphs from perf data collected
+# on benchmarks
+git clone -v https://github.com/brendangregg/FlameGraph ~/FlameGraph
+
diff --git a/tools/internal_ci/linux/grpc_fuzzer_client.cfg b/tools/internal_ci/linux/grpc_fuzzer_client.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b1bce0228233fe7085b4861c60e0d63bb7c78992
--- /dev/null
+++ b/tools/internal_ci/linux/grpc_fuzzer_client.cfg
@@ -0,0 +1,40 @@
+#!/bin/bash
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/linux/grpc_fuzzer_client.sh"
+timeout_mins: 1440  # 24 hours is the maximum allowed value
+action {
+  define_artifacts {
+    regex: "git/grpc/fuzzer_output/**"
+  }
+}
diff --git a/tools/internal_ci/linux/grpc_fuzzer_client.sh b/tools/internal_ci/linux/grpc_fuzzer_client.sh
new file mode 100755
index 0000000000000000000000000000000000000000..f9ff13d303e6bb3324f078e318e6a1861c9082fe
--- /dev/null
+++ b/tools/internal_ci/linux/grpc_fuzzer_client.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set -ex
+
+# change to grpc repo root
+cd $(dirname $0)/../../..
+
+git submodule update --init
+
+# download fuzzer docker image from dockerhub
+export DOCKERHUB_ORGANIZATION=grpctesting
+# runtime 23 * 60 mins
+config=asan-trace-cmp runtime=82800 tools/jenkins/run_fuzzer.sh client_fuzzer
diff --git a/tools/internal_ci/linux/grpc_master.cfg b/tools/internal_ci/linux/grpc_master.cfg
index 1f81660078001c628643a01b4e01bce8215797c4..8ce2ef11a205e26484df18e64f3d4621e6609b3f 100644
--- a/tools/internal_ci/linux/grpc_master.cfg
+++ b/tools/internal_ci/linux/grpc_master.cfg
@@ -31,4 +31,10 @@
 # Config file for the internal CI (in protobuf text format)
 
 # Location of the continuous shell script in repository.
-build_file: "grpc/tools/internal_ci/linux/run_tests.sh"
+build_file: "grpc/tools/internal_ci/linux/grpc_master.sh"
+timeout_mins: 60
+action {
+  define_artifacts {
+    regex: "**/sponge_log.xml"
+  }
+}
diff --git a/tools/internal_ci/linux/run_tests.sh b/tools/internal_ci/linux/grpc_master.sh
similarity index 88%
rename from tools/internal_ci/linux/run_tests.sh
rename to tools/internal_ci/linux/grpc_master.sh
index be477c1271dfeaae068f492b488c32a40e37a73f..ea77d1130585507284f8b01c38211e16d6ec0cad 100755
--- a/tools/internal_ci/linux/run_tests.sh
+++ b/tools/internal_ci/linux/grpc_master.sh
@@ -42,4 +42,12 @@ docker --version || true
 
 git submodule update --init
 
-tools/run_tests/run_tests.py -l c --build_only
+tools/run_tests/run_tests.py -l c -t -x sponge_log.xml || FAILED="true"
+
+# kill port_server.py to prevent the build from hanging
+ps aux | grep port_server\\.py | awk '{print $2}' | xargs kill -9
+
+if [ "$FAILED" != "" ]
+then
+  exit 1
+fi
diff --git a/tools/jenkins/run_bazel_basic.sh b/tools/jenkins/run_bazel_basic.sh
new file mode 100755
index 0000000000000000000000000000000000000000..648bc791bdca2c47c1d47d523a595bfe9dd7a569
--- /dev/null
+++ b/tools/jenkins/run_bazel_basic.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+# Copyright 2017, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Test basic Bazel features
+#
+# NOTE: No empty lines should appear in this file before igncr is set!
+set -ex -o igncr || set -ex
+
+export DOCKERFILE_DIR=tools/dockerfile/test/bazel
+export DOCKER_RUN_SCRIPT=tools/jenkins/run_bazel_basic_in_docker.sh
+exec tools/run_tests/dockerize/build_and_run_docker.sh
diff --git a/tools/jenkins/run_bazel_basic_in_docker.sh b/tools/jenkins/run_bazel_basic_in_docker.sh
new file mode 100755
index 0000000000000000000000000000000000000000..51aaa90ff82e7244829917dc2bf97434efd1a087
--- /dev/null
+++ b/tools/jenkins/run_bazel_basic_in_docker.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+# Copyright 2017, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Test basic Bazel features
+#
+# NOTE: No empty lines should appear in this file before igncr is set!
+set -ex -o igncr || set -ex
+
+mkdir -p /var/local/git
+git clone /var/local/jenkins/grpc /var/local/git/grpc
+(cd /var/local/jenkins/grpc/ && git submodule foreach 'cd /var/local/git/grpc \
+&& git submodule update --init --reference /var/local/jenkins/grpc/${name} \
+${name}')
+cd /var/local/git/grpc
+bazel build --spawn_strategy=standalone --genrule_strategy=standalone :all test/...
diff --git a/tools/jenkins/run_bazel_full.sh b/tools/jenkins/run_bazel_full.sh
new file mode 100755
index 0000000000000000000000000000000000000000..53ed360c077722e31337e7ce497e7f3c5de87ca8
--- /dev/null
+++ b/tools/jenkins/run_bazel_full.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+# Copyright 2017, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Test full Bazel
+#
+# NOTE: No empty lines should appear in this file before igncr is set!
+set -ex -o igncr || set -ex
+
+export DOCKERFILE_DIR=tools/dockerfile/test/bazel
+export DOCKER_RUN_SCRIPT=tools/jenkins/run_bazel_full_in_docker.sh
+exec tools/run_tests/dockerize/build_and_run_docker.sh
diff --git a/tools/jenkins/run_bazel_full_in_docker.sh b/tools/jenkins/run_bazel_full_in_docker.sh
new file mode 100755
index 0000000000000000000000000000000000000000..19502f19b75ce447c7bb76bb3572a76678be00e6
--- /dev/null
+++ b/tools/jenkins/run_bazel_full_in_docker.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+# Copyright 2017, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Test full Bazel
+#
+# NOTE: No empty lines should appear in this file before igncr is set!
+set -ex -o igncr || set -ex
+
+mkdir -p /var/local/git
+git clone /var/local/jenkins/grpc /var/local/git/grpc
+(cd /var/local/jenkins/grpc/ && git submodule foreach 'cd /var/local/git/grpc \
+&& git submodule update --init --reference /var/local/jenkins/grpc/${name} \
+${name}')
+cd /var/local/git/grpc/test
+bazel test --spawn_strategy=standalone --genrule_strategy=standalone ...
diff --git a/tools/jenkins/run_performance_flamegraphs.sh b/tools/jenkins/run_performance_flamegraphs.sh
new file mode 100755
index 0000000000000000000000000000000000000000..22081e22487cc055b754ac62da5f089f8744e689
--- /dev/null
+++ b/tools/jenkins/run_performance_flamegraphs.sh
@@ -0,0 +1,56 @@
+#!/usr/bin/env bash
+# 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.
+#
+# This script is invoked by Jenkins and runs full performance test suite.
+set -ex
+
+# Enter the gRPC repo root
+cd $(dirname $0)/../..
+
+# scalability with 32cores c++ benchmarks
+tools/run_tests/run_performance_tests.py \
+    -l c++ \
+    --category scalable \
+    --remote_worker_host grpc-performance-server-32core grpc-performance-client-32core grpc-performance-client2-32core \
+    --perf_args "record -F 97 --call-graph dwarf" \
+    --flame_graph_reports cpp_flamegraphs \
+    || EXIT_CODE=1
+
+# scalability with 32cores go benchmarks
+tools/run_tests/run_performance_tests.py \
+    -l go \
+    --category scalable \
+    --remote_worker_host grpc-performance-server-32core grpc-performance-client-32core grpc-performance-client2-32core \
+    --perf_args "record -F 97 -g" \
+    --flame_graph_reports go_flamegraphs \
+    || EXIT_CODE=1
+
+exit $EXIT_CODE
+
diff --git a/tools/jenkins/run_sweep_performance.sh b/tools/jenkins/run_sweep_performance.sh
index 3b22e25a451602872ede572c1be646ff09031e99..12054dc3dc83a698bd615054393459de6dd38874 100755
--- a/tools/jenkins/run_sweep_performance.sh
+++ b/tools/jenkins/run_sweep_performance.sh
@@ -43,6 +43,7 @@ tools/run_tests/run_performance_tests.py \
     --category sweep \
     --bq_result_table performance_test.performance_experiment_32core \
     --remote_worker_host ${SERVER_HOST} ${CLIENT_HOST1} ${CLIENT_HOST2} \
+    --perf_args "record -F 97 --call-graph dwarf" \
     || EXIT_CODE=1
 
 exit $EXIT_CODE
diff --git a/tools/run_tests/README.md b/tools/run_tests/README.md
index dd727f43091a38d9a31743a1b3297ceb463b32a3..e709ddd2c021718eb2b519826c03beeafb0a4e5f 100644
--- a/tools/run_tests/README.md
+++ b/tools/run_tests/README.md
@@ -22,6 +22,10 @@ The script is also capable of running interop tests for grpc-java and grpc-go, u
 ######Example
 `tools/run_tests/run_interop_tests.py -l csharp -s c++ --use_docker` (run interop tests with C# client and C++ server)
 
+Note: if you see an error like `no space left on device` when running the
+interop tests using Docker, make sure that Docker is building the image files in
+a location with sufficient disk space.
+
 #Performance benchmarks (run_performance_tests.py)
 
 Runs predefined benchmark scenarios for given languages. Besides the simple configuration of running all the scenarios locally,
diff --git a/tools/distrib/python/grpcio_tools/grpc/__init__.py b/tools/run_tests/artifacts/__init__.py
similarity index 96%
rename from tools/distrib/python/grpcio_tools/grpc/__init__.py
rename to tools/run_tests/artifacts/__init__.py
index 70ac5edd48370ac77409293d23742878764020b7..100a624dc9c1bbd89708c58e18cf2666a73f4cc7 100644
--- a/tools/distrib/python/grpcio_tools/grpc/__init__.py
+++ b/tools/run_tests/artifacts/__init__.py
@@ -26,5 +26,3 @@
 # 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.
-
-__import__('pkg_resources').declare_namespace(__name__)
diff --git a/tools/run_tests/artifact_targets.py b/tools/run_tests/artifacts/artifact_targets.py
similarity index 89%
rename from tools/run_tests/artifact_targets.py
rename to tools/run_tests/artifacts/artifact_targets.py
index 65d34e17e1c4669c9d9b2fd1eb98e19aef888f61..005d99790ad90799f0f4e4526f8312cdff11581f 100644
--- a/tools/run_tests/artifact_targets.py
+++ b/tools/run_tests/artifacts/artifact_targets.py
@@ -35,7 +35,8 @@ import random
 import string
 import sys
 
-import jobset
+sys.path.insert(0, os.path.abspath('..'))
+import python_utils.jobset as jobset
 
 
 def create_docker_jobspec(name, dockerfile_dir, shell_command, environ={},
@@ -113,7 +114,7 @@ class PythonArtifact:
       environ['GRPC_BUILD_MANYLINUX_WHEEL'] = 'TRUE'
       return create_docker_jobspec(self.name,
           'tools/dockerfile/grpc_artifact_python_manylinux_%s' % self.arch,
-          'tools/run_tests/build_artifact_python.sh',
+          'tools/run_tests/artifacts/build_artifact_python.sh',
           environ=environ,
           timeout_seconds=60*60)
     elif self.platform == 'windows':
@@ -125,7 +126,7 @@ class PythonArtifact:
       # seed.  We create a random temp-dir here
       dir = ''.join(random.choice(string.ascii_uppercase) for _ in range(10))
       return create_jobspec(self.name,
-                            ['tools\\run_tests\\build_artifact_python.bat',
+                            ['tools\\run_tests\\artifacts\\build_artifact_python.bat',
                              self.py_version,
                              '32' if self.arch == 'x86' else '64',
                              dir
@@ -136,7 +137,7 @@ class PythonArtifact:
       environ['PYTHON'] = self.py_version
       environ['SKIP_PIP_INSTALL'] = 'TRUE'
       return create_jobspec(self.name,
-                            ['tools/run_tests/build_artifact_python.sh'],
+                            ['tools/run_tests/artifacts/build_artifact_python.sh'],
                             environ=environ)
 
   def __str__(self):
@@ -165,11 +166,11 @@ class RubyArtifact:
           environ['SETARCH_CMD'] = 'linux32'
         return create_docker_jobspec(self.name,
             'tools/dockerfile/grpc_artifact_linux_%s' % self.arch,
-            'tools/run_tests/build_artifact_ruby.sh',
+            'tools/run_tests/artifacts/build_artifact_ruby.sh',
             environ=environ)
       else:
         return create_jobspec(self.name,
-                              ['tools/run_tests/build_artifact_ruby.sh'])
+                              ['tools/run_tests/artifacts/build_artifact_ruby.sh'])
 
 
 class CSharpExtArtifact:
@@ -184,7 +185,7 @@ class CSharpExtArtifact:
   def pre_build_jobspecs(self):
     if self.platform == 'windows':
       return [create_jobspec('prebuild_%s' % self.name,
-                             ['tools\\run_tests\\pre_build_c.bat'],
+                             ['tools\\run_tests\\helper_scripts\\pre_build_c.bat'],
                              shell=True,
                              flake_retries=5,
                              timeout_retries=2)]
@@ -195,7 +196,7 @@ class CSharpExtArtifact:
     if self.platform == 'windows':
       msbuild_platform = 'Win32' if self.arch == 'x86' else self.arch
       return create_jobspec(self.name,
-                            ['tools\\run_tests\\build_artifact_csharp.bat',
+                            ['tools\\run_tests\\artifacts\\build_artifact_csharp.bat',
                              'vsprojects\\grpc_csharp_ext.sln',
                              '/p:Configuration=Release',
                              '/p:PlatformToolset=v120',
@@ -210,14 +211,14 @@ class CSharpExtArtifact:
       if self.platform == 'linux':
         return create_docker_jobspec(self.name,
             'tools/dockerfile/grpc_artifact_linux_%s' % self.arch,
-            'tools/run_tests/build_artifact_csharp.sh',
+            'tools/run_tests/artifacts/build_artifact_csharp.sh',
             environ=environ)
       else:
         archflag = _ARCH_FLAG_MAP[self.arch]
         environ['CFLAGS'] += ' %s %s' % (archflag, _MACOS_COMPAT_FLAG)
         environ['LDFLAGS'] += ' %s' % archflag
         return create_jobspec(self.name,
-                              ['tools/run_tests/build_artifact_csharp.sh'],
+                              ['tools/run_tests/artifacts/build_artifact_csharp.sh'],
                               environ=environ)
 
   def __str__(self):
@@ -245,7 +246,7 @@ class NodeExtArtifact:
   def build_jobspec(self):
     if self.platform == 'windows':
       return create_jobspec(self.name,
-                            ['tools\\run_tests\\build_artifact_node.bat',
+                            ['tools\\run_tests\\artifacts\\build_artifact_node.bat',
                              self.gyp_arch],
                             shell=True)
     else:
@@ -253,10 +254,10 @@ class NodeExtArtifact:
         return create_docker_jobspec(
             self.name,
             'tools/dockerfile/grpc_artifact_linux_{}'.format(self.arch),
-            'tools/run_tests/build_artifact_node.sh {}'.format(self.gyp_arch))
+            'tools/run_tests/artifacts/build_artifact_node.sh {}'.format(self.gyp_arch))
       else:
         return create_jobspec(self.name,
-                              ['tools/run_tests/build_artifact_node.sh',
+                              ['tools/run_tests/artifacts/build_artifact_node.sh',
                                self.gyp_arch])
 
 class PHPArtifact:
@@ -276,10 +277,10 @@ class PHPArtifact:
       return create_docker_jobspec(
           self.name,
           'tools/dockerfile/grpc_artifact_linux_{}'.format(self.arch),
-          'tools/run_tests/build_artifact_php.sh')
+          'tools/run_tests/artifacts/build_artifact_php.sh')
     else:
       return create_jobspec(self.name,
-                            ['tools/run_tests/build_artifact_php.sh'])
+                            ['tools/run_tests/artifacts/build_artifact_php.sh'])
 
 class ProtocArtifact:
   """Builds protoc and protoc-plugin artifacts"""
@@ -306,18 +307,18 @@ class ProtocArtifact:
       if self.platform == 'linux':
         return create_docker_jobspec(self.name,
             'tools/dockerfile/grpc_artifact_protoc',
-            'tools/run_tests/build_artifact_protoc.sh',
+            'tools/run_tests/artifacts/build_artifact_protoc.sh',
             environ=environ)
       else:
         environ['CXXFLAGS'] += ' -std=c++11 -stdlib=libc++ %s' % _MACOS_COMPAT_FLAG
         return create_jobspec(self.name,
-            ['tools/run_tests/build_artifact_protoc.sh'],
+            ['tools/run_tests/artifacts/build_artifact_protoc.sh'],
             environ=environ)
     else:
       generator = 'Visual Studio 12 Win64' if self.arch == 'x64' else 'Visual Studio 12' 
       vcplatform = 'x64' if self.arch == 'x64' else 'Win32'
       return create_jobspec(self.name,
-                            ['tools\\run_tests\\build_artifact_protoc.bat'],
+                            ['tools\\run_tests\\artifacts\\build_artifact_protoc.bat'],
                             environ={'generator': generator,
                                      'Platform': vcplatform})
 
diff --git a/tools/run_tests/build_artifact_csharp.bat b/tools/run_tests/artifacts/build_artifact_csharp.bat
similarity index 100%
rename from tools/run_tests/build_artifact_csharp.bat
rename to tools/run_tests/artifacts/build_artifact_csharp.bat
diff --git a/tools/run_tests/build_artifact_csharp.sh b/tools/run_tests/artifacts/build_artifact_csharp.sh
similarity index 98%
rename from tools/run_tests/build_artifact_csharp.sh
rename to tools/run_tests/artifacts/build_artifact_csharp.sh
index 7438713f5c6f146530b10cb8ebd0a63cf90a75ab..aed04b2745e23929d65cd77afc80681cf7906dd1 100755
--- a/tools/run_tests/build_artifact_csharp.sh
+++ b/tools/run_tests/artifacts/build_artifact_csharp.sh
@@ -30,7 +30,7 @@
 
 set -ex
 
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 
 make grpc_csharp_ext
 
diff --git a/tools/run_tests/build_artifact_node.bat b/tools/run_tests/artifacts/build_artifact_node.bat
similarity index 83%
rename from tools/run_tests/build_artifact_node.bat
rename to tools/run_tests/artifacts/build_artifact_node.bat
index 57d55ef19ecef11eebf9087734717f2d2f77c7a6..0a2bc4b9d7dbcd399163cd02f74fc8d196ce02d0 100644
--- a/tools/run_tests/build_artifact_node.bat
+++ b/tools/run_tests/artifacts/build_artifact_node.bat
@@ -27,7 +27,9 @@
 @rem (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 @rem OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-set node_versions=0.12.0 1.0.0 1.1.0 2.0.0 3.0.0 4.0.0 5.0.0 6.0.0
+set node_versions=1.1.0 2.0.0 3.0.0 4.0.0 5.0.0 6.0.0 7.0.0
+
+set electron_versions=1.0.0 1.1.0 1.2.0 1.3.0 1.4.0
 
 set PATH=%PATH%;C:\Program Files\nodejs\;%APPDATA%\npm
 
@@ -47,6 +49,12 @@ for %%v in (%node_versions%) do (
 
   xcopy /Y /I /S build\stage\* artifacts\ || goto :error
 )
+
+for %%v in (%electron_versions%) do (
+  cmd /V /C "set "HOME=%HOMEDRIVE%%HOMEPATH%\electron-gyp" && call .\node_modules\.bin\node-pre-gyp.cmd configure rebuild package testpackage --runtime=electron --target=%%v --target_arch=%1 --disturl=https://atom.io/download/electron" || goto :error
+
+  xcopy /Y /I /S build\stage\* artifacts\ || goto :error
+)
 if %errorlevel% neq 0 exit /b %errorlevel%
 
 goto :EOF
diff --git a/tools/run_tests/build_artifact_node.sh b/tools/run_tests/artifacts/build_artifact_node.sh
similarity index 81%
rename from tools/run_tests/build_artifact_node.sh
rename to tools/run_tests/artifacts/build_artifact_node.sh
index 9d06472aa4997f8117bb6991f96ac7c32cfbb146..47b1f339fb8a9db026ff5d2a49cd6435ef744813 100755
--- a/tools/run_tests/build_artifact_node.sh
+++ b/tools/run_tests/artifacts/build_artifact_node.sh
@@ -34,7 +34,7 @@ source ~/.nvm/nvm.sh
 nvm use 4
 set -ex
 
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 
 rm -rf build || true
 
@@ -42,10 +42,18 @@ mkdir -p artifacts
 
 npm update
 
-node_versions=( 0.12.0 1.0.0 1.1.0 2.0.0 3.0.0 4.0.0 5.0.0 6.0.0 )
+node_versions=( 1.1.0 2.0.0 3.0.0 4.0.0 5.0.0 6.0.0 7.0.0 )
+
+electron_versions=( 1.0.0 1.1.0 1.2.0 1.3.0 1.4.0 )
 
 for version in ${node_versions[@]}
 do
   ./node_modules/.bin/node-pre-gyp configure rebuild package testpackage --target=$version --target_arch=$NODE_TARGET_ARCH
   cp -r build/stage/* artifacts/
 done
+
+for version in ${electron_versions[@]}
+do
+  HOME=~/.electron-gyp ./node_modules/.bin/node-pre-gyp configure rebuild package testpackage --runtime=electron --target=$version --target_arch=$NODE_TARGET_ARCH --disturl=https://atom.io/download/electron
+  cp -r build/stage/* artifacts/
+done
diff --git a/tools/run_tests/build_artifact_php.sh b/tools/run_tests/artifacts/build_artifact_php.sh
similarity index 98%
rename from tools/run_tests/build_artifact_php.sh
rename to tools/run_tests/artifacts/build_artifact_php.sh
index 669447fa9a16254598ca7d0a79a3cbc88de55bb0..c8d55860c16ac0ea28beb3cee5ffb3b8f28a2592 100755
--- a/tools/run_tests/build_artifact_php.sh
+++ b/tools/run_tests/artifacts/build_artifact_php.sh
@@ -31,7 +31,7 @@
 PHP_TARGET_ARCH=$1
 set -ex
 
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 
 mkdir -p artifacts
 
diff --git a/tools/run_tests/build_artifact_protoc.bat b/tools/run_tests/artifacts/build_artifact_protoc.bat
similarity index 88%
rename from tools/run_tests/build_artifact_protoc.bat
rename to tools/run_tests/artifacts/build_artifact_protoc.bat
index 3246a903d0bf7e9fe9372bdbfc9775bab5e0b71f..fd93318833a4ca8a328017313eb20bb4afdb88b7 100644
--- a/tools/run_tests/build_artifact_protoc.bat
+++ b/tools/run_tests/artifacts/build_artifact_protoc.bat
@@ -30,15 +30,16 @@
 mkdir artifacts
 
 setlocal
-cd third_party/protobuf
+cd third_party/protobuf/cmake
 
-cd cmake
-cmake -G "%generator%" -Dprotobuf_BUILD_TESTS=OFF || goto :error
+mkdir build & cd build
+mkdir solution & cd solution
+cmake -G "%generator%" -Dprotobuf_BUILD_TESTS=OFF ../../.. || goto :error
 endlocal
 
 call vsprojects/build_plugins.bat || goto :error
 
-xcopy /Y third_party\protobuf\cmake\Release\protoc.exe artifacts\ || goto :error
+xcopy /Y third_party\protobuf\cmake\build\solution\Release\protoc.exe artifacts\ || goto :error
 xcopy /Y vsprojects\Release\*_plugin.exe artifacts\ || xcopy /Y vsprojects\x64\Release\*_plugin.exe artifacts\ || goto :error
 
 goto :EOF
diff --git a/tools/run_tests/build_artifact_protoc.sh b/tools/run_tests/artifacts/build_artifact_protoc.sh
similarity index 98%
rename from tools/run_tests/build_artifact_protoc.sh
rename to tools/run_tests/artifacts/build_artifact_protoc.sh
index 161d3a84d6edd81e8aaea1aac055ca381d81f0b3..26c2280effc7c6affbffda092e2241c453808fcb 100755
--- a/tools/run_tests/build_artifact_protoc.sh
+++ b/tools/run_tests/artifacts/build_artifact_protoc.sh
@@ -33,7 +33,7 @@ source scl_source enable devtoolset-1.1
 
 set -ex
 
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 
 make plugins
 
diff --git a/tools/run_tests/build_artifact_python.bat b/tools/run_tests/artifacts/build_artifact_python.bat
similarity index 100%
rename from tools/run_tests/build_artifact_python.bat
rename to tools/run_tests/artifacts/build_artifact_python.bat
diff --git a/tools/run_tests/build_artifact_python.sh b/tools/run_tests/artifacts/build_artifact_python.sh
similarity index 99%
rename from tools/run_tests/build_artifact_python.sh
rename to tools/run_tests/artifacts/build_artifact_python.sh
index 2a1d41fd686862ab39f355dbcead2bc3ca59024f..5a5506029a8d08c064f31f2f9d8cfbd751ddcb30 100755
--- a/tools/run_tests/build_artifact_python.sh
+++ b/tools/run_tests/artifacts/build_artifact_python.sh
@@ -30,7 +30,7 @@
 
 set -ex
 
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 
 export GRPC_PYTHON_USE_CUSTOM_BDIST=0
 export GRPC_PYTHON_BUILD_WITH_CYTHON=1
diff --git a/tools/run_tests/build_artifact_ruby.sh b/tools/run_tests/artifacts/build_artifact_ruby.sh
similarity index 98%
rename from tools/run_tests/build_artifact_ruby.sh
rename to tools/run_tests/artifacts/build_artifact_ruby.sh
index 2d97b4068bc646340ca2bfa5ab6b24fa368e26dd..019efb01fddb24edd749a3f74746f811a553daaf 100755
--- a/tools/run_tests/build_artifact_ruby.sh
+++ b/tools/run_tests/artifacts/build_artifact_ruby.sh
@@ -31,7 +31,7 @@ set -ex
 
 SYSTEM=`uname | cut -f 1 -d_`
 
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 set +ex
 [[ -s /etc/profile.d/rvm.sh ]] && . /etc/profile.d/rvm.sh
 [[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"
diff --git a/tools/run_tests/build_package_node.sh b/tools/run_tests/artifacts/build_package_node.sh
similarity index 99%
rename from tools/run_tests/build_package_node.sh
rename to tools/run_tests/artifacts/build_package_node.sh
index a5636cf87a7f7c64cade7924cb9b5d6c3ac69311..8b5e8c0bc1be1398ae55fa8a2f0ae33bd0afa511 100755
--- a/tools/run_tests/build_package_node.sh
+++ b/tools/run_tests/artifacts/build_package_node.sh
@@ -33,7 +33,7 @@ source ~/.nvm/nvm.sh
 nvm use 4
 set -ex
 
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 
 base=$(pwd)
 
diff --git a/tools/run_tests/build_package_php.sh b/tools/run_tests/artifacts/build_package_php.sh
similarity index 98%
rename from tools/run_tests/build_package_php.sh
rename to tools/run_tests/artifacts/build_package_php.sh
index 56e3319ed9b9b56da0f3d552fc9609a238d8a72b..42a8d9f8dfa28bba36759b9f74b7fa4bc5573ada 100755
--- a/tools/run_tests/build_package_php.sh
+++ b/tools/run_tests/artifacts/build_package_php.sh
@@ -30,7 +30,7 @@
 
 set -ex
 
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 
 mkdir -p artifacts/
 cp -r $EXTERNAL_GIT_ROOT/architecture={x86,x64},language=php,platform={windows,linux,macos}/artifacts/* artifacts/ || true
diff --git a/tools/run_tests/build_package_python.sh b/tools/run_tests/artifacts/build_package_python.sh
similarity index 98%
rename from tools/run_tests/build_package_python.sh
rename to tools/run_tests/artifacts/build_package_python.sh
index 2511a6ae46598c268fcc1411c3b2b0bbe32a1008..4a1c15ceeeb9a3f3933b472a24dc6d9dda7bdded 100755
--- a/tools/run_tests/build_package_python.sh
+++ b/tools/run_tests/artifacts/build_package_python.sh
@@ -30,7 +30,7 @@
 
 set -ex
 
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 
 mkdir -p artifacts/
 
diff --git a/tools/run_tests/build_package_ruby.sh b/tools/run_tests/artifacts/build_package_ruby.sh
similarity index 99%
rename from tools/run_tests/build_package_ruby.sh
rename to tools/run_tests/artifacts/build_package_ruby.sh
index 0a755bddb0b36626728c02a9c4d7036fdffe8891..b4d20d8a4c06ed6819b3d15627e0cb39a1331c67 100755
--- a/tools/run_tests/build_package_ruby.sh
+++ b/tools/run_tests/artifacts/build_package_ruby.sh
@@ -30,7 +30,7 @@
 
 set -ex
 
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 
 base=$(pwd)
 
diff --git a/tools/run_tests/distribtest_targets.py b/tools/run_tests/artifacts/distribtest_targets.py
similarity index 99%
rename from tools/run_tests/distribtest_targets.py
rename to tools/run_tests/artifacts/distribtest_targets.py
index a16daac4fe9c04e2f56bf547fedc52d5d64231df..a7535b385216bb0f4c98540826dedd38e2ebd4e0 100644
--- a/tools/run_tests/distribtest_targets.py
+++ b/tools/run_tests/artifacts/distribtest_targets.py
@@ -30,7 +30,11 @@
 
 """Definition of targets run distribution package tests."""
 
-import jobset
+import os.path
+import sys
+
+sys.path.insert(0, os.path.abspath('..'))
+import python_utils.jobset as jobset
 
 
 def create_docker_jobspec(name, dockerfile_dir, shell_command, environ={},
diff --git a/tools/run_tests/package_targets.py b/tools/run_tests/artifacts/package_targets.py
similarity index 94%
rename from tools/run_tests/package_targets.py
rename to tools/run_tests/artifacts/package_targets.py
index 673affeac07e606e1a9d6d4532c28e52104c259a..d490f571c37f684574dbbb30b13d903b160c0116 100644
--- a/tools/run_tests/package_targets.py
+++ b/tools/run_tests/artifacts/package_targets.py
@@ -30,7 +30,12 @@
 
 """Definition of targets to build distribution packages."""
 
-import jobset
+import os.path
+import sys
+
+sys.path.insert(0, os.path.abspath('..'))
+import python_utils.jobset as jobset
+
 
 def create_docker_jobspec(name, dockerfile_dir, shell_command, environ={},
                    flake_retries=0, timeout_retries=0):
@@ -114,7 +119,7 @@ class NodePackage:
     return create_docker_jobspec(
         self.name,
         'tools/dockerfile/grpc_artifact_linux_x64',
-        'tools/run_tests/build_package_node.sh')
+        'tools/run_tests/artifacts/build_package_node.sh')
 
 
 class RubyPackage:
@@ -131,7 +136,7 @@ class RubyPackage:
     return create_docker_jobspec(
         self.name,
         'tools/dockerfile/grpc_artifact_linux_x64',
-        'tools/run_tests/build_package_ruby.sh')
+        'tools/run_tests/artifacts/build_package_ruby.sh')
 
 
 class PythonPackage:
@@ -148,7 +153,7 @@ class PythonPackage:
     return create_docker_jobspec(
         self.name,
         'tools/dockerfile/grpc_artifact_linux_x64',
-        'tools/run_tests/build_package_python.sh')
+        'tools/run_tests/artifacts/build_package_python.sh')
 
 
 class PHPPackage:
@@ -165,7 +170,7 @@ class PHPPackage:
     return create_docker_jobspec(
         self.name,
         'tools/dockerfile/grpc_artifact_linux_x64',
-        'tools/run_tests/build_package_php.sh')
+        'tools/run_tests/artifacts/build_package_php.sh')
 
 
 def targets():
diff --git a/tools/run_tests/build_stats_schema.json b/tools/run_tests/build_stats/build_stats_schema.json
similarity index 100%
rename from tools/run_tests/build_stats_schema.json
rename to tools/run_tests/build_stats/build_stats_schema.json
diff --git a/tools/run_tests/build_stats_schema_no_matrix.json b/tools/run_tests/build_stats/build_stats_schema_no_matrix.json
similarity index 100%
rename from tools/run_tests/build_stats_schema_no_matrix.json
rename to tools/run_tests/build_stats/build_stats_schema_no_matrix.json
diff --git a/tools/run_tests/dockerize/build_and_run_docker.sh b/tools/run_tests/dockerize/build_and_run_docker.sh
index 1ef34b2f960692d6e18ce800d35b46371a0e43a8..f52f16ebd683fe781e01d340bf85e046236c9204 100755
--- a/tools/run_tests/dockerize/build_and_run_docker.sh
+++ b/tools/run_tests/dockerize/build_and_run_docker.sh
@@ -41,13 +41,20 @@ cd -
 # DOCKERFILE_DIR - Directory in which Dockerfile file is located.
 # 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.
 # $@ - 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\ )
 
-# Make sure docker image has been built. Should be instantaneous if so.
-docker build -t $DOCKER_IMAGE_NAME $DOCKERFILE_DIR
+if [ "$DOCKERHUB_ORGANIZATION" != "" ]
+then
+  DOCKER_IMAGE_NAME=$DOCKERHUB_ORGANIZATION/$DOCKER_IMAGE_NAME
+  docker pull $DOCKER_IMAGE_NAME
+else
+  # Make sure docker image has been built. Should be instantaneous if so.
+  docker build -t $DOCKER_IMAGE_NAME $DOCKERFILE_DIR
+fi
 
 # Choose random name for docker container
 CONTAINER_NAME="build_and_run_docker_$(uuidgen)"
diff --git a/tools/run_tests/dockerize/build_docker_and_run_tests.sh b/tools/run_tests/dockerize/build_docker_and_run_tests.sh
index c3219c533d5ec834e55020fbda04d8ac675aab48..b68ac89121cb96948ec695f597cae04da860be97 100755
--- a/tools/run_tests/dockerize/build_docker_and_run_tests.sh
+++ b/tools/run_tests/dockerize/build_docker_and_run_tests.sh
@@ -77,8 +77,6 @@ docker run \
   -v /tmp/ccache:/tmp/ccache \
   -v /tmp/npm-cache:/tmp/npm-cache \
   -v /tmp/xdg-cache-home:/tmp/xdg-cache-home \
-  -v /var/run/docker.sock:/var/run/docker.sock \
-  -v $(which docker):/bin/docker \
   -w /var/local/git/grpc \
   --name=$CONTAINER_NAME \
   $DOCKER_IMAGE_NAME \
diff --git a/tools/run_tests/configs.json b/tools/run_tests/generated/configs.json
similarity index 100%
rename from tools/run_tests/configs.json
rename to tools/run_tests/generated/configs.json
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json
similarity index 97%
rename from tools/run_tests/sources_and_headers.json
rename to tools/run_tests/generated/sources_and_headers.json
index abdcfe501b25216060fae21e4dad7499be97067e..65379e0f42504e2144305758673d3332655ce987 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/generated/sources_and_headers.json
@@ -1508,6 +1508,57 @@
     "third_party": false, 
     "type": "target"
   }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "memory_profile_client", 
+    "src": [
+      "test/core/memory_usage/client.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "memory_profile_server", 
+    "src": [
+      "test/core/memory_usage/server.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "memory_profile_test", 
+    "src": [
+      "test/core/memory_usage/memory_usage_test.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
   {
     "deps": [
       "gpr", 
@@ -2263,7 +2314,7 @@
   }, 
   {
     "deps": [
-      "google_benchmark", 
+      "benchmark", 
       "gpr", 
       "gpr_test_util", 
       "grpc", 
@@ -2772,6 +2823,23 @@
     "third_party": false, 
     "type": "target"
   }, 
+  {
+    "deps": [
+      "grpc", 
+      "grpc++", 
+      "grpc++_test_config", 
+      "grpc++_test_util", 
+      "grpc_test_util", 
+      "http2_client_main"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "http2_client", 
+    "src": [], 
+    "third_party": false, 
+    "type": "target"
+  }, 
   {
     "deps": [
       "gpr", 
@@ -2913,7 +2981,7 @@
   }, 
   {
     "deps": [
-      "google_benchmark"
+      "benchmark"
     ], 
     "headers": [], 
     "is_filegroup": false, 
@@ -5388,6 +5456,33 @@
     "third_party": false, 
     "type": "lib"
   }, 
+  {
+    "deps": [
+      "grpc", 
+      "grpc++", 
+      "grpc++_test_config", 
+      "grpc++_test_util", 
+      "grpc_test_util"
+    ], 
+    "headers": [
+      "src/proto/grpc/testing/empty.grpc.pb.h", 
+      "src/proto/grpc/testing/empty.pb.h", 
+      "src/proto/grpc/testing/messages.grpc.pb.h", 
+      "src/proto/grpc/testing/messages.pb.h", 
+      "src/proto/grpc/testing/test.grpc.pb.h", 
+      "src/proto/grpc/testing/test.pb.h", 
+      "test/cpp/interop/http2_client.h"
+    ], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "http2_client_main", 
+    "src": [
+      "test/cpp/interop/http2_client.cc", 
+      "test/cpp/interop/http2_client.h"
+    ], 
+    "third_party": false, 
+    "type": "lib"
+  }, 
   {
     "deps": [
       "gpr", 
@@ -5447,6 +5542,7 @@
       "gpr", 
       "grpc", 
       "grpc++", 
+      "grpc++_test_util", 
       "grpc_test_util"
     ], 
     "headers": [
@@ -6207,30 +6303,30 @@
   {
     "deps": [], 
     "headers": [
-      "third_party/google_benchmark/include/benchmark/benchmark.h", 
-      "third_party/google_benchmark/include/benchmark/benchmark_api.h", 
-      "third_party/google_benchmark/include/benchmark/macros.h", 
-      "third_party/google_benchmark/include/benchmark/reporter.h", 
-      "third_party/google_benchmark/src/arraysize.h", 
-      "third_party/google_benchmark/src/benchmark_api_internal.h", 
-      "third_party/google_benchmark/src/check.h", 
-      "third_party/google_benchmark/src/colorprint.h", 
-      "third_party/google_benchmark/src/commandlineflags.h", 
-      "third_party/google_benchmark/src/complexity.h", 
-      "third_party/google_benchmark/src/cycleclock.h", 
-      "third_party/google_benchmark/src/internal_macros.h", 
-      "third_party/google_benchmark/src/log.h", 
-      "third_party/google_benchmark/src/mutex.h", 
-      "third_party/google_benchmark/src/re.h", 
-      "third_party/google_benchmark/src/sleep.h", 
-      "third_party/google_benchmark/src/stat.h", 
-      "third_party/google_benchmark/src/string_util.h", 
-      "third_party/google_benchmark/src/sysinfo.h", 
-      "third_party/google_benchmark/src/timers.h"
-    ], 
-    "is_filegroup": false, 
-    "language": "c++", 
-    "name": "google_benchmark", 
+      "third_party/benchmark/include/benchmark/benchmark.h", 
+      "third_party/benchmark/include/benchmark/benchmark_api.h", 
+      "third_party/benchmark/include/benchmark/macros.h", 
+      "third_party/benchmark/include/benchmark/reporter.h", 
+      "third_party/benchmark/src/arraysize.h", 
+      "third_party/benchmark/src/benchmark_api_internal.h", 
+      "third_party/benchmark/src/check.h", 
+      "third_party/benchmark/src/colorprint.h", 
+      "third_party/benchmark/src/commandlineflags.h", 
+      "third_party/benchmark/src/complexity.h", 
+      "third_party/benchmark/src/cycleclock.h", 
+      "third_party/benchmark/src/internal_macros.h", 
+      "third_party/benchmark/src/log.h", 
+      "third_party/benchmark/src/mutex.h", 
+      "third_party/benchmark/src/re.h", 
+      "third_party/benchmark/src/sleep.h", 
+      "third_party/benchmark/src/stat.h", 
+      "third_party/benchmark/src/string_util.h", 
+      "third_party/benchmark/src/sysinfo.h", 
+      "third_party/benchmark/src/timers.h"
+    ], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "benchmark", 
     "src": [], 
     "third_party": false, 
     "type": "lib"
@@ -6681,6 +6777,8 @@
       "src/core/lib/channel/context.h", 
       "src/core/lib/channel/deadline_filter.h", 
       "src/core/lib/channel/handshaker.h", 
+      "src/core/lib/channel/handshaker_factory.h", 
+      "src/core/lib/channel/handshaker_registry.h", 
       "src/core/lib/channel/http_client_filter.h", 
       "src/core/lib/channel/http_server_filter.h", 
       "src/core/lib/channel/message_size_filter.h", 
@@ -6747,6 +6845,7 @@
       "src/core/lib/json/json_reader.h", 
       "src/core/lib/json/json_writer.h", 
       "src/core/lib/slice/percent_encoding.h", 
+      "src/core/lib/slice/slice_internal.h", 
       "src/core/lib/slice/slice_string_helpers.h", 
       "src/core/lib/surface/api_trace.h", 
       "src/core/lib/surface/call.h", 
@@ -6799,6 +6898,10 @@
       "src/core/lib/channel/deadline_filter.h", 
       "src/core/lib/channel/handshaker.c", 
       "src/core/lib/channel/handshaker.h", 
+      "src/core/lib/channel/handshaker_factory.c", 
+      "src/core/lib/channel/handshaker_factory.h", 
+      "src/core/lib/channel/handshaker_registry.c", 
+      "src/core/lib/channel/handshaker_registry.h", 
       "src/core/lib/channel/http_client_filter.c", 
       "src/core/lib/channel/http_client_filter.h", 
       "src/core/lib/channel/http_server_filter.c", 
@@ -6940,6 +7043,7 @@
       "src/core/lib/slice/percent_encoding.h", 
       "src/core/lib/slice/slice.c", 
       "src/core/lib/slice/slice_buffer.c", 
+      "src/core/lib/slice/slice_internal.h", 
       "src/core/lib/slice/slice_string_helpers.c", 
       "src/core/lib/slice/slice_string_helpers.h", 
       "src/core/lib/surface/alarm.c", 
@@ -7068,6 +7172,7 @@
       "include/grpc/impl/codegen/byte_buffer_reader.h", 
       "include/grpc/impl/codegen/compression_types.h", 
       "include/grpc/impl/codegen/connectivity_state.h", 
+      "include/grpc/impl/codegen/exec_ctx_fwd.h", 
       "include/grpc/impl/codegen/grpc_types.h", 
       "include/grpc/impl/codegen/propagation_bits.h", 
       "include/grpc/impl/codegen/status.h"
@@ -7079,6 +7184,7 @@
       "include/grpc/impl/codegen/byte_buffer_reader.h", 
       "include/grpc/impl/codegen/compression_types.h", 
       "include/grpc/impl/codegen/connectivity_state.h", 
+      "include/grpc/impl/codegen/exec_ctx_fwd.h", 
       "include/grpc/impl/codegen/grpc_types.h", 
       "include/grpc/impl/codegen/propagation_bits.h", 
       "include/grpc/impl/codegen/status.h"
@@ -7219,9 +7325,9 @@
       "src/core/lib/security/credentials/plugin/plugin_credentials.h", 
       "src/core/lib/security/credentials/ssl/ssl_credentials.h", 
       "src/core/lib/security/transport/auth_filters.h", 
-      "src/core/lib/security/transport/handshake.h", 
       "src/core/lib/security/transport/secure_endpoint.h", 
       "src/core/lib/security/transport/security_connector.h", 
+      "src/core/lib/security/transport/security_handshaker.h", 
       "src/core/lib/security/transport/tsi_error.h", 
       "src/core/lib/security/util/b64.h", 
       "src/core/lib/security/util/json_util.h"
@@ -7260,12 +7366,12 @@
       "src/core/lib/security/credentials/ssl/ssl_credentials.h", 
       "src/core/lib/security/transport/auth_filters.h", 
       "src/core/lib/security/transport/client_auth_filter.c", 
-      "src/core/lib/security/transport/handshake.c", 
-      "src/core/lib/security/transport/handshake.h", 
       "src/core/lib/security/transport/secure_endpoint.c", 
       "src/core/lib/security/transport/secure_endpoint.h", 
       "src/core/lib/security/transport/security_connector.c", 
       "src/core/lib/security/transport/security_connector.h", 
+      "src/core/lib/security/transport/security_handshaker.c", 
+      "src/core/lib/security/transport/security_handshaker.h", 
       "src/core/lib/security/transport/server_auth_filter.c", 
       "src/core/lib/security/transport/tsi_error.c", 
       "src/core/lib/security/transport/tsi_error.h", 
@@ -7432,9 +7538,29 @@
     "deps": [
       "gpr", 
       "grpc_base", 
-      "grpc_client_channel", 
       "grpc_transport_chttp2"
     ], 
+    "headers": [
+      "src/core/ext/transport/chttp2/client/chttp2_connector.h"
+    ], 
+    "is_filegroup": true, 
+    "language": "c", 
+    "name": "grpc_transport_chttp2_client_connector", 
+    "src": [
+      "src/core/ext/transport/chttp2/client/chttp2_connector.c", 
+      "src/core/ext/transport/chttp2/client/chttp2_connector.h"
+    ], 
+    "third_party": false, 
+    "type": "filegroup"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "grpc_base", 
+      "grpc_client_channel", 
+      "grpc_transport_chttp2", 
+      "grpc_transport_chttp2_client_connector"
+    ], 
     "headers": [], 
     "is_filegroup": true, 
     "language": "c", 
@@ -7452,7 +7578,8 @@
       "grpc_base", 
       "grpc_client_channel", 
       "grpc_secure", 
-      "grpc_transport_chttp2"
+      "grpc_transport_chttp2", 
+      "grpc_transport_chttp2_client_connector"
     ], 
     "headers": [], 
     "is_filegroup": true, 
@@ -7470,6 +7597,26 @@
       "grpc_base", 
       "grpc_transport_chttp2"
     ], 
+    "headers": [
+      "src/core/ext/transport/chttp2/server/chttp2_server.h"
+    ], 
+    "is_filegroup": true, 
+    "language": "c", 
+    "name": "grpc_transport_chttp2_server", 
+    "src": [
+      "src/core/ext/transport/chttp2/server/chttp2_server.c", 
+      "src/core/ext/transport/chttp2/server/chttp2_server.h"
+    ], 
+    "third_party": false, 
+    "type": "filegroup"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "grpc_base", 
+      "grpc_transport_chttp2", 
+      "grpc_transport_chttp2_server"
+    ], 
     "headers": [], 
     "is_filegroup": true, 
     "language": "c", 
@@ -7486,7 +7633,8 @@
       "gpr", 
       "grpc_base", 
       "grpc_secure", 
-      "grpc_transport_chttp2"
+      "grpc_transport_chttp2", 
+      "grpc_transport_chttp2_server"
     ], 
     "headers": [], 
     "is_filegroup": true, 
diff --git a/tools/run_tests/tests.json b/tools/run_tests/generated/tests.json
similarity index 99%
rename from tools/run_tests/tests.json
rename to tools/run_tests/generated/tests.json
index c4bfd0a9a7467611d7dc816e02aa9bc8602ec7fa..611ddf9fd1608c7cf57fcb83f4bc0a302a20d110 100644
--- a/tools/run_tests/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -1565,6 +1565,26 @@
       "windows"
     ]
   }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.5, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c", 
+    "name": "memory_profile_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [], 
     "ci_platforms": [
@@ -36765,7 +36785,8 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -36788,7 +36809,8 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -36811,7 +36833,8 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -36834,7 +36857,8 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -36857,7 +36881,8 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -36880,7 +36905,8 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -36903,7 +36929,8 @@
     "cpu_cost": 1024, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -36926,7 +36953,8 @@
     "cpu_cost": 1024, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -36949,7 +36977,8 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -36972,7 +37001,8 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -36995,7 +37025,8 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37018,7 +37049,8 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37041,7 +37073,8 @@
     "cpu_cost": 1024, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37064,7 +37097,8 @@
     "cpu_cost": 1024, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37087,7 +37121,8 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37110,7 +37145,8 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37133,7 +37169,8 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37156,7 +37193,8 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37179,7 +37217,8 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37202,7 +37241,8 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37225,7 +37265,8 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37248,7 +37289,8 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37271,7 +37313,8 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37294,7 +37337,8 @@
     "cpu_cost": 1024, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37317,7 +37361,8 @@
     "cpu_cost": 1024, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37340,7 +37385,8 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37363,7 +37409,8 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37386,7 +37433,8 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37409,7 +37457,8 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37432,7 +37481,8 @@
     "cpu_cost": 1024, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37455,7 +37505,8 @@
     "cpu_cost": 1024, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37478,7 +37529,8 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37501,7 +37553,8 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37524,7 +37577,8 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "tsan"
+      "tsan", 
+      "asan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -37547,7 +37601,6 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -37570,7 +37623,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_generic_async_streaming_ping_pong_secure", 
+    "shortname": "json_run_localhost:cpp_generic_async_streaming_ping_pong_secure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -37585,7 +37638,6 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -37608,7 +37660,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_generic_async_streaming_qps_unconstrained_secure", 
+    "shortname": "json_run_localhost:cpp_generic_async_streaming_qps_unconstrained_secure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -37623,7 +37675,6 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -37646,7 +37697,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_generic_async_streaming_qps_one_server_core_secure", 
+    "shortname": "json_run_localhost:cpp_generic_async_streaming_qps_one_server_core_secure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -37661,7 +37712,6 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -37684,7 +37734,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_client_sync_server_unary_qps_unconstrained_secure", 
+    "shortname": "json_run_localhost:cpp_protobuf_async_client_sync_server_unary_qps_unconstrained_secure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -37699,7 +37749,6 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -37722,7 +37771,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_client_sync_server_streaming_qps_unconstrained_secure", 
+    "shortname": "json_run_localhost:cpp_protobuf_async_client_sync_server_streaming_qps_unconstrained_secure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -37737,7 +37786,6 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -37760,7 +37808,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_sync_unary_ping_pong_secure", 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_unary_ping_pong_secure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -37775,7 +37823,6 @@
     "cpu_cost": 64, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -37798,7 +37845,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_sync_unary_qps_unconstrained_secure", 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_unary_qps_unconstrained_secure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -37813,7 +37860,6 @@
     "cpu_cost": 64, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -37836,7 +37882,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_sync_unary_qps_unconstrained_secure_500kib_resource_quota", 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_unary_qps_unconstrained_secure_500kib_resource_quota_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -37851,7 +37897,6 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -37874,7 +37919,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_unary_ping_pong_secure", 
+    "shortname": "json_run_localhost:cpp_protobuf_async_unary_ping_pong_secure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -37889,7 +37934,6 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -37912,7 +37956,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_unary_qps_unconstrained_secure", 
+    "shortname": "json_run_localhost:cpp_protobuf_async_unary_qps_unconstrained_secure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -37927,7 +37971,6 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -37950,7 +37993,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_unary_qps_unconstrained_secure_500kib_resource_quota", 
+    "shortname": "json_run_localhost:cpp_protobuf_async_unary_qps_unconstrained_secure_500kib_resource_quota_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -37965,7 +38008,6 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -37988,7 +38030,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_ping_pong_secure", 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_ping_pong_secure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38003,7 +38045,6 @@
     "cpu_cost": 64, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38026,7 +38067,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_qps_unconstrained_secure", 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_qps_unconstrained_secure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38041,7 +38082,6 @@
     "cpu_cost": 64, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38064,7 +38104,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_qps_unconstrained_secure_500kib_resource_quota", 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_qps_unconstrained_secure_500kib_resource_quota_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38079,7 +38119,6 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38102,7 +38141,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_ping_pong_secure", 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_ping_pong_secure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38117,7 +38156,6 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38140,7 +38178,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_qps_unconstrained_secure", 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_qps_unconstrained_secure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38155,7 +38193,6 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38178,7 +38215,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_qps_unconstrained_secure_500kib_resource_quota", 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_qps_unconstrained_secure_500kib_resource_quota_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38193,7 +38230,6 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38216,7 +38252,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_generic_async_streaming_ping_pong_insecure", 
+    "shortname": "json_run_localhost:cpp_generic_async_streaming_ping_pong_insecure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38231,7 +38267,6 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38254,7 +38289,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_generic_async_streaming_qps_unconstrained_insecure", 
+    "shortname": "json_run_localhost:cpp_generic_async_streaming_qps_unconstrained_insecure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38269,7 +38304,6 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38292,7 +38326,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_generic_async_streaming_qps_one_server_core_insecure", 
+    "shortname": "json_run_localhost:cpp_generic_async_streaming_qps_one_server_core_insecure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38307,7 +38341,6 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38330,7 +38363,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_client_sync_server_unary_qps_unconstrained_insecure", 
+    "shortname": "json_run_localhost:cpp_protobuf_async_client_sync_server_unary_qps_unconstrained_insecure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38345,7 +38378,6 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38368,7 +38400,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_client_sync_server_streaming_qps_unconstrained_insecure", 
+    "shortname": "json_run_localhost:cpp_protobuf_async_client_sync_server_streaming_qps_unconstrained_insecure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38383,7 +38415,6 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38406,7 +38437,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_sync_unary_ping_pong_insecure", 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_unary_ping_pong_insecure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38421,7 +38452,6 @@
     "cpu_cost": 64, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38444,7 +38474,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_sync_unary_qps_unconstrained_insecure", 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_unary_qps_unconstrained_insecure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38459,7 +38489,6 @@
     "cpu_cost": 64, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38482,7 +38511,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_sync_unary_qps_unconstrained_insecure_500kib_resource_quota", 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_unary_qps_unconstrained_insecure_500kib_resource_quota_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38497,7 +38526,6 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38520,7 +38548,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_unary_ping_pong_insecure", 
+    "shortname": "json_run_localhost:cpp_protobuf_async_unary_ping_pong_insecure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38535,7 +38563,6 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38558,7 +38585,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_unary_qps_unconstrained_insecure", 
+    "shortname": "json_run_localhost:cpp_protobuf_async_unary_qps_unconstrained_insecure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38573,7 +38600,6 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38596,7 +38622,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_unary_qps_unconstrained_insecure_500kib_resource_quota", 
+    "shortname": "json_run_localhost:cpp_protobuf_async_unary_qps_unconstrained_insecure_500kib_resource_quota_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38611,7 +38637,6 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38634,7 +38659,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_ping_pong_insecure", 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_ping_pong_insecure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38649,7 +38674,6 @@
     "cpu_cost": 64, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38672,7 +38696,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_qps_unconstrained_insecure", 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_qps_unconstrained_insecure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38687,7 +38711,6 @@
     "cpu_cost": 64, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38710,7 +38733,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_qps_unconstrained_insecure_500kib_resource_quota", 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_qps_unconstrained_insecure_500kib_resource_quota_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38725,7 +38748,6 @@
     "cpu_cost": 2, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38748,7 +38770,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_ping_pong_insecure", 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_ping_pong_insecure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38763,7 +38785,6 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38786,7 +38807,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_qps_unconstrained_insecure", 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_qps_unconstrained_insecure_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -38801,7 +38822,6 @@
     "cpu_cost": "capacity", 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan", 
       "asan-noleaks", 
       "asan-trace-cmp", 
       "basicprof", 
@@ -38824,7 +38844,7 @@
     "platforms": [
       "linux"
     ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_qps_unconstrained_insecure_500kib_resource_quota", 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_qps_unconstrained_insecure_500kib_resource_quota_low_thread_count", 
     "timeout_seconds": 360
   }, 
   {
@@ -40169,6 +40189,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/08455b3ef9d516deb8155d8db7d51c43ce0ff07a"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/085865a209776911782f592c9f30ffe0ad3814a0"
@@ -40895,6 +40937,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/0d19e60f4eb1b729e272dd5dec68e8b67f03a5f4"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/0d604693a9d3e76f54d28a26142abd729b0a9acd"
@@ -41907,6 +41971,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/14064ac4844c709b247a4345a92d8be9766785c4"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/143789594154049441d565b65ce725fc4f8c12bc"
@@ -42677,6 +42763,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/1901234bd7c9cb6ee19ff8b17179c481bdc7c5f2"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/190c4ca0cf29c99bc987d2792c7f62e1007c0245"
@@ -43777,6 +43885,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/20ff1f8d5d34cb01d58aa334dc46a6644e6e1d12"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/21357c3613a47180eb668b1c6c849ce9096a46eb"
@@ -44043,7 +44173,7 @@
   }, 
   {
     "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/23982956d17d2f55e61a5d9111b1c0c7ee530214"
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/2378c3f1206f20711468391ce739116ffe58374b"
     ], 
     "ci_platforms": [
       "linux"
@@ -44065,7 +44195,7 @@
   }, 
   {
     "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/239b03041efe9bf24982b0ecbb66d5abea4a28d0"
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/23982956d17d2f55e61a5d9111b1c0c7ee530214"
     ], 
     "ci_platforms": [
       "linux"
@@ -44087,7 +44217,7 @@
   }, 
   {
     "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/240afe42d3e2834c46a79d9df0dd6ca018831398"
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/239b03041efe9bf24982b0ecbb66d5abea4a28d0"
     ], 
     "ci_platforms": [
       "linux"
@@ -44109,7 +44239,7 @@
   }, 
   {
     "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/247d0d09deeeb76422cd1d06305a63378a498656"
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/240afe42d3e2834c46a79d9df0dd6ca018831398"
     ], 
     "ci_platforms": [
       "linux"
@@ -44131,7 +44261,7 @@
   }, 
   {
     "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/24a87af0954c808fbd3f2c55185d4b1fa9459f4e"
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/247d0d09deeeb76422cd1d06305a63378a498656"
     ], 
     "ci_platforms": [
       "linux"
@@ -44153,7 +44283,7 @@
   }, 
   {
     "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/24d630240f673498d784cecb66d73e9b1d981493"
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/24a87af0954c808fbd3f2c55185d4b1fa9459f4e"
     ], 
     "ci_platforms": [
       "linux"
@@ -44175,7 +44305,7 @@
   }, 
   {
     "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/24df70902c288fcac060365c2e6f61269a3606b4"
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/24d630240f673498d784cecb66d73e9b1d981493"
     ], 
     "ci_platforms": [
       "linux"
@@ -44197,7 +44327,7 @@
   }, 
   {
     "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/24fbdfa73f26686633871ddad9698d7059db488f"
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/24df70902c288fcac060365c2e6f61269a3606b4"
     ], 
     "ci_platforms": [
       "linux"
@@ -44219,7 +44349,7 @@
   }, 
   {
     "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/2500fc12d5d1b5ed99fc3fe518c28849d1c8d6e8"
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/24fbdfa73f26686633871ddad9698d7059db488f"
     ], 
     "ci_platforms": [
       "linux"
@@ -44241,7 +44371,7 @@
   }, 
   {
     "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/2501c7c3f78829725e6bf556277785588318106b"
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/2500fc12d5d1b5ed99fc3fe518c28849d1c8d6e8"
     ], 
     "ci_platforms": [
       "linux"
@@ -44263,7 +44393,7 @@
   }, 
   {
     "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/2507810915aecd3adf1287edf8c9f54b23a8ebd5"
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/2501c7c3f78829725e6bf556277785588318106b"
     ], 
     "ci_platforms": [
       "linux"
@@ -44285,7 +44415,7 @@
   }, 
   {
     "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/2533c4ed207eed715756142667128f6eb01309e0"
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/2507810915aecd3adf1287edf8c9f54b23a8ebd5"
     ], 
     "ci_platforms": [
       "linux"
@@ -44307,7 +44437,7 @@
   }, 
   {
     "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/2535940afe69b3106b7696a486a2617d0d9a7150"
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/2533c4ed207eed715756142667128f6eb01309e0"
     ], 
     "ci_platforms": [
       "linux"
@@ -44329,7 +44459,7 @@
   }, 
   {
     "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/253b8946a7cf403dd466f1685df2f741d4660a34"
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/2535940afe69b3106b7696a486a2617d0d9a7150"
     ], 
     "ci_platforms": [
       "linux"
@@ -44351,7 +44481,7 @@
   }, 
   {
     "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/255ab618b474a659914f5167f6e89ae3f1376788"
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/253b8946a7cf403dd466f1685df2f741d4660a34"
     ], 
     "ci_platforms": [
       "linux"
@@ -44373,7 +44503,29 @@
   }, 
   {
     "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/2577397157d02cde4544e70fd6c3ff68704dd13b"
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/255ab618b474a659914f5167f6e89ae3f1376788"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/2577397157d02cde4544e70fd6c3ff68704dd13b"
     ], 
     "ci_platforms": [
       "linux"
@@ -48639,6 +48791,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/40640a91fda4e4e42d3063a28b9ffbba1b8c3701"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/40b4b92460c4e76a39af9042fb3d86d491a98e16"
@@ -50157,6 +50331,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/49f564289c79de9e0342f8b0821a167bc8c5ec00"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/49ff30e0f070fe37b642dd0d361c5cbca139f223"
@@ -50971,6 +51167,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/507865c4a5ce880b80400d93fa85def2682581cb"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/507b8ecbb9fd3eea9084087bce22a94cca8a7c41"
@@ -53303,6 +53521,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/5f2fdb01d8ff632803ca2b732a7c088c6843d7d3"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/5f52309deaa1b641fe199889d18f921d6909fc14"
@@ -53677,6 +53917,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/61de97a9d6c4b082602c02277d8d763921f5f95b"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/61f410c711bc5d53be9e932217ebd035f2716417"
@@ -53787,6 +54049,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/62b039b8a318cc08471f13629da08c68c414d8e7"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/62c995646f15be1819bd13e32a60af46297d73b4"
@@ -53985,6 +54269,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/641739453f7d4d3b55a1c7b79bed7da6dfd62ae0"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/6421db654fff309bc191aba0330fbcd1347655e3"
@@ -54227,6 +54533,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/6589505362ffb5164a3c7cb1b9feadcddfba44e9"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/65afd7305e481da5ffc44a6a66eb3117744ae77d"
@@ -56141,6 +56469,50 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/724f5400f19e5a0be97022341c39eeaaaffeb390"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/7254b9ff59ab3fcf345effdabbc25ebd2e907b23"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/727f43500183aec9c0d9be7d2363fa1761cda5d5"
@@ -57133,29 +57505,7 @@
   }, 
   {
     "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/788f18727a0aeb5e200527bca7c889c9954be343"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [
-      "tsan"
-    ], 
-    "exclude_iomgrs": [
-      "uv"
-    ], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "api_fuzzer_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/78c3bbeaeb266aac1df0d4abe78bbca68fb085a8"
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/784d6f5c093ab5360670173ce001e1a446f95822"
     ], 
     "ci_platforms": [
       "linux"
@@ -57177,7 +57527,51 @@
   }, 
   {
     "args": [
-      "test/core/end2end/fuzzers/api_fuzzer_corpus/78d8b2a1732c4528d6acdb21c236f417a0f85798"
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/788f18727a0aeb5e200527bca7c889c9954be343"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/78c3bbeaeb266aac1df0d4abe78bbca68fb085a8"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/78d8b2a1732c4528d6acdb21c236f417a0f85798"
     ], 
     "ci_platforms": [
       "linux"
@@ -57219,6 +57613,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/79328fdc89d0af0e38da089dab062fd3ea5aae59"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/7957953ca449974ec39c6a137c0acdedb71c3b02"
@@ -57417,6 +57833,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/7a2569f4daf4480ad142cb4ee7c675bed82db74c"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/7a5a769942efac79863bb154cf1e7574e6d98e22"
@@ -59397,6 +59835,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/87a300cd25d2e57745bd00499d4d2352a10a2fa1"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/87add83a18a25fe585df8adc124eae6d70733f74"
@@ -59793,6 +60253,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/8a1c629910280f8beebb88687696de98da988ecc"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/8a4183e6bb75036228a42039d678fca0ea6751b7"
@@ -60651,6 +61133,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/8ff7b1568d2da2e4196c2e28617e1911d62021e6"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/901c9a33205897999e7e78063ccdc5d363267568"
@@ -61597,6 +62101,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/982f375b183d984958667461c7767206062eb4cb"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/984b6ee241b92be62923c6dc5bacaadb36183b89"
@@ -61663,6 +62189,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/98739af631223fa05ad88f1e63a52052a9bb4b1c"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/988bd333d5dabe1561cf4429e7481ff110be0da4"
@@ -63797,6 +64345,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/a78bca1ef8829d720dd5dedac3a9d4d12684da34"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/a7ccc1f7db49512983fe4d42c16b2160357e3585"
@@ -64523,6 +65093,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/ac763d89466ebfad676e75be0076831c03fe2a5d"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/ac7b2971ff39a368145148524511dd68df83d522"
@@ -64985,6 +65577,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/b165523f699e6ae9b2ad15873b9d56465d1af546"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/b1e28018e26e6baaba5a907e5e6ff9b7a7942018"
@@ -65095,6 +65709,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/b2a5ec3ef40c68d594638218e3c943a479f82215"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/b2af0db70de3a6ddcb188d1f6f2a673133a8f9c7"
@@ -66239,6 +66875,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/b92b1c5e0dba009a9516e7185f5df019c62c5cc9"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/b9318513eb6b1db553855cd062ebbd4d1db9b846"
@@ -68373,6 +69031,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/c5cd7af16e7bc0049445d5a0b92a3d4b7e5e3533"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/c5dbc50d9174bde5542b2bb18c63f6583a23ff13"
@@ -71871,6 +72551,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/crash-da39a3ee5e6b4b0d3255bfef95601890afd80709"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/crash-dc6abf90d5e8e1b96f7e25f418b1a7f572e6a738"
@@ -75039,6 +75741,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/e28ffd8c2816f12f6395805199c792d1718191df"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/e29bab478641dd412057dfb6b0a0d78afd96dd60"
@@ -76513,6 +77237,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/ec823f4018389e64a99f6580277fba28df6bd136"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/ec89eb7e84e6cf7859ab478362e0ae5227a5e154"
@@ -76843,6 +77589,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/ee2c1ac1e668f22836cf25a59495e778b0e2c7a8"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/ee624b408f8a50c79cdaebf4fb4195e6162b70da"
@@ -76865,6 +77633,50 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/ee6855178435d2046d8763ecae46e1e0a71a95f4"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/eeb310d91038cb02862e187e68c5d6578233485b"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/eed65ac63a044c87423f333f3b9c5f0d3bc7bd3b"
@@ -77129,6 +77941,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/f07bc2907c95f2aeb79ca60e2ad826e13b848f45"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/f0a7e39c194ee3f30312ae2f4827bdbd43416a42"
@@ -78779,6 +79613,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/fb655905396b768cf3ff015afdb0b564dae2cdfd"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/fb76689d3c70bd5927b3256eda9738a2208e2b13"
@@ -79197,6 +80053,28 @@
     ], 
     "uses_polling": false
   }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/fdb038087233cd176746558875932029f779221d"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
   {
     "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/fdecd05733278ece9993eb2bef13917675cc062c"
diff --git a/tools/run_tests/build_csharp.sh b/tools/run_tests/helper_scripts/build_csharp.sh
similarity index 97%
rename from tools/run_tests/build_csharp.sh
rename to tools/run_tests/helper_scripts/build_csharp.sh
index 48ce11a10b7b6ed619eef4d1fa18697e70d7bb7b..84c5b1c77786969fc9cf930613c6a35ffcda2eb2 100755
--- a/tools/run_tests/build_csharp.sh
+++ b/tools/run_tests/helper_scripts/build_csharp.sh
@@ -30,7 +30,7 @@
 
 set -ex
 
-cd $(dirname $0)/../../src/csharp
+cd $(dirname $0)/../../../src/csharp
 
 # overriding NativeDependenciesConfigurationUnix is needed to make gcov code coverage work.
 xbuild /p:Configuration=$MSBUILD_CONFIG /p:NativeDependenciesConfigurationUnix=$CONFIG Grpc.sln
diff --git a/tools/run_tests/build_csharp_coreclr.bat b/tools/run_tests/helper_scripts/build_csharp_coreclr.bat
similarity index 98%
rename from tools/run_tests/build_csharp_coreclr.bat
rename to tools/run_tests/helper_scripts/build_csharp_coreclr.bat
index b6e3ccbd2b88d3cbb023de50aee2da84aba3f837..78e5f5998b810db19483d003618e2392bdf445c5 100644
--- a/tools/run_tests/build_csharp_coreclr.bat
+++ b/tools/run_tests/helper_scripts/build_csharp_coreclr.bat
@@ -29,7 +29,7 @@
 
 setlocal
 
-cd /d %~dp0\..\..\src\csharp
+cd /d %~dp0\..\..\..\src\csharp
 
 dotnet restore . || goto :error
 
diff --git a/tools/run_tests/build_csharp_coreclr.sh b/tools/run_tests/helper_scripts/build_csharp_coreclr.sh
similarity index 97%
rename from tools/run_tests/build_csharp_coreclr.sh
rename to tools/run_tests/helper_scripts/build_csharp_coreclr.sh
index 02cf0d39cb53835d85e051731ff66896bac3005a..dd5fd31c759cd3317b16385b3e6190cf78f3e5e4 100755
--- a/tools/run_tests/build_csharp_coreclr.sh
+++ b/tools/run_tests/helper_scripts/build_csharp_coreclr.sh
@@ -30,7 +30,7 @@
 
 set -ex
 
-cd $(dirname $0)/../../src/csharp
+cd $(dirname $0)/../../../src/csharp
 
 # TODO(jtattermusch): introduce caching
 dotnet restore .
diff --git a/tools/run_tests/build_node.bat b/tools/run_tests/helper_scripts/build_node.bat
similarity index 100%
rename from tools/run_tests/build_node.bat
rename to tools/run_tests/helper_scripts/build_node.bat
diff --git a/tools/run_tests/build_node.sh b/tools/run_tests/helper_scripts/build_node.sh
similarity index 98%
rename from tools/run_tests/build_node.sh
rename to tools/run_tests/helper_scripts/build_node.sh
index d9292fd8aa2d61407e5a3e6ab47bbcfe34198447..8a928bb762cc3d07302d1d03df27a39c696f81b4 100755
--- a/tools/run_tests/build_node.sh
+++ b/tools/run_tests/helper_scripts/build_node.sh
@@ -38,6 +38,6 @@ set -ex
 CONFIG=${CONFIG:-opt}
 
 # change to grpc repo root
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 
 npm install --unsafe-perm --build-from-source
diff --git a/tools/run_tests/helper_scripts/build_node_electron.sh b/tools/run_tests/helper_scripts/build_node_electron.sh
new file mode 100755
index 0000000000000000000000000000000000000000..1c95c513b329c8dd384b5a82ff06d54fc0e746ad
--- /dev/null
+++ b/tools/run_tests/helper_scripts/build_node_electron.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ELECTRON_VERSION=$1
+source ~/.nvm/nvm.sh
+
+nvm use 6
+set -ex
+
+# change to grpc repo root
+cd $(dirname $0)/../..
+
+export npm_config_target=$ELECTRON_VERSION
+export npm_config_disturl=https://atom.io/download/atom-shell
+export npm_config_runtime=electron
+export npm_config_build_from_source=true
+mkdir -p ~/.electron-gyp
+HOME=~/.electron-gyp npm install --unsafe-perm
diff --git a/tools/run_tests/build_php.sh b/tools/run_tests/helper_scripts/build_php.sh
similarity index 98%
rename from tools/run_tests/build_php.sh
rename to tools/run_tests/helper_scripts/build_php.sh
index 77a8abcfe70fac742674860346f34757b3de6a00..acaaa23adf4a94de46870bd56522d3392b35fcea 100755
--- a/tools/run_tests/build_php.sh
+++ b/tools/run_tests/helper_scripts/build_php.sh
@@ -33,7 +33,7 @@ set -ex
 CONFIG=${CONFIG:-opt}
 
 # change to grpc repo root
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 
 root=`pwd`
 export GRPC_LIB_SUBDIR=libs/$CONFIG
diff --git a/tools/run_tests/build_python.sh b/tools/run_tests/helper_scripts/build_python.sh
similarity index 97%
rename from tools/run_tests/build_python.sh
rename to tools/run_tests/helper_scripts/build_python.sh
index fb884ad1668c0e942ae9d3e06d7fff0b1059f166..0e88e9676585336b7ee0fcff954427535d6a00ce 100755
--- a/tools/run_tests/build_python.sh
+++ b/tools/run_tests/helper_scripts/build_python.sh
@@ -31,7 +31,7 @@
 set -ex
 
 # change to grpc repo root
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 
 ##########################
 # Portability operations #
@@ -171,8 +171,7 @@ pip_install_dir() {
 }
 
 $VENV_PYTHON -m pip install --upgrade pip
-# TODO(https://github.com/pypa/setuptools/issues/709) get the latest setuptools
-$VENV_PYTHON -m pip install setuptools==25.1.1
+$VENV_PYTHON -m pip install setuptools
 $VENV_PYTHON -m pip install cython
 pip_install_dir $ROOT
 $VENV_PYTHON $ROOT/tools/distrib/python/make_grpcio_tools.py
diff --git a/tools/run_tests/build_python_msys2.sh b/tools/run_tests/helper_scripts/build_python_msys2.sh
similarity index 100%
rename from tools/run_tests/build_python_msys2.sh
rename to tools/run_tests/helper_scripts/build_python_msys2.sh
diff --git a/tools/run_tests/build_ruby.sh b/tools/run_tests/helper_scripts/build_ruby.sh
similarity index 98%
rename from tools/run_tests/build_ruby.sh
rename to tools/run_tests/helper_scripts/build_ruby.sh
index 10343fce6963f9223cf683c89c82f16349fa14ee..32638dede9677ddfd3435d2a600d85c5df36e653 100755
--- a/tools/run_tests/build_ruby.sh
+++ b/tools/run_tests/helper_scripts/build_ruby.sh
@@ -34,7 +34,7 @@ set -ex
 export GRPC_CONFIG=${CONFIG:-opt}
 
 # change to grpc's ruby directory
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 
 rm -rf ./tmp
 rake compile
diff --git a/tools/run_tests/post_tests_c.sh b/tools/run_tests/helper_scripts/post_tests_c.sh
similarity index 97%
rename from tools/run_tests/post_tests_c.sh
rename to tools/run_tests/helper_scripts/post_tests_c.sh
index 4409526dab256ef620af474e842d007077dad979..a83a59e23b7c59af4d899031f323e42593ece2d5 100755
--- a/tools/run_tests/post_tests_c.sh
+++ b/tools/run_tests/helper_scripts/post_tests_c.sh
@@ -32,7 +32,7 @@ set -ex
 
 if [ "$CONFIG" != "gcov" ] ; then exit ; fi
 
-root=$(readlink -f $(dirname $0)/../..)
+root=$(readlink -f $(dirname $0)/../../..)
 out=$root/reports/c_cxx_coverage
 tmp1=$(mktemp)
 tmp2=$(mktemp)
diff --git a/tools/run_tests/post_tests_csharp.bat b/tools/run_tests/helper_scripts/post_tests_csharp.bat
similarity index 98%
rename from tools/run_tests/post_tests_csharp.bat
rename to tools/run_tests/helper_scripts/post_tests_csharp.bat
index 0d49a00b2aa37bb2804edfa25d8230a94a26b283..2359f148ce835e77d1dee5cf1a0e5e179afed35d 100644
--- a/tools/run_tests/post_tests_csharp.bat
+++ b/tools/run_tests/helper_scripts/post_tests_csharp.bat
@@ -36,7 +36,7 @@ if not "%CONFIG%" == "gcov" (
 )
 
 @rem enter src/csharp directory
-cd /d %~dp0\..\..\src\csharp
+cd /d %~dp0\..\..\..\src\csharp
 
 @rem Generate code coverage report
 @rem TODO(jtattermusch): currently the report list is hardcoded
diff --git a/tools/run_tests/post_tests_csharp.sh b/tools/run_tests/helper_scripts/post_tests_csharp.sh
similarity index 98%
rename from tools/run_tests/post_tests_csharp.sh
rename to tools/run_tests/helper_scripts/post_tests_csharp.sh
index bb6f5c6e18822194561920fc24b02da2653f3956..762c1f882711f04b31a9043e39c0d995a766c69c 100755
--- a/tools/run_tests/post_tests_csharp.sh
+++ b/tools/run_tests/helper_scripts/post_tests_csharp.sh
@@ -33,7 +33,7 @@ set -ex
 if [ "$CONFIG" != "gcov" ] ; then exit ; fi
 
 # change to gRPC repo root
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 
 # Generate the csharp extension coverage report
 gcov objs/gcov/src/csharp/ext/*.o
diff --git a/tools/run_tests/post_tests_php.sh b/tools/run_tests/helper_scripts/post_tests_php.sh
similarity index 97%
rename from tools/run_tests/post_tests_php.sh
rename to tools/run_tests/helper_scripts/post_tests_php.sh
index b4098066ea9fa9f9a409b75ff783d1c565ea535a..23dc202322fdb4d2d8927b4893342fbeb9e04068 100755
--- a/tools/run_tests/post_tests_php.sh
+++ b/tools/run_tests/helper_scripts/post_tests_php.sh
@@ -32,7 +32,7 @@ set -ex
 
 if [ "$CONFIG" != "gcov" ] ; then exit ; fi
 
-root=$(readlink -f $(dirname $0)/../..)
+root=$(readlink -f $(dirname $0)/../../..)
 out=$root/reports/php_ext_coverage
 tmp1=$(mktemp)
 tmp2=$(mktemp)
diff --git a/tools/run_tests/post_tests_ruby.sh b/tools/run_tests/helper_scripts/post_tests_ruby.sh
similarity index 97%
rename from tools/run_tests/post_tests_ruby.sh
rename to tools/run_tests/helper_scripts/post_tests_ruby.sh
index 0877e44805abd879b5a2309d4d41b0690e77f592..300edfe8a3cd76424e69745f35e2c4fd918dc0f1 100755
--- a/tools/run_tests/post_tests_ruby.sh
+++ b/tools/run_tests/helper_scripts/post_tests_ruby.sh
@@ -32,7 +32,7 @@ set -ex
 
 if [ "$CONFIG" != "gcov" ] ; then exit ; fi
 
-root=$(readlink -f $(dirname $0)/../..)
+root=$(readlink -f $(dirname $0)/../../..)
 out=$root/reports/ruby_ext_coverage
 tmp1=$(mktemp)
 tmp2=$(mktemp)
diff --git a/tools/run_tests/pre_build_c.bat b/tools/run_tests/helper_scripts/pre_build_c.bat
similarity index 98%
rename from tools/run_tests/pre_build_c.bat
rename to tools/run_tests/helper_scripts/pre_build_c.bat
index e4ab69384c2a754bb4ff646be508e1a4a28c06c6..75b90f85b29ee8f490d96c01c51c4833d22769f5 100644
--- a/tools/run_tests/pre_build_c.bat
+++ b/tools/run_tests/helper_scripts/pre_build_c.bat
@@ -32,7 +32,7 @@
 setlocal
 
 @rem enter repo root
-cd /d %~dp0\..\..
+cd /d %~dp0\..\..\..
 
 @rem Location of nuget.exe
 set NUGET=C:\nuget\nuget.exe
diff --git a/tools/run_tests/pre_build_csharp.bat b/tools/run_tests/helper_scripts/pre_build_csharp.bat
similarity index 99%
rename from tools/run_tests/pre_build_csharp.bat
rename to tools/run_tests/helper_scripts/pre_build_csharp.bat
index f15979a96bef3eed6fc89a368a36dd7e011688fe..139955d4dae406eabca28e8c6881fdea00b0256a 100644
--- a/tools/run_tests/pre_build_csharp.bat
+++ b/tools/run_tests/helper_scripts/pre_build_csharp.bat
@@ -32,7 +32,7 @@
 setlocal
 
 @rem enter repo root
-cd /d %~dp0\..\..
+cd /d %~dp0\..\..\..
 
 @rem Location of nuget.exe
 set NUGET=C:\nuget\nuget.exe
diff --git a/tools/run_tests/pre_build_csharp.sh b/tools/run_tests/helper_scripts/pre_build_csharp.sh
similarity index 98%
rename from tools/run_tests/pre_build_csharp.sh
rename to tools/run_tests/helper_scripts/pre_build_csharp.sh
index ee678ddce5f5c6858443f73f493ab9e493b9d6c0..1f808556f48737d1f47dd928df6ff78fec6b6a31 100755
--- a/tools/run_tests/pre_build_csharp.sh
+++ b/tools/run_tests/helper_scripts/pre_build_csharp.sh
@@ -31,7 +31,7 @@
 set -ex
 
 # cd to gRPC csharp directory
-cd $(dirname $0)/../../src/csharp
+cd $(dirname $0)/../../../src/csharp
 
 root=`pwd`
 
diff --git a/tools/run_tests/pre_build_node.bat b/tools/run_tests/helper_scripts/pre_build_node.bat
similarity index 100%
rename from tools/run_tests/pre_build_node.bat
rename to tools/run_tests/helper_scripts/pre_build_node.bat
diff --git a/tools/run_tests/pre_build_node.sh b/tools/run_tests/helper_scripts/pre_build_node.sh
similarity index 100%
rename from tools/run_tests/pre_build_node.sh
rename to tools/run_tests/helper_scripts/pre_build_node.sh
diff --git a/tools/run_tests/helper_scripts/pre_build_node_electron.sh b/tools/run_tests/helper_scripts/pre_build_node_electron.sh
new file mode 100755
index 0000000000000000000000000000000000000000..95c56aa509446eec0b1feaf3b2d0e2055eebc154
--- /dev/null
+++ b/tools/run_tests/helper_scripts/pre_build_node_electron.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ELECTRON_VERSION=$1
+
+nvm install 6
+set -ex
+
+npm install xvfb-maybe
+
+npm install electron@$ELECTRON_VERSION
diff --git a/tools/run_tests/pre_build_ruby.sh b/tools/run_tests/helper_scripts/pre_build_ruby.sh
similarity index 98%
rename from tools/run_tests/pre_build_ruby.sh
rename to tools/run_tests/helper_scripts/pre_build_ruby.sh
index e7074c45c2dc492ef20e423e837ed0cd091f6d3c..56b58df5441bed4b4f7e919ffdce4e5326d59fb2 100755
--- a/tools/run_tests/pre_build_ruby.sh
+++ b/tools/run_tests/helper_scripts/pre_build_ruby.sh
@@ -34,6 +34,6 @@ set -ex
 export GRPC_CONFIG=${CONFIG:-opt}
 
 # change to grpc repo root
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 
 bundle install
diff --git a/tools/run_tests/run_lcov.sh b/tools/run_tests/helper_scripts/run_lcov.sh
similarity index 97%
rename from tools/run_tests/run_lcov.sh
rename to tools/run_tests/helper_scripts/run_lcov.sh
index 796a0b5ceb2fe1834a1a84fc847dc0825120ad77..bc7b44cd3e75bc260c689c922625bcf7c84f7eba 100755
--- a/tools/run_tests/run_lcov.sh
+++ b/tools/run_tests/helper_scripts/run_lcov.sh
@@ -32,7 +32,7 @@ set -ex
 
 out=$(readlink -f ${1:-coverage})
 
-root=$(readlink -f $(dirname $0)/../..)
+root=$(readlink -f $(dirname $0)/../../..)
 shift || true
 tmp=$(mktemp)
 cd $root
diff --git a/tools/run_tests/run_node.bat b/tools/run_tests/helper_scripts/run_node.bat
similarity index 100%
rename from tools/run_tests/run_node.bat
rename to tools/run_tests/helper_scripts/run_node.bat
diff --git a/tools/run_tests/run_node.sh b/tools/run_tests/helper_scripts/run_node.sh
similarity index 98%
rename from tools/run_tests/run_node.sh
rename to tools/run_tests/helper_scripts/run_node.sh
index 44f75645f5f21cf8e1df65f4df40ac47126358df..0fafe9481af75fcdce4da10ecc37377b78232864 100755
--- a/tools/run_tests/run_node.sh
+++ b/tools/run_tests/helper_scripts/run_node.sh
@@ -37,7 +37,7 @@ set -ex
 CONFIG=${CONFIG:-opt}
 
 # change to grpc repo root
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 
 root=`pwd`
 
diff --git a/tools/run_tests/helper_scripts/run_node_electron.sh b/tools/run_tests/helper_scripts/run_node_electron.sh
new file mode 100755
index 0000000000000000000000000000000000000000..1999ffb0fa8803b64376db44cf889b5c58606372
--- /dev/null
+++ b/tools/run_tests/helper_scripts/run_node_electron.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+# 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.
+
+source ~/.nvm/nvm.sh
+
+nvm use 6
+set -ex
+
+# change to grpc repo root
+cd $(dirname $0)/../..
+
+test_directory='src/node/test'
+timeout=8000
+
+JUNIT_REPORT_PATH=src/node/report.xml JUNIT_REPORT_STACK=1 \
+  ./node_modules/.bin/xvfb-maybe \
+  ./node_modules/.bin/electron-mocha --timeout $timeout \
+  --reporter mocha-jenkins-reporter $test_directory
diff --git a/tools/run_tests/run_python.sh b/tools/run_tests/helper_scripts/run_python.sh
similarity index 98%
rename from tools/run_tests/run_python.sh
rename to tools/run_tests/helper_scripts/run_python.sh
index 17e0186f2a86485d9f2a598b0cce3a05c795a3a5..7be473428fba587cc3be66b55b7c7ca0afb45bd0 100755
--- a/tools/run_tests/run_python.sh
+++ b/tools/run_tests/helper_scripts/run_python.sh
@@ -31,7 +31,7 @@
 set -ex
 
 # change to grpc repo root
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 
 PYTHON=`realpath -s "${1:-py27/bin/python}"`
 
diff --git a/tools/run_tests/run_ruby.sh b/tools/run_tests/helper_scripts/run_ruby.sh
similarity index 98%
rename from tools/run_tests/run_ruby.sh
rename to tools/run_tests/helper_scripts/run_ruby.sh
index 73a84ac361f4e11c7a1cc19d2527fa813a2f5cc9..ab153b7e25c46629c729c6d32abd57d97046eaf5 100755
--- a/tools/run_tests/run_ruby.sh
+++ b/tools/run_tests/helper_scripts/run_ruby.sh
@@ -31,6 +31,6 @@
 set -ex
 
 # change to grpc repo root
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 
 rake
diff --git a/tools/run_tests/run_tests_in_workspace.sh b/tools/run_tests/helper_scripts/run_tests_in_workspace.sh
similarity index 98%
rename from tools/run_tests/run_tests_in_workspace.sh
rename to tools/run_tests/helper_scripts/run_tests_in_workspace.sh
index 9c6c5b76e06ba5c23e13e1f3f0f9c5106bc62249..002c8d6de24cbdbc1449d6f695cbc148152e5619 100755
--- a/tools/run_tests/run_tests_in_workspace.sh
+++ b/tools/run_tests/helper_scripts/run_tests_in_workspace.sh
@@ -34,7 +34,7 @@
 # newly created workspace)
 set -ex
 
-cd $(dirname $0)/../..
+cd $(dirname $0)/../../..
 export repo_root=$(pwd)
 
 rm -rf "${WORKSPACE_NAME}"
diff --git a/tools/run_tests/interop_html_report.template b/tools/run_tests/interop/interop_html_report.template
similarity index 100%
rename from tools/run_tests/interop_html_report.template
rename to tools/run_tests/interop/interop_html_report.template
diff --git a/tools/run_tests/performance/bq_upload_result.py b/tools/run_tests/performance/bq_upload_result.py
index ddcf053ae5ec9a92aead775bc55a4c3332665faa..89d2a9b320fde3b83914593b5e025b1ca3be3a34 100755
--- a/tools/run_tests/performance/bq_upload_result.py
+++ b/tools/run_tests/performance/bq_upload_result.py
@@ -115,9 +115,11 @@ def _flatten_result_inplace(scenario_result):
   scenario_result['scenario']['clientConfig'] = json.dumps(scenario_result['scenario']['clientConfig'])
   scenario_result['scenario']['serverConfig'] = json.dumps(scenario_result['scenario']['serverConfig'])
   scenario_result['latencies'] = json.dumps(scenario_result['latencies'])
+  scenario_result['serverCpuStats'] = []
   for stats in scenario_result['serverStats']:
-    stats.pop('totalCpuTime', None)
-    stats.pop('idleCpuTime', None)
+    scenario_result['serverCpuStats'].append(dict())
+    scenario_result['serverCpuStats'][-1]['totalCpuTime'] = stats.pop('totalCpuTime', None)
+    scenario_result['serverCpuStats'][-1]['idleCpuTime'] = stats.pop('idleCpuTime', None)
   for stats in scenario_result['clientStats']:
     stats['latencies'] = json.dumps(stats['latencies'])
     stats.pop('requestResults', None)
@@ -125,7 +127,7 @@ def _flatten_result_inplace(scenario_result):
   scenario_result['clientSuccess'] = json.dumps(scenario_result['clientSuccess'])
   scenario_result['serverSuccess'] = json.dumps(scenario_result['serverSuccess'])
   scenario_result['requestResults'] = json.dumps(scenario_result.get('requestResults', []))
-  scenario_result['summary'].pop('serverCpuUsage', None)
+  scenario_result['serverCpuUsage'] = scenario_result['summary'].pop('serverCpuUsage', None)
   scenario_result['summary'].pop('successfulRequestsPerSecond', None)
   scenario_result['summary'].pop('failedRequestsPerSecond', None)
 
diff --git a/tools/run_tests/performance/process_local_perf_flamegraphs.sh b/tools/run_tests/performance/process_local_perf_flamegraphs.sh
new file mode 100755
index 0000000000000000000000000000000000000000..d15610f137027735ad68afba3c3f4f3325b02ce0
--- /dev/null
+++ b/tools/run_tests/performance/process_local_perf_flamegraphs.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+# 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.
+
+mkdir -p $OUTPUT_DIR
+
+PERF_DATA_FILE=${PERF_BASE_NAME}-perf.data
+PERF_SCRIPT_OUTPUT=${PERF_BASE_NAME}-out.perf
+
+# Generate Flame graphs
+echo "running perf script on $PERF_DATA_FILE"
+perf script -i $PERF_DATA_FILE > $PERF_SCRIPT_OUTPUT
+
+~/FlameGraph/stackcollapse-perf.pl $PERF_SCRIPT_OUTPUT | ~/FlameGraph/flamegraph.pl > ${OUTPUT_DIR}/${OUTPUT_FILENAME}.svg
diff --git a/tools/run_tests/performance/process_remote_perf_flamegraphs.sh b/tools/run_tests/performance/process_remote_perf_flamegraphs.sh
new file mode 100755
index 0000000000000000000000000000000000000000..cc075354ccf58cfc384420183501843950cd57b5
--- /dev/null
+++ b/tools/run_tests/performance/process_remote_perf_flamegraphs.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+# 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.
+
+mkdir -p $OUTPUT_DIR
+
+PERF_DATA_FILE=${PERF_BASE_NAME}-perf.data
+PERF_SCRIPT_OUTPUT=${PERF_BASE_NAME}-out.perf
+
+# Generate Flame graphs
+echo "running perf script on $USER_AT_HOST with perf.data"
+ssh $USER_AT_HOST "cd ~/performance_workspace/grpc && perf script -i $PERF_DATA_FILE | gzip > ${PERF_SCRIPT_OUTPUT}.gz"
+
+scp $USER_AT_HOST:~/performance_workspace/grpc/$PERF_SCRIPT_OUTPUT.gz .
+
+gzip -d -f $PERF_SCRIPT_OUTPUT.gz
+
+~/FlameGraph/stackcollapse-perf.pl --kernel $PERF_SCRIPT_OUTPUT | ~/FlameGraph/flamegraph.pl --color=java --hash > ${OUTPUT_DIR}/${OUTPUT_FILENAME}.svg
diff --git a/tools/run_tests/performance/scenario_result_schema.json b/tools/run_tests/performance/scenario_result_schema.json
index 3285f212d7752380a218aa549571ec37a6ec77e2..8ec41c377c500cb4c19d166f81f6167693eeacd1 100644
--- a/tools/run_tests/performance/scenario_result_schema.json
+++ b/tools/run_tests/performance/scenario_result_schema.json
@@ -213,5 +213,27 @@
     "name": "requestResults",
     "type": "STRING",
     "mode": "NULLABLE"
+  },
+  {
+    "name": "serverCpuStats",
+    "type": "RECORD",
+    "mode": "REPEATED",
+    "fields": [
+      {
+        "name": "totalCpuTime",
+        "type": "INTEGER",
+        "mode": "NULLABLE"
+      },
+      {
+        "name": "idleCpuTime",
+        "type": "INTEGER",
+        "mode": "NULLABLE"
+      }
+    ]
+  },
+  {
+    "name": "serverCpuUsage",
+    "type": "FLOAT",
+    "mode": "NULLABLE"
   }
 ]
diff --git a/src/python/grpcio_reflection/grpc/__init__.py b/tools/run_tests/python_utils/__init__.py
similarity index 96%
rename from src/python/grpcio_reflection/grpc/__init__.py
rename to tools/run_tests/python_utils/__init__.py
index 70ac5edd48370ac77409293d23742878764020b7..100a624dc9c1bbd89708c58e18cf2666a73f4cc7 100644
--- a/src/python/grpcio_reflection/grpc/__init__.py
+++ b/tools/run_tests/python_utils/__init__.py
@@ -26,5 +26,3 @@
 # 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.
-
-__import__('pkg_resources').declare_namespace(__name__)
diff --git a/tools/run_tests/antagonist.py b/tools/run_tests/python_utils/antagonist.py
similarity index 100%
rename from tools/run_tests/antagonist.py
rename to tools/run_tests/python_utils/antagonist.py
diff --git a/tools/run_tests/dockerjob.py b/tools/run_tests/python_utils/dockerjob.py
similarity index 99%
rename from tools/run_tests/dockerjob.py
rename to tools/run_tests/python_utils/dockerjob.py
index 4a7e61b3c4e3be785803b675d5658b04935f8f2d..0869c5cee9bf932c3502168e737ab249f711f103 100755
--- a/tools/run_tests/dockerjob.py
+++ b/tools/run_tests/python_utils/dockerjob.py
@@ -31,13 +31,14 @@
 
 from __future__ import print_function
 
-import jobset
 import tempfile
 import time
 import uuid
 import os
 import subprocess
 
+import jobset
+
 _DEVNULL = open(os.devnull, 'w')
 
 
diff --git a/tools/run_tests/filter_pull_request_tests.py b/tools/run_tests/python_utils/filter_pull_request_tests.py
similarity index 100%
rename from tools/run_tests/filter_pull_request_tests.py
rename to tools/run_tests/python_utils/filter_pull_request_tests.py
diff --git a/tools/run_tests/jobset.py b/tools/run_tests/python_utils/jobset.py
similarity index 88%
rename from tools/run_tests/jobset.py
rename to tools/run_tests/python_utils/jobset.py
index b84eb3b5d7e50263bb0cbcdeb9518eb9d7ade827..7b2c62d1a2b12233a9c7f8352b2b0346ee34e515 100755
--- a/tools/run_tests/jobset.py
+++ b/tools/run_tests/python_utils/jobset.py
@@ -49,12 +49,22 @@ measure_cpu_costs = False
 _DEFAULT_MAX_JOBS = 16 * multiprocessing.cpu_count()
 _MAX_RESULT_SIZE = 8192
 
+
+# NOTE: If you change this, please make sure to test reviewing the
+# github PR with http://reviewable.io, which is known to add UTF-8
+# characters to the PR description, which leak into the environment here
+# and cause failures.
+def strip_non_ascii_chars(s):
+  return ''.join(c for c in s if ord(c) < 128)
+
+
 def sanitized_environment(env):
   sanitized = {}
   for key, value in env.items():
-    sanitized[str(key).encode()] = str(value).encode()
+    sanitized[strip_non_ascii_chars(key)] = strip_non_ascii_chars(value)
   return sanitized
 
+
 def platform_string():
   if platform.system() == 'Windows':
     return 'windows'
@@ -129,16 +139,16 @@ def message(tag, msg, explanatory_text=None, do_newline=False):
       if explanatory_text:
         print(explanatory_text)
       print('%s: %s' % (tag, msg))
-      return
-    sys.stdout.write('%s%s%s\x1b[%d;%dm%s\x1b[0m: %s%s' % (
-        _BEGINNING_OF_LINE,
-        _CLEAR_LINE,
-        '\n%s' % explanatory_text if explanatory_text is not None else '',
-        _COLORS[_TAG_COLOR[tag]][1],
-        _COLORS[_TAG_COLOR[tag]][0],
-        tag,
-        msg,
-        '\n' if do_newline or explanatory_text is not None else ''))
+    else:
+      sys.stdout.write('%s%s%s\x1b[%d;%dm%s\x1b[0m: %s%s' % (
+          _BEGINNING_OF_LINE,
+          _CLEAR_LINE,
+          '\n%s' % explanatory_text if explanatory_text is not None else '',
+          _COLORS[_TAG_COLOR[tag]][1],
+          _COLORS[_TAG_COLOR[tag]][0],
+          tag,
+          msg,
+          '\n' if do_newline or explanatory_text is not None else ''))
     sys.stdout.flush()
   except:
     pass
@@ -209,7 +219,8 @@ class JobResult(object):
 class Job(object):
   """Manages one job."""
 
-  def __init__(self, spec, newline_on_success, travis, add_env):
+  def __init__(self, spec, newline_on_success, travis, add_env,
+               quiet_success=False):
     self._spec = spec
     self._newline_on_success = newline_on_success
     self._travis = travis
@@ -217,7 +228,9 @@ class Job(object):
     self._retries = 0
     self._timeout_retries = 0
     self._suppress_failure_message = False
-    message('START', spec.shortname, do_newline=self._travis)
+    self._quiet_success = quiet_success
+    if not self._quiet_success:
+      message('START', spec.shortname, do_newline=self._travis)
     self.result = JobResult()
     self.start()
 
@@ -292,10 +305,11 @@ class Job(object):
           if real > 0.5:
             cores = (user + sys) / real
             measurement = '; cpu_cost=%.01f; estimated=%.01f' % (cores, self._spec.cpu_cost)
-        message('PASSED', '%s [time=%.1fsec; retries=%d:%d%s]' % (
-            self._spec.shortname, elapsed, self._retries, self._timeout_retries, measurement),
-            stdout() if self._spec.verbose_success else None,
-            do_newline=self._newline_on_success or self._travis)
+        if not self._quiet_success:
+          message('PASSED', '%s [time=%.1fsec; retries=%d:%d%s]' % (
+              self._spec.shortname, elapsed, self._retries, self._timeout_retries, measurement),
+              stdout() if self._spec.verbose_success else None,
+              do_newline=self._newline_on_success or self._travis)
         self.result.state = 'PASSED'
     elif (self._state == _RUNNING and
           self._spec.timeout_seconds is not None and
@@ -331,7 +345,7 @@ class Jobset(object):
   """Manages one run of jobs."""
 
   def __init__(self, check_cancelled, maxjobs, newline_on_success, travis,
-               stop_on_failure, add_env):
+               stop_on_failure, add_env, quiet_success):
     self._running = set()
     self._check_cancelled = check_cancelled
     self._cancelled = False
@@ -342,6 +356,7 @@ class Jobset(object):
     self._travis = travis
     self._stop_on_failure = stop_on_failure
     self._add_env = add_env
+    self._quiet_success = quiet_success
     self.resultset = {}
     self._remaining = None
     self._start_time = time.time()
@@ -370,7 +385,8 @@ class Jobset(object):
     job = Job(spec,
               self._newline_on_success,
               self._travis,
-              self._add_env)
+              self._add_env,
+              self._quiet_success)
     self._running.add(job)
     if job.GetSpec().shortname not in self.resultset:
       self.resultset[job.GetSpec().shortname] = []
@@ -393,10 +409,11 @@ class Jobset(object):
         break
       for job in dead:
         self._completed += 1
-        self.resultset[job.GetSpec().shortname].append(job.result)
+        if not self._quiet_success or job.result.state != 'PASSED':
+          self.resultset[job.GetSpec().shortname].append(job.result)
         self._running.remove(job)
       if dead: return
-      if (not self._travis):
+      if not self._travis and platform_string() != 'windows':
         rstr = '' if self._remaining is None else '%d queued, ' % self._remaining
         if self._remaining is not None and self._completed > 0:
           now = time.time()
@@ -453,7 +470,8 @@ def run(cmdlines,
         infinite_runs=False,
         stop_on_failure=False,
         add_env={},
-        skip_jobs=False):
+        skip_jobs=False,
+        quiet_success=False):
   if skip_jobs:
     results = {}
     skipped_job_result = JobResult()
@@ -464,7 +482,8 @@ def run(cmdlines,
     return results
   js = Jobset(check_cancelled,
               maxjobs if maxjobs is not None else _DEFAULT_MAX_JOBS,
-              newline_on_success, travis, stop_on_failure, add_env)
+              newline_on_success, travis, stop_on_failure, add_env,
+              quiet_success)
   for cmdline, remaining in tag_remaining(cmdlines):
     if not js.start(cmdline):
       break
diff --git a/tools/run_tests/port_server.py b/tools/run_tests/python_utils/port_server.py
similarity index 100%
rename from tools/run_tests/port_server.py
rename to tools/run_tests/python_utils/port_server.py
diff --git a/tools/run_tests/report_utils.py b/tools/run_tests/python_utils/report_utils.py
similarity index 93%
rename from tools/run_tests/report_utils.py
rename to tools/run_tests/python_utils/report_utils.py
index 90055e3530c918342c3dbf640d772d476cfd50da..352cf7abe76b260c3020aa62db5dc04208bd2e68 100644
--- a/tools/run_tests/report_utils.py
+++ b/tools/run_tests/python_utils/report_utils.py
@@ -84,7 +84,7 @@ def render_interop_html_report(
   client_langs, server_langs, test_cases, auth_test_cases, http2_cases,
   resultset, num_failures, cloud_to_prod, prod_servers, http2_interop):
   """Generate HTML report for interop tests."""
-  template_file = 'tools/run_tests/interop_html_report.template'
+  template_file = 'tools/run_tests/interop/interop_html_report.template'
   try:
     mytemplate = Template(filename=template_file, format_exceptions=True)
   except NameError:
@@ -122,3 +122,10 @@ def render_interop_html_report(
   except:
     print(exceptions.text_error_template().render())
     raise
+
+def render_perf_profiling_results(output_filepath, profile_names):
+  with open(output_filepath, 'w') as output_file:
+    output_file.write('<ul>\n')
+    for name in profile_names:
+      output_file.write('<li><a href=%s>%s</a></li>\n' % (name, name))
+    output_file.write('</ul>\n')
diff --git a/tools/run_tests/watch_dirs.py b/tools/run_tests/python_utils/watch_dirs.py
similarity index 100%
rename from tools/run_tests/watch_dirs.py
rename to tools/run_tests/python_utils/watch_dirs.py
diff --git a/tools/run_tests/run_interop_tests.py b/tools/run_tests/run_interop_tests.py
index 83cfc429f98ade556908df6d724b96ac997f7106..56efac50ca0d23793468f18680b683c8c4c18704 100755
--- a/tools/run_tests/run_interop_tests.py
+++ b/tools/run_tests/run_interop_tests.py
@@ -34,20 +34,21 @@ from __future__ import print_function
 
 import argparse
 import atexit
-import dockerjob
 import itertools
-import jobset
 import json
 import multiprocessing
 import os
 import re
-import report_utils
 import subprocess
 import sys
 import tempfile
 import time
 import uuid
 
+import python_utils.dockerjob as dockerjob
+import python_utils.jobset as jobset
+import python_utils.report_utils as report_utils
+
 # Docker doesn't clean up after itself, so we do it on exit.
 atexit.register(lambda: subprocess.call(['stty', 'echo']))
 
@@ -168,6 +169,9 @@ class JavaLanguage:
   def client_cmd(self, args):
     return ['./run-test-client.sh'] + args
 
+  def client_cmd_http2interop(self, args):
+    return ['./run-http2-client.sh'] + args
+
   def cloud_to_prod_env(self):
     return {}
 
@@ -178,10 +182,10 @@ class JavaLanguage:
     return {}
 
   def unimplemented_test_cases(self):
-    return _SKIP_ADVANCED + _SKIP_COMPRESSION
+    return _SKIP_COMPRESSION
 
   def unimplemented_test_cases_server(self):
-    return _SKIP_ADVANCED + _SKIP_COMPRESSION
+    return _SKIP_COMPRESSION
 
   def __str__(self):
     return 'java'
@@ -216,6 +220,33 @@ class GoLanguage:
   def __str__(self):
     return 'go'
 
+class Http2Server:
+  """Represents the HTTP/2 Interop Test server
+
+  This pretends to be a language in order to be built and run, but really it
+  isn't.
+  """
+  def __init__(self):
+    self.server_cwd = None
+    self.safename = str(self)
+
+  def server_cmd(self, args):
+    return ['python test/http2_test/http2_test_server.py']
+
+  def cloud_to_prod_env(self):
+    return {}
+
+  def global_env(self):
+    return {}
+
+  def unimplemented_test_cases(self):
+    return _TEST_CASES
+
+  def unimplemented_test_cases_server(self):
+    return _TEST_CASES
+
+  def __str__(self):
+    return 'http2'
 
 class Http2Client:
   """Represents the HTTP/2 Interop Test
@@ -349,10 +380,10 @@ class RubyLanguage:
     return {}
 
   def unimplemented_test_cases(self):
-    return _SKIP_ADVANCED + _SKIP_SERVER_COMPRESSION
+    return _SKIP_SERVER_COMPRESSION
 
   def unimplemented_test_cases_server(self):
-    return _SKIP_ADVANCED + _SKIP_COMPRESSION
+    return _SKIP_COMPRESSION
 
   def __str__(self):
     return 'ruby'
@@ -374,6 +405,11 @@ class PythonLanguage:
         '--args="{}"'.format(' '.join(args))
     ]
 
+  def client_cmd_http2interop(self, args):
+    return [ 'py27/bin/python',
+              'src/python/grpcio_tests/tests/http2/_negative_http2_client.py',
+           ] + args
+
   def cloud_to_prod_env(self):
     return {}
 
@@ -428,7 +464,10 @@ _TEST_CASES = ['large_unary', 'empty_unary', 'ping_pong',
 _AUTH_TEST_CASES = ['compute_engine_creds', 'jwt_token_creds',
                     'oauth2_auth_token', 'per_rpc_creds']
 
-_HTTP2_TEST_CASES = ["tls", "framing"]
+_HTTP2_TEST_CASES = ['tls', 'framing']
+
+_HTTP2_BADSERVER_TEST_CASES = ['rst_after_header', 'rst_after_data', 'rst_during_data',
+                     'goaway', 'ping', 'max_streams']
 
 DOCKER_WORKDIR_ROOT = '/var/local/git/grpc'
 
@@ -549,13 +588,25 @@ def cloud_to_prod_jobspec(language, test_case, server_host_name,
 def cloud_to_cloud_jobspec(language, test_case, server_name, server_host,
                            server_port, docker_image=None):
   """Creates jobspec for cloud-to-cloud interop test"""
-  cmdline = bash_cmdline(language.client_cmd([
+  interop_only_options = [
       '--server_host_override=foo.test.google.fr',
       '--use_tls=true',
       '--use_test_ca=true',
+  ]
+  common_options = [
       '--test_case=%s' % test_case,
       '--server_host=%s' % server_host,
-      '--server_port=%s' % server_port]))
+  ]
+  if test_case in _HTTP2_BADSERVER_TEST_CASES:
+    # We are running the http2_badserver_interop test. Adjust command line accordingly.
+    offset = sorted(_HTTP2_BADSERVER_TEST_CASES).index(test_case)
+    client_options = common_options + ['--server_port=%s' %
+                                       (int(server_port)+offset)]
+    cmdline = bash_cmdline(language.client_cmd_http2interop(client_options))
+  else:
+    client_options = interop_only_options + common_options + ['--server_port=%s' % server_port]
+    cmdline = bash_cmdline(language.client_cmd(client_options))
+
   cwd = language.client_cwd
   environ = language.global_env()
   if docker_image:
@@ -589,13 +640,30 @@ def server_jobspec(language, docker_image):
   cmdline = bash_cmdline(
       language.server_cmd(['--port=%s' % _DEFAULT_SERVER_PORT]))
   environ = language.global_env()
+  if language.safename == 'http2':
+    # we are running the http2 interop server. Open next N ports beginning
+    # with the server port. These ports are used for http2 interop test
+    # (one test case per port). We also attach the docker container running
+    # the server to local network, so we don't have to mess with port mapping
+    port_args = [
+      '-p', str(_DEFAULT_SERVER_PORT+0),
+      '-p', str(_DEFAULT_SERVER_PORT+1),
+      '-p', str(_DEFAULT_SERVER_PORT+2),
+      '-p', str(_DEFAULT_SERVER_PORT+3),
+      '-p', str(_DEFAULT_SERVER_PORT+4),
+      '-p', str(_DEFAULT_SERVER_PORT+5),
+      '-p', str(_DEFAULT_SERVER_PORT+6),
+      '--net=host',
+    ]
+  else:
+    port_args = ['-p', str(_DEFAULT_SERVER_PORT)]
+
   docker_cmdline = docker_run_cmdline(cmdline,
                                       image=docker_image,
                                       cwd=language.server_cwd,
                                       environ=environ,
-                                      docker_args=['-p', str(_DEFAULT_SERVER_PORT),
-                                                   '--name', container_name])
-
+                                      docker_args=port_args +
+                                        ['--name', container_name])
   server_job = jobset.JobSpec(
           cmdline=docker_cmdline,
           environ=environ,
@@ -729,7 +797,12 @@ argp.add_argument('--http2_interop',
                   default=False,
                   action='store_const',
                   const=True,
-                  help='Enable HTTP/2 interop tests')
+                  help='Enable HTTP/2 client edge case testing. (Bad client, good server)')
+argp.add_argument('--http2_badserver_interop',
+                  default=False,
+                  action='store_const',
+                  const=True,
+                  help='Enable HTTP/2 server edge case testing. (Good client, bad server)')
 
 args = argp.parse_args()
 
@@ -756,6 +829,7 @@ languages = set(_LANGUAGES[l]
                       for x in args.language))
 
 http2Interop = Http2Client() if args.http2_interop else None
+http2InteropServer = Http2Server() if args.http2_badserver_interop else None
 
 docker_images={}
 if args.use_docker:
@@ -765,6 +839,9 @@ if args.use_docker:
   if args.http2_interop:
     languages_to_build.add(http2Interop)
 
+  if args.http2_badserver_interop:
+    languages_to_build.add(http2InteropServer)
+
   build_jobs = []
   for l in languages_to_build:
     job = build_interop_image_jobspec(l)
@@ -796,6 +873,14 @@ try:
     server_jobs[lang] = job
     server_addresses[lang] = ('localhost', job.mapped_port(_DEFAULT_SERVER_PORT))
 
+  if args.http2_badserver_interop:
+    # launch a HTTP2 server emulator that creates edge cases
+    lang = str(http2InteropServer)
+    spec = server_jobspec(http2InteropServer, docker_images.get(lang))
+    job = dockerjob.DockerJob(spec)
+    server_jobs[lang] = job
+    server_addresses[lang] = ('localhost', _DEFAULT_SERVER_PORT)
+
   jobs = []
   if args.cloud_to_prod:
     for server_host_name in args.prod_servers:
@@ -839,17 +924,18 @@ try:
     skip_server = []  # test cases unimplemented by server
     if server_language:
       skip_server = server_language.unimplemented_test_cases_server()
-    for language in languages:
-      for test_case in _TEST_CASES:
-        if not test_case in language.unimplemented_test_cases():
-          if not test_case in skip_server:
-            test_job = cloud_to_cloud_jobspec(language,
-                                              test_case,
-                                              server_name,
-                                              server_host,
-                                              server_port,
-                                              docker_image=docker_images.get(str(language)))
-            jobs.append(test_job)
+    if not args.http2_badserver_interop:
+      for language in languages:
+        for test_case in _TEST_CASES:
+          if not test_case in language.unimplemented_test_cases():
+            if not test_case in skip_server:
+              test_job = cloud_to_cloud_jobspec(language,
+                                                test_case,
+                                                server_name,
+                                                server_host,
+                                                server_port,
+                                                docker_image=docker_images.get(str(language)))
+              jobs.append(test_job)
 
     if args.http2_interop:
       for test_case in _HTTP2_TEST_CASES:
@@ -864,6 +950,17 @@ try:
                                           docker_image=docker_images.get(str(http2Interop)))
         jobs.append(test_job)
 
+    if args.http2_badserver_interop:
+      for language in languages:
+        for test_case in _HTTP2_BADSERVER_TEST_CASES:
+          test_job = cloud_to_cloud_jobspec(language,
+                                            test_case,
+                                            server_name,
+                                            server_host,
+                                            server_port,
+                                            docker_image=docker_images.get(str(language)))
+          jobs.append(test_job)
+
   if not jobs:
     print('No jobs to run.')
     for image in docker_images.itervalues():
diff --git a/tools/run_tests/run_performance_tests.py b/tools/run_tests/run_performance_tests.py
index 1d0c98fb6903c39b64fa4d65c6d8a158cb60d699..12b5e01508d01111132ca17fa37aa871a5de60da 100755
--- a/tools/run_tests/run_performance_tests.py
+++ b/tools/run_tests/run_performance_tests.py
@@ -35,14 +35,11 @@ from __future__ import print_function
 import argparse
 import collections
 import itertools
-import jobset
 import json
 import multiprocessing
 import os
-import performance.scenario_config as scenario_config
 import pipes
 import re
-import report_utils
 import subprocess
 import sys
 import tempfile
@@ -50,6 +47,10 @@ import time
 import traceback
 import uuid
 
+import performance.scenario_config as scenario_config
+import python_utils.jobset as jobset
+import python_utils.report_utils as report_utils
+
 
 _ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
 os.chdir(_ROOT)
@@ -61,11 +62,12 @@ _REMOTE_HOST_USERNAME = 'jenkins'
 class QpsWorkerJob:
   """Encapsulates a qps worker server job."""
 
-  def __init__(self, spec, language, host_and_port):
+  def __init__(self, spec, language, host_and_port, perf_file_base_name=None):
     self._spec = spec
     self.language = language
     self.host_and_port = host_and_port
     self._job = None
+    self.perf_file_base_name = perf_file_base_name
 
   def start(self):
     self._job = jobset.Job(self._spec, newline_on_success=True, travis=True, add_env={})
@@ -80,28 +82,36 @@ class QpsWorkerJob:
       self._job = None
 
 
-def create_qpsworker_job(language, shortname=None,
-                         port=10000, remote_host=None):
-  cmdline = language.worker_cmdline() + ['--driver_port=%s' % port]
+def create_qpsworker_job(language, shortname=None, port=10000, remote_host=None, perf_cmd=None):
+  cmdline = (language.worker_cmdline() + ['--driver_port=%s' % port])
+
   if remote_host:
-    user_at_host = '%s@%s' % (_REMOTE_HOST_USERNAME, remote_host)
-    cmdline = ['ssh',
-               str(user_at_host),
-               'cd ~/performance_workspace/grpc/ && %s' % ' '.join(cmdline)]
     host_and_port='%s:%s' % (remote_host, port)
   else:
     host_and_port='localhost:%s' % port
 
+  perf_file_base_name = None
+  if perf_cmd:
+    perf_file_base_name = '%s-%s' % (host_and_port, shortname)
+    # specify -o output file so perf.data gets collected when worker stopped
+    cmdline = perf_cmd + ['-o', '%s-perf.data' % perf_file_base_name] + cmdline
+
+  if remote_host:
+    user_at_host = '%s@%s' % (_REMOTE_HOST_USERNAME, remote_host)
+    ssh_cmd = ['ssh']
+    ssh_cmd.extend([str(user_at_host), 'cd ~/performance_workspace/grpc/ && %s' % ' '.join(cmdline)])
+    cmdline = ssh_cmd
+
   jobspec = jobset.JobSpec(
       cmdline=cmdline,
       shortname=shortname,
       timeout_seconds=5*60,  # workers get restarted after each scenario
       verbose_success=True)
-  return QpsWorkerJob(jobspec, language, host_and_port)
+  return QpsWorkerJob(jobspec, language, host_and_port, perf_file_base_name)
 
 
 def create_scenario_jobspec(scenario_json, workers, remote_host=None,
-                            bq_result_table=None):
+                            bq_result_table=None, server_cpu_load=0):
   """Runs one scenario using QPS driver."""
   # setting QPS_WORKERS env variable here makes sure it works with SSH too.
   cmd = 'QPS_WORKERS="%s" ' % ','.join(workers)
@@ -109,7 +119,9 @@ def create_scenario_jobspec(scenario_json, workers, remote_host=None,
     cmd += 'BQ_RESULT_TABLE="%s" ' % bq_result_table
   cmd += 'tools/run_tests/performance/run_qps_driver.sh '
   cmd += '--scenarios_json=%s ' % pipes.quote(json.dumps({'scenarios': [scenario_json]}))
-  cmd += '--scenario_result_file=scenario_result.json'
+  cmd += '--scenario_result_file=scenario_result.json '
+  if server_cpu_load != 0:
+      cmd += '--search_param=offered_load --initial_search_value=1000 --targeted_cpu_load=%d --stride=500 --error_tolerance=0.01' % server_cpu_load
   if remote_host:
     user_at_host = '%s@%s' % (_REMOTE_HOST_USERNAME, remote_host)
     cmd = 'ssh %s "cd ~/performance_workspace/grpc/ && "%s' % (user_at_host, pipes.quote(cmd))
@@ -117,7 +129,7 @@ def create_scenario_jobspec(scenario_json, workers, remote_host=None,
   return jobset.JobSpec(
       cmdline=[cmd],
       shortname='qps_json_driver.%s' % scenario_json['name'],
-      timeout_seconds=3*60,
+      timeout_seconds=12*60,
       shell=True,
       verbose_success=True)
 
@@ -259,7 +271,7 @@ def build_on_remote_hosts(hosts, languages=scenario_config.LANGUAGES.keys(), bui
     sys.exit(1)
 
 
-def create_qpsworkers(languages, worker_hosts):
+def create_qpsworkers(languages, worker_hosts, perf_cmd=None):
   """Creates QPS workers (but does not start them)."""
   if not worker_hosts:
     # run two workers locally (for each language)
@@ -275,17 +287,38 @@ def create_qpsworkers(languages, worker_hosts):
                                shortname= 'qps_worker_%s_%s' % (language,
                                                                 worker_idx),
                                port=worker[1] + language.worker_port_offset(),
-                               remote_host=worker[0])
+                               remote_host=worker[0],
+                               perf_cmd=perf_cmd)
           for language in languages
           for worker_idx, worker in enumerate(workers)]
 
 
+def perf_report_processor_job(worker_host, perf_base_name, output_filename):
+  print('Creating perf report collection job for %s' % worker_host)
+  cmd = ''
+  if worker_host != 'localhost':
+    user_at_host = "%s@%s" % (_REMOTE_HOST_USERNAME, worker_host)
+    cmd = "USER_AT_HOST=%s OUTPUT_FILENAME=%s OUTPUT_DIR=%s PERF_BASE_NAME=%s\
+         tools/run_tests/performance/process_remote_perf_flamegraphs.sh" \
+          % (user_at_host, output_filename, args.flame_graph_reports, perf_base_name)
+  else:
+    cmd = "OUTPUT_FILENAME=%s OUTPUT_DIR=%s PERF_BASE_NAME=%s\
+          tools/run_tests/performance/process_local_perf_flamegraphs.sh" \
+          % (output_filename, args.flame_graph_reports, perf_base_name)
+
+  return jobset.JobSpec(cmdline=cmd,
+                        timeout_seconds=3*60,
+                        shell=True,
+                        verbose_success=True,
+                        shortname='process perf report')
+
+
 Scenario = collections.namedtuple('Scenario', 'jobspec workers name')
 
 
 def create_scenarios(languages, workers_by_lang, remote_host=None, regex='.*',
                      category='all', bq_result_table=None,
-                     netperf=False, netperf_hosts=[]):
+                     netperf=False, netperf_hosts=[], server_cpu_load=0):
   """Create jobspecs for scenarios to run."""
   all_workers = [worker
                  for workers in workers_by_lang.values()
@@ -346,7 +379,8 @@ def create_scenarios(languages, workers_by_lang, remote_host=None, regex='.*',
               create_scenario_jobspec(scenario_json,
                                       [w.host_and_port for w in workers],
                                       remote_host=remote_host,
-                                      bq_result_table=bq_result_table),
+                                      bq_result_table=bq_result_table,
+                                      server_cpu_load=server_cpu_load),
               workers,
               scenario_json['name'])
           scenarios.append(scenario)
@@ -372,6 +406,31 @@ def finish_qps_workers(jobs):
   print('All QPS workers finished.')
   return num_killed
 
+profile_output_files = []
+
+# Collect perf text reports and flamegraphs if perf_cmd was used
+# Note the base names of perf text reports are used when creating and processing
+# perf data. The scenario name uniqifies the output name in the final
+# perf reports directory. 
+# Alos, the perf profiles need to be fetched and processed after each scenario
+# in order to avoid clobbering the output files.
+def run_collect_perf_profile_jobs(hosts_and_base_names, scenario_name):
+  perf_report_jobs = []
+  global profile_output_files
+  for host_and_port in hosts_and_base_names:
+    perf_base_name = hosts_and_base_names[host_and_port]
+    output_filename = '%s-%s' % (scenario_name, perf_base_name)
+    # from the base filename, create .svg output filename
+    host = host_and_port.split(':')[0]
+    profile_output_files.append('%s.svg' % output_filename)
+    perf_report_jobs.append(perf_report_processor_job(host, perf_base_name, output_filename))
+
+  jobset.message('START', 'Collecting perf reports from qps workers', do_newline=True)
+  failures, _ = jobset.run(perf_report_jobs, newline_on_success=True, maxjobs=1)
+  jobset.message('END', 'Collecting perf reports from qps workers', do_newline=True)
+  return failures
+
+
 argp = argparse.ArgumentParser(description='Run performance tests.')
 argp.add_argument('-l', '--language',
                   choices=['all'] + sorted(scenario_config.LANGUAGES.keys()),
@@ -403,8 +462,39 @@ argp.add_argument('--netperf',
                   action='store_const',
                   const=True,
                   help='Run netperf benchmark as one of the scenarios.')
+argp.add_argument('--server_cpu_load',
+                  default=0, type=int,
+                  help='Select a targeted server cpu load to run. 0 means ignore this flag')
 argp.add_argument('-x', '--xml_report', default='report.xml', type=str,
                   help='Name of XML report file to generate.')
+argp.add_argument('--perf_args',
+                  help=('Example usage: "--perf_args=record -F 99 -g". '
+                        'Wrap QPS workers in a perf command '
+                        'with the arguments to perf specified here. '
+                        '".svg" flame graph profiles will be '
+                        'created for each Qps Worker on each scenario. '
+                        'Files will output to "<repo_root>/<args.flame_graph_reports>" '
+                        'directory. Output files from running the worker '
+                        'under perf are saved in the repo root where its ran. '
+                        'Note that the perf "-g" flag is necessary for '
+                        'flame graphs generation to work (assuming the binary '
+                        'being profiled uses frame pointers, check out '
+                        '"--call-graph dwarf" option using libunwind otherwise.) '
+                        'Also note that the entire "--perf_args=<arg(s)>" must '
+                        'be wrapped in quotes as in the example usage. '
+                        'If the "--perg_args" is unspecified, "perf" will '
+                        'not be used at all. '
+                        'See http://www.brendangregg.com/perf.html '
+                        'for more general perf examples.'))
+argp.add_argument('--skip_generate_flamegraphs',
+                  default=False,
+                  action='store_const',
+                  const=True,
+                  help=('Turn flame graph generation off. '
+                        'May be useful if "perf_args" arguments do not make sense for '
+                        'generating flamegraphs (e.g., "--perf_args=stat ...")'))
+argp.add_argument('-f', '--flame_graph_reports', default='perf_reports', type=str,
+                  help='Name of directory to output flame graph profiles to, if any are created.')
 
 args = argp.parse_args()
 
@@ -435,7 +525,14 @@ if not args.remote_driver_host:
 if not args.dry_run:
   build_on_remote_hosts(remote_hosts, languages=[str(l) for l in languages], build_local=build_local)
 
-qpsworker_jobs = create_qpsworkers(languages, args.remote_worker_host)
+perf_cmd = None
+if args.perf_args:
+  print('Running workers under perf profiler')
+  # Expect /usr/bin/perf to be installed here, as is usual
+  perf_cmd = ['/usr/bin/perf']
+  perf_cmd.extend(re.split('\s+', args.perf_args))
+
+qpsworker_jobs = create_qpsworkers(languages, args.remote_worker_host, perf_cmd=perf_cmd)
 
 # get list of worker addresses for each language.
 workers_by_lang = dict([(str(language), []) for language in languages])
@@ -449,7 +546,8 @@ scenarios = create_scenarios(languages,
                            category=args.category,
                            bq_result_table=args.bq_result_table,
                            netperf=args.netperf,
-                           netperf_hosts=args.remote_worker_host)
+                           netperf_hosts=args.remote_worker_host,
+                           server_cpu_load=args.server_cpu_load)
 
 if not scenarios:
   raise Exception('No scenarios to run')
@@ -457,16 +555,20 @@ if not scenarios:
 total_scenario_failures = 0
 qps_workers_killed = 0
 merged_resultset = {}
+perf_report_failures = 0
+
 for scenario in scenarios:
   if args.dry_run:
     print(scenario.name)
   else:
+    scenario_failures = 0
     try:
       for worker in scenario.workers:
         worker.start()
-      scenario_failures, resultset = jobset.run([scenario.jobspec,
-                                                create_quit_jobspec(scenario.workers, remote_host=args.remote_driver_host)],
-                                                newline_on_success=True, maxjobs=1)
+      jobs = [scenario.jobspec]
+      if scenario.workers:
+        jobs.append(create_quit_jobspec(scenario.workers, remote_host=args.remote_driver_host))
+      scenario_failures, resultset = jobset.run(jobs, newline_on_success=True, maxjobs=1)
       total_scenario_failures += scenario_failures
       merged_resultset = dict(itertools.chain(merged_resultset.iteritems(),
                                               resultset.iteritems()))
@@ -474,10 +576,27 @@ for scenario in scenarios:
       # Consider qps workers that need to be killed as failures
       qps_workers_killed += finish_qps_workers(scenario.workers)
 
+    if perf_cmd and scenario_failures == 0 and not args.skip_generate_flamegraphs:
+      workers_and_base_names = {}
+      for worker in scenario.workers:
+        if not worker.perf_file_base_name:
+          raise Exception('using perf buf perf report filename is unspecified')
+        workers_and_base_names[worker.host_and_port] = worker.perf_file_base_name
+      perf_report_failures += run_collect_perf_profile_jobs(workers_and_base_names, scenario.name)
+
 
-report_utils.render_junit_xml_report(merged_resultset, args.xml_report,
-                                     suite_name='benchmarks')
+# Still write the index.html even if some scenarios failed.
+# 'profile_output_files' will only have names for scenarios that passed
+if perf_cmd and not args.skip_generate_flamegraphs:
+  # write the index fil to the output dir, with all profiles from all scenarios/workers
+  report_utils.render_perf_profiling_results('%s/index.html' % args.flame_graph_reports, profile_output_files)
 
 if total_scenario_failures > 0 or qps_workers_killed > 0:
-  print ("%s scenarios failed and %s qps worker jobs killed" % (total_scenario_failures, qps_workers_killed))
+  print('%s scenarios failed and %s qps worker jobs killed' % (total_scenario_failures, qps_workers_killed))
+  sys.exit(1)
+
+report_utils.render_junit_xml_report(merged_resultset, args.xml_report,
+                                     suite_name='benchmarks')
+if perf_report_failures > 0:
+  print('%s perf profile collection jobs failed' % perf_report_failures)
   sys.exit(1)
diff --git a/tools/run_tests/run_stress_tests.py b/tools/run_tests/run_stress_tests.py
index de4a22877ca52327bb9df04761cefeaae957812d..a94a615b8863aea920bb943acaed1b77972766a1 100755
--- a/tools/run_tests/run_stress_tests.py
+++ b/tools/run_tests/run_stress_tests.py
@@ -33,9 +33,7 @@ from __future__ import print_function
 
 import argparse
 import atexit
-import dockerjob
 import itertools
-import jobset
 import json
 import multiprocessing
 import os
@@ -46,6 +44,9 @@ import tempfile
 import time
 import uuid
 
+import python_utils.dockerjob as dockerjob
+import python_utils.jobset as jobset
+
 # Docker doesn't clean up after itself, so we do it on exit.
 atexit.register(lambda: subprocess.call(['stty', 'echo']))
 
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index e503b82f0e3b57dc49c1f188cbb8a37ff86b22c6..5aae3432e16e064accfd8de0af8ce36600b5e0b3 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -54,9 +54,9 @@ import time
 from six.moves import urllib
 import uuid
 
-import jobset
-import report_utils
-import watch_dirs
+import python_utils.jobset as jobset
+import python_utils.report_utils as report_utils
+import python_utils.watch_dirs as watch_dirs
 
 
 _ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
@@ -116,7 +116,7 @@ class Config(object):
 def get_c_tests(travis, test_lang) :
   out = []
   platforms_str = 'ci_platforms' if travis else 'platforms'
-  with open('tools/run_tests/tests.json') as f:
+  with open('tools/run_tests/generated/tests.json') as f:
     js = json.load(f)
     return [tgt
             for tgt in js
@@ -231,6 +231,9 @@ class CLanguage(object):
              'GRPC_POLL_STRATEGY': polling_strategy,
              'GRPC_VERBOSITY': 'DEBUG'}
         shortname_ext = '' if polling_strategy=='all' else ' GRPC_POLL_STRATEGY=%s' % polling_strategy
+        timeout_scaling = 1
+        if polling_strategy == 'poll-cv':
+          timeout_scaling *= 5
         if self.config.build_config in target['exclude_configs']:
           continue
         if self.args.iomgr_platform in target.get('exclude_iomgrs', []):
@@ -269,6 +272,7 @@ class CLanguage(object):
                 out.append(self.config.job_spec(cmdline,
                                                 shortname='%s --gtest_filter=%s %s' % (binary, test, shortname_ext),
                                                 cpu_cost=cpu_cost,
+                                                timeout_seconds=_DEFAULT_TIMEOUT_SECONDS * timeout_scaling,
                                                 environ=env))
           else:
             cmdline = [binary] + target['args']
@@ -279,7 +283,7 @@ class CLanguage(object):
                                                       shortname_ext,
                                             cpu_cost=cpu_cost,
                                             flaky=target.get('flaky', False),
-                                            timeout_seconds=target.get('timeout_seconds', _DEFAULT_TIMEOUT_SECONDS),
+                                            timeout_seconds=target.get('timeout_seconds', _DEFAULT_TIMEOUT_SECONDS) * timeout_scaling,
                                             environ=env))
         elif self.args.regex == '.*' or self.platform == 'windows':
           print('\nWARNING: binary not found, skipping', binary)
@@ -296,7 +300,7 @@ class CLanguage(object):
 
   def pre_build_steps(self):
     if self.platform == 'windows':
-      return [['tools\\run_tests\\pre_build_c.bat']]
+      return [['tools\\run_tests\\helper_scripts\\pre_build_c.bat']]
     else:
       return []
 
@@ -307,7 +311,7 @@ class CLanguage(object):
     if self.platform == 'windows':
       return []
     else:
-      return [['tools/run_tests/post_tests_c.sh']]
+      return [['tools/run_tests/helper_scripts/post_tests_c.sh']]
 
   def makefile_name(self):
     return 'Makefile'
@@ -367,28 +371,43 @@ class NodeLanguage(object):
   def configure(self, config, args):
     self.config = config
     self.args = args
+    # Note: electron ABI only depends on major and minor version, so that's all
+    # we should specify in the compiler argument
     _check_compiler(self.args.compiler, ['default', 'node0.12',
                                          'node4', 'node5', 'node6',
-                                         'node7'])
+                                         'node7', 'electron1.3'])
     if self.args.compiler == 'default':
+      self.runtime = 'node'
       self.node_version = '4'
     else:
-      # Take off the word "node"
-      self.node_version = self.args.compiler[4:]
+      if self.args.compiler.startswith('electron'):
+        self.runtime = 'electron'
+        self.node_version = self.args.compiler[8:]
+      else:
+        self.runtime = 'node'
+        # Take off the word "node"
+        self.node_version = self.args.compiler[4:]
 
   def test_specs(self):
     if self.platform == 'windows':
-      return [self.config.job_spec(['tools\\run_tests\\run_node.bat'], None)]
+      return [self.config.job_spec(['tools\\run_tests\\helper_scripts\\run_node.bat'])]
     else:
-      return [self.config.job_spec(['tools/run_tests/run_node.sh', self.node_version],
+      run_script = 'run_node'
+      if self.runtime == 'electron':
+        run_script += '_electron'
+      return [self.config.job_spec(['tools/run_tests/helper_scripts/{}.sh'.format(run_script),
+                                    self.node_version],
                                    None,
                                    environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
 
   def pre_build_steps(self):
     if self.platform == 'windows':
-      return [['tools\\run_tests\\pre_build_node.bat']]
+      return [['tools\\run_tests\\helper_scripts\\pre_build_node.bat']]
     else:
-      return [['tools/run_tests/pre_build_node.sh', self.node_version]]
+      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]]
 
   def make_targets(self):
     return []
@@ -398,9 +417,14 @@ class NodeLanguage(object):
 
   def build_steps(self):
     if self.platform == 'windows':
-      return [['tools\\run_tests\\build_node.bat']]
+      return [['tools\\run_tests\\helper_scripts\\build_node.bat']]
     else:
-      return [['tools/run_tests/build_node.sh', self.node_version]]
+      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]]
 
   def post_tests_steps(self):
     return []
@@ -423,7 +447,7 @@ class PhpLanguage(object):
     _check_compiler(self.args.compiler, ['default'])
 
   def test_specs(self):
-    return [self.config.job_spec(['src/php/bin/run_tests.sh'], None,
+    return [self.config.job_spec(['src/php/bin/run_tests.sh'],
                                   environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
 
   def pre_build_steps(self):
@@ -436,10 +460,10 @@ class PhpLanguage(object):
     return []
 
   def build_steps(self):
-    return [['tools/run_tests/build_php.sh']]
+    return [['tools/run_tests/helper_scripts/build_php.sh']]
 
   def post_tests_steps(self):
-    return [['tools/run_tests/post_tests_php.sh']]
+    return [['tools/run_tests/helper_scripts/post_tests_php.sh']]
 
   def makefile_name(self):
     return 'Makefile'
@@ -459,7 +483,7 @@ class Php7Language(object):
     _check_compiler(self.args.compiler, ['default'])
 
   def test_specs(self):
-    return [self.config.job_spec(['src/php/bin/run_tests.sh'], None,
+    return [self.config.job_spec(['src/php/bin/run_tests.sh'],
                                   environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
 
   def pre_build_steps(self):
@@ -472,10 +496,10 @@ class Php7Language(object):
     return []
 
   def build_steps(self):
-    return [['tools/run_tests/build_php.sh']]
+    return [['tools/run_tests/helper_scripts/build_php.sh']]
 
   def post_tests_steps(self):
-    return [['tools/run_tests/post_tests_php.sh']]
+    return [['tools/run_tests/helper_scripts/post_tests_php.sh']]
 
   def makefile_name(self):
     return 'Makefile'
@@ -507,7 +531,7 @@ class PythonLanguage(object):
         config.run,
         timeout_seconds=5*60,
         environ=dict(list(environment.items()) +
-                     [('GRPC_PYTHON_TESTRUNNER_FILTER', suite_name)]),
+                     [('GRPC_PYTHON_TESTRUNNER_FILTER', str(suite_name))]),
         shortname='%s.test.%s' % (config.name, suite_name),)
         for suite_name in tests_json
         for config in self.pythons]
@@ -544,18 +568,18 @@ class PythonLanguage(object):
 
     if os.name == 'nt':
       shell = ['bash']
-      builder = [os.path.abspath('tools/run_tests/build_python_msys2.sh')]
+      builder = [os.path.abspath('tools/run_tests/helper_scripts/build_python_msys2.sh')]
       builder_prefix_arguments = ['MINGW{}'.format(bits)]
       venv_relative_python = ['Scripts/python.exe']
       toolchain = ['mingw32']
     else:
       shell = []
-      builder = [os.path.abspath('tools/run_tests/build_python.sh')]
+      builder = [os.path.abspath('tools/run_tests/helper_scripts/build_python.sh')]
       builder_prefix_arguments = []
       venv_relative_python = ['bin/python']
       toolchain = ['unix']
 
-    runner = [os.path.abspath('tools/run_tests/run_python.sh')]
+    runner = [os.path.abspath('tools/run_tests/helper_scripts/run_python.sh')]
     config_vars = _PythonConfigVars(shell, builder, builder_prefix_arguments,
                               venv_relative_python, toolchain, runner)
     python27_config = _python_config_generator(name='py27', major='2',
@@ -607,12 +631,12 @@ class RubyLanguage(object):
     _check_compiler(self.args.compiler, ['default'])
 
   def test_specs(self):
-    return [self.config.job_spec(['tools/run_tests/run_ruby.sh'],
+    return [self.config.job_spec(['tools/run_tests/helper_scripts/run_ruby.sh'],
                                  timeout_seconds=10*60,
                                  environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
 
   def pre_build_steps(self):
-    return [['tools/run_tests/pre_build_ruby.sh']]
+    return [['tools/run_tests/helper_scripts/pre_build_ruby.sh']]
 
   def make_targets(self):
     return []
@@ -621,10 +645,10 @@ class RubyLanguage(object):
     return []
 
   def build_steps(self):
-    return [['tools/run_tests/build_ruby.sh']]
+    return [['tools/run_tests/helper_scripts/build_ruby.sh']]
 
   def post_tests_steps(self):
-    return [['tools/run_tests/post_tests_ruby.sh']]
+    return [['tools/run_tests/helper_scripts/post_tests_ruby.sh']]
 
   def makefile_name(self):
     return 'Makefile'
@@ -698,7 +722,6 @@ class CSharpLanguage(object):
         for test in tests_by_assembly[assembly]:
           cmdline = runtime_cmd + [assembly_file, '--test=%s' % test] + nunit_args
           specs.append(self.config.job_spec(cmdline,
-                                            None,
                                             shortname='csharp.%s' % test,
                                             environ=_FORCE_ENVIRON_FOR_WRAPPERS))
       else:
@@ -716,7 +739,6 @@ class CSharpLanguage(object):
         # to prevent problems with registering the profiler.
         run_exclusive = 1000000
         specs.append(self.config.job_spec(cmdline,
-                                          None,
                                           shortname='csharp.coverage.%s' % assembly,
                                           cpu_cost=run_exclusive,
                                           environ=_FORCE_ENVIRON_FOR_WRAPPERS))
@@ -724,9 +746,9 @@ class CSharpLanguage(object):
 
   def pre_build_steps(self):
     if self.platform == 'windows':
-      return [['tools\\run_tests\\pre_build_csharp.bat']]
+      return [['tools\\run_tests\\helper_scripts\\pre_build_csharp.bat']]
     else:
-      return [['tools/run_tests/pre_build_csharp.sh']]
+      return [['tools/run_tests/helper_scripts/pre_build_csharp.sh']]
 
   def make_targets(self):
     return ['grpc_csharp_ext']
@@ -737,22 +759,22 @@ class CSharpLanguage(object):
   def build_steps(self):
     if self.args.compiler == 'coreclr':
       if self.platform == 'windows':
-        return [['tools\\run_tests\\build_csharp_coreclr.bat']]
+        return [['tools\\run_tests\\helper_scripts\\build_csharp_coreclr.bat']]
       else:
-        return [['tools/run_tests/build_csharp_coreclr.sh']]
+        return [['tools/run_tests/helper_scripts/build_csharp_coreclr.sh']]
     else:
       if self.platform == 'windows':
         return [[_windows_build_bat(self.args.compiler),
                  'src/csharp/Grpc.sln',
                  '/p:Configuration=%s' % _MSBUILD_CONFIG[self.config.build_config]]]
       else:
-        return [['tools/run_tests/build_csharp.sh']]
+        return [['tools/run_tests/helper_scripts/build_csharp.sh']]
 
   def post_tests_steps(self):
     if self.platform == 'windows':
-      return [['tools\\run_tests\\post_tests_csharp.bat']]
+      return [['tools\\run_tests\\helper_scripts\\post_tests_csharp.bat']]
     else:
-      return [['tools/run_tests/post_tests_csharp.sh']]
+      return [['tools/run_tests/helper_scripts/post_tests_csharp.sh']]
 
   def makefile_name(self):
     return 'Makefile'
@@ -775,7 +797,7 @@ class ObjCLanguage(object):
   def test_specs(self):
     return [
         self.config.job_spec(['src/objective-c/tests/run_tests.sh'],
-                              timeout_seconds=None,
+                              timeout_seconds=60*60,
                               shortname='objc-tests',
                               environ=_FORCE_ENVIRON_FOR_WRAPPERS),
         self.config.job_spec(['src/objective-c/tests/build_example_test.sh'],
@@ -819,8 +841,12 @@ class Sanity(object):
   def test_specs(self):
     import yaml
     with open('tools/run_tests/sanity/sanity_tests.yaml', 'r') as f:
+      environ={'TEST': 'true'}
+      if _is_use_docker_child():
+        environ['CLANG_FORMAT_SKIP_DOCKER'] = 'true'
       return [self.config.job_spec(cmd['script'].split(),
-                                   timeout_seconds=None, environ={'TEST': 'true'},
+                                   timeout_seconds=30*60,
+                                   environ=environ,
                                    cpu_cost=cmd.get('cpu_cost', 1))
               for cmd in yaml.load(f)]
 
@@ -871,9 +897,9 @@ class NodeExpressLanguage(object):
 
   def pre_build_steps(self):
     if self.platform == 'windows':
-      return [['tools\\run_tests\\pre_build_node.bat']]
+      return [['tools\\run_tests\\helper_scripts\\pre_build_node.bat']]
     else:
-      return [['tools/run_tests/pre_build_node.sh', self.node_version]]
+      return [['tools/run_tests/helper_scripts/pre_build_node.sh', self.node_version]]
 
   def make_targets(self):
     return []
@@ -897,7 +923,7 @@ class NodeExpressLanguage(object):
     return 'node_express'
 
 # different configurations we can run under
-with open('tools/run_tests/configs.json') as f:
+with open('tools/run_tests/generated/configs.json') as f:
   _CONFIGS = dict((cfg['config'], Config(**cfg)) for cfg in ast.literal_eval(f.read()))
 
 
@@ -1071,6 +1097,7 @@ argp.add_argument('--compiler',
                            'vs2010', 'vs2013', 'vs2015',
                            'python2.7', 'python3.4', 'python3.5', 'python3.6', 'pypy', 'pypy3',
                            'node0.12', 'node4', 'node5', 'node6', 'node7',
+                           'electron1.3',
                            'coreclr'],
                   default='default',
                   help='Selects compiler to use. Allowed values depend on the platform and language.')
@@ -1093,6 +1120,12 @@ argp.add_argument('-x', '--xml_report', default=None, type=str,
         help='Generates a JUnit-compatible XML report')
 argp.add_argument('--report_suite_name', default='tests', type=str,
         help='Test suite name to use in generated JUnit XML report')
+argp.add_argument('--quiet_success',
+                  default=False,
+                  action='store_const',
+                  const=True,
+                  help='Dont print anything when a test passes. Passing tests also will not be reported in XML report. ' +
+                       'Useful when running many iterations of each test (argument -n).')
 argp.add_argument('--force_default_poller', default=False, action='store_const', const=True,
                   help='Dont try to iterate over many polling strategies when they exist')
 args = argp.parse_args()
@@ -1292,7 +1325,7 @@ def _start_port_server(port_server_port):
     running = False
   if running:
     current_version = int(subprocess.check_output(
-        [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
+        [sys.executable, os.path.abspath('tools/run_tests/python_utils/port_server.py'),
          'dump_version']))
     print('my port server is version %d' % current_version)
     running = (version >= current_version)
@@ -1304,7 +1337,7 @@ def _start_port_server(port_server_port):
     fd, logfile = tempfile.mkstemp()
     os.close(fd)
     print('starting port_server, with log file %s' % logfile)
-    args = [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
+    args = [sys.executable, os.path.abspath('tools/run_tests/python_utils/port_server.py'),
             '-p', '%d' % port_server_port, '-l', logfile]
     env = dict(os.environ)
     env['BUILD_ID'] = 'pleaseDontKillMeJenkins'
@@ -1410,7 +1443,7 @@ def _build_and_run(
     return []
 
   # start antagonists
-  antagonists = [subprocess.Popen(['tools/run_tests/antagonist.py'])
+  antagonists = [subprocess.Popen(['tools/run_tests/python_utils/antagonist.py'])
                  for _ in range(0, args.antagonists)]
   port_server_port = 32766
   _start_port_server(port_server_port)
@@ -1440,20 +1473,24 @@ def _build_and_run(
                      else itertools.repeat(massaged_one_run, runs_per_test))
     all_runs = itertools.chain.from_iterable(runs_sequence)
 
+    if args.quiet_success:
+      jobset.message('START', 'Running tests quietly, only failing tests will be reported', do_newline=True)
     num_test_failures, resultset = jobset.run(
         all_runs, check_cancelled, newline_on_success=newline_on_success,
         travis=args.travis, infinite_runs=infinite_runs, maxjobs=args.jobs,
         stop_on_failure=args.stop_on_failure,
-        add_env={'GRPC_TEST_PORT_SERVER': 'localhost:%d' % port_server_port})
+        add_env={'GRPC_TEST_PORT_SERVER': 'localhost:%d' % port_server_port},
+        quiet_success=args.quiet_success)
     if resultset:
       for k, v in sorted(resultset.items()):
         num_runs, num_failures = _calculate_num_runs_failures(v)
-        if num_failures == num_runs:  # what about infinite_runs???
-          jobset.message('FAILED', k, do_newline=True)
-        elif num_failures > 0:
-          jobset.message(
-              'FLAKE', '%s [%d/%d runs flaked]' % (k, num_failures, num_runs),
-              do_newline=True)
+        if num_failures > 0:
+          if num_failures == num_runs:  # what about infinite_runs???
+            jobset.message('FAILED', k, do_newline=True)
+          else:
+            jobset.message(
+                'FLAKE', '%s [%d/%d runs flaked]' % (k, num_failures, num_runs),
+                do_newline=True)
   finally:
     for antagonist in antagonists:
       antagonist.kill()
diff --git a/tools/run_tests/run_tests_matrix.py b/tools/run_tests/run_tests_matrix.py
index 989bc7eb218c1169eba5ad4b2769d6d32e08915b..8f70a6d2ea9ba049a7f7c6327a88bc1fbf77ee18 100755
--- a/tools/run_tests/run_tests_matrix.py
+++ b/tools/run_tests/run_tests_matrix.py
@@ -31,12 +31,13 @@
 """Run test matrix."""
 
 import argparse
-import jobset
 import multiprocessing
 import os
-import report_utils
 import sys
-from filter_pull_request_tests import filter_tests
+
+import python_utils.jobset as jobset
+import python_utils.report_utils as report_utils
+from python_utils.filter_pull_request_tests import filter_tests
 
 _ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
 os.chdir(_ROOT)
@@ -69,7 +70,7 @@ def _workspace_jobspec(name, runtests_args=[], workspace_name=None, inner_jobs=_
     workspace_name = 'workspace_%s' % name
   env = {'WORKSPACE_NAME': workspace_name}
   test_job = jobset.JobSpec(
-          cmdline=['tools/run_tests/run_tests_in_workspace.sh',
+          cmdline=['tools/run_tests/helper_scripts/run_tests_in_workspace.sh',
                    '-t',
                    '-j', str(inner_jobs),
                    '-x', '../report_%s.xml' % name,
@@ -230,6 +231,15 @@ def _create_portability_test_jobs(extra_args=[], inner_jobs=_DEFAULT_INNER_JOBS)
                               labels=['portability'],
                               extra_args=extra_args,
                               inner_jobs=inner_jobs)
+
+  test_jobs += _generate_jobs(languages=['node'],
+                              configs=['dbg'],
+                              platforms=['linux'],
+                              arch='default',
+                              compiler='electron1.3',
+                              labels=['portability'],
+                              extra_args=extra_args,
+                              inner_jobs=inner_jobs)
   return test_jobs
 
 
@@ -242,6 +252,17 @@ def _allowed_labels():
   return sorted(all_labels)
 
 
+def _runs_per_test_type(arg_str):
+  """Auxiliary function to parse the "runs_per_test" flag."""
+  try:
+    n = int(arg_str)
+    if n <= 0: raise ValueError
+    return n
+  except:
+    msg = '\'{}\' is not a positive integer'.format(arg_str)
+    raise argparse.ArgumentTypeError(msg)
+
+
 if __name__ == "__main__":
   argp = argparse.ArgumentParser(description='Run a matrix of run_tests.py tests.')
   argp.add_argument('-j', '--jobs',
@@ -253,6 +274,11 @@ if __name__ == "__main__":
                     nargs='+',
                     default=[],
                     help='Filter targets to run by label with AND semantics.')
+  argp.add_argument('--exclude',
+                    choices=_allowed_labels(),
+                    nargs='+',
+                    default=[],
+                    help='Exclude targets with any of given labels.')
   argp.add_argument('--build_only',
                     default=False,
                     action='store_const',
@@ -269,7 +295,7 @@ if __name__ == "__main__":
                     default=False,
                     action='store_const',
                     const=True,
-                    help='Filters out tests irrelavant to pull request changes.')
+                    help='Filters out tests irrelevant to pull request changes.')
   argp.add_argument('--base_branch',
                     default='origin/master',
                     type=str,
@@ -278,6 +304,9 @@ if __name__ == "__main__":
                     default=_DEFAULT_INNER_JOBS,
                     type=int,
                     help='Number of jobs in each run_tests.py instance')
+  argp.add_argument('-n', '--runs_per_test', default=1, type=_runs_per_test_type,
+                    help='How many times to run each tests. >1 runs implies ' +
+                    'omitting passing test from the output & reports.')
   args = argp.parse_args()
 
   extra_args = []
@@ -285,6 +314,10 @@ if __name__ == "__main__":
     extra_args.append('--build_only')
   if args.force_default_poller:
     extra_args.append('--force_default_poller')
+  if args.runs_per_test > 1:
+    extra_args.append('-n')
+    extra_args.append('%s' % args.runs_per_test)
+    extra_args.append('--quiet_success')
 
   all_jobs = _create_test_jobs(extra_args=extra_args, inner_jobs=args.inner_jobs) + \
              _create_portability_test_jobs(extra_args=extra_args, inner_jobs=args.inner_jobs)
@@ -292,7 +325,8 @@ if __name__ == "__main__":
   jobs = []
   for job in all_jobs:
     if not args.filter or all(filter in job.labels for filter in args.filter):
-      jobs.append(job)
+      if not any(exclude_label in job.labels for exclude_label in args.exclude):
+        jobs.append(job)
 
   if not jobs:
     jobset.message('FAILED', 'No test suites match given criteria.',
diff --git a/tools/run_tests/sanity/check_sources_and_headers.py b/tools/run_tests/sanity/check_sources_and_headers.py
index b733ba173f179bf6787a4ec100a063a04c6b32ab..a86db02b80bf7841a952c78538b3e4dba05d0f1f 100755
--- a/tools/run_tests/sanity/check_sources_and_headers.py
+++ b/tools/run_tests/sanity/check_sources_and_headers.py
@@ -34,7 +34,7 @@ import re
 import sys
 
 root = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../../..'))
-with open(os.path.join(root, 'tools', 'run_tests', 'sources_and_headers.json')) as f:
+with open(os.path.join(root, 'tools', 'run_tests', 'generated', 'sources_and_headers.json')) as f:
   js = json.loads(f.read())
 
 re_inc1 = re.compile(r'^#\s*include\s*"([^"]*)"')
diff --git a/tools/run_tests/sanity/check_submodules.sh b/tools/run_tests/sanity/check_submodules.sh
index 6ec0786c966cd235d8001430a90cffdc440d0b4b..61e8185854b20cc3ae6e123b8babac033ac344ce 100755
--- a/tools/run_tests/sanity/check_submodules.sh
+++ b/tools/run_tests/sanity/check_submodules.sh
@@ -41,13 +41,14 @@ want_submodules=`mktemp /tmp/submXXXXXX`
 
 git submodule | awk '{ print $1 }' | sort > $submodules
 cat << EOF | awk '{ print $1 }' | sort > $want_submodules
- c880e42ba1c8032d4cdde2aba0541d8a9d9fa2e9 third_party/boringssl (version_for_cocoapods_2.0-100-gc880e42)
+ 44c25c892a6229b20db7cd9dc05584ea865896de third_party/benchmark (v0.1.0-343-g44c25c8)
+ c880e42ba1c8032d4cdde2aba0541d8a9d9fa2e9 third_party/boringssl (c880e42)
+ 886e7d75368e3f4fab3f4d0d3584e4abfc557755 third_party/boringssl-with-bazel (version_for_cocoapods_7.0-857-g886e7d7)
  05b155ff59114735ec8cd089f669c4c3d8f59029 third_party/gflags (v2.1.0-45-g05b155f)
- 44c25c892a6229b20db7cd9dc05584ea865896de third_party/google_benchmark (v0.1.0-343-g44c25c8)
  c99458533a9b4c743ed51537e25989ea55944908 third_party/googletest (release-1.7.0)
- a428e42072765993ff674fda72863c9f1aa2d268 third_party/protobuf (v3.1.0)
+ a428e42072765993ff674fda72863c9f1aa2d268 third_party/protobuf (v3.1.0-alpha-1)
+ bcad91771b7f0bff28a1cac1981d7ef2b9bcef3c third_party/thrift (bcad917)
  50893291621658f355bc5b4d450a8d06a563053d third_party/zlib (v1.2.8)
- bcad91771b7f0bff28a1cac1981d7ef2b9bcef3c third_party/thrift
 EOF
 
 diff -u $submodules $want_submodules
diff --git a/tools/run_tests/sanity/check_test_filtering.py b/tools/run_tests/sanity/check_test_filtering.py
index b522cdeb49a697a26cc56f2eabc9e44cc3611b9c..290a6e2ddf604bb63cd951489358bda72f516ab9 100755
--- a/tools/run_tests/sanity/check_test_filtering.py
+++ b/tools/run_tests/sanity/check_test_filtering.py
@@ -38,7 +38,7 @@ import re
 # hack import paths to pick up extra code
 sys.path.insert(0, os.path.abspath('tools/run_tests/'))
 from run_tests_matrix import _create_test_jobs, _create_portability_test_jobs
-import filter_pull_request_tests
+import python_utils.filter_pull_request_tests as filter_pull_request_tests
 
 _LIST_OF_LANGUAGE_LABELS = ['c', 'c++', 'csharp', 'node', 'objc', 'php', 'php7', 'python', 'ruby']
 _LIST_OF_PLATFORM_LABELS = ['linux', 'macos', 'windows']
diff --git a/tools/run_tests/sanity/core_banned_functions.py b/tools/run_tests/sanity/core_banned_functions.py
new file mode 100755
index 0000000000000000000000000000000000000000..afac10bf80e72b4a26f5e7ca9d2bd829b07c23c1
--- /dev/null
+++ b/tools/run_tests/sanity/core_banned_functions.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python2.7
+
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import sys
+
+os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '../../..'))
+
+# map of banned function signature to whitelist
+BANNED_EXCEPT = {
+    'grpc_resource_quota_ref(': ['src/core/lib/iomgr/resource_quota.c'],
+    'grpc_resource_quota_unref(': ['src/core/lib/iomgr/resource_quota.c'],
+    'grpc_slice_buffer_destroy(': ['src/core/lib/slice/slice_buffer.c'],
+    'grpc_slice_buffer_reset_and_unref(': ['src/core/lib/slice/slice_buffer.c'],
+    'grpc_slice_ref(': ['src/core/lib/slice/slice.c'],
+    'grpc_slice_unref(': ['src/core/lib/slice/slice.c'],
+}
+
+errors = 0
+for root, dirs, files in os.walk('src/core'):
+  for filename in files:
+    path = os.path.join(root, filename)
+    if os.path.splitext(path)[1] != '.c': continue
+    with open(path) as f:
+      text = f.read()
+    for banned, exceptions in BANNED_EXCEPT.items():
+      if path in exceptions: continue
+      if banned in text:
+        print 'Illegal use of "%s" in %s' % (banned, path)
+        errors += 1
+
+assert errors == 0
diff --git a/tools/run_tests/sanity/sanity_tests.yaml b/tools/run_tests/sanity/sanity_tests.yaml
index 32e62dd5292aa3b309249af4c5a6a9e2ea004812..37819166e33ff3ea3c5f9656937947c029e7b0ae 100644
--- a/tools/run_tests/sanity/sanity_tests.yaml
+++ b/tools/run_tests/sanity/sanity_tests.yaml
@@ -3,6 +3,7 @@
 - script: tools/run_tests/sanity/check_sources_and_headers.py
 - script: tools/run_tests/sanity/check_submodules.sh
 - script: tools/run_tests/sanity/check_test_filtering.py
+- script: tools/run_tests/sanity/core_banned_functions.py
 - script: tools/buildgen/generate_projects.sh -j 3
   cpu_cost: 3
 - script: tools/distrib/check_copyright.py
@@ -12,3 +13,4 @@
 - script: tools/distrib/check_nanopb_output.sh
 - script: tools/distrib/check_include_guards.py
 - script: tools/distrib/python/check_grpcio_tools.py
+
diff --git a/tools/run_tests/task_runner.py b/tools/run_tests/task_runner.py
index 2e3fa443b960075f16139b485796957c23bd7568..fdc46682223fed735454c3bb585bc8e0550b2f5d 100755
--- a/tools/run_tests/task_runner.py
+++ b/tools/run_tests/task_runner.py
@@ -33,14 +33,13 @@
 from __future__ import print_function
 
 import argparse
-import atexit
-import jobset
 import multiprocessing
 import sys
 
-import artifact_targets
-import distribtest_targets
-import package_targets
+import artifacts.artifact_targets as artifact_targets
+import artifacts.distribtest_targets as distribtest_targets
+import artifacts.package_targets as package_targets
+import python_utils.jobset as jobset
 
 _TARGETS = []
 _TARGETS += artifact_targets.targets()
diff --git a/vsprojects/README.md b/vsprojects/README.md
index 56d9f5600980b9dbbd351ced844cbfc9220d95ed..afd6430bfe5320e9a9e62e43b44acf12acf43dee 100644
--- a/vsprojects/README.md
+++ b/vsprojects/README.md
@@ -2,7 +2,7 @@ This directory contains MS Visual Studio project & solution files.
 
 #Supported Visual Studio versions
 
-Currently supported versions are Visual Studio 2013 (our primary focus) and 2010.
+Currently supported versions are Visual Studio 2013 (our primary focus).
 
 #Building
 We are using [NuGet](http://www.nuget.org) to pull zlib and openssl dependencies.
@@ -83,10 +83,12 @@ Windows .exe binaries of gRPC protoc plugins.
 1. Follow instructions in `third_party\protobuf\cmake\README.md` to create Visual Studio 2013 projects for protobuf.
 ```
 $ cd third_party/protobuf/cmake
-$ cmake -G "Visual Studio 12 2013"
+$ mkdir build & cd build
+$ mkdir solution & cd solution
+$ cmake -G "Visual Studio 12 2013" -Dprotobuf_BUILD_TESTS=OFF ../..
 ```
 
-2. Open solution `third_party\protobuf\cmake\protobuf.sln` and build it in Release mode. That will build libraries `libprotobuf.lib` and `libprotoc.lib` needed for the next step.
+2. Open solution `third_party\protobuf\cmake\build\solution\protobuf.sln` and build it in Release mode. That will build libraries `libprotobuf.lib` and `libprotoc.lib` needed for the next step.
 
 3. Open solution `vsprojects\grpc_protoc_plugins.sln` and build it in Release mode. As a result, you should obtain a set of gRPC protoc plugin binaries (`grpc_cpp_plugin.exe`, `grpc_csharp_plugin.exe`, ...)
 
diff --git a/vsprojects/build_plugins.bat b/vsprojects/build_plugins.bat
index 7c8e056dc4efad4a3fb65de27a6574dbcc9a4293..ae5c5f09bed382b478423794fd89ee6b60ca1a82 100644
--- a/vsprojects/build_plugins.bat
+++ b/vsprojects/build_plugins.bat
@@ -38,7 +38,7 @@ cd /d %~dp0
 @call "%VS120COMNTOOLS%\..\..\vc\vcvarsall.bat" x86
 
 @rem Build third_party/protobuf
-msbuild ..\third_party\protobuf\cmake\protobuf.sln /p:Configuration=Release || goto :error
+msbuild ..\third_party\protobuf\cmake\build\solution\protobuf.sln /p:Configuration=Release || goto :error
 
 @rem Build the C# protoc plugins
 msbuild grpc_protoc_plugins.sln /p:Configuration=Release || goto :error
diff --git a/vsprojects/build_vs2010.bat b/vsprojects/build_vs2010.bat
deleted file mode 100644
index d951295369651beb2967624e7a5e775d86dc60cb..0000000000000000000000000000000000000000
--- a/vsprojects/build_vs2010.bat
+++ /dev/null
@@ -1,39 +0,0 @@
-@rem Copyright 2016, Google Inc.
-@rem All rights reserved.
-@rem
-@rem Redistribution and use in source and binary forms, with or without
-@rem modification, are permitted provided that the following conditions are
-@rem met:
-@rem
-@rem     * Redistributions of source code must retain the above copyright
-@rem notice, this list of conditions and the following disclaimer.
-@rem     * Redistributions in binary form must reproduce the above
-@rem copyright notice, this list of conditions and the following disclaimer
-@rem in the documentation and/or other materials provided with the
-@rem distribution.
-@rem     * Neither the name of Google Inc. nor the names of its
-@rem contributors may be used to endorse or promote products derived from
-@rem this software without specific prior written permission.
-@rem
-@rem THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-@rem "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-@rem LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-@rem A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-@rem OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-@rem SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-@rem LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-@rem DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-@rem THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-@rem (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-@rem OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-@rem Convenience wrapper that runs specified gRPC target using msbuild
-@rem Usage: build_vs2010.bat TARGET_NAME
-
-setlocal
-@rem Set VS variables (uses Visual Studio 2010)
-@call "%VS100COMNTOOLS%\..\..\vc\vcvarsall.bat" x86
-
-msbuild %*
-exit /b %ERRORLEVEL%
-endlocal
diff --git a/vsprojects/buildtests_c.sln b/vsprojects/buildtests_c.sln
index d08a47aa86e621c042e489613a6eb6c0ac30c88f..f484f29a4b25dc3f6412404fe5634a68435f00da 100644
--- a/vsprojects/buildtests_c.sln
+++ b/vsprojects/buildtests_c.sln
@@ -1202,6 +1202,28 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "load_file_test", "vcxproj\t
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "memory_profile_client", "vcxproj\test\memory_profile_client\memory_profile_client.vcxproj", "{98C01DBE-EFFE-6988-0762-829DC88F0EB4}"
+	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}") = "memory_profile_server", "vcxproj\test\memory_profile_server\memory_profile_server.vcxproj", "{7430B8AF-2A0E-EDF4-FD53-C8004DEE39EC}"
+	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}") = "message_compress_test", "vcxproj\test\message_compress_test\message_compress_test.vcxproj", "{07170557-CCB0-D23C-8018-C2909D115DF9}"
 	ProjectSection(myProperties) = preProject
         	lib = "False"
@@ -3377,6 +3399,38 @@ Global
 		{DC76C089-0D55-DF19-7CCA-49DAE5D29E49}.Release-DLL|Win32.Build.0 = Release|Win32
 		{DC76C089-0D55-DF19-7CCA-49DAE5D29E49}.Release-DLL|x64.ActiveCfg = Release|x64
 		{DC76C089-0D55-DF19-7CCA-49DAE5D29E49}.Release-DLL|x64.Build.0 = Release|x64
+		{98C01DBE-EFFE-6988-0762-829DC88F0EB4}.Debug|Win32.ActiveCfg = Debug|Win32
+		{98C01DBE-EFFE-6988-0762-829DC88F0EB4}.Debug|x64.ActiveCfg = Debug|x64
+		{98C01DBE-EFFE-6988-0762-829DC88F0EB4}.Release|Win32.ActiveCfg = Release|Win32
+		{98C01DBE-EFFE-6988-0762-829DC88F0EB4}.Release|x64.ActiveCfg = Release|x64
+		{98C01DBE-EFFE-6988-0762-829DC88F0EB4}.Debug|Win32.Build.0 = Debug|Win32
+		{98C01DBE-EFFE-6988-0762-829DC88F0EB4}.Debug|x64.Build.0 = Debug|x64
+		{98C01DBE-EFFE-6988-0762-829DC88F0EB4}.Release|Win32.Build.0 = Release|Win32
+		{98C01DBE-EFFE-6988-0762-829DC88F0EB4}.Release|x64.Build.0 = Release|x64
+		{98C01DBE-EFFE-6988-0762-829DC88F0EB4}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{98C01DBE-EFFE-6988-0762-829DC88F0EB4}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{98C01DBE-EFFE-6988-0762-829DC88F0EB4}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{98C01DBE-EFFE-6988-0762-829DC88F0EB4}.Debug-DLL|x64.Build.0 = Debug|x64
+		{98C01DBE-EFFE-6988-0762-829DC88F0EB4}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{98C01DBE-EFFE-6988-0762-829DC88F0EB4}.Release-DLL|Win32.Build.0 = Release|Win32
+		{98C01DBE-EFFE-6988-0762-829DC88F0EB4}.Release-DLL|x64.ActiveCfg = Release|x64
+		{98C01DBE-EFFE-6988-0762-829DC88F0EB4}.Release-DLL|x64.Build.0 = Release|x64
+		{7430B8AF-2A0E-EDF4-FD53-C8004DEE39EC}.Debug|Win32.ActiveCfg = Debug|Win32
+		{7430B8AF-2A0E-EDF4-FD53-C8004DEE39EC}.Debug|x64.ActiveCfg = Debug|x64
+		{7430B8AF-2A0E-EDF4-FD53-C8004DEE39EC}.Release|Win32.ActiveCfg = Release|Win32
+		{7430B8AF-2A0E-EDF4-FD53-C8004DEE39EC}.Release|x64.ActiveCfg = Release|x64
+		{7430B8AF-2A0E-EDF4-FD53-C8004DEE39EC}.Debug|Win32.Build.0 = Debug|Win32
+		{7430B8AF-2A0E-EDF4-FD53-C8004DEE39EC}.Debug|x64.Build.0 = Debug|x64
+		{7430B8AF-2A0E-EDF4-FD53-C8004DEE39EC}.Release|Win32.Build.0 = Release|Win32
+		{7430B8AF-2A0E-EDF4-FD53-C8004DEE39EC}.Release|x64.Build.0 = Release|x64
+		{7430B8AF-2A0E-EDF4-FD53-C8004DEE39EC}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{7430B8AF-2A0E-EDF4-FD53-C8004DEE39EC}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{7430B8AF-2A0E-EDF4-FD53-C8004DEE39EC}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{7430B8AF-2A0E-EDF4-FD53-C8004DEE39EC}.Debug-DLL|x64.Build.0 = Debug|x64
+		{7430B8AF-2A0E-EDF4-FD53-C8004DEE39EC}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{7430B8AF-2A0E-EDF4-FD53-C8004DEE39EC}.Release-DLL|Win32.Build.0 = Release|Win32
+		{7430B8AF-2A0E-EDF4-FD53-C8004DEE39EC}.Release-DLL|x64.ActiveCfg = Release|x64
+		{7430B8AF-2A0E-EDF4-FD53-C8004DEE39EC}.Release-DLL|x64.Build.0 = Release|x64
 		{07170557-CCB0-D23C-8018-C2909D115DF9}.Debug|Win32.ActiveCfg = Debug|Win32
 		{07170557-CCB0-D23C-8018-C2909D115DF9}.Debug|x64.ActiveCfg = Debug|x64
 		{07170557-CCB0-D23C-8018-C2909D115DF9}.Release|Win32.ActiveCfg = Release|Win32
diff --git a/vsprojects/coapp/zlib/README.md b/vsprojects/coapp/zlib/README.md
index d62e9030ed7737553ce9ccb31c701e0985eb0daf..e9a89c030eea5596237b2299c8c0072a4ab6de69 100644
--- a/vsprojects/coapp/zlib/README.md
+++ b/vsprojects/coapp/zlib/README.md
@@ -10,7 +10,7 @@ Multiple versions of VS installed to be able to build all the targets:
 * Visual Studio 2013
 * Visual Studio 2010 (you might need SP1 to prevent LNK1123 error)
 
-CoApp toolkit: http://downloads.coapp.org/files/CoApp.Tools.Powershell.msi
+CoApp toolkit: http://coapp.org/files/CoApp.Tools.Powershell.msi
 
 More details on installation: http://coapp.org/tutorials/installation.html
 
@@ -33,4 +33,4 @@ This will create three NuGet packages:
 * the symbols package (debug symbols)
 
 Later, you can push the package to NuGet.org repo.
-Attention: before pusing the resulting nuget package to public nuget repo, you have to be 100% sure it works correctly - there’s no way how to delete or update an already existing package.
\ No newline at end of file
+Attention: before pusing the resulting nuget package to public nuget repo, you have to be 100% sure it works correctly - there’s no way how to delete or update an already existing package.
diff --git a/vsprojects/grpc.sln b/vsprojects/grpc.sln
index 63be171c6efeaae3af7320a04f5b8f05d381913b..591d3b5edb244fd4c4676415d74bd2348ead6b43 100644
--- a/vsprojects/grpc.sln
+++ b/vsprojects/grpc.sln
@@ -3,11 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 2013
 VisualStudioVersion = 12.0.21005.1
 MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "boringssl", "vcxproj\.\boringssl\boringssl.vcxproj", "{9FD9A3EF-C4A3-8390-D8F4-6F86C22A58CE}"
-	ProjectSection(myProperties) = preProject
-        	lib = "True"
-	EndProjectSection
-EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gen_hpack_tables", "vcxproj\.\gen_hpack_tables\gen_hpack_tables.vcxproj", "{FCDEA4C7-7F26-05DB-D08F-A08F499026E6}"
 	ProjectSection(myProperties) = preProject
         	lib = "False"
@@ -56,14 +51,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "grpc++", "vcxproj\.\grpc++\
 		{29D16885-7228-4C31-81ED-5F9187C7F2A9} = {29D16885-7228-4C31-81ED-5F9187C7F2A9}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "grpc++_reflection", "vcxproj\.\grpc++_reflection\grpc++_reflection.vcxproj", "{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}"
-	ProjectSection(myProperties) = preProject
-        	lib = "True"
-	EndProjectSection
-	ProjectSection(ProjectDependencies) = postProject
-		{C187A093-A0FE-489D-A40A-6E33DE0F9FEB} = {C187A093-A0FE-489D-A40A-6E33DE0F9FEB}
-	EndProjectSection
-EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "grpc++_unsecure", "vcxproj\.\grpc++_unsecure\grpc++_unsecure.vcxproj", "{6EE56155-DF7C-4F6E-BFC4-F6F776BEB211}"
 	ProjectSection(myProperties) = preProject
         	lib = "True"
@@ -161,11 +148,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_tcp_server", "vcxproj\
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "z", "vcxproj\.\z\z.vcxproj", "{FBADE9E3-6A3F-36D3-D676-C1B808451DD7}"
-	ProjectSection(myProperties) = preProject
-        	lib = "True"
-	EndProjectSection
-EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
@@ -178,22 +160,6 @@ Global
 		Release-DLL|x64 = Release-DLL|x64
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{9FD9A3EF-C4A3-8390-D8F4-6F86C22A58CE}.Debug|Win32.ActiveCfg = Debug|Win32
-		{9FD9A3EF-C4A3-8390-D8F4-6F86C22A58CE}.Debug|x64.ActiveCfg = Debug|x64
-		{9FD9A3EF-C4A3-8390-D8F4-6F86C22A58CE}.Release|Win32.ActiveCfg = Release|Win32
-		{9FD9A3EF-C4A3-8390-D8F4-6F86C22A58CE}.Release|x64.ActiveCfg = Release|x64
-		{9FD9A3EF-C4A3-8390-D8F4-6F86C22A58CE}.Debug|Win32.Build.0 = Debug|Win32
-		{9FD9A3EF-C4A3-8390-D8F4-6F86C22A58CE}.Debug|x64.Build.0 = Debug|x64
-		{9FD9A3EF-C4A3-8390-D8F4-6F86C22A58CE}.Release|Win32.Build.0 = Release|Win32
-		{9FD9A3EF-C4A3-8390-D8F4-6F86C22A58CE}.Release|x64.Build.0 = Release|x64
-		{9FD9A3EF-C4A3-8390-D8F4-6F86C22A58CE}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
-		{9FD9A3EF-C4A3-8390-D8F4-6F86C22A58CE}.Debug-DLL|Win32.Build.0 = Debug|Win32
-		{9FD9A3EF-C4A3-8390-D8F4-6F86C22A58CE}.Debug-DLL|x64.ActiveCfg = Debug|x64
-		{9FD9A3EF-C4A3-8390-D8F4-6F86C22A58CE}.Debug-DLL|x64.Build.0 = Debug|x64
-		{9FD9A3EF-C4A3-8390-D8F4-6F86C22A58CE}.Release-DLL|Win32.ActiveCfg = Release|Win32
-		{9FD9A3EF-C4A3-8390-D8F4-6F86C22A58CE}.Release-DLL|Win32.Build.0 = Release|Win32
-		{9FD9A3EF-C4A3-8390-D8F4-6F86C22A58CE}.Release-DLL|x64.ActiveCfg = Release|x64
-		{9FD9A3EF-C4A3-8390-D8F4-6F86C22A58CE}.Release-DLL|x64.Build.0 = Release|x64
 		{FCDEA4C7-7F26-05DB-D08F-A08F499026E6}.Debug|Win32.ActiveCfg = Debug|Win32
 		{FCDEA4C7-7F26-05DB-D08F-A08F499026E6}.Debug|x64.ActiveCfg = Debug|x64
 		{FCDEA4C7-7F26-05DB-D08F-A08F499026E6}.Release|Win32.ActiveCfg = Release|Win32
@@ -306,22 +272,6 @@ Global
 		{C187A093-A0FE-489D-A40A-6E33DE0F9FEB}.Release-DLL|Win32.Build.0 = Release-DLL|Win32
 		{C187A093-A0FE-489D-A40A-6E33DE0F9FEB}.Release-DLL|x64.ActiveCfg = Release-DLL|x64
 		{C187A093-A0FE-489D-A40A-6E33DE0F9FEB}.Release-DLL|x64.Build.0 = Release-DLL|x64
-		{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}.Debug|Win32.ActiveCfg = Debug|Win32
-		{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}.Debug|x64.ActiveCfg = Debug|x64
-		{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}.Release|Win32.ActiveCfg = Release|Win32
-		{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}.Release|x64.ActiveCfg = Release|x64
-		{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}.Debug|Win32.Build.0 = Debug|Win32
-		{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}.Debug|x64.Build.0 = Debug|x64
-		{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}.Release|Win32.Build.0 = Release|Win32
-		{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}.Release|x64.Build.0 = Release|x64
-		{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
-		{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}.Debug-DLL|Win32.Build.0 = Debug|Win32
-		{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}.Debug-DLL|x64.ActiveCfg = Debug|x64
-		{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}.Debug-DLL|x64.Build.0 = Debug|x64
-		{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}.Release-DLL|Win32.ActiveCfg = Release|Win32
-		{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}.Release-DLL|Win32.Build.0 = Release|Win32
-		{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}.Release-DLL|x64.ActiveCfg = Release|x64
-		{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}.Release-DLL|x64.Build.0 = Release|x64
 		{6EE56155-DF7C-4F6E-BFC4-F6F776BEB211}.Debug|Win32.ActiveCfg = Debug|Win32
 		{6EE56155-DF7C-4F6E-BFC4-F6F776BEB211}.Debug|x64.ActiveCfg = Debug|x64
 		{6EE56155-DF7C-4F6E-BFC4-F6F776BEB211}.Release|Win32.ActiveCfg = Release|Win32
@@ -482,22 +432,6 @@ Global
 		{E3110C46-A148-FF65-08FD-3324829BE7FE}.Release-DLL|Win32.Build.0 = Release|Win32
 		{E3110C46-A148-FF65-08FD-3324829BE7FE}.Release-DLL|x64.ActiveCfg = Release|x64
 		{E3110C46-A148-FF65-08FD-3324829BE7FE}.Release-DLL|x64.Build.0 = Release|x64
-		{FBADE9E3-6A3F-36D3-D676-C1B808451DD7}.Debug|Win32.ActiveCfg = Debug|Win32
-		{FBADE9E3-6A3F-36D3-D676-C1B808451DD7}.Debug|x64.ActiveCfg = Debug|x64
-		{FBADE9E3-6A3F-36D3-D676-C1B808451DD7}.Release|Win32.ActiveCfg = Release|Win32
-		{FBADE9E3-6A3F-36D3-D676-C1B808451DD7}.Release|x64.ActiveCfg = Release|x64
-		{FBADE9E3-6A3F-36D3-D676-C1B808451DD7}.Debug|Win32.Build.0 = Debug|Win32
-		{FBADE9E3-6A3F-36D3-D676-C1B808451DD7}.Debug|x64.Build.0 = Debug|x64
-		{FBADE9E3-6A3F-36D3-D676-C1B808451DD7}.Release|Win32.Build.0 = Release|Win32
-		{FBADE9E3-6A3F-36D3-D676-C1B808451DD7}.Release|x64.Build.0 = Release|x64
-		{FBADE9E3-6A3F-36D3-D676-C1B808451DD7}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
-		{FBADE9E3-6A3F-36D3-D676-C1B808451DD7}.Debug-DLL|Win32.Build.0 = Debug|Win32
-		{FBADE9E3-6A3F-36D3-D676-C1B808451DD7}.Debug-DLL|x64.ActiveCfg = Debug|x64
-		{FBADE9E3-6A3F-36D3-D676-C1B808451DD7}.Debug-DLL|x64.Build.0 = Debug|x64
-		{FBADE9E3-6A3F-36D3-D676-C1B808451DD7}.Release-DLL|Win32.ActiveCfg = Release|Win32
-		{FBADE9E3-6A3F-36D3-D676-C1B808451DD7}.Release-DLL|Win32.Build.0 = Release|Win32
-		{FBADE9E3-6A3F-36D3-D676-C1B808451DD7}.Release-DLL|x64.ActiveCfg = Release|x64
-		{FBADE9E3-6A3F-36D3-D676-C1B808451DD7}.Release-DLL|x64.Build.0 = Release|x64
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/vsprojects/protobuf.props b/vsprojects/protobuf.props
index b1de8af27a568d7f43a9096c2f41071b68a75355..b828313572972a8063e765ebb7d37d494235d267 100644
--- a/vsprojects/protobuf.props
+++ b/vsprojects/protobuf.props
@@ -1 +1 @@
-<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ImportGroup Label="PropertySheets" /> <PropertyGroup Label="UserMacros" /> <PropertyGroup /> <ItemDefinitionGroup> <Link> <AdditionalDependencies>libprotobuf.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalLibraryDirectories>$(SolutionDir)\..\third_party\protobuf\cmake\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> </Link> </ItemDefinitionGroup> <ItemGroup /> </Project>
\ No newline at end of file
+<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ImportGroup Label="PropertySheets" /> <PropertyGroup Label="UserMacros" /> <PropertyGroup /> <ItemDefinitionGroup> <Link> <AdditionalDependencies>libprotobuf.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalLibraryDirectories>$(SolutionDir)\..\third_party\protobuf\cmake\build\solution\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> </Link> </ItemDefinitionGroup> <ItemGroup /> </Project>
\ No newline at end of file
diff --git a/vsprojects/protoc.props b/vsprojects/protoc.props
index 1bdc07193bce01b5aad1e89616c79b55da335ddc..87fff8f12884fb6e0461e7947fe00a271f4e52cf 100644
--- a/vsprojects/protoc.props
+++ b/vsprojects/protoc.props
@@ -1 +1 @@
-<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ImportGroup Label="PropertySheets" /> <PropertyGroup Label="UserMacros" /> <PropertyGroup /> <ItemDefinitionGroup> <ClCompile> <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> </ClCompile> <Link> <AdditionalDependencies>libprotoc.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalLibraryDirectories>$(SolutionDir)\..\third_party\protobuf\cmake\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> </Link> </ItemDefinitionGroup> <ItemGroup /> </Project>
\ No newline at end of file
+<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ImportGroup Label="PropertySheets" /> <PropertyGroup Label="UserMacros" /> <PropertyGroup /> <ItemDefinitionGroup> <ClCompile> <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> </ClCompile> <Link> <AdditionalDependencies>libprotoc.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalLibraryDirectories>$(SolutionDir)\..\third_party\protobuf\cmake\build\solution\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> </Link> </ItemDefinitionGroup> <ItemGroup /> </Project>
\ No newline at end of file
diff --git a/vsprojects/vcxproj/google_benchmark/google_benchmark.vcxproj b/vsprojects/vcxproj/benchmark/benchmark.vcxproj
similarity index 70%
rename from vsprojects/vcxproj/google_benchmark/google_benchmark.vcxproj
rename to vsprojects/vcxproj/benchmark/benchmark.vcxproj
index 52774e0802586a73446f937d04e417e708a258bf..9f262b3b00ce2ce16acfea2fa104a3b760cac1a4 100644
--- a/vsprojects/vcxproj/google_benchmark/google_benchmark.vcxproj
+++ b/vsprojects/vcxproj/benchmark/benchmark.vcxproj
@@ -19,7 +19,7 @@
     </ProjectConfiguration>
   </ItemGroup>
   <PropertyGroup Label="Globals">
-    <ProjectGuid>{AAD4AEF3-DF1E-7A6D-EC35-233BD1031BF4}</ProjectGuid>
+    <ProjectGuid>{07978586-E47C-8709-A63E-895FBF3C3C7D}</ProjectGuid>
     <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
     <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
   </PropertyGroup>
@@ -57,10 +57,10 @@
   </ImportGroup>
   <PropertyGroup Label="UserMacros" />
   <PropertyGroup Condition="'$(Configuration)'=='Debug'">
-    <TargetName>google_benchmark</TargetName>
+    <TargetName>benchmark</TargetName>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)'=='Release'">
-    <TargetName>google_benchmark</TargetName>
+    <TargetName>benchmark</TargetName>
   </PropertyGroup>
     <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
@@ -147,53 +147,53 @@
   </ItemDefinitionGroup>
 
   <ItemGroup>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\include\benchmark\benchmark.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\include\benchmark\benchmark_api.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\include\benchmark\macros.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\include\benchmark\reporter.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\arraysize.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\benchmark_api_internal.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\check.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\colorprint.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\commandlineflags.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\complexity.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\cycleclock.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\internal_macros.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\log.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\mutex.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\re.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\sleep.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\stat.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\string_util.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\sysinfo.h" />
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\timers.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\include\benchmark\benchmark.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\include\benchmark\benchmark_api.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\include\benchmark\macros.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\include\benchmark\reporter.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\arraysize.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\benchmark_api_internal.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\check.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\colorprint.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\commandlineflags.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\complexity.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\cycleclock.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\internal_macros.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\log.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\mutex.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\re.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\sleep.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\stat.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\string_util.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\sysinfo.h" />
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\timers.h" />
   </ItemGroup>
   <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\benchmark.cc">
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\benchmark.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\benchmark_register.cc">
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\benchmark_register.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\colorprint.cc">
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\colorprint.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\commandlineflags.cc">
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\commandlineflags.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\complexity.cc">
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\complexity.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\console_reporter.cc">
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\console_reporter.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\csv_reporter.cc">
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\csv_reporter.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\json_reporter.cc">
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\json_reporter.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\reporter.cc">
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\reporter.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\sleep.cc">
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\sleep.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\string_util.cc">
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\string_util.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\sysinfo.cc">
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\sysinfo.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\timers.cc">
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\timers.cc">
     </ClCompile>
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
diff --git a/vsprojects/vcxproj/benchmark/benchmark.vcxproj.filters b/vsprojects/vcxproj/benchmark/benchmark.vcxproj.filters
new file mode 100644
index 0000000000000000000000000000000000000000..ccc9ca2cae759ccbc5c99cf27b13dd1f1b5d78cd
--- /dev/null
+++ b/vsprojects/vcxproj/benchmark/benchmark.vcxproj.filters
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\benchmark.cc">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\benchmark_register.cc">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\colorprint.cc">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\commandlineflags.cc">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\complexity.cc">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\console_reporter.cc">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\csv_reporter.cc">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\json_reporter.cc">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\reporter.cc">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\sleep.cc">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\string_util.cc">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\sysinfo.cc">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\third_party\benchmark\src\timers.cc">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\include\benchmark\benchmark.h">
+      <Filter>third_party\benchmark\include\benchmark</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\include\benchmark\benchmark_api.h">
+      <Filter>third_party\benchmark\include\benchmark</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\include\benchmark\macros.h">
+      <Filter>third_party\benchmark\include\benchmark</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\include\benchmark\reporter.h">
+      <Filter>third_party\benchmark\include\benchmark</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\arraysize.h">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\benchmark_api_internal.h">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\check.h">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\colorprint.h">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\commandlineflags.h">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\complexity.h">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\cycleclock.h">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\internal_macros.h">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\log.h">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\mutex.h">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\re.h">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\sleep.h">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\stat.h">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\string_util.h">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\sysinfo.h">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\third_party\benchmark\src\timers.h">
+      <Filter>third_party\benchmark\src</Filter>
+    </ClInclude>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="third_party">
+      <UniqueIdentifier>{7b593518-9fee-107e-6b64-24bdce73f939}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="third_party\benchmark">
+      <UniqueIdentifier>{f0d35de1-6b41-778d-0ba0-faad514fb0f4}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="third_party\benchmark\include">
+      <UniqueIdentifier>{cbc02dfa-face-8cc6-0efb-efacc0c3369c}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="third_party\benchmark\include\benchmark">
+      <UniqueIdentifier>{4f2f03fc-b82d-df33-63ee-bedebeb2c0ee}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="third_party\benchmark\src">
+      <UniqueIdentifier>{f42a8e0a-5a76-0e6f-d708-f0306858f673}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/google_benchmark/google_benchmark.vcxproj.filters b/vsprojects/vcxproj/google_benchmark/google_benchmark.vcxproj.filters
deleted file mode 100644
index 9db6ed465742fa812b31c133d28dd8d6a60c1112..0000000000000000000000000000000000000000
--- a/vsprojects/vcxproj/google_benchmark/google_benchmark.vcxproj.filters
+++ /dev/null
@@ -1,125 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\benchmark.cc">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\benchmark_register.cc">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\colorprint.cc">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\commandlineflags.cc">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\complexity.cc">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\console_reporter.cc">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\csv_reporter.cc">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\json_reporter.cc">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\reporter.cc">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\sleep.cc">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\string_util.cc">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\sysinfo.cc">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\third_party\google_benchmark\src\timers.cc">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClCompile>
-  </ItemGroup>
-  <ItemGroup>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\include\benchmark\benchmark.h">
-      <Filter>third_party\google_benchmark\include\benchmark</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\include\benchmark\benchmark_api.h">
-      <Filter>third_party\google_benchmark\include\benchmark</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\include\benchmark\macros.h">
-      <Filter>third_party\google_benchmark\include\benchmark</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\include\benchmark\reporter.h">
-      <Filter>third_party\google_benchmark\include\benchmark</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\arraysize.h">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\benchmark_api_internal.h">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\check.h">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\colorprint.h">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\commandlineflags.h">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\complexity.h">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\cycleclock.h">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\internal_macros.h">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\log.h">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\mutex.h">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\re.h">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\sleep.h">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\stat.h">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\string_util.h">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\sysinfo.h">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\third_party\google_benchmark\src\timers.h">
-      <Filter>third_party\google_benchmark\src</Filter>
-    </ClInclude>
-  </ItemGroup>
-
-  <ItemGroup>
-    <Filter Include="third_party">
-      <UniqueIdentifier>{7458b63d-7ba4-103d-2bed-3e3ad30d8237}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="third_party\google_benchmark">
-      <UniqueIdentifier>{54a154e8-669b-a7c1-9b6e-bd1aab2f86e3}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="third_party\google_benchmark\include">
-      <UniqueIdentifier>{f54c3cb1-ec20-a651-6956-78379b51e1a5}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="third_party\google_benchmark\include\benchmark">
-      <UniqueIdentifier>{0483a457-8050-4565-bc15-09695bf7b822}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="third_party\google_benchmark\src">
-      <UniqueIdentifier>{c39ff2d1-691e-4614-4d75-4bc20db05e09}</UniqueIdentifier>
-    </Filter>
-  </ItemGroup>
-</Project>
-
diff --git a/vsprojects/vcxproj/grpc++/grpc++.vcxproj b/vsprojects/vcxproj/grpc++/grpc++.vcxproj
index f281db72b6675506ab42685ae004772f97746a7f..7dd0b85a0a8bd6d3219b5201561d00f1d787c26d 100644
--- a/vsprojects/vcxproj/grpc++/grpc++.vcxproj
+++ b/vsprojects/vcxproj/grpc++/grpc++.vcxproj
@@ -331,6 +331,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\exec_ctx_fwd.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
diff --git a/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters b/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters
index f359e4ef31ad032401244b72f29cda5ac3a7026d..28766bf76ed4da795dec68ded11a3d062903ceab 100644
--- a/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters
@@ -333,6 +333,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\exec_ctx_fwd.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj
index d2305b2e25591b4f0eb892de982197f8cf1e21fb..5b05d0918c83a1fa15d1a8725d3d72cbe82afc9b 100644
--- a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj
+++ b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj
@@ -178,6 +178,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\exec_ctx_fwd.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
diff --git a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters
index d1aaba70926a9c53a58be9c2d71818adc0d33ccd..b1fa72a8410738925ab35e8b84f5b578872240ed 100644
--- a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters
@@ -126,6 +126,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\exec_ctx_fwd.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj
index 1511a2cfe4cec342e194c6f20563ce9b7ae15d9a..48fb7619a2d5d600b377d04d0eae1f9211beaa9e 100644
--- a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj
+++ b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj
@@ -331,6 +331,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\exec_ctx_fwd.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
diff --git a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
index bed77b25a449d859bb76cbc5ebb61bbe21dcd617..7ce21ad286ecdb3aada8eb3dd4d5e8862b10c626 100644
--- a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
@@ -318,6 +318,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\exec_ctx_fwd.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj b/vsprojects/vcxproj/grpc/grpc.vcxproj
index 81b43f7a87ee35ae9fb4c5ae7e5ce428127bfadd..2550d1ed9778b08d143eb48eb95797d43d0dba9f 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj
@@ -279,6 +279,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\exec_ctx_fwd.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
@@ -305,6 +306,8 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\context.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\deadline_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_factory.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_registry.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h" />
@@ -371,6 +374,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json_writer.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\percent_encoding.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_internal.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\api_trace.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\call.h" />
@@ -428,9 +432,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\plugin\plugin_credentials.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\ssl\ssl_credentials.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\auth_filters.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\handshake.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\secure_endpoint.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\security_connector.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\security_handshaker.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\tsi_error.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\util\b64.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\util\json_util.h" />
@@ -439,6 +443,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\tsi\ssl_types.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\tsi\transport_security.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\tsi\transport_security_interface.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\server\chttp2_server.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_channel\client_channel.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_channel\client_channel_factory.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_channel\connector.h" />
@@ -454,6 +459,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_channel\subchannel.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_channel\subchannel_index.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_channel\uri_parser.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\chttp2_connector.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\grpclb.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\load_balancer_api.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\proto\grpc\lb\v1\load_balancer.pb.h" />
@@ -492,6 +498,10 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_factory.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_registry.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
@@ -780,12 +790,12 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\client_auth_filter.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\handshake.c">
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\secure_endpoint.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\security_connector.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\security_handshaker.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\server_auth_filter.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\tsi_error.c">
@@ -802,6 +812,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\tsi\transport_security.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\server\chttp2_server.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\secure\secure_channel_create.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_channel\channel_connectivity.c">
@@ -840,6 +852,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_channel\uri_parser.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\chttp2_connector.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\server\insecure\server_chttp2.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\server\insecure\server_chttp2_posix.c">
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
index 2f4a659aaa0e1045a9ecf4d2e6a4999e6f1633cf..642ace6f40d0168b8d1acab27644e87cbd54c805 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
@@ -25,6 +25,12 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.c">
       <Filter>src\core\lib\channel</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_factory.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_registry.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.c">
       <Filter>src\core\lib\channel</Filter>
     </ClCompile>
@@ -457,15 +463,15 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\client_auth_filter.c">
       <Filter>src\core\lib\security\transport</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\handshake.c">
-      <Filter>src\core\lib\security\transport</Filter>
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\secure_endpoint.c">
       <Filter>src\core\lib\security\transport</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\security_connector.c">
       <Filter>src\core\lib\security\transport</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\security_handshaker.c">
+      <Filter>src\core\lib\security\transport</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\server_auth_filter.c">
       <Filter>src\core\lib\security\transport</Filter>
     </ClCompile>
@@ -490,6 +496,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\tsi\transport_security.c">
       <Filter>src\core\lib\tsi</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\server\chttp2_server.c">
+      <Filter>src\core\ext\transport\chttp2\server</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\secure\secure_channel_create.c">
       <Filter>src\core\ext\transport\chttp2\client\secure</Filter>
     </ClCompile>
@@ -547,6 +556,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_channel\uri_parser.c">
       <Filter>src\core\ext\client_channel</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\chttp2_connector.c">
+      <Filter>src\core\ext\transport\chttp2\client</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\server\insecure\server_chttp2.c">
       <Filter>src\core\ext\transport\chttp2\server\insecure</Filter>
     </ClCompile>
@@ -678,6 +690,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\exec_ctx_fwd.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -752,6 +767,12 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.h">
       <Filter>src\core\lib\channel</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_factory.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_registry.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.h">
       <Filter>src\core\lib\channel</Filter>
     </ClInclude>
@@ -950,6 +971,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\percent_encoding.h">
       <Filter>src\core\lib\slice</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_internal.h">
+      <Filter>src\core\lib\slice</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.h">
       <Filter>src\core\lib\slice</Filter>
     </ClInclude>
@@ -1121,15 +1145,15 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\auth_filters.h">
       <Filter>src\core\lib\security\transport</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\handshake.h">
-      <Filter>src\core\lib\security\transport</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\secure_endpoint.h">
       <Filter>src\core\lib\security\transport</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\security_connector.h">
       <Filter>src\core\lib\security\transport</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\security_handshaker.h">
+      <Filter>src\core\lib\security\transport</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\tsi_error.h">
       <Filter>src\core\lib\security\transport</Filter>
     </ClInclude>
@@ -1154,6 +1178,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\tsi\transport_security_interface.h">
       <Filter>src\core\lib\tsi</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\server\chttp2_server.h">
+      <Filter>src\core\ext\transport\chttp2\server</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_channel\client_channel.h">
       <Filter>src\core\ext\client_channel</Filter>
     </ClInclude>
@@ -1199,6 +1226,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_channel\uri_parser.h">
       <Filter>src\core\ext\client_channel</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\chttp2_connector.h">
+      <Filter>src\core\ext\transport\chttp2\client</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\grpclb.h">
       <Filter>src\core\ext\lb_policy\grpclb</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj
index 2acdd32cf3ec3ddf92d211a3f1180e484b6ae50e..15e3097743bc5937a7ad89b1a376da26490db911 100644
--- a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj
+++ b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj
@@ -159,6 +159,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\exec_ctx_fwd.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
@@ -198,6 +199,8 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\context.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\deadline_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_factory.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_registry.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h" />
@@ -264,6 +267,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json_writer.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\percent_encoding.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_internal.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\api_trace.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\call.h" />
@@ -343,6 +347,10 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_factory.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_registry.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
diff --git a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters
index 6c918f125463d4dbc6df7fe589ecb7a34aba938e..43b7eaad0e7c607ba46a273ea2ff2e6a732d1b9e 100644
--- a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters
@@ -82,6 +82,12 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.c">
       <Filter>src\core\lib\channel</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_factory.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_registry.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.c">
       <Filter>src\core\lib\channel</Filter>
     </ClCompile>
@@ -435,6 +441,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\exec_ctx_fwd.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -548,6 +557,12 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.h">
       <Filter>src\core\lib\channel</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_factory.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_registry.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.h">
       <Filter>src\core\lib\channel</Filter>
     </ClInclude>
@@ -746,6 +761,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\percent_encoding.h">
       <Filter>src\core\lib\slice</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_internal.h">
+      <Filter>src\core\lib\slice</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.h">
       <Filter>src\core\lib\slice</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
index c96d48db58890caaa71b6bdb56ba42c348d6a10c..d1d3cb2a360161e44d6aff5a16d713d373ac816f 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
@@ -270,6 +270,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\exec_ctx_fwd.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
@@ -295,6 +296,8 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\context.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\deadline_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_factory.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_registry.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h" />
@@ -361,6 +364,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json_writer.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\percent_encoding.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_internal.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\api_trace.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\call.h" />
@@ -405,6 +409,8 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_map.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\varint.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\alpn\alpn.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\server\chttp2_server.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\chttp2_connector.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_channel\client_channel.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_channel\client_channel_factory.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_channel\connector.h" />
@@ -460,6 +466,10 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_factory.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_registry.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
@@ -718,10 +728,14 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\alpn\alpn.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\server\chttp2_server.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\insecure\channel_create.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\insecure\channel_create_posix.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\chttp2_connector.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_channel\channel_connectivity.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_channel\client_channel.c">
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
index 4b75288b2b673d1714a7c3c3ab46256116be6779..7741c62ecb81554894a6116296e71f57bbc7236b 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
@@ -28,6 +28,12 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.c">
       <Filter>src\core\lib\channel</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_factory.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_registry.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.c">
       <Filter>src\core\lib\channel</Filter>
     </ClCompile>
@@ -415,12 +421,18 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\alpn\alpn.c">
       <Filter>src\core\ext\transport\chttp2\alpn</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\server\chttp2_server.c">
+      <Filter>src\core\ext\transport\chttp2\server</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\insecure\channel_create.c">
       <Filter>src\core\ext\transport\chttp2\client\insecure</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\insecure\channel_create_posix.c">
       <Filter>src\core\ext\transport\chttp2\client\insecure</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\chttp2_connector.c">
+      <Filter>src\core\ext\transport\chttp2\client</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_channel\channel_connectivity.c">
       <Filter>src\core\ext\client_channel</Filter>
     </ClCompile>
@@ -594,6 +606,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\exec_ctx_fwd.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -665,6 +680,12 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.h">
       <Filter>src\core\lib\channel</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_factory.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker_registry.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.h">
       <Filter>src\core\lib\channel</Filter>
     </ClInclude>
@@ -863,6 +884,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\percent_encoding.h">
       <Filter>src\core\lib\slice</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_internal.h">
+      <Filter>src\core\lib\slice</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.h">
       <Filter>src\core\lib\slice</Filter>
     </ClInclude>
@@ -995,6 +1019,12 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\alpn\alpn.h">
       <Filter>src\core\ext\transport\chttp2\alpn</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\server\chttp2_server.h">
+      <Filter>src\core\ext\transport\chttp2\server</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\chttp2_connector.h">
+      <Filter>src\core\ext\transport\chttp2\client</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_channel\client_channel.h">
       <Filter>src\core\ext\client_channel</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/http2_client_main/http2_client_main.vcxproj b/vsprojects/vcxproj/http2_client_main/http2_client_main.vcxproj
new file mode 100644
index 0000000000000000000000000000000000000000..c94c346ed9943cdbefed5bd593021661dfe5de0b
--- /dev/null
+++ b/vsprojects/vcxproj/http2_client_main/http2_client_main.vcxproj
@@ -0,0 +1,206 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <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>{8008C126-4B35-5A76-A795-39DA13DA57F4}</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>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</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\winsock.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>http2_client_main</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>http2_client_main</TargetName>
+  </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>Windows</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>Windows</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>Windows</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>Windows</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClInclude Include="$(SolutionDir)\..\test\cpp\interop\http2_client.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\empty.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\empty.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\empty.grpc.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\empty.grpc.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\messages.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\messages.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\messages.grpc.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\messages.grpc.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\test.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\test.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\test.grpc.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\test.grpc.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\interop\http2_client.cc">
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++_test_util\grpc++_test_util.vcxproj">
+      <Project>{0BE77741-552A-929B-A497-4EF7ECE17A64}</Project>
+    </ProjectReference>
+    <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>{C187A093-A0FE-489D-A40A-6E33DE0F9FEB}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
+      <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++_test_config\grpc++_test_config.vcxproj">
+      <Project>{3F7D093D-11F9-C4BC-BEB7-18EB28E3F290}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </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>
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/http2_client_main/http2_client_main.vcxproj.filters b/vsprojects/vcxproj/http2_client_main/http2_client_main.vcxproj.filters
new file mode 100644
index 0000000000000000000000000000000000000000..40e419134510680ccc93fee67d1a132df6a0e6d4
--- /dev/null
+++ b/vsprojects/vcxproj/http2_client_main/http2_client_main.vcxproj.filters
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\empty.proto">
+      <Filter>src\proto\grpc\testing</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\messages.proto">
+      <Filter>src\proto\grpc\testing</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\test.proto">
+      <Filter>src\proto\grpc\testing</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\interop\http2_client.cc">
+      <Filter>test\cpp\interop</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="$(SolutionDir)\..\test\cpp\interop\http2_client.h">
+      <Filter>test\cpp\interop</Filter>
+    </ClInclude>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="src">
+      <UniqueIdentifier>{8f01743f-58cf-2127-20d1-21303c8e0e8e}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\proto">
+      <UniqueIdentifier>{219dd0bc-d605-d0fa-cca4-460276dd64cf}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\proto\grpc">
+      <UniqueIdentifier>{7ee50971-dbc6-3240-e6a2-a432dac43e90}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\proto\grpc\testing">
+      <UniqueIdentifier>{66289b19-9541-151c-03a1-771101833b4e}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test">
+      <UniqueIdentifier>{18fdd193-31a2-a622-980e-df055c23af50}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\cpp">
+      <UniqueIdentifier>{f6da8892-ac51-d835-468a-b3b093df4baa}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\cpp\interop">
+      <UniqueIdentifier>{e32cff29-3247-cece-56cf-8fd16a1a79fe}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/interop_server_helper/interop_server_helper.vcxproj b/vsprojects/vcxproj/interop_server_helper/interop_server_helper.vcxproj
index 4c99988a34c482f55a4e8f9749ab54fa404db885..377cadc1a1ea1f4781930439f2ce36db9b9213ae 100644
--- a/vsprojects/vcxproj/interop_server_helper/interop_server_helper.vcxproj
+++ b/vsprojects/vcxproj/interop_server_helper/interop_server_helper.vcxproj
@@ -154,6 +154,9 @@
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++_test_util\grpc++_test_util.vcxproj">
+      <Project>{0BE77741-552A-929B-A497-4EF7ECE17A64}</Project>
+    </ProjectReference>
     <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
       <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
     </ProjectReference>
diff --git a/vsprojects/vcxproj/test/bm_fullstack/bm_fullstack.vcxproj b/vsprojects/vcxproj/test/bm_fullstack/bm_fullstack.vcxproj
index 1ce993e3230786a4a5b358004a69b918da422374..3809beb508bf3707c2dda7306744e629138712b7 100644
--- a/vsprojects/vcxproj/test/bm_fullstack/bm_fullstack.vcxproj
+++ b/vsprojects/vcxproj/test/bm_fullstack/bm_fullstack.vcxproj
@@ -164,8 +164,8 @@
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\google_benchmark\google_benchmark.vcxproj">
-      <Project>{AAD4AEF3-DF1E-7A6D-EC35-233BD1031BF4}</Project>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\benchmark\benchmark.vcxproj">
+      <Project>{07978586-E47C-8709-A63E-895FBF3C3C7D}</Project>
     </ProjectReference>
     <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++_test_util\grpc++_test_util.vcxproj">
       <Project>{0BE77741-552A-929B-A497-4EF7ECE17A64}</Project>
diff --git a/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj b/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj
index a2b2a1dfa0fa3f84902ebabb037a71164b7bcb28..d2699a5729f21cdd083635396530a9642971689e 100644
--- a/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj
+++ b/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj
@@ -191,6 +191,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\exec_ctx_fwd.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
diff --git a/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj.filters b/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj.filters
index 94b6c2530ef003e74174d671358f7104a9fa45e1..dc0d89ecd57f7633b0a54d59153a866332a3d997 100644
--- a/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj.filters
+++ b/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj.filters
@@ -114,6 +114,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\exec_ctx_fwd.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj b/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj
index 1a3c1579837556b65af06c64a55e79d38492524f..742c9a6865c5c7d1fcb348692e0f52f48d8e2246 100644
--- a/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj
+++ b/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj
@@ -191,6 +191,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\exec_ctx_fwd.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
diff --git a/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj.filters b/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj.filters
index 1f4b60ca4d91e57b958398a65aca03b7c854155e..88165167bdaca5aefa75f9b0f6471570da757cf1 100644
--- a/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj.filters
+++ b/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj.filters
@@ -117,6 +117,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\exec_ctx_fwd.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj b/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj
index 1e3cc3ca043c11d90f554ab1a66cb17ca077890f..65719c31002e301571c85c2886449713a7e2ff88 100644
--- a/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj
+++ b/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj
@@ -192,6 +192,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\exec_ctx_fwd.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
diff --git a/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj.filters b/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj.filters
index 1c308c58817b4e41ae23d4fb5539181d47488619..7f3745c7a9394bcdfc00e3ea3a2805e87326980f 100644
--- a/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj.filters
+++ b/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj.filters
@@ -108,6 +108,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\exec_ctx_fwd.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/test/memory_profile_client/memory_profile_client.vcxproj b/vsprojects/vcxproj/test/memory_profile_client/memory_profile_client.vcxproj
new file mode 100644
index 0000000000000000000000000000000000000000..b955bcae37e3719cae988161fff3f54be7addefd
--- /dev/null
+++ b/vsprojects/vcxproj/test/memory_profile_client/memory_profile_client.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>{98C01DBE-EFFE-6988-0762-829DC88F0EB4}</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>memory_profile_client</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>memory_profile_client</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\memory_usage\client.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/memory_profile_client/memory_profile_client.vcxproj.filters b/vsprojects/vcxproj/test/memory_profile_client/memory_profile_client.vcxproj.filters
new file mode 100644
index 0000000000000000000000000000000000000000..b250b538d12ad33176c4424a9054d957be198d86
--- /dev/null
+++ b/vsprojects/vcxproj/test/memory_profile_client/memory_profile_client.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\memory_usage\client.c">
+      <Filter>test\core\memory_usage</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="test">
+      <UniqueIdentifier>{4445c589-fe29-9a74-3b53-c83c7bac669e}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core">
+      <UniqueIdentifier>{8f263e3f-4f47-64ed-e45f-8f4385a6056d}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\memory_usage">
+      <UniqueIdentifier>{2de23f1d-3faf-3318-2e31-b7cfa4a89220}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/memory_profile_server/memory_profile_server.vcxproj b/vsprojects/vcxproj/test/memory_profile_server/memory_profile_server.vcxproj
new file mode 100644
index 0000000000000000000000000000000000000000..0b3c87997e1ef6c29aa23599357063225a1b6848
--- /dev/null
+++ b/vsprojects/vcxproj/test/memory_profile_server/memory_profile_server.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>{7430B8AF-2A0E-EDF4-FD53-C8004DEE39EC}</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>memory_profile_server</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>memory_profile_server</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\memory_usage\server.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/memory_profile_server/memory_profile_server.vcxproj.filters b/vsprojects/vcxproj/test/memory_profile_server/memory_profile_server.vcxproj.filters
new file mode 100644
index 0000000000000000000000000000000000000000..c95888e7a1c5f08c736139e3b0346cb731ee0e6d
--- /dev/null
+++ b/vsprojects/vcxproj/test/memory_profile_server/memory_profile_server.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\memory_usage\server.c">
+      <Filter>test\core\memory_usage</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="test">
+      <UniqueIdentifier>{0193b199-ddad-5387-8a4f-ab1e6c36b835}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core">
+      <UniqueIdentifier>{31db3ee2-d7ca-bedb-eacc-1bc7785dcae6}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\memory_usage">
+      <UniqueIdentifier>{5b7c6ebd-9730-e59c-59bf-806a8b7ccda2}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/noop-benchmark/noop-benchmark.vcxproj b/vsprojects/vcxproj/test/noop-benchmark/noop-benchmark.vcxproj
index 99f33b21658312c90c85928b4de1b8a8f6a9f478..15a82c0ed6cdc4288d7c1cdc4e7e65306fe06810 100644
--- a/vsprojects/vcxproj/test/noop-benchmark/noop-benchmark.vcxproj
+++ b/vsprojects/vcxproj/test/noop-benchmark/noop-benchmark.vcxproj
@@ -164,8 +164,8 @@
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\google_benchmark\google_benchmark.vcxproj">
-      <Project>{AAD4AEF3-DF1E-7A6D-EC35-233BD1031BF4}</Project>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\benchmark\benchmark.vcxproj">
+      <Project>{07978586-E47C-8709-A63E-895FBF3C3C7D}</Project>
     </ProjectReference>
   </ItemGroup>
   <ItemGroup>