Merged revisions 83944 via svnmerge from

svn+ssh://pythondev@svn.python.org/python/branches/py3k

........
  r83944 | antoine.pitrou | 2010-08-11 15:31:33 +0200 (mer., 11 août 2010) | 6 lines

  Issue #9550: a BufferedReader could issue an additional read when the
  original read request had been satisfied, which can block indefinitely
  when the underlying raw IO channel is e.g. a socket.  Report and original
  patch by Jason V. Miller.
........
This commit is contained in:
Antoine Pitrou 2010-08-11 13:40:17 +00:00
parent 0a1fa0e8b1
commit cb4f47c377
4 changed files with 34 additions and 1 deletions

View File

@ -57,12 +57,14 @@ class MockRawIO:
self._read_stack = list(read_stack) self._read_stack = list(read_stack)
self._write_stack = [] self._write_stack = []
self._reads = 0 self._reads = 0
self._extraneous_reads = 0
def read(self, n=None): def read(self, n=None):
self._reads += 1 self._reads += 1
try: try:
return self._read_stack.pop(0) return self._read_stack.pop(0)
except: except:
self._extraneous_reads += 1
return b"" return b""
def write(self, b): def write(self, b):
@ -93,6 +95,7 @@ class MockRawIO:
try: try:
data = self._read_stack[0] data = self._read_stack[0]
except IndexError: except IndexError:
self._extraneous_reads += 1
return 0 return 0
if data is None: if data is None:
del self._read_stack[0] del self._read_stack[0]
@ -831,6 +834,27 @@ class BufferedReaderTest(unittest.TestCase, CommonBufferedTests):
self.assertRaises(IOError, bufio.seek, 0) self.assertRaises(IOError, bufio.seek, 0)
self.assertRaises(IOError, bufio.tell) self.assertRaises(IOError, bufio.tell)
def test_no_extraneous_read(self):
# Issue #9550; when the raw IO object has satisfied the read request,
# we should not issue any additional reads, otherwise it may block
# (e.g. socket).
bufsize = 16
for n in (2, bufsize - 1, bufsize, bufsize + 1, bufsize * 2):
rawio = self.MockRawIO([b"x" * n])
bufio = self.tp(rawio, bufsize)
self.assertEqual(bufio.read(n), b"x" * n)
# Simple case: one raw read is enough to satisfy the request.
self.assertEqual(rawio._extraneous_reads, 0,
"failed for {}: {} != 0".format(n, rawio._extraneous_reads))
# A more complex case where two raw reads are needed to satisfy
# the request.
rawio = self.MockRawIO([b"x" * (n - 1), b"x"])
bufio = self.tp(rawio, bufsize)
self.assertEqual(bufio.read(n), b"x" * n)
self.assertEqual(rawio._extraneous_reads, 0,
"failed for {}: {} != 0".format(n, rawio._extraneous_reads))
class CBufferedReaderTest(BufferedReaderTest): class CBufferedReaderTest(BufferedReaderTest):
tp = io.BufferedReader tp = io.BufferedReader

View File

@ -528,6 +528,7 @@ Trent Mick
Aristotelis Mikropoulos Aristotelis Mikropoulos
Damien Miller Damien Miller
Chad Miller Chad Miller
Jason V. Miller
Jay T. Miller Jay T. Miller
Roman Milner Roman Milner
Andrii V. Mishkovskyi Andrii V. Mishkovskyi

View File

@ -29,6 +29,11 @@ Core and Builtins
Library Library
------- -------
- Issue #9550: a BufferedReader could issue an additional read when the
original read request had been satisfied, which could block indefinitely
when the underlying raw IO channel was e.g. a socket. Report and original
patch by Jason V. Miller.
- Issue #9551: Don't raise TypeError when setting the value to None for - Issue #9551: Don't raise TypeError when setting the value to None for
SafeConfigParser instances constructed with allow_no_value == True. SafeConfigParser instances constructed with allow_no_value == True.

View File

@ -1388,7 +1388,10 @@ _bufferedreader_read_generic(buffered *self, Py_ssize_t n)
self->pos = 0; self->pos = 0;
self->raw_pos = 0; self->raw_pos = 0;
self->read_end = 0; self->read_end = 0;
while (self->read_end < self->buffer_size) { /* NOTE: when the read is satisfied, we avoid issuing any additional
reads, which could block indefinitely (e.g. on a socket).
See issue #9550. */
while (remaining > 0 && self->read_end < self->buffer_size) {
Py_ssize_t r = _bufferedreader_fill_buffer(self); Py_ssize_t r = _bufferedreader_fill_buffer(self);
if (r == -1) if (r == -1)
goto error; goto error;