Allow immutable borrow to access QuantumCircuit.parameters
(backport #12918)
#12958
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
QuantumCircuit.parameters
is logically a read-only operation onQuantumCircuit
. For efficiency in multiple calls toassign_parameters
, we actually cache the sort order of the internalParameterTable
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 toCircuitData::copy
(so an immutable borrow was still live), allowing another thread to continue on to the getterCircuitData::get_parameters
, which required a mutable borrow, which failed due to the paused thread incopy
.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.Details and comments
This won't be the last time we have problems with Python threading letting people see Rust reject conditions that allow data races. This particular case isn't the user's fault, though -
QuantumCircuit.parameters
shouldn't need a mutable borrow, and should be thread-safe to be combined with other thread-safe methods onQuantumCircuit
. The issue was an internal caching detail, and that's something Qiskit should help.An example script that produced errors before:
The thread workers in this example aren't doing anything wrong;
assign_parameters(inplace=False)
should be safe to run in multiple threads. Before the Rust-space work, Python would silently ignore the potential data race, and the GIL would have prevented anything catastrophic happening (though multiple threads might have separately calculated the sort order).In that example, the
QuantumCircuit.copy
call withinQuantumCircuit.assign_parameters
takes an immutable borrow in Rust space, but the Rust space method can become interrupted part way through, while copying theMeasure
instructions (since this requires yielding control to the Python interpreter to runMeasure.copy
). This allows another thread to progress toQuantumCircuit.parameters
, which needed a mutable borrow, which could not be taken out due to the other paused thread.This is an automatic backport of pull request #12918 done by Mergify.