asyncio, Tulip issue 204: Fix IocpProactor.accept_pipe()

Overlapped.ConnectNamedPipe() now returns a boolean: True if the pipe is
connected (if ConnectNamedPipe() failed with ERROR_PIPE_CONNECTED), False if
the connection is in progress.

This change removes multiple hacks in IocpProactor.
This commit is contained in:
Victor Stinner 2015-01-22 23:50:03 +01:00
parent 34cd2ae69f
commit 2b77c5467f
2 changed files with 19 additions and 26 deletions

View File

@ -490,16 +490,21 @@ class IocpProactor:
def accept_pipe(self, pipe): def accept_pipe(self, pipe):
self._register_with_iocp(pipe) self._register_with_iocp(pipe)
ov = _overlapped.Overlapped(NULL) ov = _overlapped.Overlapped(NULL)
ov.ConnectNamedPipe(pipe.fileno()) connected = ov.ConnectNamedPipe(pipe.fileno())
if connected:
# ConnectNamePipe() failed with ERROR_PIPE_CONNECTED which means
# that the pipe is connected. There is no need to wait for the
# completion of the connection.
f = futures.Future(loop=self._loop)
f.set_result(pipe)
return f
def finish_accept_pipe(trans, key, ov): def finish_accept_pipe(trans, key, ov):
ov.getresult() ov.getresult()
return pipe return pipe
# FIXME: Tulip issue 196: why do we need register=False? return self._register(ov, pipe, finish_accept_pipe)
# See also the comment in the _register() method
return self._register(ov, pipe, finish_accept_pipe,
register=False)
def _connect_pipe(self, fut, address, delay): def _connect_pipe(self, fut, address, delay):
# Unfortunately there is no way to do an overlapped connect to a pipe. # Unfortunately there is no way to do an overlapped connect to a pipe.
@ -581,15 +586,14 @@ class IocpProactor:
# to avoid sending notifications to completion port of ops # to avoid sending notifications to completion port of ops
# that succeed immediately. # that succeed immediately.
def _register(self, ov, obj, callback, def _register(self, ov, obj, callback):
wait_for_post=False, register=True):
# Return a future which will be set with the result of the # Return a future which will be set with the result of the
# operation when it completes. The future's value is actually # operation when it completes. The future's value is actually
# the value returned by callback(). # the value returned by callback().
f = _OverlappedFuture(ov, loop=self._loop) f = _OverlappedFuture(ov, loop=self._loop)
if f._source_traceback: if f._source_traceback:
del f._source_traceback[-1] del f._source_traceback[-1]
if not ov.pending and not wait_for_post: if not ov.pending:
# The operation has completed, so no need to postpone the # The operation has completed, so no need to postpone the
# work. We cannot take this short cut if we need the # work. We cannot take this short cut if we need the
# NumberOfBytes, CompletionKey values returned by # NumberOfBytes, CompletionKey values returned by
@ -605,18 +609,11 @@ class IocpProactor:
# Register the overlapped operation to keep a reference to the # Register the overlapped operation to keep a reference to the
# OVERLAPPED object, otherwise the memory is freed and Windows may # OVERLAPPED object, otherwise the memory is freed and Windows may
# read uninitialized memory. # read uninitialized memory.
#
# For an unknown reason, ConnectNamedPipe() behaves differently: # Register the overlapped operation for later. Note that
# the completion is not notified by GetOverlappedResult() if we # we only store obj to prevent it from being garbage
# already called GetOverlappedResult(). For this specific case, we # collected too early.
# don't expect notification (register is set to False). self._cache[ov.address] = (f, ov, obj, callback)
else:
register = True
if register:
# Register the overlapped operation for later. Note that
# we only store obj to prevent it from being garbage
# collected too early.
self._cache[ov.address] = (f, ov, obj, callback)
return f return f
def _unregister(self, ov): def _unregister(self, ov):
@ -708,10 +705,6 @@ class IocpProactor:
elif isinstance(fut, _WaitCancelFuture): elif isinstance(fut, _WaitCancelFuture):
# _WaitCancelFuture must not be cancelled # _WaitCancelFuture must not be cancelled
pass pass
elif fut.done():
# FIXME: Tulip issue 196: remove this case, it should not
# happen
del self._cache[address]
else: else:
try: try:
fut.cancel() fut.cancel()

View File

@ -1117,10 +1117,10 @@ Overlapped_ConnectNamedPipe(OverlappedObject *self, PyObject *args)
switch (err) { switch (err) {
case ERROR_PIPE_CONNECTED: case ERROR_PIPE_CONNECTED:
mark_as_completed(&self->overlapped); mark_as_completed(&self->overlapped);
Py_RETURN_NONE; Py_RETURN_TRUE;
case ERROR_SUCCESS: case ERROR_SUCCESS:
case ERROR_IO_PENDING: case ERROR_IO_PENDING:
Py_RETURN_NONE; Py_RETURN_FALSE;
default: default:
self->type = TYPE_NOT_STARTED; self->type = TYPE_NOT_STARTED;
return SetFromWindowsErr(err); return SetFromWindowsErr(err);