[Python-checkins] cpython: Issue #23285: PEP 475 -- Retry system calls failing with EINTR.

charles-francois.natali python-checkins at python.org
Sat Feb 7 14:29:46 CET 2015


https://hg.python.org/cpython/rev/5b63010be19e
changeset:   94552:5b63010be19e
user:        Charles-François Natali <cf.natali at gmail.com>
date:        Sat Feb 07 13:27:50 2015 +0000
summary:
  Issue #23285: PEP 475 -- Retry system calls failing with EINTR.

files:
  Doc/whatsnew/3.5.rst               |   10 +
  Lib/_pyio.py                       |   19 +-
  Lib/distutils/spawn.py             |    3 -
  Lib/multiprocessing/connection.py  |   18 +-
  Lib/multiprocessing/forkserver.py  |   18 +-
  Lib/multiprocessing/popen_fork.py  |    3 -
  Lib/socket.py                      |    2 -
  Lib/socketserver.py                |    2 -
  Lib/subprocess.py                  |   18 +-
  Lib/test/eintrdata/eintr_tester.py |  260 ++++++++++
  Lib/test/test_eintr.py             |   20 +
  Lib/test/test_signal.py            |    7 +-
  Lib/test/test_socket.py            |  124 +----
  Lib/test/test_subprocess.py        |   20 -
  Misc/NEWS                          |    2 +
  Modules/_io/fileio.c               |  125 ++--
  Modules/posixmodule.c              |  431 ++++++++++------
  Modules/socketmodule.c             |  249 +++++----
  18 files changed, 781 insertions(+), 550 deletions(-)


diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst
--- a/Doc/whatsnew/3.5.rst
+++ b/Doc/whatsnew/3.5.rst
@@ -111,6 +111,16 @@
        PEP written by Carl Meyer
 
 
+PEP 475: Retry system calls failing with EINTR
+----------------------------------------------
+
+:pep:`475` adds support for automatic retry of system calls failing with EINTR:
+this means that user code doesn't have to deal with EINTR or InterruptedError
+manually, and should make it more robust against asynchronous signal reception.
+
+.. seealso::
+
+   :pep:`475` -- Retry system calls failing with EINTR
 
 
 Other Language Changes
diff --git a/Lib/_pyio.py b/Lib/_pyio.py
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -1012,10 +1012,7 @@
             current_size = 0
             while True:
                 # Read until EOF or until read() would block.
-                try:
-                    chunk = self.raw.read()
-                except InterruptedError:
-                    continue
+                chunk = self.raw.read()
                 if chunk in empty_values:
                     nodata_val = chunk
                     break
@@ -1034,10 +1031,7 @@
         chunks = [buf[pos:]]
         wanted = max(self.buffer_size, n)
         while avail < n:
-            try:
-                chunk = self.raw.read(wanted)
-            except InterruptedError:
-                continue
+            chunk = self.raw.read(wanted)
             if chunk in empty_values:
                 nodata_val = chunk
                 break
@@ -1066,12 +1060,7 @@
         have = len(self._read_buf) - self._read_pos
         if have < want or have <= 0:
             to_read = self.buffer_size - have
-            while True:
-                try:
-                    current = self.raw.read(to_read)
-                except InterruptedError:
-                    continue
-                break
+            current = self.raw.read(to_read)
             if current:
                 self._read_buf = self._read_buf[self._read_pos:] + current
                 self._read_pos = 0
@@ -1220,8 +1209,6 @@
         while self._write_buf:
             try:
                 n = self.raw.write(self._write_buf)
-            except InterruptedError:
-                continue
             except BlockingIOError:
                 raise RuntimeError("self.raw should implement RawIOBase: it "
                                    "should not raise BlockingIOError")
diff --git a/Lib/distutils/spawn.py b/Lib/distutils/spawn.py
--- a/Lib/distutils/spawn.py
+++ b/Lib/distutils/spawn.py
@@ -137,9 +137,6 @@
             try:
                 pid, status = os.waitpid(pid, 0)
             except OSError as exc:
