Skip to main content

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}