about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2023-09-01 20:45:46 +0200
committerLukas Wirth <lukastw97@gmail.com>2023-09-01 20:45:46 +0200
commitcc8b78601d3a40bcc8ccf0b6d5efff386d602c1f (patch)
treebaed619105bf7d68acb4d170572a678e0b7b216d
parent70e21dc30b5cbaecad463adb9ec28e32ae5c35c8 (diff)
downloadrust-cc8b78601d3a40bcc8ccf0b6d5efff386d602c1f.tar.gz
rust-cc8b78601d3a40bcc8ccf0b6d5efff386d602c1f.zip
Shuffle some locking around
-rw-r--r--crates/rust-analyzer/src/global_state.rs30
-rw-r--r--crates/rust-analyzer/src/handlers/notification.rs11
2 files changed, 29 insertions, 12 deletions
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index ea8a6975195..5024e04ffd3 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -12,12 +12,12 @@ use ide_db::base_db::{CrateId, FileLoader, ProcMacroPaths, SourceDatabase};
 use load_cargo::SourceRootConfig;
 use lsp_types::{SemanticTokens, Url};
 use nohash_hasher::IntMap;
-use parking_lot::{Mutex, RwLock};
+use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard, RwLockWriteGuard};
 use proc_macro_api::ProcMacroServer;
 use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
 use rustc_hash::{FxHashMap, FxHashSet};
 use triomphe::Arc;
-use vfs::AnchoredPathBuf;
+use vfs::{AnchoredPathBuf, Vfs};
 
 use crate::{
     config::{Config, ConfigError},
@@ -216,12 +216,15 @@ impl GlobalState {
         let mut file_changes = FxHashMap::default();
         let (change, changed_files, workspace_structure_change) = {
             let mut change = Change::new();
-            let (vfs, line_endings_map) = &mut *self.vfs.write();
-            let changed_files = vfs.take_changes();
+            let mut guard = self.vfs.write();
+            let changed_files = guard.0.take_changes();
             if changed_files.is_empty() {
                 return false;
             }
 
+            // downgrade to read lock to allow more readers while we are normalizing text
+            let guard = RwLockWriteGuard::downgrade_to_upgradable(guard);
+            let vfs: &Vfs = &guard.0;
             // We need to fix up the changed events a bit. If we have a create or modify for a file
             // id that is followed by a delete we actually skip observing the file text from the
             // earlier event, to avoid problems later on.
@@ -272,6 +275,7 @@ impl GlobalState {
             let mut workspace_structure_change = None;
             // A file was added or deleted
             let mut has_structure_changes = false;
+            let mut bytes = vec![];
             for file in &changed_files {
                 let vfs_path = &vfs.file_path(file.file_id);
                 if let Some(path) = vfs_path.as_path() {
@@ -293,16 +297,28 @@ impl GlobalState {
 
                 let text = if file.exists() {
                     let bytes = vfs.file_contents(file.file_id).to_vec();
+
                     String::from_utf8(bytes).ok().and_then(|text| {
+                        // FIXME: Consider doing normalization in the `vfs` instead? That allows
+                        // getting rid of some locking
                         let (text, line_endings) = LineEndings::normalize(text);
-                        line_endings_map.insert(file.file_id, line_endings);
-                        Some(Arc::from(text))
+                        Some((Arc::from(text), line_endings))
                     })
                 } else {
                     None
                 };
-                change.change_file(file.file_id, text);
+                // delay `line_endings_map` changes until we are done normalizing the text
+                // this allows delaying the re-acquisition of the write lock
+                bytes.push((file.file_id, text));
             }
+            let (vfs, line_endings_map) = &mut *RwLockUpgradableReadGuard::upgrade(guard);
+            bytes.into_iter().for_each(|(file_id, text)| match text {
+                None => change.change_file(file_id, None),
+                Some((text, line_endings)) => {
+                    line_endings_map.insert(file_id, line_endings);
+                    change.change_file(file_id, Some(text));
+                }
+            });
             if has_structure_changes {
                 let roots = self.source_root_config.partition(vfs);
                 change.set_roots(roots);
diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs
index e830e5e9a64..3fd10274e0d 100644
--- a/crates/rust-analyzer/src/handlers/notification.rs
+++ b/crates/rust-analyzer/src/handlers/notification.rs
@@ -84,15 +84,16 @@ pub(crate) fn handle_did_change_text_document(
             }
         };
 
-        let vfs = &mut state.vfs.write().0;
-        let file_id = vfs.file_id(&path).unwrap();
         let text = apply_document_changes(
             state.config.position_encoding(),
-            || std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into(),
+            || {
+                let vfs = &state.vfs.read().0;
+                let file_id = vfs.file_id(&path).unwrap();
+                std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into()
+            },
             params.content_changes,
         );
-
-        vfs.set_file_contents(path, Some(text.into_bytes()));
+        state.vfs.write().0.set_file_contents(path, Some(text.into_bytes()));
     }
     Ok(())
 }