Skip to content

Commit 3b11481

Browse files
gh-158287: Use watchdog timer in test_stopped_child_does_not_block_event_loop
1 parent 660453b commit 3b11481

1 file changed

Lines changed: 35 additions & 4 deletions

File tree

‎Lib/test/test_asyncio/test_subprocess.py‎

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import signal
44
import sys
55
import textwrap
6+
import threading
67
import unittest
78
import warnings
89
from unittest import mock
@@ -1169,25 +1170,55 @@ def test_stopped_child_does_not_block_event_loop(self):
11691170
# stopped children (si_code == CLD_STOPPED). The child watcher
11701171
# must not mistake this for process termination and attempt to reap
11711172
# the child with waitpid(), which blocks the event loop thread.
1172-
async def run():
1173-
async with asyncio.timeout(support.SHORT_TIMEOUT):
1173+
proc = None
1174+
timed_out = False
1175+
1176+
def watchdog():
1177+
nonlocal timed_out
1178+
timed_out = True
1179+
# If the event loop gets stuck in waitpid() because of this bug,
1180+
# killing the child process unblocks waitpid() so the test fails
1181+
# instead of hanging indefinitely.
1182+
if proc is not None:
1183+
if hasattr(signal, 'SIGCONT'):
1184+
try:
1185+
os.kill(proc.pid, signal.SIGCONT)
1186+
except ProcessLookupError:
1187+
pass
1188+
try:
1189+
os.kill(proc.pid, signal.SIGKILL)
1190+
except ProcessLookupError:
1191+
pass
1192+
1193+
timer = threading.Timer(support.SHORT_TIMEOUT, watchdog)
1194+
timer.start()
1195+
try:
1196+
async def run():
1197+
nonlocal proc
11741198
proc = await asyncio.create_subprocess_exec(*PROGRAM_BLOCKED)
11751199
try:
11761200
proc.send_signal(signal.SIGSTOP)
11771201
# Verify event loop remains responsive while child is stopped
11781202
for _ in range(3):
11791203
await asyncio.sleep(0.05)
1204+
self.assertFalse(timed_out, "event loop was blocked by stopped child")
11801205
self.assertIsNone(proc.returncode)
11811206
finally:
11821207
if hasattr(signal, 'SIGCONT'):
11831208
try:
11841209
proc.send_signal(signal.SIGCONT)
11851210
except ProcessLookupError:
11861211
pass
1187-
proc.kill()
1212+
try:
1213+
proc.kill()
1214+
except ProcessLookupError:
1215+
pass
11881216
await proc.wait()
11891217

1190-
self.loop.run_until_complete(run())
1218+
self.loop.run_until_complete(run())
1219+
finally:
1220+
timer.cancel()
1221+
timer.join()
11911222

11921223
@unittest.skipUnless(
11931224
unix_events.can_use_pidfd(),

0 commit comments

Comments
 (0)