You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Allow immutable borrow to access QuantumCircuit.parameters (#12918)
* Allow immutable borrow to access `QuantumCircuit.parameters`
`QuantumCircuit.parameters` is logically a read-only operation on
`QuantumCircuit`. For efficiency in multiple calls to
`assign_parameters`, we actually cache the sort order of the internal
`ParameterTable` on access. This is purely a caching effect, and should
not leak out to users.
The previous implementation took a Rust-space mutable borrow out in
order to (potentially) mutate the cache. This caused problems if
multiple Python threads attempted to call `assign_parameters`
simultaneously; it was possible for one thread to give up the GIL during
its initial call to `CircuitData::copy` (so an immutable borrow was
still live), allowing another thread to continue on to the getter
`CircuitData::get_parameters`, which required a mutable borrow, which
failed due to the paused thread in `copy`.
This moves the cache into a `RefCell`, allowing the parameter getters to
take an immutable borrow as the receiver. We now write the cache out
only if we *can* take the mutable borrow out necessary. This can mean
that other threads will have to repeat the work of re-sorting the
parameters, because their borrows were blocking the saving of the cache,
but this will not cause failures.
The methods on `ParameterTable` that invalidate the cache all require a
mutable borrow on the table itself. This makes it impossible for an
immutable borrow to exist simultaneously on the cache, so these methods
should always succeed to acquire the cache lock to invalidate it.
* Use `RefCell::get_mut` where possible
In several cases, the previous code was using the runtime-checked
`RefCell::borrow_mut` in locations that can be statically proven to be
safe to take the mutable reference. Using the correct function for this
makes the logic clearer (as well as technically removing a small amount
of runtime overhead).
* Use `OnceCell` instead of `RefCell`
`OnceCell` has less runtime checking than `RefCell` (only whether it is
initialised or not, which is an `Option` check), and better represents
the dynamic extensions to the borrow checker that we actually need for
the caching in this method.
All methods that can invalidate the cache all necessarily take `&mut
ParameterTable` already, since they will modify Rust-space data. A
`OnceCell` can be deinitialised through a mutable reference, so this is
fine. The only reason a `&ParameterTable` method would need to mutate
the cache is to create it, which is the allowed set of `OnceCell`
operations.
0 commit comments