Issue #23285: PEP 475 -- Retry system calls failing with EINTR.
This commit is contained in:
parent
d005090e01
commit
6e6c59b508
|
@ -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
|
||||||
|
|
19
Lib/_pyio.py
19
Lib/_pyio.py
|
@ -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")
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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())
|
||||||
|
|
||||||
|
|
|
@ -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:]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
|
@ -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()
|
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue