Skip to content

Commit

Permalink
fix and test spawn_forever (#2216)
Browse files Browse the repository at this point in the history
  • Loading branch information
ealmloff authored Apr 2, 2024
1 parent 947b23b commit 5df333f
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 9 deletions.
4 changes: 3 additions & 1 deletion packages/core/src/global_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,10 @@ pub fn spawn(fut: impl Future<Output = ()> + 'static) -> Task {
/// 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.
///
/// **This will run the task in the root scope. Any calls to global methods inside the future (including `context`) will be run in the root scope.**
pub fn spawn_forever(fut: impl Future<Output = ()> + 'static) -> Option<Task> {
Runtime::with_current_scope(|cx| cx.spawn_forever(fut))
Runtime::with_scope(ScopeId::ROOT, |cx| cx.spawn(fut))
}

/// Informs the scheduler that this task is no longer needed and should be removed.
Expand Down
8 changes: 0 additions & 8 deletions packages/core/src/scope_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,14 +265,6 @@ impl Scope {
id
}

/// 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<Output = ()> + 'static) -> Task {
// The root scope will never be unmounted so we can just add the task at the top of the app
Runtime::with(|rt| rt.spawn(self.id, fut)).expect("Runtime to exist")
}

/// Mark this component as suspended on a specific task and then return None
pub fn suspend(&self, task: Task) -> Option<Element> {
self.last_suspendable_task.set(Some(task));
Expand Down
41 changes: 41 additions & 0 deletions packages/core/tests/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,47 @@ async fn running_async() {
);
}

#[tokio::test]
async fn spawn_forever_persists() {
use std::sync::atomic::Ordering;
static POLL_COUNT: AtomicUsize = AtomicUsize::new(0);

fn app() -> Element {
if generation() > 0 {
rsx!(div {})
} else {
needs_update();
rsx!(Child {})
}
}

#[component]
fn Child() -> Element {
spawn_forever(async move {
loop {
POLL_COUNT.fetch_add(1, Ordering::Relaxed);
tokio::time::sleep(Duration::from_millis(50)).await;
}
});

rsx!(div {})
}

let mut dom = VirtualDom::new(app);

dom.rebuild(&mut dioxus_core::NoOpMutations);
dom.render_immediate(&mut dioxus_core::NoOpMutations);

tokio::select! {
_ = dom.wait_for_work() => {}
_ = tokio::time::sleep(Duration::from_millis(500)) => {}
};

// By the time the tasks are finished, we should've accumulated ticks from two tasks
// Be warned that by setting the delay to too short, tokio might not schedule in the tasks
assert_eq!(POLL_COUNT.load(Ordering::Relaxed), 10);
}

/// Prove that yield_now doesn't cause a deadlock
#[tokio::test]
async fn yield_now_works() {
Expand Down

0 comments on commit 5df333f

Please sign in to comment.