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
This commit is contained in:
Mikayla Maki 2025-01-30 18:00:55 -08:00 committed by GitHub
parent ff43b6875b
commit 517e519bdc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 133 additions and 18 deletions

View file

@ -22,9 +22,9 @@ use slotmap::SlotMap;
pub use async_context::*; pub use async_context::*;
use collections::{FxHashMap, FxHashSet, HashMap, VecDeque}; use collections::{FxHashMap, FxHashSet, HashMap, VecDeque};
pub use context::*;
pub use entity_map::*; pub use entity_map::*;
use http_client::HttpClient; use http_client::HttpClient;
pub use model_context::*;
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
pub use test_context::*; pub use test_context::*;
use util::ResultExt; use util::ResultExt;
@ -41,8 +41,8 @@ use crate::{
}; };
mod async_context; mod async_context;
mod context;
mod entity_map; mod entity_map;
mod model_context;
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
mod test_context; mod test_context;
@ -1667,6 +1667,21 @@ impl AppContext for App {
Ok(read(view, self)) Ok(read(view, self))
} }
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
where
R: Send + 'static,
{
self.background_executor.spawn(future)
}
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
where
G: Global,
{
let mut g = self.global::<G>();
callback(&g, self)
}
} }
/// These effects are processed at the end of each application update cycle. /// These effects are processed at the end of each application update cycle.

View file

@ -104,6 +104,22 @@ impl AppContext for AsyncApp {
let lock = app.borrow(); let lock = app.borrow();
lock.read_window(window, read) lock.read_window(window, read)
} }
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
where
R: Send + 'static,
{
self.background_executor.spawn(future)
}
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
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 { impl AsyncApp {
@ -367,6 +383,20 @@ impl AppContext for AsyncWindowContext {
{ {
self.app.read_window(window, read) self.app.read_window(window, read)
} }
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
where
R: Send + 'static,
{
self.app.background_executor.spawn(future)
}
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Result<R>
where
G: Global,
{
self.app.read_global(callback)
}
} }
impl VisualContext for AsyncWindowContext { impl VisualContext for AsyncWindowContext {

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
AnyView, AnyWindowHandle, App, AppContext, AsyncApp, DispatchPhase, Effect, EntityId, AnyView, AnyWindowHandle, AppContext, AsyncApp, DispatchPhase, Effect, EntityId, EventEmitter,
EventEmitter, FocusHandle, FocusOutEvent, Focusable, Global, KeystrokeObserver, Reservation, FocusHandle, FocusOutEvent, Focusable, Global, KeystrokeObserver, Reservation, SubscriberSet,
SubscriberSet, Subscription, Task, WeakEntity, WeakFocusHandle, Window, WindowHandle, Subscription, Task, WeakEntity, WeakFocusHandle, Window, WindowHandle,
}; };
use anyhow::Result; use anyhow::Result;
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
@ -13,7 +13,7 @@ use std::{
sync::Arc, sync::Arc,
}; };
use super::{AsyncWindowContext, Entity, KeystrokeEvent}; use super::{App, AsyncWindowContext, Entity, KeystrokeEvent};
/// The app context, with specialized behavior for the given model. /// The app context, with specialized behavior for the given model.
#[derive(Deref, DerefMut)] #[derive(Deref, DerefMut)]
@ -717,6 +717,20 @@ impl<'a, T> AppContext for Context<'a, T> {
{ {
self.app.read_window(window, read) self.app.read_window(window, read)
} }
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
where
R: Send + 'static,
{
self.app.background_executor.spawn(future)
}
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
where
G: Global,
{
self.app.read_global(callback)
}
} }
impl<T> Borrow<App> for Context<'_, T> { impl<T> Borrow<App> for Context<'_, T> {

View file

@ -94,6 +94,21 @@ impl AppContext for TestAppContext {
let app = self.app.borrow(); let app = self.app.borrow();
app.read_window(window, read) app.read_window(window, read)
} }
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
where
R: Send + 'static,
{
self.background_executor.spawn(future)
}
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
where
G: Global,
{
let app = self.app.borrow();
app.read_global(callback)
}
} }
impl TestAppContext { impl TestAppContext {
@ -906,6 +921,20 @@ impl AppContext for VisualTestContext {
{ {
self.cx.read_window(window, read) self.cx.read_window(window, read)
} }
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
where
R: Send + 'static,
{
self.cx.background_spawn(future)
}
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
where
G: Global,
{
self.cx.read_global(callback)
}
} }
impl VisualContext for VisualTestContext { impl VisualContext for VisualTestContext {

View file

@ -155,7 +155,7 @@ pub use util::arc_cow::ArcCow;
pub use view::*; pub use view::*;
pub use window::*; pub use window::*;
use std::{any::Any, borrow::BorrowMut}; use std::{any::Any, borrow::BorrowMut, future::Future};
use taffy::TaffyLayoutEngine; use taffy::TaffyLayoutEngine;
/// The context trait, allows the different contexts in GPUI to be used /// The context trait, allows the different contexts in GPUI to be used
@ -215,6 +215,16 @@ pub trait AppContext {
) -> Result<R> ) -> Result<R>
where where
T: 'static; T: 'static;
/// Spawn a future on a background thread
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
where
R: Send + 'static;
/// Read a global from this app context
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
where
G: Global;
} }
/// Returned by [Context::reserve_entity] to later be passed to [Context::insert_model]. /// Returned by [Context::reserve_entity] to later be passed to [Context::insert_model].

View file

@ -81,6 +81,20 @@ pub fn derive_app_context(input: TokenStream) -> TokenStream {
{ {
self.#app_variable.read_window(window, read) self.#app_variable.read_window(window, read)
} }
fn background_spawn<R>(&self, future: impl std::future::Future<Output = R> + Send + 'static) -> gpui::Task<R>
where
R: Send + 'static,
{
self.#app_variable.background_spawn(future)
}
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &gpui::App) -> R) -> Self::Result<R>
where
G: gpui::Global,
{
self.#app_variable.read_global(callback)
}
} }
}; };

View file

@ -1,6 +1,6 @@
use std::future::Future; use std::future::Future;
use gpui::{App, Global, ReadGlobal, Task}; use gpui::{App, AppContext, Global, ReadGlobal, Task};
use tokio::task::JoinError; use tokio::task::JoinError;
use util::defer; use util::defer;
@ -32,20 +32,23 @@ pub struct Tokio {}
impl Tokio { impl Tokio {
/// Spawns the given future on Tokio's thread pool, and returns it via a GPUI task /// 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 /// Note that the Tokio task will be cancelled if the GPUI task is dropped
pub fn spawn<Fut, R>(cx: &mut App, f: Fut) -> Task<Result<R, JoinError>> pub fn spawn<C, Fut, R>(cx: &mut C, f: Fut) -> C::Result<Task<Result<R, JoinError>>>
where where
C: AppContext,
Fut: Future<Output = R> + Send + 'static, Fut: Future<Output = R> + Send + 'static,
R: Send + 'static, R: Send + 'static,
{ {
let join_handle = GlobalTokio::global(cx).runtime.spawn(f); cx.read_global(|tokio: &GlobalTokio, cx| {
let abort_handle = join_handle.abort_handle(); let join_handle = tokio.runtime.spawn(f);
let cancel = defer(move || { let abort_handle = join_handle.abort_handle();
abort_handle.abort(); let cancel = defer(move || {
}); abort_handle.abort();
cx.background_executor().spawn(async move { });
let result = join_handle.await; cx.background_spawn(async move {
drop(cancel); let result = join_handle.await;
result drop(cancel);
result
})
}) })
} }