Skip to content

Instantly share code, notes, and snippets.

@clinuxrulz
Last active January 29, 2023 08:55
Show Gist options
  • Save clinuxrulz/4c32a63bf5423c0cc848d1e3932d9c8f to your computer and use it in GitHub Desktop.
Save clinuxrulz/4c32a63bf5423c0cc848d1e3932d9c8f to your computer and use it in GitHub Desktop.
Alternative fine grain reactivity implementation (untested)
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