use crate::errors::{Error, ErrorEnum};
use crate::generation::Generation;
use crate::generation::defer_gc::maybe_defer_task;
use crate::generation::stats::GcStats;
use std::cell::RefCell;
use std::fmt;
use std::marker::PhantomData;
use std::rc::Weak;
type TaskPtr = Box<dyn FnMut(GcTask)>;
thread_local! {
static GC_TASK_FN: RefCell<Option<TaskPtr>> = const{RefCell::new(None)};
}
#[derive(Debug)]
pub struct GcTask {
generation: Option<Weak<Generation>>,
}
pub struct GcTaskCallback<'a> {
lifetime: PhantomData<&'a ()>,
}
impl Drop for GcTask {
#[inline]
fn drop(&mut self) {
if let Some(generation) = self
.generation
.take()
.and_then(|generation_ptr| Weak::upgrade(&generation_ptr))
{
generation.run_gc();
}
}
}
impl GcTask {
pub(super) const fn new(generation: Weak<Generation>) -> Self {
GcTask {
generation: Some(generation),
}
}
#[inline]
pub fn run(mut self) -> Option<GcStats> {
self.generation
.take()
.as_ref()
.and_then(Weak::upgrade)
.map(Generation::run_gc)
}
pub(super) fn post(self) -> Option<Self> {
maybe_defer_task(self).err().and_then(Self::post_no_defer)
}
pub(super) fn post_no_defer(self) -> Option<Self> {
GC_TASK_FN.with(|gc_task_fn| {
if let Some(gc_task_fn) = &mut *gc_task_fn.borrow_mut() {
gc_task_fn(self);
None
} else {
Some(self)
}
})
}
#[allow(
clippy::missing_inline_in_public_items,
reason = "This is rarely called."
)]
pub fn install_callback<'a>(
callback: impl FnMut(GcTask) + 'a,
) -> Result<GcTaskCallback<'a>, Error> {
GC_TASK_FN.with(move |gc_task_fn| {
let gc_task_fn = &mut (*gc_task_fn.borrow_mut());
if gc_task_fn.is_some() {
Err(Error::new(ErrorEnum::CallbackAlreadyInstalled))
} else {
let callback = Box::new(callback);
let callback: Box<dyn FnMut(GcTask) + 'a> = callback;
let callback = unsafe {
std::mem::transmute::<
Box<dyn FnMut(GcTask) + 'a>,
Box<dyn FnMut(GcTask) + 'static>,
>(callback)
};
*gc_task_fn = Some(callback);
Ok(GcTaskCallback {
lifetime: PhantomData,
})
}
})
}
}
impl Drop for GcTaskCallback<'_> {
#[allow(
clippy::missing_inline_in_public_items,
reason = "This is rarely called."
)]
fn drop(&mut self) {
GC_TASK_FN.with(|gc_task_fn| *gc_task_fn.borrow_mut() = None);
}
}
impl fmt::Debug for GcTaskCallback<'_> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str("GcTaskCallback")
}
}