use std::alloc::{GlobalAlloc, Layout};
use std::cell::Cell;
use std::hint::black_box;
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
use rusty_alloc_api::RustyAlloc;
static CALLS: AtomicUsize = AtomicUsize::new(0);
static REENTRANT: AtomicUsize = AtomicUsize::new(0);
thread_local! {
static DEPTH: Cell<u32> = const { Cell::new(0) };
}
struct Counting;
impl Counting {
fn enter() -> bool {
CALLS.fetch_add(1, Relaxed);
let nested = DEPTH.with(|d| {
let n = d.get();
d.set(n + 1);
n > 0
});
if nested {
REENTRANT.fetch_add(1, Relaxed);
}
nested
}
fn leave() {
DEPTH.with(|d| d.set(d.get() - 1));
}
}
unsafe impl GlobalAlloc for Counting {
unsafe fn alloc(&self, l: Layout) -> *mut u8 {
Counting::enter();
let p = unsafe { RustyAlloc.alloc(l) };
Counting::leave();
p
}
unsafe fn dealloc(&self, p: *mut u8, l: Layout) {
Counting::enter();
unsafe { RustyAlloc.dealloc(p, l) };
Counting::leave();
}
unsafe fn alloc_zeroed(&self, l: Layout) -> *mut u8 {
Counting::enter();
let p = unsafe { RustyAlloc.alloc_zeroed(l) };
Counting::leave();
p
}
unsafe fn realloc(&self, p: *mut u8, l: Layout, n: usize) -> *mut u8 {
Counting::enter();
let q = unsafe { RustyAlloc.realloc(p, l, n) };
Counting::leave();
q
}
}
#[global_allocator]
static A: Counting = Counting;
#[test]
fn the_allocator_never_re_enters_the_global_allocator() {
let small = black_box(vec![1u8; 64]);
let medium = black_box(vec![2u8; 8 << 10]);
let large = black_box(vec![3u8; 300 << 10]);
#[cfg(not(miri))]
let huge = black_box(vec![4u8; 33 << 20]);
let mut grown: Vec<u64> = Vec::with_capacity(8);
grown.extend(0..(1 << 16));
let from_worker = std::thread::spawn(|| black_box(vec![5u8; 4096]))
.join()
.expect("worker");
drop((small, medium, large, black_box(grown), from_worker));
#[cfg(not(miri))]
drop(huge);
let calls = CALLS.load(Relaxed);
let reentrant = REENTRANT.load(Relaxed);
println!("global allocator calls so far: {calls}, re-entrant: {reentrant}");
assert!(
calls > 0,
"the counting wrapper is not the global allocator"
);
assert_eq!(
reentrant, 0,
"rusty_alloc allocated through the global allocator {reentrant} times \
while serving a request — the start-up options pass used to do this \
251 times (docs/plans/youslowbro.md §4)"
);
}