Add invalidatable value node

This commit is contained in:
Shadowfacts 2024-11-02 11:15:04 -04:00
parent 05348a5dbc
commit 02b5226a90
4 changed files with 121 additions and 18 deletions

View File

@ -1,9 +1,9 @@
use crate::node::{AsyncRuleNode, ConstNode, Node, NodeValue, RuleNode}; use crate::node::{AsyncRuleNode, ConstNode, InvalidatableConstNode, Node, NodeValue, RuleNode};
use crate::rule::{AsyncRule, Rule}; use crate::rule::{AsyncRule, Rule};
use crate::util; use crate::util;
use crate::{ use crate::{
Asynchronous, ErasedNode, Graph, Input, InvalidationSignal, NodeGraph, NodeId, Synchronicity, Asynchronous, ErasedNode, Graph, Input, InvalidationSignal, NodeGraph, NodeId, Synchronicity,
Synchronous, Synchronous, ValueInvalidationSignal,
}; };
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::rc::Rc; use std::rc::Rc;
@ -57,6 +57,20 @@ impl<O: 'static, S: Synchronicity> GraphBuilder<O, S> {
return self.add_node(ConstNode::new(value)); return self.add_node(ConstNode::new(value));
} }
pub fn add_invalidatable_value<V: NodeValue>(
&mut self,
value: V,
) -> (Input<V>, ValueInvalidationSignal<V, S>) {
let input = self.add_node(InvalidatableConstNode::new(value));
let signal = ValueInvalidationSignal {
node_idx: input.node_idx,
value: Rc::clone(&input.value),
graph: Rc::clone(&self.node_graph),
graph_is_valid: Rc::clone(&self.is_valid),
};
(input, signal)
}
pub fn add_rule<R>(&mut self, rule: R) -> Input<R::Output> pub fn add_rule<R>(&mut self, rule: R) -> Input<R::Output>
where where
R: Rule, R: Rule,

View File

