浏览代码

feat: refactor out the hooks implementation

Jonathan Kelley 4 年之前
父节点
当前提交
1cc1679a6b

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

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

+ 1 - 0
packages/core/Cargo.toml

@@ -42,6 +42,7 @@ futures = "0.3.15"
 
 
 async-std = { version="1.9.0", features=["attributes"] }
+appendlist = "1.4.0"
 
 [features]
 default = ["serialize"]

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

@@ -0,0 +1,21 @@
+use std::pin::Pin;
+
+use dioxus_core::prelude::*;
+use futures::Future;
+
+fn main() {}
+
+const App: FC<()> = |cx| {
+    let mut fut = cx.use_hook(
+        || {
+            //
+            Box::pin(async { loop {} }) as Pin<Box<dyn Future<Output = ()>>>
+        },
+        |f| f,
+        |_| {},
+    );
+
+    cx.submit_task(fut);
+
+    todo!()
+};

+ 65 - 0
packages/core/src/hooklist.rs

@@ -0,0 +1,65 @@
+use std::{
+    any::Any,
+    cell::{Cell, UnsafeCell},
+};
+
+pub struct HookList {
+    vals: appendlist::AppendList<InnerHook<Box<dyn Any>>>,
+    idx: Cell<usize>,
+}
+
+impl Default for HookList {
+    fn default() -> Self {
+        Self {
+            vals: Default::default(),
+            idx: Cell::new(0),
+        }
+    }
+}
+
+struct InnerHook<T> {
+    cell: UnsafeCell<T>,
+}
+impl<T> InnerHook<T> {
+    fn new(new: T) -> Self {
+        Self {
+            cell: UnsafeCell::new(new),
+        }
+    }
+}
+
+impl HookList {
+    pub(crate) fn push<T: 'static>(&self, new: T) {
+        self.vals.push(InnerHook::new(Box::new(new)))
+    }
+
+    pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
+        self.vals.get(self.idx.get()).and_then(|inn| {
+            self.idx.set(self.idx.get() + 1);
+            let mut raw_box = unsafe { &mut *inn.cell.get() };
+            raw_box.downcast_mut::<T>()
+        })
+    }
+
+    /// 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
+    pub(crate) unsafe fn reset(&mut self) {
+        self.idx.set(0);
+    }
+
+    #[inline]
+    pub(crate) fn len(&self) -> usize {
+        self.vals.len()
+    }
+
+    #[inline]
+    pub(crate) fn cur_idx(&self) -> usize {
+        self.idx.get()
+    }
+
+    #[inline]
+    pub(crate) fn is_finished(&self) -> bool {
+        self.idx.get() == self.vals.len()
+    }
+}

+ 3 - 2
packages/core/src/lib.rs

@@ -13,10 +13,11 @@ pub mod serialize;
 
 pub mod arena;
 pub mod component;
+pub mod hooklist;
 pub mod styles;
+pub mod tasks;
 pub mod util; // Logic for extending FC
-
-// pub mod debug_renderer;
+              // pub mod debug_renderer;
 pub mod diff;
 
 pub mod error; // Error type we expose to the renderers

+ 1 - 0
packages/core/src/nodes.rs

@@ -427,6 +427,7 @@ fn create_closure<'a, P: 'a>(
         let cx: Context<P> = Context {
             props: safe_props,
             scope: scp,
+            tasks: todo!(),
         };
 
         let g = component(cx);

+ 38 - 0
packages/core/src/tasks.rs

@@ -0,0 +1,38 @@
+//! The TaskQueue serves as a centralized async store for all tasks in Dioxus.
+//! When a component renders, it may submit an async task to the queue.
+//!
+//! Then the task complete, it is emitted from the virtual dom in the event loop, which is then fed back into the virtualdom
+//! as an event trigger.
+//!
+//! When a component is scheduled to re-render, the awaing task must be dumped from the queue.
+//!
+//! 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 futures::{Future, Stream, StreamExt};
+use slotmap::{DefaultKey, SlotMap};
+
+use crate::events::EventTrigger;
+
+pub struct TaskQueue {
+    slots: SlotMap<DefaultKey, Task>,
+}
+
+impl TaskQueue {
+    unsafe fn push_task(&mut self, task: Task) -> TaskHandle {
+        todo!()
+    }
+
+    async fn next(&mut self) -> EventTrigger {
+        for (key, task) in self.slots.iter_mut() {
+            let ptr = task.0;
+        }
+        todo!()
+    }
+}
+
+struct Task(*mut Pin<Box<dyn Future<Output = ()>>>);
+
+struct TaskHandle {}

+ 32 - 33
packages/core/src/virtual_dom.rs

@@ -19,11 +19,14 @@
 //! 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::{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},
@@ -159,7 +162,11 @@ impl VirtualDom {
             // the lifetime of this closure is just as long as the lifetime on the scope reference
             // this closure moves root props (which is static) into this closure
             let props = unsafe { &*(&root_props as *const _) };
-            root(Context { props, scope })
+            root(Context {
+                props,
+                scope,
+                tasks: todo!(),
+            })
         });
 
         // Create a weak reference to the OpaqueComponent for the root scope to use as its render function
