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); } } }