Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Py::drop_ref method #3871

Merged
merged 7 commits into from Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions newsfragments/3871.added.md
@@ -0,0 +1 @@
Add `Py::drop_ref` to explicitly drop a `Py`` and immediately decrease the Python reference count if the GIL is already held.
50 changes: 50 additions & 0 deletions src/instance.rs
Expand Up @@ -817,6 +817,10 @@ impl<T> IntoPy<PyObject> for Borrowed<'_, '_, T> {
/// Otherwise, the reference count will be decreased the next time the GIL is
/// reacquired.
///
/// If you happen to be already holding the GIL, [`Py::drop_ref`] will decrease
/// the Python reference count immediately and will execute slightly faster than
/// relying on implicit [`Drop`]s.
///
/// # A note on `Send` and `Sync`
///
/// Accessing this object is threadsafe, since any access to its API requires a [`Python<'py>`](crate::Python) token.
Expand Down Expand Up @@ -1215,6 +1219,35 @@ impl<T> Py<T> {
unsafe { Py::from_borrowed_ptr(py, self.0.as_ptr()) }
}

/// Drops `self` and immediately decreases its reference count.
///
/// This method is a micro-optimisation over [`Drop`] if you happen to be holding the GIL
/// already.
juntyr marked this conversation as resolved.
Show resolved Hide resolved
///
/// Note that if you are using [`Bound`], you do not need to use [`Self::drop_ref`] since
/// [`Bound`] guarantees that the GIL is held.
///
/// # Examples
///
/// ```rust
/// use pyo3::prelude::*;
/// use pyo3::types::PyDict;
///
/// # fn main() {
/// Python::with_gil(|py| {
/// let object: Py<PyDict> = PyDict::new_bound(py).unbind();
///
/// // some usage of object
///
/// object.drop_ref(py);
/// });
/// # }
/// ```
#[inline]
pub fn drop_ref(self, py: Python<'_>) {
let _ = self.into_bound(py);
}

/// Returns whether the object is considered to be None.
///
/// This is equivalent to the Python expression `self is None`.
Expand Down Expand Up @@ -2129,6 +2162,23 @@ a = A()
})
}

#[test]
fn explicit_drop_ref() {
Python::with_gil(|py| {
let object: Py<PyDict> = PyDict::new_bound(py).unbind();
let object2 = object.clone_ref(py);

assert_eq!(object.as_ptr(), object2.as_ptr());
assert_eq!(object.get_refcnt(py), 2);

object.drop_ref(py);

assert_eq!(object2.get_refcnt(py), 1);

object2.drop_ref(py);
});
}

#[cfg(feature = "macros")]
mod using_macros {
use crate::PyCell;
Expand Down