mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
bpo-32622: Implement loop.sendfile() (#5271)
This commit is contained in:
parent
f13f12d8da
commit
7c684073f9
12 changed files with 560 additions and 8 deletions
|
@ -540,6 +540,20 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop):
|
|||
else:
|
||||
fut.set_result((conn, address))
|
||||
|
||||
async def _sendfile_native(self, transp, file, offset, count):
|
||||
del self._transports[transp._sock_fd]
|
||||
resume_reading = transp.is_reading()
|
||||
transp.pause_reading()
|
||||
await transp._make_empty_waiter()
|
||||
try:
|
||||
return await self.sock_sendfile(transp._sock, file, offset, count,
|
||||
fallback=False)
|
||||
finally:
|
||||
transp._reset_empty_waiter()
|
||||
if resume_reading:
|
||||
transp.resume_reading()
|
||||
self._transports[transp._sock_fd] = transp
|
||||
|
||||
def _process_events(self, event_list):
|
||||
for key, mask in event_list:
|
||||
fileobj, (reader, writer) = key.fileobj, key.data
|
||||
|
@ -695,12 +709,14 @@ class _SelectorTransport(transports._FlowControlMixin,
|
|||
class _SelectorSocketTransport(_SelectorTransport):
|
||||
|
||||
_start_tls_compatible = True
|
||||
_sendfile_compatible = constants._SendfileMode.TRY_NATIVE
|
||||
|
||||
def __init__(self, loop, sock, protocol, waiter=None,
|
||||
extra=None, server=None):
|
||||
super().__init__(loop, sock, protocol, extra, server)
|
||||
self._eof = False
|
||||
self._paused = False
|
||||
self._empty_waiter = None
|
||||
|
||||
# Disable the Nagle algorithm -- small writes will be
|
||||
# sent without waiting for the TCP ACK. This generally
|
||||
|
@ -765,6 +781,8 @@ class _SelectorSocketTransport(_SelectorTransport):
|
|||
f'not {type(data).__name__!r}')
|
||||
if self._eof:
|
||||
raise RuntimeError('Cannot call write() after write_eof()')
|
||||
if self._empty_waiter is not None:
|
||||
raise RuntimeError('unable to write; sendfile is in progress')
|
||||
if not data:
|
||||
return
|
||||
|
||||
|
@ -807,12 +825,16 @@ class _SelectorSocketTransport(_SelectorTransport):
|
|||
self._loop._remove_writer(self._sock_fd)
|
||||
self._buffer.clear()
|
||||
self._fatal_error(exc, 'Fatal write error on socket transport')
|
||||
if self._empty_waiter is not None:
|
||||
self._empty_waiter.set_exception(exc)
|
||||
else:
|
||||
if n:
|
||||
del self._buffer[:n]
|
||||
self._maybe_resume_protocol() # May append to buffer.
|
||||
if not self._buffer:
|
||||
self._loop._remove_writer(self._sock_fd)
|
||||
if self._empty_waiter is not None:
|
||||
self._empty_waiter.set_result(None)
|
||||
if self._closing:
|
||||
self._call_connection_lost(None)
|
||||
elif self._eof:
|
||||
|
@ -828,6 +850,23 @@ class _SelectorSocketTransport(_SelectorTransport):
|
|||
def can_write_eof(self):
|
||||
return True
|
||||
|
||||
def _call_connection_lost(self, exc):
|
||||
super()._call_connection_lost(exc)
|
||||
if self._empty_waiter is not None:
|
||||
self._empty_waiter.set_exception(
|
||||
ConnectionError("Connection is closed by peer"))
|
||||
|
||||
def _make_empty_waiter(self):
|
||||
if self._empty_waiter is not None:
|
||||
raise RuntimeError("Empty waiter is already set")
|
||||
self._empty_waiter = self._loop.create_future()
|
||||
if not self._buffer:
|
||||
self._empty_waiter.set_result(None)
|
||||
return self._empty_waiter
|
||||
|
||||
def _reset_empty_waiter(self):
|
||||
self._empty_waiter = None
|
||||
|
||||
|
||||
class _SelectorDatagramTransport(_SelectorTransport):
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue