Skip to content

Commit 5d1f32d

Browse files
authored
bpo-39995: Split test_concurrent_futures.test_crash() into sub-tests (GH-19739)
Now only test_error_during_result_unpickle_in_result_handler() captures and ignores sys.stderr in the test process. Tools like test.bisect_cmd don't support subTest() but only work with the granularity of one method. Remove unused ExecutorDeadlockTest._sleep_id() method.
1 parent 1a27501 commit 5d1f32d

File tree

1 file changed

+85
-63
lines changed

1 file changed

+85
-63
lines changed

Lib/test/test_concurrent_futures.py

Lines changed: 85 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,11 +1006,6 @@ def test_idle_process_reuse_multiple(self):
10061006
ProcessPoolForkserverMixin,
10071007
ProcessPoolSpawnMixin))
10081008

1009-
def hide_process_stderr():
1010-
import io
1011-
sys.stderr = io.StringIO()
1012-
1013-
10141009
def _crash(delay=None):
10151010
"""Induces a segfault."""
10161011
if delay:
@@ -1027,13 +1022,18 @@ def _exit():
10271022

10281023
def _raise_error(Err):
10291024
"""Function that raises an Exception in process."""
1030-
hide_process_stderr()
1025+
raise Err()
1026+
1027+
1028+
def _raise_error_ignore_stderr(Err):
1029+
"""Function that raises an Exception in process and ignores stderr."""
1030+
import io
1031+
sys.stderr = io.StringIO()
10311032
raise Err()
10321033

10331034

10341035
def _return_instance(cls):
10351036
"""Function that returns a instance of cls."""
1036-
hide_process_stderr()
10371037
return cls()
10381038

10391039

@@ -1072,17 +1072,12 @@ class ErrorAtUnpickle(object):
10721072
"""Bad object that triggers an error at unpickling time."""
10731073
def __reduce__(self):
10741074
from pickle import UnpicklingError
1075-
return _raise_error, (UnpicklingError, )
1075+
return _raise_error_ignore_stderr, (UnpicklingError, )
10761076

10771077

10781078
class ExecutorDeadlockTest:
10791079
TIMEOUT = support.SHORT_TIMEOUT
10801080

1081-
@classmethod
1082-
def _sleep_id(cls, x, delay):
1083-
time.sleep(delay)
1084-
return x
1085-
10861081
def _fail_on_deadlock(self, executor):
10871082
# If we did not recover before TIMEOUT seconds, consider that the
10881083
# executor is in a deadlock state and forcefully clean all its
@@ -1102,57 +1097,84 @@ def _fail_on_deadlock(self, executor):
11021097
self.fail(f"Executor deadlock:\n\n{tb}")
11031098

11041099

