gpui_tokio.rs

  1use std::future::Future;
  2
  3use gpui::{App, AppContext, Global, ReadGlobal, Task};
  4use util::defer;
  5
  6pub use tokio::task::JoinError;
  7
  8/// Initializes the Tokio wrapper using a new Tokio runtime with 2 worker threads.
  9///
 10/// If you need more threads (or access to the runtime outside of GPUI), you can create the runtime
 11/// yourself and pass a Handle to `init_from_handle`.
 12pub fn init(cx: &mut App) {
 13    let runtime = tokio::runtime::Builder::new_multi_thread()
 14        // Since we now have two executors, let's try to keep our footprint small
 15        .worker_threads(2)
 16        .enable_all()
 17        .build()
 18        .expect("Failed to initialize Tokio");
 19
 20    cx.set_global(GlobalTokio::new(RuntimeHolder::Owned(runtime)));
 21}
 22
 23/// Initializes the Tokio wrapper using a Tokio runtime handle.
 24pub fn init_from_handle(cx: &mut App, handle: tokio::runtime::Handle) {
 25    cx.set_global(GlobalTokio::new(RuntimeHolder::Shared(handle)));
 26}
 27
 28enum RuntimeHolder {
 29    Owned(tokio::runtime::Runtime),
 30    Shared(tokio::runtime::Handle),
 31}
 32
 33impl RuntimeHolder {
 34    pub fn handle(&self) -> &tokio::runtime::Handle {
 35        match self {
 36            RuntimeHolder::Owned(runtime) => runtime.handle(),
 37            RuntimeHolder::Shared(handle) => handle,
 38        }
 39    }
 40}
 41
 42struct GlobalTokio {
 43    runtime: RuntimeHolder,
 44}
 45
 46impl Global for GlobalTokio {}
 47
 48impl GlobalTokio {
 49    fn new(runtime: RuntimeHolder) -> Self {
 50        Self { runtime }
 51    }
 52}
 53
 54pub struct Tokio {}
 55
 56impl Tokio {
 57    /// Spawns the given future on Tokio's thread pool, and returns it via a GPUI task
 58    /// Note that the Tokio task will be cancelled if the GPUI task is dropped
 59    pub fn spawn<C, Fut, R>(cx: &C, f: Fut) -> C::Result<Task<Result<R, JoinError>>>
 60    where
 61        C: AppContext,
 62        Fut: Future<Output = R> + Send + 'static,
 63        R: Send + 'static,
 64    {
 65        cx.read_global(|tokio: &GlobalTokio, cx| {
 66            let join_handle = tokio.runtime.handle().spawn(f);
 67            let abort_handle = join_handle.abort_handle();
 68            let cancel = defer(move || {
 69                abort_handle.abort();
 70            });
 71            cx.background_spawn(async move {
 72                let result = join_handle.await;
 73                drop(cancel);
 74                result
 75            })
 76        })
 77    }
 78
 79    /// Spawns the given future on Tokio's thread pool, and returns it via a GPUI task
 80    /// Note that the Tokio task will be cancelled if the GPUI task is dropped
 81    pub fn spawn_result<C, Fut, R>(cx: &C, f: Fut) -> C::Result<Task<anyhow::Result<R>>>
 82    where
 83        C: AppContext,
 84        Fut: Future<Output = anyhow::Result<R>> + Send + 'static,
 85        R: Send + 'static,
 86    {
 87        cx.read_global(|tokio: &GlobalTokio, cx| {
 88            let join_handle = tokio.runtime.handle().spawn(f);
 89            let abort_handle = join_handle.abort_handle();
 90            let cancel = defer(move || {
 91                abort_handle.abort();
 92            });
 93            cx.background_spawn(async move {
 94                let result = join_handle.await?;
 95                drop(cancel);
 96                result
 97            })
 98        })
 99    }
100
101    pub fn handle(cx: &App) -> tokio::runtime::Handle {
102        GlobalTokio::global(cx).runtime.handle().clone()
103    }
104}