-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use OnceLock
in imports.
#13776
base: main
Are you sure you want to change the base?
Use OnceLock
in imports.
#13776
Conversation
It appears that GILOnceCell is not sufficient for module import and initialization. When running free-threaded Python in unit tests, CPython throws a _DeadLockError during module import. Even though GILOnceCell allows threads to race and the first one to finish gets the initialization, it appears that the import process itself is not reentrant. This may also require a bug report on PyO3, since its implementation of GILOnceCell::import does what we were doing previously.
One or more of the following people are relevant to this code:
|
Pull Request Test Coverage Report for Build 13077893147Details
💛 - Coveralls |
Is there any performance impact with this change. We don't actually support free threaded python and I don't think it's something we have immediate plans for. So I'm curious if there are side effects doing and what if any tradeoffs there are. |
@mtreinish I'll run some benchmarks and report back. The other thing we can do is run the Rust tests with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change makes sense, but it won't prevent issues with deadlocking in Python. If you try to run tests that perform imports in parallel using cargo test
or tox -e rust
, the condition will still stand and those tests will fail due to a deadlock. Try with these tests:
#[cfg(all(test, not(miri)))]
mod test {
use super::*;
#[test]
fn import_qubit() -> PyResult<()> {
Python::with_gil(|py| -> PyResult<()> {
QUBIT.get_bound(py).call0()?;
Ok(())
})
}
#[test]
fn import_clbit() -> PyResult<()> {
Python::with_gil(|py| -> PyResult<()> {
CLBIT.get_bound(py).call0()?;
Ok(())
})
}
#[test]
fn import_circ() -> PyResult<()> {
Python::with_gil(|py| -> PyResult<()> {
QUANTUM_CIRCUIT.get_bound(py).call1((3, 4))?;
Ok(())
})
}
}
Which will result in
---- dag_circuit::test::test_push_back stdout ----
thread 'dag_circuit::test::test_push_back' panicked at crates/circuit/src/imports.rs:46:18:
called `Result::unwrap()` on an `Err` value: PyErr { type: <class '_frozen_importlib._DeadlockError'>, value: _DeadlockError("deadlock detected by _ModuleLock('qiskit.circuit.quantumcircuit') at 4318559312"), traceback: Some(<traceback object at 0x1025e8ac0>) }
---- circuit_data::test::import_clbit stdout ----
thread 'circuit_data::test::import_clbit' panicked at crates/circuit/src/imports.rs:46:18:
called `Result::unwrap()` on an `Err` value: PyErr { type: <class '_frozen_importlib._DeadlockError'>, value: _DeadlockError("deadlock detected by _ModuleLock('qiskit.circuit.quantumcircuit') at 4318559312"), traceback: Some(<traceback object at 0x1027d5a40>) }
---- circuit_data::test::import_circ stdout ----
thread 'circuit_data::test::import_circ' panicked at crates/circuit/src/imports.rs:46:18:
called `Result::unwrap()` on an `Err` value: PyErr { type: <class '_frozen_importlib._DeadlockError'>, value: _DeadlockError("deadlock detected by _ModuleLock('qiskit.circuit.quantumregister') at 4318460096"), traceback: Some(<traceback object at 0x10437f780>) }
That is a python issue that we, unfortunately, won't be able to get around easily.
That being said, using the thread safe OnceLock
seems sensible to me for future reference. It doesn't seem to affect utility scale, (I haven't been able to test other things due to some ASV failures, fixed by #13781.) I will abstain from adding this to the merge queue in case @mtreinish's questions haven't been answered.
Summary
It appears that
GILOnceCell
is not sufficient for module import and initialization. When running free-threaded Python in unit tests, CPython throws a_DeadLockError
during module import. Even thoughGILOnceCell
allows threads to race and the first one to finish gets the initialization, it appears that the import process itself is not reentrant.Details and comments
This may also require a bug report on PyO3, since its implementation of
GILOnceCell::import
does what we were doing previously.This issue was discovered by @raynelfss when using Rust PyO3 tests on Linux.