From 22ee7ad1475b57711b0f1e3e3728857520032bd2 Mon Sep 17 00:00:00 2001 From: Max Carrara Date: Mon, 6 May 2024 17:15:43 +0200 Subject: [PATCH] impl: add module pymodule_state This module contains `ModuleState`, a struct used to represent state that lies on the per-module memory region of a Python module, as well as various exported functions that are used to integrate `ModuleState` with the Python interpreter. This commit does not implement any further functionality (such as actually representing any kind of state) and is instead more of a blank slate for further additions. Signed-off-by: Max Carrara --- src/impl_.rs | 1 + src/impl_/pymodule_state.rs | 92 +++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 src/impl_/pymodule_state.rs diff --git a/src/impl_.rs b/src/impl_.rs index 5bfeda39f65..a183f6a8301 100644 --- a/src/impl_.rs +++ b/src/impl_.rs @@ -19,6 +19,7 @@ pub mod pyclass; pub mod pyfunction; pub mod pymethods; pub mod pymodule; +pub mod pymodule_state; #[doc(hidden)] pub mod trampoline; pub mod wrap; diff --git a/src/impl_/pymodule_state.rs b/src/impl_/pymodule_state.rs new file mode 100644 index 00000000000..2806d3270cb --- /dev/null +++ b/src/impl_/pymodule_state.rs @@ -0,0 +1,92 @@ +use std::ffi::{c_int, c_void}; +use std::ptr::NonNull; + +use crate::ffi; + +/// Represents a Python module's state. +/// +/// More precisely, this `struct` resides on the per-module memory area +/// allocated during the module's creation. +#[repr(C)] +#[derive(Debug)] +pub struct ModuleState { + inner: Option>, +} + +impl ModuleState { + pub fn new() -> Self { + let boxed = Box::new(ModuleStateImpl::new()); + + Self { + inner: NonNull::new(Box::into_raw(boxed)), + } + } +} + +/// Inner layout of [`ModuleState`]. +/// +/// In order to guarantee that all resources acquired during the initialization +/// of per-module state are correctly released, this `struct` exists as the sole +/// field of [`ModuleState`] in the form of a pointer. This allows +/// [`module_state_free`] to safely [`drop`] this `struct` when [`ModuleState`] +/// is being deallocated by the Python interpreter. +#[repr(C)] +#[derive(Debug)] +struct ModuleStateImpl {} + +impl ModuleStateImpl { + fn new() -> Self { + Self {} + } +} + +/// Called during multi-phase initialization in order to create an instance of +/// [`ModuleState`] on the memory area specific to modules. +/// +/// Slot: [Py_mod_exec] +/// +/// [Py_mod_exec]: https://docs.python.org/3/c-api/module.html#c.Py_mod_exec +pub unsafe extern "C" fn module_state_init(module: *mut ffi::PyObject) -> c_int { + let state: *mut ModuleState = ffi::PyModule_GetState(module.cast()).cast(); + *state = ModuleState::new(); + + 0 +} + +/// Called during GC traversal of the module object. +/// +/// Used for the [`m_traverse`] field of [`PyModuleDef`]. +/// +/// [`m_traverse`]: https://docs.python.org/3/c-api/module.html#c.PyModuleDef.m_traverse +/// [`PyModuleDef`]: https://docs.python.org/3/c-api/module.html#c.PyModuleDef +pub unsafe extern "C" fn module_state_traverse( + _module: *mut ffi::PyObject, + _visit: ffi::visitproc, + _arg: *mut c_void, +) -> c_int { + 0 +} + +/// Called during GC clearing of the module object. +/// +/// Used for the [`m_clear`] field of [`PyModuleDef`]. +/// +/// [`m_clear`]: https://docs.python.org/3/c-api/module.html#c.PyModuleDef.m_clear +/// [`PyModuleDef`]: https://docs.python.org/3/c-api/module.html#c.PyModuleDef +pub unsafe extern "C" fn module_state_clear(_module: *mut ffi::PyObject) -> c_int { + // Should any PyObjects be made part of ModuleState or ModuleStateInner, + // these have to be Py_CLEARed here. + // See: examples/sequential/src/module.rs + 0 +} + +/// Called during deallocation of the module object. +/// +/// Used for the [`m_free`] field of [`PyModuleDef`]. +/// +/// [`m_free`]: https://docs.python.org/3/c-api/module.html#c.PyModuleDef.m_free +/// [`PyModuleDef`]: https://docs.python.org/3/c-api/module.html#c.PyModuleDef +pub unsafe extern "C" fn module_state_free(module: *mut c_void) { + let state: *mut ModuleState = ffi::PyModule_GetState(module.cast()).cast(); + (*state).inner.map(drop); +}