Skip to content

Commit 37f805c

Browse files
authored
Merge branch 'master' into contract-self-impl
2 parents 0005d33 + bfb38f8 commit 37f805c

File tree

1 file changed

+65
-2
lines changed

1 file changed

+65
-2
lines changed

forc-util/src/fs_locking.rs

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{hash_path, user_forc_directory};
22
use std::{
3-
fs::{create_dir_all, remove_file, File},
3+
fs::{create_dir_all, read_dir, remove_file, File},
44
io::{self, Read, Write},
55
path::{Path, PathBuf},
66
};
@@ -19,6 +19,9 @@ impl PidFileLocking {
1919
dir: Y,
2020
extension: &str,
2121
) -> PidFileLocking {
22+
// Try to cleanup stale files, ignore any errors as this is best-effort
23+
let _ = Self::cleanup_stale_files();
24+
2225
let file_name = hash_path(filename);
2326
Self(
2427
user_forc_directory()
@@ -136,6 +139,38 @@ impl PidFileLocking {
136139
fs.flush()?;
137140
Ok(())
138141
}
142+
143+
/// Cleans up all stale lock files in the .lsp-locks directory
144+
/// Returns a vector of paths that were cleaned up
145+
pub fn cleanup_stale_files() -> io::Result<Vec<PathBuf>> {
146+
let lock_dir = user_forc_directory().join(".lsp-locks");
147+
let entries = read_dir(&lock_dir)?;
148+
let mut cleaned_paths = Vec::new();
149+
150+
for entry in entries {
151+
let entry = entry?;
152+
let path = entry.path();
153+
if let Some(ext) = path.extension().and_then(|ext| ext.to_str()) {
154+
if ext == "lock" {
155+
if let Ok(mut file) = File::open(&path) {
156+
let mut contents = String::new();
157+
if file.read_to_string(&mut contents).is_ok() {
158+
if let Ok(pid) = contents.trim().parse::<usize>() {
159+
if !Self::is_pid_active(pid) {
160+
remove_file(&path)?;
161+
cleaned_paths.push(path);
162+
}
163+
} else {
164+
remove_file(&path)?;
165+
cleaned_paths.push(path);
166+
}
167+
}
168+
}
169+
}
170+
}
171+
}
172+
Ok(cleaned_paths)
173+
}
139174
}
140175

141176
/// Checks if the specified file is marked as "dirty".
@@ -149,7 +184,7 @@ pub fn is_file_dirty<X: AsRef<Path>>(path: X) -> bool {
149184

150185
#[cfg(test)]
151186
mod test {
152-
use super::PidFileLocking;
187+
use super::{user_forc_directory, PidFileLocking};
153188
use std::{
154189
fs::{metadata, File},
155190
io::{ErrorKind, Write},
@@ -210,4 +245,32 @@ mod test {
210245
let e = metadata(&x.0).unwrap_err().kind();
211246
assert_eq!(e, ErrorKind::NotFound);
212247
}
248+
249+
#[test]
250+
fn test_cleanup_stale_files() {
251+
// First create some test files
252+
let test_lock = PidFileLocking::lsp("test_cleanup");
253+
test_lock.lock().expect("Failed to create test lock file");
254+
255+
// Create a test lock file with invalid PID
256+
let lock_path = user_forc_directory()
257+
.join(".lsp-locks")
258+
.join("test_cleanup.lock");
259+
File::create(&lock_path).expect("Failed to create test lock file");
260+
261+
// Run cleanup and check returned paths
262+
let cleaned_paths =
263+
PidFileLocking::cleanup_stale_files().expect("Failed to cleanup stale files");
264+
265+
// Verify that only the invalid lock file was cleaned up
266+
assert_eq!(cleaned_paths.len(), 1);
267+
assert_eq!(cleaned_paths[0], lock_path);
268+
269+
// Verify file system state
270+
assert!(test_lock.0.exists(), "Active lock file should still exist");
271+
assert!(!lock_path.exists(), "Lock file should be removed");
272+
273+
// Cleanup after test
274+
test_lock.release().expect("Failed to release test lock");
275+
}
213276
}

0 commit comments

Comments
 (0)