Merge pull request #14105 from mehrdada/relax-call-details

Relax call details interface from interceptor
diff --git a/.gitignore b/.gitignore
index 882828e..0f3cd78 100644
--- a/.gitignore
+++ b/.gitignore
@@ -94,9 +94,9 @@
 *.pbrpc.*
 
 # Cocoapods artifacts
-# Podfile.lock and the workspace file are tracked, to ease deleting them. That's
-# needed to trigger "pod install" to rerun the preinstall commands.
 Pods/
+Podfile.lock
+*.xcworkspace
 
 # Artifacts directory
 /artifacts/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b679fb8..e558f1d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -590,6 +590,7 @@
 add_dependencies(buildtests_cxx server_crash_test)
 endif()
 add_dependencies(buildtests_cxx server_crash_test_client)
+add_dependencies(buildtests_cxx server_early_return_test)
 add_dependencies(buildtests_cxx server_request_call_test)
 add_dependencies(buildtests_cxx shutdown_test)
 add_dependencies(buildtests_cxx stats_test)
@@ -11711,6 +11712,44 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+add_executable(server_early_return_test
+  test/cpp/end2end/server_early_return_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(server_early_return_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(server_early_return_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc++_test_util
+  grpc_test_util
+  grpc++
+  grpc
+  gpr_test_util
+  gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
 add_executable(server_request_call_test
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/echo_messages.pb.cc
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/echo_messages.grpc.pb.cc
diff --git a/Makefile b/Makefile
index ada461f..57ebffc 100644
--- a/Makefile
+++ b/Makefile
@@ -1179,6 +1179,7 @@
 server_context_test_spouse_test: $(BINDIR)/$(CONFIG)/server_context_test_spouse_test
 server_crash_test: $(BINDIR)/$(CONFIG)/server_crash_test
 server_crash_test_client: $(BINDIR)/$(CONFIG)/server_crash_test_client
+server_early_return_test: $(BINDIR)/$(CONFIG)/server_early_return_test
 server_request_call_test: $(BINDIR)/$(CONFIG)/server_request_call_test
 shutdown_test: $(BINDIR)/$(CONFIG)/shutdown_test
 stats_test: $(BINDIR)/$(CONFIG)/stats_test
@@ -1622,6 +1623,7 @@
   $(BINDIR)/$(CONFIG)/server_context_test_spouse_test \
   $(BINDIR)/$(CONFIG)/server_crash_test \
   $(BINDIR)/$(CONFIG)/server_crash_test_client \
+  $(BINDIR)/$(CONFIG)/server_early_return_test \
   $(BINDIR)/$(CONFIG)/server_request_call_test \
   $(BINDIR)/$(CONFIG)/shutdown_test \
   $(BINDIR)/$(CONFIG)/stats_test \
@@ -1754,6 +1756,7 @@
   $(BINDIR)/$(CONFIG)/server_context_test_spouse_test \
   $(BINDIR)/$(CONFIG)/server_crash_test \
   $(BINDIR)/$(CONFIG)/server_crash_test_client \
+  $(BINDIR)/$(CONFIG)/server_early_return_test \
   $(BINDIR)/$(CONFIG)/server_request_call_test \
   $(BINDIR)/$(CONFIG)/shutdown_test \
   $(BINDIR)/$(CONFIG)/stats_test \
@@ -2171,6 +2174,8 @@
 	$(Q) $(BINDIR)/$(CONFIG)/server_context_test_spouse_test || ( echo test server_context_test_spouse_test failed ; exit 1 )
 	$(E) "[RUN]     Testing server_crash_test"
 	$(Q) $(BINDIR)/$(CONFIG)/server_crash_test || ( echo test server_crash_test failed ; exit 1 )
+	$(E) "[RUN]     Testing server_early_return_test"
+	$(Q) $(BINDIR)/$(CONFIG)/server_early_return_test || ( echo test server_early_return_test failed ; exit 1 )
 	$(E) "[RUN]     Testing server_request_call_test"
 	$(Q) $(BINDIR)/$(CONFIG)/server_request_call_test || ( echo test server_request_call_test failed ; exit 1 )
 	$(E) "[RUN]     Testing shutdown_test"
@@ -16990,6 +16995,49 @@
 endif
 
 
+SERVER_EARLY_RETURN_TEST_SRC = \
+    test/cpp/end2end/server_early_return_test.cc \
+
+SERVER_EARLY_RETURN_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SERVER_EARLY_RETURN_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/server_early_return_test: 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)/server_early_return_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/server_early_return_test: $(PROTOBUF_DEP) $(SERVER_EARLY_RETURN_TEST_OBJS) $(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) $(SERVER_EARLY_RETURN_TEST_OBJS) $(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)/server_early_return_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/cpp/end2end/server_early_return_test.o:  $(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_server_early_return_test: $(SERVER_EARLY_RETURN_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(SERVER_EARLY_RETURN_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 SERVER_REQUEST_CALL_TEST_SRC = \
     $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc \
     $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc \
diff --git a/build.yaml b/build.yaml
index 61f76f2..908e387 100644
--- a/build.yaml
+++ b/build.yaml
@@ -4704,6 +4704,19 @@
   - grpc
   - gpr_test_util
   - gpr
+- name: server_early_return_test
+  gtest: true
+  build: test
+  language: c++
+  src:
+  - test/cpp/end2end/server_early_return_test.cc
+  deps:
+  - grpc++_test_util
+  - grpc_test_util
+  - grpc++
+  - grpc
+  - gpr_test_util
+  - gpr
 - name: server_request_call_test
   gtest: true
   build: test
diff --git a/examples/cpp/helloworld/cocoapods/HelloWorldCpp.xcodeproj/project.pbxproj b/examples/cpp/helloworld/cocoapods/HelloWorldCpp.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..b722b80
--- /dev/null
+++ b/examples/cpp/helloworld/cocoapods/HelloWorldCpp.xcodeproj/project.pbxproj
@@ -0,0 +1,409 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 48;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		3DC71E95B8670DC619CF8693 /* libPods-HelloWorldCpp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E3077D82811A6434769B437 /* libPods-HelloWorldCpp.a */; };
+		5E9D65611FFD689B00C955D4 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5E9D65601FFD689B00C955D4 /* AppDelegate.mm */; };
+		5E9D65641FFD689B00C955D4 /* ViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5E9D65631FFD689B00C955D4 /* ViewController.mm */; };
+		5E9D65671FFD689B00C955D4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E9D65651FFD689B00C955D4 /* Main.storyboard */; };
+		5E9D65691FFD689B00C955D4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5E9D65681FFD689B00C955D4 /* Assets.xcassets */; };
+		5E9D656C1FFD689B00C955D4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E9D656A1FFD689B00C955D4 /* LaunchScreen.storyboard */; };
+		5E9D656F1FFD689C00C955D4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E9D656E1FFD689C00C955D4 /* main.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		0A6137606FDBA874FCF64753 /* Pods-HelloWorldCpp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorldCpp.release.xcconfig"; path = "Pods/Target Support Files/Pods-HelloWorldCpp/Pods-HelloWorldCpp.release.xcconfig"; sourceTree = "<group>"; };
+		5E9D655C1FFD689B00C955D4 /* HelloWorldCpp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloWorldCpp.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		5E9D655F1FFD689B00C955D4 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+		5E9D65601FFD689B00C955D4 /* AppDelegate.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AppDelegate.mm; sourceTree = "<group>"; };
+		5E9D65621FFD689B00C955D4 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
+		5E9D65631FFD689B00C955D4 /* ViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ViewController.mm; sourceTree = "<group>"; };
+		5E9D65661FFD689B00C955D4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+		5E9D65681FFD689B00C955D4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		5E9D656B1FFD689B00C955D4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+		5E9D656D1FFD689C00C955D4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		5E9D656E1FFD689C00C955D4 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+		8E3077D82811A6434769B437 /* libPods-HelloWorldCpp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HelloWorldCpp.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		AF0FF6EB66DA2C30CFC575EA /* Pods-HelloWorldCpp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorldCpp.debug.xcconfig"; path = "Pods/Target Support Files/Pods-HelloWorldCpp/Pods-HelloWorldCpp.debug.xcconfig"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		5E9D65591FFD689B00C955D4 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				3DC71E95B8670DC619CF8693 /* libPods-HelloWorldCpp.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		08FBFA5AB49E037A86EA64D7 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				8E3077D82811A6434769B437 /* libPods-HelloWorldCpp.a */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		5E9D65531FFD689B00C955D4 = {
+			isa = PBXGroup;
+			children = (
+				5E9D655E1FFD689B00C955D4 /* HelloWorldCpp */,
+				5E9D655D1FFD689B00C955D4 /* Products */,
+				E40B264856595518D408CF3E /* Pods */,
+				08FBFA5AB49E037A86EA64D7 /* Frameworks */,
+			);
+			sourceTree = "<group>";
+		};
+		5E9D655D1FFD689B00C955D4 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				5E9D655C1FFD689B00C955D4 /* HelloWorldCpp.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		5E9D655E1FFD689B00C955D4 /* HelloWorldCpp */ = {
+			isa = PBXGroup;
+			children = (
+				5E9D655F1FFD689B00C955D4 /* AppDelegate.h */,
+				5E9D65601FFD689B00C955D4 /* AppDelegate.mm */,
+				5E9D65621FFD689B00C955D4 /* ViewController.h */,
+				5E9D65631FFD689B00C955D4 /* ViewController.mm */,
+				5E9D65651FFD689B00C955D4 /* Main.storyboard */,
+				5E9D65681FFD689B00C955D4 /* Assets.xcassets */,
+				5E9D656A1FFD689B00C955D4 /* LaunchScreen.storyboard */,
+				5E9D656D1FFD689C00C955D4 /* Info.plist */,
+				5E9D656E1FFD689C00C955D4 /* main.m */,
+			);
+			path = HelloWorldCpp;
+			sourceTree = "<group>";
+		};
+		E40B264856595518D408CF3E /* Pods */ = {
+			isa = PBXGroup;
+			children = (
+				AF0FF6EB66DA2C30CFC575EA /* Pods-HelloWorldCpp.debug.xcconfig */,
+				0A6137606FDBA874FCF64753 /* Pods-HelloWorldCpp.release.xcconfig */,
+			);
+			name = Pods;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		5E9D655B1FFD689B00C955D4 /* HelloWorldCpp */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 5E9D65721FFD689C00C955D4 /* Build configuration list for PBXNativeTarget "HelloWorldCpp" */;
+			buildPhases = (
+				4D66D60BD88AA4D5813859A9 /* [CP] Check Pods Manifest.lock */,
+				5E9D65581FFD689B00C955D4 /* Sources */,
+				5E9D65591FFD689B00C955D4 /* Frameworks */,
+				5E9D655A1FFD689B00C955D4 /* Resources */,
+				CA89B315EACC5A6F8816FD26 /* [CP] Embed Pods Frameworks */,
+				6C4D930C765719D03893663B /* [CP] Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = HelloWorldCpp;
+			productName = HelloWorldCpp;
+			productReference = 5E9D655C1FFD689B00C955D4 /* HelloWorldCpp.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		5E9D65541FFD689B00C955D4 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0920;
+				ORGANIZATIONNAME = gRPC;
+				TargetAttributes = {
+					5E9D655B1FFD689B00C955D4 = {
+						CreatedOnToolsVersion = 9.2;
+						ProvisioningStyle = Automatic;
+					};
+				};
+			};
+			buildConfigurationList = 5E9D65571FFD689B00C955D4 /* Build configuration list for PBXProject "HelloWorldCpp" */;
+			compatibilityVersion = "Xcode 8.0";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 5E9D65531FFD689B00C955D4;
+			productRefGroup = 5E9D655D1FFD689B00C955D4 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				5E9D655B1FFD689B00C955D4 /* HelloWorldCpp */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		5E9D655A1FFD689B00C955D4 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5E9D656C1FFD689B00C955D4 /* LaunchScreen.storyboard in Resources */,
+				5E9D65691FFD689B00C955D4 /* Assets.xcassets in Resources */,
+				5E9D65671FFD689B00C955D4 /* Main.storyboard in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		4D66D60BD88AA4D5813859A9 /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-HelloWorldCpp-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+		6C4D930C765719D03893663B /* [CP] Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "[CP] Copy Pods Resources";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-HelloWorldCpp/Pods-HelloWorldCpp-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		CA89B315EACC5A6F8816FD26 /* [CP] Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "[CP] Embed Pods Frameworks";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-HelloWorldCpp/Pods-HelloWorldCpp-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		5E9D65581FFD689B00C955D4 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5E9D65641FFD689B00C955D4 /* ViewController.mm in Sources */,
+				5E9D656F1FFD689C00C955D4 /* main.m in Sources */,
+				5E9D65611FFD689B00C955D4 /* AppDelegate.mm in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+		5E9D65651FFD689B00C955D4 /* Main.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				5E9D65661FFD689B00C955D4 /* Base */,
+			);
+			name = Main.storyboard;
+			sourceTree = "<group>";
+		};
+		5E9D656A1FFD689B00C955D4 /* LaunchScreen.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				5E9D656B1FFD689B00C955D4 /* Base */,
+			);
+			name = LaunchScreen.storyboard;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		5E9D65701FFD689C00C955D4 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.2;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+			};
+			name = Debug;
+		};
+		5E9D65711FFD689C00C955D4 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.2;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		5E9D65731FFD689C00C955D4 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = AF0FF6EB66DA2C30CFC575EA /* Pods-HelloWorldCpp.debug.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = EQHXZ8M8AV;
+				INFOPLIST_FILE = HelloWorldCpp/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.HelloWorldCpp;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		5E9D65741FFD689C00C955D4 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 0A6137606FDBA874FCF64753 /* Pods-HelloWorldCpp.release.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = EQHXZ8M8AV;
+				INFOPLIST_FILE = HelloWorldCpp/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.HelloWorldCpp;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		5E9D65571FFD689B00C955D4 /* Build configuration list for PBXProject "HelloWorldCpp" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				5E9D65701FFD689C00C955D4 /* Debug */,
+				5E9D65711FFD689C00C955D4 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		5E9D65721FFD689C00C955D4 /* Build configuration list for PBXNativeTarget "HelloWorldCpp" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				5E9D65731FFD689C00C955D4 /* Debug */,
+				5E9D65741FFD689C00C955D4 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 5E9D65541FFD689B00C955D4 /* Project object */;
+}
diff --git a/examples/cpp/helloworld/cocoapods/HelloWorldCpp/AppDelegate.h b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/AppDelegate.h
new file mode 100644
index 0000000..91ba988
--- /dev/null
+++ b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/AppDelegate.h
@@ -0,0 +1,23 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#import <UIKit/UIKit.h>
+
+@interface AppDelegate : UIResponder <UIApplicationDelegate>
+@property (strong, nonatomic) UIWindow *window;
+@end
diff --git a/examples/cpp/helloworld/cocoapods/HelloWorldCpp/AppDelegate.mm b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/AppDelegate.mm
new file mode 100644
index 0000000..5c60aec
--- /dev/null
+++ b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/AppDelegate.mm
@@ -0,0 +1,22 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#import "AppDelegate.h"
+
+@implementation AppDelegate
+@end
diff --git a/examples/cpp/helloworld/cocoapods/HelloWorldCpp/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..1d060ed
--- /dev/null
+++ b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,93 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "size" : "20x20",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "20x20",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "20x20",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "20x20",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "83.5x83.5",
+      "scale" : "2x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/examples/cpp/helloworld/cocoapods/HelloWorldCpp/Base.lproj/LaunchScreen.storyboard b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f83f6fd
--- /dev/null
+++ b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A277" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="EHf-IW-A2E">
+            <objects>
+                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="53" y="375"/>
+        </scene>
+    </scenes>
+</document>
diff --git a/examples/cpp/helloworld/cocoapods/HelloWorldCpp/Base.lproj/Main.storyboard b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..d7c78a1
--- /dev/null
+++ b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/Base.lproj/Main.storyboard
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A277" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="tne-QT-ifu">
+            <objects>
+                <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+            </objects>
+        </scene>
+    </scenes>
+</document>
diff --git a/examples/cpp/helloworld/cocoapods/HelloWorldCpp/Info.plist b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/Info.plist
new file mode 100644
index 0000000..16be3b6
--- /dev/null
+++ b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/Info.plist
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIMainStoryboardFile</key>
+	<string>Main</string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>armv7</string>
+	</array>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+</dict>
+</plist>
diff --git a/examples/cpp/helloworld/cocoapods/HelloWorldCpp/ViewController.h b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/ViewController.h
new file mode 100644
index 0000000..28d4ca0
--- /dev/null
+++ b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/ViewController.h
@@ -0,0 +1,23 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#import <UIKit/UIKit.h>
+
+@interface ViewController : UIViewController
+@end
+
diff --git a/examples/cpp/helloworld/cocoapods/HelloWorldCpp/ViewController.mm b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/ViewController.mm
new file mode 100644
index 0000000..6ff1ca5
--- /dev/null
+++ b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/ViewController.mm
@@ -0,0 +1,101 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#import "ViewController.h"
+#import <grpc++/grpc++.h>
+#include <grpc++/generic/generic_stub.h>
+#include <grpc++/generic/async_generic_service.h>
+
+static void* tag(int i) { return (void*)(intptr_t)i; }
+
+// Serialized Proto bytes of Hello World example
+const uint8_t kMessage[] =
+    {0x0A, 0x0B, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x2D, 0x43};
+
+@interface ViewController ()
+
+@end
+
+@implementation ViewController {
+  grpc::CompletionQueue cq_;
+  std::unique_ptr<grpc::GenericStub> generic_stub_;
+}
+
+- (void)viewDidLoad {
+  [super viewDidLoad];
+
+  // Setup call stub
+  std::shared_ptr<grpc::Channel> channel =
+      CreateChannel("localhost:50051", grpc::InsecureChannelCredentials());
+  generic_stub_.reset(new grpc::GenericStub(channel));
+
+  const grpc::string kMethodName("/helloworld.Greeter/SayHello");
+  void* got_tag;
+  bool ok;
+
+  grpc::ClientContext cli_ctx;
+  std::unique_ptr<grpc::GenericClientAsyncReaderWriter> call =
+      generic_stub_->Call(&cli_ctx, kMethodName, &cq_, tag(1));
+  cq_.Next(&got_tag, &ok);
+  if (!ok || got_tag != tag(1)) {
+    NSLog(@"Failed to create call.");
+    abort();
+  }
+  grpc::Slice send_slice = grpc::Slice(kMessage, sizeof(kMessage) / sizeof(kMessage[0]));
+      std::unique_ptr<grpc::ByteBuffer> send_buffer(new grpc::ByteBuffer(&send_slice, 1));
+  call->Write(*send_buffer, tag(2));
+  cq_.Next(&got_tag, &ok);
+  if (!ok || got_tag != tag(2)) {
+    NSLog(@"Failed to send message.");
+    abort();
+  }
+  grpc::ByteBuffer recv_buffer;
+  call->Read(&recv_buffer, tag(3));
+  cq_.Next(&got_tag, &ok);
+  if (!ok || got_tag != tag(3)) {
+    NSLog(@"Failed to receive message.");
+    abort();
+  }
+
+  grpc::Status status;
+  call->Finish(&status, tag(4));
+  cq_.Next(&got_tag, &ok);
+  if (!ok || got_tag != tag(4)) {
+    NSLog(@"Failed to finish call.");
+    abort();
+  }
+  if (!status.ok()) {
+    NSLog(@"Received unsuccessful status code: %d", status.error_code());
+    abort();
+  }
+  std::vector<grpc::Slice> slices;
+  recv_buffer.Dump(&slices);
+  NSString *recvBytes = [[NSString alloc] init];
+  for (auto slice : slices) {
+    auto p = slice.begin();
+    while (p != slice.end()) {
+      recvBytes =
+          [recvBytes stringByAppendingString:[NSString stringWithFormat:@"%02x ", *p]];
+      p++;
+    }
+  }
+  NSLog(@"Hello World succeeded.\nReceived bytes: %@\n"
+        "Expected bytes: 0a 11 48 65 6c 6c 6f 20 4f 62 6a 65 63 74 69 76 65 2d 43", recvBytes);
+}
+
+@end
diff --git a/examples/cpp/helloworld/cocoapods/HelloWorldCpp/main.m b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/main.m
new file mode 100644
index 0000000..a3a2225
--- /dev/null
+++ b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/main.m
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#import <UIKit/UIKit.h>
+#import "AppDelegate.h"
+
+int main(int argc, char * argv[]) {
+  @autoreleasepool {
+      return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+  }
+}
diff --git a/examples/cpp/helloworld/cocoapods/Podfile b/examples/cpp/helloworld/cocoapods/Podfile
new file mode 100644
index 0000000..5467f97
--- /dev/null
+++ b/examples/cpp/helloworld/cocoapods/Podfile
@@ -0,0 +1,8 @@
+source 'https://github.com/CocoaPods/Specs.git'
+platform :ios, '8.0'
+
+install! 'cocoapods', :deterministic_uuids => false
+
+target 'HelloWorldCpp' do
+  pod 'gRPC-C++'
+end
diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec
new file mode 100644
index 0000000..abbb69c
--- /dev/null
+++ b/gRPC-C++.podspec
@@ -0,0 +1,683 @@
+# This file has been automatically generated from a template file.
+# Please make modifications to `templates/gRPC-C++.podspec.template`
+# instead. This file can be regenerated from the template by running
+# `tools/buildgen/generate_projects.sh`.
+
+# gRPC C++ CocoaPods podspec
+#
+# Copyright 2017 gRPC authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+Pod::Spec.new do |s|
+  s.name     = 'gRPC-C++'
+  # TODO (mxyan): use version that match gRPC version when pod is stabilized
+  # version = '1.10.0-dev'
+  version = '0.0.1'
+  s.version  = version
+  s.summary  = 'gRPC C++ library'
+  s.homepage = 'https://grpc.io'
+  s.license  = 'Apache License, Version 2.0'
+  s.authors  = { 'The gRPC contributors' => 'grpc-packages@google.com' }
+
+  grpc_version = '1.10.0-dev'
+
+  s.source = {
+    :git => 'https://github.com/grpc/grpc.git',
+    :tag => "v#{grpc_version}",
+  }
+
+  s.ios.deployment_target = '7.0'
+  s.osx.deployment_target = '10.9'
+  s.requires_arc = false
+
+  # Add include prefix `grpc++` (i.e. `#include <grpc++/xxx.h>`).
+  s.header_dir = 'grpc++'
+
+  s.pod_target_xcconfig = {
+    'HEADER_SEARCH_PATHS' => '"$(inherited)" "$(PODS_TARGET_SRCROOT)/include"',
+    'USER_HEADER_SEARCH_PATHS' => '"$(PODS_TARGET_SRCROOT)"',
+    'GCC_PREPROCESSOR_DEFINITIONS' => '"$(inherited)" "COCOAPODS=1" "PB_NO_PACKED_STRUCTS=1"',
+    'CLANG_WARN_STRICT_PROTOTYPES' => 'NO',
+    'CLANG_WARN_DOCUMENTATION_COMMENTS' => 'NO',
+
+    # If we don't set these two settings, `include/grpc/support/time.h` and
+    # `src/core/lib/support/string.h` shadow the system `<time.h>` and `<string.h>`, breaking the
+    # build.
+    'USE_HEADERMAP' => 'NO',
+    'ALWAYS_SEARCH_USER_PATHS' => 'NO',
+  }
+
+  s.libraries = 'c++'
+
+  s.default_subspecs = 'Interface', 'Implementation'
+
+  s.subspec 'Interface' do |ss|
+    ss.header_mappings_dir = 'include/grpc++'
+
+    ss.source_files = '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++/ext/health_check_service_server_builder_option.h',
+                      'include/grpc++/generic/async_generic_service.h',
+                      'include/grpc++/generic/generic_stub.h',
+                      'include/grpc++/grpc++.h',
+                      'include/grpc++/health_check_service_interface.h',
+                      'include/grpc++/impl/call.h',
+                      'include/grpc++/impl/channel_argument_option.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/byte_buffer.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/metadata_map.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/slice.h',
+                      'include/grpc++/impl/codegen/status.h',
+                      'include/grpc++/impl/codegen/status_code_enum.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'
+  end
+
+  s.subspec 'Implementation' do |ss|
+    ss.header_mappings_dir = '.'
+    ss.dependency "#{s.name}/Interface", version
+    ss.dependency 'gRPC-Core', grpc_version
+    ss.dependency 'nanopb', '~> 0.3'
+
+    ss.source_files = '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/health/default_health_check_service.h',
+                      'src/cpp/server/health/health.pb.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/channel_argument_option.cc',
+                      'src/cpp/server/create_default_thread_pool.cc',
+                      'src/cpp/server/dynamic_thread_pool.cc',
+                      'src/cpp/server/health/default_health_check_service.cc',
+                      'src/cpp/server/health/health.pb.c',
+                      'src/cpp/server/health/health_check_service.cc',
+                      'src/cpp/server/health/health_check_service_server_builder_option.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/lib/gpr/arena.h',
+                      'src/core/lib/gpr/env.h',
+                      'src/core/lib/gpr/fork.h',
+                      'src/core/lib/gpr/mpscq.h',
+                      'src/core/lib/gpr/murmur_hash.h',
+                      'src/core/lib/gpr/spinlock.h',
+                      'src/core/lib/gpr/string.h',
+                      'src/core/lib/gpr/string_windows.h',
+                      'src/core/lib/gpr/thd_internal.h',
+                      'src/core/lib/gpr/time_precise.h',
+                      'src/core/lib/gpr/tmpfile.h',
+                      'src/core/lib/gprpp/abstract.h',
+                      'src/core/lib/gprpp/atomic.h',
+                      'src/core/lib/gprpp/atomic_with_atm.h',
+                      'src/core/lib/gprpp/atomic_with_std.h',
+                      'src/core/lib/gprpp/manual_constructor.h',
+                      'src/core/lib/gprpp/memory.h',
+                      'src/core/lib/profiling/timers.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/flow_control.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_settings.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/stream_map.h',
+                      'src/core/ext/transport/chttp2/transport/varint.h',
+                      'src/core/ext/transport/chttp2/alpn/alpn.h',
+                      'src/core/ext/filters/http/client/http_client_filter.h',
+                      'src/core/ext/filters/http/message_compress/message_compress_filter.h',
+                      'src/core/ext/filters/http/server/http_server_filter.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/lb_targets_info.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/json_util.h',
+                      'src/core/tsi/fake_transport_security.h',
+                      'src/core/tsi/gts_transport_security.h',
+                      'src/core/tsi/ssl_transport_security.h',
+                      'src/core/tsi/ssl_types.h',
+                      'src/core/tsi/transport_security_grpc.h',
+                      'src/core/tsi/transport_security.h',
+                      'src/core/tsi/transport_security_adapter.h',
+                      'src/core/tsi/transport_security_interface.h',
+                      'src/core/ext/transport/chttp2/server/chttp2_server.h',
+                      'src/core/ext/filters/client_channel/backup_poller.h',
+                      'src/core/ext/filters/client_channel/client_channel.h',
+                      'src/core/ext/filters/client_channel/client_channel_factory.h',
+                      'src/core/ext/filters/client_channel/connector.h',
+                      'src/core/ext/filters/client_channel/http_connect_handshaker.h',
+                      'src/core/ext/filters/client_channel/http_proxy.h',
+                      'src/core/ext/filters/client_channel/lb_policy.h',
+                      'src/core/ext/filters/client_channel/lb_policy_factory.h',
+                      'src/core/ext/filters/client_channel/lb_policy_registry.h',
+                      'src/core/ext/filters/client_channel/parse_address.h',
+                      'src/core/ext/filters/client_channel/proxy_mapper.h',
+                      'src/core/ext/filters/client_channel/proxy_mapper_registry.h',
+                      'src/core/ext/filters/client_channel/resolver.h',
+                      'src/core/ext/filters/client_channel/resolver_factory.h',
+                      'src/core/ext/filters/client_channel/resolver_registry.h',
+                      'src/core/ext/filters/client_channel/retry_throttle.h',
+                      'src/core/ext/filters/client_channel/subchannel.h',
+                      'src/core/ext/filters/client_channel/subchannel_index.h',
+                      'src/core/ext/filters/client_channel/uri_parser.h',
+                      'src/core/ext/filters/deadline/deadline_filter.h',
+                      'src/core/ext/transport/chttp2/client/chttp2_connector.h',
+                      'src/core/ext/transport/inproc/inproc_transport.h',
+                      'src/core/lib/backoff/backoff.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/connected_channel.h',
+                      'src/core/lib/channel/context.h',
+                      'src/core/lib/channel/handshaker.h',
+                      'src/core/lib/channel/handshaker_factory.h',
+                      'src/core/lib/channel/handshaker_registry.h',
+                      'src/core/lib/compression/algorithm_metadata.h',
+                      'src/core/lib/compression/message_compress.h',
+                      'src/core/lib/compression/stream_compression.h',
+                      'src/core/lib/compression/stream_compression_gzip.h',
+                      'src/core/lib/compression/stream_compression_identity.h',
+                      'src/core/lib/debug/stats.h',
+                      'src/core/lib/debug/stats_data.h',
+                      'src/core/lib/gprpp/debug_location.h',
+                      'src/core/lib/gprpp/inlined_vector.h',
+                      'src/core/lib/gprpp/orphanable.h',
+                      'src/core/lib/gprpp/ref_counted.h',
+                      'src/core/lib/gprpp/ref_counted_ptr.h',
+                      'src/core/lib/http/format_request.h',
+                      'src/core/lib/http/httpcli.h',
+                      'src/core/lib/http/parser.h',
+                      'src/core/lib/iomgr/block_annotate.h',
+                      'src/core/lib/iomgr/call_combiner.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/error_internal.h',
+                      'src/core/lib/iomgr/ev_epoll1_linux.h',
+                      'src/core/lib/iomgr/ev_epollex_linux.h',
+                      'src/core/lib/iomgr/ev_epollsig_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/gethostname.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/iomgr_uv.h',
+                      'src/core/lib/iomgr/is_epollexclusive_available.h',
+                      'src/core/lib/iomgr/load_file.h',
+                      'src/core/lib/iomgr/lockfree_event.h',
+                      'src/core/lib/iomgr/nameser.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_factory_posix.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/sys_epoll_wrapper.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_server_utils_posix.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_manager.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/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/b64.h',
+                      'src/core/lib/slice/percent_encoding.h',
+                      'src/core/lib/slice/slice_hash_table.h',
+                      'src/core/lib/slice/slice_internal.h',
+                      'src/core/lib/slice/slice_string_helpers.h',
+                      'src/core/lib/surface/alarm_internal.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/completion_queue_factory.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/surface/validate_metadata.h',
+                      'src/core/lib/transport/bdp_estimator.h',
+                      'src/core/lib/transport/byte_stream.h',
+                      'src/core/lib/transport/connectivity_state.h',
+                      'src/core/lib/transport/error_utils.h',
+                      'src/core/lib/transport/http2_errors.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/status_conversion.h',
+                      'src/core/lib/transport/timeout_encoding.h',
+                      'src/core/lib/transport/transport.h',
+                      'src/core/lib/transport/transport_impl.h',
+                      'src/core/lib/debug/trace.h',
+                      'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h',
+                      'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h',
+                      'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h',
+                      'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h',
+                      'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h',
+                      'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h',
+                      'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h',
+                      'src/core/ext/filters/client_channel/lb_policy/subchannel_list.h',
+                      'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h',
+                      'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h',
+                      'src/core/ext/filters/load_reporting/server_load_reporting_filter.h',
+                      'src/core/ext/filters/load_reporting/server_load_reporting_plugin.h',
+                      'src/core/ext/filters/max_age/max_age_filter.h',
+                      'src/core/ext/filters/message_size/message_size_filter.h',
+                      'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h',
+                      'src/core/ext/filters/workarounds/workaround_utils.h'
+
+    ss.private_header_files = '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/health/default_health_check_service.h',
+                              'src/cpp/server/health/health.pb.h',
+                              'src/cpp/server/thread_pool_interface.h',
+                              'src/cpp/thread_manager/thread_manager.h',
+                              'src/core/lib/gpr/arena.h',
+                              'src/core/lib/gpr/env.h',
+                              'src/core/lib/gpr/fork.h',
+                              'src/core/lib/gpr/mpscq.h',
+                              'src/core/lib/gpr/murmur_hash.h',
+                              'src/core/lib/gpr/spinlock.h',
+                              'src/core/lib/gpr/string.h',
+                              'src/core/lib/gpr/string_windows.h',
+                              'src/core/lib/gpr/thd_internal.h',
+                              'src/core/lib/gpr/time_precise.h',
+                              'src/core/lib/gpr/tmpfile.h',
+                              'src/core/lib/gprpp/abstract.h',
+                              'src/core/lib/gprpp/atomic.h',
+                              'src/core/lib/gprpp/atomic_with_atm.h',
+                              'src/core/lib/gprpp/atomic_with_std.h',
+                              'src/core/lib/gprpp/manual_constructor.h',
+                              'src/core/lib/gprpp/memory.h',
+                              'src/core/lib/profiling/timers.h',
+                              'src/core/lib/backoff/backoff.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/connected_channel.h',
+                              'src/core/lib/channel/context.h',
+                              'src/core/lib/channel/handshaker.h',
+                              'src/core/lib/channel/handshaker_factory.h',
+                              'src/core/lib/channel/handshaker_registry.h',
+                              'src/core/lib/compression/algorithm_metadata.h',
+                              'src/core/lib/compression/message_compress.h',
+                              'src/core/lib/compression/stream_compression.h',
+                              'src/core/lib/compression/stream_compression_gzip.h',
+                              'src/core/lib/compression/stream_compression_identity.h',
+                              'src/core/lib/debug/stats.h',
+                              'src/core/lib/debug/stats_data.h',
+                              'src/core/lib/gprpp/debug_location.h',
+                              'src/core/lib/gprpp/inlined_vector.h',
+                              'src/core/lib/gprpp/orphanable.h',
+                              'src/core/lib/gprpp/ref_counted.h',
+                              'src/core/lib/gprpp/ref_counted_ptr.h',
+                              'src/core/lib/http/format_request.h',
+                              'src/core/lib/http/httpcli.h',
+                              'src/core/lib/http/parser.h',
+                              'src/core/lib/iomgr/block_annotate.h',
+                              'src/core/lib/iomgr/call_combiner.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/error_internal.h',
+                              'src/core/lib/iomgr/ev_epoll1_linux.h',
+                              'src/core/lib/iomgr/ev_epollex_linux.h',
+                              'src/core/lib/iomgr/ev_epollsig_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/gethostname.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/iomgr_uv.h',
+                              'src/core/lib/iomgr/is_epollexclusive_available.h',
+                              'src/core/lib/iomgr/load_file.h',
+                              'src/core/lib/iomgr/lockfree_event.h',
+                              'src/core/lib/iomgr/nameser.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_factory_posix.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/sys_epoll_wrapper.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_server_utils_posix.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_manager.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/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/b64.h',
+                              'src/core/lib/slice/percent_encoding.h',
+                              'src/core/lib/slice/slice_hash_table.h',
+                              'src/core/lib/slice/slice_internal.h',
+                              'src/core/lib/slice/slice_string_helpers.h',
+                              'src/core/lib/surface/alarm_internal.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/completion_queue_factory.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/surface/validate_metadata.h',
+                              'src/core/lib/transport/bdp_estimator.h',
+                              'src/core/lib/transport/byte_stream.h',
+                              'src/core/lib/transport/connectivity_state.h',
+                              'src/core/lib/transport/error_utils.h',
+                              'src/core/lib/transport/http2_errors.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/status_conversion.h',
+                              'src/core/lib/transport/timeout_encoding.h',
+                              'src/core/lib/transport/transport.h',
+                              'src/core/lib/transport/transport_impl.h',
+                              'src/core/lib/debug/trace.h',
+                              'src/core/ext/transport/inproc/inproc_transport.h'
+  end
+
+  s.subspec 'Tests' do |ss|
+    ss.header_mappings_dir = '.'
+
+    ss.dependency "#{s.name}/Interface", version
+    ss.dependency "#{s.name}/Implementation", version
+    ss.dependency "gRPC-Core/Tests", grpc_version
+
+    ss.source_files = 'test/cpp/util/create_test_channel.cc',
+                      'test/cpp/util/string_ref_helper.cc',
+                      'test/cpp/util/subprocess.cc',
+                      'test/cpp/util/test_credentials_provider.cc',
+                      'test/cpp/util/create_test_channel.h',
+                      'test/cpp/util/string_ref_helper.h',
+                      'test/cpp/util/subprocess.h',
+                      'test/cpp/util/test_credentials_provider.h',
+                      'test/core/util/test_config.h',
+                      'test/core/end2end/data/ssl_test_data.h',
+                      'test/core/security/oauth2_utils.h',
+                      'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h',
+                      'test/core/end2end/cq_verifier.h',
+                      'test/core/end2end/fixtures/http_proxy_fixture.h',
+                      'test/core/end2end/fixtures/proxy.h',
+                      'test/core/iomgr/endpoint_tests.h',
+                      'test/core/util/debugger_macros.h',
+                      'test/core/util/grpc_profiler.h',
+                      'test/core/util/histogram.h',
+                      'test/core/util/memory_counters.h',
+                      'test/core/util/mock_endpoint.h',
+                      'test/core/util/parse_hexstring.h',
+                      'test/core/util/passthru_endpoint.h',
+                      'test/core/util/port.h',
+                      'test/core/util/port_server_client.h',
+                      'test/core/util/slice_splitter.h',
+                      'test/core/util/tracer_util.h',
+                      'test/core/util/trickle_endpoint.h',
+                      'src/core/ext/filters/client_channel/backup_poller.h',
+                      'src/core/ext/filters/client_channel/client_channel.h',
+                      'src/core/ext/filters/client_channel/client_channel_factory.h',
+                      'src/core/ext/filters/client_channel/connector.h',
+                      'src/core/ext/filters/client_channel/http_connect_handshaker.h',
+                      'src/core/ext/filters/client_channel/http_proxy.h',
+                      'src/core/ext/filters/client_channel/lb_policy.h',
+                      'src/core/ext/filters/client_channel/lb_policy_factory.h',
+                      'src/core/ext/filters/client_channel/lb_policy_registry.h',
+                      'src/core/ext/filters/client_channel/parse_address.h',
+                      'src/core/ext/filters/client_channel/proxy_mapper.h',
+                      'src/core/ext/filters/client_channel/proxy_mapper_registry.h',
+                      'src/core/ext/filters/client_channel/resolver.h',
+                      'src/core/ext/filters/client_channel/resolver_factory.h',
+                      'src/core/ext/filters/client_channel/resolver_registry.h',
+                      'src/core/ext/filters/client_channel/retry_throttle.h',
+                      'src/core/ext/filters/client_channel/subchannel.h',
+                      'src/core/ext/filters/client_channel/subchannel_index.h',
+                      'src/core/ext/filters/client_channel/uri_parser.h',
+                      'src/core/ext/filters/deadline/deadline_filter.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/flow_control.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_settings.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/stream_map.h',
+                      'src/core/ext/transport/chttp2/transport/varint.h',
+                      'src/core/ext/transport/chttp2/alpn/alpn.h',
+                      'src/core/ext/filters/http/client/http_client_filter.h',
+                      'src/core/ext/filters/http/message_compress/message_compress_filter.h',
+                      'src/core/ext/filters/http/server/http_server_filter.h'
+  end
+
+  s.prepare_command = <<-END_OF_COMMAND
+    find src/cpp/ -type f -exec sed -E -i'.back' 's;#include "third_party/nanopb/(.*)";#include <nanopb/\\1>;g' {} \\\;
+    find src/cpp/ -name "*.back" -type f -delete
+    find src/core/ -regex ".*\.h" -type f -exec sed -E -i'.back' 's;#include "third_party/nanopb/(.*)";#include <nanopb/\\1>;g' {} \\\;
+    find src/core/ -name "*.back" -type f -delete
+  END_OF_COMMAND
+end
diff --git a/include/grpc/support/alloc.h b/include/grpc/support/alloc.h
index c559198..577d4f0 100644
--- a/include/grpc/support/alloc.h
+++ b/include/grpc/support/alloc.h
@@ -46,8 +46,9 @@
 GPRAPI void gpr_free(void* ptr);
 /** realloc, never returns NULL */
 GPRAPI void* gpr_realloc(void* p, size_t size);
