|
|
9a62d8 |
|
|
|
9a62d8 |
# HG changeset patch
|
|
|
9a62d8 |
# User Richard Oudkerk <shibturn@gmail.com>
|
|
|
9a62d8 |
# Date 1372700728 -3600
|
|
|
9a62d8 |
# Node ID bc34fe4a0d58a047509798acb0b4b2a21ce1e375
|
|
|
9a62d8 |
# Parent 26ef5d5d5c3ea76ab411f2984d507aadce0ce8d7
|
|
|
9a62d8 |
Issue #17097: Make multiprocessing ignore EINTR.
|
|
|
9a62d8 |
|
|
|
9a62d8 |
diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
|
|
|
9a62d8 |
--- a/Lib/multiprocessing/connection.py
|
|
|
9a62d8 |
+++ b/Lib/multiprocessing/connection.py
|
|
|
9a62d8 |
@@ -270,7 +270,14 @@ class SocketListener(object):
|
|
|
9a62d8 |
self._unlink = None
|
|
|
9a62d8 |
|
|
|
9a62d8 |
def accept(self):
|
|
|
9a62d8 |
- s, self._last_accepted = self._socket.accept()
|
|
|
9a62d8 |
+ while True:
|
|
|
9a62d8 |
+ try:
|
|
|
9a62d8 |
+ s, self._last_accepted = self._socket.accept()
|
|
|
9a62d8 |
+ except socket.error as e:
|
|
|
9a62d8 |
+ if e.args[0] != errno.EINTR:
|
|
|
9a62d8 |
+ raise
|
|
|
9a62d8 |
+ else:
|
|
|
9a62d8 |
+ break
|
|
|
9a62d8 |
s.setblocking(True)
|
|
|
9a62d8 |
fd = duplicate(s.fileno())
|
|
|
9a62d8 |
conn = _multiprocessing.Connection(fd)
|
|
|
9a62d8 |
diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py
|
|
|
9a62d8 |
--- a/Lib/test/test_multiprocessing.py
|
|
|
9a62d8 |
+++ b/Lib/test/test_multiprocessing.py
|
|
|
9a62d8 |
@@ -2461,12 +2461,80 @@ class TestForkAwareThreadLock(unittest.T
|
|
|
9a62d8 |
self.assertLessEqual(new_size, old_size)
|
|
|
9a62d8 |
|
|
|
9a62d8 |
#
|
|
|
9a62d8 |
+# Issue #17097: EINTR should be ignored by recv(), send(), accept() etc
|
|
|
9a62d8 |
+#
|
|
|
9a62d8 |
+
|
|
|
9a62d8 |
+class TestIgnoreEINTR(unittest.TestCase):
|
|
|
9a62d8 |
+
|
|
|
9a62d8 |
+ @classmethod
|
|
|
9a62d8 |
+ def _test_ignore(cls, conn):
|
|
|
9a62d8 |
+ def handler(signum, frame):
|
|
|
9a62d8 |
+ pass
|
|
|
9a62d8 |
+ signal.signal(signal.SIGUSR1, handler)
|
|
|
9a62d8 |
+ conn.send('ready')
|
|
|
9a62d8 |
+ x = conn.recv()
|
|
|
9a62d8 |
+ conn.send(x)
|
|
|
9a62d8 |
+ conn.send_bytes(b'x'*(1024*1024)) # sending 1 MB should block
|
|
|
9a62d8 |
+
|
|
|
9a62d8 |
+ @unittest.skipUnless(hasattr(signal, 'SIGUSR1'), 'requires SIGUSR1')
|
|
|
9a62d8 |
+ def test_ignore(self):
|
|
|
9a62d8 |
+ conn, child_conn = multiprocessing.Pipe()
|
|
|
9a62d8 |
+ try:
|
|
|
9a62d8 |
+ p = multiprocessing.Process(target=self._test_ignore,
|
|
|
9a62d8 |
+ args=(child_conn,))
|
|
|
9a62d8 |
+ p.daemon = True
|
|
|
9a62d8 |
+ p.start()
|
|
|
9a62d8 |
+ child_conn.close()
|
|
|
9a62d8 |
+ self.assertEqual(conn.recv(), 'ready')
|
|
|
9a62d8 |
+ time.sleep(0.1)
|
|
|
9a62d8 |
+ os.kill(p.pid, signal.SIGUSR1)
|
|
|
9a62d8 |
+ time.sleep(0.1)
|
|
|
9a62d8 |
+ conn.send(1234)
|
|
|
9a62d8 |
+ self.assertEqual(conn.recv(), 1234)
|
|
|
9a62d8 |
+ time.sleep(0.1)
|
|
|
9a62d8 |
+ os.kill(p.pid, signal.SIGUSR1)
|
|
|
9a62d8 |
+ self.assertEqual(conn.recv_bytes(), b'x'*(1024*1024))
|
|
|
9a62d8 |
+ time.sleep(0.1)
|
|
|
9a62d8 |
+ p.join()
|
|
|
9a62d8 |
+ finally:
|
|
|
9a62d8 |
+ conn.close()
|
|
|
9a62d8 |
+
|
|
|
9a62d8 |
+ @classmethod
|
|
|
9a62d8 |
+ def _test_ignore_listener(cls, conn):
|
|
|
9a62d8 |
+ def handler(signum, frame):
|
|
|
9a62d8 |
+ pass
|
|
|
9a62d8 |
+ signal.signal(signal.SIGUSR1, handler)
|
|
|
9a62d8 |
+ l = multiprocessing.connection.Listener()
|
|
|
9a62d8 |
+ conn.send(l.address)
|
|
|
9a62d8 |
+ a = l.accept()
|
|
|
9a62d8 |
+ a.send('welcome')
|
|
|
9a62d8 |
+
|
|
|
9a62d8 |
+ @unittest.skipUnless(hasattr(signal, 'SIGUSR1'), 'requires SIGUSR1')
|
|
|
9a62d8 |
+ def test_ignore_listener(self):
|
|
|
9a62d8 |
+ conn, child_conn = multiprocessing.Pipe()
|
|
|
9a62d8 |
+ try:
|
|
|
9a62d8 |
+ p = multiprocessing.Process(target=self._test_ignore_listener,
|
|
|
9a62d8 |
+ args=(child_conn,))
|
|
|
9a62d8 |
+ p.daemon = True
|
|
|
9a62d8 |
+ p.start()
|
|
|
9a62d8 |
+ child_conn.close()
|
|
|
9a62d8 |
+ address = conn.recv()
|
|
|
9a62d8 |
+ time.sleep(0.1)
|
|
|
9a62d8 |
+ os.kill(p.pid, signal.SIGUSR1)
|
|
|
9a62d8 |
+ time.sleep(0.1)
|
|
|
9a62d8 |
+ client = multiprocessing.connection.Client(address)
|
|
|
9a62d8 |
+ self.assertEqual(client.recv(), 'welcome')
|
|
|
9a62d8 |
+ p.join()
|
|
|
9a62d8 |
+ finally:
|
|
|
9a62d8 |
+ conn.close()
|
|
|
9a62d8 |
+
|
|
|
9a62d8 |
+#
|
|
|
9a62d8 |
#
|
|
|
9a62d8 |
#
|
|
|
9a62d8 |
|
|
|
9a62d8 |
testcases_other = [OtherTest, TestInvalidHandle, TestInitializers,
|
|
|
9a62d8 |
TestStdinBadfiledescriptor, TestTimeouts, TestNoForkBomb,
|
|
|
9a62d8 |
- TestFlags, TestForkAwareThreadLock]
|
|
|
9a62d8 |
+ TestFlags, TestForkAwareThreadLock, TestIgnoreEINTR]
|
|
|
9a62d8 |
|
|
|
9a62d8 |
#
|
|
|
9a62d8 |
#
|
|
|
9a62d8 |
diff --git a/Modules/_multiprocessing/socket_connection.c b/Modules/_multiprocessing/socket_connection.c
|
|
|
9a62d8 |
--- a/Modules/_multiprocessing/socket_connection.c
|
|
|
9a62d8 |
+++ b/Modules/_multiprocessing/socket_connection.c
|
|
|
9a62d8 |
@@ -23,6 +23,21 @@
|
|
|
9a62d8 |
#endif
|
|
|
9a62d8 |
|
|
|
9a62d8 |
/*
|
|
|
9a62d8 |
+ * Wrapper for PyErr_CheckSignals() which can be called without the GIL
|
|
|
9a62d8 |
+ */
|
|
|
9a62d8 |
+
|
|
|
9a62d8 |
+static int
|
|
|
9a62d8 |
+check_signals(void)
|
|
|
9a62d8 |
+{
|
|
|
9a62d8 |
+ PyGILState_STATE state;
|
|
|
9a62d8 |
+ int res;
|
|
|
9a62d8 |
+ state = PyGILState_Ensure();
|
|
|
9a62d8 |
+ res = PyErr_CheckSignals();
|
|
|
9a62d8 |
+ PyGILState_Release(state);
|
|
|
9a62d8 |
+ return res;
|
|
|
9a62d8 |
+}
|
|
|
9a62d8 |
+
|
|
|
9a62d8 |
+/*
|
|
|
9a62d8 |
* Send string to file descriptor
|
|
|
9a62d8 |
*/
|
|
|
9a62d8 |
|
|
|
9a62d8 |
@@ -34,8 +49,14 @@ static Py_ssize_t
|
|
|
9a62d8 |
|
|
|
9a62d8 |
while (length > 0) {
|
|
|
9a62d8 |
res = WRITE(h, p, length);
|
|
|
9a62d8 |
- if (res < 0)
|
|
|
9a62d8 |
+ if (res < 0) {
|
|
|
9a62d8 |
+ if (errno == EINTR) {
|
|
|
9a62d8 |
+ if (check_signals() < 0)
|
|
|
9a62d8 |
+ return MP_EXCEPTION_HAS_BEEN_SET;
|
|
|
9a62d8 |
+ continue;
|
|
|
9a62d8 |
+ }
|
|
|
9a62d8 |
return MP_SOCKET_ERROR;
|
|
|
9a62d8 |
+ }
|
|
|
9a62d8 |
length -= res;
|
|
|
9a62d8 |
p += res;
|
|
|
9a62d8 |
}
|
|
|
9a62d8 |
@@ -56,12 +77,16 @@ static Py_ssize_t
|
|
|
9a62d8 |
|
|
|
9a62d8 |
while (remaining > 0) {
|
|
|
9a62d8 |
temp = READ(h, p, remaining);
|
|
|
9a62d8 |
- if (temp <= 0) {
|
|
|
9a62d8 |
- if (temp == 0)
|
|
|
9a62d8 |
- return remaining == length ?
|
|
|
9a62d8 |
- MP_END_OF_FILE : MP_EARLY_END_OF_FILE;
|
|
|
9a62d8 |
- else
|
|
|
9a62d8 |
- return temp;
|
|
|
9a62d8 |
+ if (temp < 0) {
|
|
|
9a62d8 |
+ if (errno == EINTR) {
|
|
|
9a62d8 |
+ if (check_signals() < 0)
|
|
|
9a62d8 |
+ return MP_EXCEPTION_HAS_BEEN_SET;
|
|
|
9a62d8 |
+ continue;
|
|
|
9a62d8 |
+ }
|
|
|
9a62d8 |
+ return temp;
|
|
|
9a62d8 |
+ }
|
|
|
9a62d8 |
+ else if (temp == 0) {
|
|
|
9a62d8 |
+ return remaining == length ? MP_END_OF_FILE : MP_EARLY_END_OF_FILE;
|
|
|
9a62d8 |
}
|
|
|
9a62d8 |
remaining -= temp;
|
|
|
9a62d8 |
p += temp;
|
|
|
9a62d8 |
@@ -171,9 +196,16 @@ conn_poll(ConnectionObject *conn, double
|
|
|
9a62d8 |
p.revents = 0;
|
|
|
9a62d8 |
|
|
|
9a62d8 |
if (timeout < 0) {
|
|
|
9a62d8 |
- res = poll(&p, 1, -1);
|
|
|
9a62d8 |
+ do {
|
|
|
9a62d8 |
+ res = poll(&p, 1, -1);
|
|
|
9a62d8 |
+ } while (res < 0 && errno == EINTR);
|
|
|
9a62d8 |
} else {
|
|
|
9a62d8 |
res = poll(&p, 1, (int)(timeout * 1000 + 0.5));
|
|
|
9a62d8 |
+ if (res < 0 && errno == EINTR) {
|
|
|
9a62d8 |
+ /* We were interrupted by a signal. Just indicate a
|
|
|
9a62d8 |
+ timeout even though we are early. */
|
|
|
9a62d8 |
+ return FALSE;
|
|
|
9a62d8 |
+ }
|
|
|
9a62d8 |
}
|
|
|
9a62d8 |
|
|
|
9a62d8 |
if (res < 0) {
|
|
|
9a62d8 |
@@ -209,12 +241,19 @@ conn_poll(ConnectionObject *conn, double
|
|
|
9a62d8 |
FD_SET((SOCKET)conn->handle, &rfds);
|
|
|
9a62d8 |
|
|
|
9a62d8 |
if (timeout < 0.0) {
|
|
|
9a62d8 |
- res = select((int)conn->handle+1, &rfds, NULL, NULL, NULL);
|
|
|
9a62d8 |
+ do {
|
|
|
9a62d8 |
+ res = select((int)conn->handle+1, &rfds, NULL, NULL, NULL);
|
|
|
9a62d8 |
+ } while (res < 0 && errno == EINTR);
|
|
|
9a62d8 |
} else {
|
|
|
9a62d8 |
struct timeval tv;
|
|
|
9a62d8 |
tv.tv_sec = (long)timeout;
|
|
|
9a62d8 |
tv.tv_usec = (long)((timeout - tv.tv_sec) * 1e6 + 0.5);
|
|
|
9a62d8 |
res = select((int)conn->handle+1, &rfds, NULL, NULL, &tv;;
|
|
|
9a62d8 |
+ if (res < 0 && errno == EINTR) {
|
|
|
9a62d8 |
+ /* We were interrupted by a signal. Just indicate a
|
|
|
9a62d8 |
+ timeout even though we are early. */
|
|
|
9a62d8 |
+ return FALSE;
|
|
|
9a62d8 |
+ }
|
|
|
9a62d8 |
}
|
|
|
9a62d8 |
|
|
|
9a62d8 |
if (res < 0) {
|
|
|
9a62d8 |
|