#![allow(clippy::std_instead_of_core, reason = "test code")]
#![allow(clippy::unwrap_used, reason = "test code")]
#![allow(clippy::clone_on_ref_ptr, reason = "test code")]
#![allow(clippy::undocumented_unsafe_blocks, reason = "test code")]
#![allow(clippy::large_stack_arrays, reason = "test code")]
#![allow(clippy::large_types_passed_by_value, reason = "test code")]
#![allow(
clippy::redundant_clone,
reason = "explicit clones in #[should_panic] tests keep the counter visible after the panic"
)]
use core::mem::MaybeUninit;
use std::sync::Arc as StdArc;
use std::sync::atomic::{AtomicUsize, Ordering};
use multitude::{Arc, Arena};
struct DropCounter(StdArc<AtomicUsize>);
impl Drop for DropCounter {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::Relaxed);
}
}
#[test]
fn alloc_box_of_maybeuninit_assume_init_drops_inner() {
let counter = StdArc::new(AtomicUsize::new(0));
{
let arena = Arena::new();
let b_uninit = arena.alloc_box(MaybeUninit::new(DropCounter(counter.clone())));
let b = unsafe { b_uninit.assume_init() };
drop(b);
}
assert_eq!(counter.load(Ordering::Relaxed), 1);
}
#[test]
fn alloc_arc_of_maybeuninit_assume_init_drops_inner() {
let counter = StdArc::new(AtomicUsize::new(0));
{
let arena = Arena::new();
let arc_uninit = arena.alloc_arc(MaybeUninit::new(DropCounter(counter.clone())));
let arc = unsafe { arc_uninit.assume_init() };
drop(arc);
}
assert_eq!(counter.load(Ordering::Relaxed), 1);
}
#[test]
fn alloc_uninit_arc_assume_init_drops_inner() {
let counter = StdArc::new(AtomicUsize::new(0));
{
let arena = Arena::new();
let arc_uninit = arena.alloc_uninit_arc::<DropCounter>();
unsafe {
Arc::as_ptr(&arc_uninit)
.cast_mut()
.write(MaybeUninit::new(DropCounter(counter.clone())));
}
let arc = unsafe { arc_uninit.assume_init() };
drop(arc);
}
assert_eq!(counter.load(Ordering::Relaxed), 1);
}
#[test]
fn arc_concurrent_assume_init_no_race() {
let arena = Arena::new();
let counter = StdArc::new(AtomicUsize::new(0));
let a = arena.alloc_uninit_arc::<DropCounter>();
unsafe {
Arc::as_ptr(&a).cast_mut().write(MaybeUninit::new(DropCounter(counter.clone())));
}
let b = a.clone();
let h1 = std::thread::spawn(move || {
let _x = unsafe { a.assume_init() };
});
let h2 = std::thread::spawn(move || {
let _y = unsafe { b.assume_init() };
});
h1.join().unwrap();
h2.join().unwrap();
drop(arena);
assert_eq!(
counter.load(Ordering::Relaxed),
1,
"DropCounter::drop must run exactly once across both clones"
);
}
#[cfg(not(utc_backend))]
#[test]
fn alloc_slice_ref_accepts_half_chunk_alignment_for_non_drop() {
#[repr(align(32768))]
#[derive(Clone, Copy)]
struct Wide;
let arena = Arena::new();
let s = arena.alloc_slice_fill_with::<Wide, _>(1, |_| Wide);
assert_eq!(s.len(), 1);
let src: &[Wide] = &[Wide];
let c = arena.alloc_slice_clone::<Wide>(src);
assert_eq!(c.len(), 1);
}
#[test]
fn zst_shared_handouts_advance_cursor() {
let arena = Arena::new();
let a = arena.alloc_arc(());
let b = arena.alloc_arc(());
let c = arena.alloc_arc(());
assert_ne!(a.as_ptr(), b.as_ptr(), "ZST Arc handouts must get distinct addresses");
assert_ne!(b.as_ptr(), c.as_ptr(), "ZST Arc handouts must get distinct addresses");
let bx1 = arena.alloc_box(());
let bx2 = arena.alloc_box(());
assert_ne!(bx1.as_ptr(), bx2.as_ptr(), "ZST Box handouts must get distinct addresses");
for _ in 0..600 {
drop(arena.alloc_arc(()));
drop(arena.alloc_box(()));
}
let z = arena.alloc_arc(());
let _ = arena.alloc_arc(7_u64);
assert!(!z.as_ptr().is_null());
drop(arena);
}