Add a crate for spawning tokio tasks in Zed (#23857)

Part of https://github.com/zed-industries/zed/pull/21092

As we're already depending on and using `tokio` to run `reqwest`, I've
added a crate to make running tokio futures more convenient. This should
unblock the Bedrock Cloud Model provider PR.

Note that since the `gpui_tokio` code is nearly trivial glue and I
expect that it will be useful for the nascent GPUI ecosystem, I've
elected to license it under Apache 2, like GPUI itself, instead of our
normal GPL license for Zed code.

Release Notes:

- N/A
This commit is contained in:
Mikayla Maki 2025-01-29 12:53:16 -08:00 committed by GitHub
parent ee0d2a8d94
commit bd21334013
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 116 additions and 17 deletions

View file

@ -0,0 +1,18 @@
[package]
name = "gpui_tokio"
version = "0.1.0"
edition.workspace = true
publish.workspace = true
license = "Apache-2.0"
[lints]
workspace = true
[lib]
path = "src/gpui_tokio.rs"
doctest = false
[dependencies]
util.workspace = true
gpui.workspace = true
tokio = { workspace = true, features = ["rt", "rt-multi-thread"] }

View file

@ -0,0 +1 @@
../../LICENSE-APACHE

View file

@ -0,0 +1,55 @@
use std::future::Future;
use gpui::{App, Global, ReadGlobal, Task};
use tokio::task::JoinError;
use util::defer;
pub fn init(cx: &mut App) {
cx.set_global(GlobalTokio::new());
}
struct GlobalTokio {
runtime: tokio::runtime::Runtime,
}
impl Global for GlobalTokio {}
impl GlobalTokio {
fn new() -> Self {
let runtime = tokio::runtime::Builder::new_multi_thread()
// Since we now have two executors, let's try to keep our footprint small
.worker_threads(2)
.enable_all()
.build()
.expect("Failed to initialize Tokio");
Self { runtime }
}
}
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<Fut, R>(cx: &mut App, f: Fut) -> Task<Result<R, JoinError>>
where
Fut: Future<Output = R> + 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
})
}
pub fn handle(cx: &mut App) -> tokio::runtime::Handle {
GlobalTokio::global(cx).runtime.handle().clone()
}
}