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}