#[cfg(all(target_os = "linux", target_env = "gnu"))]
const TRIM_BYTES: u64 = 32 << 20;
#[cfg(all(target_os = "linux", target_env = "gnu"))]
const TAIL_BYTES: u64 = 1 << 20;
#[cfg(all(target_os = "linux", target_env = "gnu"))]
const MMAP_THRESHOLD: i32 = 256 << 10;
#[cfg(all(target_os = "linux", target_env = "gnu"))]
const TRIM_THRESHOLD: i32 = 1 << 20;
pub(crate) fn tune() {
#[cfg(all(target_os = "linux", target_env = "gnu"))]
imp::tune();
}
pub(crate) fn note_freed(bytes: u64) {
#[cfg(all(target_os = "linux", target_env = "gnu"))]
imp::note_freed(bytes);
#[cfg(not(all(target_os = "linux", target_env = "gnu")))]
let _ = bytes;
}
#[cfg(all(target_os = "linux", target_env = "gnu"))]
mod imp {
use super::{MMAP_THRESHOLD, TAIL_BYTES, TRIM_BYTES, TRIM_THRESHOLD};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Condvar, Mutex, OnceLock};
use std::time::{Duration, Instant};
static FREED: AtomicU64 = AtomicU64::new(0);
struct Trimmer {
due: Mutex<bool>,
wake: Condvar,
}
fn trimmer() -> Option<&'static Trimmer> {
static T: OnceLock<Option<&'static Trimmer>> = OnceLock::new();
*T.get_or_init(|| {
let t: &'static Trimmer = Box::leak(Box::new(Trimmer {
due: Mutex::new(false),
wake: Condvar::new(),
}));
std::thread::Builder::new()
.name("fusee-trim".into())
.spawn(move || run(t))
.ok()
.map(|_| t)
})
}
fn run(t: &'static Trimmer) {
let mut last: Option<Instant> = None;
loop {
{
let mut due = t.due.lock().unwrap_or_else(|e| e.into_inner());
while !*due {
due = t.wake.wait(due).unwrap_or_else(|e| e.into_inner());
}
*due = false;
}
let wait = last.map_or(Duration::from_millis(200), |l| {
Duration::from_secs(1).saturating_sub(l.elapsed())
});
std::thread::sleep(wait.max(Duration::from_millis(200)));
loop {
FREED.store(0, Ordering::Relaxed);
unsafe {
libc::malloc_trim(0);
}
std::thread::sleep(Duration::from_secs(1));
if FREED.load(Ordering::Relaxed) < TAIL_BYTES {
break;
}
}
for secs in [2, 4, 8] {
std::thread::sleep(Duration::from_secs(secs));
unsafe {
libc::malloc_trim(0);
}
}
last = Some(Instant::now());
}
}
pub(super) fn tune() {
static ONCE: std::sync::Once = std::sync::Once::new();
ONCE.call_once(|| {
let set = |name: &str| std::env::var_os(name).is_some();
if set("MALLOC_MMAP_THRESHOLD_") || set("MALLOC_TRIM_THRESHOLD_") {
return;
}
unsafe {
libc::mallopt(libc::M_MMAP_THRESHOLD, MMAP_THRESHOLD);
libc::mallopt(libc::M_TRIM_THRESHOLD, TRIM_THRESHOLD);
}
});
}
pub(super) fn note_freed(bytes: u64) {
if bytes == 0 {
return;
}
let total = FREED.fetch_add(bytes, Ordering::Relaxed) + bytes;
if total >= TRIM_BYTES
&& let Some(t) = trimmer()
{
*t.due.lock().unwrap_or_else(|e| e.into_inner()) = true;
t.wake.notify_one();
}
}
}