wip: move to slab

This commit is contained in:
Jonathan Kelley committed 2021-07-23 17:03:51 -04:00
1 parent f644d7c441
commit 6084fbcd11
7 files changed
+159 -170

No files matched your search

+94 -112
View File
@@ -36,10 +36,9 @@
//!
//! ## Garbage Collection
//! ---------------------
//! We roughly place the role of garbage collection onto the reconciler. Dioxus needs to manage the lifecycle of components
//! but will not spend any time cleaning up old elements. It's the Reconciler's duty to understand which elements need to
//! be cleaned up *after* the diffing is completed. The reconciler should schedule this garbage collection as the absolute
//! lowest priority task, after all edits have been applied.
//! Dioxus uses a passive garbage collection system to clean up old nodes once the work has been completed. This garabge
//! collection is done internally once the main diffing work is complete. After the "garbage" is collected, Dioxus will then
//! start to re-use old keys for new nodes. This results in a passive memory management system that is very efficient.
//!
//!
//! Further Reading and Thoughts
@@ -54,8 +53,6 @@ use smallvec::{smallvec, SmallVec};
use std::{any::Any, borrow::Borrow};
/// The accompanying "real dom" exposes an imperative API for controlling the UI layout
///
/// Instead of having handles directly over nodes, Dioxus uses simple u64s as node IDs.
/// The expectation is that the underlying renderer will mainain their Nodes in something like slotmap or an ECS memory
/// where indexing is very fast. For reference, the slotmap in the WebSys renderer takes about 3ns to randomly access any
@@ -69,39 +66,36 @@ pub trait RealDom<'a> {
fn raw_node_as_any(&self) -> &mut dyn Any;
}
pub struct DiffMachine<'real, 'bump, Dom: RealDom<'bump>> {
pub real_dom: &'real mut Dom,
pub struct DiffMachine<'real, 'bump> {
pub real_dom: &'real dyn RealDom<'bump>,
pub vdom: &'bump SharedResources,
pub edits: DomEditor<'real, 'bump>,
pub scheduled_garbage: Vec<&'bump VNode<'bump>>,
pub cur_idxs: SmallVec<[ScopeId; 5]>,
pub diffed: FxHashSet<ScopeId>,
pub seen_nodes: FxHashSet<ScopeId>,
}
impl<'r, 'b, D: RealDom<'b>> DiffMachine<'r, 'b, D> {
impl<'r, 'b> DiffMachine<'r, 'b> {
pub fn get_scope_mut(&mut self, id: &ScopeId) -> Option<&'b mut Scope> {
// ensure we haven't seen this scope before
// if we have, then we're trying to alias it, which is not allowed
debug_assert!(!self.seen_nodes.contains(id));
let compon = unsafe { &mut *self.vdom.components.get() };
compon.get_mut(*id)
unsafe { self.vdom.get_scope_mut(*id) }
}
pub fn get_scope(&mut self, id: &ScopeId) -> Option<&'b Scope> {
// ensure we haven't seen this scope before
// if we have, then we're trying to alias it, which is not allowed
let compon = unsafe { &*self.vdom.components.get() };
compon.get(*id)
unsafe { self.vdom.get_scope(*id) }
}
}
impl<'real, 'bump, Dom> DiffMachine<'real, 'bump, Dom>
where
Dom: RealDom<'bump>,
{
impl<'real, 'bump> DiffMachine<'real, 'bump> {
pub fn new(
edits: &'real mut Vec<DomEdit<'bump>>,
dom: &'real mut Dom,
dom: &'real dyn RealDom<'bump>,
cur_idx: ScopeId,
shared: &'bump SharedResources,
) -> Self {
@@ -110,6 +104,7 @@ where
edits: DomEditor::new(edits),
cur_idxs: smallvec![cur_idx],
vdom: shared,
scheduled_garbage: vec![],
diffed: FxHashSet::default(),
seen_nodes: FxHashSet::default(),
}
@@ -151,6 +146,7 @@ where
self.edits.push_root(root);
let meta = self.create(new_node);
self.edits.replace_with(meta.added_to_stack);
self.scheduled_garbage.push(old_node);
self.edits.pop();
return;
}
@@ -208,14 +204,15 @@ where
// remove any leftovers
for to_remove in old_iter {
self.edits.push_root(to_remove);
self.edits.push_root(to_remove.element_id().unwrap());
self.edits.remove();
}
// seems like we could combine this into a single instruction....
self.edits.push_root(first);
self.edits.push_root(first.element_id().unwrap());
let meta = self.create(new_node);
self.edits.replace_with(meta.added_to_stack);
self.scheduled_garbage.push(old_node);
self.edits.pop();
// Wipe the old one and plant the new one
@@ -272,11 +269,11 @@ where
// remove any leftovers
for to_remove in old_iter {
self.edits.push_root(to_remove);
self.edits.push_root(to_remove.element_id().unwrap());
self.edits.remove();
}
back_node
back_node.element_id().unwrap()
}
};
@@ -314,10 +311,7 @@ impl CreateMeta {
}
}
impl<'real, 'bump, Dom> DiffMachine<'real, 'bump, Dom>
where
Dom: RealDom<'bump>,
{
impl<'real, 'bump> DiffMachine<'real, 'bump> {
// Emit instructions to create the given virtual node.
//
// The change list stack may have any shape upon entering this function:
@@ -448,8 +442,7 @@ where
// .insert(idx);
// TODO: abstract this unsafe into the arena abstraction
let inner: &'bump mut _ = unsafe { &mut *self.vdom.components.get() };
let new_component = inner.get_mut(new_idx).unwrap();
let new_component = self.get_scope_mut(&new_idx).unwrap();
// Actually initialize the caller's slot with the right address
vcomponent.ass_scope.set(Some(new_idx));
@@ -498,7 +491,7 @@ where
}
}
impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
impl<'a, 'bump> DiffMachine<'a, 'bump> {
/// Destroy a scope and all of its descendents.
///
/// Calling this will run the destuctors on all hooks in the tree.
@@ -727,7 +720,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
// [... parent]
//
// Upon exiting, the change list stack is in the same state.
fn diff_keyed_children(&self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
fn diff_keyed_children(&mut self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
// todo!();
if cfg!(debug_assertions) {
let mut keys = fxhash::FxHashSet::default();
@@ -761,11 +754,6 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
KeyedPrefixResult::MoreWorkToDo(count) => count,
};
match self.diff_keyed_prefix(old, new) {
KeyedPrefixResult::Finished => return,
KeyedPrefixResult::MoreWorkToDo(count) => count,
};
// Next, we find out how many of the nodes at the end of the children have
// the same key. We do _not_ diff them yet, since we want to emit the change
// list instructions such that they can be applied in a single pass over the
@@ -812,46 +800,47 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
//
// Upon exit, the change list stack is the same.
fn diff_keyed_prefix(
&self,
_old: &'bump [VNode<'bump>],
_new: &'bump [VNode<'bump>],
&mut self,
old: &'bump [VNode<'bump>],
new: &'bump [VNode<'bump>],
) -> KeyedPrefixResult {
todo!()
// self.edits.go_down();
// let mut shared_prefix_count = 0;
// for (i, (old, new)) in old.iter().zip(new.iter()).enumerate() {
// if old.key() != new.key() {
// break;
// }
let mut shared_prefix_count = 0;
// self.edits.go_to_sibling(i);
for (i, (old, new)) in old.iter().zip(new.iter()).enumerate() {
// abort early if we finally run into nodes with different keys
if old.key() != new.key() {
break;
}
// self.diff_node(old, new);
// self.edits.go_to_sibling(i);
// shared_prefix_count += 1;
// }
self.diff_node(old, new);
// // If that was all of the old children, then create and append the remaining
// // new children and we're finished.
// if shared_prefix_count == old.len() {
// self.edits.go_up();
// // self.edits.commit_traversal();
// self.create_and_append_children(&new[shared_prefix_count..]);
// return KeyedPrefixResult::Finished;
// }
shared_prefix_count += 1;
}
// // And if that was all of the new children, then remove all of the remaining
// // old children and we're finished.
// if shared_prefix_count == new.len() {
// self.edits.go_to_sibling(shared_prefix_count);
// // self.edits.commit_traversal();
// self.remove_self_and_next_siblings(&old[shared_prefix_count..]);
// return KeyedPrefixResult::Finished;
// }
// If that was all of the old children, then create and append the remaining
// new children and we're finished.
if shared_prefix_count == old.len() {
// self.edits.go_up();
// self.edits.commit_traversal();
self.create_and_append_children(&new[shared_prefix_count..]);
return KeyedPrefixResult::Finished;
}
// And if that was all of the new children, then remove all of the remaining
// old children and we're finished.
if shared_prefix_count == new.len() {
// self.edits.go_to_sibling(shared_prefix_count);
// self.edits.commit_traversal();
self.remove_self_and_next_siblings(&old[shared_prefix_count..]);
return KeyedPrefixResult::Finished;
}
//
// self.edits.go_up();
// KeyedPrefixResult::MoreWorkToDo(shared_prefix_count)
KeyedPrefixResult::MoreWorkToDo(shared_prefix_count)
}
// Remove all of a node's children.
@@ -902,13 +891,12 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
// Upon exit from this function, it will be restored to that same state.
fn diff_keyed_middle(
&self,
_old: &[VNode<'bump>],
_new: &[VNode<'bump>],
_shared_prefix_count: usize,
_shared_suffix_count: usize,
_old_shared_suffix_start: usize,
old: &[VNode<'bump>],
new: &[VNode<'bump>],
shared_prefix_count: usize,
shared_suffix_count: usize,
old_shared_suffix_start: usize,
) {
todo!()
// // Should have already diffed the shared-key prefixes and suffixes.
// debug_assert_ne!(new.first().map(|n| n.key()), old.first().map(|o| o.key()));
// debug_assert_ne!(new.last().map(|n| n.key()), old.last().map(|o| o.key()));
@@ -921,24 +909,30 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
// debug_assert!(new.len() < u32::MAX as usize);
// // Map from each `old` node's key to its index within `old`.
// let mut old_key_to_old_index = FxHashMap::default();
// old_key_to_old_index.reserve(old.len());
// old_key_to_old_index.extend(old.iter().enumerate().map(|(i, o)| (o.key(), i)));
// // IE if the keys were A B C, then we would have (A, 1) (B, 2) (C, 3).
// let mut old_key_to_old_index = old
// .iter()
// .enumerate()
// .map(|(i, o)| (o.key(), i))
// .collect::<FxHashMap<_, _>>();
// // The set of shared keys between `new` and `old`.
// let mut shared_keys = FxHashSet::default();
// // Map from each index in `new` to the index of the node in `old` that
// // has the same key.
// let mut new_index_to_old_index = Vec::with_capacity(new.len());
// new_index_to_old_index.extend(new.iter().map(|n| {
// let key = n.key();
// if let Some(&i) = old_key_to_old_index.get(&key) {
// shared_keys.insert(key);
// i
// } else {
// u32::MAX as usize
// }
// }));
// let mut new_index_to_old_index = new
// .iter()
// .map(|n| {
// let key = n.key();
// if let Some(&i) = old_key_to_old_index.get(&key) {
// shared_keys.insert(key);
// i
// } else {
// u32::MAX as usize
// }
// })
// .collect::<Vec<_>>();
// // If none of the old keys are reused by the new children, then we
// // remove all the remaining old children and create the new children
@@ -948,7 +942,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
// // self.edits.commit_traversal();
// self.remove_all_children(old);
// } else {
// self.edits.go_down_to_child(shared_prefix_count);
// // self.edits.go_down_to_child(shared_prefix_count);
// // self.edits.commit_traversal();
// self.remove_self_and_next_siblings(&old[shared_prefix_count..]);
// }
@@ -998,6 +992,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
// // registry.remove_subtree(old_child);
// // todo
// // self.edits.commit_traversal();
// self.edits.remove(old_child.dom_id.get());
// self.edits.remove_child(i + shared_prefix_count);
// removed_count += 1;
// }
@@ -1115,10 +1110,6 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
// self.diff_node(&old[old_index], new_child);
// }
// }
// // [... parent child]
// self.edits.go_up();
// [... parent]
}
// Diff the suffix of keyed children that share the same keys in the same order.
@@ -1129,26 +1120,17 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
//
// When this function exits, the change list stack remains the same.
fn diff_keyed_suffix(
&self,
_old: &[VNode<'bump>],
_new: &[VNode<'bump>],
_new_shared_suffix_start: usize,
&mut self,
old: &'bump [VNode<'bump>],
new: &'bump [VNode<'bump>],
new_shared_suffix_start: usize,
) {
todo!()
// debug_assert_eq!(old.len(), new.len());
// debug_assert!(!old.is_empty());
debug_assert_eq!(old.len(), new.len());
debug_assert!(!old.is_empty());
// // [... parent]
// self.edits.go_down();
// // [... parent new_child]
// for (i, (old_child, new_child)) in old.iter().zip(new.iter()).enumerate() {
// self.edits.go_to_sibling(new_shared_suffix_start + i);
// self.diff_node(old_child, new_child);
// }
// // [... parent]
// self.edits.go_up();
for (i, (old_child, new_child)) in old.iter().zip(new.iter()).enumerate() {
self.diff_node(old_child, new_child);
}
}
// Diff children that are not keyed.
@@ -1299,11 +1281,11 @@ impl<'a> RealChildIterator<'a> {
}
impl<'a> Iterator for RealChildIterator<'a> {
type Item = ElementId;
type Item = &'a VNode<'a>;
fn next(&mut self) -> Option<ElementId> {
fn next(&mut self) -> Option<&'a VNode<'a>> {
let mut should_pop = false;
let mut returned_node = None;
let mut returned_node: Option<&'a VNode<'a>> = None;
let mut should_push = None;
while returned_node.is_none() {
@@ -1315,7 +1297,7 @@ impl<'a> Iterator for RealChildIterator<'a> {
// We've recursed INTO an element/text
// We need to recurse *out* of it and move forward to the next
should_pop = true;
returned_node = node.dom_id.get();
returned_node = Some(&*node);
}
// If we get a fragment we push the next child
@@ -1324,7 +1306,7 @@ impl<'a> Iterator for RealChildIterator<'a> {
if frag.children.len() == 0 {
should_pop = true;
returned_node = node.dom_id.get();
returned_node = Some(&*node);
}
if subcount >= frag.children.len() {