Issue #23285: PEP 475 -- Retry system calls failing with EINTR.

This commit is contained in:
Charles-François Natali 2015-02-07 13:27:50 +00:00
parent d005090e01
commit 6e6c59b508
18 changed files with 753 additions and 522 deletions

View File

@ -111,6 +111,16 @@ Please read on for a comprehensive list of user-facing changes.
PEP written by Carl Meyer 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 Other Language Changes

View File

@ -1012,10 +1012,7 @@ class BufferedReader(_BufferedIOMixin):
current_size = 0 current_size = 0
while True: while True:
# Read until EOF or until read() would block. # Read until EOF or until read() would block.
try: chunk = self.raw.read()
chunk = self.raw.read()
except InterruptedError:
continue
if chunk in empty_values: if chunk in empty_values:
nodata_val = chunk nodata_val = chunk
break break
@ -1034,10 +1031,7 @@ class BufferedReader(_BufferedIOMixin):
chunks = [buf[pos:]] chunks = [buf[pos:]]
wanted = max(self.buffer_size, n) wanted = max(self.buffer_size, n)
while avail < n: while avail < n:
try: chunk = self.raw.read(wanted)
chunk = self.raw.read(wanted)
except InterruptedError:
continue
if chunk in empty_values: if chunk in empty_values:
nodata_val = chunk nodata_val = chunk
break break
@ -1066,12 +1060,7 @@ class BufferedReader(_BufferedIOMixin):
have = len(self._read_buf) - self._read_pos have = len(self._read_buf) - self._read_pos
if have < want or have <= 0: if have < want or have <= 0:
to_read = self.buffer_size - have to_read = self.buffer_size - have
while True: current = self.raw.read(to_read)
try:
current = self.raw.read(to_read)
except InterruptedError:
continue
break
if current: if current:
self._read_buf = self._read_buf[self._read_pos:] + current self._read_buf = self._read_buf[self._read_pos:] + current
self._read_pos = 0 self._read_pos = 0
@ -1220,8 +1209,6 @@ class BufferedWriter(_BufferedIOMixin):
while self._write_buf: while self._write_buf:
try: try:
n = self.raw.write(self._write_buf) n = self.raw.write(self._write_buf)
except InterruptedError:
continue
except BlockingIOError: except BlockingIOError:
raise RuntimeError("self.raw should implement RawIOBase: it " raise RuntimeError("self.raw should implement RawIOBase: it "
"should not raise BlockingIOError") "should not raise BlockingIOError")

View File

@ -137,9 +137,6 @@ def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0):
try: try:
pid, status = os.waitpid(pid, 0) pid, status = os.waitpid(pid, 0)
except OSError as exc: except OSError as exc:
import errno
if exc.errno == errno.EINTR:
continue
if not DEBUG: if not DEBUG:
cmd = executable cmd = executable
raise DistutilsExecError( raise DistutilsExecError(

View File

@ -365,10 +365,7 @@ class Connection(_ConnectionBase):
def _send(self, buf, write=_write): def _send(self, buf, write=_write):
remaining = len(buf) remaining = len(buf)
while True: while True:
try: n = write(self._handle, buf)
n = write(self._handle, buf)
except InterruptedError:
continue
remaining -= n remaining -= n
if remaining == 0: if remaining == 0:
break break
@ -379,10 +376,7 @@ class Connection(_ConnectionBase):
handle = self._handle handle = self._handle
remaining = size remaining = size
while remaining > 0: while remaining > 0:
try: chunk = read(handle, remaining)
chunk = read(handle, remaining)
except InterruptedError:
continue
n = len(chunk) n = len(chunk)
if n == 0: if n == 0:
if remaining == size: if remaining == size:
@ -595,13 +589,7 @@ class SocketListener(object):
self._unlink = None self._unlink = None
def accept(self): def accept(self):
while True: s, self._last_accepted = self._socket.accept()
try:
s, self._last_accepted = self._socket.accept()
except InterruptedError:
pass
else:
break
s.setblocking(True) s.setblocking(True)
return Connection(s.detach()) return Connection(s.detach())

View File

@ -188,8 +188,6 @@ def main(listener_fd, alive_r, preload, main_path=None, sys_path=None):
finally: finally:
os._exit(code) os._exit(code)
except InterruptedError:
pass
except OSError as e: except OSError as e:
if e.errno != errno.ECONNABORTED: if e.errno != errno.ECONNABORTED:
raise raise
@ -230,13 +228,7 @@ def read_unsigned(fd):
data = b'' data = b''
length = UNSIGNED_STRUCT.size length = UNSIGNED_STRUCT.size
while len(data) < length: while len(data) < length:
while True: s = os.read(fd, length - len(data))
try:
s = os.read(fd, length - len(data))
except InterruptedError:
pass
else:
break
if not s: if not s:
raise EOFError('unexpected EOF') raise EOFError('unexpected EOF')
data += s data += s
@ -245,13 +237,7 @@ def read_unsigned(fd):
def write_unsigned(fd, n): def write_unsigned(fd, n):
msg = UNSIGNED_STRUCT.pack(n) msg = UNSIGNED_STRUCT.pack(n)
while msg: while msg:
while True: nbytes = os.write(fd, msg)
try:
nbytes = os.write(fd, msg)
except InterruptedError:
pass
else:
break
if nbytes == 0: if nbytes == 0:
raise RuntimeError('should not get here') raise RuntimeError('should not get here')
msg = msg[nbytes:] msg = msg[nbytes:]

View File

@ -1,7 +1,6 @@
import os import os
import sys import sys
import signal import signal
import errno
from . import util from . import util
@ -29,8 +28,6 @@ class Popen(object):
try: try:
pid, sts = os.waitpid(self.pid, flag) pid, sts = os.waitpid(self.pid, flag)
except OSError as e: except OSError as e:
if e.errno == errno.EINTR:
continue
# Child process not yet created. See #1731717 # Child process not yet created. See #1731717
# e.errno == errno.ECHILD == 10 # e.errno == errno.ECHILD == 10
return None return None

View File

@ -572,8 +572,6 @@ class SocketIO(io.RawIOBase):
except timeout: except timeout:
self._timeout_occurred = True self._timeout_occurred = True
raise raise
except InterruptedError:
continue
except error as e: except error as e:
if e.args[0] in _blocking_errnos: if e.args[0] in _blocking_errnos:
return None return None

View File

@ -553,8 +553,6 @@ class ForkingMixIn:
try: try:
pid, _ = os.waitpid(-1, 0) pid, _ = os.waitpid(-1, 0)
self.active_children.discard(pid) self.active_children.discard(pid)
except InterruptedError:
pass
except ChildProcessError: except ChildProcessError:
# we don't have any children, we're done # we don't have any children, we're done
self.active_children.clear() self.active_children.clear()

View File

@ -489,14 +489,6 @@ STDOUT = -2
DEVNULL = -3 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, # 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 # but it's here so that it can be imported when Python is compiled without
# threads. # threads.
@ -963,10 +955,10 @@ class Popen(object):
if self.stdin: if self.stdin:
self._stdin_write(input) self._stdin_write(input)
elif self.stdout: elif self.stdout:
stdout = _eintr_retry_call(self.stdout.read) stdout = self.stdout.read()
self.stdout.close() self.stdout.close()
elif self.stderr: elif self.stderr:
stderr = _eintr_retry_call(self.stderr.read) stderr = self.stderr.read()
self.stderr.close() self.stderr.close()
self.wait() self.wait()
else: else:
@ -1410,7 +1402,7 @@ class Popen(object):
# exception (limited in size) # exception (limited in size)
errpipe_data = bytearray() errpipe_data = bytearray()
while True: while True:
part = _eintr_retry_call(os.read, errpipe_read, 50000) part = os.read(errpipe_read, 50000)
errpipe_data += part errpipe_data += part
if not part or len(errpipe_data) > 50000: if not part or len(errpipe_data) > 50000:
break break
@ -1420,7 +1412,7 @@ class Popen(object):
if errpipe_data: if errpipe_data:
try: try:
_eintr_retry_call(os.waitpid, self.pid, 0) os.waitpid(self.pid, 0)
except ChildProcessError: except ChildProcessError:
pass pass
try: try:
@ -1505,7 +1497,7 @@ class Popen(object):
def _try_wait(self, wait_flags): def _try_wait(self, wait_flags):
"""All callers to this function MUST hold self._waitpid_lock.""" """All callers to this function MUST hold self._waitpid_lock."""
try: try:
(pid, sts) = _eintr_retry_call(os.waitpid, self.pid, wait_flags) (pid, sts) = os.waitpid(self.pid, wait_flags)
except ChildProcessError: except ChildProcessError:
# This happens if SIGCLD is set to be ignored or waiting # This happens if SIGCLD is set to be ignored or waiting
# for child processes has otherwise been disabled for our # for child processes has otherwise been disabled for our

View File

@ -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
@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)
@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])
@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()

