wip: rip out unsafe task engine

This commit is contained in:
Jonathan Kelley committed 2021-12-26 14:22:30 -05:00
1 parent cda759c659
commit c7d001cbb4
10 files changed
+144 -136

No files matched your search

+1 -1
View File
@@ -12,7 +12,7 @@ fn main() {
fn app(cx: Scope<()>) -> Element { fn app(cx: Scope<()>) -> Element {
let mut count = use_state(&cx, || 0); let mut count = use_state(&cx, || 0);
cx.push_task(|| async move { cx.push_future(|| async move {
tokio::time::sleep(Duration::from_millis(100)).await; tokio::time::sleep(Duration::from_millis(100)).await;
count += 1; count += 1;
}); });
+1 -1
View File
@@ -7,7 +7,7 @@ fn main() {}
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
cx.render(rsx!(div { cx.render(rsx!(div {
app2 ( app2(
p: "asd" p: "asd"
) )
})) }))
+2 -2
View File
@@ -67,8 +67,8 @@ pub(crate) mod innerlude {
pub use crate::innerlude::{ pub use crate::innerlude::{
Attribute, Component, DioxusElement, DomEdit, Element, ElementId, EventHandler, EventPriority, Attribute, Component, DioxusElement, DomEdit, Element, ElementId, EventHandler, EventPriority,
IntoVNode, LazyNodes, Listener, Mutations, NodeFactory, Properties, SchedulerMsg, Scope, IntoVNode, LazyNodes, Listener, Mutations, NodeFactory, Properties, SchedulerMsg, Scope,
ScopeId, ScopeState, UserEvent, VComponent, VElement, VFragment, VNode, VPlaceholder, VText, ScopeId, ScopeState, TaskId, UserEvent, VComponent, VElement, VFragment, VNode, VPlaceholder,
VirtualDom, VText, VirtualDom,
}; };
pub mod prelude { pub mod prelude {
+75 -87
View File
@@ -1,5 +1,7 @@
use crate::{innerlude::*, unsafe_utils::extend_vnode};
use bumpalo::Bump;
use futures_channel::mpsc::UnboundedSender; use futures_channel::mpsc::UnboundedSender;
use fxhash::{FxHashMap, FxHashSet}; use fxhash::FxHashMap;
use slab::Slab; use slab::Slab;
use std::{ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
@@ -11,9 +13,6 @@ use std::{
rc::Rc, rc::Rc,
}; };
use crate::{innerlude::*, unsafe_utils::extend_vnode};
use bumpalo::{boxed::Box as BumpBox, Bump};
pub(crate) type FcSlot = *const (); pub(crate) type FcSlot = *const ();
pub(crate) struct Heuristic { pub(crate) struct Heuristic {
@@ -26,14 +25,13 @@ pub(crate) struct Heuristic {
// //
// has an internal heuristics engine to pre-allocate arenas to the right size // has an internal heuristics engine to pre-allocate arenas to the right size
pub(crate) struct ScopeArena { pub(crate) struct ScopeArena {
pub pending_futures: RefCell<FxHashSet<ScopeId>>, pub scope_gen: Cell<usize>,
pub scope_counter: Cell<usize>,
pub sender: UnboundedSender<SchedulerMsg>,
pub bump: Bump, pub bump: Bump,
pub scopes: RefCell<FxHashMap<ScopeId, *mut ScopeState>>, pub scopes: RefCell<FxHashMap<ScopeId, *mut ScopeState>>,
pub heuristics: RefCell<FxHashMap<FcSlot, Heuristic>>, pub heuristics: RefCell<FxHashMap<FcSlot, Heuristic>>,
pub free_scopes: RefCell<Vec<*mut ScopeState>>, pub free_scopes: RefCell<Vec<*mut ScopeState>>,
pub nodes: RefCell<Slab<*const VNode<'static>>>, pub nodes: RefCell<Slab<*const VNode<'static>>>,
pub tasks: Rc<TaskQueue>,
} }
impl ScopeArena { impl ScopeArena {
@@ -61,14 +59,13 @@ impl ScopeArena {
debug_assert_eq!(root_id, 0); debug_assert_eq!(root_id, 0);
Self { Self {
scope_counter: Cell::new(0), scope_gen: Cell::new(0),
bump, bump,
pending_futures: RefCell::new(FxHashSet::default()),
scopes: RefCell::new(FxHashMap::default()), scopes: RefCell::new(FxHashMap::default()),
heuristics: RefCell::new(FxHashMap::default()), heuristics: RefCell::new(FxHashMap::default()),
free_scopes: RefCell::new(Vec::new()), free_scopes: RefCell::new(Vec::new()),
nodes: RefCell::new(nodes), nodes: RefCell::new(nodes),
sender, tasks: TaskQueue::new(sender),
} }
} }
@@ -92,8 +89,8 @@ impl ScopeArena {
subtree: u32, subtree: u32,
) -> ScopeId { ) -> ScopeId {
// Increment the ScopeId system. ScopeIDs are never reused // Increment the ScopeId system. ScopeIDs are never reused
let new_scope_id = ScopeId(self.scope_counter.get()); let new_scope_id = ScopeId(self.scope_gen.get());
self.scope_counter.set(self.scope_counter.get() + 1); self.scope_gen.set(self.scope_gen.get() + 1);
// Get the height of the scope // Get the height of the scope
let height = parent_scope let height = parent_scope
@@ -129,9 +126,9 @@ impl ScopeArena {
height, height,
container, container,
new_scope_id, new_scope_id,
self.sender.clone(),
parent_scope, parent_scope,
vcomp, vcomp,
self.tasks.clone(),
self.heuristics self.heuristics
.borrow() .borrow()
.get(&fc_ptr) .get(&fc_ptr)
@@ -236,15 +233,11 @@ impl ScopeArena {
// - We've dropped all references to the wip bump frame with "ensure_drop_safety" // - We've dropped all references to the wip bump frame with "ensure_drop_safety"
unsafe { scope.reset_wip_frame() }; unsafe { scope.reset_wip_frame() };
let mut items = scope.items.borrow_mut(); let items = scope.items.borrow();
// just forget about our suspended nodes while we're at it
items.tasks.clear();
// guarantee that we haven't screwed up - there should be no latent references anywhere // guarantee that we haven't screwed up - there should be no latent references anywhere
debug_assert!(items.listeners.is_empty()); debug_assert!(items.listeners.is_empty());
debug_assert!(items.borrowed_props.is_empty()); debug_assert!(items.borrowed_props.is_empty());
debug_assert!(items.tasks.is_empty());
} }
// safety: this is definitely not dropped // safety: this is definitely not dropped
@@ -263,10 +256,6 @@ impl ScopeArena {
let props = scope.props.borrow(); let props = scope.props.borrow();
let render = props.as_ref().unwrap(); let render = props.as_ref().unwrap();
if let Some(node) = render.render(scope) { if let Some(node) = render.render(scope) {
if !scope.items.borrow().tasks.is_empty() {
self.pending_futures.borrow_mut().insert(id);
}
let frame = scope.wip_frame(); let frame = scope.wip_frame();
let node = frame.bump.alloc(node); let node = frame.bump.alloc(node);
frame.node.set(unsafe { extend_vnode(node) }); frame.node.set(unsafe { extend_vnode(node) });
@@ -366,26 +355,6 @@ impl<P> Clone for Scope<'_, P> {
} }
} }
impl<'src, P> Scope<'src, P> {
pub fn register_task(self, task: impl Future<Output = ()> + 'src) {
let fut: &'src mut dyn Future<Output = ()> = self.scope.bump().alloc(task);
let boxed_fut: BumpBox<'src, dyn Future<Output = ()> + 'src> =
unsafe { BumpBox::from_raw(fut) };
let pinned_fut: Pin<BumpBox<'src, _>> = boxed_fut.into();
// erase the 'src lifetime for self-referential storage
// todo: provide a miri test around this
// concerned about segfaulting
let self_ref_fut = unsafe { std::mem::transmute(pinned_fut) };
self.sender
.unbounded_send(SchedulerMsg::NewTask(self.our_arena_idx))
.unwrap();
self.scope.items.borrow_mut().tasks.push(self_ref_fut);
}
}
impl<'a, P> std::ops::Deref for Scope<'a, P> { impl<'a, P> std::ops::Deref for Scope<'a, P> {
// rust will auto deref again to the original 'a lifetime at the call site // rust will auto deref again to the original 'a lifetime at the call site
type Target = &'a ScopeState; type Target = &'a ScopeState;
@@ -402,6 +371,14 @@ impl<'a, P> std::ops::Deref for Scope<'a, P> {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct ScopeId(pub usize); pub struct ScopeId(pub usize);
/// A task's unique identifier.
///
/// `TaskId` is a `usize` that is unique across the entire VirtualDOM and across time. TaskIDs will never be reused
/// once a Task has been completed.
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct TaskId(pub usize);
/// Every component in Dioxus is represented by a `ScopeState`. /// Every component in Dioxus is represented by a `ScopeState`.
/// ///
/// Scopes contain the state for hooks, the component's props, and other lifecycle information. /// Scopes contain the state for hooks, the component's props, and other lifecycle information.
@@ -416,12 +393,10 @@ pub struct ScopeState {
pub(crate) container: ElementId, pub(crate) container: ElementId,
pub(crate) our_arena_idx: ScopeId, pub(crate) our_arena_idx: ScopeId,
pub(crate) height: u32, pub(crate) height: u32,
pub(crate) sender: UnboundedSender<SchedulerMsg>,
// todo: subtrees // todo: subtrees
pub(crate) is_subtree_root: Cell<bool>, pub(crate) is_subtree_root: Cell<bool>,
pub(crate) subtree: Cell<u32>, pub(crate) subtree: Cell<u32>,
pub(crate) props: RefCell<Option<Box<dyn AnyProps>>>, pub(crate) props: RefCell<Option<Box<dyn AnyProps>>>,
// nodes, items // nodes, items
@@ -436,12 +411,12 @@ pub struct ScopeState {
// shared state -> todo: move this out of scopestate // shared state -> todo: move this out of scopestate
pub(crate) shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>, pub(crate) shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
pub(crate) tasks: Rc<TaskQueue>,
} }
pub struct SelfReferentialItems<'a> { pub struct SelfReferentialItems<'a> {
pub(crate) listeners: Vec<&'a Listener<'a>>, pub(crate) listeners: Vec<&'a Listener<'a>>,
pub(crate) borrowed_props: Vec<&'a VComponent<'a>>, pub(crate) borrowed_props: Vec<&'a VComponent<'a>>,
pub(crate) tasks: Vec<Pin<BumpBox<'a, dyn Future<Output = ()>>>>,
} }
// Public methods exposed to libraries and components // Public methods exposed to libraries and components
@@ -450,13 +425,12 @@ impl ScopeState {
height: u32, height: u32,
container: ElementId, container: ElementId,
our_arena_idx: ScopeId, our_arena_idx: ScopeId,
sender: UnboundedSender<SchedulerMsg>,
parent_scope: Option<*mut ScopeState>, parent_scope: Option<*mut ScopeState>,
vcomp: Box<dyn AnyProps>, vcomp: Box<dyn AnyProps>,
tasks: Rc<TaskQueue>,
(node_capacity, hook_capacity): (usize, usize), (node_capacity, hook_capacity): (usize, usize),
) -> Self { ) -> Self {
ScopeState { ScopeState {
sender,
container, container,
our_arena_idx, our_arena_idx,
parent_scope, parent_scope,
@@ -470,12 +444,12 @@ impl ScopeState {
generation: 0.into(), generation: 0.into(),
tasks,
shared_contexts: Default::default(), shared_contexts: Default::default(),
items: RefCell::new(SelfReferentialItems { items: RefCell::new(SelfReferentialItems {
listeners: Default::default(), listeners: Default::default(),
borrowed_props: Default::default(), borrowed_props: Default::default(),
tasks: Default::default(),
}), }),
hook_arena: Bump::new(), hook_arena: Bump::new(),
@@ -590,7 +564,7 @@ impl ScopeState {
/// ///
/// ## Notice: you should prefer using prepare_update and get_scope_id /// ## Notice: you should prefer using prepare_update and get_scope_id
pub fn schedule_update(&self) -> Rc<dyn Fn() + 'static> { pub fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
let (chan, id) = (self.sender.clone(), self.scope_id()); let (chan, id) = (self.tasks.sender.clone(), self.scope_id());
Rc::new(move || { Rc::new(move || {
let _ = chan.unbounded_send(SchedulerMsg::Immediate(id)); let _ = chan.unbounded_send(SchedulerMsg::Immediate(id));
}) })
@@ -602,7 +576,7 @@ impl ScopeState {
/// ///
/// This method should be used when you want to schedule an update for a component /// This method should be used when you want to schedule an update for a component
pub fn schedule_update_any(&self) -> Rc<dyn Fn(ScopeId)> { pub fn schedule_update_any(&self) -> Rc<dyn Fn(ScopeId)> {
let chan = self.sender.clone(); let chan = self.tasks.sender.clone();
Rc::new(move |id| { Rc::new(move |id| {
let _ = chan.unbounded_send(SchedulerMsg::Immediate(id)); let _ = chan.unbounded_send(SchedulerMsg::Immediate(id));
}) })
@@ -619,7 +593,10 @@ impl ScopeState {
/// ///
/// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted. /// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
pub fn needs_update_any(&self, id: ScopeId) { pub fn needs_update_any(&self, id: ScopeId) {
let _ = self.sender.unbounded_send(SchedulerMsg::Immediate(id)); let _ = self
.tasks
.sender
.unbounded_send(SchedulerMsg::Immediate(id));
} }
/// Get the Root Node of this scope /// Get the Root Node of this scope
@@ -682,38 +659,19 @@ impl ScopeState {
/// Pushes the future onto the poll queue to be polled after the component renders. /// Pushes the future onto the poll queue to be polled after the component renders.
/// ///
/// The future is forcibly dropped if the component is not ready by the next render /// The future is forcibly dropped if the component is not ready by the next render
pub fn push_task<'src, F>(&'src self, fut: F) -> usize pub fn push_future(&self, fut: impl Future<Output = ()> + 'static) -> TaskId {
where // wake up the scheduler if it is sleeping
F: Future<Output = ()>, self.tasks
F::Output: 'src, .sender
F: 'src,
{
// wrap it in a type that will actually drop the contents
//
// Safety: we just made the pointer above and will promise not to alias it!
// The main reason we do this through from_raw is because Bumpalo's box does
// not support unsized coercion
let fut: &'src mut dyn Future<Output = ()> = self.bump().alloc(fut);
let boxed_fut: BumpBox<'src, dyn Future<Output = ()> + 'src> =
unsafe { BumpBox::from_raw(fut) };
let pinned_fut: Pin<BumpBox<'src, _>> = boxed_fut.into();
// erase the 'src lifetime for self-referential storage
// todo: provide a miri test around this
// concerned about segfaulting
let self_ref_fut = unsafe {
std::mem::transmute::<Pin<BumpBox<'src, _>>, Pin<BumpBox<'static, _>>>(pinned_fut)
};
self.sender
.unbounded_send(SchedulerMsg::NewTask(self.our_arena_idx)) .unbounded_send(SchedulerMsg::NewTask(self.our_arena_idx))
.unwrap(); .unwrap();
// Push the future into the tasks self.tasks.push_fut(fut)
let mut items = self.items.borrow_mut(); }
items.tasks.push(self_ref_fut);
items.tasks.len() - 1 // todo: attach some state to the future to know if we should poll it
pub fn remove_future(&self, id: TaskId) {
self.tasks.remove_fut(id);
} }
/// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator. /// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
@@ -834,11 +792,6 @@ impl ScopeState {
self.generation.set(self.generation.get() + 1); self.generation.set(self.generation.get() + 1);
} }
/// Get the [`Bump`] of the WIP frame.
pub(crate) fn bump(&self) -> &Bump {
&self.wip_frame().bump
}
// todo: disable bookkeeping on drop (unncessary) // todo: disable bookkeeping on drop (unncessary)
pub(crate) fn reset(&mut self) { pub(crate) fn reset(&mut self) {
// first: book keaping // first: book keaping
@@ -855,11 +808,9 @@ impl ScopeState {
let SelfReferentialItems { let SelfReferentialItems {
borrowed_props, borrowed_props,
listeners, listeners,
tasks,
} = self.items.get_mut(); } = self.items.get_mut();
borrowed_props.clear(); borrowed_props.clear();
listeners.clear(); listeners.clear();
tasks.clear();
self.frames[0].reset(); self.frames[0].reset();
self.frames[1].reset(); self.frames[1].reset();
@@ -905,6 +856,43 @@ impl BumpFrame {
} }
} }
pub(crate) struct TaskQueue {
pub(crate) tasks: RefCell<FxHashMap<TaskId, InnerTask>>,
gen: Cell<usize>,
sender: UnboundedSender<SchedulerMsg>,
}
pub(crate) type InnerTask = Pin<Box<dyn Future<Output = ()>>>;
impl TaskQueue {
fn new(sender: UnboundedSender<SchedulerMsg>) -> Rc<Self> {
Rc::new(Self {
tasks: RefCell::new(FxHashMap::default()),
gen: Cell::new(0),
sender,
})
}
fn push_fut(&self, task: impl Future<Output = ()> + 'static) -> TaskId {
let pinned = Box::pin(task);
let id = self.gen.get();
self.gen.set(id + 1);
let tid = TaskId(id);
self.tasks.borrow_mut().insert(tid, pinned);
tid
}
fn remove_fut(&self, id: TaskId) {
if let Ok(mut tasks) = self.tasks.try_borrow_mut() {
let _ = tasks.remove(&id);
} else {
// todo: it should be okay to remote a fut while the queue is being polled
// However, it's not currently possible to do that.
log::debug!("Unable to remove task from task queue. This is probably a bug.");
}
}
pub(crate) fn has_tasks(&self) -> bool {
!self.tasks.borrow().is_empty()
}
}
#[test] #[test]
fn sizeof() { fn sizeof() {
dbg!(std::mem::size_of::<ScopeState>()); dbg!(std::mem::size_of::<ScopeState>());
+16 -30
View File
@@ -7,7 +7,6 @@ use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
use futures_util::{Future, StreamExt}; use futures_util::{Future, StreamExt};
use fxhash::FxHashSet; use fxhash::FxHashSet;
use indexmap::IndexSet; use indexmap::IndexSet;
use smallvec::SmallVec;
use std::{any::Any, collections::VecDeque, iter::FromIterator, pin::Pin, sync::Arc, task::Poll}; use std::{any::Any, collections::VecDeque, iter::FromIterator, pin::Pin, sync::Arc, task::Poll};
/// A virtual node s ystem that progresses user events and diffs UI trees. /// A virtual node s ystem that progresses user events and diffs UI trees.
@@ -312,16 +311,16 @@ impl VirtualDom {
} }
if self.pending_messages.is_empty() { if self.pending_messages.is_empty() {
if self.scopes.pending_futures.borrow().is_empty() { if self.scopes.tasks.has_tasks() {
self.pending_messages
.push_front(self.channel.1.next().await.unwrap());
} else {
use futures_util::future::{select, Either}; use futures_util::future::{select, Either};
match select(PollTasks(&mut self.scopes), self.channel.1.next()).await { match select(PollTasks(&mut self.scopes), self.channel.1.next()).await {
Either::Left((_, _)) => {} Either::Left((_, _)) => {}
Either::Right((msg, _)) => self.pending_messages.push_front(msg.unwrap()), Either::Right((msg, _)) => self.pending_messages.push_front(msg.unwrap()),
} }
} else {
self.pending_messages
.push_front(self.channel.1.next().await.unwrap());
} }
} }
@@ -345,8 +344,8 @@ impl VirtualDom {
pub fn process_message(&mut self, msg: SchedulerMsg) { pub fn process_message(&mut self, msg: SchedulerMsg) {
match msg { match msg {
SchedulerMsg::NewTask(id) => { SchedulerMsg::NewTask(_id) => {
self.scopes.pending_futures.borrow_mut().insert(id); // uh, not sure? I think end up re-polling it anyways
} }
SchedulerMsg::Event(event) => { SchedulerMsg::Event(event) => {
if let Some(element) = event.element { if let Some(element) = event.element {
@@ -705,33 +704,20 @@ impl<'a> Future for PollTasks<'a> {
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
let mut all_pending = true; let mut all_pending = true;
let mut unfinished_tasks: SmallVec<[_; 10]> = smallvec::smallvec![]; let mut tasks = self.0.tasks.tasks.borrow_mut();
let mut scopes_to_clear: SmallVec<[_; 10]> = smallvec::smallvec![]; let mut to_remove = vec![];
// Poll every scope manually // this would be better served by retain
for fut in self.0.pending_futures.borrow().iter().copied() { for (id, task) in tasks.iter_mut() {
let scope = self.0.get_scope(fut).expect("Scope should never be moved"); if task.as_mut().poll(cx).is_ready() {
to_remove.push(*id);
let mut items = scope.items.borrow_mut(); } else {
all_pending = false;
// really this should just be retain_mut but that doesn't exist yet
while let Some(mut task) = items.tasks.pop() {
if task.as_mut().poll(cx).is_ready() {
all_pending = false
} else {
unfinished_tasks.push(task);
}
} }
if unfinished_tasks.is_empty() {
scopes_to_clear.push(fut);
}
items.tasks.extend(unfinished_tasks.drain(..));
} }
for scope in scopes_to_clear { for id in to_remove {
self.0.pending_futures.borrow_mut().remove(&scope); tasks.remove(&id);
} }
// Resolve the future if any singular task is ready // Resolve the future if any singular task is ready
+1 -1
View File
@@ -19,7 +19,7 @@ fn app(cx: Scope<()>) -> Element {
let mut count = use_state(&cx, || 0); let mut count = use_state(&cx, || 0);
log::debug!("count is {:?}", count); log::debug!("count is {:?}", count);
cx.push_task(|| async move { cx.push_future(|| async move {
tokio::time::sleep(Duration::from_millis(1000)).await; tokio::time::sleep(Duration::from_millis(1000)).await;
println!("count is now {:?}", count); println!("count is now {:?}", count);
count += 1; count += 1;
+5 -2
View File
@@ -10,5 +10,8 @@ pub use use_shared_state::*;
mod usecoroutine; mod usecoroutine;
pub use usecoroutine::*; pub use usecoroutine::*;
mod usemodel; mod usefuture;
pub use usemodel::*; pub use usefuture::*;
// mod usemodel;
// pub use usemodel::*;
+8 -11
View File
@@ -1,10 +1,6 @@
use dioxus_core::ScopeState; use dioxus_core::ScopeState;
use std::future::Future; use std::future::Future;
use std::{ use std::{cell::Cell, pin::Pin, rc::Rc};
cell::{Cell, RefCell},
pin::Pin,
rc::Rc,
};
/* /*
@@ -24,8 +20,7 @@ pub fn use_coroutine<'a, F>(
create_future: impl FnOnce() -> F, create_future: impl FnOnce() -> F,
) -> CoroutineHandle<'a> ) -> CoroutineHandle<'a>
where where
F: Future<Output = ()>, F: Future<Output = ()> + 'static,
F: 'static,
{ {
cx.use_hook( cx.use_hook(
move |_| State { move |_| State {
@@ -35,11 +30,13 @@ where
}, },
|state| { |state| {
let f = create_future(); let f = create_future();
state.pending_fut.set(Some(Box::pin(f))); let id = cx.push_future(f);
if let Some(fut) = state.running_fut.as_mut() { // state.pending_fut.set(Some(Box::pin(f)));
cx.push_task(fut);
} // if let Some(fut) = state.running_fut.as_mut() {
// cx.push_future(fut);
// }
// if let Some(fut) = state.running_fut.take() { // if let Some(fut) = state.running_fut.take() {
// state.running.set(true); // state.running.set(true);
+34
View File
@@ -0,0 +1,34 @@
use dioxus_core::{ScopeState, TaskId};
use std::{cell::Cell, future::Future};
pub fn use_future<'a, T: 'static, F: Future<Output = T>>(
cx: &'a ScopeState,
f: impl FnOnce() -> F,
) -> FutureHandle<'a, T> {
cx.use_hook(
|_| {
//
UseFutureInner {
needs_regen: true,
task: None,
}
},
|_| {
//
FutureHandle {
cx,
value: Cell::new(None),
}
},
)
}
struct UseFutureInner {
needs_regen: bool,
task: Option<TaskId>,
}
pub struct FutureHandle<'a, T> {
cx: &'a ScopeState,
value: Cell<Option<T>>,
}
+1 -1
View File
@@ -17,7 +17,7 @@ fn main() {
static App: Component<()> = |cx| { static App: Component<()> = |cx| {
let mut count = use_state(&cx, || 0); let mut count = use_state(&cx, || 0);
cx.push_task(|| async move { cx.push_future(|| async move {
TimeoutFuture::new(100).await; TimeoutFuture::new(100).await;
count += 1; count += 1;
}); });