-/** aligned malloc, never returns NULL, will align to 1 << alignment_log */
-GPRAPI void* gpr_malloc_aligned(size_t size, size_t alignment_log);
+/** aligned malloc, never returns NULL, will align to alignment, which
+ * must be a power of 2. */
+GPRAPI void* gpr_malloc_aligned(size_t size, size_t alignment);
 /** free memory allocated by gpr_malloc_aligned */
 GPRAPI void gpr_free_aligned(void* ptr);
 
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
index e067b69..530ab17 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
@@ -1886,7 +1886,7 @@
   grpc_chttp2_maybe_complete_recv_message(t, s);
   if (s->recv_trailing_metadata_finished != nullptr && s->read_closed &&
       s->write_closed) {
-    if (s->seen_error) {
+    if (s->seen_error || !t->is_client) {
       grpc_slice_buffer_reset_and_unref_internal(&s->frame_storage);
       if (!s->pending_byte_stream) {
         grpc_slice_buffer_reset_and_unref_internal(
diff --git a/src/core/lib/gpr/alloc.cc b/src/core/lib/gpr/alloc.cc
index 518bdb9..000b7dc 100644
--- a/src/core/lib/gpr/alloc.cc
+++ b/src/core/lib/gpr/alloc.cc
@@ -90,8 +90,8 @@
   return p;
 }
 
-void* gpr_malloc_aligned(size_t size, size_t alignment_log) {
-  size_t alignment = ((size_t)1) << alignment_log;
+void* gpr_malloc_aligned(size_t size, size_t alignment) {
+  GPR_ASSERT(((alignment - 1) & alignment) == 0);  // Must be power of 2.
   size_t extra = alignment - 1 + sizeof(void*);
   void* p = gpr_malloc(size + extra);
   void** ret = (void**)(((uintptr_t)p + extra) & ~(alignment - 1));
diff --git a/src/core/lib/gpr/arena.cc b/src/core/lib/gpr/arena.cc
index 177c176..687592a 100644
--- a/src/core/lib/gpr/arena.cc
+++ b/src/core/lib/gpr/arena.cc
@@ -17,11 +17,19 @@
  */
 
 #include "src/core/lib/gpr/arena.h"
+
+#include <string.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/atm.h>
 #include <grpc/support/log.h>
 #include <grpc/support/useful.h>
 
+// TODO(roth): We currently assume that all callers need alignment of 16
+// bytes, which may be wrong in some cases.  As part of converting the
+// arena API to C++, we should consider replacing gpr_arena_alloc() with a
+// template that takes the type of the value being allocated, which
+// would allow us to use the alignment actually needed by the caller.
 #define ROUND_UP_TO_ALIGNMENT_SIZE(x) \
   (((x) + GPR_MAX_ALIGNMENT - 1u) & ~(GPR_MAX_ALIGNMENT - 1u))
 
@@ -36,9 +44,16 @@
   zone initial_zone;
 };
 
+static void* zalloc_aligned(size_t size) {
+  void* ptr = gpr_malloc_aligned(size, GPR_MAX_ALIGNMENT);
+  memset(ptr, 0, size);
+  return ptr;
+}
+
 gpr_arena* gpr_arena_create(size_t initial_size) {
   initial_size = ROUND_UP_TO_ALIGNMENT_SIZE(initial_size);
-  gpr_arena* a = (gpr_arena*)gpr_zalloc(sizeof(gpr_arena) + initial_size);
+  gpr_arena* a = (gpr_arena*)zalloc_aligned(
+      ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(gpr_arena)) + initial_size);
   a->initial_zone.size_end = initial_size;
   return a;
 }
@@ -46,10 +61,10 @@
 size_t gpr_arena_destroy(gpr_arena* arena) {
   gpr_atm size = gpr_atm_no_barrier_load(&arena->size_so_far);
   zone* z = (zone*)gpr_atm_no_barrier_load(&arena->initial_zone.next_atm);
-  gpr_free(arena);
+  gpr_free_aligned(arena);
   while (z) {
     zone* next_z = (zone*)gpr_atm_no_barrier_load(&z->next_atm);
-    gpr_free(z);
+    gpr_free_aligned(z);
     z = next_z;
   }
   return (size_t)size;
@@ -64,11 +79,12 @@
     zone* next_z = (zone*)gpr_atm_acq_load(&z->next_atm);
     if (next_z == nullptr) {
       size_t next_z_size = (size_t)gpr_atm_no_barrier_load(&arena->size_so_far);
-      next_z = (zone*)gpr_zalloc(sizeof(zone) + next_z_size);
+      next_z = (zone*)zalloc_aligned(ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone)) +
+                                     next_z_size);
       next_z->size_begin = z->size_end;
       next_z->size_end = z->size_end + next_z_size;
       if (!gpr_atm_rel_cas(&z->next_atm, (gpr_atm)NULL, (gpr_atm)next_z)) {
-        gpr_free(next_z);
+        gpr_free_aligned(next_z);
         next_z = (zone*)gpr_atm_acq_load(&z->next_atm);
       }
     }
@@ -79,5 +95,8 @@
   }
   GPR_ASSERT(start >= z->size_begin);
   GPR_ASSERT(start + size <= z->size_end);
-  return ((char*)(z + 1)) + start - z->size_begin;
+  char* ptr = (z == &arena->initial_zone)
+                  ? (char*)arena + ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(gpr_arena))
+                  : (char*)z + ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone));
+  return ptr + start - z->size_begin;
 }
