8000 Python issue #23293: Rewrite IocpProactor.connect_pipe() as a coroutine · python/asyncio@34214f4 · GitHub
[go: up one dir, main page]

Skip to content
This repository was archived by the owner on Nov 23, 2017. It is now read-only.

Commit 34214f4

Browse files
committed
Python issue #23293: 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().
1 parent 4945c1a commit 34214f4

File tree

2 files changed

+31
-21
lines changed

2 files changed

+31
-21
lines changed

asyncio/windows_events.py

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -518,28 +518,25 @@ def finish_accept_pipe(trans, key, ov):
518518

519519
return self._register(ov, pipe, finish_accept_pipe)
520520

521-
def _connect_pipe(self, fut, address, delay):
522-
# Unfortunately there is no way to do an overlapped connect to a pipe.
523-
# Call CreateFile() in a loop until it doesn't fail with
524-
# ERROR_PIPE_BUSY
525-
try:
526-
handle = _overlapped.ConnectPipe(address)
527-
except OSError as exc:
528-
if exc.winerror == _overlapped.ERROR_PIPE_BUSY:
529-
# Polling: retry later
530-
delay = min(delay * 2, CONNECT_PIPE_MAX_DELAY)
531-
self._loop.call_later(delay,
532-
self._connect_pipe, fut, address, delay)
533-
else:
534-
fut.set_exception(exc)
535-
else:
536-
pipe = windows_utils.PipeHandle(handle)
537-
fut.set_result(pipe)
538-
521+
@coroutine
539522
def connect_pipe(self, address):
540-
fut = futures.Future(loop=self._loop)
541-
self._connect_pipe(fut, address, CONNECT_PIPE_INIT_DELAY)
542-
return fut
523+
delay = CONNECT_PIPE_INIT_DELAY
524+
while True:
525+
# Unfortunately there is no way to do an overlapped connect to a pipe.
526+
# Call CreateFile() in a loop until it doesn't fail with
527+
# ERROR_PIPE_BUSY
528+
try:
529+
handle = _overlapped.ConnectPipe(address)
530+
break
531+
except OSError as exc:
532+
if exc.winerror != _overlapped.ERROR_PIPE_BUSY:
533+
raise
534+
535+
# ConnectPipe() failed with ERROR_PIPE_BUSY: retry later
536+
delay = min(delay * 2, CONNECT_PIPE_MAX_DELAY)
537+
yield from tasks.sleep(delay, loop=self._loop)
538+
539+
return windows_utils.PipeHandle(handle)
543540

544541
def wait_for_handle(self, handle, timeout=None):
545542
"""Wait for a handle.

tests/test_windows_events.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
import sys
33
import unittest
4+
from unittest import mock
45

56
if sys.platform != 'win32':
67
raise unittest.SkipTest('Windows only')
@@ -91,6 +92,18 @@ def _test_pipe(self):
9192

9293
return 'done'
9394

95+
def test_connect_pipe_cancel(self):
96+
exc = OSError()
97+
exc.winerror = _overlapped.ERROR_PIPE_BUSY
98+
with mock.patch.object(_overlapped, 'ConnectPipe', side_effect=exc) as connect:
99+
coro = self.loop._proactor.connect_pipe('pipe_address')
100+
task = self.loop.create_task(coro)
101+
102+
# check that it's possible to cancel connect_pipe()
103+
task.cancel()
104+
with self.assertRaises(asyncio.CancelledError):
105+
self.loop.run_until_complete(task)
106+
94107
def test_wait_for_handle(self):
95108
event = _overlapped.CreateEvent(None, True, False, None)
96109
self.addCleanup(_winapi.CloseHandle, event)

0 commit comments

Comments
 (0)
0