remove `Python::acquire_gil`

This commit is contained in:
David Hewitt 2023-02-22 21:46:42 +00:00 committed by Adam Reichold
parent 3c634dd9a9
commit dd24c9ea71
15 changed files with 158 additions and 271 deletions

View File

@ -91,8 +91,7 @@ abi3-py311 = ["abi3", "pyo3-build-config/abi3-py311", "pyo3-ffi/abi3-py311"]
# Automatically generates `python3.dll` import libraries for Windows targets. # Automatically generates `python3.dll` import libraries for Windows targets.
generate-import-lib = ["pyo3-ffi/generate-import-lib"] generate-import-lib = ["pyo3-ffi/generate-import-lib"]
# Changes `Python::with_gil` and `Python::acquire_gil` to automatically initialize the # Changes `Python::with_gil` to automatically initialize the Python interpreter if needed.
# Python interpreter if needed.
auto-initialize = [] auto-initialize = []
# Optimizes PyObject to Vec conversion and so on. # Optimizes PyObject to Vec conversion and so on.

View File

@ -45,7 +45,7 @@ section for further detail.
### `auto-initialize` ### `auto-initialize`
This feature changes [`Python::with_gil`]({{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.with_gil) and [`Python::acquire_gil`]({{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.acquire_gil) to automatically initialize a Python interpreter (by calling [`prepare_freethreaded_python`]({{#PYO3_DOCS_URL}}/pyo3/fn.prepare_freethreaded_python.html)) if needed. This feature changes [`Python::with_gil`]({{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.with_gil) to automatically initialize a Python interpreter (by calling [`prepare_freethreaded_python`]({{#PYO3_DOCS_URL}}/pyo3/fn.prepare_freethreaded_python.html)) if needed.
If you do not enable this feature, you should call `pyo3::prepare_freethreaded_python()` before attempting to call any other Python APIs. If you do not enable this feature, you should call `pyo3::prepare_freethreaded_python()` before attempting to call any other Python APIs.

View File

@ -35,10 +35,9 @@ Python::with_gil(|py| -> PyResult<()> {
# } # }
``` ```
Internally, calling `Python::with_gil()` or `Python::acquire_gil()` creates a Internally, calling `Python::with_gil()` creates a `GILPool` which owns the
`GILPool` which owns the memory pointed to by the reference. In the example memory pointed to by the reference. In the example above, the lifetime of the
above, the lifetime of the reference `hello` is bound to the `GILPool`. When reference `hello` is bound to the `GILPool`. When the `with_gil()` closure ends
the `with_gil()` closure ends or the `GILGuard` from `acquire_gil()` is dropped,
the `GILPool` is also dropped and the Python reference counts of the variables the `GILPool` is also dropped and the Python reference counts of the variables
it owns are decreased, releasing them to the Python garbage collector. Most it owns are decreased, releasing them to the Python garbage collector. Most
of the time we don't have to think about this, but consider the following: of the time we don't have to think about this, but consider the following:

View File

@ -44,7 +44,7 @@ However, if the `anyhow::Error` or `eyre::Report` has a source, then the origina
While the API provided by [`Python::acquire_gil`](https://docs.rs/pyo3/0.18.3/pyo3/marker/struct.Python.html#method.acquire_gil) seems convenient, it is somewhat brittle as the design of the GIL token [`Python`](https://docs.rs/pyo3/0.18.3/pyo3/marker/struct.Python.html) relies on proper nesting and panics if not used correctly, e.g. While the API provided by [`Python::acquire_gil`](https://docs.rs/pyo3/0.18.3/pyo3/marker/struct.Python.html#method.acquire_gil) seems convenient, it is somewhat brittle as the design of the GIL token [`Python`](https://docs.rs/pyo3/0.18.3/pyo3/marker/struct.Python.html) relies on proper nesting and panics if not used correctly, e.g.
```rust,should_panic ```rust,ignore
# #![allow(dead_code, deprecated)] # #![allow(dead_code, deprecated)]
# use pyo3::prelude::*; # use pyo3::prelude::*;

View File

@ -2,14 +2,6 @@ use pyo3::prelude::*;
#[pyfunction] #[pyfunction]
fn issue_219() { fn issue_219() {
// issue 219: acquiring GIL inside #[pyfunction] deadlocks.
#[allow(deprecated)]
let gil = Python::acquire_gil();
let _py = gil.python();
}
#[pyfunction]
fn issue_219_2() {
// issue 219: acquiring GIL inside #[pyfunction] deadlocks. // issue 219: acquiring GIL inside #[pyfunction] deadlocks.
Python::with_gil(|_| {}); Python::with_gil(|_| {});
} }
@ -17,6 +9,5 @@ fn issue_219_2() {
#[pymodule] #[pymodule]
pub fn misc(_py: Python<'_>, m: &PyModule) -> PyResult<()> { pub fn misc(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(issue_219, m)?)?; m.add_function(wrap_pyfunction!(issue_219, m)?)?;
m.add_function(wrap_pyfunction!(issue_219_2, m)?)?;
Ok(()) Ok(())
} }

View File

@ -8,7 +8,6 @@ import pytest
def test_issue_219(): def test_issue_219():
# Should not deadlock # Should not deadlock
pyo3_pytests.misc.issue_219() pyo3_pytests.misc.issue_219()
pyo3_pytests.misc.issue_219_2()
@pytest.mark.skipif( @pytest.mark.skipif(

View File

@ -6,11 +6,7 @@ use crate::impl_::not_send::{NotSend, NOT_SEND};
use crate::{ffi, Python}; use crate::{ffi, Python};
use parking_lot::{const_mutex, Mutex, Once}; use parking_lot::{const_mutex, Mutex, Once};
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::{ use std::{mem, ptr::NonNull, sync::atomic};
mem::{self, ManuallyDrop},
ptr::NonNull,
sync::atomic,
};
static START: Once = Once::new(); static START: Once = Once::new();
@ -139,25 +135,9 @@ where
} }
/// RAII type that represents the Global Interpreter Lock acquisition. /// RAII type that represents the Global Interpreter Lock acquisition.
/// struct GILGuard {
/// Users are strongly encouraged to use [`Python::with_gil`](struct.Python.html#method.with_gil)
/// instead of directly constructing this type.
/// See [`Python::acquire_gil`](struct.Python.html#method.acquire_gil) for more.
///
/// # Examples
/// ```
/// use pyo3::Python;
///
/// {
/// #[allow(deprecated)]
/// let gil_guard = Python::acquire_gil();
/// let py = gil_guard.python();
/// } // GIL is released when gil_guard is dropped
/// ```
#[must_use]
pub struct GILGuard {
gstate: ffi::PyGILState_STATE, gstate: ffi::PyGILState_STATE,
pool: ManuallyDrop<Option<GILPool>>, pool: Option<GILPool>,
_not_send: NotSend, _not_send: NotSend,
} }
@ -168,11 +148,11 @@ impl GILGuard {
unsafe { Python::assume_gil_acquired() } unsafe { Python::assume_gil_acquired() }
} }
/// PyO3 internal API for acquiring the GIL. The public API is Python::acquire_gil. /// PyO3 internal API for acquiring the GIL. The public API is Python::with_gil.
/// ///
/// If PyO3 does not yet have a `GILPool` for tracking owned PyObject references, then this new /// If PyO3 does not yet have a `GILPool` for tracking owned PyObject references, then this new
/// `GILGuard` will also contain a `GILPool`. /// `GILGuard` will also contain a `GILPool`.
pub(crate) fn acquire() -> GILGuard { fn acquire() -> GILGuard {
// Maybe auto-initialize the GIL: // Maybe auto-initialize the GIL:
// - If auto-initialize feature set and supported, try to initialize the interpreter. // - If auto-initialize feature set and supported, try to initialize the interpreter.
// - If the auto-initialize feature is set but unsupported, emit hard errors only when the // - If the auto-initialize feature is set but unsupported, emit hard errors only when the
@ -231,7 +211,7 @@ impl GILGuard {
GILGuard { GILGuard {
gstate, gstate,
pool: ManuallyDrop::new(pool), pool,
_not_send: NOT_SEND, _not_send: NOT_SEND,
} }
} }
@ -240,28 +220,12 @@ impl GILGuard {
/// The Drop implementation for `GILGuard` will release the GIL. /// The Drop implementation for `GILGuard` will release the GIL.
impl Drop for GILGuard { impl Drop for GILGuard {
fn drop(&mut self) { fn drop(&mut self) {
// First up, try to detect if the order of destruction is correct.
#[allow(clippy::manual_assert)]
let _ = GIL_COUNT.try_with(|c| {
if self.gstate == ffi::PyGILState_STATE::PyGILState_UNLOCKED && c.get() != 1 {
// XXX: this panic commits to leaking all objects in the pool as well as
// potentially meaning the GIL never releases. Perhaps should be an abort?
// Unfortunately abort UX is much worse than panic.
panic!("The first GILGuard acquired must be the last one dropped.");
}
});
// If this GILGuard doesn't own a pool, then need to decrease the count after dropping
// all objects from the pool.
let should_decrement = self.pool.is_none();
// Drop the objects in the pool before attempting to release the thread state // Drop the objects in the pool before attempting to release the thread state
unsafe { if let Some(pool) = self.pool.take() {
ManuallyDrop::drop(&mut self.pool); drop(pool)
} } else {
// This GILGuard didn't own a pool, need to decrease the count manually.
if should_decrement { decrement_gil_count()
decrement_gil_count();
} }
unsafe { unsafe {
@ -554,9 +518,7 @@ mod tests {
#[test] #[test]
fn test_owned() { fn test_owned() {
#[allow(deprecated)] Python::with_gil(|py| {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = get_object(py); let obj = get_object(py);
let obj_ptr = obj.as_ptr(); let obj_ptr = obj.as_ptr();
// Ensure that obj does not get freed // Ensure that obj does not get freed
@ -576,13 +538,12 @@ mod tests {
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1); assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
} }
} }
})
} }
#[test] #[test]
fn test_owned_nested() { fn test_owned_nested() {
#[allow(deprecated)] Python::with_gil(|py| {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = get_object(py); let obj = get_object(py);
// Ensure that obj does not get freed // Ensure that obj does not get freed
let _ref = obj.clone_ref(py); let _ref = obj.clone_ref(py);
@ -610,6 +571,7 @@ mod tests {
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1); assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
} }
} }
});
} }
#[test] #[test]
@ -666,15 +628,13 @@ mod tests {
#[test] #[test]
fn test_gil_counts() { fn test_gil_counts() {
// Check GILGuard and GILPool both increase counts correctly // Check with_gil and GILPool both increase counts correctly
let get_gil_count = || GIL_COUNT.with(|c| c.get()); let get_gil_count = || GIL_COUNT.with(|c| c.get());
assert_eq!(get_gil_count(), 0); assert_eq!(get_gil_count(), 0);
#[allow(deprecated)] Python::with_gil(|_| {
let gil = Python::acquire_gil();
assert_eq!(get_gil_count(), 1); assert_eq!(get_gil_count(), 1);
assert_eq!(get_gil_count(), 1);
let pool = unsafe { GILPool::new() }; let pool = unsafe { GILPool::new() };
assert_eq!(get_gil_count(), 2); assert_eq!(get_gil_count(), 2);
@ -684,41 +644,37 @@ mod tests {
drop(pool); drop(pool);
assert_eq!(get_gil_count(), 2); assert_eq!(get_gil_count(), 2);
#[allow(deprecated)] Python::with_gil(|_| {
let gil2 = Python::acquire_gil(); // nested with_gil doesn't update gil count
assert_eq!(get_gil_count(), 3); assert_eq!(get_gil_count(), 2);
});
drop(gil2);
assert_eq!(get_gil_count(), 2); assert_eq!(get_gil_count(), 2);
drop(pool2); drop(pool2);
assert_eq!(get_gil_count(), 1); assert_eq!(get_gil_count(), 1);
});
drop(gil);
assert_eq!(get_gil_count(), 0); assert_eq!(get_gil_count(), 0);
} }
#[test] #[test]
fn test_allow_threads() { fn test_allow_threads() {
// allow_threads should temporarily release GIL in PyO3's internal tracking too. assert!(!gil_is_acquired());
#[allow(deprecated)]
let gil = Python::acquire_gil();
let py = gil.python();
Python::with_gil(|py| {
assert!(gil_is_acquired()); assert!(gil_is_acquired());
py.allow_threads(move || { py.allow_threads(move || {
assert!(!gil_is_acquired()); assert!(!gil_is_acquired());
#[allow(deprecated)] Python::with_gil(|_| assert!(gil_is_acquired()));
let gil = Python::acquire_gil();
assert!(gil_is_acquired());
drop(gil);
assert!(!gil_is_acquired()); assert!(!gil_is_acquired());
}); });
assert!(gil_is_acquired()); assert!(gil_is_acquired());
});
assert!(!gil_is_acquired());
} }
#[test] #[test]
@ -740,25 +696,17 @@ mod tests {
#[test] #[test]
fn dropping_gil_does_not_invalidate_references() { fn dropping_gil_does_not_invalidate_references() {
// Acquiring GIL for the second time should be safe - see #864 // Acquiring GIL for the second time should be safe - see #864
#[allow(deprecated)] Python::with_gil(|py| {
let gil = Python::acquire_gil(); let obj = Python::with_gil(|_| py.eval("object()", None, None).unwrap());
let py = gil.python();
#[allow(deprecated)]
let gil2 = Python::acquire_gil();
let obj = py.eval("object()", None, None).unwrap();
drop(gil2);
// After gil2 drops, obj should still have a reference count of one // After gil2 drops, obj should still have a reference count of one
assert_eq!(obj.get_refcnt(), 1); assert_eq!(obj.get_refcnt(), 1);
})
} }
#[test] #[test]
fn test_clone_with_gil() { fn test_clone_with_gil() {
#[allow(deprecated)] Python::with_gil(|py| {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = get_object(py); let obj = get_object(py);
let count = obj.get_refcnt(py); let count = obj.get_refcnt(py);
@ -766,6 +714,7 @@ mod tests {
#[allow(clippy::redundant_clone)] #[allow(clippy::redundant_clone)]
let c = obj.clone(); let c = obj.clone();
assert_eq!(count + 1, c.get_refcnt(py)); assert_eq!(count + 1, c.get_refcnt(py));
})
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -912,11 +861,9 @@ mod tests {
// update_counts can run arbitrary Python code during Py_DECREF. // update_counts can run arbitrary Python code during Py_DECREF.
// if the locking is implemented incorrectly, it will deadlock. // if the locking is implemented incorrectly, it will deadlock.
#[allow(deprecated)] Python::with_gil(|py| {
let gil = Python::acquire_gil(); let obj = get_object(py);
let obj = get_object(gil.python());
unsafe {
unsafe extern "C" fn capsule_drop(capsule: *mut ffi::PyObject) { unsafe extern "C" fn capsule_drop(capsule: *mut ffi::PyObject) {
// This line will implicitly call update_counts // This line will implicitly call update_counts
// -> and so cause deadlock if update_counts is not handling recursion correctly. // -> and so cause deadlock if update_counts is not handling recursion correctly.
@ -930,12 +877,14 @@ mod tests {
} }
let ptr = obj.into_ptr(); let ptr = obj.into_ptr();
let capsule = ffi::PyCapsule_New(ptr as _, std::ptr::null(), Some(capsule_drop));
let capsule =
unsafe { ffi::PyCapsule_New(ptr as _, std::ptr::null(), Some(capsule_drop)) };
POOL.register_decref(NonNull::new(capsule).unwrap()); POOL.register_decref(NonNull::new(capsule).unwrap());
// Updating the counts will call decref on the capsule, which calls capsule_drop // Updating the counts will call decref on the capsule, which calls capsule_drop
POOL.update_counts(gil.python()) POOL.update_counts(py);
} })
} }
} }

