From 8f961a41c611b0baca3ed017ad5c996540534846 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 6 Jan 2026 18:13:05 -0800 Subject: [PATCH] gpui: Make all foreground tasks drop when the app dies (#46219) This expands the liveness check to cover all foreground tasks that don't take the app as a context (as some of their internal futures might.) Release Notes: - N/A --- crates/gpui/src/app.rs | 20 ++- crates/gpui/src/app/async_context.rs | 8 +- crates/gpui/src/app/test_context.rs | 7 +- crates/gpui/src/app/visual_test_context.rs | 6 +- crates/gpui/src/executor.rs | 154 +++++------------- crates/gpui/src/platform.rs | 22 +-- .../src/platform/linux/headless/client.rs | 4 +- crates/gpui/src/platform/linux/platform.rs | 7 +- .../gpui/src/platform/linux/wayland/client.rs | 4 +- crates/gpui/src/platform/linux/x11/client.rs | 4 +- crates/gpui/src/platform/mac/platform.rs | 10 +- crates/gpui/src/platform/test/dispatcher.rs | 9 +- crates/gpui/src/platform/windows/platform.rs | 4 +- 13 files changed, 96 insertions(+), 163 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index c019bda14cb211a0521e8a3513b642a0b2a369ce..c021ba499720393beb4bd33d53a41f124712d4d3 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -138,8 +138,10 @@ impl Application { #[cfg(any(test, feature = "test-support"))] log::info!("GPUI was compiled in test mode"); + let liveness = Arc::new(()); Self(App::new_app( - current_platform(false), + current_platform(false, Arc::downgrade(&liveness)), + liveness, Arc::new(()), Arc::new(NullHttpClient), )) @@ -149,8 +151,10 @@ impl Application { /// but makes it possible to run an application in an context like /// SSH, where GUI applications are not allowed. pub fn headless() -> Self { + let liveness = Arc::new(()); Self(App::new_app( - current_platform(true), + current_platform(true, Arc::downgrade(&liveness)), + liveness, Arc::new(()), Arc::new(NullHttpClient), )) @@ -584,7 +588,7 @@ impl GpuiMode { /// You need a reference to an `App` to access the state of a [Entity]. pub struct App { pub(crate) this: Weak, - pub(crate) liveness: std::sync::Arc<()>, + pub(crate) _liveness: Arc<()>, pub(crate) platform: Rc, pub(crate) mode: GpuiMode, text_system: Arc, @@ -646,13 +650,14 @@ impl App { #[allow(clippy::new_ret_no_self)] pub(crate) fn new_app( platform: Rc, + liveness: Arc<()>, asset_source: Arc, http_client: Arc, ) -> Rc { - let executor = platform.background_executor(); + let background_executor = platform.background_executor(); let foreground_executor = platform.foreground_executor(); assert!( - executor.is_main_thread(), + background_executor.is_main_thread(), "must construct App on main thread" ); @@ -664,7 +669,7 @@ impl App { let app = Rc::new_cyclic(|this| AppCell { app: RefCell::new(App { this: this.clone(), - liveness: std::sync::Arc::new(()), + _liveness: liveness, platform: platform.clone(), text_system, text_rendering_mode: Rc::new(Cell::new(TextRenderingMode::default())), @@ -673,7 +678,7 @@ impl App { flushing_effects: false, pending_updates: 0, active_drag: None, - background_executor: executor, + background_executor, foreground_executor, svg_renderer: SvgRenderer::new(asset_source.clone()), loading_assets: Default::default(), @@ -1494,7 +1499,6 @@ impl App { pub fn to_async(&self) -> AsyncApp { AsyncApp { app: self.this.clone(), - liveness_token: std::sync::Arc::downgrade(&self.liveness), background_executor: self.background_executor.clone(), foreground_executor: self.foreground_executor.clone(), } diff --git a/crates/gpui/src/app/async_context.rs b/crates/gpui/src/app/async_context.rs index 3f9d21ef65ab16350c8d16fa3197ac507e5d503e..805dfced162cd27f0cc785a8282ae3b802c2873a 100644 --- a/crates/gpui/src/app/async_context.rs +++ b/crates/gpui/src/app/async_context.rs @@ -16,7 +16,6 @@ use super::{Context, WeakEntity}; #[derive(Clone)] pub struct AsyncApp { pub(crate) app: Weak, - pub(crate) liveness_token: std::sync::Weak<()>, pub(crate) background_executor: BackgroundExecutor, pub(crate) foreground_executor: ForegroundExecutor, } @@ -186,7 +185,7 @@ impl AsyncApp { { let mut cx = self.clone(); self.foreground_executor - .spawn_context(self.liveness_token.clone(), async move { f(&mut cx).await }) + .spawn(async move { f(&mut cx).await }) } /// Determine whether global state of the specified type has been assigned. @@ -335,10 +334,7 @@ impl AsyncWindowContext { { let mut cx = self.clone(); self.foreground_executor - .spawn_context( - self.app.liveness_token.clone(), - async move { f(&mut cx).await }, - ) + .spawn(async move { f(&mut cx).await }) } /// Present a platform dialog. diff --git a/crates/gpui/src/app/test_context.rs b/crates/gpui/src/app/test_context.rs index d7a1e4704584595d769989b421ccd767fba58985..33e9365dece4da99c6733e2a2ac250a785dde05f 100644 --- a/crates/gpui/src/app/test_context.rs +++ b/crates/gpui/src/app/test_context.rs @@ -125,14 +125,16 @@ impl TestAppContext { /// Creates a new `TestAppContext`. Usually you can rely on `#[gpui::test]` to do this for you. pub fn build(dispatcher: TestDispatcher, fn_name: Option<&'static str>) -> Self { let arc_dispatcher = Arc::new(dispatcher.clone()); + let liveness = std::sync::Arc::new(()); let background_executor = BackgroundExecutor::new(arc_dispatcher.clone()); - let foreground_executor = ForegroundExecutor::new(arc_dispatcher); + let foreground_executor = + ForegroundExecutor::new(arc_dispatcher, Arc::downgrade(&liveness)); let platform = TestPlatform::new(background_executor.clone(), foreground_executor.clone()); let asset_source = Arc::new(()); let http_client = http_client::FakeHttpClient::with_404_response(); let text_system = Arc::new(TextSystem::new(platform.text_system())); - let mut app = App::new_app(platform.clone(), asset_source, http_client); + let app = App::new_app(platform.clone(), liveness, asset_source, http_client); app.borrow_mut().mode = GpuiMode::test(); Self { @@ -405,7 +407,6 @@ impl TestAppContext { pub fn to_async(&self) -> AsyncApp { AsyncApp { app: Rc::downgrade(&self.app), - liveness_token: std::sync::Arc::downgrade(&self.app.borrow().liveness), background_executor: self.background_executor.clone(), foreground_executor: self.foreground_executor.clone(), } diff --git a/crates/gpui/src/app/visual_test_context.rs b/crates/gpui/src/app/visual_test_context.rs index eecedad3aeef112b0198d434278e6772f798c73d..50a7ed6c2853379ce054ebf5277bccdeeb7b4c56 100644 --- a/crates/gpui/src/app/visual_test_context.rs +++ b/crates/gpui/src/app/visual_test_context.rs @@ -37,7 +37,9 @@ impl VisualTestAppContext { /// - Screenshots can be captured via ScreenCaptureKit /// - All platform APIs work as they do in production pub fn new() -> Self { - let platform = current_platform(false); + let liveness = Arc::new(()); + let liveness_weak = Arc::downgrade(&liveness); + let platform = current_platform(false, liveness_weak); let background_executor = platform.background_executor(); let foreground_executor = platform.foreground_executor(); let text_system = Arc::new(TextSystem::new(platform.text_system())); @@ -45,7 +47,7 @@ impl VisualTestAppContext { let asset_source = Arc::new(()); let http_client = http_client::FakeHttpClient::with_404_response(); - let mut app = App::new_app(platform.clone(), asset_source, http_client); + let mut app = App::new_app(platform.clone(), liveness, asset_source, http_client); app.borrow_mut().mode = GpuiMode::test(); Self { diff --git a/crates/gpui/src/executor.rs b/crates/gpui/src/executor.rs index 584d06f2ab46430d9a213a2bbae70c012f65f054..fedd285dca4939aadac2ab1a050291430288e60b 100644 --- a/crates/gpui/src/executor.rs +++ b/crates/gpui/src/executor.rs @@ -19,7 +19,7 @@ use std::{ thread::{self, ThreadId}, time::{Duration, Instant}, }; -use util::TryFutureExt; +use util::TryFutureExt as _; use waker_fn::waker_fn; #[cfg(any(test, feature = "test-support"))] @@ -44,6 +44,7 @@ pub struct BackgroundExecutor { pub struct ForegroundExecutor { #[doc(hidden)] pub dispatcher: Arc, + liveness: std::sync::Weak<()>, not_send: PhantomData>, } @@ -776,9 +777,10 @@ impl BackgroundExecutor { /// ForegroundExecutor runs things on the main thread. impl ForegroundExecutor { /// Creates a new ForegroundExecutor from the given PlatformDispatcher. - pub fn new(dispatcher: Arc) -> Self { + pub fn new(dispatcher: Arc, liveness: std::sync::Weak<()>) -> Self { Self { dispatcher, + liveness, not_send: PhantomData, } } @@ -789,7 +791,7 @@ impl ForegroundExecutor { where R: 'static, { - self.inner_spawn(None, Priority::default(), future) + self.inner_spawn(self.liveness.clone(), Priority::default(), future) } /// Enqueues the given Task to run on the main thread at some point in the future. @@ -802,25 +804,13 @@ impl ForegroundExecutor { where R: 'static, { - self.inner_spawn(None, priority, future) - } - - #[track_caller] - pub(crate) fn spawn_context( - &self, - app: std::sync::Weak<()>, - future: impl Future + 'static, - ) -> Task - where - R: 'static, - { - self.inner_spawn(Some(app), Priority::default(), future) + self.inner_spawn(self.liveness.clone(), priority, future) } #[track_caller] pub(crate) fn inner_spawn( &self, - app: Option>, + app: std::sync::Weak<()>, priority: Priority, future: impl Future + 'static, ) -> Task @@ -835,7 +825,7 @@ impl ForegroundExecutor { dispatcher: Arc, future: AnyLocalFuture, location: &'static core::panic::Location<'static>, - app: Option>, + app: std::sync::Weak<()>, priority: Priority, ) -> Task { let (runnable, task) = spawn_local_with_source_location( @@ -843,7 +833,10 @@ impl ForegroundExecutor { move |runnable| { dispatcher.dispatch_on_main_thread(RunnableVariant::Meta(runnable), priority) }, - RunnableMeta { location, app }, + RunnableMeta { + location, + app: Some(app), + }, ); runnable.schedule(); Task(TaskState::Spawned(task)) @@ -989,24 +982,34 @@ mod test { use rand::SeedableRng; use std::cell::RefCell; - #[test] - fn sanity_test_tasks_run() { + /// Helper to create test infrastructure. + /// Returns (dispatcher, background_executor, app) where app's foreground_executor has liveness. + fn create_test_app() -> (TestDispatcher, BackgroundExecutor, Rc) { let dispatcher = TestDispatcher::new(StdRng::seed_from_u64(0)); let arc_dispatcher = Arc::new(dispatcher.clone()); + // Create liveness for task cancellation + let liveness = std::sync::Arc::new(()); + let liveness_weak = std::sync::Arc::downgrade(&liveness); let background_executor = BackgroundExecutor::new(arc_dispatcher.clone()); - let foreground_executor = ForegroundExecutor::new(arc_dispatcher); + let foreground_executor = ForegroundExecutor::new(arc_dispatcher, liveness_weak); - let platform = TestPlatform::new(background_executor, foreground_executor.clone()); + let platform = TestPlatform::new(background_executor.clone(), foreground_executor); let asset_source = Arc::new(()); let http_client = http_client::FakeHttpClient::with_404_response(); - let app = App::new_app(platform, asset_source, http_client); - let liveness_token = std::sync::Arc::downgrade(&app.borrow().liveness); + let app = App::new_app(platform, liveness, asset_source, http_client); + (dispatcher, background_executor, app) + } + + #[test] + fn sanity_test_tasks_run() { + let (dispatcher, _background_executor, app) = create_test_app(); + let foreground_executor = app.borrow().foreground_executor.clone(); let task_ran = Rc::new(RefCell::new(false)); foreground_executor - .spawn_context(liveness_token, { + .spawn({ let task_ran = Rc::clone(&task_ran); async move { *task_ran.borrow_mut() = true; @@ -1026,24 +1029,15 @@ mod test { #[test] fn test_task_cancelled_when_app_dropped() { - let dispatcher = TestDispatcher::new(StdRng::seed_from_u64(0)); - let arc_dispatcher = Arc::new(dispatcher.clone()); - let background_executor = BackgroundExecutor::new(arc_dispatcher.clone()); - let foreground_executor = ForegroundExecutor::new(arc_dispatcher); - - let platform = TestPlatform::new(background_executor, foreground_executor.clone()); - let asset_source = Arc::new(()); - let http_client = http_client::FakeHttpClient::with_404_response(); - - let app = App::new_app(platform, asset_source, http_client); - let liveness_token = std::sync::Arc::downgrade(&app.borrow().liveness); + let (dispatcher, _background_executor, app) = create_test_app(); + let foreground_executor = app.borrow().foreground_executor.clone(); let app_weak = Rc::downgrade(&app); let task_ran = Rc::new(RefCell::new(false)); let task_ran_clone = Rc::clone(&task_ran); foreground_executor - .spawn_context(liveness_token, async move { + .spawn(async move { *task_ran_clone.borrow_mut() = true; }) .detach(); @@ -1063,17 +1057,8 @@ mod test { #[test] fn test_nested_tasks_both_cancel() { - let dispatcher = TestDispatcher::new(StdRng::seed_from_u64(0)); - let arc_dispatcher = Arc::new(dispatcher.clone()); - let background_executor = BackgroundExecutor::new(arc_dispatcher.clone()); - let foreground_executor = ForegroundExecutor::new(arc_dispatcher); - - let platform = TestPlatform::new(background_executor, foreground_executor.clone()); - let asset_source = Arc::new(()); - let http_client = http_client::FakeHttpClient::with_404_response(); - - let app = App::new_app(platform, asset_source, http_client); - let liveness_token = std::sync::Arc::downgrade(&app.borrow().liveness); + let (dispatcher, _background_executor, app) = create_test_app(); + let foreground_executor = app.borrow().foreground_executor.clone(); let app_weak = Rc::downgrade(&app); let outer_completed = Rc::new(RefCell::new(false)); @@ -1087,13 +1072,11 @@ mod test { // Channel to block the inner task until we're ready let (tx, rx) = futures::channel::oneshot::channel::<()>(); - // We need clones of executor and liveness_token for the inner spawn let inner_executor = foreground_executor.clone(); - let inner_liveness_token = liveness_token.clone(); foreground_executor - .spawn_context(liveness_token, async move { - let inner_task = inner_executor.spawn_context(inner_liveness_token, { + .spawn(async move { + let inner_task = inner_executor.spawn({ let inner_flag = Rc::clone(&inner_flag); async move { rx.await.ok(); @@ -1148,56 +1131,14 @@ mod test { ); } - #[test] - fn test_task_without_app_tracking_still_runs() { - let dispatcher = TestDispatcher::new(StdRng::seed_from_u64(0)); - let arc_dispatcher = Arc::new(dispatcher.clone()); - let background_executor = BackgroundExecutor::new(arc_dispatcher.clone()); - let foreground_executor = ForegroundExecutor::new(arc_dispatcher); - - let platform = TestPlatform::new(background_executor, foreground_executor.clone()); - let asset_source = Arc::new(()); - let http_client = http_client::FakeHttpClient::with_404_response(); - - let app = App::new_app(platform, asset_source, http_client); - let app_weak = Rc::downgrade(&app); - - let task_ran = Rc::new(RefCell::new(false)); - let task_ran_clone = Rc::clone(&task_ran); - - let _task = foreground_executor.spawn(async move { - *task_ran_clone.borrow_mut() = true; - }); - - drop(app); - - assert!(app_weak.upgrade().is_none(), "App should have been dropped"); - - dispatcher.run_until_parked(); - - assert!( - *task_ran.borrow(), - "Task without app tracking should still run after app is dropped" - ); - } - #[test] #[should_panic] fn test_polling_cancelled_task_panics() { - let dispatcher = TestDispatcher::new(StdRng::seed_from_u64(0)); - let arc_dispatcher = Arc::new(dispatcher.clone()); - let background_executor = BackgroundExecutor::new(arc_dispatcher.clone()); - let foreground_executor = ForegroundExecutor::new(arc_dispatcher); - - let platform = TestPlatform::new(background_executor.clone(), foreground_executor.clone()); - let asset_source = Arc::new(()); - let http_client = http_client::FakeHttpClient::with_404_response(); - - let app = App::new_app(platform, asset_source, http_client); - let liveness_token = std::sync::Arc::downgrade(&app.borrow().liveness); + let (dispatcher, background_executor, app) = create_test_app(); + let foreground_executor = app.borrow().foreground_executor.clone(); let app_weak = Rc::downgrade(&app); - let task = foreground_executor.spawn_context(liveness_token, async move { 42 }); + let task = foreground_executor.spawn(async move { 42 }); drop(app); @@ -1210,22 +1151,11 @@ mod test { #[test] fn test_polling_cancelled_task_returns_none_with_fallible() { - let dispatcher = TestDispatcher::new(StdRng::seed_from_u64(0)); - let arc_dispatcher = Arc::new(dispatcher.clone()); - let background_executor = BackgroundExecutor::new(arc_dispatcher.clone()); - let foreground_executor = ForegroundExecutor::new(arc_dispatcher); - - let platform = TestPlatform::new(background_executor.clone(), foreground_executor.clone()); - let asset_source = Arc::new(()); - let http_client = http_client::FakeHttpClient::with_404_response(); - - let app = App::new_app(platform, asset_source, http_client); - let liveness_token = std::sync::Arc::downgrade(&app.borrow().liveness); + let (dispatcher, background_executor, app) = create_test_app(); + let foreground_executor = app.borrow().foreground_executor.clone(); let app_weak = Rc::downgrade(&app); - let task = foreground_executor - .spawn_context(liveness_token, async move { 42 }) - .fallible(); + let task = foreground_executor.spawn(async move { 42 }).fallible(); drop(app); diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 78b09db8a960e1831d4718b7427b035bbdbef3c3..50045932493a7427c7a6932f5c5d90022cdfc82e 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -92,43 +92,45 @@ pub use test::{TestDispatcher, TestScreenCaptureSource, TestScreenCaptureStream} /// Returns a background executor for the current platform. pub fn background_executor() -> BackgroundExecutor { - current_platform(true).background_executor() + // For standalone background executor, use a dead liveness since there's no App. + // Weak::new() creates a weak reference that always returns None on upgrade. + current_platform(true, std::sync::Weak::new()).background_executor() } #[cfg(target_os = "macos")] -pub(crate) fn current_platform(headless: bool) -> Rc { - Rc::new(MacPlatform::new(headless)) +pub(crate) fn current_platform(headless: bool, liveness: std::sync::Weak<()>) -> Rc { + Rc::new(MacPlatform::new(headless, liveness)) } #[cfg(any(target_os = "linux", target_os = "freebsd"))] -pub(crate) fn current_platform(headless: bool) -> Rc { +pub(crate) fn current_platform(headless: bool, liveness: std::sync::Weak<()>) -> Rc { #[cfg(feature = "x11")] use anyhow::Context as _; if headless { - return Rc::new(HeadlessClient::new()); + return Rc::new(HeadlessClient::new(liveness)); } match guess_compositor() { #[cfg(feature = "wayland")] - "Wayland" => Rc::new(WaylandClient::new()), + "Wayland" => Rc::new(WaylandClient::new(liveness)), #[cfg(feature = "x11")] "X11" => Rc::new( - X11Client::new() + X11Client::new(liveness) .context("Failed to initialize X11 client.") .unwrap(), ), - "Headless" => Rc::new(HeadlessClient::new()), + "Headless" => Rc::new(HeadlessClient::new(liveness)), _ => unreachable!(), } } #[cfg(target_os = "windows")] -pub(crate) fn current_platform(_headless: bool) -> Rc { +pub(crate) fn current_platform(_headless: bool, liveness: std::sync::Weak<()>) -> Rc { Rc::new( - WindowsPlatform::new() + WindowsPlatform::new(liveness) .inspect_err(|err| show_error("Failed to launch", err.to_string())) .unwrap(), ) diff --git a/crates/gpui/src/platform/linux/headless/client.rs b/crates/gpui/src/platform/linux/headless/client.rs index 33f1bb17e3230d0b9c9b2c53bcd0603a9cc7f22c..214e9c12aec9d8ae94f50ce00a88f091f04c635e 100644 --- a/crates/gpui/src/platform/linux/headless/client.rs +++ b/crates/gpui/src/platform/linux/headless/client.rs @@ -21,10 +21,10 @@ pub struct HeadlessClientState { pub(crate) struct HeadlessClient(Rc>); impl HeadlessClient { - pub(crate) fn new() -> Self { + pub(crate) fn new(liveness: std::sync::Weak<()>) -> Self { let event_loop = EventLoop::try_new().unwrap(); - let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal()); + let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal(), liveness); let handle = event_loop.handle(); diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 06a81ec342e9d528a081456583f3ba0f3fb77b6f..381ab7cf577ad4b83a81249156ccae5a732ac2c5 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -149,7 +149,10 @@ pub(crate) struct LinuxCommon { } impl LinuxCommon { - pub fn new(signal: LoopSignal) -> (Self, PriorityQueueCalloopReceiver) { + pub fn new( + signal: LoopSignal, + liveness: std::sync::Weak<()>, + ) -> (Self, PriorityQueueCalloopReceiver) { let (main_sender, main_receiver) = PriorityQueueCalloopReceiver::new(); #[cfg(any(feature = "wayland", feature = "x11"))] @@ -165,7 +168,7 @@ impl LinuxCommon { let common = LinuxCommon { background_executor, - foreground_executor: ForegroundExecutor::new(dispatcher), + foreground_executor: ForegroundExecutor::new(dispatcher, liveness), text_system, appearance: WindowAppearance::Light, auto_hide_scrollbars: false, diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index df169b082203f622128de6ed9351b11bb0824972..227791324efe550390c3679cd917d6ce145f277a 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -453,7 +453,7 @@ fn wl_output_version(version: u32) -> u32 { } impl WaylandClient { - pub(crate) fn new() -> Self { + pub(crate) fn new(liveness: std::sync::Weak<()>) -> Self { let conn = Connection::connect_to_env().unwrap(); let (globals, mut event_queue) = @@ -490,7 +490,7 @@ impl WaylandClient { let event_loop = EventLoop::::try_new().unwrap(); - let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal()); + let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal(), liveness); let handle = event_loop.handle(); handle diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index b69cad8431dffb18834ae8b7ef27c3abd976984f..02e83518bc440cc9b01dd809f0f6ec86a7142fc2 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -297,10 +297,10 @@ impl X11ClientStatePtr { pub(crate) struct X11Client(Rc>); impl X11Client { - pub(crate) fn new() -> anyhow::Result { + pub(crate) fn new(liveness: std::sync::Weak<()>) -> anyhow::Result { let event_loop = EventLoop::try_new()?; - let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal()); + let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal(), liveness); let handle = event_loop.handle(); diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 9b32c6735bf6215fecc0455defc4237fd25e8cb0..e9e9e9e654e6eef45492f149784c378c493999dc 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -173,14 +173,8 @@ pub(crate) struct MacPlatformState { keyboard_mapper: Rc, } -impl Default for MacPlatform { - fn default() -> Self { - Self::new(false) - } -} - impl MacPlatform { - pub(crate) fn new(headless: bool) -> Self { + pub(crate) fn new(headless: bool, liveness: std::sync::Weak<()>) -> Self { let dispatcher = Arc::new(MacDispatcher); #[cfg(feature = "font-kit")] @@ -196,7 +190,7 @@ impl MacPlatform { headless, text_system, background_executor: BackgroundExecutor::new(dispatcher.clone()), - foreground_executor: ForegroundExecutor::new(dispatcher), + foreground_executor: ForegroundExecutor::new(dispatcher, liveness), renderer_context: renderer::Context::default(), general_pasteboard: Pasteboard::general(), find_pasteboard: Pasteboard::find(), diff --git a/crates/gpui/src/platform/test/dispatcher.rs b/crates/gpui/src/platform/test/dispatcher.rs index 8ff761cb64d71a7830f2760b682ea7a84759b9bb..a94f0737f5e43d7196965995e382e8c964647702 100644 --- a/crates/gpui/src/platform/test/dispatcher.rs +++ b/crates/gpui/src/platform/test/dispatcher.rs @@ -180,12 +180,13 @@ impl TestDispatcher { RunnableVariant::Meta(runnable) => { if !runnable.metadata().is_app_alive() { drop(runnable); - self.state.lock().is_main_thread = was_main_thread; - return true; + } else { + runnable.run(); } - runnable.run() } - RunnableVariant::Compat(runnable) => runnable.run(), + RunnableVariant::Compat(runnable) => { + runnable.run(); + } }; self.state.lock().is_main_thread = was_main_thread; diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index 76643d1896cb1d3568883cb881541ba42a7fef65..7b86a9ab876a275a217b13a9b2ca22ef7d0fc982 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -93,7 +93,7 @@ impl WindowsPlatformState { } impl WindowsPlatform { - pub(crate) fn new() -> Result { + pub(crate) fn new(liveness: std::sync::Weak<()>) -> Result { unsafe { OleInitialize(None).context("unable to initialize Windows OLE")?; } @@ -148,7 +148,7 @@ impl WindowsPlatform { let disable_direct_composition = std::env::var(DISABLE_DIRECT_COMPOSITION) .is_ok_and(|value| value == "true" || value == "1"); let background_executor = BackgroundExecutor::new(dispatcher.clone()); - let foreground_executor = ForegroundExecutor::new(dispatcher); + let foreground_executor = ForegroundExecutor::new(dispatcher, liveness); let drop_target_helper: IDropTargetHelper = unsafe { CoCreateInstance(&CLSID_DragDropHelper, None, CLSCTX_INPROC_SERVER)