Check that a buffered write, when it gets interrupted (either returning a partial result or EINTR), properly invokes the signal handler and retries if the latter returned successfully.
(self, item, **fdopen_kwargs)
| 190 | mode="r", encoding="latin1") |
| 191 | |
| 192 | def check_interrupted_write_retry(self, item, **fdopen_kwargs): |
| 193 | """Check that a buffered write, when it gets interrupted (either |
| 194 | returning a partial result or EINTR), properly invokes the signal |
| 195 | handler and retries if the latter returned successfully.""" |
| 196 | select = import_helper.import_module("select") |
| 197 | |
| 198 | # A quantity that exceeds the buffer size of an anonymous pipe's |
| 199 | # write end. |
| 200 | N = support.PIPE_MAX_SIZE |
| 201 | r, w = os.pipe() |
| 202 | fdopen_kwargs["closefd"] = False |
| 203 | |
| 204 | # We need a separate thread to read from the pipe and allow the |
| 205 | # write() to finish. This thread is started after the SIGALRM is |
| 206 | # received (forcing a first EINTR in write()). |
| 207 | read_results = [] |
| 208 | write_finished = False |
| 209 | error = None |
| 210 | def _read(): |
| 211 | try: |
| 212 | while not write_finished: |
| 213 | while r in select.select([r], [], [], 1.0)[0]: |
| 214 | s = os.read(r, 1024) |
| 215 | read_results.append(s) |
| 216 | except BaseException as exc: |
| 217 | nonlocal error |
| 218 | error = exc |
| 219 | t = threading.Thread(target=_read) |
| 220 | t.daemon = True |
| 221 | def alarm1(sig, frame): |
| 222 | signal.signal(signal.SIGALRM, alarm2) |
| 223 | signal.alarm(1) |
| 224 | def alarm2(sig, frame): |
| 225 | t.start() |
| 226 | |
| 227 | large_data = item * N |
| 228 | signal.signal(signal.SIGALRM, alarm1) |
| 229 | try: |
| 230 | wio = self.io.open(w, **fdopen_kwargs) |
| 231 | signal.alarm(1) |
| 232 | # Expected behaviour: |
| 233 | # - first raw write() is partial (because of the limited pipe buffer |
| 234 | # and the first alarm) |
| 235 | # - second raw write() returns EINTR (because of the second alarm) |
| 236 | # - subsequent write()s are successful (either partial or complete) |
| 237 | written = wio.write(large_data) |
| 238 | self.assertEqual(N, written) |
| 239 | |
| 240 | wio.flush() |
| 241 | write_finished = True |
| 242 | t.join() |
| 243 | |
| 244 | self.assertIsNone(error) |
| 245 | self.assertEqual(N, sum(len(x) for x in read_results)) |
| 246 | finally: |
| 247 | signal.alarm(0) |
| 248 | write_finished = True |
| 249 | os.close(w) |
no test coverage detected