feat: refactor out the hooks implementation
This commit is contained in:
1 parent
eb82051000
commit
1cc1679a6b
8 files changed
+162
-36
No files matched your search
Vendored
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"rust-analyzer.inlayHints.enable": true
|
"rust-analyzer.inlayHints.enable": false
|
||||||
}
|
}
|
||||||
@@ -42,6 +42,7 @@ futures = "0.3.15"
|
|||||||
|
|
||||||
|
|
||||||
async-std = { version="1.9.0", features=["attributes"] }
|
async-std = { version="1.9.0", features=["attributes"] }
|
||||||
|
appendlist = "1.4.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["serialize"]
|
default = ["serialize"]
|
||||||
|
|||||||
@@ -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!()
|
||||||
|
};
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,10 +13,11 @@ pub mod serialize;
|
|||||||
|
|
||||||
pub mod arena;
|
pub mod arena;
|
||||||
pub mod component;
|
pub mod component;
|
||||||
|
pub mod hooklist;
|
||||||
pub mod styles;
|
pub mod styles;
|
||||||
|
pub mod tasks;
|
||||||
pub mod util; // Logic for extending FC
|
pub mod util; // Logic for extending FC
|
||||||
|
// pub mod debug_renderer;
|
||||||
// pub mod debug_renderer;
|
|
||||||
pub mod diff;
|
pub mod diff;
|
||||||
|
|
||||||
pub mod error; // Error type we expose to the renderers
|
pub mod error; // Error type we expose to the renderers
|
||||||
|
|||||||
@@ -427,6 +427,7 @@ fn create_closure<'a, P: 'a>(
|
|||||||
let cx: Context<P> = Context {
|
let cx: Context<P> = Context {
|
||||||
props: safe_props,
|
props: safe_props,
|
||||||
scope: scp,
|
scope: scp,
|
||||||
|
tasks: todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let g = component(cx);
|
let g = component(cx);
|
||||||
|
|||||||
@@ -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 {}
|
||||||
@@ -19,11 +19,14 @@
|
|||||||
//! This module includes just the barebones for a complete VirtualDOM API.
|
//! This module includes just the barebones for a complete VirtualDOM API.
|
||||||
//! Additional functionality is defined in the respective files.
|
//! Additional functionality is defined in the respective files.
|
||||||
|
|
||||||
|
use crate::hooklist::HookList;
|
||||||
use crate::{arena::ScopeArena, innerlude::*};
|
use crate::{arena::ScopeArena, innerlude::*};
|
||||||
|
use appendlist::AppendList;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use slotmap::DefaultKey;
|
use slotmap::DefaultKey;
|
||||||
use slotmap::SlotMap;
|
use slotmap::SlotMap;
|
||||||
|
use std::marker::PhantomData;
|
||||||
use std::{
|
use std::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
cell::{Cell, RefCell},
|
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
|
// 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
|
// this closure moves root props (which is static) into this closure
|
||||||
let props = unsafe { &*(&root_props as *const _) };
|
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
|
// 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 caller: Weak<OpaqueComponent>,
|
||||||
|
|
||||||
pub hookidx: Cell<usize>,
|
|
||||||
|
|
||||||
// ==========================
|
// ==========================
|
||||||
// slightly unsafe stuff
|
// slightly unsafe stuff
|
||||||
// ==========================
|
// ==========================
|
||||||
@@ -391,8 +396,8 @@ pub struct Scope {
|
|||||||
// These two could be combined with "OwningRef" to remove unsafe usage
|
// These two could be combined with "OwningRef" to remove unsafe usage
|
||||||
// or we could dedicate a tiny bump arena just for them
|
// or we could dedicate a tiny bump arena just for them
|
||||||
// could also use ourborous
|
// could also use ourborous
|
||||||
hooks: RefCell<Vec<Hook>>,
|
hooks: HookList,
|
||||||
|
// hooks: RefCell<Vec<Hook>>,
|
||||||
pub(crate) listener_idx: Cell<usize>,
|
pub(crate) listener_idx: Cell<usize>,
|
||||||
|
|
||||||
// Unsafety:
|
// 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
|
// 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()>;
|
type EventChannel = Rc<dyn Fn()>;
|
||||||
|
|
||||||
impl Scope {
|
impl Scope {
|
||||||
@@ -470,7 +475,6 @@ impl Scope {
|
|||||||
hooks: Default::default(),
|
hooks: Default::default(),
|
||||||
shared_contexts: Default::default(),
|
shared_contexts: Default::default(),
|
||||||
listeners: Default::default(),
|
listeners: Default::default(),
|
||||||
hookidx: Default::default(),
|
|
||||||
descendents: Default::default(),
|
descendents: Default::default(),
|
||||||
suspended_tasks: Default::default(),
|
suspended_tasks: Default::default(),
|
||||||
}
|
}
|
||||||
@@ -510,7 +514,7 @@ impl Scope {
|
|||||||
// Remove all the outdated listeners
|
// Remove all the outdated listeners
|
||||||
self.listeners.borrow_mut().clear();
|
self.listeners.borrow_mut().clear();
|
||||||
|
|
||||||
self.hookidx.set(0);
|
unsafe { self.hooks.reset() };
|
||||||
self.listener_idx.set(0);
|
self.listener_idx.set(0);
|
||||||
|
|
||||||
let caller = self
|
let caller = self
|
||||||
@@ -611,7 +615,13 @@ impl Scope {
|
|||||||
pub struct Context<'src, T> {
|
pub struct Context<'src, T> {
|
||||||
pub props: &'src T,
|
pub props: &'src T,
|
||||||
pub scope: &'src Scope,
|
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> Copy for Context<'src, T> {}
|
||||||
impl<'src, T> Clone 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 {
|
Self {
|
||||||
props: self.props,
|
props: self.props,
|
||||||
scope: self.scope,
|
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
|
// TODO: add this to the "clean up" group for when the component is dropped
|
||||||
_cleanup: impl FnOnce(InternalHookState),
|
_cleanup: impl FnOnce(InternalHookState),
|
||||||
) -> Output {
|
) -> 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 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();
|
let new_state = initializer();
|
||||||
hooks.push(Box::pin(new_state));
|
self.scope.hooks.push(Box::new(new_state));
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.hookidx.set(idx + 1);
|
let state = self.scope.hooks.next::<InternalHookState>().expect(
|
||||||
|
|
||||||
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(
|
|
||||||
r###"
|
r###"
|
||||||
Unable to retrive the hook that was initialized in this index.
|
Unable to retrive the hook that was initialized in this index.
|
||||||
Consult the `rules of hooks` to understand how to use hooks properly.
|
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(state)
|
||||||
runner(unsafe { &mut *(internal_state as *mut _) })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This hook enables the ability to expose state to children further down the VirtualDOM Tree.
|
/// 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(
|
pub fn submit_task(
|
||||||
&self,
|
&self,
|
||||||
task: &'src mut Pin<Box<dyn Future<Output = ()> + 'static>>,
|
mut task: &'src mut Pin<Box<dyn Future<Output = ()> + 'static>>,
|
||||||
) -> TaskHandle {
|
) -> 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
|
// 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)]
|
#[derive(Clone)]
|
||||||
pub struct SuspendedContext {}
|
pub struct SuspendedContext {}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user