Added timeout to file locks.

This commit is contained in:
Samuel Guerra 2023-05-29 19:51:27 -03:00
parent eb4ec43b7d
commit dd4633948e
4 changed files with 49 additions and 22 deletions

View File

@ -32,19 +32,5 @@
* Review save debounce.
* Config test sometimes does not write any data to file.
- Data never written.
- Encountered a deadlock once.
- Encountered invalid syntax in TOML once.
- Size assert failed before rename, issue is `wait_idle` and `flush_shutdown` not working?
- No error (or much less) when test is already build?
- First build after change almost always gets an error.
- Issue caused by slower disk?
- No error observed with a `sleep(1.secs())` before rename.
- Observed deadlock again (in json again, first test?).
- Running tests 1000 times always causes the error.
- Only observed the error "entry not found".
- But assert on file sized passed and the file looks ok after the failure.
- Error happens on READ BEFORE LOAD, HOW?
- Read is always ok, when it happens (correct entry count).
- Read-only test (already generated file) shows the same error.
- THE BUG IS IN THE STATUS VAR NOT SYNCHRONIZING CORRECTLY
- Running all 3 tests on repeat 1000 times always causes a bug.
- Either "did not find entry" or worst, it hangs.

View File

@ -1247,6 +1247,38 @@ pub fn unlock_ok(file: &impl fs4::FileExt) -> std::io::Result<()> {
}
}
/// Calls [`fs4::FileExt::lock_exclusive`] with a timeout.
pub fn lock_exclusive(file: &impl fs4::FileExt, timeout: Duration) -> std::io::Result<()> {
lock_timeout(file, |f| f.try_lock_exclusive(), timeout)
}
/// Calls [`fs4::FileExt::lock_shared`] with a timeout.
pub fn lock_shared(file: &impl fs4::FileExt, timeout: Duration) -> std::io::Result<()> {
lock_timeout(file, |f| f.try_lock_shared(), timeout)
}
fn lock_timeout<F: fs4::FileExt>(file: &F, try_lock: impl Fn(&F) -> std::io::Result<()>, mut timeout: Duration) -> std::io::Result<()> {
let mut locked_error = None;
loop {
match try_lock(file) {
Ok(()) => return Ok(()),
Err(e) => {
if e.raw_os_error() != locked_error.get_or_insert_with(fs4::lock_contended_error).raw_os_error() {
return Err(e);
}
const INTERVAL: Duration = Duration::from_millis(10);
timeout = timeout.saturating_sub(INTERVAL);
if timeout.is_zero() {
return Err(e);
} else {
thread::sleep(INTERVAL.min(timeout));
}
}
}
}
}
/// Like [`std::ops::Range<usize>`], but implements [`Copy`].
#[derive(Clone, Copy)]
pub struct IndexRange(pub usize, pub usize);

View File

@ -19,7 +19,7 @@ use path_absolutize::Absolutize;
use crate::{
app::AppExtension,
context::app_local,
crate_util::{unlock_ok, Handle, HandleOwner},
crate_util::{lock_exclusive, lock_shared, unlock_ok, Handle, HandleOwner},
event::{event, event_args, EventHandle},
handler::{app_hn_once, AppHandler, FilterAppHandler},
task,
@ -264,7 +264,7 @@ impl WatchFile {
return Self::try_open_non_empty(path, false);
}
file.lock_shared()?;
lock_shared(&file, Duration::from_secs(5))?;
Ok(Self(file))
}
@ -409,10 +409,13 @@ impl WriteFile {
let transaction_path = actual_path.with_file_name(format!("{hidden_name}.{TRANSACTION_LOCK_EXT}"));
let transaction_lock = fs::OpenOptions::new().create(true).write(true).open(&transaction_path)?;
transaction_lock.lock_exclusive()?;
const TIMEOUT: Duration = Duration::from_secs(5);
lock_exclusive(&transaction_lock, TIMEOUT)?;
let actual_file = fs::OpenOptions::new().write(true).create(true).open(&actual_path)?;
actual_file.lock_exclusive()?;
lock_exclusive(&actual_file, TIMEOUT)?;
let mut n = 0;
let mut temp_path = actual_path.with_file_name(format!("{hidden_name}.{TRANSACTION_GUID}-{n}.tmp"));

View File

@ -261,7 +261,7 @@ mod file_cache {
};
use crate::{
crate_util::unlock_ok,
crate_util::{lock_exclusive, lock_shared, unlock_ok},
task::{
self,
io::{McBufErrorExt, McBufReader},
@ -440,7 +440,13 @@ mod file_cache {
}
};
let lock_r = if write { lock.lock_exclusive() } else { lock.lock_shared() };
const TIMEOUT: Duration = Duration::from_secs(5);
let lock_r = if write {
lock_exclusive(&lock, TIMEOUT)
} else {
lock_shared(&lock, TIMEOUT)
};
if let Err(e) = lock_r {
tracing::error!("cache lock error, {e:?}");
Self::try_delete_dir(&dir);