Skip to main content

gpui_authoring/
executor.rs

1use crate::App;
2use gpui_util::{TryFutureExt, TryFutureExtBacktrace};
3use scheduler::Task;
4
5/// Extension trait for `Task<Result<T, E>>` that adds `detach_and_log_err` with an `&App` context.
6///
7/// This trait is automatically implemented for all `Task<Result<T, E>>` types.
8pub trait TaskExt<T, E> {
9    /// Run the task to completion in the background and log any errors that occur.
10    fn detach_and_log_err(self, cx: &App);
11    /// Like [`Self::detach_and_log_err`], but uses `{:?}` formatting on failure so `anyhow::Error`
12    /// values emit their full backtrace. Prefer `detach_and_log_err` unless a backtrace is wanted.
13    fn detach_and_log_err_with_backtrace(self, cx: &App);
14}
15
16impl<T, E> TaskExt<T, E> for Task<Result<T, E>>
17where
18    T: 'static,
19    E: 'static + std::fmt::Display + std::fmt::Debug,
20{
21    #[track_caller]
22    fn detach_and_log_err(self, cx: &App) {
23        let location = core::panic::Location::caller();
24        cx.foreground_executor()
25            .spawn(self.log_tracked_err(*location))
26            .detach();
27    }
28
29    #[track_caller]
30    fn detach_and_log_err_with_backtrace(self, cx: &App) {
31        let location = *core::panic::Location::caller();
32        cx.foreground_executor()
33            .spawn(self.log_tracked_err_with_backtrace(location))
34            .detach();
35    }
36}
37
38#[cfg(test)]
39mod test {
40    use crate::{App, BackgroundExecutor, ForegroundExecutor, TestDispatcher, TestPlatform};
41    use std::{cell::RefCell, rc::Rc, sync::Arc};
42
43    /// Helper to create test infrastructure.
44    /// Returns (dispatcher, background_executor, app).
45    fn create_test_app() -> (TestDispatcher, BackgroundExecutor, Rc<crate::AppCell>) {
46        let dispatcher = TestDispatcher::new(0);
47        let arc_dispatcher = Arc::new(dispatcher.clone());
48        let background_executor = BackgroundExecutor::new(arc_dispatcher.clone());
49        let foreground_executor = ForegroundExecutor::new(arc_dispatcher);
50
51        let platform = TestPlatform::new(background_executor.clone(), foreground_executor);
52        let asset_source = Arc::new(());
53        let http_client = http_client::FakeHttpClient::with_404_response();
54
55        let app = App::new_app(platform, asset_source, http_client);
56        (dispatcher, background_executor, app)
57    }
58
59    #[test]
60    fn sanity_test_tasks_run() {
61        let (dispatcher, _background_executor, app) = create_test_app();
62        let foreground_executor = app.borrow().foreground_executor.clone();
63
64        let task_ran = Rc::new(RefCell::new(false));
65
66        foreground_executor
67            .spawn({
68                let task_ran = Rc::clone(&task_ran);
69                async move {
70                    *task_ran.borrow_mut() = true;
71                }
72            })
73            .detach();
74
75        // Run dispatcher while app is still alive
76        dispatcher.run_until_parked();
77
78        // Task should have run
79        assert!(
80            *task_ran.borrow(),
81            "Task should run normally when app is alive"
82        );
83    }
84}