use crate::config::KopiConfig;
use crate::error::Result;
use crate::indicator::{ProgressIndicator, StatusReporter};
use crate::locking::{LockAcquisition, LockBackend, LockController, LockScope};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
pub struct CacheWriterLockGuard {
acquisition: Option<LockAcquisition>,
backend: LockBackend,
waited: Duration,
}
impl CacheWriterLockGuard {
fn new(acquisition: LockAcquisition, waited: Duration) -> Self {
let backend = acquisition.backend();
Self {
acquisition: Some(acquisition),
backend,
waited,
}
}
pub fn acquire_with_feedback(
config: &KopiConfig,
indicator: Arc<Mutex<Box<dyn ProgressIndicator>>>,
) -> Result<Self> {
let controller = LockController::with_default_inspector(
config.kopi_home().to_path_buf(),
&config.locking,
);
let started_at = Instant::now();
let acquisition = controller.acquire_with_feedback(LockScope::CacheWriter, indicator)?;
Ok(Self::new(acquisition, started_at.elapsed()))
}
pub fn acquire_with_status_reporter(
config: &KopiConfig,
reporter: &StatusReporter,
) -> Result<Self> {
let controller = LockController::with_default_inspector(
config.kopi_home().to_path_buf(),
&config.locking,
);
let started_at = Instant::now();
let acquisition = controller.acquire_with_status_sink(LockScope::CacheWriter, reporter)?;
Ok(Self::new(acquisition, started_at.elapsed()))
}
pub fn backend(&self) -> LockBackend {
self.backend
}
pub fn waited(&self) -> Duration {
self.waited
}
}
impl Drop for CacheWriterLockGuard {
fn drop(&mut self) {
if let Some(acquisition) = self.acquisition.take()
&& let Err(err) = acquisition.release()
{
log::warn!("Failed to release cache writer lock: {err}");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::indicator::{SilentProgress, StatusReporter};
use crate::locking::LockBackend;
use tempfile::TempDir;
#[test]
fn feedback_acquisition_uses_advisory_backend() {
let temp_home = TempDir::new().unwrap();
let config = KopiConfig::new(temp_home.path().to_path_buf()).unwrap();
let indicator = Arc::new(Mutex::new(
Box::new(SilentProgress::new()) as Box<dyn ProgressIndicator>
));
let guard =
CacheWriterLockGuard::acquire_with_feedback(&config, indicator.clone()).unwrap();
assert_eq!(guard.backend(), LockBackend::Advisory);
assert!(guard.waited() >= Duration::ZERO);
drop(guard);
let reacquired =
CacheWriterLockGuard::acquire_with_feedback(&config, indicator.clone()).unwrap();
assert_eq!(reacquired.backend(), LockBackend::Advisory);
}
#[test]
fn status_reporter_acquisition_releases_on_drop() {
let temp_home = TempDir::new().unwrap();
let config = KopiConfig::new(temp_home.path().to_path_buf()).unwrap();
let reporter = StatusReporter::new(true);
let guard = CacheWriterLockGuard::acquire_with_status_reporter(&config, &reporter).unwrap();
assert_eq!(guard.backend(), LockBackend::Advisory);
drop(guard);
let reporter = StatusReporter::new(true);
assert!(CacheWriterLockGuard::acquire_with_status_reporter(&config, &reporter).is_ok());
}
}