From de6855fa6e49ae8f8d70506707403fd23354682d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 1 Dec 2025 15:23:12 +0100 Subject: [PATCH] gpui(windows): Fix apps not quitting if they overwhelm the foreground thread with tasks (#43896) Release Notes: - N/A *or* Added/Fixed/Improved ... --- crates/gpui/src/platform/windows/platform.rs | 41 +++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index b7ad6029b87dd6bfcc018fc65afbe201aecfc647..396708fb347380a168b56d7ff7e0a9129bdd3af3 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -815,12 +815,14 @@ impl WindowsPlatformInner { fn run_foreground_task(&self) -> Option { const MAIN_TASK_TIMEOUT: u128 = 10; - let mut start = std::time::Instant::now(); - loop { - loop { + let start = std::time::Instant::now(); + 'tasks: loop { + 'timeout_loop: loop { if start.elapsed().as_millis() >= MAIN_TASK_TIMEOUT { - // we spent our budget on gpui tasks, we likely have a lot of work queued so drain system events first - // before returning to main thread task work + log::debug!("foreground task timeout reached"); + // we spent our budget on gpui tasks, we likely have a lot of work queued so drain system events first to stay responsive + // then quit out of foreground work to allow us to process other gpui events first before returning back to foreground task work + // if we don't we might not for example process window quit events let mut msg = MSG::default(); let process_message = |msg: &_| { if translate_accelerator(msg).is_none() { @@ -831,39 +833,40 @@ impl WindowsPlatformInner { let peek_msg = |msg: &mut _, msg_kind| unsafe { PeekMessageW(msg, None, 0, 0, PM_REMOVE | msg_kind).as_bool() }; - // We process a paint at the start and end only to prevent getting stuck in a paint loop - // due to on going gpui animations if peek_msg(&mut msg, PM_QS_PAINT) { process_message(&msg); } while peek_msg(&mut msg, PM_QS_INPUT) { process_message(&msg); } - if peek_msg(&mut msg, PM_QS_PAINT) { - process_message(&msg); + // Allow the main loop to process other gpui events before going back into `run_foreground_task` + unsafe { + if let Err(_) = PostMessageW( + Some(self.dispatcher.platform_window_handle.as_raw()), + WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD, + WPARAM(self.validation_number), + LPARAM(0), + ) { + self.dispatcher.wake_posted.store(false, Ordering::Release); + }; } - start = std::time::Instant::now(); + break 'tasks; } match self.main_receiver.try_recv() { - Err(_) => break, + Err(_) => break 'timeout_loop, Ok(runnable) => WindowsDispatcher::execute_runnable(runnable), } } // Someone could enqueue a Runnable here. The flag is still true, so they will not PostMessage. // We need to check for those Runnables after we clear the flag. - let dispatcher = self.dispatcher.clone(); - - dispatcher.wake_posted.store(false, Ordering::Release); + self.dispatcher.wake_posted.store(false, Ordering::Release); match self.main_receiver.try_recv() { + Err(_) => break 'tasks, Ok(runnable) => { - let _ = dispatcher.wake_posted.swap(true, Ordering::AcqRel); + self.dispatcher.wake_posted.store(true, Ordering::Release); WindowsDispatcher::execute_runnable(runnable); - continue; - } - _ => { - break; } } }