Repository navigation
concurrent.futures deadlock #80047
Copy link
Copy link
Closed
Labels
3.7 (EOL)end of lifeend of life3.8 (EOL)end of lifeend of lifestdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Description
| BPO | 35866 |
|---|---|
| Nosy | @gpshead, @brianquinlan, @pitrou, @vstinner, @jwilk, @MojoVampire, @cagney, @hroncok, @pablogsal |
| Files |
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 = None
closed_at = <Date 2021-07-03.21:52:31.634>
created_at = <Date 2019-01-31.10:05:11.069>
labels = ['3.7', '3.8', 'type-bug', 'library']
title = 'concurrent.futures deadlock'
updated_at = <Date 2021-07-03.21:53:10.556>
user = 'https://github.com/jwilk'bugs.python.org fields:
activity = <Date 2021-07-03.21:53:10.556>
actor = 'vstinner'
assignee = 'none'
closed = True
closed_date = <Date 2021-07-03.21:52:31.634>
closer = 'vstinner'
components = ['Library (Lib)']
creation = <Date 2019-01-31.10:05:11.069>
creator = 'jwilk'
dependencies = []
files = ['48090', '48223', '48224', '48242', '48271', '48272', '48611']
hgrepos = []
issue_num = 35866
keywords = []
message_count = 34.0
messages = ['334618', '334648', '334652', '338376', '338377', '338402', '338501', '338549', '339334', '339361', '339370', '339451', '339464', '340263', '340278', '340343', '340344', '340358', '340359', '340360', '340362', '340430', '340431', '340432', '340442', '340451', '340456', '340459', '340464', '340468', '352707', '396675', '396910', '396929']
nosy_count = 11.0
nosy_names = ['gregory.p.smith', 'bquinlan', 'pitrou', 'vstinner', 'bentoi', 'jwilk', 'josh.r', 'cagney', 'hroncok', 'pablogsal', 'hugh']
pr_nums = []
priority = 'normal'
resolution = 'fixed'
stage = 'resolved'
status = 'closed'
superseder = None
type = 'behavior'
url = 'https://bugs.python.org/issue35866'
versions = ['Python 3.7', 'Python 3.8']Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
3.7 (EOL)end of lifeend of life3.8 (EOL)end of lifeend of lifestdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Activity
jwilk commentedon Jan 31, 2019
The attached test program hangs eventually (it may need a few thousand of iterations).
Tested with Python v3.7.2 on Linux, amd64.
MojoVampire commentedon Feb 1, 2019
I've only got 3.7.1 Ubuntu bash on Windows (also amd64) immediately available, but I'm not seeing a hang, nor is there any obvious memory leak that might eventually lead to problems (memory regularly drops back to under 10 MB shared, 24 KB private working set). I modified your code to add a sys.stdout.flush() after the write so it would actually echo the dots as they were written instead of waiting for a few thousand of them to build up in the buffer, but otherwise it's the same code.
Are you sure you're actually hanging, and it's not just the output getting buffered?
jwilk commentedon Feb 1, 2019
You're right that sys.stdout.flush() is missing in my code; but on Linux it doesn't make a big difference, because multiprocessing flushes stdout before fork()ing.
And yes, it really hangs.
pablogsal commentedon Mar 19, 2019
This seem related to https://bugs.python.org/issue35809
pablogsal commentedon Mar 19, 2019
Could you use gdb/lldb to attach to the process hanging and give us a stack trace?
jwilk commentedon Mar 19, 2019
There are two processes running (parent and child) when the thing hangs.
I'm attaching GDB backtraces for both.
hugh commentedon Mar 20, 2019
@jwilk: thanks for creating cf-deadlock.py
I can replicate the test program hang on Fedora 29 with python3-3.7.2-4.fc29.x86_64
The test program hasn't yet hung on Fedora 29 with older packages, in particular
python3-3.7.1-4.fc29.x86_64
My interest is due to the fact that the libreswan.org test suite has started to hang and we don't know why. It might well be this bug.
hugh commentedon Mar 21, 2019
I've filed a Fedora bug report that points to this one: <https://bugzilla.redhat.com/show_bug.cgi?id=1691434\>
vstinner commentedon Apr 2, 2019
Any update on this issue? I don't understand why the example hangs.
cagney commentedon Apr 2, 2019
I've attached a variation on cf-deadlock.py that, should nothing happen for 2 minutes, will kill itself. Useful with git bisect.
cagney commentedon Apr 2, 2019
I'm seeing cf-deadlock-alarm.py hangs on vanilla python 3.7.[0123] with:
Linux 5.0.5-100.fc28.x86_64 #1 SMP Wed Mar 27 22:16:29 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
glibc-2.27-37.fc28.x86_64
can anyone reproduce this?
I also wonder if this is connected to bpo-6721 where a recent "fix" made things worse - the fedora versions that work for libreswan don't have the "fix".
cagney commentedon Apr 4, 2019
More info from adding a faulthandler ...
Thread 0x00007f1ce7fff700 (most recent call first):
File "/home/python/v3.7.3/lib/python3.7/threading.py", line 296 in wait
File "/home/python/v3.7.3/lib/python3.7/multiprocessing/queues.py", line 224 in _feed
File "/home/python/v3.7.3/lib/python3.7/threading.py", line 865 in run
File "/home/python/v3.7.3/lib/python3.7/threading.py", line 917 in _bootstrap_inner
File "/home/python/v3.7.3/lib/python3.7/threading.py", line 885 in _bootstrap
Thread 0x00007f1cec917700 (most recent call first):
File "/home/python/v3.7.3/lib/python3.7/selectors.py", line 415 in select
File "/home/python/v3.7.3/lib/python3.7/multiprocessing/connection.py", line 920 in wait
File "/home/python/v3.7.3/lib/python3.7/concurrent/futures/process.py", line 354 in _queue_management_worker
File "/home/python/v3.7.3/lib/python3.7/threading.py", line 865 in run
File "/home/python/v3.7.3/lib/python3.7/threading.py", line 917 in _bootstrap_inner
File "/home/python/v3.7.3/lib/python3.7/threading.py", line 885 in _bootstrap
Current thread 0x00007f1cfd9486c0 (most recent call first):
File "/home/python/v3.7.3/lib/python3.7/threading.py", line 296 in wait
File "/home/python/v3.7.3/lib/python3.7/concurrent/futures/_base.py", line 427 in result
File "cf-deadlock.py", line 25 in <module>
10 remaining items
cagney commentedon Apr 17, 2019
Here's a possible stack taken during the fork():
Thread 1 "python3" hit Breakpoint 1, 0x00007ffff7124734 in fork () from /lib64/libc.so.6
Thread 1814 (Thread 0x7fffe69d5700 (LWP 23574)):
#0 0x00007ffff7bc24e5 in __pthread_mutex_unlock_usercnt () from /lib64/libpthread.so.0
#1 0x00007ffff71928e3 in dl_iterate_phdr () from /lib64/libc.so.6
#2 0x00007fffe5fcfe55 in _Unwind_Find_FDE () from /lib64/libgcc_s.so.1
#3 0x00007fffe5fcc403 in uw_frame_state_for () from /lib64/libgcc_s.so.1
#4 0x00007fffe5fcd90f in _Unwind_ForcedUnwind_Phase2 () from /lib64/libgcc_s.so.1
#5 0x00007fffe5fcdf30 in _Unwind_ForcedUnwind () from /lib64/libgcc_s.so.1
#6 0x00007ffff7bc7712 in __pthread_unwind () from /lib64/libpthread.so.0
#7 0x00007ffff7bbf7e7 in pthread_exit () from /lib64/libpthread.so.0
#8 0x000000000051b2fc in PyThread_exit_thread () at Python/thread_pthread.h:238
#9 0x000000000055ed16 in t_bootstrap (boot_raw=0x7fffe8da0e40) at ./Modules/_threadmodule.c:1021
#10 0x00007ffff7bbe594 in start_thread () from /lib64/libpthread.so.0
#11 0x00007ffff7157e5f in clone () from /lib64/libc.so.6
Thread 1 (Thread 0x7ffff7fca080 (LWP 20524)):
#0 0x00007ffff7124734 in fork () from /lib64/libc.so.6
#1 0x0000000000532c8a in os_fork_impl (module=<optimized out>) at ./Modules/posixmodule.c:5423
#2 os_fork (module=<optimized out>, _unused_ignored=<optimized out>) at ./Modules/clinic/posixmodule.c.h:1913
where, in my source code, dl_iterate_phdr() starts with something like:
/* Make sure nobody modifies the list of loaded objects. */
__rtld_lock_lock_recursive (GL(dl_load_write_lock));
i.e., when the fork occures, the non-fork thread has acquired dl_load_write_lock - the same lock that the child will later try to acquire (and hang)
no clue as to what that thread is doing though; other than it looks like it is trying to generate a backtrace?
cagney commentedon Apr 17, 2019
run ProcessPoolExecutor with one fixed child (over ride default of #cores)
cagney commentedon Apr 17, 2019
script to capture stack backtrace at time of fork, last backtrace printed will be for hang
gpshead commentedon Apr 17, 2019
I am unable to get cf-deadlock.py to hang on my own builds of pure CPython 3.7.2+ d7cb203 or 3.6.8+ be77fb7 (versions i had in a local git clone).
which specific python builds are seeing the hang using? Which specific platform/distro version? "3.7.2" isn't enough, if you are using a distro supplied interpreter please try and reproduce this with a build from the CPython tree itself. distros always apply their own patches to their own interpreters.
...
Do realize that while working on this it is fundamentally *impossible* per POSIX for os.fork() to be safely used at the Python level in a process also using pthreads. That this _ever_ appeared to work is a pure accident of implementations of underlying libc, malloc, system libraries, and kernel behaviors. POSIX considers it undefined behavior. Nothing done in CPython can avoid that. Any "fix" for these kinds of issues is merely working around the inevitable which will re-occur.
concurrent.futures.ProcessPoolExecutor uses multiprocessing for its process management. As of 3.7 ProcessPoolExecutor accepts a mp_context parameter to specify the multiprocessing start method. Alternatively the default appears
to be controllable as a global setting https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods.
Use the 'spawn' start method and the problem should go away as it'll no longer be misusing os.fork(). You _might_ be able to get the 'forkserver' start method to work, but only reliably if you make sure the forkserver is spawned _before_ any threads in the process (such as ProcessPoolExecutor's own queue management thread - which appears to be spawned upon the first call to .submit()).
cagney commentedon Apr 17, 2019
We're discussing vanilla Python, for instance v3.7.0 is:
git clone .../cpython
cd cpython
git checkout v3.7.0
./configure --prefix=/home/python/v3.7.0
make -j && make -j install
(my 3.6.x wasn't vanilla, but I clearly stated that)
Like I also mentioned, loading down the machine also helps. Try something like running #cores*2 of the script in parallel?
cagney commentedon Apr 17, 2019
@gregory.p.smith, I'm puzzled by your references to POSIX and/or os.fork().
The code in question looks like:
which, to me, looks like pure Python.
Are you saying that this code can't work on GNU/Linux systems.
gpshead commentedon Apr 17, 2019
concurrent.futures.ProcessPoolExecutor uses both multiprocessing and threading. multiprocessing defaults to using os.fork().
cagney commentedon Apr 18, 2019
So:
#1 we've a bug: the single-threaded ProcessPoolExecutor test program should work 100% reliably - it does not
#2 we've a cause: ProcessPoolExecutor is implemented internally using an unfortunate combination of fork and threads, this is causing the deadlock
As for a fix, maybe:
gpshead commentedon Apr 18, 2019
I doubt it is single threaded, the .submit() method appears to spawn a thread internally.
bentoi commentedon Sep 18, 2019
FYI, I'm getting a similar deadlock in a child Python process which is stuck on locking a mutex from the dl library. See attached stack.
I'm not using concurrent.futures however, the parent Python process is a test driver that uses threading.Thread and subprocess.Popen to spawn new processes... I'm not using os.fork().
This occurred on ArchLinux with Python 3.7.4.
vstinner commentedon Jun 28, 2021
The thread_run() function (previously "t_bootstrap()") which is the low-level C function to run a thread in Python _thread.start_new_thread() no longer calls PyThread_exit_thread(): see bpo-44434.
I'm able to reproduce the issue using attached cf-deadlock.py with Python 3.8:
In the main branch (with bpo-44434 fix), I can no longer reproduce the issue. I ran cf-deadlock.py in 4 terminals in parallel with "./python -m test -r -j2" in a 5th terminal for 5 minutes. I couldn't reproduce the issue. On Python 3.8, I reproduced the issue in less than 1 minute.
Can someone please confirm that the issue is now fixed? Can we mark this issue as a duplicate of bpo-44434?
jwilk commentedon Jul 3, 2021
I can no longer reproduce the bug with Python from git.
vstinner commentedon Jul 3, 2021
Great! I close the issue.