Skip to content

Commit

Permalink
gil: add unsafe variation for obtaining GILGuard without checks
Browse files Browse the repository at this point in the history
GILGuard::acquire() cannot be called during multi-phase Python
interpreter initialization because it calls Py_IsInitialized(),
which doesn't report the interpreter as initialized until all
phases of initialization have completed.

PyOxidizer uses the multi-phase initialization API and needs to
interact with pyo3's high-level APIs (not the FFI bindings) after
partial interpreter initialization, before the interpreter is fully
initialized. Attempts to use GILGuard::acquire() result in a panic
due to the aforementioned Py_IsInitialized() check failing.

This commit introduces an unsafe function to acquire the GILGuard
without performing state checking first. I've tested this with
PyOxidizer and can confirm it allows PyOxidizer to use pyo3 APIs
before interpreter initialization is complete.
  • Loading branch information
indygreg committed Aug 8, 2021
1 parent 3de5591 commit 8b6e8a8
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

- Add `indexmap` feature to add `ToPyObject`, `IntoPy` and `FromPyObject` implementations for `indexmap::IndexMap`. [#1728](https://github.com/PyO3/pyo3/pull/1728)
- Add `pyo3_build_config::add_extension_module_link_args()` to use in build scripts to set linker arguments (for macOS). [#1755](https://github.com/PyO3/pyo3/pull/1755)
- Add `Python::acquire_gil_no_checks()` unsafe variation of `Python::acquire_gil()` to allow the GIL to be obtained without performing interpreter initialization checks.

### Changed

Expand Down
9 changes: 9 additions & 0 deletions src/gil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,15 @@ impl GILGuard {
}
}

Self::acquire_no_checks()
}

/// Acquires the `GILGuard` without performing any state checking.
///
/// This can be called in "unsafe" contexts where the normal interpreter state
/// checking performed by `GILGuard::acquire` may fail. This includes calling
/// as part of multi-phase interpreter initialization.
pub(crate) fn acquire_no_checks() -> GILGuard {
let gstate = unsafe { ffi::PyGILState_Ensure() }; // acquire GIL

// If there's already a GILPool, we should not create another or this could lead to
Expand Down
18 changes: 18 additions & 0 deletions src/python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,24 @@ impl<'p> Python<'p> {
GILGuard::acquire()
}

/// Acquires the [GILGuard] without performing state checking.
///
/// # Safety
///
/// This bypasses checking that [Python::acquire] would normally perform, such
/// as ensuring the Python interpreter is fully initialized. If you call this
/// from a process where the Python interpreter isn't in a "good" state, the
/// process may crash.
///
/// One special case where calling this function over [Python::acquire_gil] is
/// justified is during multi-phase interpreter initialization. If the interpreter
/// is configured for multi-phase initialization, it is safe to call this function
/// between `Py_InitializeFromConfig()` and `_Py_InitializeMain()`.
#[inline]
pub unsafe fn acquire_gil_no_checks() -> GILGuard {
GILGuard::acquire_no_checks()
}

/// Temporarily releases the `GIL`, thus allowing other Python threads to run.
///
/// # Examples
Expand Down

0 comments on commit 8b6e8a8

Please sign in to comment.