ravenrs_reactive/
observer.rs1use std::cell::{Cell, RefCell};
2use std::fmt;
3use std::rc::{Rc, Weak};
4
5pub(crate) struct ObserverInner {
6 epoch: Cell<u64>,
7 dirty: Cell<bool>,
8 on_invalidate: Box<dyn Fn()>,
9}
10
11thread_local! {
12 static TRACKING: RefCell<Vec<Rc<ObserverInner>>> = const { RefCell::new(Vec::new()) };
13}
14
15pub struct Observer {
18 inner: Rc<ObserverInner>,
19}
20
21impl Observer {
22 pub fn new(on_invalidate: impl Fn() + 'static) -> Self {
39 Self {
40 inner: Rc::new(ObserverInner {
41 epoch: Cell::new(0),
42 dirty: Cell::new(true),
43 on_invalidate: Box::new(on_invalidate),
44 }),
45 }
46 }
47
48 pub fn track<R>(&self, f: impl FnOnce() -> R) -> R {
50 self.inner.epoch.set(self.inner.epoch.get().wrapping_add(1));
51 self.inner.dirty.set(false);
52 TRACKING.with(|t| t.borrow_mut().push(self.inner.clone()));
53 struct Pop;
55 impl Drop for Pop {
56 fn drop(&mut self) {
57 TRACKING.with(|t| t.borrow_mut().pop());
58 }
59 }
60 let _pop = Pop;
61 f()
62 }
63
64 pub fn untracked<R>(f: impl FnOnce() -> R) -> R {
66 let saved = TRACKING.with(|t| std::mem::take(&mut *t.borrow_mut()));
67 let result = f();
68 TRACKING.with(|t| *t.borrow_mut() = saved);
69 result
70 }
71
72 pub fn is_dirty(&self) -> bool {
74 self.inner.dirty.get()
75 }
76
77 pub fn invalidate(&self) {
79 self.inner.invalidate();
80 }
81}
82
83impl fmt::Debug for Observer {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 f.debug_struct("Observer")
86 .field("epoch", &self.inner.epoch.get())
87 .field("dirty", &self.inner.dirty.get())
88 .finish()
89 }
90}
91
92impl ObserverInner {
93 fn invalidate(&self) {
94 if !self.dirty.replace(true) {
95 (self.on_invalidate)();
96 }
97 }
98}
99
100#[derive(Default)]
102pub(crate) struct Subscribers {
103 list: RefCell<Vec<(Weak<ObserverInner>, u64)>>,
104}
105
106impl Subscribers {
107 pub(crate) fn track_read(&self) {
109 let Some(current) = TRACKING.with(|t| t.borrow().last().cloned()) else {
110 return;
111 };
112 let epoch = current.epoch.get();
113 let mut list = self.list.borrow_mut();
114 let already = list.iter().any(|(w, e)| *e == epoch && w.as_ptr() == Rc::as_ptr(¤t));
115 if !already {
116 list.push((Rc::downgrade(¤t), epoch));
117 }
118 }
119
120 pub(crate) fn notify(&self) {
123 let list = std::mem::take(&mut *self.list.borrow_mut());
124 for (weak, epoch) in list {
125 if let Some(observer) = weak.upgrade()
126 && observer.epoch.get() == epoch
127 {
128 observer.invalidate();
129 }
130 }
131 }
132
133 #[cfg(test)]
134 pub(crate) fn len(&self) -> usize {
135 self.list.borrow().len()
136 }
137}