20
Lib/test/test_eintr.py Normal file
View File

@ -0,0 +1,20 @@
import os
import signal
import unittest
from test import script_helper, support
@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()

View File

@ -587,7 +587,7 @@ class SiginterruptTest(unittest.TestCase):
r, w = os.pipe() r, w = os.pipe()
def handler(signum, frame): def handler(signum, frame):
pass 1 / 0
signal.signal(signal.SIGALRM, handler) signal.signal(signal.SIGALRM, handler)
if interrupt is not None: if interrupt is not None:
@ -604,9 +604,8 @@ class SiginterruptTest(unittest.TestCase):
try: try:
# blocking call: read from a pipe without data # blocking call: read from a pipe without data
os.read(r, 1) os.read(r, 1)
except OSError as err: except ZeroDivisionError:
if err.errno != errno.EINTR: pass
raise
else: else:
sys.exit(2) sys.exit(2)
sys.exit(3) sys.exit(3)

View File

@ -3590,7 +3590,7 @@ class InterruptedTimeoutBase(unittest.TestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
orig_alrm_handler = signal.signal(signal.SIGALRM, 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(signal.signal, signal.SIGALRM, orig_alrm_handler)
self.addCleanup(self.setAlarm, 0) self.addCleanup(self.setAlarm, 0)
@ -3627,13 +3627,11 @@ class InterruptedRecvTimeoutTest(InterruptedTimeoutBase, UDPTestBase):
self.serv.settimeout(self.timeout) self.serv.settimeout(self.timeout)
def checkInterruptedRecv(self, func, *args, **kwargs): 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. # errno of EINTR when interrupted by a signal.
self.setAlarm(self.alarm_time) self.setAlarm(self.alarm_time)
with self.assertRaises(OSError) as cm: with self.assertRaises(ZeroDivisionError) as cm:
func(*args, **kwargs) func(*args, **kwargs)
self.assertNotIsInstance(cm.exception, socket.timeout)
self.assertEqual(cm.exception.errno, errno.EINTR)
def testInterruptedRecvTimeout(self): def testInterruptedRecvTimeout(self):
self.checkInterruptedRecv(self.serv.recv, 1024) self.checkInterruptedRecv(self.serv.recv, 1024)
@ -3689,12 +3687,10 @@ class InterruptedSendTimeoutTest(InterruptedTimeoutBase,
# Check that func(*args, **kwargs), run in a loop, raises # Check that func(*args, **kwargs), run in a loop, raises
# OSError with an errno of EINTR when interrupted by a # OSError with an errno of EINTR when interrupted by a
# signal. # signal.
with self.assertRaises(OSError) as cm: with self.assertRaises(ZeroDivisionError) as cm:
while True: while True:
self.setAlarm(self.alarm_time) self.setAlarm(self.alarm_time)
func(*args, **kwargs) 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 # Issue #12958: The following tests have problems on OS X prior to 10.7
@support.requires_mac_ver(10, 7) @support.requires_mac_ver(10, 7)
@ -4062,117 +4058,6 @@ class FileObjectClassTestCase(SocketConnectedTest):
pass 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): class UnbufferedFileObjectClassTestCase(FileObjectClassTestCase):
"""Repeat the tests from FileObjectClassTestCase with bufsize==0. """Repeat the tests from FileObjectClassTestCase with bufsize==0.
@ -5388,7 +5273,6 @@ def test_main():
tests.extend([ tests.extend([
NonBlockingTCPTests, NonBlockingTCPTests,
FileObjectClassTestCase, FileObjectClassTestCase,
FileObjectInterruptedTestCase,
UnbufferedFileObjectClassTestCase, UnbufferedFileObjectClassTestCase,
LineBufferedFileObjectClassTestCase, LineBufferedFileObjectClassTestCase,
SmallBufferedFileObjectClassTestCase, SmallBufferedFileObjectClassTestCase,

View File

@ -2421,25 +2421,6 @@ class ProcessTestCaseNoPoll(ProcessTestCase):
ProcessTestCase.tearDown(self) 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") @unittest.skipUnless(mswindows, "Windows-specific tests")
class CommandsWithSpaces (BaseTestCase): class CommandsWithSpaces (BaseTestCase):
@ -2528,7 +2509,6 @@ def test_main():
Win32ProcessTestCase, Win32ProcessTestCase,
CommandTests, CommandTests,
ProcessTestCaseNoPoll, ProcessTestCaseNoPoll,
HelperFunctionTests,
CommandsWithSpaces, CommandsWithSpaces,
ContextManagerTests, ContextManagerTests,
) )

View File

@ -10,6 +10,8 @@ Release date: TBA
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #23285: PEP 475 - EINTR handling.
- Issue #22735: Fix many edge cases (including crashes) involving custom mro() - Issue #22735: Fix many edge cases (including crashes) involving custom mro()
implementations. implementations.

View File

@ -218,6 +218,7 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
#ifdef HAVE_FSTAT #ifdef HAVE_FSTAT
struct stat fdfstat; struct stat fdfstat;
#endif #endif
int async_err = 0;
assert(PyFileIO_Check(oself)); assert(PyFileIO_Check(oself));
if (self->fd >= 0) { if (self->fd >= 0) {
@ -360,15 +361,18 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
errno = 0; errno = 0;
if (opener == Py_None) { if (opener == Py_None) {
Py_BEGIN_ALLOW_THREADS do {
Py_BEGIN_ALLOW_THREADS
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (widename != NULL) if (widename != NULL)
self->fd = _wopen(widename, flags, 0666); self->fd = _wopen(widename, flags, 0666);
else else
#endif #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 { else {
PyObject *fdobj; PyObject *fdobj;
@ -397,7 +401,8 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
fd_is_own = 1; fd_is_own = 1;
if (self->fd < 0) { if (self->fd < 0) {
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj); if (!async_err)
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj);
goto error; goto error;
} }
@ -550,7 +555,7 @@ fileio_readinto(fileio *self, PyObject *args)
{ {
Py_buffer pbuf; Py_buffer pbuf;
Py_ssize_t n, len; Py_ssize_t n, len;
int err; int err, async_err = 0;
if (self->fd < 0) if (self->fd < 0)
return err_closed(); return err_closed();
@ -562,16 +567,19 @@ fileio_readinto(fileio *self, PyObject *args)
if (_PyVerify_fd(self->fd)) { if (_PyVerify_fd(self->fd)) {
len = pbuf.len; len = pbuf.len;
Py_BEGIN_ALLOW_THREADS do {
errno = 0; Py_BEGIN_ALLOW_THREADS
errno = 0;
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (len > INT_MAX) if (len > INT_MAX)
len = INT_MAX; len = INT_MAX;
n = read(self->fd, pbuf.buf, (int)len); n = read(self->fd, pbuf.buf, (int)len);
#else #else
n = read(self->fd, pbuf.buf, len); n = read(self->fd, pbuf.buf, len);
#endif #endif
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} while (n < 0 && errno == EINTR &&
!(async_err = PyErr_CheckSignals()));
} else } else
n = -1; n = -1;
err = errno; err = errno;
@ -580,7 +588,8 @@ fileio_readinto(fileio *self, PyObject *args)
if (err == EAGAIN) if (err == EAGAIN)
Py_RETURN_NONE; Py_RETURN_NONE;
errno = err; errno = err;
PyErr_SetFromErrno(PyExc_IOError); if (!async_err)
PyErr_SetFromErrno(PyExc_IOError);
return NULL; return NULL;
} }
@ -627,6 +636,7 @@ fileio_readall(fileio *self)
Py_ssize_t bytes_read = 0; Py_ssize_t bytes_read = 0;
Py_ssize_t n; Py_ssize_t n;
size_t bufsize; size_t bufsize;
int async_err = 0;
if (self->fd < 0) if (self->fd < 0)
return err_closed(); return err_closed();
@ -673,27 +683,23 @@ fileio_readall(fileio *self)
return NULL; return NULL;
} }
} }
Py_BEGIN_ALLOW_THREADS do {
errno = 0; Py_BEGIN_ALLOW_THREADS
n = bufsize - bytes_read; errno = 0;
n = bufsize - bytes_read;
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (n > INT_MAX) if (n > INT_MAX)
n = INT_MAX; n = INT_MAX;
n = read(self->fd, PyBytes_AS_STRING(result) + bytes_read, (int)n); n = read(self->fd, PyBytes_AS_STRING(result) + bytes_read, (int)n);
#else #else
n = read(self->fd, PyBytes_AS_STRING(result) + bytes_read, n); n = read(self->fd, PyBytes_AS_STRING(result) + bytes_read, n);
#endif #endif
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} while (n < 0 && errno == EINTR &&
!(async_err = PyErr_CheckSignals()));
if (n == 0) if (n == 0)
break; break;
if (n < 0) { if (n < 0) {
if (errno == EINTR) {
if (PyErr_CheckSignals()) {
Py_DECREF(result);
return NULL;
}
continue;
}
if (errno == EAGAIN) { if (errno == EAGAIN) {
if (bytes_read > 0) if (bytes_read > 0)
break; break;
@ -701,7 +707,8 @@ fileio_readall(fileio *self)
Py_RETURN_NONE; Py_RETURN_NONE;
} }
Py_DECREF(result); Py_DECREF(result);
PyErr_SetFromErrno(PyExc_IOError); if (!async_err)
PyErr_SetFromErrno(PyExc_IOError);
return NULL; return NULL;
} }
bytes_read += n; bytes_read += n;
@ -723,6 +730,7 @@ fileio_read(fileio *self, PyObject *args)
char *ptr; char *ptr;
Py_ssize_t n; Py_ssize_t n;
Py_ssize_t size = -1; Py_ssize_t size = -1;
int async_err = 0;
PyObject *bytes; PyObject *bytes;
if (self->fd < 0) if (self->fd < 0)
@ -747,14 +755,17 @@ fileio_read(fileio *self, PyObject *args)
ptr = PyBytes_AS_STRING(bytes); ptr = PyBytes_AS_STRING(bytes);
if (_PyVerify_fd(self->fd)) { if (_PyVerify_fd(self->fd)) {
Py_BEGIN_ALLOW_THREADS do {
errno = 0; Py_BEGIN_ALLOW_THREADS
errno = 0;
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
n = read(self->fd, ptr, (int)size); n = read(self->fd, ptr, (int)size);
#else #else
n = read(self->fd, ptr, size); n = read(self->fd, ptr, size);
#endif #endif
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} while (n < 0 && errno == EINTR &&
!(async_err = PyErr_CheckSignals()));
} else } else
n = -1; n = -1;
@ -764,7 +775,8 @@ fileio_read(fileio *self, PyObject *args)
if (err == EAGAIN) if (err == EAGAIN)
Py_RETURN_NONE; Py_RETURN_NONE;
errno = err; errno = err;
PyErr_SetFromErrno(PyExc_IOError); if (!async_err)
PyErr_SetFromErrno(PyExc_IOError);
return NULL; return NULL;
} }
@ -783,7 +795,7 @@ fileio_write(fileio *self, PyObject *args)
{ {
Py_buffer pbuf; Py_buffer pbuf;
Py_ssize_t n, len; Py_ssize_t n, len;
int err; int err, async_err = 0;
if (self->fd < 0) if (self->fd < 0)
return err_closed(); return err_closed();
@ -794,24 +806,26 @@ fileio_write(fileio *self, PyObject *args)
return NULL; return NULL;
if (_PyVerify_fd(self->fd)) { if (_PyVerify_fd(self->fd)) {
Py_BEGIN_ALLOW_THREADS do {
errno = 0; Py_BEGIN_ALLOW_THREADS
len = pbuf.len; errno = 0;
len = pbuf.len;
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (len > 32767 && isatty(self->fd)) { if (len > 32767 && isatty(self->fd)) {
/* Issue #11395: the Windows console returns an error (12: not /* Issue #11395: the Windows console returns an error (12: not
enough space error) on writing into stdout if stdout mode is enough space error) on writing into stdout if stdout mode is
binary and the length is greater than 66,000 bytes (or less, binary and the length is greater than 66,000 bytes (or less,
depending on heap usage). */ depending on heap usage). */
len = 32767; len = 32767;
} } else if (len > INT_MAX)
else if (len > INT_MAX) len = INT_MAX;
len = INT_MAX; n = write(self->fd, pbuf.buf, (int)len);
n = write(self->fd, pbuf.buf, (int)len);
#else #else
n = write(self->fd, pbuf.buf, len); n = write(self->fd, pbuf.buf, len);
#endif #endif
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} while (n < 0 && errno == EINTR &&
!(async_err = PyErr_CheckSignals()));
} else } else
n = -1; n = -1;
err = errno; err = errno;
@ -822,7 +836,8 @@ fileio_write(fileio *self, PyObject *args)
if (err == EAGAIN) if (err == EAGAIN)
Py_RETURN_NONE; Py_RETURN_NONE;
errno = err; errno = err;
PyErr_SetFromErrno(PyExc_IOError); if (!async_err)
PyErr_SetFromErrno(PyExc_IOError);
return NULL; return NULL;
} }

