#![cfg(feature = "dhat-heap")]
#![allow(clippy::unwrap_used, clippy::expect_used)]
#[global_allocator]
static ALLOC: dhat::Alloc = dhat::Alloc;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use registry_io::SyncRegistry;
#[test]
fn sync_notify_hot_path_does_not_allocate() {
let _profiler = dhat::Profiler::builder().testing().build();
{
let registry: SyncRegistry<u64> = SyncRegistry::new();
for _ in 0..1_000 {
registry.notify(&1);
}
let before = dhat::HeapStats::get();
for _ in 0..100_000 {
registry.notify(&1);
}
let after = dhat::HeapStats::get();
let new_blocks = after.total_blocks.saturating_sub(before.total_blocks);
assert_eq!(
new_blocks, 0,
"empty notify() allocated {new_blocks} new blocks across 100k calls"
);
}
{
let registry: SyncRegistry<u64> = SyncRegistry::new();
let counter = Arc::new(AtomicU64::new(0));
for _ in 0..8 {
let sink = Arc::clone(&counter);
let _ = registry.register(move |value| {
let _ = sink.fetch_add(*value, Ordering::Relaxed);
});
}
for _ in 0..1_000 {
registry.notify(&1);
}
let before = dhat::HeapStats::get();
for _ in 0..100_000 {
registry.notify(&1);
}
let after = dhat::HeapStats::get();
let new_blocks = after.total_blocks.saturating_sub(before.total_blocks);
let new_bytes = after.total_bytes.saturating_sub(before.total_bytes);
assert_eq!(
new_blocks, 0,
"notify() with 8 handlers allocated {new_blocks} new blocks ({new_bytes} bytes) across 100k calls"
);
assert_eq!(
new_bytes, 0,
"notify() with 8 handlers touched {new_bytes} bytes across 100k calls; expected zero"
);
}
}