use alloc::boxed::Box;
use core::fmt::{self, Debug, Formatter};
use core::cell::{RefCell, UnsafeCell};
use slotmap::{new_key_type, SlotMap};
use crate::{Cc, Context, Finalize, Trace};
use crate::weak::Weak;
new_key_type! {
struct CleanerKey;
}
struct CleanerMap {
map: RefCell<SlotMap<CleanerKey, CleaningAction>>,
}
unsafe impl Trace for CleanerMap {
#[inline(always)]
fn trace(&self, _: &mut Context<'_>) {
}
}
impl Finalize for CleanerMap {}
struct CleaningAction(Option<Box<dyn FnOnce() + 'static>>);
impl Drop for CleaningAction {
#[inline]
fn drop(&mut self) {
if let Some(fun) = self.0.take() {
fun();
}
}
}
pub struct Cleaner {
cleaner_map: UnsafeCell<Option<Cc<CleanerMap>>>, }
impl Cleaner {
#[inline]
pub fn new() -> Cleaner {
Cleaner {
cleaner_map: UnsafeCell::new(None),
}
}
#[inline]
pub fn register(&self, action: impl FnOnce() + 'static) -> Cleanable {
let cc = {
let map = unsafe { &mut *self.cleaner_map.get() };
map.get_or_insert_with(|| Cc::new(CleanerMap {
map: RefCell::new(SlotMap::with_capacity_and_key(3)),
}))
};
let map_key = cc.map.borrow_mut().insert(CleaningAction(Some(Box::new(action))));
Cleanable {
cleaner_map: cc.downgrade(),
key: map_key,
}
}
#[cfg(all(test, feature = "std"))] pub(crate) fn has_allocated(&self) -> bool {
unsafe { (*self.cleaner_map.get()).is_some() }
}
}
unsafe impl Trace for Cleaner {
#[inline(always)]
fn trace(&self, _: &mut Context<'_>) {
}
}
impl Finalize for Cleaner {}
impl Default for Cleaner {
#[inline]
fn default() -> Self {
Cleaner::new()
}
}
impl Debug for Cleaner {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("Cleaner").finish_non_exhaustive()
}
}
pub struct Cleanable {
cleaner_map: Weak<CleanerMap>,
key: CleanerKey,
}
impl Cleanable {
#[inline]
pub fn clean(&self) {
let Some(cc) = self.cleaner_map.upgrade() else { return };
let Ok(mut map) = cc.map.try_borrow_mut() else {
crate::utils::cold(); return;
};
let _ = map.remove(self.key);
}
}
unsafe impl Trace for Cleanable {
#[inline(always)]
fn trace(&self, _: &mut Context<'_>) {
}
}
impl Finalize for Cleanable {}
impl Debug for Cleanable {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("Cleanable").finish_non_exhaustive()
}
}