Skip to content

Commit

Permalink
Remove need for mutable reference to static
Browse files Browse the repository at this point in the history
Mutable references to static data are inherently unsafe and typically
unsound in Rust, because statics are implicitly shared between threads,
and the borrow checker can only enforce the shared/exclusive reference
limitations within a single thread here.

This just moves the thread-exclusion logic into the individual elements
of the `static`, where the `GILOnceCell` can correctly handle the
runtime exclusion of multiple threads. (The GIL is no longer suitable
for thread exclusion if we were doing a freethreaded Python build, but
that's a problem we have all over Qiskit, and would need to change to
`OnceLock` or the like.)
  • Loading branch information
jakelishman committed Jan 21, 2025
1 parent 400f22d commit 1ceac25
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 37 deletions.
95 changes: 59 additions & 36 deletions crates/circuit/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,42 +255,65 @@ static STDGATE_IMPORT_PATHS: [[&str; 2]; STANDARD_GATE_SIZE] = [
///
/// NOTE: the order here is significant it must match the StandardGate variant's number must match
/// index of it's entry in this table. This is all done statically for performance
static mut STDGATE_PYTHON_GATES: GILOnceCell<[Option<PyObject>; STANDARD_GATE_SIZE]> =
GILOnceCell::new();

#[inline]
pub fn populate_std_gate_map(py: Python, rs_gate: StandardGate, py_gate: PyObject) {
let gate_map = unsafe {
match STDGATE_PYTHON_GATES.get_mut() {
Some(gate_map) => gate_map,
None => {
let array: [Option<PyObject>; STANDARD_GATE_SIZE] = std::array::from_fn(|_| None);
STDGATE_PYTHON_GATES.set(py, array).unwrap();
STDGATE_PYTHON_GATES.get_mut().unwrap()
}
}
};
let gate_cls = &gate_map[rs_gate as usize];
if gate_cls.is_none() {
gate_map[rs_gate as usize] = Some(py_gate.clone_ref(py));
}
}
static STDGATE_PYTHON_GATES: [GILOnceCell<PyObject>; STANDARD_GATE_SIZE] = [
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
GILOnceCell::new(),
];

#[inline]
pub fn get_std_gate_class(py: Python, rs_gate: StandardGate) -> PyResult<PyObject> {
let gate_map =
unsafe { STDGATE_PYTHON_GATES.get_or_init(py, || std::array::from_fn(|_| None)) };
let gate = &gate_map[rs_gate as usize];
let populate = gate.is_none();
let out_gate = match gate {
Some(gate) => gate.clone_ref(py),
None => {
let [py_mod, py_class] = STDGATE_IMPORT_PATHS[rs_gate as usize];
py.import_bound(py_mod)?.getattr(py_class)?.unbind()
}
};
if populate {
populate_std_gate_map(py, rs_gate, out_gate.clone_ref(py));
}
Ok(out_gate)
pub fn get_std_gate_class(py: Python, rs_gate: StandardGate) -> PyResult<&'static Py<PyAny>> {
STDGATE_PYTHON_GATES[rs_gate as usize].get_or_try_init(py, || {
let [py_mod, py_class] = STDGATE_IMPORT_PATHS[rs_gate as usize];
Ok(py.import_bound(py_mod)?.getattr(py_class)?.unbind())
})
}
2 changes: 1 addition & 1 deletion crates/circuit/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,7 @@ impl StandardGate {
}

#[getter]
pub fn get_gate_class(&self, py: Python) -> PyResult<Py<PyAny>> {
pub fn get_gate_class(&self, py: Python) -> PyResult<&'static Py<PyAny>> {
get_std_gate_class(py, *self)
}

Expand Down

0 comments on commit 1ceac25

Please sign in to comment.