diff --git a/crates/crashes/src/crashes.rs b/crates/crashes/src/crashes.rs index 5d3fc954d3..959c676b4e 100644 --- a/crates/crashes/src/crashes.rs +++ b/crates/crashes/src/crashes.rs @@ -182,6 +182,7 @@ pub struct CrashServer { active_gpu: Mutex>, user_info: Mutex>, abort_message_location: Mutex>, + shutdown: Arc, has_connection: Arc, logs_dir: PathBuf, } @@ -255,6 +256,11 @@ pub fn set_user_info(crash_client: &Arc, info: UserInfo) { send_crash_server_message(crash_client, CrashServerMessage::UserInfo(info)); } +/// Requests an orderly exit from the crash-handler sidecar. +pub fn shutdown_crash_handler(crash_client: &Arc) { + send_crash_server_message(crash_client, CrashServerMessage::Shutdown); +} + #[derive(Serialize, Deserialize, Debug)] enum CrashServerMessage { Init(InitCrashHandler), @@ -262,6 +268,7 @@ enum CrashServerMessage { GPUInfo(GpuSpecs), UserInfo(UserInfo), AbortMessageLocation(AbortMessageLocation), + Shutdown, } /// glibc records the diagnostic it prints just before aborting (malloc integrity @@ -464,6 +471,9 @@ impl minidumper::ServerHandler for CrashServer { CrashServerMessage::AbortMessageLocation(location) => { self.abort_message_location.lock().replace(location); } + CrashServerMessage::Shutdown => { + self.shutdown.store(true, Ordering::SeqCst); + } } } @@ -668,6 +678,7 @@ pub fn crash_server(socket: &Path, logs_dir: PathBuf) { panic_info: Mutex::default(), user_info: Mutex::default(), abort_message_location: Mutex::default(), + shutdown: shutdown.clone(), has_connection, active_gpu: Mutex::default(), logs_dir, diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 08f4be87f5..0c58c55e8a 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -918,8 +918,19 @@ impl App { platform.on_quit(Box::new({ let cx = Rc::downgrade(&app); move || { - if let Some(cx) = cx.upgrade() { - cx.borrow_mut().shutdown(); + let Some(cx) = cx.upgrade() else { + return true; + }; + match cx.try_borrow_mut() { + Ok(mut cx) => { + cx.shutdown(); + true + } + Err(_) => { + // Quit was requested while the AppCell was borrowed, so we can't shut down synchronously. + // The platform decides how to proceed. + false + } } } })); diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index ab4b500825..bdafe8cf56 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -200,7 +200,7 @@ pub trait Platform: 'static { fn reveal_path(&self, path: &Path); fn open_with_system(&self, path: &Path); - fn on_quit(&self, callback: Box); + fn on_quit(&self, callback: Box bool>); fn on_reopen(&self, callback: Box); fn on_system_wake(&self, callback: Box); diff --git a/crates/gpui/src/platform/test/platform.rs b/crates/gpui/src/platform/test/platform.rs index 3982190f5c..86c2297d84 100644 --- a/crates/gpui/src/platform/test/platform.rs +++ b/crates/gpui/src/platform/test/platform.rs @@ -454,7 +454,7 @@ impl Platform for TestPlatform { unimplemented!() } - fn on_quit(&self, _callback: Box) {} + fn on_quit(&self, _callback: Box bool>) {} fn on_reopen(&self, _callback: Box) { unimplemented!() diff --git a/crates/gpui/src/platform/visual_test.rs b/crates/gpui/src/platform/visual_test.rs index 3ff561b26a..042a757f22 100644 --- a/crates/gpui/src/platform/visual_test.rs +++ b/crates/gpui/src/platform/visual_test.rs @@ -172,7 +172,7 @@ impl Platform for VisualTestPlatform { self.platform.open_with_system(path) } - fn on_quit(&self, _callback: Box) {} + fn on_quit(&self, _callback: Box bool>) {} fn on_reopen(&self, _callback: Box) {} diff --git a/crates/gpui_linux/src/linux/platform.rs b/crates/gpui_linux/src/linux/platform.rs index 7e8ec2224e..53dec5dca9 100644 --- a/crates/gpui_linux/src/linux/platform.rs +++ b/crates/gpui_linux/src/linux/platform.rs @@ -106,7 +106,7 @@ pub(crate) trait LinuxClient { #[derive(Default)] pub(crate) struct PlatformHandlers { pub(crate) open_urls: Option)>>, - pub(crate) quit: Option>, + pub(crate) quit: Option bool>>, pub(crate) reopen: Option>, pub(crate) app_menu_action: Option>, pub(crate) will_open_app_menu: Option>, @@ -547,7 +547,7 @@ impl Platform for LinuxPlatform

