From 9e8afa8daa47f04e1e0a46142b3cd13e8617020b Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 8 Apr 2025 11:54:51 -0600 Subject: [PATCH] Fix local task dropped on the wrong thread (#28290) Release Notes: - Fixed a panic during shutdown of the remote server --- crates/gpui/src/platform/linux/dispatcher.rs | 12 +++++++++- .../gpui/src/platform/windows/dispatcher.rs | 22 +++++++++++-------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/crates/gpui/src/platform/linux/dispatcher.rs b/crates/gpui/src/platform/linux/dispatcher.rs index 94269ea62112001092fa2e3df51b11e200bc641b..3d32dbd2fdece5259f48e52550f6983b6a8c5b1d 100644 --- a/crates/gpui/src/platform/linux/dispatcher.rs +++ b/crates/gpui/src/platform/linux/dispatcher.rs @@ -106,7 +106,17 @@ impl PlatformDispatcher for LinuxDispatcher { } fn dispatch_on_main_thread(&self, runnable: Runnable) { - self.main_sender.send(runnable).ok(); + self.main_sender.send(runnable).unwrap_or_else(|runnable| { + // NOTE: Runnable may wrap a Future that is !Send. + // + // This is usually safe because we only poll it on the main thread. + // However if the send fails, we know that: + // 1. main_receiver has been dropped (which implies the app is shutting down) + // 2. we are on a background thread. + // It is not safe to drop something !Send on the wrong thread, and + // the app will exit soon anyway, so we must forget the runnable. + std::mem::forget(runnable); + }); } fn dispatch_after(&self, duration: Duration, runnable: Runnable) { diff --git a/crates/gpui/src/platform/windows/dispatcher.rs b/crates/gpui/src/platform/windows/dispatcher.rs index d88aadcf716506466fbcf0933c725efd58fac124..e5b9c020d511b478779dc1affb3927018f8f7b3f 100644 --- a/crates/gpui/src/platform/windows/dispatcher.rs +++ b/crates/gpui/src/platform/windows/dispatcher.rs @@ -3,7 +3,6 @@ use std::{ time::Duration, }; -use anyhow::Context as _; use async_task::Runnable; use flume::Sender; use parking::Parker; @@ -95,14 +94,8 @@ impl PlatformDispatcher for WindowsDispatcher { } fn dispatch_on_main_thread(&self, runnable: Runnable) { - if self - .main_sender - .send(runnable) - .context("Dispatch on main thread failed") - .log_err() - .is_some() - { - unsafe { + match self.main_sender.send(runnable) { + Ok(_) => unsafe { PostThreadMessageW( self.main_thread_id_win32, WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD, @@ -110,6 +103,17 @@ impl PlatformDispatcher for WindowsDispatcher { LPARAM(0), ) .log_err(); + }, + Err(runnable) => { + // NOTE: Runnable may wrap a Future that is !Send. + // + // This is usually safe because we only poll it on the main thread. + // However if the send fails, we know that: + // 1. main_receiver has been dropped (which implies the app is shutting down) + // 2. we are on a background thread. + // It is not safe to drop something !Send on the wrong thread, and + // the app will exit soon anyway, so we must forget the runnable. + std::mem::forget(runnable); } } }