From 69ca1fd8a702fdc596411cea5d865f38b946affd Mon Sep 17 00:00:00 2001 From: "kidkool850@gmail.com" Date: Mon, 11 Dec 2023 16:01:40 -0600 Subject: [PATCH] Make cx.spawn() return an Option, returns None if finished immediately --- examples/compose.rs | 11 +++-------- packages/core/src/scheduler/task.rs | 19 ++++++++++++++----- packages/core/src/scope_context.rs | 16 ++++++++-------- packages/core/src/scopes.rs | 4 ++-- packages/fullstack/src/hooks/server_future.rs | 4 ++-- packages/hooks/src/use_coroutine.rs | 10 ++++------ packages/hooks/src/use_effect.rs | 7 +++---- packages/hooks/src/use_future.rs | 4 ++-- 8 files changed, 38 insertions(+), 37 deletions(-) diff --git a/examples/compose.rs b/examples/compose.rs index 3ee6d36ae..4206383d3 100644 --- a/examples/compose.rs +++ b/examples/compose.rs @@ -19,7 +19,8 @@ fn app(cx: Scope) -> Element { emails_sent.write().push(message); } } - }); + }) + .unwrap(); cx.render(rsx! { div { @@ -27,13 +28,7 @@ fn app(cx: Scope) -> Element { button { onclick: move |_| { - let dom = VirtualDom::new_with_props(compose, ComposeProps { - app_tx: tx.clone() - }); - - // this returns a weak reference to the other window - // Be careful not to keep a strong reference to the other window or it will never be dropped - // and the window will never close. + let dom = VirtualDom::new_with_props(compose, ComposeProps { app_tx: tx.clone() }); window.new_window(dom, Default::default()); }, "Click to compose a new email" diff --git a/packages/core/src/scheduler/task.rs b/packages/core/src/scheduler/task.rs index fc668b364..7f6ae8021 100644 --- a/packages/core/src/scheduler/task.rs +++ b/packages/core/src/scheduler/task.rs @@ -19,7 +19,7 @@ pub struct TaskId(pub usize); /// the task itself is the waker pub(crate) struct LocalTask { pub scope: ScopeId, - pub(super) task: RefCell + 'static>>>, + pub task: RefCell + 'static>>>, pub waker: Waker, } @@ -33,7 +33,11 @@ impl Scheduler { /// /// Spawning a future onto the root scope will cause it to be dropped when the root component is dropped - which /// will only occur when the VirtuaalDom itself has been dropped. - pub fn spawn(&self, scope: ScopeId, task: impl Future + 'static) -> TaskId { + pub fn spawn( + &self, + scope: ScopeId, + task: impl Future + 'static, + ) -> Option { let mut tasks = self.tasks.borrow_mut(); let entry = tasks.vacant_entry(); @@ -48,20 +52,25 @@ impl Scheduler { })), }; + let mut cx = std::task::Context::from_waker(&task.waker); + if task.task.borrow_mut().as_mut().poll(&mut cx).is_ready() { + return None; + } + entry.insert(task); self.sender .unbounded_send(SchedulerMsg::TaskNotified(task_id)) .expect("Scheduler should exist"); - task_id + Some(task_id) } /// Drop the future with the given TaskId /// /// This does not abort the task, so you'll want to wrap it in an aborthandle if that's important to you - pub fn remove(&self, id: TaskId) { - self.tasks.borrow_mut().try_remove(id.0); + pub fn remove(&self, id: TaskId) -> Option { + self.tasks.borrow_mut().try_remove(id.0) } } diff --git a/packages/core/src/scope_context.rs b/packages/core/src/scope_context.rs index 59bed3b1f..45df068f4 100644 --- a/packages/core/src/scope_context.rs +++ b/packages/core/src/scope_context.rs @@ -214,10 +214,10 @@ impl ScopeContext { } /// Pushes the future onto the poll queue to be polled after the component renders. - pub fn push_future(&self, fut: impl Future + 'static) -> TaskId { - let id = self.tasks.spawn(self.id, fut); + pub fn push_future(&self, fut: impl Future + 'static) -> Option { + let id = self.tasks.spawn(self.id, fut)?; self.spawned_tasks.borrow_mut().insert(id); - id + Some(id) } /// Spawns the future but does not return the [`TaskId`] @@ -228,9 +228,9 @@ impl ScopeContext { /// Spawn a future that Dioxus won't clean up when this component is unmounted /// /// This is good for tasks that need to be run after the component has been dropped. - pub fn spawn_forever(&self, fut: impl Future + 'static) -> TaskId { + pub fn spawn_forever(&self, fut: impl Future + 'static) -> Option { // The root scope will never be unmounted so we can just add the task at the top of the app - let id = self.tasks.spawn(ScopeId::ROOT, fut); + let id = self.tasks.spawn(ScopeId::ROOT, fut)?; // wake up the scheduler if it is sleeping self.tasks @@ -240,7 +240,7 @@ impl ScopeContext { self.spawned_tasks.borrow_mut().insert(id); - id + Some(id) } /// Informs the scheduler that this task is no longer needed and should be removed. @@ -339,7 +339,7 @@ pub fn throw(error: impl Debug + 'static) -> Option<()> { /// Pushes the future onto the poll queue to be polled after the component renders. pub fn push_future(fut: impl Future + 'static) -> Option { - with_current_scope(|cx| cx.push_future(fut)) + with_current_scope(|cx| cx.push_future(fut)).flatten() } /// Spawns the future but does not return the [`TaskId`] @@ -351,7 +351,7 @@ pub fn spawn(fut: impl Future + 'static) { /// /// This is good for tasks that need to be run after the component has been dropped. pub fn spawn_forever(fut: impl Future + 'static) -> Option { - with_current_scope(|cx| cx.spawn_forever(fut)) + with_current_scope(|cx| cx.spawn_forever(fut)).flatten() } /// Informs the scheduler that this task is no longer needed and should be removed. diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs index f0bbd776c..463217370 100644 --- a/packages/core/src/scopes.rs +++ b/packages/core/src/scopes.rs @@ -309,7 +309,7 @@ impl<'src> ScopeState { } /// Pushes the future onto the poll queue to be polled after the component renders. - pub fn push_future(&self, fut: impl Future + 'static) -> TaskId { + pub fn push_future(&self, fut: impl Future + 'static) -> Option { self.context().push_future(fut) } @@ -321,7 +321,7 @@ impl<'src> ScopeState { /// Spawn a future that Dioxus won't clean up when this component is unmounted /// /// This is good for tasks that need to be run after the component has been dropped. - pub fn spawn_forever(&self, fut: impl Future + 'static) -> TaskId { + pub fn spawn_forever(&self, fut: impl Future + 'static) -> Option { self.context().spawn_forever(fut) } diff --git a/packages/fullstack/src/hooks/server_future.rs b/packages/fullstack/src/hooks/server_future.rs index f8eaef963..8d9f09f71 100644 --- a/packages/fullstack/src/hooks/server_future.rs +++ b/packages/fullstack/src/hooks/server_future.rs @@ -76,7 +76,7 @@ where cx.remove_future(current); } - state.task.set(Some(cx.push_future(async move { + state.task.set(cx.push_future(async move { let data; #[cfg(feature = "ssr")] { @@ -94,7 +94,7 @@ where *value.borrow_mut() = Some(Box::new(data)); schedule_update(); - }))); + })); } if first_run { diff --git a/packages/hooks/src/use_coroutine.rs b/packages/hooks/src/use_coroutine.rs index f2577b90c..6de43a667 100644 --- a/packages/hooks/src/use_coroutine.rs +++ b/packages/hooks/src/use_coroutine.rs @@ -63,17 +63,15 @@ use std::future::Future; /// } /// }) /// ``` -pub fn use_coroutine(cx: &ScopeState, init: G) -> &Coroutine +pub fn use_coroutine(cx: &ScopeState, init: G) -> Option<&Coroutine> where M: 'static, G: FnOnce(UnboundedReceiver) -> F, F: Future + 'static, { - cx.use_hook(|| { - let (tx, rx) = futures_channel::mpsc::unbounded(); - let task = cx.push_future(init(rx)); - cx.provide_context(Coroutine { tx, task }) - }) + let (tx, rx) = futures_channel::mpsc::unbounded(); + let task = cx.push_future(init(rx))?; + Some(cx.use_hook(|| cx.provide_context(Coroutine { tx, task }))) } /// Get a handle to a coroutine higher in the tree diff --git a/packages/hooks/src/use_effect.rs b/packages/hooks/src/use_effect.rs index 6fb2fcf9e..49d8c6dd4 100644 --- a/packages/hooks/src/use_effect.rs +++ b/packages/hooks/src/use_effect.rs @@ -117,7 +117,7 @@ where T: Future + 'static, { fn apply(self, _: UseEffectCleanup, cx: &ScopeState) -> Option { - Some(cx.push_future(self)) + cx.push_future(self) } } @@ -129,11 +129,10 @@ where F: FnOnce() + 'static, { fn apply(self, oncleanup: UseEffectCleanup, cx: &ScopeState) -> Option { - let task = cx.push_future(async move { + cx.push_future(async move { let cleanup = self.await; *oncleanup.borrow_mut() = Some(Box::new(cleanup) as Box); - }); - Some(task) + }) } } diff --git a/packages/hooks/src/use_future.rs b/packages/hooks/src/use_future.rs index b9aa20c31..d3fdac1bc 100644 --- a/packages/hooks/src/use_future.rs +++ b/packages/hooks/src/use_future.rs @@ -49,10 +49,10 @@ where let val = val.clone(); let task = state.task.clone(); - state.task.set(Some(cx.push_future(async move { + state.task.set(cx.push_future(async move { val.set(Some(fut.await)); task.take(); - }))); + })); // Mark that we don't need to regenerate state.needs_regen.set(false);