🎨 Don't store path changes statefully on the background scanner

This commit is contained in:
Max Brunsfeld 2023-03-23 16:04:21 -07:00
parent 3ff5aee4a1
commit 89e99d2902

View file

@ -1,12 +1,12 @@
use super::{ignore::IgnoreStack, DiagnosticSummary}; use crate::{
use crate::{copy_recursive, ProjectEntryId, RemoveOptions}; copy_recursive, ignore::IgnoreStack, DiagnosticSummary, ProjectEntryId, RemoveOptions,
};
use ::ignore::gitignore::{Gitignore, GitignoreBuilder}; use ::ignore::gitignore::{Gitignore, GitignoreBuilder};
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use client::{proto, Client}; use client::{proto, Client};
use clock::ReplicaId; use clock::ReplicaId;
use collections::{HashMap, VecDeque}; use collections::{HashMap, VecDeque};
use fs::LineEnding; use fs::{repository::GitRepository, Fs, LineEnding};
use fs::{repository::GitRepository, Fs};
use futures::{ use futures::{
channel::{ channel::{
mpsc::{self, UnboundedReceiver, UnboundedSender}, mpsc::{self, UnboundedReceiver, UnboundedSender},
@ -20,17 +20,16 @@ use gpui::{
executor, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, executor, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext,
Task, Task,
}; };
use language::File as _;
use language::{ use language::{
proto::{ proto::{
deserialize_fingerprint, deserialize_version, serialize_fingerprint, serialize_line_ending, deserialize_fingerprint, deserialize_version, serialize_fingerprint, serialize_line_ending,
serialize_version, serialize_version,
}, },
Buffer, DiagnosticEntry, PointUtf16, Rope, RopeFingerprint, Unclipped, Buffer, DiagnosticEntry, File as _, PointUtf16, Rope, RopeFingerprint, Unclipped,
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
use postage::barrier;
use postage::{ use postage::{
barrier,
prelude::{Sink as _, Stream as _}, prelude::{Sink as _, Stream as _},
watch, watch,
}; };
@ -50,8 +49,7 @@ use std::{
time::{Duration, SystemTime}, time::{Duration, SystemTime},
}; };
use sum_tree::{Bias, Edit, SeekTarget, SumTree, TreeMap, TreeSet}; use sum_tree::{Bias, Edit, SeekTarget, SumTree, TreeMap, TreeSet};
use util::paths::HOME; use util::{paths::HOME, ResultExt, TryFutureExt};
use util::{ResultExt, TryFutureExt};
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)] #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
pub struct WorktreeId(usize); pub struct WorktreeId(usize);
@ -2141,7 +2139,6 @@ impl<'a> sum_tree::Dimension<'a, EntrySummary> for PathKey {
struct BackgroundScanner { struct BackgroundScanner {
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
snapshot: Mutex<LocalSnapshot>, snapshot: Mutex<LocalSnapshot>,
changes: HashMap<Arc<Path>, PathChange>,
notify: UnboundedSender<ScanState>, notify: UnboundedSender<ScanState>,
executor: Arc<executor::Background>, executor: Arc<executor::Background>,
} }
@ -2158,7 +2155,6 @@ impl BackgroundScanner {
snapshot: Mutex::new(snapshot), snapshot: Mutex::new(snapshot),
notify, notify,
executor, executor,
changes: Default::default(),
} }
} }
@ -2167,7 +2163,7 @@ impl BackgroundScanner {
} }
async fn run( async fn run(
mut self, self,
events_rx: impl Stream<Item = Vec<fsevent::Event>>, events_rx: impl Stream<Item = Vec<fsevent::Event>>,
mut changed_paths: UnboundedReceiver<(Vec<PathBuf>, barrier::Sender)>, mut changed_paths: UnboundedReceiver<(Vec<PathBuf>, barrier::Sender)>,
) { ) {
@ -2312,32 +2308,31 @@ impl BackgroundScanner {
while let Poll::Ready(Some(additional_events)) = futures::poll!(events_rx.next()) { while let Poll::Ready(Some(additional_events)) = futures::poll!(events_rx.next()) {
events.extend(additional_events); events.extend(additional_events);
} }
let abs_paths = events.into_iter().map(|e| e.path).collect();
if self.notify.unbounded_send(ScanState::Updating).is_err() { if self.notify.unbounded_send(ScanState::Updating).is_err() {
return; return;
} }
if !self if let Some(changes) = self.process_events(abs_paths, true).await {
.process_events(events.into_iter().map(|e| e.path).collect(), true) if self
.await .notify
{ .unbounded_send(ScanState::Updated {
return; snapshot: self.snapshot.lock().clone(),
} changes,
if self barrier: None,
.notify })
.unbounded_send(ScanState::Updated { .is_err()
snapshot: self.snapshot.lock().clone(), {
changes: mem::take(&mut self.changes), return;
barrier: None, }
}) } else {
.is_err()
{
return; return;
} }
} }
// Continue processing events until the worktree is dropped. // Continue processing events until the worktree is dropped.
loop { loop {
let abs_paths;
let barrier; let barrier;
let abs_paths;
select_biased! { select_biased! {
request = changed_paths.next().fuse() => { request = changed_paths.next().fuse() => {
let Some((paths, b)) = request else { break }; let Some((paths, b)) = request else { break };
@ -2354,18 +2349,19 @@ impl BackgroundScanner {
if self.notify.unbounded_send(ScanState::Updating).is_err() { if self.notify.unbounded_send(ScanState::Updating).is_err() {
return; return;
} }
if !self.process_events(abs_paths, false).await { if let Some(changes) = self.process_events(abs_paths, false).await {
return; if self
} .notify
if self .unbounded_send(ScanState::Updated {
.notify snapshot: self.snapshot.lock().clone(),
.unbounded_send(ScanState::Updated { changes,
snapshot: self.snapshot.lock().clone(), barrier,
changes: mem::take(&mut self.changes), })
barrier, .is_err()
}) {
.is_err() return;
{ }
} else {
return; return;
} }
} }
@ -2505,10 +2501,10 @@ impl BackgroundScanner {
} }
async fn process_events( async fn process_events(
&mut self, &self,
abs_paths: Vec<PathBuf>, abs_paths: Vec<PathBuf>,
received_before_initialized: bool, received_before_initialized: bool,
) -> bool { ) -> Option<HashMap<Arc<Path>, PathChange>> {
let (scan_queue_tx, scan_queue_rx) = channel::unbounded(); let (scan_queue_tx, scan_queue_rx) = channel::unbounded();
let prev_snapshot = { let prev_snapshot = {
@ -2517,14 +2513,9 @@ impl BackgroundScanner {
snapshot.clone() snapshot.clone()
}; };
let event_paths = if let Some(event_paths) = self let event_paths = self
.update_entries_for_paths(abs_paths, Some(scan_queue_tx)) .update_entries_for_paths(abs_paths, Some(scan_queue_tx))
.await .await?;
{
event_paths
} else {
return false;
};
// Scan any directories that were created as part of this event batch. // Scan any directories that were created as part of this event batch.
self.executor self.executor
@ -2553,13 +2544,13 @@ impl BackgroundScanner {
self.update_ignore_statuses().await; self.update_ignore_statuses().await;
self.update_git_repositories(); self.update_git_repositories();
self.build_change_set( let changes = self.build_change_set(
prev_snapshot.snapshot, prev_snapshot.snapshot,
event_paths, event_paths,
received_before_initialized, received_before_initialized,
); );
self.snapshot.lock().scan_completed(); self.snapshot.lock().scan_completed();
true Some(changes)
} }
async fn update_entries_for_paths( async fn update_entries_for_paths(
@ -2763,17 +2754,18 @@ impl BackgroundScanner {
} }
fn build_change_set( fn build_change_set(
&mut self, &self,
old_snapshot: Snapshot, old_snapshot: Snapshot,
event_paths: Vec<Arc<Path>>, event_paths: Vec<Arc<Path>>,
received_before_initialized: bool, received_before_initialized: bool,
) { ) -> HashMap<Arc<Path>, PathChange> {
use PathChange::{Added, AddedOrUpdated, Removed, Updated};
let new_snapshot = self.snapshot.lock(); let new_snapshot = self.snapshot.lock();
let mut changes = HashMap::default();
let mut old_paths = old_snapshot.entries_by_path.cursor::<PathKey>(); let mut old_paths = old_snapshot.entries_by_path.cursor::<PathKey>();
let mut new_paths = new_snapshot.entries_by_path.cursor::<PathKey>(); let mut new_paths = new_snapshot.entries_by_path.cursor::<PathKey>();
use PathChange::{Added, AddedOrUpdated, Removed, Updated};
for path in event_paths { for path in event_paths {
let path = PathKey(path); let path = PathKey(path);
old_paths.seek(&path, Bias::Left, &()); old_paths.seek(&path, Bias::Left, &());
@ -2792,7 +2784,7 @@ impl BackgroundScanner {
match Ord::cmp(&old_entry.path, &new_entry.path) { match Ord::cmp(&old_entry.path, &new_entry.path) {
Ordering::Less => { Ordering::Less => {
self.changes.insert(old_entry.path.clone(), Removed); changes.insert(old_entry.path.clone(), Removed);
old_paths.next(&()); old_paths.next(&());
} }
Ordering::Equal => { Ordering::Equal => {
@ -2800,31 +2792,32 @@ impl BackgroundScanner {
// If the worktree was not fully initialized when this event was generated, // If the worktree was not fully initialized when this event was generated,
// we can't know whether this entry was added during the scan or whether // we can't know whether this entry was added during the scan or whether
// it was merely updated. // it was merely updated.
self.changes.insert(old_entry.path.clone(), AddedOrUpdated); changes.insert(old_entry.path.clone(), AddedOrUpdated);
} else if old_entry.mtime != new_entry.mtime { } else if old_entry.mtime != new_entry.mtime {
self.changes.insert(old_entry.path.clone(), Updated); changes.insert(old_entry.path.clone(), Updated);
} }
old_paths.next(&()); old_paths.next(&());
new_paths.next(&()); new_paths.next(&());
} }
Ordering::Greater => { Ordering::Greater => {
self.changes.insert(new_entry.path.clone(), Added); changes.insert(new_entry.path.clone(), Added);
new_paths.next(&()); new_paths.next(&());
} }
} }
} }
(Some(old_entry), None) => { (Some(old_entry), None) => {
self.changes.insert(old_entry.path.clone(), Removed); changes.insert(old_entry.path.clone(), Removed);
old_paths.next(&()); old_paths.next(&());
} }
(None, Some(new_entry)) => { (None, Some(new_entry)) => {
self.changes.insert(new_entry.path.clone(), Added); changes.insert(new_entry.path.clone(), Added);
new_paths.next(&()); new_paths.next(&());
} }
(None, None) => break, (None, None) => break,
} }
} }
} }
changes
} }
} }