use std::sync::OnceLock;
pub(crate) fn release_journal_pages_enabled() -> bool {
static ENABLED: OnceLock<bool> = OnceLock::new();
*ENABLED.get_or_init(|| {
std::env::var("HMS_RELEASE_JOURNAL_PAGES").map_or(true, |value| {
!matches!(
value.trim().to_ascii_lowercase().as_str(),
"0" | "false" | "off" | "no"
)
})
})
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
fn page_size() -> usize {
extern "C" {
fn getpagesize() -> std::os::raw::c_int;
}
let size = unsafe { getpagesize() };
usize::try_from(size).unwrap_or(4096).max(1)
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
fn aligned(ptr: *const u8, len: usize) -> (*mut std::os::raw::c_void, usize) {
let page = page_size();
let start = (ptr as usize) & !(page - 1);
let len = len + (ptr as usize - start);
(start as *mut std::os::raw::c_void, len)
}
pub(crate) fn release_file_pages(ptr: *const u8, len: usize) {
if len == 0 {
return;
}
#[cfg(target_os = "linux")]
{
extern "C" {
fn madvise(
addr: *mut std::os::raw::c_void,
len: usize,
advice: std::os::raw::c_int,
) -> std::os::raw::c_int;
}
const MADV_DONTNEED: std::os::raw::c_int = 4;
let (addr, len) = aligned(ptr, len);
let _ = unsafe { madvise(addr, len, MADV_DONTNEED) };
}
#[cfg(target_os = "macos")]
{
extern "C" {
fn msync(
addr: *mut std::os::raw::c_void,
len: usize,
flags: std::os::raw::c_int,
) -> std::os::raw::c_int;
}
const MS_INVALIDATE: std::os::raw::c_int = 0x0002;
const MS_SYNC: std::os::raw::c_int = 0x0010;
let (addr, len) = aligned(ptr, len);
let _ = unsafe { msync(addr, len, MS_SYNC | MS_INVALIDATE) };
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
{
let _ = ptr;
}
}
pub(crate) fn release_free_heap() {
#[cfg(all(target_os = "linux", target_env = "gnu"))]
{
extern "C" {
fn malloc_trim(pad: usize) -> std::os::raw::c_int;
}
let _ = unsafe { malloc_trim(0) };
}
#[cfg(target_os = "macos")]
{
extern "C" {
fn malloc_zone_pressure_relief(zone: *mut std::os::raw::c_void, goal: usize) -> usize;
}
let _ = unsafe { malloc_zone_pressure_relief(std::ptr::null_mut(), 0) };
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn releasing_a_mapping_keeps_its_bytes_readable() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("pages.bin");
let pattern: Vec<u8> = (0..(256 * 1024)).map(|i| (i % 251) as u8).collect();
std::fs::write(&path, &pattern).unwrap();
let file = std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(&path)
.unwrap();
let mut map = unsafe { memmap2::MmapMut::map_mut(&file).unwrap() };
map[7] = 0xAB;
map.flush_range(0, 4096).unwrap();
release_file_pages(map.as_ptr(), map.len());
assert_eq!(map[7], 0xAB);
assert_eq!(&map[8..], &pattern[8..]);
release_file_pages(map[100..].as_ptr(), 10);
assert_eq!(map[100], pattern[100]);
drop(map);
assert_eq!(std::fs::read(&path).unwrap()[7], 0xAB);
release_free_heap();
}
}