| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | """
 | 
					
						
							|  |  |  | This test suite exercises some system calls subject to interruption with EINTR, | 
					
						
							|  |  |  | to check that it is actually handled transparently. | 
					
						
							|  |  |  | It is intended to be run by the main test suite within a child process, to | 
					
						
							|  |  |  | ensure there is no background thread running (so that signals are delivered to | 
					
						
							|  |  |  | the correct thread). | 
					
						
							|  |  |  | Signals are generated in-process using setitimer(ITIMER_REAL), which allows | 
					
						
							|  |  |  | sub-second periodicity (contrarily to signal()). | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  | import contextlib | 
					
						
							| 
									
										
										
										
											2015-10-01 13:16:43 +02:00
										 |  |  | import faulthandler | 
					
						
							| 
									
										
										
										
											2018-11-23 16:46:12 +01:00
										 |  |  | import fcntl | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2019-02-14 19:22:35 +01:00
										 |  |  | import platform | 
					
						
							| 
									
										
										
										
											2015-03-30 21:16:11 +02:00
										 |  |  | import select | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | import signal | 
					
						
							|  |  |  | import socket | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  | import subprocess | 
					
						
							|  |  |  | import sys | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | import time | 
					
						
							|  |  |  | import unittest | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from test import support | 
					
						
							| 
									
										
										
										
											2020-08-04 23:51:43 +08:00
										 |  |  | from test.support import os_helper | 
					
						
							| 
									
										
										
										
											2020-04-25 10:06:29 +03:00
										 |  |  | from test.support import socket_helper | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  | @contextlib.contextmanager | 
					
						
							|  |  |  | def kill_on_error(proc): | 
					
						
							|  |  |  |     """Context manager killing the subprocess if a Python exception is raised.""" | 
					
						
							|  |  |  |     with proc: | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             yield proc | 
					
						
							|  |  |  |         except: | 
					
						
							|  |  |  |             proc.kill() | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | @unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()") | 
					
						
							|  |  |  | class EINTRBaseTest(unittest.TestCase): | 
					
						
							|  |  |  |     """ Base class for EINTR tests. """ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # delay for initial signal delivery | 
					
						
							|  |  |  |     signal_delay = 0.1 | 
					
						
							|  |  |  |     # signal delivery periodicity | 
					
						
							|  |  |  |     signal_period = 0.1 | 
					
						
							|  |  |  |     # default sleep time for tests - should obviously have: | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |     # sleep_time > signal_period | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  |     sleep_time = 0.2 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-21 16:33:13 +01:00
										 |  |  |     def sighandler(self, signum, frame): | 
					
						
							|  |  |  |         self.signals += 1 | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-21 16:33:13 +01:00
										 |  |  |     def setUp(self): | 
					
						
							|  |  |  |         self.signals = 0 | 
					
						
							|  |  |  |         self.orig_handler = signal.signal(signal.SIGALRM, self.sighandler) | 
					
						
							|  |  |  |         signal.setitimer(signal.ITIMER_REAL, self.signal_delay, | 
					
						
							|  |  |  |                          self.signal_period) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Use faulthandler as watchdog to debug when a test hangs | 
					
						
							|  |  |  |         # (timeout of 10 minutes) | 
					
						
							| 
									
										
										
										
											2019-09-18 14:15:10 +02:00
										 |  |  |         faulthandler.dump_traceback_later(10 * 60, exit=True, | 
					
						
							|  |  |  |                                           file=sys.__stderr__) | 
					
						
							| 
									
										
										
										
											2015-10-01 13:16:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-21 16:33:13 +01:00
										 |  |  |     @staticmethod | 
					
						
							|  |  |  |     def stop_alarm(): | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  |         signal.setitimer(signal.ITIMER_REAL, 0, 0) | 
					
						
							| 
									
										
										
										
											2015-03-30 21:38:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-21 16:33:13 +01:00
										 |  |  |     def tearDown(self): | 
					
						
							|  |  |  |         self.stop_alarm() | 
					
						
							|  |  |  |         signal.signal(signal.SIGALRM, self.orig_handler) | 
					
						
							| 
									
										
										
										
											2019-09-18 14:15:10 +02:00
										 |  |  |         faulthandler.cancel_dump_traceback_later() | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |     def subprocess(self, *args, **kw): | 
					
						
							|  |  |  |         cmd_args = (sys.executable, '-c') + args | 
					
						
							|  |  |  |         return subprocess.Popen(cmd_args, **kw) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | @unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()") | 
					
						
							|  |  |  | class OSEINTRTest(EINTRBaseTest): | 
					
						
							|  |  |  |     """ EINTR tests for the os module. """ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |     def new_sleep_process(self): | 
					
						
							|  |  |  |         code = 'import time; time.sleep(%r)' % self.sleep_time | 
					
						
							|  |  |  |         return self.subprocess(code) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  |     def _test_wait_multiple(self, wait_func): | 
					
						
							|  |  |  |         num = 3 | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |         processes = [self.new_sleep_process() for _ in range(num)] | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  |         for _ in range(num): | 
					
						
							|  |  |  |             wait_func() | 
					
						
							| 
									
										
										
										
											2016-09-10 04:19:48 -04:00
										 |  |  |         # Call the Popen method to avoid a ResourceWarning | 
					
						
							|  |  |  |         for proc in processes: | 
					
						
							|  |  |  |             proc.wait() | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_wait(self): | 
					
						
							|  |  |  |         self._test_wait_multiple(os.wait) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @unittest.skipUnless(hasattr(os, 'wait3'), 'requires wait3()') | 
					
						
							|  |  |  |     def test_wait3(self): | 
					
						
							|  |  |  |         self._test_wait_multiple(lambda: os.wait3(0)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _test_wait_single(self, wait_func): | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |         proc = self.new_sleep_process() | 
					
						
							|  |  |  |         wait_func(proc.pid) | 
					
						
							| 
									
										
										
										
											2016-09-10 04:19:48 -04:00
										 |  |  |         # Call the Popen method to avoid a ResourceWarning | 
					
						
							|  |  |  |         proc.wait() | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_waitpid(self): | 
					
						
							|  |  |  |         self._test_wait_single(lambda pid: os.waitpid(pid, 0)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @unittest.skipUnless(hasattr(os, 'wait4'), 'requires wait4()') | 
					
						
							|  |  |  |     def test_wait4(self): | 
					
						
							|  |  |  |         self._test_wait_single(lambda pid: os.wait4(pid, 0)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_read(self): | 
					
						
							|  |  |  |         rd, wr = os.pipe() | 
					
						
							|  |  |  |         self.addCleanup(os.close, rd) | 
					
						
							|  |  |  |         # wr closed explicitly by parent | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # the payload below are smaller than PIPE_BUF, hence the writes will be | 
					
						
							|  |  |  |         # atomic | 
					
						
							|  |  |  |         datas = [b"hello", b"world", b"spam"] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |         code = '\n'.join(( | 
					
						
							|  |  |  |             'import os, sys, time', | 
					
						
							|  |  |  |             '', | 
					
						
							|  |  |  |             'wr = int(sys.argv[1])', | 
					
						
							|  |  |  |             'datas = %r' % datas, | 
					
						
							|  |  |  |             'sleep_time = %r' % self.sleep_time, | 
					
						
							|  |  |  |             '', | 
					
						
							|  |  |  |             'for data in datas:', | 
					
						
							|  |  |  |             '    # let the parent block on read()', | 
					
						
							|  |  |  |             '    time.sleep(sleep_time)', | 
					
						
							|  |  |  |             '    os.write(wr, data)', | 
					
						
							|  |  |  |         )) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         proc = self.subprocess(code, str(wr), pass_fds=[wr]) | 
					
						
							|  |  |  |         with kill_on_error(proc): | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  |             os.close(wr) | 
					
						
							|  |  |  |             for data in datas: | 
					
						
							|  |  |  |                 self.assertEqual(data, os.read(rd, len(data))) | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |             self.assertEqual(proc.wait(), 0) | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_write(self): | 
					
						
							|  |  |  |         rd, wr = os.pipe() | 
					
						
							|  |  |  |         self.addCleanup(os.close, wr) | 
					
						
							|  |  |  |         # rd closed explicitly by parent | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # we must write enough data for the write() to block | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |         data = b"x" * support.PIPE_MAX_SIZE | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         code = '\n'.join(( | 
					
						
							|  |  |  |             'import io, os, sys, time', | 
					
						
							|  |  |  |             '', | 
					
						
							|  |  |  |             'rd = int(sys.argv[1])', | 
					
						
							|  |  |  |             'sleep_time = %r' % self.sleep_time, | 
					
						
							|  |  |  |             'data = b"x" * %s' % support.PIPE_MAX_SIZE, | 
					
						
							|  |  |  |             'data_len = len(data)', | 
					
						
							|  |  |  |             '', | 
					
						
							|  |  |  |             '# let the parent block on write()', | 
					
						
							|  |  |  |             'time.sleep(sleep_time)', | 
					
						
							|  |  |  |             '', | 
					
						
							|  |  |  |             'read_data = io.BytesIO()', | 
					
						
							|  |  |  |             'while len(read_data.getvalue()) < data_len:', | 
					
						
							|  |  |  |             '    chunk = os.read(rd, 2 * data_len)', | 
					
						
							|  |  |  |             '    read_data.write(chunk)', | 
					
						
							|  |  |  |             '', | 
					
						
							|  |  |  |             'value = read_data.getvalue()', | 
					
						
							|  |  |  |             'if value != data:', | 
					
						
							|  |  |  |             '    raise Exception("read error: %s vs %s bytes"', | 
					
						
							|  |  |  |             '                    % (len(value), data_len))', | 
					
						
							|  |  |  |         )) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         proc = self.subprocess(code, str(rd), pass_fds=[rd]) | 
					
						
							|  |  |  |         with kill_on_error(proc): | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  |             os.close(rd) | 
					
						
							|  |  |  |             written = 0 | 
					
						
							|  |  |  |             while written < len(data): | 
					
						
							|  |  |  |                 written += os.write(wr, memoryview(data)[written:]) | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |             self.assertEqual(proc.wait(), 0) | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()") | 
					
						
							|  |  |  | class SocketEINTRTest(EINTRBaseTest): | 
					
						
							|  |  |  |     """ EINTR tests for the socket module. """ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @unittest.skipUnless(hasattr(socket, 'socketpair'), 'needs socketpair()') | 
					
						
							|  |  |  |     def _test_recv(self, recv_func): | 
					
						
							|  |  |  |         rd, wr = socket.socketpair() | 
					
						
							|  |  |  |         self.addCleanup(rd.close) | 
					
						
							|  |  |  |         # wr closed explicitly by parent | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # single-byte payload guard us against partial recv | 
					
						
							|  |  |  |         datas = [b"x", b"y", b"z"] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |         code = '\n'.join(( | 
					
						
							|  |  |  |             'import os, socket, sys, time', | 
					
						
							|  |  |  |             '', | 
					
						
							|  |  |  |             'fd = int(sys.argv[1])', | 
					
						
							|  |  |  |             'family = %s' % int(wr.family), | 
					
						
							|  |  |  |             'sock_type = %s' % int(wr.type), | 
					
						
							|  |  |  |             'datas = %r' % datas, | 
					
						
							|  |  |  |             'sleep_time = %r' % self.sleep_time, | 
					
						
							|  |  |  |             '', | 
					
						
							|  |  |  |             'wr = socket.fromfd(fd, family, sock_type)', | 
					
						
							|  |  |  |             'os.close(fd)', | 
					
						
							|  |  |  |             '', | 
					
						
							|  |  |  |             'with wr:', | 
					
						
							|  |  |  |             '    for data in datas:', | 
					
						
							|  |  |  |             '        # let the parent block on recv()', | 
					
						
							|  |  |  |             '        time.sleep(sleep_time)', | 
					
						
							|  |  |  |             '        wr.sendall(data)', | 
					
						
							|  |  |  |         )) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         fd = wr.fileno() | 
					
						
							|  |  |  |         proc = self.subprocess(code, str(fd), pass_fds=[fd]) | 
					
						
							|  |  |  |         with kill_on_error(proc): | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  |             wr.close() | 
					
						
							|  |  |  |             for data in datas: | 
					
						
							|  |  |  |                 self.assertEqual(data, recv_func(rd, len(data))) | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |             self.assertEqual(proc.wait(), 0) | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_recv(self): | 
					
						
							|  |  |  |         self._test_recv(socket.socket.recv) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @unittest.skipUnless(hasattr(socket.socket, 'recvmsg'), 'needs recvmsg()') | 
					
						
							|  |  |  |     def test_recvmsg(self): | 
					
						
							|  |  |  |         self._test_recv(lambda sock, data: sock.recvmsg(data)[0]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _test_send(self, send_func): | 
					
						
							|  |  |  |         rd, wr = socket.socketpair() | 
					
						
							|  |  |  |         self.addCleanup(wr.close) | 
					
						
							|  |  |  |         # rd closed explicitly by parent | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # we must send enough data for the send() to block | 
					
						
							|  |  |  |         data = b"xyz" * (support.SOCK_MAX_SIZE // 3) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |         code = '\n'.join(( | 
					
						
							|  |  |  |             'import os, socket, sys, time', | 
					
						
							|  |  |  |             '', | 
					
						
							|  |  |  |             'fd = int(sys.argv[1])', | 
					
						
							|  |  |  |             'family = %s' % int(rd.family), | 
					
						
							|  |  |  |             'sock_type = %s' % int(rd.type), | 
					
						
							|  |  |  |             'sleep_time = %r' % self.sleep_time, | 
					
						
							|  |  |  |             'data = b"xyz" * %s' % (support.SOCK_MAX_SIZE // 3), | 
					
						
							|  |  |  |             'data_len = len(data)', | 
					
						
							|  |  |  |             '', | 
					
						
							|  |  |  |             'rd = socket.fromfd(fd, family, sock_type)', | 
					
						
							|  |  |  |             'os.close(fd)', | 
					
						
							|  |  |  |             '', | 
					
						
							|  |  |  |             'with rd:', | 
					
						
							|  |  |  |             '    # let the parent block on send()', | 
					
						
							|  |  |  |             '    time.sleep(sleep_time)', | 
					
						
							|  |  |  |             '', | 
					
						
							|  |  |  |             '    received_data = bytearray(data_len)', | 
					
						
							|  |  |  |             '    n = 0', | 
					
						
							|  |  |  |             '    while n < data_len:', | 
					
						
							|  |  |  |             '        n += rd.recv_into(memoryview(received_data)[n:])', | 
					
						
							|  |  |  |             '', | 
					
						
							|  |  |  |             'if received_data != data:', | 
					
						
							|  |  |  |             '    raise Exception("recv error: %s vs %s bytes"', | 
					
						
							|  |  |  |             '                    % (len(received_data), data_len))', | 
					
						
							|  |  |  |         )) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         fd = rd.fileno() | 
					
						
							|  |  |  |         proc = self.subprocess(code, str(fd), pass_fds=[fd]) | 
					
						
							|  |  |  |         with kill_on_error(proc): | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  |             rd.close() | 
					
						
							|  |  |  |             written = 0 | 
					
						
							|  |  |  |             while written < len(data): | 
					
						
							|  |  |  |                 sent = send_func(wr, memoryview(data)[written:]) | 
					
						
							|  |  |  |                 # sendall() returns None | 
					
						
							|  |  |  |                 written += len(data) if sent is None else sent | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |             self.assertEqual(proc.wait(), 0) | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_send(self): | 
					
						
							|  |  |  |         self._test_send(socket.socket.send) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_sendall(self): | 
					
						
							|  |  |  |         self._test_send(socket.socket.sendall) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @unittest.skipUnless(hasattr(socket.socket, 'sendmsg'), 'needs sendmsg()') | 
					
						
							|  |  |  |     def test_sendmsg(self): | 
					
						
							|  |  |  |         self._test_send(lambda sock, data: sock.sendmsg([data])) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_accept(self): | 
					
						
							| 
									
										
										
										
											2020-04-25 10:06:29 +03:00
										 |  |  |         sock = socket.create_server((socket_helper.HOST, 0)) | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  |         self.addCleanup(sock.close) | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |         port = sock.getsockname()[1] | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |         code = '\n'.join(( | 
					
						
							|  |  |  |             'import socket, time', | 
					
						
							|  |  |  |             '', | 
					
						
							| 
									
										
										
										
											2020-04-25 10:06:29 +03:00
										 |  |  |             'host = %r' % socket_helper.HOST, | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |             'port = %s' % port, | 
					
						
							|  |  |  |             'sleep_time = %r' % self.sleep_time, | 
					
						
							|  |  |  |             '', | 
					
						
							|  |  |  |             '# let parent block on accept()', | 
					
						
							|  |  |  |             'time.sleep(sleep_time)', | 
					
						
							|  |  |  |             'with socket.create_connection((host, port)):', | 
					
						
							|  |  |  |             '    time.sleep(sleep_time)', | 
					
						
							|  |  |  |         )) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         proc = self.subprocess(code) | 
					
						
							|  |  |  |         with kill_on_error(proc): | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  |             client_sock, _ = sock.accept() | 
					
						
							|  |  |  |             client_sock.close() | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |             self.assertEqual(proc.wait(), 0) | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |     # Issue #25122: There is a race condition in the FreeBSD kernel on | 
					
						
							|  |  |  |     # handling signals in the FIFO device. Skip the test until the bug is | 
					
						
							| 
									
										
										
										
											2015-09-21 14:05:02 +02:00
										 |  |  |     # fixed in the kernel. | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |     # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=203162 | 
					
						
							| 
									
										
										
										
											2015-09-21 14:05:02 +02:00
										 |  |  |     @support.requires_freebsd_version(10, 3) | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  |     @unittest.skipUnless(hasattr(os, 'mkfifo'), 'needs mkfifo()') | 
					
						
							|  |  |  |     def _test_open(self, do_open_close_reader, do_open_close_writer): | 
					
						
							| 
									
										
										
										
											2020-08-04 23:51:43 +08:00
										 |  |  |         filename = os_helper.TESTFN | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  |         # Use a fifo: until the child opens it for reading, the parent will | 
					
						
							|  |  |  |         # block when trying to open it for writing. | 
					
						
							| 
									
										
										
										
											2020-08-04 23:51:43 +08:00
										 |  |  |         os_helper.unlink(filename) | 
					
						
							| 
									
										
										
										
											2017-11-12 17:31:07 +01:00
										 |  |  |         try: | 
					
						
							|  |  |  |             os.mkfifo(filename) | 
					
						
							|  |  |  |         except PermissionError as e: | 
					
						
							|  |  |  |             self.skipTest('os.mkfifo(): %s' % e) | 
					
						
							| 
									
										
										
										
											2020-08-04 23:51:43 +08:00
										 |  |  |         self.addCleanup(os_helper.unlink, filename) | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         code = '\n'.join(( | 
					
						
							|  |  |  |             'import os, time', | 
					
						
							|  |  |  |             '', | 
					
						
							|  |  |  |             'path = %a' % filename, | 
					
						
							|  |  |  |             'sleep_time = %r' % self.sleep_time, | 
					
						
							|  |  |  |             '', | 
					
						
							|  |  |  |             '# let the parent block', | 
					
						
							|  |  |  |             'time.sleep(sleep_time)', | 
					
						
							|  |  |  |             '', | 
					
						
							|  |  |  |             do_open_close_reader, | 
					
						
							|  |  |  |         )) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         proc = self.subprocess(code) | 
					
						
							|  |  |  |         with kill_on_error(proc): | 
					
						
							|  |  |  |             do_open_close_writer(filename) | 
					
						
							|  |  |  |             self.assertEqual(proc.wait(), 0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def python_open(self, path): | 
					
						
							|  |  |  |         fp = open(path, 'w') | 
					
						
							|  |  |  |         fp.close() | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-04 17:13:33 +01:00
										 |  |  |     @unittest.skipIf(sys.platform == "darwin", | 
					
						
							|  |  |  |                      "hangs under macOS; see bpo-25234, bpo-35363") | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  |     def test_open(self): | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |         self._test_open("fp = open(path, 'r')\nfp.close()", | 
					
						
							|  |  |  |                         self.python_open) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def os_open(self, path): | 
					
						
							|  |  |  |         fd = os.open(path, os.O_WRONLY) | 
					
						
							|  |  |  |         os.close(fd) | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-04 17:13:33 +01:00
										 |  |  |     @unittest.skipIf(sys.platform == "darwin", | 
					
						
							|  |  |  |                      "hangs under macOS; see bpo-25234, bpo-35363") | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  |     def test_os_open(self): | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |         self._test_open("fd = os.open(path, os.O_RDONLY)\nos.close(fd)", | 
					
						
							|  |  |  |                         self.os_open) | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-19 21:54:09 +01:00
										 |  |  | @unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()") | 
					
						
							|  |  |  | class TimeEINTRTest(EINTRBaseTest): | 
					
						
							|  |  |  |     """ EINTR tests for the time module. """ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_sleep(self): | 
					
						
							|  |  |  |         t0 = time.monotonic() | 
					
						
							| 
									
										
										
										
											2015-03-20 01:42:20 +01:00
										 |  |  |         time.sleep(self.sleep_time) | 
					
						
							| 
									
										
										
										
											2015-03-30 21:38:00 +02:00
										 |  |  |         self.stop_alarm() | 
					
						
							| 
									
										
										
										
											2015-03-19 21:54:09 +01:00
										 |  |  |         dt = time.monotonic() - t0 | 
					
						
							| 
									
										
										
										
											2015-03-20 01:42:20 +01:00
										 |  |  |         self.assertGreaterEqual(dt, self.sleep_time) | 
					
						
							| 
									
										
										
										
											2015-03-19 21:54:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-20 12:54:28 +01:00
										 |  |  | @unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()") | 
					
						
							| 
									
										
										
										
											2017-05-10 02:37:42 +02:00
										 |  |  | # bpo-30320: Need pthread_sigmask() to block the signal, otherwise the test | 
					
						
							|  |  |  | # is vulnerable to a race condition between the child and the parent processes. | 
					
						
							|  |  |  | @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'), | 
					
						
							|  |  |  |                      'need signal.pthread_sigmask()') | 
					
						
							| 
									
										
										
										
											2015-03-20 12:54:28 +01:00
										 |  |  | class SignalEINTRTest(EINTRBaseTest): | 
					
						
							|  |  |  |     """ EINTR tests for the signal module. """ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-10 02:37:42 +02:00
										 |  |  |     def check_sigwait(self, wait_func): | 
					
						
							| 
									
										
										
										
											2015-03-20 12:54:28 +01:00
										 |  |  |         signum = signal.SIGUSR1 | 
					
						
							|  |  |  |         pid = os.getpid() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         old_handler = signal.signal(signum, lambda *args: None) | 
					
						
							|  |  |  |         self.addCleanup(signal.signal, signum, old_handler) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |         code = '\n'.join(( | 
					
						
							|  |  |  |             'import os, time', | 
					
						
							|  |  |  |             'pid = %s' % os.getpid(), | 
					
						
							|  |  |  |             'signum = %s' % int(signum), | 
					
						
							|  |  |  |             'sleep_time = %r' % self.sleep_time, | 
					
						
							|  |  |  |             'time.sleep(sleep_time)', | 
					
						
							|  |  |  |             'os.kill(pid, signum)', | 
					
						
							|  |  |  |         )) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-10 02:37:42 +02:00
										 |  |  |         old_mask = signal.pthread_sigmask(signal.SIG_BLOCK, [signum]) | 
					
						
							|  |  |  |         self.addCleanup(signal.pthread_sigmask, signal.SIG_UNBLOCK, [signum]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-20 12:54:28 +01:00
										 |  |  |         t0 = time.monotonic() | 
					
						
							| 
									
										
										
										
											2017-05-10 02:37:42 +02:00
										 |  |  |         proc = self.subprocess(code) | 
					
						
							| 
									
										
										
										
											2015-09-18 11:29:16 +02:00
										 |  |  |         with kill_on_error(proc): | 
					
						
							| 
									
										
										
										
											2017-05-10 02:37:42 +02:00
										 |  |  |             wait_func(signum) | 
					
						
							|  |  |  |             dt = time.monotonic() - t0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(proc.wait(), 0) | 
					
						
							| 
									
										
										
										
											2015-12-15 11:29:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-10 02:37:42 +02:00
										 |  |  |     @unittest.skipUnless(hasattr(signal, 'sigwaitinfo'), | 
					
						
							|  |  |  |                          'need signal.sigwaitinfo()') | 
					
						
							|  |  |  |     def test_sigwaitinfo(self): | 
					
						
							|  |  |  |         def wait_func(signum): | 
					
						
							| 
									
										
										
										
											2015-03-20 12:54:28 +01:00
										 |  |  |             signal.sigwaitinfo([signum]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-10 02:37:42 +02:00
										 |  |  |         self.check_sigwait(wait_func) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @unittest.skipUnless(hasattr(signal, 'sigtimedwait'), | 
					
						
							|  |  |  |                          'need signal.sigwaitinfo()') | 
					
						
							|  |  |  |     def test_sigtimedwait(self): | 
					
						
							|  |  |  |         def wait_func(signum): | 
					
						
							|  |  |  |             signal.sigtimedwait([signum], 120.0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.check_sigwait(wait_func) | 
					
						
							| 
									
										
										
										
											2015-03-20 12:54:28 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-30 21:16:11 +02:00
										 |  |  | @unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()") | 
					
						
							|  |  |  | class SelectEINTRTest(EINTRBaseTest): | 
					
						
							|  |  |  |     """ EINTR tests for the select module. """ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_select(self): | 
					
						
							|  |  |  |         t0 = time.monotonic() | 
					
						
							|  |  |  |         select.select([], [], [], self.sleep_time) | 
					
						
							| 
									
										
										
										
											2015-03-30 21:38:00 +02:00
										 |  |  |         dt = time.monotonic() - t0 | 
					
						
							| 
									
										
										
										
											2015-03-31 11:48:34 +02:00
										 |  |  |         self.stop_alarm() | 
					
						
							| 
									
										
										
										
											2015-03-30 21:38:00 +02:00
										 |  |  |         self.assertGreaterEqual(dt, self.sleep_time) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-04 06:00:37 -05:00
										 |  |  |     @unittest.skipIf(sys.platform == "darwin", | 
					
						
							|  |  |  |                      "poll may fail on macOS; see issue #28087") | 
					
						
							| 
									
										
										
										
											2015-03-30 21:38:00 +02:00
										 |  |  |     @unittest.skipUnless(hasattr(select, 'poll'), 'need select.poll') | 
					
						
							|  |  |  |     def test_poll(self): | 
					
						
							|  |  |  |         poller = select.poll() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         t0 = time.monotonic() | 
					
						
							|  |  |  |         poller.poll(self.sleep_time * 1e3) | 
					
						
							| 
									
										
										
										
											2015-03-30 21:16:11 +02:00
										 |  |  |         dt = time.monotonic() - t0 | 
					
						
							| 
									
										
										
										
											2015-03-31 11:48:34 +02:00
										 |  |  |         self.stop_alarm() | 
					
						
							| 
									
										
										
										
											2015-03-30 21:16:11 +02:00
										 |  |  |         self.assertGreaterEqual(dt, self.sleep_time) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-30 21:59:21 +02:00
										 |  |  |     @unittest.skipUnless(hasattr(select, 'epoll'), 'need select.epoll') | 
					
						
							|  |  |  |     def test_epoll(self): | 
					
						
							|  |  |  |         poller = select.epoll() | 
					
						
							|  |  |  |         self.addCleanup(poller.close) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         t0 = time.monotonic() | 
					
						
							|  |  |  |         poller.poll(self.sleep_time) | 
					
						
							| 
									
										
										
										
											2015-03-31 11:48:34 +02:00
										 |  |  |         dt = time.monotonic() - t0 | 
					
						
							| 
									
										
										
										
											2015-03-30 21:59:21 +02:00
										 |  |  |         self.stop_alarm() | 
					
						
							| 
									
										
										
										
											2015-03-31 11:48:34 +02:00
										 |  |  |         self.assertGreaterEqual(dt, self.sleep_time) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @unittest.skipUnless(hasattr(select, 'kqueue'), 'need select.kqueue') | 
					
						
							|  |  |  |     def test_kqueue(self): | 
					
						
							|  |  |  |         kqueue = select.kqueue() | 
					
						
							|  |  |  |         self.addCleanup(kqueue.close) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         t0 = time.monotonic() | 
					
						
							|  |  |  |         kqueue.control(None, 1, self.sleep_time) | 
					
						
							| 
									
										
										
										
											2015-03-30 21:59:21 +02:00
										 |  |  |         dt = time.monotonic() - t0 | 
					
						
							| 
									
										
										
										
											2015-03-31 11:48:34 +02:00
										 |  |  |         self.stop_alarm() | 
					
						
							| 
									
										
										
										
											2015-03-30 21:59:21 +02:00
										 |  |  |         self.assertGreaterEqual(dt, self.sleep_time) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-31 12:10:33 +02:00
										 |  |  |     @unittest.skipUnless(hasattr(select, 'devpoll'), 'need select.devpoll') | 
					
						
							|  |  |  |     def test_devpoll(self): | 
					
						
							|  |  |  |         poller = select.devpoll() | 
					
						
							|  |  |  |         self.addCleanup(poller.close) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         t0 = time.monotonic() | 
					
						
							|  |  |  |         poller.poll(self.sleep_time * 1e3) | 
					
						
							|  |  |  |         dt = time.monotonic() - t0 | 
					
						
							|  |  |  |         self.stop_alarm() | 
					
						
							|  |  |  |         self.assertGreaterEqual(dt, self.sleep_time) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-30 21:16:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-23 16:46:12 +01:00
										 |  |  | class FNTLEINTRTest(EINTRBaseTest): | 
					
						
							|  |  |  |     def _lock(self, lock_func, lock_name): | 
					
						
							| 
									
										
										
										
											2020-08-04 23:51:43 +08:00
										 |  |  |         self.addCleanup(os_helper.unlink, os_helper.TESTFN) | 
					
						
							| 
									
										
										
										
											2018-11-23 16:46:12 +01:00
										 |  |  |         code = '\n'.join(( | 
					
						
							|  |  |  |             "import fcntl, time", | 
					
						
							| 
									
										
										
										
											2020-08-04 23:51:43 +08:00
										 |  |  |             "with open('%s', 'wb') as f:" % os_helper.TESTFN, | 
					
						
							| 
									
										
										
										
											2018-11-23 16:46:12 +01:00
										 |  |  |             "   fcntl.%s(f, fcntl.LOCK_EX)" % lock_name, | 
					
						
							|  |  |  |             "   time.sleep(%s)" % self.sleep_time)) | 
					
						
							|  |  |  |         start_time = time.monotonic() | 
					
						
							|  |  |  |         proc = self.subprocess(code) | 
					
						
							|  |  |  |         with kill_on_error(proc): | 
					
						
							| 
									
										
										
										
											2020-08-04 23:51:43 +08:00
										 |  |  |             with open(os_helper.TESTFN, 'wb') as f: | 
					
						
							| 
									
										
										
										
											2018-11-23 16:46:12 +01:00
										 |  |  |                 while True:  # synchronize the subprocess | 
					
						
							|  |  |  |                     dt = time.monotonic() - start_time | 
					
						
							|  |  |  |                     if dt > 60.0: | 
					
						
							|  |  |  |                         raise Exception("failed to sync child in %.1f sec" % dt) | 
					
						
							|  |  |  |                     try: | 
					
						
							|  |  |  |                         lock_func(f, fcntl.LOCK_EX | fcntl.LOCK_NB) | 
					
						
							|  |  |  |                         lock_func(f, fcntl.LOCK_UN) | 
					
						
							|  |  |  |                         time.sleep(0.01) | 
					
						
							|  |  |  |                     except BlockingIOError: | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |                 # the child locked the file just a moment ago for 'sleep_time' seconds | 
					
						
							|  |  |  |                 # that means that the lock below will block for 'sleep_time' minus some | 
					
						
							|  |  |  |                 # potential context switch delay | 
					
						
							|  |  |  |                 lock_func(f, fcntl.LOCK_EX) | 
					
						
							|  |  |  |                 dt = time.monotonic() - start_time | 
					
						
							|  |  |  |                 self.assertGreaterEqual(dt, self.sleep_time) | 
					
						
							|  |  |  |                 self.stop_alarm() | 
					
						
							|  |  |  |             proc.wait() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-14 19:22:35 +01:00
										 |  |  |     # Issue 35633: See https://bugs.python.org/issue35633#msg333662 | 
					
						
							|  |  |  |     # skip test rather than accept PermissionError from all platforms | 
					
						
							|  |  |  |     @unittest.skipIf(platform.system() == "AIX", "AIX returns PermissionError") | 
					
						
							| 
									
										
										
										
											2018-11-23 16:46:12 +01:00
										 |  |  |     def test_lockf(self): | 
					
						
							|  |  |  |         self._lock(fcntl.lockf, "lockf") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_flock(self): | 
					
						
							|  |  |  |         self._lock(fcntl.flock, "flock") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-07 13:27:50 +00:00
										 |  |  | if __name__ == "__main__": | 
					
						
							| 
									
										
										
										
											2018-11-21 16:33:13 +01:00
										 |  |  |     unittest.main() |