use std::alloc::{GlobalAlloc, Layout, System};
use std::cell::Cell;
use std::sync::atomic::{AtomicI64, Ordering};
#[derive(Debug, Clone, Copy, Default)]
pub struct Counting;
static TOTAL: AtomicI64 = AtomicI64::new(0);
static CALLS: AtomicI64 = AtomicI64::new(0);
static LIVE: AtomicI64 = AtomicI64::new(0);
thread_local! {
static LOCAL: Cell<i64> = const { Cell::new(0) };
static PEAK: Cell<i64> = const { Cell::new(0) };
}
fn add(n: i64) {
TOTAL.fetch_add(n, Ordering::Relaxed);
if n > 0 {
CALLS.fetch_add(1, Ordering::Relaxed);
}
let _ = LOCAL.try_with(|c| {
let v = c.get() + n;
c.set(v);
let _ = PEAK.try_with(|p| p.set(p.get().max(v)));
});
}
unsafe impl GlobalAlloc for Counting {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let p = unsafe { System.alloc(layout) };
if !p.is_null() {
add(layout.size() as i64);
LIVE.fetch_add(1, Ordering::Relaxed);
}
p
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
let p = unsafe { System.alloc_zeroed(layout) };
if !p.is_null() {
add(layout.size() as i64);
LIVE.fetch_add(1, Ordering::Relaxed);
}
p
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { System.dealloc(ptr, layout) };
add(-(layout.size() as i64));
LIVE.fetch_sub(1, Ordering::Relaxed);
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
let p = unsafe { System.realloc(ptr, layout, new_size) };
if !p.is_null() {
add(new_size as i64 - layout.size() as i64);
}
p
}
}
pub fn total_bytes() -> i64 {
TOTAL.load(Ordering::Relaxed)
}
pub fn live_allocations() -> i64 {
LIVE.load(Ordering::Relaxed)
}
pub fn allocation_calls() -> i64 {
CALLS.load(Ordering::Relaxed)
}
pub fn thread_bytes() -> i64 {
LOCAL.try_with(|c| c.get()).unwrap_or(0)
}
pub fn thread_peak_bytes() -> i64 {
PEAK.try_with(|c| c.get()).unwrap_or(0)
}
pub fn reset_thread_peak() {
let now = thread_bytes();
let _ = PEAK.try_with(|c| c.set(now));
}
#[cfg(all(target_os = "linux", target_env = "gnu"))]
extern "C" {
fn malloc_trim(pad: usize) -> core::ffi::c_int;
}
pub fn release_free_memory() {
#[cfg(all(target_os = "linux", target_env = "gnu"))]
unsafe {
malloc_trim(0);
}
}