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:38:10 +00:00
parent 91ecc5667f
commit 00091cada6
4 changed files with 34 additions and 1 deletions

View file

@ -51,12 +51,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):
@ -87,6 +89,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]
@ -822,6 +825,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

@ -524,6 +524,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

@ -93,6 +93,11 @@ C-API
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 #6915: Under Windows, os.listdir() didn't release the Global - Issue #6915: Under Windows, os.listdir() didn't release the Global
Interpreter Lock around all system calls. Original patch by Ryan Kelly. Interpreter Lock around all system calls. Original patch by Ryan Kelly.

View file

@ -1379,7 +1379,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;