Added timeout to file locks.
This commit is contained in:
parent
eb4ec43b7d
commit
dd4633948e
|
@ -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.
|
|
@ -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);
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue