1use std::future::Future;
7
8use gpui::{App, AppContext, Global, Task};
9
10pub use tokio::task::JoinError;
11
12pub fn init(cx: &mut App) {
14 let runtime = tokio::runtime::Builder::new_multi_thread()
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
23enum RuntimeHolder {
24 Owned(tokio::runtime::Runtime),
25 #[allow(dead_code)]
26 Shared(tokio::runtime::Handle),
27}
28
29impl RuntimeHolder {
30 pub fn handle(&self) -> &tokio::runtime::Handle {
31 match self {
32 RuntimeHolder::Owned(runtime) => runtime.handle(),
33 RuntimeHolder::Shared(handle) => handle,
34 }
35 }
36}
37
38struct GlobalTokio {
39 runtime: RuntimeHolder,
40}
41
42impl Global for GlobalTokio {}
43
44impl GlobalTokio {
45 fn new(runtime: RuntimeHolder) -> Self {
46 Self { runtime }
47 }
48}
49
50struct AbortOnDrop(tokio::task::AbortHandle);
52
53impl Drop for AbortOnDrop {
54 fn drop(&mut self) {
55 self.0.abort();
56 }
57}
58
59pub struct Tokio;
60
61impl Tokio {
62 pub fn spawn<T, Fut, R>(cx: &mut gpui::Context<'_, T>, f: Fut) -> Task<Result<R, JoinError>>
65 where
66 Fut: Future<Output = R> + Send + 'static,
67 R: Send + 'static,
68 {
69 let tokio = cx.global::<GlobalTokio>();
70 let join_handle = tokio.runtime.handle().spawn(f);
71 let abort_guard = AbortOnDrop(join_handle.abort_handle());
72 cx.background_spawn(async move {
73 let result = join_handle.await;
74 drop(abort_guard);
75 result
76 })
77 }
78}