Skip to content

Commit d63953d

Browse files
[3.11] gh-94777: Fix deadlock in ProcessPoolExecutor (GH-94784) (#106607)
gh-94777: Fix deadlock in ProcessPoolExecutor (GH-94784) Fixes a hang in multiprocessing process pool executor when a child process crashes and code could otherwise block on writing to the pipe. See GH-94777 for more details. (cherry picked from commit 6782fc0) Co-authored-by: Louis Paulot <[email protected]>
1 parent 563829d commit d63953d

File tree

3 files changed

+23
-0
lines changed

3 files changed

+23
-0
lines changed

Lib/concurrent/futures/process.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,10 @@ def terminate_broken(self, cause):
497497
for p in self.processes.values():
498498
p.terminate()
499499

500+
# Prevent queue writing to a pipe which is no longer read.
501+
# https://github.com/python/cpython/issues/94777
502+
self.call_queue._reader.close()
503+
500504
# clean up resources
501505
self.join_executor_internals()
502506

Lib/test/test_concurrent_futures.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,11 @@ def _crash(delay=None):
11671167
faulthandler._sigsegv()
11681168

11691169

1170+
def _crash_with_data(data):
1171+
"""Induces a segfault with dummy data in input."""
1172+
_crash()
1173+
1174+
11701175
def _exit():
11711176
"""Induces a sys exit with exitcode 1."""
11721177
sys.exit(1)
@@ -1366,6 +1371,19 @@ def test_shutdown_deadlock_pickle(self):
13661371
# dangling threads
13671372
executor_manager.join()
13681373

1374+
def test_crash_big_data(self):
1375+
# Test that there is a clean exception instad of a deadlock when a
1376+
# child process crashes while some data is being written into the
1377+
# queue.
1378+
# https://github.com/python/cpython/issues/94777
1379+
self.executor.shutdown(wait=True)
1380+
data = "a" * support.PIPE_MAX_SIZE
1381+
with self.executor_type(max_workers=2,
1382+
mp_context=self.get_context()) as executor:
1383+
self.executor = executor # Allow clean up in fail_on_deadlock
1384+
with self.assertRaises(BrokenProcessPool):
1385+
list(executor.map(_crash_with_data, [data] * 10))
1386+
13691387

13701388
create_executor_tests(ExecutorDeadlockTest,
13711389
executor_mixins=(ProcessPoolForkMixin,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix hanging :mod:`multiprocessing` ``ProcessPoolExecutor`` when a child process crashes while data is being written in the call queue.

0 commit comments

Comments
 (0)