diff options
author | elfstrom <elfstrom@users.noreply.github.com> | 2023-09-22 12:55:56 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-09-22 12:55:56 (GMT) |
commit | 405b06375a8a4cdb08ff53afade09a8b66ec23d5 (patch) | |
tree | c39dfff42dd01b675e01f805f7632b1cf3648146 /Lib/concurrent | |
parent | e94a2232eac07eb526ec93ef01699513cf9b0fa3 (diff) | |
download | cpython-405b06375a8a4cdb08ff53afade09a8b66ec23d5.zip cpython-405b06375a8a4cdb08ff53afade09a8b66ec23d5.tar.gz cpython-405b06375a8a4cdb08ff53afade09a8b66ec23d5.tar.bz2 |
gh-105829: Fix concurrent.futures.ProcessPoolExecutor deadlock (#108513)
This fixes issue #105829, https://github.com/python/cpython/issues/105829
Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Antoine Pitrou <antoine@python.org>
Co-authored-by: Chris Withers <chris@withers.org>
Co-authored-by: Thomas Moreau <thomas.moreau.2010@gmail.com>
Diffstat (limited to 'Lib/concurrent')
-rw-r--r-- | Lib/concurrent/futures/process.py | 18 |
1 files changed, 15 insertions, 3 deletions
diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py index fba19d3..48d8db3 100644 --- a/Lib/concurrent/futures/process.py +++ b/Lib/concurrent/futures/process.py @@ -71,6 +71,11 @@ class _ThreadWakeup: self._reader, self._writer = mp.Pipe(duplex=False) def close(self): + # Please note that we do not take the shutdown lock when + # calling clear() (to avoid deadlocking) so this method can + # only be called safely from the same thread as all calls to + # clear() even if you hold the shutdown lock. Otherwise we + # might try to read from the closed pipe. if not self._closed: self._closed = True self._writer.close() @@ -426,8 +431,12 @@ class _ExecutorManagerThread(threading.Thread): elif wakeup_reader in ready: is_broken = False - with self.shutdown_lock: - self.thread_wakeup.clear() + # No need to hold the _shutdown_lock here because: + # 1. we're the only thread to use the wakeup reader + # 2. we're also the only thread to call thread_wakeup.close() + # 3. we want to avoid a possible deadlock when both reader and writer + # would block (gh-105829) + self.thread_wakeup.clear() return result_item, is_broken, cause @@ -717,7 +726,10 @@ class ProcessPoolExecutor(_base.Executor): # as it could result in a deadlock if a worker process dies with the # _result_queue write lock still acquired. # - # _shutdown_lock must be locked to access _ThreadWakeup. + # _shutdown_lock must be locked to access _ThreadWakeup.close() and + # .wakeup(). Care must also be taken to not call clear or close from + # more than one thread since _ThreadWakeup.clear() is not protected by + # the _shutdown_lock self._executor_manager_thread_wakeup = _ThreadWakeup() # Create communication channels for the executor |