Skip to main content

repose_core/
reactive.rs

1use std::cell::{Cell, RefCell};
2use std::collections::VecDeque;
3use std::rc::Rc;
4
5use rustc_hash::{FxHashMap, FxHashSet};
6
7pub type SignalId = usize;
8pub type ObserverId = usize;
9
10thread_local! {
11    static CURRENT_OBSERVER: RefCell<Option<ObserverId>> = const { RefCell::new(None) };
12    static GRAPH: RefCell<DepGraph> = RefCell::new(DepGraph::default());
13    static SIGNAL_DEPTH: Cell<u32> = const { Cell::new(0) };
14    static BATCH_ACTIVE: Cell<bool> = const { Cell::new(false) };
15    static RETRY_NEEDED: Cell<bool> = const { Cell::new(false) };
16    static PENDING: RefCell<Pending> = RefCell::new(Pending::default());
17    static DEFERRED_SIGNALS: RefCell<Vec<SignalId>> = const { RefCell::new(Vec::new()) };
18    static DEFERRED_OBSERVERS: RefCell<Vec<ObserverId>> = const { RefCell::new(Vec::new()) };
19    static DEFERRED_REMOVALS: RefCell<Vec<ObserverId>> = const { RefCell::new(Vec::new()) };
20    static DEFERRED_RUNNING: RefCell<Vec<ObserverId>> = const { RefCell::new(Vec::new()) };
21    static DEFERRED_RESTORE: RefCell<Vec<(ObserverId, Option<ObserverId>)>> =
22        const { RefCell::new(Vec::new()) };
23    static RUN_NOW: RefCell<VecDeque<ObserverId>> = const { RefCell::new(VecDeque::new()) };
24    static DEFERRED_SELF_RETRIES: RefCell<Vec<ObserverId>> = const { RefCell::new(Vec::new()) };
25}
26
27#[derive(Default)]
28struct Pending {
29    queue: VecDeque<ObserverId>,
30    set: FxHashSet<ObserverId>,
31}
32
33#[derive(Default)]
34struct DepGraph {
35    next_observer: ObserverId,
36    edges: FxHashMap<SignalId, FxHashSet<ObserverId>>,
37    back: FxHashMap<ObserverId, FxHashSet<SignalId>>,
38    observers: FxHashMap<ObserverId, Rc<dyn Fn()>>,
39    running: FxHashSet<ObserverId>,
40}
41
42impl DepGraph {
43    fn remove_all_edges_for(&mut self, observer: ObserverId) {
44        if let Some(signals) = self.back.remove(&observer) {
45            for signal in signals {
46                if let Some(observers) = self.edges.get_mut(&signal) {
47                    observers.remove(&observer);
48                    if observers.is_empty() {
49                        self.edges.remove(&signal);
50                    }
51                }
52            }
53        }
54    }
55
56    fn remove_observer(&mut self, observer: ObserverId) -> Option<Rc<dyn Fn()>> {
57        let callback = self.observers.remove(&observer);
58        self.remove_all_edges_for(observer);
59        self.running.remove(&observer);
60        callback
61    }
62
63    fn prepare(&mut self, observer: ObserverId) -> Option<Option<Rc<dyn Fn()>>> {
64        if !self.observers.contains_key(&observer) {
65            return Some(None);
66        }
67        if !self.running.insert(observer) {
68            return None;
69        }
70        self.remove_all_edges_for(observer);
71        Some(self.observers.get(&observer).cloned())
72    }
73}
74
75fn schedule_retry() {
76    RETRY_NEEDED.with(|retry| retry.set(true));
77    crate::request_frame();
78}
79
80fn enqueue_observer(observer: ObserverId) -> bool {
81    let queued = PENDING.try_with(|pending| match pending.try_borrow_mut() {
82        Ok(mut pending) => {
83            if pending.set.insert(observer) {
84                pending.queue.push_back(observer);
85            }
86            true
87        }
88        Err(_) => false,
89    });
90    if matches!(queued, Ok(true)) {
91        return true;
92    }
93    let deferred = DEFERRED_OBSERVERS.try_with(|deferred| {
94        if let Ok(mut deferred) = deferred.try_borrow_mut() {
95            deferred.push(observer);
96            true
97        } else {
98            false
99        }
100    });
101    if !matches!(deferred, Ok(true)) {
102        schedule_retry();
103    }
104    schedule_retry();
105    false
106}
107
108fn requeue_observer(observer: ObserverId, front: bool) -> bool {
109    let queued = PENDING.try_with(|pending| match pending.try_borrow_mut() {
110        Ok(mut pending) => {
111            if pending.set.insert(observer) {
112                if front {
113                    pending.queue.push_front(observer);
114                } else {
115                    pending.queue.push_back(observer);
116                }
117                true
118            } else {
119                false
120            }
121        }
122        Err(_) => false,
123    });
124    if matches!(queued, Ok(true)) {
125        return true;
126    }
127    let deferred = DEFERRED_OBSERVERS.try_with(|deferred| {
128        if let Ok(mut deferred) = deferred.try_borrow_mut() {
129            deferred.push(observer);
130            true
131        } else {
132            false
133        }
134    });
135    if !matches!(deferred, Ok(true)) {
136        schedule_retry();
137    }
138    schedule_retry();
139    false
140}
141
142fn remove_pending_observer(observer: ObserverId) {
143    let _ = PENDING.try_with(|pending| {
144        if let Ok(mut pending) = pending.try_borrow_mut() {
145            pending.set.remove(&observer);
146            pending.queue.retain(|queued| *queued != observer);
147        } else {
148            schedule_retry();
149        }
150    });
151    let _ = DEFERRED_OBSERVERS.try_with(|deferred| {
152        if let Ok(mut deferred) = deferred.try_borrow_mut() {
153            deferred.retain(|queued| *queued != observer);
154        }
155    });
156    let _ = DEFERRED_SELF_RETRIES.try_with(|deferred| {
157        if let Ok(mut deferred) = deferred.try_borrow_mut() {
158            deferred.retain(|queued| *queued != observer);
159        }
160    });
161    let _ = RUN_NOW.try_with(|queue| {
162        if let Ok(mut queue) = queue.try_borrow_mut() {
163            queue.retain(|queued| *queued != observer);
164        }
165    });
166    let _ = DEFERRED_RESTORE.try_with(|restore| {
167        if let Ok(mut restore) = restore.try_borrow_mut() {
168            restore.retain(|(finished, _)| *finished != observer);
169        }
170    });
171}
172
173fn defer_self_retry(observer: ObserverId) {
174    let queued = DEFERRED_SELF_RETRIES.try_with(|deferred| {
175        if let Ok(mut deferred) = deferred.try_borrow_mut() {
176            if !deferred.contains(&observer) {
177                deferred.push(observer);
178            }
179            true
180        } else {
181            false
182        }
183    });
184    if !matches!(queued, Ok(true)) {
185        schedule_retry();
186    }
187    schedule_retry();
188}
189
190fn enqueue_affected(signal: SignalId) {
191    let snapshot = GRAPH
192        .try_with(|graph| match graph.try_borrow() {
193            Ok(graph) => Some((
194                graph
195                    .edges
196                    .get(&signal)
197                    .map(|set| set.iter().copied().collect::<Vec<_>>())
198                    .unwrap_or_default(),
199                graph.running.clone(),
200            )),
201            Err(_) => None,
202        })
203        .ok()
204        .flatten();
205    let Some((observers, running)) = snapshot else {
206        let queued = DEFERRED_SIGNALS.try_with(|deferred| {
207            if let Ok(mut deferred) = deferred.try_borrow_mut() {
208                deferred.push(signal);
209                true
210            } else {
211                false
212            }
213        });
214        if !matches!(queued, Ok(true)) {
215            schedule_retry();
216        }
217        return;
218    };
219
220    for observer in observers {
221        if running.contains(&observer) {
222            defer_self_retry(observer);
223        } else if !enqueue_observer(observer) {
224            schedule_retry();
225            break;
226        }
227    }
228}
229
230pub fn register_signal_read(signal: SignalId) {
231    let observer = CURRENT_OBSERVER
232        .try_with(|current| current.try_borrow().ok().and_then(|value| *value))
233        .unwrap_or(None);
234    if let Some(observer) = observer {
235        let _ = GRAPH.try_with(|graph| match graph.try_borrow_mut() {
236            Ok(mut graph) => {
237                if graph.observers.contains_key(&observer) {
238                    graph.edges.entry(signal).or_default().insert(observer);
239                    graph.back.entry(observer).or_default().insert(signal);
240                }
241            }
242            Err(_) => schedule_retry(),
243        });
244    }
245    crate::scope_cache::record_scope_signal_dep(signal);
246}
247
248fn restore_current_observer(observer: ObserverId, previous: Option<ObserverId>) {
249    let applied = CURRENT_OBSERVER
250        .try_with(|current| match current.try_borrow_mut() {
251            Ok(mut current) => {
252                if *current == Some(observer) {
253                    *current = previous;
254                    true
255                } else {
256                    *current == previous
257                }
258            }
259            Err(_) => false,
260        })
261        .unwrap_or(false);
262    if !applied {
263        let queued = DEFERRED_RESTORE.try_with(|restore| {
264            if let Ok(mut restore) = restore.try_borrow_mut() {
265                restore.push((observer, previous));
266                true
267            } else {
268                false
269            }
270        });
271        if !matches!(queued, Ok(true)) {
272            schedule_retry();
273        }
274    }
275}
276
277fn clear_running(observer: ObserverId) {
278    let cleared = GRAPH
279        .try_with(|graph| match graph.try_borrow_mut() {
280            Ok(mut graph) => {
281                graph.running.remove(&observer);
282                true
283            }
284            Err(_) => false,
285        })
286        .unwrap_or(false);
287    if !cleared {
288        let queued = DEFERRED_RUNNING.try_with(|deferred| {
289            if let Ok(mut deferred) = deferred.try_borrow_mut() {
290                if !deferred.contains(&observer) {
291                    deferred.push(observer);
292                }
293                true
294            } else {
295                false
296            }
297        });
298        if !matches!(queued, Ok(true)) {
299            schedule_retry();
300        }
301    }
302}
303
304fn run_observer_guarded(
305    observer: ObserverId,
306    function: Rc<dyn Fn()>,
307) -> Result<(), Box<dyn std::any::Any + Send>> {
308    struct ObserverGuard {
309        observer: ObserverId,
310        previous: Option<ObserverId>,
311    }
312
313    impl Drop for ObserverGuard {
314        fn drop(&mut self) {
315            restore_current_observer(self.observer, self.previous);
316            clear_running(self.observer);
317        }
318    }
319
320    let previous = CURRENT_OBSERVER
321        .try_with(|current| match current.try_borrow_mut() {
322            Ok(mut current) => {
323                let previous = *current;
324                *current = Some(observer);
325                previous
326            }
327            Err(_) => {
328                schedule_retry();
329                None
330            }
331        })
332        .unwrap_or(None);
333    let _guard = ObserverGuard { observer, previous };
334
335    let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| function()));
336    match result {
337        Ok(()) => Ok(()),
338        Err(payload) => {
339            log::error!("reactive observer {observer} panicked");
340            if !cfg!(target_arch = "wasm32") {
341                let message = payload
342                    .downcast_ref::<String>()
343                    .map(String::as_str)
344                    .or_else(|| payload.downcast_ref::<&str>().copied())
345                    .unwrap_or("unknown panic payload");
346                log::error!("observer panic payload: {message}");
347            }
348            Err(payload)
349        }
350    }
351}
352
353pub fn signal_changed(signal: SignalId) {
354    crate::scope_cache::mark_current_scope_dirty_for_signal(signal);
355    crate::scope_cache::mark_scope_deps_dirty(signal);
356
357    if in_batch() {
358        enqueue_affected(signal);
359        return;
360    }
361
362    enqueue_affected(signal);
363    if SIGNAL_DEPTH.with(Cell::get) == 0 {
364        drain_pending();
365    }
366}
367
368pub fn batch<R>(function: impl FnOnce() -> R) -> R {
369    struct BatchGuard {
370        outer: bool,
371    }
372
373    impl Drop for BatchGuard {
374        fn drop(&mut self) {
375            if self.outer {
376                BATCH_ACTIVE.with(|active| active.set(false));
377                if SIGNAL_DEPTH.with(Cell::get) == 0 {
378                    drain_pending();
379                }
380            }
381        }
382    }
383
384    let outer = !in_batch();
385    if outer {
386        BATCH_ACTIVE.with(|active| active.set(true));
387    }
388    let _guard = BatchGuard { outer };
389    function()
390}
391
392fn in_batch() -> bool {
393    BATCH_ACTIVE.with(Cell::get)
394}
395
396fn retry_deferred_housekeeping() {
397    let restores = match DEFERRED_RESTORE.try_with(|queue| match queue.try_borrow_mut() {
398        Ok(mut queue) => Some(std::mem::take(&mut *queue)),
399        Err(_) => None,
400    }) {
401        Ok(Some(restores)) => restores,
402        Ok(None) => {
403            schedule_retry();
404            Vec::new()
405        }
406        Err(_) => {
407            schedule_retry();
408            Vec::new()
409        }
410    };
411    if !restores.is_empty() {
412        let applied = CURRENT_OBSERVER
413            .try_with(|current| match current.try_borrow_mut() {
414                Ok(mut current) => {
415                    let mut result = Vec::new();
416                    for (observer, previous) in restores {
417                        if *current == Some(observer) {
418                            *current = previous;
419                        } else if *current != previous {
420                            result.push((observer, previous));
421                        }
422                    }
423                    result
424                }
425                Err(_) => restores,
426            })
427            .unwrap_or_default();
428        if !applied.is_empty() {
429            schedule_retry();
430            let _ = DEFERRED_RESTORE.try_with(|queue| {
431                if let Ok(mut queue) = queue.try_borrow_mut() {
432                    queue.extend(applied);
433                }
434            });
435        }
436    }
437
438    let running = match DEFERRED_RUNNING.try_with(|queue| match queue.try_borrow_mut() {
439        Ok(mut queue) => Some(std::mem::take(&mut *queue)),
440        Err(_) => None,
441    }) {
442        Ok(Some(running)) => running,
443        Ok(None) | Err(_) => {
444            schedule_retry();
445            Vec::new()
446        }
447    };
448    if !running.is_empty() {
449        let applied = GRAPH
450            .try_with(|graph| match graph.try_borrow_mut() {
451                Ok(mut graph) => {
452                    for observer in &running {
453                        graph.running.remove(observer);
454                    }
455                    true
456                }
457                Err(_) => false,
458            })
459            .unwrap_or(false);
460        if !applied {
461            schedule_retry();
462            let _ = DEFERRED_RUNNING.try_with(|queue| {
463                if let Ok(mut queue) = queue.try_borrow_mut() {
464                    queue.extend(running);
465                }
466            });
467        }
468    }
469
470    let removals = match DEFERRED_REMOVALS.try_with(|queue| match queue.try_borrow_mut() {
471        Ok(mut queue) => Some(std::mem::take(&mut *queue)),
472        Err(_) => None,
473    }) {
474        Ok(Some(removals)) => removals,
475        Ok(None) | Err(_) => {
476            schedule_retry();
477            Vec::new()
478        }
479    };
480    if !removals.is_empty() {
481        let mut callbacks = Vec::new();
482        let applied = GRAPH
483            .try_with(|graph| match graph.try_borrow_mut() {
484                Ok(mut graph) => {
485                    for observer in &removals {
486                        if let Some(callback) = graph.remove_observer(*observer) {
487                            callbacks.push(callback);
488                        }
489                        remove_pending_observer(*observer);
490                    }
491                    true
492                }
493                Err(_) => false,
494            })
495            .unwrap_or(false);
496        drop(callbacks);
497        if !applied {
498            schedule_retry();
499            let _ = DEFERRED_REMOVALS.try_with(|queue| {
500                if let Ok(mut queue) = queue.try_borrow_mut() {
501                    queue.extend(removals);
502                }
503            });
504        }
505    }
506}
507
508fn drain_pending() {
509    struct DepthGuard {
510        previous: u32,
511    }
512
513    impl Drop for DepthGuard {
514        fn drop(&mut self) {
515            SIGNAL_DEPTH.with(|depth| depth.set(self.previous));
516        }
517    }
518
519    let previous_depth = SIGNAL_DEPTH.with(Cell::get);
520    SIGNAL_DEPTH.with(|depth| depth.set(previous_depth + 1));
521    let _depth_guard = DepthGuard {
522        previous: previous_depth,
523    };
524    if previous_depth == 0 {
525        let retries = match DEFERRED_SELF_RETRIES.try_with(|deferred| {
526            deferred
527                .try_borrow_mut()
528                .ok()
529                .map(|mut deferred| std::mem::take(&mut *deferred))
530        }) {
531            Ok(Some(retries)) => retries,
532            Ok(None) | Err(_) => {
533                schedule_retry();
534                Vec::new()
535            }
536        };
537        for observer in retries {
538            enqueue_observer(observer);
539        }
540    }
541
542    loop {
543        retry_deferred_housekeeping();
544        if !drain_run_now_queue() {
545            break;
546        }
547
548        let deferred_observers = DEFERRED_OBSERVERS
549            .try_with(|deferred| match deferred.try_borrow_mut() {
550                Ok(mut deferred) => std::mem::take(&mut *deferred),
551                Err(_) => Vec::new(),
552            })
553            .unwrap_or_default();
554        for observer in deferred_observers {
555            enqueue_observer(observer);
556        }
557
558        let deferred = DEFERRED_SIGNALS
559            .try_with(|signals| match signals.try_borrow_mut() {
560                Ok(mut signals) => std::mem::take(&mut *signals),
561                Err(_) => Vec::new(),
562            })
563            .unwrap_or_default();
564        for signal in deferred {
565            enqueue_affected(signal);
566        }
567
568        let next = PENDING
569            .try_with(|pending| match pending.try_borrow_mut() {
570                Ok(mut pending) => pending
571                    .queue
572                    .pop_front()
573                    .filter(|observer| pending.set.remove(observer)),
574                Err(_) => {
575                    schedule_retry();
576                    None
577                }
578            })
579            .unwrap_or(None);
580        let Some(observer) = next else { break };
581
582        let prepared = GRAPH
583            .try_with(|graph| match graph.try_borrow_mut() {
584                Ok(mut graph) => graph.prepare(observer),
585                Err(_) => {
586                    schedule_retry();
587                    None
588                }
589            })
590            .unwrap_or(None);
591        let Some(function) = prepared else {
592            requeue_observer(observer, true);
593            schedule_retry();
594            if DEFERRED_RUNNING
595                .try_with(|queue| {
596                    queue
597                        .try_borrow()
598                        .ok()
599                        .is_some_and(|q| q.contains(&observer))
600                })
601                .unwrap_or(false)
602            {
603                break;
604            }
605            break;
606        };
607        let Some(function) = function else {
608            continue;
609        };
610        let _ = run_observer_guarded(observer, function);
611    }
612}
613
614pub fn new_observer(function: impl Fn() + 'static) -> ObserverId {
615    GRAPH.with(|graph| {
616        let mut graph = graph.borrow_mut();
617        let observer = graph.next_observer;
618        graph.next_observer = graph.next_observer.wrapping_add(1);
619        graph.observers.insert(observer, Rc::new(function));
620        observer
621    })
622}
623
624pub fn remove_observer(observer: ObserverId) {
625    remove_pending_observer(observer);
626    let callback = GRAPH
627        .try_with(|graph| match graph.try_borrow_mut() {
628            Ok(mut graph) => graph.remove_observer(observer),
629            Err(_) => None,
630        })
631        .unwrap_or(None);
632    let removed = callback.is_some()
633        || GRAPH
634            .try_with(|graph| {
635                graph
636                    .try_borrow()
637                    .map(|graph| !graph.observers.contains_key(&observer))
638                    .unwrap_or(false)
639            })
640            .unwrap_or(false);
641    drop(callback);
642    if !removed {
643        let queued = DEFERRED_REMOVALS.try_with(|queue| {
644            if let Ok(mut queue) = queue.try_borrow_mut() {
645                if !queue.contains(&observer) {
646                    queue.push(observer);
647                }
648                true
649            } else {
650                false
651            }
652        });
653        if !matches!(queued, Ok(true)) {
654            schedule_retry();
655        }
656    }
657}
658
659pub fn without_observer<R>(function: impl FnOnce() -> R) -> R {
660    struct RestoreGuard {
661        previous: Option<Option<ObserverId>>,
662    }
663
664    impl Drop for RestoreGuard {
665        fn drop(&mut self) {
666            let Some(previous) = self.previous.take() else {
667                return;
668            };
669            let restored = CURRENT_OBSERVER
670                .try_with(|current| match current.try_borrow_mut() {
671                    Ok(mut current) => {
672                        if current.is_none() {
673                            *current = previous;
674                            true
675                        } else {
676                            false
677                        }
678                    }
679                    Err(_) => false,
680                })
681                .unwrap_or(false);
682            if !restored {
683                schedule_retry();
684            }
685        }
686    }
687
688    let previous = CURRENT_OBSERVER
689        .try_with(|current| match current.try_borrow_mut() {
690            Ok(mut current) => {
691                let previous = *current;
692                *current = None;
693                Some(previous)
694            }
695            Err(_) => None,
696        })
697        .unwrap_or(None);
698    let _guard = RestoreGuard { previous };
699    function()
700}
701
702pub(crate) fn try_run_observer_now(
703    observer: ObserverId,
704) -> Result<(), Box<dyn std::any::Any + Send>> {
705    struct DepthGuard {
706        previous: u32,
707    }
708
709    impl Drop for DepthGuard {
710        fn drop(&mut self) {
711            SIGNAL_DEPTH.with(|depth| depth.set(self.previous));
712        }
713    }
714
715    let previous = SIGNAL_DEPTH.with(Cell::get);
716    SIGNAL_DEPTH.with(|depth| depth.set(previous.wrapping_add(1)));
717    let _depth_guard = DepthGuard { previous };
718
719    let prepared = GRAPH
720        .try_with(|graph| match graph.try_borrow_mut() {
721            Ok(mut graph) => graph.prepare(observer),
722            Err(_) => {
723                schedule_retry();
724                None
725            }
726        })
727        .unwrap_or(None);
728    match prepared {
729        Some(Some(function)) => run_observer_guarded(observer, function),
730        Some(None) => Ok(()),
731        None => {
732            let queued = RUN_NOW.try_with(|queue| {
733                if let Ok(mut queue) = queue.try_borrow_mut() {
734                    queue.push_back(observer);
735                    true
736                } else {
737                    false
738                }
739            });
740            if !matches!(queued, Ok(true)) {
741                schedule_retry();
742            }
743            schedule_retry();
744            Ok(())
745        }
746    }
747}
748
749pub fn run_observer_now(observer: ObserverId) {
750    let _ = try_run_observer_now(observer);
751}
752
753fn drain_run_now_queue() -> bool {
754    let observer = RUN_NOW
755        .try_with(|queue| {
756            queue
757                .try_borrow_mut()
758                .ok()
759                .and_then(|mut queue| queue.pop_front())
760        })
761        .unwrap_or(None);
762    let Some(observer) = observer else {
763        return true;
764    };
765    run_observer_now(observer);
766    RUN_NOW
767        .try_with(|queue| {
768            queue
769                .try_borrow()
770                .map(|queue| queue.is_empty())
771                .unwrap_or(false)
772        })
773        .unwrap_or(false)
774}
775
776pub fn take_retry_request() -> bool {
777    RETRY_NEEDED.with(|retry| retry.replace(false))
778}