mnemosyne_core/lib.rs
1//! Core allocator types, constants, size classes, and synchronization primitives for Mnemosyne.
2
3#![no_std]
4#![deny(missing_docs)]
5
6extern crate alloc;
7
8#[cfg(any(feature = "std", test))]
9extern crate std;
10
11pub mod abort;
12pub mod constants;
13pub mod kernel_budget;
14pub mod loom_shim;
15pub mod options;
16pub mod os_tls;
17pub mod policy;
18pub mod size_class;
19pub mod sync;
20pub mod types;
21pub mod validation;
22
23pub use constants::*;
24pub use kernel_budget::{KernelResourceBudget, OccupancyLimits};
25pub use policy::{
26 AllocPolicy, HardenedPolicy, PolicyMarker, SecurePolicy, StandardPolicy, mitigations,
27};
28pub use size_class::*;
29pub use sync::*;
30pub use types::*;
31pub use validation::{is_valid_alloc_request, is_valid_layout_alloc_request};
32
33/// Trait defining the contract for low-level virtual memory mapping backends.
34pub trait MemoryBackend: Send + Sync + 'static {
35 /// Indicates whether the backend supports advisory page resetting.
36 const SUPPORTS_PAGE_RESET: bool = false;
37
38 /// Indicates whether the backend supports page protection guard installation.
39 const SUPPORTS_MAKE_GUARD: bool = false;
40
41 /// Indicates whether the backend supports releasing memory commitment while keeping the reservation.
42 const SUPPORTS_DECOMMIT: bool = false;
43
44 /// Indicates whether the backend enables the lock-free per-CPU block cache.
45 const ENABLE_CPU_CACHE: bool = false;
46
47 /// Allocates page-aligned memory from the OS.
48 ///
49 /// # Safety
50 ///
51 /// The size must be greater than zero and page-aligned.
52 unsafe fn allocate(size: usize) -> *mut u8;
53
54 /// Releases page-aligned memory back to the OS.
55 ///
56 /// Returns `true` when the OS confirmed the release, `false` when the
57 /// release call reported failure. Callers must defer telemetry that
58 /// observes "unmapped bytes" to a `true` outcome to keep the
59 /// `current_mapped_bytes` counter consistent with the live mapping set.
60 /// Cleanup-path callers that have no recovery action available for a
61 /// failed release must still bind the result with `let _released =` and
62 /// document why the leaked mapping is unrecoverable in that context.
63 ///
64 /// # Safety
65 ///
66 /// The ptr must be valid and size must match the allocated size.
67 #[must_use = "ignoring the release result drops the OS-failure signal; bind it to `_released` and document why no recovery is possible"]
68 unsafe fn deallocate(ptr: *mut u8, size: usize) -> bool;
69
70 /// Asks the OS to drop the physical backing of a mapped page range while
71 /// keeping the virtual address range reserved and accessible.
72 ///
73 /// Returns `true` when the OS confirmed the reset, `false` when the
74 /// backend either does not implement page-level reset, the call failed,
75 /// or the platform's reset semantics are too lax for this allocator's
76 /// purposes (for example, `MADV_FREE` on a backend that requires
77 /// observable zeroing).
78 ///
79 /// The reset is *advisory at the address-space level* — the mapping
80 /// remains readable and writable after the call. Subsequent reads may
81 /// return zeroed pages (Linux `MADV_DONTNEED`, Windows
82 /// `VirtualAlloc(MEM_RESET)` after touch) or the previous contents
83 /// until the next write (macOS `MADV_FREE`). Callers must therefore
84 /// treat the contents of the reset region as undefined.
85 ///
86 /// The default implementation returns `false` so a backend that has no
87 /// equivalent operation (such as the CUDA unified memory backend)
88 /// silently opts out without breaking the trait surface.
89 ///
90 /// # Safety
91 ///
92 /// `ptr` must be a system-page-aligned address inside an active mapping
93 /// from this backend, `size` must be a non-zero multiple of the system
94 /// page size, and `[ptr, ptr + size)` must lie entirely within a single
95 /// allocation returned by `allocate`. After a successful reset the
96 /// region may be re-faulted by the kernel; callers must not assume the
97 /// previous bytes are still present.
98 #[expect(unused_variables)]
99 unsafe fn page_reset(ptr: *mut u8, size: usize) -> bool {
100 false
101 }
102
103 /// Marks a page-aligned range as an inaccessible guard region.
104 ///
105 /// On Unix the implementation calls `mprotect(ptr, size, PROT_NONE)`;
106 /// on Windows it calls `VirtualProtect(ptr, size, PAGE_NOACCESS, _)`.
107 /// Either flavor leaves the address range mapped (so subsequent
108 /// `deallocate` calls still cover it) but raises a fault on any read
109 /// or write. The default implementation returns `false` so backends
110 /// without an equivalent operation silently opt out.
111 ///
112 /// Callers should treat the guard as one-way: there is no
113 /// corresponding "remove guard" operation in this trait. A backend
114 /// that needs to reuse the range must release the entire mapping via
115 /// `deallocate` and re-allocate.
116 ///
117 /// # Safety
118 ///
119 /// `ptr` must be a system-page-aligned address inside an active
120 /// mapping from this backend, `size` must be a non-zero multiple of
121 /// the system page size, and `[ptr, ptr + size)` must lie entirely
122 /// within a single allocation returned by `allocate`. After a
123 /// successful guard install, every read or write to the range raises
124 /// the platform's protection fault — callers must ensure no live
125 /// allocator data lives in the range.
126 #[expect(unused_variables)]
127 unsafe fn make_guard(ptr: *mut u8, size: usize) -> bool {
128 false
129 }
130
131 /// Releases the commit charge / physical backing of a page-aligned range
132 /// while keeping the surrounding reservation intact, so the range can still
133 /// be covered by the eventual `deallocate` of the base allocation.
134 ///
135 /// This differs from `page_reset`: `page_reset` keeps the range committed
136 /// (Windows `MEM_RESET` only discards contents; the pages still count
137 /// against the commit limit), whereas `decommit` actually releases the
138 /// commitment — Windows `VirtualFree(MEM_DECOMMIT)` drops the commit charge,
139 /// and Unix `madvise(MADV_DONTNEED)` drops the resident pages. It is used to
140 /// return the alignment slack that aligned segment/huge mappings reserve but
141 /// never touch (on Windows that slack is eagerly committed and would
142 /// otherwise hold ~`SEGMENT_ALIGN` of commit charge per mapping).
143 ///
144 /// The default implementation returns `false` so backends without an
145 /// equivalent operation silently opt out.
146 ///
147 /// # Safety
148 ///
149 /// `ptr` must be a system-page-aligned address inside an active mapping from
150 /// this backend, `size` must be a non-zero multiple of the system page size,
151 /// and `[ptr, ptr + size)` must lie entirely within a single allocation
152 /// returned by `allocate` **and** must hold no live allocator data — after a
153 /// successful decommit the range faults on access until re-committed or
154 /// released.
155 #[expect(unused_variables)]
156 unsafe fn decommit(ptr: *mut u8, size: usize) -> bool {
157 false
158 }
159}