Skip to main content

j_agent/util/
sync.rs

1use crate::util::log::write_error_log;
2use std::path::{Path, PathBuf};
3use std::sync::{Mutex, MutexGuard};
4
5/// 安全获取 Mutex 锁:如果锁被毒化(另一个线程持锁时 panic),
6/// 自动恢复并记录错误日志,而不是直接 panic
7pub fn safe_lock<'a, T>(mutex: &'a Mutex<T>, context: &str) -> MutexGuard<'a, T> {
8    mutex.lock().unwrap_or_else(|poisoned| {
9        write_error_log(
10            "safe_lock",
11            &format!("Mutex poisoned at [{}], recovering", context),
12        );
13        poisoned.into_inner()
14    })
15}
16
17/// 基于独立文件的互斥守卫
18///
19/// 通过创建 `.lock` 文件实现进程间互斥,Drop 时自动删除释放。
20/// 使用 `File::create_new()` 原子操作,跨平台无兼容问题。
21///
22/// # Examples
23///
24/// ```ignore
25/// let lock_path = config_path.with_extension("yaml.lock");
26/// let _guard = LockFileGuard::acquire(&lock_path)?;
27/// // 临界区:此时独占访问
28/// fs::write(&config_path, content)?;
29/// // _guard drop 时自动删除 lock 文件
30/// ```
31pub struct LockFileGuard {
32    path: PathBuf,
33}
34
35impl LockFileGuard {
36    /// 获取文件锁:创建 lock 文件,已存在则等待重试
37    ///
38    /// - `lock_path`: lock 文件路径(如 `config.yaml.lock`)
39    ///
40    /// 最多重试 20 次,每次间隔 50ms(总计约 1 秒)。
41    pub fn acquire(lock_path: &Path) -> Result<Self, String> {
42        const MAX_RETRIES: u32 = 20;
43        const RETRY_DELAY_MS: u64 = 50;
44
45        for attempt in 0..MAX_RETRIES {
46            // create_new:文件已存在则返回 Err,否则原子创建,跨平台一致
47            match std::fs::File::create_new(lock_path) {
48                Ok(_) => {
49                    return Ok(Self {
50                        path: lock_path.to_path_buf(),
51                    });
52                }
53                Err(_) if attempt < MAX_RETRIES - 1 => {
54                    std::thread::sleep(std::time::Duration::from_millis(RETRY_DELAY_MS));
55                }
56                Err(e) => {
57                    return Err(format!("文件被占用,请稍后重试: {}", e));
58                }
59            }
60        }
61        unreachable!()
62    }
63}
64
65impl Drop for LockFileGuard {
66    fn drop(&mut self) {
67        let _ = std::fs::remove_file(&self.path);
68    }
69}