Skip to content

fix hanging - #1112

Open
Sajish06 wants to merge 2 commits into
quantumlib:mainfrom
Sajish06:fix-hanging
Open

Sajish06 wants to merge 2 commits into
quantumlib:mainfrom
Sajish06:fix-hanging

Conversation

@Sajish06

Copy link
Copy Markdown

Fix deadlock and thread-safety issues during concurrent calls to sample()

Description

This PR resolves an issue where programs hang indefinitely when making concurrent calls to CompiledMeasurementSampler.sample() (and related sampling methods) across multiple Python threads.

Root Causes

  1. Deadlock between C++ static initialization lock and Python GIL:
    When multiple threads invoke .sample() concurrently for the first time without prior NumPy array initialization in the process, pybind11::array_t lazily initializes its NumPy C-API bindings via pybind11::detail::npy_api::get().

    • npy_api::get() initializes a function-local static (static const npy_api api = lookup();), which acquires an internal C++ compiler static initialization lock (_Init_thread_header on MSVC / __cxa_guard_acquire on GCC/Clang).
    • lookup() executes pybind11::module_::import("numpy.core.multiarray"). In Python 3.12 / NumPy 2.x, if this module is not already loaded in sys.modules, importlib runs.
    • During module resolution and file I/O, importlib yields or releases the GIL. Another thread waiting on the GIL runs, calls sample(), and enters npy_api::get(), where it blocks on the C++ compiler's static initialization lock while holding the Python GIL.
    • The first thread finishes I/O and attempts to reacquire the Python GIL to finish the import, resulting in an unrecoverable deadlock.
  2. Unsynchronized RNG & Moved-from State in CompiledMeasurementSampler:
    In compiled_measurement_sampler.pybind.h, the sampler holds a mutable std::mt19937_64 rng instance. When sample_batch_measurements is called, it does std::move(rng) into a local FrameSimulator, leaving the sampler's rng in an unspecified moved-from state while the circuit simulates. Concurrent calls on the same sampler instance race on rng, causing internal state corruption of the Mersenne Twister engine and unpredictable execution.

  3. Missing GIL Release in CompiledMeasurementSampler:
    Unlike CompiledDetectorSampler, CompiledMeasurementSampler::sample_to_numpy() did not release the GIL (pybind11::gil_scoped_release), unnecessarily serializing simulation workloads across separate sampler instances.


Changes

  • Pre-import NumPy during module initialization:
    • In glue/python/src/stim/__init__.py: Pre-imported numpy and numpy.core.multiarray during initial import on the main thread so that sys.modules is populated before worker threads are spawned.
    • In src/stim/py/stim.pybind.cc: Added an explicit import of numpy in PYBIND11_MODULE for cases where the compiled C extension is imported directly.
  • Synchronized rng and released GIL in CompiledMeasurementSampler:
    • In src/stim/py/compiled_measurement_sampler.pybind.h: Added std::unique_ptr<std::mutex> rng_mutex to preserve move-constructibility.
    • In src/stim/py/compiled_measurement_sampler.pybind.cc: Protected rng with std::lock_guard<std::mutex> and released the GIL via pybind11::gil_scoped_release during sample_batch_measurements and sample_write.
  • Synchronized frame_sim in CompiledDetectorSampler:
    • In src/stim/py/compiled_detector_sampler.pybind.h and src/stim/py/compiled_detector_sampler.pybind.cc: Guarded frame_sim with a mutex during sample_to_numpy and sample_write to ensure thread-safe usage when multiple threads share an instance.
  • Unit Test:
    • Added test_concurrent_measurement_sampler() in src/stim/py/compiled_measurement_sampler_pybind_test.py using ThreadPoolExecutor across both shared and independent sampler instances.

Verification

  1. Reproduction Test: Ran concurrent sampling with barriers in fresh Python processes without prior warmup. All threads completed in milliseconds with zero hangs.
  2. Stress Test: Executed 16 jobs across 8 workers on a single sampler instance with ThreadPoolExecutor; completed successfully with expected array shapes and data integrity.
  3. Test Suite: Ran all unit tests in src/stim/py/; all 24 tests passed.

@google-cla

google-cla Bot commented Sep 24, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@Sajish06

Copy link
Copy Markdown
Author

@google-bot I signed it!

@jfoliveiraramos

jfoliveiraramos commented Sep 25, 2026 •

Copy link
Copy Markdown

This AI-generated PR (at least the description, good lord) is supposed to fix #1107 btw, since it's not linked

Comment thread glue/python/src/stim/__init__.py
Comment thread src/stim/py/stim.pybind.cc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants