From fc213f1c26ed65410214f6678600a84090eb6a8f Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 28 Nov 2025 08:46:09 +0100 Subject: [PATCH] git_ui: Introduce explicit yield points for project diff (#43706) These are long running foreground tasks that tend to not have pending await points, meaning once we start executing them they will never reschedule themselves, starving the foreground thread. Release Notes: - Improved git project diff responsiveness --- Cargo.lock | 1 + crates/git_ui/Cargo.toml | 1 + crates/git_ui/src/project_diff.rs | 4 ++++ crates/project/src/git_store.rs | 11 +++++++++++ 4 files changed, 17 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 082f420f11..9dc5c45f34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7129,6 +7129,7 @@ dependencies = [ "serde", "serde_json", "settings", + "smol", "strum 0.27.2", "telemetry", "theme", diff --git a/crates/git_ui/Cargo.toml b/crates/git_ui/Cargo.toml index 326424f28d..8ac2f318e4 100644 --- a/crates/git_ui/Cargo.toml +++ b/crates/git_ui/Cargo.toml @@ -50,6 +50,7 @@ schemars.workspace = true serde.workspace = true serde_json.workspace = true settings.workspace = true +smol.workspace = true strum.workspace = true telemetry.workspace = true theme.workspace = true diff --git a/crates/git_ui/src/project_diff.rs b/crates/git_ui/src/project_diff.rs index a6de68b789..515056bef4 100644 --- a/crates/git_ui/src/project_diff.rs +++ b/crates/git_ui/src/project_diff.rs @@ -32,6 +32,7 @@ use project::{ }, }; use settings::{Settings, SettingsStore}; +use smol::future::yield_now; use std::any::{Any, TypeId}; use std::ops::Range; use std::sync::Arc; @@ -584,6 +585,9 @@ impl ProjectDiff { for (entry, path_key) in buffers_to_load.into_iter().zip(path_keys.into_iter()) { if let Some((buffer, diff)) = entry.load.await.log_err() { + // We might be lagging behind enough that all future entry.load futures are no longer pending. + // If that is the case, this task will never yield, starving the foreground thread of execution time. + yield_now().await; cx.update(|window, cx| { this.update(cx, |this, cx| { this.register_buffer(path_key, entry.file_status, buffer, diff, window, cx) diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index a2efc12e52..d0888d9693 100644 --- a/crates/project/src/git_store.rs +++ b/crates/project/src/git_store.rs @@ -56,6 +56,7 @@ use rpc::{ }; use serde::Deserialize; use settings::WorktreeId; +use smol::future::yield_now; use std::{ cmp::Ordering, collections::{BTreeSet, HashSet, VecDeque}, @@ -2984,6 +2985,10 @@ impl BufferGitState { ); } + // Dropping BufferDiff can be expensive, so yield back to the event loop + // for a bit + yield_now().await; + let mut new_uncommitted_diff = None; if let Some(uncommitted_diff) = &uncommitted_diff { new_uncommitted_diff = if index_matches_head { @@ -3005,6 +3010,10 @@ impl BufferGitState { } } + // Dropping BufferDiff can be expensive, so yield back to the event loop + // for a bit + yield_now().await; + let cancel = this.update(cx, |this, _| { // This checks whether all pending stage/unstage operations // have quiesced (i.e. both the corresponding write and the @@ -3043,6 +3052,8 @@ impl BufferGitState { None }; + yield_now().await; + if let Some((uncommitted_diff, new_uncommitted_diff)) = uncommitted_diff.as_ref().zip(new_uncommitted_diff.clone()) {