Skip to main content

osdk_core/
lock.rs

1//! Cross-process file locks via `fslock`, used to serialize installs of the same
2//! tool version and to guard store GC.
3
4use std::path::{Path, PathBuf};
5
6use crate::dirs::create_dir_all;
7use crate::error::{Error, Result};
8
9/// A held exclusive lock. Released on drop.
10pub struct FileLock {
11    _inner: fslock::LockFile,
12    path: PathBuf,
13}
14
15impl FileLock {
16    /// Acquire an exclusive lock at `path`, blocking until available. The
17    /// parent directory is created if needed.
18    pub fn acquire(path: impl AsRef<Path>) -> Result<FileLock> {
19        let path = path.as_ref().to_path_buf();
20        if let Some(parent) = path.parent() {
21            create_dir_all(parent)?;
22        }
23        let mut lf = fslock::LockFile::open(&path).map_err(|e| Error::io(&path, e))?;
24        lf.lock().map_err(|e| Error::io(&path, e))?;
25        Ok(FileLock { _inner: lf, path })
26    }
27
28    /// Try to acquire without blocking. Returns `Ok(None)` if already held.
29    pub fn try_acquire(path: impl AsRef<Path>) -> Result<Option<FileLock>> {
30        let path = path.as_ref().to_path_buf();
31        if let Some(parent) = path.parent() {
32            create_dir_all(parent)?;
33        }
34        let mut lf = fslock::LockFile::open(&path).map_err(|e| Error::io(&path, e))?;
35        if lf.try_lock().map_err(|e| Error::io(&path, e))? {
36            Ok(Some(FileLock { _inner: lf, path }))
37        } else {
38            Ok(None)
39        }
40    }
41
42    pub fn path(&self) -> &Path {
43        &self.path
44    }
45}