use super::runtime::{NodeData, NodeId};
use super::with_runtime;
const FLUSH_ITERATION_CAP: usize = 256;
pub(crate) fn schedule(node: NodeId) {
let was_empty = with_runtime(|rt| {
let was_empty = rt.pending.is_empty();
if !rt.pending.contains(&node) {
rt.pending.push(node);
}
was_empty
});
if was_empty {
crate::host_wake::wake_runtime();
}
}
pub fn flush() {
let already_flushing = with_runtime(|rt| {
let was = rt.flushing;
if !was {
rt.flushing = true;
}
was
});
if already_flushing {
return;
}
let mut iterations = 0;
loop {
let batch: Vec<NodeId> = with_runtime(|rt| std::mem::take(&mut rt.pending));
if batch.is_empty() {
break;
}
iterations += 1;
if iterations > FLUSH_ITERATION_CAP {
eprintln!(
"whisker-reactive: flush exceeded {FLUSH_ITERATION_CAP} iterations; \
likely an effect with a self-feedback loop. Dropping {} pending nodes.",
batch.len()
);
with_runtime(|rt| rt.pending.clear());
break;
}
for node in batch {
run_node_if_alive(node);
}
}
with_runtime(|rt| rt.flushing = false);
}
fn run_node_if_alive(node: NodeId) {
let prep = with_runtime(|rt| {
let n = rt.nodes.get(node)?;
let owner = n.owner;
let compute = match &n.data {
NodeData::Effect { compute } => compute.clone(),
NodeData::Computed { compute, .. } => compute.clone(),
NodeData::Signal { .. } => return None,
};
let paused = rt.owners.get(owner).map(|o| o.paused).unwrap_or(false);
if paused {
if !rt.deferred.contains(&node) {
rt.deferred.push(node);
}
return None;
}
let sources: Vec<_> = rt.nodes.get(node)?.sources.iter().copied().collect();
for src in sources {
if let Some(src_node) = rt.nodes.get_mut(src) {
src_node.subscribers.remove(&node);
}
}
if let Some(n) = rt.nodes.get_mut(node) {
n.sources.clear();
}
let arc_sources = rt
.nodes
.get_mut(node)
.map(|n| std::mem::take(&mut n.arc_sources))
.unwrap_or_default();
rt.current_tracker = Some(node);
rt.owner_stack.push(owner);
Some((compute, arc_sources))
});
let Some((compute, arc_sources)) = prep else {
return;
};
for arc_src in arc_sources {
arc_src.unsubscribe(node);
}
{
let mut borrow = compute.borrow_mut();
(*borrow)();
}
with_runtime(|rt| {
rt.owner_stack.pop();
rt.current_tracker = None;
});
}