Skip to content

Commit 62e7342

Browse files
authored
cache pid lock files to reduce IO operations (#6297)
## Description Implements a new `PidLockedFiles` type for efficient file locking. Before this we were do file IO on every did change key press event. We now cache the results for efficiency.
1 parent bdd14c3 commit 62e7342

File tree

4 files changed

+69
-34
lines changed

4 files changed

+69
-34
lines changed

sway-lsp/src/core/document.rs

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#![allow(dead_code)]
2+
use std::sync::Arc;
3+
24
use crate::{
35
error::{DirectoryError, DocumentError, LanguageServerError},
46
utils::document,
@@ -109,30 +111,6 @@ impl TextDocument {
109111
}
110112
}
111113

112-
/// Marks the specified file as "dirty" by creating a corresponding flag file.
113-
///
114-
/// This function ensures the necessary directory structure exists before creating the flag file.
115-
pub fn mark_file_as_dirty(uri: &Url) -> Result<(), LanguageServerError> {
116-
let path = document::get_path_from_url(uri)?;
117-
Ok(PidFileLocking::lsp(path)
118-
.lock()
119-
.map_err(|e| DirectoryError::LspLocksDirFailed(e.to_string()))?)
120-
}
121-
122-
/// Removes the corresponding flag file for the specified Url.
123-
///
124-
/// If the flag file does not exist, this function will do nothing.
125-
pub fn remove_dirty_flag(uri: &Url) -> Result<(), LanguageServerError> {
126-
let path = document::get_path_from_url(uri)?;
127-
let uri = uri.clone();
128-
Ok(PidFileLocking::lsp(path)
129-
.release()
130-
.map_err(|err| DocumentError::UnableToRemoveFile {
131-
path: uri.path().to_string(),
132-
err: err.to_string(),
133-
})?)
134-
}
135-
136114
#[derive(Debug)]
137115
struct EditText<'text> {
138116
start_index: usize,
@@ -242,6 +220,57 @@ impl std::ops::Deref for Documents {
242220
}
243221
}
244222

