[issue23285] PEP 475 - EINTR handling

Charles-François Natali report at bugs.python.org
Thu Jan 29 23:49:06 CET 2015


Charles-François Natali added the comment:

> With eintr-2.diff, fast!:

Victory \°/.

> Instrumented test_send, 3 socket.send calls, many socket.recv_into calls:

Yep, that's expected.
I think we should keep the default socket buffer size: it increases
the test coverage, and it's probably not worth trying to increase it
to make the test a bit faster, especially since it spends so much time
sleeping.

Antoine, I'm now happy with the patch, so we'll be waiting for your
decision on the PEP with Victor :-).

----------
Added file: http://bugs.python.org/file37910/eintr-3.diff

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue23285>
_______________________________________
-------------- next part --------------
diff -r fe0fddd6fd21 Lib/_pyio.py
--- a/Lib/_pyio.py	Sun Jan 18 11:17:39 2015 +0200
+++ b/Lib/_pyio.py	Thu Jan 29 22:20:01 2015 +0000
@@ -1006,10 +1006,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
@@ -1028,10 +1025,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
@@ -1060,12 +1054,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
@@ -1214,8 +1203,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 -r fe0fddd6fd21 Lib/distutils/spawn.py
--- a/Lib/distutils/spawn.py	Sun Jan 18 11:17:39 2015 +0200
+++ b/Lib/distutils/spawn.py	Thu Jan 29 22:20:01 2015 +0000
@@ -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 -r fe0fddd6fd21 Lib/multiprocessing/connection.py
--- a/Lib/multiprocessing/connection.py	Sun Jan 18 11:17:39 2015 +0200
+++ b/Lib/multiprocessing/connection.py	Thu Jan 29 22:20:01 2015 +0000
@@ -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 -r fe0fddd6fd21 Lib/multiprocessing/forkserver.py
--- a/Lib/multiprocessing/forkserver.py	Sun Jan 18 11:17:39 2015 +0200
+++ b/Lib/multiprocessing/forkserver.py	Thu Jan 29 22:20:01 2015 +0000
@@ -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 -r fe0fddd6fd21 Lib/multiprocessing/popen_fork.py
--- a/Lib/multiprocessing/popen_fork.py	Sun Jan 18 11:17:39 2015 +0200
+++ b/Lib/multiprocessing/popen_fork.py	Thu Jan 29 22:20:01 2015 +0000
@@ -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 -r fe0fddd6fd21 Lib/socket.py
--- a/Lib/socket.py	Sun Jan 18 11:17:39 2015 +0200
+++ b/Lib/socket.py	Thu Jan 29 22:20:01 2015 +0000
@@ -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 -r fe0fddd6fd21 Lib/socketserver.py
--- a/Lib/socketserver.py	Sun Jan 18 11:17:39 2015 +0200
+++ b/Lib/socketserver.py	Thu Jan 29 22:20:01 2015 +0000
@@ -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 -r fe0fddd6fd21 Lib/subprocess.py
--- a/Lib/subprocess.py	Sun Jan 18 11:17:39 2015 +0200
+++ b/Lib/subprocess.py	Thu Jan 29 22:20:01 2015 +0000
@@ -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 -r fe0fddd6fd21 Lib/test/eintrdata/eintr_tester.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/test/eintrdata/eintr_tester.py	Thu Jan 29 22:20:01 2015 +0000
@@ -0,0 +1,234 @@
+"""
+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()
+
+
+def test_main():
+    support.run_unittest(OSEINTRTest, SocketEINTRTest)
+
+
+if __name__ == "__main__":
+    test_main()
diff -r fe0fddd6fd21 Lib/test/test_eintr.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/test/test_eintr.py	Thu Jan 29 22:20:01 2015 +0000
@@ -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 -r fe0fddd6fd21 Lib/test/test_signal.py
--- a/Lib/test/test_signal.py	Sun Jan 18 11:17:39 2015 +0200
+++ b/Lib/test/test_signal.py	Thu Jan 29 22:20:01 2015 +0000
@@ -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 -r fe0fddd6fd21 Lib/test/test_socket.py
--- a/Lib/test/test_socket.py	Sun Jan 18 11:17:39 2015 +0200
+++ b/Lib/test/test_socket.py	Thu Jan 29 22:20:01 2015 +0000
@@ -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 -r fe0fddd6fd21 Lib/test/test_subprocess.py
--- a/Lib/test/test_subprocess.py	Sun Jan 18 11:17:39 2015 +0200
+++ b/Lib/test/test_subprocess.py	Thu Jan 29 22:20:01 2015 +0000
@@ -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 -r fe0fddd6fd21 Modules/_io/fileio.c
--- a/Modules/_io/fileio.c	Sun Jan 18 11:17:39 2015 +0200
+++ b/Modules/_io/fileio.c	Thu Jan 29 22:20:01 2015 +0000
@@ -550,7 +550,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 +562,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 +583,8 @@
         if (err == EAGAIN)
             Py_RETURN_NONE;
         errno = err;
-        PyErr_SetFromErrno(PyExc_IOError);
+        if (!async_err)
+            PyErr_SetFromErrno(PyExc_IOError);
         return NULL;
     }
 
@@ -627,6 +631,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 +678,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 +702,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 +725,7 @@
     char *ptr;
     Py_ssize_t n;
     Py_ssize_t size = -1;
+    int async_err = 0;
     PyObject *bytes;
 
     if (self->fd < 0)
@@ -747,14 +750,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 +770,8 @@
         if (err == EAGAIN)
             Py_RETURN_NONE;
         errno = err;
-        PyErr_SetFromErrno(PyExc_IOError);
+        if (!async_err)
+            PyErr_SetFromErrno(PyExc_IOError);
         return NULL;
     }
 
@@ -783,7 +790,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 +801,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 +831,8 @@
         if (err == EAGAIN)
             Py_RETURN_NONE;
         errno = err;
-        PyErr_SetFromErrno(PyExc_IOError);
+        if (!async_err)
+            PyErr_SetFromErrno(PyExc_IOError);
         return NULL;
     }
 
diff -r fe0fddd6fd21 Modules/posixmodule.c
--- a/Modules/posixmodule.c	Sun Jan 18 11:17:39 2015 +0200
+++ b/Modules/posixmodule.c	Thu Jan 29 22:20:01 2015 +0000
@@ -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));
 }
@@ -11080,6 +11118,7 @@
 /*[clinic end generated code: output=531e482dd11a99a0 input=76e96f511be0352f]*/
 {
     int res;
+    int async_err = 0;
 #if defined(HAVE_DUP3) && \
     !(defined(HAVE_FCNTL_H) && defined(F_DUP2FD_CLOEXEC))
     /* dup3() is available on Linux 2.6.27+ and glibc 2.9 */
@@ -11103,38 +11142,46 @@
     }
 
 #elif defined(HAVE_FCNTL_H) && defined(F_DUP2FD_CLOEXEC)
-    Py_BEGIN_ALLOW_THREADS
-    if (!inheritable)
-        res = fcntl(fd, F_DUP2FD_CLOEXEC, fd2);
-    else
-        res = dup2(fd, fd2);
-    Py_END_ALLOW_THREADS
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        if (!inheritable)
+            res = fcntl(fd, F_DUP2FD_CLOEXEC, fd2);
+        else
+            res = dup2(fd, fd2);
+         Py_END_ALLOW_THREADS
+    } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
     if (res < 0)
-        return posix_error();
+        return (!async_err) ? posix_error() : NULL;
 
 #else
 
 #ifdef HAVE_DUP3
     if (!inheritable && dup3_works != 0) {
-        Py_BEGIN_ALLOW_THREADS
-        res = dup3(fd, fd2, O_CLOEXEC);
-        Py_END_ALLOW_THREADS
+        do {
+            Py_BEGIN_ALLOW_THREADS
+            res = dup3(fd, fd2, O_CLOEXEC);
+            Py_END_ALLOW_THREADS
+        } while (res < 0 && errno == EINTR &&
+                 !(async_err = PyErr_CheckSignals()));
         if (res < 0) {
             if (dup3_works == -1)
                 dup3_works = (errno != ENOSYS);
             if (dup3_works)
-                return posix_error();
+                return (!async_err) ? posix_error() : NULL;
         }
     }
 
     if (inheritable || dup3_works == 0)
     {
 #endif
-        Py_BEGIN_ALLOW_THREADS
-        res = dup2(fd, fd2);
-        Py_END_ALLOW_THREADS
+        do {
+            Py_BEGIN_ALLOW_THREADS
+            res = dup2(fd, fd2);
+            Py_END_ALLOW_THREADS
+        } while (res < 0 && errno == EINTR &&
+                 !(async_err = PyErr_CheckSignals()));
         if (res < 0)
-            return posix_error();
+            return (!async_err) ? posix_error() : NULL;
 
         if (!inheritable && _Py_set_inheritable(fd2, 0, NULL) < 0) {
             close(fd2);
@@ -11355,6 +11402,7 @@
 /*[clinic end generated code: output=1f3bc27260a24968 input=1df2eaa27c0bf1d3]*/
 {
     Py_ssize_t n;
+    int async_err = 0;
     PyObject *buffer;
 
     if (length < 0) {
@@ -11375,13 +11423,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 +11566,7 @@
 {
     int cnt;
     Py_ssize_t n;
+    int async_err = 0;
     struct iovec *iov;
     Py_buffer *buf;
 
@@ -11529,13 +11581,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 +11653,7 @@
 /*[clinic end generated code: output=7b62bf6c06e20ae8 input=084948dcbaa35d4c]*/
 {
     Py_ssize_t n;
+    int async_err = 0;
     PyObject *buffer;
 
     if (length < 0) {
@@ -11611,12 +11667,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 +11737,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 +11745,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 +11778,7 @@
 {
     int in, out;
     Py_ssize_t ret;
+    int async_err = 0;
     off_t offset;
 
 #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__)
@@ -11775,13 +11841,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 +11868,7 @@
                 return posix_error();
             }
         }
-        return posix_error();
+        return (!async_err) ? posix_error() : NULL;
     }
     goto done;
 
@@ -11821,21 +11889,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 +11964,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 +12261,7 @@
 {
     int cnt;
     Py_ssize_t result;
+    int async_err = 0;
     struct iovec *iov;
     Py_buffer *buf;
 
@@ -12199,12 +12276,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 +12354,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 +12435,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 +12533,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 +12750,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 +12897,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 +12977,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 +13841,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 -r fe0fddd6fd21 Modules/socketmodule.c
--- a/Modules/socketmodule.c	Sun Jan 18 11:17:39 2015 +0200
+++ b/Modules/socketmodule.c	Thu Jan 29 22:20:01 2015 +0000
@@ -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)) {
@@ -2513,10 +2514,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 +2649,7 @@
 {
     Py_ssize_t outlen = -1;
     int timeout;
+    int async_err = 0;
 
     if (!IS_SELECTABLE(s)) {
         select_error();
@@ -2661,18 +2661,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 +2684,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 +2822,7 @@
     int timeout;
     Py_ssize_t n = -1;
     socklen_t addrlen;
+    int async_err = 0;
 
     *addr = NULL;
 
@@ -2831,21 +2835,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 +2859,8 @@
     }
     END_SELECT_LOOP(s)
     if (n < 0) {
-        s->errorhandler();
+        if (!async_err)
+            s->errorhandler();
         return -1;
     }
 
@@ -2993,6 +3000,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 +3036,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 +3317,7 @@
 {
     char *buf;
     Py_ssize_t len, n = -1;
+    int async_err = 0;
     int flags = 0, timeout;
     Py_buffer pbuf;
 
@@ -3319,18 +3332,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 +3355,7 @@
 
     PyBuffer_Release(&pbuf);
     if (n < 0)
-        return s->errorhandler();
+        return (!async_err) ? s->errorhandler() : NULL;
     return PyLong_FromSsize_t(n);
 }
 
@@ -3359,7 +3374,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 +3407,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 +3442,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 +3477,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 +3502,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 +3534,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 +3692,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);


More information about the Python-bugs-list mailing list