fs: Replace a bunch of uses of smol::fs with manual impls (#40172)
smol::fs uses a separate threadpool, which is a bit yuck. This PR also added a benchmark you can use to run a full worktree scan (initial one, that is) for arbitrary worktree.. and refactored worktree scanner to use async locks, as otherwise tests were deadlocking. :) I've benchmarked it against Zed, Linux and Chromium and saw a ~60% drop in initial worktree scan times across the board. Release Notes: - Significantly (3.3x speedup over the old implementation) improved speed of Zed's worktree scanner, that's responsible for synchronizing the state of your project with the state of files on hard drive. --------- Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
This commit is contained in:
co-authored by
Smit Barmase
parent
9c70ba7dcc
commit
c37a2f885a
+67
-26
@@ -7,6 +7,7 @@ pub mod fs_watcher;
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
use ashpd::desktop::trash;
|
||||
use futures::stream::iter;
|
||||
use gpui::App;
|
||||
use gpui::BackgroundExecutor;
|
||||
use gpui::Global;
|
||||
@@ -562,12 +563,17 @@ impl Fs for RealFs {
|
||||
|
||||
async fn load(&self, path: &Path) -> Result<String> {
|
||||
let path = path.to_path_buf();
|
||||
let text = smol::unblock(|| std::fs::read_to_string(path)).await?;
|
||||
Ok(text)
|
||||
self.executor
|
||||
.spawn(async move { Ok(std::fs::read_to_string(path)?) })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn load_bytes(&self, path: &Path) -> Result<Vec<u8>> {
|
||||
let path = path.to_path_buf();
|
||||
let bytes = smol::unblock(|| std::fs::read(path)).await?;
|
||||
let bytes = self
|
||||
.executor
|
||||
.spawn(async move { std::fs::read(path) })
|
||||
.await?;
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
@@ -635,30 +641,46 @@ impl Fs for RealFs {
|
||||
if let Some(path) = path.parent() {
|
||||
self.create_dir(path).await?;
|
||||
}
|
||||
smol::fs::write(path, content).await?;
|
||||
Ok(())
|
||||
let path = path.to_owned();
|
||||
let contents = content.to_owned();
|
||||
self.executor
|
||||
.spawn(async move {
|
||||
std::fs::write(path, contents)?;
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn canonicalize(&self, path: &Path) -> Result<PathBuf> {
|
||||
Ok(smol::fs::canonicalize(path)
|
||||
let path = path.to_owned();
|
||||
self.executor
|
||||
.spawn(async move {
|
||||
std::fs::canonicalize(&path).with_context(|| format!("canonicalizing {path:?}"))
|
||||
})
|
||||
.await
|
||||
.with_context(|| format!("canonicalizing {path:?}"))?)
|
||||
}
|
||||
|
||||
async fn is_file(&self, path: &Path) -> bool {
|
||||
smol::fs::metadata(path)
|
||||
let path = path.to_owned();
|
||||
self.executor
|
||||
.spawn(async move { std::fs::metadata(path).is_ok_and(|metadata| metadata.is_file()) })
|
||||
.await
|
||||
.is_ok_and(|metadata| metadata.is_file())
|
||||
}
|
||||
|
||||
async fn is_dir(&self, path: &Path) -> bool {
|
||||
smol::fs::metadata(path)
|
||||
let path = path.to_owned();
|
||||
self.executor
|
||||
.spawn(async move { std::fs::metadata(path).is_ok_and(|metadata| metadata.is_dir()) })
|
||||
.await
|
||||
.is_ok_and(|metadata| metadata.is_dir())
|
||||
}
|
||||
|
||||
async fn metadata(&self, path: &Path) -> Result<Option<Metadata>> {
|
||||
let symlink_metadata = match smol::fs::symlink_metadata(path).await {
|
||||
let path_buf = path.to_owned();
|
||||
let symlink_metadata = match self
|
||||
.executor
|
||||
.spawn(async move { std::fs::symlink_metadata(&path_buf) })
|
||||
.await
|
||||
{
|
||||
Ok(metadata) => metadata,
|
||||
Err(err) => {
|
||||
return match (err.kind(), err.raw_os_error()) {
|
||||
@@ -669,19 +691,28 @@ impl Fs for RealFs {
|
||||
}
|
||||
};
|
||||
|
||||
let path_buf = path.to_path_buf();
|
||||
let path_exists = smol::unblock(move || {
|
||||
path_buf
|
||||
.try_exists()
|
||||
.with_context(|| format!("checking existence for path {path_buf:?}"))
|
||||
})
|
||||
.await?;
|
||||
let is_symlink = symlink_metadata.file_type().is_symlink();
|
||||
let metadata = match (is_symlink, path_exists) {
|
||||
(true, true) => smol::fs::metadata(path)
|
||||
.await
|
||||
.with_context(|| "accessing symlink for path {path}")?,
|
||||
_ => symlink_metadata,
|
||||
let metadata = if is_symlink {
|
||||
let path_buf = path.to_path_buf();
|
||||
let path_exists = self
|
||||
.executor
|
||||
.spawn(async move {
|
||||
path_buf
|
||||
.try_exists()
|
||||
.with_context(|| format!("checking existence for path {path_buf:?}"))
|
||||
})
|
||||
.await?;
|
||||
if path_exists {
|
||||
let path_buf = path.to_path_buf();
|
||||
self.executor
|
||||
.spawn(async move { std::fs::metadata(path_buf) })
|
||||
.await
|
||||
.with_context(|| "accessing symlink for path {path}")?
|
||||
} else {
|
||||
symlink_metadata
|
||||
}
|
||||
} else {
|
||||
symlink_metadata
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -707,7 +738,11 @@ impl Fs for RealFs {
|
||||
}
|
||||
|
||||
async fn read_link(&self, path: &Path) -> Result<PathBuf> {
|
||||
let path = smol::fs::read_link(path).await?;
|
||||
let path = path.to_owned();
|
||||
let path = self
|
||||
.executor
|
||||
.spawn(async move { std::fs::read_link(&path) })
|
||||
.await?;
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
@@ -715,7 +750,13 @@ impl Fs for RealFs {
|
||||
&self,
|
||||
path: &Path,
|
||||
) -> Result<Pin<Box<dyn Send + Stream<Item = Result<PathBuf>>>>> {
|
||||
let result = smol::fs::read_dir(path).await?.map(|entry| match entry {
|
||||
let path = path.to_owned();
|
||||
let result = iter(
|
||||
self.executor
|
||||
.spawn(async move { std::fs::read_dir(path) })
|
||||
.await?,
|
||||
)
|
||||
.map(|entry| match entry {
|
||||
Ok(entry) => Ok(entry.path()),
|
||||
Err(error) => Err(anyhow!("failed to read dir entry {error:?}")),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user