Skip to content

threadpool_limits is not thread-safe under ThreadPoolExecutor #205

Description

@ZX-ModelCloud

When using threadpoolctl.threadpool_limits inside multiple Python threads (e.g. via ThreadPoolExecutor), the thread limit is not reliably enforced.
Despite using with threadpool_limits(limits=2) inside each worker, some threads observe OpenBLAS using the full default thread count.

Reproducible Example

#!/usr/bin/env python3
from threadpoolctl import threadpool_limits, threadpool_info
import torch

from concurrent.futures import ThreadPoolExecutor, as_completed


def worker(i):
    # NOTE: this appears to be process-global, not thread-local
    with threadpool_limits(limits=2):
        a = torch.randn(512, 256, device="cpu")
        b = torch.randn(256, 512, device="cpu")
        _ = a @ b
        info = threadpool_info()
        print(f"[worker {i}] threadpool_info:", info)
        return info


results = []

with ThreadPoolExecutor(max_workers=8) as executor:
    futures = [executor.submit(worker, i) for i in range(8)]
    for fut in as_completed(futures):
        results.append(fut.result())

print("final threadpool_info:")
print(threadpool_info())

Actual Output

[worker 6] threadpool_info: [{'user_api': 'blas', 'internal_api': 'openblas', 'num_threads': 2, 'prefix': 'libscipy_openblas', 'filepath': '/root/miniconda3/envs/gp_py311/lib/python3.11/site-packages/numpy.libs/libscipy_openblas64_-56d6093b.so', 'version': '0.3.29', 'threading_layer': 'pthreads', 'architecture': 'Haswell'}, {'user_api': 'openmp', 'internal_api': 'openmp', 'num_threads': 2, 'prefix': 'libgomp', 'filepath': '/root/miniconda3/envs/gp_py311/lib/python3.11/site-packages/torch/lib/libgomp.so.1', 'version': None}]
[worker 1] threadpool_info: [{'user_api': 'blas', 'internal_api': 'openblas', 'num_threads': 2, 'prefix': 'libscipy_openblas', 'filepath': '/root/miniconda3/envs/gp_py311/lib/python3.11/site-packages/numpy.libs/libscipy_openblas64_-56d6093b.so', 'version': '0.3.29', 'threading_layer': 'pthreads', 'architecture': 'Haswell'}, {'user_api': 'openmp', 'internal_api': 'openmp', 'num_threads': 2, 'prefix': 'libgomp', 'filepath': '/root/miniconda3/envs/gp_py311/lib/python3.11/site-packages/torch/lib/libgomp.so.1', 'version': None}]
[worker 5] threadpool_info: [{'user_api': 'blas', 'internal_api': 'openblas', 'num_threads': 24, 'prefix': 'libscipy_openblas', 'filepath': '/root/miniconda3/envs/gp_py311/lib/python3.11/site-packages/numpy.libs/libscipy_openblas64_-56d6093b.so', 'version': '0.3.29', 'threading_layer': 'pthreads', 'architecture': 'Haswell'}, {'user_api': 'openmp', 'internal_api': 'openmp', 'num_threads': 2, 'prefix': 'libgomp', 'filepath': '/root/miniconda3/envs/gp_py311/lib/python3.11/site-packages/torch/lib/libgomp.so.1', 'version': None}]
[worker 3] threadpool_info: [{'user_api': 'blas', 'internal_api': 'openblas', 'num_threads': 2, 'prefix': 'libscipy_openblas', 'filepath': '/root/miniconda3/envs/gp_py311/lib/python3.11/site-packages/numpy.libs/libscipy_openblas64_-56d6093b.so', 'version': '0.3.29', 'threading_layer': 'pthreads', 'architecture': 'Haswell'}, {'user_api': 'openmp', 'internal_api': 'openmp', 'num_threads': 2, 'prefix': 'libgomp', 'filepath': '/root/miniconda3/envs/gp_py311/lib/python3.11/site-packages/torch/lib/libgomp.so.1', 'version': None}]
[worker 4] threadpool_info: [{'user_api': 'blas', 'internal_api': 'openblas', 'num_threads': 2, 'prefix': 'libscipy_openblas', 'filepath': '/root/miniconda3/envs/gp_py311/lib/python3.11/site-packages/numpy.libs/libscipy_openblas64_-56d6093b.so', 'version': '0.3.29', 'threading_layer': 'pthreads', 'architecture': 'Haswell'}, {'user_api': 'openmp', 'internal_api': 'openmp', 'num_threads': 2, 'prefix': 'libgomp', 'filepath': '/root/miniconda3/envs/gp_py311/lib/python3.11/site-packages/torch/lib/libgomp.so.1', 'version': None}]
[worker 0] threadpool_info: [{'user_api': 'blas', 'internal_api': 'openblas', 'num_threads': 2, 'prefix': 'libscipy_openblas', 'filepath': '/root/miniconda3/envs/gp_py311/lib/python3.11/site-packages/numpy.libs/libscipy_openblas64_-56d6093b.so', 'version': '0.3.29', 'threading_layer': 'pthreads', 'architecture': 'Haswell'}, {'user_api': 'openmp', 'internal_api': 'openmp', 'num_threads': 2, 'prefix': 'libgomp', 'filepath': '/root/miniconda3/envs/gp_py311/lib/python3.11/site-packages/torch/lib/libgomp.so.1', 'version': None}]
[worker 7] threadpool_info: [{'user_api': 'blas', 'internal_api': 'openblas', 'num_threads': 2, 'prefix': 'libscipy_openblas', 'filepath': '/root/miniconda3/envs/gp_py311/lib/python3.11/site-packages/numpy.libs/libscipy_openblas64_-56d6093b.so', 'version': '0.3.29', 'threading_layer': 'pthreads', 'architecture': 'Haswell'}, {'user_api': 'openmp', 'internal_api': 'openmp', 'num_threads': 2, 'prefix': 'libgomp', 'filepath': '/root/miniconda3/envs/gp_py311/lib/python3.11/site-packages/torch/lib/libgomp.so.1', 'version': None}]
[worker 2] threadpool_info: [{'user_api': 'blas', 'internal_api': 'openblas', 'num_threads': 2, 'prefix': 'libscipy_openblas', 'filepath': '/root/miniconda3/envs/gp_py311/lib/python3.11/site-packages/numpy.libs/libscipy_openblas64_-56d6093b.so', 'version': '0.3.29', 'threading_layer': 'pthreads', 'architecture': 'Haswell'}, {'user_api': 'openmp', 'internal_api': 'openmp', 'num_threads': 2, 'prefix': 'libgomp', 'filepath': '/root/miniconda3/envs/gp_py311/lib/python3.11/site-packages/torch/lib/libgomp.so.1', 'version': None}]
final threadpool_info:
[{'user_api': 'blas', 'internal_api': 'openblas', 'num_threads': 2, 'prefix': 'libscipy_openblas', 'filepath': '/root/miniconda3/envs/gp_py311/lib/python3.11/site-packages/numpy.libs/libscipy_openblas64_-56d6093b.so', 'version': '0.3.29', 'threading_layer': 'pthreads', 'architecture': 'Haswell'}, {'user_api': 'openmp', 'internal_api': 'openmp', 'num_threads': 24, 'prefix': 'libgomp', 'filepath': '/root/miniconda3/envs/gp_py311/lib/python3.11/site-packages/torch/lib/libgomp.so.1', 'version': None}]

Some workers correctly observe num_threads = 2, while others observe num_threads = 24, even though all are wrapped in with threadpool_limits(limits=2).


Environment

Distributor ID: Ubuntu
Description: Ubuntu 22.04.5 LTS
Release: 22.04
Codename: jammy

torch: 2.9.0
numpy: 2.2.6
scipy: 1.16.2
threadpoolctl: 3.6.0

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions