瀏覽代碼

Merge remote-tracking branch 'upstream/master' into mpmcqueue

Yunjia Wang 6 年之前
父節點
當前提交
e4adb6ac56
共有 57 個文件被更改,包括 1268 次插入1511 次删除
  1. 117 72
      CMakeLists.txt
  2. 202 60
      Makefile
  3. 4 4
      doc/environment_variables.md
  4. 0 5
      include/grpcpp/impl/codegen/call_op_set.h
  5. 8 3
      include/grpcpp/impl/codegen/server_callback.h
  6. 328 106
      src/core/ext/filters/client_channel/client_channel.cc
  7. 2 17
      src/core/ext/filters/client_channel/lb_policy.cc
  8. 4 4
      src/core/ext/filters/client_channel/lb_policy.h
  9. 4 5
      src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
  10. 23 21
      src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
  11. 10 14
      src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
  12. 25 101
      src/core/ext/filters/client_channel/lb_policy/subchannel_list.h
  13. 85 20
      src/core/ext/filters/client_channel/lb_policy/xds/xds.cc
  14. 3 11
      src/core/ext/filters/client_channel/resolver.h
  15. 13 10
      src/core/ext/filters/client_channel/subchannel.cc
  16. 50 21
      src/core/ext/filters/client_channel/subchannel.h
  17. 15 30
      src/core/ext/filters/client_channel/subchannel_interface.h
  18. 28 0
      src/core/lib/gprpp/map.h
  19. 1 1
      src/core/lib/iomgr/call_combiner.cc
  20. 1 1
      src/core/lib/iomgr/call_combiner.h
  21. 3 2
      src/core/lib/iomgr/cfstream_handle.cc
  22. 4 0
      src/core/lib/iomgr/cfstream_handle.h
  23. 3 3
      src/core/lib/iomgr/ev_posix.cc
  24. 3 2
      src/core/lib/iomgr/ev_posix.h
  25. 2 2
      src/core/lib/iomgr/ev_windows.cc
  26. 1 1
      src/core/lib/iomgr/lockfree_event.cc
  27. 5 1
      src/core/lib/iomgr/port.h
  28. 5 6
      src/core/lib/iomgr/tcp_uv.cc
  29. 2 2
      src/core/lib/security/transport/security_handshaker.cc
  30. 31 21
      src/core/lib/slice/slice_buffer.cc
  31. 5 0
      src/core/lib/transport/metadata.cc
  32. 1 1
      src/core/lib/transport/transport.cc
  33. 8 2
      src/core/lib/transport/transport.h
  34. 0 3
      src/objective-c/GRPCClient/GRPCCall+ChannelArg.m
  35. 2 3
      src/objective-c/GRPCClient/GRPCCallOptions.h
  36. 7 3
      src/objective-c/GRPCClient/GRPCCallOptions.m
  37. 0 2
      src/objective-c/GRPCClient/private/GRPCChannel.m
  38. 3 1
      src/objective-c/GRPCClient/private/GRPCChannelPool.m
  39. 5 5
      src/objective-c/GRPCClient/private/GRPCHost.m
  40. 0 4
      src/objective-c/tests/CronetTests/InteropTestsRemoteWithCronet.m
  41. 0 6
      src/objective-c/tests/InteropTests/InteropTests.h
  42. 9 181
      src/objective-c/tests/InteropTests/InteropTests.m
  43. 0 4
      src/objective-c/tests/InteropTests/InteropTestsRemote.m
  44. 3 380
      src/objective-c/tests/MacTests/StressTests.m
  45. 3 327
      src/objective-c/tests/UnitTests/APIv2Tests.m
  46. 28 0
      templates/tools/dockerfile/test/python_stretch_3.8_x64/Dockerfile.template
  47. 2 2
      test/core/bad_client/gen_build_yaml.py
  48. 4 0
      test/core/bad_client/generate_tests.bzl
  49. 30 11
      test/core/bad_client/tests/unknown_frame.cc
  50. 29 0
      test/core/gprpp/map_test.cc
  51. 1 1
      test/core/util/test_lb_policies.cc
  52. 1 2
      test/cpp/interop/interop_server.cc
  53. 79 0
      tools/dockerfile/test/python_stretch_3.8_x64/Dockerfile
  54. 29 0
      tools/dockerfile/test/python_stretch_3.8_x64/get_cpython.sh
  55. 12 12
      tools/run_tests/generated/sources_and_headers.json
  56. 11 11
      tools/run_tests/generated/tests.json
  57. 14 4
      tools/run_tests/run_tests.py

+ 117 - 72
CMakeLists.txt

@@ -441,17 +441,6 @@ add_dependencies(buildtests_c udp_server_test)
 endif()
 add_dependencies(buildtests_c uri_parser_test)
 add_dependencies(buildtests_c public_headers_must_be_c89)
-add_dependencies(buildtests_c badreq_bad_client_test)
-add_dependencies(buildtests_c connection_prefix_bad_client_test)
-add_dependencies(buildtests_c duplicate_header_bad_client_test)
-add_dependencies(buildtests_c head_of_line_blocking_bad_client_test)
-add_dependencies(buildtests_c headers_bad_client_test)
-add_dependencies(buildtests_c initial_settings_frame_bad_client_test)
-add_dependencies(buildtests_c large_metadata_bad_client_test)
-add_dependencies(buildtests_c server_registered_method_bad_client_test)
-add_dependencies(buildtests_c simple_request_bad_client_test)
-add_dependencies(buildtests_c unknown_frame_bad_client_test)
-add_dependencies(buildtests_c window_overflow_bad_client_test)
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_c bad_ssl_cert_server)
 endif()
@@ -736,6 +725,17 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_cxx writes_per_rpc_test)
 endif()
 add_dependencies(buildtests_cxx xds_end2end_test)
+add_dependencies(buildtests_cxx badreq_bad_client_test)
+add_dependencies(buildtests_cxx connection_prefix_bad_client_test)
+add_dependencies(buildtests_cxx duplicate_header_bad_client_test)
+add_dependencies(buildtests_cxx head_of_line_blocking_bad_client_test)
+add_dependencies(buildtests_cxx headers_bad_client_test)
+add_dependencies(buildtests_cxx initial_settings_frame_bad_client_test)
+add_dependencies(buildtests_cxx large_metadata_bad_client_test)
+add_dependencies(buildtests_cxx server_registered_method_bad_client_test)
+add_dependencies(buildtests_cxx simple_request_bad_client_test)
+add_dependencies(buildtests_cxx unknown_frame_bad_client_test)
+add_dependencies(buildtests_cxx window_overflow_bad_client_test)
 add_dependencies(buildtests_cxx resolver_component_test_unsecure)
 add_dependencies(buildtests_cxx resolver_component_test)
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
@@ -5852,18 +5852,19 @@ target_include_directories(bad_client_test
   PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
   PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
   PRIVATE ${_gRPC_NANOPB_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}
 )
-  # avoid dependency on libstdc++
-  if (_gRPC_CORE_NOSTDCXX_FLAGS)
-    set_target_properties(bad_client_test PROPERTIES LINKER_LANGUAGE C)
-    # only use the flags for C++ source files
-    target_compile_options(bad_client_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${_gRPC_CORE_NOSTDCXX_FLAGS}>)
-  endif()
 target_link_libraries(bad_client_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
   grpc_test_util_unsecure
   grpc_unsecure
   gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
 )
 
 
@@ -17072,6 +17073,8 @@ if (gRPC_BUILD_TESTS)
 
 add_executable(badreq_bad_client_test
   test/core/bad_client/tests/badreq.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
 )
 
 
