]> git.proxmox.com Git - proxmox-backup.git/blame - src/tools/process_locker.rs
fix last commit: the filename var was not ment to be removed, sorry
[proxmox-backup.git] / src / tools / process_locker.rs
CommitLineData
a650f503
DM
1//! Inter-process reader-writer lock builder.
2//!
3//! This implemenation uses fcntl record locks with non-blocking
4//! F_SETLK command (never blocks).
5
6use failure::*;
7
8use std::sync::{Arc, Mutex};
9use std::os::unix::io::AsRawFd;
10
11// fixme: use F_OFD_ locks when implemented with nix::fcntl
12
13// Note: flock lock conversion is not atomic, so we need to use fcntl
14
15/// Inter-process reader-writer lock
16pub struct ProcessLocker {
17 file: std::fs::File,
18 exclusive: bool,
19 writers: usize,
20}
21
22/// Lock guard for shared locks
23///
24/// Release the lock when it goes out of scope.
25pub struct ProcessLockSharedGuard {
26 locker: Arc<Mutex<ProcessLocker>>,
27}
28
29impl Drop for ProcessLockSharedGuard {
30 fn drop(&mut self) {
31 let mut data = self.locker.lock().unwrap();
32
33 if data.writers == 0 { panic!("unexpected ProcessLocker state"); }
34
35 if data.writers == 1 && !data.exclusive {
36
37 let op = libc::flock {
38 l_type: libc::F_UNLCK as i16,
39 l_whence: libc::SEEK_SET as i16,
40 l_start: 0,
41 l_len: 0,
42 l_pid: 0,
43 };
44
45 if let Err(err) = nix::fcntl::fcntl(data.file.as_raw_fd(), nix::fcntl::FcntlArg::F_SETLKW(&op)) {
46 panic!("unable to drop writer lock - {}", err);
47 }
48 data.writers = 0;
49 }
50 }
51}
52
53/// Lock guard for exclusive locks
54///
55/// Release the lock when it goes out of scope.
56pub struct ProcessLockExclusiveGuard {
57 locker: Arc<Mutex<ProcessLocker>>,
58}
59
60impl Drop for ProcessLockExclusiveGuard {
61 fn drop(&mut self) {
62 let mut data = self.locker.lock().unwrap();
63
64 if !data.exclusive { panic!("unexpected ProcessLocker state"); }
65
66 let ltype = if data.writers != 0 { libc::F_RDLCK } else { libc::F_UNLCK };
67 let op = libc::flock {
68 l_type: ltype as i16,
69 l_whence: libc::SEEK_SET as i16,
70 l_start: 0,
71 l_len: 0,
72 l_pid: 0,
73 };
74
75 if let Err(err) = nix::fcntl::fcntl(data.file.as_raw_fd(), nix::fcntl::FcntlArg::F_SETLKW(&op)) {
76 panic!("unable to drop exclusive lock - {}", err);
77 }
78
79 data.exclusive = false;
80 }
81}
82
83impl ProcessLocker {
84
85 /// Create a new instance for the specified file.
86 ///
87 /// This simply creates the file if it does not exist.
abfc001f 88 pub fn new<P: AsRef<std::path::Path>>(lockfile: P) -> Result<Arc<Mutex<Self>>, Error> {
a650f503
DM
89
90 let file = std::fs::OpenOptions::new()
91 .create(true)
92 .read(true)
93 .write(true)
94 .open(lockfile)?;
95
96 Ok(Arc::new(Mutex::new(Self {
97 file: file,
98 exclusive: false,
99 writers: 0,
100 })))
101 }
102
103 fn try_lock(file: &std::fs::File, ltype: i32) -> Result<(), Error> {
104
105 let op = libc::flock {
106 l_type: ltype as i16,
107 l_whence: libc::SEEK_SET as i16,
108 l_start: 0,
109 l_len: 0,
110 l_pid: 0,
111 };
112
113 nix::fcntl::fcntl(file.as_raw_fd(), nix::fcntl::FcntlArg::F_SETLK(&op))?;
114
115 Ok(())
116 }
117
118 /// Try to aquire a shared lock
119 ///
120 /// On sucess, this makes sure that no other process can get an exclusive lock for the file.
121 pub fn try_shared_lock(locker: Arc<Mutex<Self>>) -> Result<ProcessLockSharedGuard, Error> {
122
123 let mut data = locker.lock().unwrap();
124
125 if data.writers == 0 && !data.exclusive {
126 if let Err(err) = Self::try_lock(&data.file, libc::F_RDLCK) {
127 bail!("unable to get shared lock - {}", err);
128 }
129 }
130
131 data.writers += 1;
132
133 Ok(ProcessLockSharedGuard { locker: locker.clone() })
134 }
135
136 /// Try to aquire a exclusive lock
137 ///
138 /// Make sure the we are the only process which has locks for this file (shared or exclusive).
139 pub fn try_exclusive_lock(locker: Arc<Mutex<Self>>) -> Result<ProcessLockExclusiveGuard, Error> {
140
141 let mut data = locker.lock().unwrap();
142
143 if data.exclusive {
144 bail!("already locked exclusively");
145 }
146
147 if let Err(err) = Self::try_lock(&data.file, libc::F_WRLCK) {
148 bail!("unable to get exclusive lock - {}", err);
149 }
150
151 data.exclusive = true;
152
153 Ok(ProcessLockExclusiveGuard { locker: locker.clone() })
154 }
155}