#![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);
}
}
#[cfg(not(miri))]
#[test]
#[should_panic(expected = "no drop entry reserved")]
fn alloc_rc_of_maybeuninit_assume_init_panics_when_unsupported() {
let counter = StdArc::new(AtomicUsize::new(0));
let arena = Arena::new();
let rc_uninit = arena.alloc_rc(MaybeUninit::new(DropCounter(counter.clone())));
let _rc = unsafe { rc_uninit.assume_init() };
}
#[test]
fn alloc_uninit_rc_assume_init_drops_inner() {
let counter = StdArc::new(AtomicUsize::new(0));
{
let arena = Arena::new();
let rc_uninit = arena.alloc_uninit_rc::<DropCounter>();
unsafe {
multitude::Rc::as_ptr(&rc_uninit)
.cast_mut()
.write(MaybeUninit::new(DropCounter(counter.clone())));
}
let rc = unsafe { rc_uninit.assume_init() };
drop(rc);
}
assert_eq!(counter.load(Ordering::Relaxed), 1);
}
#[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);
}
#[cfg(not(miri))]
#[test]
#[should_panic(expected = "no drop entry reserved")]
fn alloc_arc_of_maybeuninit_assume_init_panics_when_unsupported() {
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() };
}
#[cfg(not(miri))]
#[test]
#[should_panic(expected = "no drop entry reserved")]
fn box_into_rc_panics_when_drop_entry_missing() {
let counter = StdArc::new(AtomicUsize::new(0));
let arena = Arena::new();
let b = arena.alloc_box(MaybeUninit::new(DropCounter(counter.clone())));
let b = unsafe { b.assume_init() };
let _rc = b.into_rc();
}
#[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"
);
}
#[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);
}