use rusty_alloc::init::{self, HeapBox};
use rusty_alloc::{alloc, arena, options};
unsafe fn hmalloc(hb: *mut HeapBox, n: usize) -> *mut u8 {
unsafe { alloc::heap_malloc(hb, n) }
}
#[test]
fn heaps_arenas_subprocs_options() {
let h1 = init::create_heap(7, false, -1);
let mut blocks = Vec::new();
unsafe {
for i in 0..500usize {
let p = hmalloc(h1, 32 + (i % 900));
assert!(!p.is_null());
core::ptr::write_bytes(p, 0x77, 32 + (i % 900));
blocks.push((p, 32 + (i % 900)));
}
for &(p, _) in blocks.iter().take(100) {
alloc::free(p);
}
let mut seen = 0usize;
let ok = (*(*h1).heap.get()).visit_blocks(true, &mut |_area, block, _sz| {
if !block.is_null() {
seen += 1;
}
true
});
assert!(ok);
assert_eq!(seen, 400, "visitor missed blocks");
let (probe, _) = blocks[200];
assert!(alloc::heap_contains_block(h1, probe));
assert!(alloc::heap_check_owned(h1, probe));
let foreign = alloc::malloc(64);
assert!(!alloc::heap_contains_block(h1, foreign));
alloc::free(foreign);
init::heap_delete(h1);
for &(p, n) in blocks.iter().skip(100) {
assert_eq!(p.read(), 0x77);
assert_eq!(p.add(n - 1).read(), 0x77);
alloc::free(p); }
}
let h2 = init::create_heap(0, true, -1);
unsafe {
for i in 0..300usize {
let p = hmalloc(h2, 64 + i * 7 % 2000);
assert!(!p.is_null());
}
init::heap_destroy(h2);
}
let sanity = alloc::malloc(128);
assert!(!sanity.is_null());
unsafe { alloc::free(sanity) };
let h3 = init::create_heap(0, true, -1);
unsafe {
let prev = init::set_default_heap(h3);
let p = alloc::malloc(64); assert!(alloc::heap_contains_block(h3, p));
alloc::free(p);
init::set_default_heap(prev);
assert_eq!(init::backing_heap(), prev);
init::heap_destroy(h3);
}
let arena_id = arena::reserve_os_memory_ex(64 * 1024 * 1024, true, false, true)
.expect("arena reserve failed");
let (abase, asize) = arena::arena_area(arena_id);
assert!(!abase.is_null() && asize == 64 * 1024 * 1024);
let ha = init::create_heap(0, true, arena_id);
unsafe {
let s0 = (*(*ha).heap.get()).stats.segments;
let p = hmalloc(ha, 100_000);
assert!(!p.is_null());
let addr = p.addr();
assert!(
addr >= abase.addr() && addr < abase.addr() + asize,
"exclusive-arena heap allocated outside its arena"
);
assert!((*(*ha).heap.get()).stats.segments > s0);
alloc::free(p);
init::heap_destroy(ha);
}
let hb2 = init::create_heap(0, true, arena_id);
unsafe {
let p = hmalloc(hb2, 4096);
assert!(!p.is_null());
assert!(p.addr() >= abase.addr() && p.addr() < abase.addr() + asize);
let z = alloc::heap_zalloc(hb2, 200_000);
for i in (0..200_000).step_by(4096) {
assert_eq!(z.add(i).read(), 0, "dirty arena chunk leaked at +{i}");
}
alloc::free(z);
alloc::free(p);
init::heap_destroy(hb2);
}
if cfg!(miri) {
return;
}
let sp = init::subproc_new();
let handle = std::thread::spawn(move || {
init::subproc_add_current_thread(sp);
let p = alloc::malloc(5000);
assert!(!p.is_null());
unsafe { core::ptr::write_bytes(p, 0x3C, 5000) };
p as usize
});
let leaked = handle.join().unwrap();
let before = alloc::stats().reclaims;
let churn: Vec<Vec<u8>> = (0..100).map(|_| vec![1u8; 40_000]).collect();
drop(churn);
let _ = before; let mut found = 0usize;
init::abandoned_visit_blocks(sp, -1, true, &mut |_a, b, _s| {
if !b.is_null() {
found += 1;
}
true
});
assert!(found >= 1, "abandoned block not visible in its subproc");
unsafe { alloc::free(leaked as *mut u8) };
assert_eq!(options::get(15), -1, "purge_delay default");
options::set(15, 0);
assert_eq!(options::get(15), 0);
assert!(
options::get_size(23) >= 1024 * 1024 * 1024,
"arena_reserve KiB scaling"
);
let merged = rusty_alloc::stats::merged();
assert!(merged.allocs > 0);
let (_, _, _, rss, ..) = rusty_alloc::stats::process_info();
assert!(rss > 0, "process_info rss");
}