Skip to content

Commit

Permalink
add a way to declare free-threaded support without macros
Browse files Browse the repository at this point in the history
  • Loading branch information
ngoldbaum committed Oct 21, 2024
1 parent cf00bba commit 1bd1338
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 8 deletions.
20 changes: 13 additions & 7 deletions src/impl_/pymodule.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Implementation details of `#[pymodule]` which need to be accessible from proc-macro generated code.

use std::{cell::UnsafeCell, ffi::CStr, marker::PhantomData};
use std::{cell::UnsafeCell, ffi::CStr, marker::PhantomData, os::raw::c_int};

#[cfg(all(
not(any(PyPy, GraalPy)),
Expand All @@ -24,7 +24,7 @@ use crate::{
impl_::pymethods::PyMethodDef,
sync::GILOnceCell,
types::{PyCFunction, PyModule, PyModuleMethods},
Bound, Py, PyClass, PyResult, PyTypeInfo, Python,
Bound, Py, PyClass, PyErr, PyResult, PyTypeInfo, Python,
};

/// `Sync` wrapper of `ffi::PyModuleDef`.
Expand Down Expand Up @@ -141,11 +141,17 @@ impl ModuleDef {
ffi::PyModule_Create(self.ffi_def.get()),
)?
};
if supports_free_threaded {
unsafe {
ffi::PyUnstable_Module_SetGIL(module.as_ptr(), ffi::Py_MOD_GIL_NOT_USED)
};
}
let gil_used = {
if supports_free_threaded {
ffi::Py_MOD_GIL_NOT_USED
} else {
ffi::Py_MOD_GIL_USED
}
};
match unsafe { ffi::PyUnstable_Module_SetGIL(module.as_ptr(), gil_used) } {
c_int::MIN..=-1 => return Err(PyErr::fetch(py)),
0..=c_int::MAX => {}
};
self.initializer.0(module.bind(py))?;
Ok(module)
})
Expand Down
45 changes: 44 additions & 1 deletion src/types/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::types::{
};
use crate::{exceptions, ffi, Borrowed, Bound, BoundObject, Py, PyObject, Python};
use std::ffi::{CStr, CString};
use std::os::raw::c_int;
use std::str;

/// Represents a Python [`module`][1] object.
Expand Down Expand Up @@ -384,6 +385,38 @@ pub trait PyModuleMethods<'py>: crate::sealed::Sealed {
/// [1]: crate::prelude::pyfunction
/// [2]: crate::wrap_pyfunction
fn add_function(&self, fun: Bound<'_, PyCFunction>) -> PyResult<()>;

/// Declare whether or not this module supports running with the GIL disabled
///
/// If the module does not rely on the GIL for thread safety, you can pass True
/// to this function so that when the module is imported the interpreter will
/// not enable the GIL at runtime on the free-threaded interpreter.
///
/// This function sets the [`Py_MOD_GIL`
/// slot](https://docs.python.org/3/c-api/module.html#c.Py_mod_gil) on the
/// module object. The default is `Py_MOD_GIL_USED`, so passing `false` to
/// this function is a no-op unless you have already set `Py_MOD_GIL` to
/// `Py_MOD_GIL_NOT_USED` elsewhere.
///
/// # Examples
///
/// ```rust
/// use pyo3::prelude::*;
///
/// #[pymodule(supports_free_threaded = true)]
/// fn my_module(py: Python<'_>, module: &Bound<'_, PyModule>) -> PyResult<()> {
/// let submodule = PyModule::new(py, "submodule")?;
/// submodule.supports_free_threaded(true)?;
/// module.add_submodule(&submodule)?;
/// Ok(())
/// }
/// ```
///
/// The resulting module will not print a `RuntimeWarning` and re-enable the
/// GIL when Python imports it on the free-threaded build, since all module
/// objects defined in the extension have `Py_MOD_GIL` set to
/// `Py_MOD_GIL_NOT_USED`.
fn supports_free_threaded(&self, supports_free_threaded: bool) -> PyResult<()>;
}

impl<'py> PyModuleMethods<'py> for Bound<'py, PyModule> {
Expand Down Expand Up @@ -493,7 +526,6 @@ impl<'py> PyModuleMethods<'py> for Bound<'py, PyModule> {
T: IntoPyCallbackOutput<'py, PyObject>,
{
fn inner(module: &Bound<'_, PyModule>, object: Bound<'_, PyAny>) -> PyResult<()> {
if object.is_instance_of::<PyModule>() {}
let name = object.getattr(__name__(module.py()))?;
module.add(name.downcast_into::<PyString>()?, object)
}
Expand All @@ -511,6 +543,17 @@ impl<'py> PyModuleMethods<'py> for Bound<'py, PyModule> {
let name = fun.getattr(__name__(self.py()))?;
self.add(name.downcast_into::<PyString>()?, fun)
}

fn supports_free_threaded(&self, supports_free_threaded: bool) -> PyResult<()> {
let gil_used = match supports_free_threaded {
true => ffi::Py_MOD_GIL_NOT_USED,
false => ffi::Py_MOD_GIL_USED,
};
match unsafe { ffi::PyUnstable_Module_SetGIL(self.as_ptr(), gil_used) } {
c_int::MIN..=-1 => Err(PyErr::fetch(self.py())),
0..=c_int::MAX => Ok(()),
}
}
}

fn __all__(py: Python<'_>) -> &Bound<'_, PyString> {
Expand Down

0 comments on commit 1bd1338

Please sign in to comment.