Merge pull request #913 from davidhewitt/gil_state_list

Fix issue with PyObject drop and allow_threads
This commit is contained in:
Yuji Kanagawa 2020-05-08 12:59:36 +09:00 committed by GitHub
commit 027c90c223
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 36 additions and 2 deletions

View File

@ -16,7 +16,9 @@ thread_local! {
/// whenever they are dropped. /// whenever they are dropped.
/// ///
/// As a result, if this thread has the GIL, GIL_COUNT is greater than zero. /// As a result, if this thread has the GIL, GIL_COUNT is greater than zero.
static GIL_COUNT: Cell<u32> = Cell::new(0); ///
/// pub(crate) because it is manipulated temporarily by Python::allow_threads
pub(crate) static GIL_COUNT: Cell<u32> = Cell::new(0);
/// These are objects owned by the current thread, to be released when the GILPool drops. /// 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)); static OWNED_OBJECTS: RefCell<Vec<NonNull<ffi::PyObject>>> = RefCell::new(Vec::with_capacity(256));
@ -319,7 +321,7 @@ fn decrement_gil_count() {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::{GILPool, GIL_COUNT, OWNED_OBJECTS}; use super::{GILPool, GIL_COUNT, OWNED_OBJECTS, POOL};
use crate::{ffi, gil, AsPyPointer, IntoPyPointer, PyObject, Python, ToPyObject}; use crate::{ffi, gil, AsPyPointer, IntoPyPointer, PyObject, Python, ToPyObject};
use std::ptr::NonNull; use std::ptr::NonNull;
@ -475,4 +477,34 @@ mod test {
drop(gil); drop(gil);
assert_eq!(get_gil_count(), 0); assert_eq!(get_gil_count(), 0);
} }
#[test]
fn test_allow_threads() {
let gil = Python::acquire_gil();
let py = gil.python();
let object = get_object();
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() });
let object = get_object();
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() });
})
}
} }

View File

@ -135,6 +135,7 @@ impl<'p> Python<'p> {
// The `Send` bound on the closure prevents the user from // The `Send` bound on the closure prevents the user from
// transferring the `Python` token into the closure. // transferring the `Python` token into the closure.
unsafe { unsafe {
let count = gil::GIL_COUNT.with(|c| c.replace(0));
let save = ffi::PyEval_SaveThread(); let save = ffi::PyEval_SaveThread();
// Unwinding right here corrupts the Python interpreter state and leads to weird // Unwinding right here corrupts the Python interpreter state and leads to weird
// crashes such as stack overflows. We will catch the unwind and resume as soon as // crashes such as stack overflows. We will catch the unwind and resume as soon as
@ -144,6 +145,7 @@ impl<'p> Python<'p> {
// that the closure is unwind safe. // that the closure is unwind safe.
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)); let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f));
ffi::PyEval_RestoreThread(save); ffi::PyEval_RestoreThread(save);
gil::GIL_COUNT.with(|c| c.set(count));
// Now that the GIL state has been safely reset, we can unwind if a panic was caught. // Now that the GIL state has been safely reset, we can unwind if a panic was caught.
result.unwrap_or_else(|payload| std::panic::resume_unwind(payload)) result.unwrap_or_else(|payload| std::panic::resume_unwind(payload))
} }