use slab::Slab;
use std::sync::{Arc, Mutex, Weak};
pub(crate) struct LiveReferenceSet<T>(Arc<LiveReferenceSetInner<T>>);
impl<T> Default for LiveReferenceSet<T> {
fn default() -> Self {
LiveReferenceSet(Arc::new(LiveReferenceSetInner {
active_set: Default::default(),
}))
}
}
struct LiveReferenceSetInner<T> {
active_set: Mutex<Slab<Weak<LiveReferenceHandle<T>>>>,
}
impl<T> LiveReferenceSet<T> {
pub(crate) fn track(&self, value: T) -> Arc<LiveReferenceHandle<T>> {
let set_ref = Arc::clone(&self.0);
Arc::new_cyclic(|weak| {
let slot = self.0.active_set.lock().unwrap().insert(Weak::clone(weak));
LiveReferenceHandle {
set_ref,
slot,
value,
}
})
}
pub(crate) fn get_live_references(&self) -> Vec<Arc<LiveReferenceHandle<T>>> {
self.0
.active_set
.lock()
.unwrap()
.iter()
.filter_map(|(_, span)| span.upgrade())
.collect()
}
}
pub(crate) struct LiveReferenceHandle<T> {
value: T,
set_ref: Arc<LiveReferenceSetInner<T>>,
slot: usize,
}
impl<T> Drop for LiveReferenceHandle<T> {
fn drop(&mut self) {
self.set_ref.active_set.lock().unwrap().remove(self.slot);
}
}
impl<T> std::ops::Deref for LiveReferenceHandle<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T: std::fmt::Debug> std::fmt::Debug for LiveReferenceHandle<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.value.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
struct NotifyOnDrop {
target: Arc<Mutex<Vec<usize>>>,
inner_val: usize,
}
impl Drop for NotifyOnDrop {
fn drop(&mut self) {
self.target.lock().unwrap().push(self.inner_val);
}
}
#[test]
fn test_live_references() {
let notify_vec = Arc::new(Mutex::new(vec![]));
let ref_set = Arc::new(LiveReferenceSet::default());
drop(ref_set.track(NotifyOnDrop {
target: Arc::clone(¬ify_vec),
inner_val: 1,
}));
assert_eq!(&*notify_vec.lock().unwrap(), &[1]);
assert_eq!(ref_set.get_live_references().len(), 0);
let r1 = ref_set.track(NotifyOnDrop {
target: Arc::clone(¬ify_vec),
inner_val: 2,
});
assert_eq!(&*notify_vec.lock().unwrap(), &[1]);
assert_eq!(ref_set.get_live_references()[0].inner_val, 2);
let r2 = ref_set.track(NotifyOnDrop {
target: Arc::clone(¬ify_vec),
inner_val: 3,
});
assert_eq!(&*notify_vec.lock().unwrap(), &[1]);
assert_eq!(ref_set.get_live_references()[0].inner_val, 2);
assert_eq!(ref_set.get_live_references()[1].inner_val, 3);
drop(r1);
assert_eq!(&*notify_vec.lock().unwrap(), &[1, 2]);
assert_eq!(ref_set.get_live_references()[0].inner_val, 3);
drop(r2);
assert_eq!(&*notify_vec.lock().unwrap(), &[1, 2, 3]);
assert_eq!(ref_set.get_live_references().len(), 0);
}
}