Browse Source

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

kpayson64 7 years ago
parent
commit
378daa874e
67 changed files with 1309 additions and 1319 deletions
  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)
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_cxx address_sorting_test)
 add_dependencies(buildtests_cxx address_sorting_test)
 endif()
 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
 add_custom_target(buildtests
   DEPENDS buildtests_c buildtests_cxx)
   DEPENDS buildtests_c buildtests_cxx)
@@ -15843,48 +15840,6 @@ target_link_libraries(address_sorting_test
   ${_gRPC_GFLAGS_LIBRARIES}
   ${_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()
 endif (gRPC_BUILD_TESTS)
 endif (gRPC_BUILD_TESTS)
 if (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
 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_unsecure: $(BINDIR)/$(CONFIG)/address_sorting_test_unsecure
 address_sorting_test: $(BINDIR)/$(CONFIG)/address_sorting_test
 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
 alts_credentials_fuzzer_one_entry: $(BINDIR)/$(CONFIG)/alts_credentials_fuzzer_one_entry
 api_fuzzer_one_entry: $(BINDIR)/$(CONFIG)/api_fuzzer_one_entry
 api_fuzzer_one_entry: $(BINDIR)/$(CONFIG)/api_fuzzer_one_entry
 client_fuzzer_one_entry: $(BINDIR)/$(CONFIG)/client_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)/resolver_component_tests_runner_invoker \
   $(BINDIR)/$(CONFIG)/address_sorting_test_unsecure \
   $(BINDIR)/$(CONFIG)/address_sorting_test_unsecure \
   $(BINDIR)/$(CONFIG)/address_sorting_test \
   $(BINDIR)/$(CONFIG)/address_sorting_test \
-  $(BINDIR)/$(CONFIG)/cancel_ares_query_test \
 
 
 else
 else
 buildtests_cxx: privatelibs_cxx \
 buildtests_cxx: privatelibs_cxx \
@@ -1879,7 +1877,6 @@ buildtests_cxx: privatelibs_cxx \
   $(BINDIR)/$(CONFIG)/resolver_component_tests_runner_invoker \
   $(BINDIR)/$(CONFIG)/resolver_component_tests_runner_invoker \
   $(BINDIR)/$(CONFIG)/address_sorting_test_unsecure \
   $(BINDIR)/$(CONFIG)/address_sorting_test_unsecure \
   $(BINDIR)/$(CONFIG)/address_sorting_test \
   $(BINDIR)/$(CONFIG)/address_sorting_test \
-  $(BINDIR)/$(CONFIG)/cancel_ares_query_test \
 
 
 endif
 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 )
 	$(Q) $(BINDIR)/$(CONFIG)/address_sorting_test_unsecure || ( echo test address_sorting_test_unsecure failed ; exit 1 )
 	$(E) "[RUN]     Testing address_sorting_test"
 	$(E) "[RUN]     Testing address_sorting_test"
 	$(Q) $(BINDIR)/$(CONFIG)/address_sorting_test || ( echo test address_sorting_test failed ; exit 1 )
 	$(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
 flaky_test_cxx: buildtests_cxx
@@ -23692,49 +23687,6 @@ endif
 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 = \
 ALTS_CREDENTIALS_FUZZER_ONE_ENTRY_SRC = \
     test/core/security/alts_credentials_fuzzer.cc \
     test/core/security/alts_credentials_fuzzer.cc \
     test/core/util/one_corpus_entry_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'
   src_dir = 'src/objective-c/ProtoRPC'
 
 
+  s.default_subspec = 'Main'
 
 
   s.subspec 'Main' do |ss|
   s.subspec 'Main' do |ss|
     ss.header_mappings_dir = "#{src_dir}"
     ss.header_mappings_dir = "#{src_dir}"

File diff suppressed because it is too large
+ 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)
 #if GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER)
 
 
 #include <ares.h>
 #include <ares.h>
-#include <string.h>
 #include <sys/ioctl.h>
 #include <sys/ioctl.h>
 
 
 #include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.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;
   bool readable_registered;
   /** if the writable closure has been registered */
   /** if the writable closure has been registered */
   bool writable_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;
 } fd_node;
 
 
 struct grpc_ares_ev_driver {
 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_log(GPR_DEBUG, "delete fd: %d", grpc_fd_wrapped_fd(fdn->fd));
   GPR_ASSERT(!fdn->readable_registered);
   GPR_ASSERT(!fdn->readable_registered);
   GPR_ASSERT(!fdn->writable_registered);
   GPR_ASSERT(!fdn->writable_registered);
-  GPR_ASSERT(fdn->already_shutdown);
   gpr_mu_destroy(&fdn->mu);
   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
   /* 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
      immediately by another thread, and should not be closed by the following
      grpc_fd_orphan. */
      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");
                  "c-ares query finished");
   gpr_free(fdn);
   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) {
                                        grpc_pollset_set* pollset_set) {
   *ev_driver = static_cast<grpc_ares_ev_driver*>(
   *ev_driver = static_cast<grpc_ares_ev_driver*>(
       gpr_malloc(sizeof(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");
   gpr_log(GPR_DEBUG, "grpc_ares_ev_driver_create");
   if (status != ARES_SUCCESS) {
   if (status != ARES_SUCCESS) {
     char* err_msg;
     char* err_msg;
@@ -169,9 +164,8 @@ void grpc_ares_ev_driver_shutdown(grpc_ares_ev_driver* ev_driver) {
   ev_driver->shutting_down = true;
   ev_driver->shutting_down = true;
   fd_node* fn = ev_driver->fds;
   fd_node* fn = ev_driver->fds;
   while (fn != nullptr) {
   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;
     fn = fn->next;
   }
   }
   gpr_mu_unlock(&ev_driver->mu);
   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);
   gpr_mu_lock(&fdn->mu);
   const int fd = grpc_fd_wrapped_fd(fdn->fd);
   const int fd = grpc_fd_wrapped_fd(fdn->fd);
   fdn->readable_registered = false;
   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_mu_unlock(&fdn->mu);
+
   gpr_log(GPR_DEBUG, "readable on %d", fd);
   gpr_log(GPR_DEBUG, "readable on %d", fd);
   if (error == GRPC_ERROR_NONE) {
   if (error == GRPC_ERROR_NONE) {
     do {
     do {
@@ -235,7 +236,14 @@ static void on_writable_cb(void* arg, grpc_error* error) {
   gpr_mu_lock(&fdn->mu);
   gpr_mu_lock(&fdn->mu);
   const int fd = grpc_fd_wrapped_fd(fdn->fd);
   const int fd = grpc_fd_wrapped_fd(fdn->fd);
   fdn->writable_registered = false;
   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_mu_unlock(&fdn->mu);
+
   gpr_log(GPR_DEBUG, "writable on %d", fd);
   gpr_log(GPR_DEBUG, "writable on %d", fd);
   if (error == GRPC_ERROR_NONE) {
   if (error == GRPC_ERROR_NONE) {
     ares_process_fd(ev_driver->channel, ARES_SOCKET_BAD, fd);
     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->ev_driver = ev_driver;
           fdn->readable_registered = false;
           fdn->readable_registered = false;
           fdn->writable_registered = false;
           fdn->writable_registered = false;
-          fdn->already_shutdown = false;
+          fdn->shutting_down = false;
           gpr_mu_init(&fdn->mu);
           gpr_mu_init(&fdn->mu);
           GRPC_CLOSURE_INIT(&fdn->read_closure, on_readable_cb, fdn,
           GRPC_CLOSURE_INIT(&fdn->read_closure, on_readable_cb, fdn,
                             grpc_schedule_on_exec_ctx);
                             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) {
   while (ev_driver->fds != nullptr) {
     fd_node* cur = ev_driver->fds;
     fd_node* cur = ev_driver->fds;
     ev_driver->fds = ev_driver->fds->next;
     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;
   ev_driver->fds = new_list;
   // If the ev driver has no working fd, all the tasks are done.
   // 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);
   grpc_deadline_state* deadline_state = static_cast<grpc_deadline_state*>(arg);
   cancel_timer_if_needed(deadline_state);
   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);
                     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
 // 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
     // Make sure we know when the call is complete, so that we can cancel
     // the timer.
     // the timer.
     if (op->recv_trailing_metadata) {
     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
     // the client never sends trailing metadata, because this is the
     // hook that tells us when the call is complete on the server side.
     // hook that tells us when the call is complete on the server side.
     if (op->recv_trailing_metadata) {
     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.
   // 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_deadline_timer_state timer_state;
   grpc_timer timer;
   grpc_timer timer;
   grpc_closure timer_callback;
   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.
   // 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;
 } 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;
   grpc_closure recv_initial_metadata_ready;
   // State for handling recv_trailing_metadata ops.
   // State for handling recv_trailing_metadata ops.
   grpc_metadata_batch* recv_trailing_metadata;
   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.
   // State for handling send_message ops.
   grpc_transport_stream_op_batch* send_message_batch;
   grpc_transport_stream_op_batch* send_message_batch;
   size_t send_message_bytes_read;
   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);
   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);
   grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
   call_data* calld = static_cast<call_data*>(elem->call_data);
   call_data* calld = static_cast<call_data*>(elem->call_data);
   if (error == GRPC_ERROR_NONE) {
   if (error == GRPC_ERROR_NONE) {
@@ -163,7 +162,7 @@ static void recv_trailing_metadata_on_complete(void* user_data,
   } else {
   } else {
     GRPC_ERROR_REF(error);
     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) {
 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 */
     /* substitute our callback for the higher callback */
     calld->recv_trailing_metadata =
     calld->recv_trailing_metadata =
         batch->payload->recv_trailing_metadata.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;
   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,
   GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready,
                     recv_initial_metadata_ready, elem,
                     recv_initial_metadata_ready, elem,
                     grpc_schedule_on_exec_ctx);
                     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_schedule_on_exec_ctx);
   GRPC_CLOSURE_INIT(&calld->send_message_on_complete, send_message_on_complete,
   GRPC_CLOSURE_INIT(&calld->send_message_on_complete, send_message_on_complete,
                     elem, grpc_schedule_on_exec_ctx);
                     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.
   // Inject callback for receiving trailing metadata.
   if (op->recv_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.
   // Chain to the next filter.
   grpc_call_next_op(elem, op);
   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
 /* 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
    we should not complete this closure until we can prove that the write got
    scheduled */
    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
 /* First bit of the reference count, stored in the high order bits (with the low
    bits being used for flags defined above) */
    bits being used for flags defined above) */
 #define CLOSURE_BARRIER_FIRST_REF_BIT (1 << 16)
 #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);
         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_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) ||
     if ((t->write_state == GRPC_CHTTP2_WRITE_STATE_IDLE) ||
         !(closure->next_data.scratch & CLOSURE_BARRIER_MAY_COVER_WRITE)) {
         !(closure->next_data.scratch & CLOSURE_BARRIER_MAY_COVER_WRITE)) {
       GRPC_CLOSURE_RUN(closure, closure->error_data.error);
       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;
   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) {
   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
   /* 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->next_data.scratch = CLOSURE_BARRIER_FIRST_REF_BIT;
   on_complete->error_data.error = GRPC_ERROR_NONE;
   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) {
   if (op->cancel_stream) {
     GRPC_STATS_INC_HTTP2_OP_CANCEL();
     GRPC_STATS_INC_HTTP2_OP_CANCEL();
     grpc_chttp2_cancel_stream(t, s, op_payload->cancel_stream.cancel_error);
     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) {
   if (op->recv_trailing_metadata) {
     GRPC_STATS_INC_HTTP2_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);
     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 =
     s->recv_trailing_metadata =
         op_payload->recv_trailing_metadata.recv_trailing_metadata;
         op_payload->recv_trailing_metadata.recv_trailing_metadata;
     s->final_metadata_requested = true;
     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 &&
     if (s->read_closed && s->frame_storage.length == 0 && !pending_data &&
         s->recv_trailing_metadata_finished != nullptr) {
         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],
       grpc_chttp2_incoming_metadata_buffer_publish(&s->metadata_buffer[1],
                                                    s->recv_trailing_metadata);
                                                    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;
       result = false;
     }
     }
     /* Check if every op that was asked for is done. */
     /* 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 &&
     else if (curr_op->send_initial_metadata &&
              !stream_state->state_callback_received[OP_SEND_INITIAL_METADATA]) {
              !stream_state->state_callback_received[OP_SEND_INITIAL_METADATA]) {
       CRONET_LOG(GPR_DEBUG, "Because");
       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_can_be_run(stream_op, s, &oas->state,
                            OP_RECV_TRAILING_METADATA)) {
                            OP_RECV_TRAILING_METADATA)) {
     CRONET_LOG(GPR_DEBUG, "running: %p  OP_RECV_TRAILING_METADATA", oas);
     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(
       grpc_chttp2_incoming_metadata_buffer_publish(
           &oas->s->state.rs.trailing_metadata,
           &oas->s->state.rs.trailing_metadata,
           stream_op->payload->recv_trailing_metadata.recv_trailing_metadata);
           stream_op->payload->recv_trailing_metadata.recv_trailing_metadata);
       stream_state->rs.trailing_metadata_valid = false;
       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;
     stream_state->state_op_done[OP_RECV_TRAILING_METADATA] = true;
     result = ACTION_TAKEN_NO_CALLBACK;
     result = ACTION_TAKEN_NO_CALLBACK;
   } else if (stream_op->cancel_stream &&
   } 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_CLOSURE_SCHED(op->payload->recv_message.recv_message_ready,
                          GRPC_ERROR_CANCELLED);
                          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);
     GRPC_CLOSURE_SCHED(op->on_complete, GRPC_ERROR_CANCELLED);
     return;
     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;
   struct inproc_stream* stream_list_next;
 } inproc_stream;
 } inproc_stream;
 
 
-static grpc_closure do_nothing_closure;
 static bool cancel_stream_locked(inproc_stream* s, grpc_error* error);
 static bool cancel_stream_locked(inproc_stream* s, grpc_error* error);
 static void op_state_machine(void* arg, 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) {
                                          const char* msg) {
   int is_sm = static_cast<int>(op == s->send_message_op);
   int is_sm = static_cast<int>(op == s->send_message_op);
   int is_stm = static_cast<int>(op == s->send_trailing_md_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_rim = static_cast<int>(op == s->recv_initial_md_op);
   int is_rm = static_cast<int>(op == s->recv_message_op);
   int is_rm = static_cast<int>(op == s->recv_message_op);
   int is_rtm = static_cast<int>(op == s->recv_trailing_md_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;
     s->send_trailing_md_op = nullptr;
   }
   }
   if (s->recv_trailing_md_op) {
   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",
     INPROC_LOG(GPR_INFO, "fail_helper %p scheduling trailing-md-on-complete %p",
                s, error);
                s, error);
     complete_if_batch_end_locked(
     complete_if_batch_end_locked(
@@ -638,6 +646,12 @@ static void op_state_machine(void* arg, grpc_error* error) {
       }
       }
       s->trailing_md_sent = true;
       s->trailing_md_sent = true;
       if (!s->t->is_client && s->trailing_md_recvd && s->recv_trailing_md_op) {
       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,
         INPROC_LOG(GPR_INFO,
                    "op_state_machine %p scheduling trailing-md-on-complete", s);
                    "op_state_machine %p scheduling trailing-md-on-complete", s);
         GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->on_complete,
         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 &&
   if (s->recv_trailing_md_op && s->t->is_client && other &&
       other->send_message_op) {
       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);
     maybe_schedule_op_closure_locked(other, GRPC_ERROR_NONE);
   }
   }
   if (s->to_read_trailing_md_filled) {
   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,
         INPROC_LOG(GPR_INFO,
                    "op_state_machine %p scheduling trailing-md-on-complete %p",
                    "op_state_machine %p scheduling trailing-md-on-complete %p",
                    s, new_err);
                    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_CLOSURE_SCHED(s->recv_trailing_md_op->on_complete,
                            GRPC_ERROR_REF(new_err));
                            GRPC_ERROR_REF(new_err));
         s->recv_trailing_md_op = nullptr;
         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
     // couldn't complete that because we hadn't yet sent out trailing
     // md, now's the chance
     // md, now's the chance
     if (!s->t->is_client && s->trailing_md_recvd && s->recv_trailing_md_op) {
     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(
       complete_if_batch_end_locked(
           s, s->cancel_self_error, s->recv_trailing_md_op,
           s, s->cancel_self_error, s->recv_trailing_md_op,
           "cancel_stream scheduling trailing-md-on-complete");
           "cancel_stream scheduling trailing-md-on-complete");
@@ -873,6 +900,8 @@ static bool cancel_stream_locked(inproc_stream* s, grpc_error* error) {
   return ret;
   return ret;
 }
 }
 
 
+static void do_nothing(void* arg, grpc_error* error) {}
+
 static void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
 static void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
                               grpc_transport_stream_op_batch* op) {
                               grpc_transport_stream_op_batch* op) {
   INPROC_LOG(GPR_INFO, "perform_stream_op %p %p %p", gt, gs, 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_error* error = GRPC_ERROR_NONE;
   grpc_closure* on_complete = op->on_complete;
   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) {
   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) {
   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_CLOSURE_SCHED(op->payload->recv_message.recv_message_ready,
                            GRPC_ERROR_REF(error));
                            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,
     INPROC_LOG(GPR_INFO, "perform_stream_op %p scheduling on_complete %p", s,
                error);
                error);
@@ -1129,12 +1173,8 @@ static grpc_endpoint* get_endpoint(grpc_transport* t) { return nullptr; }
 /*******************************************************************************
 /*******************************************************************************
  * GLOBAL INIT AND DESTROY
  * GLOBAL INIT AND DESTROY
  */
  */
-static void do_nothing(void* arg, grpc_error* error) {}
-
 void grpc_inproc_transport_init(void) {
 void grpc_inproc_transport_init(void) {
   grpc_core::ExecCtx exec_ctx;
   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);
   g_empty_slice = grpc_slice_from_static_buffer(nullptr, 0);
 
 
   grpc_slice key_tmp = grpc_slice_from_static_string(":path");
   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 on_complete[6];  // Max number of pending batches.
   callback_state recv_initial_metadata_ready;
   callback_state recv_initial_metadata_ready;
   callback_state recv_message_ready;
   callback_state recv_message_ready;
+  callback_state recv_trailing_metadata_ready;
 } call_data;
 } call_data;
 
 
 static void run_in_call_combiner(void* arg, grpc_error* error) {
 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",
     intercept_callback(calld, state, false, "recv_message_ready",
                        &batch->payload->recv_message.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) {
   if (batch->cancel_stream) {
     // There can be more than one cancellation batch in flight at any
     // 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
     // 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)));
         static_cast<callback_state*>(gpr_malloc(sizeof(*state)));
     intercept_callback(calld, state, true, "on_complete (cancel_stream)",
     intercept_callback(calld, state, true, "on_complete (cancel_stream)",
                        &batch->on_complete);
                        &batch->on_complete);
-  } else {
+  } else if (batch->on_complete != nullptr) {
     callback_state* state = get_state_for_batch(calld, batch);
     callback_state* state = get_state_for_batch(calld, batch);
     intercept_callback(calld, state, false, "on_complete", &batch->on_complete);
     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 <grpc/support/atm.h>
 
 
 #include "src/core/lib/gpr/mpscq.h"
 #include "src/core/lib/gpr/mpscq.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
 #include "src/core/lib/iomgr/closure.h"
 #include "src/core/lib/iomgr/closure.h"
 
 
 // A simple, lock-free mechanism for serializing activity related to a
 // 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,
 void grpc_call_combiner_cancel(grpc_call_combiner* call_combiner,
                                grpc_error* error);
                                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 */
 #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) {
     if (c->scheduled) {
       gpr_log(GPR_ERROR,
       gpr_log(GPR_ERROR,
               "Closure already scheduled. (closure: %p, created: [%s:%d], "
               "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, 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();
       abort();
     }
     }
     c->scheduled = true;
     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 */
 /* Might be called multiple times */
 static void fd_shutdown(grpc_fd* fd, grpc_error* why) {
 static void fd_shutdown(grpc_fd* fd, grpc_error* why) {
   if (fd->read_closure->SetShutdown(GRPC_ERROR_REF(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));
     fd->write_closure->SetShutdown(GRPC_ERROR_REF(why));
   }
   }
   GRPC_ERROR_UNREF(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_slice_ready;
   grpc_closure receiving_stream_ready;
   grpc_closure receiving_stream_ready;
   grpc_closure receiving_initial_metadata_ready;
   grpc_closure receiving_initial_metadata_ready;
+  grpc_closure receiving_trailing_metadata_ready;
   uint32_t test_only_last_message_flags;
   uint32_t test_only_last_message_flags;
   bool cancelled;
   bool cancelled;
 
 
@@ -1079,7 +1080,6 @@ static void post_batch_completion(batch_control* bctl) {
 
 
   if (bctl->op.send_initial_metadata) {
   if (bctl->op.send_initial_metadata) {
     grpc_metadata_batch_destroy(
     grpc_metadata_batch_destroy(
-
         &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]);
         &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]);
   }
   }
   if (bctl->op.send_message) {
   if (bctl->op.send_message) {
@@ -1087,14 +1087,9 @@ static void post_batch_completion(batch_control* bctl) {
   }
   }
   if (bctl->op.send_trailing_metadata) {
   if (bctl->op.send_trailing_metadata) {
     grpc_metadata_batch_destroy(
     grpc_metadata_batch_destroy(
-
         &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]);
         &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]);
   }
   }
   if (bctl->op.recv_trailing_metadata) {
   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 */
     /* propagate cancellation to any interested children */
     gpr_atm_rel_store(&call->received_final_op_atm, 1);
     gpr_atm_rel_store(&call->received_final_op_atm, 1);
     parent_call* pc = get_parent_call(call);
     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);
       gpr_mu_unlock(&pc->child_list_mu);
     }
     }
-
     GRPC_ERROR_UNREF(error);
     GRPC_ERROR_UNREF(error);
     error = GRPC_ERROR_NONE;
     error = GRPC_ERROR_NONE;
   }
   }
@@ -1390,6 +1384,16 @@ static void receiving_initial_metadata_ready(void* bctlp, grpc_error* error) {
   finish_batch_step(bctl);
   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) {
 static void finish_batch(void* bctlp, grpc_error* error) {
   batch_control* bctl = static_cast<batch_control*>(bctlp);
   batch_control* bctl = static_cast<batch_control*>(bctlp);
   grpc_call* call = bctl->call;
   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;
   size_t i;
   const grpc_op* op;
   const grpc_op* op;
   batch_control* bctl;
   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_call_error error = GRPC_CALL_OK;
   grpc_transport_stream_op_batch* stream_op;
   grpc_transport_stream_op_batch* stream_op;
   grpc_transport_stream_op_batch_payload* stream_op_payload;
   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 =
           stream_op_payload->send_initial_metadata.peer_string =
               &call->peer_string;
               &call->peer_string;
         }
         }
+        has_send_ops = true;
         break;
         break;
       }
       }
       case GRPC_OP_SEND_MESSAGE: {
       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);
             &op->data.send_message.send_message->data.raw.slice_buffer, flags);
         stream_op_payload->send_message.send_message.reset(
         stream_op_payload->send_message.send_message.reset(
             call->sending_stream.get());
             call->sending_stream.get());
+        has_send_ops = true;
         break;
         break;
       }
       }
       case GRPC_OP_SEND_CLOSE_FROM_CLIENT: {
       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;
         call->sent_final_op = true;
         stream_op_payload->send_trailing_metadata.send_trailing_metadata =
         stream_op_payload->send_trailing_metadata.send_trailing_metadata =
             &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */];
             &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */];
+        has_send_ops = true;
         break;
         break;
       }
       }
       case GRPC_OP_SEND_STATUS_FROM_SERVER: {
       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 =
         stream_op_payload->send_trailing_metadata.send_trailing_metadata =
             &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */];
             &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */];
+        has_send_ops = true;
         break;
         break;
       }
       }
       case GRPC_OP_RECV_INITIAL_METADATA: {
       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 =
           stream_op_payload->recv_initial_metadata.peer_string =
               &call->peer_string;
               &call->peer_string;
         }
         }
-        num_completion_callbacks_needed++;
+        ++num_recv_ops;
         break;
         break;
       }
       }
       case GRPC_OP_RECV_MESSAGE: {
       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);
                           grpc_schedule_on_exec_ctx);
         stream_op_payload->recv_message.recv_message_ready =
         stream_op_payload->recv_message.recv_message_ready =
             &call->receiving_stream_ready;
             &call->receiving_stream_ready;
