feat: abstract to rc wake
This commit is contained in:
1 parent
a38fc9e4ab
commit
e6c53803a6
12 files changed
+188
-67
No files matched your search
@@ -99,7 +99,10 @@ impl VirtualDom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We're on top of a node that has a dynamic child for a descendant
|
// We're on top of a node that has a dynamic child for a descendant
|
||||||
while let Some((idx, path)) = dynamic_nodes.next_if(|(_, p)| p[0] == root_idx as u8) {
|
// Skip any node that's a root
|
||||||
|
while let Some((idx, path)) =
|
||||||
|
dynamic_nodes.next_if(|(_, p)| p.len() > 1 && p[0] == root_idx as u8)
|
||||||
|
{
|
||||||
let node = &template.dynamic_nodes[idx];
|
let node = &template.dynamic_nodes[idx];
|
||||||
let m = self.create_dynamic_node(mutations, template, node, idx);
|
let m = self.create_dynamic_node(mutations, template, node, idx);
|
||||||
if m > 0 {
|
if m > 0 {
|
||||||
@@ -180,6 +183,7 @@ impl VirtualDom {
|
|||||||
DynamicNode::Component {
|
DynamicNode::Component {
|
||||||
props, placeholder, ..
|
props, placeholder, ..
|
||||||
} => {
|
} => {
|
||||||
|
println!("creaitng component");
|
||||||
let id = self.new_scope(unsafe { std::mem::transmute(props.get()) });
|
let id = self.new_scope(unsafe { std::mem::transmute(props.get()) });
|
||||||
|
|
||||||
let render_ret = self.run_scope(id);
|
let render_ret = self.run_scope(id);
|
||||||
@@ -211,6 +215,7 @@ impl VirtualDom {
|
|||||||
|
|
||||||
let scope = self.scope_stack.last().unwrap();
|
let scope = self.scope_stack.last().unwrap();
|
||||||
let scope = &self.scopes[scope.0];
|
let scope = &self.scopes[scope.0];
|
||||||
|
|
||||||
let boundary = scope.consume_context::<SuspenseContext>().unwrap();
|
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
|
// try to poll the future once - many times it will be ready immediately or require little to no work
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ impl<'b> VirtualDom {
|
|||||||
{
|
{
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
match (left_node, right_node) {
|
match (left_node, right_node) {
|
||||||
(DynamicNode::Component { props: lprops, .. }, DynamicNode::Component { is_static , props: rprops, .. }) => {
|
(DynamicNode::Component { props: lprops, .. }, DynamicNode::Component { static_props: is_static , props: rprops, .. }) => {
|
||||||
let left_props = unsafe { &mut *lprops.get()};
|
let left_props = unsafe { &mut *lprops.get()};
|
||||||
let right_props = unsafe { &mut *rprops.get()};
|
let right_props = unsafe { &mut *rprops.get()};
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ impl ScopeState {
|
|||||||
|
|
||||||
DynamicNode::Component {
|
DynamicNode::Component {
|
||||||
name: fn_name,
|
name: fn_name,
|
||||||
is_static: P::IS_STATIC,
|
static_props: P::IS_STATIC,
|
||||||
props: Cell::new(detached_dyn),
|
props: Cell::new(detached_dyn),
|
||||||
placeholder: Cell::new(None),
|
placeholder: Cell::new(None),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{any_props::AnyProps, arena::ElementId};
|
use crate::{any_props::AnyProps, arena::ElementId, ScopeState};
|
||||||
use std::{
|
use std::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
@@ -26,6 +26,29 @@ pub struct VNode<'a> {
|
|||||||
pub dynamic_attrs: &'a [Attribute<'a>],
|
pub dynamic_attrs: &'a [Attribute<'a>],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> VNode<'a> {
|
||||||
|
pub fn single_component(
|
||||||
|
cx: &'a ScopeState,
|
||||||
|
node: DynamicNode<'a>,
|
||||||
|
id: &'static str,
|
||||||
|
) -> Option<Self> {
|
||||||
|
Some(VNode {
|
||||||
|
node_id: Cell::new(ElementId(0)),
|
||||||
|
key: None,
|
||||||
|
parent: None,
|
||||||
|
root_ids: &[],
|
||||||
|
dynamic_nodes: cx.bump().alloc([node]),
|
||||||
|
dynamic_attrs: &[],
|
||||||
|
template: Template {
|
||||||
|
id,
|
||||||
|
roots: &[TemplateNode::Dynamic(0)],
|
||||||
|
node_paths: &[&[0]],
|
||||||
|
attr_paths: &[],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Template<'a> {
|
pub struct Template<'a> {
|
||||||
pub id: &'a str,
|
pub id: &'a str,
|
||||||
@@ -77,7 +100,7 @@ pub enum DynamicNode<'a> {
|
|||||||
// IE in caps or with underscores
|
// IE in caps or with underscores
|
||||||
Component {
|
Component {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
is_static: bool,
|
static_props: bool,
|
||||||
props: Cell<*mut dyn AnyProps<'a>>,
|
props: Cell<*mut dyn AnyProps<'a>>,
|
||||||
placeholder: Cell<Option<ElementId>>,
|
placeholder: Cell<Option<ElementId>>,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,8 +16,12 @@ impl std::ops::Deref for SchedulerHandle {
|
|||||||
|
|
||||||
pub struct HandleInner {
|
pub struct HandleInner {
|
||||||
pub sender: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
|
pub sender: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
|
||||||
pub tasks: RefCell<Slab<LocalTask>>,
|
|
||||||
pub leaves: RefCell<Slab<SuspenseLeaf>>,
|
/// Tasks created with cx.spawn
|
||||||
|
pub tasks: RefCell<Slab<Rc<LocalTask>>>,
|
||||||
|
|
||||||
|
/// Async components
|
||||||
|
pub leaves: RefCell<Slab<Rc<SuspenseLeaf>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SchedulerHandle {
|
impl SchedulerHandle {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ mod handle;
|
|||||||
mod suspense;
|
mod suspense;
|
||||||
mod task;
|
mod task;
|
||||||
mod wait;
|
mod wait;
|
||||||
|
mod waker;
|
||||||
|
|
||||||
pub use handle::*;
|
pub use handle::*;
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
|
|||||||
@@ -1,12 +1,21 @@
|
|||||||
use std::{cell::RefCell, mem, ops::DerefMut, pin::Pin, process::Output, rc::Rc, sync::Arc};
|
use std::{
|
||||||
|
cell::{RefCell, UnsafeCell},
|
||||||
|
marker::PhantomData,
|
||||||
|
mem::{self, MaybeUninit},
|
||||||
|
ops::DerefMut,
|
||||||
|
pin::Pin,
|
||||||
|
process::Output,
|
||||||
|
rc::Rc,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use futures_task::{waker, ArcWake, Context, RawWaker, RawWakerVTable, Waker};
|
use futures_task::{waker, ArcWake, Context, RawWaker, RawWakerVTable, Waker};
|
||||||
use futures_util::{pin_mut, Future, FutureExt};
|
use futures_util::{pin_mut, Future, FutureExt};
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
|
|
||||||
use crate::ScopeId;
|
use crate::{Element, ScopeId};
|
||||||
|
|
||||||
use super::{HandleInner, SchedulerHandle, SchedulerMsg};
|
use super::{waker::RcWake, HandleInner, SchedulerHandle, SchedulerMsg};
|
||||||
|
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
@@ -14,27 +23,25 @@ pub struct TaskId(pub usize);
|
|||||||
|
|
||||||
/// the task itself is the waker
|
/// the task itself is the waker
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct LocalTask {
|
pub struct LocalTask {
|
||||||
id: TaskId,
|
id: TaskId,
|
||||||
scope: ScopeId,
|
scope: ScopeId,
|
||||||
tx: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
|
tx: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
|
||||||
pub task: *mut dyn Future<Output = ()>,
|
pub task: UnsafeCell<Pin<Box<dyn Future<Output = ()> + 'static>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HandleInner {
|
impl HandleInner {
|
||||||
pub fn spawn(&self, scope: ScopeId, task: impl Future<Output = ()> + 'static) -> TaskId {
|
pub fn spawn(&self, scope: ScopeId, task: impl Future<Output = ()> + 'static) -> TaskId {
|
||||||
let mut tasks = self.tasks.borrow_mut();
|
let mut tasks = self.tasks.borrow_mut();
|
||||||
|
|
||||||
let entry = tasks.vacant_entry();
|
let entry = tasks.vacant_entry();
|
||||||
let task_id = TaskId(entry.key());
|
let task_id = TaskId(entry.key());
|
||||||
|
|
||||||
entry.insert(LocalTask {
|
entry.insert(Rc::new(LocalTask {
|
||||||
id: task_id,
|
id: task_id,
|
||||||
tx: self.sender.clone(),
|
tx: self.sender.clone(),
|
||||||
task: Box::into_raw(Box::new(task)),
|
task: UnsafeCell::new(Box::pin(task)),
|
||||||
scope,
|
scope,
|
||||||
});
|
}));
|
||||||
|
|
||||||
self.sender
|
self.sender
|
||||||
.unbounded_send(SchedulerMsg::TaskNotified(task_id))
|
.unbounded_send(SchedulerMsg::TaskNotified(task_id))
|
||||||
@@ -54,28 +61,15 @@ impl HandleInner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_task_waker(task: &LocalTask) -> Waker {
|
pub fn make_task_waker(task: Rc<LocalTask>) -> Waker {
|
||||||
let raw = RawWaker::new(task as *const LocalTask as *const _, task_vtable());
|
let ptr = Rc::into_raw(task).cast::<()>();
|
||||||
unsafe { Waker::from_raw(raw) }
|
super::waker::make_rc_waker(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn task_vtable() -> &'static RawWakerVTable {
|
impl RcWake for LocalTask {
|
||||||
&RawWakerVTable::new(clone, wake, wake_by_ref, drop_task)
|
fn wake_by_ref(arc_self: &Rc<Self>) {
|
||||||
}
|
_ = arc_self
|
||||||
|
.tx
|
||||||
unsafe fn clone(data: *const ()) -> RawWaker {
|
.unbounded_send(SchedulerMsg::TaskNotified(arc_self.id));
|
||||||
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 LocalTask);
|
|
||||||
task.tx
|
|
||||||
.unbounded_send(SchedulerMsg::TaskNotified(task.id))
|
|
||||||
.expect("Scheduler should exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn drop_task(_data: *const ()) {
|
|
||||||
// doesnt do anything
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
use std::{ops::DerefMut, pin::Pin};
|
|
||||||
|
|
||||||
use futures_task::Context;
|
use futures_task::Context;
|
||||||
use futures_util::StreamExt;
|
use futures_util::{FutureExt, StreamExt};
|
||||||
|
|
||||||
use crate::{innerlude::make_task_waker, VirtualDom};
|
use crate::{innerlude::make_task_waker, VirtualDom};
|
||||||
|
|
||||||
@@ -13,38 +11,28 @@ impl VirtualDom {
|
|||||||
/// This is cancel safe, so if the future is dropped, you can push events into the virtualdom
|
/// This is cancel safe, so if the future is dropped, you can push events into the virtualdom
|
||||||
pub async fn wait_for_work(&mut self) {
|
pub async fn wait_for_work(&mut self) {
|
||||||
loop {
|
loop {
|
||||||
let msg = self.scheduler.rx.next().await.unwrap();
|
match self.scheduler.rx.next().await.unwrap() {
|
||||||
|
|
||||||
println!("msg received: {:?}", msg);
|
|
||||||
|
|
||||||
match msg {
|
|
||||||
SchedulerMsg::Event => todo!(),
|
SchedulerMsg::Event => todo!(),
|
||||||
SchedulerMsg::Immediate(_) => todo!(),
|
SchedulerMsg::Immediate(_) => todo!(),
|
||||||
SchedulerMsg::DirtyAll => todo!(),
|
SchedulerMsg::DirtyAll => todo!(),
|
||||||
|
|
||||||
SchedulerMsg::TaskNotified(id) => {
|
SchedulerMsg::TaskNotified(id) => {
|
||||||
let mut tasks = self.scheduler.handle.tasks.borrow_mut();
|
let mut tasks = self.scheduler.handle.tasks.borrow_mut();
|
||||||
let local_task = &tasks[id.0];
|
let local_task = &tasks[id.0];
|
||||||
|
|
||||||
// // attach the waker to itself
|
// attach the waker to itself
|
||||||
let waker = make_task_waker(local_task);
|
// 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 mut cx = Context::from_waker(&waker);
|
let mut cx = Context::from_waker(&waker);
|
||||||
let mut fut = unsafe { &mut *local_task.task };
|
|
||||||
|
|
||||||
let pinned = unsafe { Pin::new_unchecked(fut.deref_mut()) };
|
// safety: the waker owns its task and everythig is single threaded
|
||||||
|
let fut = unsafe { &mut *local_task.task.get() };
|
||||||
|
|
||||||
match pinned.poll(&mut cx) {
|
if let futures_task::Poll::Ready(_) = fut.poll_unpin(&mut cx) {
|
||||||
futures_task::Poll::Ready(_) => {
|
tasks.remove(id.0);
|
||||||
// remove the task
|
|
||||||
tasks.remove(id.0);
|
|
||||||
}
|
|
||||||
futures_task::Poll::Pending => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if tasks.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// SchedulerMsg::TaskNotified(id) => {},
|
|
||||||
SchedulerMsg::SuspenseNotified(_) => todo!(),
|
SchedulerMsg::SuspenseNotified(_) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
use std::{
|
||||||
|
cell::{RefCell, UnsafeCell},
|
||||||
|
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 {
|
||||||
|
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>()))
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ use crate::{
|
|||||||
bump_frame::BumpFrame,
|
bump_frame::BumpFrame,
|
||||||
factory::RenderReturn,
|
factory::RenderReturn,
|
||||||
scopes::{ScopeId, ScopeState},
|
scopes::{ScopeId, ScopeState},
|
||||||
virtualdom::VirtualDom,
|
virtualdom::VirtualDom, innerlude::SuspenseLeaf,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl VirtualDom {
|
impl VirtualDom {
|
||||||
@@ -68,11 +68,25 @@ impl VirtualDom {
|
|||||||
let res = match &mut new_nodes {
|
let res = match &mut new_nodes {
|
||||||
RenderReturn::Sync(_) => new_nodes,
|
RenderReturn::Sync(_) => new_nodes,
|
||||||
RenderReturn::Async(fut) => {
|
RenderReturn::Async(fut) => {
|
||||||
use futures_util::FutureExt;
|
// use futures_util::FutureExt;
|
||||||
let mut cx = Context::from_waker(&noop_waker_ref());
|
|
||||||
|
let leaves = self.scheduler.handle.leaves.borrow_mut();
|
||||||
|
|
||||||
|
leaves.insert(Rc::new(SuspenseLeaf {
|
||||||
|
id: todo!(),
|
||||||
|
scope: todo!(),
|
||||||
|
boundary: todo!(),
|
||||||
|
tx: todo!(),
|
||||||
|
task: todo!(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
let waker = crate::scheduler::make_suspense_waker(task);
|
||||||
|
|
||||||
match fut.poll_unpin(&mut cx) {
|
match fut.poll_unpin(&mut cx) {
|
||||||
std::task::Poll::Ready(nodes) => RenderReturn::Sync(nodes),
|
std::task::Poll::Ready(nodes) => {
|
||||||
|
leaves.remove(key;)
|
||||||
|
RenderReturn::Sync(nodes)
|
||||||
|
},
|
||||||
std::task::Poll::Pending => new_nodes,
|
std::task::Poll::Pending => new_nodes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
use std::{cell::Cell, ptr::null_mut, time::Duration};
|
||||||
|
|
||||||
|
use dioxus_core::*;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn it_works() {
|
||||||
|
let mut dom = VirtualDom::new(app);
|
||||||
|
|
||||||
|
let mut mutations = vec![];
|
||||||
|
dom.rebuild(&mut mutations);
|
||||||
|
|
||||||
|
println!("mutations: {:?}", mutations);
|
||||||
|
|
||||||
|
dom.wait_for_work().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn app(cx: Scope) -> Element {
|
||||||
|
let dy = cx.component(async_child, (), "async_child");
|
||||||
|
VNode::single_component(&cx, dy, "app")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn async_child(cx: Scope<'_>) -> Element {
|
||||||
|
println!("rendering async child");
|
||||||
|
|
||||||
|
let fut = cx.use_hook(|| {
|
||||||
|
Box::pin(async {
|
||||||
|
println!("Starting sleep");
|
||||||
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||||
|
println!("Sleep ended");
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
fut.await;
|
||||||
|
|
||||||
|
println!("Future awaited");
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
@@ -17,14 +17,14 @@ async fn it_works() {
|
|||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
cx.spawn(async {
|
cx.spawn(async {
|
||||||
for x in 0..10 {
|
for x in 0..10 {
|
||||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||||
println!("Hello, world! {x}");
|
println!("Hello, world! {x}");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.spawn(async {
|
cx.spawn(async {
|
||||||
for x in 0..10 {
|
for x in 0..10 {
|
||||||
tokio::time::sleep(Duration::from_millis(500)).await;
|
tokio::time::sleep(Duration::from_millis(250)).await;
|
||||||
println!("Hello, world does! {x}");
|
println!("Hello, world does! {x}");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in new issue
Block a user