You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
concurrent.futures.ProcessPoolExecutor can deadlock when tcmalloc is used #90622
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee='https://github.com/gpshead'closed_at=Nonecreated_at=<Date2022-01-21.23:32:53.148>labels= ['3.11', 'library', '3.9', '3.10', 'performance']
title='concurrent.futures.ProcessPoolExecutor can deadlock when tcmalloc is used'updated_at=<Date2022-01-25.11:06:46.406>user='https://github.com/yilei'
For context, the fundamental problem is that os.fork() is unsafe to use in any process with threads. concurrent/futures/process.py violates this. So long as its design involves a thread, it can never be guaranteed not to deadlock.
POSIX forbids execution of most code after fork() has been called. Returning to the CPython interpreter after os.fork() in the child process is never safe unless the parent process had no threads at fork time.
The change that triggered the issue moved from doing all of the os.fork() calls for the workers up front *before the thread was spawned* to doing them on demand after the thread was running.
The bugfix for this is simply a rollback to revert the bpo-39207 change. It was a performance enhancement, but it causes a new deadlock in code that didn't already have one when thread tuned malloc implementations are used.
The only way to safely launch worker processes on demand is to spawn a worker launcher process spawned prior to any thread creation that remains idle, with a sole job of spawn new worker processes for us. That sounds complicated. That'd be a feature. Lets go with the bugfix first.
https://bugs.python.org/issue44733 for 3.11 attempts to build upon the dynamic spawning ability and will need reverting unless a non-thread+fork implementation is provided.
Note that there is still one deadlock possibility that I left as a comment in the code. If the "fork" mp_context spawn method is used and one of the worker processes dies, a new one will be forked after multiprocessing has already started its executor manager thread. mp_context "forkserver" and "spawn" methods avoid that.
Activity
yilei commentedon Jan 21, 2022
When Python is built and linked with tcmalloc, using ProcessPoolExecutor may deadlock. Here is a reproducible example:
The child process fails to finish. It's more likely to reproduce when the system is busy.
With some bisect search internally, this commit 1ac6e37 "triggered" the deadlock threshold with tcmalloc.
gpshead commentedon Jan 21, 2022
For context, the fundamental problem is that os.fork() is unsafe to use in any process with threads. concurrent/futures/process.py violates this. So long as its design involves a thread, it can never be guaranteed not to deadlock.
POSIX forbids execution of most code after fork() has been called. Returning to the CPython interpreter after os.fork() in the child process is never safe unless the parent process had no threads at fork time.
The change that triggered the issue moved from doing all of the os.fork() calls for the workers up front *before the thread was spawned* to doing them on demand after the thread was running.
The bugfix for this is simply a rollback to revert the bpo-39207 change. It was a performance enhancement, but it causes a new deadlock in code that didn't already have one when thread tuned malloc implementations are used.
The only way to safely launch worker processes on demand is to spawn a worker launcher process spawned prior to any thread creation that remains idle, with a sole job of spawn new worker processes for us. That sounds complicated. That'd be a feature. Lets go with the bugfix first.
gpshead commentedon Jan 24, 2022
https://bugs.python.org/issue44733 for 3.11 attempts to build upon the dynamic spawning ability and will need reverting unless a non-thread+fork implementation is provided.
12 remaining items
pythongh-90622: Do not spawn ProcessPool workers on demand via fork m…
gh-90622: Do not spawn ProcessPool workers on demand via fork method. (…
[3.10] pythongh-90622: Do not spawn ProcessPool workers on demand via…
[3.10] gh-90622: Do not spawn ProcessPool workers on demand via fork …
[3.10] pythongh-90622: Do not spawn ProcessPool workers on demand via…
gh-90622: Do not spawn ProcessPool workers on demand via fork method. (…
[3.10] gh-90622: Do not spawn ProcessPool workers on demand via fork …
gpshead commentedon May 8, 2022
Note that there is still one deadlock possibility that I left as a comment in the code. If the "fork" mp_context spawn method is used and one of the worker processes dies, a new one will be forked after multiprocessing has already started its executor manager thread. mp_context "forkserver" and "spawn" methods avoid that.
[3.10] pythongh-90622: Do not spawn ProcessPool workers on demand via…