Merge pull request #18867 from lidizheng/backport-gevent-fix

Backport c-ares gevent segfault fix to v1.20.1
diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
index 7de1c22..13fde4a 100644
--- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
+++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
@@ -43,6 +43,7 @@
 #include "src/core/lib/gprpp/manual_constructor.h"
 #include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/iomgr/gethostname.h"
+#include "src/core/lib/iomgr/iomgr_custom.h"
 #include "src/core/lib/iomgr/resolve_address.h"
 #include "src/core/lib/iomgr/timer.h"
 #include "src/core/lib/json/json.h"
@@ -426,8 +427,11 @@
     grpc_resolve_address_ares, blocking_resolve_address_ares};
 
 static bool should_use_ares(const char* resolver_env) {
-  return resolver_env == nullptr || strlen(resolver_env) == 0 ||
-         gpr_stricmp(resolver_env, "ares") == 0;
+  // TODO(lidiz): Remove the "g_custom_iomgr_enabled" flag once c-ares support
+  // custom IO managers (e.g. gevent).
+  return !g_custom_iomgr_enabled &&
+         (resolver_env == nullptr || strlen(resolver_env) == 0 ||
+          gpr_stricmp(resolver_env, "ares") == 0);
 }
 
 void grpc_resolver_dns_ares_init() {
diff --git a/src/core/lib/iomgr/iomgr_custom.cc b/src/core/lib/iomgr/iomgr_custom.cc
index 56363c3..f5ac8a0 100644
--- a/src/core/lib/iomgr/iomgr_custom.cc
+++ b/src/core/lib/iomgr/iomgr_custom.cc
@@ -49,6 +49,8 @@
   return false;
 }
 
+bool g_custom_iomgr_enabled = false;
+
 static grpc_iomgr_platform_vtable vtable = {
     iomgr_platform_init,
     iomgr_platform_flush,
@@ -61,6 +63,7 @@
                             grpc_custom_resolver_vtable* resolver,
                             grpc_custom_timer_vtable* timer,
                             grpc_custom_poller_vtable* poller) {
+  g_custom_iomgr_enabled = true;
   grpc_custom_endpoint_init(socket);
   grpc_custom_timer_init(timer);
   grpc_custom_pollset_init(poller);
diff --git a/src/core/lib/iomgr/iomgr_custom.h b/src/core/lib/iomgr/iomgr_custom.h
index 57cc2f9..e6a8884 100644
--- a/src/core/lib/iomgr/iomgr_custom.h
+++ b/src/core/lib/iomgr/iomgr_custom.h
@@ -39,6 +39,8 @@
 #define GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD()
 #endif /* GRPC_CUSTOM_IOMGR_THREAD_CHECK */
 
+extern bool g_custom_iomgr_enabled;
+
 void grpc_custom_iomgr_init(grpc_socket_vtable* socket,
                             grpc_custom_resolver_vtable* resolver,
                             grpc_custom_timer_vtable* timer,
diff --git a/src/python/grpcio_tests/commands.py b/src/python/grpcio_tests/commands.py
index 7a441fe..8f27ab5 100644
--- a/src/python/grpcio_tests/commands.py
+++ b/src/python/grpcio_tests/commands.py
@@ -153,6 +153,9 @@
         # TODO(https://github.com/grpc/grpc/issues/15411) enable this test
         'unit._cython._channel_test.ChannelTest.test_negative_deadline_connectivity'
     )
+    BANNED_WINDOWS_TESTS = (
+        # TODO(https://github.com/grpc/grpc/pull/15411) enable this test
+        'unit._dns_resolver_test.DNSResolverTest.test_connect_loopback',)
     description = 'run tests with gevent.  Assumes grpc/gevent are installed'
     user_options = []
 
@@ -178,7 +181,10 @@
         loader = tests.Loader()
         loader.loadTestsFromNames(['tests'])
         runner = tests.Runner()
-        runner.skip_tests(self.BANNED_TESTS)
+        if sys.platform == 'win32':
+            runner.skip_tests(self.BANNED_TESTS + self.BANNED_WINDOWS_TESTS)
+        else:
+            runner.skip_tests(self.BANNED_TESTS)
         result = gevent.spawn(runner.run, loader.suite)
         result.join()
         if not result.value.wasSuccessful():
diff --git a/src/python/grpcio_tests/tests/tests.json b/src/python/grpcio_tests/tests/tests.json
index 7729ca0..cc08d56 100644
--- a/src/python/grpcio_tests/tests/tests.json
+++ b/src/python/grpcio_tests/tests/tests.json
@@ -46,6 +46,7 @@
   "unit._cython.cygrpc_test.InsecureServerInsecureClient",
   "unit._cython.cygrpc_test.SecureServerSecureClient",
   "unit._cython.cygrpc_test.TypeSmokeTest",
+  "unit._dns_resolver_test.DNSResolverTest",
   "unit._empty_message_test.EmptyMessageTest",
   "unit._error_message_encoding_test.ErrorMessageEncodingTest",
   "unit._exit_test.ExitTest",
diff --git a/src/python/grpcio_tests/tests/unit/BUILD.bazel b/src/python/grpcio_tests/tests/unit/BUILD.bazel
index 54b3c9b..9c9887b 100644
--- a/src/python/grpcio_tests/tests/unit/BUILD.bazel
+++ b/src/python/grpcio_tests/tests/unit/BUILD.bazel
@@ -14,6 +14,7 @@
     "_channel_ready_future_test.py",
     "_compression_test.py",
     "_credentials_test.py",
+    "_dns_resolver_test.py",
     "_empty_message_test.py",
     "_exit_test.py",
     "_interceptor_test.py",
diff --git a/src/python/grpcio_tests/tests/unit/_dns_resolver_test.py b/src/python/grpcio_tests/tests/unit/_dns_resolver_test.py
new file mode 100644
index 0000000..d119707
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/_dns_resolver_test.py
@@ -0,0 +1,63 @@
+# Copyright 2019 The gRPC Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Tests for an actual dns resolution."""
+
+import unittest
+import logging
+import six
+
+import grpc
+from tests.unit import test_common
+from tests.unit.framework.common import test_constants
+
+_METHOD = '/ANY/METHOD'
+_REQUEST = b'\x00\x00\x00'
+_RESPONSE = _REQUEST
+
+
+class GenericHandler(grpc.GenericRpcHandler):
+
+    def service(self, unused_handler_details):
+        return grpc.unary_unary_rpc_method_handler(
+            lambda request, unused_context: request,
+        )
+
+
+class DNSResolverTest(unittest.TestCase):
+
+    def setUp(self):
+        self._server = test_common.test_server()
+        self._server.add_generic_rpc_handlers((GenericHandler(),))
+        self._port = self._server.add_insecure_port('[::]:0')
+        self._server.start()
+
+    def tearDown(self):
+        self._server.stop(None)
+
+    def test_connect_loopback(self):
+        # NOTE(https://github.com/grpc/grpc/issues/18422)
+        # In short, Gevent + C-Ares = Segfault. The C-Ares driver is not
+        # supported by custom io manager like "gevent" or "libuv".
+        with grpc.insecure_channel(
+                'loopback4.unittest.grpc.io:%d' % self._port) as channel:
+            self.assertEqual(
+                channel.unary_unary(_METHOD)(
+                    _REQUEST,
+                    timeout=test_constants.SHORT_TIMEOUT,
+                ), _RESPONSE)
+
+
+if __name__ == '__main__':
+    logging.basicConfig()
+    unittest.main(verbosity=2)