@@ -379,8 +386,6 @@ pub struct Scope {
 
     pub caller: Weak<OpaqueComponent>,
 
-    pub hookidx: Cell<usize>,
-
     // ==========================
     // slightly unsafe stuff
     // ==========================
@@ -391,8 +396,8 @@ pub struct Scope {
     // These two could be combined with "OwningRef" to remove unsafe usage
     // or we could dedicate a tiny bump arena just for them
     // could also use ourborous
-    hooks: RefCell<Vec<Hook>>,
-
+    hooks: HookList,
+    // hooks: RefCell<Vec<Hook>>,
     pub(crate) listener_idx: Cell<usize>,
 
     // Unsafety:
@@ -410,7 +415,7 @@ pub struct Scope {
 }
 
 // We need to pin the hook so it doesn't move as we initialize the list of hooks
-type Hook = Pin<Box<dyn std::any::Any>>;
+type Hook = Box<dyn std::any::Any>;
 type EventChannel = Rc<dyn Fn()>;
 
 impl Scope {
@@ -470,7 +475,6 @@ impl Scope {
             hooks: Default::default(),
             shared_contexts: Default::default(),
             listeners: Default::default(),
-            hookidx: Default::default(),
             descendents: Default::default(),
             suspended_tasks: Default::default(),
         }
@@ -510,7 +514,7 @@ impl Scope {
         // Remove all the outdated listeners
         self.listeners.borrow_mut().clear();
 
-        self.hookidx.set(0);
+        unsafe { self.hooks.reset() };
         self.listener_idx.set(0);
 
         let caller = self
@@ -611,7 +615,13 @@ impl Scope {
 pub struct Context<'src, T> {
     pub props: &'src T,
     pub scope: &'src Scope,
+    pub tasks: &'src AppendList<&'src mut DTask>,
+    // pub task: &'src RefCell<Vec<&'src mut >>,
 }
+pub type DTask = Pin<Box<dyn Future<Output = ()>>>;
+// // pub task: &'src RefCell<Option<&'src mut Pin<Box<dyn Future<Output = ()>>>>>,
+// pub task: Option<()>, // pub task: &'src RefCell<Option<&'src mut Pin<Box<dyn Future<Output = ()>>>>>,
+// pub task: &'src RefCell<Option<&'src mut Pin<Box<dyn Future<Output = ()>>>>>
 
 impl<'src, T> Copy for Context<'src, T> {}
 impl<'src, T> Clone for Context<'src, T> {
@@ -619,6 +629,7 @@ impl<'src, T> Clone for Context<'src, T> {
         Self {
             props: self.props,
             scope: self.scope,
+            tasks: todo!(),
         }
     }
 }
@@ -712,29 +723,13 @@ impl<'src, P> Context<'src, P> {
         // TODO: add this to the "clean up" group for when the component is dropped
         _cleanup: impl FnOnce(InternalHookState),
     ) -> Output {
-        let scope = self.scope;
-
-        let idx = scope.hookidx.get();
-
-        // Grab out the hook list
-        let mut hooks = scope.hooks.borrow_mut();
-
         // If the idx is the same as the hook length, then we need to add the current hook
-        if idx >= hooks.len() {
+        if self.scope.hooks.is_finished() {
             let new_state = initializer();
-            hooks.push(Box::pin(new_state));
+            self.scope.hooks.push(Box::new(new_state));
         }
 
-        scope.hookidx.set(idx + 1);
-
-        let stable_ref = hooks
-            .get_mut(idx)
-            .expect("Should not fail, idx is validated")
-            .as_mut();
-
-        let pinned_state = unsafe { Pin::get_unchecked_mut(stable_ref) };
-
-        let internal_state = pinned_state.downcast_mut::<InternalHookState>().expect(
+        let state = self.scope.hooks.next::<InternalHookState>().expect(
             r###"
 Unable to retrive the hook that was initialized in this index.
 Consult the `rules of hooks` to understand how to use hooks properly.
@@ -744,8 +739,7 @@ Any function prefixed with "use" should not be called conditionally.
             "###,
         );
 
-        // We extend the lifetime of the internal state
-        runner(unsafe { &mut *(internal_state as *mut _) })
+        runner(state)
     }
 
     /// This hook enables the ability to expose state to children further down the VirtualDOM Tree.
@@ -882,16 +876,21 @@ Any function prefixed with "use" should not be called conditionally.
     ///
     pub fn submit_task(
         &self,
-        task: &'src mut Pin<Box<dyn Future<Output = ()> + 'static>>,
+        mut task: &'src mut Pin<Box<dyn Future<Output = ()> + 'static>>,
     ) -> TaskHandle {
+        self.tasks.push(task);
+        // let mut g = self.task.borrow_mut();
+        // *g = Some(task);
         // the pointer to the task is stable - we guarantee stability of all &'src references
-        let task_ptr = task as *mut _;
+        // let task_ptr = task as *mut _;
 
-        TaskHandle {}
+        TaskHandle { _p: PhantomData {} }
     }
 }
 
-pub struct TaskHandle {}
+pub struct TaskHandle<'src> {
+    _p: PhantomData<&'src ()>,
+}
 #[derive(Clone)]
 pub struct SuspendedContext {}