diff --git a/src/core/lib/gpr/fork.cc b/src/core/lib/gpr/fork.cc
index c47e686..92023f4 100644
--- a/src/core/lib/gpr/fork.cc
+++ b/src/core/lib/gpr/fork.cc
@@ -38,18 +38,32 @@
   fork_support_enabled = 1;
 #else
   fork_support_enabled = 0;
+#endif
+  bool env_var_set = false;
   char* env = gpr_getenv("GRPC_ENABLE_FORK_SUPPORT");
   if (env != nullptr) {
     static const char* truthy[] = {"yes",  "Yes",  "YES", "true",
                                    "True", "TRUE", "1"};
+    static const char* falsey[] = {"no",    "No",    "NO", "false",
+                                   "False", "FALSE", "0"};
     for (size_t i = 0; i < GPR_ARRAY_SIZE(truthy); i++) {
       if (0 == strcmp(env, truthy[i])) {
         fork_support_enabled = 1;
+        env_var_set = true;
+        break;
+      }
+    }
+    if (!env_var_set) {
+      for (size_t i = 0; i < GPR_ARRAY_SIZE(falsey); i++) {
+        if (0 == strcmp(env, falsey[i])) {
+          fork_support_enabled = 0;
+          env_var_set = true;
+          break;
+        }
       }
     }
     gpr_free(env);
   }
-#endif
   if (override_fork_support_enabled != -1) {
     fork_support_enabled = override_fork_support_enabled;
   }
diff --git a/src/core/tsi/ssl_transport_security.cc b/src/core/tsi/ssl_transport_security.cc
index 229f7ef..b396a62 100644
--- a/src/core/tsi/ssl_transport_security.cc
+++ b/src/core/tsi/ssl_transport_security.cc
@@ -96,8 +96,7 @@
 typedef struct {
   tsi_handshaker base;
   SSL* ssl;
-  BIO* into_ssl;
-  BIO* from_ssl;
+  BIO* network_io;
   tsi_result result;
   tsi_ssl_handshaker_factory* factory_ref;
 } tsi_ssl_handshaker;
