use std::fs::{File, OpenOptions, TryLockError};
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::time::Duration;
use crate::error::{Result, StorageError};
const MAX_PAUSE: Duration = Duration::from_millis(20);
#[derive(Debug)]
pub(crate) struct FileLock {
file: File,
}
impl FileLock {
pub(crate) fn acquire(path: &Path, give_up: &AtomicBool) -> Result<Self> {
if let Some(parent) = path.parent().filter(|parent| !parent.as_os_str().is_empty()) {
let _ = std::fs::create_dir_all(parent);
}
let file =
OpenOptions::new().read(true).write(true).create(true).truncate(false).open(path).map_err(|error| {
StorageError::io(format!("Could not open license lock file {}", path.display()), error)
})?;
let mut pause = Duration::from_millis(1);
loop {
match file.try_lock() {
Ok(()) => return Ok(Self { file }),
Err(TryLockError::WouldBlock) => {}
Err(TryLockError::Error(error)) => {
return Err(StorageError::io(format!("Could not lock {}", path.display()), error).into());
}
}
if give_up.load(Ordering::Acquire) {
return Err(StorageError::new("Stopped waiting for the license file lock").into());
}
thread::sleep(pause);
pause = (pause * 2).min(MAX_PAUSE);
}
}
}
impl Drop for FileLock {
fn drop(&mut self) {
let _ = self.file.unlock();
}
}