View File

@ -1361,13 +1361,16 @@ static PyObject *
posix_fildes_fd(int fd, int (*func)(int)) posix_fildes_fd(int fd, int (*func)(int))
{ {
int res; int res;
Py_BEGIN_ALLOW_THREADS int async_err = 0;
res = (*func)(fd);
Py_END_ALLOW_THREADS do {
if (res < 0) Py_BEGIN_ALLOW_THREADS
return posix_error(); res = (*func)(fd);
Py_INCREF(Py_None); Py_END_ALLOW_THREADS
return Py_None; } 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 @@ os_fchmod_impl(PyModuleDef *module, int fd, int mode)
/*[clinic end generated code: output=3c19fbfd724a8e0f input=8ab11975ca01ee5b]*/ /*[clinic end generated code: output=3c19fbfd724a8e0f input=8ab11975ca01ee5b]*/
{ {
int res; int res;
Py_BEGIN_ALLOW_THREADS int async_err = 0;
res = fchmod(fd, mode);
Py_END_ALLOW_THREADS do {
if (res < 0) Py_BEGIN_ALLOW_THREADS
return posix_error(); 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; Py_RETURN_NONE;
} }
#endif /* HAVE_FCHMOD */ #endif /* HAVE_FCHMOD */
@ -4104,11 +4112,16 @@ os_fchown_impl(PyModuleDef *module, int fd, uid_t uid, gid_t gid)
/*[clinic end generated code: output=687781cb7d8974d6 input=3af544ba1b13a0d7]*/ /*[clinic end generated code: output=687781cb7d8974d6 input=3af544ba1b13a0d7]*/
{ {
int res; int res;
Py_BEGIN_ALLOW_THREADS int async_err = 0;
res = fchown(fd, uid, gid);
Py_END_ALLOW_THREADS do {
if (res < 0) Py_BEGIN_ALLOW_THREADS
return posix_error(); 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; Py_RETURN_NONE;
} }
#endif /* HAVE_FCHOWN */ #endif /* HAVE_FCHOWN */
@ -9602,12 +9615,17 @@ os_wait3_impl(PyModuleDef *module, int options)
{ {
pid_t pid; pid_t pid;
struct rusage ru; struct rusage ru;
int async_err = 0;
WAIT_TYPE status; WAIT_TYPE status;
WAIT_STATUS_INT(status) = 0; WAIT_STATUS_INT(status) = 0;
Py_BEGIN_ALLOW_THREADS do {
pid = wait3(&status, options, &ru); Py_BEGIN_ALLOW_THREADS
Py_END_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); return wait_helper(pid, WAIT_STATUS_INT(status), &ru);
} }
@ -9665,15 +9683,21 @@ static PyObject *
os_wait4_impl(PyModuleDef *module, pid_t pid, int options) os_wait4_impl(PyModuleDef *module, pid_t pid, int options)
/*[clinic end generated code: output=20dfb05289d37dc6 input=d11deed0750600ba]*/ /*[clinic end generated code: output=20dfb05289d37dc6 input=d11deed0750600ba]*/
{ {
pid_t res;
struct rusage ru; struct rusage ru;
int async_err = 0;
WAIT_TYPE status; WAIT_TYPE status;
WAIT_STATUS_INT(status) = 0; WAIT_STATUS_INT(status) = 0;
Py_BEGIN_ALLOW_THREADS do {
pid = wait4(pid, &status, options, &ru); Py_BEGIN_ALLOW_THREADS
Py_END_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(pid, WAIT_STATUS_INT(status), &ru); return wait_helper(res, WAIT_STATUS_INT(status), &ru);
} }
#endif /* HAVE_WAIT4 */ #endif /* HAVE_WAIT4 */
@ -9744,14 +9768,17 @@ os_waitid_impl(PyModuleDef *module, idtype_t idtype, id_t id, int options)
{ {
PyObject *result; PyObject *result;
int res; int res;
int async_err = 0;
siginfo_t si; siginfo_t si;
si.si_pid = 0; si.si_pid = 0;
Py_BEGIN_ALLOW_THREADS do {
res = waitid(idtype, id, &si, options); Py_BEGIN_ALLOW_THREADS
Py_END_ALLOW_THREADS res = waitid(idtype, id, &si, options);
if (res == -1) Py_END_ALLOW_THREADS
return posix_error(); } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (res < 0)
return (!async_err) ? posix_error() : NULL;
if (si.si_pid == 0) if (si.si_pid == 0)
Py_RETURN_NONE; Py_RETURN_NONE;
@ -9828,16 +9855,20 @@ static PyObject *
os_waitpid_impl(PyModuleDef *module, pid_t pid, int options) os_waitpid_impl(PyModuleDef *module, pid_t pid, int options)
/*[clinic end generated code: output=095a6b00af70b7ac input=0bf1666b8758fda3]*/ /*[clinic end generated code: output=095a6b00af70b7ac input=0bf1666b8758fda3]*/
{ {
pid_t res;
int async_err = 0;
WAIT_TYPE status; WAIT_TYPE status;
WAIT_STATUS_INT(status) = 0; WAIT_STATUS_INT(status) = 0;
Py_BEGIN_ALLOW_THREADS do {
pid = waitpid(pid, &status, options); Py_BEGIN_ALLOW_THREADS
Py_END_ALLOW_THREADS res = waitpid(pid, &status, options);
if (pid == -1) Py_END_ALLOW_THREADS
return posix_error(); } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (res < 0)
return (!async_err) ? posix_error() : NULL;
return Py_BuildValue("Ni", PyLong_FromPid(pid), WAIT_STATUS_INT(status)); return Py_BuildValue("Ni", PyLong_FromPid(res), WAIT_STATUS_INT(status));
} }
#elif defined(HAVE_CWAIT) #elif defined(HAVE_CWAIT)
/* MS C has a variant of waitpid() that's usable for most purposes. */ /* MS C has a variant of waitpid() that's usable for most purposes. */
@ -9894,15 +9925,19 @@ os_waitpid_impl(PyModuleDef *module, Py_intptr_t pid, int options)
/*[clinic end generated code: output=c20b95b15ad44a3a input=444c8f51cca5b862]*/ /*[clinic end generated code: output=c20b95b15ad44a3a input=444c8f51cca5b862]*/
{ {
int status; int status;
Py_intptr_t res;
int async_err = 0;
Py_BEGIN_ALLOW_THREADS do {
pid = _cwait(&status, pid, options); Py_BEGIN_ALLOW_THREADS
Py_END_ALLOW_THREADS res = _cwait(&status, pid, options);
if (pid == -1) Py_END_ALLOW_THREADS
return posix_error(); } 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 */ /* 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 #endif
@ -9943,14 +9978,17 @@ os_wait_impl(PyModuleDef *module)
/*[clinic end generated code: output=2a83a9d164e7e6a8 input=03b0182d4a4700ce]*/ /*[clinic end generated code: output=2a83a9d164e7e6a8 input=03b0182d4a4700ce]*/
{ {
pid_t pid; pid_t pid;
int async_err = 0;
WAIT_TYPE status; WAIT_TYPE status;
WAIT_STATUS_INT(status) = 0; WAIT_STATUS_INT(status) = 0;
Py_BEGIN_ALLOW_THREADS do {
pid = wait(&status); Py_BEGIN_ALLOW_THREADS
Py_END_ALLOW_THREADS pid = wait(&status);
if (pid == -1) Py_END_ALLOW_THREADS
return posix_error(); } 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)); return Py_BuildValue("Ni", PyLong_FromPid(pid), WAIT_STATUS_INT(status));
} }
@ -10837,6 +10875,7 @@ os_open_impl(PyModuleDef *module, path_t *path, int flags, int mode, int dir_fd)
/*[clinic end generated code: output=05b68fc4ed5e29c9 input=ad8623b29acd2934]*/ /*[clinic end generated code: output=05b68fc4ed5e29c9 input=ad8623b29acd2934]*/
{ {
int fd; int fd;
int async_err = 0;
#ifdef O_CLOEXEC #ifdef O_CLOEXEC
int *atomic_flag_works = &_Py_open_cloexec_works; int *atomic_flag_works = &_Py_open_cloexec_works;
@ -10850,22 +10889,25 @@ os_open_impl(PyModuleDef *module, path_t *path, int flags, int mode, int dir_fd)
flags |= O_CLOEXEC; flags |= O_CLOEXEC;
#endif #endif
Py_BEGIN_ALLOW_THREADS do {
Py_BEGIN_ALLOW_THREADS
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (path->wide) if (path->wide)
fd = _wopen(path->wide, flags, mode); fd = _wopen(path->wide, flags, mode);
else else
#endif #endif
#ifdef HAVE_OPENAT #ifdef HAVE_OPENAT
if (dir_fd != DEFAULT_DIR_FD) if (dir_fd != DEFAULT_DIR_FD)
fd = openat(dir_fd, path->narrow, flags, mode); fd = openat(dir_fd, path->narrow, flags, mode);
else else
#endif #endif
fd = open(path->narrow, flags, mode); fd = open(path->narrow, flags, mode);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} while (fd < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (fd == -1) { if (fd == -1) {
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object); if (!async_err)
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object);
return -1; return -1;
} }
@ -10924,6 +10966,10 @@ os_close_impl(PyModuleDef *module, int fd)
int res; int res;
if (!_PyVerify_fd(fd)) if (!_PyVerify_fd(fd))
return posix_error(); 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 Py_BEGIN_ALLOW_THREADS
res = close(fd); res = close(fd);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
@ -11089,6 +11135,10 @@ os_dup2_impl(PyModuleDef *module, int fd, int fd2, int inheritable)
if (!_PyVerify_fd_dup2(fd, fd2)) if (!_PyVerify_fd_dup2(fd, fd2))
return posix_error(); 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 #ifdef MS_WINDOWS
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
res = dup2(fd, fd2); res = dup2(fd, fd2);
@ -11355,6 +11405,7 @@ os_read_impl(PyModuleDef *module, int fd, Py_ssize_t length)
/*[clinic end generated code: output=1f3bc27260a24968 input=1df2eaa27c0bf1d3]*/ /*[clinic end generated code: output=1f3bc27260a24968 input=1df2eaa27c0bf1d3]*/
{ {
Py_ssize_t n; Py_ssize_t n;
int async_err = 0;
PyObject *buffer; PyObject *buffer;
if (length < 0) { if (length < 0) {
@ -11375,13 +11426,16 @@ os_read_impl(PyModuleDef *module, int fd, Py_ssize_t length)
buffer = PyBytes_FromStringAndSize((char *)NULL, length); buffer = PyBytes_FromStringAndSize((char *)NULL, length);
if (buffer == NULL) if (buffer == NULL)
return NULL; return NULL;
Py_BEGIN_ALLOW_THREADS
n = read(fd, PyBytes_AS_STRING(buffer), READ_CAST length); do {
Py_END_ALLOW_THREADS 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) { if (n < 0) {
Py_DECREF(buffer); Py_DECREF(buffer);
return posix_error(); return (!async_err) ? posix_error() : NULL;
} }
if (n != length) if (n != length)
@ -11515,6 +11569,7 @@ os_readv_impl(PyModuleDef *module, int fd, PyObject *buffers)
{ {
int cnt; int cnt;
Py_ssize_t n; Py_ssize_t n;
int async_err = 0;
struct iovec *iov; struct iovec *iov;
Py_buffer *buf; Py_buffer *buf;
@ -11529,13 +11584,16 @@ os_readv_impl(PyModuleDef *module, int fd, PyObject *buffers)
if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_WRITABLE) < 0) if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_WRITABLE) < 0)
return -1; return -1;
Py_BEGIN_ALLOW_THREADS do {
n = readv(fd, iov, cnt); Py_BEGIN_ALLOW_THREADS
Py_END_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); iov_cleanup(iov, buf, cnt);
if (n < 0) { if (n < 0) {
posix_error(); if (!async_err)
posix_error();
return -1; return -1;
} }
@ -11598,6 +11656,7 @@ os_pread_impl(PyModuleDef *module, int fd, int length, Py_off_t offset)
/*[clinic end generated code: output=7b62bf6c06e20ae8 input=084948dcbaa35d4c]*/ /*[clinic end generated code: output=7b62bf6c06e20ae8 input=084948dcbaa35d4c]*/
{ {
Py_ssize_t n; Py_ssize_t n;
int async_err = 0;
PyObject *buffer; PyObject *buffer;
if (length < 0) { if (length < 0) {
@ -11611,12 +11670,16 @@ os_pread_impl(PyModuleDef *module, int fd, int length, Py_off_t offset)
Py_DECREF(buffer); Py_DECREF(buffer);
return posix_error(); return posix_error();
} }
Py_BEGIN_ALLOW_THREADS
n = pread(fd, PyBytes_AS_STRING(buffer), length, offset); do {
Py_END_ALLOW_THREADS 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) { if (n < 0) {
Py_DECREF(buffer); Py_DECREF(buffer);
return posix_error(); return (!async_err) ? posix_error() : NULL;
} }
if (n != length) if (n != length)
_PyBytes_Resize(&buffer, n); _PyBytes_Resize(&buffer, n);
@ -11677,6 +11740,7 @@ os_write_impl(PyModuleDef *module, int fd, Py_buffer *data)
/*[clinic end generated code: output=aeb96acfdd4d5112 input=3207e28963234f3c]*/ /*[clinic end generated code: output=aeb96acfdd4d5112 input=3207e28963234f3c]*/
{ {
Py_ssize_t size; Py_ssize_t size;
int async_err = 0;
Py_ssize_t len = data->len; Py_ssize_t len = data->len;
if (!_PyVerify_fd(fd)) { if (!_PyVerify_fd(fd)) {
@ -11684,17 +11748,21 @@ os_write_impl(PyModuleDef *module, int fd, Py_buffer *data)
return -1; return -1;
} }
Py_BEGIN_ALLOW_THREADS do {
Py_BEGIN_ALLOW_THREADS
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (len > INT_MAX) if (len > INT_MAX)
len = INT_MAX; len = INT_MAX;
size = write(fd, data->buf, (int)len); size = write(fd, data->buf, (int)len);
#else #else
size = write(fd, data->buf, len); size = write(fd, data->buf, len);
#endif #endif
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} while (size < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (size < 0) { if (size < 0) {
posix_error(); if (!async_err)
posix_error();
return -1; return -1;
} }
return size; return size;
@ -11713,6 +11781,7 @@ posix_sendfile(PyObject *self, PyObject *args, PyObject *kwdict)
{ {
int in, out; int in, out;
Py_ssize_t ret; Py_ssize_t ret;
int async_err = 0;
off_t offset; off_t offset;
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__) #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__)
@ -11775,13 +11844,15 @@ posix_sendfile(PyObject *self, PyObject *args, PyObject *kwdict)
} }
} }
Py_BEGIN_ALLOW_THREADS do {
Py_BEGIN_ALLOW_THREADS
#ifdef __APPLE__ #ifdef __APPLE__
ret = sendfile(in, out, offset, &sbytes, &sf, flags); ret = sendfile(in, out, offset, &sbytes, &sf, flags);
#else #else
ret = sendfile(in, out, offset, len, &sf, &sbytes, flags); ret = sendfile(in, out, offset, len, &sf, &sbytes, flags);
#endif #endif
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (sf.headers != NULL) if (sf.headers != NULL)
iov_cleanup(sf.headers, hbuf, sf.hdr_cnt); iov_cleanup(sf.headers, hbuf, sf.hdr_cnt);
@ -11800,7 +11871,7 @@ posix_sendfile(PyObject *self, PyObject *args, PyObject *kwdict)
return posix_error(); return posix_error();
} }
} }
return posix_error(); return (!async_err) ? posix_error() : NULL;
} }
goto done; goto done;
@ -11821,21 +11892,26 @@ done:
return NULL; return NULL;
#ifdef linux #ifdef linux
if (offobj == Py_None) { if (offobj == Py_None) {
Py_BEGIN_ALLOW_THREADS do {
ret = sendfile(out, in, NULL, count); Py_BEGIN_ALLOW_THREADS
Py_END_ALLOW_THREADS ret = sendfile(out, in, NULL, count);
Py_END_ALLOW_THREADS
} while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (ret < 0) if (ret < 0)
return posix_error(); return (!async_err) ? posix_error() : NULL;
return Py_BuildValue("n", ret); return Py_BuildValue("n", ret);
} }
#endif #endif
if (!Py_off_t_converter(offobj, &offset)) if (!Py_off_t_converter(offobj, &offset))
return NULL; return NULL;
Py_BEGIN_ALLOW_THREADS
ret = sendfile(out, in, &offset, count); do {
Py_END_ALLOW_THREADS 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) if (ret < 0)
return posix_error(); return (!async_err) ? posix_error() : NULL;
return Py_BuildValue("n", ret); return Py_BuildValue("n", ret);
#endif #endif
} }
@ -11891,15 +11967,18 @@ os_fstat_impl(PyModuleDef *module, int fd)
{ {
STRUCT_STAT st; STRUCT_STAT st;
int res; int res;
int async_err = 0;
Py_BEGIN_ALLOW_THREADS do {
res = FSTAT(fd, &st); Py_BEGIN_ALLOW_THREADS
Py_END_ALLOW_THREADS res = FSTAT(fd, &st);
Py_END_ALLOW_THREADS
} while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (res != 0) { if (res != 0) {
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
return PyErr_SetFromWindowsErr(0); return PyErr_SetFromWindowsErr(0);
#else #else
return posix_error(); return (!async_err) ? posix_error() : NULL;
#endif #endif
} }
@ -12185,6 +12264,7 @@ os_writev_impl(PyModuleDef *module, int fd, PyObject *buffers)
{ {
int cnt; int cnt;
Py_ssize_t result; Py_ssize_t result;
int async_err = 0;
struct iovec *iov; struct iovec *iov;
Py_buffer *buf; Py_buffer *buf;
@ -12199,12 +12279,14 @@ os_writev_impl(PyModuleDef *module, int fd, PyObject *buffers)
return -1; return -1;
} }
Py_BEGIN_ALLOW_THREADS do {
result = writev(fd, iov, cnt); Py_BEGIN_ALLOW_THREADS
Py_END_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); iov_cleanup(iov, buf, cnt);
if (result < 0) if (result < 0 && !async_err)
posix_error(); posix_error();
return result; return result;
@ -12275,17 +12357,20 @@ os_pwrite_impl(PyModuleDef *module, int fd, Py_buffer *buffer, Py_off_t offset)
/*[clinic end generated code: output=ec9cc5b2238e96a7 input=19903f1b3dd26377]*/ /*[clinic end generated code: output=ec9cc5b2238e96a7 input=19903f1b3dd26377]*/
{ {
Py_ssize_t size; Py_ssize_t size;
int async_err = 0;
if (!_PyVerify_fd(fd)) { if (!_PyVerify_fd(fd)) {
posix_error(); posix_error();
return -1; return -1;
} }
Py_BEGIN_ALLOW_THREADS do {
size = pwrite(fd, buffer->buf, (size_t)buffer->len, offset); Py_BEGIN_ALLOW_THREADS
Py_END_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) if (size < 0 && !async_err)
posix_error(); posix_error();
return size; return size;
} }
@ -12353,18 +12438,21 @@ os_mkfifo_impl(PyModuleDef *module, path_t *path, int mode, int dir_fd)
/*[clinic end generated code: output=b3321927546893d0 input=73032e98a36e0e19]*/ /*[clinic end generated code: output=b3321927546893d0 input=73032e98a36e0e19]*/
{ {
int result; int result;
int async_err = 0;
Py_BEGIN_ALLOW_THREADS do {
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_MKFIFOAT #ifdef HAVE_MKFIFOAT
if (dir_fd != DEFAULT_DIR_FD) if (dir_fd != DEFAULT_DIR_FD)
result = mkfifoat(dir_fd, path->narrow, mode); result = mkfifoat(dir_fd, path->narrow, mode);
else else
#endif #endif
result = mkfifo(path->narrow, mode); result = mkfifo(path->narrow, mode);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} while (result != 0 && errno == EINTR &&
if (result < 0) !(async_err = PyErr_CheckSignals()));
return posix_error(); if (result != 0)
return (!async_err) ? posix_error() : NULL;
Py_RETURN_NONE; Py_RETURN_NONE;
} }
@ -12448,18 +12536,21 @@ os_mknod_impl(PyModuleDef *module, path_t *path, int mode, dev_t device, int dir
/*[clinic end generated code: output=f71d54eaf9bb6f1a input=ee44531551a4d83b]*/ /*[clinic end generated code: output=f71d54eaf9bb6f1a input=ee44531551a4d83b]*/
{ {
int result; int result;
int async_err = 0;
Py_BEGIN_ALLOW_THREADS do {
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_MKNODAT #ifdef HAVE_MKNODAT
if (dir_fd != DEFAULT_DIR_FD) if (dir_fd != DEFAULT_DIR_FD)
result = mknodat(dir_fd, path->narrow, mode, device); result = mknodat(dir_fd, path->narrow, mode, device);
else else
#endif #endif
result = mknod(path->narrow, mode, device); result = mknod(path->narrow, mode, device);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} while (result != 0 && errno == EINTR &&
if (result < 0) !(async_err = PyErr_CheckSignals()));
return posix_error(); if (result != 0)
return (!async_err) ? posix_error() : NULL;
Py_RETURN_NONE; Py_RETURN_NONE;
} }
@ -12662,12 +12753,16 @@ os_ftruncate_impl(PyModuleDef *module, int fd, Py_off_t length)
/*[clinic end generated code: output=62326766cb9b76bf input=63b43641e52818f2]*/ /*[clinic end generated code: output=62326766cb9b76bf input=63b43641e52818f2]*/
{ {
int result; int result;
int async_err = 0;
Py_BEGIN_ALLOW_THREADS do {
result = ftruncate(fd, length); Py_BEGIN_ALLOW_THREADS
Py_END_ALLOW_THREADS result = ftruncate(fd, length);
if (result < 0) Py_END_ALLOW_THREADS
return posix_error(); } while (result != 0 && errno == EINTR &&
!(async_err = PyErr_CheckSignals()));
if (result != 0)
return (!async_err) ? posix_error() : NULL;
Py_RETURN_NONE; Py_RETURN_NONE;
} }
#endif /* HAVE_FTRUNCATE */ #endif /* HAVE_FTRUNCATE */
@ -12805,14 +12900,16 @@ os_posix_fallocate_impl(PyModuleDef *module, int fd, Py_off_t offset, Py_off_t l
/*[clinic end generated code: output=0cd702d2065c79db input=d7a2ef0ab2ca52fb]*/ /*[clinic end generated code: output=0cd702d2065c79db input=d7a2ef0ab2ca52fb]*/
{ {
int result; int result;
int async_err = 0;
Py_BEGIN_ALLOW_THREADS do {
result = posix_fallocate(fd, offset, length); Py_BEGIN_ALLOW_THREADS
Py_END_ALLOW_THREADS result = posix_fallocate(fd, offset, length);
if (result != 0) { Py_END_ALLOW_THREADS
errno = result; } while (result != 0 && errno == EINTR &&
return posix_error(); !(async_err = PyErr_CheckSignals()));
} if (result != 0)
return (!async_err) ? posix_error() : NULL;
Py_RETURN_NONE; Py_RETURN_NONE;
} }
#endif /* HAVE_POSIX_FALLOCATE) && !POSIX_FADVISE_AIX_BUG */ #endif /* HAVE_POSIX_FALLOCATE) && !POSIX_FADVISE_AIX_BUG */
@ -12883,14 +12980,16 @@ os_posix_fadvise_impl(PyModuleDef *module, int fd, Py_off_t offset, Py_off_t len
/*[clinic end generated code: output=dad93f32c04dd4f7 input=0fbe554edc2f04b5]*/ /*[clinic end generated code: output=dad93f32c04dd4f7 input=0fbe554edc2f04b5]*/
{ {
int result; int result;
int async_err = 0;
Py_BEGIN_ALLOW_THREADS do {
result = posix_fadvise(fd, offset, length, advice); Py_BEGIN_ALLOW_THREADS
Py_END_ALLOW_THREADS result = posix_fadvise(fd, offset, length, advice);
if (result != 0) { Py_END_ALLOW_THREADS
errno = result; } while (result != 0 && errno == EINTR &&
return posix_error(); !(async_err = PyErr_CheckSignals()));
} if (result != 0)
return (!async_err) ? posix_error() : NULL;
Py_RETURN_NONE; Py_RETURN_NONE;
} }
#endif /* HAVE_POSIX_FADVISE && !POSIX_FADVISE_AIX_BUG */ #endif /* HAVE_POSIX_FADVISE && !POSIX_FADVISE_AIX_BUG */
@ -13745,13 +13844,17 @@ os_fstatvfs_impl(PyModuleDef *module, int fd)
/*[clinic end generated code: output=0e32bf07f946ec0d input=d8122243ac50975e]*/ /*[clinic end generated code: output=0e32bf07f946ec0d input=d8122243ac50975e]*/
{ {
int result; int result;
int async_err = 0;
struct statvfs st; struct statvfs st;
Py_BEGIN_ALLOW_THREADS do {
result = fstatvfs(fd, &st); Py_BEGIN_ALLOW_THREADS
Py_END_ALLOW_THREADS result = fstatvfs(fd, &st);
Py_END_ALLOW_THREADS
} while (result != 0 && errno == EINTR &&
!(async_err = PyErr_CheckSignals()));
if (result != 0) if (result != 0)
return posix_error(); return (!async_err) ? posix_error() : NULL;
return _pystatvfs_fromstructstatvfs(st); return _pystatvfs_fromstructstatvfs(st);
} }

View File

@ -2037,6 +2037,7 @@ sock_accept(PySocketSockObject *s)
PyObject *addr = NULL; PyObject *addr = NULL;
PyObject *res = NULL; PyObject *res = NULL;
int timeout; int timeout;
int async_err = 0;
#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
/* accept4() is available on Linux 2.6.28+ and glibc 2.10 */ /* accept4() is available on Linux 2.6.28+ and glibc 2.10 */
static int accept4_works = -1; static int accept4_works = -1;
@ -2050,27 +2051,27 @@ sock_accept(PySocketSockObject *s)
return select_error(); return select_error();
BEGIN_SELECT_LOOP(s) BEGIN_SELECT_LOOP(s)
do {
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
timeout = internal_select_ex(s, 0, interval); timeout = internal_select_ex(s, 0, interval);
if (!timeout) { if (!timeout) {
#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
if (accept4_works != 0) { if (accept4_works != 0) {
newfd = accept4(s->sock_fd, SAS2SA(&addrbuf), &addrlen, newfd = accept4(s->sock_fd, SAS2SA(&addrbuf), &addrlen,
SOCK_CLOEXEC); SOCK_CLOEXEC);
if (newfd == INVALID_SOCKET && accept4_works == -1) { if (newfd == INVALID_SOCKET && accept4_works == -1) {
/* On Linux older than 2.6.28, accept4() fails with ENOSYS */ /* On Linux older than 2.6.28, accept4() fails with ENOSYS */
accept4_works = (errno != ENOSYS); accept4_works = (errno != ENOSYS);
}
} }
} if (accept4_works == 0)
if (accept4_works == 0) newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
#else #else
newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen); newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
#endif #endif
} }
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} while (newfd < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (timeout == 1) { if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
return NULL; return NULL;
@ -2078,7 +2079,7 @@ sock_accept(PySocketSockObject *s)
END_SELECT_LOOP(s) END_SELECT_LOOP(s)
if (newfd == INVALID_SOCKET) if (newfd == INVALID_SOCKET)
return s->errorhandler(); return (!async_err) ? s->errorhandler() : NULL;
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (!SetHandleInformation((HANDLE)newfd, HANDLE_FLAG_INHERIT, 0)) { if (!SetHandleInformation((HANDLE)newfd, HANDLE_FLAG_INHERIT, 0)) {
@ -2341,6 +2342,10 @@ sock_close(PySocketSockObject *s)
{ {
SOCKET_T fd; 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) { if ((fd = s->sock_fd) != -1) {
s->sock_fd = -1; s->sock_fd = -1;
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
@ -2513,10 +2518,8 @@ sock_connect_ex(PySocketSockObject *s, PyObject *addro)
/* Signals are not errors (though they may raise exceptions). Adapted /* Signals are not errors (though they may raise exceptions). Adapted
from PyErr_SetFromErrnoWithFilenameObject(). */ from PyErr_SetFromErrnoWithFilenameObject(). */
#ifdef EINTR
if (res == EINTR && PyErr_CheckSignals()) if (res == EINTR && PyErr_CheckSignals())
return NULL; return NULL;
#endif
return PyLong_FromLong((long) res); return PyLong_FromLong((long) res);
} }
@ -2650,6 +2653,7 @@ sock_recv_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags)
{ {
Py_ssize_t outlen = -1; Py_ssize_t outlen = -1;
int timeout; int timeout;
int async_err = 0;
if (!IS_SELECTABLE(s)) { if (!IS_SELECTABLE(s)) {
select_error(); select_error();
@ -2661,18 +2665,20 @@ sock_recv_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags)
} }
BEGIN_SELECT_LOOP(s) BEGIN_SELECT_LOOP(s)
Py_BEGIN_ALLOW_THREADS do {
timeout = internal_select_ex(s, 0, interval); Py_BEGIN_ALLOW_THREADS
if (!timeout) { timeout = internal_select_ex(s, 0, interval);
if (!timeout) {
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (len > INT_MAX) if (len > INT_MAX)
len = INT_MAX; len = INT_MAX;
outlen = recv(s->sock_fd, cbuf, (int)len, flags); outlen = recv(s->sock_fd, cbuf, (int)len, flags);
#else #else
outlen = recv(s->sock_fd, cbuf, len, flags); outlen = recv(s->sock_fd, cbuf, len, flags);
#endif #endif
} }
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} while (outlen < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (timeout == 1) { if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
@ -2682,7 +2688,8 @@ sock_recv_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags)
if (outlen < 0) { if (outlen < 0) {
/* Note: the call to errorhandler() ALWAYS indirectly returned /* Note: the call to errorhandler() ALWAYS indirectly returned
NULL, so ignore its return value */ NULL, so ignore its return value */
s->errorhandler(); if (!async_err)
s->errorhandler();
return -1; return -1;
} }
return outlen; return outlen;
@ -2819,6 +2826,7 @@ sock_recvfrom_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags,
int timeout; int timeout;
Py_ssize_t n = -1; Py_ssize_t n = -1;
socklen_t addrlen; socklen_t addrlen;
int async_err = 0;
*addr = NULL; *addr = NULL;
@ -2831,21 +2839,23 @@ sock_recvfrom_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags,
} }
BEGIN_SELECT_LOOP(s) BEGIN_SELECT_LOOP(s)
Py_BEGIN_ALLOW_THREADS do {
memset(&addrbuf, 0, addrlen); Py_BEGIN_ALLOW_THREADS
timeout = internal_select_ex(s, 0, interval); memset(&addrbuf, 0, addrlen);
if (!timeout) { timeout = internal_select_ex(s, 0, interval);
if (!timeout) {
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (len > INT_MAX) if (len > INT_MAX)
len = INT_MAX; len = INT_MAX;
n = recvfrom(s->sock_fd, cbuf, (int)len, flags, n = recvfrom(s->sock_fd, cbuf, (int)len, flags,
(void *) &addrbuf, &addrlen); (void *) &addrbuf, &addrlen);
#else #else
n = recvfrom(s->sock_fd, cbuf, len, flags, n = recvfrom(s->sock_fd, cbuf, len, flags,
SAS2SA(&addrbuf), &addrlen); SAS2SA(&addrbuf), &addrlen);
#endif #endif
} }
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (timeout == 1) { if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
@ -2853,7 +2863,8 @@ sock_recvfrom_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags,
} }
END_SELECT_LOOP(s) END_SELECT_LOOP(s)
if (n < 0) { if (n < 0) {
s->errorhandler(); if (!async_err)
s->errorhandler();
return -1; return -1;
} }
@ -2993,6 +3004,7 @@ sock_recvmsg_guts(PySocketSockObject *s, struct iovec *iov, int iovlen,
{ {
ssize_t bytes_received = -1; ssize_t bytes_received = -1;
int timeout; int timeout;
int async_err = 0;
sock_addr_t addrbuf; sock_addr_t addrbuf;
socklen_t addrbuflen; socklen_t addrbuflen;
struct msghdr msg = {0}; struct msghdr msg = {0};
@ -3028,25 +3040,29 @@ sock_recvmsg_guts(PySocketSockObject *s, struct iovec *iov, int iovlen,
} }
BEGIN_SELECT_LOOP(s) BEGIN_SELECT_LOOP(s)
Py_BEGIN_ALLOW_THREADS; do {
msg.msg_name = SAS2SA(&addrbuf); Py_BEGIN_ALLOW_THREADS;
msg.msg_namelen = addrbuflen; msg.msg_name = SAS2SA(&addrbuf);
msg.msg_iov = iov; msg.msg_namelen = addrbuflen;
msg.msg_iovlen = iovlen; msg.msg_iov = iov;
msg.msg_control = controlbuf; msg.msg_iovlen = iovlen;
msg.msg_controllen = controllen; msg.msg_control = controlbuf;
timeout = internal_select_ex(s, 0, interval); msg.msg_controllen = controllen;
if (!timeout) timeout = internal_select_ex(s, 0, interval);
bytes_received = recvmsg(s->sock_fd, &msg, flags); if (!timeout)
Py_END_ALLOW_THREADS; bytes_received = recvmsg(s->sock_fd, &msg, flags);
if (timeout == 1) { Py_END_ALLOW_THREADS;
PyErr_SetString(socket_timeout, "timed out"); if (timeout == 1) {
goto finally; PyErr_SetString(socket_timeout, "timed out");
} goto finally;
}
} while (bytes_received < 0 && errno == EINTR &&
!(async_err = PyErr_CheckSignals()));
END_SELECT_LOOP(s) END_SELECT_LOOP(s)
if (bytes_received < 0) { if (bytes_received < 0) {
s->errorhandler(); if (!async_err)
s->errorhandler();
goto finally; goto finally;
} }
@ -3305,6 +3321,7 @@ sock_send(PySocketSockObject *s, PyObject *args)
{ {
char *buf; char *buf;
Py_ssize_t len, n = -1; Py_ssize_t len, n = -1;
int async_err = 0;
int flags = 0, timeout; int flags = 0, timeout;
Py_buffer pbuf; Py_buffer pbuf;
@ -3319,18 +3336,20 @@ sock_send(PySocketSockObject *s, PyObject *args)
len = pbuf.len; len = pbuf.len;
BEGIN_SELECT_LOOP(s) BEGIN_SELECT_LOOP(s)
Py_BEGIN_ALLOW_THREADS do {
timeout = internal_select_ex(s, 1, interval); Py_BEGIN_ALLOW_THREADS
if (!timeout) { timeout = internal_select_ex(s, 1, interval);
if (!timeout) {
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (len > INT_MAX) if (len > INT_MAX)
len = INT_MAX; len = INT_MAX;
n = send(s->sock_fd, buf, (int)len, flags); n = send(s->sock_fd, buf, (int)len, flags);
#else #else
n = send(s->sock_fd, buf, len, flags); n = send(s->sock_fd, buf, len, flags);
#endif #endif
} }
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (timeout == 1) { if (timeout == 1) {
PyBuffer_Release(&pbuf); PyBuffer_Release(&pbuf);
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
@ -3340,7 +3359,7 @@ sock_send(PySocketSockObject *s, PyObject *args)
PyBuffer_Release(&pbuf); PyBuffer_Release(&pbuf);
if (n < 0) if (n < 0)
return s->errorhandler(); return (!async_err) ? s->errorhandler() : NULL;
return PyLong_FromSsize_t(n); return PyLong_FromSsize_t(n);
} }
@ -3359,7 +3378,8 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
{ {
char *buf; char *buf;
Py_ssize_t len, n = -1; Py_ssize_t len, n = -1;
int flags = 0, timeout, saved_errno; int async_err = 0;
int flags = 0, timeout;
Py_buffer pbuf; Py_buffer pbuf;
if (!PyArg_ParseTuple(args, "y*|i:sendall", &pbuf, &flags)) if (!PyArg_ParseTuple(args, "y*|i:sendall", &pbuf, &flags))
@ -3391,29 +3411,16 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
return NULL; return NULL;
} }
/* PyErr_CheckSignals() might change errno */ if (n >= 0) {
saved_errno = errno; buf += n;
/* We must run our signal handlers before looping again. len -= n;
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) { } while (len > 0 && (n >= 0 || errno == EINTR) &&
/* If interrupted, try again */ !(async_err = PyErr_CheckSignals()));
if (saved_errno == EINTR)
continue;
else
break;
}
buf += n;
len -= n;
} while (len > 0);
PyBuffer_Release(&pbuf); PyBuffer_Release(&pbuf);
if (n < 0) if (n < 0 || async_err)
return s->errorhandler(); return (!async_err) ? s->errorhandler() : NULL;
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
@ -3439,6 +3446,7 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
Py_ssize_t len, arglen; Py_ssize_t len, arglen;
sock_addr_t addrbuf; sock_addr_t addrbuf;
int addrlen, n = -1, flags, timeout; int addrlen, n = -1, flags, timeout;
int async_err = 0;
flags = 0; flags = 0;
arglen = PyTuple_Size(args); arglen = PyTuple_Size(args);
@ -3473,20 +3481,22 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
} }
BEGIN_SELECT_LOOP(s) BEGIN_SELECT_LOOP(s)
Py_BEGIN_ALLOW_THREADS do {
timeout = internal_select_ex(s, 1, interval); Py_BEGIN_ALLOW_THREADS
if (!timeout) { timeout = internal_select_ex(s, 1, interval);
if (!timeout) {
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (len > INT_MAX) if (len > INT_MAX)
len = INT_MAX; len = INT_MAX;
n = sendto(s->sock_fd, buf, (int)len, flags, n = sendto(s->sock_fd, buf, (int)len, flags,
SAS2SA(&addrbuf), addrlen); SAS2SA(&addrbuf), addrlen);
#else #else
n = sendto(s->sock_fd, buf, len, flags, n = sendto(s->sock_fd, buf, len, flags,
SAS2SA(&addrbuf), addrlen); SAS2SA(&addrbuf), addrlen);
#endif #endif
} }
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (timeout == 1) { if (timeout == 1) {
PyBuffer_Release(&pbuf); PyBuffer_Release(&pbuf);
@ -3496,7 +3506,7 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
END_SELECT_LOOP(s) END_SELECT_LOOP(s)
PyBuffer_Release(&pbuf); PyBuffer_Release(&pbuf);
if (n < 0) if (n < 0)
return s->errorhandler(); return (!async_err) ? s->errorhandler() : NULL;
return PyLong_FromSsize_t(n); return PyLong_FromSsize_t(n);
} }
@ -3528,6 +3538,7 @@ sock_sendmsg(PySocketSockObject *s, PyObject *args)
void *controlbuf = NULL; void *controlbuf = NULL;
size_t controllen, controllen_last; size_t controllen, controllen_last;
ssize_t bytes_sent = -1; ssize_t bytes_sent = -1;
int async_err = 0;
int addrlen, timeout, flags = 0; int addrlen, timeout, flags = 0;
PyObject *data_arg, *cmsg_arg = NULL, *addr_arg = NULL, *data_fast = NULL, PyObject *data_arg, *cmsg_arg = NULL, *addr_arg = NULL, *data_fast = NULL,
*cmsg_fast = NULL, *retval = NULL; *cmsg_fast = NULL, *retval = NULL;
@ -3685,19 +3696,23 @@ sock_sendmsg(PySocketSockObject *s, PyObject *args)
} }
BEGIN_SELECT_LOOP(s) BEGIN_SELECT_LOOP(s)
Py_BEGIN_ALLOW_THREADS; do {
timeout = internal_select_ex(s, 1, interval); Py_BEGIN_ALLOW_THREADS;
if (!timeout) timeout = internal_select_ex(s, 1, interval);
bytes_sent = sendmsg(s->sock_fd, &msg, flags); if (!timeout)
Py_END_ALLOW_THREADS; bytes_sent = sendmsg(s->sock_fd, &msg, flags);
if (timeout == 1) { Py_END_ALLOW_THREADS;
PyErr_SetString(socket_timeout, "timed out"); if (timeout == 1) {
goto finally; PyErr_SetString(socket_timeout, "timed out");
} goto finally;
}
} while (bytes_sent < 0 && errno == EINTR &&
!(async_err = PyErr_CheckSignals()));
END_SELECT_LOOP(s) END_SELECT_LOOP(s)
if (bytes_sent < 0) { if (bytes_sent < 0) {
s->errorhandler(); if (!async_err)
s->errorhandler();
goto finally; goto finally;
} }
retval = PyLong_FromSsize_t(bytes_sent); retval = PyLong_FromSsize_t(bytes_sent);