223+
/// Manages process-based file locking for multiple files.
224+
pub struct PidLockedFiles {
225+
locks: DashMap<Url, Arc<PidFileLocking>>,
226+
}
227+
228+
impl Default for PidLockedFiles {
229+
fn default() -> Self {
230+
Self::new()
231+
}
232+
}
233+
234+
impl PidLockedFiles {
235+
pub fn new() -> Self {
236+
Self {
237+
locks: DashMap::new(),
238+
}
239+
}
240+
241+
/// Marks the specified file as "dirty" by creating a corresponding flag file.
242+
///
243+
/// This function ensures the necessary directory structure exists before creating the flag file.
244+
/// If the file is already locked, this function will do nothing. This is to reduce the number of
245+
/// unnecessary file IO operations.
246+
pub fn mark_file_as_dirty(&self, uri: &Url) -> Result<(), LanguageServerError> {
247+
if !self.locks.contains_key(uri) {
248+
let path = document::get_path_from_url(uri)?;
249+
let file_lock = Arc::new(PidFileLocking::lsp(path));
250+
file_lock
251+
.lock()
252+
.map_err(|e| DirectoryError::LspLocksDirFailed(e.to_string()))?;
253+
self.locks.insert(uri.clone(), file_lock);
254+
}
255+
Ok(())
256+
}
257+
258+
/// Removes the corresponding flag file for the specified Url.
259+
///
260+
/// If the flag file does not exist, this function will do nothing.
261+
pub fn remove_dirty_flag(&self, uri: &Url) -> Result<(), LanguageServerError> {
262+
if let Some((uri, file_lock)) = self.locks.remove(uri) {
263+
file_lock
264+
.release()
265+
.map_err(|err| DocumentError::UnableToRemoveFile {
266+
path: uri.path().to_string(),
267+
err: err.to_string(),
268+
})?;
269+
}
270+
Ok(())
271+
}
272+
}
273+
245274
#[cfg(test)]
246275
mod tests {
247276
use super::*;

sway-lsp/src/handlers/notification.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22
//! Protocol. This module specifically handles notification messages sent by the Client.
33
44
use crate::{
5-
core::{
6-
document::{self, Documents},
7-
session::Session,
8-
},
5+
core::{document::Documents, session::Session},
96
error::LanguageServerError,
107
server_state::{CompilationContext, ServerState, TaskMessage},
118
};
@@ -89,7 +86,10 @@ pub async fn handle_did_change_text_document(
8986
state: &ServerState,
9087
params: DidChangeTextDocumentParams,
9188
) -> Result<(), LanguageServerError> {
92-
if let Err(err) = document::mark_file_as_dirty(&params.text_document.uri) {
89+
if let Err(err) = state
90+
.pid_locked_files
91+
.mark_file_as_dirty(&params.text_document.uri)
92+
{
9393
tracing::warn!("Failed to mark file as dirty: {}", err);
9494
}
9595

@@ -138,7 +138,9 @@ pub(crate) async fn handle_did_save_text_document(
138138
state: &ServerState,
139139
params: DidSaveTextDocumentParams,
140140
) -> Result<(), LanguageServerError> {
141-
document::remove_dirty_flag(&params.text_document.uri)?;
141+
state
142+
.pid_locked_files
143+
.remove_dirty_flag(&params.text_document.uri)?;
142144
let (uri, session) = state
143145
.uri_and_session_from_workspace(&params.text_document.uri)
144146
.await?;
@@ -159,7 +161,7 @@ pub(crate) async fn handle_did_change_watched_files(
159161
for event in params.changes {
160162
let (uri, _) = state.uri_and_session_from_workspace(&event.uri).await?;
161163
if let FileChangeType::DELETED = event.typ {
162-
document::remove_dirty_flag(&event.uri)?;
164+
state.pid_locked_files.remove_dirty_flag(&event.uri)?;
163165
let _ = state.documents.remove_document(&uri);
164166
}
165167
}

sway-lsp/src/server.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
//! It provides an interface between the LSP protocol and the sway-lsp internals.
33
44
use crate::{
5-
core::document,
65
handlers::{notification, request},
76
lsp_ext::{MetricsParams, OnEnterParams, ShowAstParams, VisualizeParams},
87
server_state::ServerState,
@@ -43,7 +42,10 @@ impl LanguageServer for ServerState {
4342
}
4443

4544
async fn did_close(&self, params: DidCloseTextDocumentParams) {
46-
if let Err(err) = document::remove_dirty_flag(&params.text_document.uri) {
45+
if let Err(err) = self
46+
.pid_locked_files
47+
.remove_dirty_flag(&params.text_document.uri)
48+
{
4749
tracing::error!("{}", err.to_string());
4850
}
4951
}

sway-lsp/src/server_state.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use crate::{
44
config::{Config, GarbageCollectionConfig, Warnings},
55
core::{
6-
document::Documents,
6+
document::{Documents, PidLockedFiles},
77
session::{self, Session},
88
},
99
error::{DirectoryError, DocumentError, LanguageServerError},
@@ -49,6 +49,7 @@ pub struct ServerState {
4949
pub(crate) cb_tx: Sender<TaskMessage>,
5050
pub(crate) cb_rx: Arc<Receiver<TaskMessage>>,
5151
pub(crate) finished_compilation: Arc<Notify>,
52+
pub(crate) pid_locked_files: PidLockedFiles,
5253
last_compilation_state: Arc<RwLock<LastCompilationState>>,
5354
}
5455

@@ -66,6 +67,7 @@ impl Default for ServerState {
6667
cb_tx,
6768
cb_rx: Arc::new(cb_rx),
6869
finished_compilation: Arc::new(Notify::new()),
70+
pid_locked_files: PidLockedFiles::new(),
6971
last_compilation_state: Arc::new(RwLock::new(LastCompilationState::Uninitialized)),
7072
};
7173
// Spawn a new thread dedicated to handling compilation tasks

0 commit comments

Comments
 (0)