Fix background scanner deadlock (#44109)
Fixes a deadlock in the background scanner that occurs on single-core Linux devices. This happens because the background scanner would `block` on a background thread waiting for a future, but on single-core Linux devices there would be no other thread to pick it up. This mostly affects SSH remoting use cases where it's common for servers to have 1 vCPU. Closes #43884 Closes #43809 Release Notes: - Fix SSH remoting hang when connecting to 1 vCPU servers
This commit is contained in:
@@ -52,7 +52,7 @@ use std::{
|
||||
fmt,
|
||||
future::Future,
|
||||
mem::{self},
|
||||
ops::{Deref, DerefMut},
|
||||
ops::{Deref, DerefMut, Range},
|
||||
path::{Path, PathBuf},
|
||||
pin::Pin,
|
||||
sync::{
|
||||
@@ -3877,29 +3877,35 @@ impl BackgroundScanner {
|
||||
abs_paths.dedup_by(|a, b| a.starts_with(b));
|
||||
{
|
||||
let snapshot = &self.state.lock().await.snapshot;
|
||||
abs_paths.retain(|abs_path| {
|
||||
let abs_path = &SanitizedPath::new(abs_path);
|
||||
|
||||
let mut ranges_to_drop = SmallVec::<[Range<usize>; 4]>::new();
|
||||
|
||||
fn skip_ix(ranges: &mut SmallVec<[Range<usize>; 4]>, ix: usize) {
|
||||
if let Some(last_range) = ranges.last_mut()
|
||||
&& last_range.end == ix
|
||||
{
|
||||
last_range.end += 1;
|
||||
} else {
|
||||
ranges.push(ix..ix + 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (ix, abs_path) in abs_paths.iter().enumerate() {
|
||||
let abs_path = &SanitizedPath::new(&abs_path);
|
||||
|
||||
{
|
||||
let mut is_git_related = false;
|
||||
let mut dot_git_paths = None;
|
||||
|
||||
let dot_git_paths = self.executor.block(maybe!(async {
|
||||
let mut path = None;
|
||||
for ancestor in abs_path.as_path().ancestors() {
|
||||
|
||||
for ancestor in abs_path.as_path().ancestors() {
|
||||
if is_git_dir(ancestor, self.fs.as_ref()).await {
|
||||
let path_in_git_dir = abs_path
|
||||
.as_path()
|
||||
.strip_prefix(ancestor)
|
||||
.expect("stripping off the ancestor");
|
||||
path = Some((ancestor.to_owned(), path_in_git_dir.to_owned()));
|
||||
break;
|
||||
dot_git_paths = Some((ancestor.to_owned(), path_in_git_dir.to_owned()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
path
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
if let Some((dot_git_abs_path, path_in_git_dir)) = dot_git_paths {
|
||||
if skipped_files_in_dot_git
|
||||
@@ -3909,8 +3915,11 @@ impl BackgroundScanner {
|
||||
path_in_git_dir.starts_with(skipped_git_subdir)
|
||||
})
|
||||
{
|
||||
log::debug!("ignoring event {abs_path:?} as it's in the .git directory among skipped files or directories");
|
||||
return false;
|
||||
log::debug!(
|
||||
"ignoring event {abs_path:?} as it's in the .git directory among skipped files or directories"
|
||||
);
|
||||
skip_ix(&mut ranges_to_drop, ix);
|
||||
continue;
|
||||
}
|
||||
|
||||
is_git_related = true;
|
||||
@@ -3919,8 +3928,7 @@ impl BackgroundScanner {
|
||||
}
|
||||
}
|
||||
|
||||
let relative_path = if let Ok(path) =
|
||||
abs_path.strip_prefix(&root_canonical_path)
|
||||
let relative_path = if let Ok(path) = abs_path.strip_prefix(&root_canonical_path)
|
||||
&& let Ok(path) = RelPath::new(path, PathStyle::local())
|
||||
{
|
||||
path
|
||||
@@ -3931,10 +3939,11 @@ impl BackgroundScanner {
|
||||
);
|
||||
} else {
|
||||
log::error!(
|
||||
"ignoring event {abs_path:?} outside of root path {root_canonical_path:?}",
|
||||
"ignoring event {abs_path:?} outside of root path {root_canonical_path:?}",
|
||||
);
|
||||
}
|
||||
return false;
|
||||
skip_ix(&mut ranges_to_drop, ix);
|
||||
continue;
|
||||
};
|
||||
|
||||
if abs_path.file_name() == Some(OsStr::new(GITIGNORE)) {
|
||||
@@ -3958,21 +3967,26 @@ impl BackgroundScanner {
|
||||
});
|
||||
if !parent_dir_is_loaded {
|
||||
log::debug!("ignoring event {relative_path:?} within unloaded directory");
|
||||
return false;
|
||||
skip_ix(&mut ranges_to_drop, ix);
|
||||
continue;
|
||||
}
|
||||
|
||||
if self.settings.is_path_excluded(&relative_path) {
|
||||
if !is_git_related {
|
||||
log::debug!("ignoring FS event for excluded path {relative_path:?}");
|
||||
}
|
||||
return false;
|
||||
skip_ix(&mut ranges_to_drop, ix);
|
||||
continue;
|
||||
}
|
||||
|
||||
relative_paths.push(relative_path.into_arc());
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
for range_to_drop in ranges_to_drop.into_iter().rev() {
|
||||
abs_paths.drain(range_to_drop);
|
||||
}
|
||||
}
|
||||
|
||||
if relative_paths.is_empty() && dot_git_abs_paths.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user