[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

AltStyle によって変換されたページ (->オリジナル) /