浏览代码

wip: consolidation, simplification

Jonathan Kelley 3 年之前
父节点
当前提交
70b983d

+ 0 - 2
packages/core/src/bumpframe.rs

@@ -28,7 +28,6 @@ impl ActiveFrame {
             head_node: VNode::Fragment(VFragment {
             head_node: VNode::Fragment(VFragment {
                 key: None,
                 key: None,
                 children: &[],
                 children: &[],
-                is_static: false,
             }),
             }),
             bump: b1,
             bump: b1,
 
 
@@ -39,7 +38,6 @@ impl ActiveFrame {
             head_node: VNode::Fragment(VFragment {
             head_node: VNode::Fragment(VFragment {
                 key: None,
                 key: None,
                 children: &[],
                 children: &[],
-                is_static: false,
             }),
             }),
             bump: b2,
             bump: b2,
 
 

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

@@ -62,7 +62,7 @@ impl<'a> Iterator for RealChildIterator<'a> {
                     VNode::Component(sc) => {
                     VNode::Component(sc) => {
                         let scope = self
                         let scope = self
                             .scopes
                             .scopes
-                            .get_scope(sc.associated_scope.get().unwrap())
+                            .get_scope(&sc.associated_scope.get().unwrap())
                             .unwrap();
                             .unwrap();
 
 
                         // Simply swap the current node on the stack with the root of the component
                         // Simply swap the current node on the stack with the root of the component

+ 3 - 6
packages/core/src/component.rs

@@ -5,10 +5,7 @@
 //! if the type supports PartialEq. The Properties trait is used by the rsx! and html! macros to generate the type-safe builder
 //! if the type supports PartialEq. The Properties trait is used by the rsx! and html! macros to generate the type-safe builder
 //! that ensures compile-time required and optional fields on cx.
 //! that ensures compile-time required and optional fields on cx.
 
 
-use crate::{
-    innerlude::{Context, Element, VAnchor, VFragment, VNode},
-    LazyNodes, ScopeChildren,
-};
+use crate::innerlude::{Context, Element, LazyNodes, ScopeChildren};
 /// A component is a wrapper around a Context and some Props that share a lifetime
 /// A component is a wrapper around a Context and some Props that share a lifetime
 ///
 ///
 ///
 ///
@@ -51,7 +48,7 @@ pub struct FragmentBuilder<'a, const BUILT: bool> {
     children: Option<ScopeChildren<'a>>,
     children: Option<ScopeChildren<'a>>,
 }
 }
 impl<'a> FragmentBuilder<'a, false> {
 impl<'a> FragmentBuilder<'a, false> {
-    pub fn children(mut self, children: ScopeChildren<'a>) -> FragmentBuilder<'a, true> {
+    pub fn children(self, children: ScopeChildren<'a>) -> FragmentBuilder<'a, true> {
         FragmentBuilder {
         FragmentBuilder {
             children: Some(children),
             children: Some(children),
         }
         }
@@ -75,7 +72,7 @@ impl<'a> Properties for FragmentProps<'a> {
         FragmentBuilder { children: None }
         FragmentBuilder { children: None }
     }
     }
 
 
-    unsafe fn memoize(&self, other: &Self) -> bool {
+    unsafe fn memoize(&self, _other: &Self) -> bool {
         false
         false
     }
     }
 }
 }

+ 0 - 17
packages/core/src/coroutines.rs

@@ -1,17 +0,0 @@
-//! Coroutines are just a "futures unordered" buffer for tasks that can be submitted through the use_coroutine hook.
-//!
-//! The idea here is to move *coroutine* support as a layer on top of *tasks*
-
-use futures_util::{stream::FuturesUnordered, Future};
-
-pub struct CoroutineScheduler {
-    futures: FuturesUnordered<Box<dyn Future<Output = ()>>>,
-}
-
-impl CoroutineScheduler {
-    pub fn new() -> Self {
-        CoroutineScheduler {
-            futures: FuturesUnordered::new(),
-        }
-    }
-}

+ 61 - 32
packages/core/src/diff.rs

@@ -169,7 +169,7 @@ impl<'bump> DiffMachine<'bump> {
     }
     }
 
 
     pub fn diff_scope(&mut self, id: ScopeId) {
     pub fn diff_scope(&mut self, id: ScopeId) {
-        if let Some(component) = self.vdom.get_scope_mut(id) {
+        if let Some(component) = self.vdom.get_scope_mut(&id) {
             let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
             let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
             self.stack.push(DiffInstruction::Diff { new, old });
             self.stack.push(DiffInstruction::Diff { new, old });
             self.work(|| false);
             self.work(|| false);
@@ -189,7 +189,10 @@ impl<'bump> DiffMachine<'bump> {
                 DiffInstruction::Diff { old, new } => self.diff_node(old, new),
                 DiffInstruction::Diff { old, new } => self.diff_node(old, new),
                 DiffInstruction::Create { node } => self.create_node(node),
                 DiffInstruction::Create { node } => self.create_node(node),
                 DiffInstruction::Mount { and } => self.mount(and),
                 DiffInstruction::Mount { and } => self.mount(and),
-                DiffInstruction::PrepareMove { node } => self.prepare_move_node(node),
+                DiffInstruction::PrepareMove { node } => {
+                    let num_on_stack = self.push_all_nodes(node);
+                    self.stack.add_child_count(num_on_stack);
+                }
                 DiffInstruction::PopScope => self.stack.pop_off_scope(),
                 DiffInstruction::PopScope => self.stack.pop_off_scope(),
             };
             };
 
 
@@ -202,10 +205,29 @@ impl<'bump> DiffMachine<'bump> {
         true
         true
     }
     }
 
 
-    fn prepare_move_node(&mut self, node: &'bump VNode<'bump>) {
-        for el in RealChildIterator::new(node, self.vdom) {
-            self.mutations.push_root(el.mounted_id());
-            self.stack.add_child_count(1);
+    // recursively push all the nodes of a tree onto the stack and return how many are there
+    fn push_all_nodes(&mut self, node: &'bump VNode<'bump>) -> usize {
+        match node {
+            VNode::Text(_) | VNode::Anchor(_) | VNode::Suspended(_) => {
+                self.mutations.push_root(node.mounted_id());
+                1
+            }
+
+            VNode::Fragment(_) | VNode::Component(_) => node
+                .children()
+                .iter()
+                .map(|child| self.push_all_nodes(child))
+                .sum(),
+
+            VNode::Element(el) => {
+                let mut num_on_stack = 0;
+                for child in el.children.iter() {
+                    num_on_stack += self.push_all_nodes(child);
+                }
+                self.mutations.push_root(el.dom_id.get().unwrap());
+
+                num_on_stack + 1
+            }
         }
         }
     }
     }
 
 
@@ -307,7 +329,7 @@ impl<'bump> DiffMachine<'bump> {
         self.stack.add_child_count(1);
         self.stack.add_child_count(1);
 
 
         if let Some(cur_scope_id) = self.stack.current_scope() {
         if let Some(cur_scope_id) = self.stack.current_scope() {
-            let scope = self.vdom.get_scope(cur_scope_id).unwrap();
+            let scope = self.vdom.get_scope_mut(&cur_scope_id).unwrap();
 
 
             listeners.iter().for_each(|listener| {
             listeners.iter().for_each(|listener| {
                 self.attach_listener_to_scope(listener, scope);
                 self.attach_listener_to_scope(listener, scope);
@@ -332,18 +354,18 @@ impl<'bump> DiffMachine<'bump> {
     }
     }
 
 
     fn create_component_node(&mut self, vcomponent: &'bump VComponent<'bump>) {
     fn create_component_node(&mut self, vcomponent: &'bump VComponent<'bump>) {
-        let caller = vcomponent.caller;
+        // let caller = vcomponent.caller;
 
 
         let parent_idx = self.stack.current_scope().unwrap();
         let parent_idx = self.stack.current_scope().unwrap();
 
 
         let shared = self.vdom.channel.clone();
         let shared = self.vdom.channel.clone();
 
 
         // Insert a new scope into our component list
         // Insert a new scope into our component list
-        let parent_scope = self.vdom.get_scope(parent_idx).unwrap();
+        let parent_scope = self.vdom.get_scope(&parent_idx).unwrap();
 
 
         let new_idx = self.vdom.insert_scope_with_key(|new_idx| {
         let new_idx = self.vdom.insert_scope_with_key(|new_idx| {
             ScopeInner::new(
             ScopeInner::new(
-                caller,
+                vcomponent,
                 new_idx,
                 new_idx,
                 Some(parent_idx),
                 Some(parent_idx),
                 parent_scope.height + 1,
                 parent_scope.height + 1,
@@ -356,17 +378,18 @@ impl<'bump> DiffMachine<'bump> {
         vcomponent.associated_scope.set(Some(new_idx));
         vcomponent.associated_scope.set(Some(new_idx));
 
 
         if !vcomponent.can_memoize {
         if !vcomponent.can_memoize {
-            let cur_scope = self.vdom.get_scope_mut(parent_idx).unwrap();
+            let cur_scope = self.vdom.get_scope_mut(&parent_idx).unwrap();
             let extended = vcomponent as *const VComponent;
             let extended = vcomponent as *const VComponent;
             let extended: *const VComponent<'static> = unsafe { std::mem::transmute(extended) };
             let extended: *const VComponent<'static> = unsafe { std::mem::transmute(extended) };
-            cur_scope.borrowed_props.borrow_mut().push(extended);
+
+            cur_scope.items.get_mut().borrowed_props.push(extended);
         }
         }
 
 
         // TODO:
         // TODO:
         //  add noderefs to current noderef list Noderefs
         //  add noderefs to current noderef list Noderefs
         //  add effects to current effect list Effects
         //  add effects to current effect list Effects
 
 
-        let new_component = self.vdom.get_scope_mut(new_idx).unwrap();
+        let new_component = self.vdom.get_scope_mut(&new_idx).unwrap();
 
 
         log::debug!(
         log::debug!(
             "initializing component {:?} with height {:?}",
             "initializing component {:?} with height {:?}",
@@ -494,7 +517,7 @@ impl<'bump> DiffMachine<'bump> {
         //
         //
         // TODO: take a more efficient path than this
         // TODO: take a more efficient path than this
         if let Some(cur_scope_id) = self.stack.current_scope() {
         if let Some(cur_scope_id) = self.stack.current_scope() {
-            let scope = self.vdom.get_scope(cur_scope_id).unwrap();
+            let scope = self.vdom.get_scope_mut(&cur_scope_id).unwrap();
 
 
             if old.listeners.len() == new.listeners.len() {
             if old.listeners.len() == new.listeners.len() {
                 for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
                 for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
@@ -549,19 +572,21 @@ impl<'bump> DiffMachine<'bump> {
             new.associated_scope.set(Some(scope_addr));
             new.associated_scope.set(Some(scope_addr));
 
 
             // make sure the component's caller function is up to date
             // make sure the component's caller function is up to date
-            let scope = self.vdom.get_scope_mut(scope_addr).unwrap();
-            scope.update_scope_dependencies(new.caller);
+            let scope = self.vdom.get_scope_mut(&scope_addr).unwrap();
+            scope.update_vcomp(new);
 
 
             // React doesn't automatically memoize, but we do.
             // React doesn't automatically memoize, but we do.
-            let props_are_the_same = old.comparator.unwrap();
+            let props_are_the_same = todo!("reworking component memoization");
+            // let props_are_the_same = todo!("reworking component memoization");
+            // let props_are_the_same = old.comparator.unwrap();
 
 
-            if self.cfg.force_diff || !props_are_the_same(new) {
-                let succeeded = scope.run_scope(self.vdom);
+            // if self.cfg.force_diff || !props_are_the_same(new) {
+            //     let succeeded = scope.run_scope(self.vdom);
 
 
-                if succeeded {
-                    self.diff_node(scope.frames.wip_head(), scope.frames.fin_head());
-                }
-            }
+            //     if succeeded {
+            //         self.diff_node(scope.frames.wip_head(), scope.frames.fin_head());
+            //     }
+            // }
 
 
             self.stack.scope_stack.pop();
             self.stack.scope_stack.pop();
         } else {
         } else {
@@ -1038,7 +1063,7 @@ impl<'bump> DiffMachine<'bump> {
                 }
                 }
                 VNode::Component(el) => {
                 VNode::Component(el) => {
                     let scope_id = el.associated_scope.get().unwrap();
                     let scope_id = el.associated_scope.get().unwrap();
-                    let scope = self.vdom.get_scope(scope_id).unwrap();
+                    let scope = self.vdom.get_scope(&scope_id).unwrap();
                     search_node = Some(scope.root_node());
                     search_node = Some(scope.root_node());
                 }
                 }
             }
             }
@@ -1056,7 +1081,7 @@ impl<'bump> DiffMachine<'bump> {
                 }
                 }
                 VNode::Component(el) => {
                 VNode::Component(el) => {
                     let scope_id = el.associated_scope.get().unwrap();
                     let scope_id = el.associated_scope.get().unwrap();
-                    let scope = self.vdom.get_scope(scope_id).unwrap();
+                    let scope = self.vdom.get_scope(&scope_id).unwrap();
                     search_node = Some(scope.root_node());
                     search_node = Some(scope.root_node());
                 }
                 }
                 VNode::Text(t) => break t.dom_id.get(),
                 VNode::Text(t) => break t.dom_id.get(),
@@ -1131,12 +1156,12 @@ impl<'bump> DiffMachine<'bump> {
 
 
                 VNode::Component(c) => {
                 VNode::Component(c) => {
                     let scope_id = c.associated_scope.get().unwrap();
                     let scope_id = c.associated_scope.get().unwrap();
-                    let scope = self.vdom.get_scope_mut(scope_id).unwrap();
+                    let scope = self.vdom.get_scope_mut(&scope_id).unwrap();
                     let root = scope.root_node();
                     let root = scope.root_node();
                     self.remove_nodes(Some(root), gen_muts);
                     self.remove_nodes(Some(root), gen_muts);
 
 
                     log::debug!("Destroying scope {:?}", scope_id);
                     log::debug!("Destroying scope {:?}", scope_id);
-                    let mut s = self.vdom.try_remove(scope_id).unwrap();
+                    let mut s = self.vdom.try_remove(&scope_id).unwrap();
                     s.hooks.clear_hooks();
                     s.hooks.clear_hooks();
                 }
                 }
             }
             }
@@ -1161,23 +1186,27 @@ impl<'bump> DiffMachine<'bump> {
     }
     }
 
 
     /// Adds a listener closure to a scope during diff.
     /// Adds a listener closure to a scope during diff.
-    fn attach_listener_to_scope<'a>(&mut self, listener: &'a Listener<'a>, scope: &ScopeInner) {
-        let mut queue = scope.listeners.borrow_mut();
+    fn attach_listener_to_scope<'a>(&mut self, listener: &'a Listener<'a>, scope: &mut ScopeInner) {
         let long_listener: &'a Listener<'static> = unsafe { std::mem::transmute(listener) };
         let long_listener: &'a Listener<'static> = unsafe { std::mem::transmute(listener) };
-        queue.push(long_listener as *const _)
+        scope
+            .items
+            .get_mut()
+            .listeners
+            .push(long_listener as *const _)
     }
     }
 
 
     fn attach_suspended_node_to_scope(&mut self, suspended: &'bump VSuspended) {
     fn attach_suspended_node_to_scope(&mut self, suspended: &'bump VSuspended) {
         if let Some(scope) = self
         if let Some(scope) = self
             .stack
             .stack
             .current_scope()
             .current_scope()
-            .and_then(|id| self.vdom.get_scope_mut(id))
+            .and_then(|id| self.vdom.get_scope_mut(&id))
         {
         {
             // safety: this lifetime is managed by the logic on scope
             // safety: this lifetime is managed by the logic on scope
             let extended: &VSuspended<'static> = unsafe { std::mem::transmute(suspended) };
             let extended: &VSuspended<'static> = unsafe { std::mem::transmute(suspended) };
             scope
             scope
+                .items
+                .get_mut()
                 .suspended_nodes
                 .suspended_nodes
-                .borrow_mut()
                 .insert(suspended.task_id, extended as *const _);
                 .insert(suspended.task_id, extended as *const _);
         }
         }
     }
     }

+ 0 - 50
packages/core/src/heuristics.rs

@@ -1,50 +0,0 @@
-use std::collections::HashMap;
-
-use fxhash::FxHashMap;
-
-use crate::FC;
-
-/// Provides heuristics to the "SharedResources" object for improving allocation performance.
-///
-/// This heuristics engine records the memory footprint of bump arenas and hook lists for each component. These records are
-/// then used later on to optimize the initial allocation for future components. This helps save large allocations later on
-/// that would slow down the diffing and initialization process.
-///
-///
-pub struct HeuristicsEngine {
-    heuristics: FxHashMap<FcSlot, Heuristic>,
-}
-
-pub type FcSlot = *const ();
-
-pub struct Heuristic {
-    hooks: u32,
-    bump_size: u64,
-}
-
-impl HeuristicsEngine {
-    pub(crate) fn new() -> Self {
-        Self {
-            heuristics: FxHashMap::default(),
-        }
-    }
-
-    fn recommend<T>(&mut self, fc: FC<T>, heuristic: Heuristic) {
-        let g = fc as FcSlot;
-        let e = self.heuristics.entry(g);
-    }
-
-    fn get_recommendation<T>(&mut self, fc: FC<T>) -> &Heuristic {
-        let id = fc as FcSlot;
-
-        self.heuristics.entry(id).or_insert(Heuristic {
-            bump_size: 100,
-            hooks: 10,
-        })
-    }
-}
-
-#[test]
-fn types_work() {
-    let engine = HeuristicsEngine::new();
-}

+ 19 - 14
packages/core/src/hooklist.rs

@@ -1,10 +1,9 @@
 use std::{
 use std::{
     any::Any,
     any::Any,
-    cell::{Cell, RefCell, UnsafeCell},
+    cell::{Cell, RefCell},
 };
 };
 
 
-type UnsafeInnerHookState = UnsafeCell<Box<dyn Any>>;
-type HookCleanup = Box<dyn FnOnce(Box<dyn Any>)>;
+use bumpalo::Bump;
 
 
 /// An abstraction over internally stored data using a hook-based memory layout.
 /// An abstraction over internally stored data using a hook-based memory layout.
 ///
 ///
@@ -15,7 +14,8 @@ type HookCleanup = Box<dyn FnOnce(Box<dyn Any>)>;
 /// Todo: this could use its very own bump arena, but that might be a tad overkill
 /// Todo: this could use its very own bump arena, but that might be a tad overkill
 #[derive(Default)]
 #[derive(Default)]
 pub(crate) struct HookList {
 pub(crate) struct HookList {
-    vals: RefCell<Vec<(UnsafeInnerHookState, HookCleanup)>>,
+    arena: Bump,
+    vals: RefCell<Vec<*mut dyn Any>>,
     idx: Cell<usize>,
     idx: Cell<usize>,
 }
 }
 
 
@@ -23,7 +23,7 @@ impl HookList {
     pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
     pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
         self.vals.borrow().get(self.idx.get()).and_then(|inn| {
         self.vals.borrow().get(self.idx.get()).and_then(|inn| {
             self.idx.set(self.idx.get() + 1);
             self.idx.set(self.idx.get() + 1);
-            let raw_box = unsafe { &mut *inn.0.get() };
+            let raw_box = unsafe { &mut **inn };
             raw_box.downcast_mut::<T>()
             raw_box.downcast_mut::<T>()
         })
         })
     }
     }
@@ -38,10 +38,9 @@ impl HookList {
         self.idx.set(0);
         self.idx.set(0);
     }
     }
 
 
-    pub(crate) fn push_hook<T: 'static>(&self, new: T, cleanup: HookCleanup) {
-        self.vals
-            .borrow_mut()
-            .push((UnsafeCell::new(Box::new(new)), cleanup))
+    pub(crate) fn push_hook<T: 'static>(&self, new: T) {
+        let val = self.arena.alloc(new);
+        self.vals.borrow_mut().push(val)
     }
     }
 
 
     pub(crate) fn len(&self) -> usize {
     pub(crate) fn len(&self) -> usize {
@@ -57,10 +56,16 @@ impl HookList {
     }
     }
 
 
     pub fn clear_hooks(&mut self) {
     pub fn clear_hooks(&mut self) {
-        log::debug!("clearing hooks...");
-        self.vals
-            .borrow_mut()
-            .drain(..)
-            .for_each(|(state, cleanup)| cleanup(state.into_inner()));
+        self.vals.borrow_mut().drain(..).for_each(|state| {
+            let as_mut = unsafe { &mut *state };
+            let boxed = unsafe { bumpalo::boxed::Box::from_raw(as_mut) };
+            drop(boxed);
+        });
+    }
+
+    /// Get the ammount of memory a hooklist uses
+    /// Used in heuristics
+    pub fn get_hook_arena_size(&self) -> usize {
+        self.arena.allocated_bytes()
     }
     }
 }
 }

+ 72 - 72
packages/core/src/hooks.rs

@@ -13,73 +13,73 @@ use crate::innerlude::*;
 use futures_util::FutureExt;
 use futures_util::FutureExt;
 use std::{any::Any, cell::RefCell, future::Future, ops::Deref, rc::Rc};
 use std::{any::Any, cell::RefCell, future::Future, ops::Deref, rc::Rc};
 
 
-/// Awaits the given task, forcing the component to re-render when the value is ready.
-///
-/// Returns the handle to the task and the value (if it is ready, else None).
-///
-/// ```
-/// static Example: FC<()> = |(cx, props)| {
-///     let (task, value) = use_task(|| async {
-///         timer::sleep(Duration::from_secs(1)).await;
-///         "Hello World"
-///     });
-///
-///     match contents {
-///         Some(contents) => rsx!(cx, div { "{title}" }),
-///         None => rsx!(cx, div { "Loading..." }),
-///     }
-/// };
-/// ```
-pub fn use_coroutine<'src, Out, Fut, Init>(
-    cx: Context<'src>,
-    task_initializer: Init,
-) -> (&'src TaskHandle, &'src Option<Out>)
-where
-    Out: 'static,
-    Fut: Future<Output = Out> + 'static,
-    Init: FnOnce() -> Fut + 'src,
-{
-    struct TaskHook<T> {
-        handle: TaskHandle,
-        task_dump: Rc<RefCell<Option<T>>>,
-        value: Option<T>,
-    }
-
-    todo!()
-
-    // // whenever the task is complete, save it into th
-    // cx.use_hook(
-    //     move |_| {
-    //         let task_fut = task_initializer();
-
-    //         let task_dump = Rc::new(RefCell::new(None));
-
-    //         let slot = task_dump.clone();
-
-    //         let updater = cx.schedule_update_any();
-    //         let originator = cx.scope.our_arena_idx;
-
-    //         let handle = cx.submit_task(Box::pin(task_fut.then(move |output| async move {
-    //             *slot.as_ref().borrow_mut() = Some(output);
-    //             updater(originator);
-    //             originator
-    //         })));
-
-    //         TaskHook {
-    //             task_dump,
-    //             value: None,
-    //             handle,
-    //         }
-    //     },
-    //     |hook| {
-    //         if let Some(val) = hook.task_dump.as_ref().borrow_mut().take() {
-    //             hook.value = Some(val);
-    //         }
-    //         (&hook.handle, &hook.value)
-    //     },
-    //     |_| {},
-    // )
-}
+// /// Awaits the given task, forcing the component to re-render when the value is ready.
+// ///
+// /// Returns the handle to the task and the value (if it is ready, else None).
+// ///
+// /// ```
+// /// static Example: FC<()> = |(cx, props)| {
+// ///     let (task, value) = use_task(|| async {
+// ///         timer::sleep(Duration::from_secs(1)).await;
+// ///         "Hello World"
+// ///     });
+// ///
+// ///     match contents {
+// ///         Some(contents) => rsx!(cx, div { "{title}" }),
+// ///         None => rsx!(cx, div { "Loading..." }),
+// ///     }
+// /// };
+// /// ```
+// pub fn use_coroutine<'src, Out, Fut, Init>(
+//     cx: Context<'src>,
+//     task_initializer: Init,
+// ) -> (&'src TaskHandle, &'src Option<Out>)
+// where
+//     Out: 'static,
+//     Fut: Future<Output = Out> + 'static,
+//     Init: FnOnce() -> Fut + 'src,
+// {
+//     struct TaskHook<T> {
+//         handle: TaskHandle,
+//         task_dump: Rc<RefCell<Option<T>>>,
+//         value: Option<T>,
+//     }
+
+//     todo!()
+
+//     // // whenever the task is complete, save it into th
+//     // cx.use_hook(
+//     //     move |_| {
+//     //         let task_fut = task_initializer();
+
+//     //         let task_dump = Rc::new(RefCell::new(None));
+
+//     //         let slot = task_dump.clone();
+
+//     //         let updater = cx.schedule_update_any();
+//     //         let originator = cx.scope.our_arena_idx;
+
+//     //         let handle = cx.submit_task(Box::pin(task_fut.then(move |output| async move {
+//     //             *slot.as_ref().borrow_mut() = Some(output);
+//     //             updater(originator);
+//     //             originator
+//     //         })));
+
+//     //         TaskHook {
+//     //             task_dump,
+//     //             value: None,
+//     //             handle,
+//     //         }
+//     //     },
+//     //     |hook| {
+//     //         if let Some(val) = hook.task_dump.as_ref().borrow_mut().take() {
+//     //             hook.value = Some(val);
+//     //         }
+//     //         (&hook.handle, &hook.value)
+//     //     },
+//     //     |_| {},
+//     // )
+// }
 
 
 /// Asynchronously render new nodes once the given future has completed.
 /// Asynchronously render new nodes once the given future has completed.
 ///
 ///
@@ -167,10 +167,10 @@ where
     // )
     // )
 }
 }
 
 
-pub(crate) struct SuspenseHook {
-    pub handle: TaskHandle,
-    pub value: Rc<RefCell<Option<Box<dyn Any>>>>,
-}
+// pub(crate) struct SuspenseHook {
+//     pub handle: TaskHandle,
+//     pub value: Rc<RefCell<Option<Box<dyn Any>>>>,
+// }
 
 
 #[derive(Clone, Copy)]
 #[derive(Clone, Copy)]
 pub struct NodeRef<'src, T: 'static>(&'src RefCell<Option<T>>);
 pub struct NodeRef<'src, T: 'static>(&'src RefCell<Option<T>>);
@@ -183,5 +183,5 @@ impl<'a, T> Deref for NodeRef<'a, T> {
 }
 }
 
 
 pub fn use_node_ref<T, P>(cx: Context) -> NodeRef<T> {
 pub fn use_node_ref<T, P>(cx: Context) -> NodeRef<T> {
-    cx.use_hook(|_| RefCell::new(None), |f| NodeRef { 0: f }, |_| {})
+    cx.use_hook(|_| RefCell::new(None), |f| NodeRef { 0: f })
 }
 }

+ 2 - 13
packages/core/src/lib.rs

@@ -13,22 +13,16 @@ Navigating this crate:
 Some utilities
 Some utilities
 */
 */
 pub(crate) mod bumpframe;
 pub(crate) mod bumpframe;
-pub(crate) mod childiter;
 pub(crate) mod component;
 pub(crate) mod component;
-pub(crate) mod coroutines;
 pub(crate) mod diff;
 pub(crate) mod diff;
 pub(crate) mod diff_stack;
 pub(crate) mod diff_stack;
 pub(crate) mod events;
 pub(crate) mod events;
-pub(crate) mod heuristics;
 pub(crate) mod hooklist;
 pub(crate) mod hooklist;
 pub(crate) mod hooks;
 pub(crate) mod hooks;
 pub(crate) mod lazynodes;
 pub(crate) mod lazynodes;
 pub(crate) mod mutations;
 pub(crate) mod mutations;
 pub(crate) mod nodes;
 pub(crate) mod nodes;
-pub(crate) mod resources;
-pub(crate) mod scheduler;
 pub(crate) mod scope;
 pub(crate) mod scope;
-pub(crate) mod tasks;
 pub(crate) mod test_dom;
 pub(crate) mod test_dom;
 pub(crate) mod threadsafe;
 pub(crate) mod threadsafe;
 pub(crate) mod util;
 pub(crate) mod util;
@@ -39,21 +33,16 @@ pub mod debug_dom;
 
 
 pub(crate) mod innerlude {
 pub(crate) mod innerlude {
     pub(crate) use crate::bumpframe::*;
     pub(crate) use crate::bumpframe::*;
-    pub(crate) use crate::childiter::*;
     pub use crate::component::*;
     pub use crate::component::*;
     pub(crate) use crate::diff::*;
     pub(crate) use crate::diff::*;
     pub use crate::diff_stack::*;
     pub use crate::diff_stack::*;
     pub use crate::events::*;
     pub use crate::events::*;
-    pub use crate::heuristics::*;
     pub(crate) use crate::hooklist::*;
     pub(crate) use crate::hooklist::*;
     pub use crate::hooks::*;
     pub use crate::hooks::*;
     pub use crate::lazynodes::*;
     pub use crate::lazynodes::*;
     pub use crate::mutations::*;
     pub use crate::mutations::*;
     pub use crate::nodes::*;
     pub use crate::nodes::*;
-    pub(crate) use crate::resources::*;
-    pub use crate::scheduler::*;
     pub use crate::scope::*;
     pub use crate::scope::*;
-    pub use crate::tasks::*;
     pub use crate::test_dom::*;
     pub use crate::test_dom::*;
     pub use crate::threadsafe::*;
     pub use crate::threadsafe::*;
     pub use crate::util::*;
     pub use crate::util::*;
@@ -65,8 +54,8 @@ pub(crate) mod innerlude {
 
 
 pub use crate::innerlude::{
 pub use crate::innerlude::{
     Context, DioxusElement, DomEdit, Element, ElementId, EventPriority, LazyNodes, MountType,
     Context, DioxusElement, DomEdit, Element, ElementId, EventPriority, LazyNodes, MountType,
-    Mutations, NodeFactory, Properties, SchedulerMsg, ScopeChildren, ScopeId, TaskHandle, TestDom,
-    ThreadsafeVirtualDom, UserEvent, VNode, VirtualDom, FC,
+    Mutations, NodeFactory, Properties, ScopeChildren, ScopeId, TestDom, ThreadsafeVirtualDom,
+    UserEvent, VNode, VirtualDom, FC,
 };
 };
 
 
 pub mod prelude {
 pub mod prelude {

+ 111 - 84
packages/core/src/nodes.rs

@@ -154,6 +154,14 @@ impl<'src> VNode<'src> {
         }
         }
     }
     }
 
 
+    pub fn children(&self) -> &[VNode<'src>] {
+        match &self {
+            VNode::Fragment(f) => &f.children,
+            VNode::Component(c) => &c.children,
+            _ => &[],
+        }
+    }
+
     // Create an "owned" version of the vnode.
     // Create an "owned" version of the vnode.
     pub fn decouple(&self) -> VNode<'src> {
     pub fn decouple(&self) -> VNode<'src> {
         match self {
         match self {
@@ -165,7 +173,6 @@ impl<'src> VNode<'src> {
             VNode::Fragment(f) => VNode::Fragment(VFragment {
             VNode::Fragment(f) => VNode::Fragment(VFragment {
                 children: f.children,
                 children: f.children,
                 key: f.key,
                 key: f.key,
-                is_static: f.is_static,
             }),
             }),
         }
         }
     }
     }
@@ -209,8 +216,6 @@ pub struct VFragment<'src> {
     pub key: Option<&'src str>,
     pub key: Option<&'src str>,
 
 
     pub children: &'src [VNode<'src>],
     pub children: &'src [VNode<'src>],
-
-    pub is_static: bool,
 }
 }
 
 
 /// An element like a "div" with children, listeners, and attributes.
 /// An element like a "div" with children, listeners, and attributes.
@@ -316,21 +321,21 @@ pub struct VComponent<'src> {
 
 
     pub associated_scope: Cell<Option<ScopeId>>,
     pub associated_scope: Cell<Option<ScopeId>>,
 
 
-    pub is_static: bool,
-
     // Function pointer to the FC that was used to generate this component
     // Function pointer to the FC that was used to generate this component
     pub user_fc: *const (),
     pub user_fc: *const (),
-
-    pub(crate) caller: BumpBox<'src, dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> + 'src>,
-
-    pub(crate) comparator: Option<BumpBox<'src, dyn Fn(&VComponent) -> bool + 'src>>,
-
-    pub(crate) drop_props: RefCell<Option<BumpBox<'src, dyn FnMut()>>>,
-
     pub(crate) can_memoize: bool,
     pub(crate) can_memoize: bool,
 
 
     // Raw pointer into the bump arena for the props of the component
     // Raw pointer into the bump arena for the props of the component
     pub(crate) raw_props: *const (),
     pub(crate) raw_props: *const (),
+
+    // during the "teardown" process we'll take the caller out so it can be dropped properly
+    pub(crate) caller: Option<VCompCaller<'src>>,
+    pub(crate) comparator: Option<BumpBox<'src, dyn Fn(&VComponent) -> bool + 'src>>,
+}
+
+pub enum VCompCaller<'src> {
+    Borrowed(BumpBox<'src, dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> + 'src>),
+    Owned(Box<dyn for<'b> Fn(&'b ScopeInner) -> Element<'b>>),
 }
 }
 
 
 pub struct VSuspended<'a> {
 pub struct VSuspended<'a> {
@@ -473,7 +478,7 @@ impl<'a> NodeFactory<'a> {
         }
         }
     }
     }
 
 
-    pub fn component<P>(
+    pub fn component<P, P1>(
         &self,
         &self,
         component: fn(Scope<'a, P>) -> Element<'a>,
         component: fn(Scope<'a, P>) -> Element<'a>,
         props: P,
         props: P,
@@ -482,85 +487,109 @@ impl<'a> NodeFactory<'a> {
     where
     where
         P: Properties + 'a,
         P: Properties + 'a,
     {
     {
+        /*
+        our strategy:
+        - unsafe hackery
+        - lol
+
+        - we don't want to hit the global allocator
+        - allocate into our bump arena
+        - if the props aren't static, then we convert them into a box which we pass off between renders
+        */
+
         let bump = self.bump();
         let bump = self.bump();
-        let props = bump.alloc(props);
-        let raw_props = props as *mut P as *mut ();
+
+        // let p = BumpBox::new_in(x, a)
+
+        // the best place to allocate the props are the other component's arena
+        // the second best place is the global allocator
+
+        // // if the props are static
+        // let boxed = if P::IS_STATIC {
+        //     todo!()
+        // } else {
+        //     todo!()
+        // }
+
+        // let caller = Box::new(|f: &ScopeInner| -> Element {
+        //     //
+        //     component((f, &props))
+        // });
+
         let user_fc = component as *const ();
         let user_fc = component as *const ();
 
 
-        let comparator: &mut dyn Fn(&VComponent) -> bool = bump.alloc_with(|| {
-            move |other: &VComponent| {
-                if user_fc == other.user_fc {
-                    // Safety
-                    // - We guarantee that FC<P> is the same by function pointer
-                    // - Because FC<P> is the same, then P must be the same (even with generics)
-                    // - Non-static P are autoderived to memoize as false
-                    // - This comparator is only called on a corresponding set of bumpframes
-                    let props_memoized = unsafe {
-                        let real_other: &P = &*(other.raw_props as *const _ as *const P);
-                        props.memoize(real_other)
-                    };
-
-                    // It's only okay to memoize if there are no children and the props can be memoized
-                    // Implementing memoize is unsafe and done automatically with the props trait
-                    props_memoized
-                } else {
-                    false
-                }
-            }
-        });
-        let comparator = Some(unsafe { BumpBox::from_raw(comparator) });
-
-        let drop_props = {
-            // create a closure to drop the props
-            let mut has_dropped = false;
-
-            let drop_props: &mut dyn FnMut() = bump.alloc_with(|| {
-                move || unsafe {
-                    log::debug!("dropping props!");
-                    if !has_dropped {
-                        let real_other = raw_props as *mut _ as *mut P;
-                        let b = BumpBox::from_raw(real_other);
-                        std::mem::drop(b);
-
-                        has_dropped = true;
-                    } else {
-                        panic!("Drop props called twice - this is an internal failure of Dioxus");
-                    }
-                }
-            });
-
-            let drop_props = unsafe { BumpBox::from_raw(drop_props) };
-
-            RefCell::new(Some(drop_props))
-        };
+        // let comparator: &mut dyn Fn(&VComponent) -> bool = bump.alloc_with(|| {
+        //     move |other: &VComponent| {
+        //         if user_fc == other.user_fc {
+        //             // Safety
+        //             // - We guarantee that FC<P> is the same by function pointer
+        //             // - Because FC<P> is the same, then P must be the same (even with generics)
+        //             // - Non-static P are autoderived to memoize as false
+        //             // - This comparator is only called on a corresponding set of bumpframes
+        //             let props_memoized = unsafe {
+        //                 let real_other: &P = &*(other.raw_props as *const _ as *const P);
+        //                 props.memoize(real_other)
+        //             };
+
+        //             // It's only okay to memoize if there are no children and the props can be memoized
+        //             // Implementing memoize is unsafe and done automatically with the props trait
+        //             props_memoized
+        //         } else {
+        //             false
+        //         }
+        //     }
+        // });
+        // let comparator = Some(unsafe { BumpBox::from_raw(comparator) });
 
 
         let key = key.map(|f| self.raw_text(f).0);
         let key = key.map(|f| self.raw_text(f).0);
 
 
-        let caller: &'a mut dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> =
-            bump.alloc(move |scope: &ScopeInner| -> Element {
-                log::debug!("calling component renderr {:?}", scope.our_arena_idx);
-                let props: &'_ P = unsafe { &*(raw_props as *const P) };
+        let caller = match P::IS_STATIC {
+            true => {
+                // it just makes sense to box the props
+                let boxed_props: Box<P> = Box::new(props);
+                let props_we_know_are_static = todo!();
+                VCompCaller::Owned(Box::new(|f| {
+                    //
 
 
-                let scp: &'a ScopeInner = unsafe { std::mem::transmute(scope) };
-                let s: Scope<'a, P> = (scp, props);
+                    let p = todo!();
 
 
-                let res: Element = component(s);
-                unsafe { std::mem::transmute(res) }
-            });
+                    todo!()
+                }))
+            }
+            false => VCompCaller::Borrowed({
+                //
 
 
-        let caller = unsafe { BumpBox::from_raw(caller) };
+                todo!()
+                // let caller = bump.alloc()
+            }),
+        };
 
 
-        VNode::Component(bump.alloc(VComponent {
-            user_fc,
-            comparator,
-            raw_props,
-            caller,
-            is_static: P::IS_STATIC,
-            key,
-            can_memoize: P::IS_STATIC,
-            drop_props,
-            associated_scope: Cell::new(None),
-        }))
+        todo!()
+        // let caller: &'a mut dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> =
+        //     bump.alloc(move |scope: &ScopeInner| -> Element {
+        //         log::debug!("calling component renderr {:?}", scope.our_arena_idx);
+        //         let props: &'_ P = unsafe { &*(raw_props as *const P) };
+
+        //         let scp: &'a ScopeInner = unsafe { std::mem::transmute(scope) };
+        //         let s: Scope<'a, P> = (scp, props);
+
+        //         let res: Element = component(s);
+        //         unsafe { std::mem::transmute(res) }
+        //     });
+
+        // let caller = unsafe { BumpBox::from_raw(caller) };
+
+        // VNode::Component(bump.alloc(VComponent {
+        //     user_fc,
+        //     comparator,
+        //     raw_props,
+        //     caller,
+        //     is_static: P::IS_STATIC,
+        //     key,
+        //     can_memoize: P::IS_STATIC,
+        //     drop_props,
+        //     associated_scope: Cell::new(None),
+        // }))
     }
     }
 
 
     pub fn fragment_from_iter(
     pub fn fragment_from_iter(
@@ -604,7 +633,6 @@ impl<'a> NodeFactory<'a> {
         VNode::Fragment(VFragment {
         VNode::Fragment(VFragment {
             children,
             children,
             key: None,
             key: None,
-            is_static: false,
         })
         })
     }
     }
 
 
@@ -684,7 +712,6 @@ impl<'a> IntoVNode<'a> for Option<LazyNodes<'a, '_>> {
             Some(lazy) => lazy.call(cx),
             Some(lazy) => lazy.call(cx),
             None => VNode::Fragment(VFragment {
             None => VNode::Fragment(VFragment {
                 children: &[],
                 children: &[],
-                is_static: false,
                 key: None,
                 key: None,
             }),
             }),
         }
         }

+ 3 - 3
packages/core/src/resources.rs

@@ -32,18 +32,18 @@ pub(crate) struct ResourcePool {
 
 
 impl ResourcePool {
 impl ResourcePool {
     /// this is unsafe because the caller needs to track which other scopes it's already using
     /// this is unsafe because the caller needs to track which other scopes it's already using
-    pub fn get_scope(&self, idx: ScopeId) -> Option<&ScopeInner> {
+    pub fn get_scope(&self, idx: &ScopeId) -> Option<&ScopeInner> {
         let inner = unsafe { &*self.components.get() };
         let inner = unsafe { &*self.components.get() };
         inner.get(idx.0)
         inner.get(idx.0)
     }
     }
 
 
     /// this is unsafe because the caller needs to track which other scopes it's already using
     /// this is unsafe because the caller needs to track which other scopes it's already using
-    pub fn get_scope_mut(&self, idx: ScopeId) -> Option<&mut ScopeInner> {
+    pub fn get_scope_mut(&self, idx: &ScopeId) -> Option<&mut ScopeInner> {
         let inner = unsafe { &mut *self.components.get() };
         let inner = unsafe { &mut *self.components.get() };
         inner.get_mut(idx.0)
         inner.get_mut(idx.0)
     }
     }
 
 
-    pub fn try_remove(&self, id: ScopeId) -> Option<ScopeInner> {
+    pub fn try_remove(&self, id: &ScopeId) -> Option<ScopeInner> {
         let inner = unsafe { &mut *self.components.get() };
         let inner = unsafe { &mut *self.components.get() };
         Some(inner.remove(id.0))
         Some(inner.remove(id.0))
         // .try_remove(id.0)
         // .try_remove(id.0)

+ 105 - 121
packages/core/src/scheduler.rs

@@ -68,14 +68,17 @@ do anything too arduous from onInput.
 
 
 For the rest, we defer to the rIC period and work down each queue from high to low.
 For the rest, we defer to the rIC period and work down each queue from high to low.
 */
 */
-use crate::heuristics::*;
+
 use crate::innerlude::*;
 use crate::innerlude::*;
+use bumpalo::Bump;
 use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
 use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
 use futures_util::{pin_mut, stream::FuturesUnordered, Future, FutureExt, StreamExt};
 use futures_util::{pin_mut, stream::FuturesUnordered, Future, FutureExt, StreamExt};
 use fxhash::FxHashMap;
 use fxhash::FxHashMap;
 use fxhash::FxHashSet;
 use fxhash::FxHashSet;
 use indexmap::IndexSet;
 use indexmap::IndexSet;
 use slab::Slab;
 use slab::Slab;
+use std::pin::Pin;
+use std::task::Poll;
 use std::{
 use std::{
     any::{Any, TypeId},
     any::{Any, TypeId},
     cell::{Cell, UnsafeCell},
     cell::{Cell, UnsafeCell},
@@ -85,11 +88,7 @@ use std::{
 
 
 #[derive(Clone)]
 #[derive(Clone)]
 pub(crate) struct EventChannel {
 pub(crate) struct EventChannel {
-    pub task_counter: Rc<Cell<u64>>,
-    pub cur_subtree: Rc<Cell<u32>>,
     pub sender: UnboundedSender<SchedulerMsg>,
     pub sender: UnboundedSender<SchedulerMsg>,
-    pub schedule_any_immediate: Rc<dyn Fn(ScopeId)>,
-    pub submit_task: Rc<dyn Fn(FiberTask) -> TaskHandle>,
     pub get_shared_context: GetSharedContext,
     pub get_shared_context: GetSharedContext,
 }
 }
 
 
@@ -101,16 +100,6 @@ pub enum SchedulerMsg {
 
 
     // setstate
     // setstate
     Immediate(ScopeId),
     Immediate(ScopeId),
-
-    // tasks
-    Task(TaskMsg),
-}
-
-pub enum TaskMsg {
-    ToggleTask(u64),
-    PauseTask(u64),
-    ResumeTask(u64),
-    DropTask(u64),
 }
 }
 
 
 /// The scheduler holds basically everything around "working"
 /// The scheduler holds basically everything around "working"
@@ -126,16 +115,26 @@ pub enum TaskMsg {
 ///
 ///
 /// We can prevent this safety issue from occurring if we track which scopes are invalidated when starting a new task.
 /// We can prevent this safety issue from occurring if we track which scopes are invalidated when starting a new task.
 ///
 ///
+/// There's a lot of raw pointers here...
+///
+/// Since we're building self-referential structures for each component, we need to make sure that the referencs stay stable
+/// The best way to do that is a bump allocator.
+///
+///
 ///
 ///
 pub(crate) struct Scheduler {
 pub(crate) struct Scheduler {
-    /// All mounted components are arena allocated to make additions, removals, and references easy to work with
-    /// A generational arena is used to re-use slots of deleted scopes without having to resize the underlying arena.
-    ///
-    /// This is wrapped in an UnsafeCell because we will need to get mutable access to unique values in unique bump arenas
-    /// and rusts's guarantees cannot prove that this is safe. We will need to maintain the safety guarantees manually.
-    pub pool: ResourcePool,
+    // /// All mounted components are arena allocated to make additions, removals, and references easy to work with
+    // /// A generational arena is used to re-use slots of deleted scopes without having to resize the underlying arena.
+    // ///
+    // /// This is wrapped in an UnsafeCell because we will need to get mutable access to unique values in unique bump arenas
+    // /// and rusts's guarantees cannot prove that this is safe. We will need to maintain the safety guarantees manually.
+    // pub pool: ResourcePool,
+    //
+    pub component_arena: Bump,
+
+    pub free_components: VecDeque<*mut ScopeInner>,
 
 
-    pub heuristics: HeuristicsEngine,
+    pub heuristics: FxHashMap<FcSlot, Heuristic>,
 
 
     pub receiver: UnboundedReceiver<SchedulerMsg>,
     pub receiver: UnboundedReceiver<SchedulerMsg>,
 
 
@@ -145,104 +144,70 @@ pub(crate) struct Scheduler {
     // Every component that has futures that need to be polled
     // Every component that has futures that need to be polled
     pub pending_futures: FxHashSet<ScopeId>,
     pub pending_futures: FxHashSet<ScopeId>,
 
 
-    // In-flight futures
-    pub async_tasks: FuturesUnordered<FiberTask>,
-
     // // scheduler stuff
     // // scheduler stuff
     // pub current_priority: EventPriority,
     // pub current_priority: EventPriority,
     pub ui_events: VecDeque<UserEvent>,
     pub ui_events: VecDeque<UserEvent>,
 
 
     pub pending_immediates: VecDeque<ScopeId>,
     pub pending_immediates: VecDeque<ScopeId>,
 
 
-    pub pending_tasks: VecDeque<UserEvent>,
-
     pub batched_events: VecDeque<UserEvent>,
     pub batched_events: VecDeque<UserEvent>,
 
 
     pub garbage_scopes: HashSet<ScopeId>,
     pub garbage_scopes: HashSet<ScopeId>,
 
 
     pub dirty_scopes: IndexSet<ScopeId>,
     pub dirty_scopes: IndexSet<ScopeId>,
+
     pub saved_state: Option<SavedDiffWork<'static>>,
     pub saved_state: Option<SavedDiffWork<'static>>,
+
     pub in_progress: bool,
     pub in_progress: bool,
 }
 }
 
 
+pub type FcSlot = *const ();
+
+pub struct Heuristic {
+    hook_arena_size: usize,
+    node_arena_size: usize,
+}
+
 impl Scheduler {
 impl Scheduler {
     pub(crate) fn new(
     pub(crate) fn new(
         sender: UnboundedSender<SchedulerMsg>,
         sender: UnboundedSender<SchedulerMsg>,
         receiver: UnboundedReceiver<SchedulerMsg>,
         receiver: UnboundedReceiver<SchedulerMsg>,
+        component_capacity: usize,
+        element_capacity: usize,
     ) -> Self {
     ) -> Self {
         /*
         /*
         Preallocate 2000 elements and 100 scopes to avoid dynamic allocation.
         Preallocate 2000 elements and 100 scopes to avoid dynamic allocation.
         Perhaps this should be configurable from some external config?
         Perhaps this should be configurable from some external config?
         */
         */
-        let components = Rc::new(UnsafeCell::new(Slab::with_capacity(100)));
-        let raw_elements = Rc::new(UnsafeCell::new(Slab::with_capacity(2000)));
 
 
-        let heuristics = HeuristicsEngine::new();
-
-        let task_counter = Rc::new(Cell::new(0));
-        let cur_subtree = Rc::new(Cell::new(0));
+        // let components = Rc::new(UnsafeCell::new(Slab::with_capacity(component_capacity)));
+        let raw_elements = Rc::new(UnsafeCell::new(Slab::with_capacity(element_capacity)));
 
 
         let channel = EventChannel {
         let channel = EventChannel {
-            cur_subtree,
-            task_counter: task_counter.clone(),
             sender: sender.clone(),
             sender: sender.clone(),
-            schedule_any_immediate: {
-                let sender = sender.clone();
-                Rc::new(move |id| {
-                    //
-                    log::debug!("scheduling immediate! {:?}", id);
-                    sender.unbounded_send(SchedulerMsg::Immediate(id)).unwrap()
-                })
-            },
-            // todo: we want to get the futures out of the scheduler message
-            // the scheduler message should be send/sync
-            submit_task: {
-                Rc::new(move |fiber_task| {
-                    let task_id = task_counter.get();
-                    task_counter.set(task_id + 1);
-
-                    todo!();
-                    // sender
-                    //     .unbounded_send(SchedulerMsg::Task(TaskMsg::SubmitTask(
-                    //         fiber_task, task_id,
-                    //     )))
-                    //     .unwrap();
-                    TaskHandle {
-                        our_id: task_id,
-                        sender: sender.clone(),
-                    }
-                })
-            },
             get_shared_context: {
             get_shared_context: {
-                let components = components.clone();
-                Rc::new(move |id, ty| {
-                    let components = unsafe { &*components.get() };
-                    let mut search: Option<&ScopeInner> = components.get(id.0);
-                    while let Some(inner) = search.take() {
-                        if let Some(shared) = inner.shared_contexts.borrow().get(&ty) {
-                            return Some(shared.clone());
-                        } else {
-                            search = inner.parent_idx.map(|id| components.get(id.0)).flatten();
-                        }
-                    }
-                    None
-                })
+                todo!()
+                // let components = components.clone();
+                // Rc::new(move |id, ty| {
+                //     let components = unsafe { &*components.get() };
+                //     let mut search: Option<&ScopeInner> = components.get(id.0);
+                //     while let Some(inner) = search.take() {
+                //         if let Some(shared) = inner.shared_contexts.borrow().get(&ty) {
+                //             return Some(shared.clone());
+                //         } else {
+                //             search = inner.parent_idx.map(|id| components.get(id.0)).flatten();
+                //         }
+                //     }
+                //     None
+                // })
             },
             },
         };
         };
 
 
-        let pool = ResourcePool {
-            components,
-            raw_elements,
-            channel,
-        };
-
-        let async_tasks = FuturesUnordered::new();
-
-        // push a task that would never resolve - prevents us from immediately aborting the scheduler
-        async_tasks.push(Box::pin(async {
-            std::future::pending::<()>().await;
-            ScopeId(0)
-        }) as FiberTask);
+        // let pool = ResourcePool {
+        //     components,
+        //     raw_elements,
+        //     channel,
+        // };
 
 
         let saved_state = SavedDiffWork {
         let saved_state = SavedDiffWork {
             mutations: Mutations::new(),
             mutations: Mutations::new(),
@@ -251,22 +216,15 @@ impl Scheduler {
         };
         };
 
 
         Self {
         Self {
-            pool,
-
+            // pool,
             receiver,
             receiver,
 
 
-            async_tasks,
-
             pending_garbage: FxHashSet::default(),
             pending_garbage: FxHashSet::default(),
 
 
-            heuristics,
-
             ui_events: VecDeque::new(),
             ui_events: VecDeque::new(),
 
 
             pending_immediates: VecDeque::new(),
             pending_immediates: VecDeque::new(),
 
 
-            pending_tasks: VecDeque::new(),
-
             batched_events: VecDeque::new(),
             batched_events: VecDeque::new(),
 
 
             garbage_scopes: HashSet::new(),
             garbage_scopes: HashSet::new(),
@@ -275,6 +233,8 @@ impl Scheduler {
             dirty_scopes: Default::default(),
             dirty_scopes: Default::default(),
             saved_state: Some(saved_state),
             saved_state: Some(saved_state),
             in_progress: false,
             in_progress: false,
+
+            heuristics: todo!(),
         }
         }
     }
     }
 
 
@@ -282,7 +242,7 @@ impl Scheduler {
     pub fn handle_ui_event(&mut self, event: UserEvent) -> bool {
     pub fn handle_ui_event(&mut self, event: UserEvent) -> bool {
         let (discrete, priority) = event_meta(&event);
         let (discrete, priority) = event_meta(&event);
 
 
-        if let Some(scope) = self.pool.get_scope_mut(event.scope) {
+        if let Some(scope) = self.get_scope_mut(&event.scope) {
             if let Some(element) = event.mounted_dom_id {
             if let Some(element) = event.mounted_dom_id {
                 // TODO: bubble properly here
                 // TODO: bubble properly here
                 scope.call_listener(event, element);
                 scope.call_listener(event, element);
@@ -308,7 +268,7 @@ impl Scheduler {
 
 
     fn prepare_work(&mut self) {
     fn prepare_work(&mut self) {
         // while let Some(trigger) = self.ui_events.pop_back() {
         // while let Some(trigger) = self.ui_events.pop_back() {
-        //     if let Some(scope) = self.pool.get_scope_mut(trigger.scope) {}
+        //     if let Some(scope) = self.get_scope_mut(&trigger.scope) {}
         // }
         // }
     }
     }
 
 
@@ -328,15 +288,9 @@ impl Scheduler {
     unsafe fn load_work(&mut self) -> SavedDiffWork<'static> {
     unsafe fn load_work(&mut self) -> SavedDiffWork<'static> {
         self.saved_state.take().unwrap().extend()
         self.saved_state.take().unwrap().extend()
     }
     }
-    pub fn handle_task(&mut self, evt: TaskMsg) {
-        //
-    }
 
 
     pub fn handle_channel_msg(&mut self, msg: SchedulerMsg) {
     pub fn handle_channel_msg(&mut self, msg: SchedulerMsg) {
         match msg {
         match msg {
-            //
-            SchedulerMsg::Task(msg) => todo!(),
-
             SchedulerMsg::Immediate(_) => todo!(),
             SchedulerMsg::Immediate(_) => todo!(),
 
 
             SchedulerMsg::UiEvent(event) => {
             SchedulerMsg::UiEvent(event) => {
@@ -344,7 +298,7 @@ impl Scheduler {
 
 
                 let (discrete, priority) = event_meta(&event);
                 let (discrete, priority) = event_meta(&event);
 
 
-                if let Some(scope) = self.pool.get_scope_mut(event.scope) {
+                if let Some(scope) = self.get_scope_mut(&event.scope) {
                     if let Some(element) = event.mounted_dom_id {
                     if let Some(element) = event.mounted_dom_id {
                         // TODO: bubble properly here
                         // TODO: bubble properly here
                         scope.call_listener(event, element);
                         scope.call_listener(event, element);
@@ -374,19 +328,19 @@ impl Scheduler {
         let saved_state = unsafe { self.load_work() };
         let saved_state = unsafe { self.load_work() };
 
 
         // We have to split away some parts of ourself - current lane is borrowed mutably
         // We have to split away some parts of ourself - current lane is borrowed mutably
-        let shared = self.pool.clone();
+        let shared = self.clone();
         let mut machine = unsafe { saved_state.promote(&shared) };
         let mut machine = unsafe { saved_state.promote(&shared) };
 
 
         let mut ran_scopes = FxHashSet::default();
         let mut ran_scopes = FxHashSet::default();
 
 
         if machine.stack.is_empty() {
         if machine.stack.is_empty() {
-            let shared = self.pool.clone();
+            let shared = self.clone();
 
 
             self.dirty_scopes
             self.dirty_scopes
-                .retain(|id| shared.get_scope(*id).is_some());
+                .retain(|id| shared.get_scope(id).is_some());
             self.dirty_scopes.sort_by(|a, b| {
             self.dirty_scopes.sort_by(|a, b| {
-                let h1 = shared.get_scope(*a).unwrap().height;
-                let h2 = shared.get_scope(*b).unwrap().height;
+                let h1 = shared.get_scope(a).unwrap().height;
+                let h2 = shared.get_scope(b).unwrap().height;
                 h1.cmp(&h2).reverse()
                 h1.cmp(&h2).reverse()
             });
             });
 
 
@@ -396,8 +350,8 @@ impl Scheduler {
                     ran_scopes.insert(scopeid);
                     ran_scopes.insert(scopeid);
                     log::debug!("about to run scope {:?}", scopeid);
                     log::debug!("about to run scope {:?}", scopeid);
 
 
-                    if let Some(component) = self.pool.get_scope_mut(scopeid) {
-                        if component.run_scope(&self.pool) {
+                    if let Some(component) = self.get_scope_mut(&scopeid) {
+                        if component.run_scope(&self) {
                             let (old, new) =
                             let (old, new) =
                                 (component.frames.wip_head(), component.frames.fin_head());
                                 (component.frames.wip_head(), component.frames.fin_head());
                             // let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
                             // let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
@@ -494,7 +448,6 @@ impl Scheduler {
         while self.has_any_work() {
         while self.has_any_work() {
             while let Ok(Some(msg)) = self.receiver.try_next() {
             while let Ok(Some(msg)) = self.receiver.try_next() {
                 match msg {
                 match msg {
-                    SchedulerMsg::Task(t) => todo!(),
                     SchedulerMsg::Immediate(im) => {
                     SchedulerMsg::Immediate(im) => {
                         self.dirty_scopes.insert(im);
                         self.dirty_scopes.insert(im);
                     }
                     }
@@ -506,7 +459,7 @@ impl Scheduler {
 
 
             // switch our priority, pop off any work
             // switch our priority, pop off any work
             while let Some(event) = self.ui_events.pop_front() {
             while let Some(event) = self.ui_events.pop_front() {
-                if let Some(scope) = self.pool.get_scope_mut(event.scope) {
+                if let Some(scope) = self.get_scope_mut(&event.scope) {
                     if let Some(element) = event.mounted_dom_id {
                     if let Some(element) = event.mounted_dom_id {
                         log::info!("Calling listener {:?}, {:?}", event.scope, element);
                         log::info!("Calling listener {:?}, {:?}", event.scope, element);
 
 
@@ -519,7 +472,6 @@ impl Scheduler {
                                     self.dirty_scopes.insert(im);
                                     self.dirty_scopes.insert(im);
                                 }
                                 }
                                 SchedulerMsg::UiEvent(e) => self.ui_events.push_back(e),
                                 SchedulerMsg::UiEvent(e) => self.ui_events.push_back(e),
-                                SchedulerMsg::Task(_) => todo!(),
                             }
                             }
                         }
                         }
                     }
                     }
@@ -562,19 +514,19 @@ impl Scheduler {
     ///
     ///
     /// Typically used to kickstart the VirtualDOM after initialization.
     /// Typically used to kickstart the VirtualDOM after initialization.
     pub fn rebuild(&mut self, base_scope: ScopeId) -> Mutations {
     pub fn rebuild(&mut self, base_scope: ScopeId) -> Mutations {
-        let mut shared = self.pool.clone();
+        let mut shared = self.clone();
         let mut diff_machine = DiffMachine::new(Mutations::new(), &mut shared);
         let mut diff_machine = DiffMachine::new(Mutations::new(), &mut shared);
 
 
         // TODO: drain any in-flight work
         // TODO: drain any in-flight work
         let cur_component = self
         let cur_component = self
             .pool
             .pool
-            .get_scope_mut(base_scope)
+            .get_scope_mut(&base_scope)
             .expect("The base scope should never be moved");
             .expect("The base scope should never be moved");
 
 
         log::debug!("rebuild {:?}", base_scope);
         log::debug!("rebuild {:?}", base_scope);
 
 
         // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
         // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
-        if cur_component.run_scope(&self.pool) {
+        if cur_component.run_scope(&self) {
             diff_machine
             diff_machine
                 .stack
                 .stack
                 .create_node(cur_component.frames.fin_head(), MountType::Append);
                 .create_node(cur_component.frames.fin_head(), MountType::Append);
@@ -596,13 +548,13 @@ impl Scheduler {
     pub fn hard_diff(&mut self, base_scope: ScopeId) -> Mutations {
     pub fn hard_diff(&mut self, base_scope: ScopeId) -> Mutations {
         let cur_component = self
         let cur_component = self
             .pool
             .pool
-            .get_scope_mut(base_scope)
+            .get_scope_mut(&base_scope)
             .expect("The base scope should never be moved");
             .expect("The base scope should never be moved");
 
 
         log::debug!("hard diff {:?}", base_scope);
         log::debug!("hard diff {:?}", base_scope);
 
 
-        if cur_component.run_scope(&self.pool) {
-            let mut diff_machine = DiffMachine::new(Mutations::new(), &mut self.pool);
+        if cur_component.run_scope(&self) {
+            let mut diff_machine = DiffMachine::new(Mutations::new(), &mut self);
             diff_machine.cfg.force_diff = true;
             diff_machine.cfg.force_diff = true;
             diff_machine.diff_scope(base_scope);
             diff_machine.diff_scope(base_scope);
             diff_machine.mutations
             diff_machine.mutations
@@ -611,3 +563,35 @@ impl Scheduler {
         }
         }
     }
     }
 }
 }
+
+impl Future for Scheduler {
+    type Output = ();
+
+    fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
+        let mut all_pending = true;
+
+        for fut in self.pending_futures.iter() {
+            let scope = self
+                .pool
+                .get_scope_mut(&fut)
+                .expect("Scope should never be moved");
+
+            let items = scope.items.get_mut();
+            for task in items.tasks.iter_mut() {
+                let t = task.as_mut();
+                let g = unsafe { Pin::new_unchecked(t) };
+                match g.poll(cx) {
+                    Poll::Ready(r) => {
+                        all_pending = false;
+                    }
+                    Poll::Pending => {}
+                }
+            }
+        }
+
+        match all_pending {
+            true => Poll::Pending,
+            false => Poll::Ready(()),
+        }
+    }
+}

+ 201 - 227
packages/core/src/scope.rs

@@ -6,36 +6,27 @@ use std::{
     cell::{Cell, RefCell},
     cell::{Cell, RefCell},
     collections::HashMap,
     collections::HashMap,
     future::Future,
     future::Future,
-    pin::Pin,
     rc::Rc,
     rc::Rc,
 };
 };
 
 
-use crate::{innerlude::*, lazynodes::LazyNodes};
 use bumpalo::{boxed::Box as BumpBox, Bump};
 use bumpalo::{boxed::Box as BumpBox, Bump};
-use std::ops::Deref;
 
 
 /// Components in Dioxus use the "Context" object to interact with their lifecycle.
 /// Components in Dioxus use the "Context" object to interact with their lifecycle.
 ///
 ///
 /// This lets components access props, schedule updates, integrate hooks, and expose shared state.
 /// This lets components access props, schedule updates, integrate hooks, and expose shared state.
 ///
 ///
-/// Note: all of these methods are *imperative* - they do not act as hooks! They are meant to be used by hooks
-/// to provide complex behavior. For instance, calling "add_shared_state" on every render is considered a leak. This method
-/// exists for the `use_provide_state` hook to provide a shared state object.
-///
 /// For the most part, the only method you should be using regularly is `render`.
 /// For the most part, the only method you should be using regularly is `render`.
 ///
 ///
 /// ## Example
 /// ## Example
 ///
 ///
 /// ```ignore
 /// ```ignore
-/// #[derive(Properties)]
-/// struct Props {
+/// #[derive(Props)]
+/// struct ExampleProps {
 ///     name: String
 ///     name: String
 /// }
 /// }
 ///
 ///
-/// fn example(cx: Context<Props>) -> VNode {
-///     html! {
-///         <div> "Hello, {cx.name}" </div>
-///     }
+/// fn Example((cx, props): Scope<Props>) -> Element {
+///     cx.render(rsx!{ div {"Hello, {props.name}"} })
 /// }
 /// }
 /// ```
 /// ```
 pub type Context<'a> = &'a ScopeInner;
 pub type Context<'a> = &'a ScopeInner;
@@ -59,7 +50,8 @@ pub struct ScopeInner {
 
 
     // Nodes
     // Nodes
     pub(crate) frames: ActiveFrame,
     pub(crate) frames: ActiveFrame,
-    pub(crate) caller: BumpBox<'static, dyn for<'b> Fn(&'b ScopeInner) -> Element<'b>>,
+
+    pub(crate) vcomp: *const VComponent<'static>,
 
 
     /*
     /*
     we care about:
     we care about:
@@ -67,12 +59,7 @@ pub struct ScopeInner {
     - borrowed props (and how to drop them when the parent is dropped)
     - borrowed props (and how to drop them when the parent is dropped)
     - suspended nodes (and how to call their callback when their associated tasks are complete)
     - suspended nodes (and how to call their callback when their associated tasks are complete)
     */
     */
-    pub(crate) listeners: RefCell<Vec<*const Listener<'static>>>,
-    pub(crate) borrowed_props: RefCell<Vec<*const VComponent<'static>>>,
-    pub(crate) suspended_nodes: RefCell<FxHashMap<u64, *const VSuspended<'static>>>,
-
-    pub(crate) tasks: RefCell<Vec<BumpBox<'static, dyn Future<Output = ()>>>>,
-    pub(crate) pending_effects: RefCell<Vec<BumpBox<'static, dyn FnMut()>>>,
+    pub(crate) items: RefCell<SelfReferentialItems<'static>>,
 
 
     // State
     // State
     pub(crate) hooks: HookList,
     pub(crate) hooks: HookList,
@@ -80,126 +67,17 @@ pub struct ScopeInner {
     // todo: move this into a centralized place - is more memory efficient
     // todo: move this into a centralized place - is more memory efficient
     pub(crate) shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
     pub(crate) shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
 
 
-    // whenever set_state is called, we fire off a message to the scheduler
-    // this closure _is_ the method called by schedule_update that marks this component as dirty
-    pub(crate) memoized_updater: Rc<dyn Fn()>,
-
     pub(crate) shared: EventChannel,
     pub(crate) shared: EventChannel,
 }
 }
 
 
-/// Public interface for Scopes.
-impl ScopeInner {
-    /// Get the root VNode for this Scope.
-    ///
-    /// This VNode is the "entrypoint" VNode. If the component renders multiple nodes, then this VNode will be a fragment.
-    ///
-    /// # Example
-    /// ```rust
-    /// let mut dom = VirtualDom::new(|(cx, props)|cx.render(rsx!{ div {} }));
-    /// dom.rebuild();
-    ///
-    /// let base = dom.base_scope();
-    ///
-    /// if let VNode::VElement(node) = base.root_node() {
-    ///     assert_eq!(node.tag_name, "div");
-    /// }
-    /// ```
-    pub fn root_node(&self) -> &VNode {
-        self.frames.fin_head()
-    }
-
-    /// Get the subtree ID that this scope belongs to.
-    ///
-    /// Each component has its own subtree ID - the root subtree has an ID of 0. This ID is used by the renderer to route
-    /// the mutations to the correct window/portal/subtree.
-    ///
-    ///
-    /// # Example
-    ///
-    /// ```rust
-    /// let mut dom = VirtualDom::new(|(cx, props)|cx.render(rsx!{ div {} }));
-    /// dom.rebuild();
-    ///
-    /// let base = dom.base_scope();
-    ///
-    /// assert_eq!(base.subtree(), 0);
-    /// ```
-    pub fn subtree(&self) -> u32 {
-        self.subtree.get()
-    }
-
-    pub(crate) fn new_subtree(&self) -> Option<u32> {
-        if self.is_subtree_root.get() {
-            None
-        } else {
-            let cur = self.shared.cur_subtree.get();
-            self.shared.cur_subtree.set(cur + 1);
-            Some(cur)
-        }
-    }
-
-    /// Get the height of this Scope - IE the number of scopes above it.
-    ///
-    /// A Scope with a height of `0` is the root scope - there are no other scopes above it.
-    ///
-    /// # Example
-    ///
-    /// ```rust
-    /// let mut dom = VirtualDom::new(|(cx, props)|cx.render(rsx!{ div {} }));
-    /// dom.rebuild();
-    ///
-    /// let base = dom.base_scope();
-    ///
-    /// assert_eq!(base.height(), 0);
-    /// ```
-    pub fn height(&self) -> u32 {
-        self.height
-    }
-
-    /// Get the Parent of this Scope within this Dioxus VirtualDOM.
-    ///
-    /// This ID is not unique across Dioxus VirtualDOMs or across time. IDs will be reused when components are unmounted.
-    ///
-    /// The base component will not have a parent, and will return `None`.
-    ///
-    /// # Example
-    ///
-    /// ```rust
-    /// let mut dom = VirtualDom::new(|(cx, props)|cx.render(rsx!{ div {} }));
-    /// dom.rebuild();
-    ///
-    /// let base = dom.base_scope();
-    ///
-    /// assert_eq!(base.parent(), None);
-    /// ```
-    pub fn parent(&self) -> Option<ScopeId> {
-        self.parent_idx
-    }
-
-    /// Get the ID of this Scope within this Dioxus VirtualDOM.
-    ///
-    /// This ID is not unique across Dioxus VirtualDOMs or across time. IDs will be reused when components are unmounted.
-    ///
-    /// # Example
-    ///
-    /// ```rust
-    /// let mut dom = VirtualDom::new(|(cx, props)|cx.render(rsx!{ div {} }));
-    /// dom.rebuild();
-    /// let base = dom.base_scope();
-    ///
-    /// assert_eq!(base.scope_id(), 0);
-    /// ```
-    pub fn scope_id(&self) -> ScopeId {
-        self.our_arena_idx
-    }
+pub struct SelfReferentialItems<'a> {
+    pub(crate) listeners: Vec<*const Listener<'a>>,
+    pub(crate) borrowed_props: Vec<*const VComponent<'a>>,
+    pub(crate) suspended_nodes: FxHashMap<u64, *const VSuspended<'a>>,
+    pub(crate) tasks: Vec<BumpBox<'a, dyn Future<Output = ()>>>,
+    pub(crate) pending_effects: Vec<BumpBox<'a, dyn FnMut()>>,
 }
 }
 
 
-// The type of closure that wraps calling components
-/// The type of task that gets sent to the task scheduler
-/// Submitting a fiber task returns a handle to that task, which can be used to wake up suspended nodes
-pub type FiberTask = Pin<Box<dyn Future<Output = ScopeId>>>;
-
-/// Private interface for Scopes.
 impl ScopeInner {
 impl ScopeInner {
     // we are being created in the scope of an existing component (where the creator_node lifetime comes into play)
     // we are being created in the scope of an existing component (where the creator_node lifetime comes into play)
     // we are going to break this lifetime by force in order to save it on ourselves.
     // we are going to break this lifetime by force in order to save it on ourselves.
@@ -209,48 +87,36 @@ impl ScopeInner {
     // Scopes cannot be made anywhere else except for this file
     // Scopes cannot be made anywhere else except for this file
     // Therefore, their lifetimes are connected exclusively to the virtual dom
     // Therefore, their lifetimes are connected exclusively to the virtual dom
     pub(crate) fn new(
     pub(crate) fn new(
-        caller: BumpBox<dyn for<'b> Fn(&'b ScopeInner) -> Element<'b>>,
+        vcomp: &VComponent,
         our_arena_idx: ScopeId,
         our_arena_idx: ScopeId,
         parent_idx: Option<ScopeId>,
         parent_idx: Option<ScopeId>,
         height: u32,
         height: u32,
         subtree: u32,
         subtree: u32,
         shared: EventChannel,
         shared: EventChannel,
     ) -> Self {
     ) -> Self {
-        let schedule_any_update = shared.schedule_any_immediate.clone();
-
-        let memoized_updater = Rc::new(move || schedule_any_update(our_arena_idx));
-
-        // wipe away the associated lifetime - we are going to manually manage the one-way lifetime graph
-        let caller = unsafe { std::mem::transmute(caller) };
+        let vcomp = unsafe { std::mem::transmute(vcomp as *const VComponent) };
 
 
         Self {
         Self {
-            memoized_updater,
             shared,
             shared,
-            caller,
             parent_idx,
             parent_idx,
             our_arena_idx,
             our_arena_idx,
             height,
             height,
             subtree: Cell::new(subtree),
             subtree: Cell::new(subtree),
             is_subtree_root: Cell::new(false),
             is_subtree_root: Cell::new(false),
-            tasks: Default::default(),
             frames: ActiveFrame::new(),
             frames: ActiveFrame::new(),
-            hooks: Default::default(),
+            vcomp,
 
 
-            pending_effects: Default::default(),
-            suspended_nodes: Default::default(),
+            hooks: Default::default(),
             shared_contexts: Default::default(),
             shared_contexts: Default::default(),
-            listeners: Default::default(),
-            borrowed_props: Default::default(),
-        }
-    }
 
 
-    pub(crate) fn update_scope_dependencies(
-        &mut self,
-        caller: &dyn for<'b> Fn(&'b ScopeInner) -> Element<'b>,
-    ) {
-        log::debug!("Updating scope dependencies {:?}", self.our_arena_idx);
-        let caller = caller as *const _;
-        self.caller = unsafe { std::mem::transmute(caller) };
+            items: RefCell::new(SelfReferentialItems {
+                listeners: Default::default(),
+                borrowed_props: Default::default(),
+                suspended_nodes: Default::default(),
+                tasks: Default::default(),
+                pending_effects: Default::default(),
+            }),
+        }
     }
     }
 
 
     /// This method cleans up any references to data held within our hook list. This prevents mutable aliasing from
     /// This method cleans up any references to data held within our hook list. This prevents mutable aliasing from
@@ -269,8 +135,9 @@ impl ScopeInner {
         // make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we
         // make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we
         // run the hooks (which hold an &mut Reference)
         // run the hooks (which hold an &mut Reference)
         // right now, we don't drop
         // right now, we don't drop
-        self.borrowed_props
+        self.items
             .get_mut()
             .get_mut()
+            .borrowed_props
             .drain(..)
             .drain(..)
             .map(|li| unsafe { &*li })
             .map(|li| unsafe { &*li })
             .for_each(|comp| {
             .for_each(|comp| {
@@ -280,17 +147,19 @@ impl ScopeInner {
                     .get()
                     .get()
                     .expect("VComponents should be associated with a valid Scope");
                     .expect("VComponents should be associated with a valid Scope");
 
 
-                if let Some(scope) = pool.get_scope_mut(scope_id) {
+                if let Some(scope) = pool.get_scope_mut(&scope_id) {
                     scope.ensure_drop_safety(pool);
                     scope.ensure_drop_safety(pool);
 
 
-                    let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
-                    drop_props();
+                    todo!("drop the component's props");
+                    // let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
+                    // drop_props();
                 }
                 }
             });
             });
 
 
         // Now that all the references are gone, we can safely drop our own references in our listeners.
         // Now that all the references are gone, we can safely drop our own references in our listeners.
-        self.listeners
+        self.items
             .get_mut()
             .get_mut()
+            .listeners
             .drain(..)
             .drain(..)
             .map(|li| unsafe { &*li })
             .map(|li| unsafe { &*li })
             .for_each(|listener| drop(listener.callback.borrow_mut().take()));
             .for_each(|listener| drop(listener.callback.borrow_mut().take()));
@@ -298,7 +167,7 @@ impl ScopeInner {
 
 
     /// A safe wrapper around calling listeners
     /// A safe wrapper around calling listeners
     pub(crate) fn call_listener(&mut self, event: UserEvent, element: ElementId) {
     pub(crate) fn call_listener(&mut self, event: UserEvent, element: ElementId) {
-        let listners = self.listeners.borrow_mut();
+        let listners = &mut self.items.get_mut().listeners;
 
 
         let raw_listener = listners.iter().find(|lis| {
         let raw_listener = listners.iter().find(|lis| {
             let search = unsafe { &***lis };
             let search = unsafe { &***lis };
@@ -321,12 +190,10 @@ impl ScopeInner {
         }
         }
     }
     }
 
 
-    /*
-    General strategy here is to load up the appropriate suspended task and then run it.
-    Suspended nodes cannot be called repeatedly.
-    */
+    // General strategy here is to load up the appropriate suspended task and then run it.
+    // Suspended nodes cannot be called repeatedly.
     pub(crate) fn call_suspended_node<'a>(&'a mut self, task_id: u64) {
     pub(crate) fn call_suspended_node<'a>(&'a mut self, task_id: u64) {
-        let mut nodes = self.suspended_nodes.borrow_mut();
+        let mut nodes = &mut self.items.get_mut().suspended_nodes;
 
 
         if let Some(suspended) = nodes.remove(&task_id) {
         if let Some(suspended) = nodes.remove(&task_id) {
             let sus: &'a VSuspended<'static> = unsafe { &*suspended };
             let sus: &'a VSuspended<'static> = unsafe { &*suspended };
@@ -338,19 +205,9 @@ impl ScopeInner {
 
 
     // run the list of effects
     // run the list of effects
     pub(crate) fn run_effects(&mut self, pool: &ResourcePool) {
     pub(crate) fn run_effects(&mut self, pool: &ResourcePool) {
-        todo!()
-        // let mut effects = self.frames.effects.borrow_mut();
-        // let mut effects = effects.drain(..).collect::<Vec<_>>();
-
-        // for effect in effects {
-        //     let effect = unsafe { &*effect };
-        //     let effect = effect.as_ref();
-
-        //     let mut effect = effect.borrow_mut();
-        //     let mut effect = effect.as_mut();
-
-        //     effect.run(pool);
-        // }
+        for mut effect in self.items.get_mut().pending_effects.drain(..) {
+            effect();
+        }
     }
     }
 
 
     /// Render this component.
     /// Render this component.
@@ -371,18 +228,22 @@ impl ScopeInner {
         // - We've dropped all references to the wip bump frame
         // - We've dropped all references to the wip bump frame
         unsafe { self.frames.reset_wip_frame() };
         unsafe { self.frames.reset_wip_frame() };
 
 
+        let items = self.items.get_mut();
+
         // just forget about our suspended nodes while we're at it
         // just forget about our suspended nodes while we're at it
-        self.suspended_nodes.get_mut().clear();
+        items.suspended_nodes.clear();
 
 
         // guarantee that we haven't screwed up - there should be no latent references anywhere
         // guarantee that we haven't screwed up - there should be no latent references anywhere
-        debug_assert!(self.listeners.borrow().is_empty());
-        debug_assert!(self.suspended_nodes.borrow().is_empty());
-        debug_assert!(self.borrowed_props.borrow().is_empty());
+        debug_assert!(items.listeners.is_empty());
+        debug_assert!(items.suspended_nodes.is_empty());
+        debug_assert!(items.borrowed_props.is_empty());
 
 
         log::debug!("Borrowed stuff is successfully cleared");
         log::debug!("Borrowed stuff is successfully cleared");
 
 
-        // Cast the caller ptr from static to one with our own reference
-        let render: &dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> = unsafe { &*self.caller };
+        // temporarily cast the vcomponent to the right lifetime
+        let vcomp = self.load_vcomp();
+
+        let render: &dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> = todo!();
 
 
         // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
         // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
         if let Some(builder) = render(self) {
         if let Some(builder) = render(self) {
@@ -400,34 +261,160 @@ impl ScopeInner {
             false
             false
         }
         }
     }
     }
+
+    pub(crate) fn new_subtree(&self) -> Option<u32> {
+        todo!()
+        // if self.is_subtree_root.get() {
+        //     None
+        // } else {
+        //     let cur = self.shared.cur_subtree.get();
+        //     self.shared.cur_subtree.set(cur + 1);
+        //     Some(cur)
+        // }
+    }
+
+    pub(crate) fn update_vcomp(&mut self, vcomp: &VComponent) {
+        let f: *const _ = vcomp;
+        self.vcomp = unsafe { std::mem::transmute(f) };
+    }
+
+    pub(crate) fn load_vcomp<'a>(&'a mut self) -> &'a VComponent<'a> {
+        unsafe { std::mem::transmute(&*self.vcomp) }
+    }
+
+    /// Get the root VNode for this Scope.
+    ///
+    /// This VNode is the "entrypoint" VNode. If the component renders multiple nodes, then this VNode will be a fragment.
+    ///
+    /// # Example
+    /// ```rust
+    /// let mut dom = VirtualDom::new(|(cx, props)|cx.render(rsx!{ div {} }));
+    /// dom.rebuild();
+    ///
+    /// let base = dom.base_scope();
+    ///
+    /// if let VNode::VElement(node) = base.root_node() {
+    ///     assert_eq!(node.tag_name, "div");
+    /// }
+    /// ```
+    pub fn root_node(&self) -> &VNode {
+        self.frames.fin_head()
+    }
+
+    /// Get the subtree ID that this scope belongs to.
+    ///
+    /// Each component has its own subtree ID - the root subtree has an ID of 0. This ID is used by the renderer to route
+    /// the mutations to the correct window/portal/subtree.
+    ///
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// let mut dom = VirtualDom::new(|(cx, props)|cx.render(rsx!{ div {} }));
+    /// dom.rebuild();
+    ///
+    /// let base = dom.base_scope();
+    ///
+    /// assert_eq!(base.subtree(), 0);
+    /// ```
+    pub fn subtree(&self) -> u32 {
+        self.subtree.get()
+    }
+
+    /// Get the height of this Scope - IE the number of scopes above it.
+    ///
+    /// A Scope with a height of `0` is the root scope - there are no other scopes above it.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// let mut dom = VirtualDom::new(|(cx, props)|cx.render(rsx!{ div {} }));
+    /// dom.rebuild();
+    ///
+    /// let base = dom.base_scope();
+    ///
+    /// assert_eq!(base.height(), 0);
+    /// ```
+    pub fn height(&self) -> u32 {
+        self.height
+    }
+
+    /// Get the Parent of this Scope within this Dioxus VirtualDOM.
+    ///
+    /// This ID is not unique across Dioxus VirtualDOMs or across time. IDs will be reused when components are unmounted.
+    ///
+    /// The base component will not have a parent, and will return `None`.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// let mut dom = VirtualDom::new(|(cx, props)|cx.render(rsx!{ div {} }));
+    /// dom.rebuild();
+    ///
+    /// let base = dom.base_scope();
+    ///
+    /// assert_eq!(base.parent(), None);
+    /// ```
+    pub fn parent(&self) -> Option<ScopeId> {
+        self.parent_idx
+    }
+
+    /// Get the ID of this Scope within this Dioxus VirtualDOM.
+    ///
+    /// This ID is not unique across Dioxus VirtualDOMs or across time. IDs will be reused when components are unmounted.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// let mut dom = VirtualDom::new(|(cx, props)|cx.render(rsx!{ div {} }));
+    /// dom.rebuild();
+    /// let base = dom.base_scope();
+    ///
+    /// assert_eq!(base.scope_id(), 0);
+    /// ```
+    pub fn scope_id(&self) -> ScopeId {
+        self.our_arena_idx
+    }
+
     /// Create a subscription that schedules a future render for the reference component
     /// Create a subscription that schedules a future render for the reference component
     ///
     ///
     /// ## Notice: you should prefer using prepare_update and get_scope_id
     /// ## Notice: you should prefer using prepare_update and get_scope_id
     pub fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
     pub fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
-        self.memoized_updater.clone()
+        // pub fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
+        let chan = self.shared.sender.clone();
+        let id = self.scope_id();
+        Rc::new(move || {
+            chan.unbounded_send(SchedulerMsg::Immediate(id));
+        })
+    }
+
+    /// Schedule an update for any component given its ScopeId.
+    ///
+    /// A component's ScopeId can be obtained from `use_hook` or the [`Context::scope_id`] method.
+    ///
+    /// This method should be used when you want to schedule an update for a component
+    pub fn schedule_update_any(&self) -> Rc<dyn Fn(ScopeId)> {
+        let chan = self.shared.sender.clone();
+        Rc::new(move |id| {
+            chan.unbounded_send(SchedulerMsg::Immediate(id));
+        })
     }
     }
 
 
     /// Get the [`ScopeId`] of a mounted component.
     /// Get the [`ScopeId`] of a mounted component.
     ///
     ///
     /// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
     /// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
     pub fn needs_update(&self) {
     pub fn needs_update(&self) {
-        (self.memoized_updater)()
+        self.needs_update_any(self.scope_id())
     }
     }
 
 
     /// Get the [`ScopeId`] of a mounted component.
     /// Get the [`ScopeId`] of a mounted component.
     ///
     ///
     /// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
     /// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
     pub fn needs_update_any(&self, id: ScopeId) {
     pub fn needs_update_any(&self, id: ScopeId) {
-        (self.shared.schedule_any_immediate)(id)
-    }
-
-    /// Schedule an update for any component given its ScopeId.
-    ///
-    /// A component's ScopeId can be obtained from `use_hook` or the [`Context::scope_id`] method.
-    ///
-    /// This method should be used when you want to schedule an update for a component
-    pub fn schedule_update_any(&self) -> Rc<dyn Fn(ScopeId)> {
-        self.shared.schedule_any_immediate.clone()
+        self.shared
+            .sender
+            .unbounded_send(SchedulerMsg::Immediate(id))
+            .unwrap();
     }
     }
 
 
     /// Get the [`ScopeId`] of a mounted component.
     /// Get the [`ScopeId`] of a mounted component.
@@ -475,8 +462,9 @@ impl ScopeInner {
         // erase the 'src lifetime for self-referential storage
         // erase the 'src lifetime for self-referential storage
         let self_ref_fut = unsafe { std::mem::transmute(boxed_fut) };
         let self_ref_fut = unsafe { std::mem::transmute(boxed_fut) };
 
 
-        self.pending_effects.borrow_mut().push(self_ref_fut);
-        self.pending_effects.borrow().len() - 1
+        let mut items = self.items.borrow_mut();
+        items.pending_effects.push(self_ref_fut);
+        items.pending_effects.len() - 1
     }
     }
 
 
     /// Pushes the future onto the poll queue to be polled
     /// Pushes the future onto the poll queue to be polled
@@ -491,8 +479,9 @@ impl ScopeInner {
         // erase the 'src lifetime for self-referential storage
         // erase the 'src lifetime for self-referential storage
         let self_ref_fut = unsafe { std::mem::transmute(boxed_fut) };
         let self_ref_fut = unsafe { std::mem::transmute(boxed_fut) };
 
 
-        self.tasks.borrow_mut().push(self_ref_fut);
-        self.tasks.borrow().len() - 1
+        let mut items = self.items.borrow_mut();
+        items.tasks.push(self_ref_fut);
+        items.tasks.len() - 1
     }
     }
 
 
     /// This method enables the ability to expose state to children further down the VirtualDOM Tree.
     /// This method enables the ability to expose state to children further down the VirtualDOM Tree.
@@ -580,8 +569,9 @@ impl ScopeInner {
     ///
     ///
     /// - Initializer: closure used to create the initial hook state
     /// - Initializer: closure used to create the initial hook state
     /// - Runner: closure used to output a value every time the hook is used
     /// - Runner: closure used to output a value every time the hook is used
-    /// - Cleanup: closure used to teardown the hook once the dom is cleaned up
     ///
     ///
+    /// To "cleanup" the hook, implement `Drop` on the stored hook value. Whenever the component is dropped, the hook
+    /// will be dropped as well.
     ///
     ///
     /// # Example
     /// # Example
     ///
     ///
@@ -591,32 +581,16 @@ impl ScopeInner {
     ///     use_hook(
     ///     use_hook(
     ///         || Rc::new(RefCell::new(initial_value())),
     ///         || Rc::new(RefCell::new(initial_value())),
     ///         |state| state,
     ///         |state| state,
-    ///         |_| {},
     ///     )
     ///     )
     /// }
     /// }
     /// ```
     /// ```
-    pub fn use_hook<'src, State, Output, Init, Run, Cleanup>(
+    pub fn use_hook<'src, State: 'static, Output: 'src>(
         &'src self,
         &'src self,
-        initializer: Init,
-        runner: Run,
-        cleanup: Cleanup,
-    ) -> Output
-    where
-        State: 'static,
-        Output: 'src,
-        Init: FnOnce(usize) -> State,
-        Run: FnOnce(&'src mut State) -> Output,
-        Cleanup: FnOnce(Box<State>) + 'static,
-    {
-        // If the idx is the same as the hook length, then we need to add the current hook
+        initializer: impl FnOnce(usize) -> State,
+        runner: impl FnOnce(&'src mut State) -> Output,
+    ) -> Output {
         if self.hooks.at_end() {
         if self.hooks.at_end() {
-            self.hooks.push_hook(
-                initializer(self.hooks.len()),
-                Box::new(|raw| {
-                    let s = raw.downcast::<State>().unwrap();
-                    cleanup(s);
-                }),
-            );
+            self.hooks.push_hook(initializer(self.hooks.len()));
         }
         }
 
 
         runner(self.hooks.next::<State>().expect(HOOK_ERR_MSG))
         runner(self.hooks.next::<State>().expect(HOOK_ERR_MSG))

+ 0 - 39
packages/core/src/tasks.rs

@@ -1,39 +0,0 @@
-use crate::innerlude::*;
-use futures_channel::mpsc::UnboundedSender;
-
-pub struct TaskHandle {
-    pub(crate) sender: UnboundedSender<SchedulerMsg>,
-    pub(crate) our_id: u64,
-}
-
-impl TaskHandle {
-    /// Toggles this coroutine off/on.
-    ///
-    /// This method is not synchronous - your task will not stop immediately.
-    pub fn toggle(&self) {
-        self.sender
-            .unbounded_send(SchedulerMsg::Task(TaskMsg::ToggleTask(self.our_id)))
-            .unwrap()
-    }
-
-    /// This method is not synchronous - your task will not stop immediately.
-    pub fn resume(&self) {
-        self.sender
-            .unbounded_send(SchedulerMsg::Task(TaskMsg::ResumeTask(self.our_id)))
-            .unwrap()
-    }
-
-    /// This method is not synchronous - your task will not stop immediately.
-    pub fn stop(&self) {
-        self.sender
-            .unbounded_send(SchedulerMsg::Task(TaskMsg::ToggleTask(self.our_id)))
-            .unwrap()
-    }
-
-    /// This method is not synchronous - your task will not stop immediately.
-    pub fn restart(&self) {
-        self.sender
-            .unbounded_send(SchedulerMsg::Task(TaskMsg::ToggleTask(self.our_id)))
-            .unwrap()
-    }
-}

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

@@ -13,7 +13,7 @@ impl TestDom {
     pub fn new() -> TestDom {
     pub fn new() -> TestDom {
         let bump = Bump::new();
         let bump = Bump::new();
         let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
         let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
-        let scheduler = Scheduler::new(sender, receiver);
+        let scheduler = Scheduler::new(sender, receiver, 10, 100);
         TestDom { bump, scheduler }
         TestDom { bump, scheduler }
     }
     }
 
 

+ 0 - 53
packages/core/src/util.rs

@@ -11,59 +11,6 @@ pub fn type_name_of<T>(_: T) -> &'static str {
     std::any::type_name::<T>()
     std::any::type_name::<T>()
 }
 }
 
 
-use std::future::Future;
-use std::pin::Pin;
-use std::task::{Context, Poll};
-
-// use crate::task::{Context, Poll};
-
-/// Cooperatively gives up a timeslice to the task scheduler.
-///
-/// Calling this function will move the currently executing future to the back
-/// of the execution queue, making room for other futures to execute. This is
-/// especially useful after running CPU-intensive operations inside a future.
-///
-/// See also [`task::spawn_blocking`].
-///
-/// [`task::spawn_blocking`]: fn.spawn_blocking.html
-///
-/// # Examples
-///
-/// Basic usage:
-///
-/// ```
-/// # async_std::task::block_on(async {
-/// #
-/// use async_std::task;
-///
-/// task::yield_now().await;
-/// #
-/// # })
-/// ```
-#[inline]
-pub async fn yield_now() {
-    YieldNow(false).await
-}
-
-struct YieldNow(bool);
-
-impl Future for YieldNow {
-    type Output = ();
-
-    // The futures executor is implemented as a FIFO queue, so all this future
-    // does is re-schedule the future back to the end of the queue, giving room
-    // for other futures to progress.
-    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-        if !self.0 {
-            self.0 = true;
-            cx.waker().wake_by_ref();
-            Poll::Pending
-        } else {
-            Poll::Ready(())
-        }
-    }
-}
-
 /// A component's unique identifier.
 /// A component's unique identifier.
 ///
 ///
 /// `ScopeId` is a `usize` that is unique across the entire VirtualDOM - but not unique across time. If a component is
 /// `ScopeId` is a `usize` that is unique across the entire VirtualDOM - but not unique across time. If a component is

+ 605 - 57
packages/core/src/virtual_dom.rs

@@ -19,10 +19,24 @@
 //! This module includes just the barebones for a complete VirtualDOM API.
 //! This module includes just the barebones for a complete VirtualDOM API.
 //! Additional functionality is defined in the respective files.
 //! Additional functionality is defined in the respective files.
 
 
+use crate::innerlude::*;
+use bumpalo::Bump;
 use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
 use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use futures_util::{pin_mut, stream::FuturesUnordered, Future, FutureExt, StreamExt};
+use fxhash::FxHashMap;
+use fxhash::FxHashSet;
+use indexmap::IndexSet;
+use slab::Slab;
+use std::pin::Pin;
+use std::task::Poll;
+use std::{
+    any::{Any, TypeId},
+    cell::{Cell, UnsafeCell},
+    collections::{HashSet, VecDeque},
+    rc::Rc,
+};
 
 
 use crate::innerlude::*;
 use crate::innerlude::*;
-use std::{any::Any, rc::Rc};
 
 
 /// An integrated virtual node system that progresses events and diffs UI trees.
 /// An integrated virtual node system that progresses events and diffs UI trees.
 ///
 ///
@@ -55,8 +69,6 @@ use std::{any::Any, rc::Rc};
 /// }
 /// }
 /// ```
 /// ```
 pub struct VirtualDom {
 pub struct VirtualDom {
-    scheduler: Scheduler,
-
     base_scope: ScopeId,
     base_scope: ScopeId,
 
 
     root_fc: Box<dyn Any>,
     root_fc: Box<dyn Any>,
@@ -65,6 +77,49 @@ pub struct VirtualDom {
 
 
     // we need to keep the allocation around, but we don't necessarily use it
     // we need to keep the allocation around, but we don't necessarily use it
     _root_caller: Box<dyn Any>,
     _root_caller: Box<dyn Any>,
+
+    // /// All mounted components are arena allocated to make additions, removals, and references easy to work with
+    // /// A generational arena is used to re-use slots of deleted scopes without having to resize the underlying arena.
+    // ///
+    // /// This is wrapped in an UnsafeCell because we will need to get mutable access to unique values in unique bump arenas
+    // /// and rusts's guarantees cannot prove that this is safe. We will need to maintain the safety guarantees manually.
+    // pub pool: ResourcePool,
+    //
+    pub component_arena: Bump,
+
+    pub free_components: VecDeque<*mut ScopeInner>,
+
+    pub heuristics: FxHashMap<FcSlot, Heuristic>,
+
+    pub receiver: UnboundedReceiver<SchedulerMsg>,
+
+    // Garbage stored
+    pub pending_garbage: FxHashSet<ScopeId>,
+
+    // Every component that has futures that need to be polled
+    pub pending_futures: FxHashSet<ScopeId>,
+
+    pub ui_events: VecDeque<UserEvent>,
+
+    pub pending_immediates: VecDeque<ScopeId>,
+
+    pub batched_events: VecDeque<UserEvent>,
+
+    pub garbage_scopes: HashSet<ScopeId>,
+
+    pub dirty_scopes: IndexSet<ScopeId>,
+
+    pub saved_state: Option<SavedDiffWork<'static>>,
+
+    pub in_progress: bool,
+}
+
+pub enum SchedulerMsg {
+    // events from the host
+    UiEvent(UserEvent),
+
+    // setstate
+    Immediate(ScopeId),
 }
 }
 
 
 impl VirtualDom {
 impl VirtualDom {
@@ -127,7 +182,7 @@ impl VirtualDom {
         Self::new_with_props_and_scheduler(root, root_props, sender, receiver)
         Self::new_with_props_and_scheduler(root, root_props, sender, receiver)
     }
     }
 
 
-    /// Launch the VirtualDom, but provide your own channel for receiving and sending messages into the scheduler.
+    /// Launch the VirtualDom, but provide your own channel for receiving and sending messages into the
     ///
     ///
     /// This is useful when the VirtualDom must be driven from outside a thread and it doesn't make sense to wait for the
     /// 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.
     /// VirtualDom to be created just to retrieve its channel receiver.
@@ -137,33 +192,71 @@ impl VirtualDom {
         sender: UnboundedSender<SchedulerMsg>,
         sender: UnboundedSender<SchedulerMsg>,
         receiver: UnboundedReceiver<SchedulerMsg>,
         receiver: UnboundedReceiver<SchedulerMsg>,
     ) -> Self {
     ) -> Self {
-        let root_fc = Box::new(root);
-
-        let root_props: Rc<dyn Any> = Rc::new(root_props);
-
-        let props = root_props.clone();
-
-        let root_caller: Box<dyn Fn(&ScopeInner) -> Element> =
-            Box::new(move |scope: &ScopeInner| {
-                let props = props.downcast_ref::<P>().unwrap();
-                let node = root((scope, props));
-                // cast into the right lifetime
-                unsafe { std::mem::transmute(node) }
-            });
-        let caller = unsafe { bumpalo::boxed::Box::from_raw(root_caller.as_mut() as *mut _) };
-
-        let scheduler = Scheduler::new(sender, receiver);
-
-        let base_scope = scheduler.pool.insert_scope_with_key(|myidx| {
-            ScopeInner::new(caller, myidx, None, 0, 0, scheduler.pool.channel.clone())
-        });
+        let mut component_arena = Bump::new();
+
+        // let root_fc = Box::new(root);
+
+        // let root_props: Rc<dyn Any> = Rc::new(root_props);
+
+        // let props = root_props.clone();
+
+        // let mut root_caller: Box<dyn Fn(&ScopeInner) -> Element> =
+        //     Box::new(move |scope: &ScopeInner| {
+        //         let props = props.downcast_ref::<P>().unwrap();
+        //         let node = root((scope, props));
+        //         // cast into the right lifetime
+        //         unsafe { std::mem::transmute(node) }
+        //     });
+
+        // let caller = unsafe { bumpalo::boxed::Box::from_raw(root_caller.as_mut() as *mut _) };
+
+        // // todo make the memory footprint congifurable
+        // let scheduler = Scheduler::new(sender, receiver, 100, 2000);
+
+        // let vcomp = VComponent {
+        //     key: todo!(),
+        //     associated_scope: todo!(),
+        //     user_fc: root as *const _,
+        //     can_memoize: todo!(),
+        //     raw_props: todo!(),
+        //     // drop_props: todo!(),
+        //     // caller,
+        //     comparator: todo!(),
+        //     caller: todo!(),
+        // };
+
+        // let boxed_comp = Box::new(vcomp);
+
+        // let base_scope = pool.insert_scope_with_key(|myidx| {
+        //     ScopeInner::new(
+        //         boxed_comp.as_ref(),
+        //         myidx,
+        //         None,
+        //         0,
+        //         0,
+        //         pool.channel.clone(),
+        //     )
+        // });
 
 
         Self {
         Self {
-            _root_caller: Box::new(root_caller),
-            root_fc,
-            base_scope,
-            scheduler,
-            root_props,
+            scheduler: todo!(),
+            base_scope: todo!(),
+            root_fc: todo!(),
+            root_props: todo!(),
+            _root_caller: todo!(),
+            component_arena: todo!(),
+            free_components: todo!(),
+            heuristics: todo!(),
+            receiver,
+            pending_garbage: todo!(),
+            pending_futures: todo!(),
+            ui_events: todo!(),
+            pending_immediates: todo!(),
+            batched_events: todo!(),
+            garbage_scopes: todo!(),
+            dirty_scopes: todo!(),
+            saved_state: todo!(),
+            in_progress: todo!(),
         }
         }
     }
     }
 
 
@@ -172,12 +265,12 @@ impl VirtualDom {
     /// This is useful for traversing the tree from the root for heuristics or alternsative renderers that use Dioxus
     /// This is useful for traversing the tree from the root for heuristics or alternsative renderers that use Dioxus
     /// directly.
     /// directly.
     pub fn base_scope(&self) -> &ScopeInner {
     pub fn base_scope(&self) -> &ScopeInner {
-        self.scheduler.pool.get_scope(self.base_scope).unwrap()
+        self.pool.get_scope(&self.base_scope).unwrap()
     }
     }
 
 
     /// Get the [`Scope`] for a component given its [`ScopeId`]
     /// Get the [`Scope`] for a component given its [`ScopeId`]
     pub fn get_scope(&self, id: ScopeId) -> Option<&ScopeInner> {
     pub fn get_scope(&self, id: ScopeId) -> Option<&ScopeInner> {
-        self.scheduler.pool.get_scope(id)
+        self.pool.get_scope(&id)
     }
     }
 
 
     /// Update the root props of this VirtualDOM.
     /// Update the root props of this VirtualDOM.
@@ -202,10 +295,10 @@ impl VirtualDom {
     where
     where
         P: 'static,
         P: 'static,
     {
     {
-        let root_scope = self.scheduler.pool.get_scope_mut(self.base_scope).unwrap();
+        let root_scope = self.pool.get_scope_mut(&self.base_scope).unwrap();
 
 
         // Pre-emptively drop any downstream references of the old props
         // Pre-emptively drop any downstream references of the old props
-        root_scope.ensure_drop_safety(&self.scheduler.pool);
+        root_scope.ensure_drop_safety(&self.pool);
 
 
         let mut root_props: Rc<dyn Any> = Rc::new(root_props);
         let mut root_props: Rc<dyn Any> = Rc::new(root_props);
 
 
@@ -221,8 +314,6 @@ impl VirtualDom {
                     std::mem::transmute(root((scope, props)))
                     std::mem::transmute(root((scope, props)))
                 });
                 });
 
 
-            root_scope.update_scope_dependencies(&root_caller);
-
             drop(root_props);
             drop(root_props);
 
 
             Some(self.rebuild())
             Some(self.rebuild())
@@ -249,7 +340,7 @@ impl VirtualDom {
     /// apply_edits(edits);
     /// apply_edits(edits);
     /// ```
     /// ```
     pub fn rebuild(&mut self) -> Mutations {
     pub fn rebuild(&mut self) -> Mutations {
-        self.scheduler.rebuild(self.base_scope)
+        self.rebuild(self.base_scope)
     }
     }
 
 
     /// Compute a manual diff of the VirtualDOM between states.
     /// Compute a manual diff of the VirtualDOM between states.
@@ -287,7 +378,7 @@ impl VirtualDom {
     /// let edits = dom.diff();
     /// let edits = dom.diff();
     /// ```
     /// ```
     pub fn diff(&mut self) -> Mutations {
     pub fn diff(&mut self) -> Mutations {
-        self.scheduler.hard_diff(self.base_scope)
+        self.hard_diff(self.base_scope)
     }
     }
 
 
     /// Runs the virtualdom immediately, not waiting for any suspended nodes to complete.
     /// Runs the virtualdom immediately, not waiting for any suspended nodes to complete.
@@ -295,8 +386,8 @@ impl VirtualDom {
     /// This method will not wait for any suspended nodes to complete. If there is no pending work, then this method will
     /// This method will not wait for any suspended nodes to complete. If there is no pending work, then this method will
     /// return "None"
     /// return "None"
     pub fn run_immediate(&mut self) -> Option<Vec<Mutations>> {
     pub fn run_immediate(&mut self) -> Option<Vec<Mutations>> {
-        if self.scheduler.has_any_work() {
-            Some(self.scheduler.work_sync())
+        if self.has_any_work() {
+            Some(self.work_sync())
         } else {
         } else {
             None
             None
         }
         }
@@ -346,18 +437,18 @@ impl VirtualDom {
     ///
     ///
     /// Mutations are the only link between the RealDOM and the VirtualDOM.
     /// Mutations are the only link between the RealDOM and the VirtualDOM.
     pub fn run_with_deadline(&mut self, deadline: impl FnMut() -> bool) -> Vec<Mutations<'_>> {
     pub fn run_with_deadline(&mut self, deadline: impl FnMut() -> bool) -> Vec<Mutations<'_>> {
-        self.scheduler.work_with_deadline(deadline)
+        self.work_with_deadline(deadline)
     }
     }
 
 
     pub fn get_event_sender(&self) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
     pub fn get_event_sender(&self) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
-        self.scheduler.pool.channel.sender.clone()
+        self.pool.channel.sender.clone()
     }
     }
 
 
     /// Waits for the scheduler to have work
     /// Waits for the scheduler to have work
     /// This lets us poll async tasks during idle periods without blocking the main thread.
     /// This lets us poll async tasks during idle periods without blocking the main thread.
     pub async fn wait_for_work(&mut self) {
     pub async fn wait_for_work(&mut self) {
         // todo: poll the events once even if there is work to do to prevent starvation
         // todo: poll the events once even if there is work to do to prevent starvation
-        if self.scheduler.has_any_work() {
+        if self.has_any_work() {
             return;
             return;
         }
         }
 
 
@@ -365,28 +456,485 @@ impl VirtualDom {
 
 
         // Wait for any new events if we have nothing to do
         // Wait for any new events if we have nothing to do
 
 
-        let tasks_fut = self.scheduler.async_tasks.next();
-        let scheduler_fut = self.scheduler.receiver.next();
+        // let tasks_fut = self.async_tasks.next();
+        // let scheduler_fut = self.receiver.next();
+
+        // use futures_util::future::{select, Either};
+        // match select(tasks_fut, scheduler_fut).await {
+        //     // poll the internal futures
+        //     Either::Left((_id, _)) => {
+        //         //
+        //     }
+
+        //     // wait for an external event
+        //     Either::Right((msg, _)) => match msg.unwrap() {
+        //         SchedulerMsg::Task(t) => {
+        //             self.handle_task(t);
+        //         }
+        //         SchedulerMsg::Immediate(im) => {
+        //             self.dirty_scopes.insert(im);
+        //         }
+        //         SchedulerMsg::UiEvent(evt) => {
+        //             self.ui_events.push_back(evt);
+        //         }
+        //     },
+        // }
+    }
+}
+
+pub type FcSlot = *const ();
+
+pub struct Heuristic {
+    hook_arena_size: usize,
+    node_arena_size: usize,
+}
+
+/*
+Welcome to Dioxus's cooperative, priority-based scheduler.
+
+I hope you enjoy your stay.
+
+Some essential reading:
+- https://github.com/facebook/react/blob/main/packages/scheduler/src/forks/Scheduler.js#L197-L200
+- https://github.com/facebook/react/blob/main/packages/scheduler/src/forks/Scheduler.js#L440
+- https://github.com/WICG/is-input-pending
+- https://web.dev/rail/
+- https://indepth.dev/posts/1008/inside-fiber-in-depth-overview-of-the-new-reconciliation-algorithm-in-react
+
+# What's going on?
+
+Dioxus is a framework for "user experience" - not just "user interfaces." Part of the "experience" is keeping the UI
+snappy and "jank free" even under heavy work loads. Dioxus already has the "speed" part figured out - but there's no
+point in being "fast" if you can't also be "responsive."
+
+As such, Dioxus can manually decide on what work is most important at any given moment in time. With a properly tuned
+priority system, Dioxus can ensure that user interaction is prioritized and committed as soon as possible (sub 100ms).
+The controller responsible for this priority management is called the "scheduler" and is responsible for juggling many
+different types of work simultaneously.
+
+# How does it work?
 
 
-        use futures_util::future::{select, Either};
-        match select(tasks_fut, scheduler_fut).await {
-            // poll the internal futures
-            Either::Left((_id, _)) => {
+Per the RAIL guide, we want to make sure that A) inputs are handled ASAP and B) animations are not blocked.
+React-three-fiber is a testament to how amazing this can be - a ThreeJS scene is threaded in between work periods of
+React, and the UI still stays snappy!
+
+While it's straightforward to run code ASAP and be as "fast as possible", what's not  _not_ straightforward is how to do
+this while not blocking the main thread. The current prevailing thought is to stop working periodically so the browser
+has time to paint and run animations. When the browser is finished, we can step in and continue our work.
+
+React-Fiber uses the "Fiber" concept to achieve a pause-resume functionality. This is worth reading up on, but not
+necessary to understand what we're doing here. In Dioxus, our DiffMachine is guided by DiffInstructions - essentially
+"commands" that guide the Diffing algorithm through the tree. Our "diff_scope" method is async - we can literally pause
+our DiffMachine "mid-sentence" (so to speak) by just stopping the poll on the future. The DiffMachine periodically yields
+so Rust's async machinery can take over, allowing us to customize when exactly to pause it.
+
+React's "should_yield" method is more complex than ours, and I assume we'll move in that direction as Dioxus matures. For
+now, Dioxus just assumes a TimeoutFuture, and selects! on both the Diff algorithm and timeout. If the DiffMachine finishes
+before the timeout, then Dioxus will work on any pending work in the interim. If there is no pending work, then the changes
+are committed, and coroutines are polled during the idle period. However, if the timeout expires, then the DiffMachine
+future is paused and saved (self-referentially).
+
+# Priority System
+
+So far, we've been able to thread our Dioxus work between animation frames - the main thread is not blocked! But that
+doesn't help us _under load_. How do we still stay snappy... even if we're doing a lot of work? Well, that's where
+priorities come into play. The goal with priorities is to schedule shorter work as a "high" priority and longer work as
+a "lower" priority. That way, we can interrupt long-running low-priority work with short-running high-priority work.
+
+React's priority system is quite complex.
+
+There are 5 levels of priority and 2 distinctions between UI events (discrete, continuous). I believe React really only
+uses 3 priority levels and "idle" priority isn't used... Regardless, there's some batching going on.
+
+For Dioxus, we're going with a 4 tier priority system:
+- Sync: Things that need to be done by the next frame, like TextInput on controlled elements
+- High: for events that block all others - clicks, keyboard, and hovers
+- Medium: for UI events caused by the user but not directly - scrolls/forms/focus (all other events)
+- Low: set_state called asynchronously, and anything generated by suspense
+
+In "Sync" state, we abort our "idle wait" future, and resolve the sync queue immediately and escape. Because we completed
+work before the next rAF, any edits can be immediately processed before the frame ends. Generally though, we want to leave
+as much time to rAF as possible. "Sync" is currently only used by onInput - we'll leave some docs telling people not to
+do anything too arduous from onInput.
+
+For the rest, we defer to the rIC period and work down each queue from high to low.
+*/
+
+/// The scheduler holds basically everything around "working"
+///
+/// Each scope has the ability to lightly interact with the scheduler (IE, schedule an update) but ultimately the scheduler calls the components.
+///
+/// In Dioxus, the scheduler provides 4 priority levels - each with their own "DiffMachine". The DiffMachine state can be saved if the deadline runs
+/// out.
+///
+/// Saved DiffMachine state can be self-referential, so we need to be careful about how we save it. All self-referential data is a link between
+/// pending DiffInstructions, Mutations, and their underlying Scope. It's okay for us to be self-referential with this data, provided we don't priority
+/// task shift to a higher priority task that needs mutable access to the same scopes.
+///
+/// We can prevent this safety issue from occurring if we track which scopes are invalidated when starting a new task.
+///
+/// There's a lot of raw pointers here...
+///
+/// Since we're building self-referential structures for each component, we need to make sure that the referencs stay stable
+/// The best way to do that is a bump allocator.
+///
+///
+///
+impl VirtualDom {
+    // returns true if the event is discrete
+    pub fn handle_ui_event(&mut self, event: UserEvent) -> bool {
+        let (discrete, priority) = event_meta(&event);
+
+        if let Some(scope) = self.get_scope_mut(&event.scope) {
+            if let Some(element) = event.mounted_dom_id {
+                // TODO: bubble properly here
+                scope.call_listener(event, element);
+
+                while let Ok(Some(dirty_scope)) = self.receiver.try_next() {
+                    //
+                    //     self.add_dirty_scope(dirty_scope, trigger.priority)
+                }
+            }
+        }
+
+        // use EventPriority::*;
+
+        // match priority {
+        //     Immediate => todo!(),
+        //     High => todo!(),
+        //     Medium => todo!(),
+        //     Low => todo!(),
+        // }
+
+        discrete
+    }
+
+    fn prepare_work(&mut self) {
+        // while let Some(trigger) = self.ui_events.pop_back() {
+        //     if let Some(scope) = self.get_scope_mut(&trigger.scope) {}
+        // }
+    }
+
+    // nothing to do, no events on channels, no work
+    pub fn has_any_work(&self) -> bool {
+        !(self.dirty_scopes.is_empty() && self.ui_events.is_empty())
+    }
+
+    /// re-balance the work lanes, ensuring high-priority work properly bumps away low priority work
+    fn balance_lanes(&mut self) {}
+
+    fn save_work(&mut self, lane: SavedDiffWork) {
+        let saved: SavedDiffWork<'static> = unsafe { std::mem::transmute(lane) };
+        self.saved_state = Some(saved);
+    }
+
+    unsafe fn load_work(&mut self) -> SavedDiffWork<'static> {
+        self.saved_state.take().unwrap().extend()
+    }
+
+    pub fn handle_channel_msg(&mut self, msg: SchedulerMsg) {
+        match msg {
+            SchedulerMsg::Immediate(_) => todo!(),
+
+            SchedulerMsg::UiEvent(event) => {
                 //
                 //
+
+                let (discrete, priority) = event_meta(&event);
+
+                if let Some(scope) = self.get_scope_mut(&event.scope) {
+                    if let Some(element) = event.mounted_dom_id {
+                        // TODO: bubble properly here
+                        scope.call_listener(event, element);
+
+                        while let Ok(Some(dirty_scope)) = self.receiver.try_next() {
+                            //
+                            //     self.add_dirty_scope(dirty_scope, trigger.priority)
+                        }
+                    }
+                }
+
+                discrete;
             }
             }
+        }
+    }
 
 
-            // wait for an external event
-            Either::Right((msg, _)) => match msg.unwrap() {
-                SchedulerMsg::Task(t) => {
-                    self.scheduler.handle_task(t);
+    /// Load the current lane, and work on it, periodically checking in if the deadline has been reached.
+    ///
+    /// Returns true if the lane is finished before the deadline could be met.
+    pub fn work_on_current_lane(
+        &mut self,
+        deadline_reached: impl FnMut() -> bool,
+        mutations: &mut Vec<Mutations>,
+    ) -> bool {
+        // Work through the current subtree, and commit the results when it finishes
+        // When the deadline expires, give back the work
+        let saved_state = unsafe { self.load_work() };
+
+        // We have to split away some parts of ourself - current lane is borrowed mutably
+        let shared = self.clone();
+        let mut machine = unsafe { saved_state.promote(&shared) };
+
+        let mut ran_scopes = FxHashSet::default();
+
+        if machine.stack.is_empty() {
+            let shared = self.clone();
+
+            self.dirty_scopes
+                .retain(|id| shared.get_scope(id).is_some());
+            self.dirty_scopes.sort_by(|a, b| {
+                let h1 = shared.get_scope(a).unwrap().height;
+                let h2 = shared.get_scope(b).unwrap().height;
+                h1.cmp(&h2).reverse()
+            });
+
+            if let Some(scopeid) = self.dirty_scopes.pop() {
+                log::info!("handling dirty scope {:?}", scopeid);
+                if !ran_scopes.contains(&scopeid) {
+                    ran_scopes.insert(scopeid);
+                    log::debug!("about to run scope {:?}", scopeid);
+
+                    if let Some(component) = self.get_scope_mut(&scopeid) {
+                        if component.run_scope(&self) {
+                            let (old, new) =
+                                (component.frames.wip_head(), component.frames.fin_head());
+                            // let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
+                            machine.stack.scope_stack.push(scopeid);
+                            machine.stack.push(DiffInstruction::Diff { new, old });
+                        }
+                    }
                 }
                 }
-                SchedulerMsg::Immediate(im) => {
-                    self.scheduler.dirty_scopes.insert(im);
+            }
+        }
+
+        let work_completed = machine.work(deadline_reached);
+
+        // log::debug!("raw edits {:?}", machine.mutations.edits);
+
+        let mut machine: DiffMachine<'static> = unsafe { std::mem::transmute(machine) };
+        // let mut saved = machine.save();
+
+        if work_completed {
+            for node in machine.seen_scopes.drain() {
+                // self.dirty_scopes.clear();
+                // self.ui_events.clear();
+                self.dirty_scopes.remove(&node);
+                // self.dirty_scopes.remove(&node);
+            }
+
+            let mut new_mutations = Mutations::new();
+
+            for edit in machine.mutations.edits.drain(..) {
+                new_mutations.edits.push(edit);
+            }
+
+            // for edit in saved.edits.drain(..) {
+            //     new_mutations.edits.push(edit);
+            // }
+
+            // std::mem::swap(&mut new_mutations, &mut saved.mutations);
+
+            mutations.push(new_mutations);
+
+            // log::debug!("saved edits {:?}", mutations);
+
+            let mut saved = machine.save();
+            self.save_work(saved);
+            true
+
+            // self.save_work(saved);
+            // false
+        } else {
+            false
+        }
+    }
+
+    /// The primary workhorse of the VirtualDOM.
+    ///
+    /// Uses some fairly complex logic to schedule what work should be produced.
+    ///
+    /// Returns a list of successful mutations.
+    pub fn work_with_deadline<'a>(
+        &'a mut self,
+        mut deadline: impl FnMut() -> bool,
+    ) -> Vec<Mutations<'a>> {
+        /*
+        Strategy:
+        - When called, check for any UI events that might've been received since the last frame.
+        - Dump all UI events into a "pending discrete" queue and a "pending continuous" queue.
+
+        - If there are any pending discrete events, then elevate our priority level. If our priority level is already "high,"
+            then we need to finish the high priority work first. If the current work is "low" then analyze what scopes
+            will be invalidated by this new work. If this interferes with any in-flight medium or low work, then we need
+            to bump the other work out of the way, or choose to process it so we don't have any conflicts.
+            'static components have a leg up here since their work can be re-used among multiple scopes.
+            "High priority" is only for blocking! Should only be used on "clicks"
+
+        - If there are no pending discrete events, then check for continuous events. These can be completely batched
+
+        - we batch completely until we run into a discrete event
+        - all continuous events are batched together
+        - so D C C C C C would be two separate events - D and C. IE onclick and onscroll
+        - D C C C C C C D C C C D would be D C D C D in 5 distinct phases.
+
+        - !listener bubbling is not currently implemented properly and will need to be implemented somehow in the future
+            - we need to keep track of element parents to be able to traverse properly
+
+
+        Open questions:
+        - what if we get two clicks from the component during the same slice?
+            - should we batch?
+            - react says no - they are continuous
+            - but if we received both - then we don't need to diff, do we? run as many as we can and then finally diff?
+        */
+        let mut committed_mutations = Vec::<Mutations<'static>>::new();
+
+        while self.has_any_work() {
+            while let Ok(Some(msg)) = self.receiver.try_next() {
+                match msg {
+                    SchedulerMsg::Immediate(im) => {
+                        self.dirty_scopes.insert(im);
+                    }
+                    SchedulerMsg::UiEvent(evt) => {
+                        self.ui_events.push_back(evt);
+                    }
                 }
                 }
-                SchedulerMsg::UiEvent(evt) => {
-                    self.scheduler.ui_events.push_back(evt);
+            }
+
+            // switch our priority, pop off any work
+            while let Some(event) = self.ui_events.pop_front() {
+                if let Some(scope) = self.get_scope_mut(&event.scope) {
+                    if let Some(element) = event.mounted_dom_id {
+                        log::info!("Calling listener {:?}, {:?}", event.scope, element);
+
+                        // TODO: bubble properly here
+                        scope.call_listener(event, element);
+
+                        while let Ok(Some(dirty_scope)) = self.receiver.try_next() {
+                            match dirty_scope {
+                                SchedulerMsg::Immediate(im) => {
+                                    self.dirty_scopes.insert(im);
+                                }
+                                SchedulerMsg::UiEvent(e) => self.ui_events.push_back(e),
+                            }
+                        }
+                    }
                 }
                 }
-            },
+            }
+
+            let work_complete = self.work_on_current_lane(&mut deadline, &mut committed_mutations);
+
+            if !work_complete {
+                return committed_mutations;
+            }
+        }
+
+        committed_mutations
+    }
+
+    /// Work the scheduler down, not polling any ongoing tasks.
+    ///
+    /// Will use the standard priority-based scheduling, batching, etc, but just won't interact with the async reactor.
+    pub fn work_sync<'a>(&'a mut self) -> Vec<Mutations<'a>> {
+        let mut committed_mutations = Vec::new();
+
+        while let Ok(Some(msg)) = self.receiver.try_next() {
+            self.handle_channel_msg(msg);
+        }
+
+        if !self.has_any_work() {
+            return committed_mutations;
+        }
+
+        while self.has_any_work() {
+            self.prepare_work();
+            self.work_on_current_lane(|| false, &mut committed_mutations);
+        }
+
+        committed_mutations
+    }
+
+    /// Restart the entire VirtualDOM from scratch, wiping away any old state and components.
+    ///
+    /// Typically used to kickstart the VirtualDOM after initialization.
+    pub fn rebuild(&mut self, base_scope: ScopeId) -> Mutations {
+        let mut shared = self.clone();
+        let mut diff_machine = DiffMachine::new(Mutations::new(), &mut shared);
+
+        // TODO: drain any in-flight work
+        let cur_component = self
+            .pool
+            .get_scope_mut(&base_scope)
+            .expect("The base scope should never be moved");
+
+        log::debug!("rebuild {:?}", base_scope);
+
+        // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
+        if cur_component.run_scope(&self) {
+            diff_machine
+                .stack
+                .create_node(cur_component.frames.fin_head(), MountType::Append);
+
+            diff_machine.stack.scope_stack.push(base_scope);
+
+            diff_machine.work(|| false);
+        } else {
+            // todo: should this be a hard error?
+            log::warn!(
+                "Component failed to run successfully during rebuild.
+                This does not result in a failed rebuild, but indicates a logic failure within your app."
+            );
+        }
+
+        unsafe { std::mem::transmute(diff_machine.mutations) }
+    }
+
+    pub fn hard_diff(&mut self, base_scope: ScopeId) -> Mutations {
+        let cur_component = self
+            .pool
+            .get_scope_mut(&base_scope)
+            .expect("The base scope should never be moved");
+
+        log::debug!("hard diff {:?}", base_scope);
+
+        if cur_component.run_scope(&self) {
+            let mut diff_machine = DiffMachine::new(Mutations::new(), &mut self);
+            diff_machine.cfg.force_diff = true;
+            diff_machine.diff_scope(base_scope);
+            diff_machine.mutations
+        } else {
+            Mutations::new()
+        }
+    }
+}
+
+impl Future for VirtualDom {
+    type Output = ();
+
+    fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
+        let mut all_pending = true;
+
+        for fut in self.pending_futures.iter() {
+            let scope = self
+                .pool
+                .get_scope_mut(&fut)
+                .expect("Scope should never be moved");
+
+            let items = scope.items.get_mut();
+            for task in items.tasks.iter_mut() {
+                let t = task.as_mut();
+                let g = unsafe { Pin::new_unchecked(t) };
+                match g.poll(cx) {
+                    Poll::Ready(r) => {
+                        all_pending = false;
+                    }
+                    Poll::Pending => {}
+                }
+            }
+        }
+
+        match all_pending {
+            true => Poll::Pending,
+            false => Poll::Ready(()),
         }
         }
     }
     }
 }
 }