From 9e1f72bfa9c90bc0a286cb4fd47781c942060f87 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 13 Jul 2021 09:45:59 +0200 Subject: [PATCH] Use an unfair lock to ensure tests are deterministic Also avoid using `smol::block_on` as it interacts with their `Reactor`. --- zed/src/worktree.rs | 53 +++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index 744449a6cb852322975c50883609df8e3d67d0f2..afb6fc3e49f05c666b49d57751e8f5a616947821 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -86,7 +86,7 @@ pub trait Fs: Send + Sync { async fn save(&self, path: &Path, text: &Rope) -> Result<()>; async fn canonicalize(&self, path: &Path) -> Result; async fn is_file(&self, path: &Path) -> bool; - fn watch( + async fn watch( &self, path: &Path, latency: Duration, @@ -203,7 +203,7 @@ impl Fs for RealFs { .map_or(false, |metadata| metadata.is_file()) } - fn watch( + async fn watch( &self, path: &Path, latency: Duration, @@ -269,7 +269,8 @@ impl FakeFsState { #[cfg(any(test, feature = "test-support"))] pub struct FakeFs { - state: smol::lock::RwLock, + // Use an unfair lock to ensure tests are deterministic. + state: futures::lock::Mutex, } #[cfg(any(test, feature = "test-support"))] @@ -288,7 +289,7 @@ impl FakeFs { }, ); Self { - state: smol::lock::RwLock::new(FakeFsState { + state: futures::lock::Mutex::new(FakeFsState { entries, next_inode: 1, events_tx, @@ -297,7 +298,7 @@ impl FakeFs { } pub async fn insert_dir(&self, path: impl AsRef) -> Result<()> { - let mut state = self.state.write().await; + let mut state = self.state.lock().await; let path = path.as_ref(); state.validate_path(path)?; @@ -318,7 +319,7 @@ impl FakeFs { } pub async fn insert_file(&self, path: impl AsRef, content: String) -> Result<()> { - let mut state = self.state.write().await; + let mut state = self.state.lock().await; let path = path.as_ref(); state.validate_path(path)?; @@ -339,7 +340,7 @@ impl FakeFs { } pub async fn remove(&self, path: &Path) -> Result<()> { - let mut state = self.state.write().await; + let mut state = self.state.lock().await; state.validate_path(path)?; state.entries.retain(|path, _| !path.starts_with(path)); state.emit_event(&[path]).await; @@ -347,7 +348,7 @@ impl FakeFs { } pub async fn rename(&self, source: &Path, target: &Path) -> Result<()> { - let mut state = self.state.write().await; + let mut state = self.state.lock().await; state.validate_path(source)?; state.validate_path(target)?; if state.entries.contains_key(target) { @@ -384,7 +385,7 @@ impl Fs for FakeFs { path: Arc, abs_path: &Path, ) -> Result> { - let state = self.state.read().await; + let state = self.state.lock().await; if let Some(entry) = state.entries.get(abs_path) { Ok(Some(Entry { id: next_entry_id.fetch_add(1, SeqCst), @@ -413,7 +414,7 @@ impl Fs for FakeFs { ) -> Result> + Send>>> { use futures::{future, stream}; - let state = self.state.read().await; + let state = self.state.lock().await; Ok(stream::iter(state.entries.clone()) .filter(move |(child_path, _)| future::ready(child_path.parent() == Some(abs_path))) .then(move |(child_abs_path, child_entry)| async move { @@ -437,7 +438,7 @@ impl Fs for FakeFs { } async fn load(&self, path: &Path) -> Result { - let state = self.state.read().await; + let state = self.state.lock().await; let text = state .entries .get(path) @@ -447,7 +448,7 @@ impl Fs for FakeFs { } async fn save(&self, path: &Path, text: &Rope) -> Result<()> { - let mut state = self.state.write().await; + let mut state = self.state.lock().await; state.validate_path(path)?; if let Some(entry) = state.entries.get_mut(path) { if entry.is_dir { @@ -479,16 +480,16 @@ impl Fs for FakeFs { } async fn is_file(&self, path: &Path) -> bool { - let state = self.state.read().await; + let state = self.state.lock().await; state.entries.get(path).map_or(false, |entry| !entry.is_dir) } - fn watch( + async fn watch( &self, path: &Path, _: Duration, ) -> Pin>>> { - let state = smol::block_on(self.state.read()); + let state = self.state.lock().await; let rx = state.events_tx.subscribe(); let path = path.to_path_buf(); Box::pin(futures::StreamExt::filter(rx, move |events| { @@ -547,10 +548,10 @@ impl Worktree { cx: &mut ModelContext, ) -> Self { let (mut tree, scan_states_tx) = LocalWorktree::new(path, languages, fs.clone(), cx); - - let events = fs.watch(tree.snapshot.abs_path.as_ref(), Duration::from_millis(100)); + let abs_path = tree.snapshot.abs_path.clone(); let background_snapshot = tree.background_snapshot.clone(); tree._background_scanner_task = Some(cx.background().spawn(async move { + let events = fs.watch(&abs_path, Duration::from_millis(100)).await; let scanner = BackgroundScanner::new( background_snapshot, scan_states_tx, @@ -1059,20 +1060,26 @@ impl LocalWorktree { cx.spawn_weak(|this, mut cx| async move { while let Ok(scan_state) = scan_states_rx.recv().await { if let Some(handle) = cx.read(|cx| this.upgrade(&cx)) { - handle.update(&mut cx, |this, cx| { + let to_send = handle.update(&mut cx, |this, cx| { last_scan_state_tx.blocking_send(scan_state).ok(); this.poll_snapshot(cx); let tree = this.as_local_mut().unwrap(); if !tree.is_scanning() { if let Some(snapshots_to_send_tx) = tree.snapshots_to_send_tx.clone() { - if let Err(err) = - smol::block_on(snapshots_to_send_tx.send(tree.snapshot())) - { - log::error!("error submitting snapshot to send {}", err); - } + Some((tree.snapshot(), snapshots_to_send_tx)) + } else { + None } + } else { + None } }); + + if let Some((snapshot, snapshots_to_send_tx)) = to_send { + if let Err(err) = snapshots_to_send_tx.send(snapshot).await { + log::error!("error submitting snapshot to send {}", err); + } + } } else { break; }