remove `Python::acquire_gil`
This commit is contained in:
parent
3c634dd9a9
commit
dd24c9ea71
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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::*;
|
||||||
|
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
253
src/gil.rs
253
src/gil.rs
|
@ -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,62 +518,60 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_owned() {
|
fn test_owned() {
|
||||||
#[allow(deprecated)]
|
Python::with_gil(|py| {
|
||||||
let gil = Python::acquire_gil();
|
let obj = get_object(py);
|
||||||
let py = gil.python();
|
let obj_ptr = obj.as_ptr();
|
||||||
let obj = get_object(py);
|
// Ensure that obj does not get freed
|
||||||
let obj_ptr = obj.as_ptr();
|
let _ref = obj.clone_ref(py);
|
||||||
// Ensure that obj does not get freed
|
|
||||||
let _ref = obj.clone_ref(py);
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
{
|
{
|
||||||
let pool = py.new_pool();
|
let pool = py.new_pool();
|
||||||
gil::register_owned(pool.python(), NonNull::new_unchecked(obj.into_ptr()));
|
gil::register_owned(pool.python(), NonNull::new_unchecked(obj.into_ptr()));
|
||||||
|
|
||||||
assert_eq!(owned_object_count(), 1);
|
assert_eq!(owned_object_count(), 1);
|
||||||
assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
|
assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let _pool = py.new_pool();
|
||||||
|
assert_eq!(owned_object_count(), 0);
|
||||||
|
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
{
|
})
|
||||||
let _pool = py.new_pool();
|
|
||||||
assert_eq!(owned_object_count(), 0);
|
|
||||||
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 obj = get_object(py);
|
||||||
let py = gil.python();
|
// Ensure that obj does not get freed
|
||||||
let obj = get_object(py);
|
let _ref = obj.clone_ref(py);
|
||||||
// Ensure that obj does not get freed
|
let obj_ptr = obj.as_ptr();
|
||||||
let _ref = obj.clone_ref(py);
|
|
||||||
let obj_ptr = obj.as_ptr();
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
{
|
|
||||||
let _pool = py.new_pool();
|
|
||||||
assert_eq!(owned_object_count(), 0);
|
|
||||||
|
|
||||||
gil::register_owned(py, NonNull::new_unchecked(obj.into_ptr()));
|
|
||||||
|
|
||||||
assert_eq!(owned_object_count(), 1);
|
|
||||||
assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
|
|
||||||
{
|
{
|
||||||
let _pool = py.new_pool();
|
let _pool = py.new_pool();
|
||||||
let obj = get_object(py);
|
assert_eq!(owned_object_count(), 0);
|
||||||
|
|
||||||
gil::register_owned(py, NonNull::new_unchecked(obj.into_ptr()));
|
gil::register_owned(py, NonNull::new_unchecked(obj.into_ptr()));
|
||||||
assert_eq!(owned_object_count(), 2);
|
|
||||||
|
assert_eq!(owned_object_count(), 1);
|
||||||
|
assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
|
||||||
|
{
|
||||||
|
let _pool = py.new_pool();
|
||||||
|
let obj = get_object(py);
|
||||||
|
gil::register_owned(py, NonNull::new_unchecked(obj.into_ptr()));
|
||||||
|
assert_eq!(owned_object_count(), 2);
|
||||||
|
}
|
||||||
|
assert_eq!(owned_object_count(), 1);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assert_eq!(owned_object_count(), 0);
|
||||||
|
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
|
||||||
}
|
}
|
||||||
assert_eq!(owned_object_count(), 1);
|
|
||||||
}
|
}
|
||||||
{
|
});
|
||||||
assert_eq!(owned_object_count(), 0);
|
|
||||||
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -666,59 +628,53 @@ 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);
|
|
||||||
|
|
||||||
let pool2 = unsafe { GILPool::new() };
|
let pool2 = unsafe { GILPool::new() };
|
||||||
assert_eq!(get_gil_count(), 3);
|
assert_eq!(get_gil_count(), 3);
|
||||||
|
|
||||||
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);
|
||||||
|
});
|
||||||
|
assert_eq!(get_gil_count(), 2);
|
||||||
|
|
||||||
drop(gil2);
|
drop(pool2);
|
||||||
assert_eq!(get_gil_count(), 2);
|
assert_eq!(get_gil_count(), 1);
|
||||||
|
});
|
||||||
drop(pool2);
|
|
||||||
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();
|
|
||||||
|
|
||||||
assert!(gil_is_acquired());
|
Python::with_gil(|py| {
|
||||||
|
|
||||||
py.allow_threads(move || {
|
|
||||||
assert!(!gil_is_acquired());
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
let gil = Python::acquire_gil();
|
|
||||||
assert!(gil_is_acquired());
|
assert!(gil_is_acquired());
|
||||||
|
|
||||||
drop(gil);
|
py.allow_threads(move || {
|
||||||
assert!(!gil_is_acquired());
|
assert!(!gil_is_acquired());
|
||||||
|
|
||||||
|
Python::with_gil(|_| assert!(gil_is_acquired()));
|
||||||
|
|
||||||
|
assert!(!gil_is_acquired());
|
||||||
|
});
|
||||||
|
|
||||||
|
assert!(gil_is_acquired());
|
||||||
});
|
});
|
||||||
|
|
||||||
assert!(gil_is_acquired());
|
assert!(!gil_is_acquired());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -740,32 +696,25 @@ 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)]
|
// After gil2 drops, obj should still have a reference count of one
|
||||||
let gil2 = Python::acquire_gil();
|
assert_eq!(obj.get_refcnt(), 1);
|
||||||
let obj = py.eval("object()", None, None).unwrap();
|
})
|
||||||
drop(gil2);
|
|
||||||
|
|
||||||
// After gil2 drops, obj should still have a reference count of one
|
|
||||||
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 obj = get_object(py);
|
||||||
let py = gil.python();
|
let count = obj.get_refcnt(py);
|
||||||
|
|
||||||
let obj = get_object(py);
|
// Cloning with the GIL should increase reference count immediately
|
||||||
let count = obj.get_refcnt(py);
|
#[allow(clippy::redundant_clone)]
|
||||||
|
let c = obj.clone();
|
||||||
// Cloning with the GIL should increase reference count immediately
|
assert_eq!(count + 1, c.get_refcnt(py));
|
||||||
#[allow(clippy::redundant_clone)]
|
})
|
||||||
let c = obj.clone();
|
|
||||||
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);
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -319,22 +319,20 @@ 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 list = PyCell::new(
|
||||||
let py = gil.python();
|
py,
|
||||||
|
OptionList {
|
||||||
|
items: vec![Some(1), None],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let list = PyCell::new(
|
PySequence::register::<OptionList>(py).unwrap();
|
||||||
py,
|
|
||||||
OptionList {
|
|
||||||
items: vec![Some(1), None],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
PySequence::register::<OptionList>(py).unwrap();
|
assert!(list.as_ref().downcast::<PyMapping>().is_err());
|
||||||
|
assert!(list.as_ref().downcast::<PySequence>().is_ok());
|
||||||
assert!(list.as_ref().downcast::<PyMapping>().is_err());
|
})
|
||||||
assert!(list.as_ref().downcast::<PySequence>().is_ok());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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!
|
||||||
|
let c: &PyCell<NotThreadSafe> = obj.as_ref(py).downcast().unwrap();
|
||||||
|
|
||||||
// Uh oh, moved Rc to a new thread!
|
assert_eq!(*c.borrow().data, 5);
|
||||||
let c: &PyCell<NotThreadSafe> = obj.as_ref(py).downcast().unwrap();
|
})
|
||||||
|
})
|
||||||
assert_eq!(*c.borrow().data, 5);
|
.join()
|
||||||
}).join().unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
Loading…
Reference in New Issue