Last active
January 29, 2023 08:55
-
-
Save clinuxrulz/4c32a63bf5423c0cc848d1e3932d9c8f to your computer and use it in GitHub Desktop.
Alternative fine grain reactivity implementation (untested)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::cell::{Cell, RefCell}; | |
use std::rc::{Rc, Weak}; | |
use std::ops::{Deref, DerefMut}; | |
pub struct FgrCtx { | |
observer: Rc<Node>, | |
dirty_nodes: Vec<Rc<Node>>, | |
transaction_depth: usize, | |
} | |
impl Drop for FgrCtx { | |
fn drop(&mut self) { | |
let mut cleanups: Vec<Box<dyn FnMut(&mut FgrCtx)>> = Vec::new(); | |
std::mem::swap(&mut cleanups, &mut self.observer.cleanups.borrow_mut()); | |
for mut cleanup in cleanups { | |
cleanup(self); | |
} | |
} | |
} | |
impl FgrCtx { | |
pub fn new() -> FgrCtx { | |
FgrCtx { | |
observer: Rc::new(Node::mk_root()), | |
dirty_nodes: Vec::new(), | |
transaction_depth: 0, | |
} | |
} | |
pub fn create_signal<A: 'static>(&mut self, a: A) -> Signal<A> { | |
Signal::new(a) | |
} | |
pub fn create_memo<A: 'static, Callback: FnMut(&mut FgrCtx) -> A + 'static>(&mut self, callback: Callback) -> Memo<A> { | |
Memo::new(self, callback) | |
} | |
pub fn create_effect<Callback: FnMut(&mut FgrCtx) + 'static>(&mut self, callback: Callback) { | |
Memo::new(self, callback); | |
} | |
pub fn batch<A,Callback: FnOnce(&mut FgrCtx)->A>(&mut self, callback: Callback) -> A { | |
self.transaction_depth += 1; | |
let result = callback(self); | |
self.transaction_depth -= 1; | |
if self.transaction_depth == 0 { | |
self.propergate(); | |
} | |
return result; | |
} | |
fn propergate(&mut self) { | |
let mut stack = Vec::<Rc<Node>>::new(); | |
stack.append(&mut self.dirty_nodes); | |
while let Some(node) = stack.pop() { | |
node.buffered.set(false); | |
let mut has_dirty_source = false; | |
for source in &*node.sources.borrow() { | |
if source.dirty.get() && !source.buffered.get() { | |
stack.push(Rc::clone(source)); | |
source.buffered.set(true); | |
has_dirty_source = true; | |
} | |
} | |
if !has_dirty_source { | |
node.update.borrow_mut()(self); | |
node.dirty.set(false); | |
for target in &*node.targets.borrow() { | |
target.dirty.set(true); | |
if !target.buffered.get() { | |
stack.push(Rc::clone(target)); | |
target.buffered.set(true); | |
} | |
} | |
} | |
} | |
} | |
} | |
struct Node { | |
sources: RefCell<Vec<Rc<Node>>>, | |
targets: RefCell<Vec<Rc<Node>>>, | |
dirty: Cell<bool>, | |
buffered: Cell<bool>, | |
update: RefCell<Box<dyn FnMut(&mut FgrCtx)>>, | |
cleanups: RefCell<Vec<Box<dyn FnMut(&mut FgrCtx)>>>, | |
mounts: RefCell<Vec<Box<dyn FnMut(&mut FgrCtx)>>>, | |
} | |
impl Node { | |
fn mk_root() -> Self { | |
Node { | |
sources: RefCell::new(Vec::new()), | |
targets: RefCell::new(Vec::new()), | |
dirty: Cell::new(false), | |
buffered: Cell::new(false), | |
update: RefCell::new(Box::new(|ctx| {})), | |
cleanups: RefCell::new(Vec::new()), | |
mounts: RefCell::new(Vec::new()), | |
} | |
} | |
fn cleanup(&self, fgr_ctx: &mut FgrCtx) { | |
let mut cleanups = self.cleanups.borrow_mut(); | |
let cleanups: &mut Vec<_> = &mut cleanups; | |
for mut cleanup in cleanups.drain(0..) { | |
cleanup(fgr_ctx); | |
} | |
} | |
} | |
pub struct Signal<A> { | |
data: Rc<SignalData<A>>, | |
} | |
struct SignalData<A> { | |
value: RefCell<A>, | |
node: Rc<Node>, | |
} | |
impl<A> Signal<A> { | |
fn new(a: A) -> Signal<A> where A: 'static { | |
Signal { | |
data: Rc::new(SignalData { | |
value: RefCell::new(a), | |
node: Rc::new(Node::mk_root()), | |
}), | |
} | |
} | |
pub fn get<'a>(&'a self, fgr_ctx: &mut FgrCtx) -> impl Deref<Target=A> + 'a { | |
self.data.node.targets.borrow_mut().push(Rc::clone(&fgr_ctx.observer)); | |
fgr_ctx.observer.sources.borrow_mut().push(Rc::clone(&self.data.node)); | |
self.data.value.borrow() | |
} | |
pub fn update<Callback: FnOnce(&mut A)>(&mut self, fgr_ctx: &mut FgrCtx, callback: Callback) { | |
fgr_ctx.batch(|fgr_ctx| { | |
callback(&mut self.data.value.borrow_mut()); | |
let mut stack = Vec::<Rc<Node>>::new(); | |
for target in &*self.data.node.targets.borrow() { | |
if !target.buffered.get() { | |
fgr_ctx.dirty_nodes.push(Rc::clone(target)); | |
target.buffered.set(true); | |
stack.push(Rc::clone(target)); | |
} | |
} | |
while let Some(target) = stack.pop() { | |
target.dirty.set(true); | |
for target2 in &*target.targets.borrow() { | |
if !target2.dirty.get() { | |
stack.push(Rc::clone(target2)); | |
} | |
} | |
} | |
}); | |
} | |
} | |
pub struct Memo<A> { | |
data: Rc<MemoData<A>>, | |
} | |
struct MemoData<A> { | |
value: RefCell<A>, | |
node: Rc<Node>, | |
} | |
impl<A> Memo<A> { | |
fn new<Callback: FnMut(&mut FgrCtx) -> A + 'static>(fgr_ctx: &mut FgrCtx, mut callback: Callback) -> Memo<A> | |
where A: 'static { | |
let mut update: Box<dyn FnMut(&mut FgrCtx)->A> = Box::new(|_fgr_ctx: &mut FgrCtx| { unreachable!(); }); | |
let node = Rc::new_cyclic(|node| { | |
let node = Weak::clone(node); | |
update = Box::new(move |fgr_ctx: &mut FgrCtx| { | |
let node: Rc<Node> = node.upgrade().unwrap(); | |
node.cleanup(fgr_ctx); | |
for target in &*node.targets.borrow() { | |
for i in (0..target.sources.borrow().len()).rev() { | |
if std::ptr::eq(&*target.sources.borrow()[i], &*node) { | |
target.sources.borrow_mut().remove(i); | |
break; | |
} | |
} | |
} | |
node.targets.borrow_mut().clear(); | |
let mut node2 = Rc::clone(&node); | |
std::mem::swap(&mut fgr_ctx.observer, &mut node2); | |
let r = callback(fgr_ctx); | |
std::mem::swap(&mut fgr_ctx.observer, &mut node2); | |
r | |
}); | |
let node = Node::mk_root(); | |
node | |
}); | |
let r = update(fgr_ctx); | |
let memo_data = Rc::new(MemoData { | |
value: RefCell::new(r), | |
node: Rc::clone(&node), | |
}); | |
{ | |
let memo_data = Rc::clone(&memo_data); | |
*node.update.borrow_mut() = Box::new(move |fgr_ctx: &mut FgrCtx| { | |
let r = update(fgr_ctx); | |
*memo_data.value.borrow_mut() = r; | |
}); | |
} | |
Memo { | |
data: memo_data, | |
} | |
} | |
pub fn get<'a>(&'a self, fgr_ctx: &mut FgrCtx) -> impl Deref<Target=A> + 'a { | |
self.data.node.targets.borrow_mut().push(Rc::clone(&fgr_ctx.observer)); | |
fgr_ctx.observer.sources.borrow_mut().push(Rc::clone(&self.data.node)); | |
self.data.value.borrow() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment