From 18532995ecfe37f6f82861985e20f05e3c6f1ec0 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 2 Mar 2026 10:23:37 +0100 Subject: [PATCH] language: Defer dropping the `SyntaxSnapshot` to a background thread (#50386) Dropping deep tree-sitter Trees can be quite slow due to deallocating lots of memory (in the 10s of milliseconds for big diffs). To avoid blocking the main thread, we offload the drop operation to a background thread. Instead of a static thread we could also integrate this with the gpui app or use the executors, but both of that would require threading that through as a field which I don't think is too great either Release Notes: - N/A *or* Added/Fixed/Improved ... --- crates/language/src/syntax_map.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index bd24424679f3e6cb02303c91e0d86db335cd0a26..c5931c474d2962fc7ceb66954f2f00d3bf14b4f8 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -13,7 +13,7 @@ use std::{ collections::BinaryHeap, fmt, iter, ops::{ControlFlow, Deref, DerefMut, Range}, - sync::Arc, + sync::{Arc, LazyLock}, time::{Duration, Instant}, }; use streaming_iterator::StreamingIterator; @@ -40,6 +40,27 @@ pub struct SyntaxSnapshot { update_count: usize, } +// Dropping deep treesitter Trees can be quite slow due to deallocating lots of memory. +// To avoid blocking the main thread, we offload the drop operation to a background thread. +impl Drop for SyntaxSnapshot { + fn drop(&mut self) { + static DROP_TX: LazyLock>> = + LazyLock::new(|| { + let (tx, rx) = std::sync::mpsc::channel(); + std::thread::Builder::new() + .name("SyntaxSnapshot::drop".into()) + .spawn(move || while let Ok(_) = rx.recv() {}) + .expect("failed to spawn drop thread"); + tx + }); + // This does allocate a new Arc, but it's cheap and avoids blocking the main thread without needing to use an `Option` or `MaybeUninit`. + let _ = DROP_TX.send(std::mem::replace( + &mut self.layers, + SumTree::from_summary(Default::default()), + )); + } +} + #[derive(Default)] pub struct SyntaxMapCaptures<'a> { layers: Vec>,