ยปCore Development>Code coverage>Lib/test/eintrdata/eintr_tester.py

Python code coverage for Lib/test/eintrdata/eintr_tester.py

#countcontent
1n/a"""
2n/aThis test suite exercises some system calls subject to interruption with EINTR,
3n/ato check that it is actually handled transparently.
4n/aIt is intended to be run by the main test suite within a child process, to
5n/aensure there is no background thread running (so that signals are delivered to
6n/athe correct thread).
7n/aSignals are generated in-process using setitimer(ITIMER_REAL), which allows
8n/asub-second periodicity (contrarily to signal()).
9n/a"""
10n/a
11n/aimport contextlib
12n/aimport faulthandler
13n/aimport os
14n/aimport select
15n/aimport signal
16n/aimport socket
17n/aimport subprocess
18n/aimport sys
19n/aimport time
20n/aimport unittest
21n/a
22n/afrom test import support
23n/aandroid_not_root = support.android_not_root
24n/a
25n/a@contextlib.contextmanager
26n/adef kill_on_error(proc):
27n/a """Context manager killing the subprocess if a Python exception is raised."""
28n/a with proc:
29n/a try:
30n/a yield proc
31n/a except:
32n/a proc.kill()
33n/a raise
34n/a
35n/a
36n/a@unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()")
37n/aclass EINTRBaseTest(unittest.TestCase):
38n/a """ Base class for EINTR tests. """
39n/a
40n/a # delay for initial signal delivery
41n/a signal_delay = 0.1
42n/a # signal delivery periodicity
43n/a signal_period = 0.1
44n/a # default sleep time for tests - should obviously have:
45n/a # sleep_time > signal_period
46n/a sleep_time = 0.2
47n/a
48n/a @classmethod
49n/a def setUpClass(cls):
50n/a cls.orig_handler = signal.signal(signal.SIGALRM, lambda *args: None)
51n/a signal.setitimer(signal.ITIMER_REAL, cls.signal_delay,
52n/a cls.signal_period)
53n/a
54n/a # Issue #25277: Use faulthandler to try to debug a hang on FreeBSD
55n/a if hasattr(faulthandler, 'dump_traceback_later'):
56n/a faulthandler.dump_traceback_later(10 * 60, exit=True)
57n/a
58n/a @classmethod
59n/a def stop_alarm(cls):
60n/a signal.setitimer(signal.ITIMER_REAL, 0, 0)
61n/a
62n/a @classmethod
63n/a def tearDownClass(cls):
64n/a cls.stop_alarm()
65n/a signal.signal(signal.SIGALRM, cls.orig_handler)
66n/a if hasattr(faulthandler, 'cancel_dump_traceback_later'):
67n/a faulthandler.cancel_dump_traceback_later()
68n/a
69n/a def subprocess(self, *args, **kw):
70n/a cmd_args = (sys.executable, '-c') + args
71n/a return subprocess.Popen(cmd_args, **kw)
72n/a
73n/a
74n/a@unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()")
75n/aclass OSEINTRTest(EINTRBaseTest):
76n/a """ EINTR tests for the os module. """
77n/a
78n/a def new_sleep_process(self):
79n/a code = 'import time; time.sleep(%r)' % self.sleep_time
80n/a return self.subprocess(code)
81n/a
82n/a def _test_wait_multiple(self, wait_func):
83n/a num = 3
84n/a processes = [self.new_sleep_process() for _ in range(num)]
85n/a for _ in range(num):
86n/a wait_func()
87n/a # Call the Popen method to avoid a ResourceWarning
88n/a for proc in processes:
89n/a proc.wait()
90n/a
91n/a def test_wait(self):
92n/a self._test_wait_multiple(os.wait)
93n/a
94n/a @unittest.skipUnless(hasattr(os, 'wait3'), 'requires wait3()')
95n/a def test_wait3(self):
96n/a self._test_wait_multiple(lambda: os.wait3(0))
97n/a
98n/a def _test_wait_single(self, wait_func):
99n/a proc = self.new_sleep_process()
100n/a wait_func(proc.pid)
101n/a # Call the Popen method to avoid a ResourceWarning
102n/a proc.wait()
103n/a
104n/a def test_waitpid(self):
105n/a self._test_wait_single(lambda pid: os.waitpid(pid, 0))
106n/a
107n/a @unittest.skipUnless(hasattr(os, 'wait4'), 'requires wait4()')
108n/a def test_wait4(self):
109n/a self._test_wait_single(lambda pid: os.wait4(pid, 0))
110n/a
111n/a def test_read(self):
112n/a rd, wr = os.pipe()
113n/a self.addCleanup(os.close, rd)
114n/a # wr closed explicitly by parent
115n/a
116n/a # the payload below are smaller than PIPE_BUF, hence the writes will be
117n/a # atomic
118n/a datas = [b"hello", b"world", b"spam"]
119n/a
120n/a code = '\n'.join((
121n/a 'import os, sys, time',
122n/a '',
123n/a 'wr = int(sys.argv[1])',
124n/a 'datas = %r' % datas,
125n/a 'sleep_time = %r' % self.sleep_time,
126n/a '',
127n/a 'for data in datas:',
128n/a ' # let the parent block on read()',
129n/a ' time.sleep(sleep_time)',
130n/a ' os.write(wr, data)',
131n/a ))
132n/a
133n/a proc = self.subprocess(code, str(wr), pass_fds=[wr])
134n/a with kill_on_error(proc):
135n/a os.close(wr)
136n/a for data in datas:
137n/a self.assertEqual(data, os.read(rd, len(data)))
138n/a self.assertEqual(proc.wait(), 0)
139n/a
140n/a def test_write(self):
141n/a rd, wr = os.pipe()
142n/a self.addCleanup(os.close, wr)
143n/a # rd closed explicitly by parent
144n/a
145n/a # we must write enough data for the write() to block
146n/a data = b"x" * support.PIPE_MAX_SIZE
147n/a
148n/a code = '\n'.join((
149n/a 'import io, os, sys, time',
150n/a '',
151n/a 'rd = int(sys.argv[1])',
152n/a 'sleep_time = %r' % self.sleep_time,
153n/a 'data = b"x" * %s' % support.PIPE_MAX_SIZE,
154n/a 'data_len = len(data)',
155n/a '',
156n/a '# let the parent block on write()',
157n/a 'time.sleep(sleep_time)',
158n/a '',
159n/a 'read_data = io.BytesIO()',
160n/a 'while len(read_data.getvalue()) < data_len:',
161n/a ' chunk = os.read(rd, 2 * data_len)',
162n/a ' read_data.write(chunk)',
163n/a '',
164n/a 'value = read_data.getvalue()',
165n/a 'if value != data:',
166n/a ' raise Exception("read error: %s vs %s bytes"',
167n/a ' % (len(value), data_len))',
168n/a ))
169n/a
170n/a proc = self.subprocess(code, str(rd), pass_fds=[rd])
171n/a with kill_on_error(proc):
172n/a os.close(rd)
173n/a written = 0
174n/a while written < len(data):
175n/a written += os.write(wr, memoryview(data)[written:])
176n/a self.assertEqual(proc.wait(), 0)
177n/a
178n/a
179n/a@unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()")
180n/aclass SocketEINTRTest(EINTRBaseTest):
181n/a """ EINTR tests for the socket module. """
182n/a
183n/a @unittest.skipUnless(hasattr(socket, 'socketpair'), 'needs socketpair()')
184n/a def _test_recv(self, recv_func):
185n/a rd, wr = socket.socketpair()
186n/a self.addCleanup(rd.close)
187n/a # wr closed explicitly by parent
188n/a
189n/a # single-byte payload guard us against partial recv
190n/a datas = [b"x", b"y", b"z"]
191n/a
192n/a code = '\n'.join((
193n/a 'import os, socket, sys, time',
194n/a '',
195n/a 'fd = int(sys.argv[1])',
196n/a 'family = %s' % int(wr.family),
197n/a 'sock_type = %s' % int(wr.type),
198n/a 'datas = %r' % datas,
199n/a 'sleep_time = %r' % self.sleep_time,
200n/a '',
201n/a 'wr = socket.fromfd(fd, family, sock_type)',
202n/a 'os.close(fd)',
203n/a '',
204n/a 'with wr:',
205n/a ' for data in datas:',
206n/a ' # let the parent block on recv()',
207n/a ' time.sleep(sleep_time)',
208n/a ' wr.sendall(data)',
209n/a ))
210n/a
211n/a fd = wr.fileno()
212n/a proc = self.subprocess(code, str(fd), pass_fds=[fd])
213n/a with kill_on_error(proc):
214n/a wr.close()
215n/a for data in datas:
216n/a self.assertEqual(data, recv_func(rd, len(data)))
217n/a self.assertEqual(proc.wait(), 0)
218n/a
219n/a def test_recv(self):
220n/a self._test_recv(socket.socket.recv)
221n/a
222n/a @unittest.skipUnless(hasattr(socket.socket, 'recvmsg'), 'needs recvmsg()')
223n/a def test_recvmsg(self):
224n/a self._test_recv(lambda sock, data: sock.recvmsg(data)[0])
225n/a
226n/a def _test_send(self, send_func):
227n/a rd, wr = socket.socketpair()
228n/a self.addCleanup(wr.close)
229n/a # rd closed explicitly by parent
230n/a
231n/a # we must send enough data for the send() to block
232n/a data = b"xyz" * (support.SOCK_MAX_SIZE // 3)
233n/a
234n/a code = '\n'.join((
235n/a 'import os, socket, sys, time',
236n/a '',
237n/a 'fd = int(sys.argv[1])',
238n/a 'family = %s' % int(rd.family),
239n/a 'sock_type = %s' % int(rd.type),
240n/a 'sleep_time = %r' % self.sleep_time,
241n/a 'data = b"xyz" * %s' % (support.SOCK_MAX_SIZE // 3),
242n/a 'data_len = len(data)',
243n/a '',
244n/a 'rd = socket.fromfd(fd, family, sock_type)',
245n/a 'os.close(fd)',
246n/a '',
247n/a 'with rd:',
248n/a ' # let the parent block on send()',
249n/a ' time.sleep(sleep_time)',
250n/a '',
251n/a ' received_data = bytearray(data_len)',
252n/a ' n = 0',
253n/a ' while n < data_len:',
254n/a ' n += rd.recv_into(memoryview(received_data)[n:])',
255n/a '',
256n/a 'if received_data != data:',
257n/a ' raise Exception("recv error: %s vs %s bytes"',
258n/a ' % (len(received_data), data_len))',
259n/a ))
260n/a
261n/a fd = rd.fileno()
262n/a proc = self.subprocess(code, str(fd), pass_fds=[fd])
263n/a with kill_on_error(proc):
264n/a rd.close()
265n/a written = 0
266n/a while written < len(data):
267n/a sent = send_func(wr, memoryview(data)[written:])
268n/a # sendall() returns None
269n/a written += len(data) if sent is None else sent
270n/a self.assertEqual(proc.wait(), 0)
271n/a
272n/a def test_send(self):
273n/a self._test_send(socket.socket.send)
274n/a
275n/a def test_sendall(self):
276n/a self._test_send(socket.socket.sendall)
277n/a
278n/a @unittest.skipUnless(hasattr(socket.socket, 'sendmsg'), 'needs sendmsg()')
279n/a def test_sendmsg(self):
280n/a self._test_send(lambda sock, data: sock.sendmsg([data]))
281n/a
282n/a def test_accept(self):
283n/a sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
284n/a self.addCleanup(sock.close)
285n/a
286n/a sock.bind((support.HOST, 0))
287n/a port = sock.getsockname()[1]
288n/a sock.listen()
289n/a
290n/a code = '\n'.join((
291n/a 'import socket, time',
292n/a '',
293n/a 'host = %r' % support.HOST,
294n/a 'port = %s' % port,
295n/a 'sleep_time = %r' % self.sleep_time,
296n/a '',
297n/a '# let parent block on accept()',
298n/a 'time.sleep(sleep_time)',
299n/a 'with socket.create_connection((host, port)):',
300n/a ' time.sleep(sleep_time)',
301n/a ))
302n/a
303n/a proc = self.subprocess(code)
304n/a with kill_on_error(proc):
305n/a client_sock, _ = sock.accept()
306n/a client_sock.close()
307n/a self.assertEqual(proc.wait(), 0)
308n/a
309n/a # Issue #25122: There is a race condition in the FreeBSD kernel on
310n/a # handling signals in the FIFO device. Skip the test until the bug is
311n/a # fixed in the kernel.
312n/a # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=203162
313n/a @support.requires_freebsd_version(10, 3)
314n/a @unittest.skipUnless(hasattr(os, 'mkfifo'), 'needs mkfifo()')
315n/a @unittest.skipIf(android_not_root, "mkfifo not allowed, non root user")
316n/a def _test_open(self, do_open_close_reader, do_open_close_writer):
317n/a filename = support.TESTFN
318n/a
319n/a # Use a fifo: until the child opens it for reading, the parent will
320n/a # block when trying to open it for writing.
321n/a support.unlink(filename)
322n/a os.mkfifo(filename)
323n/a self.addCleanup(support.unlink, filename)
324n/a
325n/a code = '\n'.join((
326n/a 'import os, time',
327n/a '',
328n/a 'path = %a' % filename,
329n/a 'sleep_time = %r' % self.sleep_time,
330n/a '',
331n/a '# let the parent block',
332n/a 'time.sleep(sleep_time)',
333n/a '',
334n/a do_open_close_reader,
335n/a ))
336n/a
337n/a proc = self.subprocess(code)
338n/a with kill_on_error(proc):
339n/a do_open_close_writer(filename)
340n/a self.assertEqual(proc.wait(), 0)
341n/a
342n/a def python_open(self, path):
343n/a fp = open(path, 'w')
344n/a fp.close()
345n/a
346n/a def test_open(self):
347n/a self._test_open("fp = open(path, 'r')\nfp.close()",
348n/a self.python_open)
349n/a
350n/a @unittest.skipIf(sys.platform == 'darwin', "hangs under OS X; see issue #25234")
351n/a def os_open(self, path):
352n/a fd = os.open(path, os.O_WRONLY)
353n/a os.close(fd)
354n/a
355n/a @unittest.skipIf(sys.platform == "darwin", "hangs under OS X; see issue #25234")
356n/a def test_os_open(self):
357n/a self._test_open("fd = os.open(path, os.O_RDONLY)\nos.close(fd)",
358n/a self.os_open)
359n/a
360n/a
361n/a@unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()")
362n/aclass TimeEINTRTest(EINTRBaseTest):
363n/a """ EINTR tests for the time module. """
364n/a
365n/a def test_sleep(self):
366n/a t0 = time.monotonic()
367n/a time.sleep(self.sleep_time)
368n/a self.stop_alarm()
369n/a dt = time.monotonic() - t0
370n/a self.assertGreaterEqual(dt, self.sleep_time)
371n/a
372n/a
373n/a@unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()")
374n/aclass SignalEINTRTest(EINTRBaseTest):
375n/a """ EINTR tests for the signal module. """
376n/a
377n/a @unittest.skipUnless(hasattr(signal, 'sigtimedwait'),
378n/a 'need signal.sigtimedwait()')
379n/a def test_sigtimedwait(self):
380n/a t0 = time.monotonic()
381n/a signal.sigtimedwait([signal.SIGUSR1], self.sleep_time)
382n/a dt = time.monotonic() - t0
383n/a self.assertGreaterEqual(dt, self.sleep_time)
384n/a
385n/a @unittest.skipUnless(hasattr(signal, 'sigwaitinfo'),
386n/a 'need signal.sigwaitinfo()')
387n/a def test_sigwaitinfo(self):
388n/a # Issue #25277, #25868: give a few milliseconds to the parent process
389n/a # between os.write() and signal.sigwaitinfo() to works around a race
390n/a # condition
391n/a self.sleep_time = 0.100
392n/a
393n/a signum = signal.SIGUSR1
394n/a pid = os.getpid()
395n/a
396n/a old_handler = signal.signal(signum, lambda *args: None)
397n/a self.addCleanup(signal.signal, signum, old_handler)
398n/a
399n/a rpipe, wpipe = os.pipe()
400n/a
401n/a code = '\n'.join((
402n/a 'import os, time',
403n/a 'pid = %s' % os.getpid(),
404n/a 'signum = %s' % int(signum),
405n/a 'sleep_time = %r' % self.sleep_time,
406n/a 'rpipe = %r' % rpipe,
407n/a 'os.read(rpipe, 1)',
408n/a 'os.close(rpipe)',
409n/a 'time.sleep(sleep_time)',
410n/a 'os.kill(pid, signum)',
411n/a ))
412n/a
413n/a t0 = time.monotonic()
414n/a proc = self.subprocess(code, pass_fds=(rpipe,))
415n/a os.close(rpipe)
416n/a with kill_on_error(proc):
417n/a # sync child-parent
418n/a os.write(wpipe, b'x')
419n/a os.close(wpipe)
420n/a
421n/a # parent
422n/a signal.sigwaitinfo([signum])
423n/a dt = time.monotonic() - t0
424n/a self.assertEqual(proc.wait(), 0)
425n/a
426n/a self.assertGreaterEqual(dt, self.sleep_time)
427n/a
428n/a
429n/a@unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()")
430n/aclass SelectEINTRTest(EINTRBaseTest):
431n/a """ EINTR tests for the select module. """
432n/a
433n/a def test_select(self):
434n/a t0 = time.monotonic()
435n/a select.select([], [], [], self.sleep_time)
436n/a dt = time.monotonic() - t0
437n/a self.stop_alarm()
438n/a self.assertGreaterEqual(dt, self.sleep_time)
439n/a
440n/a @unittest.skipUnless(hasattr(select, 'poll'), 'need select.poll')
441n/a def test_poll(self):
442n/a poller = select.poll()
443n/a
444n/a t0 = time.monotonic()
445n/a poller.poll(self.sleep_time * 1e3)
446n/a dt = time.monotonic() - t0
447n/a self.stop_alarm()
448n/a self.assertGreaterEqual(dt, self.sleep_time)
449n/a
450n/a @unittest.skipUnless(hasattr(select, 'epoll'), 'need select.epoll')
451n/a def test_epoll(self):
452n/a poller = select.epoll()
453n/a self.addCleanup(poller.close)
454n/a
455n/a t0 = time.monotonic()
456n/a poller.poll(self.sleep_time)
457n/a dt = time.monotonic() - t0
458n/a self.stop_alarm()
459n/a self.assertGreaterEqual(dt, self.sleep_time)
460n/a
461n/a @unittest.skipUnless(hasattr(select, 'kqueue'), 'need select.kqueue')
462n/a def test_kqueue(self):
463n/a kqueue = select.kqueue()
464n/a self.addCleanup(kqueue.close)
465n/a
466n/a t0 = time.monotonic()
467n/a kqueue.control(None, 1, self.sleep_time)
468n/a dt = time.monotonic() - t0
469n/a self.stop_alarm()
470n/a self.assertGreaterEqual(dt, self.sleep_time)
471n/a
472n/a @unittest.skipUnless(hasattr(select, 'devpoll'), 'need select.devpoll')
473n/a def test_devpoll(self):
474n/a poller = select.devpoll()
475n/a self.addCleanup(poller.close)
476n/a
477n/a t0 = time.monotonic()
478n/a poller.poll(self.sleep_time * 1e3)
479n/a dt = time.monotonic() - t0
480n/a self.stop_alarm()
481n/a self.assertGreaterEqual(dt, self.sleep_time)
482n/a
483n/a
484n/adef test_main():
485n/a support.run_unittest(
486n/a OSEINTRTest,
487n/a SocketEINTRTest,
488n/a TimeEINTRTest,
489n/a SignalEINTRTest,
490n/a SelectEINTRTest)
491n/a
492n/a
493n/aif __name__ == "__main__":
494n/a test_main()