[Python-checkins] cpython: asyncio: Fix deadlock in readexactly(). Fixes issue #20154.

guido.van.rossum python-checkins at python.org
Tue Jan 7 01:09:30 CET 2014


http://hg.python.org/cpython/rev/54d32e01bbfd
changeset:   88329:54d32e01bbfd
user:        Guido van Rossum <guido at python.org>
date:        Mon Jan 06 16:09:18 2014 -0800
summary:
  asyncio: Fix deadlock in readexactly(). Fixes issue #20154.

files:
  Lib/asyncio/streams.py |  29 +++++++++++++++++++----------
  Misc/NEWS              |   2 ++
  2 files changed, 21 insertions(+), 10 deletions(-)


diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py
--- a/Lib/asyncio/streams.py
+++ b/Lib/asyncio/streams.py
@@ -220,6 +220,7 @@
         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 @@
         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.
 
-        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
+        blocks = []
+        while n > 0:
+            block = yield from self.read(n)
+            if not block:
+                break
+            blocks.append(block)
+            n -= len(block)
 
-        return (yield from self.read(n))
+        # 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 b''.join(blocks)
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -75,6 +75,8 @@
 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.

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list