Skip to main content

euv_core/reactive/effect/
fn.rs

1use crate::*;
2
3/// Creates a new `RenderEffect` that automatically tracks signal dependencies
4/// and re-executes when any dependency changes.
5///
6/// This is the public API for creating reactive effects. During the first
7/// execution, any `Signal::get()` calls establish dependencies. When those
8/// signals change, the closure is re-executed with fresh dependency tracking.
9///
10/// # Arguments
11///
12/// - `F` - The closure to execute reactively.
13///
14/// # Returns
15///
16/// - `RenderEffect` - A handle to the created effect.
17pub fn create_render_effect<F>(effect_fn: F) -> RenderEffect
18where
19    F: FnMut() + 'static,
20{
21    RenderEffect::create(effect_fn)
22}
23
24/// Removes all effect subscriber entries for the given effect from the
25/// global registry and clears the effect's dependency list.
26///
27/// Called at the beginning of each `run_once` to clean up old
28/// subscriptions before re-tracking fresh dependencies.
29///
30/// # Arguments
31///
32/// - `usize` - The effect's inner pointer address.
33/// - `&mut Vec<usize>` - The effect's current dependency list (will be cleared).
34pub(crate) fn cleanup_effect_dependencies(effect_addr: usize, dependencies: &mut Vec<usize>) {
35    let registry: &mut HashMap<usize, Vec<usize>> = effect_subscribers_mut();
36    for signal_addr in dependencies.iter() {
37        if let Some(subscribers) = registry.get_mut(signal_addr) {
38            subscribers.retain(|addr| *addr != effect_addr);
39            if subscribers.is_empty() {
40                registry.remove(signal_addr);
41            }
42        }
43    }
44    dependencies.clear();
45}
46
47/// Ensures the effect subscribers registry is initialized and returns a mutable reference.
48///
49/// SAFETY: Must only be called from the main thread (WASM single-threaded context).
50#[allow(static_mut_refs)]
51fn ensure_effect_subscribers_mut() -> &'static mut HashMap<usize, Vec<usize>> {
52    unsafe {
53        if (*EFFECT_SUBSCRIBERS.get_0().get()).is_none() {
54            (*EFFECT_SUBSCRIBERS.get_0().get()) = Some(HashMap::new());
55        }
56        (*EFFECT_SUBSCRIBERS.get_0().get())
57            .as_mut()
58            .unwrap_unchecked()
59    }
60}
61
62/// Returns a mutable reference to the effect subscribers registry.
63///
64/// SAFETY: Must only be called from the main thread (WASM single-threaded context).
65#[allow(static_mut_refs)]
66pub(crate) fn effect_subscribers_mut() -> &'static mut HashMap<usize, Vec<usize>> {
67    ensure_effect_subscribers_mut()
68}
69
70/// Sets the currently active RenderEffect address.
71///
72/// # Arguments
73///
74/// - `Option<usize>`: The effect address to set, or `None` to clear.
75#[allow(static_mut_refs)]
76pub(crate) fn set_current_effect(addr: Option<usize>) {
77    unsafe {
78        *CURRENT_EFFECT.get_0().get() = addr;
79    }
80}
81
82/// Atomically swaps the current RenderEffect address with a new value.
83///
84/// # Arguments
85///
86/// - `Option<usize>`: The new effect address to set.
87///
88/// # Returns
89///
90/// - `Option<usize>`: The previous effect address that was replaced.
91#[allow(static_mut_refs)]
92pub(crate) fn swap_current_effect(new_addr: Option<usize>) -> Option<usize> {
93    unsafe {
94        let old: Option<usize> = *CURRENT_EFFECT.get_0().get();
95        *CURRENT_EFFECT.get_mut_0().get() = new_addr;
96        old
97    }
98}
99
100/// Returns a mutable reference to the render effect registry.
101///
102/// SAFETY: Must only be called from the main thread (WASM single-threaded context).
103#[allow(static_mut_refs)]
104pub(crate) fn render_effect_registry_mut()
105-> &'static mut HashMap<usize, Rc<RefCell<RenderEffectInner>>> {
106    unsafe {
107        if (*RENDER_EFFECT_REGISTRY.get_0().get()).is_none() {
108            (*RENDER_EFFECT_REGISTRY.get_0().get()) = Some(HashMap::new());
109        }
110        (*RENDER_EFFECT_REGISTRY.get_0().get())
111            .as_mut()
112            .unwrap_unchecked()
113    }
114}