]> granicus.if.org Git - python/commitdiff
asyncio: Fix deadlock in readexactly(). Fixes issue #20154.
authorGuido van Rossum <guido@python.org>
Tue, 7 Jan 2014 00:09:18 +0000 (16:09 -0800)
committerGuido van Rossum <guido@python.org>
Tue, 7 Jan 2014 00:09:18 +0000 (16:09 -0800)
Lib/asyncio/streams.py
Misc/NEWS

index 50c4c5d1c2ec101ff3bb9ae7e698d40616ccc1f0..93a21d1af736b9328cf0ab2369dff2f2f5faaf48 100644 (file)
@@ -220,6 +220,7 @@ class StreamReader:
         if loop is None:
             loop = events.get_event_loop()
         self._loop = loop
+        # TODO: Use a bytearray for a buffer, like the transport.
         self._buffer = collections.deque()  # Deque of bytes objects.
         self._byte_count = 0  # Bytes in buffer.
         self._eof = False  # Whether we're done.
@@ -384,15 +385,23 @@ class StreamReader:
         if self._exception is not None:
             raise self._exception
 
-        if n <= 0:
-            return b''
+        # There used to be "optimized" code here.  It created its own
+        # Future and waited until self._buffer had at least the n
+        # bytes, then called read(n).  Unfortunately, this could pause
+        # the transport if the argument was larger than the pause
+        # limit (which is twice self._limit).  So now we just read()
+        # into a local buffer.
+
+        blocks = []
+        while n > 0:
+            block = yield from self.read(n)
+            if not block:
+                break
+            blocks.append(block)
+            n -= len(block)
 
-        while self._byte_count < n and not self._eof:
-            assert not self._waiter
-            self._waiter = futures.Future(loop=self._loop)
-            try:
-                yield from self._waiter
-            finally:
-                self._waiter = None
+        # TODO: Raise EOFError if we break before n == 0?  (That would
+        # be a change in specification, but I've always had to add an
+        # explicit size check to the caller.)
 
-        return (yield from self.read(n))
+        return b''.join(blocks)
index 7750233e3631cf5897944ce72802c870d0218753..189e30ba745f938c64e722645919826a1c46de54 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -75,6 +75,8 @@ Core and Builtins
 Library
 -------
 
+- Issue #20154: Deadlock in asyncio.StreamReader.readexactly().
+
 - Issue #16113: Remove sha3 module again.
 
 - Issue #20111: pathlib.Path.with_suffix() now sanity checks the given suffix.