(original) (raw)

# HG changeset patch # Parent 19488e23dcdb74faab92eaf46a2d642dd85be8ef Issue #22331: Send signal after reading to avoid race Also, close the read end of the pipe first to avoid the EBADF hack. diff -r 19488e23dcdb Lib/test/test_io.py --- a/Lib/test/test_io.py Fri Jun 03 10:48:43 2016 +0300 +++ b/Lib/test/test_io.py Fri Jun 03 13:40:41 2016 +0000 @@ -3693,46 +3693,40 @@ invokes the signal handler, and bubbles up the exception raised in the latter.""" read_results = [] + main_thread = threading.get_ident() def _read(): - if hasattr(signal, 'pthread_sigmask'): - signal.pthread_sigmask(signal.SIG_BLOCK, [signal.SIGALRM]) s = os.read(r, 1) read_results.append(s) + # The main thread is very likely to be inside the write() syscall + # now, so interrupt it + signal.pthread_kill(main_thread, signal.SIGALRM) t = threading.Thread(target=_read) t.daemon = True r, w = os.pipe() - fdopen_kwargs["closefd"] = False large_data = item * (support.PIPE_MAX_SIZE // len(item) + 1) try: wio = self.io.open(w, **fdopen_kwargs) t.start() # Fill the pipe enough that the write will be blocking. - # It will be interrupted by the timer armed above. Since the + # It will be interrupted by the read thread above. Since the # other thread has read one byte, the low-level write will # return with a successful (partial) result rather than an EINTR. # The buffered IO layer must check for pending signal # handlers, which in this case will invoke alarm_interrupt(). - signal.alarm(1) try: self.assertRaises(ZeroDivisionError, wio.write, large_data) finally: - signal.alarm(0) t.join() # We got one byte, get another one and check that it isn't a # repeat of the first one. read_results.append(os.read(r, 1)) self.assertEqual(read_results, [bytes[0:1], bytes[1:2]]) finally: - os.close(w) os.close(r) - # This is deliberate. If we didn't close the file descriptor - # before closing wio, wio would try to flush its internal - # buffer, and block again. try: wio.close() - except OSError as e: - if e.errno != errno.EBADF: - raise + except BrokenPipeError: + pass def test_interrupted_write_unbuffered(self): self.check_interrupted_write(b"xy", b"xy", mode="wb", buffering=0)