bpo-35589: Prevent buffer copy in sock_sendall() (GH-11418)
authorAndrew Svetlov <andrew.svetlov@gmail.com>
Thu, 16 May 2019 13:30:16 +0000 (16:30 +0300)
committerMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Thu, 16 May 2019 13:30:16 +0000 (06:30 -0700)
No NEWs is needed since the problem was introduced on master only and never released.

https://bugs.python.org/issue35589

Lib/asyncio/selector_events.py

index 29968214f8ed92fc8043713896fe7daf7c89b8bd..6461d3077633d024f0fdecc4fcc2c5ae73fa7f2c 100644 (file)
@@ -428,32 +428,35 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop):
         if n == len(data):
             # all data sent
             return
-        else:
-            data = bytearray(memoryview(data)[n:])
 
         fut = self.create_future()
         fd = sock.fileno()
         fut.add_done_callback(
             functools.partial(self._sock_write_done, fd))
-        self.add_writer(fd, self._sock_sendall, fut, sock, data)
+        # use a trick with a list in closure to store a mutable state
+        self.add_writer(fd, self._sock_sendall, fut, sock,
+                        memoryview(data), [n])
         return await fut
 
-    def _sock_sendall(self, fut, sock, data):
+    def _sock_sendall(self, fut, sock, view, pos):
         if fut.done():
             # Future cancellation can be scheduled on previous loop iteration
             return
+        start = pos[0]
         try:
-            n = sock.send(data)
+            n = sock.send(view[start:])
         except (BlockingIOError, InterruptedError):
             return
         except Exception as exc:
             fut.set_exception(exc)
             return
 
-        if n == len(data):
+        start += n
+
+        if start == len(view):
             fut.set_result(None)
         else:
-            del data[:n]
+            pos[0] = start
 
     async def sock_connect(self, sock, address):
         """Connect to a remote socket at address.