Merge branch 'master' into jk/remove_node_safety
This commit is contained in:
commit
db0004758c
45 files changed
+156
-156
No files matched your search
@@ -7,7 +7,7 @@ use crate::innerlude::*;
|
||||
pub(crate) struct RealChildIterator<'a> {
|
||||
scopes: &'a ResourcePool,
|
||||
|
||||
// Heuristcally we should never bleed into 4 completely nested fragments/components
|
||||
// Heuristically we should never bleed into 4 completely nested fragments/components
|
||||
// Smallvec lets us stack allocate our little stack machine so the vast majority of cases are sane
|
||||
// TODO: use const generics instead of the 4 estimation
|
||||
stack: smallvec::SmallVec<[(u16, &'a VNode<'a>); 4]>,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! for components to be used within Nodes.
|
||||
//!
|
||||
//! Note - using the builder pattern does not required the Properties trait to be implemented - the only thing that matters is
|
||||
//! if the type suppports 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.
|
||||
|
||||
use crate::innerlude::{Context, DomTree, LazyNodes, FC};
|
||||
@@ -108,7 +108,7 @@ pub trait Properties: Sized {
|
||||
///
|
||||
/// # Safety
|
||||
/// The user must know if their props are static, but if they make a mistake, UB happens
|
||||
/// Therefore it's unsafe to memeoize.
|
||||
/// Therefore it's unsafe to memoize.
|
||||
unsafe fn memoize(&self, other: &Self) -> bool;
|
||||
}
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ impl<'src> Context<'src> {
|
||||
/// fn Component(cx: Context<()>) -> VNode {
|
||||
/// // Lazy assemble the VNode tree
|
||||
/// let lazy_tree = html! {<div> "Hello World" </div>};
|
||||
///
|
||||
///
|
||||
/// // Actually build the tree and allocate it
|
||||
/// cx.render(lazy_tree)
|
||||
/// }
|
||||
@@ -203,7 +203,7 @@ impl<'src> Context<'src> {
|
||||
.flatten();
|
||||
}
|
||||
|
||||
/// Try to retrive a SharedState with type T from the any parent Scope.
|
||||
/// Try to retrieve a SharedState with type T from the any parent Scope.
|
||||
pub fn consume_state<T: 'static>(self) -> Option<Rc<T>> {
|
||||
let getter = &self.scope.shared.get_shared_context;
|
||||
let ty = TypeId::of::<T>();
|
||||
@@ -225,7 +225,7 @@ impl<'src> Context<'src> {
|
||||
/// todo!();
|
||||
/// rsx!(cx, div { "Subtree {id}"})
|
||||
/// };
|
||||
/// ```
|
||||
/// ```
|
||||
pub fn create_subtree(self) -> Option<u32> {
|
||||
self.scope.new_subtree()
|
||||
}
|
||||
@@ -298,9 +298,9 @@ impl<'src> Context<'src> {
|
||||
}
|
||||
|
||||
const HOOK_ERR_MSG: &str = r###"
|
||||
Unable to retrive the hook that was initialized in this index.
|
||||
Unable to retrieve the hook that was initialized at this index.
|
||||
Consult the `rules of hooks` to understand how to use hooks properly.
|
||||
|
||||
You likely used the hook in a conditional. Hooks rely on consistent ordering between renders.
|
||||
Any function prefixed with "use" should not be called conditionally.
|
||||
Functions prefixed with "use" should never be called conditionally.
|
||||
"###;
|
||||
+21
-21
@@ -6,7 +6,7 @@
|
||||
//! ## Notice:
|
||||
//!
|
||||
//! The inspiration and code for this module was originally taken from Dodrio (@fitzgen) and then modified to support
|
||||
//! Components, Fragments, Suspense, SubTree memoization, incremental diffing, cancelation, NodeRefs, pausing, priority
|
||||
//! Components, Fragments, Suspense, SubTree memoization, incremental diffing, cancellation, NodeRefs, pausing, priority
|
||||
//! scheduling, and additional batching operations.
|
||||
//!
|
||||
//! ## Implementation Details:
|
||||
@@ -15,8 +15,8 @@
|
||||
//! --------------------
|
||||
//! All nodes are addressed by their IDs. The RealDom provides an imperative interface for making changes to these nodes.
|
||||
//! We don't necessarily require that DOM changes happen instantly during the diffing process, so the implementor may choose
|
||||
//! to batch nodes if it is more performant for their application. The element IDs are indicies into the internal element
|
||||
//! array. The expectation is that implemenetors will use the ID as an index into a Vec of real nodes, allowing for passive
|
||||
//! to batch nodes if it is more performant for their application. The element IDs are indices into the internal element
|
||||
//! array. The expectation is that implementors will use the ID as an index into a Vec of real nodes, allowing for passive
|
||||
//! garbage collection as the VirtualDOM replaces old nodes.
|
||||
//!
|
||||
//! When new vnodes are created through `cx.render`, they won't know which real node they correspond to. During diffing,
|
||||
@@ -30,34 +30,34 @@
|
||||
//! fragment to be replaced with when it is no longer empty. This is guaranteed by logic in the NodeFactory - it is
|
||||
//! impossible to craft a fragment with 0 elements - they must always have at least a single placeholder element. Adding
|
||||
//! "dummy" nodes _is_ inefficient, but it makes our diffing algorithm faster and the implementation is completely up to
|
||||
//! the platform.
|
||||
//! the platform.
|
||||
//!
|
||||
//! Other implementations either don't support fragments or use a "child + sibling" pattern to represent them. Our code is
|
||||
//! vastly simpler and more performant when we can just create a placeholder element while the fragment has no children.
|
||||
//!
|
||||
//! ### Suspense
|
||||
//! ------------
|
||||
//! Dioxus implements suspense slightly differently than React. In React, each fiber is manually progressed until it runs
|
||||
//! Dioxus implements Suspense slightly differently than React. In React, each fiber is manually progressed until it runs
|
||||
//! into a promise-like value. React will then work on the next "ready" fiber, checking back on the previous fiber once
|
||||
//! it has finished its new work. In Dioxus, we use a similar approach, but try to completely render the tree before
|
||||
//! switching sub-fibers. Instead, each future is submitted into a futures-queue and the node is manually loaded later on.
|
||||
//! Due to the frequent calls to "yield_now" we can get the pure "fetch-as-you-render" behavior of React fiber.
|
||||
//! Due to the frequent calls to "yield_now" we can get the pure "fetch-as-you-render" behavior of React Fiber.
|
||||
//!
|
||||
//! We're able to use this approach because we use placeholder nodes - futures that aren't ready still get submitted to
|
||||
//! DOM, but as a placeholder.
|
||||
//!
|
||||
//! Right now, the "suspense" queue is intertwined the hooks. In the future, we should allow any future to drive attributes
|
||||
//! and contents, without the need for the "use_suspense" hook. In the interim, this is the quickest way to get suspense working.
|
||||
//! Right now, the "suspense" queue is intertwined with hooks. In the future, we should allow any future to drive attributes
|
||||
//! and contents, without the need for the "use_suspense" hook. In the interim, this is the quickest way to get Suspense working.
|
||||
//!
|
||||
//! ## Subtree Memoization
|
||||
//! -----------------------
|
||||
//! We also employ "subtree memoization" which saves us from having to check trees which take no dynamic content. We can
|
||||
//! We also employ "subtree memoization" which saves us from having to check trees which hold no dynamic content. We can
|
||||
//! detect if a subtree is "static" by checking if its children are "static". Since we dive into the tree depth-first, the
|
||||
//! calls to "create" propogate this information upwards. Structures like the one below are entirely static:
|
||||
//! calls to "create" propagate this information upwards. Structures like the one below are entirely static:
|
||||
//! ```rust
|
||||
//! rsx!( div { class: "hello world", "this node is entirely static" } )
|
||||
//! ```
|
||||
//! Because the subtrees won't be diffed, their "real node" data will be stale (invalid), so its up to the reconciler to
|
||||
//! Because the subtrees won't be diffed, their "real node" data will be stale (invalid), so it's up to the reconciler to
|
||||
//! track nodes created in a scope and clean up all relevant data. Support for this is currently WIP and depends on comp-time
|
||||
//! hashing of the subtree from the rsx! macro. We do a very limited form of static analysis via static string pointers as
|
||||
//! a way of short-circuiting the most expensive checks.
|
||||
@@ -70,11 +70,11 @@
|
||||
//!
|
||||
//! ## Garbage Collection
|
||||
//! ---------------------
|
||||
//! Dioxus uses a passive garbage collection system to clean up old nodes once the work has been completed. This garabge
|
||||
//! Dioxus uses a passive garbage collection system to clean up old nodes once the work has been completed. This garbage
|
||||
//! collection is done internally once the main diffing work is complete. After the "garbage" is collected, Dioxus will then
|
||||
//! start to re-use old keys for new nodes. This results in a passive memory management system that is very efficient.
|
||||
//!
|
||||
//! The IDs used by the key/map are just an index into a vec. This means that Dioxus will drive the key allocation strategy
|
||||
//! The IDs used by the key/map are just an index into a Vec. This means that Dioxus will drive the key allocation strategy
|
||||
//! so the client only needs to maintain a simple list of nodes. By default, Dioxus will not manually clean up old nodes
|
||||
//! for the client. As new nodes are created, old nodes will be over-written.
|
||||
//!
|
||||
@@ -95,7 +95,7 @@ use DomEdit::*;
|
||||
/// Our DiffMachine is an iterative tree differ.
|
||||
///
|
||||
/// It uses techniques of a stack machine to allow pausing and restarting of the diff algorithm. This
|
||||
/// was origially implemented using recursive techniques, but Rust lacks the abilty to call async functions recursively,
|
||||
/// was originally implemented using recursive techniques, but Rust lacks the ability to call async functions recursively,
|
||||
/// meaning we could not "pause" the original diffing algorithm.
|
||||
///
|
||||
/// Instead, we use a traditional stack machine approach to diff and create new nodes. The diff algorithm periodically
|
||||
@@ -194,7 +194,7 @@ impl<'bump> DiffMachine<'bump> {
|
||||
};
|
||||
|
||||
if deadline_expired() {
|
||||
log::debug!("Deadling expired before we could finished!");
|
||||
log::debug!("Deadline expired before we could finished!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -591,7 +591,7 @@ impl<'bump> DiffMachine<'bump> {
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// Utilites for creating new diff instructions
|
||||
// Utilities for creating new diff instructions
|
||||
// =============================================
|
||||
|
||||
// Diff the given set of old and new children.
|
||||
@@ -607,7 +607,7 @@ impl<'bump> DiffMachine<'bump> {
|
||||
//
|
||||
// Remember, non-empty lists does not mean that there are real elements, just that there are virtual elements.
|
||||
//
|
||||
// Frament nodes cannot generate empty children lists, so we can assume that when a list is empty, it belongs only
|
||||
// Fragment nodes cannot generate empty children lists, so we can assume that when a list is empty, it belongs only
|
||||
// to an element, and appending makes sense.
|
||||
fn diff_children(&mut self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
|
||||
// Remember, fragments can never be empty (they always have a single child)
|
||||
@@ -757,7 +757,7 @@ impl<'bump> DiffMachine<'bump> {
|
||||
self.remove_nodes(old_middle, true);
|
||||
} else if old_middle.is_empty() {
|
||||
// there were no old elements, so just create the new elements
|
||||
// we need to find the right "foothold" though - we shouldnt use the "append" at all
|
||||
// we need to find the right "foothold" though - we shouldn't use the "append" at all
|
||||
if left_offset == 0 {
|
||||
// insert at the beginning of the old list
|
||||
let foothold = &old[old.len() - right_offset];
|
||||
@@ -862,7 +862,7 @@ impl<'bump> DiffMachine<'bump> {
|
||||
// Upon exit from this function, it will be restored to that same state.
|
||||
fn diff_keyed_middle(&mut self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
|
||||
/*
|
||||
1. Map the old keys into a numerical ordering based on indicies.
|
||||
1. Map the old keys into a numerical ordering based on indices.
|
||||
2. Create a map of old key to its index
|
||||
3. Map each new key to the old key, carrying over the old index.
|
||||
- IE if we have ABCD becomes BACD, our sequence would be 1,0,2,3
|
||||
@@ -888,7 +888,7 @@ impl<'bump> DiffMachine<'bump> {
|
||||
debug_assert_ne!(new.first().map(|n| n.key()), old.first().map(|o| o.key()));
|
||||
debug_assert_ne!(new.last().map(|n| n.key()), old.last().map(|o| o.key()));
|
||||
|
||||
// 1. Map the old keys into a numerical ordering based on indicies.
|
||||
// 1. Map the old keys into a numerical ordering based on indices.
|
||||
// 2. Create a map of old key to its index
|
||||
// IE if the keys were A B C, then we would have (A, 1) (B, 2) (C, 3).
|
||||
let old_key_to_old_index = old
|
||||
@@ -956,7 +956,7 @@ impl<'bump> DiffMachine<'bump> {
|
||||
if old_index == u32::MAX as usize {
|
||||
stack.create_node(new_node, MountType::Absorb);
|
||||
} else {
|
||||
// this funciton should never take LIS indicies
|
||||
// this function should never take LIS indices
|
||||
stack.push(DiffInstruction::PrepareMove { node: new_node });
|
||||
stack.push(DiffInstruction::Diff {
|
||||
new: new_node,
|
||||
|
||||
@@ -111,7 +111,7 @@ impl<'bump> DiffStack<'bump> {
|
||||
self.instructions.push(DiffInstruction::PopScope);
|
||||
|
||||
// Run the creation algorithm with this scope on the stack
|
||||
// ?? I think we treat components as framgnets??
|
||||
// ?? I think we treat components as fragments??
|
||||
self.instructions.push(DiffInstruction::Create { node });
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ pub struct UserEvent {
|
||||
|
||||
/// Priority of Event Triggers.
|
||||
///
|
||||
/// Internally, Dioxus will abort work that's taking too long if new, more important, work arrives. Unlike React, Dioxus
|
||||
/// Internally, Dioxus will abort work that's taking too long if new, more important work arrives. Unlike React, Dioxus
|
||||
/// won't be afraid to pause work or flush changes to the RealDOM. This is called "cooperative scheduling". Some Renderers
|
||||
/// implement this form of scheduling internally, however Dioxus will perform its own scheduling as well.
|
||||
///
|
||||
@@ -69,17 +69,17 @@ pub enum EventPriority {
|
||||
High = 2,
|
||||
|
||||
/// "Medium priority" work is generated by page events not triggered by the user. These types of events are less important
|
||||
/// than "High Priority" events and will take presedence over low priority events.
|
||||
/// than "High Priority" events and will take precedence over low priority events.
|
||||
///
|
||||
/// This is typically reserved for VirtualEvents that are not related to keyboard or mouse input.
|
||||
///
|
||||
/// React calls these "continuous" events (e.g. mouse move, mouse wheel, touch move, etc).
|
||||
Medium = 1,
|
||||
|
||||
/// "Low Priority" work will always be pre-empted unless the work is significantly delayed, in which case it will be
|
||||
/// "Low Priority" work will always be preempted unless the work is significantly delayed, in which case it will be
|
||||
/// advanced to the front of the work queue until completed.
|
||||
///
|
||||
/// The primary user of Low Priority work is the asynchronous work system (suspense).
|
||||
/// The primary user of Low Priority work is the asynchronous work system (Suspense).
|
||||
///
|
||||
/// This is considered "idle" work or "background" work.
|
||||
Low = 0,
|
||||
@@ -505,7 +505,7 @@ pub mod on {
|
||||
/// _ => {}
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
pub key: String,
|
||||
|
||||
/// Get the key code as an enum Variant.
|
||||
@@ -525,7 +525,7 @@ pub mod on {
|
||||
/// _ => {}
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
pub key_code: KeyCode,
|
||||
|
||||
/// Indicate if the `alt` modifier key was pressed during this keyboard event
|
||||
|
||||
@@ -6,9 +6,9 @@ use crate::FC;
|
||||
|
||||
/// Provides heuristics to the "SharedResources" object for improving allocation performance.
|
||||
///
|
||||
/// This heueristic engine records the memory footprint of bump arenas and hook lists for each component. These records are
|
||||
/// 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 initializion process.
|
||||
/// that would slow down the diffing and initialization process.
|
||||
///
|
||||
///
|
||||
pub struct HeuristicsEngine {
|
||||
|
||||
@@ -30,10 +30,10 @@ impl HookList {
|
||||
|
||||
/// This resets the internal iterator count
|
||||
/// It's okay that we've given out each hook, but now we have the opportunity to give it out again
|
||||
/// Therefore, resetting is cosudered unsafe
|
||||
/// Therefore, resetting is considered unsafe
|
||||
///
|
||||
/// This should only be ran by Dioxus itself before "running scope".
|
||||
/// Dioxus knows how to descened through the tree to prevent mutable aliasing.
|
||||
/// Dioxus knows how to descend through the tree to prevent mutable aliasing.
|
||||
pub(crate) unsafe fn reset(&mut self) {
|
||||
self.idx.set(0);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Built-in hooks
|
||||
//!
|
||||
//! This module contains all the low-level built-in hooks that require 1st party support to work.
|
||||
//! This module contains all the low-level built-in hooks that require first party support to work.
|
||||
//!
|
||||
//! Hooks:
|
||||
//! - [`use_hook`]
|
||||
|
||||
@@ -41,7 +41,7 @@ impl<'a> Mutations<'a> {
|
||||
self.edits.push(InsertBefore { n, root });
|
||||
}
|
||||
|
||||
// Remove Nodesfrom the dom
|
||||
// Remove Nodes from the dom
|
||||
pub(crate) fn remove(&mut self, id: u64) {
|
||||
self.edits.push(Remove { root: id });
|
||||
}
|
||||
@@ -146,7 +146,7 @@ impl<'a> NodeRefMutation<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A `DomEdit` represents a serialzied form of the VirtualDom's trait-based API. This allows streaming edits across the
|
||||
/// A `DomEdit` represents a serialized form of the VirtualDom's trait-based API. This allows streaming edits across the
|
||||
/// network or through FFI boundaries.
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[cfg_attr(
|
||||
@@ -164,7 +164,7 @@ pub enum DomEdit<'bump> {
|
||||
many: u32,
|
||||
},
|
||||
|
||||
// "Root" refers to the item direclty
|
||||
// "Root" refers to the item directly
|
||||
// it's a waste of an instruction to push the root directly
|
||||
ReplaceWith {
|
||||
root: u64,
|
||||
|
||||
@@ -15,7 +15,7 @@ use std::{
|
||||
|
||||
/// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
|
||||
///
|
||||
/// VNodes are designed to be lightweight and used with with a bump alloactor. To create a VNode, you can use either of:
|
||||
/// VNodes are designed to be lightweight and used with with a bump allocator. To create a VNode, you can use either of:
|
||||
/// - the [`rsx`] macro
|
||||
/// - the [`html`] macro
|
||||
/// - the [`NodeFactory`] API
|
||||
@@ -110,7 +110,7 @@ pub enum VNode<'src> {
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// let node = cx.render(rsx! ( Fragment {} )).unwrap();
|
||||
/// let node = cx.render(rsx! ( Fragment {} )).unwrap();
|
||||
/// if let VNode::Fragment(frag) = node {
|
||||
/// let root = &frag.children[0];
|
||||
/// assert_eq!(root, VNode::Anchor);
|
||||
@@ -159,7 +159,7 @@ pub struct VAnchor {
|
||||
pub dom_id: Cell<Option<ElementId>>,
|
||||
}
|
||||
|
||||
/// A bump-alloacted string slice and metadata.
|
||||
/// A bump-allocated string slice and metadata.
|
||||
pub struct VText<'src> {
|
||||
pub text: &'src str,
|
||||
|
||||
@@ -593,7 +593,7 @@ impl<'a> NodeFactory<'a> {
|
||||
/// impl IntoIterator<Item = impl IntoVNode<'a>>
|
||||
/// ```
|
||||
///
|
||||
/// As such, all node creation must go through the factory, which is only availble in the component context.
|
||||
/// As such, all node creation must go through the factory, which is only available in the component context.
|
||||
/// These strict requirements make it possible to manage lifetimes and state.
|
||||
pub trait IntoVNode<'a> {
|
||||
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
|
||||
|
||||
@@ -8,7 +8,7 @@ pub(crate) struct ResourcePool {
|
||||
This *has* to be an UnsafeCell.
|
||||
|
||||
Each BumpFrame and Scope is located in this Slab - and we'll need mutable access to a scope while holding on to
|
||||
its bumpframe conents immutably.
|
||||
its bumpframe contents immutably.
|
||||
|
||||
However, all of the interaction with this Slab is done in this module and the Diff module, so it should be fairly
|
||||
simple to audit.
|
||||
|
||||
@@ -43,19 +43,19 @@ before the timeout, then Dioxus will work on any pending work in the interim. If
|
||||
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).
|
||||
|
||||
# Priorty System
|
||||
# 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-prioty work with short-running high-priority work.
|
||||
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 priorty system:
|
||||
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)
|
||||
@@ -132,7 +132,7 @@ pub(crate) struct Scheduler {
|
||||
/// 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 guartnees cannot prove that this is safe. We will need to maintain the safety guarantees manually.
|
||||
/// and rusts's guarantees cannot prove that this is safe. We will need to maintain the safety guarantees manually.
|
||||
pub pool: ResourcePool,
|
||||
|
||||
pub heuristics: HeuristicsEngine,
|
||||
@@ -187,7 +187,7 @@ impl Scheduler {
|
||||
let sender = sender.clone();
|
||||
Rc::new(move |id| {
|
||||
//
|
||||
log::debug!("scheduling immedate! {:?}", id);
|
||||
log::debug!("scheduling immediate! {:?}", id);
|
||||
sender.unbounded_send(SchedulerMsg::Immediate(id)).unwrap()
|
||||
})
|
||||
},
|
||||
@@ -387,7 +387,7 @@ impl Scheduler {
|
||||
});
|
||||
|
||||
if let Some(scopeid) = self.dirty_scopes.pop() {
|
||||
log::info!("handlng dirty scope {:?}", scopeid);
|
||||
log::info!("handling dirty scope {:?}", scopeid);
|
||||
if !ran_scopes.contains(&scopeid) {
|
||||
ran_scopes.insert(scopeid);
|
||||
log::debug!("about to run scope {:?}", scopeid);
|
||||
@@ -461,7 +461,7 @@ impl Scheduler {
|
||||
- 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 priorty level. If our priority level is already "high,"
|
||||
- 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.
|
||||
@@ -581,7 +581,7 @@ impl Scheduler {
|
||||
} else {
|
||||
// todo: should this be a hard error?
|
||||
log::warn!(
|
||||
"Component failed to run succesfully during rebuild.
|
||||
"Component failed to run successfully during rebuild.
|
||||
This does not result in a failed rebuild, but indicates a logic failure within your app."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ use std::{
|
||||
/// The actual contents of the hooks, though, will be allocated with the standard allocator. These should not allocate as frequently.
|
||||
///
|
||||
/// We expose the `Scope` type so downstream users can traverse the Dioxus VirtualDOM for whatever
|
||||
/// usecase they might have.
|
||||
/// use case they might have.
|
||||
pub struct Scope {
|
||||
// Book-keeping about our spot in the arena
|
||||
pub(crate) parent_idx: Option<ScopeId>,
|
||||
@@ -233,20 +233,20 @@ impl Scope {
|
||||
}
|
||||
|
||||
/// This method cleans up any references to data held within our hook list. This prevents mutable aliasing from
|
||||
/// causuing UB in our tree.
|
||||
/// causing UB in our tree.
|
||||
///
|
||||
/// This works by cleaning up our references from the bottom of the tree to the top. The directed graph of components
|
||||
/// essentially forms a dependency tree that we can traverse from the bottom to the top. As we traverse, we remove
|
||||
/// any possible references to the data in the hook list.
|
||||
///
|
||||
/// Refrences to hook data can only be stored in listeners and component props. During diffing, we make sure to log
|
||||
/// References to hook data can only be stored in listeners and component props. During diffing, we make sure to log
|
||||
/// all listeners and borrowed props so we can clear them here.
|
||||
///
|
||||
/// This also makes sure that drop order is consistent and predictable. All resources that rely on being dropped will
|
||||
/// be dropped.
|
||||
pub(crate) fn ensure_drop_safety(&mut self, pool: &ResourcePool) {
|
||||
// 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 Referrence)
|
||||
// run the hooks (which hold an &mut Reference)
|
||||
// right now, we don't drop
|
||||
self.borrowed_props
|
||||
.get_mut()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//! This module provides the primary mechanics to create a hook-based, concurrent VDOM for Rust.
|
||||
//!
|
||||
//! In this file, multiple items are defined. This file is big, but should be documented well to
|
||||
//! navigate the innerworkings of the Dom. We try to keep these main mechanics in this file to limit
|
||||
//! navigate the inner workings of the Dom. We try to keep these main mechanics in this file to limit
|
||||
//! the possible exposed API surface (keep fields private). This particular implementation of VDOM
|
||||
//! is extremely efficient, but relies on some unsafety under the hood to do things like manage
|
||||
//! micro-heaps for components. We are currently working on refactoring the safety out into safe(r)
|
||||
@@ -11,10 +11,10 @@
|
||||
//!
|
||||
//! Included is:
|
||||
//! - The [`VirtualDom`] itself
|
||||
//! - The [`Scope`] object for mangning component lifecycle
|
||||
//! - The [`Scope`] object for managing component lifecycle
|
||||
//! - The [`ActiveFrame`] object for managing the Scope`s microheap
|
||||
//! - The [`Context`] object for exposing VirtualDOM API to components
|
||||
//! - The [`NodeFactory`] object for lazyily exposing the `Context` API to the nodebuilder API
|
||||
//! - The [`NodeFactory`] object for lazily exposing the `Context` API to the nodebuilder API
|
||||
//!
|
||||
//! This module includes just the barebones for a complete VirtualDOM API.
|
||||
//! Additional functionality is defined in the respective files.
|
||||
@@ -130,7 +130,7 @@ impl VirtualDom {
|
||||
/// Launch the VirtualDom, but provide your own channel for receiving and sending messages into the scheduler.
|
||||
///
|
||||
/// This is useful when the VirtualDom must be driven from outside a thread and it doesn't make sense to wait for the
|
||||
/// VirtualDom to be created just to retrive its channel receiver.
|
||||
/// VirtualDom to be created just to retrieve its channel receiver.
|
||||
pub fn new_with_props_and_scheduler<P: 'static + Send>(
|
||||
root: FC<P>,
|
||||
root_props: P,
|
||||
@@ -173,7 +173,7 @@ impl VirtualDom {
|
||||
|
||||
/// Get the [`Scope`] for the root component.
|
||||
///
|
||||
/// This is useful for traversing the tree from the root for heuristics or altnerative renderers that use Dioxus
|
||||
/// This is useful for traversing the tree from the root for heuristics or alternsative renderers that use Dioxus
|
||||
/// directly.
|
||||
pub fn base_scope(&self) -> &Scope {
|
||||
self.scheduler.pool.get_scope(self.base_scope).unwrap()
|
||||
@@ -186,7 +186,7 @@ impl VirtualDom {
|
||||
|
||||
/// Update the root props of this VirtualDOM.
|
||||
///
|
||||
/// This method retuns None if the old props could not be removed. The entire VirtualDOM will be rebuilt immediately,
|
||||
/// This method returns None if the old props could not be removed. The entire VirtualDOM will be rebuilt immediately,
|
||||
/// so calling this method will block the main thread until computation is done.
|
||||
///
|
||||
/// ## Example
|
||||
@@ -258,7 +258,7 @@ impl VirtualDom {
|
||||
|
||||
/// Compute a manual diff of the VirtualDOM between states.
|
||||
///
|
||||
/// This can be useful when state inside the DOM is remotely changed from the outside, but not propogated as an event.
|
||||
/// This can be useful when state inside the DOM is remotely changed from the outside, but not propagated as an event.
|
||||
///
|
||||
/// In this case, every component will be diffed, even if their props are memoized. This method is intended to be used
|
||||
/// to force an update of the DOM when the state of the app is changed outside of the app.
|
||||
|
||||
Reference in new issue
Block a user