//! Vendored from zed's `crates/gpui_tokio` (Apache-2.0) because the `gpui-pre` family //! does not republish it, and `nostr-sdk`'s reqwest client needs a Tokio runtime. use std::future::Future; use gpui::{App, AppContext, Global, ReadGlobal, Task}; use gpui_util::defer; pub use tokio::task::JoinError; /// Initializes the Tokio wrapper using a new Tokio runtime with 2 worker threads. /// /// If you need more threads (or access to the runtime outside of GPUI), you can create the runtime /// yourself and pass a Handle to `init_from_handle`. pub fn init(cx: &mut App) { 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"); let handle = runtime.handle().clone(); cx.set_global(GlobalTokio { owned_runtime: Some(runtime), handle, }); } /// Initializes the Tokio wrapper using a Tokio runtime handle. pub fn init_from_handle(cx: &mut App, handle: tokio::runtime::Handle) { cx.set_global(GlobalTokio { owned_runtime: None, handle, }); } struct GlobalTokio { owned_runtime: Option, handle: tokio::runtime::Handle, } impl Global for GlobalTokio {} impl Drop for GlobalTokio { fn drop(&mut self) { if let Some(runtime) = self.owned_runtime.take() { runtime.shutdown_background(); } } } 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: &C, f: Fut) -> Task> where C: AppContext, Fut: Future + Send + 'static, R: Send + 'static, { cx.read_global(|tokio: &GlobalTokio, cx| { let join_handle = tokio.handle.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 }) }) } /// 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_result(cx: &C, f: Fut) -> Task> where C: AppContext, Fut: Future> + Send + 'static, R: Send + 'static, { cx.read_global(|tokio: &GlobalTokio, cx| { let join_handle = tokio.handle.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 }) }) } pub fn handle(cx: &App) -> tokio::runtime::Handle { GlobalTokio::global(cx).handle.clone() } }