use ffi_support::{ConcurrentHandleMap, ExternError};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
fn with_error<F: FnOnce(&mut ExternError) -> T, T>(callback: F) -> T {
let mut e = ExternError::success();
let result = callback(&mut e);
if let Some(m) = unsafe { e.get_and_consume_message() } {
panic!("unexpected error: {}", m);
}
result
}
struct DropChecking {
counter: Arc<AtomicUsize>,
id: usize,
}
impl Drop for DropChecking {
fn drop(&mut self) {
let val = self.counter.fetch_add(1, Ordering::SeqCst);
log::debug!("Dropped {} :: {}", self.id, val);
}
}
#[test]
fn test_concurrent_drop() {
use rand::prelude::*;
use rayon::prelude::*;
let _ = env_logger::try_init();
let drop_counter = Arc::new(AtomicUsize::new(0));
let id = Arc::new(AtomicUsize::new(1));
let map = ConcurrentHandleMap::new();
let count = 1000;
let mut handles = (0..count)
.into_par_iter()
.map(|_| {
let id = id.fetch_add(1, Ordering::SeqCst);
let handle = with_error(|e| {
map.insert_with_output(e, || {
log::debug!("Created {}", id);
DropChecking {
counter: drop_counter.clone(),
id,
}
})
});
(id, handle)
})
.collect::<Vec<_>>();
handles.shuffle(&mut thread_rng());
assert_eq!(drop_counter.load(Ordering::SeqCst), 0);
handles.par_iter().for_each(|(id, h)| {
with_error(|e| {
map.call_with_output(e, *h, |val| {
assert_eq!(val.id, *id);
})
});
});
assert_eq!(drop_counter.load(Ordering::SeqCst), 0);
handles.par_iter().for_each(|(id, h)| {
with_error(|e| {
map.call_with_output(e, *h, |val| {
assert_eq!(val.id, *id);
})
});
});
handles.par_iter().for_each(|(id, h)| {
let item = map
.remove_u64(*h)
.expect("remove to succeed")
.expect("item to exist");
assert_eq!(item.id, *id);
let h = map.insert(item).into_u64();
map.delete_u64(h).expect("delete to succeed");
});
assert_eq!(drop_counter.load(Ordering::SeqCst), count);
}