Skip to content

Commit aac5291

Browse files
committed
Create single_instance.rs
1 parent 230bbd1 commit aac5291

File tree

1 file changed

+84
-0
lines changed

1 file changed

+84
-0
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
use dirs::cache_dir;
2+
use fs4::fs_std::FileExt;
3+
use log::{debug, error};
4+
use std::fs::{File, OpenOptions};
5+
use std::io;
6+
use std::sync::atomic::{AtomicBool, Ordering};
7+
use std::sync::Arc;
8+
9+
#[derive(Debug)]
10+
pub struct SingleInstance {
11+
file: Option<File>,
12+
locked: Arc<AtomicBool>,
13+
}
14+
15+
#[derive(Debug, thiserror::Error)]
16+
pub enum SingleInstanceError {
17+
#[error("Another instance is already running")]
18+
AlreadyRunning,
19+
#[error("IO error: {0}")]
20+
Io(#[from] io::Error),
21+
#[error("Failed to create lock directory")]
22+
LockDirCreation,
23+
}
24+
25+
impl SingleInstance {
26+
pub fn new(client_name: &str) -> Result<SingleInstance, SingleInstanceError> {
27+
let cache_dir = cache_dir().ok_or(SingleInstanceError::LockDirCreation)?;
28+
let lock_dir = cache_dir.join("activitywatch").join("client_locks");
29+
std::fs::create_dir_all(&lock_dir).map_err(|_| SingleInstanceError::LockDirCreation)?;
30+
31+
let lockfile = lock_dir.join(client_name);
32+
debug!("SingleInstance lockfile: {:?}", lockfile);
33+
34+
#[cfg(windows)]
35+
{
36+
// On Windows, try to create an exclusive file
37+
// Remove existing file if it exists (in case of previous crash)
38+
let _ = std::fs::remove_file(&lockfile);
39+
40+
match OpenOptions::new()
41+
.write(true)
42+
.create(true)
43+
.create_new(true)
44+
.open(&lockfile)
45+
{
46+
Ok(file) => Ok(SingleInstance {
47+
file: Some(file),
48+
locked: Arc::new(AtomicBool::new(true)),
49+
}),
50+
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {
51+
error!("Another instance is already running");
52+
Err(SingleInstanceError::AlreadyRunning)
53+
}
54+
Err(e) => Err(SingleInstanceError::Io(e)),
55+
}
56+
}
57+
58+
#[cfg(unix)]
59+
{
60+
// On Unix-like systems, use flock
61+
match OpenOptions::new().write(true).create(true).open(&lockfile) {
62+
Ok(file) => match file.try_lock_exclusive() {
63+
Ok(true) => Ok(SingleInstance {
64+
file: Some(file),
65+
locked: Arc::new(AtomicBool::new(true)),
66+
}),
67+
Ok(false) => Err(SingleInstanceError::AlreadyRunning),
68+
Err(e) => Err(SingleInstanceError::Io(e)),
69+
},
70+
Err(e) => Err(SingleInstanceError::Io(e)),
71+
}
72+
}
73+
}
74+
}
75+
76+
impl Drop for SingleInstance {
77+
fn drop(&mut self) {
78+
if self.locked.load(Ordering::SeqCst) {
79+
//drop the file handle and lock on Unix and Windows
80+
self.file.take();
81+
self.locked.store(false, Ordering::SeqCst);
82+
}
83+
}
84+
}

0 commit comments

Comments
 (0)