use std::cmp::Reverse;
use super::flush::flush;
use super::surface::current_surface;
use super::{EffectEntry, EffectId, RUNTIME, SignalId};
pub(crate) fn current_observer() -> Option<EffectId> {
RUNTIME.with(|rt| rt.borrow().observer_stack.last().copied())
}
pub(crate) fn register_effect(f: Box<dyn Fn()>) -> EffectId {
RUNTIME.with(|rt| {
let mut rt = rt.borrow_mut();
let id = rt.effects.insert(EffectEntry {
callback: f,
surface: current_surface(),
is_pure: false,
last_run_epoch: 0,
sources: Vec::new(),
source_slots: Vec::new(),
source_versions: Vec::new(),
height: 0,
memo_dirty: false,
});
id
})
}
pub(crate) fn register_pure_effect(f: Box<dyn Fn()>) -> EffectId {
RUNTIME.with(|rt| {
let mut rt = rt.borrow_mut();
let id = rt.effects.insert(EffectEntry {
callback: f,
surface: current_surface(),
is_pure: true,
last_run_epoch: 0,
sources: Vec::new(),
source_slots: Vec::new(),
source_versions: Vec::new(),
height: 0,
memo_dirty: false,
});
id
})
}
pub(crate) fn deregister_effect(id: EffectId) {
clean_effect(id);
let removed = RUNTIME.with(|rt| {
let mut rt = rt.borrow_mut();
let removed = if rt.effects.contains(id) {
Some(rt.effects.remove(id))
} else {
None
};
rt.pending_set.remove(&id);
removed
});
drop(removed);
}
pub(crate) fn is_alive(id: EffectId) -> bool {
RUNTIME.with(|rt| rt.borrow().effects.contains(id))
}
pub(crate) fn schedule(id: EffectId) {
let should_flush = RUNTIME.with(|rt| {
let mut rt = rt.borrow_mut();
let alive = rt.effects.contains(id);
if alive {
rt.effects[id].memo_dirty = true;
}
if alive && rt.pending_set.insert(id) {
if rt.effects[id].is_pure {
let h = rt.effects[id].height;
rt.memo_pending.push((Reverse(h), id));
} else {
rt.pending.push(id);
}
}
rt.batch_depth == 0 && !rt.flushing
});
if should_flush {
flush();
}
}
pub(crate) fn clean_effect(id: EffectId) {
let (sources, source_slots) = RUNTIME.with(|rt| {
let mut rt = rt.borrow_mut();
if let Some(entry) = rt.effects.get_mut(id) {
(
std::mem::take(&mut entry.sources),
std::mem::take(&mut entry.source_slots),
)
} else {
(Vec::new(), Vec::new())
}
});
for (&sig_id, &slot) in sources.iter().zip(source_slots.iter()) {
RUNTIME.with(|rt| {
let mut rt = rt.borrow_mut();
if !rt.signals.contains(sig_id) {
return;
}
let sig = &mut rt.signals[sig_id];
let last = sig.subscribers.len().saturating_sub(1);
if slot > last {
return;
}
if slot == last {
sig.subscribers.pop();
sig.observer_slots.pop();
} else {
let moved_id = sig.subscribers[last];
let moved_obs_slot = sig.observer_slots[last];
sig.subscribers[slot] = moved_id;
sig.observer_slots[slot] = moved_obs_slot;
sig.subscribers.pop();
sig.observer_slots.pop();
if let Some(entry) = rt.effects.get_mut(moved_id) {
if moved_obs_slot < entry.source_slots.len() {
entry.source_slots[moved_obs_slot] = slot;
}
}
}
});
}
}
pub(crate) fn run_effect(id: EffectId) {
let ptr = RUNTIME.with(|rt| {
let mut rt = rt.borrow_mut();
if !rt.effects.contains(id) {
return None;
}
if rt.effects[id].last_run_epoch == rt.flush_epoch && rt.flush_epoch > 0 {
return None;
}
rt.effects[id].last_run_epoch = rt.flush_epoch;
let memo_dirty = std::mem::take(&mut rt.effects[id].memo_dirty);
let sig_ids: Vec<SignalId> = rt
.effects
.get(id)
.map(|e| e.sources.clone())
.unwrap_or_default();
let stored_versions: Vec<u64> = rt
.effects
.get(id)
.map(|e| e.source_versions.clone())
.unwrap_or_default();
if !memo_dirty && !sig_ids.is_empty() && sig_ids.len() == stored_versions.len() {
let all_same = sig_ids
.iter()
.zip(stored_versions.iter())
.all(|(&sig_id, &ver)| {
if rt.signals.contains(sig_id) {
rt.signals[sig_id].version == ver
} else {
false
}
});
if all_same {
return None;
}
}
let ptr: *const dyn Fn() = &*rt.effects[id].callback;
let surface = rt.effects[id].surface;
Some((ptr, surface)) });
if let Some((ptr, surface)) = ptr {
clean_effect(id);
RUNTIME.with(|rt| rt.borrow_mut().observer_stack.push(id));
struct PopGuard;
impl Drop for PopGuard {
fn drop(&mut self) {
RUNTIME.with(|rt| rt.borrow_mut().observer_stack.pop());
}
}
let _guard = PopGuard;
{
let _surface_guard = surface.enter();
unsafe { (*ptr)() };
}
drop(_guard);
RUNTIME.with(|rt| {
let mut rt = rt.borrow_mut();
let sig_ids: Vec<SignalId> = rt
.effects
.get(id)
.map(|e| e.sources.clone())
.unwrap_or_default();
let versions: Vec<u64> = sig_ids
.iter()
.map(|&sig_id| {
if rt.signals.contains(sig_id) {
rt.signals[sig_id].version
} else {
0
}
})
.collect();
let new_height: u32 = if sig_ids.is_empty() { 0 } else { 1 };
if let Some(entry) = rt.effects.get_mut(id) {
entry.source_versions = versions;
entry.height = new_height;
}
});
}
}