feat: suspense!
This commit is contained in:
1 parent
e6c53803a6
commit
203935834d
13 files changed
+219
-205
No files matched your search
@@ -188,7 +188,6 @@ impl VirtualDom {
|
|||||||
|
|
||||||
let render_ret = self.run_scope(id);
|
let render_ret = self.run_scope(id);
|
||||||
|
|
||||||
// shut up about lifetimes please, I know what I'm doing
|
|
||||||
let render_ret: &mut RenderReturn = unsafe { std::mem::transmute(render_ret) };
|
let render_ret: &mut RenderReturn = unsafe { std::mem::transmute(render_ret) };
|
||||||
|
|
||||||
match render_ret {
|
match render_ret {
|
||||||
@@ -204,53 +203,22 @@ impl VirtualDom {
|
|||||||
RenderReturn::Sync(None) => {
|
RenderReturn::Sync(None) => {
|
||||||
let new_id = self.next_element(template);
|
let new_id = self.next_element(template);
|
||||||
placeholder.set(Some(new_id));
|
placeholder.set(Some(new_id));
|
||||||
|
self.scopes[id.0].placeholder.set(Some(new_id));
|
||||||
mutations.push(AssignId {
|
mutations.push(AssignId {
|
||||||
id: new_id,
|
id: new_id,
|
||||||
path: &template.template.node_paths[idx][1..],
|
path: &template.template.node_paths[idx][1..],
|
||||||
});
|
});
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderReturn::Async(fut) => {
|
RenderReturn::Async(fut) => {
|
||||||
let new_id = self.next_element(template);
|
let new_id = self.next_element(template);
|
||||||
|
|
||||||
let scope = self.scope_stack.last().unwrap();
|
|
||||||
let scope = &self.scopes[scope.0];
|
|
||||||
|
|
||||||
let boundary = scope.consume_context::<SuspenseContext>().unwrap();
|
|
||||||
|
|
||||||
// try to poll the future once - many times it will be ready immediately or require little to no work
|
|
||||||
|
|
||||||
todo!();
|
|
||||||
|
|
||||||
// // move up the tree looking for the first suspense boundary
|
|
||||||
// // our current component can not be a suspense boundary, so we skip it
|
|
||||||
// for scope_id in self.scope_stack.iter().rev().skip(1) {
|
|
||||||
// if let Some(fiber) = &mut scope.suspense_boundary {
|
|
||||||
// // save the fiber leaf onto the fiber itself
|
|
||||||
// let detached: &mut FiberLeaf<'static> =
|
|
||||||
// unsafe { std::mem::transmute(fut) };
|
|
||||||
|
|
||||||
// // And save the fiber leaf using the placeholder node
|
|
||||||
// // this way, when we resume the fiber, we just need to "pick up placeholder"
|
|
||||||
// fiber.futures.insert(
|
|
||||||
// LeafLocation {
|
|
||||||
// element: new_id,
|
|
||||||
// scope: *scope_id,
|
|
||||||
// },
|
|
||||||
// detached,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// self.suspended_scopes.insert(*scope_id);
|
|
||||||
// break;
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
placeholder.set(Some(new_id));
|
placeholder.set(Some(new_id));
|
||||||
|
self.scopes[id.0].placeholder.set(Some(new_id));
|
||||||
mutations.push(AssignId {
|
mutations.push(AssignId {
|
||||||
id: new_id,
|
id: new_id,
|
||||||
path: &template.template.node_paths[idx][1..],
|
path: &template.template.node_paths[idx][1..],
|
||||||
});
|
});
|
||||||
|
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ where
|
|||||||
fn as_return(self, cx: &'a ScopeState) -> RenderReturn<'a> {
|
fn as_return(self, cx: &'a ScopeState) -> RenderReturn<'a> {
|
||||||
let f: &mut dyn Future<Output = Element<'a>> = cx.bump().alloc(self);
|
let f: &mut dyn Future<Output = Element<'a>> = cx.bump().alloc(self);
|
||||||
let boxed = unsafe { BumpBox::from_raw(f) };
|
let boxed = unsafe { BumpBox::from_raw(f) };
|
||||||
let pined: Pin<BumpBox<_>> = boxed.into();
|
let pined: BumpBox<_> = boxed.into();
|
||||||
RenderReturn::Async(pined)
|
RenderReturn::Async(pined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,7 +133,7 @@ fn takes_it() {
|
|||||||
|
|
||||||
pub enum RenderReturn<'a> {
|
pub enum RenderReturn<'a> {
|
||||||
Sync(Element<'a>),
|
Sync(Element<'a>),
|
||||||
Async(Pin<BumpBox<'a, dyn Future<Output = Element<'a>> + 'a>>),
|
Async(BumpBox<'a, dyn Future<Output = Element<'a>> + 'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type FiberLeaf<'a> = Pin<BumpBox<'a, dyn Future<Output = Element<'a>> + 'a>>;
|
pub type FiberLeaf<'a> = Pin<BumpBox<'a, dyn Future<Output = Element<'a>> + 'a>>;
|
||||||
|
|||||||
+27
-31
@@ -47,6 +47,27 @@ impl<'a> VNode<'a> {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn single_text(
|
||||||
|
cx: &'a ScopeState,
|
||||||
|
text: &'static [TemplateNode<'static>],
|
||||||
|
id: &'static str,
|
||||||
|
) -> Option<Self> {
|
||||||
|
Some(VNode {
|
||||||
|
node_id: Cell::new(ElementId(0)),
|
||||||
|
key: None,
|
||||||
|
parent: None,
|
||||||
|
root_ids: &[],
|
||||||
|
dynamic_nodes: &[],
|
||||||
|
dynamic_attrs: &[],
|
||||||
|
template: Template {
|
||||||
|
id,
|
||||||
|
roots: text,
|
||||||
|
node_paths: &[&[0]],
|
||||||
|
attr_paths: &[],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
@@ -62,25 +83,13 @@ impl<'a> std::hash::Hash for Template<'a> {
|
|||||||
self.id.hash(state);
|
self.id.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Eq for Template<'_> {}
|
||||||
impl PartialEq for Template<'_> {
|
impl PartialEq for Template<'_> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.id == other.id
|
self.id == other.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for Template<'_> {}
|
|
||||||
impl PartialOrd for Template<'_> {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
||||||
self.id.partial_cmp(other.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Ord for Template<'_> {
|
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
||||||
self.id.cmp(other.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A weird-ish variant of VNodes with way more limited types
|
/// A weird-ish variant of VNodes with way more limited types
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum TemplateNode<'a> {
|
pub enum TemplateNode<'a> {
|
||||||
@@ -96,24 +105,17 @@ pub enum TemplateNode<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub enum DynamicNode<'a> {
|
pub enum DynamicNode<'a> {
|
||||||
// Anything declared in component form
|
|
||||||
// IE in caps or with underscores
|
|
||||||
Component {
|
Component {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
static_props: bool,
|
static_props: bool,
|
||||||
props: Cell<*mut dyn AnyProps<'a>>,
|
props: Cell<*mut dyn AnyProps<'a>>,
|
||||||
placeholder: Cell<Option<ElementId>>,
|
placeholder: Cell<Option<ElementId>>,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Comes in with string interpolation or from format_args, include_str, etc
|
|
||||||
Text {
|
Text {
|
||||||
id: Cell<ElementId>,
|
id: Cell<ElementId>,
|
||||||
value: &'a str,
|
value: &'a str,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Anything that's coming in as an iterator
|
|
||||||
Fragment(&'a [VNode<'a>]),
|
Fragment(&'a [VNode<'a>]),
|
||||||
|
|
||||||
Placeholder(Cell<ElementId>),
|
Placeholder(Cell<ElementId>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,8 +155,8 @@ impl<'a> std::fmt::Debug for AttributeValue<'a> {
|
|||||||
Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
|
Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
|
||||||
Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
|
Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
|
||||||
Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
|
Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
|
||||||
Self::Listener(arg0) => f.debug_tuple("Listener").finish(),
|
Self::Listener(_) => f.debug_tuple("Listener").finish(),
|
||||||
Self::Any(arg0) => f.debug_tuple("Any").finish(),
|
Self::Any(_) => f.debug_tuple("Any").finish(),
|
||||||
Self::None => write!(f, "None"),
|
Self::None => write!(f, "None"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,7 +169,7 @@ impl<'a> PartialEq for AttributeValue<'a> {
|
|||||||
(Self::Float(l0), Self::Float(r0)) => l0 == r0,
|
(Self::Float(l0), Self::Float(r0)) => l0 == r0,
|
||||||
(Self::Int(l0), Self::Int(r0)) => l0 == r0,
|
(Self::Int(l0), Self::Int(r0)) => l0 == r0,
|
||||||
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
|
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
|
||||||
(Self::Listener(l0), Self::Listener(r0)) => true,
|
(Self::Listener(_), Self::Listener(_)) => true,
|
||||||
(Self::Any(l0), Self::Any(r0)) => l0.any_cmp(*r0),
|
(Self::Any(l0), Self::Any(r0)) => l0.any_cmp(*r0),
|
||||||
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
|
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
|
||||||
}
|
}
|
||||||
@@ -186,20 +188,14 @@ impl<'a> AttributeValue<'a> {
|
|||||||
_ => return false,
|
_ => return false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_listener(&self) -> bool {
|
|
||||||
matches!(self, AttributeValue::Listener(_))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AnyValue {
|
pub trait AnyValue {
|
||||||
fn any_cmp(&self, other: &dyn AnyValue) -> bool;
|
fn any_cmp(&self, other: &dyn AnyValue) -> bool;
|
||||||
fn our_typeid(&self) -> TypeId;
|
fn our_typeid(&self) -> TypeId;
|
||||||
}
|
}
|
||||||
impl<T> AnyValue for T
|
|
||||||
where
|
impl<T: PartialEq + Any> AnyValue for T {
|
||||||
T: PartialEq + Any,
|
|
||||||
{
|
|
||||||
fn any_cmp(&self, other: &dyn AnyValue) -> bool {
|
fn any_cmp(&self, other: &dyn AnyValue) -> bool {
|
||||||
if self.type_id() != other.our_typeid() {
|
if self.type_id() != other.our_typeid() {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
|
use slab::Slab;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::ScopeId;
|
use crate::ScopeId;
|
||||||
|
|
||||||
|
mod bumpslab;
|
||||||
mod handle;
|
mod handle;
|
||||||
mod suspense;
|
mod suspense;
|
||||||
mod task;
|
mod task;
|
||||||
@@ -9,9 +11,9 @@ mod wait;
|
|||||||
mod waker;
|
mod waker;
|
||||||
|
|
||||||
pub use handle::*;
|
pub use handle::*;
|
||||||
use slab::Slab;
|
|
||||||
pub use suspense::*;
|
pub use suspense::*;
|
||||||
pub use task::*;
|
pub use task::*;
|
||||||
|
pub use waker::RcWake;
|
||||||
|
|
||||||
/// The type of message that can be sent to the scheduler.
|
/// The type of message that can be sent to the scheduler.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
use std::{collections::HashSet, rc::Rc};
|
use std::{
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
collections::HashSet,
|
||||||
|
pin::Pin,
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
use futures_task::{RawWaker, RawWakerVTable, Waker};
|
use super::{waker::RcWake, SchedulerMsg};
|
||||||
|
use crate::{innerlude::Mutation, Element, ScopeId};
|
||||||
|
use futures_task::Waker;
|
||||||
use futures_util::Future;
|
use futures_util::Future;
|
||||||
|
|
||||||
use crate::{innerlude::Mutation, Element, Scope, ScopeId};
|
|
||||||
|
|
||||||
use super::SchedulerMsg;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub struct SuspenseId(pub usize);
|
pub struct SuspenseId(pub usize);
|
||||||
|
|
||||||
pub type SuspenseContext = Rc<SuspenseBoundary>;
|
pub type SuspenseContext = Rc<RefCell<SuspenseBoundary>>;
|
||||||
/// Essentially a fiber in React
|
/// Essentially a fiber in React
|
||||||
pub struct SuspenseBoundary {
|
pub struct SuspenseBoundary {
|
||||||
pub id: ScopeId,
|
pub id: ScopeId,
|
||||||
waiting_on: HashSet<SuspenseId>,
|
pub waiting_on: HashSet<SuspenseId>,
|
||||||
mutations: Vec<Mutation<'static>>,
|
pub mutations: Vec<Mutation<'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SuspenseBoundary {
|
impl SuspenseBoundary {
|
||||||
@@ -28,44 +31,24 @@ impl SuspenseBoundary {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
|
|
||||||
many times the future will be ready every time it's polled, so we can spin on it until it doesnt wake us up immediately
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
pub struct SuspenseLeaf {
|
pub struct SuspenseLeaf {
|
||||||
pub id: SuspenseId,
|
pub id: SuspenseId,
|
||||||
pub scope: ScopeId,
|
pub scope_id: ScopeId,
|
||||||
pub boundary: ScopeId,
|
pub boundary: ScopeId,
|
||||||
pub tx: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
|
pub tx: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
|
||||||
|
pub notified: Cell<bool>,
|
||||||
|
|
||||||
pub task: *mut dyn Future<Output = Element<'static>>,
|
pub task: *mut dyn Future<Output = Element<'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_suspense_waker(task: &SuspenseLeaf) -> Waker {
|
impl RcWake for SuspenseLeaf {
|
||||||
let raw = RawWaker::new(task as *const SuspenseLeaf as *const _, task_vtable());
|
fn wake_by_ref(arc_self: &Rc<Self>) {
|
||||||
unsafe { Waker::from_raw(raw) }
|
if arc_self.notified.get() {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
fn task_vtable() -> &'static RawWakerVTable {
|
arc_self.notified.set(true);
|
||||||
&RawWakerVTable::new(clone, wake, wake_by_ref, drop_task)
|
_ = arc_self
|
||||||
}
|
.tx
|
||||||
|
.unbounded_send(SchedulerMsg::SuspenseNotified(arc_self.id));
|
||||||
unsafe fn clone(data: *const ()) -> RawWaker {
|
}
|
||||||
RawWaker::new(data as *const (), task_vtable())
|
|
||||||
}
|
|
||||||
unsafe fn wake(data: *const ()) {
|
|
||||||
wake_by_ref(data);
|
|
||||||
}
|
|
||||||
unsafe fn wake_by_ref(data: *const ()) {
|
|
||||||
let task = &*(data as *const SuspenseLeaf);
|
|
||||||
task.tx
|
|
||||||
.unbounded_send(SchedulerMsg::SuspenseNotified(task.id))
|
|
||||||
.expect("Scheduler should exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn drop_task(_data: *const ()) {
|
|
||||||
// doesnt do anything
|
|
||||||
}
|
}
|
||||||
@@ -61,11 +61,6 @@ impl HandleInner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_task_waker(task: Rc<LocalTask>) -> Waker {
|
|
||||||
let ptr = Rc::into_raw(task).cast::<()>();
|
|
||||||
super::waker::make_rc_waker(task)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RcWake for LocalTask {
|
impl RcWake for LocalTask {
|
||||||
fn wake_by_ref(arc_self: &Rc<Self>) {
|
fn wake_by_ref(arc_self: &Rc<Self>) {
|
||||||
_ = arc_self
|
_ = arc_self
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
use futures_task::Context;
|
use futures_task::Context;
|
||||||
use futures_util::{FutureExt, StreamExt};
|
use futures_util::{FutureExt, StreamExt};
|
||||||
|
|
||||||
use crate::{innerlude::make_task_waker, VirtualDom};
|
use crate::{
|
||||||
|
factory::RenderReturn,
|
||||||
|
innerlude::{Mutation, SuspenseContext},
|
||||||
|
VNode, VirtualDom,
|
||||||
|
};
|
||||||
|
|
||||||
use super::SchedulerMsg;
|
use super::{waker::RcWake, SchedulerMsg, SuspenseLeaf};
|
||||||
|
|
||||||
impl VirtualDom {
|
impl VirtualDom {
|
||||||
/// Wait for futures internal to the virtualdom
|
/// Wait for futures internal to the virtualdom
|
||||||
@@ -22,7 +26,7 @@ impl VirtualDom {
|
|||||||
|
|
||||||
// attach the waker to itself
|
// attach the waker to itself
|
||||||
// todo: don't make a new waker every time, make it once and then just clone it
|
// todo: don't make a new waker every time, make it once and then just clone it
|
||||||
let waker = make_task_waker(local_task.clone());
|
let waker = local_task.waker();
|
||||||
let mut cx = Context::from_waker(&waker);
|
let mut cx = Context::from_waker(&waker);
|
||||||
|
|
||||||
// safety: the waker owns its task and everythig is single threaded
|
// safety: the waker owns its task and everythig is single threaded
|
||||||
@@ -33,7 +37,57 @@ impl VirtualDom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SchedulerMsg::SuspenseNotified(_) => todo!(),
|
SchedulerMsg::SuspenseNotified(id) => {
|
||||||
|
let leaf = self
|
||||||
|
.scheduler
|
||||||
|
.handle
|
||||||
|
.leaves
|
||||||
|
.borrow_mut()
|
||||||
|
.get(id.0)
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
let scope_id = leaf.scope_id;
|
||||||
|
|
||||||
|
// todo: cache the waker
|
||||||
|
let waker = leaf.waker();
|
||||||
|
let mut cx = Context::from_waker(&waker);
|
||||||
|
|
||||||
|
let fut = unsafe { &mut *leaf.task };
|
||||||
|
|
||||||
|
let mut pinned = unsafe { std::pin::Pin::new_unchecked(fut) };
|
||||||
|
let as_pinned_mut = &mut pinned;
|
||||||
|
|
||||||
|
// the component finished rendering and gave us nodes
|
||||||
|
// we should attach them to that component and then render its children
|
||||||
|
// continue rendering the tree until we hit yet another suspended component
|
||||||
|
if let futures_task::Poll::Ready(new_nodes) = as_pinned_mut.poll_unpin(&mut cx)
|
||||||
|
{
|
||||||
|
let boundary = &self.scopes[leaf.boundary.0]
|
||||||
|
.consume_context::<SuspenseContext>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut fiber = boundary.borrow_mut();
|
||||||
|
let scope = &mut self.scopes[scope_id.0];
|
||||||
|
let arena = scope.current_arena();
|
||||||
|
|
||||||
|
let ret = arena.bump.alloc(RenderReturn::Sync(new_nodes));
|
||||||
|
arena.node.set(ret);
|
||||||
|
|
||||||
|
if let RenderReturn::Sync(Some(template)) = ret {
|
||||||
|
let mutations = &mut fiber.mutations;
|
||||||
|
let template: &VNode = unsafe { std::mem::transmute(template) };
|
||||||
|
let mutations: &mut Vec<Mutation> =
|
||||||
|
unsafe { std::mem::transmute(mutations) };
|
||||||
|
|
||||||
|
self.scope_stack.push(scope_id);
|
||||||
|
self.create(mutations, template);
|
||||||
|
self.scope_stack.pop();
|
||||||
|
|
||||||
|
println!("{:?}", mutations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +1,36 @@
|
|||||||
use std::{
|
use futures_task::{RawWaker, RawWakerVTable, Waker};
|
||||||
cell::{RefCell, UnsafeCell},
|
use std::{mem, rc::Rc};
|
||||||
marker::PhantomData,
|
|
||||||
mem::{self, MaybeUninit},
|
|
||||||
ops::DerefMut,
|
|
||||||
pin::Pin,
|
|
||||||
process::Output,
|
|
||||||
rc::Rc,
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use futures_task::{waker, RawWaker, RawWakerVTable, Waker};
|
pub trait RcWake: Sized {
|
||||||
|
/// Create a waker from this self-wakening object
|
||||||
|
fn waker(self: &Rc<Self>) -> Waker {
|
||||||
|
unsafe fn rc_vtable<T: RcWake>() -> &'static RawWakerVTable {
|
||||||
|
&RawWakerVTable::new(
|
||||||
|
|data| {
|
||||||
|
let arc = mem::ManuallyDrop::new(Rc::<T>::from_raw(data.cast::<T>()));
|
||||||
|
let _rc_clone: mem::ManuallyDrop<_> = arc.clone();
|
||||||
|
RawWaker::new(data, rc_vtable::<T>())
|
||||||
|
},
|
||||||
|
|data| Rc::from_raw(data.cast::<T>()).wake(),
|
||||||
|
|data| {
|
||||||
|
let arc = mem::ManuallyDrop::new(Rc::<T>::from_raw(data.cast::<T>()));
|
||||||
|
RcWake::wake_by_ref(&arc);
|
||||||
|
},
|
||||||
|
|data| drop(Rc::<T>::from_raw(data.cast::<T>())),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
Waker::from_raw(RawWaker::new(
|
||||||
|
Rc::into_raw(self.clone()).cast(),
|
||||||
|
rc_vtable::<Self>(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wake_by_ref(arc_self: &Rc<Self>);
|
||||||
|
|
||||||
pub trait RcWake {
|
|
||||||
fn wake(self: Rc<Self>) {
|
fn wake(self: Rc<Self>) {
|
||||||
Self::wake_by_ref(&self)
|
Self::wake_by_ref(&self)
|
||||||
}
|
}
|
||||||
fn wake_by_ref(arc_self: &Rc<Self>);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn make_rc_waker<T: RcWake>(rc: Rc<T>) -> Waker {
|
|
||||||
unsafe { Waker::from_raw(RawWaker::new(Rc::into_raw(rc).cast(), rc_vtable::<T>())) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rc_vtable<T: RcWake>() -> &'static RawWakerVTable {
|
|
||||||
&RawWakerVTable::new(
|
|
||||||
clone_rc_raw::<T>,
|
|
||||||
wake_rc_raw::<T>,
|
|
||||||
wake_by_ref_rc_raw::<T>,
|
|
||||||
drop_rc_raw::<T>,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: panics on Rc::clone / refcount changes could wreak havoc on the
|
|
||||||
// code here. We should guard against this by aborting.
|
|
||||||
unsafe fn clone_rc_raw<T: RcWake>(data: *const ()) -> RawWaker {
|
|
||||||
let arc = mem::ManuallyDrop::new(Rc::<T>::from_raw(data.cast::<T>()));
|
|
||||||
let _rc_clone: mem::ManuallyDrop<_> = arc.clone();
|
|
||||||
RawWaker::new(data, rc_vtable::<T>())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn wake_rc_raw<T: RcWake>(data: *const ()) {
|
|
||||||
let arc: Rc<T> = Rc::from_raw(data.cast::<T>());
|
|
||||||
arc.wake();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn wake_by_ref_rc_raw<T: RcWake>(data: *const ()) {
|
|
||||||
let arc = mem::ManuallyDrop::new(Rc::<T>::from_raw(data.cast::<T>()));
|
|
||||||
arc.wake();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn drop_rc_raw<T: RcWake>(data: *const ()) {
|
|
||||||
drop(Rc::<T>::from_raw(data.cast::<T>()))
|
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,20 @@
|
|||||||
use std::task::Context;
|
use crate::{innerlude::SuspenseContext, scheduler::RcWake};
|
||||||
|
use futures_util::{pin_mut, task::noop_waker_ref};
|
||||||
use futures_util::task::noop_waker_ref;
|
use std::{
|
||||||
|
mem,
|
||||||
|
pin::Pin,
|
||||||
|
rc::Rc,
|
||||||
|
task::{Context, Poll},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
any_props::AnyProps,
|
any_props::AnyProps,
|
||||||
arena::ElementId,
|
arena::ElementId,
|
||||||
bump_frame::BumpFrame,
|
bump_frame::BumpFrame,
|
||||||
factory::RenderReturn,
|
factory::RenderReturn,
|
||||||
|
innerlude::{SuspenseId, SuspenseLeaf},
|
||||||
scopes::{ScopeId, ScopeState},
|
scopes::{ScopeId, ScopeState},
|
||||||
virtualdom::VirtualDom, innerlude::SuspenseLeaf,
|
virtualdom::VirtualDom,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl VirtualDom {
|
impl VirtualDom {
|
||||||
@@ -25,6 +31,7 @@ impl VirtualDom {
|
|||||||
id,
|
id,
|
||||||
height,
|
height,
|
||||||
props,
|
props,
|
||||||
|
placeholder: None.into(),
|
||||||
node_arena_1: BumpFrame::new(50),
|
node_arena_1: BumpFrame::new(50),
|
||||||
node_arena_2: BumpFrame::new(50),
|
node_arena_2: BumpFrame::new(50),
|
||||||
render_cnt: Default::default(),
|
render_cnt: Default::default(),
|
||||||
@@ -52,46 +59,66 @@ impl VirtualDom {
|
|||||||
.and_then(|id| self.scopes.get_mut(id.0).map(|f| f as *mut ScopeState))
|
.and_then(|id| self.scopes.get_mut(id.0).map(|f| f as *mut ScopeState))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_scope(&mut self, id: ScopeId) -> &mut RenderReturn {
|
pub fn run_scope(&mut self, scope_id: ScopeId) -> &mut RenderReturn {
|
||||||
let scope = &mut self.scopes[id.0];
|
let mut new_nodes = unsafe {
|
||||||
scope.hook_idx.set(0);
|
let scope = &mut self.scopes[scope_id.0];
|
||||||
|
scope.hook_idx.set(0);
|
||||||
|
|
||||||
let mut new_nodes = {
|
let props: &mut dyn AnyProps = mem::transmute(&mut *scope.props);
|
||||||
let props = unsafe { &mut *scope.props };
|
|
||||||
let props: &mut dyn AnyProps = unsafe { std::mem::transmute(props) };
|
|
||||||
let res: RenderReturn = props.render(scope);
|
let res: RenderReturn = props.render(scope);
|
||||||
let res: RenderReturn<'static> = unsafe { std::mem::transmute(res) };
|
let res: RenderReturn<'static> = mem::transmute(res);
|
||||||
res
|
res
|
||||||
};
|
};
|
||||||
|
|
||||||
// immediately resolve futures that can be resolved immediatelys
|
// immediately resolve futures that can be resolved
|
||||||
let res = match &mut new_nodes {
|
if let RenderReturn::Async(task) = &mut new_nodes {
|
||||||
RenderReturn::Sync(_) => new_nodes,
|
use futures_util::FutureExt;
|
||||||
RenderReturn::Async(fut) => {
|
|
||||||
// use futures_util::FutureExt;
|
|
||||||
|
|
||||||
let leaves = self.scheduler.handle.leaves.borrow_mut();
|
let mut leaves = self.scheduler.handle.leaves.borrow_mut();
|
||||||
|
let entry = leaves.vacant_entry();
|
||||||
|
let key = entry.key();
|
||||||
|
|
||||||
leaves.insert(Rc::new(SuspenseLeaf {
|
let leaf = Rc::new(SuspenseLeaf {
|
||||||
id: todo!(),
|
scope_id,
|
||||||
scope: todo!(),
|
task: task.as_mut(),
|
||||||
boundary: todo!(),
|
id: SuspenseId(key),
|
||||||
tx: todo!(),
|
tx: self.scheduler.handle.sender.clone(),
|
||||||
task: todo!(),
|
boundary: ScopeId(0),
|
||||||
}));
|
notified: false.into(),
|
||||||
|
});
|
||||||
|
|
||||||
let waker = crate::scheduler::make_suspense_waker(task);
|
let _leaf = leaf.clone();
|
||||||
|
let waker = leaf.waker();
|
||||||
|
let mut cx = Context::from_waker(&waker);
|
||||||
|
let mut pinned = unsafe { Pin::new_unchecked(task.as_mut()) };
|
||||||
|
|
||||||
match fut.poll_unpin(&mut cx) {
|
loop {
|
||||||
std::task::Poll::Ready(nodes) => {
|
match pinned.poll_unpin(&mut cx) {
|
||||||
leaves.remove(key;)
|
// If nodes are produced, then set it and we can break
|
||||||
RenderReturn::Sync(nodes)
|
Poll::Ready(nodes) => {
|
||||||
},
|
new_nodes = RenderReturn::Sync(nodes);
|
||||||
std::task::Poll::Pending => new_nodes,
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If no nodes are produced but the future woke up immediately, then try polling it again
|
||||||
|
// This circumvents things like yield_now, but is important is important when rendering
|
||||||
|
// components that are just a stream of immediately ready futures
|
||||||
|
_ if _leaf.notified.get() => {
|
||||||
|
_leaf.notified.set(false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no nodes are produced, then we need to wait for the future to be woken up
|
||||||
|
// Insert the future into fiber leaves and break
|
||||||
|
_ => {
|
||||||
|
entry.insert(leaf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let scope = &mut self.scopes[scope_id.0];
|
||||||
let frame = match scope.render_cnt % 2 {
|
let frame = match scope.render_cnt % 2 {
|
||||||
0 => &mut scope.node_arena_1,
|
0 => &mut scope.node_arena_1,
|
||||||
1 => &mut scope.node_arena_2,
|
1 => &mut scope.node_arena_2,
|
||||||
@@ -99,10 +126,10 @@ impl VirtualDom {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// set the head of the bump frame
|
// set the head of the bump frame
|
||||||
let alloced = frame.bump.alloc(res);
|
let alloced = frame.bump.alloc(new_nodes);
|
||||||
frame.node.set(alloced);
|
frame.node.set(alloced);
|
||||||
|
|
||||||
// rebind the lifetime now that its stored internally
|
// rebind the lifetime now that its stored internally
|
||||||
unsafe { std::mem::transmute(alloced) }
|
unsafe { mem::transmute(alloced) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,6 +72,7 @@ pub struct ScopeState {
|
|||||||
pub tasks: SchedulerHandle,
|
pub tasks: SchedulerHandle,
|
||||||
|
|
||||||
pub props: *mut dyn AnyProps<'static>,
|
pub props: *mut dyn AnyProps<'static>,
|
||||||
|
pub placeholder: Cell<Option<ElementId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScopeState {
|
impl ScopeState {
|
||||||
|
|||||||
@@ -13,9 +13,11 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use crate::{scheduler, Element, Scope};
|
use crate::{scheduler, Element, Scope};
|
||||||
use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||||
use scheduler::SuspenseContext;
|
use scheduler::{SuspenseBoundary, SuspenseContext};
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::collections::{BTreeSet, HashMap};
|
use std::collections::{BTreeSet, HashMap};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct VirtualDom {
|
pub struct VirtualDom {
|
||||||
pub(crate) templates: HashMap<TemplateId, Template<'static>>,
|
pub(crate) templates: HashMap<TemplateId, Template<'static>>,
|
||||||
@@ -47,7 +49,7 @@ impl VirtualDom {
|
|||||||
let root = res.new_scope(props);
|
let root = res.new_scope(props);
|
||||||
|
|
||||||
// the root component is always a suspense boundary for any async children
|
// the root component is always a suspense boundary for any async children
|
||||||
// res.scopes[root.0].provide_context(SuspenseContext::new(root));
|
res.scopes[root.0].provide_context(Rc::new(RefCell::new(SuspenseBoundary::new(root))));
|
||||||
|
|
||||||
assert_eq!(root, ScopeId(0));
|
assert_eq!(root, ScopeId(0));
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,10 @@ async fn async_child(cx: Scope<'_>) -> Element {
|
|||||||
|
|
||||||
fut.await;
|
fut.await;
|
||||||
|
|
||||||
println!("Future awaited");
|
println!("Future awaited and complete");
|
||||||
|
|
||||||
None
|
let dy = cx.component(async_child, (), "async_child");
|
||||||
|
VNode::single_component(&cx, dy, "app")
|
||||||
|
|
||||||
|
// VNode::single_text(&cx, &[TemplateNode::Text("it works!")], "beauty")
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user