clean up dirty scopes module
This commit is contained in:
1 parent
cca92b9fed
commit
9bad802beb
3 files changed
+79
-79
No files matched your search
@@ -29,9 +29,9 @@
|
|||||||
|
|
||||||
use crate::ScopeId;
|
use crate::ScopeId;
|
||||||
use crate::Task;
|
use crate::Task;
|
||||||
|
use crate::VirtualDom;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::BTreeSet;
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq)]
|
#[derive(Debug, Clone, Copy, Eq)]
|
||||||
@@ -70,50 +70,71 @@ impl Hash for ScopeOrder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
impl VirtualDom {
|
||||||
pub struct DirtyScopes {
|
|
||||||
pub(crate) scopes: BTreeSet<ScopeOrder>,
|
|
||||||
pub(crate) tasks: BTreeSet<DirtyTasks>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DirtyScopes {
|
|
||||||
/// Queue a task to be polled
|
/// Queue a task to be polled
|
||||||
pub fn queue_task(&mut self, task: Task, order: ScopeOrder) {
|
pub(crate) fn queue_task(&mut self, task: Task, order: ScopeOrder) {
|
||||||
match self.tasks.get(&order) {
|
match self.dirty_tasks.get(&order) {
|
||||||
Some(scope) => scope.queue_task(task),
|
Some(scope) => scope.queue_task(task),
|
||||||
None => {
|
None => {
|
||||||
let scope = DirtyTasks::from(order);
|
let scope = DirtyTasks::from(order);
|
||||||
scope.queue_task(task);
|
scope.queue_task(task);
|
||||||
self.tasks.insert(scope);
|
self.dirty_tasks.insert(scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Queue a scope to be rerendered
|
/// Queue a scope to be rerendered
|
||||||
pub fn queue_scope(&mut self, order: ScopeOrder) {
|
pub(crate) fn queue_scope(&mut self, order: ScopeOrder) {
|
||||||
self.scopes.insert(order);
|
self.dirty_scopes.insert(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if there are any dirty scopes
|
/// Check if there are any dirty scopes
|
||||||
pub fn has_dirty_scopes(&self) -> bool {
|
pub(crate) fn has_dirty_scopes(&self) -> bool {
|
||||||
!self.scopes.is_empty()
|
!self.dirty_scopes.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take any tasks from the highest scope
|
/// Take any tasks from the highest scope
|
||||||
pub fn pop_task(&mut self) -> Option<DirtyTasks> {
|
pub(crate) fn pop_task(&mut self) -> Option<DirtyTasks> {
|
||||||
self.tasks.pop_first()
|
let mut task = self.dirty_tasks.pop_first()?;
|
||||||
|
|
||||||
|
// If the scope doesn't exist for whatever reason, then we should skip it
|
||||||
|
while !self.scopes.contains(task.order.id.0) {
|
||||||
|
task = self.dirty_tasks.pop_first()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take any work from the highest scope. This may include rerunning the scope and/or running tasks
|
/// Take any work from the highest scope. This may include rerunning the scope and/or running tasks
|
||||||
pub fn pop_work(&mut self) -> Option<Work> {
|
pub(crate) fn pop_work(&mut self) -> Option<Work> {
|
||||||
let dirty_scope = self.scopes.first();
|
let mut dirty_scope = self.dirty_scopes.first();
|
||||||
let dirty_task = self.tasks.first();
|
// Pop any invalid scopes off of each dirty task;
|
||||||
|
while let Some(scope) = dirty_scope {
|
||||||
|
if !self.scopes.contains(scope.id.0) {
|
||||||
|
self.dirty_scopes.pop_first();
|
||||||
|
dirty_scope = self.dirty_scopes.first();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut dirty_task = self.dirty_tasks.first();
|
||||||
|
// Pop any invalid tasks off of each dirty scope;
|
||||||
|
while let Some(task) = dirty_task {
|
||||||
|
if !self.scopes.contains(task.order.id.0) {
|
||||||
|
self.dirty_tasks.pop_first();
|
||||||
|
dirty_task = self.dirty_tasks.first();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match (dirty_scope, dirty_task) {
|
match (dirty_scope, dirty_task) {
|
||||||
(Some(scope), Some(task)) => {
|
(Some(scope), Some(task)) => {
|
||||||
let tasks_order = task.borrow();
|
let tasks_order = task.borrow();
|
||||||
match scope.cmp(tasks_order) {
|
match scope.cmp(tasks_order) {
|
||||||
std::cmp::Ordering::Less => {
|
std::cmp::Ordering::Less => {
|
||||||
let scope = self.scopes.pop_first().unwrap();
|
let scope = self.dirty_scopes.pop_first().unwrap();
|
||||||
Some(Work {
|
Some(Work {
|
||||||
scope,
|
scope,
|
||||||
rerun_scope: true,
|
rerun_scope: true,
|
||||||
@@ -121,7 +142,7 @@ impl DirtyScopes {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
std::cmp::Ordering::Greater => {
|
std::cmp::Ordering::Greater => {
|
||||||
let task = self.tasks.pop_first().unwrap();
|
let task = self.dirty_tasks.pop_first().unwrap();
|
||||||
Some(Work {
|
Some(Work {
|
||||||
scope: task.order,
|
scope: task.order,
|
||||||
rerun_scope: false,
|
rerun_scope: false,
|
||||||
@@ -129,8 +150,8 @@ impl DirtyScopes {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
std::cmp::Ordering::Equal => {
|
std::cmp::Ordering::Equal => {
|
||||||
let scope = self.scopes.pop_first().unwrap();
|
let scope = self.dirty_scopes.pop_first().unwrap();
|
||||||
let task = self.tasks.pop_first().unwrap();
|
let task = self.dirty_tasks.pop_first().unwrap();
|
||||||
Some(Work {
|
Some(Work {
|
||||||
scope,
|
scope,
|
||||||
rerun_scope: true,
|
rerun_scope: true,
|
||||||
@@ -140,7 +161,7 @@ impl DirtyScopes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Some(_), None) => {
|
(Some(_), None) => {
|
||||||
let scope = self.scopes.pop_first().unwrap();
|
let scope = self.dirty_scopes.pop_first().unwrap();
|
||||||
Some(Work {
|
Some(Work {
|
||||||
scope,
|
scope,
|
||||||
rerun_scope: true,
|
rerun_scope: true,
|
||||||
@@ -148,7 +169,7 @@ impl DirtyScopes {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
(None, Some(_)) => {
|
(None, Some(_)) => {
|
||||||
let task = self.tasks.pop_first().unwrap();
|
let task = self.dirty_tasks.pop_first().unwrap();
|
||||||
Some(Work {
|
Some(Work {
|
||||||
scope: task.order,
|
scope: task.order,
|
||||||
rerun_scope: false,
|
rerun_scope: false,
|
||||||
@@ -158,10 +179,6 @@ impl DirtyScopes {
|
|||||||
(None, None) => None,
|
(None, None) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(&mut self, scope: &ScopeOrder) {
|
|
||||||
self.scopes.remove(scope);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
//!
|
//!
|
||||||
//! This module provides the primary mechanics to create a hook-based, concurrent VDOM for Rust.
|
//! This module provides the primary mechanics to create a hook-based, concurrent VDOM for Rust.
|
||||||
|
|
||||||
use crate::innerlude::ScopeOrder;
|
use crate::innerlude::{DirtyTasks, ScopeOrder};
|
||||||
use crate::Task;
|
use crate::Task;
|
||||||
use crate::{
|
use crate::{
|
||||||
any_props::AnyProps,
|
any_props::AnyProps,
|
||||||
arena::ElementId,
|
arena::ElementId,
|
||||||
innerlude::{
|
innerlude::{
|
||||||
DirtyScopes, ElementRef, ErrorBoundary, NoOpMutations, SchedulerMsg, ScopeState,
|
ElementRef, ErrorBoundary, NoOpMutations, SchedulerMsg, ScopeState, VNodeMount, VProps,
|
||||||
VNodeMount, VProps, WriteMutations,
|
WriteMutations,
|
||||||
},
|
},
|
||||||
nodes::RenderReturn,
|
nodes::RenderReturn,
|
||||||
nodes::{Template, TemplateId},
|
nodes::{Template, TemplateId},
|
||||||
@@ -20,6 +20,7 @@ use crate::{
|
|||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
|
use std::collections::BTreeSet;
|
||||||
use std::{any::Any, rc::Rc};
|
use std::{any::Any, rc::Rc};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
@@ -185,7 +186,8 @@ use tracing::instrument;
|
|||||||
pub struct VirtualDom {
|
pub struct VirtualDom {
|
||||||
pub(crate) scopes: Slab<ScopeState>,
|
pub(crate) scopes: Slab<ScopeState>,
|
||||||
|
|
||||||
pub(crate) dirty_scopes: DirtyScopes,
|
pub(crate) dirty_scopes: BTreeSet<ScopeOrder>,
|
||||||
|
pub(crate) dirty_tasks: BTreeSet<DirtyTasks>,
|
||||||
|
|
||||||
// Maps a template path to a map of byte indexes to templates
|
// Maps a template path to a map of byte indexes to templates
|
||||||
pub(crate) templates: FxHashMap<TemplateId, FxHashMap<usize, Template>>,
|
pub(crate) templates: FxHashMap<TemplateId, FxHashMap<usize, Template>>,
|
||||||
@@ -312,6 +314,7 @@ impl VirtualDom {
|
|||||||
runtime: Runtime::new(tx),
|
runtime: Runtime::new(tx),
|
||||||
scopes: Default::default(),
|
scopes: Default::default(),
|
||||||
dirty_scopes: Default::default(),
|
dirty_scopes: Default::default(),
|
||||||
|
dirty_tasks: Default::default(),
|
||||||
templates: Default::default(),
|
templates: Default::default(),
|
||||||
queued_templates: Default::default(),
|
queued_templates: Default::default(),
|
||||||
elements: Default::default(),
|
elements: Default::default(),
|
||||||
@@ -376,7 +379,8 @@ impl VirtualDom {
|
|||||||
|
|
||||||
tracing::event!(tracing::Level::TRACE, "Marking scope {:?} as dirty", id);
|
tracing::event!(tracing::Level::TRACE, "Marking scope {:?} as dirty", id);
|
||||||
let order = ScopeOrder::new(scope.height(), id);
|
let order = ScopeOrder::new(scope.height(), id);
|
||||||
self.dirty_scopes.queue_scope(order);
|
drop(scope);
|
||||||
|
self.queue_scope(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark a task as dirty
|
/// Mark a task as dirty
|
||||||
@@ -396,7 +400,8 @@ impl VirtualDom {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let order = ScopeOrder::new(scope.height(), scope.id);
|
let order = ScopeOrder::new(scope.height(), scope.id);
|
||||||
self.dirty_scopes.queue_task(task, order);
|
drop(scope);
|
||||||
|
self.queue_task(task, order);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call a listener inside the VirtualDom with data from outside the VirtualDom. **The ElementId passed in must be the id of an element with a listener, not a static node or a text node.**
|
/// Call a listener inside the VirtualDom with data from outside the VirtualDom. **The ElementId passed in must be the id of an element with a listener, not a static node or a text node.**
|
||||||
@@ -450,7 +455,7 @@ impl VirtualDom {
|
|||||||
self.process_events();
|
self.process_events();
|
||||||
|
|
||||||
// Now that we have collected all queued work, we should check if we have any dirty scopes. If there are not, then we can poll any queued futures
|
// Now that we have collected all queued work, we should check if we have any dirty scopes. If there are not, then we can poll any queued futures
|
||||||
if self.dirty_scopes.has_dirty_scopes() {
|
if self.has_dirty_scopes() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,7 +496,7 @@ impl VirtualDom {
|
|||||||
self.queue_events();
|
self.queue_events();
|
||||||
|
|
||||||
// Now that we have collected all queued work, we should check if we have any dirty scopes. If there are not, then we can poll any queued futures
|
// Now that we have collected all queued work, we should check if we have any dirty scopes. If there are not, then we can poll any queued futures
|
||||||
if self.dirty_scopes.has_dirty_scopes() {
|
if self.has_dirty_scopes() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,19 +510,14 @@ impl VirtualDom {
|
|||||||
let _runtime = RuntimeGuard::new(self.runtime.clone());
|
let _runtime = RuntimeGuard::new(self.runtime.clone());
|
||||||
// Next, run any queued tasks
|
// Next, run any queued tasks
|
||||||
// We choose not to poll the deadline since we complete pretty quickly anyways
|
// We choose not to poll the deadline since we complete pretty quickly anyways
|
||||||
while let Some(task) = self.dirty_scopes.pop_task() {
|
while let Some(task) = self.pop_task() {
|
||||||
// If the scope doesn't exist for whatever reason, then we should skip it
|
|
||||||
if !self.scopes.contains(task.order.id.0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then poll any tasks that might be pending
|
// Then poll any tasks that might be pending
|
||||||
let tasks = task.tasks_queued.into_inner();
|
let tasks = task.tasks_queued.into_inner();
|
||||||
for task in tasks {
|
for task in tasks {
|
||||||
let _ = self.runtime.handle_task_wakeup(task);
|
let _ = self.runtime.handle_task_wakeup(task);
|
||||||
// Running that task, may mark a scope higher up as dirty. If it does, return from the function early
|
// Running that task, may mark a scope higher up as dirty. If it does, return from the function early
|
||||||
self.queue_events();
|
self.queue_events();
|
||||||
if self.dirty_scopes.has_dirty_scopes() {
|
if self.has_dirty_scopes() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -609,12 +609,7 @@ impl VirtualDom {
|
|||||||
|
|
||||||
// Next, diff any dirty scopes
|
// Next, diff any dirty scopes
|
||||||
// We choose not to poll the deadline since we complete pretty quickly anyways
|
// We choose not to poll the deadline since we complete pretty quickly anyways
|
||||||
while let Some(work) = self.dirty_scopes.pop_work() {
|
while let Some(work) = self.pop_work() {
|
||||||
// If the scope doesn't exist for whatever reason, then we should skip it
|
|
||||||
if !self.scopes.contains(work.scope.id.0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let _runtime = RuntimeGuard::new(self.runtime.clone());
|
let _runtime = RuntimeGuard::new(self.runtime.clone());
|
||||||
// Then, poll any tasks that might be pending in the scope
|
// Then, poll any tasks that might be pending in the scope
|
||||||
@@ -660,7 +655,7 @@ impl VirtualDom {
|
|||||||
self.queue_events();
|
self.queue_events();
|
||||||
|
|
||||||
// Now that we have collected all queued work, we should check if we have any dirty scopes. If there are not, then we can poll any queued futures
|
// Now that we have collected all queued work, we should check if we have any dirty scopes. If there are not, then we can poll any queued futures
|
||||||
if self.dirty_scopes.has_dirty_scopes() {
|
if self.has_dirty_scopes() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -669,12 +664,7 @@ impl VirtualDom {
|
|||||||
let _runtime = RuntimeGuard::new(self.runtime.clone());
|
let _runtime = RuntimeGuard::new(self.runtime.clone());
|
||||||
// Next, run any queued tasks
|
// Next, run any queued tasks
|
||||||
// We choose not to poll the deadline since we complete pretty quickly anyways
|
// We choose not to poll the deadline since we complete pretty quickly anyways
|
||||||
while let Some(task) = self.dirty_scopes.pop_task() {
|
while let Some(task) = self.pop_task() {
|
||||||
// If the scope doesn't exist for whatever reason, then we should skip it
|
|
||||||
if !self.scopes.contains(task.order.id.0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then poll any tasks that might be pending
|
// Then poll any tasks that might be pending
|
||||||
let tasks = task.tasks_queued.into_inner();
|
let tasks = task.tasks_queued.into_inner();
|
||||||
for task in tasks {
|
for task in tasks {
|
||||||
@@ -682,7 +672,7 @@ impl VirtualDom {
|
|||||||
let _ = self.runtime.handle_task_wakeup(task);
|
let _ = self.runtime.handle_task_wakeup(task);
|
||||||
// Running that task, may mark a scope higher up as dirty. If it does, return from the function early
|
// Running that task, may mark a scope higher up as dirty. If it does, return from the function early
|
||||||
self.queue_events();
|
self.queue_events();
|
||||||
if self.dirty_scopes.has_dirty_scopes() {
|
if self.has_dirty_scopes() {
|
||||||
break 'wait_for_work;
|
break 'wait_for_work;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -694,27 +684,20 @@ impl VirtualDom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Render whatever work needs to be rendered, unlocking new futures and suspense leaves
|
// Render whatever work needs to be rendered, unlocking new futures and suspense leaves
|
||||||
while let Some(work) = self.dirty_scopes.pop_work() {
|
let _runtime = RuntimeGuard::new(self.runtime.clone());
|
||||||
// If the scope doesn't exist for whatever reason, then we should skip it
|
while let Some(work) = self.pop_work() {
|
||||||
if !self.scopes.contains(work.scope.id.0) {
|
// Then, poll any tasks that might be pending in the scope
|
||||||
continue;
|
for task in work.tasks {
|
||||||
|
// During suspense, we only want to run tasks that are suspended
|
||||||
|
if self.runtime.suspended_tasks.borrow().contains(&task) {
|
||||||
|
let _ = self.runtime.handle_task_wakeup(task);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// If the scope is dirty, run the scope and get the mutations
|
||||||
|
if work.rerun_scope {
|
||||||
|
let new_nodes = self.run_scope(work.scope.id);
|
||||||
|
|
||||||
{
|
self.diff_scope(&mut NoOpMutations, work.scope.id, new_nodes);
|
||||||
let _runtime = RuntimeGuard::new(self.runtime.clone());
|
|
||||||
// Then, poll any tasks that might be pending in the scope
|
|
||||||
for task in work.tasks {
|
|
||||||
// During suspense, we only want to run tasks that are suspended
|
|
||||||
if self.runtime.suspended_tasks.borrow().contains(&task) {
|
|
||||||
let _ = self.runtime.handle_task_wakeup(task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If the scope is dirty, run the scope and get the mutations
|
|
||||||
if work.rerun_scope {
|
|
||||||
let new_nodes = self.run_scope(work.scope.id);
|
|
||||||
|
|
||||||
self.diff_scope(&mut NoOpMutations, work.scope.id, new_nodes);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Hydrate the page
|
// Hydrate the page
|
||||||
#[cfg(not(feature = "server"))]
|
#[cfg(all(feature = "web", not(feature = "server")))]
|
||||||
fn main() {
|
fn main() {
|
||||||
dioxus_web::launch_with_props(
|
dioxus_web::launch_with_props(
|
||||||
dioxus_fullstack::router::RouteWithCfg::<Route>,
|
dioxus_fullstack::router::RouteWithCfg::<Route>,
|
||||||
|
|||||||
Reference in new issue
Block a user