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}