Skip to content

Commit

Permalink
impl: add module pymodule_state
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
Aequitosh committed Jul 18, 2024
1 parent 5ac5cef commit 22ee7ad
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/impl_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
92 changes: 92 additions & 0 deletions src/impl_/pymodule_state.rs
Original file line number Diff line number Diff line change
@@ -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<NonNull<ModuleStateImpl>>,
}

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);
}

0 comments on commit 22ee7ad

Please sign in to comment.