[pypy-commit] pypy merge-2.7.2: Buffered IO retries read() and write() when the raw stream raises errno=EINTR.

amauryfa noreply at buildbot.pypy.org
Sun Jan 22 23:33:04 CET 2012


Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: merge-2.7.2
Changeset: r51669:311b0271907b
Date: 2012-01-22 23:32 +0100
http://bitbucket.org/pypy/pypy/changeset/311b0271907b/

Log:	Buffered IO retries read() and write() when the raw stream raises
	errno=EINTR.

diff --git a/pypy/module/_io/interp_bufferedio.py b/pypy/module/_io/interp_bufferedio.py
--- a/pypy/module/_io/interp_bufferedio.py
+++ b/pypy/module/_io/interp_bufferedio.py
@@ -11,9 +11,23 @@
     W_IOBase, DEFAULT_BUFFER_SIZE, convert_size,
     check_readable_w, check_writable_w, check_seekable_w)
 from pypy.module._io.interp_io import W_BlockingIOError
+import errno
 
 STATE_ZERO, STATE_OK, STATE_DETACHED = range(3)
 
+def trap_eintr(space, error):
+    # Return True if an EnvironmentError with errno == EINTR is set
+    if not error.match(space, space.w_EnvironmentError):
+        return False
+    try:
+        w_value = error.get_w_value(space)
+        w_errno = space.getattr(w_value, space.wrap("errno"))
+        return space.is_true(
+            space.eq(w_errno, space.wrap(errno.EINTR)))
+    except OperationError:
+        return False
+
+
 class BlockingIOError(Exception):
     pass
 
@@ -315,7 +329,16 @@
 
     def _write(self, space, data):
         w_data = space.wrap(data)
-        w_written = space.call_method(self.w_raw, "write", w_data)
+        while True:
+            try:
+                w_written = space.call_method(self.w_raw, "write", w_data)
+            except OperationError, e:
+                if trap_eintr(space, e):
+                    continue  # try again
+                raise
+            else:
+                break
+                
         written = space.getindex_w(w_written, space.w_IOError)
         if not 0 <= written <= len(data):
             raise OperationError(space.w_IOError, space.wrap(
@@ -481,7 +504,16 @@
     def _raw_read(self, space, buffer, start, length):
         length = intmask(length)
         w_buf = space.wrap(RawBuffer(buffer, start, length))
-        w_size = space.call_method(self.w_raw, "readinto", w_buf)
+        while True:
+            try:
+                w_size = space.call_method(self.w_raw, "readinto", w_buf)
+            except OperationError, e:
+                if trap_eintr(space, e):
+                    continue  # try again
+                raise
+            else:
+                break
+                
         if space.is_w(w_size, space.w_None):
             raise BlockingIOError()
         size = space.int_w(w_size)
diff --git a/pypy/module/_io/test/test_bufferedio.py b/pypy/module/_io/test/test_bufferedio.py
--- a/pypy/module/_io/test/test_bufferedio.py
+++ b/pypy/module/_io/test/test_bufferedio.py
@@ -191,6 +191,26 @@
         f = _io.BufferedReader(raw)
         assert repr(f) == '<_io.BufferedReader name=%r>' % (self.tmpfile,)
 
+    def test_read_interrupted(self):
+        import _io, errno
+        class MockRawIO(_io._RawIOBase):
+            def __init__(self):
+                self.count = 0
+            def readable(self):
+                return True
+            def readinto(self, buf):
+                self.count += 1
+                if self.count < 3:
+                    raise IOError(errno.EINTR, "interrupted")
+                else:
+                    buf[:3] = "abc"
+                    return 3
+        rawio = MockRawIO()
+        bufio = _io.BufferedReader(rawio)
+        r = bufio.read(4)
+        assert r == "abca"
+        assert rawio.count == 4
+
 class AppTestBufferedReaderWithThreads(AppTestBufferedReader):
     spaceconfig = dict(usemodules=['_io', 'thread'])
 
@@ -386,6 +406,25 @@
         assert bufio.read() is None
         assert bufio.read() == ""
 
+    def test_write_interrupted(self):
+        import _io, errno
+        class MockRawIO(_io._RawIOBase):
+            def __init__(self):
+                self.count = 0
+            def writable(self):
+                return True
+            def write(self, data):
+                self.count += 1
+                if self.count < 3:
+                    raise IOError(errno.EINTR, "interrupted")
+                else:
+                    return len(data)
+        rawio = MockRawIO()
+        bufio = _io.BufferedWriter(rawio)
+        assert bufio.write("test") == 4
+        bufio.flush()
+        assert rawio.count == 3
+
 class AppTestBufferedRWPair:
     def test_pair(self):
         import _io


More information about the pypy-commit mailing list