Jonathan Kelley преди 3 години
родител
ревизия
bcaa76a
променени са 3 файла, в които са добавени 172 реда и са изтрити 43 реда
  1. 80 19
      packages/core/src/diff.rs
  2. 90 22
      packages/core/src/scheduler.rs
  3. 2 2
      packages/core/src/virtual_dom.rs

+ 80 - 19
packages/core/src/diff.rs

@@ -90,6 +90,7 @@
 
 use crate::{innerlude::*, scheduler::Scheduler};
 use fxhash::{FxHashMap, FxHashSet};
+use slab::Slab;
 use DomEdit::*;
 
 /// Our DiffMachine is an iterative tree differ.
@@ -105,19 +106,69 @@ use DomEdit::*;
 /// Funnily enough, this stack machine's entire job is to create instructions for another stack machine to execute. It's
 /// stack machines all the way down!
 pub struct DiffMachine<'bump> {
-    pub vdom: &'bump Scheduler,
-
+    vdom: SharedVdom<'bump>,
     pub mutations: Mutations<'bump>,
-
     pub stack: DiffStack<'bump>,
-
     pub diffed: FxHashSet<ScopeId>,
+    pub seen_scopes: FxHashSet<ScopeId>,
+}
+
+pub struct SharedVdom<'bump> {
+    pub components: &'bump mut Slab<Scope>,
+    pub elements: &'bump mut Slab<()>,
+    pub channel: EventChannel,
+}
+
+impl<'bump> SharedVdom<'bump> {
+    fn get_scope_mut(&mut self, scope: ScopeId) -> Option<&'bump mut Scope> {
+        todo!()
+    }
 
+    fn get_scope(&mut self, scope: ScopeId) -> Option<&'bump Scope> {
+        todo!()
+    }
+
+    fn reserve_node(&mut self) -> ElementId {
+        todo!()
+    }
+    fn collect_garbage(&mut self, element: ElementId) {}
+
+    pub fn insert_scope_with_key(&mut self, f: impl FnOnce(ScopeId) -> Scope) -> ScopeId {
+        let entry = self.components.vacant_entry();
+        let id = ScopeId(entry.key());
+        entry.insert(f(id));
+        id
+    }
+}
+
+/// a "saved" form of a diff machine
+/// in regular diff machine, the &'bump reference is a stack borrow, but the
+/// bump lifetimes are heap borrows.
+pub struct SavedDiffWork<'bump> {
+    pub mutations: Mutations<'bump>,
+    pub stack: DiffStack<'bump>,
+    pub diffed: FxHashSet<ScopeId>,
     pub seen_scopes: FxHashSet<ScopeId>,
 }
 
