Use os.dup in connect_accepted_socket; explain why it's necessary

This commit is contained in:
Yury Selivanov 2016-10-20 12:08:04 -04:00
parent 80b5ca4458
commit b2d34939a1
1 changed files with 41 additions and 3 deletions

View File

@ -1289,7 +1289,10 @@ cdef class Loop:
raise ValueError('Neither host/port nor sock were specified') raise ValueError('Neither host/port nor sock were specified')
tcp = TCPServer.new(self, protocol_factory, server, ssl, tcp = TCPServer.new(self, protocol_factory, server, ssl,
uv.AF_UNSPEC) uv.AF_UNSPEC)
# See a comment on os_dup in create_connection
fileno = os_dup(sock.fileno()) fileno = os_dup(sock.fileno())
try: try:
tcp._open(fileno) tcp._open(fileno)
tcp._attach_fileobj(sock) tcp._attach_fileobj(sock)
@ -1472,8 +1475,35 @@ cdef class Loop:
waiter = self._new_future() waiter = self._new_future()
tr = TCPTransport.new(self, protocol, None, waiter) tr = TCPTransport.new(self, protocol, None, waiter)
try: try:
# libuv will make socket non-blocking # Why we use os.dup here and other places
# ---------------------------------------
#
# Prerequisite: in Python 3.6, Python Socket Objects (PSO)
# were fixed to raise an OSError if the `socket.close()` call
# failed. So if the underlying FD is already closed,
# `socket.close()` call will fail with OSError(EBADF).
#
# Problem:
#
# - Vanilla asyncio uses the passed PSO directly. When the
# transport eventually closes the PSO, the PSO is marked as
# 'closed'. If the user decides to close the PSO after
# closing the loop, everything works normal in Python 3.5
# and 3.6.
#
# - asyncio+uvloop unwraps the FD from the passed PSO.
# Eventually the transport is closed and so the FD.
# If the user decides to close the PSO after closing the
# loop, an OSError(EBADF) will be raised in Python 3.6.
#
# All in all, duping FDs makes sense, because uvloop
# (and libuv) manage the FD once the user passes a PSO to
# `loop.create_connection`. We don't want the user to have
# any control of the FD once it is passed to uvloop.
# See also: https://github.com/python/asyncio/pull/449
fileno = os_dup(sock.fileno()) fileno = os_dup(sock.fileno())
# libuv will make socket non-blocking
tr._open(fileno) tr._open(fileno)
tr._attach_fileobj(sock) tr._attach_fileobj(sock)
tr._init_protocol() tr._init_protocol()
@ -1555,7 +1585,9 @@ cdef class Loop:
.format(sock)) .format(sock))
try: try:
# See a comment on os_dup in create_connection
fileno = os_dup(sock.fileno()) fileno = os_dup(sock.fileno())
pipe._open(fileno) pipe._open(fileno)
except: except:
pipe._close() pipe._close()
@ -1628,8 +1660,10 @@ cdef class Loop:
waiter = self._new_future() waiter = self._new_future()
tr = UnixTransport.new(self, protocol, None, waiter) tr = UnixTransport.new(self, protocol, None, waiter)
try: try:
# libuv will make socket non-blocking # See a comment on os_dup in create_connection
fileno = os_dup(sock.fileno()) fileno = os_dup(sock.fileno())
# libuv will make socket non-blocking
tr._open(fileno) tr._open(fileno)
tr._attach_fileobj(sock) tr._attach_fileobj(sock)
tr._init_protocol() tr._init_protocol()
@ -1921,7 +1955,9 @@ cdef class Loop:
if sock.type != uv.SOCK_STREAM: if sock.type != uv.SOCK_STREAM:
raise ValueError('invalid socket type, SOCK_STREAM expected') raise ValueError('invalid socket type, SOCK_STREAM expected')
fileno = sock.fileno() # See a comment on os_dup in create_connection
fileno = os_dup(sock.fileno())
app_protocol = protocol_factory() app_protocol = protocol_factory()
waiter = self._new_future() waiter = self._new_future()
transport_waiter = None transport_waiter = None
@ -2067,6 +2103,7 @@ cdef class Loop:
ReadTransport interface.""" ReadTransport interface."""
cdef: cdef:
ReadUnixTransport transp ReadUnixTransport transp
# See a comment on os_dup in create_connection
int fileno = os_dup(pipe.fileno()) int fileno = os_dup(pipe.fileno())
waiter = self._new_future() waiter = self._new_future()
@ -2092,6 +2129,7 @@ cdef class Loop:
WriteTransport interface.""" WriteTransport interface."""
cdef: cdef:
WriteUnixTransport transp WriteUnixTransport transp
# See a comment on os_dup in create_connection
int fileno = os_dup(pipe.fileno()) int fileno = os_dup(pipe.fileno())
waiter = self._new_future() waiter = self._new_future()