-                import errno
-                if exc.errno == errno.EINTR:
-                    continue
                 if not DEBUG:
                     cmd = executable
                 raise DistutilsExecError(
diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
--- a/Lib/multiprocessing/connection.py
+++ b/Lib/multiprocessing/connection.py
@@ -365,10 +365,7 @@
     def _send(self, buf, write=_write):
         remaining = len(buf)
         while True:
-            try:
-                n = write(self._handle, buf)
-            except InterruptedError:
-                continue
+            n = write(self._handle, buf)
             remaining -= n
             if remaining == 0:
                 break
@@ -379,10 +376,7 @@
         handle = self._handle
         remaining = size
         while remaining > 0:
-            try:
-                chunk = read(handle, remaining)
-            except InterruptedError:
-                continue
+            chunk = read(handle, remaining)
             n = len(chunk)
             if n == 0:
                 if remaining == size:
@@ -595,13 +589,7 @@
             self._unlink = None
 
     def accept(self):
-        while True:
-            try:
-                s, self._last_accepted = self._socket.accept()
-            except InterruptedError:
-                pass
-            else:
-                break
+        s, self._last_accepted = self._socket.accept()
         s.setblocking(True)
         return Connection(s.detach())
 
diff --git a/Lib/multiprocessing/forkserver.py b/Lib/multiprocessing/forkserver.py
--- a/Lib/multiprocessing/forkserver.py
+++ b/Lib/multiprocessing/forkserver.py
@@ -188,8 +188,6 @@
                         finally:
                             os._exit(code)
 
-            except InterruptedError:
-                pass
             except OSError as e:
                 if e.errno != errno.ECONNABORTED:
                     raise
@@ -230,13 +228,7 @@
     data = b''
     length = UNSIGNED_STRUCT.size
     while len(data) < length:
-        while True:
-            try:
-                s = os.read(fd, length - len(data))
-            except InterruptedError:
-                pass
-            else:
-                break
+        s = os.read(fd, length - len(data))
         if not s:
             raise EOFError('unexpected EOF')
         data += s
@@ -245,13 +237,7 @@
 def write_unsigned(fd, n):
     msg = UNSIGNED_STRUCT.pack(n)
     while msg:
-        while True:
-            try:
-                nbytes = os.write(fd, msg)
-            except InterruptedError:
-                pass
-            else:
-                break
+        nbytes = os.write(fd, msg)
         if nbytes == 0:
             raise RuntimeError('should not get here')
         msg = msg[nbytes:]
diff --git a/Lib/multiprocessing/popen_fork.py b/Lib/multiprocessing/popen_fork.py
--- a/Lib/multiprocessing/popen_fork.py
+++ b/Lib/multiprocessing/popen_fork.py
@@ -1,7 +1,6 @@
 import os
 import sys
 import signal
-import errno
 
 from . import util
 
@@ -29,8 +28,6 @@
                 try:
                     pid, sts = os.waitpid(self.pid, flag)
                 except OSError as e:
-                    if e.errno == errno.EINTR:
-                        continue
                     # Child process not yet created. See #1731717
                     # e.errno == errno.ECHILD == 10
                     return None
diff --git a/Lib/socket.py b/Lib/socket.py
--- a/Lib/socket.py
+++ b/Lib/socket.py
@@ -572,8 +572,6 @@
             except timeout:
                 self._timeout_occurred = True
                 raise
-            except InterruptedError:
-                continue
             except error as e:
                 if e.args[0] in _blocking_errnos:
                     return None
diff --git a/Lib/socketserver.py b/Lib/socketserver.py
--- a/Lib/socketserver.py
+++ b/Lib/socketserver.py
@@ -553,8 +553,6 @@
             try:
                 pid, _ = os.waitpid(-1, 0)
                 self.active_children.discard(pid)
-            except InterruptedError:
-                pass
             except ChildProcessError:
                 # we don't have any children, we're done
                 self.active_children.clear()
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -489,14 +489,6 @@
 DEVNULL = -3
 
 
-def _eintr_retry_call(func, *args):
-    while True:
-        try:
-            return func(*args)
-        except InterruptedError:
-            continue
-
-
 # XXX This function is only used by multiprocessing and the test suite,
 # but it's here so that it can be imported when Python is compiled without
 # threads.
@@ -963,10 +955,10 @@
             if self.stdin:
                 self._stdin_write(input)
             elif self.stdout:
-                stdout = _eintr_retry_call(self.stdout.read)
+                stdout = self.stdout.read()
                 self.stdout.close()
             elif self.stderr:
-                stderr = _eintr_retry_call(self.stderr.read)
+                stderr = self.stderr.read()
                 self.stderr.close()
             self.wait()
         else:
@@ -1410,7 +1402,7 @@
                 # exception (limited in size)
                 errpipe_data = bytearray()
                 while True:
-                    part = _eintr_retry_call(os.read, errpipe_read, 50000)
+                    part = os.read(errpipe_read, 50000)
                     errpipe_data += part
                     if not part or len(errpipe_data) > 50000:
                         break
@@ -1420,7 +1412,7 @@
 
             if errpipe_data:
                 try:
-                    _eintr_retry_call(os.waitpid, self.pid, 0)
+                    os.waitpid(self.pid, 0)
                 except ChildProcessError:
                     pass
                 try:
@@ -1505,7 +1497,7 @@
         def _try_wait(self, wait_flags):
             """All callers to this function MUST hold self._waitpid_lock."""
             try:
-                (pid, sts) = _eintr_retry_call(os.waitpid, self.pid, wait_flags)
+                (pid, sts) = os.waitpid(self.pid, wait_flags)
             except ChildProcessError:
                 # This happens if SIGCLD is set to be ignored or waiting
                 # for child processes has otherwise been disabled for our
diff --git a/Lib/test/eintrdata/eintr_tester.py b/Lib/test/eintrdata/eintr_tester.py
new file mode 100644
--- /dev/null
+++ b/Lib/test/eintrdata/eintr_tester.py
@@ -0,0 +1,260 @@
+"""
+This test suite exercises some system calls subject to interruption with EINTR,
+to check that it is actually handled transparently.
+It is intended to be run by the main test suite within a child process, to
+ensure there is no background thread running (so that signals are delivered to
+the correct thread).
+Signals are generated in-process using setitimer(ITIMER_REAL), which allows
+sub-second periodicity (contrarily to signal()).
+"""
+
+import io
+import os
+import signal
+import socket
+import time
+import unittest
+
+from test import support
+
+
+ at unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()")
+class EINTRBaseTest(unittest.TestCase):
+    """ Base class for EINTR tests. """
+
+    # delay for initial signal delivery
+    signal_delay = 0.1
+    # signal delivery periodicity
+    signal_period = 0.1
+    # default sleep time for tests - should obviously have:
+    # sleep_time > signal_period
+    sleep_time = 0.2
+
+    @classmethod
+    def setUpClass(cls):
+        cls.orig_handler = signal.signal(signal.SIGALRM, lambda *args: None)
+        signal.setitimer(signal.ITIMER_REAL, cls.signal_delay,
+                         cls.signal_period)
+
+    @classmethod
+    def tearDownClass(cls):
+        signal.setitimer(signal.ITIMER_REAL, 0, 0)
+        signal.signal(signal.SIGALRM, cls.orig_handler)
+
+    @classmethod
+    def _sleep(cls):
+        # default sleep time
+        time.sleep(cls.sleep_time)
+
+
+ at unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()")
+class OSEINTRTest(EINTRBaseTest):
+    """ EINTR tests for the os module. """
+
+    def _test_wait_multiple(self, wait_func):
+        num = 3
+        for _ in range(num):
+            pid = os.fork()
+            if pid == 0:
+                self._sleep()
+                os._exit(0)
+        for _ in range(num):
+            wait_func()
+
+    def test_wait(self):
+        self._test_wait_multiple(os.wait)
+
+    @unittest.skipUnless(hasattr(os, 'wait3'), 'requires wait3()')
+    def test_wait3(self):
+        self._test_wait_multiple(lambda: os.wait3(0))
+
+    def _test_wait_single(self, wait_func):
+        pid = os.fork()
+        if pid == 0:
+            self._sleep()
+            os._exit(0)
+        else:
+            wait_func(pid)
+
+    def test_waitpid(self):
+        self._test_wait_single(lambda pid: os.waitpid(pid, 0))
+
+    @unittest.skipUnless(hasattr(os, 'wait4'), 'requires wait4()')
+    def test_wait4(self):
+        self._test_wait_single(lambda pid: os.wait4(pid, 0))
+
+    def test_read(self):
+        rd, wr = os.pipe()
+        self.addCleanup(os.close, rd)
+        # wr closed explicitly by parent
+
+        # the payload below are smaller than PIPE_BUF, hence the writes will be
+        # atomic
+        datas = [b"hello", b"world", b"spam"]
+
+        pid = os.fork()
+        if pid == 0:
+            os.close(rd)
+            for data in datas:
+                # let the parent block on read()
+                self._sleep()
+                os.write(wr, data)
+            os._exit(0)
+        else:
+            self.addCleanup(os.waitpid, pid, 0)
+            os.close(wr)
+            for data in datas:
+                self.assertEqual(data, os.read(rd, len(data)))
+
+    def test_write(self):
+        rd, wr = os.pipe()
+        self.addCleanup(os.close, wr)
+        # rd closed explicitly by parent
+
+        # we must write enough data for the write() to block
+        data = b"xyz" * support.PIPE_MAX_SIZE
+
+        pid = os.fork()
+        if pid == 0:
+            os.close(wr)
+            read_data = io.BytesIO()
+            # let the parent block on write()
+            self._sleep()
+            while len(read_data.getvalue()) < len(data):
+                chunk = os.read(rd, 2 * len(data))
+                read_data.write(chunk)
+            self.assertEqual(read_data.getvalue(), data)
+            os._exit(0)
+        else:
+            os.close(rd)
+            written = 0
+            while written < len(data):
+                written += os.write(wr, memoryview(data)[written:])
+            self.assertEqual(0, os.waitpid(pid, 0)[1])
+
+
+ at unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()")
+class SocketEINTRTest(EINTRBaseTest):
+    """ EINTR tests for the socket module. """
+
+    @unittest.skipUnless(hasattr(socket, 'socketpair'), 'needs socketpair()')
+    def _test_recv(self, recv_func):
+        rd, wr = socket.socketpair()
+        self.addCleanup(rd.close)
+        # wr closed explicitly by parent
+
+        # single-byte payload guard us against partial recv
+        datas = [b"x", b"y", b"z"]
+
+        pid = os.fork()
+        if pid == 0:
+            rd.close()
+            for data in datas:
+                # let the parent block on recv()
+                self._sleep()
+                wr.sendall(data)
+            os._exit(0)
+        else:
+            self.addCleanup(os.waitpid, pid, 0)
+            wr.close()
+            for data in datas:
+                self.assertEqual(data, recv_func(rd, len(data)))
+
+    def test_recv(self):
+        self._test_recv(socket.socket.recv)
+
+    @unittest.skipUnless(hasattr(socket.socket, 'recvmsg'), 'needs recvmsg()')
+    def test_recvmsg(self):
+        self._test_recv(lambda sock, data: sock.recvmsg(data)[0])
+
+    def _test_send(self, send_func):
+        rd, wr = socket.socketpair()
+        self.addCleanup(wr.close)
+        # rd closed explicitly by parent
+
+        # we must send enough data for the send() to block
+        data = b"xyz" * (support.SOCK_MAX_SIZE // 3)
+
+        pid = os.fork()
+        if pid == 0:
+            wr.close()
+            # let the parent block on send()
+            self._sleep()
+            received_data = bytearray(len(data))
+            n = 0
+            while n < len(data):
+                n += rd.recv_into(memoryview(received_data)[n:])
+            self.assertEqual(received_data, data)
+            os._exit(0)
+        else:
+            rd.close()
+            written = 0
+            while written < len(data):
+                sent = send_func(wr, memoryview(data)[written:])
+                # sendall() returns None
+                written += len(data) if sent is None else sent
+            self.assertEqual(0, os.waitpid(pid, 0)[1])
+
+    def test_send(self):
+        self._test_send(socket.socket.send)
+
+    def test_sendall(self):
+        self._test_send(socket.socket.sendall)
+
+    @unittest.skipUnless(hasattr(socket.socket, 'sendmsg'), 'needs sendmsg()')
+    def test_sendmsg(self):
+        self._test_send(lambda sock, data: sock.sendmsg([data]))
+
+    def test_accept(self):
+        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.addCleanup(sock.close)
+
+        sock.bind((support.HOST, 0))
+        _, port = sock.getsockname()
+        sock.listen()
+
+        pid = os.fork()
+        if pid == 0:
+            # let parent block on accept()
+            self._sleep()
+            with socket.create_connection((support.HOST, port)):
+                self._sleep()
+            os._exit(0)
+        else:
+            self.addCleanup(os.waitpid, pid, 0)
+            client_sock, _ = sock.accept()
+            client_sock.close()
+
+    @unittest.skipUnless(hasattr(os, 'mkfifo'), 'needs mkfifo()')
+    def _test_open(self, do_open_close_reader, do_open_close_writer):
+        # Use a fifo: until the child opens it for reading, the parent will
+        # block when trying to open it for writing.
+        support.unlink(support.TESTFN)
+        os.mkfifo(support.TESTFN)
+        self.addCleanup(support.unlink, support.TESTFN)
+
+        pid = os.fork()
+        if pid == 0:
+            # let the parent block
+            self._sleep()
+            do_open_close_reader(support.TESTFN)
+            os._exit(0)
+        else:
+            self.addCleanup(os.waitpid, pid, 0)
+            do_open_close_writer(support.TESTFN)
+
+    def test_open(self):
+        self._test_open(lambda path: open(path, 'r').close(),
+                        lambda path: open(path, 'w').close())
+
+    def test_os_open(self):
+        self._test_open(lambda path: os.close(os.open(path, os.O_RDONLY)),
+                        lambda path: os.close(os.open(path, os.O_WRONLY)))
+
+
+def test_main():
+    support.run_unittest(OSEINTRTest, SocketEINTRTest)
+
+
+if __name__ == "__main__":
+    test_main()
diff --git a/Lib/test/test_eintr.py b/Lib/test/test_eintr.py
new file mode 100644
--- /dev/null
+++ b/Lib/test/test_eintr.py
@@ -0,0 +1,20 @@
+import os
+import signal
+import unittest
+
+from test import script_helper, support
+
+
+ at unittest.skipUnless(os.name == "posix", "only supported on Unix")
+class EINTRTests(unittest.TestCase):
+
+    @unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()")
+    def test_all(self):
+        # Run the tester in a sub-process, to make sure there is only one
+        # thread (for reliable signal delivery).
+        tester = support.findfile("eintr_tester.py", subdir="eintrdata")
+        script_helper.assert_python_ok(tester)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py
--- a/Lib/test/test_signal.py
+++ b/Lib/test/test_signal.py
@@ -587,7 +587,7 @@
             r, w = os.pipe()
 
             def handler(signum, frame):
-                pass
+                1 / 0
 
             signal.signal(signal.SIGALRM, handler)
             if interrupt is not None:
@@ -604,9 +604,8 @@
                     try:
                         # blocking call: read from a pipe without data
                         os.read(r, 1)
-                    except OSError as err:
-                        if err.errno != errno.EINTR:
-                            raise
+                    except ZeroDivisionError:
+                        pass
                     else:
                         sys.exit(2)
                 sys.exit(3)
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -3590,7 +3590,7 @@
     def setUp(self):
         super().setUp()
         orig_alrm_handler = signal.signal(signal.SIGALRM,
-                                          lambda signum, frame: None)
+                                          lambda signum, frame: 1 / 0)
         self.addCleanup(signal.signal, signal.SIGALRM, orig_alrm_handler)
         self.addCleanup(self.setAlarm, 0)
 
@@ -3627,13 +3627,11 @@
         self.serv.settimeout(self.timeout)
 
     def checkInterruptedRecv(self, func, *args, **kwargs):
-        # Check that func(*args, **kwargs) raises OSError with an
+        # Check that func(*args, **kwargs) raises 
         # errno of EINTR when interrupted by a signal.
         self.setAlarm(self.alarm_time)
-        with self.assertRaises(OSError) as cm:
+        with self.assertRaises(ZeroDivisionError) as cm:
             func(*args, **kwargs)
-        self.assertNotIsInstance(cm.exception, socket.timeout)
-        self.assertEqual(cm.exception.errno, errno.EINTR)
 
     def testInterruptedRecvTimeout(self):
         self.checkInterruptedRecv(self.serv.recv, 1024)
@@ -3689,12 +3687,10 @@
         # Check that func(*args, **kwargs), run in a loop, raises
         # OSError with an errno of EINTR when interrupted by a
         # signal.
-        with self.assertRaises(OSError) as cm:
+        with self.assertRaises(ZeroDivisionError) as cm:
             while True:
                 self.setAlarm(self.alarm_time)
                 func(*args, **kwargs)
-        self.assertNotIsInstance(cm.exception, socket.timeout)
-        self.assertEqual(cm.exception.errno, errno.EINTR)
 
     # Issue #12958: The following tests have problems on OS X prior to 10.7
     @support.requires_mac_ver(10, 7)
@@ -4062,117 +4058,6 @@
         pass
 
 
-class FileObjectInterruptedTestCase(unittest.TestCase):
-    """Test that the file object correctly handles EINTR internally."""
-
-    class MockSocket(object):
-        def __init__(self, recv_funcs=()):
-            # A generator that returns callables that we'll call for each
-            # call to recv().
-            self._recv_step = iter(recv_funcs)
-
-        def recv_into(self, buffer):
-            data = next(self._recv_step)()
-            assert len(buffer) >= len(data)
-            buffer[:len(data)] = data
-            return len(data)
-
-        def _decref_socketios(self):
-            pass
-
-        def _textiowrap_for_test(self, buffering=-1):
-            raw = socket.SocketIO(self, "r")
-            if buffering < 0:
-                buffering = io.DEFAULT_BUFFER_SIZE
-            if buffering == 0:
-                return raw
-            buffer = io.BufferedReader(raw, buffering)
-            text = io.TextIOWrapper(buffer, None, None)
-            text.mode = "rb"
-            return text
-
-    @staticmethod
-    def _raise_eintr():
-        raise OSError(errno.EINTR, "interrupted")
-
-    def _textiowrap_mock_socket(self, mock, buffering=-1):
-        raw = socket.SocketIO(mock, "r")
-        if buffering < 0:
-            buffering = io.DEFAULT_BUFFER_SIZE
-        if buffering == 0:
-            return raw
-        buffer = io.BufferedReader(raw, buffering)
-        text = io.TextIOWrapper(buffer, None, None)
-        text.mode = "rb"
-        return text
-
-    def _test_readline(self, size=-1, buffering=-1):
-        mock_sock = self.MockSocket(recv_funcs=[
-                lambda : b"This is the first line\nAnd the sec",
-                self._raise_eintr,
-                lambda : b"ond line is here\n",
-                lambda : b"",
-                lambda : b"",  # XXX(gps): io library does an extra EOF read
-            ])
-        fo = mock_sock._textiowrap_for_test(buffering=buffering)
-        self.assertEqual(fo.readline(size), "This is the first line\n")
-        self.assertEqual(fo.readline(size), "And the second line is here\n")
-
-    def _test_read(self, size=-1, buffering=-1):
-        mock_sock = self.MockSocket(recv_funcs=[
-                lambda : b"This is the first line\nAnd the sec",
-                self._raise_eintr,
-                lambda : b"ond line is here\n",
-                lambda : b"",
-                lambda : b"",  # XXX(gps): io library does an extra EOF read
-            ])
-        expecting = (b"This is the first line\n"
-                     b"And the second line is here\n")
-        fo = mock_sock._textiowrap_for_test(buffering=buffering)
-        if buffering == 0:
-            data = b''
-        else:
-            data = ''
-            expecting = expecting.decode('utf-8')
-        while len(data) != len(expecting):
-            part = fo.read(size)
-            if not part:
-                break
-            data += part
-        self.assertEqual(data, expecting)
-
-    def test_default(self):
-        self._test_readline()
-        self._test_readline(size=100)
-        self._test_read()
-        self._test_read(size=100)
-
-    def test_with_1k_buffer(self):
-        self._test_readline(buffering=1024)
-        self._test_readline(size=100, buffering=1024)
-        self._test_read(buffering=1024)
-        self._test_read(size=100, buffering=1024)
-
-    def _test_readline_no_buffer(self, size=-1):
-        mock_sock = self.MockSocket(recv_funcs=[
-                lambda : b"a",
-                lambda : b"\n",
-                lambda : b"B",
-                self._raise_eintr,
-                lambda : b"b",
-                lambda : b"",
-            ])
-        fo = mock_sock._textiowrap_for_test(buffering=0)
-        self.assertEqual(fo.readline(size), b"a\n")
-        self.assertEqual(fo.readline(size), b"Bb")
-
-    def test_no_buffer(self):
-        self._test_readline_no_buffer()
-        self._test_readline_no_buffer(size=4)
-        self._test_read(buffering=0)
-        self._test_read(size=100, buffering=0)
-
-
 class UnbufferedFileObjectClassTestCase(FileObjectClassTestCase):
 
     """Repeat the tests from FileObjectClassTestCase with bufsize==0.
@@ -5388,7 +5273,6 @@
     tests.extend([
         NonBlockingTCPTests,
         FileObjectClassTestCase,
-        FileObjectInterruptedTestCase,
         UnbufferedFileObjectClassTestCase,
         LineBufferedFileObjectClassTestCase,
         SmallBufferedFileObjectClassTestCase,
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -2421,25 +2421,6 @@
         ProcessTestCase.tearDown(self)
 
 
-class HelperFunctionTests(unittest.TestCase):
-    @unittest.skipIf(mswindows, "errno and EINTR make no sense on windows")
-    def test_eintr_retry_call(self):
-        record_calls = []
-        def fake_os_func(*args):
-            record_calls.append(args)
-            if len(record_calls) == 2:
-                raise OSError(errno.EINTR, "fake interrupted system call")
-            return tuple(reversed(args))
-
-        self.assertEqual((999, 256),
-                         subprocess._eintr_retry_call(fake_os_func, 256, 999))
-        self.assertEqual([(256, 999)], record_calls)
-        # This time there will be an EINTR so it will loop once.
-        self.assertEqual((666,),
-                         subprocess._eintr_retry_call(fake_os_func, 666))
-        self.assertEqual([(256, 999), (666,), (666,)], record_calls)
-
-
 @unittest.skipUnless(mswindows, "Windows-specific tests")
 class CommandsWithSpaces (BaseTestCase):
 
@@ -2528,7 +2509,6 @@
                   Win32ProcessTestCase,
                   CommandTests,
                   ProcessTestCaseNoPoll,
-                  HelperFunctionTests,
                   CommandsWithSpaces,
                   ContextManagerTests,
                   )
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,8 @@
 Core and Builtins
 -----------------
 
+- Issue #23285: PEP 475 - EINTR handling.
+
 - Issue #22735: Fix many edge cases (including crashes) involving custom mro()
   implementations.
 
diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c
--- a/Modules/_io/fileio.c
+++ b/Modules/_io/fileio.c
@@ -218,6 +218,7 @@
 #ifdef HAVE_FSTAT
     struct stat fdfstat;
 #endif
+    int async_err = 0;
 
     assert(PyFileIO_Check(oself));
     if (self->fd >= 0) {
@@ -360,15 +361,18 @@
 
         errno = 0;
         if (opener == Py_None) {
-            Py_BEGIN_ALLOW_THREADS
+            do {
+                Py_BEGIN_ALLOW_THREADS
 #ifdef MS_WINDOWS
-            if (widename != NULL)
-                self->fd = _wopen(widename, flags, 0666);
-            else
+                if (widename != NULL)
+                    self->fd = _wopen(widename, flags, 0666);
+                else
 #endif
-                self->fd = open(name, flags, 0666);
+                    self->fd = open(name, flags, 0666);
 
-            Py_END_ALLOW_THREADS
+                Py_END_ALLOW_THREADS
+            } while (self->fd < 0 && errno == EINTR &&
+                     !(async_err = PyErr_CheckSignals()));
         }
         else {
             PyObject *fdobj;
@@ -397,7 +401,8 @@
 
         fd_is_own = 1;
         if (self->fd < 0) {
-            PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj);
+            if (!async_err)
+                PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj);
             goto error;
         }
 
@@ -550,7 +555,7 @@
 {
     Py_buffer pbuf;
     Py_ssize_t n, len;
-    int err;
+    int err, async_err = 0;
 
     if (self->fd < 0)
         return err_closed();
@@ -562,16 +567,19 @@
 
     if (_PyVerify_fd(self->fd)) {
         len = pbuf.len;
-        Py_BEGIN_ALLOW_THREADS
-        errno = 0;
+        do {
+            Py_BEGIN_ALLOW_THREADS
+            errno = 0;
 #ifdef MS_WINDOWS
-        if (len > INT_MAX)
-            len = INT_MAX;
-        n = read(self->fd, pbuf.buf, (int)len);
+            if (len > INT_MAX)
+                len = INT_MAX;
+            n = read(self->fd, pbuf.buf, (int)len);
 #else
-        n = read(self->fd, pbuf.buf, len);
+            n = read(self->fd, pbuf.buf, len);
 #endif
-        Py_END_ALLOW_THREADS
+            Py_END_ALLOW_THREADS
+        } while (n < 0 && errno == EINTR &&
+                 !(async_err = PyErr_CheckSignals()));
     } else
         n = -1;
     err = errno;
@@ -580,7 +588,8 @@
         if (err == EAGAIN)
             Py_RETURN_NONE;
         errno = err;
-        PyErr_SetFromErrno(PyExc_IOError);
+        if (!async_err)
+            PyErr_SetFromErrno(PyExc_IOError);
         return NULL;
     }
 
@@ -627,6 +636,7 @@
     Py_ssize_t bytes_read = 0;
     Py_ssize_t n;
     size_t bufsize;
+    int async_err = 0;
 
     if (self->fd < 0)
         return err_closed();
@@ -673,27 +683,23 @@
                     return NULL;
             }
         }
-        Py_BEGIN_ALLOW_THREADS
-        errno = 0;
-        n = bufsize - bytes_read;
+        do {
+            Py_BEGIN_ALLOW_THREADS
+            errno = 0;
+            n = bufsize - bytes_read;
 #ifdef MS_WINDOWS
-        if (n > INT_MAX)
-            n = INT_MAX;
-        n = read(self->fd, PyBytes_AS_STRING(result) + bytes_read, (int)n);
+            if (n > INT_MAX)
+                n = INT_MAX;
+            n = read(self->fd, PyBytes_AS_STRING(result) + bytes_read, (int)n);
 #else
-        n = read(self->fd, PyBytes_AS_STRING(result) + bytes_read, n);
+            n = read(self->fd, PyBytes_AS_STRING(result) + bytes_read, n);
 #endif
-        Py_END_ALLOW_THREADS
+            Py_END_ALLOW_THREADS
+        } while (n < 0 && errno == EINTR &&
+                 !(async_err = PyErr_CheckSignals()));
         if (n == 0)
             break;
         if (n < 0) {
-            if (errno == EINTR) {
-                if (PyErr_CheckSignals()) {
-                    Py_DECREF(result);
-                    return NULL;
-                }
-                continue;
-            }
             if (errno == EAGAIN) {
                 if (bytes_read > 0)
                     break;
@@ -701,7 +707,8 @@
                 Py_RETURN_NONE;
             }
             Py_DECREF(result);
-            PyErr_SetFromErrno(PyExc_IOError);
+            if (!async_err)
+                PyErr_SetFromErrno(PyExc_IOError);
             return NULL;
         }
         bytes_read += n;
@@ -723,6 +730,7 @@
     char *ptr;
     Py_ssize_t n;
     Py_ssize_t size = -1;
+    int async_err = 0;
     PyObject *bytes;
 
     if (self->fd < 0)
@@ -747,14 +755,17 @@
     ptr = PyBytes_AS_STRING(bytes);
 
     if (_PyVerify_fd(self->fd)) {
-        Py_BEGIN_ALLOW_THREADS
-        errno = 0;
+        do {
+            Py_BEGIN_ALLOW_THREADS
+            errno = 0;
 #ifdef MS_WINDOWS
-        n = read(self->fd, ptr, (int)size);
+            n = read(self->fd, ptr, (int)size);
 #else
-        n = read(self->fd, ptr, size);
+            n = read(self->fd, ptr, size);
 #endif
-        Py_END_ALLOW_THREADS
+            Py_END_ALLOW_THREADS
+        } while (n < 0 && errno == EINTR &&
+                 !(async_err = PyErr_CheckSignals()));
     } else
         n = -1;
 
@@ -764,7 +775,8 @@
         if (err == EAGAIN)
             Py_RETURN_NONE;
         errno = err;
-        PyErr_SetFromErrno(PyExc_IOError);
+        if (!async_err)
+            PyErr_SetFromErrno(PyExc_IOError);
         return NULL;
     }
 
@@ -783,7 +795,7 @@
 {
     Py_buffer pbuf;
     Py_ssize_t n, len;
-    int err;
+    int err, async_err = 0;
 
     if (self->fd < 0)
         return err_closed();
@@ -794,24 +806,26 @@
         return NULL;
 
     if (_PyVerify_fd(self->fd)) {
-        Py_BEGIN_ALLOW_THREADS
-        errno = 0;
-        len = pbuf.len;
+        do {
+            Py_BEGIN_ALLOW_THREADS
+            errno = 0;
+            len = pbuf.len;
 #ifdef MS_WINDOWS
-        if (len > 32767 && isatty(self->fd)) {
-            /* Issue #11395: the Windows console returns an error (12: not
-               enough space error) on writing into stdout if stdout mode is
-               binary and the length is greater than 66,000 bytes (or less,
-               depending on heap usage). */
-            len = 32767;
-        }
-        else if (len > INT_MAX)
-            len = INT_MAX;
-        n = write(self->fd, pbuf.buf, (int)len);
+            if (len > 32767 && isatty(self->fd)) {
+                /* Issue #11395: the Windows console returns an error (12: not
+                   enough space error) on writing into stdout if stdout mode is
+                   binary and the length is greater than 66,000 bytes (or less,
+                   depending on heap usage). */
+                len = 32767;
+            } else if (len > INT_MAX)
+                len = INT_MAX;
+            n = write(self->fd, pbuf.buf, (int)len);
 #else
-        n = write(self->fd, pbuf.buf, len);
+            n = write(self->fd, pbuf.buf, len);
 #endif
-        Py_END_ALLOW_THREADS
+            Py_END_ALLOW_THREADS
+        } while (n < 0 && errno == EINTR &&
+                 !(async_err = PyErr_CheckSignals()));
     } else
         n = -1;
     err = errno;
@@ -822,7 +836,8 @@
         if (err == EAGAIN)
             Py_RETURN_NONE;
         errno = err;
-        PyErr_SetFromErrno(PyExc_IOError);
+        if (!async_err)
+            PyErr_SetFromErrno(PyExc_IOError);
         return NULL;
     }
 
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -1361,13 +1361,16 @@
 posix_fildes_fd(int fd, int (*func)(int))
 {
     int res;
-    Py_BEGIN_ALLOW_THREADS
-    res = (*func)(fd);
-    Py_END_ALLOW_THREADS
-    if (res < 0)
-        return posix_error();
-    Py_INCREF(Py_None);
-    return Py_None;
+    int async_err = 0;
+
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        res = (*func)(fd);
+        Py_END_ALLOW_THREADS
+    } while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
+    if (res != 0)
+        return (!async_err) ? posix_error() : NULL;
+    Py_RETURN_NONE;
 }
 
 
@@ -3479,11 +3482,16 @@
 /*[clinic end generated code: output=3c19fbfd724a8e0f input=8ab11975ca01ee5b]*/
 {
     int res;
-    Py_BEGIN_ALLOW_THREADS
-    res = fchmod(fd, mode);
-    Py_END_ALLOW_THREADS
-    if (res < 0)
-        return posix_error();
+    int async_err = 0;
+
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        res = fchmod(fd, mode);
+        Py_END_ALLOW_THREADS
+    } while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
+    if (res != 0)
+        return (!async_err) ? posix_error() : NULL;
+
     Py_RETURN_NONE;
 }
 #endif /* HAVE_FCHMOD */
@@ -4104,11 +4112,16 @@
 /*[clinic end generated code: output=687781cb7d8974d6 input=3af544ba1b13a0d7]*/
 {
     int res;
-    Py_BEGIN_ALLOW_THREADS
-    res = fchown(fd, uid, gid);
-    Py_END_ALLOW_THREADS
-    if (res < 0)
-        return posix_error();
+    int async_err = 0;
+
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        res = fchown(fd, uid, gid);
+        Py_END_ALLOW_THREADS
+    } while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
+    if (res != 0)
+        return (!async_err) ? posix_error() : NULL;
+
     Py_RETURN_NONE;
 }
 #endif /* HAVE_FCHOWN */
@@ -9602,12 +9615,17 @@
 {
     pid_t pid;
     struct rusage ru;
+    int async_err = 0;
     WAIT_TYPE status;
     WAIT_STATUS_INT(status) = 0;
 
-    Py_BEGIN_ALLOW_THREADS
-    pid = wait3(&status, options, &ru);
-    Py_END_ALLOW_THREADS
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        pid = wait3(&status, options, &ru);
+        Py_END_ALLOW_THREADS
+    } while (pid < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
+    if (pid < 0)
+        return (!async_err) ? posix_error() : NULL;
 
     return wait_helper(pid, WAIT_STATUS_INT(status), &ru);
 }
@@ -9665,15 +9683,21 @@
 os_wait4_impl(PyModuleDef *module, pid_t pid, int options)
 /*[clinic end generated code: output=20dfb05289d37dc6 input=d11deed0750600ba]*/
 {
+    pid_t res;
     struct rusage ru;
+    int async_err = 0;
     WAIT_TYPE status;
     WAIT_STATUS_INT(status) = 0;
 
-    Py_BEGIN_ALLOW_THREADS
-    pid = wait4(pid, &status, options, &ru);
-    Py_END_ALLOW_THREADS
-
-    return wait_helper(pid, WAIT_STATUS_INT(status), &ru);
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        res = wait4(pid, &status, options, &ru);
+        Py_END_ALLOW_THREADS
+    } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
+    if (res < 0)
+        return (!async_err) ? posix_error() : NULL;
+
+    return wait_helper(res, WAIT_STATUS_INT(status), &ru);
 }
 #endif /* HAVE_WAIT4 */
 
@@ -9744,14 +9768,17 @@
 {
     PyObject *result;
     int res;
+    int async_err = 0;
     siginfo_t si;
     si.si_pid = 0;
 
-    Py_BEGIN_ALLOW_THREADS
-    res = waitid(idtype, id, &si, options);
-    Py_END_ALLOW_THREADS
-    if (res == -1)
-        return posix_error();
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        res = waitid(idtype, id, &si, options);
+        Py_END_ALLOW_THREADS
+    } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
+    if (res < 0)
+        return (!async_err) ? posix_error() : NULL;
 
     if (si.si_pid == 0)
         Py_RETURN_NONE;
@@ -9828,16 +9855,20 @@
 os_waitpid_impl(PyModuleDef *module, pid_t pid, int options)
 /*[clinic end generated code: output=095a6b00af70b7ac input=0bf1666b8758fda3]*/
 {
+    pid_t res;
+    int async_err = 0;
     WAIT_TYPE status;
     WAIT_STATUS_INT(status) = 0;
 
-    Py_BEGIN_ALLOW_THREADS
-    pid = waitpid(pid, &status, options);
-    Py_END_ALLOW_THREADS
-    if (pid == -1)
-        return posix_error();
-
-    return Py_BuildValue("Ni", PyLong_FromPid(pid), WAIT_STATUS_INT(status));
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        res = waitpid(pid, &status, options);
+        Py_END_ALLOW_THREADS
+    } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
+    if (res < 0)
+        return (!async_err) ? posix_error() : NULL;
+
+    return Py_BuildValue("Ni", PyLong_FromPid(res), WAIT_STATUS_INT(status));
 }
 #elif defined(HAVE_CWAIT)
 /* MS C has a variant of waitpid() that's usable for most purposes. */
@@ -9894,15 +9925,19 @@
 /*[clinic end generated code: output=c20b95b15ad44a3a input=444c8f51cca5b862]*/
 {
     int status;
-
-    Py_BEGIN_ALLOW_THREADS
-    pid = _cwait(&status, pid, options);
-    Py_END_ALLOW_THREADS
-    if (pid == -1)
-        return posix_error();
+    Py_intptr_t res;
+    int async_err = 0;
+
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        res = _cwait(&status, pid, options);
+        Py_END_ALLOW_THREADS
+    } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
+    if (res != 0)
+        return (!async_err) ? posix_error() : NULL;
 
     /* shift the status left a byte so this is more like the POSIX waitpid */
-    return Py_BuildValue(_Py_PARSE_INTPTR "i", pid, status << 8);
+    return Py_BuildValue(_Py_PARSE_INTPTR "i", res, status << 8);
 }
 #endif
 
@@ -9943,14 +9978,17 @@
 /*[clinic end generated code: output=2a83a9d164e7e6a8 input=03b0182d4a4700ce]*/
 {
     pid_t pid;
+    int async_err = 0;
     WAIT_TYPE status;
     WAIT_STATUS_INT(status) = 0;
 
-    Py_BEGIN_ALLOW_THREADS
-    pid = wait(&status);
-    Py_END_ALLOW_THREADS
-    if (pid == -1)
-        return posix_error();
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        pid = wait(&status);
+        Py_END_ALLOW_THREADS
+    } while (pid < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
+    if (pid < 0)
+        return (!async_err) ? posix_error() : NULL;
 
     return Py_BuildValue("Ni", PyLong_FromPid(pid), WAIT_STATUS_INT(status));
 }
@@ -10837,6 +10875,7 @@
 /*[clinic end generated code: output=05b68fc4ed5e29c9 input=ad8623b29acd2934]*/
 {
     int fd;
+    int async_err = 0;
 
 #ifdef O_CLOEXEC
     int *atomic_flag_works = &_Py_open_cloexec_works;
@@ -10850,22 +10889,25 @@
     flags |= O_CLOEXEC;
 #endif
 
-    Py_BEGIN_ALLOW_THREADS
-#ifdef MS_WINDOWS
-    if (path->wide)
-        fd = _wopen(path->wide, flags, mode);
-    else
+    do {
+        Py_BEGIN_ALLOW_THREADS
+#ifdef MS_WINDOWS
+        if (path->wide)
+            fd = _wopen(path->wide, flags, mode);
+        else
 #endif
 #ifdef HAVE_OPENAT
-    if (dir_fd != DEFAULT_DIR_FD)
-        fd = openat(dir_fd, path->narrow, flags, mode);
-    else
-#endif
-        fd = open(path->narrow, flags, mode);
-    Py_END_ALLOW_THREADS
+        if (dir_fd != DEFAULT_DIR_FD)
+            fd = openat(dir_fd, path->narrow, flags, mode);
+        else
+#endif
+            fd = open(path->narrow, flags, mode);
+        Py_END_ALLOW_THREADS
+    } while (fd < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
 
     if (fd == -1) {
-        PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object);
+        if (!async_err)
+            PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object);
         return -1;
     }
 
@@ -10924,6 +10966,10 @@
     int res;
     if (!_PyVerify_fd(fd))
         return posix_error();
+    /* We do not want to retry upon EINTR: see http://lwn.net/Articles/576478/
+     * and http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
+     * for more details.
+     */
     Py_BEGIN_ALLOW_THREADS
     res = close(fd);
     Py_END_ALLOW_THREADS
@@ -11089,6 +11135,10 @@
     if (!_PyVerify_fd_dup2(fd, fd2))
         return posix_error();
 
+    /* dup2() can fail with EINTR if the target FD is already open, because it
+     * then has to be closed. See os_close_impl() for why we don't handle EINTR
+     * upon close(), and therefore below.
+     */
 #ifdef MS_WINDOWS
     Py_BEGIN_ALLOW_THREADS
     res = dup2(fd, fd2);
@@ -11355,6 +11405,7 @@
 /*[clinic end generated code: output=1f3bc27260a24968 input=1df2eaa27c0bf1d3]*/
 {
     Py_ssize_t n;
+    int async_err = 0;
     PyObject *buffer;
 
     if (length < 0) {
@@ -11375,13 +11426,16 @@
     buffer = PyBytes_FromStringAndSize((char *)NULL, length);
     if (buffer == NULL)
         return NULL;
-    Py_BEGIN_ALLOW_THREADS
-    n = read(fd, PyBytes_AS_STRING(buffer), READ_CAST length);
-    Py_END_ALLOW_THREADS
+
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        n = read(fd, PyBytes_AS_STRING(buffer), READ_CAST length);
+        Py_END_ALLOW_THREADS
+    } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
 
     if (n < 0) {
         Py_DECREF(buffer);
-        return posix_error();
+        return (!async_err) ? posix_error() : NULL;
     }
 
     if (n != length)
@@ -11515,6 +11569,7 @@
 {
     int cnt;
     Py_ssize_t n;
+    int async_err = 0;
     struct iovec *iov;
     Py_buffer *buf;
 
@@ -11529,13 +11584,16 @@
     if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_WRITABLE) < 0)
         return -1;
 
-    Py_BEGIN_ALLOW_THREADS
-    n = readv(fd, iov, cnt);
-    Py_END_ALLOW_THREADS
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        n = readv(fd, iov, cnt);
+        Py_END_ALLOW_THREADS
+    } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
 
     iov_cleanup(iov, buf, cnt);
     if (n < 0) {
-        posix_error();
+        if (!async_err)
+            posix_error();
         return -1;
     }
 
@@ -11598,6 +11656,7 @@
 /*[clinic end generated code: output=7b62bf6c06e20ae8 input=084948dcbaa35d4c]*/
 {
     Py_ssize_t n;
+    int async_err = 0;
     PyObject *buffer;
 
     if (length < 0) {
@@ -11611,12 +11670,16 @@
         Py_DECREF(buffer);
         return posix_error();
     }
-    Py_BEGIN_ALLOW_THREADS
-    n = pread(fd, PyBytes_AS_STRING(buffer), length, offset);
-    Py_END_ALLOW_THREADS
+
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        n = pread(fd, PyBytes_AS_STRING(buffer), length, offset);
+        Py_END_ALLOW_THREADS
+    } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
+
     if (n < 0) {
         Py_DECREF(buffer);
-        return posix_error();
+        return (!async_err) ? posix_error() : NULL;
     }
     if (n != length)
         _PyBytes_Resize(&buffer, n);
@@ -11677,6 +11740,7 @@
 /*[clinic end generated code: output=aeb96acfdd4d5112 input=3207e28963234f3c]*/
 {
     Py_ssize_t size;
+    int async_err = 0;
     Py_ssize_t len = data->len;
 
     if (!_PyVerify_fd(fd)) {
@@ -11684,17 +11748,21 @@
         return -1;
     }
 
-    Py_BEGIN_ALLOW_THREADS
-#ifdef MS_WINDOWS
-    if (len > INT_MAX)
-        len = INT_MAX;
-    size = write(fd, data->buf, (int)len);
-#else
-    size = write(fd, data->buf, len);
-#endif
-    Py_END_ALLOW_THREADS
+    do {
+        Py_BEGIN_ALLOW_THREADS
+#ifdef MS_WINDOWS
+        if (len > INT_MAX)
+            len = INT_MAX;
+        size = write(fd, data->buf, (int)len);
+#else
+        size = write(fd, data->buf, len);
+#endif
+        Py_END_ALLOW_THREADS
+    } while (size < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
+
     if (size < 0) {
-        posix_error();
+        if (!async_err)
+            posix_error();
         return -1;
     }
     return size;
@@ -11713,6 +11781,7 @@
 {
     int in, out;
     Py_ssize_t ret;
+    int async_err = 0;
     off_t offset;
 
 #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__)
@@ -11775,13 +11844,15 @@
         }
     }
 
-    Py_BEGIN_ALLOW_THREADS
+    do {
+        Py_BEGIN_ALLOW_THREADS
 #ifdef __APPLE__
-    ret = sendfile(in, out, offset, &sbytes, &sf, flags);
-#else
-    ret = sendfile(in, out, offset, len, &sf, &sbytes, flags);
-#endif
-    Py_END_ALLOW_THREADS
+        ret = sendfile(in, out, offset, &sbytes, &sf, flags);
+#else
+        ret = sendfile(in, out, offset, len, &sf, &sbytes, flags);
+#endif
+        Py_END_ALLOW_THREADS
+    } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
 
     if (sf.headers != NULL)
         iov_cleanup(sf.headers, hbuf, sf.hdr_cnt);
@@ -11800,7 +11871,7 @@
                 return posix_error();
             }
         }
-        return posix_error();
+        return (!async_err) ? posix_error() : NULL;
     }
     goto done;
 
@@ -11821,21 +11892,26 @@
         return NULL;
 #ifdef linux
     if (offobj == Py_None) {
+        do {
+            Py_BEGIN_ALLOW_THREADS
+            ret = sendfile(out, in, NULL, count);
+            Py_END_ALLOW_THREADS
+        } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
+        if (ret < 0)
+            return (!async_err) ? posix_error() : NULL;
+        return Py_BuildValue("n", ret);
+    }
+#endif
+    if (!Py_off_t_converter(offobj, &offset))
+        return NULL;
+
+    do {
         Py_BEGIN_ALLOW_THREADS
-        ret = sendfile(out, in, NULL, count);
+        ret = sendfile(out, in, &offset, count);
         Py_END_ALLOW_THREADS
-        if (ret < 0)
-            return posix_error();
-        return Py_BuildValue("n", ret);
-    }
-#endif
-    if (!Py_off_t_converter(offobj, &offset))
-        return NULL;
-    Py_BEGIN_ALLOW_THREADS
-    ret = sendfile(out, in, &offset, count);
-    Py_END_ALLOW_THREADS
+    } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
     if (ret < 0)
-        return posix_error();
+        return (!async_err) ? posix_error() : NULL;
     return Py_BuildValue("n", ret);
 #endif
 }
@@ -11891,15 +11967,18 @@
 {
     STRUCT_STAT st;
     int res;
-
-    Py_BEGIN_ALLOW_THREADS
-    res = FSTAT(fd, &st);
-    Py_END_ALLOW_THREADS
+    int async_err = 0;
+
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        res = FSTAT(fd, &st);
+        Py_END_ALLOW_THREADS
+    } while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
     if (res != 0) {
 #ifdef MS_WINDOWS
         return PyErr_SetFromWindowsErr(0);
 #else
-        return posix_error();
+        return (!async_err) ? posix_error() : NULL;
 #endif
     }
 
@@ -12185,6 +12264,7 @@
 {
     int cnt;
     Py_ssize_t result;
+    int async_err = 0;
     struct iovec *iov;
     Py_buffer *buf;
 
@@ -12199,12 +12279,14 @@
         return -1;
     }
 
-    Py_BEGIN_ALLOW_THREADS
-    result = writev(fd, iov, cnt);
-    Py_END_ALLOW_THREADS
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        result = writev(fd, iov, cnt);
+        Py_END_ALLOW_THREADS
+    } while (result < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
 
     iov_cleanup(iov, buf, cnt);
-    if (result < 0)
+    if (result < 0 && !async_err)
         posix_error();
 
     return result;
@@ -12275,17 +12357,20 @@
 /*[clinic end generated code: output=ec9cc5b2238e96a7 input=19903f1b3dd26377]*/
 {
     Py_ssize_t size;
+    int async_err = 0;
 
     if (!_PyVerify_fd(fd)) {
         posix_error();
         return -1;
     }
 
-    Py_BEGIN_ALLOW_THREADS
-    size = pwrite(fd, buffer->buf, (size_t)buffer->len, offset);
-    Py_END_ALLOW_THREADS
-
-    if (size < 0)
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        size = pwrite(fd, buffer->buf, (size_t)buffer->len, offset);
+        Py_END_ALLOW_THREADS
+    } while (size < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
+
+    if (size < 0 && !async_err)
         posix_error();
     return size;
 }
@@ -12353,18 +12438,21 @@
 /*[clinic end generated code: output=b3321927546893d0 input=73032e98a36e0e19]*/
 {
     int result;
-
-    Py_BEGIN_ALLOW_THREADS
+    int async_err = 0;
+
+    do {
+        Py_BEGIN_ALLOW_THREADS
 #ifdef HAVE_MKFIFOAT
-    if (dir_fd != DEFAULT_DIR_FD)
-        result = mkfifoat(dir_fd, path->narrow, mode);
-    else
-#endif
-        result = mkfifo(path->narrow, mode);
-    Py_END_ALLOW_THREADS
-
-    if (result < 0)
-        return posix_error();
+        if (dir_fd != DEFAULT_DIR_FD)
+            result = mkfifoat(dir_fd, path->narrow, mode);
+        else
+#endif
+            result = mkfifo(path->narrow, mode);
+        Py_END_ALLOW_THREADS
+    } while (result != 0 && errno == EINTR &&
+             !(async_err = PyErr_CheckSignals()));
+    if (result != 0)
+        return (!async_err) ? posix_error() : NULL;
 
     Py_RETURN_NONE;
 }
@@ -12448,18 +12536,21 @@
 /*[clinic end generated code: output=f71d54eaf9bb6f1a input=ee44531551a4d83b]*/
 {
     int result;
-
-    Py_BEGIN_ALLOW_THREADS
+    int async_err = 0;
+
+    do {
+        Py_BEGIN_ALLOW_THREADS
 #ifdef HAVE_MKNODAT
-    if (dir_fd != DEFAULT_DIR_FD)
-        result = mknodat(dir_fd, path->narrow, mode, device);
-    else
-#endif
-        result = mknod(path->narrow, mode, device);
-    Py_END_ALLOW_THREADS
-
-    if (result < 0)
-        return posix_error();
+        if (dir_fd != DEFAULT_DIR_FD)
+            result = mknodat(dir_fd, path->narrow, mode, device);
+        else
+#endif
+            result = mknod(path->narrow, mode, device);
+        Py_END_ALLOW_THREADS
+    } while (result != 0 && errno == EINTR &&
+             !(async_err = PyErr_CheckSignals()));
+    if (result != 0)
+        return (!async_err) ? posix_error() : NULL;
 
     Py_RETURN_NONE;
 }
@@ -12662,12 +12753,16 @@
 /*[clinic end generated code: output=62326766cb9b76bf input=63b43641e52818f2]*/
 {
     int result;
-
-    Py_BEGIN_ALLOW_THREADS
-    result = ftruncate(fd, length);
-    Py_END_ALLOW_THREADS
-    if (result < 0)
-        return posix_error();
+    int async_err = 0;
+
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        result = ftruncate(fd, length);
+        Py_END_ALLOW_THREADS
+    } while (result != 0 && errno == EINTR &&
+             !(async_err = PyErr_CheckSignals()));
+    if (result != 0)
+        return (!async_err) ? posix_error() : NULL;
     Py_RETURN_NONE;
 }
 #endif /* HAVE_FTRUNCATE */
@@ -12805,14 +12900,16 @@
 /*[clinic end generated code: output=0cd702d2065c79db input=d7a2ef0ab2ca52fb]*/
 {
     int result;
-
-    Py_BEGIN_ALLOW_THREADS
-    result = posix_fallocate(fd, offset, length);
-    Py_END_ALLOW_THREADS
-    if (result != 0) {
-        errno = result;
-        return posix_error();
-    }
+    int async_err = 0;
+
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        result = posix_fallocate(fd, offset, length);
+        Py_END_ALLOW_THREADS
+    } while (result != 0 && errno == EINTR &&
+             !(async_err = PyErr_CheckSignals()));
+    if (result != 0)
+        return (!async_err) ? posix_error() : NULL;
     Py_RETURN_NONE;
 }
 #endif /* HAVE_POSIX_FALLOCATE) && !POSIX_FADVISE_AIX_BUG */
@@ -12883,14 +12980,16 @@
 /*[clinic end generated code: output=dad93f32c04dd4f7 input=0fbe554edc2f04b5]*/
 {
     int result;
-
-    Py_BEGIN_ALLOW_THREADS
-    result = posix_fadvise(fd, offset, length, advice);
-    Py_END_ALLOW_THREADS
-    if (result != 0) {
-        errno = result;
-        return posix_error();
-    }
+    int async_err = 0;
+
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        result = posix_fadvise(fd, offset, length, advice);
+        Py_END_ALLOW_THREADS
+    } while (result != 0 && errno == EINTR &&
+             !(async_err = PyErr_CheckSignals()));
+    if (result != 0)
+        return (!async_err) ? posix_error() : NULL;
     Py_RETURN_NONE;
 }
 #endif /* HAVE_POSIX_FADVISE && !POSIX_FADVISE_AIX_BUG */
@@ -13745,13 +13844,17 @@
 /*[clinic end generated code: output=0e32bf07f946ec0d input=d8122243ac50975e]*/
 {
     int result;
+    int async_err = 0;
     struct statvfs st;
 
-    Py_BEGIN_ALLOW_THREADS
-    result = fstatvfs(fd, &st);
-    Py_END_ALLOW_THREADS
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        result = fstatvfs(fd, &st);
+        Py_END_ALLOW_THREADS
+    } while (result != 0 && errno == EINTR &&
+             !(async_err = PyErr_CheckSignals()));
     if (result != 0)
-        return posix_error();
+        return (!async_err) ? posix_error() : NULL;
 
     return _pystatvfs_fromstructstatvfs(st);
 }
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -2037,6 +2037,7 @@
     PyObject *addr = NULL;
     PyObject *res = NULL;
     int timeout;
+    int async_err = 0;
 #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
     /* accept4() is available on Linux 2.6.28+ and glibc 2.10 */
     static int accept4_works = -1;
@@ -2050,27 +2051,27 @@
         return select_error();
 
     BEGIN_SELECT_LOOP(s)
-
-    Py_BEGIN_ALLOW_THREADS
-    timeout = internal_select_ex(s, 0, interval);
-    if (!timeout) {
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        timeout = internal_select_ex(s, 0, interval);
+        if (!timeout) {
 #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
-        if (accept4_works != 0) {
-            newfd = accept4(s->sock_fd, SAS2SA(&addrbuf), &addrlen,
-                            SOCK_CLOEXEC);
-            if (newfd == INVALID_SOCKET && accept4_works == -1) {
-                /* On Linux older than 2.6.28, accept4() fails with ENOSYS */
-                accept4_works = (errno != ENOSYS);
+            if (accept4_works != 0) {
+                newfd = accept4(s->sock_fd, SAS2SA(&addrbuf), &addrlen,
+                                SOCK_CLOEXEC);
+                if (newfd == INVALID_SOCKET && accept4_works == -1) {
+                    /* On Linux older than 2.6.28, accept4() fails with ENOSYS */
+                    accept4_works = (errno != ENOSYS);
+                }
             }
+            if (accept4_works == 0)
+                newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
+#else
+            newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
+#endif
         }
-        if (accept4_works == 0)
-            newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
-#else
-        newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
-#endif
-    }
-    Py_END_ALLOW_THREADS
-
+        Py_END_ALLOW_THREADS
+    } while (newfd < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
     if (timeout == 1) {
         PyErr_SetString(socket_timeout, "timed out");
         return NULL;
@@ -2078,7 +2079,7 @@
     END_SELECT_LOOP(s)
 
     if (newfd == INVALID_SOCKET)
-        return s->errorhandler();
+        return (!async_err) ? s->errorhandler() : NULL;
 
 #ifdef MS_WINDOWS
     if (!SetHandleInformation((HANDLE)newfd, HANDLE_FLAG_INHERIT, 0)) {
@@ -2341,6 +2342,10 @@
 {
     SOCKET_T fd;
 
+    /* We do not want to retry upon EINTR: see http://lwn.net/Articles/576478/
+     * and http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
+     * for more details.
+     */
     if ((fd = s->sock_fd) != -1) {
         s->sock_fd = -1;
         Py_BEGIN_ALLOW_THREADS
@@ -2513,10 +2518,8 @@
 
     /* Signals are not errors (though they may raise exceptions).  Adapted
        from PyErr_SetFromErrnoWithFilenameObject(). */
-#ifdef EINTR
     if (res == EINTR && PyErr_CheckSignals())
         return NULL;
-#endif
 
     return PyLong_FromLong((long) res);
 }
@@ -2650,6 +2653,7 @@
 {
     Py_ssize_t outlen = -1;
     int timeout;
+    int async_err = 0;
 
     if (!IS_SELECTABLE(s)) {
         select_error();
@@ -2661,18 +2665,20 @@
     }
 
     BEGIN_SELECT_LOOP(s)
-    Py_BEGIN_ALLOW_THREADS
-    timeout = internal_select_ex(s, 0, interval);
-    if (!timeout) {
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        timeout = internal_select_ex(s, 0, interval);
+        if (!timeout) {
 #ifdef MS_WINDOWS
-        if (len > INT_MAX)
-            len = INT_MAX;
-        outlen = recv(s->sock_fd, cbuf, (int)len, flags);
+            if (len > INT_MAX)
+                len = INT_MAX;
+            outlen = recv(s->sock_fd, cbuf, (int)len, flags);
 #else
-        outlen = recv(s->sock_fd, cbuf, len, flags);
-#endif
-    }
-    Py_END_ALLOW_THREADS
+            outlen = recv(s->sock_fd, cbuf, len, flags);
+#endif
+        }
+        Py_END_ALLOW_THREADS
+    } while (outlen < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
 
     if (timeout == 1) {
         PyErr_SetString(socket_timeout, "timed out");
@@ -2682,7 +2688,8 @@
     if (outlen < 0) {
         /* Note: the call to errorhandler() ALWAYS indirectly returned
            NULL, so ignore its return value */
-        s->errorhandler();
+        if (!async_err)
+            s->errorhandler();
         return -1;
     }
     return outlen;
@@ -2819,6 +2826,7 @@
     int timeout;
     Py_ssize_t n = -1;
     socklen_t addrlen;
+    int async_err = 0;
 
     *addr = NULL;
 
@@ -2831,21 +2839,23 @@
     }
 
     BEGIN_SELECT_LOOP(s)
-    Py_BEGIN_ALLOW_THREADS
-    memset(&addrbuf, 0, addrlen);
-    timeout = internal_select_ex(s, 0, interval);
-    if (!timeout) {
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        memset(&addrbuf, 0, addrlen);
+        timeout = internal_select_ex(s, 0, interval);
+        if (!timeout) {
 #ifdef MS_WINDOWS
-        if (len > INT_MAX)
-            len = INT_MAX;
-        n = recvfrom(s->sock_fd, cbuf, (int)len, flags,
-                     (void *) &addrbuf, &addrlen);
+            if (len > INT_MAX)
+                len = INT_MAX;
+            n = recvfrom(s->sock_fd, cbuf, (int)len, flags,
+                         (void *) &addrbuf, &addrlen);
 #else
-        n = recvfrom(s->sock_fd, cbuf, len, flags,
-                     SAS2SA(&addrbuf), &addrlen);
-#endif
-    }
-    Py_END_ALLOW_THREADS
+            n = recvfrom(s->sock_fd, cbuf, len, flags,
+                         SAS2SA(&addrbuf), &addrlen);
+#endif
+        }
+        Py_END_ALLOW_THREADS
+    } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
 
     if (timeout == 1) {
         PyErr_SetString(socket_timeout, "timed out");
@@ -2853,7 +2863,8 @@
     }
     END_SELECT_LOOP(s)
     if (n < 0) {
-        s->errorhandler();
+        if (!async_err)
+            s->errorhandler();
         return -1;
     }
 
@@ -2993,6 +3004,7 @@
 {
     ssize_t bytes_received = -1;
     int timeout;
+    int async_err = 0;
     sock_addr_t addrbuf;
     socklen_t addrbuflen;
     struct msghdr msg = {0};
@@ -3028,25 +3040,29 @@
     }
 
     BEGIN_SELECT_LOOP(s)
-    Py_BEGIN_ALLOW_THREADS;
-    msg.msg_name = SAS2SA(&addrbuf);
-    msg.msg_namelen = addrbuflen;
-    msg.msg_iov = iov;
-    msg.msg_iovlen = iovlen;
-    msg.msg_control = controlbuf;
-    msg.msg_controllen = controllen;
-    timeout = internal_select_ex(s, 0, interval);
-    if (!timeout)
-        bytes_received = recvmsg(s->sock_fd, &msg, flags);
-    Py_END_ALLOW_THREADS;
-    if (timeout == 1) {
-        PyErr_SetString(socket_timeout, "timed out");
-        goto finally;
-    }
+    do {
+        Py_BEGIN_ALLOW_THREADS;
+        msg.msg_name = SAS2SA(&addrbuf);
+        msg.msg_namelen = addrbuflen;
+        msg.msg_iov = iov;
+        msg.msg_iovlen = iovlen;
+        msg.msg_control = controlbuf;
+        msg.msg_controllen = controllen;
+        timeout = internal_select_ex(s, 0, interval);
+        if (!timeout)
+            bytes_received = recvmsg(s->sock_fd, &msg, flags);
+        Py_END_ALLOW_THREADS;
+        if (timeout == 1) {
+            PyErr_SetString(socket_timeout, "timed out");
+            goto finally;
+        }
+    } while (bytes_received < 0 && errno == EINTR &&
+             !(async_err = PyErr_CheckSignals()));
     END_SELECT_LOOP(s)
 
     if (bytes_received < 0) {
-        s->errorhandler();
+        if (!async_err)
+            s->errorhandler();
         goto finally;
     }
 
@@ -3305,6 +3321,7 @@
 {
     char *buf;
     Py_ssize_t len, n = -1;
+    int async_err = 0;
     int flags = 0, timeout;
     Py_buffer pbuf;
 
@@ -3319,18 +3336,20 @@
     len = pbuf.len;
 
     BEGIN_SELECT_LOOP(s)
-    Py_BEGIN_ALLOW_THREADS
-    timeout = internal_select_ex(s, 1, interval);
-    if (!timeout) {
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        timeout = internal_select_ex(s, 1, interval);
+        if (!timeout) {
 #ifdef MS_WINDOWS
-        if (len > INT_MAX)
-            len = INT_MAX;
-        n = send(s->sock_fd, buf, (int)len, flags);
+            if (len > INT_MAX)
+                len = INT_MAX;
+            n = send(s->sock_fd, buf, (int)len, flags);
 #else
-        n = send(s->sock_fd, buf, len, flags);
-#endif
-    }
-    Py_END_ALLOW_THREADS
+            n = send(s->sock_fd, buf, len, flags);
+#endif
+        }
+        Py_END_ALLOW_THREADS
+    } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
     if (timeout == 1) {
         PyBuffer_Release(&pbuf);
         PyErr_SetString(socket_timeout, "timed out");
@@ -3340,7 +3359,7 @@
 
     PyBuffer_Release(&pbuf);
     if (n < 0)
-        return s->errorhandler();
+        return (!async_err) ? s->errorhandler() : NULL;
     return PyLong_FromSsize_t(n);
 }
 
@@ -3359,7 +3378,8 @@
 {
     char *buf;
     Py_ssize_t len, n = -1;
-    int flags = 0, timeout, saved_errno;
+    int async_err = 0;
+    int flags = 0, timeout;
     Py_buffer pbuf;
 
     if (!PyArg_ParseTuple(args, "y*|i:sendall", &pbuf, &flags))
@@ -3391,29 +3411,16 @@
             PyErr_SetString(socket_timeout, "timed out");
             return NULL;
         }
-        /* PyErr_CheckSignals() might change errno */
-        saved_errno = errno;
-        /* We must run our signal handlers before looping again.
-           send() can return a successful partial write when it is
-           interrupted, so we can't restrict ourselves to EINTR. */
-        if (PyErr_CheckSignals()) {
-            PyBuffer_Release(&pbuf);
-            return NULL;
+        if (n >= 0) {
+            buf += n;
+            len -= n;
         }
-        if (n < 0) {
-            /* If interrupted, try again */
-            if (saved_errno == EINTR)
-                continue;
-            else
-                break;
-        }
-        buf += n;
-        len -= n;
-    } while (len > 0);
+    } while (len > 0 && (n >= 0 || errno == EINTR) &&
+             !(async_err = PyErr_CheckSignals()));
     PyBuffer_Release(&pbuf);
 
-    if (n < 0)
-        return s->errorhandler();
+    if (n < 0 || async_err)
+        return (!async_err) ? s->errorhandler() : NULL;
 
     Py_INCREF(Py_None);
     return Py_None;
@@ -3439,6 +3446,7 @@
     Py_ssize_t len, arglen;
     sock_addr_t addrbuf;
     int addrlen, n = -1, flags, timeout;
+    int async_err = 0;
 
     flags = 0;
     arglen = PyTuple_Size(args);
@@ -3473,20 +3481,22 @@
     }
 
     BEGIN_SELECT_LOOP(s)
-    Py_BEGIN_ALLOW_THREADS
-    timeout = internal_select_ex(s, 1, interval);
-    if (!timeout) {
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        timeout = internal_select_ex(s, 1, interval);
+        if (!timeout) {
 #ifdef MS_WINDOWS
-        if (len > INT_MAX)
-            len = INT_MAX;
-        n = sendto(s->sock_fd, buf, (int)len, flags,
-                   SAS2SA(&addrbuf), addrlen);
+            if (len > INT_MAX)
+                len = INT_MAX;
+            n = sendto(s->sock_fd, buf, (int)len, flags,
+                       SAS2SA(&addrbuf), addrlen);
 #else
-        n = sendto(s->sock_fd, buf, len, flags,
-                   SAS2SA(&addrbuf), addrlen);
-#endif
-    }
-    Py_END_ALLOW_THREADS
+            n = sendto(s->sock_fd, buf, len, flags,
+                       SAS2SA(&addrbuf), addrlen);
+#endif
+        }
+        Py_END_ALLOW_THREADS
+    } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
 
     if (timeout == 1) {
         PyBuffer_Release(&pbuf);
@@ -3496,7 +3506,7 @@
     END_SELECT_LOOP(s)
     PyBuffer_Release(&pbuf);
     if (n < 0)
-        return s->errorhandler();
+        return (!async_err) ? s->errorhandler() : NULL;
     return PyLong_FromSsize_t(n);
 }
 
@@ -3528,6 +3538,7 @@
     void *controlbuf = NULL;
     size_t controllen, controllen_last;
     ssize_t bytes_sent = -1;
+    int async_err = 0;
     int addrlen, timeout, flags = 0;
     PyObject *data_arg, *cmsg_arg = NULL, *addr_arg = NULL, *data_fast = NULL,
         *cmsg_fast = NULL, *retval = NULL;
@@ -3685,19 +3696,23 @@
     }
 
     BEGIN_SELECT_LOOP(s)
-    Py_BEGIN_ALLOW_THREADS;
-    timeout = internal_select_ex(s, 1, interval);
-    if (!timeout)
-        bytes_sent = sendmsg(s->sock_fd, &msg, flags);
-    Py_END_ALLOW_THREADS;
-    if (timeout == 1) {
-        PyErr_SetString(socket_timeout, "timed out");
-        goto finally;
-    }
+    do {
+        Py_BEGIN_ALLOW_THREADS;
+        timeout = internal_select_ex(s, 1, interval);
+        if (!timeout)
+            bytes_sent = sendmsg(s->sock_fd, &msg, flags);
+        Py_END_ALLOW_THREADS;
+        if (timeout == 1) {
+            PyErr_SetString(socket_timeout, "timed out");
+            goto finally;
+        }
+    } while (bytes_sent < 0 && errno == EINTR &&
+             !(async_err = PyErr_CheckSignals()));
     END_SELECT_LOOP(s)
 
     if (bytes_sent < 0) {
-        s->errorhandler();
+        if (!async_err)
+            s->errorhandler();
         goto finally;
     }
     retval = PyLong_FromSsize_t(bytes_sent);

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


More information about the Python-checkins mailing list