@@ -17086,28 +17089,32 @@ target_include_directories(badreq_bad_client_test
   PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
   PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
   PRIVATE ${_gRPC_NANOPB_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(badreq_bad_client_test
   ${_gRPC_SSL_LIBRARIES}
+  ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
   bad_client_test
   grpc_test_util_unsecure
   grpc_unsecure
   gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
 )
 
-  # avoid dependency on libstdc++
-  if (_gRPC_CORE_NOSTDCXX_FLAGS)
-    set_target_properties(badreq_bad_client_test PROPERTIES LINKER_LANGUAGE C)
-    target_compile_options(badreq_bad_client_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${_gRPC_CORE_NOSTDCXX_FLAGS}>)
-  endif()
 
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
 add_executable(connection_prefix_bad_client_test
   test/core/bad_client/tests/connection_prefix.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
 )
 
 
@@ -17122,28 +17129,32 @@ target_include_directories(connection_prefix_bad_client_test
   PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
   PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
   PRIVATE ${_gRPC_NANOPB_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(connection_prefix_bad_client_test
   ${_gRPC_SSL_LIBRARIES}
+  ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
   bad_client_test
   grpc_test_util_unsecure
   grpc_unsecure
   gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
 )
 
-  # avoid dependency on libstdc++
-  if (_gRPC_CORE_NOSTDCXX_FLAGS)
-    set_target_properties(connection_prefix_bad_client_test PROPERTIES LINKER_LANGUAGE C)
-    target_compile_options(connection_prefix_bad_client_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${_gRPC_CORE_NOSTDCXX_FLAGS}>)
-  endif()
 
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
 add_executable(duplicate_header_bad_client_test
   test/core/bad_client/tests/duplicate_header.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
 )
 
 
@@ -17158,28 +17169,32 @@ target_include_directories(duplicate_header_bad_client_test
   PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
   PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
   PRIVATE ${_gRPC_NANOPB_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(duplicate_header_bad_client_test
   ${_gRPC_SSL_LIBRARIES}
+  ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
   bad_client_test
   grpc_test_util_unsecure
   grpc_unsecure
   gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
 )
 
-  # avoid dependency on libstdc++
-  if (_gRPC_CORE_NOSTDCXX_FLAGS)
-    set_target_properties(duplicate_header_bad_client_test PROPERTIES LINKER_LANGUAGE C)
-    target_compile_options(duplicate_header_bad_client_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${_gRPC_CORE_NOSTDCXX_FLAGS}>)
-  endif()
 
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
 add_executable(head_of_line_blocking_bad_client_test
   test/core/bad_client/tests/head_of_line_blocking.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
 )
 
 
@@ -17194,28 +17209,32 @@ target_include_directories(head_of_line_blocking_bad_client_test
   PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
   PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
   PRIVATE ${_gRPC_NANOPB_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(head_of_line_blocking_bad_client_test
   ${_gRPC_SSL_LIBRARIES}
+  ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
   bad_client_test
   grpc_test_util_unsecure
   grpc_unsecure
   gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
 )
 
-  # avoid dependency on libstdc++
-  if (_gRPC_CORE_NOSTDCXX_FLAGS)
-    set_target_properties(head_of_line_blocking_bad_client_test PROPERTIES LINKER_LANGUAGE C)
-    target_compile_options(head_of_line_blocking_bad_client_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${_gRPC_CORE_NOSTDCXX_FLAGS}>)
-  endif()
 
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
 add_executable(headers_bad_client_test
   test/core/bad_client/tests/headers.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
 )
 
 
@@ -17230,28 +17249,32 @@ target_include_directories(headers_bad_client_test
   PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
   PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
   PRIVATE ${_gRPC_NANOPB_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(headers_bad_client_test
   ${_gRPC_SSL_LIBRARIES}
+  ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
   bad_client_test
   grpc_test_util_unsecure
   grpc_unsecure
   gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
 )
 
-  # avoid dependency on libstdc++
-  if (_gRPC_CORE_NOSTDCXX_FLAGS)
-    set_target_properties(headers_bad_client_test PROPERTIES LINKER_LANGUAGE C)
-    target_compile_options(headers_bad_client_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${_gRPC_CORE_NOSTDCXX_FLAGS}>)
-  endif()
 
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
 add_executable(initial_settings_frame_bad_client_test
   test/core/bad_client/tests/initial_settings_frame.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
 )
 
 
@@ -17266,28 +17289,32 @@ target_include_directories(initial_settings_frame_bad_client_test
   PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
   PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
   PRIVATE ${_gRPC_NANOPB_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(initial_settings_frame_bad_client_test
   ${_gRPC_SSL_LIBRARIES}
+  ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
   bad_client_test
   grpc_test_util_unsecure
   grpc_unsecure
   gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
 )
 
-  # avoid dependency on libstdc++
-  if (_gRPC_CORE_NOSTDCXX_FLAGS)
-    set_target_properties(initial_settings_frame_bad_client_test PROPERTIES LINKER_LANGUAGE C)
-    target_compile_options(initial_settings_frame_bad_client_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${_gRPC_CORE_NOSTDCXX_FLAGS}>)
-  endif()
 
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
 add_executable(large_metadata_bad_client_test
   test/core/bad_client/tests/large_metadata.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
 )
 
 
@@ -17302,28 +17329,32 @@ target_include_directories(large_metadata_bad_client_test
   PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
   PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
   PRIVATE ${_gRPC_NANOPB_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(large_metadata_bad_client_test
   ${_gRPC_SSL_LIBRARIES}
+  ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
   bad_client_test
   grpc_test_util_unsecure
   grpc_unsecure
   gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
 )
 
-  # avoid dependency on libstdc++
-  if (_gRPC_CORE_NOSTDCXX_FLAGS)
-    set_target_properties(large_metadata_bad_client_test PROPERTIES LINKER_LANGUAGE C)
-    target_compile_options(large_metadata_bad_client_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${_gRPC_CORE_NOSTDCXX_FLAGS}>)
-  endif()
 
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
 add_executable(server_registered_method_bad_client_test
   test/core/bad_client/tests/server_registered_method.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
 )
 
 
@@ -17338,28 +17369,32 @@ target_include_directories(server_registered_method_bad_client_test
   PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
   PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
   PRIVATE ${_gRPC_NANOPB_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_registered_method_bad_client_test
   ${_gRPC_SSL_LIBRARIES}
+  ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
   bad_client_test
   grpc_test_util_unsecure
   grpc_unsecure
   gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
 )
 
-  # avoid dependency on libstdc++
-  if (_gRPC_CORE_NOSTDCXX_FLAGS)
-    set_target_properties(server_registered_method_bad_client_test PROPERTIES LINKER_LANGUAGE C)
-    target_compile_options(server_registered_method_bad_client_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${_gRPC_CORE_NOSTDCXX_FLAGS}>)
-  endif()
 
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
 add_executable(simple_request_bad_client_test
   test/core/bad_client/tests/simple_request.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
 )
 
 
@@ -17374,28 +17409,32 @@ target_include_directories(simple_request_bad_client_test
   PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
   PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
   PRIVATE ${_gRPC_NANOPB_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(simple_request_bad_client_test
   ${_gRPC_SSL_LIBRARIES}
+  ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
   bad_client_test
   grpc_test_util_unsecure
   grpc_unsecure
   gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
 )
 
-  # avoid dependency on libstdc++
-  if (_gRPC_CORE_NOSTDCXX_FLAGS)
-    set_target_properties(simple_request_bad_client_test PROPERTIES LINKER_LANGUAGE C)
-    target_compile_options(simple_request_bad_client_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${_gRPC_CORE_NOSTDCXX_FLAGS}>)
-  endif()
 
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
 add_executable(unknown_frame_bad_client_test
   test/core/bad_client/tests/unknown_frame.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
 )
 
 
@@ -17410,28 +17449,32 @@ target_include_directories(unknown_frame_bad_client_test
   PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
   PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
   PRIVATE ${_gRPC_NANOPB_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(unknown_frame_bad_client_test
   ${_gRPC_SSL_LIBRARIES}
+  ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
   bad_client_test
   grpc_test_util_unsecure
   grpc_unsecure
   gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
 )
 
-  # avoid dependency on libstdc++
-  if (_gRPC_CORE_NOSTDCXX_FLAGS)
-    set_target_properties(unknown_frame_bad_client_test PROPERTIES LINKER_LANGUAGE C)
-    target_compile_options(unknown_frame_bad_client_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${_gRPC_CORE_NOSTDCXX_FLAGS}>)
-  endif()
 
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
 add_executable(window_overflow_bad_client_test
   test/core/bad_client/tests/window_overflow.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
 )
 
 
@@ -17446,22 +17489,24 @@ target_include_directories(window_overflow_bad_client_test
   PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
   PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
   PRIVATE ${_gRPC_NANOPB_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(window_overflow_bad_client_test
   ${_gRPC_SSL_LIBRARIES}
+  ${_gRPC_PROTOBUF_LIBRARIES}
   ${_gRPC_ALLTARGETS_LIBRARIES}
   bad_client_test
   grpc_test_util_unsecure
   grpc_unsecure
   gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
 )
 
-  # avoid dependency on libstdc++
-  if (_gRPC_CORE_NOSTDCXX_FLAGS)
-    set_target_properties(window_overflow_bad_client_test PROPERTIES LINKER_LANGUAGE C)
-    target_compile_options(window_overflow_bad_client_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${_gRPC_CORE_NOSTDCXX_FLAGS}>)
-  endif()
 
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)

+ 202 - 60
Makefile

@@ -1406,7 +1406,7 @@ plugins: $(PROTOC_PLUGINS)
 
 privatelibs: privatelibs_c privatelibs_cxx
 
-privatelibs_c:  $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libcxxabi.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libreconnect_server.a $(LIBDIR)/$(CONFIG)/libtest_tcp_server.a $(LIBDIR)/$(CONFIG)/libupb.a $(LIBDIR)/$(CONFIG)/libz.a $(LIBDIR)/$(CONFIG)/libares.a $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libbad_ssl_test_server.a $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a
+privatelibs_c:  $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libcxxabi.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libreconnect_server.a $(LIBDIR)/$(CONFIG)/libtest_tcp_server.a $(LIBDIR)/$(CONFIG)/libupb.a $(LIBDIR)/$(CONFIG)/libz.a $(LIBDIR)/$(CONFIG)/libares.a $(LIBDIR)/$(CONFIG)/libbad_ssl_test_server.a $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a
 pc_c: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc.pc $(LIBDIR)/$(CONFIG)/pkgconfig/gpr.pc
 
 pc_c_unsecure: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc_unsecure.pc $(LIBDIR)/$(CONFIG)/pkgconfig/gpr.pc
@@ -1416,9 +1416,9 @@ pc_cxx: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++.pc
 pc_cxx_unsecure: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++_unsecure.pc
 
 ifeq ($(EMBED_OPENSSL),true)
-privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libdns_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_core_stats.a $(LIBDIR)/$(CONFIG)/libgrpc++_proto_reflection_desc_db.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libhttp2_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libqps.a $(LIBDIR)/$(CONFIG)/libboringssl_test_util.a $(LIBDIR)/$(CONFIG)/libbenchmark.a
+privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libdns_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_core_stats.a $(LIBDIR)/$(CONFIG)/libgrpc++_proto_reflection_desc_db.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libhttp2_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libqps.a $(LIBDIR)/$(CONFIG)/libboringssl_test_util.a $(LIBDIR)/$(CONFIG)/libbenchmark.a $(LIBDIR)/$(CONFIG)/libbad_client_test.a
 else
-privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libdns_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_core_stats.a $(LIBDIR)/$(CONFIG)/libgrpc++_proto_reflection_desc_db.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libhttp2_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libqps.a $(LIBDIR)/$(CONFIG)/libbenchmark.a
+privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libdns_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_core_stats.a $(LIBDIR)/$(CONFIG)/libgrpc++_proto_reflection_desc_db.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libhttp2_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libqps.a $(LIBDIR)/$(CONFIG)/libbenchmark.a $(LIBDIR)/$(CONFIG)/libbad_client_test.a
 endif
 
 
@@ -1557,17 +1557,6 @@ buildtests_c: privatelibs_c \
   $(BINDIR)/$(CONFIG)/udp_server_test \
   $(BINDIR)/$(CONFIG)/uri_parser_test \
   $(BINDIR)/$(CONFIG)/public_headers_must_be_c89 \
-  $(BINDIR)/$(CONFIG)/badreq_bad_client_test \
-  $(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test \
-  $(BINDIR)/$(CONFIG)/duplicate_header_bad_client_test \
-  $(BINDIR)/$(CONFIG)/head_of_line_blocking_bad_client_test \
-  $(BINDIR)/$(CONFIG)/headers_bad_client_test \
-  $(BINDIR)/$(CONFIG)/initial_settings_frame_bad_client_test \
-  $(BINDIR)/$(CONFIG)/large_metadata_bad_client_test \
-  $(BINDIR)/$(CONFIG)/server_registered_method_bad_client_test \
-  $(BINDIR)/$(CONFIG)/simple_request_bad_client_test \
-  $(BINDIR)/$(CONFIG)/unknown_frame_bad_client_test \
-  $(BINDIR)/$(CONFIG)/window_overflow_bad_client_test \
   $(BINDIR)/$(CONFIG)/bad_ssl_cert_server \
   $(BINDIR)/$(CONFIG)/bad_ssl_cert_test \
   $(BINDIR)/$(CONFIG)/h2_census_test \
@@ -1764,6 +1753,17 @@ buildtests_cxx: privatelibs_cxx \
   $(BINDIR)/$(CONFIG)/xds_end2end_test \
   $(BINDIR)/$(CONFIG)/boringssl_ssl_test \
   $(BINDIR)/$(CONFIG)/boringssl_crypto_test \
+  $(BINDIR)/$(CONFIG)/badreq_bad_client_test \
+  $(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test \
+  $(BINDIR)/$(CONFIG)/duplicate_header_bad_client_test \
+  $(BINDIR)/$(CONFIG)/head_of_line_blocking_bad_client_test \
+  $(BINDIR)/$(CONFIG)/headers_bad_client_test \
+  $(BINDIR)/$(CONFIG)/initial_settings_frame_bad_client_test \
+  $(BINDIR)/$(CONFIG)/large_metadata_bad_client_test \
+  $(BINDIR)/$(CONFIG)/server_registered_method_bad_client_test \
+  $(BINDIR)/$(CONFIG)/simple_request_bad_client_test \
+  $(BINDIR)/$(CONFIG)/unknown_frame_bad_client_test \
+  $(BINDIR)/$(CONFIG)/window_overflow_bad_client_test \
   $(BINDIR)/$(CONFIG)/resolver_component_test_unsecure \
   $(BINDIR)/$(CONFIG)/resolver_component_test \
   $(BINDIR)/$(CONFIG)/resolver_component_tests_runner_invoker_unsecure \
@@ -1912,6 +1912,17 @@ buildtests_cxx: privatelibs_cxx \
   $(BINDIR)/$(CONFIG)/transport_security_common_api_test \
   $(BINDIR)/$(CONFIG)/writes_per_rpc_test \
   $(BINDIR)/$(CONFIG)/xds_end2end_test \
+  $(BINDIR)/$(CONFIG)/badreq_bad_client_test \
+  $(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test \
+  $(BINDIR)/$(CONFIG)/duplicate_header_bad_client_test \
+  $(BINDIR)/$(CONFIG)/head_of_line_blocking_bad_client_test \
+  $(BINDIR)/$(CONFIG)/headers_bad_client_test \
+  $(BINDIR)/$(CONFIG)/initial_settings_frame_bad_client_test \
+  $(BINDIR)/$(CONFIG)/large_metadata_bad_client_test \
+  $(BINDIR)/$(CONFIG)/server_registered_method_bad_client_test \
+  $(BINDIR)/$(CONFIG)/simple_request_bad_client_test \
+  $(BINDIR)/$(CONFIG)/unknown_frame_bad_client_test \
+  $(BINDIR)/$(CONFIG)/window_overflow_bad_client_test \
   $(BINDIR)/$(CONFIG)/resolver_component_test_unsecure \
   $(BINDIR)/$(CONFIG)/resolver_component_test \
   $(BINDIR)/$(CONFIG)/resolver_component_tests_runner_invoker_unsecure \
@@ -2180,28 +2191,6 @@ test_c: buildtests_c
 	$(Q) $(BINDIR)/$(CONFIG)/uri_parser_test || ( echo test uri_parser_test failed ; exit 1 )
 	$(E) "[RUN]     Testing public_headers_must_be_c89"
 	$(Q) $(BINDIR)/$(CONFIG)/public_headers_must_be_c89 || ( echo test public_headers_must_be_c89 failed ; exit 1 )
-	$(E) "[RUN]     Testing badreq_bad_client_test"
-	$(Q) $(BINDIR)/$(CONFIG)/badreq_bad_client_test || ( echo test badreq_bad_client_test failed ; exit 1 )
-	$(E) "[RUN]     Testing connection_prefix_bad_client_test"
-	$(Q) $(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test || ( echo test connection_prefix_bad_client_test failed ; exit 1 )
-	$(E) "[RUN]     Testing duplicate_header_bad_client_test"
-	$(Q) $(BINDIR)/$(CONFIG)/duplicate_header_bad_client_test || ( echo test duplicate_header_bad_client_test failed ; exit 1 )
-	$(E) "[RUN]     Testing head_of_line_blocking_bad_client_test"
-	$(Q) $(BINDIR)/$(CONFIG)/head_of_line_blocking_bad_client_test || ( echo test head_of_line_blocking_bad_client_test failed ; exit 1 )
-	$(E) "[RUN]     Testing headers_bad_client_test"
-	$(Q) $(BINDIR)/$(CONFIG)/headers_bad_client_test || ( echo test headers_bad_client_test failed ; exit 1 )
-	$(E) "[RUN]     Testing initial_settings_frame_bad_client_test"
-	$(Q) $(BINDIR)/$(CONFIG)/initial_settings_frame_bad_client_test || ( echo test initial_settings_frame_bad_client_test failed ; exit 1 )
-	$(E) "[RUN]     Testing large_metadata_bad_client_test"
-	$(Q) $(BINDIR)/$(CONFIG)/large_metadata_bad_client_test || ( echo test large_metadata_bad_client_test failed ; exit 1 )
-	$(E) "[RUN]     Testing server_registered_method_bad_client_test"
-	$(Q) $(BINDIR)/$(CONFIG)/server_registered_method_bad_client_test || ( echo test server_registered_method_bad_client_test failed ; exit 1 )
-	$(E) "[RUN]     Testing simple_request_bad_client_test"
-	$(Q) $(BINDIR)/$(CONFIG)/simple_request_bad_client_test || ( echo test simple_request_bad_client_test failed ; exit 1 )
-	$(E) "[RUN]     Testing unknown_frame_bad_client_test"
-	$(Q) $(BINDIR)/$(CONFIG)/unknown_frame_bad_client_test || ( echo test unknown_frame_bad_client_test failed ; exit 1 )
-	$(E) "[RUN]     Testing window_overflow_bad_client_test"
-	$(Q) $(BINDIR)/$(CONFIG)/window_overflow_bad_client_test || ( echo test window_overflow_bad_client_test failed ; exit 1 )
 	$(E) "[RUN]     Testing bad_ssl_cert_test"
 	$(Q) $(BINDIR)/$(CONFIG)/bad_ssl_cert_test || ( echo test bad_ssl_cert_test failed ; exit 1 )
 
@@ -2456,6 +2445,28 @@ test_cxx: buildtests_cxx
 	$(Q) $(BINDIR)/$(CONFIG)/writes_per_rpc_test || ( echo test writes_per_rpc_test failed ; exit 1 )
 	$(E) "[RUN]     Testing xds_end2end_test"
 	$(Q) $(BINDIR)/$(CONFIG)/xds_end2end_test || ( echo test xds_end2end_test failed ; exit 1 )
+	$(E) "[RUN]     Testing badreq_bad_client_test"
+	$(Q) $(BINDIR)/$(CONFIG)/badreq_bad_client_test || ( echo test badreq_bad_client_test failed ; exit 1 )
+	$(E) "[RUN]     Testing connection_prefix_bad_client_test"
+	$(Q) $(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test || ( echo test connection_prefix_bad_client_test failed ; exit 1 )
+	$(E) "[RUN]     Testing duplicate_header_bad_client_test"
+	$(Q) $(BINDIR)/$(CONFIG)/duplicate_header_bad_client_test || ( echo test duplicate_header_bad_client_test failed ; exit 1 )
+	$(E) "[RUN]     Testing head_of_line_blocking_bad_client_test"
+	$(Q) $(BINDIR)/$(CONFIG)/head_of_line_blocking_bad_client_test || ( echo test head_of_line_blocking_bad_client_test failed ; exit 1 )
+	$(E) "[RUN]     Testing headers_bad_client_test"
+	$(Q) $(BINDIR)/$(CONFIG)/headers_bad_client_test || ( echo test headers_bad_client_test failed ; exit 1 )
+	$(E) "[RUN]     Testing initial_settings_frame_bad_client_test"
+	$(Q) $(BINDIR)/$(CONFIG)/initial_settings_frame_bad_client_test || ( echo test initial_settings_frame_bad_client_test failed ; exit 1 )
+	$(E) "[RUN]     Testing large_metadata_bad_client_test"
+	$(Q) $(BINDIR)/$(CONFIG)/large_metadata_bad_client_test || ( echo test large_metadata_bad_client_test failed ; exit 1 )
+	$(E) "[RUN]     Testing server_registered_method_bad_client_test"
+	$(Q) $(BINDIR)/$(CONFIG)/server_registered_method_bad_client_test || ( echo test server_registered_method_bad_client_test failed ; exit 1 )
+	$(E) "[RUN]     Testing simple_request_bad_client_test"
+	$(Q) $(BINDIR)/$(CONFIG)/simple_request_bad_client_test || ( echo test simple_request_bad_client_test failed ; exit 1 )
+	$(E) "[RUN]     Testing unknown_frame_bad_client_test"
+	$(Q) $(BINDIR)/$(CONFIG)/unknown_frame_bad_client_test || ( echo test unknown_frame_bad_client_test failed ; exit 1 )
+	$(E) "[RUN]     Testing window_overflow_bad_client_test"
+	$(Q) $(BINDIR)/$(CONFIG)/window_overflow_bad_client_test || ( echo test window_overflow_bad_client_test failed ; exit 1 )
 	$(E) "[RUN]     Testing resolver_component_tests_runner_invoker_unsecure"
 	$(Q) $(BINDIR)/$(CONFIG)/resolver_component_tests_runner_invoker_unsecure || ( echo test resolver_component_tests_runner_invoker_unsecure failed ; exit 1 )
 	$(E) "[RUN]     Testing resolver_component_tests_runner_invoker"
@@ -8549,7 +8560,7 @@ endif
 LIBBAD_CLIENT_TEST_SRC = \
     test/core/bad_client/bad_client.cc \
 
-PUBLIC_HEADERS_C += \
+PUBLIC_HEADERS_CXX += \
 
 LIBBAD_CLIENT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBBAD_CLIENT_TEST_SRC))))
 
@@ -8563,8 +8574,16 @@ $(LIBDIR)/$(CONFIG)/libbad_client_test.a: openssl_dep_error
 
 else
 
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build a C++ library if you don't have protobuf - a bit overreached, but still okay.
+
+$(LIBDIR)/$(CONFIG)/libbad_client_test.a: protobuf_dep_error
+
 
-$(LIBDIR)/$(CONFIG)/libbad_client_test.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(CARES_DEP) $(ADDRESS_SORTING_DEP) $(LIBBAD_CLIENT_TEST_OBJS) 
+else
+
+$(LIBDIR)/$(CONFIG)/libbad_client_test.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(CARES_DEP) $(ADDRESS_SORTING_DEP) $(PROTOBUF_DEP) $(LIBBAD_CLIENT_TEST_OBJS) 
 	$(E) "[AR]      Creating $@"
 	$(Q) mkdir -p `dirname $@`
 	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libbad_client_test.a
@@ -8576,6 +8595,8 @@ endif
 
 
 
+endif
+
 endif
 
 ifneq ($(NO_SECURE),true)
@@ -20350,10 +20371,21 @@ BADREQ_BAD_CLIENT_TEST_SRC = \
 BADREQ_BAD_CLIENT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(BADREQ_BAD_CLIENT_TEST_SRC))))
 
 
-$(BINDIR)/$(CONFIG)/badreq_bad_client_test: $(BADREQ_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/badreq_bad_client_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/badreq_bad_client_test: $(PROTOBUF_DEP) $(BADREQ_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(BADREQ_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/badreq_bad_client_test
+	$(Q) $(LDXX) $(LDFLAGS) $(BADREQ_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/badreq_bad_client_test
+
+endif
 
 $(OBJDIR)/$(CONFIG)/test/core/bad_client/tests/badreq.o:  $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
@@ -20370,10 +20402,21 @@ CONNECTION_PREFIX_BAD_CLIENT_TEST_SRC = \
 CONNECTION_PREFIX_BAD_CLIENT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CONNECTION_PREFIX_BAD_CLIENT_TEST_SRC))))
 
 
-$(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test: $(CONNECTION_PREFIX_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test: $(PROTOBUF_DEP) $(CONNECTION_PREFIX_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(CONNECTION_PREFIX_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test
+	$(Q) $(LDXX) $(LDFLAGS) $(CONNECTION_PREFIX_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test
+
+endif
 
 $(OBJDIR)/$(CONFIG)/test/core/bad_client/tests/connection_prefix.o:  $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
@@ -20390,10 +20433,21 @@ DUPLICATE_HEADER_BAD_CLIENT_TEST_SRC = \
 DUPLICATE_HEADER_BAD_CLIENT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(DUPLICATE_HEADER_BAD_CLIENT_TEST_SRC))))
 
 
-$(BINDIR)/$(CONFIG)/duplicate_header_bad_client_test: $(DUPLICATE_HEADER_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/duplicate_header_bad_client_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/duplicate_header_bad_client_test: $(PROTOBUF_DEP) $(DUPLICATE_HEADER_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(DUPLICATE_HEADER_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/duplicate_header_bad_client_test
+	$(Q) $(LDXX) $(LDFLAGS) $(DUPLICATE_HEADER_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/duplicate_header_bad_client_test
+
+endif
 
 $(OBJDIR)/$(CONFIG)/test/core/bad_client/tests/duplicate_header.o:  $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
@@ -20410,10 +20464,21 @@ HEAD_OF_LINE_BLOCKING_BAD_CLIENT_TEST_SRC = \
 HEAD_OF_LINE_BLOCKING_BAD_CLIENT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HEAD_OF_LINE_BLOCKING_BAD_CLIENT_TEST_SRC))))
 
 
-$(BINDIR)/$(CONFIG)/head_of_line_blocking_bad_client_test: $(HEAD_OF_LINE_BLOCKING_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/head_of_line_blocking_bad_client_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/head_of_line_blocking_bad_client_test: $(PROTOBUF_DEP) $(HEAD_OF_LINE_BLOCKING_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(HEAD_OF_LINE_BLOCKING_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/head_of_line_blocking_bad_client_test
+	$(Q) $(LDXX) $(LDFLAGS) $(HEAD_OF_LINE_BLOCKING_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/head_of_line_blocking_bad_client_test
+
+endif
 
 $(OBJDIR)/$(CONFIG)/test/core/bad_client/tests/head_of_line_blocking.o:  $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
@@ -20430,10 +20495,21 @@ HEADERS_BAD_CLIENT_TEST_SRC = \
 HEADERS_BAD_CLIENT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HEADERS_BAD_CLIENT_TEST_SRC))))
 
 
-$(BINDIR)/$(CONFIG)/headers_bad_client_test: $(HEADERS_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/headers_bad_client_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/headers_bad_client_test: $(PROTOBUF_DEP) $(HEADERS_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(HEADERS_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/headers_bad_client_test
+	$(Q) $(LDXX) $(LDFLAGS) $(HEADERS_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/headers_bad_client_test
+
+endif
 
 $(OBJDIR)/$(CONFIG)/test/core/bad_client/tests/headers.o:  $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
@@ -20450,10 +20526,21 @@ INITIAL_SETTINGS_FRAME_BAD_CLIENT_TEST_SRC = \
 INITIAL_SETTINGS_FRAME_BAD_CLIENT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(INITIAL_SETTINGS_FRAME_BAD_CLIENT_TEST_SRC))))
 
 
-$(BINDIR)/$(CONFIG)/initial_settings_frame_bad_client_test: $(INITIAL_SETTINGS_FRAME_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/initial_settings_frame_bad_client_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/initial_settings_frame_bad_client_test: $(PROTOBUF_DEP) $(INITIAL_SETTINGS_FRAME_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(INITIAL_SETTINGS_FRAME_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/initial_settings_frame_bad_client_test
+	$(Q) $(LDXX) $(LDFLAGS) $(INITIAL_SETTINGS_FRAME_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/initial_settings_frame_bad_client_test
+
+endif
 
 $(OBJDIR)/$(CONFIG)/test/core/bad_client/tests/initial_settings_frame.o:  $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
@@ -20470,10 +20557,21 @@ LARGE_METADATA_BAD_CLIENT_TEST_SRC = \
 LARGE_METADATA_BAD_CLIENT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LARGE_METADATA_BAD_CLIENT_TEST_SRC))))
 
 
-$(BINDIR)/$(CONFIG)/large_metadata_bad_client_test: $(LARGE_METADATA_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/large_metadata_bad_client_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/large_metadata_bad_client_test: $(PROTOBUF_DEP) $(LARGE_METADATA_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(LARGE_METADATA_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/large_metadata_bad_client_test
+	$(Q) $(LDXX) $(LDFLAGS) $(LARGE_METADATA_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/large_metadata_bad_client_test
+
+endif
 
 $(OBJDIR)/$(CONFIG)/test/core/bad_client/tests/large_metadata.o:  $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
@@ -20490,10 +20588,21 @@ SERVER_REGISTERED_METHOD_BAD_CLIENT_TEST_SRC = \
 SERVER_REGISTERED_METHOD_BAD_CLIENT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SERVER_REGISTERED_METHOD_BAD_CLIENT_TEST_SRC))))
 
 
-$(BINDIR)/$(CONFIG)/server_registered_method_bad_client_test: $(SERVER_REGISTERED_METHOD_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/server_registered_method_bad_client_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/server_registered_method_bad_client_test: $(PROTOBUF_DEP) $(SERVER_REGISTERED_METHOD_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(SERVER_REGISTERED_METHOD_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/server_registered_method_bad_client_test
+	$(Q) $(LDXX) $(LDFLAGS) $(SERVER_REGISTERED_METHOD_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/server_registered_method_bad_client_test
+
+endif
 
 $(OBJDIR)/$(CONFIG)/test/core/bad_client/tests/server_registered_method.o:  $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
@@ -20510,10 +20619,21 @@ SIMPLE_REQUEST_BAD_CLIENT_TEST_SRC = \
 SIMPLE_REQUEST_BAD_CLIENT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SIMPLE_REQUEST_BAD_CLIENT_TEST_SRC))))
 
 
-$(BINDIR)/$(CONFIG)/simple_request_bad_client_test: $(SIMPLE_REQUEST_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/simple_request_bad_client_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/simple_request_bad_client_test: $(PROTOBUF_DEP) $(SIMPLE_REQUEST_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(SIMPLE_REQUEST_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/simple_request_bad_client_test
+	$(Q) $(LDXX) $(LDFLAGS) $(SIMPLE_REQUEST_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/simple_request_bad_client_test
+
+endif
 
 $(OBJDIR)/$(CONFIG)/test/core/bad_client/tests/simple_request.o:  $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
@@ -20530,10 +20650,21 @@ UNKNOWN_FRAME_BAD_CLIENT_TEST_SRC = \
 UNKNOWN_FRAME_BAD_CLIENT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(UNKNOWN_FRAME_BAD_CLIENT_TEST_SRC))))
 
 
-$(BINDIR)/$(CONFIG)/unknown_frame_bad_client_test: $(UNKNOWN_FRAME_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/unknown_frame_bad_client_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/unknown_frame_bad_client_test: $(PROTOBUF_DEP) $(UNKNOWN_FRAME_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(UNKNOWN_FRAME_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/unknown_frame_bad_client_test
+	$(Q) $(LDXX) $(LDFLAGS) $(UNKNOWN_FRAME_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/unknown_frame_bad_client_test
+
+endif
 
 $(OBJDIR)/$(CONFIG)/test/core/bad_client/tests/unknown_frame.o:  $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
@@ -20550,10 +20681,21 @@ WINDOW_OVERFLOW_BAD_CLIENT_TEST_SRC = \
 WINDOW_OVERFLOW_BAD_CLIENT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(WINDOW_OVERFLOW_BAD_CLIENT_TEST_SRC))))
 
 
-$(BINDIR)/$(CONFIG)/window_overflow_bad_client_test: $(WINDOW_OVERFLOW_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/window_overflow_bad_client_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/window_overflow_bad_client_test: $(PROTOBUF_DEP) $(WINDOW_OVERFLOW_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(WINDOW_OVERFLOW_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/window_overflow_bad_client_test
+	$(Q) $(LDXX) $(LDFLAGS) $(WINDOW_OVERFLOW_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/window_overflow_bad_client_test
+
+endif
 
 $(OBJDIR)/$(CONFIG)/test/core/bad_client/tests/window_overflow.o:  $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr.a
 

+ 4 - 4
doc/environment_variables.md

@@ -39,7 +39,6 @@ some configuration as environment variables that can be set.
   gRPC C core is processing requests via debug logs. Available tracers include:
   - api - traces api calls to the C core
   - bdp_estimator - traces behavior of bdp estimation logic
-  - call_combiner - traces call combiner state
   - call_error - traces the possible errors contributing to final call status
   - cares_resolver - traces operations of the c-ares based DNS resolver
   - cares_address_sorting - traces operations of the c-ares based DNS
@@ -52,9 +51,6 @@ some configuration as environment variables that can be set.
   - connectivity_state - traces connectivity state changes to channels
   - cronet - traces state in the cronet transport engine
   - executor - traces grpc's internal thread pool ('the executor')
-  - fd_trace - traces fd create(), shutdown() and close() calls for channel fds.
-    Also traces epoll fd create()/close() calls in epollex polling engine
-    traces epoll-fd creation/close calls for epollex polling engine
   - glb - traces the grpclb load balancer
   - handshaker - traces handshaking state
   - health_check_client - traces health checking client code
@@ -86,7 +82,11 @@ some configuration as environment variables that can be set.
   - alarm_refcount - refcounting traces for grpc_alarm structure
   - metadata - tracks creation and mutation of metadata
   - combiner - traces combiner lock state
+  - call_combiner - traces call combiner state
   - closure - tracks closure creation, scheduling, and completion
+  - fd_trace - traces fd create(), shutdown() and close() calls for channel fds.
+    Also traces epoll fd create()/close() calls in epollex polling engine
+    traces epoll-fd creation/close calls for epollex polling engine
   - pending_tags - traces still-in-progress tags on completion queues
   - polling - traces the selected polling engine
   - polling_api - traces the api calls to polling engine

+ 0 - 5
include/grpcpp/impl/codegen/call_op_set.h

@@ -188,11 +188,6 @@ class WriteOptions {
   /// \sa GRPC_WRITE_LAST_MESSAGE
   bool is_last_message() const { return last_message_; }
 
-  WriteOptions& operator=(const WriteOptions& rhs) {
-    flags_ = rhs.flags_;
-    return *this;
-  }
-
  private:
   void SetBit(const uint32_t mask) { flags_ |= mask; }
 

+ 8 - 3
include/grpcpp/impl/codegen/server_callback.h

@@ -348,7 +348,8 @@ class ServerBidiReactor : public internal::ServerReactor {
 
  private:
   friend class ServerCallbackReaderWriter<Request, Response>;
-  void BindStream(ServerCallbackReaderWriter<Request, Response>* stream) {
+  virtual void BindStream(
+      ServerCallbackReaderWriter<Request, Response>* stream) {
     stream_ = stream;
   }
 
@@ -382,7 +383,9 @@ class ServerReadReactor : public internal::ServerReactor {
 
  private:
   friend class ServerCallbackReader<Request>;
-  void BindReader(ServerCallbackReader<Request>* reader) { reader_ = reader; }
+  virtual void BindReader(ServerCallbackReader<Request>* reader) {
+    reader_ = reader;
+  }
 
   ServerCallbackReader<Request>* reader_;
 };
@@ -424,7 +427,9 @@ class ServerWriteReactor : public internal::ServerReactor {
 
  private:
   friend class ServerCallbackWriter<Response>;
-  void BindWriter(ServerCallbackWriter<Response>* writer) { writer_ = writer; }
+  virtual void BindWriter(ServerCallbackWriter<Response>* writer) {
+    writer_ = writer;
+  }
 
   ServerCallbackWriter<Response>* writer_;
 };

+ 328 - 106
src/core/ext/filters/client_channel/client_channel.cc

@@ -147,6 +147,9 @@ class ChannelData {
     return service_config_;
   }
 
+  RefCountedPtr<ConnectedSubchannel> GetConnectedSubchannelInDataPlane(
+      SubchannelInterface* subchannel) const;
+
   grpc_connectivity_state CheckConnectivityState(bool try_to_connect);
   void AddExternalConnectivityWatcher(grpc_polling_entity pollent,
                                       grpc_connectivity_state* state,
@@ -161,9 +164,9 @@ class ChannelData {
   }
 
  private:
+  class SubchannelWrapper;
   class ConnectivityStateAndPickerSetter;
   class ServiceConfigSetter;
-  class GrpcSubchannel;
   class ClientChannelControlHelper;
 
   class ExternalConnectivityWatcher {
@@ -262,7 +265,14 @@ class ChannelData {
   UniquePtr<char> health_check_service_name_;
   RefCountedPtr<ServiceConfig> saved_service_config_;
   bool received_first_resolver_result_ = false;
+  // The number of SubchannelWrapper instances referencing a given Subchannel.
   Map<Subchannel*, int> subchannel_refcount_map_;
+  // Pending ConnectedSubchannel updates for each SubchannelWrapper.
+  // Updates are queued here in the control plane combiner and then applied
+  // in the data plane combiner when the picker is updated.
+  Map<RefCountedPtr<SubchannelWrapper>, RefCountedPtr<ConnectedSubchannel>,
+      RefCountedPtrLess<SubchannelWrapper>>
+      pending_subchannel_updates_;
 
   //
   // Fields accessed from both data plane and control plane combiners.
@@ -706,6 +716,247 @@ class CallData {
   grpc_metadata_batch send_trailing_metadata_;
 };
 
+//
+// ChannelData::SubchannelWrapper
+//
+
+// This class is a wrapper for Subchannel that hides details of the
+// channel's implementation (such as the health check service name and
+// connected subchannel) from the LB policy API.
+//
+// Note that no synchronization is needed here, because even if the
+// underlying subchannel is shared between channels, this wrapper will only
+// be used within one channel, so it will always be synchronized by the
+// control plane combiner.
+class ChannelData::SubchannelWrapper : public SubchannelInterface {
+ public:
+  SubchannelWrapper(ChannelData* chand, Subchannel* subchannel,
+                    UniquePtr<char> health_check_service_name)
+      : SubchannelInterface(&grpc_client_channel_routing_trace),
+        chand_(chand),
+        subchannel_(subchannel),
+        health_check_service_name_(std::move(health_check_service_name)) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
+      gpr_log(GPR_INFO,
+              "chand=%p: creating subchannel wrapper %p for subchannel %p",
+              chand, this, subchannel_);
+    }
+    GRPC_CHANNEL_STACK_REF(chand_->owning_stack_, "SubchannelWrapper");
+    auto* subchannel_node = subchannel_->channelz_node();
+    if (subchannel_node != nullptr) {
+      intptr_t subchannel_uuid = subchannel_node->uuid();
+      auto it = chand_->subchannel_refcount_map_.find(subchannel_);
+      if (it == chand_->subchannel_refcount_map_.end()) {
+        chand_->channelz_node_->AddChildSubchannel(subchannel_uuid);
+        it = chand_->subchannel_refcount_map_.emplace(subchannel_, 0).first;
+      }
+      ++it->second;
+    }
+  }
+
+  ~SubchannelWrapper() {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
+      gpr_log(GPR_INFO,
+              "chand=%p: destroying subchannel wrapper %p for subchannel %p",
+              chand_, this, subchannel_);
+    }
+    auto* subchannel_node = subchannel_->channelz_node();
+    if (subchannel_node != nullptr) {
+      intptr_t subchannel_uuid = subchannel_node->uuid();
+      auto it = chand_->subchannel_refcount_map_.find(subchannel_);
+      GPR_ASSERT(it != chand_->subchannel_refcount_map_.end());
+      --it->second;
+      if (it->second == 0) {
+        chand_->channelz_node_->RemoveChildSubchannel(subchannel_uuid);
+        chand_->subchannel_refcount_map_.erase(it);
+      }
+    }
+    GRPC_SUBCHANNEL_UNREF(subchannel_, "unref from LB");
+    GRPC_CHANNEL_STACK_UNREF(chand_->owning_stack_, "SubchannelWrapper");
+  }
+
+  grpc_connectivity_state CheckConnectivityState() override {
+    RefCountedPtr<ConnectedSubchannel> connected_subchannel;
+    grpc_connectivity_state connectivity_state =
+        subchannel_->CheckConnectivityState(health_check_service_name_.get(),
+                                            &connected_subchannel);
+    MaybeUpdateConnectedSubchannel(std::move(connected_subchannel));
+    return connectivity_state;
+  }
+
+  void WatchConnectivityState(
+      grpc_connectivity_state initial_state,
+      UniquePtr<ConnectivityStateWatcherInterface> watcher) override {
+    auto& watcher_wrapper = watcher_map_[watcher.get()];
+    GPR_ASSERT(watcher_wrapper == nullptr);
+    watcher_wrapper = New<WatcherWrapper>(
+        std::move(watcher), Ref(DEBUG_LOCATION, "WatcherWrapper"));
+    subchannel_->WatchConnectivityState(
+        initial_state,
+        UniquePtr<char>(gpr_strdup(health_check_service_name_.get())),
+        OrphanablePtr<Subchannel::ConnectivityStateWatcherInterface>(
+            watcher_wrapper));
+  }
+
+  void CancelConnectivityStateWatch(
+      ConnectivityStateWatcherInterface* watcher) override {
+    auto it = watcher_map_.find(watcher);
+    GPR_ASSERT(it != watcher_map_.end());
+    subchannel_->CancelConnectivityStateWatch(health_check_service_name_.get(),
+                                              it->second);
+    watcher_map_.erase(it);
+  }
+
+  void AttemptToConnect() override { subchannel_->AttemptToConnect(); }
+
+  void ResetBackoff() override { subchannel_->ResetBackoff(); }
+
+  const grpc_channel_args* channel_args() override {
+    return subchannel_->channel_args();
+  }
+
+  // Caller must be holding the control-plane combiner.
+  ConnectedSubchannel* connected_subchannel() const {
+    return connected_subchannel_.get();
+  }
+
+  // Caller must be holding the data-plane combiner.
+  ConnectedSubchannel* connected_subchannel_in_data_plane() const {
+    return connected_subchannel_in_data_plane_.get();
+  }
+  void set_connected_subchannel_in_data_plane(
+      RefCountedPtr<ConnectedSubchannel> connected_subchannel) {
+    connected_subchannel_in_data_plane_ = std::move(connected_subchannel);
+  }
+
+ private:
+  // Subchannel and SubchannelInterface have different interfaces for
+  // their respective ConnectivityStateWatcherInterface classes.
+  // The one in Subchannel updates the ConnectedSubchannel along with
+  // the state, whereas the one in SubchannelInterface does not expose
+  // the ConnectedSubchannel.
+  //
+  // This wrapper provides a bridge between the two.  It implements
+  // Subchannel::ConnectivityStateWatcherInterface and wraps
+  // the instance of SubchannelInterface::ConnectivityStateWatcherInterface
+  // that was passed in by the LB policy.  We pass an instance of this
+  // class to the underlying Subchannel, and when we get updates from
+  // the subchannel, we pass those on to the wrapped watcher to return
+  // the update to the LB policy.  This allows us to set the connected
+  // subchannel before passing the result back to the LB policy.
+  class WatcherWrapper : public Subchannel::ConnectivityStateWatcherInterface {
+   public:
+    WatcherWrapper(
+        UniquePtr<SubchannelInterface::ConnectivityStateWatcherInterface>
+            watcher,
+        RefCountedPtr<SubchannelWrapper> parent)
+        : watcher_(std::move(watcher)), parent_(std::move(parent)) {}
+
+    ~WatcherWrapper() { parent_.reset(DEBUG_LOCATION, "WatcherWrapper"); }
+
+    void Orphan() override { Unref(); }
+
+    void OnConnectivityStateChange(
+        grpc_connectivity_state new_state,
+        RefCountedPtr<ConnectedSubchannel> connected_subchannel) override {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
+        gpr_log(GPR_INFO,
+                "chand=%p: connectivity change for subchannel wrapper %p "
+                "subchannel %p (connected_subchannel=%p state=%s); "
+                "hopping into combiner",
+                parent_->chand_, parent_.get(), parent_->subchannel_,
+                connected_subchannel.get(),
+                grpc_connectivity_state_name(new_state));
+      }
+      // Will delete itself.
+      New<Updater>(Ref(), new_state, std::move(connected_subchannel));
+    }
+
+    grpc_pollset_set* interested_parties() override {
+      return watcher_->interested_parties();
+    }
+
+   private:
+    class Updater {
+     public:
+      Updater(RefCountedPtr<WatcherWrapper> parent,
+              grpc_connectivity_state new_state,
+              RefCountedPtr<ConnectedSubchannel> connected_subchannel)
+          : parent_(std::move(parent)),
+            state_(new_state),
+            connected_subchannel_(std::move(connected_subchannel)) {
+        GRPC_CLOSURE_INIT(
+            &closure_, ApplyUpdateInControlPlaneCombiner, this,
+            grpc_combiner_scheduler(parent_->parent_->chand_->combiner_));
+        GRPC_CLOSURE_SCHED(&closure_, GRPC_ERROR_NONE);
+      }
+
+     private:
+      static void ApplyUpdateInControlPlaneCombiner(void* arg,
+                                                    grpc_error* error) {
+        Updater* self = static_cast<Updater*>(arg);
+        if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
+          gpr_log(GPR_INFO,
+                  "chand=%p: processing connectivity change in combiner "
+                  "for subchannel wrapper %p subchannel %p "
+                  "(connected_subchannel=%p state=%s)",
+                  self->parent_->parent_->chand_, self->parent_->parent_.get(),
+                  self->parent_->parent_->subchannel_,
+                  self->connected_subchannel_.get(),
+                  grpc_connectivity_state_name(self->state_));
+        }
+        self->parent_->parent_->MaybeUpdateConnectedSubchannel(
+            std::move(self->connected_subchannel_));
+        self->parent_->watcher_->OnConnectivityStateChange(self->state_);
+        Delete(self);
+      }
+
+      RefCountedPtr<WatcherWrapper> parent_;
+      grpc_connectivity_state state_;
+      RefCountedPtr<ConnectedSubchannel> connected_subchannel_;
+      grpc_closure closure_;
+    };
+
+    UniquePtr<SubchannelInterface::ConnectivityStateWatcherInterface> watcher_;
+    RefCountedPtr<SubchannelWrapper> parent_;
+  };
+
+  void MaybeUpdateConnectedSubchannel(
+      RefCountedPtr<ConnectedSubchannel> connected_subchannel) {
+    // Update the connected subchannel only if the channel is not shutting
+    // down.  This is because once the channel is shutting down, we
+    // ignore picker updates from the LB policy, which means that
+    // ConnectivityStateAndPickerSetter will never process the entries
+    // in chand_->pending_subchannel_updates_.  So we don't want to add
+    // entries there that will never be processed, since that would
+    // leave dangling refs to the channel and prevent its destruction.
+    grpc_error* disconnect_error = chand_->disconnect_error();
+    if (disconnect_error != GRPC_ERROR_NONE) return;
+    // Not shutting down, so do the update.
+    if (connected_subchannel_ != connected_subchannel) {
+      connected_subchannel_ = std::move(connected_subchannel);
+      // Record the new connected subchannel so that it can be updated
+      // in the data plane combiner the next time the picker is updated.
+      chand_->pending_subchannel_updates_[Ref(
+          DEBUG_LOCATION, "ConnectedSubchannelUpdate")] = connected_subchannel_;
+    }
+  }
+
+  ChannelData* chand_;
+  Subchannel* subchannel_;
+  UniquePtr<char> health_check_service_name_;
+  // Maps from the address of the watcher passed to us by the LB policy
+  // to the address of the WrapperWatcher that we passed to the underlying
+  // subchannel.  This is needed so that when the LB policy calls
+  // CancelConnectivityStateWatch() with its watcher, we know the
+  // corresponding WrapperWatcher to cancel on the underlying subchannel.
+  Map<ConnectivityStateWatcherInterface*, WatcherWrapper*> watcher_map_;
+  // To be accessed only in the control plane combiner.
+  RefCountedPtr<ConnectedSubchannel> connected_subchannel_;
+  // To be accessed only in the data plane combiner.
+  RefCountedPtr<ConnectedSubchannel> connected_subchannel_in_data_plane_;
+};
+
 //
 // ChannelData::ConnectivityStateAndPickerSetter
 //
@@ -729,10 +980,13 @@ class ChannelData::ConnectivityStateAndPickerSetter {
           grpc_slice_from_static_string(
               GetChannelConnectivityStateChangeString(state)));
     }
+    // Grab any pending subchannel updates.
+    pending_subchannel_updates_ =
+        std::move(chand_->pending_subchannel_updates_);
     // Bounce into the data plane combiner to reset the picker.
     GRPC_CHANNEL_STACK_REF(chand->owning_stack_,
                            "ConnectivityStateAndPickerSetter");
-    GRPC_CLOSURE_INIT(&closure_, SetPicker, this,
+    GRPC_CLOSURE_INIT(&closure_, SetPickerInDataPlane, this,
                       grpc_combiner_scheduler(chand->data_plane_combiner_));
     GRPC_CLOSURE_SCHED(&closure_, GRPC_ERROR_NONE);
   }
@@ -755,16 +1009,38 @@ class ChannelData::ConnectivityStateAndPickerSetter {
     GPR_UNREACHABLE_CODE(return "UNKNOWN");
   }
 
-  static void SetPicker(void* arg, grpc_error* ignored) {
+  static void SetPickerInDataPlane(void* arg, grpc_error* ignored) {
     auto* self = static_cast<ConnectivityStateAndPickerSetter*>(arg);
-    // Update picker.
-    self->chand_->picker_ = std::move(self->picker_);
+    // Handle subchannel updates.
+    for (auto& p : self->pending_subchannel_updates_) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
+        gpr_log(GPR_INFO,
+                "chand=%p: updating subchannel wrapper %p data plane "
+                "connected_subchannel to %p",
+                self->chand_, p.first.get(), p.second.get());
+      }
+      p.first->set_connected_subchannel_in_data_plane(std::move(p.second));
+    }
+    // Swap out the picker.  We hang on to the old picker so that it can
+    // be deleted in the control-plane combiner, since that's where we need
+    // to unref the subchannel wrappers that are reffed by the picker.
+    self->picker_.swap(self->chand_->picker_);
     // Re-process queued picks.
     for (QueuedPick* pick = self->chand_->queued_picks_; pick != nullptr;
          pick = pick->next) {
       CallData::StartPickLocked(pick->elem, GRPC_ERROR_NONE);
     }
-    // Clean up.
+    // Pop back into the control plane combiner to delete ourself, so
+    // that we make sure to unref subchannel wrappers there.  This
+    // includes both the ones reffed by the old picker (now stored in
+    // self->picker_) and the ones in self->pending_subchannel_updates_.
+    GRPC_CLOSURE_INIT(&self->closure_, CleanUpInControlPlane, self,
+                      grpc_combiner_scheduler(self->chand_->combiner_));
+    GRPC_CLOSURE_SCHED(&self->closure_, GRPC_ERROR_NONE);
+  }
+
+  static void CleanUpInControlPlane(void* arg, grpc_error* ignored) {
+    auto* self = static_cast<ConnectivityStateAndPickerSetter*>(arg);
     GRPC_CHANNEL_STACK_UNREF(self->chand_->owning_stack_,
                              "ConnectivityStateAndPickerSetter");
     Delete(self);
@@ -772,6 +1048,9 @@ class ChannelData::ConnectivityStateAndPickerSetter {
 
   ChannelData* chand_;
   UniquePtr<LoadBalancingPolicy::SubchannelPicker> picker_;
+  Map<RefCountedPtr<SubchannelWrapper>, RefCountedPtr<ConnectedSubchannel>,
+      RefCountedPtrLess<SubchannelWrapper>>
+      pending_subchannel_updates_;
   grpc_closure closure_;
 };
 
@@ -946,89 +1225,6 @@ void ChannelData::ExternalConnectivityWatcher::WatchConnectivityStateLocked(
       &self->chand_->state_tracker_, self->state_, &self->my_closure_);
 }
 
-//
-// ChannelData::GrpcSubchannel
-//
-
-// This class is a wrapper for Subchannel that hides details of the
-// channel's implementation (such as the health check service name) from
-// the LB policy API.
-//
-// Note that no synchronization is needed here, because even if the
-// underlying subchannel is shared between channels, this wrapper will only
-// be used within one channel, so it will always be synchronized by the
-// control plane combiner.
-class ChannelData::GrpcSubchannel : public SubchannelInterface {
- public:
-  GrpcSubchannel(ChannelData* chand, Subchannel* subchannel,
-                 UniquePtr<char> health_check_service_name)
-      : chand_(chand),
-        subchannel_(subchannel),
-        health_check_service_name_(std::move(health_check_service_name)) {
-    GRPC_CHANNEL_STACK_REF(chand_->owning_stack_, "GrpcSubchannel");
-    auto* subchannel_node = subchannel_->channelz_node();
-    if (subchannel_node != nullptr) {
-      intptr_t subchannel_uuid = subchannel_node->uuid();
-      auto it = chand_->subchannel_refcount_map_.find(subchannel_);
-      if (it == chand_->subchannel_refcount_map_.end()) {
-        chand_->channelz_node_->AddChildSubchannel(subchannel_uuid);
-        it = chand_->subchannel_refcount_map_.emplace(subchannel_, 0).first;
-      }
-      ++it->second;
-    }
-  }
-
-  ~GrpcSubchannel() {
-    auto* subchannel_node = subchannel_->channelz_node();
-    if (subchannel_node != nullptr) {
-      intptr_t subchannel_uuid = subchannel_node->uuid();
-      auto it = chand_->subchannel_refcount_map_.find(subchannel_);
-      GPR_ASSERT(it != chand_->subchannel_refcount_map_.end());
-      --it->second;
-      if (it->second == 0) {
-        chand_->channelz_node_->RemoveChildSubchannel(subchannel_uuid);
-        chand_->subchannel_refcount_map_.erase(it);
-      }
-    }
-    GRPC_SUBCHANNEL_UNREF(subchannel_, "unref from LB");
-    GRPC_CHANNEL_STACK_UNREF(chand_->owning_stack_, "GrpcSubchannel");
-  }
-
-  grpc_connectivity_state CheckConnectivityState(
-      RefCountedPtr<ConnectedSubchannelInterface>* connected_subchannel)
-      override {
-    RefCountedPtr<ConnectedSubchannel> tmp;
-    auto retval = subchannel_->CheckConnectivityState(
-        health_check_service_name_.get(), &tmp);
-    *connected_subchannel = std::move(tmp);
-    return retval;
-  }
-
-  void WatchConnectivityState(
-      grpc_connectivity_state initial_state,
-      UniquePtr<ConnectivityStateWatcher> watcher) override {
-    subchannel_->WatchConnectivityState(
-        initial_state,
-        UniquePtr<char>(gpr_strdup(health_check_service_name_.get())),
-        std::move(watcher));
-  }
-
-  void CancelConnectivityStateWatch(
-      ConnectivityStateWatcher* watcher) override {
-    subchannel_->CancelConnectivityStateWatch(health_check_service_name_.get(),
-                                              watcher);
-  }
-
-  void AttemptToConnect() override { subchannel_->AttemptToConnect(); }
-
-  void ResetBackoff() override { subchannel_->ResetBackoff(); }
-
- private:
-  ChannelData* chand_;
-  Subchannel* subchannel_;
-  UniquePtr<char> health_check_service_name_;
-};
-
 //
 // ChannelData::ClientChannelControlHelper
 //
@@ -1066,8 +1262,8 @@ class ChannelData::ClientChannelControlHelper
         chand_->client_channel_factory_->CreateSubchannel(new_args);
     grpc_channel_args_destroy(new_args);
     if (subchannel == nullptr) return nullptr;
-    return MakeRefCounted<GrpcSubchannel>(chand_, subchannel,
-                                          std::move(health_check_service_name));
+    return MakeRefCounted<SubchannelWrapper>(
+        chand_, subchannel, std::move(health_check_service_name));
   }
 
   grpc_channel* CreateChannel(const char* target,
@@ -1078,8 +1274,7 @@ class ChannelData::ClientChannelControlHelper
   void UpdateState(
       grpc_connectivity_state state,
       UniquePtr<LoadBalancingPolicy::SubchannelPicker> picker) override {
-    grpc_error* disconnect_error =
-        chand_->disconnect_error_.Load(MemoryOrder::ACQUIRE);
+    grpc_error* disconnect_error = chand_->disconnect_error();
     if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
       const char* extra = disconnect_error == GRPC_ERROR_NONE
                               ? ""
@@ -1180,6 +1375,10 @@ ChannelData::ChannelData(grpc_channel_element_args* args, grpc_error** error)
       interested_parties_(grpc_pollset_set_create()),
       subchannel_pool_(GetSubchannelPool(args->channel_args)),
       disconnect_error_(GRPC_ERROR_NONE) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
+    gpr_log(GPR_INFO, "chand=%p: creating client_channel for channel stack %p",
+            this, owning_stack_);
+  }
   // Initialize data members.
   grpc_connectivity_state_init(&state_tracker_, GRPC_CHANNEL_IDLE,
                                "client_channel");
@@ -1236,10 +1435,8 @@ ChannelData::ChannelData(grpc_channel_element_args* args, grpc_error** error)
       std::move(target_uri), ProcessResolverResultLocked, this, error));
   grpc_channel_args_destroy(new_args);
   if (*error != GRPC_ERROR_NONE) {
-    // Orphan the resolving LB policy and flush the exec_ctx to ensure
-    // that it finishes shutting down.  This ensures that if we are
-    // failing, we destroy the ClientChannelControlHelper (and thus
-    // unref the channel stack) before we return.
+    // Before we return, shut down the resolving LB policy, which destroys
+    // the ClientChannelControlHelper and therefore unrefs the channel stack.
     // TODO(roth): This is not a complete solution, because it only
     // catches the case where channel stack initialization fails in this
     // particular filter.  If there is a failure in a different filter, we
@@ -1247,7 +1444,6 @@ ChannelData::ChannelData(grpc_channel_element_args* args, grpc_error** error)
     // in practice, there are no other filters that can cause failures in
     // channel stack initialization, so this works for now.
     resolving_lb_policy_.reset();
-    ExecCtx::Get()->Flush();
   } else {
     grpc_pollset_set_add_pollset_set(resolving_lb_policy_->interested_parties(),
                                      interested_parties_);
@@ -1259,6 +1455,9 @@ ChannelData::ChannelData(grpc_channel_element_args* args, grpc_error** error)
 }
 
 ChannelData::~ChannelData() {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
+    gpr_log(GPR_INFO, "chand=%p: destroying channel", this);
+  }
   if (resolving_lb_policy_ != nullptr) {
     grpc_pollset_set_del_pollset_set(resolving_lb_policy_->interested_parties(),
                                      interested_parties_);
@@ -1444,9 +1643,13 @@ grpc_error* ChannelData::DoPingLocked(grpc_transport_op* op) {
   }
   LoadBalancingPolicy::PickResult result =
       picker_->Pick(LoadBalancingPolicy::PickArgs());
-  if (result.connected_subchannel != nullptr) {
-    ConnectedSubchannel* connected_subchannel =
-        static_cast<ConnectedSubchannel*>(result.connected_subchannel.get());
+  ConnectedSubchannel* connected_subchannel = nullptr;
+  if (result.subchannel != nullptr) {
+    SubchannelWrapper* subchannel =
+        static_cast<SubchannelWrapper*>(result.subchannel.get());
+    connected_subchannel = subchannel->connected_subchannel();
+  }
+  if (connected_subchannel != nullptr) {
     connected_subchannel->Ping(op->send_ping.on_initiate, op->send_ping.on_ack);
   } else {
     if (result.error == GRPC_ERROR_NONE) {
@@ -1489,6 +1692,10 @@ void ChannelData::StartTransportOpLocked(void* arg, grpc_error* ignored) {
   }
   // Disconnect.
   if (op->disconnect_with_error != GRPC_ERROR_NONE) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) {
+      gpr_log(GPR_INFO, "chand=%p: channel shut down from API: %s", chand,
+              grpc_error_string(op->disconnect_with_error));
+    }
     grpc_error* error = GRPC_ERROR_NONE;
     GPR_ASSERT(chand->disconnect_error_.CompareExchangeStrong(
         &error, op->disconnect_with_error, MemoryOrder::ACQ_REL,
@@ -1563,6 +1770,17 @@ void ChannelData::RemoveQueuedPick(QueuedPick* to_remove,
   }
 }
 
+RefCountedPtr<ConnectedSubchannel>
+ChannelData::GetConnectedSubchannelInDataPlane(
+    SubchannelInterface* subchannel) const {
+  SubchannelWrapper* subchannel_wrapper =
+      static_cast<SubchannelWrapper*>(subchannel);
+  ConnectedSubchannel* connected_subchannel =
+      subchannel_wrapper->connected_subchannel_in_data_plane();
+  if (connected_subchannel == nullptr) return nullptr;
+  return connected_subchannel->Ref();
+}
+
 void ChannelData::TryToConnectLocked(void* arg, grpc_error* error_ignored) {
   auto* chand = static_cast<ChannelData*>(arg);
   if (chand->resolving_lb_policy_ != nullptr) {
@@ -3490,10 +3708,9 @@ void CallData::StartPickLocked(void* arg, grpc_error* error) {
   auto result = chand->picker()->Pick(pick_args);
   if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_routing_trace)) {
     gpr_log(GPR_INFO,
-            "chand=%p calld=%p: LB pick returned %s (connected_subchannel=%p, "
-            "error=%s)",
+            "chand=%p calld=%p: LB pick returned %s (subchannel=%p, error=%s)",
             chand, calld, PickResultTypeName(result.type),
-            result.connected_subchannel.get(), grpc_error_string(result.error));
+            result.subchannel.get(), grpc_error_string(result.error));
   }
   switch (result.type) {
     case LoadBalancingPolicy::PickResult::PICK_TRANSIENT_FAILURE: {
@@ -3535,11 +3752,16 @@ void CallData::StartPickLocked(void* arg, grpc_error* error) {
       break;
     default:  // PICK_COMPLETE
       // Handle drops.
-      if (GPR_UNLIKELY(result.connected_subchannel == nullptr)) {
+      if (GPR_UNLIKELY(result.subchannel == nullptr)) {
         result.error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
             "Call dropped by load balancing policy");
+      } else {
+        // Grab a ref to the connected subchannel while we're still
+        // holding the data plane combiner.
+        calld->connected_subchannel_ =
+            chand->GetConnectedSubchannelInDataPlane(result.subchannel.get());
+        GPR_ASSERT(calld->connected_subchannel_ != nullptr);
       }
-      calld->connected_subchannel_ = std::move(result.connected_subchannel);
       calld->lb_recv_trailing_metadata_ready_ =
           result.recv_trailing_metadata_ready;
       calld->lb_recv_trailing_metadata_ready_user_data_ =

+ 2 - 17
src/core/ext/filters/client_channel/lb_policy.cc

@@ -43,23 +43,8 @@ LoadBalancingPolicy::~LoadBalancingPolicy() {
 }
 
 void LoadBalancingPolicy::Orphan() {
-  // Invoke ShutdownAndUnrefLocked() inside of the combiner.
-  // TODO(roth): Is this actually needed?  We should already be in the
-  // combiner here.  Note that if we directly call ShutdownLocked(),
-  // then we can probably remove the hack whereby the helper is
-  // destroyed at shutdown instead of at destruction.
-  GRPC_CLOSURE_SCHED(
-      GRPC_CLOSURE_CREATE(&LoadBalancingPolicy::ShutdownAndUnrefLocked, this,
-                          grpc_combiner_scheduler(combiner_)),
-      GRPC_ERROR_NONE);
-}
-
-void LoadBalancingPolicy::ShutdownAndUnrefLocked(void* arg,
-                                                 grpc_error* ignored) {
-  LoadBalancingPolicy* policy = static_cast<LoadBalancingPolicy*>(arg);
-  policy->ShutdownLocked();
-  policy->channel_control_helper_.reset();
-  policy->Unref();
+  ShutdownLocked();
+  Unref();
 }
 
 //

+ 4 - 4
src/core/ext/filters/client_channel/lb_policy.h

@@ -128,7 +128,7 @@ class LoadBalancingPolicy : public InternallyRefCounted<LoadBalancingPolicy> {
 
     /// Used only if type is PICK_COMPLETE.  Will be set to the selected
     /// subchannel, or nullptr if the LB policy decides to drop the call.
-    RefCountedPtr<ConnectedSubchannelInterface> connected_subchannel;
+    RefCountedPtr<SubchannelInterface> subchannel;
 
     /// Used only if type is PICK_TRANSIENT_FAILURE.
     /// Error to be set when returning a transient failure.
@@ -282,6 +282,7 @@ class LoadBalancingPolicy : public InternallyRefCounted<LoadBalancingPolicy> {
 
   grpc_pollset_set* interested_parties() const { return interested_parties_; }
 
+  // Note: This must be invoked while holding the combiner.
   void Orphan() override;
 
   // A picker that returns PICK_QUEUE for all picks.
@@ -292,6 +293,8 @@ class LoadBalancingPolicy : public InternallyRefCounted<LoadBalancingPolicy> {
     explicit QueuePicker(RefCountedPtr<LoadBalancingPolicy> parent)
         : parent_(std::move(parent)) {}
 
+    ~QueuePicker() { parent_.reset(DEBUG_LOCATION, "QueuePicker"); }
+
     PickResult Pick(PickArgs args) override;
 
    private:
@@ -320,7 +323,6 @@ class LoadBalancingPolicy : public InternallyRefCounted<LoadBalancingPolicy> {
 
   // Note: LB policies MUST NOT call any method on the helper from their
   // constructor.
-  // Note: This will return null after ShutdownLocked() has been called.
   ChannelControlHelper* channel_control_helper() const {
     return channel_control_helper_.get();
   }
@@ -329,8 +331,6 @@ class LoadBalancingPolicy : public InternallyRefCounted<LoadBalancingPolicy> {
   virtual void ShutdownLocked() GRPC_ABSTRACT;
 
  private:
-  static void ShutdownAndUnrefLocked(void* arg, grpc_error* ignored);
-
   /// Combiner under which LB policy actions take place.
   grpc_combiner* combiner_;
   /// Owned pointer to interested parties in load balancing decisions.

+ 4 - 5
src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc

@@ -575,13 +575,12 @@ GrpcLb::PickResult GrpcLb::Picker::Pick(PickArgs args) {
   result = child_picker_->Pick(args);
   // If pick succeeded, add LB token to initial metadata.
   if (result.type == PickResult::PICK_COMPLETE &&
-      result.connected_subchannel != nullptr) {
+      result.subchannel != nullptr) {
     const grpc_arg* arg = grpc_channel_args_find(
-        result.connected_subchannel->args(), GRPC_ARG_GRPCLB_ADDRESS_LB_TOKEN);
+        result.subchannel->channel_args(), GRPC_ARG_GRPCLB_ADDRESS_LB_TOKEN);
     if (arg == nullptr) {
-      gpr_log(GPR_ERROR,
-              "[grpclb %p picker %p] No LB token for connected subchannel %p",
-              parent_, this, result.connected_subchannel.get());
+      gpr_log(GPR_ERROR, "[grpclb %p picker %p] No LB token for subchannel %p",
+              parent_, this, result.subchannel.get());
       abort();
     }
     grpc_mdelem lb_token = {reinterpret_cast<uintptr_t>(arg->value.pointer.p)};

+ 23 - 21
src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc

@@ -28,7 +28,6 @@
 #include "src/core/ext/filters/client_channel/subchannel.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/gprpp/sync.h"
-#include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/transport/connectivity_state.h"
 
@@ -85,9 +84,8 @@ class PickFirst : public LoadBalancingPolicy {
    public:
     PickFirstSubchannelList(PickFirst* policy, TraceFlag* tracer,
                             const ServerAddressList& addresses,
-                            grpc_combiner* combiner,
                             const grpc_channel_args& args)
-        : SubchannelList(policy, tracer, addresses, combiner,
+        : SubchannelList(policy, tracer, addresses,
                          policy->channel_control_helper(), args) {
       // Need to maintain a ref to the LB policy as long as we maintain
       // any references to subchannels, since the subchannels'
@@ -111,19 +109,18 @@ class PickFirst : public LoadBalancingPolicy {
 
   class Picker : public SubchannelPicker {
    public:
-    explicit Picker(
-        RefCountedPtr<ConnectedSubchannelInterface> connected_subchannel)
-        : connected_subchannel_(std::move(connected_subchannel)) {}
+    explicit Picker(RefCountedPtr<SubchannelInterface> subchannel)
+        : subchannel_(std::move(subchannel)) {}
 
     PickResult Pick(PickArgs args) override {
       PickResult result;
       result.type = PickResult::PICK_COMPLETE;
-      result.connected_subchannel = connected_subchannel_;
+      result.subchannel = subchannel_;
       return result;
     }
 
    private:
-    RefCountedPtr<ConnectedSubchannelInterface> connected_subchannel_;
+    RefCountedPtr<SubchannelInterface> subchannel_;
   };
 
   void ShutdownLocked() override;
@@ -166,6 +163,9 @@ void PickFirst::ShutdownLocked() {
 void PickFirst::ExitIdleLocked() {
   if (shutdown_) return;
   if (idle_) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_pick_first_trace)) {
+      gpr_log(GPR_INFO, "Pick First %p exiting idle", this);
+    }
     idle_ = false;
     if (subchannel_list_ == nullptr ||
         subchannel_list_->num_subchannels() == 0) {
@@ -200,7 +200,7 @@ void PickFirst::UpdateLocked(UpdateArgs args) {
   grpc_channel_args* new_args =
       grpc_channel_args_copy_and_add(args.args, &new_arg, 1);
   auto subchannel_list = MakeOrphanable<PickFirstSubchannelList>(
-      this, &grpc_lb_pick_first_trace, args.addresses, combiner(), *new_args);
+      this, &grpc_lb_pick_first_trace, args.addresses, *new_args);
   grpc_channel_args_destroy(new_args);
   if (subchannel_list->num_subchannels() == 0) {
     // Empty update or no valid subchannels. Unsubscribe from all current
@@ -329,7 +329,8 @@ void PickFirst::PickFirstSubchannelData::ProcessConnectivityChangeLocked(
       } else {
         p->channel_control_helper()->UpdateState(
             GRPC_CHANNEL_CONNECTING,
-            UniquePtr<SubchannelPicker>(New<QueuePicker>(p->Ref())));
+            UniquePtr<SubchannelPicker>(
+                New<QueuePicker>(p->Ref(DEBUG_LOCATION, "QueuePicker"))));
       }
     } else {
       if (connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
@@ -342,20 +343,20 @@ void PickFirst::PickFirstSubchannelData::ProcessConnectivityChangeLocked(
         p->selected_ = nullptr;
         CancelConnectivityWatchLocked("selected subchannel failed; going IDLE");
         p->channel_control_helper()->UpdateState(
-            GRPC_CHANNEL_IDLE,
-            UniquePtr<SubchannelPicker>(New<QueuePicker>(p->Ref())));
+            GRPC_CHANNEL_IDLE, UniquePtr<SubchannelPicker>(New<QueuePicker>(
+                                   p->Ref(DEBUG_LOCATION, "QueuePicker"))));
       } else {
         // This is unlikely but can happen when a subchannel has been asked
         // to reconnect by a different channel and this channel has dropped
         // some connectivity state notifications.
         if (connectivity_state == GRPC_CHANNEL_READY) {
           p->channel_control_helper()->UpdateState(
-              GRPC_CHANNEL_READY, UniquePtr<SubchannelPicker>(New<Picker>(
-                                      connected_subchannel()->Ref())));
+              GRPC_CHANNEL_READY,
+              UniquePtr<SubchannelPicker>(New<Picker>(subchannel()->Ref())));
         } else {  // CONNECTING
           p->channel_control_helper()->UpdateState(
-              connectivity_state,
-              UniquePtr<SubchannelPicker>(New<QueuePicker>(p->Ref())));
+              connectivity_state, UniquePtr<SubchannelPicker>(New<QueuePicker>(
+                                      p->Ref(DEBUG_LOCATION, "QueuePicker"))));
         }
       }
     }
@@ -411,7 +412,8 @@ void PickFirst::PickFirstSubchannelData::ProcessConnectivityChangeLocked(
       if (subchannel_list() == p->subchannel_list_.get()) {
         p->channel_control_helper()->UpdateState(
             GRPC_CHANNEL_CONNECTING,
-            UniquePtr<SubchannelPicker>(New<QueuePicker>(p->Ref())));
+            UniquePtr<SubchannelPicker>(
+                New<QueuePicker>(p->Ref(DEBUG_LOCATION, "QueuePicker"))));
       }
       break;
     }
@@ -445,13 +447,13 @@ void PickFirst::PickFirstSubchannelData::ProcessUnselectedReadyLocked() {
     p->subchannel_list_ = std::move(p->latest_pending_subchannel_list_);
   }
   // Cases 1 and 2.
-  p->selected_ = this;
-  p->channel_control_helper()->UpdateState(
-      GRPC_CHANNEL_READY,
-      UniquePtr<SubchannelPicker>(New<Picker>(connected_subchannel()->Ref())));
   if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_pick_first_trace)) {
     gpr_log(GPR_INFO, "Pick First %p selected subchannel %p", p, subchannel());
   }
+  p->selected_ = this;
+  p->channel_control_helper()->UpdateState(
+      GRPC_CHANNEL_READY,
+      UniquePtr<SubchannelPicker>(New<Picker>(subchannel()->Ref())));
 }
 
 void PickFirst::PickFirstSubchannelData::

+ 10 - 14
src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc

@@ -38,7 +38,6 @@
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
 #include "src/core/lib/gprpp/sync.h"
-#include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/transport/connectivity_state.h"
 #include "src/core/lib/transport/static_metadata.h"
@@ -106,9 +105,8 @@ class RoundRobin : public LoadBalancingPolicy {
    public:
     RoundRobinSubchannelList(RoundRobin* policy, TraceFlag* tracer,
                              const ServerAddressList& addresses,
-                             grpc_combiner* combiner,
                              const grpc_channel_args& args)
-        : SubchannelList(policy, tracer, addresses, combiner,
+        : SubchannelList(policy, tracer, addresses,
                          policy->channel_control_helper(), args) {
       // Need to maintain a ref to the LB policy as long as we maintain
       // any references to subchannels, since the subchannels'
@@ -155,7 +153,7 @@ class RoundRobin : public LoadBalancingPolicy {
     RoundRobin* parent_;
 
     size_t last_picked_index_;
-    InlinedVector<RefCountedPtr<ConnectedSubchannelInterface>, 10> subchannels_;
+    InlinedVector<RefCountedPtr<SubchannelInterface>, 10> subchannels_;
   };
 
   void ShutdownLocked() override;
@@ -180,10 +178,9 @@ RoundRobin::Picker::Picker(RoundRobin* parent,
                            RoundRobinSubchannelList* subchannel_list)
     : parent_(parent) {
   for (size_t i = 0; i < subchannel_list->num_subchannels(); ++i) {
-    auto* connected_subchannel =
-        subchannel_list->subchannel(i)->connected_subchannel();
-    if (connected_subchannel != nullptr) {
-      subchannels_.push_back(connected_subchannel->Ref());
+    RoundRobinSubchannelData* sd = subchannel_list->subchannel(i);
+    if (sd->connectivity_state() == GRPC_CHANNEL_READY) {
+      subchannels_.push_back(sd->subchannel()->Ref());
     }
   }
   // For discussion on why we generate a random starting index for
@@ -204,14 +201,13 @@ RoundRobin::PickResult RoundRobin::Picker::Pick(PickArgs args) {
   last_picked_index_ = (last_picked_index_ + 1) % subchannels_.size();
   if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_round_robin_trace)) {
     gpr_log(GPR_INFO,
-            "[RR %p picker %p] returning index %" PRIuPTR
-            ", connected_subchannel=%p",
+            "[RR %p picker %p] returning index %" PRIuPTR ", subchannel=%p",
             parent_, this, last_picked_index_,
             subchannels_[last_picked_index_].get());
   }
   PickResult result;
   result.type = PickResult::PICK_COMPLETE;
-  result.connected_subchannel = subchannels_[last_picked_index_];
+  result.subchannel = subchannels_[last_picked_index_];
   return result;
 }
 
@@ -323,8 +319,8 @@ void RoundRobin::RoundRobinSubchannelList::
   } else if (num_connecting_ > 0) {
     /* 2) CONNECTING */
     p->channel_control_helper()->UpdateState(
-        GRPC_CHANNEL_CONNECTING,
-        UniquePtr<SubchannelPicker>(New<QueuePicker>(p->Ref())));
+        GRPC_CHANNEL_CONNECTING, UniquePtr<SubchannelPicker>(New<QueuePicker>(
+                                     p->Ref(DEBUG_LOCATION, "QueuePicker"))));
   } else if (num_transient_failure_ == num_subchannels()) {
     /* 3) TRANSIENT_FAILURE */
     grpc_error* error =
@@ -424,7 +420,7 @@ void RoundRobin::UpdateLocked(UpdateArgs args) {
     }
   }
   latest_pending_subchannel_list_ = MakeOrphanable<RoundRobinSubchannelList>(
-      this, &grpc_lb_round_robin_trace, args.addresses, combiner(), *args.args);
+      this, &grpc_lb_round_robin_trace, args.addresses, *args.args);
   if (latest_pending_subchannel_list_->num_subchannels() == 0) {
     // If the new list is empty, immediately promote the new list to the
     // current list and transition to TRANSIENT_FAILURE.

+ 25 - 101
src/core/ext/filters/client_channel/lb_policy/subchannel_list.h

@@ -39,7 +39,6 @@
 #include "src/core/lib/gprpp/ref_counted.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
 #include "src/core/lib/iomgr/closure.h"
-#include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/transport/connectivity_state.h"
 
@@ -64,8 +63,7 @@ class MySubchannelList
 };
 
 */
-// All methods with a Locked() suffix must be called from within the
-// client_channel combiner.
+// All methods will be called from within the client_channel combiner.
 
 namespace grpc_core {
 
@@ -93,20 +91,13 @@ class SubchannelData {
   // Returns a pointer to the subchannel.
   SubchannelInterface* subchannel() const { return subchannel_.get(); }
 
-  // Returns the connected subchannel.  Will be null if the subchannel
-  // is not connected.
-  ConnectedSubchannelInterface* connected_subchannel() const {
-    return connected_subchannel_.get();
-  }
-
   // Synchronously checks the subchannel's connectivity state.
   // Must not be called while there is a connectivity notification
   // pending (i.e., between calling StartConnectivityWatchLocked() and
   // calling CancelConnectivityWatchLocked()).
   grpc_connectivity_state CheckConnectivityStateLocked() {
     GPR_ASSERT(pending_watcher_ == nullptr);
-    connectivity_state_ =
-        subchannel()->CheckConnectivityState(&connected_subchannel_);
+    connectivity_state_ = subchannel_->CheckConnectivityState();
     return connectivity_state_;
   }
 
@@ -144,7 +135,8 @@ class SubchannelData {
 
  private:
   // Watcher for subchannel connectivity state.
-  class Watcher : public SubchannelInterface::ConnectivityStateWatcher {
+  class Watcher
+      : public SubchannelInterface::ConnectivityStateWatcherInterface {
    public:
     Watcher(
         SubchannelData<SubchannelListType, SubchannelDataType>* subchannel_data,
@@ -154,42 +146,13 @@ class SubchannelData {
 
     ~Watcher() { subchannel_list_.reset(DEBUG_LOCATION, "Watcher dtor"); }
 
-    void OnConnectivityStateChange(grpc_connectivity_state new_state,
-                                   RefCountedPtr<ConnectedSubchannelInterface>
-                                       connected_subchannel) override;
+    void OnConnectivityStateChange(grpc_connectivity_state new_state) override;
 
     grpc_pollset_set* interested_parties() override {
       return subchannel_list_->policy()->interested_parties();
     }
 
    private:
-    // A fire-and-forget class that bounces into the combiner to process
-    // a connectivity state update.
-    class Updater {
-     public:
-      Updater(
-          SubchannelData<SubchannelListType, SubchannelDataType>*
-              subchannel_data,
-          RefCountedPtr<SubchannelList<SubchannelListType, SubchannelDataType>>
-              subchannel_list,
-          grpc_connectivity_state state,
-          RefCountedPtr<ConnectedSubchannelInterface> connected_subchannel);
-
-      ~Updater() {
-        subchannel_list_.reset(DEBUG_LOCATION, "Watcher::Updater dtor");
-      }
-
-     private:
-      static void OnUpdateLocked(void* arg, grpc_error* error);
-
-      SubchannelData<SubchannelListType, SubchannelDataType>* subchannel_data_;
-      RefCountedPtr<SubchannelList<SubchannelListType, SubchannelDataType>>
-          subchannel_list_;
-      const grpc_connectivity_state state_;
-      RefCountedPtr<ConnectedSubchannelInterface> connected_subchannel_;
-      grpc_closure closure_;
-    };
-
     SubchannelData<SubchannelListType, SubchannelDataType>* subchannel_data_;
     RefCountedPtr<SubchannelListType> subchannel_list_;
   };
@@ -202,10 +165,10 @@ class SubchannelData {
   // The subchannel.
   RefCountedPtr<SubchannelInterface> subchannel_;
   // Will be non-null when the subchannel's state is being watched.
-  SubchannelInterface::ConnectivityStateWatcher* pending_watcher_ = nullptr;
+  SubchannelInterface::ConnectivityStateWatcherInterface* pending_watcher_ =
+      nullptr;
   // Data updated by the watcher.
   grpc_connectivity_state connectivity_state_;
-  RefCountedPtr<ConnectedSubchannelInterface> connected_subchannel_;
 };
 
 // A list of subchannels.
@@ -232,7 +195,6 @@ class SubchannelList : public InternallyRefCounted<SubchannelListType> {
   // the backoff code out of subchannels and into LB policies.
   void ResetBackoffLocked();
 
-  // Note: Caller must ensure that this is invoked inside of the combiner.
   void Orphan() override {
     ShutdownLocked();
     InternallyRefCounted<SubchannelListType>::Unref(DEBUG_LOCATION, "shutdown");
@@ -242,7 +204,7 @@ class SubchannelList : public InternallyRefCounted<SubchannelListType> {
 
  protected:
   SubchannelList(LoadBalancingPolicy* policy, TraceFlag* tracer,
-                 const ServerAddressList& addresses, grpc_combiner* combiner,
+                 const ServerAddressList& addresses,
                  LoadBalancingPolicy::ChannelControlHelper* helper,
                  const grpc_channel_args& args);
 
@@ -263,8 +225,6 @@ class SubchannelList : public InternallyRefCounted<SubchannelListType> {
 
   TraceFlag* tracer_;
 
-  grpc_combiner* combiner_;
-
   // The list of subchannels.
   SubchannelVector subchannels_;
 
@@ -284,59 +244,26 @@ class SubchannelList : public InternallyRefCounted<SubchannelListType> {
 
 template <typename SubchannelListType, typename SubchannelDataType>
 void SubchannelData<SubchannelListType, SubchannelDataType>::Watcher::
-    OnConnectivityStateChange(
-        grpc_connectivity_state new_state,
-        RefCountedPtr<ConnectedSubchannelInterface> connected_subchannel) {
-  // Will delete itself.
-  New<Updater>(subchannel_data_,
-               subchannel_list_->Ref(DEBUG_LOCATION, "Watcher::Updater"),
-               new_state, std::move(connected_subchannel));
-}
-
-template <typename SubchannelListType, typename SubchannelDataType>
-SubchannelData<SubchannelListType, SubchannelDataType>::Watcher::Updater::
-    Updater(
-        SubchannelData<SubchannelListType, SubchannelDataType>* subchannel_data,
-        RefCountedPtr<SubchannelList<SubchannelListType, SubchannelDataType>>
-            subchannel_list,
-        grpc_connectivity_state state,
-        RefCountedPtr<ConnectedSubchannelInterface> connected_subchannel)
-    : subchannel_data_(subchannel_data),
-      subchannel_list_(std::move(subchannel_list)),
-      state_(state),
-      connected_subchannel_(std::move(connected_subchannel)) {
-  GRPC_CLOSURE_INIT(&closure_, &OnUpdateLocked, this,
-                    grpc_combiner_scheduler(subchannel_list_->combiner_));
-  GRPC_CLOSURE_SCHED(&closure_, GRPC_ERROR_NONE);
-}
-
-template <typename SubchannelListType, typename SubchannelDataType>
-void SubchannelData<SubchannelListType, SubchannelDataType>::Watcher::Updater::
-    OnUpdateLocked(void* arg, grpc_error* error) {
-  Updater* self = static_cast<Updater*>(arg);
-  SubchannelData* sd = self->subchannel_data_;
-  if (GRPC_TRACE_FLAG_ENABLED(*sd->subchannel_list_->tracer())) {
+    OnConnectivityStateChange(grpc_connectivity_state new_state) {
+  if (GRPC_TRACE_FLAG_ENABLED(*subchannel_list_->tracer())) {
     gpr_log(GPR_INFO,
             "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
             " (subchannel %p): connectivity changed: state=%s, "
-            "connected_subchannel=%p, shutting_down=%d, pending_watcher=%p",
-            sd->subchannel_list_->tracer()->name(),
-            sd->subchannel_list_->policy(), sd->subchannel_list_, sd->Index(),
-            sd->subchannel_list_->num_subchannels(), sd->subchannel_.get(),
-            grpc_connectivity_state_name(self->state_),
-            self->connected_subchannel_.get(),
-            sd->subchannel_list_->shutting_down(), sd->pending_watcher_);
+            "shutting_down=%d, pending_watcher=%p",
+            subchannel_list_->tracer()->name(), subchannel_list_->policy(),
+            subchannel_list_.get(), subchannel_data_->Index(),
+            subchannel_list_->num_subchannels(),
+            subchannel_data_->subchannel_.get(),
+            grpc_connectivity_state_name(new_state),
+            subchannel_list_->shutting_down(),
+            subchannel_data_->pending_watcher_);
   }
-  if (!sd->subchannel_list_->shutting_down() &&
-      sd->pending_watcher_ != nullptr) {
-    sd->connectivity_state_ = self->state_;
-    // Get or release ref to connected subchannel.
-    sd->connected_subchannel_ = std::move(self->connected_subchannel_);
+  if (!subchannel_list_->shutting_down() &&
+      subchannel_data_->pending_watcher_ != nullptr) {
+    subchannel_data_->connectivity_state_ = new_state;
     // Call the subclass's ProcessConnectivityChangeLocked() method.
-    sd->ProcessConnectivityChangeLocked(sd->connectivity_state_);
+    subchannel_data_->ProcessConnectivityChangeLocked(new_state);
   }
-  // Clean up.
-  Delete(self);
 }
 
 //
@@ -371,7 +298,6 @@ void SubchannelData<SubchannelListType, SubchannelDataType>::
               subchannel_.get());
     }
     subchannel_.reset();
-    connected_subchannel_.reset();
   }
 }
 
@@ -400,7 +326,7 @@ void SubchannelData<SubchannelListType,
       New<Watcher>(this, subchannel_list()->Ref(DEBUG_LOCATION, "Watcher"));
   subchannel_->WatchConnectivityState(
       connectivity_state_,
-      UniquePtr<SubchannelInterface::ConnectivityStateWatcher>(
+      UniquePtr<SubchannelInterface::ConnectivityStateWatcherInterface>(
           pending_watcher_));
 }
 
@@ -434,13 +360,12 @@ void SubchannelData<SubchannelListType, SubchannelDataType>::ShutdownLocked() {
 template <typename SubchannelListType, typename SubchannelDataType>
 SubchannelList<SubchannelListType, SubchannelDataType>::SubchannelList(
     LoadBalancingPolicy* policy, TraceFlag* tracer,
-    const ServerAddressList& addresses, grpc_combiner* combiner,
+    const ServerAddressList& addresses,
     LoadBalancingPolicy::ChannelControlHelper* helper,
     const grpc_channel_args& args)
     : InternallyRefCounted<SubchannelListType>(tracer),
       policy_(policy),
-      tracer_(tracer),
-      combiner_(GRPC_COMBINER_REF(combiner, "subchannel_list")) {
+      tracer_(tracer) {
   if (GRPC_TRACE_FLAG_ENABLED(*tracer_)) {
     gpr_log(GPR_INFO,
             "[%s %p] Creating subchannel list %p for %" PRIuPTR " subchannels",
@@ -509,7 +434,6 @@ SubchannelList<SubchannelListType, SubchannelDataType>::~SubchannelList() {
     gpr_log(GPR_INFO, "[%s %p] Destroying subchannel_list %p", tracer_->name(),
             policy_, this);
   }
-  GRPC_COMBINER_UNREF(combiner_, "subchannel_list");
 }
 
 template <typename SubchannelListType, typename SubchannelDataType>

+ 85 - 20
src/core/ext/filters/client_channel/lb_policy/xds/xds.cc

@@ -333,6 +333,8 @@ class XdsLb : public LoadBalancingPolicy {
     explicit FallbackHelper(RefCountedPtr<XdsLb> parent)
         : parent_(std::move(parent)) {}
 
+    ~FallbackHelper() { parent_.reset(DEBUG_LOCATION, "FallbackHelper"); }
+
     RefCountedPtr<SubchannelInterface> CreateSubchannel(
         const grpc_channel_args& args) override;
     grpc_channel* CreateChannel(const char* target,
@@ -377,19 +379,30 @@ class XdsLb : public LoadBalancingPolicy {
              strcmp(subzone_.get(), other.subzone_.get()) == 0;
     }
 
+    const char* AsHumanReadableString() {
+      if (human_readable_string_ == nullptr) {
+        char* tmp;
+        gpr_asprintf(&tmp, "{region=\"%s\", zone=\"%s\", subzone=\"%s\"}",
+                     region_.get(), zone_.get(), subzone_.get());
+        human_readable_string_.reset(tmp);
+      }
+      return human_readable_string_.get();
+    }
+
    private:
     UniquePtr<char> region_;
     UniquePtr<char> zone_;
     UniquePtr<char> subzone_;
+    UniquePtr<char> human_readable_string_;
   };
 
   class LocalityMap {
    public:
     class LocalityEntry : public InternallyRefCounted<LocalityEntry> {
      public:
-      LocalityEntry(RefCountedPtr<XdsLb> parent, uint32_t locality_weight)
-          : parent_(std::move(parent)), locality_weight_(locality_weight) {}
-      ~LocalityEntry() = default;
+      LocalityEntry(RefCountedPtr<XdsLb> parent,
+                    RefCountedPtr<LocalityName> name, uint32_t locality_weight);
+      ~LocalityEntry();
 
       void UpdateLocked(xds_grpclb_serverlist* serverlist,
                         LoadBalancingPolicy::Config* child_policy_config,
@@ -404,6 +417,8 @@ class XdsLb : public LoadBalancingPolicy {
         explicit Helper(RefCountedPtr<LocalityEntry> entry)
             : entry_(std::move(entry)) {}
 
+        ~Helper() { entry_.reset(DEBUG_LOCATION, "Helper"); }
+
         RefCountedPtr<SubchannelInterface> CreateSubchannel(
             const grpc_channel_args& args) override;
         grpc_channel* CreateChannel(const char* target,
@@ -428,9 +443,10 @@ class XdsLb : public LoadBalancingPolicy {
       grpc_channel_args* CreateChildPolicyArgsLocked(
           const grpc_channel_args* args);
 
+      RefCountedPtr<XdsLb> parent_;
+      RefCountedPtr<LocalityName> name_;
       OrphanablePtr<LoadBalancingPolicy> child_policy_;
       OrphanablePtr<LoadBalancingPolicy> pending_child_policy_;
-      RefCountedPtr<XdsLb> parent_;
       RefCountedPtr<PickerRef> picker_ref_;
       grpc_connectivity_state connectivity_state_;
       uint32_t locality_weight_;
@@ -554,7 +570,7 @@ XdsLb::PickResult XdsLb::Picker::Pick(PickArgs args) {
   PickResult result = PickFromLocality(key, args);
   // If pick succeeded, add client stats.
   if (result.type == PickResult::PICK_COMPLETE &&
-      result.connected_subchannel != nullptr && client_stats_ != nullptr) {
+      result.subchannel != nullptr && client_stats_ != nullptr) {
     // TODO(roth): Add support for client stats.
   }
   return result;
@@ -743,7 +759,7 @@ ServerAddressList ProcessServerlist(const xds_grpclb_serverlist* serverlist) {
 
 XdsLb::BalancerChannelState::BalancerChannelState(
     const char* balancer_name, const grpc_channel_args& args,
-    grpc_core::RefCountedPtr<grpc_core::XdsLb> parent_xdslb_policy)
+    RefCountedPtr<XdsLb> parent_xdslb_policy)
     : InternallyRefCounted<BalancerChannelState>(&grpc_lb_xds_trace),
       xdslb_policy_(std::move(parent_xdslb_policy)),
       lb_call_backoff_(
@@ -763,6 +779,7 @@ XdsLb::BalancerChannelState::BalancerChannelState(
 }
 
 XdsLb::BalancerChannelState::~BalancerChannelState() {
+  xdslb_policy_.reset(DEBUG_LOCATION, "BalancerChannelState");
   grpc_channel_destroy(channel_);
 }
 
@@ -1414,12 +1431,18 @@ XdsLb::XdsLb(Args args)
 }
 
 XdsLb::~XdsLb() {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
+    gpr_log(GPR_INFO, "[xdslb %p] destroying xds LB policy", this);
+  }
   gpr_free((void*)server_name_);
   grpc_channel_args_destroy(args_);
   locality_serverlist_.clear();
 }
 
 void XdsLb::ShutdownLocked() {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
+    gpr_log(GPR_INFO, "[xdslb %p] shutting down", this);
+  }
   shutting_down_ = true;
   if (fallback_at_startup_checks_pending_) {
     grpc_timer_cancel(&lb_fallback_timer_);
@@ -1486,8 +1509,9 @@ void XdsLb::ProcessAddressesAndChannelArgsLocked(
   }
   if (create_lb_channel) {
     OrphanablePtr<BalancerChannelState> lb_chand =
-        MakeOrphanable<BalancerChannelState>(balancer_name_.get(),
-                                             *lb_channel_args, Ref());
+        MakeOrphanable<BalancerChannelState>(
+            balancer_name_.get(), *lb_channel_args,
+            Ref(DEBUG_LOCATION, "BalancerChannelState"));
     if (lb_chand_ == nullptr || !lb_chand_->HasActiveCall()) {
       GPR_ASSERT(pending_lb_chand_ == nullptr);
       // If we do not have a working LB channel yet, use the newly created one.
@@ -1676,7 +1700,8 @@ void XdsLb::UpdateFallbackPolicyLocked() {
 
 OrphanablePtr<LoadBalancingPolicy> XdsLb::CreateFallbackPolicyLocked(
     const char* name, const grpc_channel_args* args) {
-  FallbackHelper* helper = New<FallbackHelper>(Ref());
+  FallbackHelper* helper =
+      New<FallbackHelper>(Ref(DEBUG_LOCATION, "FallbackHelper"));
   LoadBalancingPolicy::Args lb_policy_args;
   lb_policy_args.combiner = combiner();
   lb_policy_args.args = args;
@@ -1739,7 +1764,9 @@ void XdsLb::LocalityMap::UpdateLocked(
     auto iter = map_.find(locality_serverlist[i]->locality_name);
     if (iter == map_.end()) {
       OrphanablePtr<LocalityEntry> new_entry = MakeOrphanable<LocalityEntry>(
-          parent->Ref(), locality_serverlist[i]->locality_weight);
+          parent->Ref(DEBUG_LOCATION, "LocalityEntry"),
+          locality_serverlist[i]->locality_name,
+          locality_serverlist[i]->locality_weight);
       iter = map_.emplace(locality_serverlist[i]->locality_name,
                           std::move(new_entry))
                  .first;
@@ -1764,6 +1791,27 @@ void XdsLb::LocalityMap::ResetBackoffLocked() {
 // XdsLb::LocalityMap::LocalityEntry
 //
 
+XdsLb::LocalityMap::LocalityEntry::LocalityEntry(
+    RefCountedPtr<XdsLb> parent, RefCountedPtr<LocalityName> name,
+    uint32_t locality_weight)
+    : parent_(std::move(parent)),
+      name_(std::move(name)),
+      locality_weight_(locality_weight) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
+    gpr_log(GPR_INFO, "[xdslb %p] created LocalityEntry %p for %s",
+            parent_.get(), this, name_->AsHumanReadableString());
+  }
+}
+
+XdsLb::LocalityMap::LocalityEntry::~LocalityEntry() {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
+    gpr_log(GPR_INFO,
+            "[xdslb %p] LocalityEntry %p %s: destroying locality entry",
+            parent_.get(), this, name_->AsHumanReadableString());
+  }
+  parent_.reset(DEBUG_LOCATION, "LocalityEntry");
+}
+
 grpc_channel_args*
 XdsLb::LocalityMap::LocalityEntry::CreateChildPolicyArgsLocked(
     const grpc_channel_args* args_in) {
@@ -1785,7 +1833,7 @@ XdsLb::LocalityMap::LocalityEntry::CreateChildPolicyArgsLocked(
 OrphanablePtr<LoadBalancingPolicy>
 XdsLb::LocalityMap::LocalityEntry::CreateChildPolicyLocked(
     const char* name, const grpc_channel_args* args) {
-  Helper* helper = New<Helper>(this->Ref());
+  Helper* helper = New<Helper>(this->Ref(DEBUG_LOCATION, "Helper"));
   LoadBalancingPolicy::Args lb_policy_args;
   lb_policy_args.combiner = parent_->combiner();
   lb_policy_args.args = args;
@@ -1795,13 +1843,16 @@ XdsLb::LocalityMap::LocalityEntry::CreateChildPolicyLocked(
       LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
           name, std::move(lb_policy_args));
   if (GPR_UNLIKELY(lb_policy == nullptr)) {
-    gpr_log(GPR_ERROR, "[xdslb %p] Failure creating child policy %s", this,
-            name);
+    gpr_log(GPR_ERROR,
+            "[xdslb %p] LocalityEntry %p %s: failure creating child policy %s",
+            parent_.get(), this, name_->AsHumanReadableString(), name);
     return nullptr;
   }
   helper->set_child(lb_policy.get());
   if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
-    gpr_log(GPR_INFO, "[xdslb %p] Created new child policy %s (%p)", this, name,
+    gpr_log(GPR_INFO,
+            "[xdslb %p] LocalityEntry %p %s: Created new child policy %s (%p)",
+            parent_.get(), this, name_->AsHumanReadableString(), name,
             lb_policy.get());
   }
   // Add the xDS's interested_parties pollset_set to that of the newly created
@@ -1892,7 +1943,9 @@ void XdsLb::LocalityMap::LocalityEntry::UpdateLocked(
     // If child_policy_ is null, we set it (case 1), else we set
     // pending_child_policy_ (cases 2b and 3b).
     if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
-      gpr_log(GPR_INFO, "[xdslb %p] Creating new %schild policy %s", this,
+      gpr_log(GPR_INFO,
+              "[xdslb %p] LocalityEntry %p %s: Creating new %schild policy %s",
+              parent_.get(), this, name_->AsHumanReadableString(),
               child_policy_ == nullptr ? "" : "pending ", child_policy_name);
     }
     auto& lb_policy =
@@ -1910,7 +1963,9 @@ void XdsLb::LocalityMap::LocalityEntry::UpdateLocked(
   GPR_ASSERT(policy_to_update != nullptr);
   // Update the policy.
   if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
-    gpr_log(GPR_INFO, "[xdslb %p] Updating %schild policy %p", this,
+    gpr_log(GPR_INFO,
+            "[xdslb %p] LocalityEntry %p %s: Updating %schild policy %p",
+            parent_.get(), this, name_->AsHumanReadableString(),
             policy_to_update == pending_child_policy_.get() ? "pending " : "",
             policy_to_update);
   }
@@ -1918,17 +1973,25 @@ void XdsLb::LocalityMap::LocalityEntry::UpdateLocked(
 }
 
 void XdsLb::LocalityMap::LocalityEntry::ShutdownLocked() {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
+    gpr_log(GPR_INFO,
+            "[xdslb %p] LocalityEntry %p %s: shutting down locality entry",
+            parent_.get(), this, name_->AsHumanReadableString());
+  }
   // Remove the child policy's interested_parties pollset_set from the
   // xDS policy.
   grpc_pollset_set_del_pollset_set(child_policy_->interested_parties(),
                                    parent_->interested_parties());
+  child_policy_.reset();
   if (pending_child_policy_ != nullptr) {
     grpc_pollset_set_del_pollset_set(
         pending_child_policy_->interested_parties(),
         parent_->interested_parties());
+    pending_child_policy_.reset();
   }
-  child_policy_.reset();
-  pending_child_policy_.reset();
+  // Drop our ref to the child's picker, in case it's holding a ref to
+  // the child.
+  picker_ref_.reset();
 }
 
 void XdsLb::LocalityMap::LocalityEntry::ResetBackoffLocked() {
@@ -2061,11 +2124,13 @@ void XdsLb::LocalityMap::LocalityEntry::Helper::UpdateState(
   } else if (num_connecting > 0) {
     entry_->parent_->channel_control_helper()->UpdateState(
         GRPC_CHANNEL_CONNECTING,
-        UniquePtr<SubchannelPicker>(New<QueuePicker>(this->entry_->parent_)));
+        UniquePtr<SubchannelPicker>(New<QueuePicker>(
+            this->entry_->parent_->Ref(DEBUG_LOCATION, "QueuePicker"))));
   } else if (num_idle > 0) {
     entry_->parent_->channel_control_helper()->UpdateState(
         GRPC_CHANNEL_IDLE,
-        UniquePtr<SubchannelPicker>(New<QueuePicker>(this->entry_->parent_)));
+        UniquePtr<SubchannelPicker>(New<QueuePicker>(
+            this->entry_->parent_->Ref(DEBUG_LOCATION, "QueuePicker"))));
   } else {
     GPR_ASSERT(num_transient_failures == locality_map.size());
     grpc_error* error =

+ 3 - 11
src/core/ext/filters/client_channel/resolver.h

@@ -117,12 +117,10 @@ class Resolver : public InternallyRefCounted<Resolver> {
   /// implementations.  At that point, this method can go away.
   virtual void ResetBackoffLocked() {}
 
+  // Note: This must be invoked while holding the combiner.
   void Orphan() override {
-    // Invoke ShutdownAndUnrefLocked() inside of the combiner.
-    GRPC_CLOSURE_SCHED(
-        GRPC_CLOSURE_CREATE(&Resolver::ShutdownAndUnrefLocked, this,
-                            grpc_combiner_scheduler(combiner_)),
-        GRPC_ERROR_NONE);
+    ShutdownLocked();
+    Unref();
   }
 
   GRPC_ABSTRACT_BASE_CLASS
@@ -147,12 +145,6 @@ class Resolver : public InternallyRefCounted<Resolver> {
   ResultHandler* result_handler() const { return result_handler_.get(); }
 
  private:
-  static void ShutdownAndUnrefLocked(void* arg, grpc_error* ignored) {
-    Resolver* resolver = static_cast<Resolver*>(arg);
-    resolver->ShutdownLocked();
-    resolver->Unref();
-  }
-
   UniquePtr<ResultHandler> result_handler_;
   grpc_combiner* combiner_;
 };

+ 13 - 10
src/core/ext/filters/client_channel/subchannel.cc

@@ -86,7 +86,7 @@ ConnectedSubchannel::ConnectedSubchannel(
     grpc_channel_stack* channel_stack, const grpc_channel_args* args,
     RefCountedPtr<channelz::SubchannelNode> channelz_subchannel,
     intptr_t socket_uuid)
-    : ConnectedSubchannelInterface(&grpc_trace_subchannel_refcount),
+    : RefCounted<ConnectedSubchannel>(&grpc_trace_subchannel_refcount),
       channel_stack_(channel_stack),
       args_(grpc_channel_args_copy(args)),
       channelz_subchannel_(std::move(channelz_subchannel)),
@@ -378,12 +378,12 @@ class Subchannel::ConnectedSubchannelStateWatcher {
 //
 
 void Subchannel::ConnectivityStateWatcherList::AddWatcherLocked(
-    UniquePtr<ConnectivityStateWatcher> watcher) {
+    OrphanablePtr<ConnectivityStateWatcherInterface> watcher) {
   watchers_.insert(MakePair(watcher.get(), std::move(watcher)));
 }
 
 void Subchannel::ConnectivityStateWatcherList::RemoveWatcherLocked(
-    ConnectivityStateWatcher* watcher) {
+    ConnectivityStateWatcherInterface* watcher) {
   watchers_.erase(watcher);
 }
 
@@ -438,8 +438,9 @@ class Subchannel::HealthWatcherMap::HealthWatcher
 
   grpc_connectivity_state state() const { return state_; }
 
-  void AddWatcherLocked(grpc_connectivity_state initial_state,
-                        UniquePtr<ConnectivityStateWatcher> watcher) {
+  void AddWatcherLocked(
+      grpc_connectivity_state initial_state,
+      OrphanablePtr<ConnectivityStateWatcherInterface> watcher) {
     if (state_ != initial_state) {
       RefCountedPtr<ConnectedSubchannel> connected_subchannel;
       if (state_ == GRPC_CHANNEL_READY) {
@@ -451,7 +452,7 @@ class Subchannel::HealthWatcherMap::HealthWatcher
     watcher_list_.AddWatcherLocked(std::move(watcher));
   }
 
-  void RemoveWatcherLocked(ConnectivityStateWatcher* watcher) {
+  void RemoveWatcherLocked(ConnectivityStateWatcherInterface* watcher) {
     watcher_list_.RemoveWatcherLocked(watcher);
   }
 
@@ -527,7 +528,7 @@ class Subchannel::HealthWatcherMap::HealthWatcher
 void Subchannel::HealthWatcherMap::AddWatcherLocked(
     Subchannel* subchannel, grpc_connectivity_state initial_state,
     UniquePtr<char> health_check_service_name,
-    UniquePtr<ConnectivityStateWatcher> watcher) {
+    OrphanablePtr<ConnectivityStateWatcherInterface> watcher) {
   // If the health check service name is not already present in the map,
   // add it.
   auto it = map_.find(health_check_service_name.get());
@@ -546,7 +547,8 @@ void Subchannel::HealthWatcherMap::AddWatcherLocked(
 }
 
 void Subchannel::HealthWatcherMap::RemoveWatcherLocked(
-    const char* health_check_service_name, ConnectivityStateWatcher* watcher) {
+    const char* health_check_service_name,
+    ConnectivityStateWatcherInterface* watcher) {
   auto it = map_.find(health_check_service_name);
   GPR_ASSERT(it != map_.end());
   it->second->RemoveWatcherLocked(watcher);
@@ -818,7 +820,7 @@ grpc_connectivity_state Subchannel::CheckConnectivityState(
 void Subchannel::WatchConnectivityState(
     grpc_connectivity_state initial_state,
     UniquePtr<char> health_check_service_name,
-    UniquePtr<ConnectivityStateWatcher> watcher) {
+    OrphanablePtr<ConnectivityStateWatcherInterface> watcher) {
   MutexLock lock(&mu_);
   grpc_pollset_set* interested_parties = watcher->interested_parties();
   if (interested_parties != nullptr) {
@@ -837,7 +839,8 @@ void Subchannel::WatchConnectivityState(
 }
 
 void Subchannel::CancelConnectivityStateWatch(
-    const char* health_check_service_name, ConnectivityStateWatcher* watcher) {
+    const char* health_check_service_name,
+    ConnectivityStateWatcherInterface* watcher) {
   MutexLock lock(&mu_);
   grpc_pollset_set* interested_parties = watcher->interested_parties();
   if (interested_parties != nullptr) {

+ 50 - 21
src/core/ext/filters/client_channel/subchannel.h

@@ -23,7 +23,6 @@
 
 #include "src/core/ext/filters/client_channel/client_channel_channelz.h"
 #include "src/core/ext/filters/client_channel/connector.h"
-#include "src/core/ext/filters/client_channel/subchannel_interface.h"
 #include "src/core/ext/filters/client_channel/subchannel_pool_interface.h"
 #include "src/core/lib/backoff/backoff.h"
 #include "src/core/lib/channel/channel_stack.h"
@@ -70,7 +69,7 @@ namespace grpc_core {
 
 class SubchannelCall;
 
-class ConnectedSubchannel : public ConnectedSubchannelInterface {
+class ConnectedSubchannel : public RefCounted<ConnectedSubchannel> {
  public:
   struct CallArgs {
     grpc_polling_entity* pollent;
@@ -97,7 +96,7 @@ class ConnectedSubchannel : public ConnectedSubchannelInterface {
                                            grpc_error** error);
 
   grpc_channel_stack* channel_stack() const { return channel_stack_; }
-  const grpc_channel_args* args() const override { return args_; }
+  const grpc_channel_args* args() const { return args_; }
   channelz::SubchannelNode* channelz_subchannel() const {
     return channelz_subchannel_.get();
   }
@@ -176,10 +175,35 @@ class SubchannelCall {
 
 // A subchannel that knows how to connect to exactly one target address. It
 // provides a target for load balancing.
+//
+// Note that this is the "real" subchannel implementation, whose API is
+// different from the SubchannelInterface that is exposed to LB policy
+// implementations.  The client channel provides an adaptor class
+// (SubchannelWrapper) that "converts" between the two.
 class Subchannel {
  public:
-  typedef SubchannelInterface::ConnectivityStateWatcher
-      ConnectivityStateWatcher;
+  class ConnectivityStateWatcherInterface
+      : public InternallyRefCounted<ConnectivityStateWatcherInterface> {
+   public:
+    virtual ~ConnectivityStateWatcherInterface() = default;
+
+    // Will be invoked whenever the subchannel's connectivity state
+    // changes.  There will be only one invocation of this method on a
+    // given watcher instance at any given time.
+    //
+    // When the state changes to READY, connected_subchannel will
+    // contain a ref to the connected subchannel.  When it changes from
+    // READY to some other state, the implementation must release its
+    // ref to the connected subchannel.
+    virtual void OnConnectivityStateChange(
+        grpc_connectivity_state new_state,
+        RefCountedPtr<ConnectedSubchannel> connected_subchannel)  // NOLINT
+        GRPC_ABSTRACT;
+
+    virtual grpc_pollset_set* interested_parties() GRPC_ABSTRACT;
+
+    GRPC_ABSTRACT_BASE_CLASS
+  };
 
   // The ctor and dtor are not intended to use directly.
   Subchannel(SubchannelKey* key, grpc_connector* connector,
@@ -206,6 +230,8 @@ class Subchannel {
   // Caller doesn't take ownership.
   const char* GetTargetAddress();
 
+  const grpc_channel_args* channel_args() const { return args_; }
+
   channelz::SubchannelNode* channelz_node();
 
   // Returns the current connectivity state of the subchannel.
@@ -225,14 +251,15 @@ class Subchannel {
   // changes.
   // The watcher will be destroyed either when the subchannel is
   // destroyed or when CancelConnectivityStateWatch() is called.
-  void WatchConnectivityState(grpc_connectivity_state initial_state,
-                              UniquePtr<char> health_check_service_name,
-                              UniquePtr<ConnectivityStateWatcher> watcher);
+  void WatchConnectivityState(
+      grpc_connectivity_state initial_state,
+      UniquePtr<char> health_check_service_name,
+      OrphanablePtr<ConnectivityStateWatcherInterface> watcher);
 
   // Cancels a connectivity state watch.
   // If the watcher has already been destroyed, this is a no-op.
   void CancelConnectivityStateWatch(const char* health_check_service_name,
-                                    ConnectivityStateWatcher* watcher);
+                                    ConnectivityStateWatcherInterface* watcher);
 
   // Attempt to connect to the backend.  Has no effect if already connected.
   void AttemptToConnect();
@@ -257,14 +284,15 @@ class Subchannel {
                                                  grpc_resolved_address* addr);
 
  private:
-  // A linked list of ConnectivityStateWatchers that are monitoring the
-  // subchannel's state.
+  // A linked list of ConnectivityStateWatcherInterfaces that are monitoring
+  // the subchannel's state.
   class ConnectivityStateWatcherList {
    public:
     ~ConnectivityStateWatcherList() { Clear(); }
 
-    void AddWatcherLocked(UniquePtr<ConnectivityStateWatcher> watcher);
-    void RemoveWatcherLocked(ConnectivityStateWatcher* watcher);
+    void AddWatcherLocked(
+        OrphanablePtr<ConnectivityStateWatcherInterface> watcher);
+    void RemoveWatcherLocked(ConnectivityStateWatcherInterface* watcher);
 
     // Notifies all watchers in the list about a change to state.
     void NotifyLocked(Subchannel* subchannel, grpc_connectivity_state state);
@@ -276,12 +304,13 @@ class Subchannel {
    private:
     // TODO(roth): This could be a set instead of a map if we had a set
     // implementation.
-    Map<ConnectivityStateWatcher*, UniquePtr<ConnectivityStateWatcher>>
+    Map<ConnectivityStateWatcherInterface*,
+        OrphanablePtr<ConnectivityStateWatcherInterface>>
         watchers_;
   };
 
-  // A map that tracks ConnectivityStateWatchers using a particular health
-  // check service name.
+  // A map that tracks ConnectivityStateWatcherInterfaces using a particular
+  // health check service name.
   //
   // There is one entry in the map for each health check service name.
   // Entries exist only as long as there are watchers using the
@@ -291,12 +320,12 @@ class Subchannel {
   // state READY.
   class HealthWatcherMap {
    public:
-    void AddWatcherLocked(Subchannel* subchannel,
-                          grpc_connectivity_state initial_state,
-                          UniquePtr<char> health_check_service_name,
-                          UniquePtr<ConnectivityStateWatcher> watcher);
+    void AddWatcherLocked(
+        Subchannel* subchannel, grpc_connectivity_state initial_state,
+        UniquePtr<char> health_check_service_name,
+        OrphanablePtr<ConnectivityStateWatcherInterface> watcher);
     void RemoveWatcherLocked(const char* health_check_service_name,
-                             ConnectivityStateWatcher* watcher);
+                             ConnectivityStateWatcherInterface* watcher);
 
     // Notifies the watcher when the subchannel's state changes.
     void NotifyLocked(grpc_connectivity_state state);

+ 15 - 30
src/core/ext/filters/client_channel/subchannel_interface.h

@@ -21,42 +21,22 @@
 
 #include <grpc/support/port_platform.h>
 
-#include "src/core/lib/debug/trace.h"
 #include "src/core/lib/gprpp/ref_counted.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
 
 namespace grpc_core {
 
-// TODO(roth): In a subsequent PR, remove this from this API.
-class ConnectedSubchannelInterface
-    : public RefCounted<ConnectedSubchannelInterface> {
- public:
-  virtual const grpc_channel_args* args() const GRPC_ABSTRACT;
-
- protected:
-  template <typename TraceFlagT = TraceFlag>
-  explicit ConnectedSubchannelInterface(TraceFlagT* trace_flag = nullptr)
-      : RefCounted<ConnectedSubchannelInterface>(trace_flag) {}
-};
-
+// The interface for subchannels that is exposed to LB policy implementations.
 class SubchannelInterface : public RefCounted<SubchannelInterface> {
  public:
-  class ConnectivityStateWatcher {
+  class ConnectivityStateWatcherInterface {
    public:
-    virtual ~ConnectivityStateWatcher() = default;
+    virtual ~ConnectivityStateWatcherInterface() = default;
 
     // Will be invoked whenever the subchannel's connectivity state
     // changes.  There will be only one invocation of this method on a
     // given watcher instance at any given time.
-    //
-    // When the state changes to READY, connected_subchannel will
-    // contain a ref to the connected subchannel.  When it changes from
-    // READY to some other state, the implementation must release its
-    // ref to the connected subchannel.
-    virtual void OnConnectivityStateChange(
-        grpc_connectivity_state new_state,
-        RefCountedPtr<ConnectedSubchannelInterface>
-            connected_subchannel)  // NOLINT
+    virtual void OnConnectivityStateChange(grpc_connectivity_state new_state)
         GRPC_ABSTRACT;
 
     // TODO(roth): Remove this as soon as we move to EventManager-based
@@ -66,12 +46,14 @@ class SubchannelInterface : public RefCounted<SubchannelInterface> {
     GRPC_ABSTRACT_BASE_CLASS
   };
 
+  template <typename TraceFlagT = TraceFlag>
+  explicit SubchannelInterface(TraceFlagT* trace_flag = nullptr)
+      : RefCounted<SubchannelInterface>(trace_flag) {}
+
   virtual ~SubchannelInterface() = default;
 
   // Returns the current connectivity state of the subchannel.
-  virtual grpc_connectivity_state CheckConnectivityState(
-      RefCountedPtr<ConnectedSubchannelInterface>* connected_subchannel)
-      GRPC_ABSTRACT;
+  virtual grpc_connectivity_state CheckConnectivityState() GRPC_ABSTRACT;
 
   // Starts watching the subchannel's connectivity state.
   // The first callback to the watcher will be delivered when the
@@ -86,12 +68,12 @@ class SubchannelInterface : public RefCounted<SubchannelInterface> {
   // the previous watcher using CancelConnectivityStateWatch().
   virtual void WatchConnectivityState(
       grpc_connectivity_state initial_state,
-      UniquePtr<ConnectivityStateWatcher> watcher) GRPC_ABSTRACT;
+      UniquePtr<ConnectivityStateWatcherInterface> watcher) GRPC_ABSTRACT;
 
   // Cancels a connectivity state watch.
   // If the watcher has already been destroyed, this is a no-op.
-  virtual void CancelConnectivityStateWatch(ConnectivityStateWatcher* watcher)
-      GRPC_ABSTRACT;
+  virtual void CancelConnectivityStateWatch(
+      ConnectivityStateWatcherInterface* watcher) GRPC_ABSTRACT;
 
   // Attempt to connect to the backend.  Has no effect if already connected.
   // If the subchannel is currently in backoff delay due to a previously
@@ -105,6 +87,9 @@ class SubchannelInterface : public RefCounted<SubchannelInterface> {
   // attempt will be started as soon as AttemptToConnect() is called.
   virtual void ResetBackoff() GRPC_ABSTRACT;
 
+  // TODO(roth): Need a better non-grpc-specific abstraction here.
+  virtual const grpc_channel_args* channel_args() GRPC_ABSTRACT;
+
   GRPC_ABSTRACT_BASE_CLASS
 };
 

+ 28 - 0
src/core/lib/gprpp/map.h

@@ -30,6 +30,7 @@
 #include "src/core/lib/gpr/useful.h"
 #include "src/core/lib/gprpp/memory.h"
 #include "src/core/lib/gprpp/pair.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
 
 namespace grpc_core {
 struct StringLess {
@@ -41,6 +42,13 @@ struct StringLess {
   }
 };
 
+template <typename T>
+struct RefCountedPtrLess {
+  bool operator()(const RefCountedPtr<T>& p1, const RefCountedPtr<T>& p2) {
+    return p1.get() < p2.get();
+  }
+};
+
 namespace testing {
 class MapTest;
 }
@@ -55,8 +63,28 @@ class Map {
   typedef Compare key_compare;
   class iterator;
 
+  Map() {}
   ~Map() { clear(); }
 
+  // Copying not currently supported.
+  Map(const Map& other) = delete;
+
+  // Move support.
+  Map(Map&& other) : root_(other.root_), size_(other.size_) {
+    other.root_ = nullptr;
+    other.size_ = 0;
+  }
+  Map& operator=(Map&& other) {
+    if (this != &other) {
+      clear();
+      root_ = other.root_;
+      size_ = other.size_;
+      other.root_ = nullptr;
+      other.size_ = 0;
+    }
+    return *this;
+  }
+
   T& operator[](key_type&& key);
   T& operator[](const key_type& key);
   iterator find(const key_type& k);

+ 1 - 1
src/core/lib/iomgr/call_combiner.cc

@@ -28,7 +28,7 @@
 
 namespace grpc_core {
 
-TraceFlag grpc_call_combiner_trace(false, "call_combiner");
+DebugOnlyTraceFlag grpc_call_combiner_trace(false, "call_combiner");
 
 namespace {
 

+ 1 - 1
src/core/lib/iomgr/call_combiner.h

@@ -43,7 +43,7 @@
 
 namespace grpc_core {
 
-extern TraceFlag grpc_call_combiner_trace;
+extern DebugOnlyTraceFlag grpc_call_combiner_trace;
 
 class CallCombiner {
  public:

+ 3 - 2
src/core/lib/iomgr/cfstream_handle.cc

@@ -18,6 +18,7 @@
 
 #include <grpc/support/port_platform.h>
 
+#include "src/core/lib/gprpp/memory.h"
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_CFSTREAM
@@ -47,7 +48,7 @@ void CFStreamHandle::Release(void* info) {
 
 CFStreamHandle* CFStreamHandle::CreateStreamHandle(
     CFReadStreamRef read_stream, CFWriteStreamRef write_stream) {
-  return new CFStreamHandle(read_stream, write_stream);
+  return grpc_core::New<CFStreamHandle>(read_stream, write_stream);
 }
 
 void CFStreamHandle::ReadCallback(CFReadStreamRef stream,
@@ -188,7 +189,7 @@ void CFStreamHandle::Unref(const char* file, int line, const char* reason) {
             reason, val, val - 1);
   }
   if (gpr_unref(&refcount_)) {
-    delete this;
+    grpc_core::Delete<CFStreamHandle>(this);
   }
 }
 

+ 4 - 0
src/core/lib/iomgr/cfstream_handle.h

@@ -29,6 +29,7 @@
 #ifdef GRPC_CFSTREAM
 #import <CoreFoundation/CoreFoundation.h>
 
+#include "src/core/lib/gprpp/memory.h"
 #include "src/core/lib/iomgr/closure.h"
 #include "src/core/lib/iomgr/lockfree_event.h"
 
@@ -65,6 +66,9 @@ class CFStreamHandle final {
   dispatch_queue_t dispatch_queue_;
 
   gpr_refcount refcount_;
+
+  GRPC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
+  GRPC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
 };
 
 #ifdef DEBUG

+ 3 - 3
src/core/lib/iomgr/ev_posix.cc

@@ -44,11 +44,11 @@ GPR_GLOBAL_CONFIG_DEFINE_STRING(
     "This is a comma-separated list of engines, which are tried in priority "
     "order first -> last.")
 
-grpc_core::TraceFlag grpc_polling_trace(false,
-                                        "polling"); /* Disabled by default */
+grpc_core::DebugOnlyTraceFlag grpc_polling_trace(
+    false, "polling"); /* Disabled by default */
 
 /* Traces fd create/close operations */
-grpc_core::TraceFlag grpc_fd_trace(false, "fd_trace");
+grpc_core::DebugOnlyTraceFlag grpc_fd_trace(false, "fd_trace");
 grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount(false, "fd_refcount");
 grpc_core::DebugOnlyTraceFlag grpc_polling_api_trace(false, "polling_api");
 

+ 3 - 2
src/core/lib/iomgr/ev_posix.h

@@ -32,8 +32,9 @@
 
 GPR_GLOBAL_CONFIG_DECLARE_STRING(grpc_poll_strategy);
 
-extern grpc_core::TraceFlag grpc_fd_trace;      /* Disabled by default */
-extern grpc_core::TraceFlag grpc_polling_trace; /* Disabled by default */
+extern grpc_core::DebugOnlyTraceFlag grpc_fd_trace; /* Disabled by default */
+extern grpc_core::DebugOnlyTraceFlag
+    grpc_polling_trace; /* Disabled by default */
 
 #define GRPC_FD_TRACE(format, ...)                        \
   if (GRPC_TRACE_FLAG_ENABLED(grpc_fd_trace)) {           \

+ 2 - 2
src/core/lib/iomgr/ev_windows.cc

@@ -24,7 +24,7 @@
 
 #include "src/core/lib/debug/trace.h"
 
-grpc_core::TraceFlag grpc_polling_trace(false,
-                                        "polling"); /* Disabled by default */
+grpc_core::DebugOnlyTraceFlag grpc_polling_trace(
+    false, "polling"); /* Disabled by default */
 
 #endif  // GRPC_WINSOCK_SOCKET

+ 1 - 1
src/core/lib/iomgr/lockfree_event.cc

@@ -24,7 +24,7 @@
 
 #include "src/core/lib/debug/trace.h"
 
-extern grpc_core::TraceFlag grpc_polling_trace;
+extern grpc_core::DebugOnlyTraceFlag grpc_polling_trace;
 
 /* 'state' holds the to call when the fd is readable or writable respectively.
    It can contain one of the following values:

+ 5 - 1
src/core/lib/iomgr/port.h

@@ -26,6 +26,11 @@
 #define GRPC_CUSTOM_SOCKET
 #endif
 #endif
+/* This needs to be separate from the other conditions because it needs to
+ * apply to custom sockets too */
+#ifdef GPR_WINDOWS
+#define GRPC_ARES_RESOLVE_LOCALHOST_MANUALLY 1
+#endif
 #if defined(GRPC_CUSTOM_SOCKET)
 // Do Nothing
 #elif defined(GPR_MANYLINUX1)
@@ -45,7 +50,6 @@
 #define GRPC_WINSOCK_SOCKET 1
 #define GRPC_WINDOWS_SOCKETUTILS 1
 #define GRPC_WINDOWS_SOCKET_ARES_EV_DRIVER 1
-#define GRPC_ARES_RESOLVE_LOCALHOST_MANUALLY 1
 #elif defined(GPR_ANDROID)
 #define GRPC_HAVE_IPV6_RECVPKTINFO 1
 #define GRPC_HAVE_IP_PKTINFO 1

+ 5 - 6
src/core/lib/iomgr/tcp_uv.cc

@@ -53,7 +53,7 @@ typedef struct uv_socket_t {
   char* read_buf;
   size_t read_len;
 
-  bool pending_connection;
+  int pending_connections;
   grpc_custom_socket* accept_socket;
   grpc_error* accept_error;
 
@@ -206,7 +206,7 @@ static grpc_error* uv_socket_init_helper(uv_socket_t* uv_socket, int domain) {
   // Node uses a garbage collector to call destructors, so we don't
   // want to hold the uv loop open with active gRPC objects.
   uv_unref((uv_handle_t*)uv_socket->handle);
-  uv_socket->pending_connection = false;
+  uv_socket->pending_connections = 0;
   uv_socket->accept_socket = nullptr;
   uv_socket->accept_error = GRPC_ERROR_NONE;
   return GRPC_ERROR_NONE;
@@ -243,14 +243,14 @@ static grpc_error* uv_socket_getsockname(grpc_custom_socket* socket,
 
 static void accept_new_connection(grpc_custom_socket* socket) {
   uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
-  if (!uv_socket->pending_connection || !uv_socket->accept_socket) {
+  if (uv_socket->pending_connections == 0 || !uv_socket->accept_socket) {
     return;
   }
   grpc_custom_socket* new_socket = uv_socket->accept_socket;
   grpc_error* error = uv_socket->accept_error;
   uv_socket->accept_socket = nullptr;
   uv_socket->accept_error = GRPC_ERROR_NONE;
-  uv_socket->pending_connection = false;
+  uv_socket->pending_connections -= 1;
   if (uv_socket->accept_error != GRPC_ERROR_NONE) {
     uv_stream_t dummy_handle;
     uv_accept((uv_stream_t*)uv_socket->handle, &dummy_handle);
@@ -270,8 +270,6 @@ static void accept_new_connection(grpc_custom_socket* socket) {
 static void uv_on_connect(uv_stream_t* server, int status) {
   grpc_custom_socket* socket = (grpc_custom_socket*)server->data;
   uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
-  GPR_ASSERT(!uv_socket->pending_connection);
-  uv_socket->pending_connection = true;
   if (status < 0) {
     switch (status) {
       case UV_EINTR:
@@ -281,6 +279,7 @@ static void uv_on_connect(uv_stream_t* server, int status) {
         uv_socket->accept_error = tcp_error_create("accept failed", status);
     }
   }
+  uv_socket->pending_connections += 1;
   accept_new_connection(socket);
 }
 

+ 2 - 2
src/core/lib/security/transport/security_handshaker.cc

@@ -195,7 +195,7 @@ void SecurityHandshaker::HandshakeFailedLocked(grpc_error* error) {
 void SecurityHandshaker::OnPeerCheckedInner(grpc_error* error) {
   MutexLock lock(&mu_);
   if (error != GRPC_ERROR_NONE || is_shutdown_) {
-    HandshakeFailedLocked(GRPC_ERROR_REF(error));
+    HandshakeFailedLocked(error);
     return;
   }
   // Create zero-copy frame protector, if implemented.
@@ -255,7 +255,7 @@ void SecurityHandshaker::OnPeerCheckedInner(grpc_error* error) {
 
 void SecurityHandshaker::OnPeerCheckedFn(void* arg, grpc_error* error) {
   RefCountedPtr<SecurityHandshaker>(static_cast<SecurityHandshaker*>(arg))
-      ->OnPeerCheckedInner(error);
+      ->OnPeerCheckedInner(GRPC_ERROR_REF(error));
 }
 
 grpc_error* SecurityHandshaker::CheckPeerLocked() {

+ 31 - 21
src/core/lib/slice/slice_buffer.cc

@@ -32,35 +32,45 @@
 /* grow a buffer; requires GRPC_SLICE_BUFFER_INLINE_ELEMENTS > 1 */
 #define GROW(x) (3 * (x) / 2)
 
+/* Typically, we do not actually need to embiggen (by calling
+ * memmove/malloc/realloc) - only if we were up against the full capacity of the
+ * slice buffer. If do_embiggen is inlined, the compiler clobbers multiple
+ * registers pointlessly in the common case. */
+static void GPR_ATTRIBUTE_NOINLINE do_embiggen(grpc_slice_buffer* sb,
+                                               const size_t slice_count,
+                                               const size_t slice_offset) {
+  if (slice_offset != 0) {
+    /* Make room by moving elements if there's still space unused */
+    memmove(sb->base_slices, sb->slices, sb->count * sizeof(grpc_slice));
+    sb->slices = sb->base_slices;
+  } else {
+    /* Allocate more memory if no more space is available */
+    const size_t new_capacity = GROW(sb->capacity);
+    sb->capacity = new_capacity;
+    if (sb->base_slices == sb->inlined) {
+      sb->base_slices = static_cast<grpc_slice*>(
+          gpr_malloc(new_capacity * sizeof(grpc_slice)));
+      memcpy(sb->base_slices, sb->inlined, slice_count * sizeof(grpc_slice));
+    } else {
+      sb->base_slices = static_cast<grpc_slice*>(
+          gpr_realloc(sb->base_slices, new_capacity * sizeof(grpc_slice)));
+    }
+
+    sb->slices = sb->base_slices + slice_offset;
+  }
+}
+
 static void maybe_embiggen(grpc_slice_buffer* sb) {
   if (sb->count == 0) {
     sb->slices = sb->base_slices;
+    return;
   }
 
   /* How far away from sb->base_slices is sb->slices pointer */
   size_t slice_offset = static_cast<size_t>(sb->slices - sb->base_slices);
   size_t slice_count = sb->count + slice_offset;
-
-  if (slice_count == sb->capacity) {
-    if (sb->base_slices != sb->slices) {
-      /* Make room by moving elements if there's still space unused */
-      memmove(sb->base_slices, sb->slices, sb->count * sizeof(grpc_slice));
-      sb->slices = sb->base_slices;
-    } else {
-      /* Allocate more memory if no more space is available */
-      sb->capacity = GROW(sb->capacity);
-      GPR_ASSERT(sb->capacity > slice_count);
-      if (sb->base_slices == sb->inlined) {
-        sb->base_slices = static_cast<grpc_slice*>(
-            gpr_malloc(sb->capacity * sizeof(grpc_slice)));
-        memcpy(sb->base_slices, sb->inlined, slice_count * sizeof(grpc_slice));
-      } else {
-        sb->base_slices = static_cast<grpc_slice*>(
-            gpr_realloc(sb->base_slices, sb->capacity * sizeof(grpc_slice)));
-      }
-
-      sb->slices = sb->base_slices + slice_offset;
-    }
+  if (GPR_UNLIKELY(slice_count == sb->capacity)) {
+    do_embiggen(sb, slice_count, slice_offset);
   }
 }
 

+ 5 - 0
src/core/lib/transport/metadata.cc

@@ -222,7 +222,12 @@ void grpc_mdctx_global_shutdown() {
         abort();
       }
     }
+      // For ASAN builds, we don't want to crash here, because that will
+      // prevent ASAN from providing leak detection information, which is
+      // far more useful than this simple assertion.
+#ifndef GRPC_ASAN_ENABLED
     GPR_DEBUG_ASSERT(shard->count == 0);
+#endif
     gpr_free(shard->elems);
   }
 }

+ 1 - 1
src/core/lib/transport/transport.cc

@@ -90,7 +90,7 @@ void grpc_stream_ref_init(grpc_stream_refcount* refcount, int initial_refs,
 #endif
   GRPC_CLOSURE_INIT(&refcount->destroy, cb, cb_arg, grpc_schedule_on_exec_ctx);
 
-  new (&refcount->refs) grpc_core::RefCount();
+  new (&refcount->refs) grpc_core::RefCount(1, &grpc_trace_stream_refcount);
   new (&refcount->slice_refcount) grpc_slice_refcount(
       grpc_slice_refcount::Type::REGULAR, &refcount->refs, slice_stream_destroy,
       refcount, &refcount->slice_refcount);

+ 8 - 2
src/core/lib/transport/transport.h

@@ -80,11 +80,13 @@ inline void grpc_stream_ref(grpc_stream_refcount* refcount,
     gpr_log(GPR_DEBUG, "%s %p:%p REF %s", refcount->object_type, refcount,
             refcount->destroy.cb_arg, reason);
   }
+  refcount->refs.RefNonZero(DEBUG_LOCATION, reason);
+}
 #else
 inline void grpc_stream_ref(grpc_stream_refcount* refcount) {
-#endif
   refcount->refs.RefNonZero();
 }
+#endif
 
 void grpc_stream_destroy(grpc_stream_refcount* refcount);
 
@@ -95,13 +97,17 @@ inline void grpc_stream_unref(grpc_stream_refcount* refcount,
     gpr_log(GPR_DEBUG, "%s %p:%p UNREF %s", refcount->object_type, refcount,
             refcount->destroy.cb_arg, reason);
   }
+  if (GPR_UNLIKELY(refcount->refs.Unref(DEBUG_LOCATION, reason))) {
+    grpc_stream_destroy(refcount);
+  }
+}
 #else
 inline void grpc_stream_unref(grpc_stream_refcount* refcount) {
-#endif
   if (GPR_UNLIKELY(refcount->refs.Unref())) {
     grpc_stream_destroy(refcount);
   }
 }
+#endif
 
 /* Wrap a buffer that is owned by some stream object into a slice that shares
    the same refcount */

+ 0 - 3
src/objective-c/GRPCClient/GRPCCall+ChannelArg.m

@@ -51,9 +51,6 @@
     case GRPCCompressGzip:
       hostConfig.compressAlgorithm = GRPC_COMPRESS_GZIP;
       break;
-    case GRPCStreamCompressGzip:
-      hostConfig.compressAlgorithm = GRPC_COMPRESS_STREAM_GZIP;
-      break;
     default:
       NSLog(@"Invalid compression algorithm");
       abort();

+ 2 - 3
src/objective-c/GRPCClient/GRPCCallOptions.h

@@ -156,8 +156,7 @@ typedef NS_ENUM(NSUInteger, GRPCTransportType) {
 // HTTP/2 keep-alive feature. The parameter \a keepaliveInterval specifies the interval between two
 // PING frames. The parameter \a keepaliveTimeout specifies the length of the period for which the
 // call should wait for PING ACK. If PING ACK is not received after this period, the call fails.
-// Negative values are invalid; setting these parameters to negative value will reset the
-// corresponding parameter to the internal default value.
+// Negative values are not allowed.
 @property(readonly) NSTimeInterval keepaliveInterval;
 @property(readonly) NSTimeInterval keepaliveTimeout;
 
@@ -321,7 +320,7 @@ typedef NS_ENUM(NSUInteger, GRPCTransportType) {
 // PING frames. The parameter \a keepaliveTimeout specifies the length of the period for which the
 // call should wait for PING ACK. If PING ACK is not received after this period, the call fails.
 // Negative values are invalid; setting these parameters to negative value will reset the
-// corresponding parameter to the internal default value.
+// corresponding parameter to 0.
 @property(readwrite) NSTimeInterval keepaliveInterval;
 @property(readwrite) NSTimeInterval keepaliveTimeout;
 

+ 7 - 3
src/objective-c/GRPCClient/GRPCCallOptions.m

@@ -30,7 +30,7 @@ static const NSUInteger kDefaultResponseSizeLimit = 0;
 static const GRPCCompressionAlgorithm kDefaultCompressionAlgorithm = GRPCCompressNone;
 static const BOOL kDefaultRetryEnabled = YES;
 static const NSTimeInterval kDefaultKeepaliveInterval = 0;
-static const NSTimeInterval kDefaultKeepaliveTimeout = -1;
+static const NSTimeInterval kDefaultKeepaliveTimeout = 0;
 static const NSTimeInterval kDefaultConnectMinTimeout = 0;
 static const NSTimeInterval kDefaultConnectInitialBackoff = 0;
 static const NSTimeInterval kDefaultConnectMaxBackoff = 0;
@@ -181,7 +181,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
     _compressionAlgorithm = compressionAlgorithm;
     _retryEnabled = retryEnabled;
     _keepaliveInterval = keepaliveInterval < 0 ? 0 : keepaliveInterval;
-    _keepaliveTimeout = keepaliveTimeout;
+    _keepaliveTimeout = keepaliveTimeout < 0 ? 0 : keepaliveTimeout;
     _connectMinTimeout = connectMinTimeout < 0 ? 0 : connectMinTimeout;
     _connectInitialBackoff = connectInitialBackoff < 0 ? 0 : connectInitialBackoff;
     _connectMaxBackoff = connectMaxBackoff < 0 ? 0 : connectMaxBackoff;
@@ -486,7 +486,11 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
 }
 
 - (void)setKeepaliveTimeout:(NSTimeInterval)keepaliveTimeout {
-  _keepaliveTimeout = keepaliveTimeout;
+  if (keepaliveTimeout < 0) {
+    _keepaliveTimeout = 0;
+  } else {
+    _keepaliveTimeout = keepaliveTimeout;
+  }
 }
 
 - (void)setConnectMinTimeout:(NSTimeInterval)connectMinTimeout {

+ 0 - 2
src/objective-c/GRPCClient/private/GRPCChannel.m

@@ -109,8 +109,6 @@
   if (_callOptions.keepaliveInterval != 0) {
     args[@GRPC_ARG_KEEPALIVE_TIME_MS] =
         [NSNumber numberWithUnsignedInteger:(NSUInteger)(_callOptions.keepaliveInterval * 1000)];
-  }
-  if (_callOptions.keepaliveTimeout >= 0) {
     args[@GRPC_ARG_KEEPALIVE_TIMEOUT_MS] =
         [NSNumber numberWithUnsignedInteger:(NSUInteger)(_callOptions.keepaliveTimeout * 1000)];
   }

+ 3 - 1
src/objective-c/GRPCClient/private/GRPCChannelPool.m

@@ -118,7 +118,9 @@ static const NSTimeInterval kDefaultChannelDestroyDelay = 30;
     _lastTimedDestroy = nil;
 
     grpc_call *unmanagedCall =
-        [_wrappedChannel unmanagedCallWithPath:path completionQueue:queue callOptions:callOptions];
+        [_wrappedChannel unmanagedCallWithPath:path
+                               completionQueue:[GRPCCompletionQueue completionQueue]
+                                   callOptions:callOptions];
     if (unmanagedCall == NULL) {
       NSAssert(unmanagedCall != NULL, @"Unable to create grpc_call object");
       return nil;

+ 5 - 5
src/objective-c/GRPCClient/private/GRPCHost.m

@@ -105,11 +105,11 @@ static NSMutableDictionary *gHostCache;
   options.responseSizeLimit = _responseSizeLimitOverride;
   options.compressionAlgorithm = (GRPCCompressionAlgorithm)_compressAlgorithm;
   options.retryEnabled = _retryEnabled;
-  options.keepaliveInterval = (NSTimeInterval)_keepaliveInterval / 1000.0;
-  options.keepaliveTimeout = (NSTimeInterval)_keepaliveTimeout / 1000.0;
-  options.connectMinTimeout = (NSTimeInterval)_minConnectTimeout / 1000.0;
-  options.connectInitialBackoff = (NSTimeInterval)_initialConnectBackoff / 1000.0;
-  options.connectMaxBackoff = (NSTimeInterval)_maxConnectBackoff / 1000.0;
+  options.keepaliveInterval = (NSTimeInterval)_keepaliveInterval / 1000;
+  options.keepaliveTimeout = (NSTimeInterval)_keepaliveTimeout / 1000;
+  options.connectMinTimeout = (NSTimeInterval)_minConnectTimeout / 1000;
+  options.connectInitialBackoff = (NSTimeInterval)_initialConnectBackoff / 1000;
+  options.connectMaxBackoff = (NSTimeInterval)_maxConnectBackoff / 1000;
   options.PEMRootCertificates = _PEMRootCertificates;
   options.PEMPrivateKey = _PEMPrivateKey;
   options.PEMCertificateChain = _PEMCertificateChain;

+ 0 - 4
src/objective-c/tests/CronetTests/InteropTestsRemoteWithCronet.m

@@ -48,10 +48,6 @@ static int32_t kRemoteInteropServerOverhead = 12;
   return YES;
 }
 
-+ (BOOL)canRunCompressionTest {
-  return NO;
-}
-
 - (int32_t)encodingOverhead {
   return kRemoteInteropServerOverhead;  // bytes
 }

+ 0 - 6
src/objective-c/tests/InteropTests/InteropTests.h

@@ -64,10 +64,4 @@
  */
 + (BOOL)useCronet;
 
-/**
- * Whether we can run compression tests in the test suite.
- */
-
-+ (BOOL)canRunCompressionTest;
-
 @end

+ 9 - 181
src/objective-c/tests/InteropTests/InteropTests.m

@@ -347,10 +347,6 @@ initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
   return NO;
 }
 
-+ (BOOL)canRunCompressionTest {
-  return YES;
-}
-
 + (void)setUp {
 #ifdef GRPC_COMPILE_WITH_CRONET
   configureCronet();
@@ -434,16 +430,10 @@ initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
 
   __block BOOL messageReceived = NO;
   __block BOOL done = NO;
-  __block BOOL initialMetadataReceived = YES;
   NSCondition *cond = [[NSCondition alloc] init];
   GRPCUnaryProtoCall *call = [_service
       emptyCallWithMessage:request
-           responseHandler:[[InteropTestsBlockCallbacks alloc]
-                               initWithInitialMetadataCallback:^(NSDictionary *initialMetadata) {
-                                 [cond lock];
-                                 initialMetadataReceived = YES;
-                                 [cond unlock];
-                               }
+           responseHandler:[[InteropTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
                                messageCallback:^(id message) {
                                  if (message) {
                                    id expectedResponse = [GPBEmpty message];
@@ -469,7 +459,6 @@ initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
   while (!done && [deadline timeIntervalSinceNow] > 0) {
     [cond waitUntilDate:deadline];
   }
-  XCTAssertTrue(initialMetadataReceived);
   XCTAssertTrue(messageReceived);
   XCTAssertTrue(done);
   [cond unlock];
@@ -1025,78 +1014,6 @@ initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
   [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
-- (void)testInitialMetadataWithV2API {
-  __weak XCTestExpectation *initialMetadataReceived =
-      [self expectationWithDescription:@"Received initial metadata."];
-  __weak XCTestExpectation *closeReceived = [self expectationWithDescription:@"RPC completed."];
-
-  __block NSDictionary *init_md =
-      [NSDictionary dictionaryWithObjectsAndKeys:@"FOOBAR", @"x-grpc-test-echo-initial", nil];
-  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
-  options.initialMetadata = init_md;
-  options.transportType = self.class.transportType;
-  options.PEMRootCertificates = self.class.PEMRootCertificates;
-  options.hostNameOverride = [[self class] hostNameOverride];
-  RMTSimpleRequest *request = [RMTSimpleRequest message];
-  __block bool init_md_received = NO;
-  GRPCUnaryProtoCall *call = [_service
-      unaryCallWithMessage:request
-           responseHandler:[[InteropTestsBlockCallbacks alloc]
-                               initWithInitialMetadataCallback:^(NSDictionary *initialMetadata) {
-                                 XCTAssertEqualObjects(initialMetadata[@"x-grpc-test-echo-initial"],
-                                                       init_md[@"x-grpc-test-echo-initial"]);
-                                 init_md_received = YES;
-                                 [initialMetadataReceived fulfill];
-                               }
-                               messageCallback:nil
-                               closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
-                                 XCTAssertNil(error, @"Unexpected error: %@", error);
-                                 [closeReceived fulfill];
-                               }]
-               callOptions:options];
-
-  [call start];
-  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
-}
-
-- (void)testTrailingMetadataWithV2API {
-  // This test needs to be disabled for remote test because interop server grpc-test
-  // does not send trailing binary metadata.
-  if (isRemoteInteropTest([[self class] host])) {
-    return;
-  }
-
-  __weak XCTestExpectation *expectation =
-      [self expectationWithDescription:@"Received trailing metadata."];
-  const unsigned char raw_bytes[] = {0x1, 0x2, 0x3, 0x4};
-  NSData *trailer_data = [NSData dataWithBytes:raw_bytes length:sizeof(raw_bytes)];
-  __block NSDictionary *trailer = [NSDictionary
-      dictionaryWithObjectsAndKeys:trailer_data, @"x-grpc-test-echo-trailing-bin", nil];
-  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
-  options.initialMetadata = trailer;
-  options.transportType = self.class.transportType;
-  options.PEMRootCertificates = self.class.PEMRootCertificates;
-  options.hostNameOverride = [[self class] hostNameOverride];
-  RMTSimpleRequest *request = [RMTSimpleRequest message];
-  GRPCUnaryProtoCall *call = [_service
-      unaryCallWithMessage:request
-           responseHandler:
-               [[InteropTestsBlockCallbacks alloc]
-                   initWithInitialMetadataCallback:nil
-                                   messageCallback:nil
-                                     closeCallback:^(NSDictionary *trailingMetadata,
-                                                     NSError *error) {
-                                       XCTAssertNil(error, @"Unexpected error: %@", error);
-                                       XCTAssertEqualObjects(
-                                           trailingMetadata[@"x-grpc-test-echo-trailing-bin"],
-                                           trailer[@"x-grpc-test-echo-trailing-bin"]);
-                                       [expectation fulfill];
-                                     }]
-               callOptions:options];
-  [call start];
-  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
-}
-
 - (void)testCancelAfterFirstResponseRPC {
   XCTAssertNotNil([[self class] host]);
   __weak XCTestExpectation *expectation =
@@ -1231,7 +1148,13 @@ initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
   [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
-- (void)RPCWithCompressMethod:(GRPCCompressionAlgorithm)compressMethod {
+- (void)testCompressedUnaryRPC {
+  // This test needs to be disabled for remote test because interop server grpc-test
+  // does not support compression.
+  if (isRemoteInteropTest([[self class] host])) {
+    return;
+  }
+  XCTAssertNotNil([[self class] host]);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"LargeUnary"];
 
   RMTSimpleRequest *request = [RMTSimpleRequest message];
@@ -1239,7 +1162,7 @@ initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
   request.responseSize = 314159;
   request.payload.body = [NSMutableData dataWithLength:271828];
   request.expectCompressed.value = YES;
-  [GRPCCall setDefaultCompressMethod:compressMethod forhost:[[self class] host]];
+  [GRPCCall setDefaultCompressMethod:GRPCCompressGzip forhost:[[self class] host]];
 
   [_service unaryCallWithRequest:request
                          handler:^(RMTSimpleResponse *response, NSError *error) {
@@ -1256,67 +1179,6 @@ initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
   [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
-- (void)RPCWithCompressMethodWithV2API:(GRPCCompressionAlgorithm)compressMethod {
-  __weak XCTestExpectation *expectMessage =
-      [self expectationWithDescription:@"Reived response from server."];
-  __weak XCTestExpectation *expectComplete = [self expectationWithDescription:@"RPC completed."];
-
-  RMTSimpleRequest *request = [RMTSimpleRequest message];
-  request.responseType = RMTPayloadType_Compressable;
-  request.responseSize = 314159;
-  request.payload.body = [NSMutableData dataWithLength:271828];
-  request.expectCompressed.value = YES;
-
-  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
-  options.transportType = self.class.transportType;
-  options.PEMRootCertificates = self.class.PEMRootCertificates;
-  options.hostNameOverride = [[self class] hostNameOverride];
-  options.compressionAlgorithm = compressMethod;
-
-  GRPCUnaryProtoCall *call = [_service
-      unaryCallWithMessage:request
-           responseHandler:[[InteropTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
-                               messageCallback:^(id message) {
-                                 XCTAssertNotNil(message);
-                                 if (message) {
-                                   RMTSimpleResponse *expectedResponse =
-                                       [RMTSimpleResponse message];
-                                   expectedResponse.payload.type = RMTPayloadType_Compressable;
-                                   expectedResponse.payload.body =
-                                       [NSMutableData dataWithLength:314159];
-                                   XCTAssertEqualObjects(message, expectedResponse);
-
-                                   [expectMessage fulfill];
-                                 }
-                               }
-                               closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
-                                 XCTAssertNil(error, @"Unexpected error: %@", error);
-                                 [expectComplete fulfill];
-                               }]
-               callOptions:options];
-  [call start];
-
-  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
-}
-
-- (void)testCompressedUnaryRPC {
-  if ([[self class] canRunCompressionTest]) {
-    for (GRPCCompressionAlgorithm compress = GRPCCompressDeflate;
-         compress <= GRPCStreamCompressGzip; ++compress) {
-      [self RPCWithCompressMethod:compress];
-    }
-  }
-}
-
-- (void)testCompressedUnaryRPCWithV2API {
-  if ([[self class] canRunCompressionTest]) {
-    for (GRPCCompressionAlgorithm compress = GRPCCompressDeflate;
-         compress <= GRPCStreamCompressGzip; ++compress) {
-      [self RPCWithCompressMethodWithV2API:compress];
-    }
-  }
-}
-
 #ifndef GRPC_COMPILE_WITH_CRONET
 - (void)testKeepalive {
   XCTAssertNotNil([[self class] host]);
@@ -1358,40 +1220,6 @@ initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
 
   [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
-
-- (void)testKeepaliveWithV2API {
-  XCTAssertNotNil([[self class] host]);
-  __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Keepalive"];
-
-  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
-  options.transportType = self.class.transportType;
-  options.PEMRootCertificates = self.class.PEMRootCertificates;
-  options.hostNameOverride = [[self class] hostNameOverride];
-  options.keepaliveInterval = 1.5;
-  options.keepaliveTimeout = 0;
-
-  id request =
-      [RMTStreamingOutputCallRequest messageWithPayloadSize:@21782 requestedResponseSize:@31415];
-
-  __block GRPCStreamingProtoCall *call = [_service
-      fullDuplexCallWithResponseHandler:[[InteropTestsBlockCallbacks alloc]
-                                            initWithInitialMetadataCallback:nil
-                                                            messageCallback:nil
-                                                              closeCallback:^(
-                                                                  NSDictionary *trailingMetadata,
-                                                                  NSError *error) {
-                                                                XCTAssertNotNil(error);
-                                                                XCTAssertEqual(
-                                                                    error.code,
-                                                                    GRPC_STATUS_UNAVAILABLE);
-                                                                [expectation fulfill];
-                                                              }]
-                            callOptions:options];
-  [call start];
-  [call writeMessage:request];
-
-  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
-}
 #endif
 
 - (void)testDefaultInterceptor {

+ 0 - 4
src/objective-c/tests/InteropTests/InteropTestsRemote.m

@@ -49,10 +49,6 @@ static int32_t kRemoteInteropServerOverhead = 12;
   return nil;
 }
 
-+ (BOOL)canRunCompressionTest {
-  return NO;
-}
-
 - (int32_t)encodingOverhead {
   return kRemoteInteropServerOverhead;  // bytes
 }

+ 3 - 380
src/objective-c/tests/MacTests/StressTests.m

@@ -29,7 +29,7 @@
 #import <grpc/grpc.h>
 #import <grpc/support/log.h>
 
-#define TEST_TIMEOUT 64
+#define TEST_TIMEOUT 32
 
 extern const char *kCFStreamVarName;
 
@@ -136,11 +136,7 @@ extern const char *kCFStreamVarName;
   return GRPCTransportTypeChttp2BoringSSL;
 }
 
-- (int)getRandomNumberBetween:(int)min max:(int)max {
-  return min + arc4random_uniform((max - min + 1));
-}
-
-- (void)testNetworkFlapOnUnaryCallWithV2API {
+- (void)testNetworkFlapWithV2API {
   NSMutableArray *completeExpectations = [NSMutableArray array];
   NSMutableArray *calls = [NSMutableArray array];
   int num_rpcs = 100;
@@ -179,7 +175,6 @@ extern const char *kCFStreamVarName;
                                            UTF8String]);
                                        address_removed = YES;
                                      } else if (error != nil && !address_readded) {
-                                       XCTAssertTrue(address_removed);
                                        system([
                                            [NSString stringWithFormat:@"sudo ifconfig lo0 alias %@",
                                                                       [[self class] hostAddress]]
@@ -201,241 +196,7 @@ extern const char *kCFStreamVarName;
   [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
 }
 
-- (void)testNetworkFlapOnClientStreamingCallWithV2API {
-  NSMutableArray *completeExpectations = [NSMutableArray array];
-  NSMutableArray *calls = [NSMutableArray array];
-  int num_rpcs = 100;
-  __block BOOL address_removed = FALSE;
-  __block BOOL address_readded = FALSE;
-  for (int i = 0; i < num_rpcs; ++i) {
-    [completeExpectations
-        addObject:[self expectationWithDescription:
-                            [NSString stringWithFormat:@"Received trailer for RPC %d", i]]];
-
-    GRPCStreamingProtoCall *call = [_service
-        streamingInputCallWithResponseHandler:
-            [[MacTestsBlockCallbacks alloc]
-                initWithInitialMetadataCallback:nil
-                                messageCallback:nil
-                                  closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
-                                    @synchronized(self) {
-                                      if (error == nil && !address_removed) {
-                                        system([[NSString
-                                            stringWithFormat:@"sudo ifconfig lo0 -alias %@",
-                                                             [[self class] hostAddress]]
-                                            UTF8String]);
-                                        address_removed = YES;
-                                      } else if (error != nil && !address_readded) {
-                                        XCTAssertTrue(address_removed);
-                                        system([[NSString
-                                            stringWithFormat:@"sudo ifconfig lo0 alias %@",
-                                                             [[self class] hostAddress]]
-                                            UTF8String]);
-                                        address_readded = YES;
-                                      }
-                                    }
-                                    [completeExpectations[i] fulfill];
-                                  }]
-                                  callOptions:nil];
-    [calls addObject:call];
-  }
-
-  for (int i = 0; i < num_rpcs; ++i) {
-    GRPCStreamingProtoCall *call = calls[i];
-    [call start];
-    RMTStreamingInputCallRequest *request1 = [RMTStreamingInputCallRequest message];
-    request1.payload.body = [NSMutableData dataWithLength:27182];
-    RMTStreamingInputCallRequest *request2 = [RMTStreamingInputCallRequest message];
-    request2.payload.body = [NSMutableData dataWithLength:8];
-
-    [call writeMessage:request1];
-    [NSThread sleepForTimeInterval:0.1f];
-    [call writeMessage:request2];
-    [call finish];
-  }
-  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
-}
-
-- (void)testNetworkFlapOnServerStreamingCallWithV2API {
-  NSMutableArray *completeExpectations = [NSMutableArray array];
-  NSMutableArray *calls = [NSMutableArray array];
-  int num_rpcs = 100;
-  __block BOOL address_removed = FALSE;
-  __block BOOL address_readded = FALSE;
-  for (int i = 0; i < num_rpcs; ++i) {
-    [completeExpectations
-        addObject:[self expectationWithDescription:
-                            [NSString stringWithFormat:@"Received trailer for RPC %d", i]]];
-
-    RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
-    for (int i = 0; i < 5; i++) {
-      RMTResponseParameters *parameters = [RMTResponseParameters message];
-      parameters.size = 10000;
-      [request.responseParametersArray addObject:parameters];
-    }
-
-    request.payload.body = [NSMutableData dataWithLength:100];
-
-    GRPCUnaryProtoCall *call = [_service
-        streamingOutputCallWithMessage:request
-                       responseHandler:
-                           [[MacTestsBlockCallbacks alloc]
-                               initWithInitialMetadataCallback:nil
-                                               messageCallback:nil
-                                                 closeCallback:^(NSDictionary *trailingMetadata,
-                                                                 NSError *error) {
-                                                   @synchronized(self) {
-                                                     if (error == nil && !address_removed) {
-                                                       system([[NSString
-                                                           stringWithFormat:
-                                                               @"sudo ifconfig lo0 -alias %@",
-                                                               [[self class] hostAddress]]
-                                                           UTF8String]);
-                                                       address_removed = YES;
-                                                     } else if (error != nil && !address_readded) {
-                                                       XCTAssertTrue(address_removed);
-                                                       system([[NSString
-                                                           stringWithFormat:
-                                                               @"sudo ifconfig lo0 alias %@",
-                                                               [[self class] hostAddress]]
-                                                           UTF8String]);
-                                                       address_readded = YES;
-                                                     }
-                                                   }
-                                                   [completeExpectations[i] fulfill];
-                                                 }]
-                           callOptions:nil];
-    [calls addObject:call];
-  }
-
-  for (int i = 0; i < num_rpcs; ++i) {
-    GRPCStreamingProtoCall *call = calls[i];
-    [call start];
-    [NSThread sleepForTimeInterval:0.1f];
-  }
-  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
-}
-
-- (void)testNetworkFlapOnHalfDuplexCallWithV2API {
-  NSMutableArray *completeExpectations = [NSMutableArray array];
-  NSMutableArray *calls = [NSMutableArray array];
-  int num_rpcs = 100;
-  __block BOOL address_removed = FALSE;
-  __block BOOL address_readded = FALSE;
-  for (int i = 0; i < num_rpcs; ++i) {
-    [completeExpectations
-        addObject:[self expectationWithDescription:
-                            [NSString stringWithFormat:@"Received trailer for RPC %d", i]]];
-
-    GRPCStreamingProtoCall *call = [_service
-        halfDuplexCallWithResponseHandler:
-            [[MacTestsBlockCallbacks alloc]
-                initWithInitialMetadataCallback:nil
-                                messageCallback:nil
-                                  closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
-                                    @synchronized(self) {
-                                      if (error == nil && !address_removed) {
-                                        system([[NSString
-                                            stringWithFormat:@"sudo ifconfig lo0 -alias %@",
-                                                             [[self class] hostAddress]]
-                                            UTF8String]);
-                                        address_removed = YES;
-                                      } else if (error != nil && !address_readded) {
-                                        XCTAssertTrue(address_removed);
-                                        system([[NSString
-                                            stringWithFormat:@"sudo ifconfig lo0 alias %@",
-                                                             [[self class] hostAddress]]
-                                            UTF8String]);
-                                        address_readded = YES;
-                                      }
-                                    }
-                                    [completeExpectations[i] fulfill];
-                                  }]
-                              callOptions:nil];
-    [calls addObject:call];
-  }
-
-  for (int i = 0; i < num_rpcs; ++i) {
-    GRPCStreamingProtoCall *call = calls[i];
-    [call start];
-    RMTStreamingOutputCallRequest *request1 = [RMTStreamingOutputCallRequest message];
-    RMTStreamingOutputCallRequest *request2 = [RMTStreamingOutputCallRequest message];
-    for (int i = 0; i < 5; i++) {
-      RMTResponseParameters *parameters = [RMTResponseParameters message];
-      parameters.size = 1000;
-      [request1.responseParametersArray addObject:parameters];
-      [request2.responseParametersArray addObject:parameters];
-    }
-
-    request1.payload.body = [NSMutableData dataWithLength:100];
-    request2.payload.body = [NSMutableData dataWithLength:100];
-
-    [call writeMessage:request1];
-    [NSThread sleepForTimeInterval:0.1f];
-    [call writeMessage:request2];
-    [call finish];
-  }
-  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
-}
-
-- (void)testNetworkFlapOnFullDuplexCallWithV2API {
-  NSMutableArray *completeExpectations = [NSMutableArray array];
-  NSMutableArray *calls = [NSMutableArray array];
-  int num_rpcs = 100;
-  __block BOOL address_removed = FALSE;
-  __block BOOL address_readded = FALSE;
-  for (int i = 0; i < num_rpcs; ++i) {
-    [completeExpectations
-        addObject:[self expectationWithDescription:
-                            [NSString stringWithFormat:@"Received trailer for RPC %d", i]]];
-
-    GRPCStreamingProtoCall *call = [_service
-        fullDuplexCallWithResponseHandler:
-            [[MacTestsBlockCallbacks alloc]
-                initWithInitialMetadataCallback:nil
-                                messageCallback:nil
-                                  closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
-                                    @synchronized(self) {
-                                      if (error == nil && !address_removed) {
-                                        system([[NSString
-                                            stringWithFormat:@"sudo ifconfig lo0 -alias %@",
-                                                             [[self class] hostAddress]]
-                                            UTF8String]);
-                                        address_removed = YES;
-                                      } else if (error != nil && !address_readded) {
-                                        XCTAssertTrue(address_removed);
-                                        system([[NSString
-                                            stringWithFormat:@"sudo ifconfig lo0 alias %@",
-                                                             [[self class] hostAddress]]
-                                            UTF8String]);
-                                        address_readded = YES;
-                                      }
-                                    }
-                                    [completeExpectations[i] fulfill];
-                                  }]
-                              callOptions:nil];
-    [calls addObject:call];
-  }
-
-  for (int i = 0; i < num_rpcs; ++i) {
-    GRPCStreamingProtoCall *call = calls[i];
-    [call start];
-
-    RMTResponseParameters *parameters = [RMTResponseParameters message];
-    parameters.size = 1000;
-    for (int i = 0; i < 5; i++) {
-      RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
-      [request.responseParametersArray addObject:parameters];
-      request.payload.body = [NSMutableData dataWithLength:100];
-      [call writeMessage:request];
-    }
-    [call finish];
-    [NSThread sleepForTimeInterval:0.1f];
-  }
-  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
-}
-
-- (void)testNetworkFlapOnUnaryCallWithV1API {
+- (void)testNetworkFlapWithV1API {
   NSMutableArray *completeExpectations = [NSMutableArray array];
   int num_rpcs = 100;
   __block BOOL address_removed = FALSE;
@@ -459,7 +220,6 @@ extern const char *kCFStreamVarName;
                                      UTF8String]);
                                  address_removed = YES;
                                } else if (error != nil && !address_readded) {
-                                 XCTAssertTrue(address_removed);
                                  system([[NSString stringWithFormat:@"sudo ifconfig lo0 alias %@",
                                                                     [[self class] hostAddress]]
                                      UTF8String]);
@@ -474,141 +234,4 @@ extern const char *kCFStreamVarName;
   }
 }
 
-- (void)testTimeoutOnFullDuplexCallWithV2API {
-  NSMutableArray *completeExpectations = [NSMutableArray array];
-  NSMutableArray *calls = [NSMutableArray array];
-  int num_rpcs = 100;
-  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
-  options.transportType = [[self class] transportType];
-  options.PEMRootCertificates = [[self class] PEMRootCertificates];
-  options.hostNameOverride = [[self class] hostNameOverride];
-  options.timeout = 0.3;
-  for (int i = 0; i < num_rpcs; ++i) {
-    [completeExpectations
-        addObject:[self expectationWithDescription:
-                            [NSString stringWithFormat:@"Received trailer for RPC %d", i]]];
-
-    GRPCStreamingProtoCall *call = [_service
-        fullDuplexCallWithResponseHandler:
-            [[MacTestsBlockCallbacks alloc]
-                initWithInitialMetadataCallback:nil
-                                messageCallback:nil
-                                  closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
-                                    if (error != nil) {
-                                      XCTAssertEqual(error.code, GRPC_STATUS_DEADLINE_EXCEEDED);
-                                    }
-                                    [completeExpectations[i] fulfill];
-                                  }]
-                              callOptions:options];
-    [calls addObject:call];
-  }
-
-  for (int i = 0; i < num_rpcs; ++i) {
-    GRPCStreamingProtoCall *call = calls[i];
-    [call start];
-    RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
-    RMTResponseParameters *parameters = [RMTResponseParameters message];
-    parameters.size = 1000;
-    // delay response by 100-200 milliseconds
-    parameters.intervalUs = [self getRandomNumberBetween:100 * 1000 max:200 * 1000];
-    [request.responseParametersArray addObject:parameters];
-    request.payload.body = [NSMutableData dataWithLength:100];
-
-    [call writeMessage:request];
-    [call writeMessage:request];
-    [call finish];
-  }
-  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
-}
-
-- (void)testServerStreamingCallSlowClientWithV2API {
-  NSMutableArray *completeExpectations = [NSMutableArray array];
-  NSMutableArray *calls = [NSMutableArray array];
-  int num_rpcs = 100;
-  dispatch_queue_t q = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
-  for (int i = 0; i < num_rpcs; ++i) {
-    [completeExpectations
-        addObject:[self expectationWithDescription:
-                            [NSString stringWithFormat:@"Received trailer for RPC %d", i]]];
-
-    RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
-    for (int i = 0; i < 5; i++) {
-      RMTResponseParameters *parameters = [RMTResponseParameters message];
-      parameters.size = 10000;
-      [request.responseParametersArray addObject:parameters];
-      [request.responseParametersArray addObject:parameters];
-      [request.responseParametersArray addObject:parameters];
-      [request.responseParametersArray addObject:parameters];
-      [request.responseParametersArray addObject:parameters];
-    }
-
-    request.payload.body = [NSMutableData dataWithLength:100];
-
-    GRPCUnaryProtoCall *call = [_service
-        streamingOutputCallWithMessage:request
-                       responseHandler:[[MacTestsBlockCallbacks alloc]
-                                           initWithInitialMetadataCallback:nil
-                                           messageCallback:^(id message) {
-                                             // inject a delay
-                                             [NSThread sleepForTimeInterval:0.5f];
-                                           }
-                                           closeCallback:^(NSDictionary *trailingMetadata,
-                                                           NSError *error) {
-                                             XCTAssertNil(error, @"Unexpected error: %@", error);
-                                             [completeExpectations[i] fulfill];
-                                           }]
-                           callOptions:nil];
-    [calls addObject:call];
-  }
-
-  for (int i = 0; i < num_rpcs; ++i) {
-    dispatch_async(q, ^{
-      GRPCStreamingProtoCall *call = calls[i];
-      [call start];
-    });
-  }
-  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
-}
-
-- (void)testCancelOnFullDuplexCallWithV2API {
-  NSMutableArray *completeExpectations = [NSMutableArray array];
-  NSMutableArray *calls = [NSMutableArray array];
-  int num_rpcs = 100;
-  dispatch_queue_t q = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
-  for (int i = 0; i < num_rpcs; ++i) {
-    [completeExpectations
-        addObject:[self expectationWithDescription:
-                            [NSString stringWithFormat:@"Received trailer for RPC %d", i]]];
-
-    GRPCStreamingProtoCall *call = [_service
-        fullDuplexCallWithResponseHandler:[[MacTestsBlockCallbacks alloc]
-                                              initWithInitialMetadataCallback:nil
-                                                              messageCallback:nil
-                                                                closeCallback:^(
-                                                                    NSDictionary *trailingMetadata,
-                                                                    NSError *error) {
-                                                                  [completeExpectations[i] fulfill];
-                                                                }]
-                              callOptions:nil];
-    [calls addObject:call];
-  }
-
-  for (int i = 0; i < num_rpcs; ++i) {
-    GRPCStreamingProtoCall *call = calls[i];
-    [call start];
-    dispatch_async(q, ^{
-      RMTResponseParameters *parameters = [RMTResponseParameters message];
-      parameters.size = 1000;
-      for (int i = 0; i < 100; i++) {
-        RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
-        [request.responseParametersArray addObject:parameters];
-        [call writeMessage:request];
-      }
-      [NSThread sleepForTimeInterval:0.01f];
-      [call cancel];
-    });
-  }
-  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
-}
-
 @end

+ 3 - 327
src/objective-c/tests/UnitTests/APIv2Tests.m

@@ -21,11 +21,6 @@
 #import <RemoteTest/Messages.pbobjc.h>
 #import <XCTest/XCTest.h>
 
-#import "../../GRPCClient/private/GRPCCallInternal.h"
-#import "../../GRPCClient/private/GRPCChannel.h"
-#import "../../GRPCClient/private/GRPCChannelPool.h"
-#import "../../GRPCClient/private/GRPCWrappedCall.h"
-
 #include <grpc/grpc.h>
 #include <grpc/support/port_platform.h>
 
@@ -53,41 +48,12 @@ static const int kSimpleDataLength = 100;
 static const NSTimeInterval kTestTimeout = 8;
 static const NSTimeInterval kInvertedTimeout = 2;
 
-// Reveal the _class ivars for testing access
+// Reveal the _class ivar for testing access
 @interface GRPCCall2 () {
- @public
-  id<GRPCInterceptorInterface> _firstInterceptor;
-}
-@end
-
-@interface GRPCCall2Internal () {
  @public
   GRPCCall *_call;
 }
-@end
-
-@interface GRPCCall () {
- @public
-  GRPCWrappedCall *_wrappedCall;
-}
-@end
-
-@interface GRPCWrappedCall () {
- @public
-  GRPCPooledChannel *_pooledChannel;
-}
-@end
-
-@interface GRPCPooledChannel () {
- @public
-  GRPCChannel *_wrappedChannel;
-}
-@end
 
-@interface GRPCChannel () {
- @public
-  grpc_channel *_unmanagedChannel;
-}
 @end
 
 // Convenience class to use blocks as callbacks
@@ -182,7 +148,6 @@ static const NSTimeInterval kInvertedTimeout = 2;
   kOutputStreamingCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
                                                                 service:kService
                                                                  method:@"StreamingOutputCall"];
-
   kFullDuplexCallMethod =
       [[GRPCProtoMethod alloc] initWithPackage:kPackage service:kService method:@"FullDuplexCall"];
 }
@@ -214,7 +179,7 @@ static const NSTimeInterval kInvertedTimeout = 2;
                                  closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
                                    trailing_md = trailingMetadata;
                                    if (error) {
-                                     XCTAssertEqual(error.code, GRPCErrorCodeUnauthenticated,
+                                     XCTAssertEqual(error.code, 16,
                                                     @"Finished with unexpected error: %@", error);
                                      XCTAssertEqualObjects(init_md,
                                                            error.userInfo[kGRPCHeadersKey]);
@@ -794,7 +759,7 @@ static const NSTimeInterval kInvertedTimeout = 2;
                                  }
                                  closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
                                    XCTAssertNotNil(error, @"Expecting non-nil error");
-                                   XCTAssertEqual(error.code, GRPCErrorCodeUnknown);
+                                   XCTAssertEqual(error.code, 2);
                                    [completion fulfill];
                                  }]
                  callOptions:options];
@@ -805,293 +770,4 @@ static const NSTimeInterval kInvertedTimeout = 2;
   [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
 }
 
-- (void)testAdditionalChannelArgs {
-  __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
-
-  GRPCRequestOptions *requestOptions =
-      [[GRPCRequestOptions alloc] initWithHost:kHostAddress
-                                          path:kUnaryCallMethod.HTTPPath
-                                        safety:GRPCCallSafetyDefault];
-  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
-  // set max message length = 1 byte.
-  options.additionalChannelArgs =
-      [NSDictionary dictionaryWithObjectsAndKeys:@1, @GRPC_ARG_MAX_SEND_MESSAGE_LENGTH, nil];
-  options.transportType = GRPCTransportTypeInsecure;
-
-  RMTSimpleRequest *request = [RMTSimpleRequest message];
-  request.payload.body = [NSMutableData dataWithLength:options.responseSizeLimit];
-
-  GRPCCall2 *call = [[GRPCCall2 alloc]
-      initWithRequestOptions:requestOptions
-             responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
-                                 messageCallback:^(NSData *data) {
-                                   XCTFail(@"Received unexpected message");
-                                 }
-                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
-                                   XCTAssertNotNil(error, @"Expecting non-nil error");
-                                   NSLog(@"Got error: %@", error);
-                                   XCTAssertEqual(error.code, GRPCErrorCodeResourceExhausted,
-                                                  @"Finished with unexpected error: %@", error);
-
-                                   [completion fulfill];
-                                 }]
-                 callOptions:options];
-  [call writeData:[request data]];
-  [call start];
-  [call finish];
-
-  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
-}
-
-- (void)testChannelReuseIdentical {
-  __weak XCTestExpectation *completion1 = [self expectationWithDescription:@"RPC1 completed."];
-  __weak XCTestExpectation *completion2 = [self expectationWithDescription:@"RPC2 completed."];
-  NSArray *rpcDone = [NSArray arrayWithObjects:completion1, completion2, nil];
-  __weak XCTestExpectation *metadata1 =
-      [self expectationWithDescription:@"Received initial metadata for RPC1."];
-  __weak XCTestExpectation *metadata2 =
-      [self expectationWithDescription:@"Received initial metadata for RPC2."];
-  NSArray *initialMetadataDone = [NSArray arrayWithObjects:metadata1, metadata2, nil];
-
-  RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
-  RMTResponseParameters *parameters = [RMTResponseParameters message];
-  parameters.size = kSimpleDataLength;
-  [request.responseParametersArray addObject:parameters];
-  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
-
-  GRPCRequestOptions *requestOptions =
-      [[GRPCRequestOptions alloc] initWithHost:kHostAddress
-                                          path:kFullDuplexCallMethod.HTTPPath
-                                        safety:GRPCCallSafetyDefault];
-  GRPCMutableCallOptions *callOptions = [[GRPCMutableCallOptions alloc] init];
-  callOptions.transportType = GRPCTransportTypeInsecure;
-  GRPCCall2 *call1 = [[GRPCCall2 alloc]
-      initWithRequestOptions:requestOptions
-             responseHandler:[[ClientTestsBlockCallbacks alloc]
-                                 initWithInitialMetadataCallback:^(NSDictionary *initialMetadata) {
-                                   [metadata1 fulfill];
-                                 }
-                                 messageCallback:nil
-                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
-                                   [completion1 fulfill];
-                                 }]
-                 callOptions:callOptions];
-  GRPCCall2 *call2 = [[GRPCCall2 alloc]
-      initWithRequestOptions:requestOptions
-             responseHandler:[[ClientTestsBlockCallbacks alloc]
-                                 initWithInitialMetadataCallback:^(NSDictionary *initialMetadata) {
-                                   [metadata2 fulfill];
-                                 }
-                                 messageCallback:nil
-                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
-                                   [completion2 fulfill];
-                                 }]
-                 callOptions:callOptions];
-  [call1 start];
-  [call2 start];
-  [call1 writeData:[request data]];
-  [call2 writeData:[request data]];
-  [self waitForExpectations:initialMetadataDone timeout:kTestTimeout];
-  GRPCCall2Internal *internalCall1 = call1->_firstInterceptor;
-  GRPCCall2Internal *internalCall2 = call2->_firstInterceptor;
-  XCTAssertEqual(
-      internalCall1->_call->_wrappedCall->_pooledChannel->_wrappedChannel->_unmanagedChannel,
-      internalCall2->_call->_wrappedCall->_pooledChannel->_wrappedChannel->_unmanagedChannel);
-  [call1 finish];
-  [call2 finish];
-  [self waitForExpectations:rpcDone timeout:kTestTimeout];
-}
-
-- (void)testChannelReuseDifferentCallSafety {
-  __weak XCTestExpectation *completion1 = [self expectationWithDescription:@"RPC1 completed."];
-  __weak XCTestExpectation *completion2 = [self expectationWithDescription:@"RPC2 completed."];
-  NSArray *rpcDone = [NSArray arrayWithObjects:completion1, completion2, nil];
-  __weak XCTestExpectation *metadata1 =
-      [self expectationWithDescription:@"Received initial metadata for RPC1."];
-  __weak XCTestExpectation *metadata2 =
-      [self expectationWithDescription:@"Received initial metadata for RPC2."];
-  NSArray *initialMetadataDone = [NSArray arrayWithObjects:metadata1, metadata2, nil];
-
-  RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
-  RMTResponseParameters *parameters = [RMTResponseParameters message];
-  parameters.size = kSimpleDataLength;
-  [request.responseParametersArray addObject:parameters];
-  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
-
-  GRPCRequestOptions *requestOptions1 =
-      [[GRPCRequestOptions alloc] initWithHost:kHostAddress
-                                          path:kFullDuplexCallMethod.HTTPPath
-                                        safety:GRPCCallSafetyDefault];
-  GRPCRequestOptions *requestOptions2 =
-      [[GRPCRequestOptions alloc] initWithHost:kHostAddress
-                                          path:kFullDuplexCallMethod.HTTPPath
-                                        safety:GRPCCallSafetyIdempotentRequest];
-
-  GRPCMutableCallOptions *callOptions = [[GRPCMutableCallOptions alloc] init];
-  callOptions.transportType = GRPCTransportTypeInsecure;
-  GRPCCall2 *call1 = [[GRPCCall2 alloc]
-      initWithRequestOptions:requestOptions1
-             responseHandler:[[ClientTestsBlockCallbacks alloc]
-                                 initWithInitialMetadataCallback:^(NSDictionary *initialMetadata) {
-                                   [metadata1 fulfill];
-                                 }
-                                 messageCallback:nil
-                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
-                                   [completion1 fulfill];
-                                 }]
-                 callOptions:callOptions];
-  GRPCCall2 *call2 = [[GRPCCall2 alloc]
-      initWithRequestOptions:requestOptions2
-             responseHandler:[[ClientTestsBlockCallbacks alloc]
-                                 initWithInitialMetadataCallback:^(NSDictionary *initialMetadata) {
-                                   [metadata2 fulfill];
-                                 }
-                                 messageCallback:nil
-                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
-                                   [completion2 fulfill];
-                                 }]
-                 callOptions:callOptions];
-  [call1 start];
-  [call2 start];
-  [call1 writeData:[request data]];
-  [call2 writeData:[request data]];
-  [self waitForExpectations:initialMetadataDone timeout:kTestTimeout];
-  GRPCCall2Internal *internalCall1 = call1->_firstInterceptor;
-  GRPCCall2Internal *internalCall2 = call2->_firstInterceptor;
-  XCTAssertEqual(
-      internalCall1->_call->_wrappedCall->_pooledChannel->_wrappedChannel->_unmanagedChannel,
-      internalCall2->_call->_wrappedCall->_pooledChannel->_wrappedChannel->_unmanagedChannel);
-  [call1 finish];
-  [call2 finish];
-  [self waitForExpectations:rpcDone timeout:kTestTimeout];
-}
-
-- (void)testChannelReuseDifferentHost {
-  __weak XCTestExpectation *completion1 = [self expectationWithDescription:@"RPC1 completed."];
-  __weak XCTestExpectation *completion2 = [self expectationWithDescription:@"RPC2 completed."];
-  NSArray *rpcDone = [NSArray arrayWithObjects:completion1, completion2, nil];
-  __weak XCTestExpectation *metadata1 =
-      [self expectationWithDescription:@"Received initial metadata for RPC1."];
-  __weak XCTestExpectation *metadata2 =
-      [self expectationWithDescription:@"Received initial metadata for RPC2."];
-  NSArray *initialMetadataDone = [NSArray arrayWithObjects:metadata1, metadata2, nil];
-
-  RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
-  RMTResponseParameters *parameters = [RMTResponseParameters message];
-  parameters.size = kSimpleDataLength;
-  [request.responseParametersArray addObject:parameters];
-  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
-
-  GRPCRequestOptions *requestOptions1 =
-      [[GRPCRequestOptions alloc] initWithHost:@"[::1]:5050"
-                                          path:kFullDuplexCallMethod.HTTPPath
-                                        safety:GRPCCallSafetyDefault];
-  GRPCRequestOptions *requestOptions2 =
-      [[GRPCRequestOptions alloc] initWithHost:@"127.0.0.1:5050"
-                                          path:kFullDuplexCallMethod.HTTPPath
-                                        safety:GRPCCallSafetyDefault];
-
-  GRPCMutableCallOptions *callOptions = [[GRPCMutableCallOptions alloc] init];
-  callOptions.transportType = GRPCTransportTypeInsecure;
-
-  GRPCCall2 *call1 = [[GRPCCall2 alloc]
-      initWithRequestOptions:requestOptions1
-             responseHandler:[[ClientTestsBlockCallbacks alloc]
-                                 initWithInitialMetadataCallback:^(NSDictionary *initialMetadata) {
-                                   [metadata1 fulfill];
-                                 }
-                                 messageCallback:nil
-                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
-                                   [completion1 fulfill];
-                                 }]
-                 callOptions:callOptions];
-  GRPCCall2 *call2 = [[GRPCCall2 alloc]
-      initWithRequestOptions:requestOptions2
-             responseHandler:[[ClientTestsBlockCallbacks alloc]
-                                 initWithInitialMetadataCallback:^(NSDictionary *initialMetadata) {
-                                   [metadata2 fulfill];
-                                 }
-                                 messageCallback:nil
-                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
-                                   [completion2 fulfill];
-                                 }]
-                 callOptions:callOptions];
-  [call1 start];
-  [call2 start];
-  [call1 writeData:[request data]];
-  [call2 writeData:[request data]];
-  [self waitForExpectations:initialMetadataDone timeout:kTestTimeout];
-  GRPCCall2Internal *internalCall1 = call1->_firstInterceptor;
-  GRPCCall2Internal *internalCall2 = call2->_firstInterceptor;
-  XCTAssertNotEqual(
-      internalCall1->_call->_wrappedCall->_pooledChannel->_wrappedChannel->_unmanagedChannel,
-      internalCall2->_call->_wrappedCall->_pooledChannel->_wrappedChannel->_unmanagedChannel);
-  [call1 finish];
-  [call2 finish];
-  [self waitForExpectations:rpcDone timeout:kTestTimeout];
-}
-
-- (void)testChannelReuseDifferentChannelArgs {
-  __weak XCTestExpectation *completion1 = [self expectationWithDescription:@"RPC1 completed."];
-  __weak XCTestExpectation *completion2 = [self expectationWithDescription:@"RPC2 completed."];
-  NSArray *rpcDone = [NSArray arrayWithObjects:completion1, completion2, nil];
-  __weak XCTestExpectation *metadata1 =
-      [self expectationWithDescription:@"Received initial metadata for RPC1."];
-  __weak XCTestExpectation *metadata2 =
-      [self expectationWithDescription:@"Received initial metadata for RPC2."];
-  NSArray *initialMetadataDone = [NSArray arrayWithObjects:metadata1, metadata2, nil];
-
-  RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
-  RMTResponseParameters *parameters = [RMTResponseParameters message];
-  parameters.size = kSimpleDataLength;
-  [request.responseParametersArray addObject:parameters];
-  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
-
-  GRPCRequestOptions *requestOptions =
-      [[GRPCRequestOptions alloc] initWithHost:kHostAddress
-                                          path:kFullDuplexCallMethod.HTTPPath
-                                        safety:GRPCCallSafetyDefault];
-  GRPCMutableCallOptions *callOptions1 = [[GRPCMutableCallOptions alloc] init];
-  callOptions1.transportType = GRPCTransportTypeInsecure;
-  GRPCMutableCallOptions *callOptions2 = [[GRPCMutableCallOptions alloc] init];
-  callOptions2.transportType = GRPCTransportTypeInsecure;
-  callOptions2.channelID = 2;
-
-  GRPCCall2 *call1 = [[GRPCCall2 alloc]
-      initWithRequestOptions:requestOptions
-             responseHandler:[[ClientTestsBlockCallbacks alloc]
-                                 initWithInitialMetadataCallback:^(NSDictionary *initialMetadata) {
-                                   [metadata1 fulfill];
-                                 }
-                                 messageCallback:nil
-                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
-                                   [completion1 fulfill];
-                                 }]
-                 callOptions:callOptions1];
-  GRPCCall2 *call2 = [[GRPCCall2 alloc]
-      initWithRequestOptions:requestOptions
-             responseHandler:[[ClientTestsBlockCallbacks alloc]
-                                 initWithInitialMetadataCallback:^(NSDictionary *initialMetadata) {
-                                   [metadata2 fulfill];
-                                 }
-                                 messageCallback:nil
-                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
-                                   [completion2 fulfill];
-                                 }]
-                 callOptions:callOptions2];
-  [call1 start];
-  [call2 start];
-  [call1 writeData:[request data]];
-  [call2 writeData:[request data]];
-  [self waitForExpectations:initialMetadataDone timeout:kTestTimeout];
-  GRPCCall2Internal *internalCall1 = call1->_firstInterceptor;
-  GRPCCall2Internal *internalCall2 = call2->_firstInterceptor;
-  XCTAssertNotEqual(
-      internalCall1->_call->_wrappedCall->_pooledChannel->_wrappedChannel->_unmanagedChannel,
-      internalCall2->_call->_wrappedCall->_pooledChannel->_wrappedChannel->_unmanagedChannel);
-  [call1 finish];
-  [call2 finish];
-  [self waitForExpectations:rpcDone timeout:kTestTimeout];
-}
-
 @end

+ 28 - 0
templates/tools/dockerfile/test/python_stretch_3.8_x64/Dockerfile.template

@@ -0,0 +1,28 @@
+
+%YAML 1.2
+--- |
+  # Copyright 2019 The 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 file="../../python_stretch.include"/>
+  RUN apt-get install -y jq zlib1g-dev libssl-dev
+
+  COPY get_cpython.sh /tmp
+  RUN apt-get install -y jq build-essential libffi-dev && ${'\\'}
+    chmod +x /tmp/get_cpython.sh && ${'\\'}
+    /tmp/get_cpython.sh && ${'\\'}
+    rm /tmp/get_cpython.sh
+
+  RUN python3.8 -m ensurepip && ${'\\'}
+      python3.8 -m pip install coverage

+ 2 - 2
test/core/bad_client/gen_build_yaml.py

@@ -45,7 +45,7 @@ def main():
           {
             'name': 'bad_client_test',
             'build': 'private',
-            'language': 'c',
+            'language': 'c++',
             'src': [
               'test/core/bad_client/bad_client.cc'
             ],
@@ -64,7 +64,7 @@ def main():
               'name': '%s_bad_client_test' % t,
               'cpu_cost': BAD_CLIENT_TESTS[t].cpu_cost,
               'build': 'test',
-              'language': 'c',
+              'language': 'c++',
               'secure': 'no',
               'src': ['test/core/bad_client/tests/%s.cc' % t],
               'vs_proj_dir': 'test',

+ 4 - 0
test/core/bad_client/generate_tests.bzl

@@ -42,6 +42,10 @@ def grpc_bad_client_tests():
       name = 'bad_client_test',
       srcs = ['bad_client.cc'],
       hdrs = ['bad_client.h'],
+      external_deps = [
+        "gtest",
+      ],
+      language = "C++",
       deps = ['//test/core/util:grpc_test_util', '//:grpc', '//:gpr', '//test/core/end2end:cq_verifier']
   )
   for t, topt in BAD_CLIENT_TESTS.items():

+ 30 - 11
test/core/bad_client/tests/unknown_frame.cc

@@ -16,13 +16,14 @@
  *
  */
 
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <grpc/support/string_util.h>
 #include "src/core/lib/surface/server.h"
 #include "test/core/bad_client/bad_client.h"
 
-#define PFX_STR                      \
-  "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" \
-  "\x00\x00\x00\x04\x00\x00\x00\x00\x00"
-
 static void verifier(grpc_server* server, grpc_completion_queue* cq,
                      void* registered_method) {
   while (grpc_server_has_open_connections(server)) {
@@ -32,15 +33,33 @@ static void verifier(grpc_server* server, grpc_completion_queue* cq,
   }
 }
 
+#define APPEND_BUFFER(string, to_append) \
+  ((string).append((to_append), sizeof(to_append) - 1))
+
+namespace {
+TEST(UnknownFrameType, Test) {
+  /* test that all invalid/unknown frame types are handled */
+  for (int i = 10; i <= 255; i++) {
+    std::string unknown_frame_string;
+    APPEND_BUFFER(unknown_frame_string, "\x00\x00\x00");
+    char frame_type = static_cast<char>(i);
+    unknown_frame_string.append(&frame_type, 1);
+    APPEND_BUFFER(unknown_frame_string, "\x00\x00\x00\x00\x01");
+    grpc_bad_client_arg args[2];
+    args[0] = connection_preface_arg;
+    args[1].client_validator = nullptr;
+    args[1].client_payload = unknown_frame_string.c_str();
+    args[1].client_payload_length = unknown_frame_string.size();
+    grpc_run_bad_client_test(verifier, args, 2, GRPC_BAD_CLIENT_DISCONNECT);
+  }
+}
+}  // namespace
+
 int main(int argc, char** argv) {
   grpc_init();
   grpc::testing::TestEnvironment env(argc, argv);
-
-  /* test adding prioritization data */
-  GRPC_RUN_BAD_CLIENT_TEST(verifier, nullptr,
-                           PFX_STR "\x00\x00\x00\x88\x00\x00\x00\x00\x01",
-                           GRPC_BAD_CLIENT_DISCONNECT);
-
+  ::testing::InitGoogleTest(&argc, argv);
+  int retval = RUN_ALL_TESTS();
   grpc_shutdown();
-  return 0;
+  return retval;
 }

+ 29 - 0
test/core/gprpp/map_test.cc

@@ -437,6 +437,35 @@ TEST_F(MapTest, LowerBound) {
   EXPECT_EQ(it, test_map.end());
 }
 
+// Test move ctor
+TEST_F(MapTest, MoveCtor) {
+  Map<const char*, Payload, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.emplace(kKeys[i], Payload(i));
+  }
+  Map<const char*, Payload, StringLess> test_map2 = std::move(test_map);
+  for (int i = 0; i < 5; i++) {
+    EXPECT_EQ(test_map.end(), test_map.find(kKeys[i]));
+    EXPECT_EQ(i, test_map2.find(kKeys[i])->second.data());
+  }
+}
+
+// Test move assignment
+TEST_F(MapTest, MoveAssignment) {
+  Map<const char*, Payload, StringLess> test_map;
+  for (int i = 0; i < 5; i++) {
+    test_map.emplace(kKeys[i], Payload(i));
+  }
+  Map<const char*, Payload, StringLess> test_map2;
+  test_map2.emplace("xxx", Payload(123));
+  test_map2 = std::move(test_map);
+  for (int i = 0; i < 5; i++) {
+    EXPECT_EQ(test_map.end(), test_map.find(kKeys[i]));
+    EXPECT_EQ(i, test_map2.find(kKeys[i])->second.data());
+  }
+  EXPECT_EQ(test_map2.end(), test_map2.find("xxx"));
+}
+
 }  // namespace testing
 }  // namespace grpc_core
 

+ 1 - 1
test/core/util/test_lb_policies.cc

@@ -117,7 +117,7 @@ class InterceptRecvTrailingMetadataLoadBalancingPolicy
     PickResult Pick(PickArgs args) override {
       PickResult result = delegate_picker_->Pick(args);
       if (result.type == PickResult::PICK_COMPLETE &&
-          result.connected_subchannel != nullptr) {
+          result.subchannel != nullptr) {
         new (args.call_state->Alloc(sizeof(TrailingMetadataHandler)))
             TrailingMetadataHandler(&result, cb_, user_data_);
       }

+ 1 - 2
test/cpp/interop/interop_server.cc

@@ -118,8 +118,7 @@ bool CheckExpectedCompression(const ServerContext& context,
               "Expected compression but got uncompressed request from client.");
       return false;
     }
-    if (!(inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS) &&
-        received_compression != GRPC_COMPRESS_STREAM_GZIP) {
+    if (!(inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS)) {
       gpr_log(GPR_ERROR,
               "Failure: Requested compression in a compressable request, but "
               "compression bit in message flags not set.");

+ 79 - 0
tools/dockerfile/test/python_stretch_3.8_x64/Dockerfile

@@ -0,0 +1,79 @@
+# Copyright 2019 The 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.
+
+FROM debian:stretch
+  
+# Install Git and basic packages.
+RUN apt-get update && apt-get install -y \
+  autoconf \
+  autotools-dev \
+  build-essential \
+  bzip2 \
+  ccache \
+  curl \
+  dnsutils \
+  gcc \
+  gcc-multilib \
+  git \
+  golang \
+  gyp \
+  lcov \
+  libc6 \
+  libc6-dbg \
+  libc6-dev \
+  libgtest-dev \
+  libtool \
+  make \
+  perl \
+  strace \
+  python-dev \
+  python-setuptools \
+  python-yaml \
+  telnet \
+  unzip \
+  wget \
+  zip && apt-get clean
+
+#================
+# Build profiling
+RUN apt-get update && apt-get install -y time && apt-get clean
+
+# Google Cloud platform API libraries
+RUN apt-get update && apt-get install -y python-pip && apt-get clean
+RUN pip install --upgrade google-api-python-client oauth2client
+
+# Install Python 2.7
+RUN apt-get update && apt-get install -y python2.7 python-all-dev
+RUN curl https://bootstrap.pypa.io/get-pip.py | python2.7
+
+# Add Debian 'testing' repository
+RUN echo 'deb http://ftp.de.debian.org/debian testing main' >> /etc/apt/sources.list
+RUN echo 'APT::Default-Release "stable";' | tee -a /etc/apt/apt.conf.d/00local
+
+
+RUN mkdir /var/local/jenkins
+
+# Define the default command.
+CMD ["bash"]
+
+RUN apt-get install -y jq zlib1g-dev libssl-dev
+
+COPY get_cpython.sh /tmp
+RUN apt-get install -y jq build-essential libffi-dev && \
+  chmod +x /tmp/get_cpython.sh && \
+  /tmp/get_cpython.sh && \
+  rm /tmp/get_cpython.sh
+
+RUN python3.8 -m ensurepip && \
+    python3.8 -m pip install coverage

+ 29 - 0
tools/dockerfile/test/python_stretch_3.8_x64/get_cpython.sh

@@ -0,0 +1,29 @@
+#!/bin/bash
+
+# Copyright 2019 The 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.
+
+VERSION_REGEX="v3.8.*"
+REPO="python/cpython"
+
+LATEST=$(curl -s https://api.github.com/repos/$REPO/tags | \
+          jq -r '.[] | select(.name|test("'$VERSION_REGEX'")) | .name' \
+          | sort | tail -n1)
+
+wget https://github.com/$REPO/archive/$LATEST.tar.gz
+tar xzvf *.tar.gz
+( cd cpython*
+  ./configure
+  make install
+)

+ 12 - 12
tools/run_tests/generated/sources_and_headers.json

@@ -5292,7 +5292,7 @@
     ], 
     "headers": [], 
     "is_filegroup": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "badreq_bad_client_test", 
     "src": [
       "test/core/bad_client/tests/badreq.cc"
@@ -5309,7 +5309,7 @@
     ], 
     "headers": [], 
     "is_filegroup": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "connection_prefix_bad_client_test", 
     "src": [
       "test/core/bad_client/tests/connection_prefix.cc"
@@ -5326,7 +5326,7 @@
     ], 
     "headers": [], 
     "is_filegroup": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "duplicate_header_bad_client_test", 
     "src": [
       "test/core/bad_client/tests/duplicate_header.cc"
@@ -5343,7 +5343,7 @@
     ], 
     "headers": [], 
     "is_filegroup": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "head_of_line_blocking_bad_client_test", 
     "src": [
       "test/core/bad_client/tests/head_of_line_blocking.cc"
@@ -5360,7 +5360,7 @@
     ], 
     "headers": [], 
     "is_filegroup": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "headers_bad_client_test", 
     "src": [
       "test/core/bad_client/tests/headers.cc"
@@ -5377,7 +5377,7 @@
     ], 
     "headers": [], 
     "is_filegroup": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "initial_settings_frame_bad_client_test", 
     "src": [
       "test/core/bad_client/tests/initial_settings_frame.cc"
@@ -5394,7 +5394,7 @@
     ], 
     "headers": [], 
     "is_filegroup": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "large_metadata_bad_client_test", 
     "src": [
       "test/core/bad_client/tests/large_metadata.cc"
@@ -5411,7 +5411,7 @@
     ], 
     "headers": [], 
     "is_filegroup": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "server_registered_method_bad_client_test", 
     "src": [
       "test/core/bad_client/tests/server_registered_method.cc"
@@ -5428,7 +5428,7 @@
     ], 
     "headers": [], 
     "is_filegroup": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "simple_request_bad_client_test", 
     "src": [
       "test/core/bad_client/tests/simple_request.cc"
@@ -5445,7 +5445,7 @@
     ], 
     "headers": [], 
     "is_filegroup": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "unknown_frame_bad_client_test", 
     "src": [
       "test/core/bad_client/tests/unknown_frame.cc"
@@ -5462,7 +5462,7 @@
     ], 
     "headers": [], 
     "is_filegroup": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "window_overflow_bad_client_test", 
     "src": [
       "test/core/bad_client/tests/window_overflow.cc"
@@ -7756,7 +7756,7 @@
       "test/core/bad_client/bad_client.h"
     ], 
     "is_filegroup": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "bad_client_test", 
     "src": [
       "test/core/bad_client/bad_client.cc", 

+ 11 - 11
tools/run_tests/generated/tests.json

@@ -5958,7 +5958,7 @@
     ], 
     "flaky": false, 
     "gtest": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "badreq_bad_client_test", 
     "platforms": [
       "linux", 
@@ -5984,7 +5984,7 @@
     ], 
     "flaky": false, 
     "gtest": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "connection_prefix_bad_client_test", 
     "platforms": [
       "linux", 
@@ -6010,7 +6010,7 @@
     ], 
     "flaky": false, 
     "gtest": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "duplicate_header_bad_client_test", 
     "platforms": [
       "linux", 
@@ -6036,7 +6036,7 @@
     ], 
     "flaky": false, 
     "gtest": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "head_of_line_blocking_bad_client_test", 
     "platforms": [
       "linux", 
@@ -6062,7 +6062,7 @@
     ], 
     "flaky": false, 
     "gtest": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "headers_bad_client_test", 
     "platforms": [
       "linux", 
@@ -6088,7 +6088,7 @@
     ], 
     "flaky": false, 
     "gtest": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "initial_settings_frame_bad_client_test", 
     "platforms": [
       "linux", 
@@ -6114,7 +6114,7 @@
     ], 
     "flaky": false, 
     "gtest": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "large_metadata_bad_client_test", 
     "platforms": [
       "linux", 
@@ -6140,7 +6140,7 @@
     ], 
     "flaky": false, 
     "gtest": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "server_registered_method_bad_client_test", 
     "platforms": [
       "linux", 
@@ -6166,7 +6166,7 @@
     ], 
     "flaky": false, 
     "gtest": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "simple_request_bad_client_test", 
     "platforms": [
       "linux", 
@@ -6192,7 +6192,7 @@
     ], 
     "flaky": false, 
     "gtest": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "unknown_frame_bad_client_test", 
     "platforms": [
       "linux", 
@@ -6218,7 +6218,7 @@
     ], 
     "flaky": false, 
     "gtest": false, 
-    "language": "c", 
+    "language": "c++", 
     "name": "window_overflow_bad_client_test", 
     "platforms": [
       "linux", 

+ 14 - 4
tools/run_tests/run_tests.py

@@ -753,7 +753,7 @@ class PythonLanguage(object):
     def _python_manager_name(self):
         """Choose the docker image to use based on python version."""
         if self.args.compiler in [
-                'python2.7', 'python3.5', 'python3.6', 'python3.7'
+                'python2.7', 'python3.5', 'python3.6', 'python3.7', 'python3.8'
         ]:
             return 'stretch_' + self.args.compiler[len('python'):]
         elif self.args.compiler == 'python_alpine':
@@ -829,6 +829,12 @@ class PythonLanguage(object):
             minor='7',
             bits=bits,
             config_vars=config_vars)
+        python38_config = _python_config_generator(
+            name='py38',
+            major='3',
+            minor='8',
+            bits=bits,
+            config_vars=config_vars)
         pypy27_config = _pypy_config_generator(
             name='pypy', major='2', config_vars=config_vars)
         pypy32_config = _pypy_config_generator(
@@ -852,6 +858,8 @@ class PythonLanguage(object):
             return (python36_config,)
         elif args.compiler == 'python3.7':
             return (python37_config,)
+        elif args.compiler == 'python3.8':
+            return (python38_config,)
         elif args.compiler == 'pypy':
             return (pypy27_config,)
         elif args.compiler == 'pypy3':
@@ -865,6 +873,7 @@ class PythonLanguage(object):
                 python35_config,
                 python36_config,
                 python37_config,
+                # TODO: Add Python 3.8 once it's released.
             )
         else:
             raise Exception('Compiler %s not supported.' % args.compiler)
@@ -1340,9 +1349,10 @@ argp.add_argument(
     choices=[
         'default', 'gcc4.4', 'gcc4.6', 'gcc4.8', 'gcc4.9', 'gcc5.3', 'gcc7.2',
         'gcc_musl', 'clang3.4', 'clang3.5', 'clang3.6', 'clang3.7', 'clang7.0',
-        'python2.7', 'python3.4', 'python3.5', 'python3.6', 'python3.7', 'pypy',
-        'pypy3', 'python_alpine', 'all_the_cpythons', 'electron1.3',
-        'electron1.6', 'coreclr', 'cmake', 'cmake_vs2015', 'cmake_vs2017'
+        'python2.7', 'python3.4', 'python3.5', 'python3.6', 'python3.7',
+        'python3.8', 'pypy', 'pypy3', 'python_alpine', 'all_the_cpythons',
+        'electron1.3', 'electron1.6', 'coreclr', 'cmake', 'cmake_vs2015',
+        'cmake_vs2017'
     ],
     default='default',
     help=