An interpreted, interactive, object-oriented programming language
CentOS Sources
2017-08-01 71084d584ff953f5463757ec6536406320560b4d
commit | author | age
9a62d8 1
CS 2 # HG changeset patch
3 # User Richard Oudkerk <shibturn@gmail.com>
4 # Date 1372700728 -3600
5 # Node ID bc34fe4a0d58a047509798acb0b4b2a21ce1e375
6 # Parent  26ef5d5d5c3ea76ab411f2984d507aadce0ce8d7
7 Issue #17097: Make multiprocessing ignore EINTR.
8
9 diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
10 --- a/Lib/multiprocessing/connection.py
11 +++ b/Lib/multiprocessing/connection.py
12 @@ -270,7 +270,14 @@ class SocketListener(object):
13              self._unlink = None
14  
15      def accept(self):
16 -        s, self._last_accepted = self._socket.accept()
17 +        while True:
18 +            try:
19 +                s, self._last_accepted = self._socket.accept()
20 +            except socket.error as e:
21 +                if e.args[0] != errno.EINTR:
22 +                    raise
23 +            else:
24 +                break
25          s.setblocking(True)
26          fd = duplicate(s.fileno())
27          conn = _multiprocessing.Connection(fd)
28 diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py
29 --- a/Lib/test/test_multiprocessing.py
30 +++ b/Lib/test/test_multiprocessing.py
31 @@ -2461,12 +2461,80 @@ class TestForkAwareThreadLock(unittest.T
32          self.assertLessEqual(new_size, old_size)
33  
34  #
35 +# Issue #17097: EINTR should be ignored by recv(), send(), accept() etc
36 +#
37 +
38 +class TestIgnoreEINTR(unittest.TestCase):
39 +
40 +    @classmethod
41 +    def _test_ignore(cls, conn):
42 +        def handler(signum, frame):
43 +            pass
44 +        signal.signal(signal.SIGUSR1, handler)
45 +        conn.send('ready')
46 +        x = conn.recv()
47 +        conn.send(x)
48 +        conn.send_bytes(b'x'*(1024*1024))   # sending 1 MB should block
49 +
50 +    @unittest.skipUnless(hasattr(signal, 'SIGUSR1'), 'requires SIGUSR1')
51 +    def test_ignore(self):
52 +        conn, child_conn = multiprocessing.Pipe()
53 +        try:
54 +            p = multiprocessing.Process(target=self._test_ignore,
55 +                                        args=(child_conn,))
56 +            p.daemon = True
57 +            p.start()
58 +            child_conn.close()
59 +            self.assertEqual(conn.recv(), 'ready')
60 +            time.sleep(0.1)
61 +            os.kill(p.pid, signal.SIGUSR1)
62 +            time.sleep(0.1)
63 +            conn.send(1234)
64 +            self.assertEqual(conn.recv(), 1234)
65 +            time.sleep(0.1)
66 +            os.kill(p.pid, signal.SIGUSR1)
67 +            self.assertEqual(conn.recv_bytes(), b'x'*(1024*1024))
68 +            time.sleep(0.1)
69 +            p.join()
70 +        finally:
71 +            conn.close()
72 +
73 +    @classmethod
74 +    def _test_ignore_listener(cls, conn):
75 +        def handler(signum, frame):
76 +            pass
77 +        signal.signal(signal.SIGUSR1, handler)
78 +        l = multiprocessing.connection.Listener()
79 +        conn.send(l.address)
80 +        a = l.accept()
81 +        a.send('welcome')
82 +
83 +    @unittest.skipUnless(hasattr(signal, 'SIGUSR1'), 'requires SIGUSR1')
84 +    def test_ignore_listener(self):
85 +        conn, child_conn = multiprocessing.Pipe()
86 +        try:
87 +            p = multiprocessing.Process(target=self._test_ignore_listener,
88 +                                        args=(child_conn,))
89 +            p.daemon = True
90 +            p.start()
91 +            child_conn.close()
92 +            address = conn.recv()
93 +            time.sleep(0.1)
94 +            os.kill(p.pid, signal.SIGUSR1)
95 +            time.sleep(0.1)
96 +            client = multiprocessing.connection.Client(address)
97 +            self.assertEqual(client.recv(), 'welcome')
98 +            p.join()
99 +        finally:
100 +            conn.close()
101 +
102 +#
103  #
104  #
105  
106  testcases_other = [OtherTest, TestInvalidHandle, TestInitializers,
107                     TestStdinBadfiledescriptor, TestTimeouts, TestNoForkBomb,
108 -                   TestFlags, TestForkAwareThreadLock]
109 +                   TestFlags, TestForkAwareThreadLock, TestIgnoreEINTR]
110  
111  #
112  #
113 diff --git a/Modules/_multiprocessing/socket_connection.c b/Modules/_multiprocessing/socket_connection.c
114 --- a/Modules/_multiprocessing/socket_connection.c
115 +++ b/Modules/_multiprocessing/socket_connection.c
116 @@ -23,6 +23,21 @@
117  #endif
118  
119  /*
120 + * Wrapper for PyErr_CheckSignals() which can be called without the GIL
121 + */
122 +
123 +static int
124 +check_signals(void)
125 +{
126 +    PyGILState_STATE state;
127 +    int res;
128 +    state = PyGILState_Ensure();
129 +    res = PyErr_CheckSignals();
130 +    PyGILState_Release(state);
131 +    return res;
132 +}
133 +
134 +/*
135   * Send string to file descriptor
136   */
137  
138 @@ -34,8 +49,14 @@ static Py_ssize_t
139  
140      while (length > 0) {
141          res = WRITE(h, p, length);
142 -        if (res < 0)
143 +        if (res < 0) {
144 +            if (errno == EINTR) {
145 +                if (check_signals() < 0)
146 +                    return MP_EXCEPTION_HAS_BEEN_SET;
147 +                continue;
148 +            }
149              return MP_SOCKET_ERROR;
150 +        }
151          length -= res;
152          p += res;
153      }
154 @@ -56,12 +77,16 @@ static Py_ssize_t
155  
156      while (remaining > 0) {
157          temp = READ(h, p, remaining);
158 -        if (temp <= 0) {
159 -            if (temp == 0)
160 -                return remaining == length ?
161 -                    MP_END_OF_FILE : MP_EARLY_END_OF_FILE;
162 -            else
163 -                return temp;
164 +        if (temp < 0) {
165 +            if (errno == EINTR) {
166 +                if (check_signals() < 0)
167 +                    return MP_EXCEPTION_HAS_BEEN_SET;
168 +                continue;
169 +            }
170 +            return temp;
171 +        }
172 +        else if (temp == 0) {
173 +            return remaining == length ? MP_END_OF_FILE : MP_EARLY_END_OF_FILE;
174          }
175          remaining -= temp;
176          p += temp;
177 @@ -171,9 +196,16 @@ conn_poll(ConnectionObject *conn, double
178      p.revents = 0;
179  
180      if (timeout < 0) {
181 -        res = poll(&p, 1, -1);
182 +        do {
183 +            res = poll(&p, 1, -1);
184 +        } while (res < 0 && errno == EINTR);
185      } else {
186          res = poll(&p, 1, (int)(timeout * 1000 + 0.5));
187 +        if (res < 0 && errno == EINTR) {
188 +            /* We were interrupted by a signal.  Just indicate a
189 +               timeout even though we are early. */
190 +            return FALSE;
191 +        }
192      }
193  
194      if (res < 0) {
195 @@ -209,12 +241,19 @@ conn_poll(ConnectionObject *conn, double
196      FD_SET((SOCKET)conn->handle, &rfds);
197  
198      if (timeout < 0.0) {
199 -        res = select((int)conn->handle+1, &rfds, NULL, NULL, NULL);
200 +        do {
201 +            res = select((int)conn->handle+1, &rfds, NULL, NULL, NULL);
202 +        } while (res < 0 && errno == EINTR);
203      } else {
204          struct timeval tv;
205          tv.tv_sec = (long)timeout;
206          tv.tv_usec = (long)((timeout - tv.tv_sec) * 1e6 + 0.5);
207          res = select((int)conn->handle+1, &rfds, NULL, NULL, &tv);
208 +        if (res < 0 && errno == EINTR) {
209 +            /* We were interrupted by a signal.  Just indicate a
210 +               timeout even though we are early. */
211 +            return FALSE;
212 +        }
213      }
214  
215      if (res < 0) {
216