-        num_completion_callbacks_needed++;
+        ++num_recv_ops;
         break;
         break;
       }
       }
       case GRPC_OP_RECV_STATUS_ON_CLIENT: {
       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 =
         call->final_op.client.error_string =
             op->data.recv_status_on_client.error_string;
             op->data.recv_status_on_client.error_string;
         stream_op->recv_trailing_metadata = true;
         stream_op->recv_trailing_metadata = true;
-        stream_op->collect_stats = true;
         stream_op_payload->recv_trailing_metadata.recv_trailing_metadata =
         stream_op_payload->recv_trailing_metadata.recv_trailing_metadata =
             &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
             &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;
             &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;
         break;
       }
       }
       case GRPC_OP_RECV_CLOSE_ON_SERVER: {
       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 =
         call->final_op.server.cancelled =
             op->data.recv_close_on_server.cancelled;
             op->data.recv_close_on_server.cancelled;
         stream_op->recv_trailing_metadata = true;
         stream_op->recv_trailing_metadata = true;
-        stream_op->collect_stats = true;
         stream_op_payload->recv_trailing_metadata.recv_trailing_metadata =
         stream_op_payload->recv_trailing_metadata.recv_trailing_metadata =
             &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
             &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;
             &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;
         break;
       }
       }
     }
     }
