#![cfg(feature = "clock")]
#![allow(clippy::unwrap_used)]
use std::alloc::{GlobalAlloc, Layout, System};
use std::cell::Cell;
use std::sync::Arc;
use std::time::Duration;
use better_bucket::Bucket;
use clock_lib::ManualClock;
thread_local! {
static ALLOCATIONS: Cell<u64> = const { Cell::new(0) };
}
#[inline]
fn note_alloc() {
ALLOCATIONS.with(|c| c.set(c.get() + 1));
}
fn alloc_count() -> u64 {
ALLOCATIONS.with(Cell::get)
}
struct Counting;
unsafe impl GlobalAlloc for Counting {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
note_alloc();
unsafe { System.alloc(layout) }
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { System.dealloc(ptr, layout) }
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
note_alloc();
unsafe { System.alloc_zeroed(layout) }
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
note_alloc();
unsafe { System.realloc(ptr, layout, new_size) }
}
}
#[global_allocator]
static ALLOCATOR: Counting = Counting;
#[test]
fn test_acquire_path_does_not_allocate() {
let clock = Arc::new(ManualClock::new());
let bucket = Bucket::per_second(1_000).with_clock(Arc::clone(&clock));
let mut warm = 0u64;
for _ in 0..2_000 {
if bucket.try_acquire(1) {
warm += 1;
}
let _ = bucket.acquire(1);
let _ = bucket.available();
clock.advance(Duration::from_millis(1));
}
assert!(warm > 0);
let before = alloc_count();
let mut granted = 0u64;
for _ in 0..100_000 {
if bucket.try_acquire(1) {
granted += 1;
}
let _ = bucket.acquire(1);
let _ = bucket.available();
clock.advance(Duration::from_millis(1));
}
let allocations = alloc_count() - before;
assert_eq!(
allocations, 0,
"acquire path allocated {allocations} time(s) on the measuring thread"
);
assert!(granted > 0); }