浏览代码

chore: clean up scheduler code

Jonathan Kelley 2 年之前
父节点
当前提交
f5bc137f01

+ 0 - 6
packages/core/Cargo.toml

@@ -23,13 +23,8 @@ fxhash = "0.2"
 # Used in diffing
 # Used in diffing
 longest-increasing-subsequence = "0.1.0"
 longest-increasing-subsequence = "0.1.0"
 
 
-# internall used
-log = { version = "0.4" }
-
 futures-util = { version = "0.3", default-features = false }
 futures-util = { version = "0.3", default-features = false }
 
 
-smallvec = "1.6"
-
 slab = "0.4"
 slab = "0.4"
 
 
 futures-channel = "0.3.21"
 futures-channel = "0.3.21"
@@ -38,7 +33,6 @@ indexmap = "1.7"
 
 
 # Serialize the Edits for use in Webview/Liveview instances
 # Serialize the Edits for use in Webview/Liveview instances
 serde = { version = "1", features = ["derive"], optional = true }
 serde = { version = "1", features = ["derive"], optional = true }
-futures-task = "0.3.25"
 
 
 [dev-dependencies]
 [dev-dependencies]
 tokio = { version = "*", features = ["full"] }
 tokio = { version = "*", features = ["full"] }

+ 5 - 8
packages/core/src/any_props.rs