@@ -1746,13 +1765,15 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
   if (!is_notify_tag_closure) {
   if (!is_notify_tag_closure) {
     GPR_ASSERT(grpc_cq_begin_op(call->cq, notify_tag));
     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);
   execute_batch(call, stream_op, &bctl->start_batch);
 
 
 done:
 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) {
   if (batch->send_message) {
     batch->payload->send_message.send_message.reset();
     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) {
   if (batch->recv_initial_metadata) {
-    GRPC_CALL_COMBINER_START(
-        call_combiner,
+    closures.Add(
         batch->payload->recv_initial_metadata.recv_initial_metadata_ready,
         batch->payload->recv_initial_metadata.recv_initial_metadata_ready,
         GRPC_ERROR_REF(error), "failing 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 {
 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
 /* Transport stream op: a set of operations to perform on a transport
    against a single stream */
    against a single stream */
 typedef struct grpc_transport_stream_op_batch {
 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;
   grpc_closure* on_complete;
 
 
   /** Values for the stream op (fields set are determined by flags above) */
   /** 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;
   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 */
   /** Cancel this stream with the provided error */
   bool cancel_stream : 1;
   bool cancel_stream : 1;
 
 
@@ -219,11 +222,10 @@ struct grpc_transport_stream_op_batch_payload {
 
 
   struct {
   struct {
     grpc_metadata_batch* recv_trailing_metadata;
     grpc_metadata_batch* recv_trailing_metadata;
-  } recv_trailing_metadata;
-
-  struct {
     grpc_transport_stream_stats* collect_stats;
     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.
   /** Forcefully close this stream.
       The HTTP2 semantics should be:
       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);
     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);
   out = gpr_strvec_flatten(&b, nullptr);
   gpr_strvec_destroy(&b);
   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()
 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()),
       sync_server_settings_(SyncServerSettings()),
       resource_quota_(nullptr),
       resource_quota_(nullptr),
       generic_service_(nullptr) {
       generic_service_(nullptr) {
@@ -186,10 +186,12 @@ std::unique_ptr<Server> ServerBuilder::BuildAndStart() {
     (*plugin)->UpdateChannelArguments(&args);
     (*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_);
     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) {
   if (max_send_message_size_ >= 0) {
     args.SetInt(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH, max_send_message_size_);
     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
 %DOTNET% pack --configuration Release Grpc.Reflection --output ..\..\..\artifacts || goto :error
 
 
 %NUGET% pack Grpc.nuspec -Version %VERSION% -OutputDirectory ..\..\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
 %NUGET% pack Grpc.Tools.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts
 
 
 @rem copy resulting nuget packages to artifacts directory
 @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
 dotnet pack --configuration Release Grpc.Reflection --output ../../../artifacts
 
 
 nuget pack Grpc.nuspec -Version "1.13.0-dev" -OutputDirectory ../../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
 nuget pack Grpc.Tools.nuspec -Version "1.13.0-dev" -OutputDirectory ../../artifacts
 
 
 (cd ../../artifacts && zip csharp_nugets_dotnetcli.zip *.nupkg)
 (cd ../../artifacts && zip csharp_nugets_dotnetcli.zip *.nupkg)

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

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

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

@@ -42,7 +42,7 @@
       </AdditionalOptions>
       </AdditionalOptions>
    </TestAction>
    </TestAction>
    <LaunchAction
    <LaunchAction
-      buildConfiguration = "Debug"
+      buildConfiguration = "Release"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       launchStyle = "0"
       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 \
     build \
     -workspace *.xcworkspace \
     -workspace *.xcworkspace \
     -scheme $SCHEME \
     -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 "$XCODEBUILD_FILTER" \
     | egrep -v "^$" -
     | 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._cython import cygrpc
 from grpc.framework.foundation import callable_util
 from grpc.framework.foundation import callable_util
 
 
+_LOGGER = logging.getLogger(__name__)
+
 _USER_AGENT = 'grpc-python/{}'.format(_grpcio_metadata.__version__)
 _USER_AGENT = 'grpc-python/{}'.format(_grpcio_metadata.__version__)
 
 
 _EMPTY_FLAGS = 0
 _EMPTY_FLAGS = 0
@@ -181,7 +183,7 @@ def _consume_request_iterator(request_iterator, state, call, request_serializer,
             except Exception:  # pylint: disable=broad-except
             except Exception:  # pylint: disable=broad-except
                 code = grpc.StatusCode.UNKNOWN
                 code = grpc.StatusCode.UNKNOWN
                 details = 'Exception iterating requests!'
                 details = 'Exception iterating requests!'
-                logging.exception(details)
+                _LOGGER.exception(details)
                 call.cancel(_common.STATUS_CODE_TO_CYGRPC_STATUS_CODE[code],
                 call.cancel(_common.STATUS_CODE_TO_CYGRPC_STATUS_CODE[code],
                             details)
                             details)
                 _abort(state, code, details)
                 _abort(state, code, details)

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

@@ -20,6 +20,8 @@ import six
 import grpc
 import grpc
 from grpc._cython import cygrpc
 from grpc._cython import cygrpc
 
 
+_LOGGER = logging.getLogger(__name__)
+
 CYGRPC_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY = {
 CYGRPC_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY = {
     cygrpc.ConnectivityState.idle:
     cygrpc.ConnectivityState.idle:
     grpc.ChannelConnectivity.IDLE,
     grpc.ChannelConnectivity.IDLE,
@@ -73,7 +75,7 @@ def decode(b):
         try:
         try:
             return b.decode('utf8')
             return b.decode('utf8')
         except UnicodeDecodeError:
         except UnicodeDecodeError:
-            logging.exception('Invalid encoding on %s', b)
+            _LOGGER.exception('Invalid encoding on %s', b)
             return b.decode('latin1')
             return b.decode('latin1')
 
 
 
 
@@ -84,7 +86,7 @@ def _transform(message, transformer, exception_message):
         try:
         try:
             return transformer(message)
             return transformer(message)
         except Exception:  # pylint: disable=broad-except
         except Exception:  # pylint: disable=broad-except
-            logging.exception(exception_message)
+            _LOGGER.exception(exception_message)
             return None
             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 grpc_channel_credentials *c_credentials
 
 
 
 
+cdef class SSLSessionCacheLRU:
+
+  cdef grpc_ssl_session_cache *_cache
+
+
 cdef class SSLChannelCredentials(ChannelCredentials):
 cdef class SSLChannelCredentials(ChannelCredentials):
 
 
   cdef readonly object _pem_root_certificates
   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 grpc
 import threading
 import threading
 
 
+from libc.stdint cimport uintptr_t
+
+
 def _spawn_callback_in_thread(cb_func, args):
 def _spawn_callback_in_thread(cb_func, args):
   threading.Thread(target=cb_func, args=args).start()
   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):
 def _spawn_callback_async(callback, args):
   async_callback_func(callback, args)
   async_callback_func(callback, args)
 
 
+
 cdef class CallCredentials:
 cdef class CallCredentials:
 
 
   cdef grpc_call_credentials *c(self):
   cdef grpc_call_credentials *c(self):
@@ -107,6 +111,21 @@ cdef class ChannelCredentials:
     raise NotImplementedError()
     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):
 cdef class SSLChannelCredentials(ChannelCredentials):
 
 
   def __cinit__(self, pem_root_certificates, private_key, certificate_chain):
   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_PRIMARY_USER_AGENT_STRING
   const char *GRPC_ARG_SECONDARY_USER_AGENT_STRING
   const char *GRPC_ARG_SECONDARY_USER_AGENT_STRING
   const char *GRPC_SSL_TARGET_NAME_OVERRIDE_ARG
   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_ALGORITHM
   const char *GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL
   const char *GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL
   const char *GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET
   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)
     # We don't care about the internals (and in fact don't know them)
     pass
     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)
   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(
   void grpc_set_ssl_roots_override_callback(
       grpc_ssl_roots_override_callback cb) nogil
       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
 import logging
 
 
+_LOGGER = logging.getLogger(__name__)
 
 
 # This function will ascii encode unicode string inputs if neccesary.
 # This function will ascii encode unicode string inputs if neccesary.
 # In Python3, unicode strings are the default str type.
 # In Python3, unicode strings are the default str type.
@@ -49,5 +50,5 @@ cdef str _decode(bytes bytestring):
         try:
         try:
             return bytestring.decode('utf8')
             return bytestring.decode('utf8')
         except UnicodeDecodeError:
         except UnicodeDecodeError:
-            logging.exception('Invalid encoding on %s', bytestring)
+            _LOGGER.exception('Invalid encoding on %s', bytestring)
             return bytestring.decode('latin1')
             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
   default_authority = GRPC_ARG_DEFAULT_AUTHORITY
   primary_user_agent_string = GRPC_ARG_PRIMARY_USER_AGENT_STRING
   primary_user_agent_string = GRPC_ARG_PRIMARY_USER_AGENT_STRING
   secondary_user_agent_string = GRPC_ARG_SECONDARY_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
   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 time
 import grpc
 import grpc
 
 
+_LOGGER = logging.getLogger(__name__)
+
 cdef grpc_ssl_certificate_config_reload_status _server_cert_config_fetcher_wrapper(
 cdef grpc_ssl_certificate_config_reload_status _server_cert_config_fetcher_wrapper(
         void* user_data, grpc_ssl_server_certificate_config **config) with gil:
         void* user_data, grpc_ssl_server_certificate_config **config) with gil:
   # This is a credentials.ServerCertificateConfig
   # This is a credentials.ServerCertificateConfig
@@ -34,13 +36,13 @@ cdef grpc_ssl_certificate_config_reload_status _server_cert_config_fetcher_wrapp
     try:
     try:
       cert_config_wrapper = user_cb()
       cert_config_wrapper = user_cb()
     except Exception:
     except Exception:
-      logging.exception('Error fetching certificate config')
+      _LOGGER.exception('Error fetching certificate config')
       return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL
       return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL
     if cert_config_wrapper is None:
     if cert_config_wrapper is None:
       return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED
       return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED
     elif not isinstance(
     elif not isinstance(
         cert_config_wrapper, grpc.ServerCertificateConfiguration):
         cert_config_wrapper, grpc.ServerCertificateConfiguration):
-      logging.error(
+      _LOGGER.error(
           'Error fetching certificate configuration: certificate '
           'Error fetching certificate configuration: certificate '
           'configuration must be of type grpc.ServerCertificateConfiguration, '
           'configuration must be of type grpc.ServerCertificateConfiguration, '
           'not %s' % type(cert_config_wrapper).__name__)
           '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 import _common
 from grpc._cython import cygrpc
 from grpc._cython import cygrpc
 
 
+_LOGGER = logging.getLogger(__name__)
+
 
 
 class _AuthMetadataContext(
 class _AuthMetadataContext(
         collections.namedtuple('AuthMetadataContext', (
         collections.namedtuple('AuthMetadataContext', (
@@ -76,7 +78,7 @@ class _Plugin(object):
                                   _AuthMetadataPluginCallback(
                                   _AuthMetadataPluginCallback(
                                       callback_state, callback))
                                       callback_state, callback))
         except Exception as exception:  # pylint: disable=broad-except
         except Exception as exception:  # pylint: disable=broad-except
-            logging.exception(
+            _LOGGER.exception(
                 'AuthMetadataPluginCallback "%s" raised exception!',
                 'AuthMetadataPluginCallback "%s" raised exception!',
                 self._metadata_plugin)
                 self._metadata_plugin)
             with callback_state.lock:
             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._cython import cygrpc
 from grpc.framework.foundation import callable_util
 from grpc.framework.foundation import callable_util
 
 
+_LOGGER = logging.getLogger(__name__)
+
 _SHUTDOWN_TAG = 'shutdown'
 _SHUTDOWN_TAG = 'shutdown'
 _REQUEST_CALL_TAG = 'request_call'
 _REQUEST_CALL_TAG = 'request_call'
 
 
@@ -279,7 +281,7 @@ class _Context(grpc.ServicerContext):
     def abort(self, code, details):
     def abort(self, code, details):
         # treat OK like other invalid arguments: fail the RPC
         # treat OK like other invalid arguments: fail the RPC
         if code == grpc.StatusCode.OK:
         if code == grpc.StatusCode.OK:
-            logging.error(
+            _LOGGER.error(
                 'abort() called with StatusCode.OK; returning UNKNOWN')
                 'abort() called with StatusCode.OK; returning UNKNOWN')
             code = grpc.StatusCode.UNKNOWN
             code = grpc.StatusCode.UNKNOWN
             details = ''
             details = ''
@@ -390,7 +392,7 @@ def _call_behavior(rpc_event, state, behavior, argument, request_deserializer):
                        b'RPC Aborted')
                        b'RPC Aborted')
             elif exception not in state.rpc_errors:
             elif exception not in state.rpc_errors:
                 details = 'Exception calling application: {}'.format(exception)
                 details = 'Exception calling application: {}'.format(exception)
-                logging.exception(details)
+                _LOGGER.exception(details)
                 _abort(state, rpc_event.call, cygrpc.StatusCode.unknown,
                 _abort(state, rpc_event.call, cygrpc.StatusCode.unknown,
                        _common.encode(details))
                        _common.encode(details))
         return None, False
         return None, False
@@ -408,7 +410,7 @@ def _take_response_from_response_iterator(rpc_event, state, response_iterator):
                        b'RPC Aborted')
                        b'RPC Aborted')
             elif exception not in state.rpc_errors:
             elif exception not in state.rpc_errors:
                 details = 'Exception iterating responses: {}'.format(exception)
                 details = 'Exception iterating responses: {}'.format(exception)
-                logging.exception(details)
+                _LOGGER.exception(details)
                 _abort(state, rpc_event.call, cygrpc.StatusCode.unknown,
                 _abort(state, rpc_event.call, cygrpc.StatusCode.unknown,
                        _common.encode(details))
                        _common.encode(details))
         return None, False
         return None, False
@@ -617,7 +619,7 @@ def _handle_call(rpc_event, generic_handlers, interceptor_pipeline, thread_pool,
                                                   interceptor_pipeline)
                                                   interceptor_pipeline)
         except Exception as exception:  # pylint: disable=broad-except
         except Exception as exception:  # pylint: disable=broad-except
             details = 'Exception servicing handler: {}'.format(exception)
             details = 'Exception servicing handler: {}'.format(exception)
-            logging.exception(details)
+            _LOGGER.exception(details)
             return _reject_rpc(rpc_event, cygrpc.StatusCode.unknown,
             return _reject_rpc(rpc_event, cygrpc.StatusCode.unknown,
                                b'Error in service handler!'), None
                                b'Error in service handler!'), None
         if method_handler is 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
 import six
 
 
+_LOGGER = logging.getLogger(__name__)
+
 
 
 class Outcome(six.with_metaclass(abc.ABCMeta)):
 class Outcome(six.with_metaclass(abc.ABCMeta)):
     """A sum type describing the outcome of some call.
     """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),
         return _EasyOutcome(Outcome.Kind.RETURNED, behavior(*args, **kwargs),
                             None)
                             None)
     except Exception as e:  # pylint: disable=broad-except
     except Exception as e:  # pylint: disable=broad-except
-        logging.exception(message)
+        _LOGGER.exception(message)
         return _EasyOutcome(Outcome.Kind.RAISED, None, e)
         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
 from concurrent import futures
 
 
+_LOGGER = logging.getLogger(__name__)
+
 
 
 def _wrap(behavior):
 def _wrap(behavior):
     """Wraps an arbitrary callable behavior in exception-logging."""
     """Wraps an arbitrary callable behavior in exception-logging."""
@@ -25,7 +27,7 @@ def _wrap(behavior):
         try:
         try:
             return behavior(*args, **kwargs)
             return behavior(*args, **kwargs)
         except Exception:
         except Exception:
-            logging.exception(
+            _LOGGER.exception(
                 'Unexpected exception from %s executed in logging pool!',
                 'Unexpected exception from %s executed in logging pool!',
                 behavior)
                 behavior)
             raise
             raise

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

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

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

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

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

@@ -18,6 +18,8 @@ import threading
 import grpc
 import grpc
 from grpc_testing import _common
 from grpc_testing import _common
 
 
+_LOGGER = logging.getLogger(__name__)
+
 
 
 class Rpc(object):
 class Rpc(object):
 
 
@@ -47,7 +49,7 @@ class Rpc(object):
                 try:
                 try:
                     callback()
                     callback()
                 except Exception:  # pylint: disable=broad-except
                 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 = threading.Thread(target=call_back)
         callback_calling_thread.start()
         callback_calling_thread.start()
@@ -86,7 +88,7 @@ class Rpc(object):
     def application_exception_abort(self, exception):
     def application_exception_abort(self, exception):
         with self._condition:
         with self._condition:
             if exception not in self._rpc_errors:
             if exception not in self._rpc_errors:
-                logging.exception('Exception calling application!')
+                _LOGGER.exception('Exception calling application!')
                 self._abort(
                 self._abort(
                     grpc.StatusCode.UNKNOWN,
                     grpc.StatusCode.UNKNOWN,
                     'Exception calling application: {}'.format(exception))
                     '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
 import grpc_testing
 import grpc_testing
 
 
+_LOGGER = logging.getLogger(__name__)
+
 
 
 def _call(behaviors):
 def _call(behaviors):
     for behavior in behaviors:
     for behavior in behaviors:
         try:
         try:
             behavior()
             behavior()
         except Exception:  # pylint: disable=broad-except
         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):
 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
 from tests.unit import test_common
 
 
 _ONE_DAY_IN_SECONDS = 60 * 60 * 24
 _ONE_DAY_IN_SECONDS = 60 * 60 * 24
+_LOGGER = logging.getLogger(__name__)
 
 
 
 
 def serve():
 def serve():
@@ -52,14 +53,14 @@ def serve():
         server.add_insecure_port('[::]:{}'.format(args.port))
         server.add_insecure_port('[::]:{}'.format(args.port))
 
 
     server.start()
     server.start()
-    logging.info('Server serving.')
+    _LOGGER.info('Server serving.')
     try:
     try:
         while True:
         while True:
             time.sleep(_ONE_DAY_IN_SECONDS)
             time.sleep(_ONE_DAY_IN_SECONDS)
     except BaseException as e:
     except BaseException as e:
-        logging.info('Caught exception "%s"; stopping server...', e)
+        _LOGGER.info('Caught exception "%s"; stopping server...', e)
         server.stop(None)
         server.stop(None)
-        logging.info('Server stopped; exiting.')
+        _LOGGER.info('Server stopped; exiting.')
 
 
 
 
 if __name__ == '__main__':
 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.ServerSSLCertReloadTestCertConfigReuse",
   "unit._server_ssl_cert_config_test.ServerSSLCertReloadTestWithClientAuth",
   "unit._server_ssl_cert_config_test.ServerSSLCertReloadTestWithClientAuth",
   "unit._server_ssl_cert_config_test.ServerSSLCertReloadTestWithoutClientAuth",
   "unit._server_ssl_cert_config_test.ServerSSLCertReloadTestWithoutClientAuth",
+  "unit._session_cache_test.SSLSessionCacheTest",
   "unit.beta._beta_features_test.BetaFeaturesTest",
   "unit.beta._beta_features_test.BetaFeaturesTest",
   "unit.beta._beta_features_test.ContextManagementAndLifecycleTest",
   "unit.beta._beta_features_test.ContextManagementAndLifecycleTest",
   "unit.beta._connectivity_channel_test.ConnectivityStatesTest",
   "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
 import grpc
 from grpc import _channel
 from grpc import _channel
+from grpc.experimental import session_cache
 import six
 import six
 
 
 from tests.unit import test_common
 from tests.unit import test_common
@@ -140,6 +141,50 @@ class AuthContextTest(unittest.TestCase):
         self.assertSequenceEqual([b'*.test.google.com'],
         self.assertSequenceEqual([b'*.test.google.com'],
                                  auth_ctx['x509_common_name'])
                                  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__':
 if __name__ == '__main__':
     unittest.main(verbosity=2)
     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'
     src_dir = 'src/objective-c/ProtoRPC'
 
 
+    s.default_subspec = 'Main'
 
 
     s.subspec 'Main' do |ss|
     s.subspec 'Main' do |ss|
       ss.header_mappings_dir = "#{src_dir}"
       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
   %%DOTNET% pack --configuration Release Grpc.Reflection --output ..\..\..\artifacts || goto :error
   
   
   %%NUGET% pack Grpc.nuspec -Version %VERSION% -OutputDirectory ..\..\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
   %%NUGET% pack Grpc.Tools.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts
   
   
   @rem copy resulting nuget packages to artifacts directory
   @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
   dotnet pack --configuration Release Grpc.Reflection --output ../../../artifacts
   
   
   nuget pack Grpc.nuspec -Version "${settings.csharp_version}" -OutputDirectory ../../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
   nuget pack Grpc.Tools.nuspec -Version "${settings.csharp_version}" -OutputDirectory ../../artifacts
   
   
   (cd ../../artifacts && zip csharp_nugets_dotnetcli.zip *.nupkg)
   (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.')
     test_runner_log('Health check: attempt to connect to DNS server over TCP.')
     tcp_connect_subprocess = subprocess.Popen([
     tcp_connect_subprocess = subprocess.Popen([
         args.tcp_connect_bin_path,
         args.tcp_connect_bin_path,
-        '--server_host', '127.0.0.1',
+        '--server_host', '::1',
         '--server_port', str(args.dns_server_port),
         '--server_port', str(args.dns_server_port),
         '--timeout', str(1)])
         '--timeout', str(1)])
     tcp_connect_subprocess.communicate()
     tcp_connect_subprocess.communicate()
@@ -68,7 +68,7 @@ def wait_until_dns_server_is_up(args,
       dns_resolver_subprocess = subprocess.Popen([
       dns_resolver_subprocess = subprocess.Popen([
           args.dns_resolver_bin_path,
           args.dns_resolver_bin_path,
           '--qname', 'health-check-local-dns-server-is-alive.resolver-tests.grpctestingexp',
           '--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)],
           '--server_port', str(args.dns_server_port)],
           stdout=subprocess.PIPE)
           stdout=subprocess.PIPE)
       dns_resolver_stdout, _ = dns_resolver_subprocess.communicate()
       dns_resolver_stdout, _ = dns_resolver_subprocess.communicate()
@@ -125,7 +125,7 @@ current_test_subprocess = subprocess.Popen([\
 
 
 \
 \
   % endfor
   % 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()\
 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,
 static void StartTransportStreamOp(grpc_call_element* elem,
                                    grpc_transport_stream_op_batch* op) {
                                    grpc_transport_stream_op_batch* op) {
   call_data* calld = static_cast<call_data*>(elem->call_data);
   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) {
   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) {
   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,
 static void StartTransportOp(grpc_channel_element* elem,

+ 1 - 17
test/cpp/naming/BUILD

@@ -22,7 +22,7 @@ package(
 
 
 licenses(["notice"])  # Apache v2
 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")
 load(":generate_resolver_component_tests.bzl", "generate_resolver_component_tests")
 
 
@@ -35,20 +35,4 @@ grpc_py_binary(
     testonly = True,
     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()
 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 hashlib
 import json
 import json
 
 
-_LOCAL_DNS_SERVER_ADDRESS = '127.0.0.1:15353'
 
 
 def _append_zone_name(name, zone_name):
 def _append_zone_name(name, zone_name):
   return '%s.%s' % (name, zone_name)
   return '%s.%s' % (name, zone_name)
@@ -121,25 +120,6 @@ def main():
                   'grpc++_test_config',
                   'grpc++_test_config',
               ],
               ],
           } for unsecure_build_config_suffix in ['_unsecure', '']
           } 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/string_util.h>
 #include <grpc/support/sync.h>
 #include <grpc/support/sync.h>
 #include <grpc/support/time.h>
 #include <grpc/support/time.h>
-
 #include <string.h>
 #include <string.h>
 
 
-#include <errno.h>
-#include <fcntl.h>
 #include <gflags/gflags.h>
 #include <gflags/gflags.h>
 #include <gmock/gmock.h>
 #include <gmock/gmock.h>
-#include <thread>
 #include <vector>
 #include <vector>
 
 
 #include "test/cpp/util/subprocess.h"
 #include "test/cpp/util/subprocess.h"
@@ -52,12 +48,6 @@
 #include "test/core/util/port.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.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 grpc::SubProcess;
 using std::vector;
 using std::vector;
 using testing::UnorderedElementsAreArray;
 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) {
 void CheckResolverResultLocked(void* argsp, grpc_error* err) {
-  EXPECT_EQ(err, GRPC_ERROR_NONE);
   ArgsStruct* args = (ArgsStruct*)argsp;
   ArgsStruct* args = (ArgsStruct*)argsp;
   grpc_channel_args* channel_args = args->channel_args;
   grpc_channel_args* channel_args = args->channel_args;
   const grpc_arg* channel_arg =
   const grpc_arg* channel_arg =
@@ -347,17 +271,7 @@ void CheckResolverResultLocked(void* argsp, grpc_error* err) {
   gpr_mu_unlock(args->mu);
   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;
   grpc_core::ExecCtx exec_ctx;
   ArgsStruct args;
   ArgsStruct args;
   ArgsInit(&args);
   ArgsInit(&args);
@@ -375,32 +289,14 @@ void RunResolvesRelevantRecordsTest(void (*OnDoneLocked)(void* arg,
                                                   args.pollset_set, args.lock);
                                                   args.pollset_set, args.lock);
   gpr_free(whole_uri);
   gpr_free(whole_uri);
   grpc_closure on_resolver_result_changed;
   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);
   resolver->NextLocked(&args.channel_args, &on_resolver_result_changed);
   grpc_core::ExecCtx::Get()->Flush();
   grpc_core::ExecCtx::Get()->Flush();
   PollPollsetUntilRequestDone(&args);
   PollPollsetUntilRequestDone(&args);
   ArgsFinish(&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
 }  // namespace
 
 
 int main(int argc, char** argv) {
 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.')
     test_runner_log('Health check: attempt to connect to DNS server over TCP.')
     tcp_connect_subprocess = subprocess.Popen([
     tcp_connect_subprocess = subprocess.Popen([
         args.tcp_connect_bin_path,
         args.tcp_connect_bin_path,
-        '--server_host', '127.0.0.1',
+        '--server_host', '::1',
         '--server_port', str(args.dns_server_port),
         '--server_port', str(args.dns_server_port),
         '--timeout', str(1)])
         '--timeout', str(1)])
     tcp_connect_subprocess.communicate()
     tcp_connect_subprocess.communicate()
@@ -68,7 +68,7 @@ def wait_until_dns_server_is_up(args,
       dns_resolver_subprocess = subprocess.Popen([
       dns_resolver_subprocess = subprocess.Popen([
           args.dns_resolver_bin_path,
           args.dns_resolver_bin_path,
           '--qname', 'health-check-local-dns-server-is-alive.resolver-tests.grpctestingexp',
           '--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)],
           '--server_port', str(args.dns_server_port)],
           stdout=subprocess.PIPE)
           stdout=subprocess.PIPE)
       dns_resolver_stdout, _ = dns_resolver_subprocess.communicate()
       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_addrs', '1.2.3.4:1234,True',
   '--expected_chosen_service_config', '',
   '--expected_chosen_service_config', '',
   '--expected_lb_policy', '',
   '--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()
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
   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_addrs', '1.2.3.5:1234,True;1.2.3.6:1234,True;1.2.3.7:1234,True',
   '--expected_chosen_service_config', '',
   '--expected_chosen_service_config', '',
   '--expected_lb_policy', '',
   '--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()
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
   num_test_failures += 1
@@ -143,7 +143,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '[2607:f8b0:400a:801::1001]:1234,True',
   '--expected_addrs', '[2607:f8b0:400a:801::1001]:1234,True',
   '--expected_chosen_service_config', '',
   '--expected_chosen_service_config', '',
   '--expected_lb_policy', '',
   '--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()
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
   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_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_chosen_service_config', '',
   '--expected_lb_policy', '',
   '--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()
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
   num_test_failures += 1
@@ -167,7 +167,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '1.2.3.4:1234,True',
   '--expected_addrs', '1.2.3.4:1234,True',
   '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]}]}',
   '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]}]}',
   '--expected_lb_policy', 'round_robin',
   '--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()
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
   num_test_failures += 1
@@ -179,7 +179,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"NoSrvSimpleService","waitForReady":true}]}]}',
   '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"NoSrvSimpleService","waitForReady":true}]}]}',
   '--expected_lb_policy', 'round_robin',
   '--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()
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
   num_test_failures += 1
@@ -191,7 +191,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_chosen_service_config', '',
   '--expected_chosen_service_config', '',
   '--expected_lb_policy', '',
   '--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()
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
   num_test_failures += 1
@@ -203,7 +203,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_chosen_service_config', '',
   '--expected_chosen_service_config', '',
   '--expected_lb_policy', '',
   '--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()
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
   num_test_failures += 1
@@ -215,7 +215,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService","waitForReady":true}]}]}',
   '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService","waitForReady":true}]}]}',
   '--expected_lb_policy', 'round_robin',
   '--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()
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
   num_test_failures += 1
@@ -227,7 +227,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_addrs', '1.2.3.4:443,False',
   '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"AlwaysPickedService","waitForReady":true}]}]}',
   '--expected_chosen_service_config', '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"AlwaysPickedService","waitForReady":true}]}]}',
   '--expected_lb_policy', 'round_robin',
   '--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()
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
   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_addrs', '1.2.3.4:1234,True;1.2.3.4:443,False',
   '--expected_chosen_service_config', '',
   '--expected_chosen_service_config', '',
   '--expected_lb_policy', '',
   '--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()
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
   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_addrs', '[2607:f8b0:400a:801::1002]:1234,True;[2607:f8b0:400a:801::1002]:443,False',
   '--expected_chosen_service_config', '',
   '--expected_chosen_service_config', '',
   '--expected_lb_policy', '',
   '--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()
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
   num_test_failures += 1
@@ -263,7 +263,7 @@ current_test_subprocess = subprocess.Popen([
   '--expected_addrs', '1.2.3.4:443,False',
   '--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_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', '',
   '--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()
 current_test_subprocess.communicate()
 if current_test_subprocess.returncode != 0:
 if current_test_subprocess.returncode != 0:
   num_test_failures += 1
   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():
 def main():
   argp = argparse.ArgumentParser(description='Make DNS queries for A records')
   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.')
                     help='Host for DNS server to listen on for TCP and UDP.')
   argp.add_argument('-p', '--server_port', default=53, type=int,
   argp.add_argument('-p', '--server_port', default=53, type=int,
                     help='Port that the DNS server is listening on.')
                     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(
   server = twisted.names.server.DNSServerFactory(
       authorities=[test_domain_com], verbose=2)
       authorities=[test_domain_com], verbose=2)
   server.noisy = 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 = twisted.names.dns.DNSDatagramProtocol(server)
   dns_proto.noisy = 2
   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')
   print('starting twisted.internet.reactor')
   twisted.internet.reactor.suggestThreadPoolSize(1)
   twisted.internet.reactor.suggestThreadPoolSize(1)
   twisted.internet.reactor.run()
   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 =
   std::shared_ptr<grpc::Channel> channel =
       grpc::CreateChannel(server_address, cred.GetCredentials());
       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) {
   if (FLAGS_binary_input) {
     formatted_method_name = method_name;
     formatted_method_name = method_name;
   } else {
   } else {
     formatted_method_name = parser->GetFormattedMethodName(method_name);
     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) {
   if (argc == 3) {
@@ -711,6 +720,7 @@ bool GrpcTool::CallMethod(int argc, const char** argv,
       serialized_request_proto = parser->GetSerializedProtoFromMethod(
       serialized_request_proto = parser->GetSerializedProtoFromMethod(
           method_name, request_text, true /* is_request */);
           method_name, request_text, true /* is_request */);
       if (parser->HasError()) {
       if (parser->HasError()) {
+        fprintf(stderr, "Failed to parse request.\n");
         return false;
         return false;
       }
       }
     }
     }
@@ -735,6 +745,7 @@ bool GrpcTool::CallMethod(int argc, const char** argv,
         serialized_response_proto = parser->GetTextFormatFromMethod(
         serialized_response_proto = parser->GetTextFormatFromMethod(
             method_name, serialized_response_proto, false /* is_request */);
             method_name, serialized_response_proto, false /* is_request */);
         if (parser->HasError()) {
         if (parser->HasError()) {
+          fprintf(stderr, "Failed to parse response.\n");
           return false;
           return false;
         }
         }
       }
       }
@@ -814,6 +825,9 @@ bool GrpcTool::ParseMessage(int argc, const char** argv,
         new grpc::testing::ProtoFileParser(FLAGS_remotedb ? channel : nullptr,
         new grpc::testing::ProtoFileParser(FLAGS_remotedb ? channel : nullptr,
                                            FLAGS_proto_path, FLAGS_protofiles));
                                            FLAGS_proto_path, FLAGS_protofiles));
     if (parser->HasError()) {
     if (parser->HasError()) {
+      fprintf(
+          stderr,
+          "Failed to find remote reflection service and local proto files.\n");
       return false;
       return false;
     }
     }
   }
   }
@@ -824,6 +838,7 @@ bool GrpcTool::ParseMessage(int argc, const char** argv,
     serialized_request_proto =
     serialized_request_proto =
         parser->GetSerializedProtoFromMessageType(type_name, message_text);
         parser->GetSerializedProtoFromMessageType(type_name, message_text);
     if (parser->HasError()) {
     if (parser->HasError()) {
+      fprintf(stderr, "Failed to serialize the message.\n");
       return false;
       return false;
     }
     }
   }
   }
