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 082f420f11f12968dd5c6bec46c3fab4f2b37a7f..9dc5c45f34ba38da75c6223fee4e901e8877a7d4 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 326424f28d46c9802439293165eae9708d63b064..8ac2f318e46bb114fae151db99427a80eaba61b0 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 a6de68b789c33ff75b8e6f474f31b7f9f6d8399c..515056bef48889ecc4da5c3f3f1a8983f9d56841 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 a2efc12e52de64da8c796ee3fa38acd19fd05ac5..d0888d969303fdf67776243ab426289b25f10a7e 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()) {