Commit 00091cad authored by Antoine Pitrou's avatar Antoine Pitrou

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.
........
parent 91ecc566
...@@ -51,12 +51,14 @@ class MockRawIO: ...@@ -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: ...@@ -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): ...@@ -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
......
...@@ -524,6 +524,7 @@ Trent Mick ...@@ -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
......
...@@ -93,6 +93,11 @@ C-API ...@@ -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.
......
...@@ -1379,7 +1379,10 @@ _bufferedreader_read_generic(buffered *self, Py_ssize_t n) ...@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment