2017-06-20 21:10:12 +00:00
|
|
|
|
// Copyright (c) 2017-present PyO3 Project and Contributors
|
2019-02-23 17:01:22 +00:00
|
|
|
|
|
|
|
|
|
//! Interaction with python's global interpreter lock
|
|
|
|
|
|
2020-05-01 16:09:10 +00:00
|
|
|
|
use crate::{ffi, internal_tricks::Unsendable, Python};
|
2020-05-02 12:12:38 +00:00
|
|
|
|
use std::cell::{Cell, RefCell, UnsafeCell};
|
2020-05-04 17:30:36 +00:00
|
|
|
|
use std::sync::atomic::{spin_loop_hint, AtomicBool, Ordering};
|
2020-04-12 07:31:35 +00:00
|
|
|
|
use std::{any, mem::ManuallyDrop, ptr::NonNull, sync};
|
2015-01-05 16:05:53 +00:00
|
|
|
|
|
2019-07-07 09:52:25 +00:00
|
|
|
|
static START: sync::Once = sync::Once::new();
|
2015-01-05 16:05:53 +00:00
|
|
|
|
|
2020-04-07 19:37:02 +00:00
|
|
|
|
thread_local! {
|
|
|
|
|
/// This is a internal counter in pyo3 monitoring whether this thread has the GIL.
|
|
|
|
|
///
|
2020-05-03 21:29:44 +00:00
|
|
|
|
/// It will be incremented whenever a GILPool is created, and decremented whenever they are
|
|
|
|
|
/// dropped.
|
2020-04-07 19:37:02 +00:00
|
|
|
|
///
|
|
|
|
|
/// As a result, if this thread has the GIL, GIL_COUNT is greater than zero.
|
2020-05-07 14:43:33 +00:00
|
|
|
|
///
|
|
|
|
|
/// pub(crate) because it is manipulated temporarily by Python::allow_threads
|
|
|
|
|
pub(crate) static GIL_COUNT: Cell<u32> = Cell::new(0);
|
2020-05-02 12:12:38 +00:00
|
|
|
|
|
|
|
|
|
/// These are objects owned by the current thread, to be released when the GILPool drops.
|
|
|
|
|
static OWNED_OBJECTS: RefCell<Vec<NonNull<ffi::PyObject>>> = RefCell::new(Vec::with_capacity(256));
|
|
|
|
|
|
|
|
|
|
/// These are non-python objects such as (String) owned by the current thread, to be released
|
|
|
|
|
/// when the GILPool drops.
|
|
|
|
|
static OWNED_ANYS: RefCell<Vec<Box<dyn any::Any>>> = RefCell::new(Vec::with_capacity(256));
|
2020-04-07 19:37:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Check whether the GIL is acquired.
|
|
|
|
|
///
|
|
|
|
|
/// Note: This uses pyo3's internal count rather than PyGILState_Check for two reasons:
|
|
|
|
|
/// 1) for performance
|
|
|
|
|
/// 2) PyGILState_Check always returns 1 if the sub-interpreter APIs have ever been called,
|
|
|
|
|
/// which could lead to incorrect conclusions that the GIL is held.
|
|
|
|
|
fn gil_is_acquired() -> bool {
|
|
|
|
|
GIL_COUNT.with(|c| c.get() > 0)
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-27 20:45:35 +00:00
|
|
|
|
/// Prepares the use of Python in a free-threaded context.
|
2015-04-18 20:20:19 +00:00
|
|
|
|
///
|
2015-06-27 20:45:35 +00:00
|
|
|
|
/// If the Python interpreter is not already initialized, this function
|
2015-04-18 20:20:19 +00:00
|
|
|
|
/// will initialize it with disabled signal handling
|
2015-06-27 20:45:35 +00:00
|
|
|
|
/// (Python will not raise the `KeyboardInterrupt` exception).
|
2015-04-18 20:20:19 +00:00
|
|
|
|
/// Python signal handling depends on the notion of a 'main thread', which must be
|
2015-06-27 20:45:35 +00:00
|
|
|
|
/// the thread that initializes the Python interpreter.
|
|
|
|
|
///
|
|
|
|
|
/// If both the Python interpreter and Python threading are already initialized,
|
|
|
|
|
/// this function has no effect.
|
|
|
|
|
///
|
|
|
|
|
/// # Panic
|
|
|
|
|
/// If the Python interpreter is initialized but Python threading is not,
|
|
|
|
|
/// a panic occurs.
|
|
|
|
|
/// It is not possible to safely access the Python runtime unless the main
|
|
|
|
|
/// thread (the thread which originally initialized Python) also initializes
|
|
|
|
|
/// threading.
|
|
|
|
|
///
|
2018-11-12 21:28:45 +00:00
|
|
|
|
/// When writing an extension module, the `#[pymodule]` macro
|
2015-06-27 20:45:35 +00:00
|
|
|
|
/// will ensure that Python threading is initialized.
|
|
|
|
|
///
|
2015-01-04 04:50:28 +00:00
|
|
|
|
pub fn prepare_freethreaded_python() {
|
2015-06-27 20:45:35 +00:00
|
|
|
|
// Protect against race conditions when Python is not yet initialized
|
2015-01-04 19:11:18 +00:00
|
|
|
|
// and multiple threads concurrently call 'prepare_freethreaded_python()'.
|
2015-06-27 20:45:35 +00:00
|
|
|
|
// Note that we do not protect against concurrent initialization of the Python runtime
|
|
|
|
|
// by other users of the Python C API.
|
2015-01-04 04:50:28 +00:00
|
|
|
|
START.call_once(|| unsafe {
|
2015-01-04 19:11:18 +00:00
|
|
|
|
if ffi::Py_IsInitialized() != 0 {
|
2015-06-27 20:45:35 +00:00
|
|
|
|
// If Python is already initialized, we expect Python threading to also be initialized,
|
|
|
|
|
// as we can't make the existing Python main thread acquire the GIL.
|
2019-04-23 11:18:42 +00:00
|
|
|
|
#[cfg(not(Py_3_7))]
|
2017-07-18 11:28:49 +00:00
|
|
|
|
assert_ne!(ffi::PyEval_ThreadsInitialized(), 0);
|
2015-01-04 19:11:18 +00:00
|
|
|
|
} else {
|
2018-02-21 18:06:48 +00:00
|
|
|
|
// If Python isn't initialized yet, we expect that Python threading
|
|
|
|
|
// isn't initialized either.
|
2018-02-21 18:29:14 +00:00
|
|
|
|
#[cfg(not(Py_3_7))]
|
2018-02-21 18:06:48 +00:00
|
|
|
|
assert_eq!(ffi::PyEval_ThreadsInitialized(), 0);
|
2015-06-27 20:45:35 +00:00
|
|
|
|
// Initialize Python.
|
2015-01-04 19:11:18 +00:00
|
|
|
|
// We use Py_InitializeEx() with initsigs=0 to disable Python signal handling.
|
|
|
|
|
// Signal handling depends on the notion of a 'main thread', which doesn't exist in this case.
|
2015-06-27 20:45:35 +00:00
|
|
|
|
// Note that the 'main thread' notion in Python isn't documented properly;
|
|
|
|
|
// and running Python without one is not officially supported.
|
2019-04-23 11:18:42 +00:00
|
|
|
|
|
|
|
|
|
// PyPy does not support the embedding API
|
|
|
|
|
#[cfg(not(PyPy))]
|
2015-01-04 19:11:18 +00:00
|
|
|
|
ffi::Py_InitializeEx(0);
|
2019-04-23 11:18:42 +00:00
|
|
|
|
|
|
|
|
|
// > Changed in version 3.7: This function is now called by Py_Initialize(), so you don’t have
|
|
|
|
|
// > to call it yourself anymore.
|
|
|
|
|
#[cfg(not(Py_3_7))]
|
2015-01-04 19:11:18 +00:00
|
|
|
|
ffi::PyEval_InitThreads();
|
|
|
|
|
// PyEval_InitThreads() will acquire the GIL,
|
|
|
|
|
// but we don't want to hold it at this point
|
2015-01-04 04:50:28 +00:00
|
|
|
|
// (it's not acquired in the other code paths)
|
2015-01-04 19:11:18 +00:00
|
|
|
|
// So immediately release the GIL:
|
2019-04-23 11:18:42 +00:00
|
|
|
|
#[cfg(not(PyPy))]
|
2015-01-04 19:11:18 +00:00
|
|
|
|
let _thread_state = ffi::PyEval_SaveThread();
|
2015-06-27 20:45:35 +00:00
|
|
|
|
// Note that the PyThreadState returned by PyEval_SaveThread is also held in TLS by the Python runtime,
|
2015-01-04 19:11:18 +00:00
|
|
|
|
// and will be restored by PyGILState_Ensure.
|
2015-01-04 04:50:28 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
2015-01-05 16:05:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-03-05 16:41:04 +00:00
|
|
|
|
/// RAII type that represents the Global Interpreter Lock acquisition.
|
2015-06-27 20:45:35 +00:00
|
|
|
|
///
|
|
|
|
|
/// # Example
|
|
|
|
|
/// ```
|
2017-05-13 05:43:17 +00:00
|
|
|
|
/// use pyo3::Python;
|
2015-06-27 20:45:35 +00:00
|
|
|
|
///
|
|
|
|
|
/// {
|
|
|
|
|
/// let gil_guard = Python::acquire_gil();
|
|
|
|
|
/// let py = gil_guard.python();
|
|
|
|
|
/// } // GIL is released when gil_guard is dropped
|
|
|
|
|
/// ```
|
2015-01-04 23:07:31 +00:00
|
|
|
|
#[must_use]
|
|
|
|
|
pub struct GILGuard {
|
2016-03-05 23:22:16 +00:00
|
|
|
|
gstate: ffi::PyGILState_STATE,
|
2020-05-03 21:29:44 +00:00
|
|
|
|
pool: ManuallyDrop<Option<GILPool>>,
|
2020-04-12 07:31:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl GILGuard {
|
2020-05-03 21:29:44 +00:00
|
|
|
|
/// Acquires the global interpreter lock, which allows access to the Python runtime. This is
|
|
|
|
|
/// safe to call multiple times without causing a deadlock.
|
2020-04-12 07:31:35 +00:00
|
|
|
|
///
|
|
|
|
|
/// If the Python runtime is not already initialized, this function will initialize it.
|
|
|
|
|
/// See [prepare_freethreaded_python()](fn.prepare_freethreaded_python.html) for details.
|
2020-05-03 21:29:44 +00:00
|
|
|
|
///
|
|
|
|
|
/// If PyO3 does not yet have a `GILPool` for tracking owned PyObject references, then this
|
|
|
|
|
/// new `GILGuard` will also contain a `GILPool`.
|
2020-04-12 07:31:35 +00:00
|
|
|
|
pub fn acquire() -> GILGuard {
|
|
|
|
|
prepare_freethreaded_python();
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
let gstate = ffi::PyGILState_Ensure(); // acquire GIL
|
2020-05-03 21:29:44 +00:00
|
|
|
|
|
|
|
|
|
// If there's already a GILPool, we should not create another or this could lead to
|
|
|
|
|
// incorrect dangling references in safe code (see #864).
|
|
|
|
|
let pool = if !gil_is_acquired() {
|
|
|
|
|
Some(GILPool::new())
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
|
|
|
|
|
2020-04-12 07:31:35 +00:00
|
|
|
|
GILGuard {
|
|
|
|
|
gstate,
|
2020-05-03 21:29:44 +00:00
|
|
|
|
pool: ManuallyDrop::new(pool),
|
2020-04-12 07:31:35 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Retrieves the marker type that proves that the GIL was acquired.
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn python(&self) -> Python {
|
|
|
|
|
unsafe { Python::assume_gil_acquired() }
|
|
|
|
|
}
|
2015-01-04 23:07:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-11 15:46:23 +00:00
|
|
|
|
/// The Drop implementation for `GILGuard` will release the GIL.
|
2015-01-04 23:07:31 +00:00
|
|
|
|
impl Drop for GILGuard {
|
|
|
|
|
fn drop(&mut self) {
|
2017-06-21 06:26:28 +00:00
|
|
|
|
unsafe {
|
2020-05-02 12:12:38 +00:00
|
|
|
|
// Must drop the objects in the pool before releasing the GILGuard
|
2020-04-12 07:31:35 +00:00
|
|
|
|
ManuallyDrop::drop(&mut self.pool);
|
2017-06-21 06:26:28 +00:00
|
|
|
|
ffi::PyGILState_Release(self.gstate);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-02 12:12:38 +00:00
|
|
|
|
/// Thread-safe storage for objects which were dropped while the GIL was not held.
|
|
|
|
|
struct ReleasePool {
|
2020-05-04 17:30:36 +00:00
|
|
|
|
locked: AtomicBool,
|
|
|
|
|
pointers_to_drop: UnsafeCell<Vec<NonNull<ffi::PyObject>>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Lock<'a> {
|
|
|
|
|
lock: &'a AtomicBool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> Lock<'a> {
|
|
|
|
|
fn new(lock: &'a AtomicBool) -> Self {
|
|
|
|
|
while lock.compare_and_swap(false, true, Ordering::Acquire) {
|
|
|
|
|
spin_loop_hint();
|
|
|
|
|
}
|
|
|
|
|
Self { lock }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> Drop for Lock<'a> {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
self.lock.store(false, Ordering::Release);
|
|
|
|
|
}
|
2017-07-09 06:08:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-02 12:12:38 +00:00
|
|
|
|
impl ReleasePool {
|
|
|
|
|
const fn new() -> Self {
|
2020-04-12 07:31:35 +00:00
|
|
|
|
Self {
|
2020-05-04 17:30:36 +00:00
|
|
|
|
locked: AtomicBool::new(false),
|
|
|
|
|
pointers_to_drop: UnsafeCell::new(Vec::new()),
|
2017-07-09 06:08:57 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-02 12:12:38 +00:00
|
|
|
|
fn register_pointer(&self, obj: NonNull<ffi::PyObject>) {
|
2020-05-04 17:30:36 +00:00
|
|
|
|
let _lock = Lock::new(&self.locked);
|
|
|
|
|
let v = self.pointers_to_drop.get();
|
|
|
|
|
unsafe { (*v).push(obj) };
|
2017-07-09 06:08:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-02 12:12:38 +00:00
|
|
|
|
fn release_pointers(&self, _py: Python) {
|
2020-05-04 17:30:36 +00:00
|
|
|
|
let _lock = Lock::new(&self.locked);
|
|
|
|
|
let v = self.pointers_to_drop.get();
|
2020-05-02 12:12:38 +00:00
|
|
|
|
unsafe {
|
2020-05-04 17:30:36 +00:00
|
|
|
|
for ptr in &(*v) {
|
2020-05-02 12:12:38 +00:00
|
|
|
|
ffi::Py_DECREF(ptr.as_ptr());
|
|
|
|
|
}
|
2020-05-04 17:30:36 +00:00
|
|
|
|
(*v).clear();
|
2020-04-12 07:31:35 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe impl Sync for ReleasePool {}
|
2017-06-21 06:26:28 +00:00
|
|
|
|
|
2020-05-02 12:12:38 +00:00
|
|
|
|
static POOL: ReleasePool = ReleasePool::new();
|
|
|
|
|
|
2020-05-03 21:29:44 +00:00
|
|
|
|
/// A RAII pool which PyO3 uses to store owned Python references.
|
2020-04-12 07:31:35 +00:00
|
|
|
|
pub struct GILPool {
|
2020-05-02 12:12:38 +00:00
|
|
|
|
owned_objects_start: usize,
|
|
|
|
|
owned_anys_start: usize,
|
2020-04-12 07:31:35 +00:00
|
|
|
|
// Stable solution for impl !Send
|
2019-10-27 09:03:01 +00:00
|
|
|
|
no_send: Unsendable,
|
2017-06-22 17:26:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-12 07:31:35 +00:00
|
|
|
|
impl GILPool {
|
2020-05-03 21:29:44 +00:00
|
|
|
|
/// Create a new `GILPool`. This function should only ever be called with the GIL.
|
|
|
|
|
///
|
|
|
|
|
/// It is recommended not to use this API directly, but instead to use `Python::new_pool`, as
|
|
|
|
|
/// that guarantees the GIL is held.
|
|
|
|
|
///
|
2020-04-12 07:31:35 +00:00
|
|
|
|
/// # Safety
|
2020-05-03 21:29:44 +00:00
|
|
|
|
/// As well as requiring the GIL, see the notes on `Python::new_pool`.
|
2017-06-22 19:32:01 +00:00
|
|
|
|
#[inline]
|
2020-04-12 07:31:35 +00:00
|
|
|
|
pub unsafe fn new() -> GILPool {
|
2020-04-07 19:37:02 +00:00
|
|
|
|
increment_gil_count();
|
2020-04-12 07:31:35 +00:00
|
|
|
|
// Release objects that were dropped since last GIL acquisition
|
2020-05-02 12:12:38 +00:00
|
|
|
|
POOL.release_pointers(Python::assume_gil_acquired());
|
2018-07-30 21:01:46 +00:00
|
|
|
|
GILPool {
|
2020-05-02 12:12:38 +00:00
|
|
|
|
owned_objects_start: OWNED_OBJECTS.with(|o| o.borrow().len()),
|
|
|
|
|
owned_anys_start: OWNED_ANYS.with(|o| o.borrow().len()),
|
2019-10-27 09:03:01 +00:00
|
|
|
|
no_send: Unsendable::default(),
|
2018-07-30 21:01:46 +00:00
|
|
|
|
}
|
2017-06-22 17:26:07 +00:00
|
|
|
|
}
|
2020-05-03 21:29:44 +00:00
|
|
|
|
|
|
|
|
|
/// Get the Python token associated with this `GILPool`.
|
|
|
|
|
pub fn python(&self) -> Python {
|
|
|
|
|
unsafe { Python::assume_gil_acquired() }
|
2020-04-12 07:31:35 +00:00
|
|
|
|
}
|
2017-06-22 17:26:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-12 07:31:35 +00:00
|
|
|
|
impl Drop for GILPool {
|
2017-06-22 17:26:07 +00:00
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
unsafe {
|
2020-05-02 12:12:38 +00:00
|
|
|
|
OWNED_OBJECTS.with(|owned_objects| {
|
|
|
|
|
// Note: inside this closure we must be careful to not hold a borrow too long, because
|
|
|
|
|
// while calling Py_DECREF we may cause other callbacks to run which will need to
|
|
|
|
|
// register objects into the GILPool.
|
|
|
|
|
let len = owned_objects.borrow().len();
|
2020-05-02 14:38:12 +00:00
|
|
|
|
if self.owned_objects_start < len {
|
|
|
|
|
let rest = owned_objects
|
|
|
|
|
.borrow_mut()
|
|
|
|
|
.split_off(self.owned_objects_start);
|
|
|
|
|
for obj in rest {
|
|
|
|
|
ffi::Py_DECREF(obj.as_ptr());
|
|
|
|
|
}
|
2020-05-02 12:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
OWNED_ANYS.with(|owned_anys| owned_anys.borrow_mut().truncate(self.owned_anys_start));
|
2017-06-22 17:26:07 +00:00
|
|
|
|
}
|
2020-04-07 19:37:02 +00:00
|
|
|
|
decrement_gil_count();
|
2017-06-22 17:26:07 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-02 12:12:38 +00:00
|
|
|
|
/// Register a Python object pointer inside the release pool, to have reference count decreased
|
|
|
|
|
/// next time the GIL is acquired in pyo3.
|
|
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
/// The object must be an owned Python reference.
|
2018-11-12 20:36:08 +00:00
|
|
|
|
pub unsafe fn register_pointer(obj: NonNull<ffi::PyObject>) {
|
2020-04-07 19:37:02 +00:00
|
|
|
|
if gil_is_acquired() {
|
|
|
|
|
ffi::Py_DECREF(obj.as_ptr())
|
|
|
|
|
} else {
|
2020-05-02 12:12:38 +00:00
|
|
|
|
POOL.register_pointer(obj);
|
2020-04-07 19:37:02 +00:00
|
|
|
|
}
|
2017-06-22 17:26:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-02 12:12:38 +00:00
|
|
|
|
/// Register an owned object inside the GILPool.
|
|
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
/// The object must be an owned Python reference.
|
2020-05-01 16:09:10 +00:00
|
|
|
|
pub unsafe fn register_owned(_py: Python, obj: NonNull<ffi::PyObject>) {
|
2020-05-02 12:12:38 +00:00
|
|
|
|
debug_assert!(gil_is_acquired());
|
|
|
|
|
OWNED_OBJECTS.with(|objs| objs.borrow_mut().push(obj));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Register any value inside the GILPool.
|
|
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
/// It is the caller's responsibility to ensure that the inferred lifetime 'p is not inferred by
|
|
|
|
|
/// the Rust compiler to outlast the current GILPool.
|
|
|
|
|
pub unsafe fn register_any<'p, T: 'static>(obj: T) -> &'p T {
|
|
|
|
|
debug_assert!(gil_is_acquired());
|
|
|
|
|
OWNED_ANYS.with(|owned_anys| {
|
|
|
|
|
let boxed = Box::new(obj);
|
|
|
|
|
let value_ref: &T = &*boxed;
|
|
|
|
|
|
|
|
|
|
// Sneaky - extend the lifetime of the reference so that the box can be moved
|
|
|
|
|
let value_ref_extended_lifetime = std::mem::transmute(value_ref);
|
|
|
|
|
|
|
|
|
|
owned_anys.borrow_mut().push(boxed);
|
|
|
|
|
value_ref_extended_lifetime
|
|
|
|
|
})
|
2015-01-04 23:07:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-07 19:37:02 +00:00
|
|
|
|
/// Increment pyo3's internal GIL count - to be called whenever GILPool or GILGuard is created.
|
|
|
|
|
#[inline(always)]
|
|
|
|
|
fn increment_gil_count() {
|
|
|
|
|
GIL_COUNT.with(|c| c.set(c.get() + 1))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Decrement pyo3's internal GIL count - to be called whenever GILPool or GILGuard is dropped.
|
|
|
|
|
#[inline(always)]
|
|
|
|
|
fn decrement_gil_count() {
|
|
|
|
|
GIL_COUNT.with(|c| {
|
|
|
|
|
let current = c.get();
|
|
|
|
|
debug_assert!(
|
|
|
|
|
current > 0,
|
|
|
|
|
"Negative GIL count detected. Please report this error to the PyO3 repo as a bug."
|
|
|
|
|
);
|
|
|
|
|
c.set(current - 1);
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-13 20:01:31 +00:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod test {
|
2020-05-07 14:43:33 +00:00
|
|
|
|
use super::{GILPool, GIL_COUNT, OWNED_OBJECTS, POOL};
|
2020-05-02 06:33:10 +00:00
|
|
|
|
use crate::{ffi, gil, AsPyPointer, IntoPyPointer, PyObject, Python, ToPyObject};
|
|
|
|
|
use std::ptr::NonNull;
|
2017-07-13 20:01:31 +00:00
|
|
|
|
|
2020-05-03 21:29:44 +00:00
|
|
|
|
fn get_object(py: Python) -> PyObject {
|
|
|
|
|
// Convenience function for getting a single unique object, using `new_pool` so as to leave
|
|
|
|
|
// the original pool state unchanged.
|
|
|
|
|
let pool = unsafe { py.new_pool() };
|
|
|
|
|
let py = pool.python();
|
2018-08-22 00:16:25 +00:00
|
|
|
|
|
|
|
|
|
let obj = py.eval("object()", None, None).unwrap();
|
|
|
|
|
obj.to_object(py)
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-02 12:12:38 +00:00
|
|
|
|
fn owned_object_count() -> usize {
|
|
|
|
|
OWNED_OBJECTS.with(|objs| objs.borrow().len())
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-13 20:01:31 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_owned() {
|
2019-03-09 23:28:25 +00:00
|
|
|
|
let gil = Python::acquire_gil();
|
|
|
|
|
let py = gil.python();
|
2020-05-03 21:29:44 +00:00
|
|
|
|
let obj = get_object(py);
|
2019-03-09 23:28:25 +00:00
|
|
|
|
let obj_ptr = obj.as_ptr();
|
|
|
|
|
// Ensure that obj does not get freed
|
|
|
|
|
let _ref = obj.clone_ref(py);
|
2017-07-13 20:01:31 +00:00
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
{
|
2020-05-03 21:29:44 +00:00
|
|
|
|
let pool = py.new_pool();
|
|
|
|
|
gil::register_owned(pool.python(), NonNull::new_unchecked(obj.into_ptr()));
|
2017-07-13 20:01:31 +00:00
|
|
|
|
|
2020-05-02 12:12:38 +00:00
|
|
|
|
assert_eq!(owned_object_count(), 1);
|
2020-05-03 21:29:44 +00:00
|
|
|
|
assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
|
2017-07-13 20:01:31 +00:00
|
|
|
|
}
|
|
|
|
|
{
|
2020-05-03 21:29:44 +00:00
|
|
|
|
let _pool = py.new_pool();
|
2020-05-02 12:12:38 +00:00
|
|
|
|
assert_eq!(owned_object_count(), 0);
|
2019-03-09 23:28:25 +00:00
|
|
|
|
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
|
2017-07-13 20:01:31 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_owned_nested() {
|
2017-08-08 06:52:24 +00:00
|
|
|
|
let gil = Python::acquire_gil();
|
|
|
|
|
let py = gil.python();
|
2020-05-03 21:29:44 +00:00
|
|
|
|
let obj = get_object(py);
|
2019-03-09 23:28:25 +00:00
|
|
|
|
// Ensure that obj does not get freed
|
|
|
|
|
let _ref = obj.clone_ref(py);
|
|
|
|
|
let obj_ptr = obj.as_ptr();
|
2017-07-13 20:01:31 +00:00
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
{
|
2020-05-03 21:29:44 +00:00
|
|
|
|
let _pool = py.new_pool();
|
2020-05-02 12:12:38 +00:00
|
|
|
|
assert_eq!(owned_object_count(), 0);
|
2017-07-13 20:01:31 +00:00
|
|
|
|
|
2020-05-02 06:33:10 +00:00
|
|
|
|
gil::register_owned(py, NonNull::new_unchecked(obj.into_ptr()));
|
2017-07-13 20:01:31 +00:00
|
|
|
|
|
2020-05-02 12:12:38 +00:00
|
|
|
|
assert_eq!(owned_object_count(), 1);
|
2019-03-09 23:28:25 +00:00
|
|
|
|
assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
|
2017-07-13 20:01:31 +00:00
|
|
|
|
{
|
2020-05-03 21:29:44 +00:00
|
|
|
|
let _pool = py.new_pool();
|
|
|
|
|
let obj = get_object(py);
|
2020-05-02 06:33:10 +00:00
|
|
|
|
gil::register_owned(py, NonNull::new_unchecked(obj.into_ptr()));
|
2020-05-02 12:12:38 +00:00
|
|
|
|
assert_eq!(owned_object_count(), 2);
|
2017-07-13 20:01:31 +00:00
|
|
|
|
}
|
2020-05-02 12:12:38 +00:00
|
|
|
|
assert_eq!(owned_object_count(), 1);
|
2017-07-13 20:01:31 +00:00
|
|
|
|
}
|
|
|
|
|
{
|
2020-05-02 12:12:38 +00:00
|
|
|
|
assert_eq!(owned_object_count(), 0);
|
2019-03-09 23:28:25 +00:00
|
|
|
|
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
|
2017-07-13 20:01:31 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2020-04-07 19:37:02 +00:00
|
|
|
|
fn test_pyobject_drop_with_gil_decreases_refcnt() {
|
|
|
|
|
let gil = Python::acquire_gil();
|
|
|
|
|
let py = gil.python();
|
2020-05-03 21:29:44 +00:00
|
|
|
|
let obj = get_object(py);
|
2020-04-07 19:37:02 +00:00
|
|
|
|
// Ensure that obj does not get freed
|
|
|
|
|
let _ref = obj.clone_ref(py);
|
|
|
|
|
let obj_ptr = obj.as_ptr();
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
{
|
2020-05-02 12:12:38 +00:00
|
|
|
|
assert_eq!(owned_object_count(), 0);
|
2020-04-07 19:37:02 +00:00
|
|
|
|
assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// With the GIL held, obj can be dropped immediately
|
|
|
|
|
drop(obj);
|
|
|
|
|
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_pyobject_drop_without_gil_doesnt_decrease_refcnt() {
|
2019-03-09 23:28:25 +00:00
|
|
|
|
let gil = Python::acquire_gil();
|
|
|
|
|
let py = gil.python();
|
2020-05-03 21:29:44 +00:00
|
|
|
|
let obj = get_object(py);
|
2019-03-09 23:28:25 +00:00
|
|
|
|
// Ensure that obj does not get freed
|
|
|
|
|
let _ref = obj.clone_ref(py);
|
|
|
|
|
let obj_ptr = obj.as_ptr();
|
2017-07-13 20:01:31 +00:00
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
{
|
2020-05-02 12:12:38 +00:00
|
|
|
|
assert_eq!(owned_object_count(), 0);
|
2019-03-09 23:28:25 +00:00
|
|
|
|
assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
|
2017-07-13 20:01:31 +00:00
|
|
|
|
}
|
2020-04-07 19:37:02 +00:00
|
|
|
|
|
|
|
|
|
// Without the GIL held, obj cannot be dropped until the next GIL acquire
|
|
|
|
|
drop(gil);
|
2019-03-05 20:52:44 +00:00
|
|
|
|
drop(obj);
|
2019-04-24 20:41:59 +00:00
|
|
|
|
assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
|
2017-07-13 20:01:31 +00:00
|
|
|
|
|
|
|
|
|
{
|
2020-04-07 19:37:02 +00:00
|
|
|
|
// Next time the GIL is acquired, the object is released
|
2017-07-13 20:01:31 +00:00
|
|
|
|
let _gil = Python::acquire_gil();
|
2020-04-07 19:37:02 +00:00
|
|
|
|
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
|
2017-07-13 20:01:31 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-07 19:37:02 +00:00
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_gil_counts() {
|
|
|
|
|
// Check GILGuard and GILPool both increase counts correctly
|
|
|
|
|
let get_gil_count = || GIL_COUNT.with(|c| c.get());
|
|
|
|
|
|
|
|
|
|
assert_eq!(get_gil_count(), 0);
|
|
|
|
|
let gil = Python::acquire_gil();
|
|
|
|
|
assert_eq!(get_gil_count(), 1);
|
|
|
|
|
|
|
|
|
|
assert_eq!(get_gil_count(), 1);
|
2020-04-12 07:31:35 +00:00
|
|
|
|
let pool = unsafe { GILPool::new() };
|
2020-04-07 19:37:02 +00:00
|
|
|
|
assert_eq!(get_gil_count(), 2);
|
|
|
|
|
|
2020-04-12 07:31:35 +00:00
|
|
|
|
let pool2 = unsafe { GILPool::new() };
|
2020-04-07 19:37:02 +00:00
|
|
|
|
assert_eq!(get_gil_count(), 3);
|
|
|
|
|
|
|
|
|
|
drop(pool);
|
|
|
|
|
assert_eq!(get_gil_count(), 2);
|
|
|
|
|
|
2020-05-03 21:29:44 +00:00
|
|
|
|
// Creating a new GILGuard should not increment the gil count if a GILPool already exists
|
2020-04-07 19:37:02 +00:00
|
|
|
|
let gil2 = Python::acquire_gil();
|
|
|
|
|
assert_eq!(get_gil_count(), 2);
|
|
|
|
|
|
|
|
|
|
drop(pool2);
|
|
|
|
|
assert_eq!(get_gil_count(), 1);
|
|
|
|
|
|
2020-05-03 21:29:44 +00:00
|
|
|
|
drop(gil2);
|
|
|
|
|
assert_eq!(get_gil_count(), 1);
|
|
|
|
|
|
2020-04-07 19:37:02 +00:00
|
|
|
|
drop(gil);
|
|
|
|
|
assert_eq!(get_gil_count(), 0);
|
|
|
|
|
}
|
2020-05-07 14:43:33 +00:00
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_allow_threads() {
|
|
|
|
|
let gil = Python::acquire_gil();
|
|
|
|
|
let py = gil.python();
|
2020-05-03 21:29:44 +00:00
|
|
|
|
let object = get_object(py);
|
2020-05-07 14:43:33 +00:00
|
|
|
|
|
|
|
|
|
py.allow_threads(move || {
|
|
|
|
|
// Should be no pointers to drop
|
|
|
|
|
assert!(unsafe { (*POOL.pointers_to_drop.get()).is_empty() });
|
|
|
|
|
|
|
|
|
|
// Dropping object without the GIL should put the pointer in the pool
|
|
|
|
|
drop(object);
|
|
|
|
|
let obj_count = unsafe { (*POOL.pointers_to_drop.get()).len() };
|
|
|
|
|
assert_eq!(obj_count, 1);
|
|
|
|
|
|
|
|
|
|
// Now repeat dropping an object, with the GIL.
|
|
|
|
|
let gil = Python::acquire_gil();
|
|
|
|
|
|
|
|
|
|
// (Acquiring the GIL should have cleared the pool).
|
|
|
|
|
assert!(unsafe { (*POOL.pointers_to_drop.get()).is_empty() });
|
|
|
|
|
|
2020-05-03 21:29:44 +00:00
|
|
|
|
let object = get_object(gil.python());
|
2020-05-07 14:43:33 +00:00
|
|
|
|
drop(object);
|
|
|
|
|
drop(gil);
|
|
|
|
|
|
|
|
|
|
// Previous drop should have decreased count immediately instead of put in pool
|
|
|
|
|
assert!(unsafe { (*POOL.pointers_to_drop.get()).is_empty() });
|
|
|
|
|
})
|
|
|
|
|
}
|
2020-05-03 21:29:44 +00:00
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn dropping_gil_does_not_invalidate_references() {
|
|
|
|
|
// Acquiring GIL for the second time should be safe - see #864
|
|
|
|
|
let gil = Python::acquire_gil();
|
|
|
|
|
let py = gil.python();
|
|
|
|
|
let obj;
|
|
|
|
|
|
|
|
|
|
let gil2 = Python::acquire_gil();
|
|
|
|
|
obj = py.eval("object()", None, None).unwrap();
|
|
|
|
|
drop(gil2);
|
|
|
|
|
|
|
|
|
|
// After gil2 drops, obj should still have a reference count of one
|
|
|
|
|
assert_eq!(unsafe { ffi::Py_REFCNT(obj.as_ptr()) }, 1);
|
|
|
|
|
}
|
2017-07-13 20:01:31 +00:00
|
|
|
|
}
|