@ -5,7 +5,7 @@ mod synchronicity;
mod util; mod util;
use builder::{BuildGraphError, GraphBuilder}; use builder::{BuildGraphError, GraphBuilder};
use node::ErasedNode; use node::{ErasedNode, NodeValue};
use petgraph::visit::{IntoEdgeReferences, NodeIndexable}; use petgraph::visit::{IntoEdgeReferences, NodeIndexable};
use petgraph::{stable_graph::StableDiGraph, visit::EdgeRef}; use petgraph::{stable_graph::StableDiGraph, visit::EdgeRef};
use rule::{AsyncRule, InputVisitor, Rule}; use rule::{AsyncRule, InputVisitor, Rule};
@ -239,32 +239,48 @@ impl<S: Synchronicity> InvalidationSignal<S> {
} }
} }
pub struct ValueInvalidationSignal<V, Synch: Synchronicity> {
node_idx: NodeId,
value: Rc<RefCell<Option<V>>>,
graph: Rc<RefCell<NodeGraph<Synch>>>,
graph_is_valid: Rc<Cell<bool>>,
}
impl<V: NodeValue, S: Synchronicity> ValueInvalidationSignal<V, S> {
pub fn set_value(&self, value: V) {
let mut current_value = self.value.borrow_mut();
if !current_value
.as_ref()
.expect("invalidatable value node must be initialized with value")
.node_value_eq(&value)
{
*current_value = Some(value);
self.graph_is_valid.set(false);
let mut graph = self.graph.borrow_mut();
let node = &mut graph[self.node_idx];
node.invalidate();
}
}
}
// TODO: i really want Input to be able to implement Deref somehow // TODO: i really want Input to be able to implement Deref somehow
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::rule::ConstantRule;
struct ConstantRule<T>(T);
impl<T: Clone + crate::node::NodeValue> Rule for ConstantRule<T> {
type Output = T;
fn visit_inputs(&self, _visitor: &mut impl InputVisitor) {}
fn evaluate(&mut self) -> Self::Output {
self.0.clone()
}
}
#[test] #[test]
fn rule_output_with_no_inputs() { fn rule_output_with_no_inputs() {
let mut builder = GraphBuilder::new(); let mut builder = GraphBuilder::new();
builder.set_output(ConstantRule(1234)); builder.set_output(ConstantRule::new(1234));
assert_eq!(*builder.build().unwrap().evaluate(), 1234); assert_eq!(*builder.build().unwrap().evaluate(), 1234);
} }
#[test] #[test]
fn test_output_is_valid() { fn test_output_is_valid() {
let mut builder = GraphBuilder::new(); let mut builder = GraphBuilder::new();
builder.set_output(ConstantRule(1)); builder.set_output(ConstantRule::new(1));
let mut graph = builder.build().unwrap(); let mut graph = builder.build().unwrap();
assert!(!graph.is_output_valid()); assert!(!graph.is_output_valid());
graph.evaluate(); graph.evaluate();
@ -298,6 +314,7 @@ mod tests {
builder.set_output(Double(doubled)); builder.set_output(Double(doubled));
assert_eq!(*builder.build().unwrap().evaluate(), 168); assert_eq!(*builder.build().unwrap().evaluate(), 168);
} }
struct Inc(i32); struct Inc(i32);
impl Rule for Inc { impl Rule for Inc {
type Output = i32; type Output = i32;
@ -406,12 +423,12 @@ mod tests {
#[test] #[test]
fn modify_graph() { fn modify_graph() {
let mut builder = GraphBuilder::new(); let mut builder = GraphBuilder::new();
builder.set_output(ConstantRule(1)); builder.set_output(ConstantRule::new(1));
let mut graph = builder.build().unwrap(); let mut graph = builder.build().unwrap();
assert_eq!(*graph.evaluate(), 1); assert_eq!(*graph.evaluate(), 1);
graph = graph graph = graph
.modify(|g| { .modify(|g| {
g.set_output(ConstantRule(2)); g.set_output(ConstantRule::new(2));
}) })
.expect("modify"); .expect("modify");
assert_eq!(*graph.evaluate(), 2); assert_eq!(*graph.evaluate(), 2);
@ -438,7 +455,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn async_graph() { async fn async_graph() {
let mut builder = GraphBuilder::new_async(); let mut builder = GraphBuilder::new_async();
builder.set_output(ConstantRule(42)); builder.set_output(ConstantRule::new(42));
let mut graph = builder.build().unwrap(); let mut graph = builder.build().unwrap();
assert_eq!(*graph.evaluate_async().await, 42); assert_eq!(*graph.evaluate_async().await, 42);
} }
@ -483,7 +500,7 @@ mod tests {
let mut invalidate = None; let mut invalidate = None;
let a = builder.add_invalidatable_rule(|inv| { let a = builder.add_invalidatable_rule(|inv| {
invalidate = Some(inv); invalidate = Some(inv);
ConstantRule(0) ConstantRule::new(0)
}); });
struct IncAdd(Input<i32>, i32); struct IncAdd(Input<i32>, i32);
impl Rule for IncAdd { impl Rule for IncAdd {
@ -505,4 +522,17 @@ mod tests {
assert!(!graph.is_output_valid()); assert!(!graph.is_output_valid());
assert_eq!(*graph.evaluate(), 1); assert_eq!(*graph.evaluate(), 1);
} }
#[test]
fn invalidatable_value() {
let mut builder = GraphBuilder::new();
let (a, invalidate) = builder.add_invalidatable_value(0);
let b = builder.add_value(1);
builder.set_output(Add(a, b));
let mut graph = builder.build().unwrap();
assert_eq!(*graph.evaluate(), 1);
invalidate.set_value(42);
assert!(!graph.is_output_valid());
assert_eq!(*graph.evaluate(), 43);
}
} }

View File

@ -108,6 +108,45 @@ impl<V: NodeValue, S: Synchronicity> Node<V, S> for ConstNode<V, S> {
} }
} }
pub(crate) struct InvalidatableConstNode<V, S> {
value: Rc<RefCell<Option<V>>>,
valid: bool,
synchronicity: std::marker::PhantomData<S>,
}
impl<V, S> InvalidatableConstNode<V, S> {
pub(crate) fn new(value: V) -> Self {
Self {
value: Rc::new(RefCell::new(Some(value))),
valid: true,
synchronicity: std::marker::PhantomData,
}
}
}
impl<V: NodeValue, S: Synchronicity> Node<V, S> for InvalidatableConstNode<V, S> {
fn is_valid(&self) -> bool {
self.valid
}
fn invalidate(&mut self) {
self.valid = false;
}
fn visit_inputs(&self, _visitor: &mut dyn FnMut(NodeId) -> ()) {}
fn update(&mut self) -> S::UpdateResult<'_> {
self.valid = true;
// This node is only invalidate when node_value_eq between the old/new value is false,
// so it is always the case that the update method has changed the value.
S::make_update_result(true)
}
fn value_rc(&self) -> &Rc<RefCell<Option<V>>> {
&self.value
}
}
pub(crate) struct RuleNode<R, V, S> { pub(crate) struct RuleNode<R, V, S> {
rule: R, rule: R,
value: Rc<RefCell<Option<V>>>, value: Rc<RefCell<Option<V>>>,

View File

@ -1,5 +1,7 @@
use crate::node::NodeValue; use crate::node::NodeValue;
use crate::Input; use crate::Input;
use std::cell::RefCell;
use std::rc::Rc;
pub trait Rule: 'static { pub trait Rule: 'static {
type Output: NodeValue; type Output: NodeValue;
@ -20,3 +22,21 @@ pub trait AsyncRule: 'static {
pub trait InputVisitor { pub trait InputVisitor {
fn visit<T>(&mut self, input: &Input<T>); fn visit<T>(&mut self, input: &Input<T>);
} }
pub struct ConstantRule<T>(T);
impl<T> ConstantRule<T> {
pub fn new(value: T) -> Self {
Self(value)
}
}
impl<T: Clone + NodeValue> Rule for ConstantRule<T> {
type Output = T;
fn visit_inputs(&self, _visitor: &mut impl InputVisitor) {}
fn evaluate(&mut self) -> Self::Output {
self.0.clone()
}
}