use std::cell::RefCell;
use std::rc::Rc;
type Cleanups = Rc<RefCell<Vec<Box<dyn FnOnce()>>>>;
type OwnerCount = Rc<()>;
pub struct Tracker {
cleanups: Cleanups,
}
impl Tracker {
pub(super) fn new() -> Self {
Self {
cleanups: Rc::new(RefCell::new(Vec::new())),
}
}
pub(crate) fn add<F: FnOnce() + 'static>(&self, cleanup: F) {
self.cleanups.borrow_mut().push(Box::new(cleanup));
}
pub fn subscription_count(&self) -> usize {
self.cleanups.borrow().len()
}
pub fn track(&self, mut child: DisposableTracker) {
self.add(move || {
child.dispose();
});
}
}
impl Clone for Tracker {
fn clone(&self) -> Self {
Self {
cleanups: self.cleanups.clone(),
}
}
}
#[derive(Clone)]
pub struct DisposableTracker {
tracker: Tracker,
owner_count: OwnerCount,
}
impl DisposableTracker {
pub fn new() -> Self {
Self {
tracker: Tracker::new(),
owner_count: Rc::new(()),
}
}
pub fn tracker(&self) -> &Tracker {
&self.tracker
}
pub fn dispose(&mut self) {
if let Ok(mut cleanups) = self.tracker.cleanups.try_borrow_mut() {
if cleanups.is_empty() {
return; }
#[cfg(feature = "debug")]
{
let count = cleanups.len();
tracing::debug!(
subscription_count = count,
"manually disposing DisposableTracker"
);
}
for cleanup in cleanups.drain(..) {
cleanup();
}
#[cfg(feature = "debug")]
tracing::debug!("manual dispose complete");
}
}
pub fn subscription_count(&self) -> usize {
self.tracker.subscription_count()
}
}
impl Default for DisposableTracker {
fn default() -> Self {
Self::new()
}
}
impl Drop for DisposableTracker {
fn drop(&mut self) {
if Rc::strong_count(&self.owner_count) == 1 {
if let Ok(mut cleanups) = self.tracker.cleanups.try_borrow_mut() {
if cleanups.is_empty() {
return; }
#[cfg(feature = "debug")]
{
let count = cleanups.len();
tracing::debug!(
subscription_count = count,
"dropping last DisposableTracker clone"
);
}
for cleanup in cleanups.drain(..) {
cleanup();
}
#[cfg(feature = "debug")]
tracing::debug!("DisposableTracker drop complete");
}
}
}
}