Change `GILGuard` to be able to represent a GIL that was already held (#4187)

See #4181
This commit is contained in:
Alex Gaynor 2024-05-16 17:55:05 -04:00 committed by GitHub
parent 7790dab480
commit 8de1787580
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 24 additions and 15 deletions

View File

@ -148,20 +148,26 @@ where
} }
/// RAII type that represents the Global Interpreter Lock acquisition. /// RAII type that represents the Global Interpreter Lock acquisition.
pub(crate) struct GILGuard { pub(crate) enum GILGuard {
gstate: ffi::PyGILState_STATE, /// Indicates the GIL was already held with this GILGuard was acquired.
#[allow(deprecated)] // TODO: remove this with the gil-refs feature in 0.22 Assumed,
pool: mem::ManuallyDrop<GILPool>, /// Indicates that we actually acquired the GIL when this GILGuard was acquired
Ensured {
gstate: ffi::PyGILState_STATE,
#[allow(deprecated)] // TODO: remove this with the gil-refs feature in 0.22
pool: mem::ManuallyDrop<GILPool>,
},
} }
impl GILGuard { impl GILGuard {
/// PyO3 internal API for acquiring the GIL. The public API is Python::with_gil. /// PyO3 internal API for acquiring the GIL. The public API is Python::with_gil.
/// ///
/// If the GIL was already acquired via PyO3, this returns `None`. Otherwise, /// If the GIL was already acquired via PyO3, this returns
/// the GIL will be acquired and a new `GILPool` created. /// `GILGuard::Assumed`. Otherwise, the GIL will be acquired and
pub(crate) fn acquire() -> Option<Self> { /// `GILGuard::Ensured` will be returned.
pub(crate) fn acquire() -> Self {
if gil_is_acquired() { if gil_is_acquired() {
return None; return GILGuard::Assumed;
} }
// Maybe auto-initialize the GIL: // Maybe auto-initialize the GIL:
@ -207,27 +213,30 @@ impl GILGuard {
/// This can be called in "unsafe" contexts where the normal interpreter state /// This can be called in "unsafe" contexts where the normal interpreter state
/// checking performed by `GILGuard::acquire` may fail. This includes calling /// checking performed by `GILGuard::acquire` may fail. This includes calling
/// as part of multi-phase interpreter initialization. /// as part of multi-phase interpreter initialization.
pub(crate) fn acquire_unchecked() -> Option<Self> { pub(crate) fn acquire_unchecked() -> Self {
if gil_is_acquired() { if gil_is_acquired() {
return None; return GILGuard::Assumed;
} }
let gstate = unsafe { ffi::PyGILState_Ensure() }; // acquire GIL let gstate = unsafe { ffi::PyGILState_Ensure() }; // acquire GIL
#[allow(deprecated)] #[allow(deprecated)]
let pool = unsafe { mem::ManuallyDrop::new(GILPool::new()) }; let pool = unsafe { mem::ManuallyDrop::new(GILPool::new()) };
Some(GILGuard { gstate, pool }) GILGuard::Ensured { gstate, pool }
} }
} }
/// 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) {
unsafe { match self {
// Drop the objects in the pool before attempting to release the thread state GILGuard::Assumed => {}
mem::ManuallyDrop::drop(&mut self.pool); GILGuard::Ensured { gstate, pool } => unsafe {
// Drop the objects in the pool before attempting to release the thread state
mem::ManuallyDrop::drop(pool);
ffi::PyGILState_Release(self.gstate); ffi::PyGILState_Release(*gstate);
},
} }
} }
} }