Repository navigation
BUG: Binary Builds Deadlock due to OpenBLAS threading issue with fork #30092
Description
Describe the issue:
Consider the snippet shown below.
On my system with numpy installed via pip this deadlocks with GDB showing an issue in the embedded OpenBLAS where it is stuck on a mutex. Rerunning with export OMP_NUM_THREADS=1 resolves the issue. I suspect this is due to a recent change in how the binary distributions are prepared (maybe previously they were not using multithreaded OpenBLAS?) . The above snippet should be fine, however, as it is the parent which is continuing to use NumPy and its threads should not have changed. Maybe OpenBLAS has a bad atfork handler?
If this is intended behaviour (NumPy not being compatible with any scripts which explicitly call fork) then it might be worth using os.register_at_fork to ensure an exception is thrown.
Reproduce the code example:
import numpy as np
import os
# Do some algebra
A = np.random.randn(216, 216)
np.linalg.inv(A)
# Fork but have the child do nothing
if (pid := os.fork()) != 0:
# Deadlock!
np.linalg.inv(A)
# Wait for the child
os.waitpid(pid, 0)Error message:
Python and NumPy Versions:
Build Dependencies:
blas:
detection method: pkgconfig
found: true
include directory: /opt/_internal/cpython-3.14.0/lib/python3.14/site-packages/scipy_openblas64/include
lib directory: /opt/_internal/cpython-3.14.0/lib/python3.14/site-packages/scipy_openblas64/lib
name: scipy-openblas
openblas configuration: OpenBLAS 0.3.30 USE64BITINT DYNAMIC_ARCH NO_AFFINITY
Haswell MAX_THREADS=64
pc file directory: /project/.openblas
version: 0.3.30
lapack:
detection method: pkgconfig
found: true
include directory: /opt/_internal/cpython-3.14.0/lib/python3.14/site-packages/scipy_openblas64/include
lib directory: /opt/_internal/cpython-3.14.0/lib/python3.14/site-packages/scipy_openblas64/lib
name: scipy-openblas
openblas configuration: OpenBLAS 0.3.30 USE64BITINT DYNAMIC_ARCH NO_AFFINITY
Haswell MAX_THREADS=64
pc file directory: /project/.openblas
version: 0.3.30
Compilers:
c:
commands: cc
linker: ld.bfd
name: gcc
version: 14.2.1
c++:
commands: c++
linker: ld.bfd
name: gcc
version: 14.2.1
cython:
commands: cython
linker: cython
name: cython
version: 3.1.4
Machine Information:
build:
cpu: x86_64
endian: little
family: x86_64
system: linux
host:
cpu: x86_64
endian: little
family: x86_64
system: linux
Python Information:
path: /tmp/build-env-qywnc8nt/bin/python
version: '3.14'
SIMD Extensions:
baseline:
- SSE
- SSE2
- SSE3
found:
- SSSE3
- SSE41
- POPCNT
- SSE42
- AVX
- F16C
- FMA3
- AVX2
not found:
- AVX512F
- AVX512CD
- AVX512_KNL
- AVX512_KNM
- AVX512_SKX
- AVX512_CLX
- AVX512_CNL
- AVX512_ICL
- AVX512_SPR
Runtime Environment:
No response
Context for the issue:
No response
Activity
mattip commentedon Oct 29, 2025
I can confirm the deadlock, on CPython3.14 and 3.13 (and maybe others). I used 3.13 so I could go back in versions to see where it started. numpy==2.3.1 does not deadlock, numpy2.3.2 does. The corresponding versions of OpenBLAS are 0.3.29 (does not deadlock), 0.3.30 (does deadlock). Maybe connected to #29391.
mattip commentedon Oct 29, 2025
Bisecting points to OpenMathLib/OpenBLAS#5170, which fixed some other threading problems. The backtrace at the hang is this. I am a bit confused why there multiple calls to
dgetrf_parallelin the stack. @martin-frbg any thoughts?FreddieWitherden commentedon Oct 29, 2025
I believe they're using a recursive implementation. See:
https://github.com/OpenMathLib/OpenBLAS/blob/develop/lapack/getrf/getrf_parallel.c#L678
which calls:
https://github.com/OpenMathLib/OpenBLAS/blob/develop/lapack/getrf/getrf_parallel.c#L764
I have always found the threading support in OpenBLAS to be somewhat twitchy. Likely not something I'd trust without a huge test suite.
ilayn commentedon Oct 29, 2025
Did anyone had the capacity to try this on MKL or Accelerate platforms to single out OpenBLAS?
ev-br commentedon Oct 29, 2025
Seems to run fine with numpy main (5fe514b), python 3.12, and
On the same machine, does hang with
martin-frbg commentedon Oct 29, 2025
Having a reproducer for Linux/x86_64 (as opposed to a hang happening exclusively on OSX/x86_64 as with the related scipy issue) should help, thanks.
And regarding the perceived twitchyness - the problem is (largely) that thread safety was a very late consideration - with the original GotoBLAS and any subsequent OpenBLAS until the beginning of my involvement about 10 years ago, one had to use OpenMP and hope for the best, while the pthreads build was racy as hell
FreddieWitherden commentedon Oct 29, 2025
What is surprising is that it is the parent which has issues. When forking one would expect the child to get confused (since all of the other threads it thinks it spawned in the pool are now gone). But the parent should usually be fine even if it doesn't take any action. This makes me wonder if an atfork handler is at fault.
Also, if it makes any difference the code seems to work with lower thread counts i.e.,
export OMP_NUM_THREADS=2does not exhibit the issue, but larger numbers do (and I was running on a 20 'core' machine).martin-frbg commentedon Oct 29, 2025
Yes, I'm also thinking that it is the atfork handler that causes the deadlock - it uses the same (changed in PR5170) atomic ordering constraints as the main blas_server loop while it tries to shut down the thread pool.
And I guess higher thread count simply translates to a higher probability that races/clashes occur (which would also be a main reason for the blatant lack of thread safety having no bearing on the early success of GotoBLAS/OpenBLAS - not much of a race when one can have at most 4 contestants)
martin-frbg commentedon Oct 29, 2025
Hmm, seems the hang occurs after the atfork handler has returned.
FreddieWitherden commentedon Oct 29, 2025
That makes sense to me. From the backtrace it is making it to the inversion function. However, my working assumption is that the atfork handler is messing up the threadpool which causes subsequent calls to deadlock.
mattip commentedon Oct 30, 2025
I mentioned this issue on scipy/scipy#23686, which is the same problem on SciPy. @lesteve commented with a C reproducer there that only uses OpenBLAS. I can confirm that the small C reproducer hangs on Ubuntu 24.04 x86_64 just like in this issue with NumPy. Here is a shortened incomplete version of the reproducer.
When I add some printing at various calls inside OpenBLAS, I see:
Is it expected to see the additional call to(Edit: yes, that is a consequence ofblas_thread_shutdown?exit()ing the child) In any case, the hang seems to be inblas_thread_initat the call toLOCK_COMMAND(&server_lock);.FreddieWitherden commentedon Oct 30, 2025
It would be interesting to know the state of
server_lockat the start (so whenblas_thread_initis called for the very first time), beforeatfork, and postatfork. If the atfork handler is cleaning everything up before we fork (so bringing down the thread pool) then the variables should be reset to their initial states. I am guessing this is not happening and this is what is causing us to deadlock.mattip commentedon Oct 30, 2025
Let's move further discussion to OpenMathLib/OpenBLAS#5520
FreddieWitherden commentedon Oct 31, 2025
Given the issue now appears to be fixed on the OpenBLAS side what are the next steps? Is it worth reverting the OpenBLAS version used for the NumPy builds in the interim whilst we wait for a new release of OpenBLAS to be made?
1 remaining item
pv101 commentedon Jan 19, 2026
Hi @mattip - thanks for the above. However, I'm still getting issues with Freddie's initial test code on my Mac with Python 3.12 and Numpy 2.4.1. Specifically, it works fine with OMP_NUM_THREADS=1 but hangs if OMP_NUM_THREADS is not set.
mattip commentedon Jan 20, 2026
What version of scipy-openblas64 are you using? (i.e. what version is at
/private/var/folders/pr/_3h4tm913_l56k6hrhpm89z00000gn/T/cibw-run-d6u3niut/cp312-macosx_arm64/build/venv/lib/python3.12/site-packages/scipy_openblas64? When I use the PyPI provided wheel (pip install numpy==2.4.1) I do not see the hangpv101 commentedon Jan 21, 2026
I couldn't work out how to get the version, but I have MD5 (libscipy_openblas64_.dylib) = ffc464ddedf3038f3c928b6ea9a33999 if that helps? The .dylib file lives inside the venv on my system: venv/lib/python3.12/site-packages/numpy/.dylibs/libscipy_openblas64_.dylib (the path in the above post is apparently "a build-time path that got baked into the wheel metadata when the NumPy wheel was produced" according to ChatGPT).
mattip commentedon Jan 22, 2026
This is confusing. You say you have
venv/lib/python3.12/site-packages/numpy/.dylibs/libscipy_openblas64_.dylib, which indicates you are using numpy from PyPI. Butshow_configis showing/private/var/folders/pr/_3h4tm913_l56k6hrhpm89z00000gn/T/cibw-run-d6u3niut/cp312-macosx_arm64/build/venv/lib/python3.12/site-packages/scipy_openblas64/libwhich would indicate you also have scipy-openblas64 installed from a run of cibuildwheel. Could describe how you put together this NumPy environment?pv101 commentedon Jan 25, 2026
Thanks for the reply. I just used "pip install numpy==2.4.1". I tried again in a completely fresh venv and have the same issues.
mattip commentedon Jan 26, 2026
Please open a new issue, and clearly indicate your operating system, platform, and what you are doing to produce the hang. Above you said
but then
pv101 commentedon Jan 27, 2026
Thanks. I'll open a new issue. Just FYI, I have consistently been seeing the issue my side, I think
was a comment in one of your posts.
WillTrojak commentedon Jan 30, 2026
Hi, I'm seeing this issue with numpy 2.4.1 + Python 3.13.11. This is on an arm64 system running Ubuntu 24.04.
Is there a work around or a fix that you suggest?
mattip commentedon Jan 30, 2026
See #30733