Fix borrowmut error in nested spawns

This commit is contained in:
Jonathan Kelley committed 2024-01-19 22:01:21 -08:00
1 parent bcca307a9a
commit 728a26a2cc
5 files changed
+37 -26

No files matched your search

+1 -1
View File
@@ -27,7 +27,7 @@ pub struct Runtime {
pub(crate) rendering: Cell<bool>, pub(crate) rendering: Cell<bool>,
/// Tasks created with cx.spawn /// Tasks created with cx.spawn
pub(crate) tasks: RefCell<slab::Slab<LocalTask>>, pub(crate) tasks: RefCell<slab::Slab<Rc<LocalTask>>>,
/// Queued tasks that are waiting to be polled /// Queued tasks that are waiting to be polled
pub(crate) queued_tasks: Rc<RefCell<VecDeque<Task>>>, pub(crate) queued_tasks: Rc<RefCell<VecDeque<Task>>>,
+30 -24
View File
@@ -1,11 +1,11 @@
use crate::innerlude::{remove_future, spawn, Runtime}; use crate::innerlude::{remove_future, spawn, Runtime};
use crate::ScopeId; use crate::ScopeId;
use futures_util::task::ArcWake; use futures_util::task::ArcWake;
use std::cell::RefCell;
use std::future::Future; use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use std::task::Waker; use std::task::Waker;
use std::{cell::RefCell, rc::Rc};
/// A task's unique identifier. /// A task's unique identifier.
/// ///
@@ -47,21 +47,32 @@ impl Runtime {
/// Spawning a future onto the root scope will cause it to be dropped when the root component is dropped - which /// 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 VirtualDom itself has been dropped. /// will only occur when the VirtualDom itself has been dropped.
pub fn spawn(&self, scope: ScopeId, task: impl Future<Output = ()> + 'static) -> Task { pub fn spawn(&self, scope: ScopeId, task: impl Future<Output = ()> + 'static) -> Task {
let mut tasks = self.tasks.borrow_mut(); // Insert the task, temporarily holding a borrow on the tasks map
let (task, task_id) = {
let mut tasks = self.tasks.borrow_mut();
let entry = tasks.vacant_entry(); let entry = tasks.vacant_entry();
let task_id = Task(entry.key()); let task_id = Task(entry.key());
let task = LocalTask { let task = Rc::new(LocalTask {
parent: self.current_task(), parent: self.current_task(),
task: RefCell::new(Box::pin(task)), task: RefCell::new(Box::pin(task)),
scope, scope,
waker: futures_util::task::waker(Arc::new(LocalTaskHandle { waker: futures_util::task::waker(Arc::new(LocalTaskHandle {
id: task_id, id: task_id,
tx: self.sender.clone(), tx: self.sender.clone(),
})), })),
});
entry.insert(task.clone());
(task, task_id)
}; };
// Get a borrow on the task, holding no borrows on the tasks map
debug_assert!(self.tasks.try_borrow_mut().is_ok());
debug_assert!(task.task.try_borrow_mut().is_ok());
let mut cx = std::task::Context::from_waker(&task.waker); let mut cx = std::task::Context::from_waker(&task.waker);
if !task.task.borrow_mut().as_mut().poll(&mut cx).is_ready() { if !task.task.borrow_mut().as_mut().poll(&mut cx).is_ready() {
@@ -70,23 +81,18 @@ impl Runtime {
.expect("Scheduler should exist"); .expect("Scheduler should exist");
} }
entry.insert(task);
task_id task_id
} }
pub(crate) fn handle_task_wakeup(&self, id: Task) { pub(crate) fn handle_task_wakeup(&self, id: Task) {
let mut tasks = self.tasks.borrow_mut(); let task = self.tasks.borrow().get(id.0).cloned();
let task = match tasks.get(id.0) { // The task was removed from the scheduler, so we can just ignore it
Some(task) => task, let Some(task) = task else {
// The task was removed from the scheduler, so we can just ignore it return;
None => return,
}; };
use std::task::Context; let mut cx = std::task::Context::from_waker(&task.waker);
let mut cx = Context::from_waker(&task.waker);
// update the scope stack // update the scope stack
self.scope_stack.borrow_mut().push(task.scope); self.scope_stack.borrow_mut().push(task.scope);
@@ -103,7 +109,7 @@ impl Runtime {
.remove(&id); .remove(&id);
// Remove it from the scheduler // Remove it from the scheduler
tasks.try_remove(id.0); self.tasks.borrow_mut().try_remove(id.0);
} }
// Remove the scope from the stack // Remove the scope from the stack
@@ -120,7 +126,7 @@ impl Runtime {
/// Drop the future with the given TaskId /// Drop the future with the given TaskId
/// ///
/// This does not abort the task, so you'll want to wrap it in an abort handle if that's important to you /// This does not abort the task, so you'll want to wrap it in an abort handle if that's important to you
pub(crate) fn remove_task(&self, id: Task) -> Option<LocalTask> { pub(crate) fn remove_task(&self, id: Task) -> Option<Rc<LocalTask>> {
let task = self.tasks.borrow_mut().try_remove(id.0); let task = self.tasks.borrow_mut().try_remove(id.0);
// Remove the task from the queued tasks so we don't poll a different task with the same id // Remove the task from the queued tasks so we don't poll a different task with the same id
+5
View File
@@ -138,6 +138,7 @@ impl<Cfg: Default + 'static, ContextFn: ?Sized> LaunchBuilder<Cfg, ContextFn> {
.platform_config .platform_config
.and_then(|c| c.downcast().ok()) .and_then(|c| c.downcast().ok())
.unwrap_or_default(); .unwrap_or_default();
(self.launch_fn)(app, self.contexts, *cfg) (self.launch_fn)(app, self.contexts, *cfg)
} }
} }
@@ -145,12 +146,16 @@ impl<Cfg: Default + 'static, ContextFn: ?Sized> LaunchBuilder<Cfg, ContextFn> {
mod current_platform { mod current_platform {
#[cfg(all(feature = "desktop", not(feature = "fullstack")))] #[cfg(all(feature = "desktop", not(feature = "fullstack")))]
pub use dioxus_desktop::launch::*; pub use dioxus_desktop::launch::*;
#[cfg(feature = "fullstack")] #[cfg(feature = "fullstack")]
pub use dioxus_fullstack::launch::*; pub use dioxus_fullstack::launch::*;
#[cfg(all(feature = "web", not(any(feature = "desktop", feature = "fullstack"))))] #[cfg(all(feature = "web", not(any(feature = "desktop", feature = "fullstack"))))]
pub use dioxus_web::launch::*; pub use dioxus_web::launch::*;
#[cfg(not(any(feature = "desktop", feature = "web", feature = "fullstack")))] #[cfg(not(any(feature = "desktop", feature = "web", feature = "fullstack")))]
pub type Config = (); pub type Config = ();
#[cfg(not(any(feature = "desktop", feature = "web", feature = "fullstack")))] #[cfg(not(any(feature = "desktop", feature = "web", feature = "fullstack")))]
pub fn launch( pub fn launch(
root: fn() -> dioxus_core::Element, root: fn() -> dioxus_core::Element,
@@ -48,6 +48,7 @@ async fn get_server_data() -> Result<String, ServerFnError> {
fn main() { fn main() {
#[cfg(feature = "web")] #[cfg(feature = "web")]
tracing_wasm::set_as_global_default(); tracing_wasm::set_as_global_default();
#[cfg(feature = "ssr")] #[cfg(feature = "ssr")]
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
-1
View File
@@ -1,6 +1,5 @@
//! This module contains the `launch` function, which is the main entry point for dioxus fullstack //! This module contains the `launch` function, which is the main entry point for dioxus fullstack
use core::panic;
use std::any::Any; use std::any::Any;
use dioxus_lib::prelude::{Element, VirtualDom}; use dioxus_lib::prelude::{Element, VirtualDom};