+impl<'a> SavedDiffWork<'a> {
+    pub unsafe fn extend(self: SavedDiffWork<'a>) -> SavedDiffWork<'static> {
+        std::mem::transmute(self)
+    }
+    pub unsafe fn promote<'b>(self, vdom: SharedVdom<'b>) -> DiffMachine<'b> {
+        let extended: SavedDiffWork<'b> = std::mem::transmute(self);
+        DiffMachine {
+            vdom,
+            mutations: extended.mutations,
+            stack: extended.stack,
+            diffed: extended.diffed,
+            seen_scopes: extended.seen_scopes,
+        }
+    }
+}
+
 impl<'bump> DiffMachine<'bump> {
-    pub(crate) fn new(edits: Mutations<'bump>, shared: &'bump Scheduler) -> Self {
+    pub(crate) fn new(edits: Mutations<'bump>, shared: SharedVdom<'bump>) -> Self {
         Self {
             stack: DiffStack::new(),
             mutations: edits,
@@ -127,6 +178,15 @@ impl<'bump> DiffMachine<'bump> {
         }
     }
 
+    pub fn save(self) -> SavedDiffWork<'bump> {
+        SavedDiffWork {
+            mutations: self.mutations,
+            stack: self.stack,
+            diffed: self.diffed,
+            seen_scopes: self.seen_scopes,
+        }
+    }
+
     // pub fn new_headless(shared: &'bump SharedResources) -> Self {
     //     let edits = Mutations::new();
     //     let cur_scope = ScopeId(0);
@@ -136,7 +196,6 @@ impl<'bump> DiffMachine<'bump> {
     //
     pub async fn diff_scope(&mut self, id: ScopeId) {
         if let Some(component) = self.vdom.get_scope_mut(id) {
-            self.stack.scope_stack.push(id);
             let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
             self.stack.push(DiffInstruction::DiffNode { new, old });
             self.work().await;
@@ -152,9 +211,6 @@ impl<'bump> DiffMachine<'bump> {
         // defer to individual functions so the compiler produces better code
         // large functions tend to be difficult for the compiler to work with
         while let Some(instruction) = self.stack.pop() {
-            // todo: call this less frequently, there is a bit of overhead involved
-            yield_now().await;
-
             match instruction {
                 DiffInstruction::PopScope => {
                     self.stack.pop_scope();
@@ -178,12 +234,16 @@ impl<'bump> DiffMachine<'bump> {
 
                 DiffInstruction::PrepareMoveNode { node } => {
                     log::debug!("Preparing to move node: {:?}", node);
-                    for el in RealChildIterator::new(node, self.vdom) {
-                        self.mutations.push_root(el.direct_id());
-                        self.stack.add_child_count(1);
-                    }
+                    todo!();
+                    // for el in RealChildIterator::new(node, self.vdom) {
+                    //     self.mutations.push_root(el.direct_id());
+                    //     self.stack.add_child_count(1);
+                    // }
                 }
             };
+
+            // todo: call this less frequently, there is a bit of overhead involved
+            yield_now().await;
         }
     }
 
@@ -202,11 +262,12 @@ impl<'bump> DiffMachine<'bump> {
             }
 
             MountType::Replace { old } => {
-                let mut iter = RealChildIterator::new(old, self.vdom);
-                let first = iter.next().unwrap();
-                self.mutations
-                    .replace_with(first.direct_id(), nodes_created as u32);
-                self.remove_nodes(iter);
+                todo!()
+                // let mut iter = RealChildIterator::new(old, self.vdom);
+                // let first = iter.next().unwrap();
+                // self.mutations
+                //     .replace_with(first.direct_id(), nodes_created as u32);
+                // self.remove_nodes(iter);
             }
 
             MountType::ReplaceByElementId { el: old } => {
@@ -309,8 +370,8 @@ impl<'bump> DiffMachine<'bump> {
 
         let shared = self.vdom.channel.clone();
         // Insert a new scope into our component list
+        let parent_scope = self.vdom.get_scope(parent_idx).unwrap();
         let new_idx = self.vdom.insert_scope_with_key(|new_idx| {
-            let parent_scope = self.vdom.get_scope(parent_idx).unwrap();
             let height = parent_scope.height + 1;
             Scope::new(
                 caller,

+ 90 - 22
packages/core/src/scheduler.rs

@@ -70,6 +70,7 @@ For the rest, we defer to the rIC period and work down each queue from high to l
 */
 use std::cell::{Cell, RefCell, RefMut};
 use std::fmt::Display;
+use std::intrinsics::transmute;
 use std::{cell::UnsafeCell, rc::Rc};
 
 use crate::heuristics::*;
@@ -174,7 +175,7 @@ pub struct Scheduler {
 
     pub garbage_scopes: HashSet<ScopeId>,
 
-    pub fibers: [PriortySystem; 3],
+    pub lanes: [PriortySystem; 4],
 }
 
 impl Scheduler {
@@ -253,7 +254,8 @@ impl Scheduler {
             current_priority: EventPriority::Low,
 
             // a dedicated fiber for each priority
-            fibers: [
+            lanes: [
+                PriortySystem::new(),
                 PriortySystem::new(),
                 PriortySystem::new(),
                 PriortySystem::new(),
@@ -456,7 +458,7 @@ impl Scheduler {
     ///
     pub async fn work_with_deadline<'a>(
         &'a mut self,
-        deadline: &mut Pin<Box<impl FusedFuture<Output = ()>>>,
+        mut deadline: Pin<Box<impl FusedFuture<Output = ()>>>,
     ) -> Vec<Mutations<'a>> {
         /*
         Strategy:
@@ -478,12 +480,8 @@ impl Scheduler {
             - 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::new();
+        let mut committed_mutations = Vec::<Mutations<'static>>::new();
 
         // TODO:
         // the scheduler uses a bunch of different receivers to mimic a "topic" queue system. The futures-channel implementation
@@ -496,7 +494,7 @@ impl Scheduler {
             // Wait for any new events if we have nothing to do
             if !self.has_any_work() {
                 self.clean_up_garbage();
-                let deadline_expired = self.wait_for_any_trigger(deadline).await;
+                let deadline_expired = self.wait_for_any_trigger(&mut deadline).await;
 
                 if deadline_expired {
                     return committed_mutations;
@@ -508,7 +506,76 @@ impl Scheduler {
 
             // Work through the current subtree, and commit the results when it finishes
             // When the deadline expires, give back the work
-            let mut new_mutations = Mutations::new();
+            self.current_priority = match (
+                self.lanes[0].has_work(),
+                self.lanes[1].has_work(),
+                self.lanes[2].has_work(),
+                self.lanes[3].has_work(),
+            ) {
+                (true, _, _, _) => EventPriority::Immediate,
+                (false, true, _, _) => EventPriority::High,
+                (false, false, true, _) => EventPriority::Medium,
+                (false, false, false, _) => EventPriority::Low,
+            };
+
+            let current_lane = match self.current_priority {
+                EventPriority::Immediate => &mut self.lanes[0],
+                EventPriority::High => &mut self.lanes[1],
+                EventPriority::Medium => &mut self.lanes[2],
+                EventPriority::Low => &mut self.lanes[3],
+            };
+
+            if self.current_priority == EventPriority::Immediate {
+                // IDGAF - get this out the door right now. loop poll if we need to
+            }
+
+            use futures_util::future::{select, Either};
+
+            // if let Some(state) = current_lane.saved_state.take() {
+            //     let mut machine = unsafe { state.promote(&self) };
+            //     machine.work().await;
+            // } else {
+            if let Some(scope) = current_lane.dirty_scopes.pop() {
+                let (mut saved_work, work_complete): (SavedDiffWork<'static>, bool) = {
+                    let shared = SharedVdom {
+                        channel: self.channel.clone(),
+                        components: unsafe { &mut *self.components.get() },
+                        elements: &mut self.raw_elements,
+                    };
+                    let mut machine = DiffMachine::new(Mutations::new(), shared);
+
+                    let work_complete = {
+                        let fut = machine.diff_scope(scope);
+                        pin_mut!(fut);
+                        match select(fut, &mut deadline).await {
+                            Either::Left((work, _other)) => {
+                                //
+                                true
+                            }
+                            Either::Right((deadline, _other)) => {
+                                //
+                                false
+                            }
+                        }
+                    };
+
+                    let saved = machine.save();
+                    let extended: SavedDiffWork<'static> = unsafe { transmute(saved) };
+                    (extended, work_complete)
+                };
+
+                // release the stack borrow of ourself
+
+                if work_complete {
+                    for scope in saved_work.seen_scopes.drain() {
+                        current_lane.dirty_scopes.remove(&scope);
+                    }
+                } else {
+                }
+                // }
+            };
+
+            // let mut new_mutations = Mutations::new();
             // match self.work_with_deadline(&mut deadline).await {
             //     Some(mutations) => {
             //         // safety: the scheduler will never let us mutate
@@ -519,15 +586,6 @@ impl Scheduler {
             // }
         }
         // // check if we need to elevate priority
-        // self.current_priority = match (
-        //     self.high_priorty.has_work(),
-        //     self.medium_priority.has_work(),
-        //     self.low_priority.has_work(),
-        // ) {
-        //     (true, _, _) => EventPriority::High,
-        //     (false, true, _) => EventPriority::Medium,
-        //     (false, false, _) => EventPriority::Low,
-        // };
 
         // // let mut machine = DiffMachine::new(mutations, ScopeId(0), &self);
 
@@ -595,7 +653,17 @@ impl Scheduler {
     }
 
     pub fn handle_channel_msg(&mut self, msg: SchedulerMsg) {
-        //
+        match msg {
+            SchedulerMsg::Immediate(_) => todo!(),
+            SchedulerMsg::UiEvent(_) => todo!(),
+
+            //
+            SchedulerMsg::SubmitTask(_, _) => todo!(),
+            SchedulerMsg::ToggleTask(_) => todo!(),
+            SchedulerMsg::PauseTask(_) => todo!(),
+            SchedulerMsg::ResumeTask(_) => todo!(),
+            SchedulerMsg::DropTask(_) => todo!(),
+        }
     }
 
     pub fn add_dirty_scope(&mut self, scope: ScopeId, priority: EventPriority) {
@@ -610,13 +678,13 @@ impl Scheduler {
 
 pub struct PriortySystem {
     pub dirty_scopes: IndexSet<ScopeId>,
-    pub machine: DiffMachine<'static>,
+    pub saved_state: Option<SavedDiffWork<'static>>,
 }
 
 impl PriortySystem {
     pub fn new() -> Self {
         Self {
-            machine: DiffMachine::new(edits, shared),
+            saved_state: None,
             dirty_scopes: Default::default(),
         }
     }

+ 2 - 2
packages/core/src/virtual_dom.rs

@@ -194,7 +194,7 @@ impl VirtualDom {
     }
 
     pub async fn diff_async<'s>(&'s mut self) -> Mutations<'s> {
-        let mut diff_machine = DiffMachine::new(Mutations::new(), &self.scheduler);
+        let mut diff_machine = DiffMachine::new(Mutations::new(), todo!());
 
         let cur_component = self
             .scheduler
@@ -268,7 +268,7 @@ impl VirtualDom {
         deadline: impl Future<Output = ()>,
     ) -> Vec<Mutations<'s>> {
         let mut deadline = Box::pin(deadline.fuse());
-        self.scheduler.work_with_deadline(&mut deadline).await
+        self.scheduler.work_with_deadline(deadline).await
     }
 
     pub fn get_event_sender(&self) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {