pikadick_util/
async_lock_file.rs

1use anyhow::Context;
2use fslock::{
3    IntoOsString,
4    LockFile,
5    ToOsStr,
6};
7use std::sync::Arc;
8
9/// An async `LockFile`.
10///
11/// Implemented by blocking on the tokio threadpool
12#[derive(Debug, Clone)]
13pub struct AsyncLockFile {
14    file: Arc<tokio::sync::Mutex<LockFile>>,
15}
16
17impl AsyncLockFile {
18    /// Open a file for locking
19    pub async fn open<P>(path: P) -> anyhow::Result<Self>
20    where
21        P: IntoOsString,
22    {
23        let path = path.into_os_string()?;
24        tokio::task::spawn_blocking(move || Self::blocking_open(&path))
25            .await
26            .context("failed to join task")?
27    }
28
29    /// Open a file for locking in a blocking manner
30    pub fn blocking_open<P>(path: &P) -> anyhow::Result<Self>
31    where
32        P: ToOsStr + ?Sized,
33    {
34        let file = LockFile::open(path)?;
35        Ok(Self {
36            file: Arc::new(tokio::sync::Mutex::new(file)),
37        })
38    }
39
40    /// Lock the file
41    pub async fn lock(&self) -> anyhow::Result<()> {
42        let mut file = self.file.clone().lock_owned().await;
43        Ok(tokio::task::spawn_blocking(move || file.lock())
44            .await
45            .context("failed to join task")??)
46    }
47
48    /// Lock the file, writing the PID to it
49    pub async fn lock_with_pid(&self) -> anyhow::Result<()> {
50        let mut file = self.file.clone().lock_owned().await;
51        Ok(tokio::task::spawn_blocking(move || file.lock_with_pid())
52            .await
53            .context("failed to join task")??)
54    }
55
56    /// Try to lock the file, returning `true` if successful.
57    pub async fn try_lock(&self) -> anyhow::Result<bool> {
58        let mut file = self.file.clone().lock_owned().await;
59        Ok(tokio::task::spawn_blocking(move || file.try_lock())
60            .await
61            .context("failed to join task")??)
62    }
63
64    /// Try to lock a file with a pid, returning `true` if successful.
65    pub async fn try_lock_with_pid(&self) -> anyhow::Result<bool> {
66        let mut file = self.file.clone().lock_owned().await;
67        Ok(
68            tokio::task::spawn_blocking(move || file.try_lock_with_pid())
69                .await
70                .context("failed to join task")??,
71        )
72    }
73
74    /// Try to lock a file with a pid, returning `true` if successful in a blocking manner.
75    pub fn try_lock_with_pid_blocking(&self) -> anyhow::Result<bool> {
76        Ok(self.file.blocking_lock().try_lock_with_pid()?)
77    }
78
79    /// Returns `true` if this owns the lock
80    pub async fn owns_lock(&self) -> anyhow::Result<bool> {
81        let file = self.file.clone().lock_owned().await;
82        tokio::task::spawn_blocking(move || file.owns_lock())
83            .await
84            .context("failed to join task")
85    }
86
87    /// Unlock the file
88    pub async fn unlock(&self) -> anyhow::Result<()> {
89        let mut file = self.file.clone().lock_owned().await;
90        Ok(tokio::task::spawn_blocking(move || file.unlock())
91            .await
92            .context("failed to join task")??)
93    }
94
95    /// Unlock the file in a blocking manner
96    pub fn blocking_unlock(&self) -> anyhow::Result<()> {
97        Ok(self.file.blocking_lock().unlock()?)
98    }
99}