use sefer_region::{Handle, Region};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
#[path = "common/mod.rs"]
mod common;
#[derive(Debug, Clone)]
struct PanicDropCounter {
id: usize,
bomb_id: usize, drop_count: Arc<AtomicUsize>,
}
impl PanicDropCounter {
fn new(id: usize, bomb_id: usize, drop_count: Arc<AtomicUsize>) -> Self {
Self {
id,
bomb_id,
drop_count,
}
}
}
impl Drop for PanicDropCounter {
fn drop(&mut self) {
self.drop_count.fetch_add(1, Ordering::SeqCst);
if self.id == self.bomb_id && !std::thread::panicking() {
panic!("intentional drop panic in PanicDropCounter id={}", self.id);
}
}
}
#[test]
fn region_clear_partial_under_panic() {
let drop_count = Arc::new(AtomicUsize::new(0));
let mut r: Region<PanicDropCounter> = Region::new();
let bomb_id = 2; let mut handles: Vec<Handle<PanicDropCounter>> = Vec::new();
for i in 0..5 {
let counter = PanicDropCounter::new(i, bomb_id, Arc::clone(&drop_count));
handles.push(r.insert(counter));
}
for (i, &h) in handles.iter().enumerate() {
assert!(
r.get(h).is_some(),
"handle {} should resolve before clear",
i
);
}
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
r.clear();
}));
assert!(
result.is_err(),
"clear() should have panicked due to Drop panic"
);
assert_eq!(
drop_count.load(Ordering::SeqCst) + r.len(),
5,
"drops + survivors must equal total constructions (5)"
);
let survivor_ids: std::collections::HashSet<usize> =
r.iter().map(|counter| counter.id).collect();
assert!(
!survivor_ids.contains(&bomb_id),
"bomb id {} should have been dropped during partial clear",
bomb_id
);
for &survivor_id in &survivor_ids {
let handle = &handles[survivor_id];
assert!(
r.get(*handle).is_some(),
"survivor id {} should resolve correctly",
survivor_id
);
assert_eq!(r.get(*handle).map(|c| c.id), Some(survivor_id));
}
for (i, handle) in handles.iter().enumerate() {
if !survivor_ids.contains(&i) {
assert!(
r.get(*handle).is_none(),
"dropped id {} should not resolve",
i
);
}
}
let new_counter = PanicDropCounter::new(10, 999, Arc::clone(&drop_count));
let h_new = r.insert(new_counter);
assert_eq!(r.get(h_new).map(|c| c.id), Some(10));
assert!(!r.is_empty(), "region should have at least the new value");
drop(r);
assert_eq!(
drop_count.load(Ordering::SeqCst),
6,
"total drops should equal total constructions (6)"
);
}
#[cfg(feature = "std")]
mod sync_tests {
use super::*;
use sefer_region::SyncRegion;
use std::sync::Arc;
#[test]
fn sync_region_clear_partial_under_panic() {
let drop_count = Arc::new(AtomicUsize::new(0));
let sr: Arc<SyncRegion<PanicDropCounter>> = Arc::new(SyncRegion::new());
let bomb_id = 2; let mut handles: Vec<Handle<PanicDropCounter>> = Vec::new();
for i in 0..5 {
let counter = PanicDropCounter::new(i, bomb_id, Arc::clone(&drop_count));
handles.push(sr.insert(counter));
}
for (i, &h) in handles.iter().enumerate() {
assert!(
sr.read().get(h).is_some(),
"handle {} should resolve before clear",
i
);
}
let sr_clone = Arc::clone(&sr);
let join = std::thread::spawn(move || {
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
sr_clone.clear();
}))
});
let join_result = join.join();
assert!(join_result.is_ok(), "thread should have completed");
let clear_result = join_result.unwrap();
assert!(
clear_result.is_err(),
"clear() should have panicked due to Drop panic"
);
assert_eq!(
drop_count.load(Ordering::SeqCst) + sr.len(),
5,
"drops + survivors must equal total constructions (5)"
);
let survivor_ids: std::collections::HashSet<usize> =
sr.read().iter().map(|counter| counter.id).collect();
assert!(
!survivor_ids.contains(&bomb_id),
"bomb id {} should have been dropped during partial clear",
bomb_id
);
for &survivor_id in &survivor_ids {
let handle = &handles[survivor_id];
assert!(
sr.read().get(*handle).is_some(),
"survivor id {} should resolve correctly",
survivor_id
);
assert_eq!(sr.read().get(*handle).map(|c| c.id), Some(survivor_id));
}
for (i, handle) in handles.iter().enumerate() {
if !survivor_ids.contains(&i) {
assert!(
sr.read().get(*handle).is_none(),
"dropped id {} should not resolve",
i
);
}
}
let new_counter = PanicDropCounter::new(10, 999, Arc::clone(&drop_count));
let h_new = sr.insert(new_counter);
assert_eq!(sr.read().get(h_new).map(|c| c.id), Some(10));
assert!(
!sr.is_empty(),
"SyncRegion should have at least the new value"
);
sr.clear();
assert_eq!(sr.len(), 0, "second clear() should empty the region");
assert_eq!(
drop_count.load(Ordering::SeqCst),
6,
"total drops should equal total constructions (6)"
);
drop(sr);
assert_eq!(
drop_count.load(Ordering::SeqCst),
6,
"total drops should equal total constructions (6)"
);
}
}