pub mod graph2;
use graph2::{Graph2, Graph2Guard, NodeGuard, NodeKey, RecalcState};
pub use graph2::AnchorHandle;
use crate::{Anchor, AnchorInner, OutputContext, Poll, UpdateContext};
use std::any::Any;
use std::cell::RefCell;
use std::panic::Location;
use std::rc::Rc;
use std::num::NonZeroU64;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct Generation(NonZeroU64);
impl Generation {
fn new() -> Generation {
Generation(NonZeroU64::new(1).unwrap())
}
fn increment(&mut self) {
let gen: u64 = u64::from(self.0) + 1;
self.0 = NonZeroU64::new(gen).unwrap();
}
}
thread_local! {
static DEFAULT_MOUNTER: RefCell<Option<Mounter>> = RefCell::new(None);
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ObservedState {
Observed,
Necessary,
Unnecessary,
}
pub struct Engine {
graph: Rc<Graph2>,
dirty_marks: Rc<RefCell<Vec<NodeKey>>>,
generation: Generation,
}
struct Mounter {
graph: Rc<Graph2>,
}
impl crate::Engine for Engine {
type AnchorHandle = AnchorHandle;
type DirtyHandle = DirtyHandle;
fn mount<I: AnchorInner<Self> + 'static>(inner: I) -> Anchor<I::Output, Self> {
DEFAULT_MOUNTER.with(|default_mounter| {
let mut borrow1 = default_mounter.borrow_mut();
let this = borrow1
.as_mut()
.expect("no engine was initialized. did you call `Engine::new()`?");
let debug_info = inner.debug_info();
let handle = this.graph.insert(Box::new(inner), debug_info);
Anchor::new(handle)
})
}
}
impl Engine {
pub fn new() -> Self {
Self::new_with_max_height(256)
}
pub fn new_with_max_height(max_height: usize) -> Self {
let graph = Rc::new(Graph2::new(max_height));
let mounter = Mounter {
graph: graph.clone(),
};
DEFAULT_MOUNTER.with(|v| *v.borrow_mut() = Some(mounter));
Self {
graph,
dirty_marks: Default::default(),
generation: Generation::new(),
}
}
pub fn mark_observed<O: 'static>(&mut self, anchor: &Anchor<O, Engine>) {
self.graph.with(|graph| {
let node = graph.get(anchor.token()).unwrap();
node.observed.set(true);
if graph2::recalc_state(node) != RecalcState::Ready {
graph.queue_recalc(node);
}
})
}
pub fn mark_unobserved<O: 'static>(&mut self, anchor: &Anchor<O, Engine>) {
self.graph.with(|graph| {
let node = graph.get(anchor.token()).unwrap();
node.observed.set(false);
Self::update_necessary_children(node);
})
}
fn update_necessary_children<'a>(node: NodeGuard<'a>) {
if Self::check_observed_raw(node) != ObservedState::Unnecessary {
return;
}
for child in node.drain_necessary_children() {
Self::update_necessary_children(child);
}
}
pub fn get<'out, O: Clone + 'static>(&mut self, anchor: &Anchor<O, Engine>) -> O {
self.stabilize();
self.graph.with(|graph| {
let anchor_node = graph.get(anchor.token()).unwrap();
if graph2::recalc_state(anchor_node) != RecalcState::Ready {
graph.queue_recalc(anchor_node);
self.stabilize0();
}
let target_anchor = &graph.get(anchor.token()).unwrap().anchor;
let borrow = target_anchor.borrow();
borrow
.as_ref()
.unwrap()
.output(&mut EngineContext { engine: &self })
.downcast_ref::<O>()
.unwrap()
.clone()
})
}
pub(crate) fn update_dirty_marks(&mut self) {
self.graph.with(|graph| {
let dirty_marks = std::mem::replace(&mut *self.dirty_marks.borrow_mut(), Vec::new());
for dirty in dirty_marks {
let node = graph.get(dirty).unwrap();
mark_dirty(graph, node, false);
}
})
}
pub fn stabilize(&mut self) {
self.update_dirty_marks();
self.generation.increment();
self.stabilize0();
}
fn stabilize0(&self) {
self.graph.with(|graph| {
while let Some((height, node)) = graph.recalc_pop_next() {
let calculation_complete = if graph2::height(node) == height {
self.recalculate(graph, node)
} else {
false
};
if !calculation_complete {
graph.queue_recalc(node);
}
}
})
}
fn recalculate<'a>(&self, graph: Graph2Guard<'a>, node: NodeGuard<'a>) -> bool {
let this_anchor = &node.anchor;
let mut ecx = EngineContextMut {
engine: &self,
node,
graph,
pending_on_anchor_get: false,
};
let poll_result = this_anchor
.borrow_mut()
.as_mut()
.unwrap()
.poll_updated(&mut ecx);
let pending_on_anchor_get = ecx.pending_on_anchor_get;
match poll_result {
Poll::Pending => {
if pending_on_anchor_get {
false
} else {
panic!("poll_updated return pending without requesting another anchor");
}
}
Poll::Updated => {
mark_dirty(graph, node, true);
node.last_update.set(Some(self.generation));
node.last_ready.set(Some(self.generation));
true
}
Poll::Unchanged => {
node.last_ready.set(Some(self.generation));
true
}
}
}
pub fn debug_state(&self) -> String {
let debug = "".to_string();
debug
}
pub fn check_observed<T>(&self, anchor: &Anchor<T, Engine>) -> ObservedState {
self.graph.with(|graph| {
let node = graph.get(anchor.token()).unwrap();
Self::check_observed_raw(node)
})
}
pub fn check_observed_raw<'a>(node: NodeGuard<'a>) -> ObservedState {
if node.observed.get() {
return ObservedState::Observed;
}
if node.necessary_count.get() > 0 {
ObservedState::Necessary
} else {
ObservedState::Unnecessary
}
}
}
fn mark_dirty<'a>(graph: Graph2Guard<'a>, node: NodeGuard<'a>, skip_self: bool) {
if skip_self {
let parents = node.drain_clean_parents();
for parent in parents {
parent
.anchor
.borrow_mut()
.as_mut()
.unwrap()
.dirty(&node.key());
mark_dirty0(graph, parent);
}
} else {
mark_dirty0(graph, node);
}
}
fn mark_dirty0<'a>(graph: Graph2Guard<'a>, next: NodeGuard<'a>) {
let id = next.key();
if Engine::check_observed_raw(next) != ObservedState::Unnecessary {
graph.queue_recalc(next);
} else if graph2::recalc_state(next) == RecalcState::Ready {
graph2::needs_recalc(next);
let parents = next.drain_clean_parents();
for parent in parents {
if let Some(v) = parent.anchor.borrow_mut().as_mut() {
v.dirty(&id);
mark_dirty0(graph, parent);
}
}
}
}
#[derive(Debug, Clone)]
pub struct DirtyHandle {
num: NodeKey,
dirty_marks: Rc<RefCell<Vec<NodeKey>>>,
}
impl crate::DirtyHandle for DirtyHandle {
fn mark_dirty(&self) {
self.dirty_marks.borrow_mut().push(self.num);
}
}
struct EngineContext<'eng> {
engine: &'eng Engine,
}
struct EngineContextMut<'eng, 'gg> {
engine: &'eng Engine,
graph: Graph2Guard<'gg>,
node: NodeGuard<'gg>,
pending_on_anchor_get: bool,
}
impl<'eng> OutputContext<'eng> for EngineContext<'eng> {
type Engine = Engine;
fn get<'out, O: 'static>(&self, anchor: &Anchor<O, Self::Engine>) -> &'out O
where
'eng: 'out,
{
self.engine.graph.with(|graph| {
let node = graph.get(anchor.token()).unwrap();
if graph2::recalc_state(node) != RecalcState::Ready {
panic!("attempted to get node that was not previously requested")
}
let unsafe_borrow = unsafe { node.anchor.as_ptr().as_ref().unwrap() };
let output: &O = unsafe_borrow
.as_ref()
.unwrap()
.output(&mut EngineContext {
engine: self.engine,
})
.downcast_ref()
.unwrap();
output
})
}
}
impl<'eng, 'gg> UpdateContext for EngineContextMut<'eng, 'gg> {
type Engine = Engine;
fn get<'out, 'slf, O: 'static>(&'slf self, anchor: &Anchor<O, Self::Engine>) -> &'out O
where
'slf: 'out,
{
self.engine.graph.with(|graph| {
let node = graph.get(anchor.token()).unwrap();
if graph2::recalc_state(node) != RecalcState::Ready {
panic!("attempted to get node that was not previously requested")
}
let unsafe_borrow = unsafe { node.anchor.as_ptr().as_ref().unwrap() };
let output: &O = unsafe_borrow
.as_ref()
.unwrap()
.output(&mut EngineContext {
engine: self.engine,
})
.downcast_ref()
.unwrap();
output
})
}
fn request<'out, O: 'static>(
&mut self,
anchor: &Anchor<O, Self::Engine>,
necessary: bool,
) -> Poll {
let child = self.graph.get(anchor.token()).unwrap();
let height_already_increased = match graph2::ensure_height_increases(child, self.node) {
Ok(v) => v,
Err(()) => {
panic!("loop detected in anchors!\n");
}
};
let self_is_necessary = Engine::check_observed_raw(self.node) != ObservedState::Unnecessary;
if graph2::recalc_state(child) != RecalcState::Ready {
self.pending_on_anchor_get = true;
self.graph.queue_recalc(child);
if necessary && self_is_necessary {
self.node.add_necessary_child(child);
}
Poll::Pending
} else if !height_already_increased {
self.pending_on_anchor_get = true;
Poll::Pending
} else {
child.add_clean_parent(self.node);
if necessary && self_is_necessary {
self.node.add_necessary_child(child);
}
match (child.last_update.get(), self.node.last_ready.get()) {
(Some(a), Some(b)) if a <= b => Poll::Unchanged,
_ => Poll::Updated,
}
}
}
fn unrequest<'out, O: 'static>(&mut self, anchor: &Anchor<O, Self::Engine>) {
let child = self.graph.get(anchor.token()).unwrap();
self.node.remove_necessary_child(child);
Engine::update_necessary_children(child);
}
fn dirty_handle(&mut self) -> DirtyHandle {
DirtyHandle {
num: self.node.key(),
dirty_marks: self.engine.dirty_marks.clone(),
}
}
}
trait GenericAnchor {
fn dirty(&mut self, child: &NodeKey);
fn poll_updated<'eng, 'gg>(&mut self, ctx: &mut EngineContextMut<'eng, 'gg>) -> Poll;
fn output<'slf, 'out>(&'slf self, ctx: &mut EngineContext<'out>) -> &'out dyn Any
where
'slf: 'out;
fn debug_info(&self) -> AnchorDebugInfo;
}
impl<I: AnchorInner<Engine> + 'static> GenericAnchor for I {
fn dirty(&mut self, child: &NodeKey) {
AnchorInner::dirty(self, child)
}
fn poll_updated<'eng, 'gg>(&mut self, ctx: &mut EngineContextMut<'eng, 'gg>) -> Poll {
AnchorInner::poll_updated(self, ctx)
}
fn output<'slf, 'out>(&'slf self, ctx: &mut EngineContext<'out>) -> &'out dyn Any
where
'slf: 'out,
{
AnchorInner::output(self, ctx)
}
fn debug_info(&self) -> AnchorDebugInfo {
AnchorDebugInfo {
location: self.debug_location(),
type_info: std::any::type_name::<I>(),
}
}
}
#[derive(Debug, Clone, Copy)]
struct AnchorDebugInfo {
location: Option<(&'static str, &'static Location<'static>)>,
type_info: &'static str,
}
impl AnchorDebugInfo {
fn to_string(&self) -> String {
match self.location {
Some((name, location)) => format!("{} ({})", location, name),
None => format!("{}", self.type_info),
}
}
}