Skip to content

Commit 599d008

Browse files
committed
improved lock files
1 parent 273d9f5 commit 599d008

File tree

2 files changed

+52
-21
lines changed

2 files changed

+52
-21
lines changed

tmc-langs-framework/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ tempfile = "3"
2323
anyhow = "1"
2424
fd-lock = "2"
2525

26+
[target.'cfg(windows)'.dependencies]
27+
winapi = "0.3"
28+
2629
[dev-dependencies]
2730
tempfile = "3"
2831
mockall = "0.9"

tmc-langs-framework/src/file_util/lock_windows.rs

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@
66
use crate::error::FileIo;
77
use crate::file_util::*;
88
use fd_lock::{FdLock, FdLockGuard};
9+
use std::os::windows::fs::OpenOptionsExt;
910
use std::path::PathBuf;
1011
use std::{borrow::Cow, io::ErrorKind};
1112
use std::{
1213
fs::OpenOptions,
1314
time::{Duration, Instant},
1415
};
16+
use winapi::um::{
17+
winbase::FILE_FLAG_DELETE_ON_CLOSE,
18+
winnt::{FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_TEMPORARY},
19+
};
1520

1621
#[macro_export]
1722
macro_rules! lock {
@@ -56,35 +61,40 @@ impl FileLock {
5661
let lock = self.lock.as_mut().unwrap();
5762
let guard = lock.lock().unwrap();
5863
Ok(FileLockGuard {
59-
_guard: guard,
64+
_guard: LockInner::FdLockGuard(guard),
6065
path: Cow::Borrowed(&self.path),
61-
is_lock_file: false,
6266
})
6367
} else if self.path.is_dir() {
6468
// for directories, we'll create/open a .tmc.lock file
6569
let lock_path = self.path.join(".tmc.lock");
6670
loop {
6771
// try to create a new lock file
6872
match OpenOptions::new()
73+
// needed for create_new
6974
.write(true)
75+
// only creates file if it exists, check and creation are atomic
7076
.create_new(true)
77+
// hidden, so it won't be a problem when going through the directory
78+
.attributes(FILE_ATTRIBUTE_HIDDEN)
79+
// just tells windows there's probably no point in writing this to disk;
80+
// this might further reduce the risk of leftover lock files
81+
.attributes(FILE_ATTRIBUTE_TEMPORARY)
82+
// windows deletes the lock file when the handle is closed = when the lock is dropped
83+
.custom_flags(FILE_FLAG_DELETE_ON_CLOSE)
7184
.open(&lock_path)
7285
{
7386
Ok(file) => {
7487
// was able to create a new lock file
75-
let lock = FdLock::new(file);
76-
self.lock = Some(lock);
77-
let lock = self.lock.as_mut().unwrap();
78-
let guard = lock.lock().unwrap();
7988
return Ok(FileLockGuard {
80-
_guard: guard,
89+
_guard: LockInner::LockFile(file),
8190
path: Cow::Owned(lock_path),
82-
is_lock_file: true,
8391
});
8492
}
8593
Err(err) => {
8694
if err.kind() == ErrorKind::AlreadyExists {
8795
// lock file already exists, let's wait a little and try again
96+
// after 30 seconds, print a warning in the logs every 10 seconds
97+
// after 120 seconds, print an error in the logs every 10 seconds
8898
if start_time.elapsed() > Duration::from_secs(30)
8999
&& warning_timer.elapsed() > Duration::from_secs(10)
90100
{
@@ -95,6 +105,16 @@ impl FileLock {
95105
lock_path.display(),
96106
start_time.elapsed().as_secs()
97107
);
108+
} else if start_time.elapsed() > Duration::from_secs(120)
109+
&& warning_timer.elapsed() > Duration::from_secs(10)
110+
{
111+
warning_timer = Instant::now();
112+
log::error!(
113+
"The program has been waiting for lock file {} to be deleted for {} seconds,
114+
the lock file might have been left over from a previous run due to an error.",
115+
lock_path.display(),
116+
start_time.elapsed().as_secs()
117+
);
98118
}
99119
std::thread::sleep(Duration::from_millis(500));
100120
} else {
@@ -105,30 +125,24 @@ impl FileLock {
105125
}
106126
}
107127
} else {
108-
return Err(FileIo::InvalidLockPath(self.path.to_path_buf()));
128+
Err(FileIo::InvalidLockPath(self.path.to_path_buf()))
109129
}
110130
}
111131
}
112132

113133
pub struct FileLockGuard<'a> {
114-
_guard: FdLockGuard<'a, File>,
134+
_guard: LockInner<'a>,
115135
path: Cow<'a, PathBuf>,
116-
is_lock_file: bool,
136+
}
137+
138+
enum LockInner<'a> {
139+
LockFile(File),
140+
FdLockGuard(FdLockGuard<'a, File>),
117141
}
118142

119143
impl Drop for FileLockGuard<'_> {
120144
fn drop(&mut self) {
121145
log::debug!("unlocking {}", self.path.display());
122-
if self.is_lock_file {
123-
log::debug!("removing lock file");
124-
if let Err(err) = remove_file(self.path.as_ref()) {
125-
log::error!(
126-
"failed to remove lock file at {}: {}",
127-
self.path.display(),
128-
err
129-
);
130-
}
131-
}
132146
}
133147
}
134148

@@ -216,4 +230,18 @@ mod test {
216230
// wait for thread, if it panicked, it tried to lock the mutex without the file lock
217231
handle.join().unwrap();
218232
}
233+
234+
#[test]
235+
fn lock_file_is_created_and_is_deleted() {
236+
init();
237+
238+
let temp = tempfile::tempdir().unwrap();
239+
let mut lock = FileLock::new(temp.path().to_path_buf()).unwrap();
240+
let lock_path = temp.path().join(".tmc.lock");
241+
assert!(!lock_path.exists());
242+
let guard = lock.lock().unwrap();
243+
assert!(lock_path.exists());
244+
drop(guard);
245+
assert!(!lock_path.exists());
246+
}
219247
}

0 commit comments

Comments
 (0)