use std::cell::{Cell, RefCell};
use crate::signal::executor_schedule;
thread_local! {
static BATCH_DEPTH: Cell<u32> = const { Cell::new(0) };
static BATCHED_NOTIFICATIONS: RefCell<Vec<Box<dyn FnOnce()>>> = RefCell::new(Vec::new());
}
pub(crate) fn batch_depth() -> u32 {
BATCH_DEPTH.with(Cell::get)
}
pub(crate) fn push_batched_notification(f: Box<dyn FnOnce()>) {
BATCHED_NOTIFICATIONS.with(|cell| cell.borrow_mut().push(f));
}
struct BatchGuard;
impl Drop for BatchGuard {
fn drop(&mut self) {
BATCH_DEPTH.with(|c| {
let depth = c.get();
if depth == 1 {
c.set(0);
let notifications: Vec<Box<dyn FnOnce()>> =
BATCHED_NOTIFICATIONS.with(|cell| std::mem::take(&mut *cell.borrow_mut()));
for notification in notifications {
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
executor_schedule(notification);
}));
}
} else {
c.set(depth - 1);
}
});
}
}
pub fn batch<F: FnOnce() -> R, R>(f: F) -> R {
BATCH_DEPTH.with(|c| c.set(c.get() + 1));
let _guard = BatchGuard;
f()
}
#[must_use]
pub fn in_batch() -> bool {
batch_depth() > 0
}