Skip to content

Commit 90ea3be

Browse files
[3.12] gh-94777: Fix deadlock in ProcessPoolExecutor (GH-94784) (#106609)
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 68ca190 commit 90ea3be

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
@@ -499,6 +499,10 @@ def terminate_broken(self, cause):
499499
for p in self.processes.values():
500500
p.terminate()
501501

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

Lib/test/test_concurrent_futures.py

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

11741174

1175+
def _crash_with_data(data):
1176+
"""Induces a segfault with dummy data in input."""
1177+
_crash()
1178+
1179+
11751180
def _exit():
11761181
"""Induces a sys exit with exitcode 1."""
11771182
sys.exit(1)
@@ -1371,6 +1376,19 @@ def test_shutdown_deadlock_pickle(self):
13711376
# dangling threads
13721377
executor_manager.join()
13731378

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

13751393
create_executor_tests(ExecutorDeadlockTest,
13761394
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)