@@ -834,6 +849,7 @@ bool GrpcTool::ParseMessage(int argc, const char** argv,
     grpc::string output_text = parser->GetTextFormatFromMessageType(
     grpc::string output_text = parser->GetTextFormatFromMessageType(
         type_name, serialized_request_proto);
         type_name, serialized_request_proto);
     if (parser->HasError()) {
     if (parser->HasError()) {
+      fprintf(stderr, "Failed to deserialize the message.\n");
       return false;
       return false;
     }
     }
     output_ss << output_text << std::endl;
     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
 @call tools\run_tests\helper_scripts\pre_build_csharp.bat %ARCHITECTURE% || goto :error
 
 
 cd cmake\build\%ARCHITECTURE%
 cd cmake\build\%ARCHITECTURE%
-cmake --build . --target grpc_csharp_ext --config Release
+cmake --build . --target grpc_csharp_ext --config RelWithDebInfo
 cd ..\..\..
 cd ..\..\..
 
 
 mkdir -p %ARTIFACTS_OUT%
 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
 goto :EOF
 
 

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

@@ -6541,26 +6541,6 @@
     "third_party": false, 
     "third_party": false, 
     "type": "target"
     "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": [
     "deps": [
       "gpr", 
       "gpr", 

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

@@ -5638,28 +5638,6 @@
     ], 
     ], 
     "uses_polling": true
     "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": [], 
     "args": [], 
     "boringssl": true, 
     "boringssl": true, 

Some files were not shown because too many files changed in this diff