use std::cell::Cell;
use std::hint::black_box;
use std::sync::{Mutex, MutexGuard, PoisonError};
use stats_alloc::{INSTRUMENTED_SYSTEM, Region};
static SERIAL: Mutex<()> = Mutex::new(());
thread_local! {
static HELD: Cell<bool> = const { Cell::new(false) };
}
pub struct Serial(#[allow(dead_code)] MutexGuard<'static, ()>);
impl Drop for Serial {
fn drop(&mut self) {
HELD.with(|held| held.set(false));
}
}
#[must_use = "the lock is released when the guard drops"]
pub fn serial() -> Serial {
let guard = SERIAL.lock().unwrap_or_else(PoisonError::into_inner);
HELD.with(|held| held.set(true));
Serial(guard)
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Allocs {
pub count: usize,
pub bytes: usize,
}
pub fn count<R>(f: impl FnOnce() -> R) -> (R, Allocs) {
assert!(HELD.with(Cell::get), "allocs::count needs `let _serial = allocs::serial();` at the top of the test");
assert_installed();
let region = Region::new(&INSTRUMENTED_SYSTEM);
let r = f();
let s = region.change();
let grown = usize::try_from(s.bytes_reallocated).unwrap_or(0);
(r, Allocs { count: s.allocations + s.reallocations, bytes: s.bytes_allocated + grown })
}
fn assert_installed() {
let region = Region::new(&INSTRUMENTED_SYSTEM);
black_box(Vec::<u8>::with_capacity(black_box(16)));
assert!(
region.change().allocations > 0,
"allocs::count needs `#[global_allocator] static GLOBAL: &StatsAlloc<System> = &INSTRUMENTED_SYSTEM;` in the test binary"
);
}