@@ -21,7 +21,7 @@ where
 {
 {
     pub render_fn: fn(Scope<'a, P>) -> F,
     pub render_fn: fn(Scope<'a, P>) -> F,
     pub memo: unsafe fn(&P, &P) -> bool,
     pub memo: unsafe fn(&P, &P) -> bool,
-    pub props: *const P,
+    pub props: P,
     pub _marker: PhantomData<A>,
     pub _marker: PhantomData<A>,
 }
 }
 
 
@@ -30,7 +30,7 @@ impl<'a> VComponentProps<'a, (), ()> {
         Self {
         Self {
             render_fn,
             render_fn,
             memo: <() as PartialEq>::eq,
             memo: <() as PartialEq>::eq,
-            props: std::ptr::null_mut(),
+            props: (),
             _marker: PhantomData,
             _marker: PhantomData,
         }
         }
     }
     }
@@ -40,7 +40,7 @@ impl<'a, P, A, F: ComponentReturn<'a, A>> VComponentProps<'a, P, A, F> {
     pub(crate) fn new(
     pub(crate) fn new(
         render_fn: fn(Scope<'a, P>) -> F,
         render_fn: fn(Scope<'a, P>) -> F,
         memo: unsafe fn(&P, &P) -> bool,
         memo: unsafe fn(&P, &P) -> bool,
-        props: *const P,
+        props: P,
     ) -> Self {
     ) -> Self {
         Self {
         Self {
             render_fn,
             render_fn,
@@ -66,12 +66,9 @@ impl<'a, P, A, F: ComponentReturn<'a, A>> AnyProps<'a> for VComponentProps<'a, P
         (self.memo)(real_us, real_other)
         (self.memo)(real_us, real_other)
     }
     }
 
 
-    fn render(&self, cx: &'a ScopeState) -> RenderReturn<'a> {
-        // Make sure the scope ptr is not null
-        // self.props.state.set(scope);
-
+    fn render(&'a self, cx: &'a ScopeState) -> RenderReturn<'a> {
         let scope = cx.bump().alloc(Scoped {
         let scope = cx.bump().alloc(Scoped {
-            props: unsafe { &*self.props },
+            props: &self.props,
             scope: cx,
             scope: cx,
         });
         });
 
 

+ 50 - 43
packages/core/src/create.rs

@@ -1,20 +1,32 @@
-use std::pin::Pin;
-
-use crate::factory::{FiberLeaf, RenderReturn};
-use crate::innerlude::{Renderer, SuspenseContext};
+use crate::factory::RenderReturn;
+use crate::innerlude::{Mutations, SuspenseContext};
 use crate::mutations::Mutation;
 use crate::mutations::Mutation;
 use crate::mutations::Mutation::*;
 use crate::mutations::Mutation::*;
 use crate::nodes::VNode;
 use crate::nodes::VNode;
 use crate::nodes::{DynamicNode, TemplateNode};
 use crate::nodes::{DynamicNode, TemplateNode};
 use crate::virtual_dom::VirtualDom;
 use crate::virtual_dom::VirtualDom;
-use crate::{AttributeValue, Element, ElementId, TemplateAttribute};
-use bumpalo::boxed::Box as BumpBox;
-use futures_util::Future;
+use crate::{AttributeValue, ElementId, ScopeId, TemplateAttribute};
 
 
 impl VirtualDom {
 impl VirtualDom {
+    pub(crate) fn create_scope<'a>(
+        &mut self,
+        scope: ScopeId,
+        mutations: &mut Mutations<'a>,
+        template: &'a VNode<'a>,
+    ) -> usize {
+        self.scope_stack.push(scope);
+        let out = self.create(mutations, template);
+        self.scope_stack.pop();
+        out
+    }
+
     /// Create this template and write its mutations
     /// Create this template and write its mutations
-    pub fn create<'a>(&mut self, mutations: &mut Renderer<'a>, template: &'a VNode<'a>) -> usize {
-        // The best renderers will have templates prehydrated
+    pub(crate) fn create<'a>(
+        &mut self,
+        mutations: &mut Mutations<'a>,
+        template: &'a VNode<'a>,
+    ) -> usize {
+        // The best renderers will have templates prehydrated and registered
         // Just in case, let's create the template using instructions anyways
         // Just in case, let's create the template using instructions anyways
         if !self.templates.contains_key(&template.template.id) {
         if !self.templates.contains_key(&template.template.id) {
             for node in template.template.roots {
             for node in template.template.roots {
@@ -31,7 +43,7 @@ impl VirtualDom {
                 .insert(template.template.id, template.template.clone());
                 .insert(template.template.id, template.template.clone());
         }
         }
 
 
-        // Walk the roots backwards, creating nodes and assigning IDs
+        // Walk the roots, creating nodes and assigning IDs
         // todo: adjust dynamic nodes to be in the order of roots and then leaves (ie BFS)
         // todo: adjust dynamic nodes to be in the order of roots and then leaves (ie BFS)
         let mut dynamic_attrs = template.template.attr_paths.iter().enumerate().peekable();
         let mut dynamic_attrs = template.template.attr_paths.iter().enumerate().peekable();
         let mut dynamic_nodes = template.template.node_paths.iter().enumerate().peekable();
         let mut dynamic_nodes = template.template.node_paths.iter().enumerate().peekable();
@@ -39,25 +51,23 @@ impl VirtualDom {
         let mut on_stack = 0;
         let mut on_stack = 0;
         for (root_idx, root) in template.template.roots.iter().enumerate() {
         for (root_idx, root) in template.template.roots.iter().enumerate() {
             on_stack += match root {
             on_stack += match root {
-                TemplateNode::Element { .. } | TemplateNode::Text(_) => {
+                TemplateNode::Element { .. }
+                | TemplateNode::Text(_)
+                | TemplateNode::DynamicText { .. } => {
                     mutations.push(LoadTemplate {
                     mutations.push(LoadTemplate {
                         name: template.template.id,
                         name: template.template.id,
                         index: root_idx,
                         index: root_idx,
                     });
                     });
-
                     1
                     1
                 }
                 }
 
 
                 TemplateNode::Dynamic(id) => {
                 TemplateNode::Dynamic(id) => {
                     self.create_dynamic_node(mutations, template, &template.dynamic_nodes[*id], *id)
                     self.create_dynamic_node(mutations, template, &template.dynamic_nodes[*id], *id)
                 }
                 }
-
-                TemplateNode::DynamicText { .. } => 1,
             };
             };
 
 
             // we're on top of a node that has a dynamic attribute for a descendant
             // we're on top of a node that has a dynamic attribute for a descendant
             // Set that attribute now before the stack gets in a weird state
             // Set that attribute now before the stack gets in a weird state
-
             while let Some((mut attr_id, path)) =
             while let Some((mut attr_id, path)) =
                 dynamic_attrs.next_if(|(_, p)| p[0] == root_idx as u8)
                 dynamic_attrs.next_if(|(_, p)| p[0] == root_idx as u8)
             {
             {
@@ -67,18 +77,17 @@ impl VirtualDom {
                     id,
                     id,
                 });
                 });
 
 
-                // set any future attrs with the same path (ie same element)
                 loop {
                 loop {
-                    let attr = &template.dynamic_attrs[attr_id];
-                    attr.mounted_element.set(id);
+                    let attribute = &template.dynamic_attrs[attr_id];
+                    attribute.mounted_element.set(id);
 
 
-                    match attr.value {
+                    match attribute.value {
                         AttributeValue::Text(value) => mutations.push(SetAttribute {
                         AttributeValue::Text(value) => mutations.push(SetAttribute {
-                            name: attr.name,
+                            name: attribute.name,
                             value,
                             value,
                             id,
                             id,
                         }),
                         }),
-                        AttributeValue::Listener(_) => {}
+                        AttributeValue::Listener(_) => todo!("create listener attributes"),
                         AttributeValue::Float(_) => todo!(),
                         AttributeValue::Float(_) => todo!(),
                         AttributeValue::Int(_) => todo!(),
                         AttributeValue::Int(_) => todo!(),
                         AttributeValue::Bool(_) => todo!(),
                         AttributeValue::Bool(_) => todo!(),
@@ -86,10 +95,10 @@ impl VirtualDom {
                         AttributeValue::None => todo!(),
                         AttributeValue::None => todo!(),
                     }
                     }
 
 
-                    if let Some((next_attr_id, _)) = dynamic_attrs.next_if(|(_, p)| *p == path) {
-                        attr_id = next_attr_id
-                    } else {
-                        break;
+                    // Only push the dynamic attributes forward if they match the current path (same element)
+                    match dynamic_attrs.next_if(|(_, p)| *p == path) {
+                        Some((next_attr_id, _)) => attr_id = next_attr_id,
+                        None => break,
                     }
                     }
                 }
                 }
             }
             }
@@ -113,7 +122,7 @@ impl VirtualDom {
         on_stack
         on_stack
     }
     }
 
 
-    pub fn create_static_node<'a>(
+    pub(crate) fn create_static_node<'a>(
         &mut self,
         &mut self,
         mutations: &mut Vec<Mutation<'a>>,
         mutations: &mut Vec<Mutation<'a>>,
         template: &'a VNode<'a>,
         template: &'a VNode<'a>,
@@ -156,9 +165,9 @@ impl VirtualDom {
         }
         }
     }
     }
 
 
-    pub fn create_dynamic_node<'a>(
+    pub(crate) fn create_dynamic_node<'a>(
         &mut self,
         &mut self,
-        mutations: &mut Renderer<'a>,
+        mutations: &mut Mutations<'a>,
         template: &'a VNode<'a>,
         template: &'a VNode<'a>,
         node: &'a DynamicNode<'a>,
         node: &'a DynamicNode<'a>,
         idx: usize,
         idx: usize,
@@ -172,24 +181,22 @@ impl VirtualDom {
                     path: &template.template.node_paths[idx][1..],
                     path: &template.template.node_paths[idx][1..],
                     value,
                     value,
                 });
                 });
-
                 1
                 1
             }
             }
 
 
             DynamicNode::Component {
             DynamicNode::Component {
                 props, placeholder, ..
                 props, placeholder, ..
             } => {
             } => {
-                let id = self.new_scope(unsafe { std::mem::transmute(props.get()) });
-                let render_ret = self.run_scope(id);
-                let render_ret: &mut RenderReturn = unsafe { std::mem::transmute(render_ret) };
-
-                // if boundary or subtree, start working on a new stack of mutations
+                let scope = self
+                    .new_scope(unsafe { std::mem::transmute(props.get()) })
+                    .id;
+                let return_nodes = unsafe { self.run_scope(scope).extend_lifetime_ref() };
 
 
-                match render_ret {
+                match return_nodes {
                     RenderReturn::Sync(None) | RenderReturn::Async(_) => {
                     RenderReturn::Sync(None) | RenderReturn::Async(_) => {
                         let new_id = self.next_element(template);
                         let new_id = self.next_element(template);
                         placeholder.set(Some(new_id));
                         placeholder.set(Some(new_id));
-                        self.scopes[id.0].placeholder.set(Some(new_id));
+                        self.scopes[scope.0].placeholder.set(Some(new_id));
                         mutations.push(AssignId {
                         mutations.push(AssignId {
                             id: new_id,
                             id: new_id,
                             path: &template.template.node_paths[idx][1..],
                             path: &template.template.node_paths[idx][1..],
@@ -200,28 +207,28 @@ impl VirtualDom {
                     RenderReturn::Sync(Some(template)) => {
                     RenderReturn::Sync(Some(template)) => {
                         let mutations_to_this_point = mutations.len();
                         let mutations_to_this_point = mutations.len();
 
 
-                        self.scope_stack.push(id);
+                        self.scope_stack.push(scope);
                         let mut created = self.create(mutations, template);
                         let mut created = self.create(mutations, template);
                         self.scope_stack.pop();
                         self.scope_stack.pop();
 
 
-                        if !self.waiting_on.is_empty() {
+                        if !self.collected_leaves.is_empty() {
                             if let Some(boundary) =
                             if let Some(boundary) =
-                                self.scopes[id.0].has_context::<SuspenseContext>()
+                                self.scopes[scope.0].has_context::<SuspenseContext>()
                             {
                             {
                                 let mut boundary_mut = boundary.borrow_mut();
                                 let mut boundary_mut = boundary.borrow_mut();
                                 let split_off = mutations.split_off(mutations_to_this_point);
                                 let split_off = mutations.split_off(mutations_to_this_point);
 
 
                                 let split_off = unsafe { std::mem::transmute(split_off) };
                                 let split_off = unsafe { std::mem::transmute(split_off) };
 
 
-                                println!("SPLIT OFF: {:#?}", split_off);
-
                                 boundary_mut.mutations.mutations = split_off;
                                 boundary_mut.mutations.mutations = split_off;
-                                boundary_mut.waiting_on.extend(self.waiting_on.drain(..));
+                                boundary_mut
+                                    .waiting_on
+                                    .extend(self.collected_leaves.drain(..));
 
 
                                 // Since this is a boundary, use it as a placeholder
                                 // Since this is a boundary, use it as a placeholder
                                 let new_id = self.next_element(template);
                                 let new_id = self.next_element(template);
                                 placeholder.set(Some(new_id));
                                 placeholder.set(Some(new_id));
-                                self.scopes[id.0].placeholder.set(Some(new_id));
+                                self.scopes[scope.0].placeholder.set(Some(new_id));
                                 mutations.push(AssignId {
                                 mutations.push(AssignId {
                                     id: new_id,
                                     id: new_id,
                                     path: &template.template.node_paths[idx][1..],
                                     path: &template.template.node_paths[idx][1..],

+ 21 - 8
packages/core/src/diff.rs

@@ -1,6 +1,6 @@
 use std::any::Any;
 use std::any::Any;
 
 
-use crate::innerlude::Renderer;
+use crate::innerlude::Mutations;
 use crate::virtual_dom::VirtualDom;
 use crate::virtual_dom::VirtualDom;
 use crate::{Attribute, AttributeValue, TemplateNode};
 use crate::{Attribute, AttributeValue, TemplateNode};
 
 
@@ -20,19 +20,32 @@ use crate::{
 use fxhash::{FxHashMap, FxHashSet};
 use fxhash::{FxHashMap, FxHashSet};
 use slab::Slab;
 use slab::Slab;
 
 
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct DirtyScope {
 pub struct DirtyScope {
-    height: usize,
-    id: ScopeId,
+    pub height: u32,
+    pub id: ScopeId,
+}
+
+impl PartialOrd for DirtyScope {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.height.cmp(&other.height))
+    }
+}
+
+impl Ord for DirtyScope {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.height.cmp(&other.height)
+    }
 }
 }
 
 
 impl<'b> VirtualDom {
 impl<'b> VirtualDom {
-    pub fn diff_scope(&mut self, mutations: &mut Renderer<'b>, scope: ScopeId) {
+    pub fn diff_scope(&mut self, mutations: &mut Mutations<'b>, scope: ScopeId) {
         let scope_state = &mut self.scopes[scope.0];
         let scope_state = &mut self.scopes[scope.0];
     }
     }
 
 
     pub fn diff_node(
     pub fn diff_node(
         &mut self,
         &mut self,
-        muts: &mut Renderer<'b>,
+        muts: &mut Mutations<'b>,
         left_template: &'b VNode<'b>,
         left_template: &'b VNode<'b>,
         right_template: &'b VNode<'b>,
         right_template: &'b VNode<'b>,
     ) {
     ) {
@@ -177,7 +190,7 @@ impl<'b> VirtualDom {
     // the change list stack is in the same state when this function returns.
     // the change list stack is in the same state when this function returns.
     fn diff_non_keyed_children(
     fn diff_non_keyed_children(
         &mut self,
         &mut self,
-        muts: &mut Renderer<'b>,
+        muts: &mut Mutations<'b>,
         old: &'b [VNode<'b>],
         old: &'b [VNode<'b>],
         new: &'b [VNode<'b>],
         new: &'b [VNode<'b>],
     ) {
     ) {
@@ -217,7 +230,7 @@ impl<'b> VirtualDom {
     // The stack is empty upon entry.
     // The stack is empty upon entry.
     fn diff_keyed_children(
     fn diff_keyed_children(
         &mut self,
         &mut self,
-        muts: &mut Renderer<'b>,
+        muts: &mut Mutations<'b>,
         old: &'b [VNode<'b>],
         old: &'b [VNode<'b>],
         new: &'b [VNode<'b>],
         new: &'b [VNode<'b>],
     ) {
     ) {
@@ -533,7 +546,7 @@ impl<'b> VirtualDom {
 
 
     /// Remove these nodes from the dom
     /// Remove these nodes from the dom
     /// Wont generate mutations for the inner nodes
     /// Wont generate mutations for the inner nodes
-    fn remove_nodes(&mut self, muts: &mut Renderer<'b>, nodes: &'b [VNode<'b>]) {
+    fn remove_nodes(&mut self, muts: &mut Mutations<'b>, nodes: &'b [VNode<'b>]) {
         //
         //
     }
     }
 }
 }

+ 9 - 1
packages/core/src/factory.rs

@@ -80,7 +80,6 @@ impl ScopeState {
     where
     where
         P: Properties + 'a,
         P: Properties + 'a,
     {
     {
-        let props = self.bump().alloc(props);
         let as_component = component;
         let as_component = component;
         let vcomp = VComponentProps::new(as_component, P::memoize, props);
         let vcomp = VComponentProps::new(as_component, P::memoize, props);
         let as_dyn = self.bump().alloc(vcomp) as &mut dyn AnyProps;
         let as_dyn = self.bump().alloc(vcomp) as &mut dyn AnyProps;
@@ -136,6 +135,15 @@ pub enum RenderReturn<'a> {
     Async(BumpBox<'a, dyn Future<Output = Element<'a>> + 'a>),
     Async(BumpBox<'a, dyn Future<Output = Element<'a>> + 'a>),
 }
 }
 
 
+impl<'a> RenderReturn<'a> {
+    pub(crate) unsafe fn extend_lifetime_ref<'c>(&self) -> &'c RenderReturn<'c> {
+        unsafe { std::mem::transmute(self) }
+    }
+    pub(crate) unsafe fn extend_lifetime<'c>(self) -> RenderReturn<'c> {
+        unsafe { std::mem::transmute(self) }
+    }
+}
+
 pub type FiberLeaf<'a> = Pin<BumpBox<'a, dyn Future<Output = Element<'a>> + 'a>>;
 pub type FiberLeaf<'a> = Pin<BumpBox<'a, dyn Future<Output = Element<'a>> + 'a>>;
 
 
 pub trait IntoVnode<'a, A = ()> {
 pub trait IntoVnode<'a, A = ()> {

+ 4 - 44
packages/core/src/mutations.rs

@@ -1,13 +1,13 @@
 use crate::arena::ElementId;
 use crate::arena::ElementId;
 
 
 #[derive(Debug)]
 #[derive(Debug)]
-pub struct Renderer<'a> {
+pub struct Mutations<'a> {
     pub subtree: usize,
     pub subtree: usize,
     pub template_mutations: Vec<Mutation<'a>>,
     pub template_mutations: Vec<Mutation<'a>>,
     pub mutations: Vec<Mutation<'a>>,
     pub mutations: Vec<Mutation<'a>>,
 }
 }
 
 
-impl<'a> Renderer<'a> {
+impl<'a> Mutations<'a> {
     pub fn new(subtree: usize) -> Self {
     pub fn new(subtree: usize) -> Self {
         Self {
         Self {
             subtree,
             subtree,
@@ -17,7 +17,7 @@ impl<'a> Renderer<'a> {
     }
     }
 }
 }
 
 
-impl<'a> std::ops::Deref for Renderer<'a> {
+impl<'a> std::ops::Deref for Mutations<'a> {
     type Target = Vec<Mutation<'a>>;
     type Target = Vec<Mutation<'a>>;
 
 
     fn deref(&self) -> &Self::Target {
     fn deref(&self) -> &Self::Target {
@@ -25,52 +25,12 @@ impl<'a> std::ops::Deref for Renderer<'a> {
     }
     }
 }
 }
 
 
-impl std::ops::DerefMut for Renderer<'_> {
+impl std::ops::DerefMut for Mutations<'_> {
     fn deref_mut(&mut self) -> &mut Self::Target {
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut self.mutations
         &mut self.mutations
     }
     }
 }
 }
 
 
-// impl<'a> Renderer<'a> {
-//     pub fn new(subtree: usize) -> Self {
-//         Self {
-//             mutations: vec![Mutations {
-//                 subtree,
-//                 mutations: Vec::new(),
-//             }],
-//         }
-//     }
-// }
-// impl<'a> Renderer<'a> {
-//     pub fn push(&mut self, mutation: Mutation<'a>) {
-//         self.mutations.last_mut().unwrap().mutations.push(mutation)
-//     }
-
-//     pub fn extend(&mut self, mutations: impl IntoIterator<Item = Mutation<'a>>) {
-//         self.mutations
-//             .last_mut()
-//             .unwrap()
-//             .mutations
-//             .extend(mutations)
-//     }
-
-//     pub fn len(&self) -> usize {
-//         self.mutations.last().unwrap().mutations.len()
-//     }
-
-//     pub fn split_off(&mut self, idx: usize) -> Renderer<'a> {
-//         let mut mutations = self.mutations.split_off(idx);
-//         let subtree = mutations.pop().unwrap().subtree;
-//         Renderer { mutations }
-//     }
-// }
-
-// #[derive(Debug)]
-// pub struct Mutations<'a> {
-//     subtree: usize,
-//     mutations: Vec<Mutation<'a>>,
-// }
-
 /*
 /*
 each subtree has its own numbering scheme
 each subtree has its own numbering scheme
 */
 */

+ 1 - 1
packages/core/src/nodes.rs

@@ -49,7 +49,7 @@ impl<'a> VNode<'a> {
     }
     }
 
 
     pub fn single_text(
     pub fn single_text(
-        cx: &'a ScopeState,
+        _cx: &'a ScopeState,
         text: &'static [TemplateNode<'static>],
         text: &'static [TemplateNode<'static>],
         id: &'static str,
         id: &'static str,
     ) -> Option<Self> {
     ) -> Option<Self> {

+ 0 - 1
packages/core/src/scheduler/bumpslab.rs

@@ -1 +0,0 @@
-

+ 0 - 35
packages/core/src/scheduler/handle.rs

@@ -1,35 +0,0 @@
-use futures_util::Future;
-use slab::Slab;
-use std::{cell::RefCell, pin::Pin, rc::Rc, sync::Arc};
-
-use super::{LocalTask, SchedulerMsg, SuspenseLeaf};
-
-#[derive(Clone)]
-pub struct SchedulerHandle(Rc<HandleInner>);
-
-impl std::ops::Deref for SchedulerHandle {
-    type Target = HandleInner;
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-
-pub struct HandleInner {
-    pub sender: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
-
-    /// Tasks created with cx.spawn
-    pub tasks: RefCell<Slab<Rc<LocalTask>>>,
-
-    /// Async components
-    pub leaves: RefCell<Slab<Rc<SuspenseLeaf>>>,
-}
-
-impl SchedulerHandle {
-    pub fn new(sender: futures_channel::mpsc::UnboundedSender<SchedulerMsg>) -> Self {
-        Self(Rc::new(HandleInner {
-            sender,
-            tasks: RefCell::new(Slab::new()),
-            leaves: RefCell::new(Slab::new()),
-        }))
-    }
-}

+ 27 - 24
packages/core/src/scheduler/mod.rs

@@ -1,16 +1,11 @@
-use slab::Slab;
-use std::sync::Arc;
-
 use crate::ScopeId;
 use crate::ScopeId;
+use slab::Slab;
 
 
-mod bumpslab;
-mod handle;
 mod suspense;
 mod suspense;
 mod task;
 mod task;
 mod wait;
 mod wait;
 mod waker;
 mod waker;
 
 
-pub use handle::*;
 pub use suspense::*;
 pub use suspense::*;
 pub use task::*;
 pub use task::*;
 pub use waker::RcWake;
 pub use waker::RcWake;
@@ -36,26 +31,34 @@ pub enum SchedulerMsg {
     SuspenseNotified(SuspenseId),
     SuspenseNotified(SuspenseId),
 }
 }
 
 
-pub struct Scheduler {
-    rx: futures_channel::mpsc::UnboundedReceiver<SchedulerMsg>,
-    ready_suspense: Vec<ScopeId>,
-    pub handle: SchedulerHandle,
-}
+use std::{cell::RefCell, rc::Rc};
 
 
-impl Scheduler {
-    pub fn new() -> Self {
-        let (tx, rx) = futures_channel::mpsc::unbounded();
-        Self {
-            rx,
-            handle: SchedulerHandle::new(tx),
-            ready_suspense: Default::default(),
-        }
+#[derive(Clone)]
+pub(crate) struct Scheduler(Rc<HandleInner>);
+
+impl std::ops::Deref for Scheduler {
+    type Target = HandleInner;
+    fn deref(&self) -> &Self::Target {
+        &self.0
     }
     }
+}
+
+pub struct HandleInner {
+    pub sender: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
+
+    /// Tasks created with cx.spawn
+    pub tasks: RefCell<Slab<Rc<LocalTask>>>,
 
 
-    /// Waits for a future to complete that marks the virtualdom as dirty
-    ///
-    /// Not all messages will mark a virtualdom as dirty, so this waits for a message that has side-effects that do
-    pub fn wait_for_work(&mut self) {
-        //
+    /// Async components
+    pub leaves: RefCell<Slab<Rc<SuspenseLeaf>>>,
+}
+
+impl Scheduler {
+    pub fn new(sender: futures_channel::mpsc::UnboundedSender<SchedulerMsg>) -> Self {
+        Self(Rc::new(HandleInner {
+            sender,
+            tasks: RefCell::new(Slab::new()),
+            leaves: RefCell::new(Slab::new()),
+        }))
     }
     }
 }
 }

+ 5 - 9
packages/core/src/scheduler/suspense.rs

@@ -1,16 +1,15 @@
 use std::{
 use std::{
     cell::{Cell, RefCell},
     cell::{Cell, RefCell},
     collections::HashSet,
     collections::HashSet,
-    pin::Pin,
     rc::Rc,
     rc::Rc,
 };
 };
 
 
 use super::{waker::RcWake, SchedulerMsg};
 use super::{waker::RcWake, SchedulerMsg};
 use crate::{
 use crate::{
-    innerlude::{Mutation, Renderer},
+    innerlude::{Mutation, Mutations},
     Element, ScopeId,
     Element, ScopeId,
 };
 };
-use futures_task::Waker;
+
 use futures_util::Future;
 use futures_util::Future;
 
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
@@ -22,7 +21,7 @@ pub type SuspenseContext = Rc<RefCell<SuspenseBoundary>>;
 pub struct SuspenseBoundary {
 pub struct SuspenseBoundary {
     pub id: ScopeId,
     pub id: ScopeId,
     pub waiting_on: HashSet<SuspenseId>,
     pub waiting_on: HashSet<SuspenseId>,
-    pub mutations: Renderer<'static>,
+    pub mutations: Mutations<'static>,
 }
 }
 
 
 impl SuspenseBoundary {
 impl SuspenseBoundary {
@@ -30,7 +29,7 @@ impl SuspenseBoundary {
         Rc::new(RefCell::new(Self {
         Rc::new(RefCell::new(Self {
             id,
             id,
             waiting_on: Default::default(),
             waiting_on: Default::default(),
-            mutations: Renderer::new(0),
+            mutations: Mutations::new(0),
         }))
         }))
     }
     }
 }
 }
@@ -46,10 +45,7 @@ pub struct SuspenseLeaf {
 
 
 impl RcWake for SuspenseLeaf {
 impl RcWake for SuspenseLeaf {
     fn wake_by_ref(arc_self: &Rc<Self>) {
     fn wake_by_ref(arc_self: &Rc<Self>) {
-        // if arc_self.notified.get() {
-        //     return;
-        // }
-        // arc_self.notified.set(true);
+        arc_self.notified.set(true);
         _ = arc_self
         _ = arc_self
             .tx
             .tx
             .unbounded_send(SchedulerMsg::SuspenseNotified(arc_self.id));
             .unbounded_send(SchedulerMsg::SuspenseNotified(arc_self.id));

+ 23 - 5
packages/core/src/scheduler/task.rs

@@ -7,15 +7,16 @@ use std::{
     process::Output,
     process::Output,
     rc::Rc,
     rc::Rc,
     sync::Arc,
     sync::Arc,
+    task::Poll,
 };
 };
 
 
-use futures_task::{waker, ArcWake, Context, RawWaker, RawWakerVTable, Waker};
 use futures_util::{pin_mut, Future, FutureExt};
 use futures_util::{pin_mut, Future, FutureExt};
 use slab::Slab;
 use slab::Slab;
+use std::task::{Context, RawWaker, RawWakerVTable, Waker};
 
 
 use crate::{Element, ScopeId};
 use crate::{Element, ScopeId};
 
 
-use super::{waker::RcWake, HandleInner, SchedulerHandle, SchedulerMsg};
+use super::{waker::RcWake, HandleInner, Scheduler, SchedulerMsg};
 
 
 #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
 #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
@@ -24,12 +25,29 @@ pub struct TaskId(pub usize);
 /// the task itself is the waker
 /// the task itself is the waker
 
 
 pub struct LocalTask {
 pub struct LocalTask {
-    id: TaskId,
-    scope: ScopeId,
-    tx: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
+    pub id: TaskId,
+    pub scope: ScopeId,
+    pub tx: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
+
+    // todo: use rc and weak, or the bump slab instead of unsafecell
     pub task: UnsafeCell<Pin<Box<dyn Future<Output = ()> + 'static>>>,
     pub task: UnsafeCell<Pin<Box<dyn Future<Output = ()> + 'static>>>,
 }
 }
 
 
+impl LocalTask {
+    pub fn progress(self: &Rc<Self>) -> bool {
+        let waker = self.waker();
+        let mut cx = Context::from_waker(&waker);
+
+        // safety: the waker owns its task and everythig is single threaded
+        let fut = unsafe { &mut *self.task.get() };
+
+        match fut.poll_unpin(&mut cx) {
+            Poll::Ready(_) => true,
+            _ => false,
+        }
+    }
+}
+
 impl HandleInner {
 impl HandleInner {
     pub fn spawn(&self, scope: ScopeId, task: impl Future<Output = ()> + 'static) -> TaskId {
     pub fn spawn(&self, scope: ScopeId, task: impl Future<Output = ()> + 'static) -> TaskId {
         let mut tasks = self.tasks.borrow_mut();
         let mut tasks = self.tasks.borrow_mut();

+ 24 - 24
packages/core/src/scheduler/wait.rs

@@ -1,9 +1,10 @@
-use futures_task::Context;
 use futures_util::{FutureExt, StreamExt};
 use futures_util::{FutureExt, StreamExt};
+use std::task::{Context, Poll};
 
 
 use crate::{
 use crate::{
+    diff::DirtyScope,
     factory::RenderReturn,
     factory::RenderReturn,
-    innerlude::{Mutation, Renderer, SuspenseContext},
+    innerlude::{Mutation, Mutations, SuspenseContext},
     VNode, VirtualDom,
     VNode, VirtualDom,
 };
 };
 
 
@@ -14,25 +15,29 @@ impl VirtualDom {
     ///
     ///
     /// This is cancel safe, so if the future is dropped, you can push events into the virtualdom
     /// This is cancel safe, so if the future is dropped, you can push events into the virtualdom
     pub async fn wait_for_work(&mut self) {
     pub async fn wait_for_work(&mut self) {
+        // todo: make sure the scheduler queue is completely drained
         loop {
         loop {
-            match self.scheduler.rx.next().await.unwrap() {
-                SchedulerMsg::Event => todo!(),
-                SchedulerMsg::Immediate(_) => todo!(),
+            match self.rx.next().await.unwrap() {
+                SchedulerMsg::Event => break,
+
+                SchedulerMsg::Immediate(id) => {
+                    let height = self.scopes[id.0].height;
+                    self.dirty_scopes.insert(DirtyScope { height, id });
+                    break;
+                }
+
                 SchedulerMsg::DirtyAll => todo!(),
                 SchedulerMsg::DirtyAll => todo!(),
 
 
                 SchedulerMsg::TaskNotified(id) => {
                 SchedulerMsg::TaskNotified(id) => {
-                    let mut tasks = self.scheduler.handle.tasks.borrow_mut();
-                    let local_task = &tasks[id.0];
+                    let mut tasks = self.scheduler.tasks.borrow_mut();
+                    let task = &tasks[id.0];
 
 
-                    // attach the waker to itself
-                    // todo: don't make a new waker every time, make it once and then just clone it
-                    let waker = local_task.waker();
-                    let mut cx = Context::from_waker(&waker);
+                    // If the task completes...
+                    if task.progress() {
+                        // Remove it from the scope so we dont try to double drop it when the scope dropes
+                        self.scopes[task.scope.0].spawned_tasks.remove(&id);
 
 
-                    // safety: the waker owns its task and everythig is single threaded
-                    let fut = unsafe { &mut *local_task.task.get() };
-
-                    if let futures_task::Poll::Ready(_) = fut.poll_unpin(&mut cx) {
+                        // Remove it from the scheduler
                         tasks.remove(id.0);
                         tasks.remove(id.0);
                     }
                     }
                 }
                 }
@@ -42,7 +47,6 @@ impl VirtualDom {
 
 
                     let leaf = self
                     let leaf = self
                         .scheduler
                         .scheduler
-                        .handle
                         .leaves
                         .leaves
                         .borrow_mut()
                         .borrow_mut()
                         .get(id.0)
                         .get(id.0)
@@ -55,16 +59,14 @@ impl VirtualDom {
                     let waker = leaf.waker();
                     let waker = leaf.waker();
                     let mut cx = Context::from_waker(&waker);
                     let mut cx = Context::from_waker(&waker);
 
 
-                    let fut = unsafe { &mut *leaf.task };
-
-                    let mut pinned = unsafe { std::pin::Pin::new_unchecked(fut) };
+                    // Safety: the future is always pinned to the bump arena
+                    let mut pinned = unsafe { std::pin::Pin::new_unchecked(&mut *leaf.task) };
                     let as_pinned_mut = &mut pinned;
                     let as_pinned_mut = &mut pinned;
 
 
                     // the component finished rendering and gave us nodes
                     // the component finished rendering and gave us nodes
                     // we should attach them to that component and then render its children
                     // we should attach them to that component and then render its children
                     // continue rendering the tree until we hit yet another suspended component
                     // continue rendering the tree until we hit yet another suspended component
-                    if let futures_task::Poll::Ready(new_nodes) = as_pinned_mut.poll_unpin(&mut cx)
-                    {
+                    if let Poll::Ready(new_nodes) = as_pinned_mut.poll_unpin(&mut cx) {
                         let boundary = &self.scopes[leaf.scope_id.0]
                         let boundary = &self.scopes[leaf.scope_id.0]
                             .consume_context::<SuspenseContext>()
                             .consume_context::<SuspenseContext>()
                             .unwrap();
                             .unwrap();
@@ -87,7 +89,7 @@ impl VirtualDom {
                         if let RenderReturn::Sync(Some(template)) = ret {
                         if let RenderReturn::Sync(Some(template)) = ret {
                             let mutations = &mut fiber.mutations;
                             let mutations = &mut fiber.mutations;
                             let template: &VNode = unsafe { std::mem::transmute(template) };
                             let template: &VNode = unsafe { std::mem::transmute(template) };
-                            let mutations: &mut Renderer =
+                            let mutations: &mut Mutations =
                                 unsafe { std::mem::transmute(mutations) };
                                 unsafe { std::mem::transmute(mutations) };
 
 
                             self.scope_stack.push(scope_id);
                             self.scope_stack.push(scope_id);
@@ -103,8 +105,6 @@ impl VirtualDom {
                     }
                     }
                 }
                 }
             }
             }
-
-            // now proces any events. If we end up running a component and it generates mutations, then we should run those mutations
         }
         }
     }
     }
 }
 }

+ 1 - 1
packages/core/src/scheduler/waker.rs

@@ -1,4 +1,4 @@
-use futures_task::{RawWaker, RawWakerVTable, Waker};
+use std::task::{RawWaker, RawWakerVTable, Waker};
 use std::{mem, rc::Rc};
 use std::{mem, rc::Rc};
 
 
 pub trait RcWake: Sized {
 pub trait RcWake: Sized {

+ 33 - 29
packages/core/src/scope_arena.rs

@@ -1,24 +1,23 @@
-use crate::{innerlude::SuspenseContext, scheduler::RcWake};
-use futures_util::{pin_mut, task::noop_waker_ref};
-use std::{
-    mem,
-    pin::Pin,
-    rc::Rc,
-    task::{Context, Poll},
-};
-
 use crate::{
 use crate::{
     any_props::AnyProps,
     any_props::AnyProps,
     arena::ElementId,
     arena::ElementId,
     bump_frame::BumpFrame,
     bump_frame::BumpFrame,
     factory::RenderReturn,
     factory::RenderReturn,
     innerlude::{SuspenseId, SuspenseLeaf},
     innerlude::{SuspenseId, SuspenseLeaf},
+    scheduler::RcWake,
     scopes::{ScopeId, ScopeState},
     scopes::{ScopeId, ScopeState},
     virtual_dom::VirtualDom,
     virtual_dom::VirtualDom,
 };
 };
+use futures_util::FutureExt;
+use std::{
+    mem,
+    pin::Pin,
+    rc::Rc,
+    task::{Context, Poll},
+};
 
 
 impl VirtualDom {
 impl VirtualDom {
-    pub fn new_scope(&mut self, props: *mut dyn AnyProps<'static>) -> ScopeId {
+    pub fn new_scope(&mut self, props: *mut dyn AnyProps<'static>) -> &mut ScopeState {
         let parent = self.acquire_current_scope_raw();
         let parent = self.acquire_current_scope_raw();
         let container = self.acquire_current_container();
         let container = self.acquire_current_container();
         let entry = self.scopes.vacant_entry();
         let entry = self.scopes.vacant_entry();
@@ -34,18 +33,17 @@ impl VirtualDom {
             placeholder: None.into(),
             placeholder: None.into(),
             node_arena_1: BumpFrame::new(50),
             node_arena_1: BumpFrame::new(50),
             node_arena_2: BumpFrame::new(50),
             node_arena_2: BumpFrame::new(50),
+            spawned_tasks: Default::default(),
             render_cnt: Default::default(),
             render_cnt: Default::default(),
             hook_arena: Default::default(),
             hook_arena: Default::default(),
             hook_vals: Default::default(),
             hook_vals: Default::default(),
             hook_idx: Default::default(),
             hook_idx: Default::default(),
             shared_contexts: Default::default(),
             shared_contexts: Default::default(),
-            tasks: self.scheduler.handle.clone(),
-        });
-
-        id
+            tasks: self.scheduler.clone(),
+        })
     }
     }
 
 
-    pub fn acquire_current_container(&self) -> ElementId {
+    fn acquire_current_container(&self) -> ElementId {
         self.element_stack
         self.element_stack
             .last()
             .last()
             .copied()
             .copied()
@@ -59,39 +57,45 @@ impl VirtualDom {
             .and_then(|id| self.scopes.get_mut(id.0).map(|f| f as *mut ScopeState))
             .and_then(|id| self.scopes.get_mut(id.0).map(|f| f as *mut ScopeState))
     }
     }
 
 
-    pub fn run_scope(&mut self, scope_id: ScopeId) -> &mut RenderReturn {
+    pub(crate) unsafe fn run_scope_extend<'a>(
+        &mut self,
+        scope_id: ScopeId,
+    ) -> &'a RenderReturn<'a> {
+        unsafe { self.run_scope(scope_id).extend_lifetime_ref() }
+    }
+
+    pub(crate) fn run_scope(&mut self, scope_id: ScopeId) -> &RenderReturn {
         let mut new_nodes = unsafe {
         let mut new_nodes = unsafe {
             let scope = &mut self.scopes[scope_id.0];
             let scope = &mut self.scopes[scope_id.0];
             scope.hook_idx.set(0);
             scope.hook_idx.set(0);
 
 
+            // safety: due to how we traverse the tree, we know that the scope is not currently aliased
             let props: &mut dyn AnyProps = mem::transmute(&mut *scope.props);
             let props: &mut dyn AnyProps = mem::transmute(&mut *scope.props);
-            let res: RenderReturn = props.render(scope);
-            let res: RenderReturn<'static> = mem::transmute(res);
-            res
+            props.render(scope).extend_lifetime()
         };
         };
 
 
         // immediately resolve futures that can be resolved
         // immediately resolve futures that can be resolved
         if let RenderReturn::Async(task) = &mut new_nodes {
         if let RenderReturn::Async(task) = &mut new_nodes {
-            use futures_util::FutureExt;
+            let mut leaves = self.scheduler.leaves.borrow_mut();
 
 
-            let mut leaves = self.scheduler.handle.leaves.borrow_mut();
             let entry = leaves.vacant_entry();
             let entry = leaves.vacant_entry();
-            let key = entry.key();
-            let suspense_id = SuspenseId(key);
+            let suspense_id = SuspenseId(entry.key());
 
 
             let leaf = Rc::new(SuspenseLeaf {
             let leaf = Rc::new(SuspenseLeaf {
                 scope_id,
                 scope_id,
                 task: task.as_mut(),
                 task: task.as_mut(),
                 id: suspense_id,
                 id: suspense_id,
-                tx: self.scheduler.handle.sender.clone(),
-                notified: false.into(),
+                tx: self.scheduler.sender.clone(),
+                notified: Default::default(),
             });
             });
 
 
-            let _leaf = leaf.clone();
             let waker = leaf.waker();
             let waker = leaf.waker();
             let mut cx = Context::from_waker(&waker);
             let mut cx = Context::from_waker(&waker);
+
+            // safety: the task is already pinned in the bump arena
             let mut pinned = unsafe { Pin::new_unchecked(task.as_mut()) };
             let mut pinned = unsafe { Pin::new_unchecked(task.as_mut()) };
 
 
+            // Keep polling until either we get a value or the future is not ready
             loop {
             loop {
                 match pinned.poll_unpin(&mut cx) {
                 match pinned.poll_unpin(&mut cx) {
                     // If nodes are produced, then set it and we can break
                     // If nodes are produced, then set it and we can break
@@ -103,8 +107,8 @@ impl VirtualDom {
                     // If no nodes are produced but the future woke up immediately, then try polling it again
                     // If no nodes are produced but the future woke up immediately, then try polling it again
                     // This circumvents things like yield_now, but is important is important when rendering
                     // This circumvents things like yield_now, but is important is important when rendering
                     // components that are just a stream of immediately ready futures
                     // components that are just a stream of immediately ready futures
-                    _ if _leaf.notified.get() => {
-                        _leaf.notified.set(false);
+                    _ if leaf.notified.get() => {
+                        leaf.notified.set(false);
                         continue;
                         continue;
                     }
                     }
 
 
@@ -112,7 +116,7 @@ impl VirtualDom {
                     // Insert the future into fiber leaves and break
                     // Insert the future into fiber leaves and break
                     _ => {
                     _ => {
                         entry.insert(leaf);
                         entry.insert(leaf);
-                        self.waiting_on.push(suspense_id);
+                        self.collected_leaves.push(suspense_id);
                         break;
                         break;
                     }
                     }
                 };
                 };

+ 17 - 19
packages/core/src/scopes.rs

@@ -1,7 +1,7 @@
 use std::{
 use std::{
     any::{Any, TypeId},
     any::{Any, TypeId},
     cell::{Cell, RefCell},
     cell::{Cell, RefCell},
-    collections::HashMap,
+    collections::{HashMap, HashSet},
     sync::Arc,
     sync::Arc,
 };
 };
 
 
@@ -13,7 +13,7 @@ use crate::{
     any_props::AnyProps,
     any_props::AnyProps,
     arena::ElementId,
     arena::ElementId,
     bump_frame::BumpFrame,
     bump_frame::BumpFrame,
-    innerlude::{SchedulerHandle, SchedulerMsg},
+    innerlude::{Scheduler, SchedulerMsg},
     lazynodes::LazyNodes,
     lazynodes::LazyNodes,
     nodes::VNode,
     nodes::VNode,
     TaskId,
     TaskId,
@@ -43,27 +43,28 @@ impl<'a, T> std::ops::Deref for Scoped<'a, T> {
 pub struct ScopeId(pub usize);
 pub struct ScopeId(pub usize);
 
 
 pub struct ScopeState {
 pub struct ScopeState {
-    pub render_cnt: usize,
+    pub(crate) render_cnt: usize,
 
 
-    pub node_arena_1: BumpFrame,
-    pub node_arena_2: BumpFrame,
+    pub(crate) node_arena_1: BumpFrame,
+    pub(crate) node_arena_2: BumpFrame,
 
 
-    pub parent: Option<*mut ScopeState>,
-    pub container: ElementId,
-    pub id: ScopeId,
+    pub(crate) parent: Option<*mut ScopeState>,
+    pub(crate) container: ElementId,
+    pub(crate) id: ScopeId,
 
 
-    pub height: u32,
+    pub(crate) height: u32,
 
 
-    pub hook_arena: Bump,
-    pub hook_vals: RefCell<Vec<*mut dyn Any>>,
-    pub hook_idx: Cell<usize>,
+    pub(crate) hook_arena: Bump,
+    pub(crate) hook_vals: RefCell<Vec<*mut dyn Any>>,
+    pub(crate) hook_idx: Cell<usize>,
 
 
     pub(crate) shared_contexts: RefCell<HashMap<TypeId, Box<dyn Any>>>,
     pub(crate) shared_contexts: RefCell<HashMap<TypeId, Box<dyn Any>>>,
 
 
-    pub tasks: SchedulerHandle,
+    pub(crate) tasks: Scheduler,
+    pub(crate) spawned_tasks: HashSet<TaskId>,
 
 
-    pub props: *mut dyn AnyProps<'static>,
-    pub placeholder: Cell<Option<ElementId>>,
+    pub(crate) props: *mut dyn AnyProps<'static>,
+    pub(crate) placeholder: Cell<Option<ElementId>>,
 }
 }
 
 
 impl ScopeState {
 impl ScopeState {
@@ -243,14 +244,11 @@ impl ScopeState {
             let parent = unsafe { &*parent };
             let parent = unsafe { &*parent };
 
 
             if parent.scope_id() == ScopeId(0) {
             if parent.scope_id() == ScopeId(0) {
-                let exists = parent
+                let _ = parent
                     .shared_contexts
                     .shared_contexts
                     .borrow_mut()
                     .borrow_mut()
                     .insert(TypeId::of::<T>(), Box::new(value.clone()));
                     .insert(TypeId::of::<T>(), Box::new(value.clone()));
 
 
-                if exists.is_some() {
-                    log::warn!("Context already provided to parent scope - replacing it");
-                }
                 return value;
                 return value;
             }
             }
 
 

+ 216 - 33
packages/core/src/virtual_dom.rs

@@ -1,12 +1,10 @@
 use crate::any_props::VComponentProps;
 use crate::any_props::VComponentProps;
 use crate::arena::ElementPath;
 use crate::arena::ElementPath;
-use crate::component::Component;
 use crate::diff::DirtyScope;
 use crate::diff::DirtyScope;
 use crate::factory::RenderReturn;
 use crate::factory::RenderReturn;
-use crate::innerlude::{Renderer, Scheduler, SchedulerMsg};
+use crate::innerlude::{Mutations, Scheduler, SchedulerMsg};
 use crate::mutations::Mutation;
 use crate::mutations::Mutation;
 use crate::nodes::{Template, TemplateId};
 use crate::nodes::{Template, TemplateId};
-
 use crate::{
 use crate::{
     arena::ElementId,
     arena::ElementId,
     scopes::{ScopeId, ScopeState},
     scopes::{ScopeId, ScopeState},
@@ -14,10 +12,101 @@ use crate::{
 use crate::{scheduler, Element, Scope};
 use crate::{scheduler, Element, Scope};
 use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
 use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
 use futures_util::Future;
 use futures_util::Future;
-use scheduler::{SuspenseBoundary, SuspenseContext, SuspenseId};
+use scheduler::{SuspenseBoundary, SuspenseId};
 use slab::Slab;
 use slab::Slab;
 use std::collections::{BTreeSet, HashMap};
 use std::collections::{BTreeSet, HashMap};
 
 
+/// A virtual node system that progresses user events and diffs UI trees.
+///
+/// ## Guide
+///
+/// Components are defined as simple functions that take [`Scope`] and return an [`Element`].
+///
+/// ```rust, ignore
+/// #[derive(Props, PartialEq)]
+/// struct AppProps {
+///     title: String
+/// }
+///
+/// fn App(cx: Scope<AppProps>) -> Element {
+///     cx.render(rsx!(
+///         div {"hello, {cx.props.title}"}
+///     ))
+/// }
+/// ```
+///
+/// Components may be composed to make complex apps.
+///
+/// ```rust, ignore
+/// fn App(cx: Scope<AppProps>) -> Element {
+///     cx.render(rsx!(
+///         NavBar { routes: ROUTES }
+///         Title { "{cx.props.title}" }
+///         Footer {}
+///     ))
+/// }
+/// ```
+///
+/// To start an app, create a [`VirtualDom`] and call [`VirtualDom::rebuild`] to get the list of edits required to
+/// draw the UI.
+///
+/// ```rust, ignore
+/// let mut vdom = VirtualDom::new(App);
+/// let edits = vdom.rebuild();
+/// ```
+///
+/// To inject UserEvents into the VirtualDom, call [`VirtualDom::get_scheduler_channel`] to get access to the scheduler.
+///
+/// ```rust, ignore
+/// let channel = vdom.get_scheduler_channel();
+/// channel.send_unbounded(SchedulerMsg::UserEvent(UserEvent {
+///     // ...
+/// }))
+/// ```
+///
+/// While waiting for UserEvents to occur, call [`VirtualDom::wait_for_work`] to poll any futures inside the VirtualDom.
+///
+/// ```rust, ignore
+/// vdom.wait_for_work().await;
+/// ```
+///
+/// Once work is ready, call [`VirtualDom::work_with_deadline`] to compute the differences between the previous and
+/// current UI trees. This will return a [`Mutations`] object that contains Edits, Effects, and NodeRefs that need to be
+/// handled by the renderer.
+///
+/// ```rust, ignore
+/// let mutations = vdom.work_with_deadline(|| false);
+/// for edit in mutations {
+///     apply(edit);
+/// }
+/// ```
+///
+/// ## Building an event loop around Dioxus:
+///
+/// Putting everything together, you can build an event loop around Dioxus by using the methods outlined above.
+///
+/// ```rust, ignore
+/// fn App(cx: Scope) -> Element {
+///     cx.render(rsx!{
+///         div { "Hello World" }
+///     })
+/// }
+///
+/// async fn main() {
+///     let mut dom = VirtualDom::new(App);
+///
+///     let mut inital_edits = dom.rebuild();
+///     apply_edits(inital_edits);
+///
+///     loop {
+///         dom.wait_for_work().await;
+///         let frame_timeout = TimeoutFuture::new(Duration::from_millis(16));
+///         let deadline = || (&mut frame_timeout).now_or_never();
+///         let edits = dom.run_with_deadline(deadline).await;
+///         apply_edits(edits);
+///     }
+/// }
+/// ```
 pub struct VirtualDom {
 pub struct VirtualDom {
     pub(crate) templates: HashMap<TemplateId, Template<'static>>,
     pub(crate) templates: HashMap<TemplateId, Template<'static>>,
     pub(crate) elements: Slab<ElementPath>,
     pub(crate) elements: Slab<ElementPath>,
@@ -28,59 +117,153 @@ pub struct VirtualDom {
 
 
     // While diffing we need some sort of way of breaking off a stream of suspended mutations.
     // While diffing we need some sort of way of breaking off a stream of suspended mutations.
     pub(crate) scope_stack: Vec<ScopeId>,
     pub(crate) scope_stack: Vec<ScopeId>,
-    pub(crate) waiting_on: Vec<SuspenseId>,
+    pub(crate) collected_leaves: Vec<SuspenseId>,
+
+    // Whenever a suspense tree is finished, we push its boundary onto this stack.
+    // When "render_with_deadline" is called, we pop the stack and return the mutations
+    pub(crate) finished_fibers: Vec<ScopeId>,
+
+    pub(crate) rx: futures_channel::mpsc::UnboundedReceiver<SchedulerMsg>,
 }
 }
 
 
 impl VirtualDom {
 impl VirtualDom {
+    /// Create a new VirtualDom with a component that does not have special props.
+    ///
+    /// # Description
+    ///
+    /// Later, the props can be updated by calling "update" with a new set of props, causing a set of re-renders.
+    ///
+    /// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive
+    /// to toss out the entire tree.
+    ///
+    ///
+    /// # Example
+    /// ```rust, ignore
+    /// fn Example(cx: Scope) -> Element  {
+    ///     cx.render(rsx!( div { "hello world" } ))
+    /// }
+    ///
+    /// let dom = VirtualDom::new(Example);
+    /// ```
+    ///
+    /// Note: the VirtualDom is not progressed, you must either "run_with_deadline" or use "rebuild" to progress it.
     pub fn new(app: fn(Scope) -> Element) -> Self {
     pub fn new(app: fn(Scope) -> Element) -> Self {
-        let scheduler = Scheduler::new();
+        Self::new_with_props(app, ())
+    }
+
+    /// Create a new VirtualDom with the given properties for the root component.
+    ///
+    /// # Description
+    ///
+    /// Later, the props can be updated by calling "update" with a new set of props, causing a set of re-renders.
+    ///
+    /// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive
+    /// to toss out the entire tree.
+    ///
+    ///
+    /// # Example
+    /// ```rust, ignore
+    /// #[derive(PartialEq, Props)]
+    /// struct SomeProps {
+    ///     name: &'static str
+    /// }
+    ///
+    /// fn Example(cx: Scope<SomeProps>) -> Element  {
+    ///     cx.render(rsx!{ div{ "hello {cx.props.name}" } })
+    /// }
+    ///
+    /// let dom = VirtualDom::new(Example);
+    /// ```
+    ///
+    /// Note: the VirtualDom is not progressed on creation. You must either "run_with_deadline" or use "rebuild" to progress it.
+    ///
+    /// ```rust, ignore
+    /// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" });
+    /// let mutations = dom.rebuild();
+    /// ```
+    pub fn new_with_props<P>(root: fn(Scope<P>) -> Element, root_props: P) -> Self
+    where
+        P: 'static,
+    {
+        let channel = futures_channel::mpsc::unbounded();
+        Self::new_with_props_and_scheduler(root, root_props, channel)
+    }
 
 
-        let mut res = Self {
+    /// Launch the VirtualDom, but provide your own channel for receiving and sending messages into the scheduler
+    ///
+    /// This is useful when the VirtualDom must be driven from outside a thread and it doesn't make sense to wait for the
+    /// VirtualDom to be created just to retrieve its channel receiver.
+    ///
+    /// ```rust, ignore
+    /// let channel = futures_channel::mpsc::unbounded();
+    /// let dom = VirtualDom::new_with_scheduler(Example, (), channel);
+    /// ```
+    pub fn new_with_props_and_scheduler<P: 'static>(
+        root: fn(Scope<P>) -> Element,
+        root_props: P,
+        (tx, rx): (
+            UnboundedSender<SchedulerMsg>,
+            UnboundedReceiver<SchedulerMsg>,
+        ),
+    ) -> Self {
+        let mut dom = Self {
+            rx,
+            scheduler: Scheduler::new(tx),
             templates: Default::default(),
             templates: Default::default(),
             scopes: Slab::default(),
             scopes: Slab::default(),
             elements: Default::default(),
             elements: Default::default(),
             scope_stack: Vec::new(),
             scope_stack: Vec::new(),
             element_stack: vec![ElementId(0)],
             element_stack: vec![ElementId(0)],
             dirty_scopes: BTreeSet::new(),
             dirty_scopes: BTreeSet::new(),
-            waiting_on: Vec::new(),
-            scheduler,
+            collected_leaves: Vec::new(),
+            finished_fibers: Vec::new(),
         };
         };
 
 
-        let props = Box::into_raw(Box::new(VComponentProps::new_empty(app)));
-        let props: *mut VComponentProps<(), ()> = unsafe { std::mem::transmute(props) };
-
-        let root = res.new_scope(props);
-
-        // the root component is always a suspense boundary for any async children
-        res.scopes[root.0].provide_context(SuspenseBoundary::new(root));
-        assert_eq!(root, ScopeId(0));
+        dom.new_scope(Box::into_raw(Box::new(VComponentProps::new(
+            root,
+            |_, _| unreachable!(),
+            root_props,
+        ))))
+        // The root component is always a suspense boundary for any async children
+        // This could be unexpected, so we might rethink this behavior
+        .provide_context(SuspenseBoundary::new(ScopeId(0)));
 
 
-        res
+        dom
     }
     }
 
 
-    /// Render the virtualdom, without processing any suspense.
+    /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom from scratch.
     ///
     ///
-    /// This does register futures with wakers, but does not process any of them.
-    pub fn rebuild<'a>(&'a mut self) -> Renderer<'a> {
-        let mut mutations = Renderer::new(0);
-        let root_node: &RenderReturn = self.run_scope(ScopeId(0));
-        let root_node: &RenderReturn = unsafe { std::mem::transmute(root_node) };
+    /// The diff machine expects the RealDom's stack to be the root of the application.
+    ///
+    /// Tasks will not be polled with this method, nor will any events be processed from the event queue. Instead, the
+    /// root component will be ran once and then diffed. All updates will flow out as mutations.
+    ///
+    /// All state stored in components will be completely wiped away.
+    ///
+    /// Any templates previously registered will remain.
+    ///
+    /// # Example
+    /// ```rust, ignore
+    /// static App: Component = |cx|  cx.render(rsx!{ "hello world" });
+    ///
+    /// let mut dom = VirtualDom::new();
+    /// let edits = dom.rebuild();
+    ///
+    /// apply_edits(edits);
+    /// ```
+    pub fn rebuild<'a>(&'a mut self) -> Mutations<'a> {
+        let mut mutations = Mutations::new(0);
 
 
-        let mut created = 0;
+        let root_node = unsafe { self.run_scope_extend(ScopeId(0)) };
         match root_node {
         match root_node {
             RenderReturn::Sync(Some(node)) => {
             RenderReturn::Sync(Some(node)) => {
-                self.scope_stack.push(ScopeId(0));
-                created = self.create(&mut mutations, node);
-                self.scope_stack.pop();
-            }
-            RenderReturn::Sync(None) => {
-                //
+                let m = self.create_scope(ScopeId(0), &mut mutations, node);
+                mutations.push(Mutation::AppendChildren { m });
             }
             }
+            RenderReturn::Sync(None) => {}
             RenderReturn::Async(_) => unreachable!("Root scope cannot be an async component"),
             RenderReturn::Async(_) => unreachable!("Root scope cannot be an async component"),
         }
         }
 
 
-        mutations.push(Mutation::AppendChildren { m: created });
-
         mutations
         mutations
     }
     }