From 37b9866bf3d9da8cc8fbf01705e3fd306b3f4dcb Mon Sep 17 00:00:00 2001 From: Jim Garrison Date: Fri, 16 Oct 2026 12:20:13 -0400 Subject: [PATCH] Give the static TLS block enough headroom for libgomp (#64) Since qiskit 2.6.0 reached PyPI, every Linux CI job fails before running a test: importing our extension raises "cannot allocate memory in static TLS block" from libgomp, and the whole suite errors out. macOS is unaffected. glibc sizes the per-thread TLS block at startup and keeps only a small surplus for libraries dlopen'ed later that need initial-exec TLS. Every Python extension module arrives that way, so they all compete for that one surplus, and the library that fails is whichever asks last rather than whichever took the space. qiskit 2.6.0's _accelerate grew from 736 to 1544 bytes of it, which leaves too little for the libgomp we pull in afterwards. The growth is matrixmultiply 0.3.11 enabling its AVX-512 kernels, which quadruple one thread-local scratch buffer; it takes effect at rustc >= 1.89, and 2.6.0 also raised its MSRV to exactly that. Raising the surplus restores the headroom. It goes in tox.ini rather than the workflows so local runs get it too; passenv would only forward the variable when the caller already set it, which is no help in CI. tox replaces setenv rather than merging it in a child environment, so the mpi env repeats the setting. Measured on glibc 2.42: a single later dlopen can claim 1664 bytes of initial-exec TLS, so 2.6.0 clears it by about seven bytes once alignment is counted. That thin margin is why the same build fails in one environment and passes in another. Preloading libgomp also works, but only for libgomp, since it removes it from the queue without enlarging the surplus. Reported upstream as Qiskit/qiskit#17213; revert this once that is fixed. Assisted-by: Claude Opus 5 (cherry picked from commit ca78f4cf138ec147ed97cf455083762e4df2493e) --- tox.ini | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tox.ini b/tox.ini index aaa6118..d149506 100644 --- a/tox.ini +++ b/tox.ini @@ -29,6 +29,25 @@ passenv = OMP_NUM_THREADS PATH LD_LIBRARY_PATH +# Work around Qiskit/qiskit#17213: with qiskit 2.6.0 installed, importing our +# extension fails on Linux with "cannot allocate memory in static TLS block". +# +# glibc sizes the per-thread TLS block at startup and keeps only a small surplus for +# libraries dlopen'ed afterwards that need initial-exec TLS. Every Python extension +# arrives that way, so they all draw on that surplus. qiskit 2.6.0's _accelerate grew +# from 736 to 1544 bytes of it (matrixmultiply 0.3.11 enabling AVX-512 kernels, which +# quadruple one thread-local buffer), which leaves too little for the libgomp our +# extension pulls in. The error names libgomp, but it is whoever asks last that fails. +# +# Raising the surplus restores the headroom. It is reserved per thread and only commits +# the pages actually used, so a generous value costs little. Drop this once 17213 is +# fixed upstream. +# +# Linux-only: this is a glibc mechanism, and macOS links libomp with no equivalent +# limit. Note glibc silently ignores an unrecognized tunable name, so a typo here looks +# like a working config. +setenv = + linux: GLIBC_TUNABLES = glibc.rtld.optional_static_tls=65536 extras = test commands = @@ -52,6 +71,9 @@ allowlist_externals = mpirun setenv = SBD_TEST_NPROCS = {env:SBD_TEST_NPROCS:2} + # setenv is replaced, not merged, in a child environment, so repeat the + # static-TLS headroom [testenv] sets; see the comment there. + linux: GLIBC_TUNABLES = glibc.rtld.optional_static_tls=65536 commands = mpirun -n {env:SBD_TEST_NPROCS} pytest --only-mpi {posargs}