use parking_lot::RwLock;
use std::{
sync::{Arc, Barrier},
thread,
};
const THREADS: usize = 8;
const ITERS: usize = 10_000;
slotmap::new_key_type! {
struct Id;
}
type Handle = refslots::Handle<Id>;
type RegistryCell = Arc<RwLock<refslots::Registry<Id>>>;
#[test]
fn clone_upgrade_drop() {
let registry = RegistryCell::default();
let primary = Handle::insert(®istry, ());
let key = primary.key();
let weak = primary.downgrade();
thread::scope(|s| {
for _ in 0..THREADS {
let weak = weak.clone();
let held = primary.clone(); s.spawn(move || {
for _ in 0..ITERS {
let strong = weak.upgrade().expect("slot is live");
let clone = strong.clone();
drop(strong);
drop(clone);
}
drop(held);
});
}
});
assert_eq!(registry.read().strong_count(key), Some(1));
assert_eq!(registry.write().take_released().count(), 0);
drop(primary);
assert!(registry.read().strong_count(key).is_none());
let released = registry.write().take_released().collect::<Vec<_>>();
assert_eq!(released, [key]);
assert!(weak.upgrade().is_none());
}
const RACE_ROUNDS: usize = 100;
#[test]
fn last_drop_upgrade_race() {
for _ in 0..RACE_ROUNDS {
let registry = RegistryCell::default();
let primary = Handle::insert(®istry, ());
let key = primary.key();
let weak = primary.downgrade();
let barrier = Barrier::new(THREADS + 1);
thread::scope(|s| {
for _ in 0..THREADS {
let weak = weak.clone();
let barrier = &barrier;
s.spawn(move || {
barrier.wait();
for _ in 0..ITERS {
let Some(strong) = weak.upgrade() else { continue };
assert_eq!(strong.key(), key);
drop(strong);
}
});
}
barrier.wait();
drop(primary); });
assert!(registry.read().strong_count(key).is_none());
let released = registry.write().take_released().collect::<Vec<_>>();
assert_eq!(released, [key]);
assert!(weak.upgrade().is_none());
}
}