{ .detach(); } - fn on_quit(&self, callback: Box) { + fn on_quit(&self, callback: Box bool>) { self.inner.with_common(|common| { common.callbacks.quit = Some(callback); }); diff --git a/crates/gpui_macos/src/platform.rs b/crates/gpui_macos/src/platform.rs index 3ec54e8452..c1d857c78a 100644 --- a/crates/gpui_macos/src/platform.rs +++ b/crates/gpui_macos/src/platform.rs @@ -178,7 +178,7 @@ pub(crate) struct MacPlatformState { on_thermal_state_change: Option>, on_system_wake: Option>, system_wake_observer_registered: bool, - quit: Option>, + quit: Option bool>>, menu_command: Option>, validate_menu_command: Option bool>>, will_open_menu: Option>, @@ -940,7 +940,7 @@ impl Platform for MacPlatform { .detach(); } - fn on_quit(&self, callback: Box) { + fn on_quit(&self, callback: Box bool>) { self.0.lock().quit = Some(callback); } diff --git a/crates/gpui_web/src/platform.rs b/crates/gpui_web/src/platform.rs index 6e6f9c0c77..a546173198 100644 --- a/crates/gpui_web/src/platform.rs +++ b/crates/gpui_web/src/platform.rs @@ -106,7 +106,7 @@ impl std::error::Error for WebWindowError {} #[derive(Default)] struct WebPlatformCallbacks { open_urls: Option)>>, - quit: Option>, + quit: Option bool>>, reopen: Option>, app_menu_action: Option>, will_open_app_menu: Option>, @@ -458,7 +458,7 @@ impl Platform for WebPlatform { fn open_with_system(&self, _path: &Path) {} - fn on_quit(&self, callback: Box) { + fn on_quit(&self, callback: Box bool>) { self.callbacks.borrow_mut().quit = Some(callback); } diff --git a/crates/gpui_windows/src/events.rs b/crates/gpui_windows/src/events.rs index 1bd18493d0..e700cb0fc5 100644 --- a/crates/gpui_windows/src/events.rs +++ b/crates/gpui_windows/src/events.rs @@ -28,6 +28,7 @@ pub(crate) const WM_GPUI_FORCE_UPDATE_WINDOW: u32 = WM_USER + 5; pub(crate) const WM_GPUI_KEYBOARD_LAYOUT_CHANGED: u32 = WM_USER + 6; pub(crate) const WM_GPUI_GPU_DEVICE_LOST: u32 = WM_USER + 7; pub(crate) const WM_GPUI_KEYDOWN: u32 = WM_USER + 8; +pub(crate) const WM_GPUI_END_SESSION: u32 = WM_USER + 9; const SIZE_MOVE_LOOP_TIMER_ID: usize = 1; @@ -108,6 +109,8 @@ impl WindowsWindowInner { WM_PAINT => self.handle_paint_msg(handle), WM_CLOSE => self.handle_close_msg(), WM_DESTROY => self.handle_destroy_msg(handle), + WM_QUERYENDSESSION => Some(1), + WM_ENDSESSION => self.handle_end_session_msg(wparam), WM_MOUSEMOVE => self.handle_mouse_move_msg(handle, lparam, wparam), WM_MOUSELEAVE | WM_NCMOUSELEAVE => self.handle_mouse_leave_msg(), WM_NCMOUSEMOVE => self.handle_nc_mouse_move_msg(handle, lparam), @@ -170,6 +173,20 @@ impl WindowsWindowInner { } } + fn handle_end_session_msg(&self, wparam: WPARAM) -> Option { + if wparam.0 != 0 { + unsafe { + SendMessageW( + self.platform_window_handle, + WM_GPUI_END_SESSION, + Some(WPARAM(self.validation_number)), + None, + ); + } + } + Some(0) + } + fn handle_move_msg(&self, handle: HWND, lparam: LPARAM) -> Option { let origin = logical_point( lparam.signed_loword() as f32, diff --git a/crates/gpui_windows/src/platform.rs b/crates/gpui_windows/src/platform.rs index 0c0f18a0ee..9147ce51f2 100644 --- a/crates/gpui_windows/src/platform.rs +++ b/crates/gpui_windows/src/platform.rs @@ -79,7 +79,7 @@ pub(crate) struct WindowsPlatformState { #[derive(Default)] struct PlatformCallbacks { open_urls: Cell)>>>, - quit: Cell>>, + quit: Cell bool>>>, reopen: Cell>>, app_menu_action: Cell>>, will_open_app_menu: Cell>>, @@ -460,8 +460,12 @@ impl Platform for WindowsPlatform { } } - self.inner - .with_callback(|callbacks| &callbacks.quit, |callback| callback()); + self.inner.with_callback( + |callbacks| &callbacks.quit, + |callback| { + callback(); + }, + ); } fn quit(&self) { @@ -667,7 +671,7 @@ impl Platform for WindowsPlatform { .detach(); } - fn on_quit(&self, callback: Box) { + fn on_quit(&self, callback: Box bool>) { self.inner.state.callbacks.quit.set(Some(callback)); } @@ -997,7 +1001,8 @@ impl WindowsPlatformInner { | WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD | WM_GPUI_DOCK_MENU_ACTION | WM_GPUI_KEYBOARD_LAYOUT_CHANGED - | WM_GPUI_GPU_DEVICE_LOST => self.handle_gpui_events(msg, wparam, lparam), + | WM_GPUI_GPU_DEVICE_LOST + | WM_GPUI_END_SESSION => self.handle_gpui_events(msg, wparam, lparam), WM_POWERBROADCAST => self.handle_power_broadcast(wparam), _ => None, }; @@ -1022,10 +1027,29 @@ impl WindowsPlatformInner { WM_GPUI_DOCK_MENU_ACTION => self.handle_dock_action_event(lparam.0 as _), WM_GPUI_KEYBOARD_LAYOUT_CHANGED => self.handle_keyboard_layout_change(), WM_GPUI_GPU_DEVICE_LOST => self.handle_device_lost(lparam), + WM_GPUI_END_SESSION => self.handle_end_session(), _ => unreachable!(), } } + fn handle_end_session(&self) -> Option { + let mut shutdown_completed = false; + self.with_callback( + |callbacks| &callbacks.quit, + |callback| shutdown_completed = callback(), + ); + log::logger().flush(); + if shutdown_completed { + std::process::exit(0); + } + + // Shutdown couldn't run synchronously, since the AppCell is already borrowed. + // Windows may terminate the application as soon as we return from this handler, but if we post a WM_QUIT message now, + // we may get to gracefully shut down the app before we're terminated by the OS. + unsafe { PostQuitMessage(0) }; + Some(0) + } + fn close_one_window(&self, target_window: HWND) -> bool { let Some(all_windows) = self.raw_window_handles.upgrade() else { log::error!("Failed to upgrade raw window handles");