1use std::any::Any;
25use std::cell::{Cell, RefCell};
26use std::collections::VecDeque;
27use std::marker::PhantomData;
28use std::rc::Rc;
29
30#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
34pub(crate) struct NodeId {
35 index: u32,
36 generation: u32,
37}
38
39enum Kind {
40 Signal,
42 Memo(Rc<dyn Fn() -> bool>),
45 Effect(Rc<dyn Fn()>),
47 Owner,
49}
50
51struct Node {
52 generation: u32,
53 alive: bool,
54 kind: Kind,
55 value: Option<Rc<dyn Any>>,
57 subscribers: Vec<NodeId>,
59 sources: Vec<NodeId>,
61 dirty: bool,
62 owner: Option<NodeId>,
63 owned: Vec<NodeId>,
64 cleanups: Vec<Box<dyn FnOnce()>>,
65}
66
67#[derive(Default)]
68struct Runtime {
69 nodes: RefCell<Vec<Node>>,
70 free: RefCell<Vec<u32>>,
71 observer: Cell<Option<NodeId>>,
73 owner: Cell<Option<NodeId>>,
75 queue: RefCell<VecDeque<NodeId>>,
76 batch_depth: Cell<u32>,
77 flushing: Cell<bool>,
78}
79
80ferroui_base::ferro_thread_local! {
81 static RUNTIME: Runtime = Runtime::default();
82}
83
84fn with_runtime<R>(f: impl FnOnce(&Runtime) -> R) -> R {
85 RUNTIME.with(f)
86}
87
88impl Runtime {
89 fn create(&self, kind: Kind, value: Option<Rc<dyn Any>>) -> NodeId {
90 let owner = self.owner.get();
91 let node = Node {
92 generation: 0,
93 alive: true,
94 kind,
95 value,
96 subscribers: Vec::new(),
97 sources: Vec::new(),
98 dirty: false,
99 owner,
100 owned: Vec::new(),
101 cleanups: Vec::new(),
102 };
103 let mut nodes = self.nodes.borrow_mut();
104 let id = match self.free.borrow_mut().pop() {
105 Some(index) => {
106 let slot = &mut nodes[index as usize];
107 let generation = slot.generation + 1;
108 *slot = Node { generation, ..node };
109 NodeId { index, generation }
110 }
111 None => {
112 nodes.push(node);
113 NodeId { index: (nodes.len() - 1) as u32, generation: 0 }
114 }
115 };
116 if let Some(owner) = owner {
117 if let Some(owner) = Self::node_mut(&mut nodes, owner) {
118 owner.owned.push(id);
119 }
120 }
121 id
122 }
123
124 fn node_mut(nodes: &mut [Node], id: NodeId) -> Option<&mut Node> {
125 nodes.get_mut(id.index as usize).filter(|node| node.alive && node.generation == id.generation)
126 }
127
128 fn is_alive(&self, id: NodeId) -> bool {
129 let nodes = self.nodes.borrow();
130 nodes.get(id.index as usize).is_some_and(|node| node.alive && node.generation == id.generation)
131 }
132
133 fn value(&self, id: NodeId) -> Rc<dyn Any> {
134 let nodes = self.nodes.borrow();
135 match nodes.get(id.index as usize) {
136 Some(node) if node.alive && node.generation == id.generation => {
137 node.value.clone().expect("the node holds a value")
138 }
139 _ => panic!("a signal or memo was used after its owner was disposed"),
140 }
141 }
142
143 fn track(&self, source: NodeId) {
145 let Some(observer) = self.observer.get() else { return };
146 let mut nodes = self.nodes.borrow_mut();
147 let Some(node) = Self::node_mut(&mut nodes, source) else { return };
148 if !node.subscribers.contains(&observer) {
149 node.subscribers.push(observer);
150 }
151 if let Some(observer) = Self::node_mut(&mut nodes, observer) {
152 if !observer.sources.contains(&source) {
153 observer.sources.push(source);
154 }
155 }
156 }
157
158 fn notify(&self, id: NodeId) {
161 let subscribers = {
162 let mut nodes = self.nodes.borrow_mut();
163 Self::node_mut(&mut nodes, id).map(|node| node.subscribers.clone()).unwrap_or_default()
164 };
165 for subscriber in subscribers {
166 self.mark_dirty(subscriber);
167 }
168 self.flush();
169 }
170
171 fn mark_dirty(&self, id: NodeId) {
172 let (is_effect, subscribers) = {
173 let mut nodes = self.nodes.borrow_mut();
174 let Some(node) = Self::node_mut(&mut nodes, id) else { return };
175 if node.dirty {
176 return;
177 }
178 node.dirty = true;
179 match node.kind {
180 Kind::Effect(_) => (true, Vec::new()),
181 Kind::Memo(_) => (false, node.subscribers.clone()),
182 _ => (false, Vec::new()),
183 }
184 };
185 if is_effect {
186 self.queue.borrow_mut().push_back(id);
187 }
188 for subscriber in subscribers {
189 self.mark_dirty(subscriber);
190 }
191 }
192
193 fn flush(&self) {
194 if self.batch_depth.get() > 0 || self.flushing.get() {
195 return;
196 }
197 self.flushing.set(true);
198 loop {
199 let next = self.queue.borrow_mut().pop_front();
200 let Some(id) = next else { break };
201 let dirty = {
202 let mut nodes = self.nodes.borrow_mut();
203 Self::node_mut(&mut nodes, id).is_some_and(|node| node.dirty)
204 };
205 if dirty {
206 self.run(id);
207 }
208 }
209 self.flushing.set(false);
210 }
211
212 fn run(&self, id: NodeId) -> bool {
215 self.dispose_owned(id);
216 self.clear_sources(id);
217 let kind = {
218 let mut nodes = self.nodes.borrow_mut();
219 let Some(node) = Self::node_mut(&mut nodes, id) else { return false };
220 node.dirty = false;
221 match &node.kind {
222 Kind::Memo(compute) => Some(Ok(compute.clone())),
223 Kind::Effect(run) => Some(Err(run.clone())),
224 _ => None,
225 }
226 };
227 let previous = (self.observer.replace(Some(id)), self.owner.replace(Some(id)));
228 let changed = match kind {
229 Some(Ok(compute)) => compute(),
230 Some(Err(run)) => {
231 run();
232 true
233 }
234 None => false,
235 };
236 self.observer.set(previous.0);
237 self.owner.set(previous.1);
238 changed
239 }
240
241 fn clear_sources(&self, id: NodeId) {
242 let mut nodes = self.nodes.borrow_mut();
243 let sources = match Self::node_mut(&mut nodes, id) {
244 Some(node) => std::mem::take(&mut node.sources),
245 None => return,
246 };
247 for source in sources {
248 if let Some(source) = Self::node_mut(&mut nodes, source) {
249 source.subscribers.retain(|subscriber| *subscriber != id);
250 }
251 }
252 }
253
254 fn dispose_owned(&self, id: NodeId) {
255 let (owned, cleanups) = {
256 let mut nodes = self.nodes.borrow_mut();
257 match Self::node_mut(&mut nodes, id) {
258 Some(node) => (std::mem::take(&mut node.owned), std::mem::take(&mut node.cleanups)),
259 None => return,
260 }
261 };
262 for cleanup in cleanups.into_iter().rev() {
263 cleanup();
264 }
265 for child in owned.into_iter().rev() {
266 self.dispose(child);
267 }
268 }
269
270 fn dispose(&self, id: NodeId) {
271 if !self.is_alive(id) {
272 return;
273 }
274 self.dispose_owned(id);
275 self.clear_sources(id);
276 let (subscribers, value, owner) = {
277 let mut nodes = self.nodes.borrow_mut();
278 let Some(node) = Self::node_mut(&mut nodes, id) else { return };
279 node.alive = false;
280 node.kind = Kind::Owner;
281 (std::mem::take(&mut node.subscribers), node.value.take(), node.owner.take())
282 };
283 {
284 let mut nodes = self.nodes.borrow_mut();
285 if let Some(owner) = owner.and_then(|owner| Self::node_mut(&mut nodes, owner)) {
287 owner.owned.retain(|owned| *owned != id);
288 }
289 for subscriber in subscribers {
290 if let Some(subscriber) = Self::node_mut(&mut nodes, subscriber) {
291 subscriber.sources.retain(|source| *source != id);
292 }
293 }
294 }
295 drop(value);
297 self.free.borrow_mut().push(id.index);
298 }
299}
300
301pub struct Signal<T: 'static> {
304 id: NodeId,
305 _marker: PhantomData<fn() -> T>,
306}
307
308impl<T: 'static> Clone for Signal<T> {
309 fn clone(&self) -> Self {
310 *self
311 }
312}
313
314impl<T: 'static> Copy for Signal<T> {}
315
316impl<T: 'static> PartialEq for Signal<T> {
317 fn eq(&self, other: &Self) -> bool {
318 self.id == other.id
319 }
320}
321
322impl<T: 'static> std::fmt::Debug for Signal<T> {
323 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
324 write!(f, "Signal({:?})", self.id)
325 }
326}
327
328pub fn signal<T: 'static>(value: T) -> Signal<T> {
330 let id = with_runtime(|runtime| runtime.create(Kind::Signal, Some(Rc::new(RefCell::new(value)))));
331 Signal { id, _marker: PhantomData }
332}
333
334impl<T: 'static> Signal<T> {
335 fn cell(&self) -> Rc<RefCell<T>> {
336 with_runtime(|runtime| runtime.value(self.id))
337 .downcast::<RefCell<T>>()
338 .unwrap_or_else(|_| panic!("a signal holds a value of its type"))
339 }
340
341 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
344 with_runtime(|runtime| runtime.track(self.id));
345 self.with_untracked(f)
346 }
347
348 pub fn with_untracked<R>(&self, f: impl FnOnce(&T) -> R) -> R {
350 let cell = self.cell();
351 let value = cell.borrow();
352 f(&value)
353 }
354
355 pub fn get(&self) -> T
357 where
358 T: Clone,
359 {
360 self.with(T::clone)
361 }
362
363 pub fn get_untracked(&self) -> T
365 where
366 T: Clone,
367 {
368 self.with_untracked(T::clone)
369 }
370
371 pub fn set(&self, value: T) {
373 *self.cell().borrow_mut() = value;
374 with_runtime(|runtime| runtime.notify(self.id));
375 }
376
377 pub fn set_if_changed(&self, value: T)
380 where
381 T: PartialEq,
382 {
383 let changed = self.with_untracked(|current| *current != value);
384 if changed {
385 self.set(value);
386 }
387 }
388
389 pub fn update(&self, f: impl FnOnce(&mut T)) {
391 f(&mut self.cell().borrow_mut());
392 with_runtime(|runtime| runtime.notify(self.id));
393 }
394
395 pub fn take(&self) -> T
397 where
398 T: Default,
399 {
400 let value = std::mem::take(&mut *self.cell().borrow_mut());
401 with_runtime(|runtime| runtime.notify(self.id));
402 value
403 }
404
405 pub fn two_way(self) -> TwoWay<T> {
409 TwoWay(self)
410 }
411
412 pub fn is_alive(&self) -> bool {
414 with_runtime(|runtime| runtime.is_alive(self.id))
415 }
416}
417
418pub struct TwoWay<T: 'static>(pub(crate) Signal<T>);
420
421pub struct Memo<T: 'static> {
424 id: NodeId,
425 _marker: PhantomData<fn() -> T>,
426}
427
428impl<T: 'static> Clone for Memo<T> {
429 fn clone(&self) -> Self {
430 *self
431 }
432}
433
434impl<T: 'static> Copy for Memo<T> {}
435
436pub fn memo<T: PartialEq + 'static>(compute: impl Fn(Option<&T>) -> T + 'static) -> Memo<T> {
439 let cell: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None));
440 let target = cell.clone();
441 let recompute = move || {
442 let next = {
443 let previous = target.borrow();
444 compute(previous.as_ref())
445 };
446 let changed = target.borrow().as_ref() != Some(&next);
447 if changed {
448 *target.borrow_mut() = Some(next);
449 }
450 changed
451 };
452 let id = with_runtime(|runtime| {
453 let id = runtime.create(Kind::Memo(Rc::new(recompute)), Some(cell));
454 let mut nodes = runtime.nodes.borrow_mut();
456 if let Some(node) = Runtime::node_mut(&mut nodes, id) {
457 node.dirty = true;
458 }
459 id
460 });
461 Memo { id, _marker: PhantomData }
462}
463
464impl<T: 'static> Memo<T> {
465 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
468 let dirty = with_runtime(|runtime| {
469 runtime.track(self.id);
470 let mut nodes = runtime.nodes.borrow_mut();
471 Runtime::node_mut(&mut nodes, self.id).is_some_and(|node| node.dirty)
472 });
473 if dirty {
474 with_runtime(|runtime| runtime.run(self.id));
475 }
476 let cell = with_runtime(|runtime| runtime.value(self.id))
477 .downcast::<RefCell<Option<T>>>()
478 .unwrap_or_else(|_| panic!("a memo holds a value of its type"));
479 let value = cell.borrow();
480 f(value.as_ref().expect("a memo that was read is computed"))
481 }
482
483 pub fn get(&self) -> T
485 where
486 T: Clone,
487 {
488 self.with(T::clone)
489 }
490
491 pub fn get_untracked(&self) -> T
493 where
494 T: Clone,
495 {
496 untrack(|| self.get())
497 }
498}
499
500pub fn effect(f: impl Fn() + 'static) {
504 with_runtime(|runtime| {
505 let id = runtime.create(Kind::Effect(Rc::new(f)), None);
506 runtime.run(id);
507 });
508}
509
510pub fn on_cleanup(f: impl FnOnce() + 'static) {
513 with_runtime(|runtime| {
514 let Some(owner) = runtime.owner.get() else { return };
515 let mut nodes = runtime.nodes.borrow_mut();
516 if let Some(node) = Runtime::node_mut(&mut nodes, owner) {
517 node.cleanups.push(Box::new(f));
518 }
519 });
520}
521
522pub fn untrack<R>(f: impl FnOnce() -> R) -> R {
524 let previous = with_runtime(|runtime| runtime.observer.replace(None));
525 let result = f();
526 with_runtime(|runtime| runtime.observer.set(previous));
527 result
528}
529
530pub fn batch<R>(f: impl FnOnce() -> R) -> R {
532 with_runtime(|runtime| runtime.batch_depth.set(runtime.batch_depth.get() + 1));
533 let result = f();
534 with_runtime(|runtime| {
535 runtime.batch_depth.set(runtime.batch_depth.get() - 1);
536 runtime.flush();
537 });
538 result
539}
540
541#[derive(Clone, Copy, PartialEq, Eq, Debug)]
544pub struct Owner {
545 id: NodeId,
546}
547
548impl Owner {
549 pub fn new() -> Self {
551 Owner { id: with_runtime(|runtime| runtime.create(Kind::Owner, None)) }
552 }
553
554 pub fn new_root() -> Self {
556 let previous = with_runtime(|runtime| runtime.owner.replace(None));
557 let owner = Self::new();
558 with_runtime(|runtime| runtime.owner.set(previous));
559 owner
560 }
561
562 pub fn current() -> Option<Self> {
564 with_runtime(|runtime| runtime.owner.get()).map(|id| Owner { id })
565 }
566
567 pub fn with<R>(&self, f: impl FnOnce() -> R) -> R {
570 let previous = with_runtime(|runtime| (runtime.owner.replace(Some(self.id)), runtime.observer.replace(None)));
571 let result = f();
572 with_runtime(|runtime| {
573 runtime.owner.set(previous.0);
574 runtime.observer.set(previous.1);
575 });
576 result
577 }
578
579 pub fn dispose(self) {
581 with_runtime(|runtime| runtime.dispose(self.id));
582 }
583
584 pub fn is_alive(&self) -> bool {
586 with_runtime(|runtime| runtime.is_alive(self.id))
587 }
588}
589
590impl Default for Owner {
591 fn default() -> Self {
592 Self::new()
593 }
594}