feat: poll async once

This commit is contained in:
Jonathan Kelley 2022-11-03 20:56:31 -07:00
parent 94601ccd1f
commit d2ce57ba6e
9 changed files with 150 additions and 67 deletions

View file

@ -3,20 +3,20 @@ use std::marker::PhantomData;
use futures_util::Future;
use crate::{
factory::ReturnType,
factory::{ComponentReturn, RenderReturn},
scopes::{Scope, ScopeState},
Element,
};
pub trait AnyProps<'a> {
fn as_ptr(&self) -> *const ();
fn render(&'a self, bump: &'a ScopeState) -> Element<'a>;
fn render(&'a self, bump: &'a ScopeState) -> RenderReturn<'a>;
unsafe fn memoize(&self, other: &dyn AnyProps) -> bool;
}
pub(crate) struct VComponentProps<'a, P, A, F = Element<'a>>
where
F: ReturnType<'a, A>,
F: ComponentReturn<'a, A>,
{
pub render_fn: fn(Scope<'a, P>) -> F,
pub memo: unsafe fn(&P, &P) -> bool,
@ -35,7 +35,7 @@ impl<'a> VComponentProps<'a, (), ()> {
}
}
impl<'a, P, A, F: ReturnType<'a, A>> VComponentProps<'a, P, A, F> {
impl<'a, P, A, F: ComponentReturn<'a, A>> VComponentProps<'a, P, A, F> {
pub(crate) fn new(
render_fn: fn(Scope<'a, P>) -> F,
memo: unsafe fn(&P, &P) -> bool,
@ -50,7 +50,7 @@ impl<'a, P, A, F: ReturnType<'a, A>> VComponentProps<'a, P, A, F> {
}
}
impl<'a, P, A, F: ReturnType<'a, A>> AnyProps<'a> for VComponentProps<'a, P, A, F> {
impl<'a, P, A, F: ComponentReturn<'a, A>> AnyProps<'a> for VComponentProps<'a, P, A, F> {
fn as_ptr(&self) -> *const () {
&self.props as *const _ as *const ()
}
@ -65,25 +65,16 @@ impl<'a, P, A, F: ReturnType<'a, A>> AnyProps<'a> for VComponentProps<'a, P, A,
(self.memo)(real_us, real_other)
}
fn render<'b>(&'b self, scope: &'b ScopeState) -> Element<'b> {
fn render(&self, cx: &'a ScopeState) -> RenderReturn<'a> {
// Make sure the scope ptr is not null
// self.props.state.set(scope);
let scope = Scope {
props: unsafe { &*self.props },
scope,
scope: cx,
};
// Call the render function directly
// todo: implement async
// let res = match self.render_fn {
// ComponentFn::Sync(f) => {
// let f = unsafe { std::mem::transmute(f) };
// f(scope)
// }
// ComponentFn::Async(_) => todo!(),
// };
todo!()
(self.render_fn)(scope).as_return(cx)
}
}

View file

@ -2,23 +2,23 @@ use std::cell::Cell;
use bumpalo::Bump;
use crate::nodes::VNode;
use crate::factory::RenderReturn;
pub struct BumpFrame {
pub bump: Bump,
pub node: Cell<*const VNode<'static>>,
pub node: Cell<*mut RenderReturn<'static>>,
}
impl BumpFrame {
pub fn new(capacity: usize) -> Self {
let bump = Bump::with_capacity(capacity);
Self {
bump,
node: Cell::new(std::ptr::null()),
node: Cell::new(std::ptr::null_mut()),
}
}
pub fn reset(&mut self) {
self.bump.reset();
self.node.set(std::ptr::null());
self.node.set(std::ptr::null_mut());
}
}

View file

@ -1,9 +1,15 @@
use std::task::Context;
use futures_util::task::noop_waker_ref;
use futures_util::{pin_mut, Future};
use crate::factory::RenderReturn;
use crate::mutations::Mutation;
use crate::mutations::Mutation::*;
use crate::nodes::VNode;
use crate::nodes::{DynamicNode, TemplateNode};
use crate::virtualdom::VirtualDom;
use crate::{AttributeValue, ElementId, TemplateAttribute};
use crate::{AttributeValue, Element, ElementId, TemplateAttribute};
impl VirtualDom {
/// Create this template and write its mutations
@ -96,10 +102,12 @@ impl VirtualDom {
while let Some((idx, path)) = dynamic_nodes.next_if(|(_, p)| p[0] == root_idx as u8) {
let node = &template.dynamic_nodes[idx];
let m = self.create_dynamic_node(mutations, template, node, idx);
mutations.push(ReplacePlaceholder {
m,
path: &path[1..],
});
if m > 0 {
mutations.push(ReplacePlaceholder {
m,
path: &path[1..],
});
}
}
}
@ -172,16 +180,53 @@ impl VirtualDom {
DynamicNode::Component { props, .. } => {
let id = self.new_scope(unsafe { std::mem::transmute(props.get()) });
let template = self.run_scope(id);
let render_ret = self.run_scope(id);
// shut up about lifetimes please, I know what I'm doing
let template: &VNode = unsafe { std::mem::transmute(template) };
let render_ret: &mut RenderReturn = unsafe { std::mem::transmute(render_ret) };
self.scope_stack.push(id);
let created = self.create(mutations, template);
self.scope_stack.pop();
match render_ret {
RenderReturn::Sync(Some(template)) => {
self.scope_stack.push(id);
let created = self.create(mutations, template);
self.scope_stack.pop();
created
}
RenderReturn::Sync(None) => todo!("nodes that return nothing"),
RenderReturn::Async(fut) => {
use futures_util::FutureExt;
created
// Poll the suspense node once to see if we can get any nodes from it
let mut cx = Context::from_waker(&noop_waker_ref());
let res = fut.poll_unpin(&mut cx);
match res {
std::task::Poll::Ready(Some(val)) => {
let scope = self.get_scope(id).unwrap();
let ready = &*scope.bump().alloc(val);
let ready = unsafe { std::mem::transmute(ready) };
self.scope_stack.push(id);
let created = self.create(mutations, ready);
self.scope_stack.pop();
created
}
std::task::Poll::Ready(None) => {
todo!("Pending suspense")
}
std::task::Poll::Pending => {
let new_id = self.next_element(template);
// id.set(new_id);
mutations.push(AssignId {
id: new_id,
path: &template.template.node_paths[idx][1..],
});
0
}
}
}
}
}
DynamicNode::Fragment(children) => children

View file

@ -1,5 +1,6 @@
use std::{cell::Cell, fmt::Arguments};
use std::{cell::Cell, fmt::Arguments, pin::Pin};
use bumpalo::boxed::Box as BumpBox;
use bumpalo::Bump;
use futures_util::Future;
@ -70,7 +71,7 @@ impl ScopeState {
}
/// Create a new [`VNode::Component`]
pub fn component<'a, P, A, F: ReturnType<'a, A>>(
pub fn component<'a, P, A, F: ComponentReturn<'a, A>>(
&'a self,
component: fn(Scope<'a, P>) -> F,
props: P,
@ -100,11 +101,27 @@ impl ScopeState {
}
}
pub trait ReturnType<'a, A = ()> {}
impl<'a> ReturnType<'a> for Element<'a> {}
pub trait ComponentReturn<'a, A = ()> {
fn as_return(self, cx: &'a ScopeState) -> RenderReturn<'a>;
}
impl<'a> ComponentReturn<'a> for Element<'a> {
fn as_return(self, _cx: &ScopeState) -> RenderReturn<'a> {
RenderReturn::Sync(self)
}
}
pub struct AsyncMarker;
impl<'a, F> ReturnType<'a, AsyncMarker> for F where F: Future<Output = Element<'a>> + 'a {}
impl<'a, F> ComponentReturn<'a, AsyncMarker> for F
where
F: Future<Output = Element<'a>> + 'a,
{
fn as_return(self, cx: &'a ScopeState) -> RenderReturn<'a> {
let f: &mut dyn Future<Output = Element<'a>> = cx.bump().alloc(self);
let boxed = unsafe { BumpBox::from_raw(f) };
let pined: Pin<BumpBox<_>> = boxed.into();
RenderReturn::Async(pined)
}
}
#[test]
fn takes_it() {
@ -113,9 +130,9 @@ fn takes_it() {
}
}
enum RenderReturn<'a> {
pub enum RenderReturn<'a> {
Sync(Element<'a>),
Async(*mut dyn Future<Output = Element<'a>>),
Async(Pin<BumpBox<'a, dyn Future<Output = Element<'a>> + 'a>>),
}
pub trait IntoVnode<'a, A = ()> {

View file

@ -132,19 +132,3 @@ macro_rules! to_owned {
let mut $es = $es.to_owned();
)*}
}
/// get the code location of the code that called this function
#[macro_export]
macro_rules! get_line_num {
() => {
concat!(
file!(),
":",
line!(),
":",
column!(),
":",
env!("CARGO_MANIFEST_DIR")
)
};
}

View file

@ -2,6 +2,7 @@ use crate::{
any_props::AnyProps,
arena::ElementId,
bump_frame::BumpFrame,
factory::RenderReturn,
nodes::VNode,
scopes::{ScopeId, ScopeState},
virtualdom::VirtualDom,
@ -48,15 +49,15 @@ impl VirtualDom {
.and_then(|id| self.scopes.get_mut(id.0).map(|f| f as *mut ScopeState))
}
pub fn run_scope(&mut self, id: ScopeId) -> &VNode {
pub fn run_scope(&mut self, id: ScopeId) -> &mut RenderReturn {
let scope = &mut self.scopes[id.0];
scope.hook_idx.set(0);
let res = {
let props = unsafe { &mut *scope.props };
let props: &mut dyn AnyProps = unsafe { std::mem::transmute(props) };
let res: VNode = props.render(scope).unwrap();
let res: VNode<'static> = unsafe { std::mem::transmute(res) };
let res: RenderReturn = props.render(scope);
let res: RenderReturn<'static> = unsafe { std::mem::transmute(res) };
res
};

View file

@ -2,6 +2,7 @@ use crate::any_props::VComponentProps;
use crate::arena::ElementPath;
use crate::component::Component;
use crate::diff::DirtyScope;
use crate::factory::RenderReturn;
use crate::future_container::FutureQueue;
use crate::innerlude::SchedulerMsg;
use crate::mutations::Mutation;
@ -57,13 +58,17 @@ impl VirtualDom {
pub fn rebuild<'a>(&'a mut self, mutations: &mut Vec<Mutation<'a>>) {
// let root = self.scopes.get(0).unwrap();
let root_node = unsafe { std::mem::transmute(self.run_scope(ScopeId(0))) };
// let root_node = unsafe { std::mem::transmute(root.root_node()) };
self.scope_stack.push(ScopeId(0));
self.create(mutations, root_node);
self.scope_stack.pop();
let root_node: &RenderReturn = self.run_scope(ScopeId(0));
let root_node: &RenderReturn = unsafe { std::mem::transmute(root_node) };
match root_node {
RenderReturn::Sync(Some(node)) => {
self.scope_stack.push(ScopeId(0));
self.create(mutations, node);
self.scope_stack.pop();
}
RenderReturn::Sync(None) => todo!("Handle empty root node"),
RenderReturn::Async(_) => unreachable!(),
}
}
/// Render what you can given the timeline and then move on

View file

@ -110,6 +110,9 @@ impl<'a> ToTokens for TemplateRenderer<'a> {
None => quote! { None },
};
let spndbg = format!("{:?}", self.roots[0].span());
let root_col = spndbg[9..].split("..").next().unwrap();
let root_printer = self.roots.iter().enumerate().map(|(idx, root)| {
context.current_path.push(idx as u8);
let out = context.render_static_node(root);
@ -126,7 +129,15 @@ impl<'a> ToTokens for TemplateRenderer<'a> {
out_tokens.append_all(quote! {
static TEMPLATE: ::dioxus::core::Template = ::dioxus::core::Template {
id: ::dioxus::core::get_line_num!(),
id: concat!(
file!(),
":",
line!(),
":",
column!(),
":",
#root_col
),
roots: &[ #roots ],
node_paths: &[ #(#node_paths),* ],
attr_paths: &[ #(#attr_paths),* ],

View file

@ -131,7 +131,7 @@ impl SsrRender {
// todo: escape the text
write!(buf, "{}", value)?
}
DynamicNode::Fragment { children } => {
DynamicNode::Fragment(children) => {
for child in *children {
self.render_template(buf, child)?;
}
@ -140,6 +140,9 @@ impl SsrRender {
DynamicNode::Component { .. } => {
//
}
DynamicNode::Placeholder(el) => {
//
}
},
Segment::PreRendered(text) => buf.push_str(&text),
@ -217,3 +220,29 @@ fn children_processes_properly() {
dom.rebuild(&mut mutations);
dbg!(mutations);
}
#[test]
fn async_children() {
use dioxus::prelude::*;
fn app(cx: Scope) -> Element {
let d = 123;
render! {
div {
async_child {}
}
}
}
async fn async_child(cx: Scope<'_>) -> Element {
let d = 123;
render! { p { "{d}" "hii" } }
}
let mut dom = VirtualDom::new(app);
let mut mutations = vec![];
dom.rebuild(&mut mutations);
dbg!(mutations);
}