From a8cae4071c795e55be46e339eda37e241fa0d7f8 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 6 Sep 2023 15:57:01 +0200 Subject: [PATCH] gh-107219: Fix concurrent.futures terminate_broken() (#108974) Fix a race condition in _ExecutorManagerThread.terminate_broken(): ignore the InvalidStateError on future.set_exception(). It can happen if the future is cancelled before the caller. Moreover, test_crash_big_data() now waits explicitly until the executor completes. --- Lib/concurrent/futures/process.py | 9 ++++++++- Lib/test/test_concurrent_futures/test_deadlock.py | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py index 301207f59de..9933d3d0e04 100644 --- a/Lib/concurrent/futures/process.py +++ b/Lib/concurrent/futures/process.py @@ -489,7 +489,14 @@ class _ExecutorManagerThread(threading.Thread): # Mark pending tasks as failed. for work_id, work_item in self.pending_work_items.items(): - work_item.future.set_exception(bpe) + try: + work_item.future.set_exception(bpe) + except _base.InvalidStateError as exc: + # set_exception() fails if the future is cancelled: ignore it. + # Trying to check if the future is cancelled before calling + # set_exception() would leave a race condition if the future is + # cancelled betwen the check and set_exception(). + pass # Delete references to object. See issue16284 del work_item self.pending_work_items.clear() diff --git a/Lib/test/test_concurrent_futures/test_deadlock.py b/Lib/test/test_concurrent_futures/test_deadlock.py index 6b78b360d15..baac2b51e0d 100644 --- a/Lib/test/test_concurrent_futures/test_deadlock.py +++ b/Lib/test/test_concurrent_futures/test_deadlock.py @@ -239,6 +239,8 @@ class ExecutorDeadlockTest: with self.assertRaises(BrokenProcessPool): list(executor.map(_crash_with_data, [data] * 10)) + executor.shutdown(wait=True) + create_executor_tests(globals(), ExecutorDeadlockTest, executor_mixins=(ProcessPoolForkMixin,