1105-
def test_crash(self):
1106-
# extensive testing for deadlock caused by crashes in a pool.
1100+
def _check_crash(self, error, func, *args, ignore_stderr=False):
1101+
# test for deadlock caused by crashes in a pool
11071102
self.executor.shutdown(wait=True)
1108-
crash_cases = [
1109-
# Check problem occurring while pickling a task in
1110-
# the task_handler thread
1111-
(id, (ErrorAtPickle(),), PicklingError, "error at task pickle"),
1112-
# Check problem occurring while unpickling a task on workers
1113-
(id, (ExitAtUnpickle(),), BrokenProcessPool,
1114-
"exit at task unpickle"),
1115-
(id, (ErrorAtUnpickle(),), BrokenProcessPool,
1116-
"error at task unpickle"),
1117-
(id, (CrashAtUnpickle(),), BrokenProcessPool,
1118-
"crash at task unpickle"),
1119-
# Check problem occurring during func execution on workers
1120-
(_crash, (), BrokenProcessPool,
1121-
"crash during func execution on worker"),
1122-
(_exit, (), SystemExit,
1123-
"exit during func execution on worker"),
1124-
(_raise_error, (RuntimeError, ), RuntimeError,
1125-
"error during func execution on worker"),
1126-
# Check problem occurring while pickling a task result
1127-
# on workers
1128-
(_return_instance, (CrashAtPickle,), BrokenProcessPool,
1129-
"crash during result pickle on worker"),
1130-
(_return_instance, (ExitAtPickle,), SystemExit,
1131-
"exit during result pickle on worker"),
1132-
(_return_instance, (ErrorAtPickle,), PicklingError,
1133-
"error during result pickle on worker"),
1134-
# Check problem occurring while unpickling a task in
1135-
# the result_handler thread
1136-
(_return_instance, (ErrorAtUnpickle,), BrokenProcessPool,
1137-
"error during result unpickle in result_handler"),
1138-
(_return_instance, (ExitAtUnpickle,), BrokenProcessPool,
1139-
"exit during result unpickle in result_handler")
1140-
]
1141-
for func, args, error, name in crash_cases:
1142-
with self.subTest(name):
1143-
# The captured_stderr reduces the noise in the test report
1144-
with support.captured_stderr():
1145-
executor = self.executor_type(
1146-
max_workers=2, mp_context=get_context(self.ctx))
1147-
res = executor.submit(func, *args)
1148-
with self.assertRaises(error):
1149-
try:
1150-
res.result(timeout=self.TIMEOUT)
1151-
except futures.TimeoutError:
1152-
# If we did not recover before TIMEOUT seconds,
1153-
# consider that the executor is in a deadlock state
1154-
self._fail_on_deadlock(executor)
1155-
executor.shutdown(wait=True)
1103+
1104+
executor = self.executor_type(
1105+
max_workers=2, mp_context=get_context(self.ctx))
1106+
res = executor.submit(func, *args)
1107+
1108+
if ignore_stderr:
1109+
cm = support.captured_stderr()
1110+
else:
1111+
cm = contextlib.nullcontext()
1112+
1113+
try:
1114+
with self.assertRaises(error):
1115+
with cm:
1116+
res.result(timeout=self.TIMEOUT)
1117+
except futures.TimeoutError:
1118+
# If we did not recover before TIMEOUT seconds,
1119+
# consider that the executor is in a deadlock state
1120+
self._fail_on_deadlock(executor)
1121+
executor.shutdown(wait=True)
1122+
1123+
def test_error_at_task_pickle(self):
1124+
# Check problem occurring while pickling a task in
1125+
# the task_handler thread
1126+
self._check_crash(PicklingError, id, ErrorAtPickle())
1127+
1128+
def test_exit_at_task_unpickle(self):
1129+
# Check problem occurring while unpickling a task on workers
1130+
self._check_crash(BrokenProcessPool, id, ExitAtUnpickle())
1131+
1132+
def test_error_at_task_unpickle(self):
1133+
# Check problem occurring while unpickling a task on workers
1134+
self._check_crash(BrokenProcessPool, id, ErrorAtUnpickle())
1135+
1136+
def test_crash_at_task_unpickle(self):
1137+
# Check problem occurring while unpickling a task on workers
1138+
self._check_crash(BrokenProcessPool, id, CrashAtUnpickle())
1139+
1140+
def test_crash_during_func_exec_on_worker(self):
1141+
# Check problem occurring during func execution on workers
1142+
self._check_crash(BrokenProcessPool, _crash)
1143+
1144+
def test_exit_during_func_exec_on_worker(self):
1145+
# Check problem occurring during func execution on workers
1146+
self._check_crash(SystemExit, _exit)
1147+
1148+
def test_error_during_func_exec_on_worker(self):
1149+
# Check problem occurring during func execution on workers
1150+
self._check_crash(RuntimeError, _raise_error, RuntimeError)
1151+
1152+
def test_crash_during_result_pickle_on_worker(self):
1153+
# Check problem occurring while pickling a task result
1154+
# on workers
1155+
self._check_crash(BrokenProcessPool, _return_instance, CrashAtPickle)
1156+
1157+
def test_exit_during_result_pickle_on_worker(self):
1158+
# Check problem occurring while pickling a task result
1159+
# on workers
1160+
self._check_crash(SystemExit, _return_instance, ExitAtPickle)
1161+
1162+
def test_error_during_result_pickle_on_worker(self):
1163+
# Check problem occurring while pickling a task result
1164+
# on workers
1165+
self._check_crash(PicklingError, _return_instance, ErrorAtPickle)
1166+
1167+
def test_error_during_result_unpickle_in_result_handler(self):
1168+
# Check problem occurring while unpickling a task in
1169+
# the result_handler thread
1170+
self._check_crash(BrokenProcessPool,
1171+
_return_instance, ErrorAtUnpickle,
1172+
ignore_stderr=True)
1173+
1174+
def test_exit_during_result_unpickle_in_result_handler(self):
1175+
# Check problem occurring while unpickling a task in
1176+
# the result_handler thread
1177+
self._check_crash(BrokenProcessPool, _return_instance, ExitAtUnpickle)
11561178

11571179
def test_shutdown_deadlock(self):
11581180
# Test that the pool calling shutdown do not cause deadlock

0 commit comments

Comments
 (0)