add sync behind a feature flag
This commit is contained in:
1 parent
0ac02cc893
commit
74e9d5977a
9 files changed
+137
-33
No files matched your search
@@ -44,3 +44,4 @@ dioxus = { path = "../dioxus" }
|
||||
[features]
|
||||
default = []
|
||||
serialize = ["serde"]
|
||||
sync_attributes = []
|
||||
@@ -70,10 +70,10 @@ pub(crate) mod innerlude {
|
||||
}
|
||||
|
||||
pub use crate::innerlude::{
|
||||
fc_to_builder, AnyValueRc, Attribute, AttributeValue, Component, DynamicNode, Element,
|
||||
ElementId, Event, Fragment, IntoAttributeValue, IntoDynNode, LazyNodes, Mutation, Mutations,
|
||||
Properties, RenderReturn, Scope, ScopeId, ScopeState, Scoped, SuspenseContext, TaskId,
|
||||
Template, TemplateAttribute, TemplateNode, VComponent, VNode, VText, VirtualDom,
|
||||
fc_to_builder, AnyValue, AnyValueContainer, Attribute, AttributeValue, Component, DynamicNode,
|
||||
Element, ElementId, Event, Fragment, IntoAttributeValue, IntoDynNode, LazyNodes, Mutation,
|
||||
Mutations, Properties, RenderReturn, Scope, ScopeId, ScopeState, Scoped, SuspenseContext,
|
||||
TaskId, Template, TemplateAttribute, TemplateNode, VComponent, VNode, VText, VirtualDom,
|
||||
};
|
||||
|
||||
/// The purpose of this module is to alleviate imports of many common types
|
||||
|
||||
+49
-19
@@ -338,7 +338,7 @@ pub enum AttributeValue<'a> {
|
||||
Listener(ListenerCb<'a>),
|
||||
|
||||
/// An arbitrary value that implements PartialEq and is static
|
||||
Any(AnyValueRc),
|
||||
Any(AnyValueContainer),
|
||||
|
||||
/// A "none" value, resulting in the removal of an attribute from the dom
|
||||
None,
|
||||
@@ -375,26 +375,38 @@ impl<'de, 'a> serde::Deserialize<'de> for ListenerCb<'a> {
|
||||
|
||||
/// A boxed value that implements PartialEq and Any
|
||||
#[derive(Clone)]
|
||||
pub struct AnyValueRc(pub Rc<dyn AnyValue>);
|
||||
#[cfg(not(feature = "sync_attributes"))]
|
||||
pub struct AnyValueContainer(pub Rc<dyn AnyValue>);
|
||||
|
||||
impl PartialEq for AnyValueRc {
|
||||
#[derive(Clone)]
|
||||
#[cfg(feature = "sync_attributes")]
|
||||
pub struct AnyValueContainer(pub std::sync::Arc<dyn AnyValue>);
|
||||
|
||||
impl PartialEq for AnyValueContainer {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.any_cmp(other.0.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl AnyValueRc {
|
||||
impl AnyValueContainer {
|
||||
pub fn new<T: AnyValueBounds>(value: T) -> Self {
|
||||
#[cfg(feature = "sync_attributes")]
|
||||
return Self(std::sync::Arc::new(value));
|
||||
#[cfg(not(feature = "sync_attributes"))]
|
||||
return Self(Rc::new(value));
|
||||
}
|
||||
|
||||
/// Returns a reference to the inner value without checking the type.
|
||||
///
|
||||
/// # Safety
|
||||
/// The caller must ensure that the type of the inner value is `T`.
|
||||
pub unsafe fn downcast_ref_unchecked<T: Any + PartialEq>(&self) -> &T {
|
||||
unsafe { &*(self.0.as_ref() as *const dyn AnyValue as *const T) }
|
||||
pub unsafe fn downcast_ref_unchecked<T: AnyValueBounds>(&self) -> &T {
|
||||
unsafe { &*(self.0.as_ref() as *const _ as *const T) }
|
||||
}
|
||||
|
||||
/// Returns a reference to the inner value.
|
||||
pub fn downcast_ref<T: Any + PartialEq>(&self) -> Option<&T> {
|
||||
if self.0.type_id() == TypeId::of::<T>() {
|
||||
pub fn downcast_ref<T: AnyValueBounds>(&self) -> Option<&T> {
|
||||
if self.0.our_typeid() == TypeId::of::<T>() {
|
||||
Some(unsafe { self.downcast_ref_unchecked() })
|
||||
} else {
|
||||
None
|
||||
@@ -402,25 +414,25 @@ impl AnyValueRc {
|
||||
}
|
||||
|
||||
/// Checks if the inner value is of type `T`.
|
||||
pub fn is<T: Any + PartialEq>(&self) -> bool {
|
||||
self.0.type_id() == TypeId::of::<T>()
|
||||
pub fn is<T: AnyValueBounds>(&self) -> bool {
|
||||
self.0.our_typeid() == TypeId::of::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_any_value_rc() {
|
||||
let a = AnyValueRc(Rc::new(1i32));
|
||||
assert!(a.is::<i32>());
|
||||
assert!(!a.is::<i64>());
|
||||
let a = AnyValueContainer::new(1i32);
|
||||
assert_eq!(a.downcast_ref::<i32>(), Some(&1i32));
|
||||
assert_eq!(a.downcast_ref::<i64>(), None);
|
||||
assert!(a.is::<i32>());
|
||||
assert!(!a.is::<i64>());
|
||||
unsafe {
|
||||
assert_eq!(a.downcast_ref_unchecked::<i32>(), &1i32);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl serde::Serialize for AnyValueRc {
|
||||
impl serde::Serialize for AnyValueContainer {
|
||||
fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
@@ -430,7 +442,7 @@ impl serde::Serialize for AnyValueRc {
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'de> serde::Deserialize<'de> for AnyValueRc {
|
||||
impl<'de> serde::Deserialize<'de> for AnyValueContainer {
|
||||
fn deserialize<D>(_: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
@@ -467,19 +479,37 @@ impl<'a> PartialEq for AttributeValue<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "sync_attributes")]
|
||||
pub trait AnyValueBounds: Any + PartialEq + Sync {}
|
||||
#[cfg(not(feature = "sync_attributes"))]
|
||||
pub trait AnyValueBounds: Any + PartialEq {}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait AnyValue: Any {
|
||||
#[cfg(feature = "sync_attributes")]
|
||||
pub trait AnyValue: Sync {
|
||||
fn any_cmp(&self, other: &dyn AnyValue) -> bool;
|
||||
fn our_typeid(&self) -> TypeId;
|
||||
}
|
||||
|
||||
impl<T: PartialEq + Any> AnyValue for T {
|
||||
impl<T: AnyValueBounds> AnyValue for T {
|
||||
fn any_cmp(&self, other: &dyn AnyValue) -> bool {
|
||||
if self.type_id() != other.type_id() {
|
||||
if self.our_typeid() != other.our_typeid() {
|
||||
return false;
|
||||
}
|
||||
|
||||
self == unsafe { &*(other as *const _ as *const T) }
|
||||
}
|
||||
|
||||
fn our_typeid(&self) -> TypeId {
|
||||
self.type_id()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(feature = "sync_attributes"))]
|
||||
pub trait AnyValue {
|
||||
fn any_cmp(&self, other: &dyn AnyValue) -> bool;
|
||||
fn our_typeid(&self) -> TypeId;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@@ -679,7 +709,7 @@ impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAttributeValue<'a> for AnyValueRc {
|
||||
impl<'a> IntoAttributeValue<'a> for AnyValueContainer {
|
||||
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
||||
AttributeValue::Any(self)
|
||||
}
|
||||
|
||||
@@ -26,4 +26,8 @@ dashmap = "5.4.0"
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.8.5"
|
||||
dioxus = { path = "../dioxus", version = "^0.2.1" }
|
||||
dioxus = { path = "../dioxus", version = "^0.2.1" }
|
||||
|
||||
[features]
|
||||
default = ["parallel"]
|
||||
parallel = ["dioxus-core/sync_attributes"]
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::{state::State, tree::NodeId};
|
||||
use dioxus_core::{AnyValueRc, AttributeValue, ElementId};
|
||||
use dioxus_core::{AnyValueContainer, AttributeValue, ElementId};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use std::fmt::Debug;
|
||||
|
||||
@@ -79,7 +79,7 @@ pub enum OwnedAttributeValue {
|
||||
Float(f64),
|
||||
Int(i64),
|
||||
Bool(bool),
|
||||
Any(AnyValueRc),
|
||||
Any(AnyValueContainer),
|
||||
None,
|
||||
}
|
||||
|
||||
|
||||
@@ -314,7 +314,7 @@ struct RawPointer<T>(*mut T);
|
||||
unsafe impl<T> Send for RawPointer<T> {}
|
||||
unsafe impl<T> Sync for RawPointer<T> {}
|
||||
|
||||
pub fn resolve_passes<T, Tr: TreeView<T>>(
|
||||
pub fn resolve_passes<T, Tr: TreeView<T> + Sync>(
|
||||
tree: &mut Tr,
|
||||
dirty_nodes: DirtyNodeStates,
|
||||
mut passes: Vec<&AnyPass<T>>,
|
||||
@@ -368,6 +368,50 @@ pub fn resolve_passes<T, Tr: TreeView<T>>(
|
||||
std::sync::Arc::try_unwrap(nodes_updated).unwrap()
|
||||
}
|
||||
|
||||
pub fn resolve_passes_single_threaded<T, Tr: TreeView<T>>(
|
||||
tree: &mut Tr,
|
||||
dirty_nodes: DirtyNodeStates,
|
||||
mut passes: Vec<&AnyPass<T>>,
|
||||
ctx: SendAnyMap,
|
||||
) -> FxDashSet<NodeId> {
|
||||
let dirty_states = Arc::new(dirty_nodes);
|
||||
let mut resolved_passes: FxHashSet<PassId> = FxHashSet::default();
|
||||
let mut resolving = Vec::new();
|
||||
let nodes_updated = Arc::new(FxDashSet::default());
|
||||
let ctx = Arc::new(ctx);
|
||||
while !passes.is_empty() {
|
||||
let mut currently_borrowed = MemberMask::default();
|
||||
let mut i = 0;
|
||||
while i < passes.len() {
|
||||
let pass = &passes[i];
|
||||
let pass_id = pass.pass_id();
|
||||
let pass_mask = pass.mask();
|
||||
if pass
|
||||
.dependancies()
|
||||
.iter()
|
||||
.all(|d| resolved_passes.contains(d) || *d == pass_id)
|
||||
&& !pass_mask.overlaps(currently_borrowed)
|
||||
{
|
||||
let pass = passes.remove(i);
|
||||
resolving.push(pass_id);
|
||||
currently_borrowed |= pass_mask;
|
||||
let dirty_states = dirty_states.clone();
|
||||
let nodes_updated = nodes_updated.clone();
|
||||
let ctx = ctx.clone();
|
||||
// this is safe because the member_mask acts as a per-member mutex and we have verified that the pass does not overlap with any other pass
|
||||
let mut dirty = DirtyNodes::default();
|
||||
dirty_states.all_dirty(pass_id, &mut dirty, tree);
|
||||
pass.resolve(tree, dirty, &dirty_states, &nodes_updated, &ctx);
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
resolved_passes.extend(resolving.iter().copied());
|
||||
resolving.clear()
|
||||
}
|
||||
std::sync::Arc::try_unwrap(nodes_updated).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn node_pass() {
|
||||
use crate::tree::{Tree, TreeLike};
|
||||
|
||||
@@ -339,12 +339,12 @@ impl<S: State> RealDom<S> {
|
||||
}
|
||||
|
||||
/// Update the state of the dom, after appling some mutations. This will keep the nodes in the dom up to date with their VNode counterparts.
|
||||
pub fn update_state(
|
||||
pub fn update_state_single_threaded(
|
||||
&mut self,
|
||||
nodes_updated: DirtyNodeStates,
|
||||
ctx: SendAnyMap,
|
||||
) -> FxDashSet<RealNodeId> {
|
||||
S::update(nodes_updated, &mut self.tree, ctx)
|
||||
S::update_single_threaded(nodes_updated, &mut self.tree, ctx)
|
||||
}
|
||||
|
||||
/// Find all nodes that are listening for an event, sorted by there height in the dom progressing starting at the bottom and progressing up.
|
||||
@@ -396,6 +396,21 @@ impl<S: State> RealDom<S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: State + Sync> RealDom<S>
|
||||
where
|
||||
Tree<Node<S>>: Sync,
|
||||
{
|
||||
/// Update the state of the dom, after appling some mutations. This will keep the nodes in the dom up to date with their VNode counterparts.
|
||||
/// This will resolve the state in parallel
|
||||
pub fn update_state(
|
||||
&mut self,
|
||||
nodes_updated: DirtyNodeStates,
|
||||
ctx: SendAnyMap,
|
||||
) -> FxDashSet<RealNodeId> {
|
||||
S::update(nodes_updated, &mut self.tree, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: State> Deref for RealDom<S> {
|
||||
type Target = Tree<Node<S>>;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::cmp::Ordering;
|
||||
|
||||
use crate::node::Node;
|
||||
use crate::node_ref::{NodeMask, NodeView};
|
||||
use crate::passes::{resolve_passes, AnyPass, DirtyNodeStates};
|
||||
use crate::passes::{resolve_passes, resolve_passes_single_threaded, AnyPass, DirtyNodeStates};
|
||||
use crate::tree::TreeView;
|
||||
use crate::{FxDashSet, RealNodeId, SendAnyMap};
|
||||
|
||||
@@ -217,7 +217,7 @@ pub trait State: Default + Clone + 'static {
|
||||
const MASKS: &'static [NodeMask];
|
||||
|
||||
#[doc(hidden)]
|
||||
fn update<T: TreeView<Node<Self>>>(
|
||||
fn update<T: TreeView<Node<Self>> + Sync>(
|
||||
dirty: DirtyNodeStates,
|
||||
tree: &mut T,
|
||||
ctx: SendAnyMap,
|
||||
@@ -225,6 +225,16 @@ pub trait State: Default + Clone + 'static {
|
||||
let passes = Self::PASSES.iter().collect();
|
||||
resolve_passes(tree, dirty, passes, ctx)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn update_single_threaded<T: TreeView<Node<Self>>>(
|
||||
dirty: DirtyNodeStates,
|
||||
tree: &mut T,
|
||||
ctx: SendAnyMap,
|
||||
) -> FxDashSet<RealNodeId> {
|
||||
let passes = Self::PASSES.iter().collect();
|
||||
resolve_passes_single_threaded(tree, dirty, passes, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl ChildDepState for () {
|
||||
|
||||
@@ -564,8 +564,8 @@ impl<'a, T, Tr: TreeView<T>> SharedView<'a, T, Tr> {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, T, Tr: TreeView<T>> Send for SharedView<'a, T, Tr> {}
|
||||
unsafe impl<'a, T, Tr: TreeView<T>> Sync for SharedView<'a, T, Tr> {}
|
||||
unsafe impl<'a, T, Tr: TreeView<T> + Send> Send for SharedView<'a, T, Tr> {}
|
||||
unsafe impl<'a, T, Tr: TreeView<T> + Sync> Sync for SharedView<'a, T, Tr> {}
|
||||
impl<'a, T, Tr: TreeView<T>> Clone for SharedView<'a, T, Tr> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
|
||||
Reference in new issue
Block a user