Skip to content

Commit

Permalink
Add Py::get_frozen for GIL-independent access to frozen classes.
Browse files Browse the repository at this point in the history
  • Loading branch information
adamreichold committed May 16, 2023
1 parent edb9522 commit 0d5e834
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 2 deletions.
1 change: 1 addition & 0 deletions newsfragments/3158.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `Py::get_frozen` for GIL-indepedent access to internally synchronized frozen classes.
35 changes: 34 additions & 1 deletion src/instance.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::pyclass::boolean_struct::False;
// Copyright (c) 2017-present PyO3 Project and Contributors
use crate::conversion::PyTryFrom;
use crate::err::{self, PyDowncastError, PyErr, PyResult};
use crate::gil;
use crate::pycell::{PyBorrowError, PyBorrowMutError, PyCell};
use crate::pyclass::boolean_struct::{False, True};
use crate::types::{PyDict, PyString, PyTuple};
use crate::{
ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyClass, PyClassInitializer,
Expand Down Expand Up @@ -467,6 +467,39 @@ where
{
self.as_ref(py).try_borrow_mut()
}

/// Provide an immutable borrow of the value `T` without acquiring the GIL.
///
/// This is sound if the class is frozen and `Sync`.
///
/// # Examples
///
/// ```
/// use std::sync::atomic::{AtomicUsize, Ordering};
/// # use pyo3::prelude::*;
///
/// #[pyclass(frozen)]
/// struct FrozenCounter {
/// value: AtomicUsize,
/// }
///
/// let cell = Python::with_gil(|py| {
/// let counter = FrozenCounter { value: AtomicUsize::new(0) };
///
/// Py::new(py, counter).unwrap()
/// });
///
/// cell.get_frozen().value.fetch_add(1, Ordering::Relaxed);
/// ```
pub fn get_frozen(&self) -> &T
where
T: PyClass<Frozen = True> + Sync,
{
let any = self.as_ptr() as *const PyAny;
// SAFETY: The class itself is frozen and `Sync` and we do not access anything but `cell.contents.value`.
let cell: &PyCell<T> = unsafe { PyNativeType::unchecked_downcast(&*any) };
cell.get_frozen()
}
}

impl<T> Py<T> {
Expand Down
13 changes: 12 additions & 1 deletion src/pycell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,10 @@ use crate::exceptions::PyRuntimeError;
use crate::impl_::pyclass::{
PyClassBaseType, PyClassDict, PyClassImpl, PyClassThreadChecker, PyClassWeakRef,
};
use crate::pyclass::{boolean_struct::False, PyClass};
use crate::pyclass::{
boolean_struct::{False, True},
PyClass,
};
use crate::pyclass_init::PyClassInitializer;
use crate::type_object::{PyLayout, PySizedLayout};
use crate::types::PyAny;
Expand Down Expand Up @@ -410,6 +413,14 @@ impl<T: PyClass> PyCell<T> {
.map(|_: ()| &*self.contents.value.get())
}

pub(crate) fn get_frozen(&self) -> &T
where
T: PyClass<Frozen = True> + Sync,
{
// SAFETY: The class is frozen and `Sync`.
unsafe { &*self.get_ptr() }
}

/// Replaces the wrapped value with a new one, returning the old value.
///
/// # Panics
Expand Down

0 comments on commit 0d5e834

Please # to comment.