Jonathan Kelley 4 lat temu
rodzic
commit
feab50f24a

+ 1 - 1
packages/core/.vscode/settings.json

@@ -1,3 +1,3 @@
 {
-    "rust-analyzer.inlayHints.enable": false
+    "rust-analyzer.inlayHints.enable": true
 }

+ 2 - 0
packages/core/examples/async.rs

@@ -17,5 +17,7 @@ const App: FC<()> = |cx| {
 
     cx.submit_task(fut);
 
+    cx.submit_task(fut);
+
     todo!()
 };

+ 0 - 69
packages/core/src/arena.rs

@@ -21,75 +21,6 @@ enum MutStatus {
     Mut,
 }
 
-// impl ScopeArenaInner {
-//     pub fn new(arena: Arena<Scope>) -> 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<Scope> {
-//         todo!()
-//     }
-
-//     fn inner_mut(&mut self) -> &mut Arena<Scope> {
-//         todo!()
-//     }
-
-//     /// THIS METHOD IS CURRENTLY UNSAFE
-//     /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
-//     pub fn with<T>(&self, f: impl FnOnce(&mut Arena<Scope>) -> T) -> Result<T> {
-//         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<O> {
-//         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<Scope> {
-//         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<Scope> {
-//         todo!()
-//     }
-// }
 impl ScopeArena {
     pub fn new(arena: ScopeMap) -> Self {
         ScopeArena(Rc::new(RefCell::new(ScopeArenaInner {

+ 20 - 9
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<RealDomNode>,
 
-    ///
+    /// 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<RealDomNode>,
         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),

+ 1 - 1
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 {:?}",

+ 139 - 11
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<DefaultKey, Task>,
+    slots: Arc<RwLock<SlotMap<DefaultKey, DTask>>>,
+    submitter: Arc<dyn Fn(DTask)>,
 }
 
 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<Box<dyn Future<Output = ()>>>);
+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<Option<Self::Item>> {
+        // 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<Box<dyn Future<Output = ()>>>,
+    originator: ScopeIdx,
+    dead: Cell<bool>,
+}
+impl DTask {
+    pub fn new(fut: &mut Pin<Box<dyn Future<Output = ()>>>, originator: ScopeIdx) -> Self {
+        Self {
+            fut,
+            originator,
+            dead: Cell::new(false),
+        }
+    }
+    fn debug_new(fut: &mut Pin<Box<dyn Future<Output = ()>>>) -> 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<Box<dyn Future<Output = ()>>>;
+        // 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);
+        }
+    }
+}

+ 1 - 0
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) {}

+ 7 - 17
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<OpaqueComponent>,
-    // _root_caller: Rc<OpaqueComponent<'static>>,
+
     /// 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::<P>(),
         }
     }
@@ -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)?;