summaryrefslogtreecommitdiffstats
path: root/Lib/concurrent
diff options
context:
space:
mode:
authorelfstrom <elfstrom@users.noreply.github.com>2023-09-22 12:55:56 (GMT)
committerGitHub <noreply@github.com>2023-09-22 12:55:56 (GMT)
commit405b06375a8a4cdb08ff53afade09a8b66ec23d5 (patch)
treec39dfff42dd01b675e01f805f7632b1cf3648146 /Lib/concurrent
parente94a2232eac07eb526ec93ef01699513cf9b0fa3 (diff)
downloadcpython-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.py18
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