Skip to content

Commit

Permalink
rename supports_free_threaded to gil_used
Browse files Browse the repository at this point in the history
  • Loading branch information
ngoldbaum committed Oct 28, 2024
1 parent 19910f1 commit 39ffe29
Show file tree
Hide file tree
Showing 23 changed files with 55 additions and 70 deletions.
10 changes: 5 additions & 5 deletions guide/src/free-threading.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ with no side-effects or defining an immutable Python class, will likely work
"out of the box" on the free-threaded build. All that will be necessary is to
annotate Python modules declared by rust code in your project to declare that
they support free-threaded Python, for example by declaring the module with
`#[pymodule(supports_free_threaded = true)]`.
`#[pymodule(gil_used = false)]`.

At a low-level, annotating a module sets the `Py_MOD_GIL` slot on modules
defined by an extension to `Py_MOD_GIL_NOT_USED`, which allows the interpreter
Expand All @@ -62,15 +62,15 @@ GIL to remain disabled by setting the `PYTHON_GIL=0` as an environment variable
or passing `-Xgil=0` when starting Python (`0` means the GIL is turned off).

If you are sure that all data structures exposed in a `PyModule` are
thread-safe, then pass `supports_free_threaded = true` as a parameter to the
thread-safe, then pass `gil_used = false` as a parameter to the
`pymodule` procedural macro declaring the module or call
`PyModule::supports_free_threaded` on a `PyModule` instance. For example:
`PyModule::gil_used` on a `PyModule` instance. For example:

