Skip to main content

mnemosyne_backend/
mapping.rs

1//! Page-level OS allocation and release for the `MemoryBackendWrapper`.
2//!
3//! Owns the [`MemoryBackendWrapper`] struct, the capability consts that
4//! forward the platform-conditional [`crate::DefaultBackend`] feature
5//! surface, and the single central
6//! `impl MemoryBackend for MemoryBackendWrapper` block. Per-method
7//! bodies delegate to per-concern helpers:
8//!
9//! - `do_allocate`/`do_deallocate` (mapping concern) live here.
10//! - `crate::guard::do_make_guard` handles `make_guard`.
11//! - `crate::reset::do_page_reset` / `crate::reset::do_decommit`
12//!   handle `page_reset` / `decommit`.
13//!
14//! Rust's trait coherence rule keeps the `impl` block in one file; the
15//! `#[inline(always)]` glue keeps the per-method delegation statically
16//! dispatched with no vtable or heap allocation. Benchmark threshold
17//! gates are the empirical evidence for non-regression.
18
19use crate::DefaultBackend;
20use crate::recorders::{record_map, record_unmap, record_unmap_failure};
21use mnemosyne_core::MemoryBackend;
22
23/// High-level OS page mapping backend helper. Owns the wrapper struct
24/// shape and forwards the platform-conditional capability consts from
25/// [`crate::DefaultBackend`]; per-method bodies delegate to
26/// per-concern helpers in [`crate::guard`] and [`crate::reset`].
27pub struct MemoryBackendWrapper;
28
29/// Performs the allocate-side work for [`MemoryBackendWrapper`]:
30/// delegate the platform call to `B`, then forward the confirmed
31/// mapping to [`crate::recorders::record_map`].
32///
33/// `#[inline(always)]` keeps this wrapper statically dispatched at
34/// the call site.
35#[inline(always)]
36pub(crate) fn do_allocate<B: MemoryBackend>(size: usize) -> *mut u8 {
37    // SAFETY: forwarded to the platform backend; the size contract
38    // (page-aligned, non-zero) is upheld by the trait-level safety
39    // expectation on `allocate`.
40    let ptr = unsafe { B::allocate(size) };
41    if !ptr.is_null() {
42        record_map(size);
43    }
44    ptr
45}
46
47/// Performs the deallocate-side work for [`MemoryBackendWrapper`]:
48/// delegate the platform call to `B`, then route the outcome to
49/// [`crate::recorders::record_unmap`] on a confirmed release or
50/// [`crate::recorders::record_unmap_failure`] on a failed release
51/// (so `current_mapped_bytes` stays consistent with the live mapping
52/// set).
53///
54/// `#[inline(always)]` keeps this wrapper statically dispatched at
55/// the call site.
56#[inline(always)]
57pub(crate) fn do_deallocate<B: MemoryBackend>(ptr: *mut u8, size: usize) -> bool {
58    if ptr.is_null() {
59        return false;
60    }
61    let released = {
62        // SAFETY: `ptr` is non-null per the guard above; `size` matches the
63        // original allocation by the caller's invariant (same contract as
64        // `B::deallocate`). The OS mapping is released; `ptr` must not be
65        // dereferenced after this point.
66        unsafe { B::deallocate(ptr, size) }
67    };
68    if released {
69        record_unmap(size);
70    } else {
71        record_unmap_failure();
72    }
73    released
74}
75
76impl MemoryBackend for MemoryBackendWrapper {
77    const SUPPORTS_PAGE_RESET: bool = DefaultBackend::SUPPORTS_PAGE_RESET;
78    const SUPPORTS_MAKE_GUARD: bool = DefaultBackend::SUPPORTS_MAKE_GUARD;
79    const SUPPORTS_DECOMMIT: bool = DefaultBackend::SUPPORTS_DECOMMIT;
80    const ENABLE_CPU_CACHE: bool = DefaultBackend::ENABLE_CPU_CACHE;
81
82    /// Allocates memory from the OS.
83    ///
84    /// # Safety
85    ///
86    /// Size must be greater than zero and page-aligned.
87    #[inline(always)]
88    unsafe fn allocate(size: usize) -> *mut u8 {
89        do_allocate::<DefaultBackend>(size)
90    }
91
92    /// Releases memory to the OS.
93    ///
94    /// # Safety
95    ///
96    /// The ptr must be valid and size must match the allocated size.
97    #[inline(always)]
98    unsafe fn deallocate(ptr: *mut u8, size: usize) -> bool {
99        do_deallocate::<DefaultBackend>(ptr, size)
100    }
101
102    /// Installs a `PROT_NONE` / `PAGE_NOACCESS` guard region.
103    ///
104    /// Delegates to `crate::guard::do_make_guard` which records the
105    /// confirmed install through `crate::recorders::record_guard_install`.
106    #[inline(always)]
107    unsafe fn make_guard(ptr: *mut u8, size: usize) -> bool {
108        crate::guard::do_make_guard::<DefaultBackend>(ptr, size)
109    }
110
111    /// Drops the physical backing of an idle page range while keeping
112    /// the virtual mapping committed.
113    ///
114    /// Delegates to `crate::reset::do_page_reset` which records the
115    /// confirmed reset through `crate::recorders::record_page_reset`.
116    #[inline(always)]
117    unsafe fn page_reset(ptr: *mut u8, size: usize) -> bool {
118        crate::reset::do_page_reset::<DefaultBackend>(ptr, size)
119    }
120
121    /// Releases the commit charge / physical backing of a page-aligned
122    /// range while keeping the reservation.
123    ///
124    /// Delegates to `crate::reset::do_decommit` which records the
125    /// confirmed decommit through `crate::recorders::record_decommit`.
126    #[inline(always)]
127    unsafe fn decommit(ptr: *mut u8, size: usize) -> bool {
128        crate::reset::do_decommit::<DefaultBackend>(ptr, size)
129    }
130}