diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index da1cd5673..f90fa98eb 100644 --- a/packages/core/src/diff.rs +++ b/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, - pub seen_scopes: FxHashSet, } +pub struct SharedVdom<'bump> { + pub components: &'bump mut Slab, + 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, + pub seen_scopes: FxHashSet, +} + +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, diff --git a/packages/core/src/scheduler.rs b/packages/core/src/scheduler.rs index b3c0c148a..ac01bbfa4 100644 --- a/packages/core/src/scheduler.rs +++ b/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, - 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>>, + mut deadline: Pin>>, ) -> Vec> { /* 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::>::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, - pub machine: DiffMachine<'static>, + pub saved_state: Option>, } impl PriortySystem { pub fn new() -> Self { Self { - machine: DiffMachine::new(edits, shared), + saved_state: None, dirty_scopes: Default::default(), } } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 861642f03..17cc056e2 100644 --- a/packages/core/src/virtual_dom.rs +++ b/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, ) -> Vec> { 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 {