View File

@ -79,8 +79,8 @@
//! //!
//! - `abi3`: Restricts PyO3's API to a subset of the full Python API which is guaranteed by //! - `abi3`: Restricts PyO3's API to a subset of the full Python API which is guaranteed by
//! [PEP 384] to be forward-compatible with future Python versions. //! [PEP 384] to be forward-compatible with future Python versions.
//! - `auto-initialize`: Changes [`Python::with_gil`] and [`Python::acquire_gil`] to automatically //! - `auto-initialize`: Changes [`Python::with_gil`] to automatically initialize the Python
//! initialize the Python interpreter if needed. //! interpreter if needed.
//! - `extension-module`: This will tell the linker to keep the Python symbols unresolved, so that //! - `extension-module`: This will tell the linker to keep the Python symbols unresolved, so that
//! your module can also be used with statically linked Python interpreters. Use this feature when //! your module can also be used with statically linked Python interpreters. Use this feature when
//! building an extension module. //! building an extension module.
@ -307,9 +307,9 @@ pub use crate::conversion::{
ToPyObject, ToPyObject,
}; };
pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyResult}; pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyResult};
pub use crate::gil::GILPool;
#[cfg(not(PyPy))] #[cfg(not(PyPy))]
pub use crate::gil::{prepare_freethreaded_python, with_embedded_python_interpreter}; pub use crate::gil::{prepare_freethreaded_python, with_embedded_python_interpreter};
pub use crate::gil::{GILGuard, GILPool};
pub use crate::instance::{Py, PyNativeType, PyObject}; pub use crate::instance::{Py, PyNativeType, PyObject};
pub use crate::marker::Python; pub use crate::marker::Python;
pub use crate::pycell::{PyCell, PyRef, PyRefMut}; pub use crate::pycell::{PyCell, PyRef, PyRefMut};

View File

@ -120,7 +120,7 @@
//! [`Rc`]: std::rc::Rc //! [`Rc`]: std::rc::Rc
//! [`Py`]: crate::Py //! [`Py`]: crate::Py
use crate::err::{self, PyDowncastError, PyErr, PyResult}; use crate::err::{self, PyDowncastError, PyErr, PyResult};
use crate::gil::{self, GILGuard, GILPool, SuspendGIL}; use crate::gil::{self, EnsureGIL, GILPool, SuspendGIL};
use crate::impl_::not_send::NotSend; use crate::impl_::not_send::NotSend;
use crate::types::{PyAny, PyDict, PyModule, PyString, PyType}; use crate::types::{PyAny, PyDict, PyModule, PyString, PyType};
use crate::version::PythonVersionInfo; use crate::version::PythonVersionInfo;
@ -273,12 +273,16 @@ mod negative_impls {
/// [`Py::clone_ref`]: crate::Py::clone_ref /// [`Py::clone_ref`]: crate::Py::clone_ref
/// [Memory Management]: https://pyo3.rs/main/memory.html#gil-bound-memory /// [Memory Management]: https://pyo3.rs/main/memory.html#gil-bound-memory
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct Python<'py>(PhantomData<(&'py GILGuard, NotSend)>); pub struct Python<'py>(PhantomData<(&'py EnsureGIL, NotSend)>);
impl Python<'_> { impl Python<'_> {
/// Acquires the global interpreter lock, allowing access to the Python interpreter. The /// Acquires the global interpreter lock, allowing access to the Python interpreter. The
/// provided closure `F` will be executed with the acquired `Python` marker token. /// provided closure `F` will be executed with the acquired `Python` marker token.
/// ///
/// If implementing [`#[pymethods]`](crate::pymethods) or [`#[pyfunction]`](crate::pyfunction),
/// declare `py: Python` as an argument. PyO3 will pass in the token to grant access to the GIL
/// context in which the function is running, avoiding the need to call `with_gil`.
///
/// If the [`auto-initialize`] feature is enabled and the Python runtime is not already /// If the [`auto-initialize`] feature is enabled and the Python runtime is not already
/// initialized, this function will initialize it. See /// initialized, this function will initialize it. See
#[cfg_attr( #[cfg_attr(
@ -353,52 +357,6 @@ impl Python<'_> {
} }
impl<'py> Python<'py> { impl<'py> Python<'py> {
/// Acquires the global interpreter lock, allowing access to the Python interpreter.
///
/// If the [`auto-initialize`] feature is enabled and the Python runtime is not already
/// initialized, this function will initialize it. See
#[cfg_attr(
not(PyPy),
doc = "[`prepare_freethreaded_python`](crate::prepare_freethreaded_python)"
)]
#[cfg_attr(PyPy, doc = "`prepare_freethreaded_python`")]
/// for details.
///
/// Most users should not need to use this API directly, and should prefer one of two options:
/// 1. If implementing [`#[pymethods]`](crate::pymethods) or [`#[pyfunction]`](crate::pyfunction), declare `py: Python` as an argument.
/// PyO3 will pass in the token to grant access to the GIL context in which the function is running.
/// 2. Use [`Python::with_gil`] to run a closure with the GIL, acquiring only if needed.
///
/// # Panics
///
/// - If the [`auto-initialize`] feature is not enabled and the Python interpreter is not
/// initialized.
/// - If multiple [`GILGuard`]s are not dropped in in the reverse order of acquisition, PyO3
/// may panic. It is recommended to use [`Python::with_gil`] instead to avoid this.
///
/// # Notes
///
/// The return type from this function, [`GILGuard`], is implemented as a RAII guard
/// around [`PyGILState_Ensure`]. This means that multiple `acquire_gil()` calls are
/// allowed, and will not deadlock. However, [`GILGuard`]s must be dropped in the reverse order
/// to acquisition. If PyO3 detects this order is not maintained, it will panic when the out-of-order drop occurs.
///
/// # Deprecation
///
/// This API has been deprecated for several reasons:
/// - GIL drop order tracking has turned out to be [error prone](https://github.com/PyO3/pyo3/issues/1683).
/// With a scoped API like `Python::with_gil`, these are always dropped in the correct order.
/// - It promotes passing and keeping the GILGuard around, which is almost always not what you actually want.
///
/// [`PyGILState_Ensure`]: crate::ffi::PyGILState_Ensure
/// [`auto-initialize`]: https://pyo3.rs/main/features.html#auto-initialize
#[inline]
// Once removed, we can remove GILGuard's drop tracking.
#[deprecated(since = "0.17.0", note = "prefer Python::with_gil")]
pub fn acquire_gil() -> GILGuard {
GILGuard::acquire()
}
/// Temporarily releases the GIL, thus allowing other Python threads to run. The GIL will be /// Temporarily releases the GIL, thus allowing other Python threads to run. The GIL will be
/// reacquired when `F`'s scope ends. /// reacquired when `F`'s scope ends.
/// ///
@ -820,8 +778,8 @@ impl<'py> Python<'py> {
/// all have their Python reference counts decremented, potentially allowing Python to drop /// all have their Python reference counts decremented, potentially allowing Python to drop
/// the corresponding Python objects. /// the corresponding Python objects.
/// ///
/// Typical usage of PyO3 will not need this API, as [`Python::with_gil`] and /// Typical usage of PyO3 will not need this API, as [`Python::with_gil`] automatically creates
/// [`Python::acquire_gil`] automatically create a `GILPool` where appropriate. /// a `GILPool` where appropriate.
/// ///
/// Advanced uses of PyO3 which perform long-running tasks which never free the GIL may need /// Advanced uses of PyO3 which perform long-running tasks which never free the GIL may need
/// to use this API to clear memory, as PyO3 usually does not clear memory until the GIL is /// to use this API to clear memory, as PyO3 usually does not clear memory until the GIL is

View File

@ -14,7 +14,6 @@ pub use crate::conversion::{
FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, ToPyObject, FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, ToPyObject,
}; };
pub use crate::err::{PyErr, PyResult}; pub use crate::err::{PyErr, PyResult};
pub use crate::gil::GILGuard;
pub use crate::instance::{Py, PyObject}; pub use crate::instance::{Py, PyObject};
pub use crate::marker::Python; pub use crate::marker::Python;
pub use crate::pycell::{PyCell, PyRef, PyRefMut}; pub use crate::pycell::{PyCell, PyRef, PyRefMut};

View File

@ -319,10 +319,7 @@ fn test_option_list_get() {
#[test] #[test]
fn sequence_is_not_mapping() { fn sequence_is_not_mapping() {
#[allow(deprecated)] Python::with_gil(|py| {
let gil = Python::acquire_gil();
let py = gil.python();
let list = PyCell::new( let list = PyCell::new(
py, py,
OptionList { OptionList {
@ -335,6 +332,7 @@ fn sequence_is_not_mapping() {
assert!(list.as_ref().downcast::<PyMapping>().is_err()); assert!(list.as_ref().downcast::<PyMapping>().is_err());
assert!(list.as_ref().downcast::<PySequence>().is_ok()); assert!(list.as_ref().downcast::<PySequence>().is_ok());
})
} }
#[test] #[test]

View File

@ -9,8 +9,8 @@ error[E0277]: `*mut pyo3::Python<'static>` cannot be shared between threads safe
= help: within `pyo3::Python<'_>`, the trait `Sync` is not implemented for `*mut pyo3::Python<'static>` = help: within `pyo3::Python<'_>`, the trait `Sync` is not implemented for `*mut pyo3::Python<'static>`
= note: required because it appears within the type `PhantomData<*mut Python<'static>>` = note: required because it appears within the type `PhantomData<*mut Python<'static>>`
= note: required because it appears within the type `NotSend` = note: required because it appears within the type `NotSend`
= note: required because it appears within the type `(&GILGuard, NotSend)` = note: required because it appears within the type `(&EnsureGIL, NotSend)`
= note: required because it appears within the type `PhantomData<(&GILGuard, NotSend)>` = note: required because it appears within the type `PhantomData<(&EnsureGIL, NotSend)>`
= note: required because it appears within the type `Python<'_>` = note: required because it appears within the type `Python<'_>`
= note: required for `&pyo3::Python<'_>` to implement `Send` = note: required for `&pyo3::Python<'_>` to implement `Send`
note: required because it's used within this closure note: required because it's used within this closure

View File

@ -3,23 +3,24 @@ use std::rc::Rc;
#[pyclass] #[pyclass]
struct NotThreadSafe { struct NotThreadSafe {
data: Rc<i32> data: Rc<i32>,
} }
fn main() { fn main() {
let gil = Python::acquire_gil(); let obj = Python::with_gil(|py| {
let py = gil.python(); PyCell::new(py, NotThreadSafe { data: Rc::new(5) })
.unwrap()
let obj = PyCell::new(py, NotThreadSafe { data: Rc::new(5) }).unwrap().to_object(py); .to_object(py)
drop(gil); });
std::thread::spawn(move || { std::thread::spawn(move || {
let gil = Python::acquire_gil(); Python::with_gil(|py| {
let py = gil.python();
// Uh oh, moved Rc to a new thread! // Uh oh, moved Rc to a new thread!
let c: &PyCell<NotThreadSafe> = obj.as_ref(py).downcast().unwrap(); let c: &PyCell<NotThreadSafe> = obj.as_ref(py).downcast().unwrap();
assert_eq!(*c.borrow().data, 5); assert_eq!(*c.borrow().data, 5);
}).join().unwrap(); })
})
.join()
.unwrap();
} }

View File

@ -1,11 +1,10 @@
use pyo3::{types::PyDict, Py, Python}; use pyo3::{types::PyDict, Py, Python};
fn main() { fn main() {
#[allow(deprecated)] let dict: Py<PyDict> = Python::with_gil(|py| PyDict::new(py).into());
let gil = Python::acquire_gil();
let dict: Py<PyDict> = PyDict::new(gil.python()).into(); // Should not be able to get access to Py contents outside of with_gil.
let dict: &PyDict = dict.as_ref(gil.python()); let dict: &PyDict = Python::with_gil(|py| dict.as_ref(py));
drop(gil);
let _py: Python = dict.py(); // Obtain a Python<'p> without GIL. let _py: Python = dict.py(); // Obtain a Python<'p> without GIL.
} }

View File

@ -1,13 +1,8 @@
error[E0505]: cannot move out of `gil` because it is borrowed error: lifetime may not live long enough
--> tests/ui/wrong_aspyref_lifetimes.rs:8:10 --> tests/ui/wrong_aspyref_lifetimes.rs:7:47
| |
5 | let gil = Python::acquire_gil(); 7 | let dict: &PyDict = Python::with_gil(|py| dict.as_ref(py));
| --- binding `gil` declared here | --- ^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
6 | let dict: Py<PyDict> = PyDict::new(gil.python()).into(); | | |
7 | let dict: &PyDict = dict.as_ref(gil.python()); | | return type of closure is &'2 PyDict
| ------------ borrow of `gil` occurs here | has type `pyo3::Python<'1>`
8 | drop(gil);
| ^^^ move out of `gil` occurs here
9 |
10 | let _py: Python = dict.py(); // Obtain a Python<'p> without GIL.
| --------- borrow later used here