use std::fmt;
use std::sync::{Arc, Weak, RwLock, atomic::{AtomicUsize, Ordering}};
#[derive(Debug)]
pub struct DropToken {
set: Weak<RwLock<Vec<Arc<DropState>>>>,
state: Arc<DropState>,
}
impl Drop for DropToken {
fn drop(&mut self) {
self.state.set_dropped();
}
}
impl Clone for DropToken {
fn clone(&self) -> Self {
let state = DropState::new();
if let Some(set) = self.set.upgrade() {
set.write().unwrap().push(Arc::clone(&state));
Self {
set: Arc::downgrade(&set),
state,
}
} else {
Self {
set: Weak::new(),
state,
}
}
}
}
pub struct DropState {
count: AtomicUsize,
}
impl fmt::Debug for DropState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct(&format!("DropState<{:p}>", self))
.field("count", &self.count)
.finish()
}
}
impl Drop for DropState {
fn drop(&mut self) {
match self.count.get_mut() {
1 => {},
0 => panic!("token not dropped"),
_ => panic!("invalid drop count: {}"),
}
}
}
impl DropState {
pub fn is_dropped(&self) -> bool {
!self.is_not_dropped()
}
pub fn is_not_dropped(&self) -> bool {
match self.count.load(Ordering::SeqCst) {
0 => true,
1 => false,
x => panic!("invalid drop count: {}", x),
}
}
fn new() -> Arc<Self> {
Arc::new(Self { count: AtomicUsize::new(0) })
}
fn set_dropped(&self) {
match self.count.swap(1, Ordering::SeqCst) {
0 => {},
1 => panic!("already dropped"),
x => panic!("invalid drop count: {}", x),
}
}
}
#[derive(Debug, Default)]
pub struct DropCheck {
set: Arc<RwLock<Vec<Arc<DropState>>>>,
}
impl Drop for DropCheck {
fn drop(&mut self) {
assert!(self.all_dropped(), "not all tokens dropped");
}
}
impl DropCheck {
pub fn new() -> Self {
Self::default()
}
fn push(&self, state: Arc<DropState>) {
self.set.write().unwrap().push(state)
}
pub fn token(&self) -> DropToken {
let state = DropState::new();
self.push(Arc::clone(&state));
DropToken {
set: Arc::downgrade(&self.set),
state,
}
}
pub fn pair(&self) -> (DropToken, Arc<DropState>) {
let state = DropState::new();
self.push(Arc::clone(&state));
(DropToken {
set: Arc::downgrade(&self.set),
state: Arc::clone(&state),
}, state)
}
pub fn none_dropped(&self) -> bool {
self.set.read().unwrap()
.iter().all(|state| state.is_not_dropped())
}
pub fn all_dropped(&self) -> bool {
self.set.read().unwrap()
.iter().all(|state| state.is_dropped())
}
}