#[cfg(feature = "multi_thread")]
use crate::generation::sync_task;
use crate::generation::task;
use std::cell::RefCell;
use std::fmt;
use std::marker::PhantomData;
#[derive(Debug, Default)]
struct TaskListInner {
tasks: Vec<task::GcTask>,
#[cfg(feature = "multi_thread")]
sync_tasks: Vec<sync_task::GcTask>,
}
#[derive(Debug)]
struct TaskList {
active: usize,
inner: TaskListInner,
}
impl TaskList {
const fn new() -> Self {
Self {
active: 0,
inner: TaskListInner::new(),
}
}
}
impl TaskListInner {
const fn new() -> Self {
Self {
tasks: Vec::new(),
#[cfg(feature = "multi_thread")]
sync_tasks: Vec::new(),
}
}
#[inline(never)]
fn execute_all_tasks(&mut self) {
std::mem::take(&mut self.tasks)
.into_iter()
.for_each(|task| {
if let Some(task) = task.post_no_defer() {
task.run();
}
});
#[cfg(feature = "multi_thread")]
std::mem::take(&mut self.sync_tasks)
.into_iter()
.for_each(|task| {
if let Some(task) = task.post_no_defer() {
task.run();
}
});
}
}
#[non_exhaustive]
pub struct DeferGc {
no_send_sync: PhantomData<&'static RefCell<TaskList>>,
}
impl DeferGc {
#[inline]
pub fn new() -> Self {
DEFERRED_TASK_LIST
.with_borrow_mut(|task_list| task_list.active = task_list.active.strict_add(1));
Self {
no_send_sync: PhantomData,
}
}
#[inline]
pub fn run<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
let _ = Self::new();
f()
}
}
impl Drop for DeferGc {
#[inline]
fn drop(&mut self) {
let inner = DEFERRED_TASK_LIST.with_borrow_mut(|task_list| {
task_list.active = task_list.active.strict_sub(1);
if task_list.active == 0 {
Some(std::mem::take(&mut task_list.inner))
} else {
None
}
});
if let Some(mut inner) = inner {
inner.execute_all_tasks();
}
}
}
impl Default for DeferGc {
#[inline]
fn default() -> Self {
DeferGc::new()
}
}
impl Clone for DeferGc {
#[inline]
fn clone(&self) -> Self {
Self::new()
}
}
impl fmt::Debug for DeferGc {
#[inline(never)]
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
DEFERRED_TASK_LIST.with_borrow(|task_list| {
fmt.debug_struct("DeferGc")
.field("task_list", task_list)
.finish()
})
}
}
thread_local! {
static DEFERRED_TASK_LIST: RefCell<TaskList> = const { RefCell::new(TaskList::new()) }
}
pub(super) fn maybe_defer_task(t: task::GcTask) -> Result<(), task::GcTask> {
DEFERRED_TASK_LIST.with_borrow_mut(|task_list| {
if task_list.active != 0 {
task_list.inner.tasks.push(t);
Ok(())
} else {
Err(t)
}
})
}
#[cfg(feature = "multi_thread")]
pub(super) fn maybe_defer_sync_task(t: sync_task::GcTask) -> Result<(), sync_task::GcTask> {
DEFERRED_TASK_LIST.with_borrow_mut(|task_list| {
if task_list.active != 0 {
task_list.inner.sync_tasks.push(t);
Ok(())
} else {
Err(t)
}
})
}