feat: refactor out the hooks implementation

This commit is contained in:
Jonathan Kelley 2021-07-09 01:26:15 -04:00
parent eb82051000
commit 1cc1679a6b
8 changed files with 163 additions and 37 deletions

View file

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

View file

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

View file

@ -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!()
};

View file

@ -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()
}
}

View file

@ -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

View file

@ -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);

View file

@ -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 {}

View file

@ -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 {}