Blame SOURCES/00195-make-multiproc-ignore-EINTR.patch

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