Browse Source

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

kpayson64 7 năm trước cách đây
mục cha
commit
378daa874e
67 tập tin đã thay đổi với 1309 bổ sung1319 xóa
  1. 0 45
      CMakeLists.txt
  2. 0 48
      Makefile
  3. 1 0
      gRPC-ProtoRPC.podspec
  4. 344 468
      src/core/ext/filters/client_channel/client_channel.cc
  5. 32 33
      src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc
  6. 16 12
      src/core/ext/filters/deadline/deadline_filter.cc
  7. 5 5
      src/core/ext/filters/deadline/deadline_filter.h
  8. 10 9
      src/core/ext/filters/http/client/http_client_filter.cc
  9. 4 2
      src/core/ext/filters/message_size/message_size_filter.cc
  10. 16 19
      src/core/ext/transport/chttp2/transport/chttp2_transport.cc
  11. 18 1
      src/core/ext/transport/cronet/transport/cronet_transport.cc
  12. 46 6
      src/core/ext/transport/inproc/inproc_transport.cc
  13. 8 1
      src/core/lib/channel/connected_channel.cc
  14. 80 0
      src/core/lib/iomgr/call_combiner.h
  15. 3 2
      src/core/lib/iomgr/closure.h
  16. 1 6
      src/core/lib/iomgr/ev_epollex_linux.cc
  17. 40 19
      src/core/lib/surface/call.cc
  18. 20 9
      src/core/lib/transport/transport.cc
  19. 12 10
      src/core/lib/transport/transport.h
  20. 0 7
      src/core/lib/transport/transport_op_string.cc
  21. 5 3
      src/cpp/server/server_builder.cc
  22. 25 0
      src/csharp/Grpc.Core.NativeDebug.nuspec
  23. 1 0
      src/csharp/build_packages_dotnetcli.bat
  24. 1 0
      src/csharp/build_packages_dotnetcli.sh
  25. 2 0
      src/objective-c/examples/Sample/Sample.xcodeproj/project.pbxproj
  26. 1 1
      src/objective-c/examples/Sample/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme
  27. 0 78
      src/objective-c/tests/analyze_link_map.py
  28. 4 1
      src/objective-c/tests/build_one_example.sh
  29. 3 1
      src/python/grpcio/grpc/_channel.py
  30. 4 2
      src/python/grpcio/grpc/_common.py
  31. 5 0
      src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd.pxi
  32. 19 0
      src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
  33. 9 0
      src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
  34. 2 1
      src/python/grpcio/grpc/_cython/_cygrpc/grpc_string.pyx.pxi
  35. 1 0
      src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
  36. 4 2
      src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
  37. 3 1
      src/python/grpcio/grpc/_plugin_wrapping.py
  38. 6 4
      src/python/grpcio/grpc/_server.py
  39. 45 0
      src/python/grpcio/grpc/experimental/session_cache.py
  40. 3 1
      src/python/grpcio/grpc/framework/foundation/callable_util.py
  41. 3 1
      src/python/grpcio/grpc/framework/foundation/logging_pool.py
  42. 2 1
      src/python/grpcio/grpc/framework/foundation/stream_util.py
  43. 2 1
      src/python/grpcio_testing/grpc_testing/_channel/_invocation.py
  44. 4 2
      src/python/grpcio_testing/grpc_testing/_server/_rpc.py
  45. 3 1
      src/python/grpcio_testing/grpc_testing/_time.py
  46. 4 3
      src/python/grpcio_tests/tests/interop/server.py
  47. 1 0
      src/python/grpcio_tests/tests/tests.json
  48. 45 0
      src/python/grpcio_tests/tests/unit/_auth_context_test.py
  49. 145 0
      src/python/grpcio_tests/tests/unit/_session_cache_test.py
  50. 1 0
      templates/gRPC-ProtoRPC.podspec.template
  51. 1 0
      templates/src/csharp/build_packages_dotnetcli.bat.template
  52. 1 0
      templates/src/csharp/build_packages_dotnetcli.sh.template
  53. 3 3
      templates/test/cpp/naming/resolver_component_tests_defs.include
  54. 16 8
      test/cpp/microbenchmarks/bm_call_create.cc
  55. 1 17
      test/cpp/naming/BUILD
  56. 0 289
      test/cpp/naming/cancel_ares_query_test.cc
  57. 0 20
      test/cpp/naming/gen_build_yaml.py
  58. 3 107
      test/cpp/naming/resolver_component_test.cc
  59. 15 15
      test/cpp/naming/resolver_component_tests_runner.py
  60. 1 1
      test/cpp/naming/utils/dns_resolver.py
  61. 3 3
      test/cpp/naming/utils/dns_server.py
  62. 22 6
      test/cpp/util/grpc_tool.cc
  63. 127 0
      tools/profiling/ios_bin/binary_diff.py
  64. 104 0
      tools/profiling/ios_bin/parse_link_map.py
  65. 3 2
      tools/run_tests/artifacts/build_artifact_csharp.bat
  66. 0 20
      tools/run_tests/generated/sources_and_headers.json
  67. 0 22
      tools/run_tests/generated/tests.json

+ 0 - 45
CMakeLists.txt

@@ -663,9 +663,6 @@ endif()
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_cxx address_sorting_test)
 endif()
-if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
-add_dependencies(buildtests_cxx cancel_ares_query_test)
-endif()
 
 add_custom_target(buildtests
   DEPENDS buildtests_c buildtests_cxx)
@@ -15843,48 +15840,6 @@ target_link_libraries(address_sorting_test
   ${_gRPC_GFLAGS_LIBRARIES}
 )
 
