diff --git a/packages/native-core/src/lib.rs b/packages/native-core/src/lib.rs index 56c4a6a73..14089c669 100644 --- a/packages/native-core/src/lib.rs +++ b/packages/native-core/src/lib.rs @@ -1,3 +1,6 @@ +use std::hash::BuildHasherDefault; + +use rustc_hash::FxHasher; use tree::NodeId; pub mod layout_attributes; @@ -10,7 +13,10 @@ pub mod tree; pub mod utils; /// A id for a node that lives in the real dom. -type RealNodeId = NodeId; +pub type RealNodeId = NodeId; +pub(crate) type FxDashMap = dashmap::DashMap>; +pub(crate) type FxDashSet = dashmap::DashSet>; +pub(crate) type SendAnyMap = anymap::Map; /// Used in derived state macros #[derive(Eq, PartialEq)] diff --git a/packages/native-core/src/passes.rs b/packages/native-core/src/passes.rs index 6c8582170..86bb50537 100644 --- a/packages/native-core/src/passes.rs +++ b/packages/native-core/src/passes.rs @@ -1,13 +1,11 @@ -use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; +use crate::tree::{NodeId, TreeView}; +use crate::{FxDashMap, FxDashSet, SendAnyMap}; +use rustc_hash::{FxHashMap, FxHashSet}; use std::collections::BTreeMap; -use std::hash::BuildHasherDefault; use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign}; - use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; -use crate::tree::{NodeId, TreeView}; - #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct DirtyNodes { map: BTreeMap>, @@ -57,8 +55,6 @@ fn dirty_nodes() { assert_eq!(dirty_nodes.pop_front(), Some(NodeId(3))); } -type FxDashMap = dashmap::DashMap>; - #[derive(Default)] pub struct DirtyNodeStates { dirty: FxDashMap>, @@ -167,7 +163,12 @@ pub trait Pass { } pub trait UpwardPass: Pass { - fn pass<'a>(&self, node: &mut T, children: &mut dyn Iterator) -> PassReturn; + fn pass<'a>( + &self, + node: &mut T, + children: &mut dyn Iterator, + ctx: &SendAnyMap, + ) -> PassReturn; } fn resolve_upward_pass + ?Sized>( @@ -175,12 +176,15 @@ fn resolve_upward_pass + ?Sized>( pass: &P, mut dirty: DirtyNodes, dirty_states: &DirtyNodeStates, + nodes_updated: &FxDashSet, + ctx: &SendAnyMap, ) { while let Some(id) = dirty.pop_back() { let (node, mut children) = tree.parent_child_mut(id).unwrap(); - let result = pass.pass(node, &mut children); + let result = pass.pass(node, &mut children, ctx); drop(children); if result.progress || result.mark_dirty { + nodes_updated.insert(id); if let Some(id) = tree.parent_id(id) { if result.mark_dirty { for dependant in pass.dependants() { @@ -197,7 +201,7 @@ fn resolve_upward_pass + ?Sized>( } pub trait DownwardPass: Pass { - fn pass(&self, node: &mut T, parent: Option<&mut T>) -> PassReturn; + fn pass(&self, node: &mut T, parent: Option<&mut T>, ctx: &SendAnyMap) -> PassReturn; } fn resolve_downward_pass + ?Sized>( @@ -205,11 +209,14 @@ fn resolve_downward_pass + ?Sized>( pass: &P, mut dirty: DirtyNodes, dirty_states: &DirtyNodeStates, + nodes_updated: &FxDashSet, + ctx: &SendAnyMap, ) { while let Some(id) = dirty.pop_front() { let (node, parent) = tree.node_parent_mut(id).unwrap(); - let result = pass.pass(node, parent); + let result = pass.pass(node, parent, ctx); if result.mark_dirty || result.progress { + nodes_updated.insert(id); for id in tree.children_ids(id).unwrap() { if result.mark_dirty { for dependant in pass.dependants() { @@ -226,7 +233,7 @@ fn resolve_downward_pass + ?Sized>( } pub trait NodePass: Pass { - fn pass(&self, node: &mut T) -> bool; + fn pass(&self, node: &mut T, ctx: &SendAnyMap) -> bool; } fn resolve_node_pass + ?Sized>( @@ -234,10 +241,13 @@ fn resolve_node_pass + ?Sized>( pass: &P, mut dirty: DirtyNodes, dirty_states: &DirtyNodeStates, + nodes_updated: &FxDashSet, + ctx: &SendAnyMap, ) { while let Some(id) = dirty.pop_back() { let node = tree.get_mut(id).unwrap(); - if pass.pass(node) { + if pass.pass(node, ctx) { + nodes_updated.insert(id); for dependant in pass.dependants() { dirty_states.insert(*dependant, id); } @@ -245,14 +255,14 @@ fn resolve_node_pass + ?Sized>( } } -pub enum AnyPass { - Upward(Box + Send + Sync>), - Downward(Box + Send + Sync>), - Node(Box + Send + Sync>), +pub enum AnyPass { + Upward(&'static (dyn UpwardPass + Send + Sync + 'static)), + Downward(&'static (dyn DownwardPass + Send + Sync + 'static)), + Node(&'static (dyn NodePass + Send + Sync + 'static)), } impl AnyPass { - fn pass_id(&self) -> PassId { + pub fn pass_id(&self) -> PassId { match self { Self::Upward(pass) => pass.pass_id(), Self::Downward(pass) => pass.pass_id(), @@ -260,7 +270,7 @@ impl AnyPass { } } - fn dependancies(&self) -> &'static [PassId] { + pub fn dependancies(&self) -> &'static [PassId] { match self { Self::Upward(pass) => pass.dependancies(), Self::Downward(pass) => pass.dependancies(), @@ -281,11 +291,19 @@ impl AnyPass { tree: &mut impl TreeView, dirty: DirtyNodes, dirty_states: &DirtyNodeStates, + nodes_updated: &FxDashSet, + ctx: &SendAnyMap, ) { match self { - Self::Downward(pass) => resolve_downward_pass(tree, pass.as_ref(), dirty, dirty_states), - Self::Upward(pass) => resolve_upward_pass(tree, pass.as_ref(), dirty, dirty_states), - Self::Node(pass) => resolve_node_pass(tree, pass.as_ref(), dirty, dirty_states), + Self::Downward(pass) => { + resolve_downward_pass(tree, *pass, dirty, dirty_states, nodes_updated, ctx) + } + Self::Upward(pass) => { + resolve_upward_pass(tree, *pass, dirty, dirty_states, nodes_updated, ctx) + } + Self::Node(pass) => { + resolve_node_pass(tree, *pass, dirty, dirty_states, nodes_updated, ctx) + } } } } @@ -297,11 +315,14 @@ unsafe impl Sync for RawPointer {} pub fn resolve_passes>( tree: &mut Tr, dirty_nodes: DirtyNodeStates, - mut passes: Vec>, -) { + mut passes: Vec<&AnyPass>, + ctx: SendAnyMap, +) -> FxDashSet { let dirty_states = Arc::new(dirty_nodes); let mut resolved_passes: FxHashSet = FxHashSet::default(); let mut resolving = Vec::new(); + let nodes_updated = Arc::new(FxDashSet::default()); + let ctx = Arc::new(ctx); while !passes.is_empty() { let mut currently_borrowed = MemberMask::default(); std::thread::scope(|s| { @@ -322,13 +343,16 @@ pub fn resolve_passes>( let tree_mut = tree as *mut _; let raw_ptr = RawPointer(tree_mut); let dirty_states = dirty_states.clone(); + let nodes_updated = nodes_updated.clone(); + let ctx = ctx.clone(); s.spawn(move || unsafe { // let tree_mut: &mut Tr = &mut *raw_ptr.0; let raw = raw_ptr; + // this is safe because the member_mask acts as a per-member mutex and we have verified that the pass does not overlap with any other pass let tree_mut: &mut Tr = &mut *raw.0; let mut dirty = DirtyNodes::default(); dirty_states.all_dirty(pass_id, &mut dirty, tree_mut); - pass.resolve(tree_mut, dirty, &dirty_states); + pass.resolve(tree_mut, dirty, &dirty_states, &nodes_updated, &ctx); }); } else { i += 1; @@ -339,14 +363,13 @@ pub fn resolve_passes>( resolved_passes.extend(resolving.iter().copied()); resolving.clear() } + std::sync::Arc::try_unwrap(nodes_updated).unwrap() } #[test] fn node_pass() { use crate::tree::{Tree, TreeLike}; let mut tree = Tree::new(0); - let parent = tree.root(); - println!("{:#?}", tree); struct AddPass; impl Pass for AddPass { @@ -368,16 +391,17 @@ fn node_pass() { } impl NodePass for AddPass { - fn pass(&self, node: &mut i32) -> bool { + fn pass(&self, node: &mut i32, _: &SendAnyMap) -> bool { *node += 1; true } } - let passes = vec![AnyPass::Node(Box::new(AddPass))]; + let add_pass = AnyPass::Node(&AddPass); + let passes = vec![&add_pass]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(0), tree.root()); - resolve_passes(&mut tree, dirty_nodes, passes); + resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); assert_eq!(tree.get(tree.root()).unwrap(), &1); } @@ -407,8 +431,7 @@ fn dependant_node_pass() { } impl NodePass for AddPass { - fn pass(&self, node: &mut i32) -> bool { - println!("AddPass: {}", node); + fn pass(&self, node: &mut i32, _: &SendAnyMap) -> bool { *node += 1; true } @@ -434,20 +457,18 @@ fn dependant_node_pass() { } } impl NodePass for SubtractPass { - fn pass(&self, node: &mut i32) -> bool { - println!("SubtractPass: {}", node); + fn pass(&self, node: &mut i32, _: &SendAnyMap) -> bool { *node -= 1; true } } - let passes = vec![ - AnyPass::Node(Box::new(AddPass)), - AnyPass::Node(Box::new(SubtractPass)), - ]; + let add_pass = AnyPass::Node(&AddPass); + let subtract_pass = AnyPass::Node(&SubtractPass); + let passes = vec![&add_pass, &subtract_pass]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(1), tree.root()); - resolve_passes(&mut tree, dirty_nodes, passes); + resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); assert_eq!(*tree.get(tree.root()).unwrap(), 0); } @@ -477,7 +498,7 @@ fn independant_node_pass() { } impl NodePass<(i32, i32)> for AddPass1 { - fn pass(&self, node: &mut (i32, i32)) -> bool { + fn pass(&self, node: &mut (i32, i32), _: &SendAnyMap) -> bool { node.0 += 1; true } @@ -503,20 +524,19 @@ fn independant_node_pass() { } impl NodePass<(i32, i32)> for AddPass2 { - fn pass(&self, node: &mut (i32, i32)) -> bool { + fn pass(&self, node: &mut (i32, i32), _: &SendAnyMap) -> bool { node.1 += 1; true } } - let passes = vec![ - AnyPass::Node(Box::new(AddPass1)), - AnyPass::Node(Box::new(AddPass2)), - ]; + let add_pass1 = AnyPass::Node(&AddPass1); + let add_pass2 = AnyPass::Node(&AddPass2); + let passes = vec![&add_pass1, &add_pass2]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(0), tree.root()); dirty_nodes.insert(PassId(1), tree.root()); - resolve_passes(&mut tree, dirty_nodes, passes); + resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); assert_eq!(tree.get(tree.root()).unwrap(), &(1, 1)); } @@ -555,7 +575,7 @@ fn down_pass() { } } impl DownwardPass for AddPass { - fn pass(&self, node: &mut i32, parent: Option<&mut i32>) -> PassReturn { + fn pass(&self, node: &mut i32, parent: Option<&mut i32>, _: &SendAnyMap) -> PassReturn { if let Some(parent) = parent { *node += *parent; } @@ -566,10 +586,11 @@ fn down_pass() { } } - let passes = vec![AnyPass::Downward(Box::new(AddPass))]; + let add_pass = AnyPass::Downward(&AddPass); + let passes = vec![&add_pass]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(0), tree.root()); - resolve_passes(&mut tree, dirty_nodes, passes); + resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); assert_eq!(tree.get(tree.root()).unwrap(), &1); assert_eq!(tree.get(child1).unwrap(), &2); @@ -616,12 +637,10 @@ fn dependant_down_pass() { } } impl DownwardPass for AddPass { - fn pass(&self, node: &mut i32, parent: Option<&mut i32>) -> PassReturn { + fn pass(&self, node: &mut i32, parent: Option<&mut i32>, _: &SendAnyMap) -> PassReturn { if let Some(parent) = parent { - println!("AddPass: {} -> {}", node, *node + *parent); *node += *parent; } else { - println!("AddPass: {}", node); } PassReturn { progress: true, @@ -649,12 +668,10 @@ fn dependant_down_pass() { } } impl DownwardPass for SubtractPass { - fn pass(&self, node: &mut i32, parent: Option<&mut i32>) -> PassReturn { + fn pass(&self, node: &mut i32, parent: Option<&mut i32>, _: &SendAnyMap) -> PassReturn { if let Some(parent) = parent { - println!("SubtractPass: {} -> {}", node, *node - *parent); *node -= *parent; } else { - println!("SubtractPass: {}", node); } PassReturn { progress: true, @@ -663,13 +680,12 @@ fn dependant_down_pass() { } } - let passes = vec![ - AnyPass::Downward(Box::new(AddPass)), - AnyPass::Downward(Box::new(SubtractPass)), - ]; + let add_pass = AnyPass::Downward(&AddPass); + let subtract_pass = AnyPass::Downward(&SubtractPass); + let passes = vec![&add_pass, &subtract_pass]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(1), tree.root()); - resolve_passes(&mut tree, dirty_nodes, passes); + resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); // Tree before: // 1=\ @@ -745,6 +761,7 @@ fn up_pass() { &self, node: &mut i32, children: &mut dyn Iterator, + _: &SendAnyMap, ) -> PassReturn { *node += children.map(|i| *i).sum::(); PassReturn { @@ -754,11 +771,12 @@ fn up_pass() { } } - let passes = vec![AnyPass::Upward(Box::new(AddPass))]; + let add_pass = AnyPass::Upward(&AddPass); + let passes = vec![&add_pass]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(0), grandchild1); dirty_nodes.insert(PassId(0), grandchild2); - resolve_passes(&mut tree, dirty_nodes, passes); + resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); assert_eq!(tree.get(tree.root()).unwrap(), &2); assert_eq!(tree.get(child1).unwrap(), &1); @@ -809,6 +827,7 @@ fn dependant_up_pass() { &self, node: &mut i32, children: &mut dyn Iterator, + _: &SendAnyMap, ) -> PassReturn { *node += children.map(|i| *i).sum::(); PassReturn { @@ -841,6 +860,7 @@ fn dependant_up_pass() { &self, node: &mut i32, children: &mut dyn Iterator, + _: &SendAnyMap, ) -> PassReturn { *node -= children.map(|i| *i).sum::(); PassReturn { @@ -850,14 +870,13 @@ fn dependant_up_pass() { } } - let passes = vec![ - AnyPass::Upward(Box::new(AddPass)), - AnyPass::Upward(Box::new(SubtractPass)), - ]; + let add_pass = AnyPass::Upward(&AddPass); + let subtract_pass = AnyPass::Upward(&SubtractPass); + let passes = vec![&add_pass, &subtract_pass]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(1), grandchild1); dirty_nodes.insert(PassId(1), grandchild2); - resolve_passes(&mut tree, dirty_nodes, passes); + resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); // Tree before: // 0=\ diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index 683d3bb65..48c1febcc 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -1,13 +1,13 @@ -use anymap::AnyMap; use dioxus_core::{ElementId, Mutations}; use rustc_hash::{FxHashMap, FxHashSet}; use std::ops::{Index, IndexMut}; -use crate::node::{Node, NodeData, NodeType, OwnedAttributeDiscription}; +use crate::node::{Node, NodeType, OwnedAttributeDiscription}; use crate::node_ref::{AttributeMask, NodeMask}; +use crate::passes::DirtyNodeStates; use crate::state::State; use crate::tree::{NodeId, Tree, TreeLike, TreeView}; -use crate::RealNodeId; +use crate::{FxDashSet, RealNodeId, SendAnyMap}; fn mark_dirty( node_id: NodeId, @@ -96,10 +96,7 @@ impl RealDom { } /// Updates the dom with some mutations and return a set of nodes that were updated. Pass the dirty nodes to update_state. - pub fn apply_mutations( - &mut self, - mutations_vec: Vec, - ) -> FxHashMap { + pub fn apply_mutations(&mut self, mutations_vec: Vec) -> DirtyNodeStates { let mut nodes_updated: FxHashMap = FxHashMap::default(); for mutations in mutations_vec { for e in mutations.edits { @@ -346,20 +343,28 @@ impl RealDom { } } - // remove any nodes that were created and then removed in the same mutations from the dirty nodes list - nodes_updated.retain(|k, _| self.tree.get(*k).is_some()); + let dirty_nodes = DirtyNodeStates::default(); + for (n, mask) in nodes_updated { + // remove any nodes that were created and then removed in the same mutations from the dirty nodes list + if self.tree.contains(n) { + for (m, p) in S::MASKS.iter().zip(S::PASSES.iter()) { + if mask.overlaps(m) { + dirty_nodes.insert(p.pass_id(), n); + } + } + } + } - nodes_updated + dirty_nodes } /// Update the state of the dom, after appling some mutations. This will keep the nodes in the dom up to date with their VNode counterparts. pub fn update_state( &mut self, - nodes_updated: FxHashMap, - ctx: AnyMap, - ) -> FxHashSet { - let (mut state_tree, node_tree) = self.split(); - S::update(&nodes_updated, &mut state_tree, &node_tree, &ctx) + nodes_updated: DirtyNodeStates, + ctx: SendAnyMap, + ) -> FxDashSet { + S::update(nodes_updated, &mut self.tree, ctx) } /// Find all nodes that are listening for an event, sorted by there height in the dom progressing starting at the bottom and progressing up. @@ -389,19 +394,6 @@ impl RealDom { self.tree.root() } - pub fn split(&mut self) -> (impl TreeView + '_, impl TreeView + '_) { - let raw = self as *mut Self; - // this is safe beacuse the treeview trait does not allow mutation of the position of elements, and within elements the access is disjoint. - ( - unsafe { &mut *raw } - .tree - .map(|n| &n.state, |n| &mut n.state), - unsafe { &mut *raw } - .tree - .map(|n| &n.node_data, |n| &mut n.node_data), - ) - } - fn clone_node(&mut self, node_id: NodeId) -> RealNodeId { let node = self.tree.get(node_id).unwrap(); let new_node = node.clone(); diff --git a/packages/native-core/src/state.rs b/packages/native-core/src/state.rs index 18d405ed7..09ae4333b 100644 --- a/packages/native-core/src/state.rs +++ b/packages/native-core/src/state.rs @@ -1,11 +1,10 @@ use std::cmp::Ordering; -use crate::node::NodeData; +use crate::node::Node; use crate::node_ref::{NodeMask, NodeView}; +use crate::passes::{resolve_passes, AnyPass, DirtyNodeStates}; use crate::tree::TreeView; -use crate::RealNodeId; -use anymap::AnyMap; -use rustc_hash::{FxHashMap, FxHashSet}; +use crate::{FxDashSet, RealNodeId, SendAnyMap}; /// Join two sorted iterators pub(crate) fn union_ordered_iter<'a>( @@ -204,20 +203,29 @@ pub trait NodeDepState { fn reduce<'a>( &mut self, node: NodeView, - siblings: ::ElementBorrowed<'a>, + node_state: ::ElementBorrowed<'a>, ctx: &Self::Ctx, ) -> bool; } /// Do not implement this trait. It is only meant to be derived and used through [crate::real_dom::RealDom]. -pub trait State: Default + Clone { +pub trait State: Default + Clone + 'static { #[doc(hidden)] - fn update<'a, T: TreeView, T2: TreeView>( - dirty: &FxHashMap, - state_tree: &'a mut T, - rdom: &'a T2, - ctx: &AnyMap, - ) -> FxHashSet; + const PASSES: &'static [AnyPass>]; + #[doc(hidden)] + const MASKS: &'static [NodeMask]; + + #[doc(hidden)] + fn update>>( + dirty: DirtyNodeStates, + tree: &mut T, + ctx: SendAnyMap, + ) -> FxDashSet { + let set = FxDashSet::default(); + let passes = Self::PASSES.iter().collect(); + resolve_passes(tree, dirty, passes, ctx); + set + } } impl ChildDepState for () { diff --git a/packages/native-core/src/tree.rs b/packages/native-core/src/tree.rs index 53f81ba54..0547fc3a9 100644 --- a/packages/native-core/src/tree.rs +++ b/packages/native-core/src/tree.rs @@ -73,6 +73,10 @@ pub trait TreeView: Sized { fn root(&self) -> NodeId; + fn contains(&self, id: NodeId) -> bool { + self.get(id).is_some() + } + fn get(&self, id: NodeId) -> Option<&T>; fn get_unchecked(&self, id: NodeId) -> &T {