@@ -105,8 +104,7 @@
 typedef struct {
   tsi_frame_protector base;
   SSL* ssl;
-  BIO* into_ssl;
-  BIO* from_ssl;
+  BIO* network_io;
   unsigned char* buffer;
   size_t buffer_size;
   size_t buffer_offset;
@@ -730,11 +728,11 @@
   tsi_result result = TSI_OK;
 
   /* First see if we have some pending data in the SSL BIO. */
-  int pending_in_ssl = (int)BIO_pending(impl->from_ssl);
+  int pending_in_ssl = (int)BIO_pending(impl->network_io);
   if (pending_in_ssl > 0) {
     *unprotected_bytes_size = 0;
     GPR_ASSERT(*protected_output_frames_size <= INT_MAX);
-    read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames,
+    read_from_ssl = BIO_read(impl->network_io, protected_output_frames,
                              (int)*protected_output_frames_size);
     if (read_from_ssl < 0) {
       gpr_log(GPR_ERROR,
@@ -762,7 +760,7 @@
   if (result != TSI_OK) return result;
 
   GPR_ASSERT(*protected_output_frames_size <= INT_MAX);
-  read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames,
+  read_from_ssl = BIO_read(impl->network_io, protected_output_frames,
                            (int)*protected_output_frames_size);
   if (read_from_ssl < 0) {
     gpr_log(GPR_ERROR, "Could not read from BIO after SSL_write.");
@@ -788,20 +786,20 @@
     impl->buffer_offset = 0;
   }
 
-  pending = (int)BIO_pending(impl->from_ssl);
+  pending = (int)BIO_pending(impl->network_io);
   GPR_ASSERT(pending >= 0);
   *still_pending_size = (size_t)pending;
   if (*still_pending_size == 0) return TSI_OK;
 
   GPR_ASSERT(*protected_output_frames_size <= INT_MAX);
-  read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames,
+  read_from_ssl = BIO_read(impl->network_io, protected_output_frames,
                            (int)*protected_output_frames_size);
   if (read_from_ssl <= 0) {
     gpr_log(GPR_ERROR, "Could not read from BIO after SSL_write.");
     return TSI_INTERNAL_ERROR;
   }
   *protected_output_frames_size = (size_t)read_from_ssl;
-  pending = (int)BIO_pending(impl->from_ssl);
+  pending = (int)BIO_pending(impl->network_io);
   GPR_ASSERT(pending >= 0);
   *still_pending_size = (size_t)pending;
   return TSI_OK;
@@ -831,7 +829,7 @@
 
   /* Then, try to write some data to ssl. */
   GPR_ASSERT(*protected_frames_bytes_size <= INT_MAX);
-  written_into_ssl = BIO_write(impl->into_ssl, protected_frames_bytes,
+  written_into_ssl = BIO_write(impl->network_io, protected_frames_bytes,
                                (int)*protected_frames_bytes_size);
   if (written_into_ssl < 0) {
     gpr_log(GPR_ERROR, "Sending protected frame to ssl failed with %d",
@@ -853,6 +851,7 @@
   tsi_ssl_frame_protector* impl = (tsi_ssl_frame_protector*)self;
   if (impl->buffer != nullptr) gpr_free(impl->buffer);
   if (impl->ssl != nullptr) SSL_free(impl->ssl);
+  if (impl->network_io != nullptr) BIO_free(impl->network_io);
   gpr_free(self);
 }
 
@@ -916,10 +915,10 @@
     return TSI_INVALID_ARGUMENT;
   }
   GPR_ASSERT(*bytes_size <= INT_MAX);
-  bytes_read_from_ssl = BIO_read(impl->from_ssl, bytes, (int)*bytes_size);
+  bytes_read_from_ssl = BIO_read(impl->network_io, bytes, (int)*bytes_size);
   if (bytes_read_from_ssl < 0) {
     *bytes_size = 0;
-    if (!BIO_should_retry(impl->from_ssl)) {
+    if (!BIO_should_retry(impl->network_io)) {
       impl->result = TSI_INTERNAL_ERROR;
       return impl->result;
     } else {
@@ -927,7 +926,7 @@
     }
   }
   *bytes_size = (size_t)bytes_read_from_ssl;
-  return BIO_pending(impl->from_ssl) == 0 ? TSI_OK : TSI_INCOMPLETE_DATA;
+  return BIO_pending(impl->network_io) == 0 ? TSI_OK : TSI_INCOMPLETE_DATA;
 }
 
 static tsi_result ssl_handshaker_get_result(tsi_handshaker* self) {
@@ -948,7 +947,7 @@
   }
   GPR_ASSERT(*bytes_size <= INT_MAX);
   bytes_written_into_ssl_size =
-      BIO_write(impl->into_ssl, bytes, (int)*bytes_size);
+      BIO_write(impl->network_io, bytes, (int)*bytes_size);
   if (bytes_written_into_ssl_size < 0) {
     gpr_log(GPR_ERROR, "Could not write to memory BIO.");
     impl->result = TSI_INTERNAL_ERROR;
@@ -965,7 +964,7 @@
     ssl_result = SSL_get_error(impl->ssl, ssl_result);
     switch (ssl_result) {
       case SSL_ERROR_WANT_READ:
-        if (BIO_pending(impl->from_ssl) == 0) {
+        if (BIO_pending(impl->network_io) == 0) {
           /* We need more data. */
           return TSI_INCOMPLETE_DATA;
         } else {
@@ -1058,12 +1057,13 @@
     return TSI_INTERNAL_ERROR;
   }
 
-  /* Transfer ownership of ssl to the frame protector. It is OK as the caller
-   * cannot call anything else but destroy on the handshaker after this call. */
+  /* Transfer ownership of ssl and network_io to the frame protector. It is OK
+   * as the caller cannot call anything else but destroy on the handshaker
+   * after this call. */
   protector_impl->ssl = impl->ssl;
   impl->ssl = nullptr;
-  protector_impl->into_ssl = impl->into_ssl;
-  protector_impl->from_ssl = impl->from_ssl;
+  protector_impl->network_io = impl->network_io;
+  impl->network_io = nullptr;
 
   protector_impl->base.vtable = &frame_protector_vtable;
   *protector = &protector_impl->base;
@@ -1072,7 +1072,8 @@
 
 static void ssl_handshaker_destroy(tsi_handshaker* self) {
   tsi_ssl_handshaker* impl = (tsi_ssl_handshaker*)self;
-  SSL_free(impl->ssl); /* The BIO objects are owned by ssl */
+  SSL_free(impl->ssl);
+  BIO_free(impl->network_io);
   tsi_ssl_handshaker_factory_unref(impl->factory_ref);
   gpr_free(impl);
 }
@@ -1094,8 +1095,8 @@
                                             tsi_ssl_handshaker_factory* factory,
                                             tsi_handshaker** handshaker) {
   SSL* ssl = SSL_new(ctx);
-  BIO* into_ssl = nullptr;
-  BIO* from_ssl = nullptr;
+  BIO* network_io = nullptr;
+  BIO* ssl_io = nullptr;
   tsi_ssl_handshaker* impl = nullptr;
   *handshaker = nullptr;
   if (ctx == nullptr) {
@@ -1107,16 +1108,12 @@
   }
   SSL_set_info_callback(ssl, ssl_info_callback);
 
-  into_ssl = BIO_new(BIO_s_mem());
-  from_ssl = BIO_new(BIO_s_mem());
-  if (into_ssl == nullptr || from_ssl == nullptr) {
-    gpr_log(GPR_ERROR, "BIO_new failed.");
+  if (!BIO_new_bio_pair(&network_io, 0, &ssl_io, 0)) {
+    gpr_log(GPR_ERROR, "BIO_new_bio_pair failed.");
     SSL_free(ssl);
-    if (into_ssl != nullptr) BIO_free(into_ssl);
-    if (from_ssl != nullptr) BIO_free(into_ssl);
     return TSI_OUT_OF_RESOURCES;
   }
-  SSL_set_bio(ssl, into_ssl, from_ssl);
+  SSL_set_bio(ssl, ssl_io, ssl_io);
   if (is_client) {
     int ssl_result;
     SSL_set_connect_state(ssl);
@@ -1125,6 +1122,7 @@
         gpr_log(GPR_ERROR, "Invalid server name indication %s.",
                 server_name_indication);
         SSL_free(ssl);
+        BIO_free(network_io);
         return TSI_INTERNAL_ERROR;
       }
     }
@@ -1135,6 +1133,7 @@
               "Unexpected error received from first SSL_do_handshake call: %s",
               ssl_error_string(ssl_result));
       SSL_free(ssl);
+      BIO_free(network_io);
       return TSI_INTERNAL_ERROR;
     }
   } else {
@@ -1143,8 +1142,7 @@
 
   impl = (tsi_ssl_handshaker*)gpr_zalloc(sizeof(*impl));
   impl->ssl = ssl;
-  impl->into_ssl = into_ssl;
-  impl->from_ssl = from_ssl;
+  impl->network_io = network_io;
   impl->result = TSI_HANDSHAKE_IN_PROGRESS;
   impl->base.vtable = &handshaker_vtable;
   impl->factory_ref = tsi_ssl_handshaker_factory_ref(factory);
diff --git a/src/csharp/Grpc.Core.Tests/Internal/DefaultObjectPoolTest.cs b/src/csharp/Grpc.Core.Tests/Internal/DefaultObjectPoolTest.cs
index b6bb0a9..9c6f8a2 100644
--- a/src/csharp/Grpc.Core.Tests/Internal/DefaultObjectPoolTest.cs
+++ b/src/csharp/Grpc.Core.Tests/Internal/DefaultObjectPoolTest.cs
@@ -60,6 +60,16 @@
         }
 
         [Test]
+        public void LeaseSetsReturnAction()
+        {
+            var pool = new DefaultObjectPool<TestPooledObject>(() => new TestPooledObject(), 10, 0);
+            var origLeased = pool.Lease();
+            origLeased.ReturnAction(origLeased);
+            pool.Dispose();
+            Assert.AreNotSame(origLeased, pool.Lease());
+        }
+
+        [Test]
         public void Constructor()
         {
             Assert.Throws<ArgumentNullException>(() => new DefaultObjectPool<TestPooledObject>(null, 10, 2));
@@ -67,8 +77,14 @@
             Assert.Throws<ArgumentException>(() => new DefaultObjectPool<TestPooledObject>(() => new TestPooledObject(), 10, -1));
         }
 
-        class TestPooledObject : IDisposable
+        class TestPooledObject : IPooledObject<TestPooledObject>
         {
+            public Action<TestPooledObject> ReturnAction;
+
+            public void SetReturnToPoolAction(Action<TestPooledObject> returnAction)
+            {
+                this.ReturnAction = returnAction;
+            }
 
             public void Dispose()
             {
diff --git a/src/csharp/Grpc.Core/GrpcEnvironment.cs b/src/csharp/Grpc.Core/GrpcEnvironment.cs
index 6296f18..6bb2f6c 100644
--- a/src/csharp/Grpc.Core/GrpcEnvironment.cs
+++ b/src/csharp/Grpc.Core/GrpcEnvironment.cs
@@ -299,8 +299,8 @@
         private GrpcEnvironment()
         {
             GrpcNativeInit();
-            batchContextPool = new DefaultObjectPool<BatchContextSafeHandle>(() => BatchContextSafeHandle.Create(this.batchContextPool), batchContextPoolSharedCapacity, batchContextPoolThreadLocalCapacity);
-            requestCallContextPool = new DefaultObjectPool<RequestCallContextSafeHandle>(() => RequestCallContextSafeHandle.Create(this.requestCallContextPool), requestCallContextPoolSharedCapacity, requestCallContextPoolThreadLocalCapacity);
+            batchContextPool = new DefaultObjectPool<BatchContextSafeHandle>(() => BatchContextSafeHandle.Create(), batchContextPoolSharedCapacity, batchContextPoolThreadLocalCapacity);
+            requestCallContextPool = new DefaultObjectPool<RequestCallContextSafeHandle>(() => RequestCallContextSafeHandle.Create(), requestCallContextPoolSharedCapacity, requestCallContextPoolThreadLocalCapacity);
             threadPool = new GrpcThreadPool(this, GetThreadPoolSizeOrDefault(), GetCompletionQueueCountOrDefault(), inlineHandlers);
             threadPool.Start();
         }
diff --git a/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs b/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs
index 83385ad..53a859d 100644
--- a/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs
@@ -33,22 +33,21 @@
     /// <summary>
     /// grpcsharp_batch_context
     /// </summary>
-    internal class BatchContextSafeHandle : SafeHandleZeroIsInvalid, IOpCompletionCallback
+    internal class BatchContextSafeHandle : SafeHandleZeroIsInvalid, IOpCompletionCallback, IPooledObject<BatchContextSafeHandle>
     {
         static readonly NativeMethods Native = NativeMethods.Get();
         static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<BatchContextSafeHandle>();
 
-        IObjectPool<BatchContextSafeHandle> ownedByPool;
+        Action<BatchContextSafeHandle> returnToPoolAction;
         CompletionCallbackData completionCallbackData;
 
         private BatchContextSafeHandle()
         {
         }
 
-        public static BatchContextSafeHandle Create(IObjectPool<BatchContextSafeHandle> ownedByPool = null)
+        public static BatchContextSafeHandle Create()
         {
             var ctx = Native.grpcsharp_batch_context_create();
-            ctx.ownedByPool = ownedByPool;
             return ctx;
         }
 
@@ -60,6 +59,12 @@
             }
         }
 
+        public void SetReturnToPoolAction(Action<BatchContextSafeHandle> returnAction)
+        {
+            GrpcPreconditions.CheckState(returnToPoolAction == null);
+            returnToPoolAction = returnAction;
+        }
+
         public void SetCompletionCallback(BatchCompletionDelegate callback, object state)
         {
             GrpcPreconditions.CheckState(completionCallbackData.Callback == null);
@@ -109,10 +114,15 @@
 
         public void Recycle()
         {
-            if (ownedByPool != null)
+            if (returnToPoolAction != null)
             {
                 Native.grpcsharp_batch_context_reset(this);
-                ownedByPool.Return(this);
+
+                var origReturnAction = returnToPoolAction;
+                // Not clearing all the references to the pool could prevent garbage collection of the pool object
+                // and thus cause memory leaks.
+                returnToPoolAction = null;
+                origReturnAction(this);
             }
             else
             {
diff --git a/src/csharp/Grpc.Core/Internal/DefaultObjectPool.cs b/src/csharp/Grpc.Core/Internal/DefaultObjectPool.cs
index 2f030f3..0e1dc4d 100644
--- a/src/csharp/Grpc.Core/Internal/DefaultObjectPool.cs
+++ b/src/csharp/Grpc.Core/Internal/DefaultObjectPool.cs
@@ -27,9 +27,10 @@
     /// Pool of objects that combines a shared pool and a thread local pool.
     /// </summary>
     internal class DefaultObjectPool<T> : IObjectPool<T>
-        where T : class, IDisposable
+        where T : class, IPooledObject<T>
     {
         readonly object myLock = new object();
+        readonly Action<T> returnAction;
         readonly Func<T> itemFactory;
 
         // Queue shared between threads, access needs to be synchronized.
@@ -54,6 +55,7 @@
         {
             GrpcPreconditions.CheckArgument(sharedCapacity >= 0);
             GrpcPreconditions.CheckArgument(threadLocalCapacity >= 0);
+            this.returnAction = Return;
             this.itemFactory = GrpcPreconditions.CheckNotNull(itemFactory, nameof(itemFactory));
             this.sharedQueue = new Queue<T>(sharedCapacity);
             this.sharedCapacity = sharedCapacity;
@@ -74,6 +76,13 @@
         /// </summary>
         public T Lease()
         {
+            var item = LeaseInternal();
+            item.SetReturnToPoolAction(returnAction);
+            return item;
+        }
+
+        private T LeaseInternal()
+        {
             var localData = threadLocalData.Value;
             if (localData.Queue.Count > 0)
             {
diff --git a/src/csharp/Grpc.Core/Internal/IPooledObject.cs b/src/csharp/Grpc.Core/Internal/IPooledObject.cs
new file mode 100644
index 0000000..e20bd51
--- /dev/null
+++ b/src/csharp/Grpc.Core/Internal/IPooledObject.cs
@@ -0,0 +1,34 @@
+#region Copyright notice and license
+
+// Copyright 2018 gRPC authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#endregion
+
+using System;
+
+namespace Grpc.Core.Internal
+{
+    /// <summary>
+    /// An object that can be pooled in <c>IObjectPool</c>.
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    internal interface IPooledObject<T> : IDisposable
+    {
+        /// <summary>
+        /// Set the action that will be invoked to return a leased object to the pool.
+        /// </summary>
+        void SetReturnToPoolAction(Action<T> returnAction);
+    }
+}
diff --git a/src/csharp/Grpc.Core/Internal/RequestCallContextSafeHandle.cs b/src/csharp/Grpc.Core/Internal/RequestCallContextSafeHandle.cs
index 59e9d9b..ebc2d6d 100644
--- a/src/csharp/Grpc.Core/Internal/RequestCallContextSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/RequestCallContextSafeHandle.cs
@@ -20,26 +20,26 @@
 using System.Runtime.InteropServices;
 using Grpc.Core;
 using Grpc.Core.Logging;
+using Grpc.Core.Utils;
 
 namespace Grpc.Core.Internal
 {
     /// <summary>
     /// grpcsharp_request_call_context
     /// </summary>
-    internal class RequestCallContextSafeHandle : SafeHandleZeroIsInvalid, IOpCompletionCallback
+    internal class RequestCallContextSafeHandle : SafeHandleZeroIsInvalid, IOpCompletionCallback, IPooledObject<RequestCallContextSafeHandle>
     {
         static readonly NativeMethods Native = NativeMethods.Get();
         static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<RequestCallContextSafeHandle>();
-        IObjectPool<RequestCallContextSafeHandle> ownedByPool;
+        Action<RequestCallContextSafeHandle> returnToPoolAction;
 
         private RequestCallContextSafeHandle()
         {
         }
 
-        public static RequestCallContextSafeHandle Create(IObjectPool<RequestCallContextSafeHandle> ownedByPool = null)
+        public static RequestCallContextSafeHandle Create()
         {
             var ctx = Native.grpcsharp_request_call_context_create();
-            ctx.ownedByPool = ownedByPool;
             return ctx;
         }
 
@@ -51,6 +51,12 @@
             }
         }
 
+        public void SetReturnToPoolAction(Action<RequestCallContextSafeHandle> returnAction)
+        {
+            GrpcPreconditions.CheckState(returnToPoolAction == null);
+            returnToPoolAction = returnAction;
+        }
+
         public RequestCallCompletionDelegate CompletionCallback { get; set; }
 
         // Gets data of server_rpc_new completion.
@@ -76,10 +82,15 @@
 
         public void Recycle()
         {
-            if (ownedByPool != null)
+            if (returnToPoolAction != null)
             {
                 Native.grpcsharp_request_call_context_reset(this);
-                ownedByPool.Return(this);
+
+                var origReturnAction = returnToPoolAction;
+                // Not clearing all the references to the pool could prevent garbage collection of the pool object
+                // and thus cause memory leaks.
+                returnToPoolAction = null;
+                origReturnAction(this);
             }
             else
             {
diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c
index c4997f7..9c0f3f8 100644
--- a/src/php/ext/grpc/call.c
+++ b/src/php/ext/grpc/call.c
@@ -174,6 +174,18 @@
   return true;
 }
 
+void grpc_php_metadata_array_destroy_including_entries(
+    grpc_metadata_array* array) {
+  size_t i;
+  if (array->metadata) {
+    for (i = 0; i < array->count; i++) {
+      grpc_slice_unref(array->metadata[i].key);
+      grpc_slice_unref(array->metadata[i].value);
+    }
+  }
+  grpc_metadata_array_destroy(array);
+}
+
 /* Wraps a grpc_call struct in a PHP object. Owned indicates whether the
    struct should be destroyed at the end of the object's lifecycle */
 zval *grpc_php_wrap_call(grpc_call *wrapped, bool owned TSRMLS_DC) {
@@ -502,8 +514,8 @@
   }
 
 cleanup:
-  grpc_metadata_array_destroy(&metadata);
-  grpc_metadata_array_destroy(&trailing_metadata);
+  grpc_php_metadata_array_destroy_including_entries(&metadata);
+  grpc_php_metadata_array_destroy_including_entries(&trailing_metadata);
   grpc_metadata_array_destroy(&recv_metadata);
   grpc_metadata_array_destroy(&recv_trailing_metadata);
   grpc_slice_unref(recv_status_details);
diff --git a/src/php/ext/grpc/call.h b/src/php/ext/grpc/call.h
index 5bde5d5..104ac30 100644
--- a/src/php/ext/grpc/call.h
+++ b/src/php/ext/grpc/call.h
@@ -69,5 +69,6 @@
 /* Populates a grpc_metadata_array with the data in a PHP array object.
    Returns true on success and false on failure */
 bool create_metadata_array(zval *array, grpc_metadata_array *metadata);
-
+void grpc_php_metadata_array_destroy_including_entries(
+    grpc_metadata_array* array);
 #endif /* NET_GRPC_PHP_GRPC_CHANNEL_H_ */
diff --git a/src/php/ext/grpc/channel.c b/src/php/ext/grpc/channel.c
index db59869..4054723 100644
--- a/src/php/ext/grpc/channel.c
+++ b/src/php/ext/grpc/channel.c
@@ -41,6 +41,7 @@
 
 #include <grpc/grpc.h>
 #include <grpc/grpc_security.h>
+#include <grpc/support/alloc.h>
 
 #include "completion_queue.h"
 #include "channel_credentials.h"
@@ -56,22 +57,63 @@
 
 /* Frees and destroys an instance of wrapped_grpc_channel */
 PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_channel)
+  bool is_last_wrapper = false;
+  // In_persistent_list is used when the user don't close the channel.
+  // In this case, le in the list won't be freed.
+  bool in_persistent_list = true;
   if (p->wrapper != NULL) {
     gpr_mu_lock(&p->wrapper->mu);
     if (p->wrapper->wrapped != NULL) {
-      php_grpc_zend_resource *rsrc;
-      php_grpc_int key_len = strlen(p->wrapper->key);
-      // only destroy the channel here if not found in the persistent list
-      gpr_mu_lock(&global_persistent_list_mu);
-      if (!(PHP_GRPC_PERSISTENT_LIST_FIND(&EG(persistent_list), p->wrapper->key,
-                                          key_len, rsrc))) {
-        grpc_channel_destroy(p->wrapper->wrapped);
-        free(p->wrapper->target);
-        free(p->wrapper->args_hashstr);
+      if (p->wrapper->is_valid) {
+        php_grpc_zend_resource *rsrc;
+        php_grpc_int key_len = strlen(p->wrapper->key);
+        // only destroy the channel here if not found in the persistent list
+        gpr_mu_lock(&global_persistent_list_mu);
+        if (!(PHP_GRPC_PERSISTENT_LIST_FIND(&EG(persistent_list), p->wrapper->key,
+                                            key_len, rsrc))) {
+          in_persistent_list = false;
+          grpc_channel_destroy(p->wrapper->wrapped);
+          free(p->wrapper->target);
+          free(p->wrapper->args_hashstr);
+          if(p->wrapper->creds_hashstr != NULL){
+            free(p->wrapper->creds_hashstr);
+            p->wrapper->creds_hashstr = NULL;
+          }
+        }
+        gpr_mu_unlock(&global_persistent_list_mu);
       }
-      gpr_mu_unlock(&global_persistent_list_mu);
+    }
+    p->wrapper->ref_count -= 1;
+    if (p->wrapper->ref_count == 0) {
+      is_last_wrapper = true;
     }
     gpr_mu_unlock(&p->wrapper->mu);
+    if (is_last_wrapper) {
+      if (in_persistent_list) {
+        // If ref_count==0 and the key still in the list, it means the user
+        // don't call channel->close().persistent list should free the
+        // allocation in such case, as well as related wrapped channel.
+        if (p->wrapper->wrapped != NULL) {
+          gpr_mu_lock(&p->wrapper->mu);
+          grpc_channel_destroy(p->wrapper->wrapped);
+          free(p->wrapper->target);
+          free(p->wrapper->args_hashstr);
+          if(p->wrapper->creds_hashstr != NULL){
+            free(p->wrapper->creds_hashstr);
+            p->wrapper->creds_hashstr = NULL;
+          }
+          p->wrapper->wrapped = NULL;
+          php_grpc_delete_persistent_list_entry(p->wrapper->key,
+                                                strlen(p->wrapper->key)
+                                                TSRMLS_CC);
+          gpr_mu_unlock(&p->wrapper->mu);
+        }
+      }
+      gpr_mu_destroy(&p->wrapper->mu);
+      free(p->wrapper->key);
+      free(p->wrapper);
+    }
+    p->wrapper = NULL;
   }
 PHP_GRPC_FREE_WRAPPED_FUNC_END()
 
@@ -276,9 +318,16 @@
   channel->wrapper->key = key;
   channel->wrapper->target = strdup(target);
   channel->wrapper->args_hashstr = strdup(sha1str);
+  channel->wrapper->creds_hashstr = NULL;
+  channel->wrapper->ref_count = 1;
+  channel->wrapper->is_valid = true;
   if (creds != NULL && creds->hashstr != NULL) {
-    channel->wrapper->creds_hashstr = creds->hashstr;
+    php_grpc_int creds_hashstr_len = strlen(creds->hashstr);
+    char *channel_creds_hashstr = malloc(creds_hashstr_len + 1);
+    strcpy(channel_creds_hashstr, creds->hashstr);
+    channel->wrapper->creds_hashstr = channel_creds_hashstr;
   }
+
   gpr_mu_init(&channel->wrapper->mu);
   smart_str_free(&buf);
 
@@ -303,7 +352,17 @@
           channel, target, args, creds, key, key_len TSRMLS_CC);
     } else {
       efree(args.args);
+      if (channel->wrapper->creds_hashstr != NULL){
+        free(channel->wrapper->creds_hashstr);
+        channel->wrapper->creds_hashstr = NULL;
+      }
+      free(channel->wrapper->creds_hashstr);
+      free(channel->wrapper->key);
+      free(channel->wrapper->target);
+      free(channel->wrapper->args_hashstr);
+      free(channel->wrapper);
       channel->wrapper = le->channel;
+      channel->wrapper->ref_count += 1;
     }
   }
 }
@@ -323,7 +382,8 @@
   }
   char *target = grpc_channel_get_target(channel->wrapper->wrapped);
   gpr_mu_unlock(&channel->wrapper->mu);
-  PHP_GRPC_RETURN_STRING(target, 1);
+  PHP_GRPC_RETVAL_STRING(target, 1);
+  gpr_free(target);
 }
 
 /**
@@ -411,18 +471,46 @@
  */
 PHP_METHOD(Channel, close) {
   wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
-  gpr_mu_lock(&channel->wrapper->mu);
-  if (channel->wrapper->wrapped != NULL) {
-    grpc_channel_destroy(channel->wrapper->wrapped);
-    free(channel->wrapper->target);
-    free(channel->wrapper->args_hashstr);
-    channel->wrapper->wrapped = NULL;
+  bool is_last_wrapper = false;
+  if (channel->wrapper != NULL) {
+    // Channel_wrapper hasn't call close before.
+    gpr_mu_lock(&channel->wrapper->mu);
+    if (channel->wrapper->wrapped != NULL) {
+      if (channel->wrapper->is_valid) {
+        // Wrapped channel hasn't been destoryed by other wrapper.
+        grpc_channel_destroy(channel->wrapper->wrapped);
+        free(channel->wrapper->target);
+        free(channel->wrapper->args_hashstr);
+        if(channel->wrapper->creds_hashstr != NULL){
+          free(channel->wrapper->creds_hashstr);
+          channel->wrapper->creds_hashstr = NULL;
+        }
+        channel->wrapper->wrapped = NULL;
+        channel->wrapper->is_valid = false;
 
-    php_grpc_delete_persistent_list_entry(channel->wrapper->key,
-                                          strlen(channel->wrapper->key)
-                                          TSRMLS_CC);
+        php_grpc_delete_persistent_list_entry(channel->wrapper->key,
+                                              strlen(channel->wrapper->key)
+                                              TSRMLS_CC);
+      }
+    }
+    channel->wrapper->ref_count -= 1;
+    if(channel->wrapper->ref_count == 0){
+      // Mark that the wrapper can be freed because mu should be
+      // destroyed outside the lock.
+      is_last_wrapper = true;
+    }
+    gpr_mu_unlock(&channel->wrapper->mu);
   }
-  gpr_mu_unlock(&channel->wrapper->mu);
+  gpr_mu_lock(&global_persistent_list_mu);
+  if (is_last_wrapper) {
+    gpr_mu_destroy(&channel->wrapper->mu);
+    free(channel->wrapper->key);
+    free(channel->wrapper);
+  }
+  // Set channel->wrapper to NULL to avoid call close twice for the same
+  // channel.
+  channel->wrapper = NULL;
+  gpr_mu_unlock(&global_persistent_list_mu);
 }
 
 // Delete an entry from the persistent list
@@ -437,6 +525,7 @@
     le = (channel_persistent_le_t *)rsrc->ptr;
     le->channel = NULL;
     php_grpc_zend_hash_del(&EG(persistent_list), key, key_len+1);
+    free(le);
   }
   gpr_mu_unlock(&global_persistent_list_mu);
 }
diff --git a/src/php/ext/grpc/channel.h b/src/php/ext/grpc/channel.h
index 69adc47..86bfdea 100755
--- a/src/php/ext/grpc/channel.h
+++ b/src/php/ext/grpc/channel.h
@@ -40,6 +40,11 @@
   char *args_hashstr;
   char *creds_hashstr;
   gpr_mu mu;
+  // is_valid is used to check the wrapped channel has been freed
+  // before to avoid double free.
+  bool is_valid;
+  // ref_count is used to let the last wrapper free related channel and key.
+  size_t ref_count;
 } grpc_channel_wrapper;
 
 /* Wrapper struct for grpc_channel that can be associated with a PHP object */
diff --git a/src/php/ext/grpc/channel_credentials.c b/src/php/ext/grpc/channel_credentials.c
index d120d6e..ed74b99 100644
--- a/src/php/ext/grpc/channel_credentials.c
+++ b/src/php/ext/grpc/channel_credentials.c
@@ -59,6 +59,7 @@
 PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_channel_credentials)
   if (p->wrapped != NULL) {
     grpc_channel_credentials_release(p->wrapped);
+    p->wrapped = NULL;
   }
 PHP_GRPC_FREE_WRAPPED_FUNC_END()
 
@@ -199,8 +200,13 @@
   grpc_channel_credentials *creds =
       grpc_composite_channel_credentials_create(cred1->wrapped, cred2->wrapped,
                                                 NULL);
+  // wrapped_grpc_channel_credentials object should keeps it's own
+  // allocation. Otherwise it conflicts free hashstr with call.c.
+  php_grpc_int cred1_len = strlen(cred1->hashstr);
+  char *cred1_hashstr = malloc(cred1_len+1);
+  strcpy(cred1_hashstr, cred1->hashstr);
   zval *creds_object =
-      grpc_php_wrap_channel_credentials(creds, cred1->hashstr, true
+      grpc_php_wrap_channel_credentials(creds, cred1_hashstr, true
                                         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 96091f9..2f4a536 100644
--- a/src/php/ext/grpc/php7_wrapper.h
+++ b/src/php/ext/grpc/php7_wrapper.h
@@ -33,6 +33,7 @@
 #define php_grpc_add_next_index_stringl(data, str, len, b) \
   add_next_index_stringl(data, str, len, b)
 
+#define PHP_GRPC_RETVAL_STRING(val, dup) RETVAL_STRING(val, dup)
 #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)
@@ -145,6 +146,7 @@
 #define php_grpc_add_next_index_stringl(data, str, len, b) \
   add_next_index_stringl(data, str, len)
 
+#define PHP_GRPC_RETVAL_STRING(val, dup) RETVAL_STRING(val)
 #define PHP_GRPC_RETURN_STRING(val, dup) RETURN_STRING(val)
 #define PHP_GRPC_MAKE_STD_ZVAL(pzv) \
   pzv = (zval *)emalloc(sizeof(zval));
diff --git a/src/php/ext/grpc/php_grpc.c b/src/php/ext/grpc/php_grpc.c
index 0f2c5b8..5971bab 100644
--- a/src/php/ext/grpc/php_grpc.c
+++ b/src/php/ext/grpc/php_grpc.c
@@ -253,7 +253,8 @@
  */
 PHP_MINFO_FUNCTION(grpc) {
   php_info_print_table_start();
-  php_info_print_table_header(2, "grpc support", "enabled");
+  php_info_print_table_row(2, "grpc support", "enabled");
+  php_info_print_table_row(2, "grpc module version", PHP_GRPC_VERSION);
   php_info_print_table_end();
 
   /* Remove comments if you have entries in php.ini
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
index 6377008..1f2bd3e 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
@@ -579,7 +579,7 @@
 typedef void*(*gpr_realloc_type)(void* p, size_t size);
 extern gpr_realloc_type gpr_realloc_import;
 #define gpr_realloc gpr_realloc_import
-typedef void*(*gpr_malloc_aligned_type)(size_t size, size_t alignment_log);
+typedef void*(*gpr_malloc_aligned_type)(size_t size, size_t alignment);
 extern gpr_malloc_aligned_type gpr_malloc_aligned_import;
 #define gpr_malloc_aligned gpr_malloc_aligned_import
 typedef void(*gpr_free_aligned_type)(void* ptr);
diff --git a/summerofcode/ideas.md b/summerofcode/ideas.md
index 49991f0..d89bc37 100644
--- a/summerofcode/ideas.md
+++ b/summerofcode/ideas.md
@@ -11,37 +11,26 @@
 
 **Required skills for all projects:** git version control, collaborative
 software development on github.com, and software development in at least one
-of gRPC's ten languages on at least one of Linux, Mac OS X, and Windows.
+of gRPC's ten languages on at least one of Linux, macOS, and Windows.
 
 -------------------------------------
 
-gRPC C Core:
+gRPC Core:
 
-1. Port gRPC to  one of the major BSD platforms ([FreeBSD](https://freebsd.org), [NetBSD](https://netbsd.org), and [OpenBSD](https://openbsd.org)) and create packages for them. Add [kqueue](https://www.freebsd.org/cgi/man.cgi?query=kqueue) support in the process.
- * **Required skills:** C programming language, BSD operating system.
- * **Likely mentors:** [Nicolas Noble](https://github.com/nicolasnoble),
- [Vijay Pai](https://github.com/vjpai).
+1. Implement ["early OK" semantics](https://github.com/grpc/grpc/issues/7032). The gRPC wire protocol allows servers to complete an RPC with OK status without having processed all requests ever sent to the client; it's the gRPC Core that currently restricts applications from so behaving. This behavioral gap in the gRPC Core should be filled in.
+    * **Required skills:** C programming language, C++ programming language.
+    * **Likely mentors:** [Nathaniel Manista](https://github.com/nathanielmanistaatgoogle), [Nicolas Noble](https://github.com/nicolasnoble).
 
+1. [Make channel-connectivity-watching cancellable](https://github.com/grpc/grpc/issues/3064). Anything worth waiting for is worth cancelling. The fact that channel connectivity is currently poll-based means that clean shutdown of gRPC channels can take as long as the poll interval. No one should have to wait two hundred milliseconds to garbage-collect an object.
+    * **Required skills:** C programming language, C++ programming language, Python programming language.
+    * **Likely mentors:** [Nathaniel Manista](https://github.com/nathanielmanistaatgoogle), [Vijay Pai](https://github.com/vjpai).
 
 gRPC Python:
 
-1. Port gRPC Python to [PyPy](http://pypy.org). Investigate the state of [Cython support](http://docs.cython.org/src/userguide/pypy.html) to do this or potentially explore [cffi](https://cffi.readthedocs.org/en/latest/).
- * **Required skills:** Python programming language, PyPy Python interpreter.
- * **Likely mentors:** [Nathaniel Manista](https://github.com/nathanielmanistaatgoogle), [Masood Malekghassemi](https://github.com/soltanmm).
-1. Develop and test Python 3.5 Support for gRPC. Make necessary changes to port gRPC and package it for supported platforms.
- * **Required skills:** Python programming language, Python 3.5 interpreter.
- * **Likely mentors:** [Nathaniel Manista](https://github.com/nathanielmanistaatgoogle), [Masood Malekghassemi](https://github.com/soltanmm).
+1. Support static type-checking of both gRPC Python itself and of code that uses gRPC Python. No one likes dynamic typing and Python is finally outgrowing it! There are probably errors in the implementation of gRPC Python that [pytype](https://github.com/google/pytype) or [mypy](http://mypy-lang.org/) could detect. There are certainly errors in other code that uses gRPC Python that they could detect.
+    * **Required skills:** Python programming language, open source development across multiple repositories and projects.
+    * **Likely mentors:** [Nathaniel Manista](https://github.com/nathanielmanistaatgoogle), [Kailash Sethuraman](https://github.com/hsaliak), [Ken Payson](https://github.com/kpayson64), [Mehrdad Afshari](https://github.com/mehrdada).
 
-gRPC Ruby/Java:
-
-1. [jRuby](http://jruby.org) support for gRPC. Develop a jRuby wrapper for gRPC based on grpc-java and ensure that it is API compatible with the existing Ruby implementation and passes all tests.
- * **Required skills:** Java programming language, Ruby programming language.
- * **Likely mentors:** [Michael Lumish](https://github.com/murgatroid99), [Eric Anderson](https://github.com/ejona86).
-
-
-gRPC Wire Protocol:
-
-1. Develop a [Wireshark](https://wireshark.org) plugin for the gRPC protocol. Provide documentation and tutorials for this plugin.
- * **Bonus:** consider set-up and use with mobile clients.
- * **Required skills:** Wireshark software.
- * **Likely mentors:** [Nicolas Noble](https://github.com/nicolasnoble).
+1. [Enable building of gRPC Python with Bazel](https://github.com/grpc/grpc/issues/8079). Bazel is the designated replacement for our constellation of crufty build scripts, but it's still under active development itself. Up for a challenge? gRPC Python could easily be the most complex codebase to be built with Bazel.
+    * **Required skills:** Python programming language, Bazel toolchain, Cython, open source development across multiple repositories and projects.
+    * **Likely mentors:** [Nathaniel Manista](https://github.com/nathanielmanistaatgoogle), [Ken Payson](https://github.com/kpayson64), [Mehrdad Afshari](https://github.com/mehrdada).
diff --git a/templates/gRPC-C++.podspec.template b/templates/gRPC-C++.podspec.template
new file mode 100644
index 0000000..78adb27
--- /dev/null
+++ b/templates/gRPC-C++.podspec.template
@@ -0,0 +1,193 @@
+%YAML 1.2
+--- |
+  # This file has been automatically generated from a template file.
+  # Please make modifications to `templates/gRPC-C++.podspec.template`
+  # instead. This file can be regenerated from the template by running
+  # `tools/buildgen/generate_projects.sh`.
+
+  # gRPC C++ CocoaPods podspec
+  #
+  # Copyright 2017 gRPC authors.
+  #
+  # Licensed under the Apache License, Version 2.0 (the "License");
+  # you may not use this file except in compliance with the License.
+  # You may obtain a copy of the License at
+  #
+  #     http://www.apache.org/licenses/LICENSE-2.0
+  #
+  # Unless required by applicable law or agreed to in writing, software
+  # distributed under the License is distributed on an "AS IS" BASIS,
+  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  # See the License for the specific language governing permissions and
+  # limitations under the License.
+
+  <%!
+  def grpc_lib_files(libs, expect_libs, groups):
+    out = []
+    for lib in libs:
+      if lib.name in expect_libs:
+        for group in groups:
+          out += lib.get(group, [])
+    return out
+
+  def grpc_private_files(libs):
+    out = grpc_lib_files(libs, ("grpc", "gpr"), ("headers", "src"))
+    return out
+
+  def grpc_private_headers(libs):
+    out = grpc_lib_files(libs, ("grpc", "gpr"), ("headers",))
+    return out
+
+  def grpc_public_headers(libs):
+    out = grpc_lib_files(libs, ("grpc", "gpr"), ("public_headers",))
+    return out
+
+  def grpcpp_proto_files(filegroups):
+    out = grpc_lib_files(filegroups, ("grpc++_codegen_proto", "grpc++_config_proto"), ("headers", "src", "public_headers"))
+    excl_files = grpc_lib_files(filegroups, ("grpc++_codegen_base",), ("headers", "src", "public_headers"))
+    out = [file for file in out if file not in excl_files]
+    return out
+
+  def grpcpp_private_files(libs, filegroups):
+    out = grpc_lib_files(libs, ("grpc++",), ("headers", "src"))
+    excl_files = grpc_private_files(libs)
+
+    # We exclude proto related files in this particular podspec so that we can provide a protobuf-independent lib
+    excl_files += grpcpp_proto_files(filegroups)
+    out = [file for file in out if file not in excl_files]
+
+    # Since some C++ source files directly included private headers in C core, we include all the
+    # C core headers in C++ Implementation subspec as well.
+    out += [file for file in grpc_private_headers(libs) if not file.startswith("third_party/nanopb/")]
+    return out
+
+  def grpcpp_private_headers(libs, filegroups):
+    out = grpc_lib_files(libs, ("grpc++",), ("headers",))
+
+    # We exclude proto related files in this particular podspec so that we can provide a protobuf-independent lib
+    excl_files = grpcpp_proto_files(filegroups)
+    out = [file for file in out if file not in excl_files]
+
+    # Since some C++ source files directly included private headers in C core, we intentionally
+    # keep the C core headers in \a out. But we should exclude nanopb headers.
+    out = [file for file in out if not file.startswith("third_party/nanopb/")]
+    return out
+
+  def grpcpp_public_headers(libs, filegroups):
+    out = grpc_lib_files(libs, ("grpc++",), ("public_headers",))
+    excl_files = grpc_public_headers(libs)
+
+    # We exclude proto related files in this particular podspec so that we can provide a protobuf-independent lib
+    excl_files += grpcpp_proto_files(filegroups)
+
+    out = [file for file in out if file not in excl_files]
+    return out
+
+  def grpc_test_util_files(libs):
+    out = grpc_lib_files(libs, ("grpc_test_util", "gpr_test_util"), ("src", "headers"))
+    return out
+
+  def grpc_test_util_headers(libs):
+    out = grpc_lib_files(libs, ("grpc_test_util", "gpr_test_util"), ("headers",))
+    return out
+
+  def grpcpp_test_util_files(libs, filegroups):
+    out = grpc_lib_files(libs, ("grpc++_test_util",), ("src", "headers"))
+    excl_files = grpc_test_util_files(libs) + grpcpp_private_files(libs, filegroups)
+
+    # We exclude proto related files in this particular podspec so that we can provide a protobuf-independent lib
+    excl_files += grpc_lib_files(filegroups, ("grpc++_codegen_proto", "grpc++_config_proto"), ("headers", "src"))
+    excl_files += ["test/cpp/util/byte_buffer_proto_helper.cc",
+                   "test/cpp/util/byte_buffer_proto_helper.h",
+                   "test/cpp/end2end/test_service_impl.cc",
+                   "test/cpp/end2end/test_service_impl.h"]
+    excl_files += [file for file in out if file.endswith(".proto")]
+
+    out = [file for file in out if not file in excl_files]
+
+    # Since some C++ test files directly included private headers in C core, we intentionally add these header
+    # files to this subspec
+    out += grpc_test_util_headers(libs)
+
+    return out
+
+  def ruby_multiline_list(files, indent):
+    return (',\n' + indent*' ').join('\'%s\'' % f for f in files)
+  %>
+  Pod::Spec.new do |s|
+    s.name     = 'gRPC-C++'
+    # TODO (mxyan): use version that match gRPC version when pod is stabilized
+    # version = '${settings.version}'
+    version = '0.0.1'
+    s.version  = version
+    s.summary  = 'gRPC C++ library'
+    s.homepage = 'https://grpc.io'
+    s.license  = 'Apache License, Version 2.0'
+    s.authors  = { 'The gRPC contributors' => 'grpc-packages@google.com' }
+
+    grpc_version = '${settings.version}'
+
+    s.source = {
+      :git => 'https://github.com/grpc/grpc.git',
+      :tag => "v#{grpc_version}",
+    }
+
+    s.ios.deployment_target = '7.0'
+    s.osx.deployment_target = '10.9'
+    s.requires_arc = false
+
+    # Add include prefix `grpc++` (i.e. `#include <grpc++/xxx.h>`).
+    s.header_dir = 'grpc++'
+
+    s.pod_target_xcconfig = {
+      'HEADER_SEARCH_PATHS' => '"$(inherited)" "$(PODS_TARGET_SRCROOT)/include"',
+      'USER_HEADER_SEARCH_PATHS' => '"$(PODS_TARGET_SRCROOT)"',
+      'GCC_PREPROCESSOR_DEFINITIONS' => '"$(inherited)" "COCOAPODS=1" "PB_NO_PACKED_STRUCTS=1"',
+      'CLANG_WARN_STRICT_PROTOTYPES' => 'NO',
+      'CLANG_WARN_DOCUMENTATION_COMMENTS' => 'NO',
+
+      # If we don't set these two settings, `include/grpc/support/time.h` and
+      # `src/core/lib/support/string.h` shadow the system `<time.h>` and `<string.h>`, breaking the
+      # build.
+      'USE_HEADERMAP' => 'NO',
+      'ALWAYS_SEARCH_USER_PATHS' => 'NO',
+    }
+
+    s.libraries = 'c++'
+
+    s.default_subspecs = 'Interface', 'Implementation'
+
+    s.subspec 'Interface' do |ss|
+      ss.header_mappings_dir = 'include/grpc++'
+
+      ss.source_files = ${ruby_multiline_list(grpcpp_public_headers(libs, filegroups), 22)}
+    end
+
+    s.subspec 'Implementation' do |ss|
+      ss.header_mappings_dir = '.'
+      ss.dependency "#{s.name}/Interface", version
+      ss.dependency 'gRPC-Core', grpc_version
+      ss.dependency 'nanopb', '~> 0.3'
+
+      ss.source_files = ${ruby_multiline_list(grpcpp_private_files(libs, filegroups), 22)}
+
+      ss.private_header_files = ${ruby_multiline_list(grpcpp_private_headers(libs, filegroups), 30)}
+    end
+
+    s.subspec 'Tests' do |ss|
+      ss.header_mappings_dir = '.'
+
+      ss.dependency "#{s.name}/Interface", version
+      ss.dependency "#{s.name}/Implementation", version
+      ss.dependency "gRPC-Core/Tests", grpc_version
+
+      ss.source_files = ${ruby_multiline_list(grpcpp_test_util_files(libs, filegroups), 22)}
+    end
+
+    s.prepare_command = <<-END_OF_COMMAND
+      find src/cpp/ -type f -exec sed -E -i'.back' 's;#include "third_party/nanopb/(.*)";#include <nanopb/\\1>;g' {} \\\;
+      find src/cpp/ -name "*.back" -type f -delete
+      find src/core/ -regex ".*\.h" -type f -exec sed -E -i'.back' 's;#include "third_party/nanopb/(.*)";#include <nanopb/\\1>;g' {} \\\;
+      find src/core/ -name "*.back" -type f -delete
+    END_OF_COMMAND
+  end
diff --git a/test/core/end2end/goaway_server_test.cc b/test/core/end2end/goaway_server_test.cc
index 94cfbdd..f23d87e 100644
--- a/test/core/end2end/goaway_server_test.cc
+++ b/test/core/end2end/goaway_server_test.cc
@@ -187,7 +187,7 @@
   op = ops;
   op->op = GRPC_OP_SEND_INITIAL_METADATA;
   op->data.send_initial_metadata.count = 0;
-  op->flags = 0;
+  op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY;
   op->reserved = nullptr;
   op++;
   GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(call1, ops,
@@ -263,7 +263,7 @@
   op = ops;
   op->op = GRPC_OP_SEND_INITIAL_METADATA;
   op->data.send_initial_metadata.count = 0;
-  op->flags = 0;
+  op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY;
   op->reserved = nullptr;
   op++;
   GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(call2, ops,
diff --git a/test/core/gpr/alloc_test.cc b/test/core/gpr/alloc_test.cc
index 6074c6e..bf4471c 100644
--- a/test/core/gpr/alloc_test.cc
+++ b/test/core/gpr/alloc_test.cc
@@ -16,8 +16,11 @@
  *
  */
 
+#include <string.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+
 #include "test/core/util/test_config.h"
 
 static void* fake_malloc(size_t size) { return (void*)size; }
@@ -48,8 +51,19 @@
   gpr_free(i);
 }
 
+static void test_malloc_aligned() {
+  for (size_t size = 1; size <= 256; ++size) {
+    void* ptr = gpr_malloc_aligned(size, 16);
+    GPR_ASSERT(ptr != nullptr);
+    GPR_ASSERT(((intptr_t)ptr & 0xf) == 0);
+    memset(ptr, 0, size);
+    gpr_free_aligned(ptr);
+  }
+}
+
 int main(int argc, char** argv) {
   grpc_test_init(argc, argv);
   test_custom_allocs();
+  test_malloc_aligned();
   return 0;
 }
diff --git a/test/core/gpr/arena_test.cc b/test/core/gpr/arena_test.cc
index 59ea04c..62a3f8b 100644
--- a/test/core/gpr/arena_test.cc
+++ b/test/core/gpr/arena_test.cc
@@ -53,6 +53,8 @@
   void** ps = static_cast<void**>(gpr_zalloc(sizeof(*ps) * nallocs));
   for (size_t i = 0; i < nallocs; i++) {
     ps[i] = gpr_arena_alloc(a, allocs[i]);
+    // ensure the returned address is aligned
+    GPR_ASSERT(((intptr_t)ps[i] & 0xf) == 0);
     // ensure no duplicate results
     for (size_t j = 0; j < i; j++) {
       GPR_ASSERT(ps[i] != ps[j]);
diff --git a/test/cpp/cocoapods/GRPCCppTests.xcodeproj/project.pbxproj b/test/cpp/cocoapods/GRPCCppTests.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..1d332ba
--- /dev/null
+++ b/test/cpp/cocoapods/GRPCCppTests.xcodeproj/project.pbxproj
@@ -0,0 +1,533 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 48;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		2D3F2189E2CDF493639A17C5 /* libPods-test.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 99B1FD20127AB0F23D6AB570 /* libPods-test.a */; };
+		5E63948A1FDB64B50051E9AA /* server_context_test_spouse_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5E6394891FDB64B50051E9AA /* server_context_test_spouse_test.mm */; };
+		5EFA5F731FEDB36700EBF4B7 /* generic.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5EFA5F721FEDB36700EBF4B7 /* generic.mm */; };
+		D0F8FABF3ECF587C207C12F3 /* libPods-generic.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D0872BAEE90C8A149743DB7 /* libPods-generic.a */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		3D0872BAEE90C8A149743DB7 /* libPods-generic.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-generic.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		5E63947F1FDB5EA10051E9AA /* test.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = test.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		5E6394831FDB5EA10051E9AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		5E6394891FDB64B50051E9AA /* server_context_test_spouse_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = server_context_test_spouse_test.mm; sourceTree = "<group>"; };
+		5EFA5F701FEDB36700EBF4B7 /* generic.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = generic.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		5EFA5F721FEDB36700EBF4B7 /* generic.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = generic.mm; sourceTree = "<group>"; };
+		5EFA5F741FEDB36700EBF4B7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		8682448F311EDE94C14D551D /* Pods-test.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-test.release.xcconfig"; path = "Pods/Target Support Files/Pods-test/Pods-test.release.xcconfig"; sourceTree = "<group>"; };
+		91EDF22ADDE71926C7BC1AF1 /* Pods-test.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-test.debug.xcconfig"; path = "Pods/Target Support Files/Pods-test/Pods-test.debug.xcconfig"; sourceTree = "<group>"; };
+		99B1FD20127AB0F23D6AB570 /* libPods-test.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-test.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		B357AF2DC912B224C026D114 /* Pods-generic.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-generic.release.xcconfig"; path = "Pods/Target Support Files/Pods-generic/Pods-generic.release.xcconfig"; sourceTree = "<group>"; };
+		D9B4F77163CB9089C4436BF4 /* Pods-generic.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-generic.debug.xcconfig"; path = "Pods/Target Support Files/Pods-generic/Pods-generic.debug.xcconfig"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		5E63947C1FDB5EA10051E9AA /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				2D3F2189E2CDF493639A17C5 /* libPods-test.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		5EFA5F6D1FEDB36700EBF4B7 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D0F8FABF3ECF587C207C12F3 /* libPods-generic.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		4439BD4D56FD5738BF780F8E /* Pods */ = {
+			isa = PBXGroup;
+			children = (
+				91EDF22ADDE71926C7BC1AF1 /* Pods-test.debug.xcconfig */,
+				8682448F311EDE94C14D551D /* Pods-test.release.xcconfig */,
+				D9B4F77163CB9089C4436BF4 /* Pods-generic.debug.xcconfig */,
+				B357AF2DC912B224C026D114 /* Pods-generic.release.xcconfig */,
+			);
+			name = Pods;
+			sourceTree = "<group>";
+		};
+		5E63944B1FDB5D9B0051E9AA = {
+			isa = PBXGroup;
+			children = (
+				5E63947F1FDB5EA10051E9AA /* test.xctest */,
+				5E6394801FDB5EA10051E9AA /* test */,
+				4439BD4D56FD5738BF780F8E /* Pods */,
+				5EFA5F711FEDB36700EBF4B7 /* generic */,
+				C2E6603B58413BD724AFB400 /* Frameworks */,
+				5EFA5F701FEDB36700EBF4B7 /* generic.xctest */,
+			);
+			sourceTree = "<group>";
+		};
+		5E6394801FDB5EA10051E9AA /* test */ = {
+			isa = PBXGroup;
+			children = (
+				5E6394891FDB64B50051E9AA /* server_context_test_spouse_test.mm */,
+				5E6394831FDB5EA10051E9AA /* Info.plist */,
+			);
+			path = test;
+			sourceTree = "<group>";
+		};
+		5EFA5F711FEDB36700EBF4B7 /* generic */ = {
+			isa = PBXGroup;
+			children = (
+				5EFA5F721FEDB36700EBF4B7 /* generic.mm */,
+				5EFA5F741FEDB36700EBF4B7 /* Info.plist */,
+			);
+			path = generic;
+			sourceTree = "<group>";
+		};
+		C2E6603B58413BD724AFB400 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				99B1FD20127AB0F23D6AB570 /* libPods-test.a */,
+				3D0872BAEE90C8A149743DB7 /* libPods-generic.a */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		5E63947E1FDB5EA10051E9AA /* test */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 5E6394841FDB5EA10051E9AA /* Build configuration list for PBXNativeTarget "test" */;
+			buildPhases = (
+				6D8317CF72F9E252442662F8 /* [CP] Check Pods Manifest.lock */,
+				5E63947B1FDB5EA10051E9AA /* Sources */,
+				5E63947C1FDB5EA10051E9AA /* Frameworks */,
+				5E63947D1FDB5EA10051E9AA /* Resources */,
+				B972D278DA2A2BF12386177C /* [CP] Embed Pods Frameworks */,
+				3C2FE7A8DBA8BBCB2923B173 /* [CP] Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = test;
+			productName = test;
+			productReference = 5E63947F1FDB5EA10051E9AA /* test.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+		5EFA5F6F1FEDB36700EBF4B7 /* generic */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 5EFA5F751FEDB36700EBF4B7 /* Build configuration list for PBXNativeTarget "generic" */;
+			buildPhases = (
+				4844599DC62113265AB660B3 /* [CP] Check Pods Manifest.lock */,
+				5EFA5F6C1FEDB36700EBF4B7 /* Sources */,
+				5EFA5F6D1FEDB36700EBF4B7 /* Frameworks */,
+				5EFA5F6E1FEDB36700EBF4B7 /* Resources */,
+				11E4716E0919C734CC6AA8C2 /* [CP] Embed Pods Frameworks */,
+				9E149E84C3AA06289FE1F244 /* [CP] Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = generic;
+			productName = generic;
+			productReference = 5EFA5F701FEDB36700EBF4B7 /* generic.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		5E63944C1FDB5D9B0051E9AA /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0910;
+				ORGANIZATIONNAME = gRPC;
+				TargetAttributes = {
+					5E63947E1FDB5EA10051E9AA = {
+						CreatedOnToolsVersion = 9.1;
+						ProvisioningStyle = Automatic;
+					};
+					5EFA5F6F1FEDB36700EBF4B7 = {
+						CreatedOnToolsVersion = 9.2;
+						ProvisioningStyle = Automatic;
+					};
+				};
+			};
+			buildConfigurationList = 5E63944F1FDB5D9B0051E9AA /* Build configuration list for PBXProject "GRPCCppTests" */;
+			compatibilityVersion = "Xcode 8.0";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 5E63944B1FDB5D9B0051E9AA;
+			productRefGroup = 5E63944B1FDB5D9B0051E9AA;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				5E63947E1FDB5EA10051E9AA /* test */,
+				5EFA5F6F1FEDB36700EBF4B7 /* generic */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		5E63947D1FDB5EA10051E9AA /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		5EFA5F6E1FEDB36700EBF4B7 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		11E4716E0919C734CC6AA8C2 /* [CP] Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "[CP] Embed Pods Frameworks";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-generic/Pods-generic-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		3C2FE7A8DBA8BBCB2923B173 /* [CP] Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "[CP] Copy Pods Resources";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-test/Pods-test-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		4844599DC62113265AB660B3 /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-generic-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+		6D8317CF72F9E252442662F8 /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-test-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+		9E149E84C3AA06289FE1F244 /* [CP] Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "[CP] Copy Pods Resources";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-generic/Pods-generic-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		B972D278DA2A2BF12386177C /* [CP] Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "[CP] Embed Pods Frameworks";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-test/Pods-test-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		5E63947B1FDB5EA10051E9AA /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5E63948A1FDB64B50051E9AA /* server_context_test_spouse_test.mm in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		5EFA5F6C1FEDB36700EBF4B7 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5EFA5F731FEDB36700EBF4B7 /* generic.mm in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		5E6394731FDB5D9B0051E9AA /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				HEADER_SEARCH_PATHS = ../../../include;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.1;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+				USER_HEADER_SEARCH_PATHS = ../../..;
+			};
+			name = Debug;
+		};
+		5E6394741FDB5D9B0051E9AA /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				HEADER_SEARCH_PATHS = ../../../include;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.1;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				USER_HEADER_SEARCH_PATHS = ../../..;
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		5E6394851FDB5EA10051E9AA /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 91EDF22ADDE71926C7BC1AF1 /* Pods-test.debug.xcconfig */;
+			buildSettings = {
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = EQHXZ8M8AV;
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					"\"${PODS_ROOT}/Headers/Public\"",
+					"\"${PODS_ROOT}/Headers/Public/BoringSSL\"",
+					"\"${PODS_ROOT}/Headers/Public/gRPC-C++\"",
+					"\"${PODS_ROOT}/Headers/Public/gRPC-Core\"",
+					"\"${PODS_ROOT}/Headers/Public/nanopb\"",
+				);
+				INFOPLIST_FILE = test/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.test;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		5E6394861FDB5EA10051E9AA /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 8682448F311EDE94C14D551D /* Pods-test.release.xcconfig */;
+			buildSettings = {
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = EQHXZ8M8AV;
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					"\"${PODS_ROOT}/Headers/Public\"",
+					"\"${PODS_ROOT}/Headers/Public/BoringSSL\"",
+					"\"${PODS_ROOT}/Headers/Public/gRPC-C++\"",
+					"\"${PODS_ROOT}/Headers/Public/gRPC-Core\"",
+					"\"${PODS_ROOT}/Headers/Public/nanopb\"",
+				);
+				INFOPLIST_FILE = test/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.test;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Release;
+		};
+		5EFA5F761FEDB36700EBF4B7 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = D9B4F77163CB9089C4436BF4 /* Pods-generic.debug.xcconfig */;
+			buildSettings = {
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = EQHXZ8M8AV;
+				INFOPLIST_FILE = generic/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.2;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.generic;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		5EFA5F771FEDB36700EBF4B7 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = B357AF2DC912B224C026D114 /* Pods-generic.release.xcconfig */;
+			buildSettings = {
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = EQHXZ8M8AV;
+				INFOPLIST_FILE = generic/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.2;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.generic;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		5E63944F1FDB5D9B0051E9AA /* Build configuration list for PBXProject "GRPCCppTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				5E6394731FDB5D9B0051E9AA /* Debug */,
+				5E6394741FDB5D9B0051E9AA /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		5E6394841FDB5EA10051E9AA /* Build configuration list for PBXNativeTarget "test" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				5E6394851FDB5EA10051E9AA /* Debug */,
+				5E6394861FDB5EA10051E9AA /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		5EFA5F751FEDB36700EBF4B7 /* Build configuration list for PBXNativeTarget "generic" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				5EFA5F761FEDB36700EBF4B7 /* Debug */,
+				5EFA5F771FEDB36700EBF4B7 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 5E63944C1FDB5D9B0051E9AA /* Project object */;
+}
diff --git a/test/cpp/cocoapods/Podfile b/test/cpp/cocoapods/Podfile
new file mode 100644
index 0000000..8d8cdaa
--- /dev/null
+++ b/test/cpp/cocoapods/Podfile
@@ -0,0 +1,71 @@
+source 'https://github.com/CocoaPods/Specs.git'
+platform :ios, '8.0'
+
+install! 'cocoapods', :deterministic_uuids => false
+
+# Location of gRPC's repo root relative to this file.
+GRPC_LOCAL_SRC = '../../..'
+
+%w(
+  test
+  generic
+).each do |target_name|
+  target target_name do
+    pod 'gRPC-Core',      :path => GRPC_LOCAL_SRC
+    pod 'gRPC-Core/Tests', :path => GRPC_LOCAL_SRC
+    pod 'gRPC-C++',       :path => GRPC_LOCAL_SRC
+    pod 'BoringSSL',      :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c", :inhibit_warnings => true
+  end
+end
+
+# gRPC-Core.podspec needs to be modified to be successfully used for local development. A Podfile's
+# pre_install hook lets us do that. The block passed to it runs after the podspecs are downloaded
+# and before they are installed in the user project.
+#
+# This podspec searches for the gRPC core library headers under "$(PODS_ROOT)/gRPC-Core", where
+# Cocoapods normally places the downloaded sources. When doing local development of the libraries,
+# though, Cocoapods just takes the sources from whatever directory was specified using `:path`, and
+# doesn't copy them under $(PODS_ROOT). When using static libraries, one can sometimes rely on the
+# symbolic links to the pods headers that Cocoapods creates under "$(PODS_ROOT)/Headers". But those
+# aren't created when using dynamic frameworks. So our solution is to modify the podspec on the fly
+# to point at the local directory where the sources are.
+#
+# TODO(jcanizales): Send a PR to Cocoapods to get rid of this need.
+pre_install do |installer|
+  # This is the gRPC-Core podspec object, as initialized by its podspec file.
+  grpc_core_spec = installer.pod_targets.find{|t| t.name == 'gRPC-Core'}.root_spec
+
+  # Copied from gRPC-Core.podspec, except for the adjusted src_root:
+  src_root = "$(PODS_ROOT)/../#{GRPC_LOCAL_SRC}"
+  grpc_core_spec.pod_target_xcconfig = {
+    'GRPC_SRC_ROOT' => src_root,
+    'HEADER_SEARCH_PATHS' => '"$(inherited)" "$(GRPC_SRC_ROOT)/include"',
+    'USER_HEADER_SEARCH_PATHS' => '"$(GRPC_SRC_ROOT)"',
+    # If we don't set these two settings, `include/grpc/support/time.h` and
+    # `src/core/lib/support/string.h` shadow the system `<time.h>` and `<string.h>`, breaking the
+    # build.
+    'USE_HEADERMAP' => 'NO',
+    'ALWAYS_SEARCH_USER_PATHS' => 'NO',
+  }
+end
+
+post_install do |installer|
+  installer.pods_project.targets.each do |target|
+    target.build_configurations.each do |config|
+      config.build_settings['GCC_TREAT_WARNINGS_AS_ERRORS'] = 'YES'
+    end
+
+    # CocoaPods creates duplicated library targets of gRPC-Core when the test targets include
+    # non-default subspecs of gRPC-Core. All of these library targets start with prefix 'gRPC-Core'
+    # and require the same error suppresion.
+    if target.name.start_with?('gRPC-Core')
+      target.build_configurations.each do |config|
+        # TODO(zyc): Remove this setting after the issue is resolved
+        # GPR_UNREACHABLE_CODE causes "Control may reach end of non-void
+        # function" warning
+        config.build_settings['GCC_WARN_ABOUT_RETURN_TYPE'] = 'NO'
+        config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) COCOAPODS=1 GRPC_CRONET_WITH_PACKET_COALESCING=1'
+      end
+    end
+  end
+end
diff --git a/test/cpp/cocoapods/generic/Info.plist b/test/cpp/cocoapods/generic/Info.plist
new file mode 100644
index 0000000..6c40a6c
--- /dev/null
+++ b/test/cpp/cocoapods/generic/Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>
diff --git a/test/cpp/cocoapods/generic/generic.mm b/test/cpp/cocoapods/generic/generic.mm
new file mode 100644
index 0000000..30b43ec
--- /dev/null
+++ b/test/cpp/cocoapods/generic/generic.mm
@@ -0,0 +1,244 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#import <XCTest/XCTest.h>
+
+#include <sstream>
+
+#include <grpc++/channel.h>
+#include <grpc++/client_context.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/generic/async_generic_service.h>
+#include <grpc++/generic/generic_stub.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include <grpc++/server_context.h>
+#include <grpc++/support/slice.h>
+#include <grpc/grpc.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+using std::chrono::system_clock;
+using namespace grpc;
+
+void* tag(int i) { return (void*)(intptr_t)i; }
+
+static grpc_slice merge_slices(grpc_slice* slices, size_t nslices) {
+  size_t i;
+  size_t len = 0;
+  uint8_t* cursor;
+  grpc_slice out;
+
+  for (i = 0; i < nslices; i++) {
+    len += GRPC_SLICE_LENGTH(slices[i]);
+  }
+
+  out = grpc_slice_malloc(len);
+  cursor = GRPC_SLICE_START_PTR(out);
+
+  for (i = 0; i < nslices; i++) {
+    memcpy(cursor, GRPC_SLICE_START_PTR(slices[i]),
+           GRPC_SLICE_LENGTH(slices[i]));
+    cursor += GRPC_SLICE_LENGTH(slices[i]);
+  }
+
+  return out;
+}
+
+int byte_buffer_eq_string(ByteBuffer* bb, const char* str) {
+  int res;
+
+  std::vector<Slice> slices;
+  bb->Dump(&slices);
+  grpc_slice* c_slices = new grpc_slice[slices.size()];
+  for (int i = 0; i < slices.size(); i++) {
+    c_slices[i] = slices[i].c_slice();
+  }
+  grpc_slice a = merge_slices(c_slices, slices.size());
+  grpc_slice b = grpc_slice_from_copied_string(str);
+  res =
+      (GRPC_SLICE_LENGTH(a) == GRPC_SLICE_LENGTH(b)) &&
+      (0 == memcmp(GRPC_SLICE_START_PTR(a), GRPC_SLICE_START_PTR(b),
+                   GRPC_SLICE_LENGTH(a)));
+  grpc_slice_unref(a);
+  grpc_slice_unref(b);
+  for (int i = 0; i < slices.size(); i++) {
+    grpc_slice_unref(c_slices[i]);
+  }
+  delete [] c_slices;
+
+  return res;
+}
+
+@interface GenericTest : XCTestCase
+
+@end
+
+@implementation GenericTest {
+  grpc::string server_host_;
+  CompletionQueue cli_cq_;
+  std::unique_ptr<ServerCompletionQueue> srv_cq_;
+  std::unique_ptr<GenericStub> generic_stub_;
+  std::unique_ptr<Server> server_;
+  AsyncGenericService generic_service_;
+  std::ostringstream server_address_;
+}
+
+- (void)verify_ok:(grpc::CompletionQueue*)cq
+                i:(int)i
+        expect_ok:(bool)expect_ok {
+  bool ok;
+  void* got_tag;
+  XCTAssertTrue(cq->Next(&got_tag, &ok));
+  XCTAssertEqual(expect_ok, ok);
+  XCTAssertEqual(tag(i), got_tag);
+}
+
+- (void)server_ok:(int)i { [self verify_ok:srv_cq_.get() i:i expect_ok:true]; }
+- (void)client_ok:(int)i { [self verify_ok:&cli_cq_ i:i expect_ok:true]; }
+- (void)server_fail:(int)i { [self verify_ok:srv_cq_.get() i:i expect_ok:false]; }
+- (void)client_fail:(int)i { [self verify_ok:&cli_cq_ i:i expect_ok:false]; }
+
+- (void)setUp {
+  [super setUp];
+
+  server_host_ = "localhost";
+  int port = grpc_pick_unused_port_or_die();
+  server_address_ << server_host_ << ":" << port;
+  // Setup server
+  ServerBuilder builder;
+  builder.AddListeningPort(server_address_.str(),
+                           InsecureServerCredentials());
+  builder.RegisterAsyncGenericService(&generic_service_);
+  // Include a second call to RegisterAsyncGenericService to make sure that
+  // we get an error in the log, since it is not allowed to have 2 async
+  // generic services
+  builder.RegisterAsyncGenericService(&generic_service_);
+  srv_cq_ = builder.AddCompletionQueue();
+  server_ = builder.BuildAndStart();
+}
+
+- (void)tearDown {
+  // Put teardown code here. This method is called after the invocation of each test method in the class.
+  server_->Shutdown();
+  void* ignored_tag;
+  bool ignored_ok;
+  cli_cq_.Shutdown();
+  srv_cq_->Shutdown();
+  while (cli_cq_.Next(&ignored_tag, &ignored_ok));
+  while (srv_cq_->Next(&ignored_tag, &ignored_ok));
+  [super tearDown];
+}
+
+- (void)ResetStub {
+  std::shared_ptr<Channel> channel =
+      CreateChannel(server_address_.str(), InsecureChannelCredentials());
+  generic_stub_.reset(new GenericStub(channel));
+}
+
+- (void)SendRpc:(int)num_rpcs {
+  [self SendRpc:num_rpcs check_deadline:false deadline:gpr_inf_future(GPR_CLOCK_MONOTONIC)];
+ }
+
+- (void)SendRpc:(int)num_rpcs
+ check_deadline:(bool)check_deadline
+       deadline:(gpr_timespec)deadline {
+  const grpc::string kMethodName("/grpc.cpp.test.util.EchoTestService/Echo");
+  for (int i = 0; i < num_rpcs; i++) {
+    Status recv_status;
+
+    ClientContext cli_ctx;
+    GenericServerContext srv_ctx;
+    GenericServerAsyncReaderWriter stream(&srv_ctx);
+
+    // The string needs to be long enough to test heap-based slice.
+    /*send_request.set_message("Hello world. Hello world. Hello world.");*/
+
+    if (check_deadline) {
+      cli_ctx.set_deadline(deadline);
+    }
+
+    std::unique_ptr<GenericClientAsyncReaderWriter> call =
+    generic_stub_->Call(&cli_ctx, kMethodName, &cli_cq_, tag(1));
+    [self client_ok:1];
+    Slice send_slice = Slice("hello world", 11);
+    std::unique_ptr<ByteBuffer> send_buffer =
+        std::unique_ptr<ByteBuffer>(new ByteBuffer(&send_slice, 1));
+    call->Write(*send_buffer, tag(2));
+    // Send ByteBuffer can be destroyed after calling Write.
+    send_buffer.reset();
+    [self client_ok:2];
+    call->WritesDone(tag(3));
+    [self client_ok:3];
+
+    generic_service_.RequestCall(&srv_ctx, &stream, srv_cq_.get(),
+                                 srv_cq_.get(), tag(4));
+
+    [self verify_ok:srv_cq_.get() i:4 expect_ok:true];
+    XCTAssertEqual(server_host_, srv_ctx.host().substr(0, server_host_.length()));
+    XCTAssertEqual(kMethodName, srv_ctx.method());
+
+    if (check_deadline) {
+      XCTAssertTrue(gpr_time_similar(deadline, srv_ctx.raw_deadline(),
+                                   gpr_time_from_millis(1000, GPR_TIMESPAN)));
+    }
+
+    ByteBuffer recv_buffer;
+    stream.Read(&recv_buffer, tag(5));
+    [self server_ok:5];
+    XCTAssertTrue(byte_buffer_eq_string(&recv_buffer, "hello world"));
+
+    send_buffer = std::unique_ptr<ByteBuffer>(new ByteBuffer(recv_buffer));
+    stream.Write(*send_buffer, tag(6));
+    send_buffer.reset();
+    [self server_ok:6];
+
+    stream.Finish(Status::OK, tag(7));
+    [self server_ok:7];
+
+    recv_buffer.Clear();
+    call->Read(&recv_buffer, tag(8));
+    [self client_ok:8];
+    XCTAssertTrue(byte_buffer_eq_string(&recv_buffer, "hello world"));
+
+    call->Finish(&recv_status, tag(9));
+    [self client_ok:9];
+
+    XCTAssertTrue(recv_status.ok());
+  }
+}
+
+- (void)testSimpleRpc {
+  [self ResetStub];
+  [self SendRpc:1];
+}
+
+- (void)testSequentialRpcs {
+  [self ResetStub];
+  [self SendRpc:10];
+}
+
++ (void)setUp {
+  grpc_test_init(0, NULL);
+}
+
+@end
+
diff --git a/test/cpp/cocoapods/test/Info.plist b/test/cpp/cocoapods/test/Info.plist
new file mode 100644
index 0000000..6c40a6c
--- /dev/null
+++ b/test/cpp/cocoapods/test/Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>
diff --git a/test/cpp/cocoapods/test/server_context_test_spouse_test.mm b/test/cpp/cocoapods/test/server_context_test_spouse_test.mm
new file mode 100644
index 0000000..fd6878e
--- /dev/null
+++ b/test/cpp/cocoapods/test/server_context_test_spouse_test.mm
@@ -0,0 +1,100 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Hack TEST macro of gTest and make they conform XCTest style. We only
+// need test name (b), not test case name (a).
+#define TEST(a,b) - (void)test ## b
+#define ASSERT_TRUE XCTAssert
+#define ASSERT_EQ XCTAssertEqual
+
+#import <XCTest/XCTest.h>
+
+#include <grpc++/test/server_context_test_spouse.h>
+
+#include <cstring>
+#include <vector>
+
+#include <grpc++/impl/grpc_library.h>
+
+static grpc::internal::GrpcLibraryInitializer g_initializer;
+
+const char key1[] = "metadata-key1";
+const char key2[] = "metadata-key2";
+const char val1[] = "metadata-val1";
+const char val2[] = "metadata-val2";
+
+bool ClientMetadataContains(const grpc::ServerContext& context,
+                            const grpc::string_ref& key,
+                            const grpc::string_ref& value) {
+  const auto& client_metadata = context.client_metadata();
+  for (auto iter = client_metadata.begin(); iter != client_metadata.end();
+       ++iter) {
+    if (iter->first == key && iter->second == value) {
+      return true;
+    }
+  }
+  return false;
+}
+
+@interface ServerContextTestSpouseTest : XCTestCase
+
+@end
+
+@implementation ServerContextTestSpouseTest
+
+TEST(ServerContextTestSpouseTest, ClientMetadata) {
+  grpc::ServerContext context;
+  grpc::testing::ServerContextTestSpouse spouse(&context);
+
+  spouse.AddClientMetadata(key1, val1);
+  ASSERT_TRUE(ClientMetadataContains(context, key1, val1));
+
+  spouse.AddClientMetadata(key2, val2);
+  ASSERT_TRUE(ClientMetadataContains(context, key1, val1));
+  ASSERT_TRUE(ClientMetadataContains(context, key2, val2));
+}
+
+TEST(ServerContextTestSpouseTest, InitialMetadata) {
+  grpc::ServerContext context;
+  grpc::testing::ServerContextTestSpouse spouse(&context);
+  std::multimap<grpc::string, grpc::string> metadata;
+
+  context.AddInitialMetadata(key1, val1);
+  metadata.insert(std::pair<grpc::string, grpc::string>(key1, val1));
+  ASSERT_EQ(metadata, spouse.GetInitialMetadata());
+
+  context.AddInitialMetadata(key2, val2);
+  metadata.insert(std::pair<grpc::string, grpc::string>(key2, val2));
+  ASSERT_EQ(metadata, spouse.GetInitialMetadata());
+}
+
+TEST(ServerContextTestSpouseTest, TrailingMetadata) {
+  grpc::ServerContext context;
+  grpc::testing::ServerContextTestSpouse spouse(&context);
+  std::multimap<grpc::string, grpc::string> metadata;
+
+  context.AddTrailingMetadata(key1, val1);
+  metadata.insert(std::pair<grpc::string, grpc::string>(key1, val1));
+  ASSERT_EQ(metadata, spouse.GetTrailingMetadata());
+
+  context.AddTrailingMetadata(key2, val2);
+  metadata.insert(std::pair<grpc::string, grpc::string>(key2, val2));
+  ASSERT_EQ(metadata, spouse.GetTrailingMetadata());
+}
+
+@end
diff --git a/test/cpp/end2end/BUILD b/test/cpp/end2end/BUILD
index 8894c68..27e8da1 100644
--- a/test/cpp/end2end/BUILD
+++ b/test/cpp/end2end/BUILD
@@ -120,6 +120,24 @@
 )
 
 grpc_cc_test(
+    name = "server_early_return_test",
+    srcs = ["server_early_return_test.cc"],
+    external_deps = [
+        "gtest",
+    ],
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//:grpc++",
+        "//src/proto/grpc/testing:echo_messages_proto",
+        "//src/proto/grpc/testing:echo_proto",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+        "//test/cpp/util:test_util",
+    ],
+)
+
+grpc_cc_test(
     name = "end2end_test",
     deps = [
         ":end2end_test_lib",
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index e25de81..b1b0b5d 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -1087,31 +1087,6 @@
   EXPECT_FALSE(s.ok());
 }
 
-// Client sends 20 requests and the server returns CANCELLED status after
-// reading 10 requests.
-TEST_P(End2endTest, RequestStreamServerEarlyCancelTest) {
-  ResetStub();
-  EchoRequest request;
-  EchoResponse response;
-  ClientContext context;
-
-  context.AddMetadata(kServerCancelAfterReads, "10");
-  auto stream = stub_->RequestStream(&context, &response);
-  request.set_message("hello");
-  int send_messages = 20;
-  while (send_messages > 10) {
-    EXPECT_TRUE(stream->Write(request));
-    send_messages--;
-  }
-  while (send_messages > 0) {
-    stream->Write(request);
-    send_messages--;
-  }
-  stream->WritesDone();
-  Status s = stream->Finish();
-  EXPECT_EQ(s.error_code(), StatusCode::CANCELLED);
-}
-
 void ReaderThreadFunc(ClientReaderWriter<EchoRequest, EchoResponse>* stream,
                       gpr_event* ev) {
   EchoResponse resp;
diff --git a/test/cpp/end2end/server_early_return_test.cc b/test/cpp/end2end/server_early_return_test.cc
new file mode 100644
index 0000000..6fd26fc
--- /dev/null
+++ b/test/cpp/end2end/server_early_return_test.cc
@@ -0,0 +1,233 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc++/channel.h>
+#include <grpc++/client_context.h>
+#include <grpc++/create_channel.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/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+
+#include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+#include "test/cpp/util/string_ref_helper.h"
+
+#include <gtest/gtest.h>
+
+namespace grpc {
+namespace testing {
+namespace {
+
+const char kServerReturnStatusCode[] = "server_return_status_code";
+const char kServerDelayBeforeReturnUs[] = "server_delay_before_return_us";
+const char kServerReturnAfterNReads[] = "server_return_after_n_reads";
+
+class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
+ public:
+  // Unused methods are not implemented.
+
+  Status RequestStream(ServerContext* context,
+                       ServerReader<EchoRequest>* reader,
+                       EchoResponse* response) override {
+    int server_return_status_code =
+        GetIntValueFromMetadata(context, kServerReturnStatusCode, 0);
+    int server_delay_before_return_us =
+        GetIntValueFromMetadata(context, kServerDelayBeforeReturnUs, 0);
+    int server_return_after_n_reads =
+        GetIntValueFromMetadata(context, kServerReturnAfterNReads, 0);
+
+    EchoRequest request;
+    while (server_return_after_n_reads--) {
+      EXPECT_TRUE(reader->Read(&request));
+    }
+
+    response->set_message("response msg");
+
+    gpr_sleep_until(gpr_time_add(
+        gpr_now(GPR_CLOCK_MONOTONIC),
+        gpr_time_from_micros(server_delay_before_return_us, GPR_TIMESPAN)));
+
+    return Status(static_cast<StatusCode>(server_return_status_code), "");
+  }
+
+  Status BidiStream(
+      ServerContext* context,
+      ServerReaderWriter<EchoResponse, EchoRequest>* stream) override {
+    int server_return_status_code =
+        GetIntValueFromMetadata(context, kServerReturnStatusCode, 0);
+    int server_delay_before_return_us =
+        GetIntValueFromMetadata(context, kServerDelayBeforeReturnUs, 0);
+    int server_return_after_n_reads =
+        GetIntValueFromMetadata(context, kServerReturnAfterNReads, 0);
+
+    EchoRequest request;
+    EchoResponse response;
+    while (server_return_after_n_reads--) {
+      EXPECT_TRUE(stream->Read(&request));
+      response.set_message(request.message());
+      EXPECT_TRUE(stream->Write(response));
+    }
+
+    gpr_sleep_until(gpr_time_add(
+        gpr_now(GPR_CLOCK_MONOTONIC),
+        gpr_time_from_micros(server_delay_before_return_us, GPR_TIMESPAN)));
+
+    return Status(static_cast<StatusCode>(server_return_status_code), "");
+  }
+
+  int GetIntValueFromMetadata(ServerContext* context, const char* key,
+                              int default_value) {
+    auto metadata = context->client_metadata();
+    if (metadata.find(key) != metadata.end()) {
+      std::istringstream iss(ToString(metadata.find(key)->second));
+      iss >> default_value;
+    }
+    return default_value;
+  }
+};
+
+class ServerEarlyReturnTest : public ::testing::Test {
+ protected:
+  ServerEarlyReturnTest() : picked_port_(0) {}
+
+  void SetUp() override {
+    int port = grpc_pick_unused_port_or_die();
+    picked_port_ = port;
+    server_address_ << "127.0.0.1:" << port;
+    ServerBuilder builder;
+    builder.AddListeningPort(server_address_.str(),
+                             InsecureServerCredentials());
+    builder.RegisterService(&service_);
+    server_ = builder.BuildAndStart();
+
+    channel_ =
+        CreateChannel(server_address_.str(), InsecureChannelCredentials());
+    stub_ = grpc::testing::EchoTestService::NewStub(channel_);
+  }
+
+  void TearDown() override {
+    server_->Shutdown();
+    if (picked_port_ > 0) {
+      grpc_recycle_unused_port(picked_port_);
+    }
+  }
+
+  // Client sends 20 requests and the server returns after reading 10 requests.
+  // If return_cancel is true, server returns CANCELLED status. Otherwise it
+  // returns OK.
+  void DoBidiStream(bool return_cancelled) {
+    EchoRequest request;
+    EchoResponse response;
+    ClientContext context;
+
+    context.AddMetadata(kServerReturnAfterNReads, "10");
+    if (return_cancelled) {
+      // "1" means CANCELLED
+      context.AddMetadata(kServerReturnStatusCode, "1");
+    }
+    context.AddMetadata(kServerDelayBeforeReturnUs, "10000");
+
+    auto stream = stub_->BidiStream(&context);
+
+    for (int i = 0; i < 20; i++) {
+      request.set_message(grpc::string("hello") + grpc::to_string(i));
+      bool write_ok = stream->Write(request);
+      bool read_ok = stream->Read(&response);
+      if (i < 10) {
+        EXPECT_TRUE(write_ok);
+        EXPECT_TRUE(read_ok);
+        EXPECT_EQ(response.message(), request.message());
+      } else {
+        EXPECT_FALSE(read_ok);
+      }
+    }
+
+    stream->WritesDone();
+    EXPECT_FALSE(stream->Read(&response));
+
+    Status s = stream->Finish();
+    if (return_cancelled) {
+      EXPECT_EQ(s.error_code(), StatusCode::CANCELLED);
+    } else {
+      EXPECT_TRUE(s.ok());
+    }
+  }
+
+  void DoRequestStream(bool return_cancelled) {
+    EchoRequest request;
+    EchoResponse response;
+    ClientContext context;
+
+    context.AddMetadata(kServerReturnAfterNReads, "10");
+    if (return_cancelled) {
+      // "1" means CANCELLED
+      context.AddMetadata(kServerReturnStatusCode, "1");
+    }
+    context.AddMetadata(kServerDelayBeforeReturnUs, "10000");
+
+    auto stream = stub_->RequestStream(&context, &response);
+    for (int i = 0; i < 20; i++) {
+      request.set_message(grpc::string("hello") + grpc::to_string(i));
+      bool written = stream->Write(request);
+      if (i < 10) {
+        EXPECT_TRUE(written);
+      }
+    }
+    stream->WritesDone();
+    Status s = stream->Finish();
+    if (return_cancelled) {
+      EXPECT_EQ(s.error_code(), StatusCode::CANCELLED);
+    } else {
+      EXPECT_TRUE(s.ok());
+    }
+  }
+
+  std::shared_ptr<Channel> channel_;
+  std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
+  std::unique_ptr<Server> server_;
+  std::ostringstream server_address_;
+  TestServiceImpl service_;
+  int picked_port_;
+};
+
+TEST_F(ServerEarlyReturnTest, BidiStreamEarlyOk) { DoBidiStream(false); }
+
+TEST_F(ServerEarlyReturnTest, BidiStreamEarlyCancel) { DoBidiStream(true); }
+
+TEST_F(ServerEarlyReturnTest, RequestStreamEarlyOK) { DoRequestStream(false); }
+TEST_F(ServerEarlyReturnTest, RequestStreamEarlyCancel) {
+  DoRequestStream(true);
+}
+
+}  // namespace
+}  // namespace testing
+}  // namespace grpc
+
+int main(int argc, char** argv) {
+  grpc_test_init(argc, argv);
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/test/cpp/end2end/test_service_impl.cc b/test/cpp/end2end/test_service_impl.cc
index e4f7c08..875a27b 100644
--- a/test/cpp/end2end/test_service_impl.cc
+++ b/test/cpp/end2end/test_service_impl.cc
@@ -181,13 +181,6 @@
   int server_try_cancel = GetIntValueFromMetadata(
       kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL);
 
-  // If 'cancel_after_reads' is set in the metadata AND non-zero, the server
-  // will cancel the RPC (by just returning Status::CANCELLED - doesn't call
-  // ServerContext::TryCancel()) after reading the number of records specified
-  // by the 'cancel_after_reads' value set in the metadata.
-  int cancel_after_reads = GetIntValueFromMetadata(
-      kServerCancelAfterReads, context->client_metadata(), 0);
-
   EchoRequest request;
   response->set_message("");
 
@@ -204,12 +197,6 @@
 
   int num_msgs_read = 0;
   while (reader->Read(&request)) {
-    if (cancel_after_reads == 1) {
-      gpr_log(GPR_INFO, "return cancel status");
-      return Status::CANCELLED;
-    } else if (cancel_after_reads > 0) {
-      cancel_after_reads--;
-    }
     response->mutable_message()->append(request.message());
   }
   gpr_log(GPR_INFO, "Read: %d messages", num_msgs_read);
diff --git a/test/cpp/end2end/test_service_impl.h b/test/cpp/end2end/test_service_impl.h
index e485769..070f357 100644
--- a/test/cpp/end2end/test_service_impl.h
+++ b/test/cpp/end2end/test_service_impl.h
@@ -31,7 +31,6 @@
 
 const int kServerDefaultResponseStreamsToSend = 3;
 const char* const kServerResponseStreamsToSend = "server_responses_to_send";
-const char* const kServerCancelAfterReads = "cancel_after_reads";
 const char* const kServerTryCancelRequest = "server_try_cancel";
 const char* const kDebugInfoTrailerKey = "debug-info-bin";
 const char* const kServerFinishAfterNReads = "server_finish_after_n_reads";
diff --git a/test/cpp/qps/driver.cc b/test/cpp/qps/driver.cc
index 2f765a6..a809a27 100644
--- a/test/cpp/qps/driver.cc
+++ b/test/cpp/qps/driver.cc
@@ -33,7 +33,6 @@
 
 #include "src/core/lib/gpr/env.h"
 #include "src/core/lib/profiling/timers.h"
-#include "src/proto/grpc/testing/services.grpc.pb.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
 #include "test/cpp/qps/client.h"
diff --git a/tools/distrib/run_clang_tidy.py b/tools/distrib/run_clang_tidy.py
index bc61d4e..337e6b4 100755
--- a/tools/distrib/run_clang_tidy.py
+++ b/tools/distrib/run_clang_tidy.py
@@ -26,6 +26,8 @@
 
 GRPC_CHECKS = [
     'modernize-use-nullptr',
+    'google-build-namespaces',
+    'google-build-explicit-make-pair',
 ]
 
 extra_args = [
diff --git a/tools/dockerfile/grpc_clang_tidy/clang_tidy_all_the_things.sh b/tools/dockerfile/grpc_clang_tidy/clang_tidy_all_the_things.sh
index 1a82dd5..e1932ed 100755
--- a/tools/dockerfile/grpc_clang_tidy/clang_tidy_all_the_things.sh
+++ b/tools/dockerfile/grpc_clang_tidy/clang_tidy_all_the_things.sh
@@ -20,5 +20,5 @@
 
 cd ${CLANG_TIDY_ROOT}
 
-find src/core src/cpp test/core test/cpp -name '*.h' -or -name '*.cc' -print0 \
+find src/core src/cpp test/core test/cpp -name '*.h' -print0 -or -name '*.cc' -print0 \
   | xargs -0 tools/distrib/run_clang_tidy.py "$@"
diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json
index 362effd..1e59510 100644
--- a/tools/run_tests/generated/sources_and_headers.json
+++ b/tools/run_tests/generated/sources_and_headers.json
@@ -4122,6 +4122,25 @@
     "deps": [
       "gpr", 
       "gpr_test_util", 
+      "grpc", 
+      "grpc++", 
+      "grpc++_test_util", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "server_early_return_test", 
+    "src": [
+      "test/cpp/end2end/server_early_return_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
       "grpc++_test_util_unsecure", 
       "grpc++_unsecure", 
       "grpc_test_util_unsecure", 
diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json
index 7b23cab..866e2c3 100644
--- a/tools/run_tests/generated/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -4398,6 +4398,30 @@
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
+    "name": "server_early_return_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": true, 
+    "language": "c++", 
     "name": "server_request_call_test", 
     "platforms": [
       "linux", 
diff --git a/tools/run_tests/run_tests_matrix.py b/tools/run_tests/run_tests_matrix.py
index 3440354..ae3a28b 100755
--- a/tools/run_tests/run_tests_matrix.py
+++ b/tools/run_tests/run_tests_matrix.py
@@ -44,14 +44,19 @@
 _REPORT_SUFFIX = 'sponge_log.xml'
 
 
+def _safe_report_name(name):
+    """Reports with '+' in target name won't show correctly in ResultStore"""
+    return name.replace('+', 'p')
+
+
 def _report_filename(name):
     """Generates report file name"""
-    return 'report_%s_%s' % (name, _REPORT_SUFFIX)
+    return 'report_%s_%s' % (_safe_report_name(name), _REPORT_SUFFIX)
 
 
 def _report_filename_internal_ci(name):
     """Generates report file name that leads to better presentation by internal CI"""
-    return '%s/%s' % (name, _REPORT_SUFFIX)
+    return '%s/%s' % (_safe_report_name(name), _REPORT_SUFFIX)
 
 
 def _docker_jobspec(name,
@@ -68,7 +73,7 @@
             '-j',
             str(inner_jobs), '-x',
             _report_filename(name), '--report_suite_name',
-            '%s' % name
+            '%s' % _safe_report_name(name)
         ] + runtests_args,
         environ=runtests_envs,
         shortname='run_tests_%s' % name,
@@ -95,7 +100,7 @@
             '-t', '-j',
             str(inner_jobs), '-x',
             '../%s' % _report_filename(name), '--report_suite_name',
-            '%s' % name
+            '%s' % _safe_report_name(name)
         ] + runtests_args,
         environ=env,
         shortname='run_tests_%s' % name,