-endif()
-endif (gRPC_BUILD_TESTS)
-if (gRPC_BUILD_TESTS)
-if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
-
-add_executable(cancel_ares_query_test
-  test/cpp/naming/cancel_ares_query_test.cc
-  third_party/googletest/googletest/src/gtest-all.cc
-  third_party/googletest/googlemock/src/gmock-all.cc
-)
-
-
-target_include_directories(cancel_ares_query_test
-  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
-  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
-  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
-  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
-  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
-  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
-  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
-  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
-  PRIVATE ${_gRPC_ADDRESS_SORTING_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(cancel_ares_query_test
-  ${_gRPC_PROTOBUF_LIBRARIES}
-  ${_gRPC_ALLTARGETS_LIBRARIES}
-  grpc++_test_util
-  grpc_test_util
-  gpr_test_util
-  grpc++
-  grpc
-  gpr
-  grpc++_test_config
-  ${_gRPC_GFLAGS_LIBRARIES}
-)
-
 endif()
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)

+ 0 - 48
Makefile

@@ -1316,7 +1316,6 @@ resolver_component_tests_runner_invoker_unsecure: $(BINDIR)/$(CONFIG)/resolver_c
 resolver_component_tests_runner_invoker: $(BINDIR)/$(CONFIG)/resolver_component_tests_runner_invoker
 address_sorting_test_unsecure: $(BINDIR)/$(CONFIG)/address_sorting_test_unsecure
 address_sorting_test: $(BINDIR)/$(CONFIG)/address_sorting_test
-cancel_ares_query_test: $(BINDIR)/$(CONFIG)/cancel_ares_query_test
 alts_credentials_fuzzer_one_entry: $(BINDIR)/$(CONFIG)/alts_credentials_fuzzer_one_entry
 api_fuzzer_one_entry: $(BINDIR)/$(CONFIG)/api_fuzzer_one_entry
 client_fuzzer_one_entry: $(BINDIR)/$(CONFIG)/client_fuzzer_one_entry
@@ -1754,7 +1753,6 @@ buildtests_cxx: privatelibs_cxx \
   $(BINDIR)/$(CONFIG)/resolver_component_tests_runner_invoker \
   $(BINDIR)/$(CONFIG)/address_sorting_test_unsecure \
   $(BINDIR)/$(CONFIG)/address_sorting_test \
-  $(BINDIR)/$(CONFIG)/cancel_ares_query_test \
 
 else
 buildtests_cxx: privatelibs_cxx \
@@ -1879,7 +1877,6 @@ buildtests_cxx: privatelibs_cxx \
   $(BINDIR)/$(CONFIG)/resolver_component_tests_runner_invoker \
   $(BINDIR)/$(CONFIG)/address_sorting_test_unsecure \
   $(BINDIR)/$(CONFIG)/address_sorting_test \
-  $(BINDIR)/$(CONFIG)/cancel_ares_query_test \
 
 endif
 
@@ -2365,8 +2362,6 @@ test_cxx: buildtests_cxx
 	$(Q) $(BINDIR)/$(CONFIG)/address_sorting_test_unsecure || ( echo test address_sorting_test_unsecure failed ; exit 1 )
 	$(E) "[RUN]     Testing address_sorting_test"
 	$(Q) $(BINDIR)/$(CONFIG)/address_sorting_test || ( echo test address_sorting_test failed ; exit 1 )
-	$(E) "[RUN]     Testing cancel_ares_query_test"
-	$(Q) $(BINDIR)/$(CONFIG)/cancel_ares_query_test || ( echo test cancel_ares_query_test failed ; exit 1 )
 
 
 flaky_test_cxx: buildtests_cxx
@@ -23692,49 +23687,6 @@ endif
 endif
 
 
-CANCEL_ARES_QUERY_TEST_SRC = \
-    test/cpp/naming/cancel_ares_query_test.cc \
-
-CANCEL_ARES_QUERY_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CANCEL_ARES_QUERY_TEST_SRC))))
-ifeq ($(NO_SECURE),true)
-
-# You can't build secure targets if you don't have OpenSSL.
-
-$(BINDIR)/$(CONFIG)/cancel_ares_query_test: openssl_dep_error
-
-else
-
-
-
-
-ifeq ($(NO_PROTOBUF),true)
-
-# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
-
-$(BINDIR)/$(CONFIG)/cancel_ares_query_test: protobuf_dep_error
-
-else
-
-$(BINDIR)/$(CONFIG)/cancel_ares_query_test: $(PROTOBUF_DEP) $(CANCEL_ARES_QUERY_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
-	$(E) "[LD]      Linking $@"
-	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LDXX) $(LDFLAGS) $(CANCEL_ARES_QUERY_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/cancel_ares_query_test
-
-endif
-
-endif
-
-$(OBJDIR)/$(CONFIG)/test/cpp/naming/cancel_ares_query_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
-
-deps_cancel_ares_query_test: $(CANCEL_ARES_QUERY_TEST_OBJS:.o=.dep)
-
-ifneq ($(NO_SECURE),true)
-ifneq ($(NO_DEPS),true)
--include $(CANCEL_ARES_QUERY_TEST_OBJS:.o=.dep)
-endif
-endif
-
-
 ALTS_CREDENTIALS_FUZZER_ONE_ENTRY_SRC = \
     test/core/security/alts_credentials_fuzzer.cc \
     test/core/util/one_corpus_entry_fuzzer.cc \

+ 1 - 0
gRPC-ProtoRPC.podspec

@@ -42,6 +42,7 @@ Pod::Spec.new do |s|
 
   src_dir = 'src/objective-c/ProtoRPC'
 
+  s.default_subspec = 'Main'
 
   s.subspec 'Main' do |ss|
     ss.header_mappings_dir = "#{src_dir}"

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 344 - 468
src/core/ext/filters/client_channel/client_channel.cc


+ 32 - 33
src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc

@@ -21,7 +21,6 @@
 #if GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER)
 
 #include <ares.h>
-#include <string.h>
 #include <sys/ioctl.h>
 
 #include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h"
@@ -56,8 +55,8 @@ typedef struct fd_node {
   bool readable_registered;
   /** if the writable closure has been registered */
   bool writable_registered;
-  /** if the fd has been shutdown yet from grpc iomgr perspective */
-  bool already_shutdown;
+  /** if the fd is being shut down */
+  bool shutting_down;
 } fd_node;
 
 struct grpc_ares_ev_driver {
@@ -102,26 +101,25 @@ static void fd_node_destroy(fd_node* fdn) {
   gpr_log(GPR_DEBUG, "delete fd: %d", grpc_fd_wrapped_fd(fdn->fd));
   GPR_ASSERT(!fdn->readable_registered);
   GPR_ASSERT(!fdn->writable_registered);
-  GPR_ASSERT(fdn->already_shutdown);
   gpr_mu_destroy(&fdn->mu);
-  /* TODO: we need to pass a non-null "release_fd" parameter to
-   * grpc_fd_orphan because "epollsig" iomgr will close the fd
-   * even if "already_closed" is true, and it only leaves it open
-   * if "release_fd" is non-null. This is unlike the rest of the
-   * pollers, should this be changed within epollsig? */
-  int dummy_release_fd;
   /* c-ares library has closed the fd inside grpc_fd. This fd may be picked up
      immediately by another thread, and should not be closed by the following
      grpc_fd_orphan. */
-  grpc_fd_orphan(fdn->fd, nullptr, &dummy_release_fd, true /* already_closed */,
+  grpc_fd_orphan(fdn->fd, nullptr, nullptr, true /* already_closed */,
                  "c-ares query finished");
   gpr_free(fdn);
 }
 
-static void fd_node_shutdown_locked(fd_node* fdn, const char* reason) {
-  if (!fdn->already_shutdown) {
-    fdn->already_shutdown = true;
-    grpc_fd_shutdown(fdn->fd, GRPC_ERROR_CREATE_FROM_STATIC_STRING(reason));
+static void fd_node_shutdown(fd_node* fdn) {
+  gpr_mu_lock(&fdn->mu);
+  fdn->shutting_down = true;
+  if (!fdn->readable_registered && !fdn->writable_registered) {
+    gpr_mu_unlock(&fdn->mu);
+    fd_node_destroy(fdn);
+  } else {
+    grpc_fd_shutdown(
+        fdn->fd, GRPC_ERROR_CREATE_FROM_STATIC_STRING("c-ares fd shutdown"));
+    gpr_mu_unlock(&fdn->mu);
   }
 }
 
@@ -129,10 +127,7 @@ grpc_error* grpc_ares_ev_driver_create(grpc_ares_ev_driver** ev_driver,
                                        grpc_pollset_set* pollset_set) {
   *ev_driver = static_cast<grpc_ares_ev_driver*>(
       gpr_malloc(sizeof(grpc_ares_ev_driver)));
-  ares_options opts;
-  memset(&opts, 0, sizeof(opts));
-  opts.flags |= ARES_FLAG_STAYOPEN;
-  int status = ares_init_options(&(*ev_driver)->channel, &opts, ARES_OPT_FLAGS);
+  int status = ares_init(&(*ev_driver)->channel);
   gpr_log(GPR_DEBUG, "grpc_ares_ev_driver_create");
   if (status != ARES_SUCCESS) {
     char* err_msg;
@@ -169,9 +164,8 @@ void grpc_ares_ev_driver_shutdown(grpc_ares_ev_driver* ev_driver) {
   ev_driver->shutting_down = true;
   fd_node* fn = ev_driver->fds;
   while (fn != nullptr) {
-    gpr_mu_lock(&fn->mu);
-    fd_node_shutdown_locked(fn, "grpc_ares_ev_driver_shutdown");
-    gpr_mu_unlock(&fn->mu);
+    grpc_fd_shutdown(fn->fd, GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                                 "grpc_ares_ev_driver_shutdown"));
     fn = fn->next;
   }
   gpr_mu_unlock(&ev_driver->mu);
@@ -208,7 +202,14 @@ static void on_readable_cb(void* arg, grpc_error* error) {
   gpr_mu_lock(&fdn->mu);
   const int fd = grpc_fd_wrapped_fd(fdn->fd);
   fdn->readable_registered = false;
+  if (fdn->shutting_down && !fdn->writable_registered) {
+    gpr_mu_unlock(&fdn->mu);
+    fd_node_destroy(fdn);
+    grpc_ares_ev_driver_unref(ev_driver);
+    return;
+  }
   gpr_mu_unlock(&fdn->mu);
+
   gpr_log(GPR_DEBUG, "readable on %d", fd);
   if (error == GRPC_ERROR_NONE) {
     do {
@@ -235,7 +236,14 @@ static void on_writable_cb(void* arg, grpc_error* error) {
   gpr_mu_lock(&fdn->mu);
   const int fd = grpc_fd_wrapped_fd(fdn->fd);
   fdn->writable_registered = false;
+  if (fdn->shutting_down && !fdn->readable_registered) {
+    gpr_mu_unlock(&fdn->mu);
+    fd_node_destroy(fdn);
+    grpc_ares_ev_driver_unref(ev_driver);
+    return;
+  }
   gpr_mu_unlock(&fdn->mu);
+
   gpr_log(GPR_DEBUG, "writable on %d", fd);
   if (error == GRPC_ERROR_NONE) {
     ares_process_fd(ev_driver->channel, ARES_SOCKET_BAD, fd);
@@ -280,7 +288,7 @@ static void grpc_ares_notify_on_event_locked(grpc_ares_ev_driver* ev_driver) {
           fdn->ev_driver = ev_driver;
           fdn->readable_registered = false;
           fdn->writable_registered = false;
-          fdn->already_shutdown = false;
+          fdn->shutting_down = false;
           gpr_mu_init(&fdn->mu);
           GRPC_CLOSURE_INIT(&fdn->read_closure, on_readable_cb, fdn,
                             grpc_schedule_on_exec_ctx);
@@ -321,16 +329,7 @@ static void grpc_ares_notify_on_event_locked(grpc_ares_ev_driver* ev_driver) {
   while (ev_driver->fds != nullptr) {
     fd_node* cur = ev_driver->fds;
     ev_driver->fds = ev_driver->fds->next;
-    gpr_mu_lock(&cur->mu);
-    fd_node_shutdown_locked(cur, "c-ares fd shutdown");
-    if (!cur->readable_registered && !cur->writable_registered) {
-      gpr_mu_unlock(&cur->mu);
-      fd_node_destroy(cur);
-    } else {
-      cur->next = new_list;
-      new_list = cur;
-      gpr_mu_unlock(&cur->mu);
-    }
+    fd_node_shutdown(cur);
   }
   ev_driver->fds = new_list;
   // If the ev driver has no working fd, all the tasks are done.

+ 16 - 12
src/core/ext/filters/deadline/deadline_filter.cc

@@ -128,21 +128,25 @@ static void cancel_timer_if_needed(grpc_deadline_state* deadline_state) {
   }
 }
 
-// Callback run when the call is complete.
-static void on_complete(void* arg, grpc_error* error) {
+// Callback run when we receive trailing metadata.
+static void recv_trailing_metadata_ready(void* arg, grpc_error* error) {
   grpc_deadline_state* deadline_state = static_cast<grpc_deadline_state*>(arg);
   cancel_timer_if_needed(deadline_state);
-  // Invoke the next callback.
-  GRPC_CLOSURE_RUN(deadline_state->next_on_complete, GRPC_ERROR_REF(error));
+  // Invoke the original callback.
+  GRPC_CLOSURE_RUN(deadline_state->original_recv_trailing_metadata_ready,
+                   GRPC_ERROR_REF(error));
 }
 
-// Inject our own on_complete callback into op.
-static void inject_on_complete_cb(grpc_deadline_state* deadline_state,
-                                  grpc_transport_stream_op_batch* op) {
-  deadline_state->next_on_complete = op->on_complete;
-  GRPC_CLOSURE_INIT(&deadline_state->on_complete, on_complete, deadline_state,
+// Inject our own recv_trailing_metadata_ready callback into op.
+static void inject_recv_trailing_metadata_ready(
+    grpc_deadline_state* deadline_state, grpc_transport_stream_op_batch* op) {
+  deadline_state->original_recv_trailing_metadata_ready =
+      op->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
+  GRPC_CLOSURE_INIT(&deadline_state->recv_trailing_metadata_ready,
+                    recv_trailing_metadata_ready, deadline_state,
                     grpc_schedule_on_exec_ctx);
-  op->on_complete = &deadline_state->on_complete;
+  op->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+      &deadline_state->recv_trailing_metadata_ready;
 }
 
 // Callback and associated state for starting the timer after call stack
@@ -226,7 +230,7 @@ void grpc_deadline_state_client_start_transport_stream_op_batch(
     // Make sure we know when the call is complete, so that we can cancel
     // the timer.
     if (op->recv_trailing_metadata) {
-      inject_on_complete_cb(deadline_state, op);
+      inject_recv_trailing_metadata_ready(deadline_state, op);
     }
   }
 }
@@ -323,7 +327,7 @@ static void server_start_transport_stream_op_batch(
     // the client never sends trailing metadata, because this is the
     // hook that tells us when the call is complete on the server side.
     if (op->recv_trailing_metadata) {
-      inject_on_complete_cb(&calld->base.deadline_state, op);
+      inject_recv_trailing_metadata_ready(&calld->base.deadline_state, op);
     }
   }
   // Chain to next filter.

+ 5 - 5
src/core/ext/filters/deadline/deadline_filter.h

@@ -37,12 +37,12 @@ typedef struct grpc_deadline_state {
   grpc_deadline_timer_state timer_state;
   grpc_timer timer;
   grpc_closure timer_callback;
-  // Closure to invoke when the call is complete.
+  // Closure to invoke when we receive trailing metadata.
   // We use this to cancel the timer.
-  grpc_closure on_complete;
-  // The original on_complete closure, which we chain to after our own
-  // closure is invoked.
-  grpc_closure* next_on_complete;
+  grpc_closure recv_trailing_metadata_ready;
+  // The original recv_trailing_metadata_ready closure, which we chain to
+  // after our own closure is invoked.
+  grpc_closure* original_recv_trailing_metadata_ready;
 } grpc_deadline_state;
 
 //

+ 10 - 9
src/core/ext/filters/http/client/http_client_filter.cc

@@ -55,8 +55,8 @@ struct call_data {
   grpc_closure recv_initial_metadata_ready;
   // State for handling recv_trailing_metadata ops.
   grpc_metadata_batch* recv_trailing_metadata;
-  grpc_closure* original_recv_trailing_metadata_on_complete;
-  grpc_closure recv_trailing_metadata_on_complete;
+  grpc_closure* original_recv_trailing_metadata_ready;
+  grpc_closure recv_trailing_metadata_ready;
   // State for handling send_message ops.
   grpc_transport_stream_op_batch* send_message_batch;
   size_t send_message_bytes_read;
@@ -153,8 +153,7 @@ static void recv_initial_metadata_ready(void* user_data, grpc_error* error) {
   GRPC_CLOSURE_RUN(calld->original_recv_initial_metadata_ready, error);
 }
 
-static void recv_trailing_metadata_on_complete(void* user_data,
-                                               grpc_error* error) {
+static void recv_trailing_metadata_ready(void* user_data, grpc_error* error) {
   grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
   call_data* calld = static_cast<call_data*>(elem->call_data);
   if (error == GRPC_ERROR_NONE) {
@@ -163,7 +162,7 @@ static void recv_trailing_metadata_on_complete(void* user_data,
   } else {
     GRPC_ERROR_REF(error);
   }
-  GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_on_complete, error);
+  GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_ready, error);
 }
 
 static void send_message_on_complete(void* arg, grpc_error* error) {
@@ -312,8 +311,10 @@ static void hc_start_transport_stream_op_batch(
     /* substitute our callback for the higher callback */
     calld->recv_trailing_metadata =
         batch->payload->recv_trailing_metadata.recv_trailing_metadata;
-    calld->original_recv_trailing_metadata_on_complete = batch->on_complete;
-    batch->on_complete = &calld->recv_trailing_metadata_on_complete;
+    calld->original_recv_trailing_metadata_ready =
+        batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
+    batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+        &calld->recv_trailing_metadata_ready;
   }
 
   grpc_error* error = GRPC_ERROR_NONE;
@@ -420,8 +421,8 @@ static grpc_error* init_call_elem(grpc_call_element* elem,
   GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready,
                     recv_initial_metadata_ready, elem,
                     grpc_schedule_on_exec_ctx);
-  GRPC_CLOSURE_INIT(&calld->recv_trailing_metadata_on_complete,
-                    recv_trailing_metadata_on_complete, elem,
+  GRPC_CLOSURE_INIT(&calld->recv_trailing_metadata_ready,
+                    recv_trailing_metadata_ready, elem,
                     grpc_schedule_on_exec_ctx);
   GRPC_CLOSURE_INIT(&calld->send_message_on_complete, send_message_on_complete,
                     elem, grpc_schedule_on_exec_ctx);

+ 4 - 2
src/core/ext/filters/message_size/message_size_filter.cc

@@ -198,8 +198,10 @@ static void start_transport_stream_op_batch(
   }
   // Inject callback for receiving trailing metadata.
   if (op->recv_trailing_metadata) {
-    calld->next_recv_trailing_metadata = op->on_complete;
-    op->on_complete = &calld->recv_trailing_metadata;
+    calld->next_recv_trailing_metadata =
+        op->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
+    op->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+        &calld->recv_trailing_metadata;
   }
   // Chain to the next filter.
   grpc_call_next_op(elem, op);

+ 16 - 19
src/core/ext/transport/chttp2/transport/chttp2_transport.cc

@@ -1149,12 +1149,10 @@ static void maybe_start_some_streams(grpc_chttp2_transport* t) {
   }
 }
 
-/* Flag that this closure barrier wants stats to be updated before finishing */
-#define CLOSURE_BARRIER_STATS_BIT (1 << 0)
 /* Flag that this closure barrier may be covering a write in a pollset, and so
    we should not complete this closure until we can prove that the write got
    scheduled */
-#define CLOSURE_BARRIER_MAY_COVER_WRITE (1 << 1)
+#define CLOSURE_BARRIER_MAY_COVER_WRITE (1 << 0)
 /* First bit of the reference count, stored in the high order bits (with the low
    bits being used for flags defined above) */
 #define CLOSURE_BARRIER_FIRST_REF_BIT (1 << 16)
@@ -1206,10 +1204,6 @@ void grpc_chttp2_complete_closure_step(grpc_chttp2_transport* t,
         grpc_error_add_child(closure->error_data.error, error);
   }
   if (closure->next_data.scratch < CLOSURE_BARRIER_FIRST_REF_BIT) {
-    if (closure->next_data.scratch & CLOSURE_BARRIER_STATS_BIT) {
-      grpc_transport_move_stats(&s->stats, s->collecting_stats);
-      s->collecting_stats = nullptr;
-    }
     if ((t->write_state == GRPC_CHTTP2_WRITE_STATE_IDLE) ||
         !(closure->next_data.scratch & CLOSURE_BARRIER_MAY_COVER_WRITE)) {
       GRPC_CLOSURE_RUN(closure, closure->error_data.error);
@@ -1351,9 +1345,14 @@ static void perform_stream_op_locked(void* stream_op,
   }
 
   grpc_closure* on_complete = op->on_complete;
+  // TODO(roth): This is a hack needed because we use data inside of the
+  // closure itself to do the barrier calculation (i.e., to ensure that
+  // we don't schedule the closure until all ops in the batch have been
+  // completed).  This can go away once we move to a new C++ closure API
+  // that provides the ability to create a barrier closure.
   if (on_complete == nullptr) {
-    on_complete =
-        GRPC_CLOSURE_CREATE(do_nothing, nullptr, grpc_schedule_on_exec_ctx);
+    on_complete = GRPC_CLOSURE_INIT(&op->handler_private.closure, do_nothing,
+                                    nullptr, grpc_schedule_on_exec_ctx);
   }
 
   /* use final_data as a barrier until enqueue time; the inital counter is
@@ -1361,12 +1360,6 @@ static void perform_stream_op_locked(void* stream_op,
   on_complete->next_data.scratch = CLOSURE_BARRIER_FIRST_REF_BIT;
   on_complete->error_data.error = GRPC_ERROR_NONE;
 
-  if (op->collect_stats) {
-    GPR_ASSERT(s->collecting_stats == nullptr);
-    s->collecting_stats = op_payload->collect_stats.collect_stats;
-    on_complete->next_data.scratch |= CLOSURE_BARRIER_STATS_BIT;
-  }
-
   if (op->cancel_stream) {
     GRPC_STATS_INC_HTTP2_OP_CANCEL();
     grpc_chttp2_cancel_stream(t, s, op_payload->cancel_stream.cancel_error);
@@ -1600,8 +1593,11 @@ static void perform_stream_op_locked(void* stream_op,
 
   if (op->recv_trailing_metadata) {
     GRPC_STATS_INC_HTTP2_OP_RECV_TRAILING_METADATA();
+    GPR_ASSERT(s->collecting_stats == nullptr);
+    s->collecting_stats = op_payload->recv_trailing_metadata.collect_stats;
     GPR_ASSERT(s->recv_trailing_metadata_finished == nullptr);
-    s->recv_trailing_metadata_finished = add_closure_barrier(on_complete);
+    s->recv_trailing_metadata_finished =
+        op_payload->recv_trailing_metadata.recv_trailing_metadata_ready;
     s->recv_trailing_metadata =
         op_payload->recv_trailing_metadata.recv_trailing_metadata;
     s->final_metadata_requested = true;
@@ -1960,11 +1956,12 @@ void grpc_chttp2_maybe_complete_recv_trailing_metadata(grpc_chttp2_transport* t,
     }
     if (s->read_closed && s->frame_storage.length == 0 && !pending_data &&
         s->recv_trailing_metadata_finished != nullptr) {
+      grpc_transport_move_stats(&s->stats, s->collecting_stats);
+      s->collecting_stats = nullptr;
       grpc_chttp2_incoming_metadata_buffer_publish(&s->metadata_buffer[1],
                                                    s->recv_trailing_metadata);
-      grpc_chttp2_complete_closure_step(
-          t, s, &s->recv_trailing_metadata_finished, GRPC_ERROR_NONE,
-          "recv_trailing_metadata_finished");
+      null_then_run_closure(&s->recv_trailing_metadata_finished,
+                            GRPC_ERROR_NONE);
     }
   }
 }

+ 18 - 1
src/core/ext/transport/cronet/transport/cronet_transport.cc

@@ -925,6 +925,10 @@ static bool op_can_be_run(grpc_transport_stream_op_batch* curr_op,
       result = false;
     }
     /* Check if every op that was asked for is done. */
+    /* TODO(muxi): We should not consider the recv ops here, since they
+     * have their own callbacks.  We should invoke a batch's on_complete
+     * as soon as all of the batch's send ops are complete, even if
+     * there are still recv ops pending. */
     else if (curr_op->send_initial_metadata &&
              !stream_state->state_callback_received[OP_SEND_INITIAL_METADATA]) {
       CRONET_LOG(GPR_DEBUG, "Because");
@@ -1280,12 +1284,20 @@ static enum e_op_result execute_stream_op(struct op_and_state* oas) {
              op_can_be_run(stream_op, s, &oas->state,
                            OP_RECV_TRAILING_METADATA)) {
     CRONET_LOG(GPR_DEBUG, "running: %p  OP_RECV_TRAILING_METADATA", oas);
-    if (oas->s->state.rs.trailing_metadata_valid) {
+    grpc_error* error = GRPC_ERROR_NONE;
+    if (stream_state->state_op_done[OP_CANCEL_ERROR]) {
+      error = GRPC_ERROR_REF(stream_state->cancel_error);
+    } else if (stream_state->state_op_done[OP_FAILED]) {
+      error = make_error_with_desc(GRPC_STATUS_UNAVAILABLE, "Unavailable.");
+    } else if (oas->s->state.rs.trailing_metadata_valid) {
       grpc_chttp2_incoming_metadata_buffer_publish(
           &oas->s->state.rs.trailing_metadata,
           stream_op->payload->recv_trailing_metadata.recv_trailing_metadata);
       stream_state->rs.trailing_metadata_valid = false;
     }
+    GRPC_CLOSURE_SCHED(
+        stream_op->payload->recv_trailing_metadata.recv_trailing_metadata_ready,
+        error);
     stream_state->state_op_done[OP_RECV_TRAILING_METADATA] = true;
     result = ACTION_TAKEN_NO_CALLBACK;
   } else if (stream_op->cancel_stream &&
@@ -1398,6 +1410,11 @@ static void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
       GRPC_CLOSURE_SCHED(op->payload->recv_message.recv_message_ready,
                          GRPC_ERROR_CANCELLED);
     }
+    if (op->recv_trailing_metadata) {
+      GRPC_CLOSURE_SCHED(
+          op->payload->recv_trailing_metadata.recv_trailing_metadata_ready,
+          GRPC_ERROR_CANCELLED);
+    }
     GRPC_CLOSURE_SCHED(op->on_complete, GRPC_ERROR_CANCELLED);
     return;
   }

+ 46 - 6
src/core/ext/transport/inproc/inproc_transport.cc

@@ -120,7 +120,6 @@ typedef struct inproc_stream {
   struct inproc_stream* stream_list_next;
 } inproc_stream;
 
-static grpc_closure do_nothing_closure;
 static bool cancel_stream_locked(inproc_stream* s, grpc_error* error);
 static void op_state_machine(void* arg, grpc_error* error);
 
@@ -373,6 +372,10 @@ static void complete_if_batch_end_locked(inproc_stream* s, grpc_error* error,
                                          const char* msg) {
   int is_sm = static_cast<int>(op == s->send_message_op);
   int is_stm = static_cast<int>(op == s->send_trailing_md_op);
+  // TODO(vjpai): We should not consider the recv ops here, since they
+  // have their own callbacks.  We should invoke a batch's on_complete
+  // as soon as all of the batch's send ops are complete, even if there
+  // are still recv ops pending.
   int is_rim = static_cast<int>(op == s->recv_initial_md_op);
   int is_rm = static_cast<int>(op == s->recv_message_op);
   int is_rtm = static_cast<int>(op == s->recv_trailing_md_op);
@@ -496,6 +499,11 @@ static void fail_helper_locked(inproc_stream* s, grpc_error* error) {
     s->send_trailing_md_op = nullptr;
   }
   if (s->recv_trailing_md_op) {
+    INPROC_LOG(GPR_INFO, "fail_helper %p scheduling trailing-metadata-ready %p",
+               s, error);
+    GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->payload->recv_trailing_metadata
+                           .recv_trailing_metadata_ready,
+                       GRPC_ERROR_REF(error));
     INPROC_LOG(GPR_INFO, "fail_helper %p scheduling trailing-md-on-complete %p",
                s, error);
     complete_if_batch_end_locked(
@@ -638,6 +646,12 @@ static void op_state_machine(void* arg, grpc_error* error) {
       }
       s->trailing_md_sent = true;
       if (!s->t->is_client && s->trailing_md_recvd && s->recv_trailing_md_op) {
+        INPROC_LOG(GPR_INFO,
+                   "op_state_machine %p scheduling trailing-metadata-ready", s);
+        GRPC_CLOSURE_SCHED(
+            s->recv_trailing_md_op->payload->recv_trailing_metadata
+                .recv_trailing_metadata_ready,
+            GRPC_ERROR_NONE);
         INPROC_LOG(GPR_INFO,
                    "op_state_machine %p scheduling trailing-md-on-complete", s);
         GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->on_complete,
@@ -711,6 +725,12 @@ static void op_state_machine(void* arg, grpc_error* error) {
   }
   if (s->recv_trailing_md_op && s->t->is_client && other &&
       other->send_message_op) {
+    INPROC_LOG(GPR_INFO,
+               "op_state_machine %p scheduling trailing-metadata-ready %p", s,
+               GRPC_ERROR_NONE);
+    GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->payload->recv_trailing_metadata
+                           .recv_trailing_metadata_ready,
+                       GRPC_ERROR_NONE);
     maybe_schedule_op_closure_locked(other, GRPC_ERROR_NONE);
   }
   if (s->to_read_trailing_md_filled) {
@@ -766,6 +786,10 @@ static void op_state_machine(void* arg, grpc_error* error) {
         INPROC_LOG(GPR_INFO,
                    "op_state_machine %p scheduling trailing-md-on-complete %p",
                    s, new_err);
+        GRPC_CLOSURE_SCHED(
+            s->recv_trailing_md_op->payload->recv_trailing_metadata
+                .recv_trailing_metadata_ready,
+            GRPC_ERROR_REF(new_err));
         GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->on_complete,
                            GRPC_ERROR_REF(new_err));
         s->recv_trailing_md_op = nullptr;
@@ -859,6 +883,9 @@ static bool cancel_stream_locked(inproc_stream* s, grpc_error* error) {
     // couldn't complete that because we hadn't yet sent out trailing
     // md, now's the chance
     if (!s->t->is_client && s->trailing_md_recvd && s->recv_trailing_md_op) {
+      GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->payload->recv_trailing_metadata
+                             .recv_trailing_metadata_ready,
+                         GRPC_ERROR_REF(s->cancel_self_error));
       complete_if_batch_end_locked(
           s, s->cancel_self_error, s->recv_trailing_md_op,
           "cancel_stream scheduling trailing-md-on-complete");
@@ -873,6 +900,8 @@ static bool cancel_stream_locked(inproc_stream* s, grpc_error* error) {
   return ret;
 }
 
+static void do_nothing(void* arg, grpc_error* error) {}
+
 static void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
                               grpc_transport_stream_op_batch* op) {
   INPROC_LOG(GPR_INFO, "perform_stream_op %p %p %p", gt, gs, op);
@@ -892,8 +921,14 @@ static void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
   }
   grpc_error* error = GRPC_ERROR_NONE;
   grpc_closure* on_complete = op->on_complete;
+  // TODO(roth): This is a hack needed because we use data inside of the
+  // closure itself to do the barrier calculation (i.e., to ensure that
+  // we don't schedule the closure until all ops in the batch have been
+  // completed).  This can go away once we move to a new C++ closure API
+  // that provides the ability to create a barrier closure.
   if (on_complete == nullptr) {
-    on_complete = &do_nothing_closure;
+    on_complete = GRPC_CLOSURE_INIT(&op->handler_private.closure, do_nothing,
+                                    nullptr, grpc_schedule_on_exec_ctx);
   }
 
   if (op->cancel_stream) {
@@ -1026,6 +1061,15 @@ static void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
         GRPC_CLOSURE_SCHED(op->payload->recv_message.recv_message_ready,
                            GRPC_ERROR_REF(error));
       }
+      if (op->recv_trailing_metadata) {
+        INPROC_LOG(
+            GPR_INFO,
+            "perform_stream_op error %p scheduling trailing-metadata-ready %p",
+            s, error);
+        GRPC_CLOSURE_SCHED(
+            op->payload->recv_trailing_metadata.recv_trailing_metadata_ready,
+            GRPC_ERROR_REF(error));
+      }
     }
     INPROC_LOG(GPR_INFO, "perform_stream_op %p scheduling on_complete %p", s,
                error);
@@ -1129,12 +1173,8 @@ static grpc_endpoint* get_endpoint(grpc_transport* t) { return nullptr; }
 /*******************************************************************************
  * GLOBAL INIT AND DESTROY
  */
-static void do_nothing(void* arg, grpc_error* error) {}
-
 void grpc_inproc_transport_init(void) {
   grpc_core::ExecCtx exec_ctx;
-  GRPC_CLOSURE_INIT(&do_nothing_closure, do_nothing, nullptr,
-                    grpc_schedule_on_exec_ctx);
   g_empty_slice = grpc_slice_from_static_buffer(nullptr, 0);
 
   grpc_slice key_tmp = grpc_slice_from_static_string(":path");

+ 8 - 1
src/core/lib/channel/connected_channel.cc

@@ -51,6 +51,7 @@ typedef struct connected_channel_call_data {
   callback_state on_complete[6];  // Max number of pending batches.
   callback_state recv_initial_metadata_ready;
   callback_state recv_message_ready;
+  callback_state recv_trailing_metadata_ready;
 } call_data;
 
 static void run_in_call_combiner(void* arg, grpc_error* error) {
@@ -111,6 +112,12 @@ static void con_start_transport_stream_op_batch(
     intercept_callback(calld, state, false, "recv_message_ready",
                        &batch->payload->recv_message.recv_message_ready);
   }
+  if (batch->recv_trailing_metadata) {
+    callback_state* state = &calld->recv_trailing_metadata_ready;
+    intercept_callback(
+        calld, state, false, "recv_trailing_metadata_ready",
+        &batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready);
+  }
   if (batch->cancel_stream) {
     // There can be more than one cancellation batch in flight at any
     // given time, so we can't just pick out a fixed index into
@@ -121,7 +128,7 @@ static void con_start_transport_stream_op_batch(
         static_cast<callback_state*>(gpr_malloc(sizeof(*state)));
     intercept_callback(calld, state, true, "on_complete (cancel_stream)",
                        &batch->on_complete);
-  } else {
+  } else if (batch->on_complete != nullptr) {
     callback_state* state = get_state_for_batch(calld, batch);
     intercept_callback(calld, state, false, "on_complete", &batch->on_complete);
   }

+ 80 - 0
src/core/lib/iomgr/call_combiner.h

@@ -26,6 +26,7 @@
 #include <grpc/support/atm.h>
 
 #include "src/core/lib/gpr/mpscq.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
 #include "src/core/lib/iomgr/closure.h"
 
 // A simple, lock-free mechanism for serializing activity related to a
@@ -109,4 +110,83 @@ void grpc_call_combiner_set_notify_on_cancel(grpc_call_combiner* call_combiner,
 void grpc_call_combiner_cancel(grpc_call_combiner* call_combiner,
                                grpc_error* error);
 
+namespace grpc_core {
+
+// Helper for running a list of closures in a call combiner.
+//
+// Each callback running in the call combiner will eventually be
+// returned to the surface, at which point the surface will yield the
+// call combiner.  So when we are running in the call combiner and have
+// more than one callback to return to the surface, we need to re-enter
+// the call combiner for all but one of those callbacks.
+class CallCombinerClosureList {
+ public:
+  CallCombinerClosureList() {}
+
+  // Adds a closure to the list.  The closure must eventually result in
+  // the call combiner being yielded.
+  void Add(grpc_closure* closure, grpc_error* error, const char* reason) {
+    closures_.emplace_back(closure, error, reason);
+  }
+
+  // Runs all closures in the call combiner and yields the call combiner.
+  //
+  // All but one of the closures in the list will be scheduled via
+  // GRPC_CALL_COMBINER_START(), and the remaining closure will be
+  // scheduled via GRPC_CLOSURE_SCHED(), which will eventually result in
+  // yielding the call combiner.  If the list is empty, then the call
+  // combiner will be yielded immediately.
+  void RunClosures(grpc_call_combiner* call_combiner) {
+    for (size_t i = 1; i < closures_.size(); ++i) {
+      auto& closure = closures_[i];
+      GRPC_CALL_COMBINER_START(call_combiner, closure.closure, closure.error,
+                               closure.reason);
+    }
+    if (closures_.size() > 0) {
+      if (grpc_call_combiner_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "CallCombinerClosureList executing closure while already "
+                "holding call_combiner %p: closure=%p error=%s reason=%s",
+                call_combiner, closures_[0].closure,
+                grpc_error_string(closures_[0].error), closures_[0].reason);
+      }
+      // This will release the call combiner.
+      GRPC_CLOSURE_SCHED(closures_[0].closure, closures_[0].error);
+    } else {
+      GRPC_CALL_COMBINER_STOP(call_combiner, "no closures to schedule");
+    }
+    closures_.clear();
+  }
+
+  // Runs all closures in the call combiner, but does NOT yield the call
+  // combiner.  All closures will be scheduled via GRPC_CALL_COMBINER_START().
+  void RunClosuresWithoutYielding(grpc_call_combiner* call_combiner) {
+    for (size_t i = 0; i < closures_.size(); ++i) {
+      auto& closure = closures_[i];
+      GRPC_CALL_COMBINER_START(call_combiner, closure.closure, closure.error,
+                               closure.reason);
+    }
+    closures_.clear();
+  }
+
+  size_t size() const { return closures_.size(); }
+
+ private:
+  struct CallCombinerClosure {
+    grpc_closure* closure;
+    grpc_error* error;
+    const char* reason;
+
+    CallCombinerClosure(grpc_closure* closure, grpc_error* error,
+                        const char* reason)
+        : closure(closure), error(error), reason(reason) {}
+  };
+
+  // There are generally a maximum of 6 closures to run in the call
+  // combiner, one for each pending op.
+  InlinedVector<CallCombinerClosure, 6> closures_;
+};
+
+}  // namespace grpc_core
+
 #endif /* GRPC_CORE_LIB_IOMGR_CALL_COMBINER_H */

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

@@ -283,9 +283,10 @@ inline void grpc_closure_sched(grpc_closure* c, grpc_error* error) {
     if (c->scheduled) {
       gpr_log(GPR_ERROR,
               "Closure already scheduled. (closure: %p, created: [%s:%d], "
-              "previously scheduled at: [%s: %d] run?: %s",
+              "previously scheduled at: [%s: %d], newly scheduled at [%s: %d], "
+              "run?: %s",
               c, c->file_created, c->line_created, c->file_initiated,
-              c->line_initiated, c->run ? "true" : "false");
+              c->line_initiated, file, line, c->run ? "true" : "false");
       abort();
     }
     c->scheduled = true;

+ 1 - 6
src/core/lib/iomgr/ev_epollex_linux.cc

@@ -438,12 +438,7 @@ static bool fd_is_shutdown(grpc_fd* fd) {
 /* Might be called multiple times */
 static void fd_shutdown(grpc_fd* fd, grpc_error* why) {
   if (fd->read_closure->SetShutdown(GRPC_ERROR_REF(why))) {
-    if (shutdown(fd->fd, SHUT_RDWR)) {
-      if (errno != ENOTCONN) {
-        gpr_log(GPR_ERROR, "Error shutting down fd %d. errno: %d",
-                grpc_fd_wrapped_fd(fd), errno);
-      }
-    }
+    shutdown(fd->fd, SHUT_RDWR);
     fd->write_closure->SetShutdown(GRPC_ERROR_REF(why));
   }
   GRPC_ERROR_UNREF(why);

+ 40 - 19
src/core/lib/surface/call.cc

@@ -187,6 +187,7 @@ struct grpc_call {
   grpc_closure receiving_slice_ready;
   grpc_closure receiving_stream_ready;
   grpc_closure receiving_initial_metadata_ready;
+  grpc_closure receiving_trailing_metadata_ready;
   uint32_t test_only_last_message_flags;
   bool cancelled;
 
@@ -1079,7 +1080,6 @@ static void post_batch_completion(batch_control* bctl) {
 
   if (bctl->op.send_initial_metadata) {
     grpc_metadata_batch_destroy(
-
         &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]);
   }
   if (bctl->op.send_message) {
@@ -1087,14 +1087,9 @@ static void post_batch_completion(batch_control* bctl) {
   }
   if (bctl->op.send_trailing_metadata) {
     grpc_metadata_batch_destroy(
-
         &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]);
   }
   if (bctl->op.recv_trailing_metadata) {
-    grpc_metadata_batch* md =
-        &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
-    recv_trailing_filter(call, md, GRPC_ERROR_REF(bctl->batch_error));
-
     /* propagate cancellation to any interested children */
     gpr_atm_rel_store(&call->received_final_op_atm, 1);
     parent_call* pc = get_parent_call(call);
@@ -1115,7 +1110,6 @@ static void post_batch_completion(batch_control* bctl) {
       }
       gpr_mu_unlock(&pc->child_list_mu);
     }
-
     GRPC_ERROR_UNREF(error);
     error = GRPC_ERROR_NONE;
   }
@@ -1390,6 +1384,16 @@ static void receiving_initial_metadata_ready(void* bctlp, grpc_error* error) {
   finish_batch_step(bctl);
 }
 
+static void receiving_trailing_metadata_ready(void* bctlp, grpc_error* error) {
+  batch_control* bctl = static_cast<batch_control*>(bctlp);
+  grpc_call* call = bctl->call;
+  GRPC_CALL_COMBINER_STOP(&call->call_combiner, "recv_trailing_metadata_ready");
+  grpc_metadata_batch* md =
+      &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
+  recv_trailing_filter(call, md, GRPC_ERROR_REF(error));
+  finish_batch_step(bctl);
+}
+
 static void finish_batch(void* bctlp, grpc_error* error) {
   batch_control* bctl = static_cast<batch_control*>(bctlp);
   grpc_call* call = bctl->call;
@@ -1415,7 +1419,8 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
   size_t i;
   const grpc_op* op;
   batch_control* bctl;
-  int num_completion_callbacks_needed = 1;
+  bool has_send_ops = false;
+  int num_recv_ops = 0;
   grpc_call_error error = GRPC_CALL_OK;
   grpc_transport_stream_op_batch* stream_op;
   grpc_transport_stream_op_batch_payload* stream_op_payload;
@@ -1521,6 +1526,7 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
           stream_op_payload->send_initial_metadata.peer_string =
               &call->peer_string;
         }
+        has_send_ops = true;
         break;
       }
       case GRPC_OP_SEND_MESSAGE: {
@@ -1550,6 +1556,7 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
             &op->data.send_message.send_message->data.raw.slice_buffer, flags);
         stream_op_payload->send_message.send_message.reset(
             call->sending_stream.get());
+        has_send_ops = true;
         break;
       }
       case GRPC_OP_SEND_CLOSE_FROM_CLIENT: {
@@ -1570,6 +1577,7 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
         call->sent_final_op = true;
         stream_op_payload->send_trailing_metadata.send_trailing_metadata =
             &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */];
+        has_send_ops = true;
         break;
       }
       case GRPC_OP_SEND_STATUS_FROM_SERVER: {
@@ -1632,6 +1640,7 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
         }
         stream_op_payload->send_trailing_metadata.send_trailing_metadata =
             &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */];
+        has_send_ops = true;
         break;
       }
       case GRPC_OP_RECV_INITIAL_METADATA: {
@@ -1659,7 +1668,7 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
           stream_op_payload->recv_initial_metadata.peer_string =
               &call->peer_string;
         }
-        num_completion_callbacks_needed++;
+        ++num_recv_ops;
         break;
       }
       case GRPC_OP_RECV_MESSAGE: {
@@ -1681,7 +1690,7 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
                           grpc_schedule_on_exec_ctx);
         stream_op_payload->recv_message.recv_message_ready =
             &call->receiving_stream_ready;
-        num_completion_callbacks_needed++;
+        ++num_recv_ops;
         break;
       }
       case GRPC_OP_RECV_STATUS_ON_CLIENT: {
@@ -1707,11 +1716,16 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
         call->final_op.client.error_string =
             op->data.recv_status_on_client.error_string;
         stream_op->recv_trailing_metadata = true;
-        stream_op->collect_stats = true;
         stream_op_payload->recv_trailing_metadata.recv_trailing_metadata =
             &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
-        stream_op_payload->collect_stats.collect_stats =
+        stream_op_payload->recv_trailing_metadata.collect_stats =
             &call->final_info.stats.transport_stream_stats;
+        GRPC_CLOSURE_INIT(&call->receiving_trailing_metadata_ready,
+                          receiving_trailing_metadata_ready, bctl,
+                          grpc_schedule_on_exec_ctx);
+        stream_op_payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+            &call->receiving_trailing_metadata_ready;
+        ++num_recv_ops;
         break;
       }
       case GRPC_OP_RECV_CLOSE_ON_SERVER: {
@@ -1732,11 +1746,16 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
         call->final_op.server.cancelled =
             op->data.recv_close_on_server.cancelled;
         stream_op->recv_trailing_metadata = true;
-        stream_op->collect_stats = true;
         stream_op_payload->recv_trailing_metadata.recv_trailing_metadata =
             &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
-        stream_op_payload->collect_stats.collect_stats =
+        stream_op_payload->recv_trailing_metadata.collect_stats =
             &call->final_info.stats.transport_stream_stats;
+        GRPC_CLOSURE_INIT(&call->receiving_trailing_metadata_ready,
+                          receiving_trailing_metadata_ready, bctl,
+                          grpc_schedule_on_exec_ctx);
+        stream_op_payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+            &call->receiving_trailing_metadata_ready;
+        ++num_recv_ops;
         break;
       }
     }
@@ -1746,13 +1765,15 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
   if (!is_notify_tag_closure) {
     GPR_ASSERT(grpc_cq_begin_op(call->cq, notify_tag));
   }
-  gpr_ref_init(&bctl->steps_to_complete, num_completion_callbacks_needed);
+  gpr_ref_init(&bctl->steps_to_complete, (has_send_ops ? 1 : 0) + num_recv_ops);
 
-  GRPC_CLOSURE_INIT(&bctl->finish_batch, finish_batch, bctl,
-                    grpc_schedule_on_exec_ctx);
-  stream_op->on_complete = &bctl->finish_batch;
-  gpr_atm_rel_store(&call->any_ops_sent_atm, 1);
+  if (has_send_ops) {
+    GRPC_CLOSURE_INIT(&bctl->finish_batch, finish_batch, bctl,
+                      grpc_schedule_on_exec_ctx);
+    stream_op->on_complete = &bctl->finish_batch;
+  }
 
+  gpr_atm_rel_store(&call->any_ops_sent_atm, 1);
   execute_batch(call, stream_op, &bctl->start_batch);
 
 done:

+ 20 - 9
src/core/lib/transport/transport.cc

@@ -212,21 +212,32 @@ void grpc_transport_stream_op_batch_finish_with_failure(
   if (batch->send_message) {
     batch->payload->send_message.send_message.reset();
   }
-  if (batch->recv_message) {
-    GRPC_CALL_COMBINER_START(
-        call_combiner, batch->payload->recv_message.recv_message_ready,
-        GRPC_ERROR_REF(error), "failing recv_message_ready");
+  if (batch->cancel_stream) {
+    GRPC_ERROR_UNREF(batch->payload->cancel_stream.cancel_error);
   }
+  // Construct a list of closures to execute.
+  grpc_core::CallCombinerClosureList closures;
   if (batch->recv_initial_metadata) {
-    GRPC_CALL_COMBINER_START(
-        call_combiner,
+    closures.Add(
         batch->payload->recv_initial_metadata.recv_initial_metadata_ready,
         GRPC_ERROR_REF(error), "failing recv_initial_metadata_ready");
   }
-  GRPC_CLOSURE_SCHED(batch->on_complete, error);
-  if (batch->cancel_stream) {
-    GRPC_ERROR_UNREF(batch->payload->cancel_stream.cancel_error);
+  if (batch->recv_message) {
+    closures.Add(batch->payload->recv_message.recv_message_ready,
+                 GRPC_ERROR_REF(error), "failing recv_message_ready");
+  }
+  if (batch->recv_trailing_metadata) {
+    closures.Add(
+        batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready,
+        GRPC_ERROR_REF(error), "failing recv_trailing_metadata_ready");
+  }
+  if (batch->on_complete != nullptr) {
+    closures.Add(batch->on_complete, GRPC_ERROR_REF(error),
+                 "failing on_complete");
   }
+  // Execute closures.
+  closures.RunClosures(call_combiner);
+  GRPC_ERROR_UNREF(error);
 }
 
 typedef struct {

+ 12 - 10
src/core/lib/transport/transport.h

@@ -122,9 +122,15 @@ typedef struct grpc_transport_stream_op_batch_payload
 /* Transport stream op: a set of operations to perform on a transport
    against a single stream */
 typedef struct grpc_transport_stream_op_batch {
-  /** Should be enqueued when all requested operations (excluding recv_message
-      and recv_initial_metadata which have their own closures) in a given batch
-      have been completed. */
+  /** Should be scheduled when all of the non-recv operations in the batch
+      are complete.
+
+      The recv ops (recv_initial_metadata, recv_message, and
+      recv_trailing_metadata) each have their own callbacks.  If a batch
+      contains both recv ops and non-recv ops, on_complete should be
+      scheduled as soon as the non-recv ops are complete, regardless of
+      whether or not the recv ops are complete.  If a batch contains
+      only recv ops, on_complete can be null. */
   grpc_closure* on_complete;
 
   /** Values for the stream op (fields set are determined by flags above) */
@@ -149,9 +155,6 @@ typedef struct grpc_transport_stream_op_batch {
    */
   bool recv_trailing_metadata : 1;
 
-  /** Collect any stats into provided buffer, zero internal stat counters */
-  bool collect_stats : 1;
-
   /** Cancel this stream with the provided error */
   bool cancel_stream : 1;
 
@@ -219,11 +222,10 @@ struct grpc_transport_stream_op_batch_payload {
 
   struct {
     grpc_metadata_batch* recv_trailing_metadata;
-  } recv_trailing_metadata;
-
-  struct {
     grpc_transport_stream_stats* collect_stats;
-  } collect_stats;
+    /** Should be enqueued when initial metadata is ready to be processed. */
+    grpc_closure* recv_trailing_metadata_ready;
+  } recv_trailing_metadata;
 
   /** Forcefully close this stream.
       The HTTP2 semantics should be:

+ 0 - 7
src/core/lib/transport/transport_op_string.cc

@@ -120,13 +120,6 @@ char* grpc_transport_stream_op_batch_string(
     gpr_strvec_add(&b, tmp);
   }
 
-  if (op->collect_stats) {
-    gpr_strvec_add(&b, gpr_strdup(" "));
-    gpr_asprintf(&tmp, "COLLECT_STATS:%p",
-                 op->payload->collect_stats.collect_stats);
-    gpr_strvec_add(&b, tmp);
-  }
-
   out = gpr_strvec_flatten(&b, nullptr);
   gpr_strvec_destroy(&b);
 

+ 5 - 3
src/cpp/server/server_builder.cc

@@ -39,8 +39,8 @@ static void do_plugin_list_init(void) {
 }
 
 ServerBuilder::ServerBuilder()
-    : max_receive_message_size_(-1),
-      max_send_message_size_(-1),
+    : max_receive_message_size_(INT_MIN),
+      max_send_message_size_(INT_MIN),
       sync_server_settings_(SyncServerSettings()),
       resource_quota_(nullptr),
       generic_service_(nullptr) {
@@ -186,10 +186,12 @@ std::unique_ptr<Server> ServerBuilder::BuildAndStart() {
     (*plugin)->UpdateChannelArguments(&args);
   }
 
-  if (max_receive_message_size_ >= 0) {
+  if (max_receive_message_size_ >= -1) {
     args.SetInt(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, max_receive_message_size_);
   }
 
+  // The default message size is -1 (max), so no need to explicitly set it for
+  // -1.
   if (max_send_message_size_ >= 0) {
     args.SetInt(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH, max_send_message_size_);
   }

+ 25 - 0
src/csharp/Grpc.Core.NativeDebug.nuspec

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<package>
+  <metadata>
+    <id>Grpc.Core.NativeDebug</id>
+    <title>Grpc.Core: Native Debug Symbols</title>
+    <summary>Debug symbols for the native library contained in Grpc.Core</summary>
+    <description>Currently contains grpc_csharp_ext.pdb</description>
+    <version>$version$</version>
+    <authors>Google Inc.</authors>
+    <owners>grpc-packages</owners>
+    <licenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</licenseUrl>
+    <projectUrl>https://github.com/grpc/grpc</projectUrl>
+    <requireLicenseAcceptance>false</requireLicenseAcceptance>
+    <releaseNotes>Release $version$</releaseNotes>
+    <copyright>Copyright 2015, Google Inc.</copyright>
+    <tags>gRPC RPC Protocol HTTP/2</tags>
+  </metadata>
+  <files>
+    <!-- forward slashes in src path enable building on Linux -->
+    <file src="nativelibs/csharp_ext_windows_x86/grpc_csharp_ext.dll" target="runtimes/win/native/grpc_csharp_ext.x86.dll" />
+    <file src="nativelibs/csharp_ext_windows_x86/grpc_csharp_ext.pdb" target="runtimes/win/native/grpc_csharp_ext.x86.pdb" />
+    <file src="nativelibs/csharp_ext_windows_x64/grpc_csharp_ext.dll" target="runtimes/win/native/grpc_csharp_ext.x64.dll" />
+    <file src="nativelibs/csharp_ext_windows_x64/grpc_csharp_ext.pdb" target="runtimes/win/native/grpc_csharp_ext.x64.pdb" />
+  </files>
+</package>

+ 1 - 0
src/csharp/build_packages_dotnetcli.bat

@@ -47,6 +47,7 @@ xcopy /Y /I nativelibs\csharp_ext_windows_x64\grpc_csharp_ext.dll ..\..\cmake\bu
 %DOTNET% pack --configuration Release Grpc.Reflection --output ..\..\..\artifacts || goto :error
 
 %NUGET% pack Grpc.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts || goto :error
+%NUGET% pack Grpc.Core.NativeDebug.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts
 %NUGET% pack Grpc.Tools.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts
 
 @rem copy resulting nuget packages to artifacts directory

+ 1 - 0
src/csharp/build_packages_dotnetcli.sh

@@ -46,6 +46,7 @@ dotnet pack --configuration Release Grpc.HealthCheck --output ../../../artifacts
 dotnet pack --configuration Release Grpc.Reflection --output ../../../artifacts
 
 nuget pack Grpc.nuspec -Version "1.13.0-dev" -OutputDirectory ../../artifacts
+nuget pack Grpc.Core.NativeDebug.nuspec -Version "1.13.0-dev" -OutputDirectory ../../artifacts
 nuget pack Grpc.Tools.nuspec -Version "1.13.0-dev" -OutputDirectory ../../artifacts
 
 (cd ../../artifacts && zip csharp_nugets_dotnetcli.zip *.nupkg)

+ 2 - 0
src/objective-c/examples/Sample/Sample.xcodeproj/project.pbxproj

@@ -325,6 +325,7 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				INFOPLIST_FILE = Sample/Info.plist;
+				LD_GENERATE_MAP_FILE = YES;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = "org.grpc.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -337,6 +338,7 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				INFOPLIST_FILE = Sample/Info.plist;
+				LD_GENERATE_MAP_FILE = YES;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = "org.grpc.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";

+ 1 - 1
src/objective-c/examples/Sample/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme

@@ -42,7 +42,7 @@
       </AdditionalOptions>
    </TestAction>
    <LaunchAction
-      buildConfiguration = "Debug"
+      buildConfiguration = "Release"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       launchStyle = "0"

+ 0 - 78
src/objective-c/tests/analyze_link_map.py

@@ -1,78 +0,0 @@
-#!/usr/bin/python
-# Copyright 2018 gRPC authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This script analyzes link map file generated by Xcode. It calculates and
-# prints out the sizes of each dependent library and the total sizes of the
-# symbols.
-# The script takes one parameter, which is the path to the link map file.
-
-import sys
-import re
-
-table_tag = {}
-state = "start"
-
-table_stats_symbol = {}
-table_stats_dead = {}
-section_total_size = 0
-symbol_total_size = 0
-
-
-file_import = sys.argv[1]
-lines = list(open(file_import))
-for line in lines:
-  line_stripped = line[:-1]
-  if "# Object files:" == line_stripped:
-    state = "object"
-    continue
-  elif "# Sections:" == line_stripped:
-    state = "section"
-    continue
-  elif "# Symbols:" == line_stripped:
-    state = "symbol"
-    continue
-  elif "# Dead Stripped Symbols:" == line_stripped:
-    state = "dead"
-    continue
-
-  if state == "object":
-    segs = re.search('(\[ *[0-9]*\]) (.*)', line_stripped)
-    table_tag[segs.group(1)] = segs.group(2)
-
-  if state == "section":
-    if len(line_stripped) == 0 or line_stripped[0] == '#':
-      continue
-    segs = re.search('^(.+?)\s+(.+?)\s+.*', line_stripped)
-    section_total_size += int(segs.group(2), 16)
-
-  if state == "symbol":
-    if len(line_stripped) == 0 or line_stripped[0] == '#':
-      continue
-    segs = re.search('^.+?\s+(.+?)\s+(\[.+?\]).*', line_stripped)
-    target = table_tag[segs.group(2)]
-    target_stripped = re.search('^(.*?)(\(.+?\))?$', target).group(1)
-    size = int(segs.group(1), 16)
-    if not target_stripped in table_stats_symbol:
-      table_stats_symbol[target_stripped] = 0
-    table_stats_symbol[target_stripped] += size
-
-print("Sections total size: %d" % section_total_size)
-
-for target in table_stats_symbol:
-  print(target)
-  print(table_stats_symbol[target])
-  symbol_total_size += table_stats_symbol[target]
-
-print("Symbols total size: %d" % symbol_total_size)

+ 4 - 1
src/objective-c/tests/build_one_example.sh

@@ -42,6 +42,9 @@ xcodebuild \
     build \
     -workspace *.xcworkspace \
     -scheme $SCHEME \
-    -destination name="iPhone 6" \
+    -destination generic/platform=iOS \
+    -derivedDataPath Build \
+    CODE_SIGN_IDENTITY="" \
+    CODE_SIGNING_REQUIRED=NO \
     | egrep -v "$XCODEBUILD_FILTER" \
     | egrep -v "^$" -

+ 3 - 1
src/python/grpcio/grpc/_channel.py

@@ -24,6 +24,8 @@ from grpc import _grpcio_metadata
 from grpc._cython import cygrpc
 from grpc.framework.foundation import callable_util
 
+_LOGGER = logging.getLogger(__name__)
+
 _USER_AGENT = 'grpc-python/{}'.format(_grpcio_metadata.__version__)
 
 _EMPTY_FLAGS = 0
@@ -181,7 +183,7 @@ def _consume_request_iterator(request_iterator, state, call, request_serializer,
             except Exception:  # pylint: disable=broad-except
                 code = grpc.StatusCode.UNKNOWN
                 details = 'Exception iterating requests!'
-                logging.exception(details)
+                _LOGGER.exception(details)
                 call.cancel(_common.STATUS_CODE_TO_CYGRPC_STATUS_CODE[code],
                             details)
                 _abort(state, code, details)

+ 4 - 2
src/python/grpcio/grpc/_common.py

@@ -20,6 +20,8 @@ import six
 import grpc
 from grpc._cython import cygrpc
 
+_LOGGER = logging.getLogger(__name__)
+
 CYGRPC_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY = {
     cygrpc.ConnectivityState.idle:
     grpc.ChannelConnectivity.IDLE,
@@ -73,7 +75,7 @@ def decode(b):
         try:
             return b.decode('utf8')
         except UnicodeDecodeError:
-            logging.exception('Invalid encoding on %s', b)
+            _LOGGER.exception('Invalid encoding on %s', b)
             return b.decode('latin1')
 
 
@@ -84,7 +86,7 @@ def _transform(message, transformer, exception_message):
         try:
             return transformer(message)
         except Exception:  # pylint: disable=broad-except
-            logging.exception(exception_message)
+            _LOGGER.exception(exception_message)
             return None
 
 

+ 5 - 0
src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd.pxi

@@ -57,6 +57,11 @@ cdef class ChannelCredentials:
   cdef grpc_channel_credentials *c_credentials
 
 
+cdef class SSLSessionCacheLRU:
+
+  cdef grpc_ssl_session_cache *_cache
+
+
 cdef class SSLChannelCredentials(ChannelCredentials):
 
   cdef readonly object _pem_root_certificates

+ 19 - 0
src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi

@@ -17,6 +17,9 @@ cimport cpython
 import grpc
 import threading
 
+from libc.stdint cimport uintptr_t
+
+
 def _spawn_callback_in_thread(cb_func, args):
   threading.Thread(target=cb_func, args=args).start()
 
@@ -29,6 +32,7 @@ def set_async_callback_func(callback_func):
 def _spawn_callback_async(callback, args):
   async_callback_func(callback, args)
 
+
 cdef class CallCredentials:
 
   cdef grpc_call_credentials *c(self):
@@ -107,6 +111,21 @@ cdef class ChannelCredentials:
     raise NotImplementedError()
 
 
+cdef class SSLSessionCacheLRU:
+
+  def __cinit__(self, capacity):
+    grpc_init()
+    self._cache = grpc_ssl_session_cache_create_lru(capacity)
+
+  def __int__(self):
+    return <uintptr_t>self._cache
+
+  def __dealloc__(self):
+    if self._cache != NULL:
+        grpc_ssl_session_cache_destroy(self._cache)
+    grpc_shutdown()
+
+
 cdef class SSLChannelCredentials(ChannelCredentials):
 
   def __cinit__(self, pem_root_certificates, private_key, certificate_chain):

+ 9 - 0
src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi

@@ -131,6 +131,7 @@ cdef extern from "grpc/grpc.h":
   const char *GRPC_ARG_PRIMARY_USER_AGENT_STRING
   const char *GRPC_ARG_SECONDARY_USER_AGENT_STRING
   const char *GRPC_SSL_TARGET_NAME_OVERRIDE_ARG
+  const char *GRPC_SSL_SESSION_CACHE_ARG
   const char *GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM
   const char *GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL
   const char *GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET
@@ -452,8 +453,16 @@ cdef extern from "grpc/grpc_security.h":
     # We don't care about the internals (and in fact don't know them)
     pass
 
+
+  ctypedef struct grpc_ssl_session_cache:
+    # We don't care about the internals (and in fact don't know them)
+    pass
+
   ctypedef void (*grpc_ssl_roots_override_callback)(char **pem_root_certs)
 
+  grpc_ssl_session_cache *grpc_ssl_session_cache_create_lru(size_t capacity)
+  void grpc_ssl_session_cache_destroy(grpc_ssl_session_cache* cache)
+
   void grpc_set_ssl_roots_override_callback(
       grpc_ssl_roots_override_callback cb) nogil
 

+ 2 - 1
src/python/grpcio/grpc/_cython/_cygrpc/grpc_string.pyx.pxi

@@ -14,6 +14,7 @@
 
 import logging
 
+_LOGGER = logging.getLogger(__name__)
 
 # This function will ascii encode unicode string inputs if neccesary.
 # In Python3, unicode strings are the default str type.
@@ -49,5 +50,5 @@ cdef str _decode(bytes bytestring):
         try:
             return bytestring.decode('utf8')
         except UnicodeDecodeError:
-            logging.exception('Invalid encoding on %s', bytestring)
+            _LOGGER.exception('Invalid encoding on %s', bytestring)
             return bytestring.decode('latin1')

+ 1 - 0
src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi

@@ -51,6 +51,7 @@ class ChannelArgKey:
   default_authority = GRPC_ARG_DEFAULT_AUTHORITY
   primary_user_agent_string = GRPC_ARG_PRIMARY_USER_AGENT_STRING
   secondary_user_agent_string = GRPC_ARG_SECONDARY_USER_AGENT_STRING
+  ssl_session_cache = GRPC_SSL_SESSION_CACHE_ARG
   ssl_target_name_override = GRPC_SSL_TARGET_NAME_OVERRIDE_ARG
 
 

+ 4 - 2
src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi

@@ -18,6 +18,8 @@ import logging
 import time
 import grpc
 
+_LOGGER = logging.getLogger(__name__)
+
 cdef grpc_ssl_certificate_config_reload_status _server_cert_config_fetcher_wrapper(
         void* user_data, grpc_ssl_server_certificate_config **config) with gil:
   # This is a credentials.ServerCertificateConfig
@@ -34,13 +36,13 @@ cdef grpc_ssl_certificate_config_reload_status _server_cert_config_fetcher_wrapp
     try:
       cert_config_wrapper = user_cb()
     except Exception:
-      logging.exception('Error fetching certificate config')
+      _LOGGER.exception('Error fetching certificate config')
       return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL
     if cert_config_wrapper is None:
       return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED
     elif not isinstance(
         cert_config_wrapper, grpc.ServerCertificateConfiguration):
-      logging.error(
+      _LOGGER.error(
           'Error fetching certificate configuration: certificate '
           'configuration must be of type grpc.ServerCertificateConfiguration, '
           'not %s' % type(cert_config_wrapper).__name__)

+ 3 - 1
src/python/grpcio/grpc/_plugin_wrapping.py

@@ -20,6 +20,8 @@ import grpc
 from grpc import _common
 from grpc._cython import cygrpc
 
+_LOGGER = logging.getLogger(__name__)
+
 
 class _AuthMetadataContext(
         collections.namedtuple('AuthMetadataContext', (
@@ -76,7 +78,7 @@ class _Plugin(object):
                                   _AuthMetadataPluginCallback(
                                       callback_state, callback))
         except Exception as exception:  # pylint: disable=broad-except
-            logging.exception(
+            _LOGGER.exception(
                 'AuthMetadataPluginCallback "%s" raised exception!',
                 self._metadata_plugin)
             with callback_state.lock:

+ 6 - 4
src/python/grpcio/grpc/_server.py

@@ -27,6 +27,8 @@ from grpc import _interceptor
 from grpc._cython import cygrpc
 from grpc.framework.foundation import callable_util
 
+_LOGGER = logging.getLogger(__name__)
+
 _SHUTDOWN_TAG = 'shutdown'
 _REQUEST_CALL_TAG = 'request_call'
 
@@ -279,7 +281,7 @@ class _Context(grpc.ServicerContext):
     def abort(self, code, details):
         # treat OK like other invalid arguments: fail the RPC
         if code == grpc.StatusCode.OK:
-            logging.error(
+            _LOGGER.error(
                 'abort() called with StatusCode.OK; returning UNKNOWN')
             code = grpc.StatusCode.UNKNOWN
             details = ''
@@ -390,7 +392,7 @@ def _call_behavior(rpc_event, state, behavior, argument, request_deserializer):
                        b'RPC Aborted')
             elif exception not in state.rpc_errors:
                 details = 'Exception calling application: {}'.format(exception)
-                logging.exception(details)
+                _LOGGER.exception(details)
                 _abort(state, rpc_event.call, cygrpc.StatusCode.unknown,
                        _common.encode(details))
         return None, False
@@ -408,7 +410,7 @@ def _take_response_from_response_iterator(rpc_event, state, response_iterator):
                        b'RPC Aborted')
             elif exception not in state.rpc_errors:
                 details = 'Exception iterating responses: {}'.format(exception)
-                logging.exception(details)
+                _LOGGER.exception(details)
                 _abort(state, rpc_event.call, cygrpc.StatusCode.unknown,
                        _common.encode(details))
         return None, False
@@ -617,7 +619,7 @@ def _handle_call(rpc_event, generic_handlers, interceptor_pipeline, thread_pool,
                                                   interceptor_pipeline)
         except Exception as exception:  # pylint: disable=broad-except
             details = 'Exception servicing handler: {}'.format(exception)
-            logging.exception(details)
+            _LOGGER.exception(details)
             return _reject_rpc(rpc_event, cygrpc.StatusCode.unknown,
                                b'Error in service handler!'), None
         if method_handler is None:

+ 45 - 0
src/python/grpcio/grpc/experimental/session_cache.py

@@ -0,0 +1,45 @@
+# Copyright 2018 gRPC authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""gRPC's APIs for TLS Session Resumption support"""
+
+from grpc._cython import cygrpc as _cygrpc
+
+
+def ssl_session_cache_lru(capacity):
+    """Creates an SSLSessionCache with LRU replacement policy
+
+    Args:
+      capacity: Size of the cache
+
+    Returns:
+      An SSLSessionCache with LRU replacement policy that can be passed as a value for
+      the grpc.ssl_session_cache option to a grpc.Channel. SSL session caches are used
+      to store session tickets, which clients can present to resume previous TLS sessions
+      with a server.
+    """
+    return SSLSessionCache(_cygrpc.SSLSessionCacheLRU(capacity))
+
+
+class SSLSessionCache(object):
+    """An encapsulation of a session cache used for TLS session resumption.
+
+    Instances of this class can be passed to a Channel as values for the
+    grpc.ssl_session_cache option
+    """
+
+    def __init__(self, cache):
+        self._cache = cache
+
+    def __int__(self):
+        return int(self._cache)

+ 3 - 1
src/python/grpcio/grpc/framework/foundation/callable_util.py

@@ -21,6 +21,8 @@ import logging
 
 import six
 
+_LOGGER = logging.getLogger(__name__)
+
 
 class Outcome(six.with_metaclass(abc.ABCMeta)):
     """A sum type describing the outcome of some call.
@@ -53,7 +55,7 @@ def _call_logging_exceptions(behavior, message, *args, **kwargs):
         return _EasyOutcome(Outcome.Kind.RETURNED, behavior(*args, **kwargs),
                             None)
     except Exception as e:  # pylint: disable=broad-except
-        logging.exception(message)
+        _LOGGER.exception(message)
         return _EasyOutcome(Outcome.Kind.RAISED, None, e)
 
 

+ 3 - 1
src/python/grpcio/grpc/framework/foundation/logging_pool.py

@@ -17,6 +17,8 @@ import logging
 
 from concurrent import futures
 
+_LOGGER = logging.getLogger(__name__)
+
 
 def _wrap(behavior):
     """Wraps an arbitrary callable behavior in exception-logging."""
@@ -25,7 +27,7 @@ def _wrap(behavior):
         try:
             return behavior(*args, **kwargs)
         except Exception:
-            logging.exception(
+            _LOGGER.exception(
                 'Unexpected exception from %s executed in logging pool!',
                 behavior)
             raise

+ 2 - 1
src/python/grpcio/grpc/framework/foundation/stream_util.py

@@ -19,6 +19,7 @@ import threading
 from grpc.framework.foundation import stream
 
 _NO_VALUE = object()
+_LOGGER = logging.getLogger(__name__)
 
 
 class TransformingConsumer(stream.Consumer):
@@ -103,7 +104,7 @@ class ThreadSwitchingConsumer(stream.Consumer):
                 else:
                     sink.consume(value)
             except Exception as e:  # pylint:disable=broad-except
-                logging.exception(e)
+                _LOGGER.exception(e)
 
             with self._lock:
                 if terminate:

+ 2 - 1
src/python/grpcio_testing/grpc_testing/_channel/_invocation.py

@@ -18,6 +18,7 @@ import threading
 import grpc
 
 _NOT_YET_OBSERVED = object()
+_LOGGER = logging.getLogger(__name__)
 
 
 def _cancel(handler):
@@ -248,7 +249,7 @@ def consume_requests(request_iterator, handler):
                 break
             except Exception:  # pylint: disable=broad-except
                 details = 'Exception iterating requests!'
-                logging.exception(details)
+                _LOGGER.exception(details)
                 handler.cancel(grpc.StatusCode.UNKNOWN, details)
 
     consumption = threading.Thread(target=_consume)

+ 4 - 2
src/python/grpcio_testing/grpc_testing/_server/_rpc.py

@@ -18,6 +18,8 @@ import threading
 import grpc
 from grpc_testing import _common
 
+_LOGGER = logging.getLogger(__name__)
+
 
 class Rpc(object):
 
@@ -47,7 +49,7 @@ class Rpc(object):
                 try:
                     callback()
                 except Exception:  # pylint: disable=broad-except
-                    logging.exception('Exception calling server-side callback!')
+                    _LOGGER.exception('Exception calling server-side callback!')
 
         callback_calling_thread = threading.Thread(target=call_back)
         callback_calling_thread.start()
@@ -86,7 +88,7 @@ class Rpc(object):
     def application_exception_abort(self, exception):
         with self._condition:
             if exception not in self._rpc_errors:
-                logging.exception('Exception calling application!')
+                _LOGGER.exception('Exception calling application!')
                 self._abort(
                     grpc.StatusCode.UNKNOWN,
                     'Exception calling application: {}'.format(exception))

+ 3 - 1
src/python/grpcio_testing/grpc_testing/_time.py

@@ -21,13 +21,15 @@ import time as _time
 import grpc
 import grpc_testing
 
+_LOGGER = logging.getLogger(__name__)
+
 
 def _call(behaviors):
     for behavior in behaviors:
         try:
             behavior()
         except Exception:  # pylint: disable=broad-except
-            logging.exception('Exception calling behavior "%r"!', behavior)
+            _LOGGER.exception('Exception calling behavior "%r"!', behavior)
 
 
 def _call_in_thread(behaviors):

+ 4 - 3
src/python/grpcio_tests/tests/interop/server.py

@@ -26,6 +26,7 @@ from tests.interop import resources
 from tests.unit import test_common
 
 _ONE_DAY_IN_SECONDS = 60 * 60 * 24
+_LOGGER = logging.getLogger(__name__)
 
 
 def serve():
@@ -52,14 +53,14 @@ def serve():
         server.add_insecure_port('[::]:{}'.format(args.port))
 
     server.start()
-    logging.info('Server serving.')
+    _LOGGER.info('Server serving.')
     try:
         while True:
             time.sleep(_ONE_DAY_IN_SECONDS)
     except BaseException as e:
-        logging.info('Caught exception "%s"; stopping server...', e)
+        _LOGGER.info('Caught exception "%s"; stopping server...', e)
         server.stop(None)
-        logging.info('Server stopped; exiting.')
+        _LOGGER.info('Server stopped; exiting.')
 
 
 if __name__ == '__main__':

+ 1 - 0
src/python/grpcio_tests/tests/tests.json

@@ -53,6 +53,7 @@
   "unit._server_ssl_cert_config_test.ServerSSLCertReloadTestCertConfigReuse",
   "unit._server_ssl_cert_config_test.ServerSSLCertReloadTestWithClientAuth",
   "unit._server_ssl_cert_config_test.ServerSSLCertReloadTestWithoutClientAuth",
+  "unit._session_cache_test.SSLSessionCacheTest",
   "unit.beta._beta_features_test.BetaFeaturesTest",
   "unit.beta._beta_features_test.ContextManagementAndLifecycleTest",
   "unit.beta._connectivity_channel_test.ConnectivityStatesTest",

+ 45 - 0
src/python/grpcio_tests/tests/unit/_auth_context_test.py

@@ -18,6 +18,7 @@ import unittest
 
 import grpc
 from grpc import _channel
+from grpc.experimental import session_cache
 import six
 
 from tests.unit import test_common
@@ -140,6 +141,50 @@ class AuthContextTest(unittest.TestCase):
         self.assertSequenceEqual([b'*.test.google.com'],
                                  auth_ctx['x509_common_name'])
 
+    def _do_one_shot_client_rpc(self, channel_creds, channel_options, port,
+                                expect_ssl_session_reused):
+        channel = grpc.secure_channel(
+            'localhost:{}'.format(port), channel_creds, options=channel_options)
+        response = channel.unary_unary(_UNARY_UNARY)(_REQUEST)
+        auth_data = pickle.loads(response)
+        self.assertEqual(expect_ssl_session_reused,
+                         auth_data[_AUTH_CTX]['ssl_session_reused'])
+        channel.close()
+
+    def testSessionResumption(self):
+        # Set up a secure server
+        handler = grpc.method_handlers_generic_handler('test', {
+            'UnaryUnary':
+            grpc.unary_unary_rpc_method_handler(handle_unary_unary)
+        })
+        server = test_common.test_server()
+        server.add_generic_rpc_handlers((handler,))
+        server_cred = grpc.ssl_server_credentials(_SERVER_CERTS)
+        port = server.add_secure_port('[::]:0', server_cred)
+        server.start()
+
+        # Create a cache for TLS session tickets
+        cache = session_cache.ssl_session_cache_lru(1)
+        channel_creds = grpc.ssl_channel_credentials(
+            root_certificates=_TEST_ROOT_CERTIFICATES)
+        channel_options = _PROPERTY_OPTIONS + (
+            ('grpc.ssl_session_cache', cache),)
+
+        # Initial connection has no session to resume
+        self._do_one_shot_client_rpc(
+            channel_creds,
+            channel_options,
+            port,
+            expect_ssl_session_reused=[b'false'])
+
+        # Subsequent connections resume sessions
+        self._do_one_shot_client_rpc(
+            channel_creds,
+            channel_options,
+            port,
+            expect_ssl_session_reused=[b'true'])
+        server.stop(None)
+
 
 if __name__ == '__main__':
     unittest.main(verbosity=2)

+ 145 - 0
src/python/grpcio_tests/tests/unit/_session_cache_test.py

@@ -0,0 +1,145 @@
+# Copyright 2018 gRPC authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Tests experimental TLS Session Resumption API"""
+
+import pickle
+import unittest
+
+import grpc
+from grpc import _channel
+from grpc.experimental import session_cache
+
+from tests.unit import test_common
+from tests.unit import resources
+
+_REQUEST = b'\x00\x00\x00'
+_RESPONSE = b'\x00\x00\x00'
+
+_UNARY_UNARY = '/test/UnaryUnary'
+
+_SERVER_HOST_OVERRIDE = 'foo.test.google.fr'
+_ID = 'id'
+_ID_KEY = 'id_key'
+_AUTH_CTX = 'auth_ctx'
+
+_PRIVATE_KEY = resources.private_key()
+_CERTIFICATE_CHAIN = resources.certificate_chain()
+_TEST_ROOT_CERTIFICATES = resources.test_root_certificates()
+_SERVER_CERTS = ((_PRIVATE_KEY, _CERTIFICATE_CHAIN),)
+_PROPERTY_OPTIONS = ((
+    'grpc.ssl_target_name_override',
+    _SERVER_HOST_OVERRIDE,
+),)
+
+
+def handle_unary_unary(request, servicer_context):
+    return pickle.dumps({
+        _ID: servicer_context.peer_identities(),
+        _ID_KEY: servicer_context.peer_identity_key(),
+        _AUTH_CTX: servicer_context.auth_context()
+    })
+
+
+def start_secure_server():
+    handler = grpc.method_handlers_generic_handler('test', {
+        'UnaryUnary':
+        grpc.unary_unary_rpc_method_handler(handle_unary_unary)
+    })
+    server = test_common.test_server()
+    server.add_generic_rpc_handlers((handler,))
+    server_cred = grpc.ssl_server_credentials(_SERVER_CERTS)
+    port = server.add_secure_port('[::]:0', server_cred)
+    server.start()
+
+    return server, port
+
+
+class SSLSessionCacheTest(unittest.TestCase):
+
+    def _do_one_shot_client_rpc(self, channel_creds, channel_options, port,
+                                expect_ssl_session_reused):
+        channel = grpc.secure_channel(
+            'localhost:{}'.format(port), channel_creds, options=channel_options)
+        response = channel.unary_unary(_UNARY_UNARY)(_REQUEST)
+        auth_data = pickle.loads(response)
+        self.assertEqual(expect_ssl_session_reused,
+                         auth_data[_AUTH_CTX]['ssl_session_reused'])
+        channel.close()
+
+    def testSSLSessionCacheLRU(self):
+        server_1, port_1 = start_secure_server()
+
+        cache = session_cache.ssl_session_cache_lru(1)
+        channel_creds = grpc.ssl_channel_credentials(
+            root_certificates=_TEST_ROOT_CERTIFICATES)
+        channel_options = _PROPERTY_OPTIONS + (
+            ('grpc.ssl_session_cache', cache),)
+
+        # Initial connection has no session to resume
+        self._do_one_shot_client_rpc(
+            channel_creds,
+            channel_options,
+            port_1,
+            expect_ssl_session_reused=[b'false'])
+
+        # Connection to server_1 resumes from initial session
+        self._do_one_shot_client_rpc(
+            channel_creds,
+            channel_options,
+            port_1,
+            expect_ssl_session_reused=[b'true'])
+
+        # Connection to a different server with the same name overwrites the cache entry
+        server_2, port_2 = start_secure_server()
+        self._do_one_shot_client_rpc(
+            channel_creds,
+            channel_options,
+            port_2,
+            expect_ssl_session_reused=[b'false'])
+        self._do_one_shot_client_rpc(
+            channel_creds,
+            channel_options,
+            port_2,
+            expect_ssl_session_reused=[b'true'])
+        server_2.stop(None)
+
+        # Connection to server_1 now falls back to full TLS handshake
+        self._do_one_shot_client_rpc(
+            channel_creds,
+            channel_options,
+            port_1,
+            expect_ssl_session_reused=[b'false'])
+
+        # Re-creating server_1 causes old sessions to become invalid
+        server_1.stop(None)
+        server_1, port_1 = start_secure_server()
+
+        # Old sessions should no longer be valid
+        self._do_one_shot_client_rpc(
+            channel_creds,
+            channel_options,
+            port_1,
+            expect_ssl_session_reused=[b'false'])
+
+        # Resumption should work for subsequent connections
+        self._do_one_shot_client_rpc(
+            channel_creds,
+            channel_options,
+            port_1,
+            expect_ssl_session_reused=[b'true'])
+        server_1.stop(None)
+
+
+if __name__ == '__main__':
+    unittest.main(verbosity=2)

+ 1 - 0
templates/gRPC-ProtoRPC.podspec.template

@@ -44,6 +44,7 @@
 
     src_dir = 'src/objective-c/ProtoRPC'
 
+    s.default_subspec = 'Main'
 
     s.subspec 'Main' do |ss|
       ss.header_mappings_dir = "#{src_dir}"

+ 1 - 0
templates/src/csharp/build_packages_dotnetcli.bat.template

@@ -49,6 +49,7 @@
   %%DOTNET% pack --configuration Release Grpc.Reflection --output ..\..\..\artifacts || goto :error
   
   %%NUGET% pack Grpc.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts || goto :error
+  %%NUGET% pack Grpc.Core.NativeDebug.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts
   %%NUGET% pack Grpc.Tools.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts
   
   @rem copy resulting nuget packages to artifacts directory

+ 1 - 0
templates/src/csharp/build_packages_dotnetcli.sh.template

@@ -48,6 +48,7 @@
   dotnet pack --configuration Release Grpc.Reflection --output ../../../artifacts
   
   nuget pack Grpc.nuspec -Version "${settings.csharp_version}" -OutputDirectory ../../artifacts
+  nuget pack Grpc.Core.NativeDebug.nuspec -Version "${settings.csharp_version}" -OutputDirectory ../../artifacts
   nuget pack Grpc.Tools.nuspec -Version "${settings.csharp_version}" -OutputDirectory ../../artifacts
   
   (cd ../../artifacts && zip csharp_nugets_dotnetcli.zip *.nupkg)

+ 3 - 3
templates/test/cpp/naming/resolver_component_tests_defs.include

@@ -58,7 +58,7 @@ def wait_until_dns_server_is_up(args,
     test_runner_log('Health check: attempt to connect to DNS server over TCP.')
     tcp_connect_subprocess = subprocess.Popen([
         args.tcp_connect_bin_path,
-        '--server_host', '127.0.0.1',
+        '--server_host', '::1',
         '--server_port', str(args.dns_server_port),
         '--timeout', str(1)])
     tcp_connect_subprocess.communicate()
@@ -68,7 +68,7 @@ def wait_until_dns_server_is_up(args,
       dns_resolver_subprocess = subprocess.Popen([
           args.dns_resolver_bin_path,
           '--qname', 'health-check-local-dns-server-is-alive.resolver-tests.grpctestingexp',
-          '--server_host', '127.0.0.1',
+          '--server_host', '::1',
           '--server_port', str(args.dns_server_port)],
           stdout=subprocess.PIPE)
       dns_resolver_stdout, _ = dns_resolver_subprocess.communicate()
@@ -125,7 +125,7 @@ current_test_subprocess = subprocess.Popen([\
 
 \
   % endfor
-  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])\
+  '--local_dns_server_address', '[::1]:%d' % args.dns_server_port])\
 
 current_test_subprocess.communicate()\
 

+ 16 - 8
test/cpp/microbenchmarks/bm_call_create.cc

@@ -621,18 +621,26 @@ typedef struct {
 static void StartTransportStreamOp(grpc_call_element* elem,
                                    grpc_transport_stream_op_batch* op) {
   call_data* calld = static_cast<call_data*>(elem->call_data);
+  // Construct list of closures to return.
+  grpc_core::CallCombinerClosureList closures;
   if (op->recv_initial_metadata) {
-    GRPC_CALL_COMBINER_START(
-        calld->call_combiner,
-        op->payload->recv_initial_metadata.recv_initial_metadata_ready,
-        GRPC_ERROR_NONE, "recv_initial_metadata");
+    closures.Add(op->payload->recv_initial_metadata.recv_initial_metadata_ready,
+                 GRPC_ERROR_NONE, "recv_initial_metadata");
   }
   if (op->recv_message) {
-    GRPC_CALL_COMBINER_START(calld->call_combiner,
-                             op->payload->recv_message.recv_message_ready,
-                             GRPC_ERROR_NONE, "recv_message");
+    closures.Add(op->payload->recv_message.recv_message_ready, GRPC_ERROR_NONE,
+                 "recv_message");
   }
-  GRPC_CLOSURE_SCHED(op->on_complete, GRPC_ERROR_NONE);
+  if (op->recv_trailing_metadata) {
+    closures.Add(
+        op->payload->recv_trailing_metadata.recv_trailing_metadata_ready,
+        GRPC_ERROR_NONE, "recv_trailing_metadata");
+  }
+  if (op->on_complete != nullptr) {
+    closures.Add(op->on_complete, GRPC_ERROR_NONE, "on_complete");
+  }
+  // Execute closures.
+  closures.RunClosures(calld->call_combiner);
 }
 
 static void StartTransportOp(grpc_channel_element* elem,

+ 1 - 17
test/cpp/naming/BUILD

@@ -22,7 +22,7 @@ package(
 
 licenses(["notice"])  # Apache v2
 
-load("//bazel:grpc_build_system.bzl", "grpc_py_binary", "grpc_cc_test")
+load("//bazel:grpc_build_system.bzl", "grpc_py_binary")
 
 load(":generate_resolver_component_tests.bzl", "generate_resolver_component_tests")
 
@@ -35,20 +35,4 @@ grpc_py_binary(
     testonly = True,
 )
 
-grpc_cc_test(
-    name = "cancel_ares_query_test",
-    srcs = ["cancel_ares_query_test.cc"],
-    external_deps = ["gmock"],
-    deps = [
-        "//test/cpp/util:test_util",
-        "//test/core/util:grpc_test_util",
-        "//test/core/util:gpr_test_util",
-        "//:grpc++",
-        "//:grpc",
-        "//:gpr",
-        "//test/cpp/util:test_config",
-        "//test/core/end2end:cq_verifier",
-    ],
-)
-
 generate_resolver_component_tests()

+ 0 - 289
test/cpp/naming/cancel_ares_query_test.cc

@@ -1,289 +0,0 @@
-/*
- *
- * Copyright 2015 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 <stdio.h>
-#include <string.h>
-
-#include <gflags/gflags.h>
-#include <gmock/gmock.h>
-
-#include <grpc/byte_buffer.h>
-#include <grpc/grpc.h>
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
-#include <grpc/support/time.h>
-#include "include/grpc/support/string_util.h"
-#include "src/core/ext/filters/client_channel/resolver.h"
-#include "src/core/ext/filters/client_channel/resolver_registry.h"
-#include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/debug/stats.h"
-#include "src/core/lib/gpr/env.h"
-#include "src/core/lib/gpr/host_port.h"
-#include "src/core/lib/gpr/string.h"
-#include "src/core/lib/gprpp/orphanable.h"
-#include "src/core/lib/gprpp/thd.h"
-#include "src/core/lib/iomgr/combiner.h"
-#include "src/core/lib/iomgr/pollset.h"
-#include "src/core/lib/iomgr/pollset_set.h"
-#include "test/core/end2end/cq_verifier.h"
-#include "test/core/util/cmdline.h"
-#include "test/core/util/port.h"
-#include "test/core/util/test_config.h"
-
-// TODO: pull in different headers when enabling this
-// test on windows. Also set BAD_SOCKET_RETURN_VAL
-// to INVALID_SOCKET on windows.
-#include "src/core/lib/iomgr/sockaddr_posix.h"
-#define BAD_SOCKET_RETURN_VAL -1
-
-namespace {
-
-void* Tag(intptr_t t) { return (void*)t; }
-
-gpr_timespec FiveSecondsFromNow(void) {
-  return grpc_timeout_seconds_to_deadline(5);
-}
-
-void DrainCq(grpc_completion_queue* cq) {
-  grpc_event ev;
-  do {
-    ev = grpc_completion_queue_next(cq, FiveSecondsFromNow(), nullptr);
-  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
-}
-
-void EndTest(grpc_channel* client, grpc_completion_queue* cq) {
-  grpc_channel_destroy(client);
-  grpc_completion_queue_shutdown(cq);
-  DrainCq(cq);
-  grpc_completion_queue_destroy(cq);
-}
-
-class FakeNonResponsiveDNSServer {
- public:
-  FakeNonResponsiveDNSServer(int port) {
-    socket_ = socket(AF_INET6, SOCK_DGRAM, 0);
-    if (socket_ == BAD_SOCKET_RETURN_VAL) {
-      gpr_log(GPR_DEBUG, "Failed to create UDP ipv6 socket");
-      abort();
-    }
-    sockaddr_in6 addr;
-    memset(&addr, 0, sizeof(addr));
-    addr.sin6_family = AF_INET6;
-    addr.sin6_port = htons(port);
-    ((char*)&addr.sin6_addr)[15] = 1;
-    if (bind(socket_, (const sockaddr*)&addr, sizeof(addr)) != 0) {
-      gpr_log(GPR_DEBUG, "Failed to bind UDP ipv6 socket to [::1]:%d", port);
-      abort();
-    }
-  }
-  ~FakeNonResponsiveDNSServer() { close(socket_); }
-
- private:
-  int socket_;
-};
-
-struct ArgsStruct {
-  gpr_atm done_atm;
-  gpr_mu* mu;
-  grpc_pollset* pollset;
-  grpc_pollset_set* pollset_set;
-  grpc_combiner* lock;
-  grpc_channel_args* channel_args;
-};
-
-void ArgsInit(ArgsStruct* args) {
-  args->pollset = (grpc_pollset*)gpr_zalloc(grpc_pollset_size());
-  grpc_pollset_init(args->pollset, &args->mu);
-  args->pollset_set = grpc_pollset_set_create();
-  grpc_pollset_set_add_pollset(args->pollset_set, args->pollset);
-  args->lock = grpc_combiner_create();
-  gpr_atm_rel_store(&args->done_atm, 0);
-  args->channel_args = nullptr;
-}
-
-void DoNothing(void* arg, grpc_error* error) {}
-
-void ArgsFinish(ArgsStruct* args) {
-  grpc_pollset_set_del_pollset(args->pollset_set, args->pollset);
-  grpc_pollset_set_destroy(args->pollset_set);
-  grpc_closure DoNothing_cb;
-  GRPC_CLOSURE_INIT(&DoNothing_cb, DoNothing, nullptr,
-                    grpc_schedule_on_exec_ctx);
-  grpc_pollset_shutdown(args->pollset, &DoNothing_cb);
-  // exec_ctx needs to be flushed before calling grpc_pollset_destroy()
-  grpc_channel_args_destroy(args->channel_args);
-  grpc_core::ExecCtx::Get()->Flush();
-  grpc_pollset_destroy(args->pollset);
-  gpr_free(args->pollset);
-  GRPC_COMBINER_UNREF(args->lock, nullptr);
-}
-
-void PollPollsetUntilRequestDone(ArgsStruct* args) {
-  while (true) {
-    bool done = gpr_atm_acq_load(&args->done_atm) != 0;
-    if (done) {
-      break;
-    }
-    grpc_pollset_worker* worker = nullptr;
-    grpc_core::ExecCtx exec_ctx;
-    gpr_mu_lock(args->mu);
-    GRPC_LOG_IF_ERROR(
-        "pollset_work",
-        grpc_pollset_work(args->pollset, &worker,
-                          grpc_timespec_to_millis_round_up(
-                              gpr_inf_future(GPR_CLOCK_REALTIME))));
-    gpr_mu_unlock(args->mu);
-  }
-}
-
-void CheckResolverResultAssertFailureLocked(void* arg, grpc_error* error) {
-  EXPECT_NE(error, GRPC_ERROR_NONE);
-  ArgsStruct* args = static_cast<ArgsStruct*>(arg);
-  gpr_atm_rel_store(&args->done_atm, 1);
-  gpr_mu_lock(args->mu);
-  GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(args->pollset, nullptr));
-  gpr_mu_unlock(args->mu);
-}
-
-TEST(CancelDuringAresQuery, TestCancelActiveDNSQuery) {
-  grpc_core::ExecCtx exec_ctx;
-  ArgsStruct args;
-  ArgsInit(&args);
-  int fake_dns_port = grpc_pick_unused_port_or_die();
-  FakeNonResponsiveDNSServer fake_dns_server(fake_dns_port);
-  char* client_target;
-  GPR_ASSERT(gpr_asprintf(
-      &client_target,
-      "dns://[::1]:%d/dont-care-since-wont-be-resolved.test.com:1234",
-      fake_dns_port));
-  // create resolver and resolve
-  grpc_core::OrphanablePtr<grpc_core::Resolver> resolver =
-      grpc_core::ResolverRegistry::CreateResolver(client_target, nullptr,
-                                                  args.pollset_set, args.lock);
-  gpr_free(client_target);
-  grpc_closure on_resolver_result_changed;
-  GRPC_CLOSURE_INIT(&on_resolver_result_changed,
-                    CheckResolverResultAssertFailureLocked, (void*)&args,
-                    grpc_combiner_scheduler(args.lock));
-  resolver->NextLocked(&args.channel_args, &on_resolver_result_changed);
-  // Without resetting and causing resolver shutdown, the
-  // PollPollsetUntilRequestDone call should never finish.
-  resolver.reset();
-  grpc_core::ExecCtx::Get()->Flush();
-  PollPollsetUntilRequestDone(&args);
-  ArgsFinish(&args);
-}
-
-TEST(CancelDuringAresQuery,
-     TestHitDeadlineAndDestroyChannelDuringAresResolutionIsGraceful) {
-  // Start up fake non responsive DNS server
-  int fake_dns_port = grpc_pick_unused_port_or_die();
-  FakeNonResponsiveDNSServer fake_dns_server(fake_dns_port);
-  // Create a call that will try to use the fake DNS server
-  char* client_target = nullptr;
-  GPR_ASSERT(gpr_asprintf(
-      &client_target,
-      "dns://[::1]:%d/dont-care-since-wont-be-resolved.test.com:1234",
-      fake_dns_port));
-  grpc_channel* client =
-      grpc_insecure_channel_create(client_target,
-                                   /* client_args */ nullptr, nullptr);
-  gpr_free(client_target);
-  grpc_completion_queue* cq = grpc_completion_queue_create_for_next(nullptr);
-  cq_verifier* cqv = cq_verifier_create(cq);
-  gpr_timespec deadline = grpc_timeout_milliseconds_to_deadline(10);
-  grpc_call* call = grpc_channel_create_call(
-      client, nullptr, GRPC_PROPAGATE_DEFAULTS, cq,
-      grpc_slice_from_static_string("/foo"), nullptr, deadline, nullptr);
-  GPR_ASSERT(call);
-  grpc_metadata_array initial_metadata_recv;
-  grpc_metadata_array trailing_metadata_recv;
-  grpc_metadata_array request_metadata_recv;
-  grpc_metadata_array_init(&initial_metadata_recv);
-  grpc_metadata_array_init(&trailing_metadata_recv);
-  grpc_metadata_array_init(&request_metadata_recv);
-  grpc_call_details call_details;
-  grpc_call_details_init(&call_details);
-  grpc_status_code status;
-  const char* error_string;
-  grpc_slice details;
-  // Set ops for client the request
-  grpc_op ops_base[6];
-  memset(ops_base, 0, sizeof(ops_base));
-  grpc_op* op = ops_base;
-  op->op = GRPC_OP_SEND_INITIAL_METADATA;
-  op->data.send_initial_metadata.count = 0;
-  op->flags = 0;
-  op->reserved = nullptr;
-  op++;
-  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
-  op->flags = 0;
-  op->reserved = nullptr;
-  op++;
-  op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
-  op->flags = 0;
-  op->reserved = nullptr;
-  op++;
-  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
-  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
-  op->data.recv_status_on_client.status = &status;
-  op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.error_string = &error_string;
-  op->flags = 0;
-  op->reserved = nullptr;
-  op++;
-  // Run the call and sanity check it failed as expected
-  grpc_call_error error = grpc_call_start_batch(
-      call, ops_base, static_cast<size_t>(op - ops_base), Tag(1), nullptr);
-  EXPECT_EQ(GRPC_CALL_OK, error);
-  CQ_EXPECT_COMPLETION(cqv, Tag(1), 1);
-  cq_verify(cqv);
-  EXPECT_EQ(status, GRPC_STATUS_DEADLINE_EXCEEDED);
-  // Teardown
-  grpc_slice_unref(details);
-  gpr_free((void*)error_string);
-  grpc_metadata_array_destroy(&initial_metadata_recv);
-  grpc_metadata_array_destroy(&trailing_metadata_recv);
-  grpc_metadata_array_destroy(&request_metadata_recv);
-  grpc_call_details_destroy(&call_details);
-  grpc_call_unref(call);
-  cq_verifier_destroy(cqv);
-  EndTest(client, cq);
-}
-
-}  // namespace
-
-int main(int argc, char** argv) {
-  grpc_test_init(argc, argv);
-  ::testing::InitGoogleTest(&argc, argv);
-  gpr_setenv("GRPC_DNS_RESOLVER", "ares");
-  // Sanity check the time that it takes to run the test
-  // including the teardown time (the teardown
-  // part of the test involves cancelling the DNS query,
-  // which is the main point of interest for this test).
-  gpr_timespec overall_deadline = grpc_timeout_seconds_to_deadline(4);
-  grpc_init();
-  auto result = RUN_ALL_TESTS();
-  grpc_shutdown();
-  if (gpr_time_cmp(gpr_now(GPR_CLOCK_MONOTONIC), overall_deadline) > 0) {
-    gpr_log(GPR_ERROR, "Test took too long");
-    abort();
-  }
-  return result;
-}

+ 0 - 20
test/cpp/naming/gen_build_yaml.py

@@ -22,7 +22,6 @@ import collections
 import hashlib
 import json
 
-_LOCAL_DNS_SERVER_ADDRESS = '127.0.0.1:15353'
 
 def _append_zone_name(name, zone_name):
   return '%s.%s' % (name, zone_name)
@@ -121,25 +120,6 @@ def main():
                   'grpc++_test_config',
               ],
           } for unsecure_build_config_suffix in ['_unsecure', '']
-      ] + [
-          {
-          'name': 'cancel_ares_query_test',
-          'build': 'test',
-          'language': 'c++',
-          'gtest': True,
-          'run': True,
-          'src': ['test/cpp/naming/cancel_ares_query_test.cc'],
-          'platforms': ['linux', 'posix', 'mac'],
-          'deps': [
-              'grpc++_test_util',
-              'grpc_test_util',
-              'gpr_test_util',
-              'grpc++',
-              'grpc',
-              'gpr',
-              'grpc++_test_config',
-          ],
-          },
       ]
   }
 

+ 3 - 107
test/cpp/naming/resolver_component_test.cc

@@ -22,14 +22,10 @@
 #include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
 #include <grpc/support/time.h>
-
 #include <string.h>
 
-#include <errno.h>
-#include <fcntl.h>
 #include <gflags/gflags.h>
 #include <gmock/gmock.h>
-#include <thread>
 #include <vector>
 
 #include "test/cpp/util/subprocess.h"
@@ -52,12 +48,6 @@
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
 
-// TODO: pull in different headers when enabling this
-// test on windows. Also set BAD_SOCKET_RETURN_VAL
-// to INVALID_SOCKET on windows.
-#include "src/core/lib/iomgr/sockaddr_posix.h"
-#define BAD_SOCKET_RETURN_VAL -1
-
 using grpc::SubProcess;
 using std::vector;
 using testing::UnorderedElementsAreArray;
@@ -241,73 +231,7 @@ void CheckLBPolicyResultLocked(grpc_channel_args* channel_args,
   }
 }
 
-void OpenAndCloseSocketsStressLoop(int dummy_port, gpr_event* done_ev) {
-  // The goal of this loop is to catch socket
-  // "use after close" bugs within the c-ares resolver by acting
-  // like some separate thread doing I/O.
-  // It's goal is to try to hit race conditions whereby:
-  //    1) The c-ares resolver closes a socket.
-  //    2) This loop opens a socket with (coincidentally) the same handle.
-  //    3) the c-ares resolver mistakenly uses that same socket without
-  //       realizing that its closed.
-  //    4) This loop performs an operation on that socket that should
-  //       succeed but instead fails because of what the c-ares
-  //       resolver did in the meantime.
-  sockaddr_in6 addr;
-  memset(&addr, 0, sizeof(addr));
-  addr.sin6_family = AF_INET6;
-  addr.sin6_port = htons(dummy_port);
-  ((char*)&addr.sin6_addr)[15] = 1;
-  for (;;) {
-    if (gpr_event_get(done_ev)) {
-      return;
-    }
-    std::vector<int> sockets;
-    // First open a bunch of sockets, bind and listen
-    // '50' is an arbitrary number that, experimentally,
-    // has a good chance of catching bugs.
-    for (size_t i = 0; i < 50; i++) {
-      int s = socket(AF_INET6, SOCK_STREAM, 0);
-      int val = 1;
-      setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
-      fcntl(s, F_SETFL, O_NONBLOCK);
-      ASSERT_TRUE(s != BAD_SOCKET_RETURN_VAL)
-          << "Failed to create TCP ipv6 socket";
-      gpr_log(GPR_DEBUG, "Opened fd: %d", s);
-      ASSERT_TRUE(bind(s, (const sockaddr*)&addr, sizeof(addr)) == 0)
-          << "Failed to bind socket " + std::to_string(s) +
-                 " to [::1]:" + std::to_string(dummy_port) +
-                 ". errno: " + std::to_string(errno);
-      ASSERT_TRUE(listen(s, 1) == 0) << "Failed to listen on socket " +
-                                            std::to_string(s) +
-                                            ". errno: " + std::to_string(errno);
-      sockets.push_back(s);
-    }
-    // Do a non-blocking accept followed by a close on all of those sockets.
-    // Do this in a separate loop to try to induce a time window to hit races.
-    for (size_t i = 0; i < sockets.size(); i++) {
-      gpr_log(GPR_DEBUG, "non-blocking accept then close on %d", sockets[i]);
-      if (accept(sockets[i], nullptr, nullptr)) {
-        // If e.g. a "shutdown" was called on this fd from another thread,
-        // then this accept call should fail with an unexpected error.
-        ASSERT_TRUE(errno == EAGAIN || errno == EWOULDBLOCK)
-            << "OpenAndCloseSocketsStressLoop accept on socket " +
-                   std::to_string(sockets[i]) +
-                   " failed in "
-                   "an unexpected way. "
-                   "errno: " +
-                   std::to_string(errno) +
-                   ". Socket use-after-close bugs are likely.";
-      }
-      ASSERT_TRUE(close(sockets[i]) == 0)
-          << "Failed to close socket: " + std::to_string(sockets[i]) +
-                 ". errno: " + std::to_string(errno);
-    }
-  }
-}
-
 void CheckResolverResultLocked(void* argsp, grpc_error* err) {
-  EXPECT_EQ(err, GRPC_ERROR_NONE);
   ArgsStruct* args = (ArgsStruct*)argsp;
   grpc_channel_args* channel_args = args->channel_args;
   const grpc_arg* channel_arg =
@@ -347,17 +271,7 @@ void CheckResolverResultLocked(void* argsp, grpc_error* err) {
   gpr_mu_unlock(args->mu);
 }
 
-void CheckResolvedWithoutErrorLocked(void* argsp, grpc_error* err) {
-  EXPECT_EQ(err, GRPC_ERROR_NONE);
-  ArgsStruct* args = (ArgsStruct*)argsp;
-  gpr_atm_rel_store(&args->done_atm, 1);
-  gpr_mu_lock(args->mu);
-  GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(args->pollset, nullptr));
-  gpr_mu_unlock(args->mu);
-}
-
-void RunResolvesRelevantRecordsTest(void (*OnDoneLocked)(void* arg,
-                                                         grpc_error* error)) {
+TEST(ResolverComponentTest, TestResolvesRelevantRecords) {
   grpc_core::ExecCtx exec_ctx;
   ArgsStruct args;
   ArgsInit(&args);
@@ -375,32 +289,14 @@ void RunResolvesRelevantRecordsTest(void (*OnDoneLocked)(void* arg,
                                                   args.pollset_set, args.lock);
   gpr_free(whole_uri);
   grpc_closure on_resolver_result_changed;
-  GRPC_CLOSURE_INIT(&on_resolver_result_changed, OnDoneLocked, (void*)&args,
-                    grpc_combiner_scheduler(args.lock));
+  GRPC_CLOSURE_INIT(&on_resolver_result_changed, CheckResolverResultLocked,
+                    (void*)&args, grpc_combiner_scheduler(args.lock));
   resolver->NextLocked(&args.channel_args, &on_resolver_result_changed);
   grpc_core::ExecCtx::Get()->Flush();
   PollPollsetUntilRequestDone(&args);
   ArgsFinish(&args);
 }
 
-TEST(ResolverComponentTest, TestResolvesRelevantRecords) {
-  RunResolvesRelevantRecordsTest(CheckResolverResultLocked);
-}
-
-TEST(ResolverComponentTest, TestResolvesRelevantRecordsWithConcurrentFdStress) {
-  // Start up background stress thread
-  int dummy_port = grpc_pick_unused_port_or_die();
-  gpr_event done_ev;
-  gpr_event_init(&done_ev);
-  std::thread socket_stress_thread(OpenAndCloseSocketsStressLoop, dummy_port,
-                                   &done_ev);
-  // Run the resolver test
-  RunResolvesRelevantRecordsTest(CheckResolvedWithoutErrorLocked);
-  // Shutdown and join stress thread
-  gpr_event_set(&done_ev, (void*)1);
-  socket_stress_thread.join();
-}
-
 }  // namespace
 
 int main(int argc, char** argv) {

+ 15 - 15
test/cpp/naming/resolver_component_tests_runner.py

@@ -58,7 +58,7 @@ def wait_until_dns_server_is_up(args,
     test_runner_log('Health check: attempt to connect to DNS server over TCP.')
     tcp_connect_subprocess = subprocess.Popen([
         args.tcp_connect_bin_path,
-        '--server_host', '127.0.0.1',
+        '--server_host', '::1',
         '--server_port', str(args.dns_server_port),
         '--timeout', str(1)])
     tcp_connect_subprocess.communicate()
@@ -68,7 +68,7 @@ def wait_until_dns_server_is_up(args,
       dns_resolver_subprocess = subprocess.Popen([
           args.dns_resolver_bin_path,
           '--qname', 'health-check-local-dns-server-is-alive.resolver-tests.grpctestingexp',
-          '--server_host', '127.0.0.1',
+          '--server_host', '::1',
           '--server_port', str(args.dns_server_port)],
           stdout=subprocess.PIPE)
       dns_resolver_stdout, _ = dns_resolver_subprocess.communicate()
@@ -119,7 +119,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '1.2.3.4:1234,True',
   '--expected_chosen_service_config', '',
   '--expected_lb_policy', '',
-  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+  '--local_dns_server_address', '[::1]:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
@@ -131,7 +131,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '1.2.3.5:1234,True;1.2.3.6:1234,True;1.2.3.7:1234,True',
   '--expected_chosen_service_config', '',
   '--expected_lb_policy', '',
-  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+  '--local_dns_server_address', '[::1]:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
@@ -143,7 +143,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '[2607:f8b0:400a:801::1001]:1234,True',
   '--expected_chosen_service_config', '',
   '--expected_lb_policy', '',
-  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+  '--local_dns_server_address', '[::1]:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
@@ -155,7 +155,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '[2607:f8b0:400a:801::1002]:1234,True;[2607:f8b0:400a:801::1003]:1234,True;[2607:f8b0:400a:801::1004]:1234,True',
   '--expected_chosen_service_config', '',
   '--expected_lb_policy', '',
-  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+  '--local_dns_server_address', '[::1]:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
@@ -167,7 +167,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '1.2.3.4:1234,True',
   '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]}]}',
   '--expected_lb_policy', 'round_robin',
-  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+  '--local_dns_server_address', '[::1]:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
@@ -179,7 +179,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"NoSrvSimpleService","waitForReady":true}]}]}',
   '--expected_lb_policy', 'round_robin',
-  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+  '--local_dns_server_address', '[::1]:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
@@ -191,7 +191,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_chosen_service_config', '',
   '--expected_lb_policy', '',
-  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+  '--local_dns_server_address', '[::1]:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
@@ -203,7 +203,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_chosen_service_config', '',
   '--expected_lb_policy', '',
-  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+  '--local_dns_server_address', '[::1]:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
@@ -215,7 +215,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService","waitForReady":true}]}]}',
   '--expected_lb_policy', 'round_robin',
-  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+  '--local_dns_server_address', '[::1]:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
@@ -227,7 +227,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"AlwaysPickedService","waitForReady":true}]}]}',
   '--expected_lb_policy', 'round_robin',
-  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+  '--local_dns_server_address', '[::1]:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
@@ -239,7 +239,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '1.2.3.4:1234,True;1.2.3.4:443,False',
   '--expected_chosen_service_config', '',
   '--expected_lb_policy', '',
-  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+  '--local_dns_server_address', '[::1]:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
@@ -251,7 +251,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '[2607:f8b0:400a:801::1002]:1234,True;[2607:f8b0:400a:801::1002]:443,False',
   '--expected_chosen_service_config', '',
   '--expected_lb_policy', '',
-  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+  '--local_dns_server_address', '[::1]:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
@@ -263,7 +263,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooThree","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFour","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFive","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSix","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSeven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEight","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooNine","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTen","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEleven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]}]}',
   '--expected_lb_policy', '',
-  '--local_dns_server_address', '127.0.0.1:%d' % args.dns_server_port])
+  '--local_dns_server_address', '[::1]:%d' % args.dns_server_port])
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1

+ 1 - 1
test/cpp/naming/utils/dns_resolver.py

@@ -25,7 +25,7 @@ import twisted.internet.reactor as reactor
 
 def main():
   argp = argparse.ArgumentParser(description='Make DNS queries for A records')
-  argp.add_argument('-s', '--server_host', default='127.0.0.1', type=str,
+  argp.add_argument('-s', '--server_host', default='::1', type=str,
                     help='Host for DNS server to listen on for TCP and UDP.')
   argp.add_argument('-p', '--server_port', default=53, type=int,
                     help='Port that the DNS server is listening on.')

+ 3 - 3
test/cpp/naming/utils/dns_server.py

@@ -103,11 +103,11 @@ def start_local_dns_server(args):
   server = twisted.names.server.DNSServerFactory(
       authorities=[test_domain_com], verbose=2)
   server.noisy = 2
-  twisted.internet.reactor.listenTCP(args.port, server)
+  twisted.internet.reactor.listenTCP(args.port, server, interface='::1')
   dns_proto = twisted.names.dns.DNSDatagramProtocol(server)
   dns_proto.noisy = 2
-  twisted.internet.reactor.listenUDP(args.port, dns_proto)
-  print('starting local dns server on 127.0.0.1:%s' % args.port)
+  twisted.internet.reactor.listenUDP(args.port, dns_proto, interface='::1')
+  print('starting local dns server on [::1]:%s' % args.port)
   print('starting twisted.internet.reactor')
   twisted.internet.reactor.suggestThreadPoolSize(1)
   twisted.internet.reactor.run()

+ 22 - 6
test/cpp/util/grpc_tool.cc

@@ -471,17 +471,26 @@ bool GrpcTool::CallMethod(int argc, const char** argv,
   std::shared_ptr<grpc::Channel> channel =
       grpc::CreateChannel(server_address, cred.GetCredentials());
 
-  parser.reset(new grpc::testing::ProtoFileParser(
-      FLAGS_remotedb ? channel : nullptr, FLAGS_proto_path, FLAGS_protofiles));
+  if (!FLAGS_binary_input || !FLAGS_binary_output) {
+    parser.reset(
+        new grpc::testing::ProtoFileParser(FLAGS_remotedb ? channel : nullptr,
+                                           FLAGS_proto_path, FLAGS_protofiles));
+    if (parser->HasError()) {
+      fprintf(
+          stderr,
+          "Failed to find remote reflection service and local proto files.\n");
+      return false;
+    }
+  }
 
   if (FLAGS_binary_input) {
     formatted_method_name = method_name;
   } else {
     formatted_method_name = parser->GetFormattedMethodName(method_name);
-  }
-
-  if (parser->HasError()) {
-    return false;
+    if (parser->HasError()) {
+      fprintf(stderr, "Failed to find method %s in proto files.\n",
+              method_name.c_str());
+    }
   }
 
   if (argc == 3) {
@@ -711,6 +720,7 @@ bool GrpcTool::CallMethod(int argc, const char** argv,
       serialized_request_proto = parser->GetSerializedProtoFromMethod(
           method_name, request_text, true /* is_request */);
       if (parser->HasError()) {
+        fprintf(stderr, "Failed to parse request.\n");
         return false;
       }
     }
@@ -735,6 +745,7 @@ bool GrpcTool::CallMethod(int argc, const char** argv,
         serialized_response_proto = parser->GetTextFormatFromMethod(
             method_name, serialized_response_proto, false /* is_request */);
         if (parser->HasError()) {
+          fprintf(stderr, "Failed to parse response.\n");
           return false;
         }
       }
@@ -814,6 +825,9 @@ bool GrpcTool::ParseMessage(int argc, const char** argv,
         new grpc::testing::ProtoFileParser(FLAGS_remotedb ? channel : nullptr,
                                            FLAGS_proto_path, FLAGS_protofiles));
     if (parser->HasError()) {
+      fprintf(
+          stderr,
+          "Failed to find remote reflection service and local proto files.\n");
       return false;
     }
   }
@@ -824,6 +838,7 @@ bool GrpcTool::ParseMessage(int argc, const char** argv,
     serialized_request_proto =
         parser->GetSerializedProtoFromMessageType(type_name, message_text);
     if (parser->HasError()) {
+      fprintf(stderr, "Failed to serialize the message.\n");
       return false;
     }
   }
@@ -834,6 +849,7 @@ bool GrpcTool::ParseMessage(int argc, const char** argv,
     grpc::string output_text = parser->GetTextFormatFromMessageType(
         type_name, serialized_request_proto);
     if (parser->HasError()) {
+      fprintf(stderr, "Failed to deserialize the message.\n");
       return false;
     }
     output_ss << output_text << std::endl;

+ 127 - 0
tools/profiling/ios_bin/binary_diff.py

@@ -0,0 +1,127 @@
+#!/usr/bin/env python2.7
+#
+# Copyright 2018 gRPC authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import glob
+import multiprocessing
+import os
+import shutil
+import subprocess
+import sys
+from parse_link_map import parse_link_map
+
+sys.path.append(
+    os.path.join(
+        os.path.dirname(sys.argv[0]), '..', '..', 'run_tests', 'python_utils'))
+import comment_on_pr
+
+size_labels = ('Core', 'ObjC', 'BoringSSL', 'Protobuf', 'Total')
+
+argp = argparse.ArgumentParser(
+    description='Binary size diff of gRPC Objective-C sample')
+
+argp.add_argument(
+    '-d',
+    '--diff_base',
+    type=str,
+    help='Commit or branch to compare the current one to')
+
+args = argp.parse_args()
+
+
+def dir_size(dir):
+    total = 0
+    for dirpath, dirnames, filenames in os.walk(dir):
+        for f in filenames:
+            fp = os.path.join(dirpath, f)
+            total += os.stat(fp).st_size
+    return total
+
+
+def get_size(where, frameworks):
+    build_dir = 'src/objective-c/examples/Sample/Build-%s/' % where
+    if not frameworks:
+        link_map_filename = 'Build/Intermediates.noindex/Sample.build/Release-iphoneos/Sample.build/Sample-LinkMap-normal-arm64.txt'
+        return parse_link_map(build_dir + link_map_filename)
+    else:
+        framework_dir = 'Build/Products/Release-iphoneos/Sample.app/Frameworks/'
+        boringssl_size = dir_size(
+            build_dir + framework_dir + 'openssl.framework')
+        core_size = dir_size(build_dir + framework_dir + 'grpc.framework')
+        objc_size = dir_size(build_dir + framework_dir + 'GRPCClient.framework') + \
+                    dir_size(build_dir + framework_dir + 'RxLibrary.framework') + \
+                    dir_size(build_dir + framework_dir + 'ProtoRPC.framework')
+        protobuf_size = dir_size(
+            build_dir + framework_dir + 'Protobuf.framework')
+        app_size = dir_size(
+            build_dir + 'Build/Products/Release-iphoneos/Sample.app')
+        return core_size, objc_size, boringssl_size, protobuf_size, app_size
+
+
+def build(where, frameworks):
+    shutil.rmtree(
+        'src/objective-c/examples/Sample/Build-%s' % where, ignore_errors=True)
+    subprocess.check_call(
+        'CONFIG=opt EXAMPLE_PATH=src/objective-c/examples/Sample SCHEME=Sample FRAMEWORKS=%s ./build_one_example.sh'
+        % ('YES' if frameworks else 'NO'),
+        shell=True,
+        cwd='src/objective-c/tests')
+    os.rename('src/objective-c/examples/Sample/Build',
+              'src/objective-c/examples/Sample/Build-%s' % where)
+
+
+text = ''
+for frameworks in [False, True]:
+    build('new', frameworks)
+    new_size = get_size('new', frameworks)
+    old_size = None
+
+    if args.diff_base:
+        old = 'old'
+        where_am_i = subprocess.check_output(
+            ['git', 'rev-parse', '--abbrev-ref', 'HEAD']).strip()
+        subprocess.check_call(['git', 'checkout', '--', '.'])
+        subprocess.check_call(['git', 'checkout', args.diff_base])
+        subprocess.check_call(['git', 'submodule', 'update'])
+        try:
+            build('old', frameworks)
+            old_size = get_size('old', frameworks)
+        finally:
+            subprocess.check_call(['git', 'checkout', '--', '.'])
+            subprocess.check_call(['git', 'checkout', where_am_i])
+            subprocess.check_call(['git', 'submodule', 'update'])
+
+    text += ('****************FRAMEWORKS*****************\n'
+             if frameworks else '******************STATIC*******************\n')
+    row_format = "{:>10}{:>15}{:>15}" + '\n'
+    text += row_format.format('New size', '', 'Old size')
+    for i in range(0, len(size_labels)):
+        if old_size == None:
+            diff_sign = ' '
+        elif new_size[i] == old_size[i]:
+            diff_sign = ' (=)'
+        elif new_size[i] > old_size[i]:
+            diff_sign = ' (>)'
+        else:
+            diff_sign = ' (<)'
+        text += ('\n' if i == len(size_labels) - 1 else '') + row_format.format(
+            '{:,}'.format(new_size[i]), size_labels[i] + diff_sign,
+            '{:,}'.format(old_size[i]) if old_size != None else '')
+    text += '\n'
+
+print text
+
+comment_on_pr.comment_on_pr('```\n%s\n```' % text)

+ 104 - 0
tools/profiling/ios_bin/parse_link_map.py

@@ -0,0 +1,104 @@
+#!/usr/bin/python
+# Copyright 2018 gRPC authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script analyzes link map file generated by Xcode. It calculates and
+# prints out the sizes of each dependent library and the total sizes of the
+# symbols.
+# The script takes one parameter, which is the path to the link map file.
+
+import sys
+import re
+
+
+def parse_link_map(filename):
+    table_tag = {}
+    state = "start"
+
+    table_stats_symbol = {}
+    table_stats_dead = {}
+    section_total_size = 0
+    symbol_total_size = 0
+
+    boringssl_size = 0
+    core_size = 0
+    objc_size = 0
+    protobuf_size = 0
+
+    lines = list(open(filename))
+    for line in lines:
+        line_stripped = line[:-1]
+        if "# Object files:" == line_stripped:
+            state = "object"
+            continue
+        elif "# Sections:" == line_stripped:
+            state = "section"
+            continue
+        elif "# Symbols:" == line_stripped:
+            state = "symbol"
+            continue
+        elif "# Dead Stripped Symbols:" == line_stripped:
+            state = "dead"
+            continue
+
+        if state == "object":
+            segs = re.search('(\[ *[0-9]*\]) (.*)', line_stripped)
+            table_tag[segs.group(1)] = segs.group(2)
+
+        if state == "section":
+            if len(line_stripped) == 0 or line_stripped[0] == '#':
+                continue
+            segs = re.search('^(.+?)\s+(.+?)\s+.*', line_stripped)
+            section_total_size += int(segs.group(2), 16)
+
+        if state == "symbol":
+            if len(line_stripped) == 0 or line_stripped[0] == '#':
+                continue
+            segs = re.search('^.+?\s+(.+?)\s+(\[.+?\]).*', line_stripped)
+            target = table_tag[segs.group(2)]
+            target_stripped = re.search('^(.*?)(\(.+?\))?$', target).group(1)
+            size = int(segs.group(1), 16)
+            if not target_stripped in table_stats_symbol:
+                table_stats_symbol[target_stripped] = 0
+            table_stats_symbol[target_stripped] += size
+            if 'BoringSSL' in target_stripped:
+                boringssl_size += size
+            elif 'libgRPC-Core' in target_stripped:
+                core_size += size
+            elif 'libgRPC-RxLibrary' in target_stripped or \
+                 'libgRPC' in target_stripped or \
+                 'libgRPC-ProtoLibrary' in target_stripped:
+                objc_size += size
+            elif 'libProtobuf' in target_stripped:
+                protobuf_size += size
+
+    for target in table_stats_symbol:
+        symbol_total_size += table_stats_symbol[target]
+
+    return core_size, objc_size, boringssl_size, protobuf_size, symbol_total_size
+
+
+def main():
+    filename = sys.argv[1]
+    core_size, objc_size, boringssl_size, protobuf_size, total_size = parse_link_map(
+        filename)
+    print('Core size:{:,}'.format(core_size))
+    print('ObjC size:{:,}'.format(objc_size))
+    print('BoringSSL size:{:,}'.format(boringssl_size))
+    print('Protobuf size:{:,}\n'.format(protobuf_size))
+    print('Total size:{:,}'.format(total_size))
+
+
+if __name__ == "__main__":
+    main()

+ 3 - 2
tools/run_tests/artifacts/build_artifact_csharp.bat

@@ -19,11 +19,12 @@ set GRPC_SKIP_DOTNET_RESTORE=true
 @call tools\run_tests\helper_scripts\pre_build_csharp.bat %ARCHITECTURE% || goto :error
 
 cd cmake\build\%ARCHITECTURE%
-cmake --build . --target grpc_csharp_ext --config Release
+cmake --build . --target grpc_csharp_ext --config RelWithDebInfo
 cd ..\..\..
 
 mkdir -p %ARTIFACTS_OUT%
-copy /Y cmake\build\Win32\Release\grpc_csharp_ext.dll %ARTIFACTS_OUT% || copy /Y cmake\build\x64\Release\grpc_csharp_ext.dll %ARTIFACTS_OUT% || goto :error
+copy /Y cmake\build\Win32\RelWithDebInfo\grpc_csharp_ext.dll %ARTIFACTS_OUT% || copy /Y cmake\build\x64\RelWithDebInfo\grpc_csharp_ext.dll %ARTIFACTS_OUT% || goto :error
+copy /Y cmake\build\Win32\RelWithDebInfo\grpc_csharp_ext.pdb %ARTIFACTS_OUT% || copy /Y cmake\build\x64\RelWithDebInfo\grpc_csharp_ext.pdb %ARTIFACTS_OUT% || goto :error
 
 goto :EOF
 

+ 0 - 20
tools/run_tests/generated/sources_and_headers.json

@@ -6541,26 +6541,6 @@
     "third_party": false, 
     "type": "target"
   }, 
-  {
-    "deps": [
-      "gpr", 
-      "gpr_test_util", 
-      "grpc", 
-      "grpc++", 
-      "grpc++_test_config", 
-      "grpc++_test_util", 
-      "grpc_test_util"
-    ], 
-    "headers": [], 
-    "is_filegroup": false, 
-    "language": "c++", 
-    "name": "cancel_ares_query_test", 
-    "src": [
-      "test/cpp/naming/cancel_ares_query_test.cc"
-    ], 
-    "third_party": false, 
-    "type": "target"
-  }, 
   {
     "deps": [
       "gpr", 

+ 0 - 22
tools/run_tests/generated/tests.json

@@ -5638,28 +5638,6 @@
     ], 
     "uses_polling": true
   }, 
-  {
-    "args": [], 
-    "benchmark": false, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "gtest": true, 
-    "language": "c++", 
-    "name": "cancel_ares_query_test", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "uses_polling": true
-  }, 
   {
     "args": [], 
     "boringssl": true, 

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác