Skip to content

Commit

Permalink
Merge pull request #3263 from PyO3/with-pool
Browse files Browse the repository at this point in the history
Add Python::with_pool as a safer alternative to Python::new_pool.
  • Loading branch information
davidhewitt committed Jun 21, 2023
2 parents b2a1a96 + 4afa994 commit a6e1051
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 0 deletions.
1 change: 1 addition & 0 deletions newsfragments/3263.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add the `Python::with_pool` which is a safer but more limited alternative to `Python::new_pool`.
66 changes: 66 additions & 0 deletions src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,72 @@ impl<'py> Python<'py> {
}
}

impl Python<'_> {
/// Creates a scope using a new pool for managing PyO3's owned references.
///
/// This is a safe alterantive to [`new_pool`][Self::new_pool] as
/// it limits the closure to using the new GIL token at the cost of
/// being unable to capture existing GIL-bound references.
///
/// Note that on stable Rust, this API suffers from the same the `SendWrapper` loophole
/// as [`allow_threads`][Self::allow_threads], c.f. the documentation of the [`Ungil`] trait,
///
/// # Examples
///
/// ```rust
/// # use pyo3::prelude::*;
/// Python::with_gil(|py| {
/// // Some long-running process like a webserver, which never releases the GIL.
/// loop {
/// // Create a new scope, so that PyO3 can clear memory at the end of the loop.
/// py.with_pool(|py| {
/// // do stuff...
/// });
/// # break; // Exit the loop so that doctest terminates!
/// }
/// });
/// ```
///
/// The `Ungil` bound on the closure does prevent hanging on to existing GIL-bound references
///
/// ```compile_fail
/// # use pyo3::prelude::*;
/// # use pyo3::types::PyString;
///
/// Python::with_gil(|py| {
/// let old_str = PyString::new(py, "a message from the past");
///
/// py.with_pool(|_py| {
/// print!("{:?}", old_str);
/// });
/// });
/// ```
///
/// or continuing to use the old GIL token
///
/// ```compile_fail
/// # use pyo3::prelude::*;
///
/// Python::with_gil(|old_py| {
/// old_py.with_pool(|_new_py| {
/// let _none = old_py.None();
/// });
/// });
/// ```
#[inline]
pub fn with_pool<F, R>(&self, f: F) -> R
where
F: for<'py> FnOnce(Python<'py>) -> R + Ungil,
{
// SAFETY: The closure is `Ungil`,
// i.e. it does not capture any GIL-bound references
// and accesses only the newly created GIL token.
let pool = unsafe { GILPool::new() };

f(pool.python())
}
}

impl<'unbound> Python<'unbound> {
/// Unsafely creates a Python token with an unbounded lifetime.
///
Expand Down

0 comments on commit a6e1051

Please sign in to comment.