[Python-checkins] cpython (3.4): Issue #23293, asyncio: Rewrite IocpProactor.connect_pipe() as a coroutine

victor.stinner python-checkins at python.org
Mon Jan 26 15:05:19 CET 2015


https://hg.python.org/cpython/rev/99c3e304a4ea
changeset:   94318:99c3e304a4ea
branch:      3.4
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Mon Jan 26 15:04:03 2015 +0100
summary:
  Issue #23293, asyncio: Rewrite IocpProactor.connect_pipe() as a coroutine

Use a coroutine with asyncio.sleep() instead of call_later() to ensure that the
schedule call is cancelled.

Add also a unit test cancelling connect_pipe().

files:
  Lib/asyncio/windows_events.py                |  39 ++++-----
  Lib/test/test_asyncio/test_windows_events.py |  13 +++
  2 files changed, 31 insertions(+), 21 deletions(-)


diff --git a/Lib/asyncio/windows_events.py b/Lib/asyncio/windows_events.py
--- a/Lib/asyncio/windows_events.py
+++ b/Lib/asyncio/windows_events.py
@@ -518,28 +518,25 @@
 
         return self._register(ov, pipe, finish_accept_pipe)
 
-    def _connect_pipe(self, fut, address, delay):
-        # Unfortunately there is no way to do an overlapped connect to a pipe.
-        # Call CreateFile() in a loop until it doesn't fail with
-        # ERROR_PIPE_BUSY
-        try:
-            handle = _overlapped.ConnectPipe(address)
-        except OSError as exc:
-            if exc.winerror == _overlapped.ERROR_PIPE_BUSY:
-                # Polling: retry later
-                delay = min(delay * 2, CONNECT_PIPE_MAX_DELAY)
-                self._loop.call_later(delay,
-                                      self._connect_pipe, fut, address, delay)
-            else:
-                fut.set_exception(exc)
-        else:
-            pipe = windows_utils.PipeHandle(handle)
-            fut.set_result(pipe)
+    @coroutine
+    def connect_pipe(self, address):
+        delay = CONNECT_PIPE_INIT_DELAY
+        while True:
+            # Unfortunately there is no way to do an overlapped connect to a pipe.
+            # Call CreateFile() in a loop until it doesn't fail with
+            # ERROR_PIPE_BUSY
+            try:
+                handle = _overlapped.ConnectPipe(address)
+                break
+            except OSError as exc:
+                if exc.winerror != _overlapped.ERROR_PIPE_BUSY:
+                    raise
 
-    def connect_pipe(self, address):
-        fut = futures.Future(loop=self._loop)
-        self._connect_pipe(fut, address, CONNECT_PIPE_INIT_DELAY)
-        return fut
+            # ConnectPipe() failed with ERROR_PIPE_BUSY: retry later
+            delay = min(delay * 2, CONNECT_PIPE_MAX_DELAY)
+            yield from tasks.sleep(delay, loop=self._loop)
+
+        return windows_utils.PipeHandle(handle)
 
     def wait_for_handle(self, handle, timeout=None):
         """Wait for a handle.
diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py
--- a/Lib/test/test_asyncio/test_windows_events.py
+++ b/Lib/test/test_asyncio/test_windows_events.py
@@ -1,6 +1,7 @@
 import os
 import sys
 import unittest
+from unittest import mock
 
 if sys.platform != 'win32':
     raise unittest.SkipTest('Windows only')
@@ -91,6 +92,18 @@
 
         return 'done'
 
+    def test_connect_pipe_cancel(self):
+        exc = OSError()
+        exc.winerror = _overlapped.ERROR_PIPE_BUSY
+        with mock.patch.object(_overlapped, 'ConnectPipe', side_effect=exc) as connect:
+            coro = self.loop._proactor.connect_pipe('pipe_address')
+            task = self.loop.create_task(coro)
+
+            # check that it's possible to cancel connect_pipe()
+            task.cancel()
+            with self.assertRaises(asyncio.CancelledError):
+                self.loop.run_until_complete(task)
+
     def test_wait_for_handle(self):
         event = _overlapped.CreateEvent(None, True, False, None)
         self.addCleanup(_winapi.CloseHandle, event)

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list