```rust
use pyo3::prelude::*;

/// This module supports free-threaded Python
#[pymodule(supports_free_threaded = true)]
#[pymodule(gil_used = false)]
fn my_extension(m: &Bound<'_, PyModule>) -> PyResult<()> {
// add members to the module that you know are thread-safe
Ok(())
Expand All @@ -85,7 +85,7 @@ use pyo3::prelude::*;
# #[allow(dead_code)]
fn register_child_module(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
let child_module = PyModule::new(parent_module.py(), "child_module")?;
child_module.supports_free_threaded(true)?;
child_module.gil_used(false)?;
parent_module.add_submodule(&child_module)
}

Expand Down
4 changes: 2 additions & 2 deletions newsfragments/4588.added.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
* It is now possible to declare that a module supports the free-threaded build
by either calling `PyModule::supports_free_threaded` or passing
`supports_free_threaded = true` as a parameter to the `pymodule` proc macro.
by either calling `PyModule::gil_used` or passing
`gil_used = false` as a parameter to the `pymodule` proc macro.
4 changes: 2 additions & 2 deletions pyo3-macros-backend/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub mod kw {
syn::custom_keyword!(transparent);
syn::custom_keyword!(unsendable);
syn::custom_keyword!(weakref);
syn::custom_keyword!(supports_free_threaded);
syn::custom_keyword!(gil_used);
}

fn take_int(read: &mut &str, tracker: &mut usize) -> String {
Expand Down Expand Up @@ -309,7 +309,7 @@ pub type RenameAllAttribute = KeywordAttribute<kw::rename_all, RenamingRuleLitSt
pub type StrFormatterAttribute = OptionalKeywordAttribute<kw::str, StringFormatter>;
pub type TextSignatureAttribute = KeywordAttribute<kw::text_signature, TextSignatureAttributeValue>;
pub type SubmoduleAttribute = kw::submodule;
pub type FreeThreadedAttribute = KeywordAttribute<kw::supports_free_threaded, LitBool>;
pub type GILUsedAttribute = KeywordAttribute<kw::gil_used, LitBool>;

impl<K: Parse + std::fmt::Debug, V: Parse> Parse for KeywordAttribute<K, V> {
fn parse(input: ParseStream<'_>) -> Result<Self> {
Expand Down
28 changes: 11 additions & 17 deletions pyo3-macros-backend/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use crate::{
attributes::{
self, kw, take_attributes, take_pyo3_options, CrateAttribute, FreeThreadedAttribute,
self, kw, take_attributes, take_pyo3_options, CrateAttribute, GILUsedAttribute,
ModuleAttribute, NameAttribute, SubmoduleAttribute,
},
get_doc,
Expand All @@ -29,7 +29,7 @@ pub struct PyModuleOptions {
name: Option<NameAttribute>,
module: Option<ModuleAttribute>,
submodule: Option<kw::submodule>,
supports_free_threaded: Option<FreeThreadedAttribute>,
gil_used: Option<GILUsedAttribute>,
}

impl Parse for PyModuleOptions {
Expand Down Expand Up @@ -73,8 +73,8 @@ impl PyModuleOptions {
submodule,
" (it is implicitly always specified for nested modules)"
),
PyModulePyO3Option::SupportsFreeThreaded(supports_free_threaded) => {
set_option!(supports_free_threaded)
PyModulePyO3Option::GILUsed(gil_used) => {
set_option!(gil_used)
}
}
}
Expand Down Expand Up @@ -353,10 +353,7 @@ pub fn pymodule_module_impl(
ctx,
module_def,
options.submodule.is_some(),
options
.supports_free_threaded
.map(|op| op.value.value)
.unwrap_or(false),
options.gil_used.map(|op| op.value.value).unwrap_or(true),
);

Ok(quote!(
Expand Down Expand Up @@ -401,10 +398,7 @@ pub fn pymodule_function_impl(
ctx,
quote! { MakeDef::make_def() },
false,
options
.supports_free_threaded
.map(|op| op.value.value)
.unwrap_or(false),
options.gil_used.map(|op| op.value.value).unwrap_or(true),
);

// Module function called with optional Python<'_> marker as first arg, followed by the module.
Expand Down Expand Up @@ -450,7 +444,7 @@ fn module_initialization(
ctx: &Ctx,
module_def: TokenStream,
is_submodule: bool,
supports_free_threaded: bool,
gil_used: bool,
) -> TokenStream {
let Ctx { pyo3_path, .. } = ctx;
let pyinit_symbol = format!("PyInit_{}", name);
Expand All @@ -472,7 +466,7 @@ fn module_initialization(
#[doc(hidden)]
#[export_name = #pyinit_symbol]
pub unsafe extern "C" fn __pyo3_init() -> *mut #pyo3_path::ffi::PyObject {
#pyo3_path::impl_::trampoline::module_init(|py| _PYO3_DEF.make_module(py, #supports_free_threaded))
#pyo3_path::impl_::trampoline::module_init(|py| _PYO3_DEF.make_module(py, #gil_used))
}
});
}
Expand Down Expand Up @@ -619,7 +613,7 @@ enum PyModulePyO3Option {
Crate(CrateAttribute),
Name(NameAttribute),
Module(ModuleAttribute),
SupportsFreeThreaded(FreeThreadedAttribute),
GILUsed(GILUsedAttribute),
}

impl Parse for PyModulePyO3Option {
Expand All @@ -633,8 +627,8 @@ impl Parse for PyModulePyO3Option {
input.parse().map(PyModulePyO3Option::Module)
} else if lookahead.peek(attributes::kw::submodule) {
input.parse().map(PyModulePyO3Option::Submodule)
} else if lookahead.peek(attributes::kw::supports_free_threaded) {
input.parse().map(PyModulePyO3Option::SupportsFreeThreaded)
} else if lookahead.peek(attributes::kw::gil_used) {
input.parse().map(PyModulePyO3Option::GILUsed)
} else {
Err(lookahead.error())
}
Expand Down
2 changes: 1 addition & 1 deletion pytests/src/awaitable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ impl FutureAwaitable {
}
}

#[pymodule(supports_free_threaded = true)]
#[pymodule(gil_used = false)]
pub fn awaitable(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<IterAwaitable>()?;
m.add_class::<FutureAwaitable>()?;
Expand Down
2 changes: 1 addition & 1 deletion pytests/src/buf_and_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ fn return_memoryview(py: Python<'_>) -> PyResult<Bound<'_, PyMemoryView>> {
PyMemoryView::from(&bytes)
}

#[pymodule(supports_free_threaded = true)]
#[pymodule(gil_used = false)]
pub fn buf_and_str(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<BytesExtractor>()?;
m.add_function(wrap_pyfunction!(return_memoryview, m)?)?;
Expand Down
2 changes: 1 addition & 1 deletion pytests/src/comparisons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ impl OrderedDefaultNe {
}
}

#[pymodule(supports_free_threaded = true)]
#[pymodule(gil_used = false)]
pub fn comparisons(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<Eq>()?;
m.add_class::<EqDefaultNe>()?;
Expand Down
2 changes: 1 addition & 1 deletion pytests/src/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ impl TzClass {
}
}

#[pymodule(supports_free_threaded = true)]
#[pymodule(gil_used = false)]
pub fn datetime(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(make_date, m)?)?;
m.add_function(wrap_pyfunction!(get_date_tuple, m)?)?;
Expand Down
2 changes: 1 addition & 1 deletion pytests/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use pyo3::{
wrap_pyfunction, Bound, PyResult,
};

#[pymodule(supports_free_threaded = true)]
#[pymodule(gil_used = false)]
pub fn enums(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<SimpleEnum>()?;
m.add_class::<ComplexEnum>()?;
Expand Down
2 changes: 1 addition & 1 deletion pytests/src/free_threaded_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ fn add_two(x: usize) -> usize {
x + 2
}

#[pymodule(supports_free_threaded = true)]
#[pymodule(gil_used = false)]
pub fn free_threaded_mod(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(add_two, m)?)?;
Ok(())
Expand Down
2 changes: 1 addition & 1 deletion pytests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub mod pyfunctions;
pub mod sequence;
pub mod subclassing;

#[pymodule(supports_free_threaded = true)]
#[pymodule(gil_used = false)]
fn pyo3_pytests(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_wrapped(wrap_pymodule!(awaitable::awaitable))?;
#[cfg(not(Py_LIMITED_API))]
Expand Down
2 changes: 1 addition & 1 deletion pytests/src/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ fn get_item_and_run_callback(dict: Bound<'_, PyDict>, callback: Bound<'_, PyAny>
Ok(())
}

#[pymodule(supports_free_threaded = true)]
#[pymodule(gil_used = false)]
pub fn misc(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(issue_219, m)?)?;
m.add_function(wrap_pyfunction!(get_type_fully_qualified_name, m)?)?;
Expand Down
2 changes: 1 addition & 1 deletion pytests/src/objstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl ObjStore {
}
}

#[pymodule(supports_free_threaded = true)]
#[pymodule(gil_used = false)]
pub fn objstore(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<ObjStore>()
}
2 changes: 1 addition & 1 deletion pytests/src/othermod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fn double(x: i32) -> i32 {
x * 2
}

#[pymodule(supports_free_threaded = true)]
#[pymodule(gil_used = false)]
pub fn othermod(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(double, m)?)?;

Expand Down
2 changes: 1 addition & 1 deletion pytests/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fn take_pathbuf(path: PathBuf) -> PathBuf {
path
}

#[pymodule(supports_free_threaded = true)]
#[pymodule(gil_used = false)]
pub fn path(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(make_path, m)?)?;
m.add_function(wrap_pyfunction!(take_pathbuf, m)?)?;
Expand Down
2 changes: 1 addition & 1 deletion pytests/src/pyclasses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ impl ClassWithDict {
}
}

#[pymodule(supports_free_threaded = true)]
#[pymodule(gil_used = false)]
pub fn pyclasses(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<EmptyClass>()?;
m.add_class::<PyClassIter>()?;
Expand Down
2 changes: 1 addition & 1 deletion pytests/src/pyfunctions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ fn args_kwargs<'py>(
(args, kwargs)
}

#[pymodule(supports_free_threaded = true)]
#[pymodule(gil_used = false)]
pub fn pyfunctions(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(none, m)?)?;
m.add_function(wrap_pyfunction!(simple, m)?)?;
Expand Down
2 changes: 1 addition & 1 deletion pytests/src/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn vec_to_vec_pystring(vec: Vec<Bound<'_, PyString>>) -> Vec<Bound<'_, PyString>
vec
}

#[pymodule(supports_free_threaded = true)]
#[pymodule(gil_used = false)]
pub fn sequence(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(vec_to_vec_i32, m)?)?;
m.add_function(wrap_pyfunction!(array_to_array_i32, m)?)?;
Expand Down
2 changes: 1 addition & 1 deletion pytests/src/subclassing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ impl Subclassable {
}
}

#[pymodule(supports_free_threaded = true)]
#[pymodule(gil_used = false)]
pub fn subclassing(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<Subclassable>()?;
Ok(())
Expand Down
27 changes: 10 additions & 17 deletions src/impl_/pymodule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub struct ModuleDef {
/// Initialized module object, cached to avoid reinitialization.
module: GILOnceCell<Py<PyModule>>,
/// Whether or not the module supports running without the GIL
supports_free_threaded: AtomicBool,
gil_used: AtomicBool,
}

/// Wrapper to enable initializer to be used in const fns.
Expand Down Expand Up @@ -90,16 +90,12 @@ impl ModuleDef {
))]
interpreter: AtomicI64::new(-1),
module: GILOnceCell::new(),
supports_free_threaded: AtomicBool::new(false),
gil_used: AtomicBool::new(true),
}
}
/// Builds a module using user given initializer. Used for [`#[pymodule]`][crate::pymodule].
#[cfg_attr(any(Py_LIMITED_API, not(Py_GIL_DISABLED)), allow(unused_variables))]
pub fn make_module(
&'static self,
py: Python<'_>,
supports_free_threaded: bool,
) -> PyResult<Py<PyModule>> {
pub fn make_module(&'static self, py: Python<'_>, gil_used: bool) -> PyResult<Py<PyModule>> {
// Check the interpreter ID has not changed, since we currently have no way to guarantee
// that static data is not reused across interpreters.
//
Expand Down Expand Up @@ -147,14 +143,14 @@ impl ModuleDef {
};
#[cfg(all(not(Py_LIMITED_API), Py_GIL_DISABLED))]
{
let gil_used = {
if supports_free_threaded {
ffi::Py_MOD_GIL_NOT_USED
} else {
let gil_used_ptr = {
if gil_used {
ffi::Py_MOD_GIL_USED
} else {
ffi::Py_MOD_GIL_NOT_USED
}
};
if unsafe { ffi::PyUnstable_Module_SetGIL(module.as_ptr(), gil_used) } < 0 {
if unsafe { ffi::PyUnstable_Module_SetGIL(module.as_ptr(), gil_used_ptr) } < 0 {
return Err(PyErr::fetch(py));
}
}
Expand Down Expand Up @@ -215,11 +211,8 @@ impl PyAddToModule for PyMethodDef {
impl PyAddToModule for ModuleDef {
fn add_to_module(&'static self, module: &Bound<'_, PyModule>) -> PyResult<()> {
module.add_submodule(
self.make_module(
module.py(),
self.supports_free_threaded.load(Ordering::Relaxed),
)?
.bind(module.py()),
self.make_module(module.py(), self.gil_used.load(Ordering::Relaxed))?
.bind(module.py()),
)
}
}
Expand Down
14 changes: 7 additions & 7 deletions src/types/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,10 +404,10 @@ pub trait PyModuleMethods<'py>: crate::sealed::Sealed {
/// ```rust
/// use pyo3::prelude::*;
///
/// #[pymodule(supports_free_threaded = true)]
/// #[pymodule(gil_used = false)]
/// fn my_module(py: Python<'_>, module: &Bound<'_, PyModule>) -> PyResult<()> {
/// let submodule = PyModule::new(py, "submodule")?;
/// submodule.supports_free_threaded(true)?;
/// submodule.gil_used(false)?;
/// module.add_submodule(&submodule)?;
/// Ok(())
/// }
Expand All @@ -419,7 +419,7 @@ pub trait PyModuleMethods<'py>: crate::sealed::Sealed {
/// `Py_MOD_GIL_NOT_USED`.
///
/// This is a no-op on the GIL-enabled build.
fn supports_free_threaded(&self, supports_free_threaded: bool) -> PyResult<()>;
fn gil_used(&self, gil_used: bool) -> PyResult<()>;
}

impl<'py> PyModuleMethods<'py> for Bound<'py, PyModule> {
Expand Down Expand Up @@ -548,12 +548,12 @@ impl<'py> PyModuleMethods<'py> for Bound<'py, PyModule> {
}

#[cfg_attr(any(Py_LIMITED_API, not(Py_GIL_DISABLED)), allow(unused_variables))]
fn supports_free_threaded(&self, supports_free_threaded: bool) -> PyResult<()> {
fn gil_used(&self, gil_used: bool) -> PyResult<()> {
#[cfg(all(not(Py_LIMITED_API), Py_GIL_DISABLED))]
{
let gil_used = match supports_free_threaded {
true => ffi::Py_MOD_GIL_NOT_USED,
false => ffi::Py_MOD_GIL_USED,
let gil_used = match gil_used {
true => ffi::Py_MOD_GIL_USED,
false => ffi::Py_MOD_GIL_NOT_USED,
};
match unsafe { ffi::PyUnstable_Module_SetGIL(self.as_ptr(), gil_used) } {
c_int::MIN..=-1 => Err(PyErr::fetch(self.py())),
Expand Down
Loading

0 comments on commit 39ffe29

Please sign in to comment.