Issue #25122: Fix test_eintr, kill child process on error
Some test_eintr hangs on waiting for the child process completion if an error occurred on the parent. Kill the child process on error (in the parent) to avoid the hang.
This commit is contained in:
parent
d7bf45f5f7
commit
44879a0b18
|
@ -8,6 +8,7 @@ Signals are generated in-process using setitimer(ITIMER_REAL), which allows
|
||||||
sub-second periodicity (contrarily to signal()).
|
sub-second periodicity (contrarily to signal()).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import contextlib
|
||||||
import faulthandler
|
import faulthandler
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
|
@ -21,6 +22,16 @@ import unittest
|
||||||
|
|
||||||
from test import support
|
from test import support
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()")
|
@unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()")
|
||||||
class EINTRBaseTest(unittest.TestCase):
|
class EINTRBaseTest(unittest.TestCase):
|
||||||
|
@ -38,7 +49,7 @@ class EINTRBaseTest(unittest.TestCase):
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
cls.orig_handler = signal.signal(signal.SIGALRM, lambda *args: None)
|
cls.orig_handler = signal.signal(signal.SIGALRM, lambda *args: None)
|
||||||
if hasattr(faulthandler, 'dump_traceback_later'):
|
if hasattr(faulthandler, 'dump_traceback_later'):
|
||||||
# Most tests take less than 30 seconds, so 15 minutes should be
|
# Most tests take less than 30 seconds, so 5 minutes should be
|
||||||
# enough. dump_traceback_later() is implemented with a thread, but
|
# enough. dump_traceback_later() is implemented with a thread, but
|
||||||
# pthread_sigmask() is used to mask all signaled on this thread.
|
# pthread_sigmask() is used to mask all signaled on this thread.
|
||||||
faulthandler.dump_traceback_later(5 * 60, exit=True)
|
faulthandler.dump_traceback_later(5 * 60, exit=True)
|
||||||
|
@ -120,7 +131,8 @@ class OSEINTRTest(EINTRBaseTest):
|
||||||
' os.write(wr, data)',
|
' os.write(wr, data)',
|
||||||
))
|
))
|
||||||
|
|
||||||
with self.subprocess(code, str(wr), pass_fds=[wr]) as proc:
|
proc = self.subprocess(code, str(wr), pass_fds=[wr])
|
||||||
|
with kill_on_error(proc):
|
||||||
os.close(wr)
|
os.close(wr)
|
||||||
for data in datas:
|
for data in datas:
|
||||||
self.assertEqual(data, os.read(rd, len(data)))
|
self.assertEqual(data, os.read(rd, len(data)))
|
||||||
|
@ -156,7 +168,8 @@ class OSEINTRTest(EINTRBaseTest):
|
||||||
' % (len(value), data_len))',
|
' % (len(value), data_len))',
|
||||||
))
|
))
|
||||||
|
|
||||||
with self.subprocess(code, str(rd), pass_fds=[rd]) as proc:
|
proc = self.subprocess(code, str(rd), pass_fds=[rd])
|
||||||
|
with kill_on_error(proc):
|
||||||
os.close(rd)
|
os.close(rd)
|
||||||
written = 0
|
written = 0
|
||||||
while written < len(data):
|
while written < len(data):
|
||||||
|
@ -198,7 +211,7 @@ class SocketEINTRTest(EINTRBaseTest):
|
||||||
|
|
||||||
fd = wr.fileno()
|
fd = wr.fileno()
|
||||||
proc = self.subprocess(code, str(fd), pass_fds=[fd])
|
proc = self.subprocess(code, str(fd), pass_fds=[fd])
|
||||||
with proc:
|
with kill_on_error(proc):
|
||||||
wr.close()
|
wr.close()
|
||||||
for data in datas:
|
for data in datas:
|
||||||
self.assertEqual(data, recv_func(rd, len(data)))
|
self.assertEqual(data, recv_func(rd, len(data)))
|
||||||
|
@ -248,7 +261,7 @@ class SocketEINTRTest(EINTRBaseTest):
|
||||||
|
|
||||||
fd = rd.fileno()
|
fd = rd.fileno()
|
||||||
proc = self.subprocess(code, str(fd), pass_fds=[fd])
|
proc = self.subprocess(code, str(fd), pass_fds=[fd])
|
||||||
with proc:
|
with kill_on_error(proc):
|
||||||
rd.close()
|
rd.close()
|
||||||
written = 0
|
written = 0
|
||||||
while written < len(data):
|
while written < len(data):
|
||||||
|
@ -288,7 +301,8 @@ class SocketEINTRTest(EINTRBaseTest):
|
||||||
' time.sleep(sleep_time)',
|
' time.sleep(sleep_time)',
|
||||||
))
|
))
|
||||||
|
|
||||||
with self.subprocess(code) as proc:
|
proc = self.subprocess(code)
|
||||||
|
with kill_on_error(proc):
|
||||||
client_sock, _ = sock.accept()
|
client_sock, _ = sock.accept()
|
||||||
client_sock.close()
|
client_sock.close()
|
||||||
self.assertEqual(proc.wait(), 0)
|
self.assertEqual(proc.wait(), 0)
|
||||||
|
@ -315,7 +329,8 @@ class SocketEINTRTest(EINTRBaseTest):
|
||||||
do_open_close_reader,
|
do_open_close_reader,
|
||||||
))
|
))
|
||||||
|
|
||||||
with self.subprocess(code) as proc:
|
proc = self.subprocess(code)
|
||||||
|
with kill_on_error(proc):
|
||||||
do_open_close_writer(filename)
|
do_open_close_writer(filename)
|
||||||
|
|
||||||
self.assertEqual(proc.wait(), 0)
|
self.assertEqual(proc.wait(), 0)
|
||||||
|
@ -372,7 +387,8 @@ class SignalEINTRTest(EINTRBaseTest):
|
||||||
))
|
))
|
||||||
|
|
||||||
t0 = time.monotonic()
|
t0 = time.monotonic()
|
||||||
with self.subprocess(code) as proc:
|
proc = self.subprocess(code)
|
||||||
|
with kill_on_error(proc):
|
||||||
# parent
|
# parent
|
||||||
signal.sigwaitinfo([signum])
|
signal.sigwaitinfo([signum])
|
||||||
dt = time.monotonic() - t0
|
dt = time.monotonic() - t0
|
||||||
|
|
Loading…
Reference in New Issue