From 517e519bdcc868d94a130e913403e5b2db4a59a3 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 30 Jan 2025 18:00:55 -0800 Subject: [PATCH] Make the gpui_tokio crate generic over the context it spawns (#23995) Part of #21092 Makes `Tokio::spawn` generic over any `AppContext`. Also removes a stray `model_context` I missed Release Notes: - N/A --- crates/gpui/src/app.rs | 19 ++++++++++-- crates/gpui/src/app/async_context.rs | 30 +++++++++++++++++++ .../src/app/{model_context.rs => context.rs} | 22 +++++++++++--- crates/gpui/src/app/test_context.rs | 29 ++++++++++++++++++ crates/gpui/src/gpui.rs | 12 +++++++- crates/gpui_macros/src/derive_app_context.rs | 14 +++++++++ crates/gpui_tokio/src/gpui_tokio.rs | 25 +++++++++------- 7 files changed, 133 insertions(+), 18 deletions(-) rename crates/gpui/src/app/{model_context.rs => context.rs} (97%) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index c197d2a015..a41870d0b6 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -22,9 +22,9 @@ use slotmap::SlotMap; pub use async_context::*; use collections::{FxHashMap, FxHashSet, HashMap, VecDeque}; +pub use context::*; pub use entity_map::*; use http_client::HttpClient; -pub use model_context::*; #[cfg(any(test, feature = "test-support"))] pub use test_context::*; use util::ResultExt; @@ -41,8 +41,8 @@ use crate::{ }; mod async_context; +mod context; mod entity_map; -mod model_context; #[cfg(any(test, feature = "test-support"))] mod test_context; @@ -1667,6 +1667,21 @@ impl AppContext for App { Ok(read(view, self)) } + + fn background_spawn(&self, future: impl Future + Send + 'static) -> Task + where + R: Send + 'static, + { + self.background_executor.spawn(future) + } + + fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result + where + G: Global, + { + let mut g = self.global::(); + callback(&g, self) + } } /// These effects are processed at the end of each application update cycle. diff --git a/crates/gpui/src/app/async_context.rs b/crates/gpui/src/app/async_context.rs index 567a4c5ae5..29edb65200 100644 --- a/crates/gpui/src/app/async_context.rs +++ b/crates/gpui/src/app/async_context.rs @@ -104,6 +104,22 @@ impl AppContext for AsyncApp { let lock = app.borrow(); lock.read_window(window, read) } + + fn background_spawn(&self, future: impl Future + Send + 'static) -> Task + where + R: Send + 'static, + { + self.background_executor.spawn(future) + } + + fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result + where + G: Global, + { + let app = self.app.upgrade().context("app was released")?; + let mut lock = app.borrow_mut(); + Ok(lock.update(|this| this.read_global(callback))) + } } impl AsyncApp { @@ -367,6 +383,20 @@ impl AppContext for AsyncWindowContext { { self.app.read_window(window, read) } + + fn background_spawn(&self, future: impl Future + Send + 'static) -> Task + where + R: Send + 'static, + { + self.app.background_executor.spawn(future) + } + + fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> Result + where + G: Global, + { + self.app.read_global(callback) + } } impl VisualContext for AsyncWindowContext { diff --git a/crates/gpui/src/app/model_context.rs b/crates/gpui/src/app/context.rs similarity index 97% rename from crates/gpui/src/app/model_context.rs rename to crates/gpui/src/app/context.rs index 17e161d8c9..80916bee9f 100644 --- a/crates/gpui/src/app/model_context.rs +++ b/crates/gpui/src/app/context.rs @@ -1,7 +1,7 @@ use crate::{ - AnyView, AnyWindowHandle, App, AppContext, AsyncApp, DispatchPhase, Effect, EntityId, - EventEmitter, FocusHandle, FocusOutEvent, Focusable, Global, KeystrokeObserver, Reservation, - SubscriberSet, Subscription, Task, WeakEntity, WeakFocusHandle, Window, WindowHandle, + AnyView, AnyWindowHandle, AppContext, AsyncApp, DispatchPhase, Effect, EntityId, EventEmitter, + FocusHandle, FocusOutEvent, Focusable, Global, KeystrokeObserver, Reservation, SubscriberSet, + Subscription, Task, WeakEntity, WeakFocusHandle, Window, WindowHandle, }; use anyhow::Result; use derive_more::{Deref, DerefMut}; @@ -13,7 +13,7 @@ use std::{ sync::Arc, }; -use super::{AsyncWindowContext, Entity, KeystrokeEvent}; +use super::{App, AsyncWindowContext, Entity, KeystrokeEvent}; /// The app context, with specialized behavior for the given model. #[derive(Deref, DerefMut)] @@ -717,6 +717,20 @@ impl<'a, T> AppContext for Context<'a, T> { { self.app.read_window(window, read) } + + fn background_spawn(&self, future: impl Future + Send + 'static) -> Task + where + R: Send + 'static, + { + self.app.background_executor.spawn(future) + } + + fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result + where + G: Global, + { + self.app.read_global(callback) + } } impl Borrow for Context<'_, T> { diff --git a/crates/gpui/src/app/test_context.rs b/crates/gpui/src/app/test_context.rs index 86a3e49034..46c4cfbbb0 100644 --- a/crates/gpui/src/app/test_context.rs +++ b/crates/gpui/src/app/test_context.rs @@ -94,6 +94,21 @@ impl AppContext for TestAppContext { let app = self.app.borrow(); app.read_window(window, read) } + + fn background_spawn(&self, future: impl Future + Send + 'static) -> Task + where + R: Send + 'static, + { + self.background_executor.spawn(future) + } + + fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result + where + G: Global, + { + let app = self.app.borrow(); + app.read_global(callback) + } } impl TestAppContext { @@ -906,6 +921,20 @@ impl AppContext for VisualTestContext { { self.cx.read_window(window, read) } + + fn background_spawn(&self, future: impl Future + Send + 'static) -> Task + where + R: Send + 'static, + { + self.cx.background_spawn(future) + } + + fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result + where + G: Global, + { + self.cx.read_global(callback) + } } impl VisualContext for VisualTestContext { diff --git a/crates/gpui/src/gpui.rs b/crates/gpui/src/gpui.rs index 54c9bcf49f..d13fa2da1a 100644 --- a/crates/gpui/src/gpui.rs +++ b/crates/gpui/src/gpui.rs @@ -155,7 +155,7 @@ pub use util::arc_cow::ArcCow; pub use view::*; pub use window::*; -use std::{any::Any, borrow::BorrowMut}; +use std::{any::Any, borrow::BorrowMut, future::Future}; use taffy::TaffyLayoutEngine; /// The context trait, allows the different contexts in GPUI to be used @@ -215,6 +215,16 @@ pub trait AppContext { ) -> Result where T: 'static; + + /// Spawn a future on a background thread + fn background_spawn(&self, future: impl Future + Send + 'static) -> Task + where + R: Send + 'static; + + /// Read a global from this app context + fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result + where + G: Global; } /// Returned by [Context::reserve_entity] to later be passed to [Context::insert_model]. diff --git a/crates/gpui_macros/src/derive_app_context.rs b/crates/gpui_macros/src/derive_app_context.rs index 943823ee61..165b60040b 100644 --- a/crates/gpui_macros/src/derive_app_context.rs +++ b/crates/gpui_macros/src/derive_app_context.rs @@ -81,6 +81,20 @@ pub fn derive_app_context(input: TokenStream) -> TokenStream { { self.#app_variable.read_window(window, read) } + + fn background_spawn(&self, future: impl std::future::Future + Send + 'static) -> gpui::Task + where + R: Send + 'static, + { + self.#app_variable.background_spawn(future) + } + + fn read_global(&self, callback: impl FnOnce(&G, &gpui::App) -> R) -> Self::Result + where + G: gpui::Global, + { + self.#app_variable.read_global(callback) + } } }; diff --git a/crates/gpui_tokio/src/gpui_tokio.rs b/crates/gpui_tokio/src/gpui_tokio.rs index ecc17fa3ec..d6fc4868b7 100644 --- a/crates/gpui_tokio/src/gpui_tokio.rs +++ b/crates/gpui_tokio/src/gpui_tokio.rs @@ -1,6 +1,6 @@ use std::future::Future; -use gpui::{App, Global, ReadGlobal, Task}; +use gpui::{App, AppContext, Global, ReadGlobal, Task}; use tokio::task::JoinError; use util::defer; @@ -32,20 +32,23 @@ pub struct Tokio {} impl Tokio { /// Spawns the given future on Tokio's thread pool, and returns it via a GPUI task /// Note that the Tokio task will be cancelled if the GPUI task is dropped - pub fn spawn(cx: &mut App, f: Fut) -> Task> + pub fn spawn(cx: &mut C, f: Fut) -> C::Result>> where + C: AppContext, Fut: Future + Send + 'static, R: Send + 'static, { - let join_handle = GlobalTokio::global(cx).runtime.spawn(f); - let abort_handle = join_handle.abort_handle(); - let cancel = defer(move || { - abort_handle.abort(); - }); - cx.background_executor().spawn(async move { - let result = join_handle.await; - drop(cancel); - result + cx.read_global(|tokio: &GlobalTokio, cx| { + let join_handle = tokio.runtime.spawn(f); + let abort_handle = join_handle.abort_handle(); + let cancel = defer(move || { + abort_handle.abort(); + }); + cx.background_spawn(async move { + let result = join_handle.await; + drop(cancel); + result + }) }) }