Avoid retaining executor in the FakeFs

This probably isn't the *root* cause of why an executor is leaked,
but by cutting off this cyclic references, it may make it a bit easier
to track down leaks of an executor.
This commit is contained in:
Max Brunsfeld 2022-02-28 22:39:17 -08:00
parent 3788efeadf
commit 1faaa91e52

View file

@ -267,13 +267,13 @@ impl FakeFsState {
pub struct FakeFs { pub struct FakeFs {
// Use an unfair lock to ensure tests are deterministic. // Use an unfair lock to ensure tests are deterministic.
state: futures::lock::Mutex<FakeFsState>, state: futures::lock::Mutex<FakeFsState>,
executor: std::sync::Arc<gpui::executor::Background>, executor: std::sync::Weak<gpui::executor::Background>,
} }
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
impl FakeFs { impl FakeFs {
pub fn new(executor: std::sync::Arc<gpui::executor::Background>) -> std::sync::Arc<Self> { pub fn new(executor: std::sync::Arc<gpui::executor::Background>) -> std::sync::Arc<Self> {
let (events_tx, _) = postage::broadcast::channel(2048); let (events_tx, _) = postage::broadcast::channel(2);
let mut entries = std::collections::BTreeMap::new(); let mut entries = std::collections::BTreeMap::new();
entries.insert( entries.insert(
Path::new("/").to_path_buf(), Path::new("/").to_path_buf(),
@ -288,7 +288,7 @@ impl FakeFs {
}, },
); );
std::sync::Arc::new(Self { std::sync::Arc::new(Self {
executor, executor: std::sync::Arc::downgrade(&executor),
state: futures::lock::Mutex::new(FakeFsState { state: futures::lock::Mutex::new(FakeFsState {
entries, entries,
next_inode: 1, next_inode: 1,
@ -375,13 +375,21 @@ impl FakeFs {
} }
.boxed() .boxed()
} }
async fn simulate_random_delay(&self) {
self.executor
.upgrade()
.expect("excecutor has been dropped")
.simulate_random_delay()
.await;
}
} }
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
#[async_trait::async_trait] #[async_trait::async_trait]
impl Fs for FakeFs { impl Fs for FakeFs {
async fn create_dir(&self, path: &Path) -> Result<()> { async fn create_dir(&self, path: &Path) -> Result<()> {
self.executor.simulate_random_delay().await; self.simulate_random_delay().await;
let state = &mut *self.state.lock().await; let state = &mut *self.state.lock().await;
let path = normalize_path(path); let path = normalize_path(path);
let mut ancestor_path = PathBuf::new(); let mut ancestor_path = PathBuf::new();
@ -418,7 +426,7 @@ impl Fs for FakeFs {
} }
async fn create_file(&self, path: &Path, options: CreateOptions) -> Result<()> { async fn create_file(&self, path: &Path, options: CreateOptions) -> Result<()> {
self.executor.simulate_random_delay().await; self.simulate_random_delay().await;
let mut state = self.state.lock().await; let mut state = self.state.lock().await;
let path = normalize_path(path); let path = normalize_path(path);
state.validate_path(&path)?; state.validate_path(&path)?;
@ -546,7 +554,7 @@ impl Fs for FakeFs {
async fn load(&self, path: &Path) -> Result<String> { async fn load(&self, path: &Path) -> Result<String> {
let path = normalize_path(path); let path = normalize_path(path);
self.executor.simulate_random_delay().await; self.simulate_random_delay().await;
let state = self.state.lock().await; let state = self.state.lock().await;
let text = state let text = state
.entries .entries
@ -557,7 +565,7 @@ impl Fs for FakeFs {
} }
async fn save(&self, path: &Path, text: &Rope) -> Result<()> { async fn save(&self, path: &Path, text: &Rope) -> Result<()> {
self.executor.simulate_random_delay().await; self.simulate_random_delay().await;
let mut state = self.state.lock().await; let mut state = self.state.lock().await;
let path = normalize_path(path); let path = normalize_path(path);
state.validate_path(&path)?; state.validate_path(&path)?;
@ -589,13 +597,13 @@ impl Fs for FakeFs {
} }
async fn canonicalize(&self, path: &Path) -> Result<PathBuf> { async fn canonicalize(&self, path: &Path) -> Result<PathBuf> {
self.executor.simulate_random_delay().await; self.simulate_random_delay().await;
Ok(normalize_path(path)) Ok(normalize_path(path))
} }
async fn is_file(&self, path: &Path) -> bool { async fn is_file(&self, path: &Path) -> bool {
let path = normalize_path(path); let path = normalize_path(path);
self.executor.simulate_random_delay().await; self.simulate_random_delay().await;
let state = self.state.lock().await; let state = self.state.lock().await;
state state
.entries .entries
@ -604,7 +612,7 @@ impl Fs for FakeFs {
} }
async fn metadata(&self, path: &Path) -> Result<Option<Metadata>> { async fn metadata(&self, path: &Path) -> Result<Option<Metadata>> {
self.executor.simulate_random_delay().await; self.simulate_random_delay().await;
let state = self.state.lock().await; let state = self.state.lock().await;
let path = normalize_path(path); let path = normalize_path(path);
Ok(state.entries.get(&path).map(|entry| entry.metadata.clone())) Ok(state.entries.get(&path).map(|entry| entry.metadata.clone()))
@ -615,7 +623,7 @@ impl Fs for FakeFs {
abs_path: &Path, abs_path: &Path,
) -> Result<Pin<Box<dyn Send + Stream<Item = Result<PathBuf>>>>> { ) -> Result<Pin<Box<dyn Send + Stream<Item = Result<PathBuf>>>>> {
use futures::{future, stream}; use futures::{future, stream};
self.executor.simulate_random_delay().await; self.simulate_random_delay().await;
let state = self.state.lock().await; let state = self.state.lock().await;
let abs_path = normalize_path(abs_path); let abs_path = normalize_path(abs_path);
Ok(Box::pin(stream::iter(state.entries.clone()).filter_map( Ok(Box::pin(stream::iter(state.entries.clone()).filter_map(
@ -635,7 +643,7 @@ impl Fs for FakeFs {
_: Duration, _: Duration,
) -> Pin<Box<dyn Send + Stream<Item = Vec<fsevent::Event>>>> { ) -> Pin<Box<dyn Send + Stream<Item = Vec<fsevent::Event>>>> {
let state = self.state.lock().await; let state = self.state.lock().await;
self.executor.simulate_random_delay().await; self.simulate_random_delay().await;
let rx = state.events_tx.subscribe(); let rx = state.events_tx.subscribe();
let path = path.to_path_buf(); let path = path.to_path_buf();
Box::pin(futures::StreamExt::filter(rx, move |events| { Box::pin(futures::StreamExt::filter(rx, move |events| {