use crate::recorders::{record_decommit, record_page_reset};
use mnemosyne_core::MemoryBackend;
#[inline(always)]
pub(crate) fn do_page_reset<B: MemoryBackend>(ptr: *mut u8, size: usize) -> bool {
if ptr.is_null() || size == 0 {
return false;
}
let reset = {
unsafe { B::page_reset(ptr, size) }
};
if reset {
record_page_reset(size);
}
reset
}
#[inline(always)]
pub(crate) fn do_decommit<B: MemoryBackend>(ptr: *mut u8, size: usize) -> bool {
if ptr.is_null() || size == 0 {
return false;
}
let decommitted = {
unsafe { B::decommit(ptr, size) }
};
if decommitted {
record_decommit(size);
}
decommitted
}
#[cfg(test)]
mod tests {
extern crate std;
use crate::mapping::MemoryBackendWrapper;
use crate::recorders::backend_memory_stats;
use crate::test_support::lock_test;
use mnemosyne_core::MemoryBackend;
#[test]
fn wrapper_page_reset_round_trips_on_active_mapping() {
let _guard = lock_test();
let size = 64 * 1024;
let ptr = unsafe { MemoryBackendWrapper::allocate(size) };
assert!(!ptr.is_null());
let stats_before = backend_memory_stats();
let reset = unsafe { MemoryBackendWrapper::page_reset(ptr, size) };
let stats_after = backend_memory_stats();
if reset {
assert_eq!(
stats_after.page_reset_calls,
stats_before.page_reset_calls + 1
);
assert_eq!(
stats_after.page_reset_bytes,
stats_before.page_reset_bytes + size
);
}
assert_eq!(
stats_after.current_mapped_bytes, stats_before.current_mapped_bytes,
"page_reset must never alter current_mapped_bytes"
);
unsafe {
ptr.write_volatile(0xCC);
assert_eq!(ptr.read_volatile(), 0xCC);
}
let released = unsafe { MemoryBackendWrapper::deallocate(ptr, size) };
assert!(released);
}
#[test]
fn wrapper_page_reset_rejects_null_and_zero() {
let _guard = lock_test();
let null_reset = unsafe { MemoryBackendWrapper::page_reset(core::ptr::null_mut(), 4096) };
assert!(!null_reset, "null pointer must not be accepted for reset");
let ptr = unsafe { MemoryBackendWrapper::allocate(4096) };
assert!(!ptr.is_null());
let zero_reset = unsafe { MemoryBackendWrapper::page_reset(ptr, 0) };
assert!(!zero_reset, "zero-size reset must not be accepted");
let _ = unsafe { MemoryBackendWrapper::deallocate(ptr, 4096) };
}
#[test]
fn wrapper_decommit_returns_slack_and_keeps_reservation_releasable() {
let _guard = lock_test();
let size = 128 * 1024;
let ptr = unsafe { MemoryBackendWrapper::allocate(size) };
assert!(!ptr.is_null());
let half = size / 2;
let tail = unsafe { ptr.add(half) };
let before = backend_memory_stats();
let decommitted = unsafe { MemoryBackendWrapper::decommit(tail, half) };
let after = backend_memory_stats();
if decommitted {
assert_eq!(after.decommit_calls, before.decommit_calls + 1);
assert_eq!(after.decommit_bytes, before.decommit_bytes + half);
}
assert_eq!(
after.current_mapped_bytes, before.current_mapped_bytes,
"decommit must never alter current_mapped_bytes"
);
unsafe {
ptr.write_volatile(0x5A);
assert_eq!(ptr.read_volatile(), 0x5A);
}
let released = unsafe { MemoryBackendWrapper::deallocate(ptr, size) };
assert!(released, "release failed after decommitting a subrange");
}
}