feat: suspense!

This commit is contained in:
Jonathan Kelley committed 2022-11-07 22:55:22 -08:00
1 parent e6c53803a6
commit 203935834d
13 files changed
+219 -205

No files matched your search

+1
View File
@@ -0,0 +1 @@
+3 -1
View File
@@ -1,7 +1,9 @@
use slab::Slab;
use std::sync::Arc;
use crate::ScopeId;
mod bumpslab;
mod handle;
mod suspense;
mod task;
@@ -9,9 +11,9 @@ mod wait;
mod waker;
pub use handle::*;
use slab::Slab;
pub use suspense::*;
pub use task::*;
pub use waker::RcWake;
/// The type of message that can be sent to the scheduler.
///
+24 -41
View File
@@ -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 crate::{innerlude::Mutation, Element, Scope, ScopeId};
use super::SchedulerMsg;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct SuspenseId(pub usize);
pub type SuspenseContext = Rc<SuspenseBoundary>;
pub type SuspenseContext = Rc<RefCell<SuspenseBoundary>>;
/// Essentially a fiber in React
pub struct SuspenseBoundary {
pub id: ScopeId,
waiting_on: HashSet<SuspenseId>,
mutations: Vec<Mutation<'static>>,
pub waiting_on: HashSet<SuspenseId>,
pub mutations: Vec<Mutation<'static>>,
}
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 id: SuspenseId,
pub scope: ScopeId,
pub scope_id: ScopeId,
pub boundary: ScopeId,
pub tx: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
pub notified: Cell<bool>,
pub task: *mut dyn Future<Output = Element<'static>>,
}
pub fn make_suspense_waker(task: &SuspenseLeaf) -> Waker {
let raw = RawWaker::new(task as *const SuspenseLeaf as *const _, task_vtable());
unsafe { Waker::from_raw(raw) }
}
fn task_vtable() -> &'static RawWakerVTable {
&RawWakerVTable::new(clone, wake, wake_by_ref, drop_task)
}
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
impl RcWake for SuspenseLeaf {
fn wake_by_ref(arc_self: &Rc<Self>) {
if arc_self.notified.get() {
return;
}
arc_self.notified.set(true);
_ = arc_self
.tx
.unbounded_send(SchedulerMsg::SuspenseNotified(arc_self.id));
}
}
-5
View File
@@ -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 {
fn wake_by_ref(arc_self: &Rc<Self>) {
_ = arc_self
+58 -4
View File
@@ -1,9 +1,13 @@
use futures_task::Context;
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 {
/// Wait for futures internal to the virtualdom
@@ -22,7 +26,7 @@ impl VirtualDom {
// attach the waker to itself
// 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);
// 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);
}
}
}
}
}
}
+30 -48
View File
@@ -1,54 +1,36 @@
use std::{
cell::{RefCell, UnsafeCell},
marker::PhantomData,
mem::{self, MaybeUninit},
ops::DerefMut,
pin::Pin,
process::Output,
rc::Rc,
sync::Arc,
};
use futures_task::{RawWaker, RawWakerVTable, Waker};
use std::{mem, rc::Rc};
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>) {
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>()))
}