From 0d5e834ee3441311e8b5a50fa497dfb56721ec60 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Tue, 16 May 2023 21:48:59 +0200 Subject: [PATCH] Add Py::get_frozen for GIL-independent access to frozen classes. --- newsfragments/3158.added.md | 1 + src/instance.rs | 35 ++++++++++++++++++++++++++++++++++- src/pycell.rs | 13 ++++++++++++- 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 newsfragments/3158.added.md diff --git a/newsfragments/3158.added.md b/newsfragments/3158.added.md new file mode 100644 index 00000000000..c8fa73680ad --- /dev/null +++ b/newsfragments/3158.added.md @@ -0,0 +1 @@ +Add `Py::get_frozen` for GIL-indepedent access to internally synchronized frozen classes. diff --git a/src/instance.rs b/src/instance.rs index 7cf64247a30..83885647a80 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -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, @@ -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 + 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 = unsafe { PyNativeType::unchecked_downcast(&*any) }; + cell.get_frozen() + } } impl Py { diff --git a/src/pycell.rs b/src/pycell.rs index dbd5fff9b9b..4553ba1ad06 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -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; @@ -410,6 +413,14 @@ impl PyCell { .map(|_: ()| &*self.contents.value.get()) } + pub(crate) fn get_frozen(&self) -> &T + where + T: PyClass + 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