From feab50f24a8ab82a2cf1d02af1c54d13dd539b01 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 9 Jul 2021 03:39:45 -0400 Subject: [PATCH] wip: some work --- packages/core/.vscode/settings.json | 2 +- packages/core/examples/async.rs | 2 + packages/core/src/arena.rs | 69 ------------- packages/core/src/events.rs | 29 ++++-- packages/core/src/scope.rs | 2 +- packages/core/src/tasks.rs | 150 ++++++++++++++++++++++++++-- packages/core/src/util.rs | 1 + packages/core/src/virtual_dom.rs | 24 ++--- 8 files changed, 171 insertions(+), 108 deletions(-) diff --git a/packages/core/.vscode/settings.json b/packages/core/.vscode/settings.json index 2b58ae251..34a811347 100644 --- a/packages/core/.vscode/settings.json +++ b/packages/core/.vscode/settings.json @@ -1,3 +1,3 @@ { - "rust-analyzer.inlayHints.enable": false + "rust-analyzer.inlayHints.enable": true } diff --git a/packages/core/examples/async.rs b/packages/core/examples/async.rs index c5a718075..a72a1366c 100644 --- a/packages/core/examples/async.rs +++ b/packages/core/examples/async.rs @@ -17,5 +17,7 @@ const App: FC<()> = |cx| { cx.submit_task(fut); + cx.submit_task(fut); + todo!() }; diff --git a/packages/core/src/arena.rs b/packages/core/src/arena.rs index aa68a6ad8..977abecfe 100644 --- a/packages/core/src/arena.rs +++ b/packages/core/src/arena.rs @@ -21,75 +21,6 @@ enum MutStatus { Mut, } -// impl ScopeArenaInner { -// pub fn new(arena: Arena) -> Self { -// ScopeArenaInner { -// arena: UnsafeCell::new(arena), -// locks: Default::default(), -// } -// } - -// /// THIS METHOD IS CURRENTLY UNSAFE -// /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS -// pub fn try_get(&self, idx: ScopeIdx) -> Result<&Scope> { -// let inner = unsafe { &*self.arena.get() }; -// let scope = inner.get(idx); -// scope.ok_or_else(|| Error::FatalInternal("Scope not found")) -// } - -// /// THIS METHOD IS CURRENTLY UNSAFE -// /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS -// pub fn try_get_mut(&self, idx: ScopeIdx) -> Result<&mut Scope> { -// let inner = unsafe { &mut *self.arena.get() }; -// let scope = inner.get_mut(idx); -// scope.ok_or_else(|| Error::FatalInternal("Scope not found")) -// } - -// fn inner(&self) -> &Arena { -// todo!() -// } - -// fn inner_mut(&mut self) -> &mut Arena { -// todo!() -// } - -// /// THIS METHOD IS CURRENTLY UNSAFE -// /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS -// pub fn with(&self, f: impl FnOnce(&mut Arena) -> T) -> Result { -// let inner = unsafe { &mut *self.arena.get() }; -// Ok(f(inner)) -// // todo!() -// } - -// pub fn with_scope<'b, O: 'static>( -// &'b self, -// id: ScopeIdx, -// f: impl FnOnce(&'b mut Scope) -> O, -// ) -> Result { -// todo!() -// } - -// // return a bumpframe with a lifetime attached to the arena borrow -// // this is useful for merging lifetimes -// pub fn with_scope_vnode<'b>( -// &self, -// id: ScopeIdx, -// f: impl FnOnce(&mut Scope) -> &VNode<'b>, -// ) -> Result<&VNode<'b>> { -// todo!() -// } - -// pub fn try_remove(&mut self, id: ScopeIdx) -> Result { -// let inner = unsafe { &mut *self.arena.get() }; -// inner -// .remove(id) -// .ok_or_else(|| Error::FatalInternal("Scope not found")) -// } - -// unsafe fn inner_unchecked<'s>() -> &'s mut Arena { -// todo!() -// } -// } impl ScopeArena { pub fn new(arena: ScopeMap) -> Self { ScopeArena(Rc::new(RefCell::new(ScopeArenaInner { diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index 9549a90e3..3a34117cf 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -10,19 +10,30 @@ use crate::{innerlude::ScopeIdx, virtual_dom::RealDomNode}; #[derive(Debug)] pub struct EventTrigger { - /// - pub component_id: ScopeIdx, + /// The originator of the event trigger + pub originator: ScopeIdx, - /// - pub real_node_id: RealDomNode, + /// The optional real node associated with the trigger + pub real_node_id: Option, - /// + /// The type of event pub event: VirtualEvent, - /// + /// The priority of the event pub priority: EventPriority, } +impl EventTrigger { + pub fn new_from_task(originator: ScopeIdx) -> Self { + Self { + originator, + event: VirtualEvent::FiberEvent, + priority: EventPriority::Low, + real_node_id: None, + } + } +} + /// Priority of Event Triggers. /// /// Internally, Dioxus will abort work that's taking too long if new, more important, work arrives. Unlike React, Dioxus @@ -61,12 +72,12 @@ impl EventTrigger { pub fn new( event: VirtualEvent, scope: ScopeIdx, - mounted_dom_id: RealDomNode, + mounted_dom_id: Option, priority: EventPriority, ) -> Self { Self { priority, - component_id: scope, + originator: scope, real_node_id: mounted_dom_id, event, } @@ -93,7 +104,7 @@ pub enum VirtualEvent { PointerEvent(on::PointerEvent), // Whenever a task is ready (complete) Dioxus produces this "FiberEvent" - FiberEvent { task_id: u16 }, + FiberEvent, // image event has conflicting method types // ImageEvent(event_data::ImageEvent), diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index e5ecb95ce..291cb1f4f 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -216,7 +216,7 @@ impl Scope { .iter() .find(|(domptr, _)| { let p = unsafe { &**domptr }; - p.get() == real_node_id + p.get() == real_node_id.expect("realdomnode not found, propery handling of true virtual events not managed") }) .expect(&format!( "Failed to find real node with ID {:?}", diff --git a/packages/core/src/tasks.rs b/packages/core/src/tasks.rs index 88d4ce8d9..33f7cfd0c 100644 --- a/packages/core/src/tasks.rs +++ b/packages/core/src/tasks.rs @@ -9,30 +9,158 @@ //! This is all pretty unsafe stuff. //! The major invariant here is that tasks that enter the queue may be invalidated during transitions. -use std::pin::Pin; +use std::{ + cell::Cell, + pin::Pin, + sync::{Arc, RwLock}, + task::{Context, Poll}, +}; use futures::{Future, Stream, StreamExt}; use slotmap::{DefaultKey, SlotMap}; -use crate::events::EventTrigger; +use crate::{events::EventTrigger, prelude::ScopeIdx}; pub struct TaskQueue { - slots: SlotMap, + slots: Arc>>, + submitter: Arc, } impl TaskQueue { - unsafe fn push_task(&mut self, task: Task) -> TaskHandle { - todo!() + pub fn new() -> Self { + let slots = Arc::new(RwLock::new(SlotMap::new())); + + let slots2 = slots.clone(); + + let submitter = Arc::new(move |task| { + let mut slots = slots2.write().unwrap(); + slots.insert(task); + }); + Self { slots, submitter } } - async fn next(&mut self) -> EventTrigger { - for (key, task) in self.slots.iter_mut() { - let ptr = task.0; - } - todo!() + fn push_task(&mut self, task: DTask) -> TaskHandle { + let key = self.slots.write().unwrap().insert(task); + TaskHandle {} + } + + fn is_empty(&self) -> bool { + self.slots.read().unwrap().is_empty() + } + fn len(&self) -> usize { + self.slots.read().unwrap().len() } } -struct Task(*mut Pin>>); +impl Stream for TaskQueue { + type Item = EventTrigger; + + /// We can never be finished polling + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + // let yield_every = self.len(); + // let mut polled = 0; + + let mut slots = self.slots.write().unwrap(); + for (key, slot) in slots.iter_mut() { + if slot.dead.get() { + continue; + } + let r = slot.fut; + let mut fut = unsafe { &mut *r }; + // use futures::{future::Future, poll, FutureExt}; + + let f2 = fut.as_mut(); + let w = cx.waker(); + let mut cx = Context::from_waker(&w); + + // Pin::new_unchecked(pointer) + // use std::future::Future; + match f2.poll(&mut cx) { + Poll::Ready(_) => { + let trigger = EventTrigger::new_from_task(slot.originator); + slot.dead.set(true); + return Poll::Ready(Some(trigger)); + } + Poll::Pending => continue, + } + } + + // we tried polling every active task. + // give up and relinquish controlto the parent + + // We have polled a large number of futures in a row without yielding. + // To ensure we do not starve other tasks waiting on the executor, + // we yield here, but immediately wake ourselves up to continue. + // cx.waker().wake_by_ref(); + return Poll::Pending; + } +} struct TaskHandle {} + +pub struct DTask { + fut: *mut Pin>>, + originator: ScopeIdx, + dead: Cell, +} +impl DTask { + pub fn new(fut: &mut Pin>>, originator: ScopeIdx) -> Self { + Self { + fut, + originator, + dead: Cell::new(false), + } + } + fn debug_new(fut: &mut Pin>>) -> Self { + let originator = ScopeIdx::default(); + Self { + fut, + originator, + dead: Cell::new(false), + } + } +} + +mod tests { + use std::time::Duration; + + use super::*; + use bumpalo::Bump; + + #[async_std::test] + async fn example() { + let bump = Bump::new(); + type RawTask = Pin>>; + // build the three + let f1 = bump.alloc(Box::pin(async { + // + async_std::task::sleep(Duration::from_secs(3)).await; + println!("3 sec") + }) as RawTask); + + let f2 = bump.alloc(Box::pin(async { + // + async_std::task::sleep(Duration::from_secs(2)).await; + println!("2 sec") + }) as RawTask); + + let f3 = bump.alloc(Box::pin(async { + // + async_std::task::sleep(Duration::from_secs(1)).await; + println!("1 sec"); + }) as RawTask); + + let mut queue = TaskQueue::new(); + queue.push_task(DTask::debug_new(f1)); + queue.push_task(DTask::debug_new(f2)); + queue.push_task(DTask::debug_new(f3)); + + while !queue.is_empty() { + let next = queue.next().await; + println!("Event received {:#?}", next); + } + } +} diff --git a/packages/core/src/util.rs b/packages/core/src/util.rs index e2600150c..8135dd1bc 100644 --- a/packages/core/src/util.rs +++ b/packages/core/src/util.rs @@ -81,6 +81,7 @@ impl<'a> RealDom<'a> for DebugDom { realnode: RealDomNode, ) { } + fn remove_event_listener(&mut self, event: &str) {} fn set_text(&mut self, text: &str) {} diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 1e08e638e..947e00729 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -19,24 +19,11 @@ //! This module includes just the barebones for a complete VirtualDOM API. //! Additional functionality is defined in the respective files. -use crate::hooklist::HookList; +use crate::tasks::TaskQueue; use crate::{arena::ScopeArena, innerlude::*}; -use appendlist::AppendList; -use bumpalo::Bump; -use futures::FutureExt; use slotmap::DefaultKey; use slotmap::SlotMap; -use std::marker::PhantomData; -use std::{ - any::{Any, TypeId}, - cell::{Cell, RefCell}, - collections::{HashMap, HashSet, VecDeque}, - fmt::Debug, - future::Future, - ops::Deref, - pin::Pin, - rc::{Rc, Weak}, -}; +use std::{any::TypeId, fmt::Debug, rc::Rc}; pub type ScopeIdx = DefaultKey; @@ -57,10 +44,12 @@ pub struct VirtualDom { /// All components dump their updates into a queue to be processed pub(crate) event_queue: EventQueue, + pub(crate) tasks: TaskQueue, + /// a strong allocation to the "caller" for the original component and its props #[doc(hidden)] _root_caller: Rc, - // _root_caller: Rc>, + /// Type of the original cx. This is stored as TypeId so VirtualDom does not need to be generic. /// /// Whenver props need to be updated, an Error will be thrown if the new props do not @@ -194,6 +183,7 @@ impl VirtualDom { base_scope, event_queue, components, + tasks: TaskQueue::new(), _root_prop_type: TypeId::of::

(), } } @@ -273,7 +263,7 @@ impl VirtualDom { realdom: &'_ mut Dom, trigger: EventTrigger, ) -> Result<()> { - let id = trigger.component_id.clone(); + let id = trigger.originator.clone(); self.components.try_get_mut(id)?.call_listener(trigger)?;