evobench_tools/io_utils/
owning_lockable_file.rs1use std::{fs::File, ops::Deref, path::Path, sync::Arc};
5
6use fs2::{FileExt, lock_contended_error};
7use lazy_static::lazy_static;
8
9use crate::io_utils::lockable_file::{HELD_LOCKS, LockStatus};
10
11pub struct OwningSharedFileLock<F: FileExt> {
14 debug: Option<Arc<Path>>,
15 file: Arc<F>,
19}
20
21impl<F: FileExt> Drop for OwningSharedFileLock<F> {
22 fn drop(&mut self) {
23 self.file
24 .unlock()
25 .expect("no way another path to unlock exists");
26 if let Some(path) = &self.debug {
27 eprintln!("dropped SharedFileLock on {path:?}");
28 HELD_LOCKS.with_borrow_mut(|set| {
29 set.remove(&**path);
30 });
31 }
32 }
33}
34
35impl<F: FileExt> Deref for OwningSharedFileLock<F> {
36 type Target = Arc<F>;
37
38 fn deref(&self) -> &Self::Target {
39 &self.file
40 }
41}
42
43#[derive(Debug)]
46pub struct OwningExclusiveFileLock<F: FileExt> {
47 debug: Option<Arc<Path>>,
48 file: Arc<F>,
49}
50
51impl<F: FileExt> PartialEq for OwningExclusiveFileLock<F> {
52 fn eq(&self, other: &Self) -> bool {
53 self.debug == other.debug
54 }
55}
56
57impl<F: FileExt> Drop for OwningExclusiveFileLock<F> {
58 fn drop(&mut self) {
59 self.file
60 .unlock()
61 .expect("no way another path to unlock exists");
62 if let Some(path) = &self.debug {
63 eprintln!("dropped ExclusiveFileLock on {path:?}");
64 HELD_LOCKS.with_borrow_mut(|set| {
65 set.remove(&**path);
66 });
67 }
68 }
69}
70
71impl<F: FileExt> Deref for OwningExclusiveFileLock<F> {
72 type Target = Arc<F>;
73
74 fn deref(&self) -> &Self::Target {
75 &self.file
76 }
77}
78
79#[derive(Debug)]
82pub struct OwningLockableFile<F: FileExt> {
83 debug: Option<Arc<Path>>,
86 file: Arc<F>,
87}
88
89impl<F: FileExt> OwningLockableFile<F> {
90 pub fn get_lock_status(&mut self) -> std::io::Result<LockStatus> {
95 use LockStatus::*;
96 Ok(if self.try_lock_exclusive()?.is_some() {
97 Unlocked
98 } else if self.try_lock_shared()?.is_some() {
99 SharedLock
100 } else {
101 ExclusiveLock
102 })
103 }
104
105 pub fn lock_shared(&self) -> std::io::Result<OwningSharedFileLock<F>> {
106 if let Some(path) = self.debug.as_ref() {
107 eprintln!("getting SharedFileLock on {path:?}");
108 HELD_LOCKS.with_borrow_mut(|set| -> std::io::Result<()> {
109 if set.contains(&**path) {
111 panic!("{path:?} is already locked by this thread")
112 }
113 FileExt::lock_shared(&*self.file)?;
114 eprintln!("got SharedFileLock on {path:?}");
115 set.insert((&**path).to_owned());
116 Ok(())
117 })?;
118 } else {
119 FileExt::lock_shared(&*self.file)?;
120 }
121 Ok(OwningSharedFileLock {
122 debug: self.debug.as_ref().map(Arc::clone),
123 file: self.file.clone(),
124 })
125 }
126
127 pub fn lock_exclusive(&self) -> std::io::Result<OwningExclusiveFileLock<F>> {
128 if let Some(path) = self.debug.as_ref() {
129 eprintln!("getting ExclusiveFileLock on {path:?}");
130 HELD_LOCKS.with_borrow_mut(|set| -> std::io::Result<()> {
131 if set.contains(&**path) {
132 panic!("{path:?} is already locked by this thread")
133 }
134 FileExt::lock_exclusive(&*self.file)?;
135 eprintln!("got ExclusiveFileLock on {path:?}");
136 set.insert((&**path).to_owned());
137 Ok(())
138 })?;
139 } else {
140 FileExt::lock_exclusive(&*self.file)?;
141 }
142 Ok(OwningExclusiveFileLock {
143 debug: self.debug.as_ref().map(Arc::clone),
144 file: self.file.clone(),
145 })
146 }
147
148 pub fn try_lock_shared(&self) -> std::io::Result<Option<OwningSharedFileLock<F>>> {
149 match FileExt::try_lock_shared(&*self.file) {
150 Ok(()) => {
151 if let Some(path) = self.debug.as_ref() {
152 eprintln!("got SharedFileLock on {path:?}");
153 HELD_LOCKS.with_borrow_mut(|set| {
154 set.insert((&**path).to_owned());
155 });
156 }
157 Ok(Some(OwningSharedFileLock {
158 debug: self.debug.as_ref().map(Arc::clone),
159 file: self.file.clone(),
160 }))
161 }
162 Err(e) => {
163 if e.kind() == lock_contended_error().kind() {
164 Ok(None)
165 } else {
166 Err(e)
167 }
168 }
169 }
170 }
171
172 pub fn try_lock_exclusive(&self) -> std::io::Result<Option<OwningExclusiveFileLock<F>>> {
173 match FileExt::try_lock_exclusive(&*self.file) {
174 Ok(()) => {
175 if let Some(path) = self.debug.as_ref() {
176 eprintln!("got ExclusiveFileLock on {path:?}");
177 HELD_LOCKS.with_borrow_mut(|set| {
178 set.insert((&**path).to_owned());
179 });
180 }
181 Ok(Some(OwningExclusiveFileLock {
182 debug: self.debug.as_ref().map(Arc::clone),
183 file: self.file.clone(),
184 }))
185 }
186 Err(e) => {
187 if e.kind() == lock_contended_error().kind() {
188 Ok(None)
189 } else {
190 Err(e)
191 }
192 }
193 }
194 }
195}
196
197lazy_static! {
198 static ref DEBUGGING: bool = if let Some(val) = std::env::var_os("DEBUG_LOCKABLE_FILE") {
199 match val
200 .into_string()
201 .expect("utf-8 for env var DEBUG_LOCKABLE_FILE")
202 .as_str()
203 {
204 "0" => false,
205 "1" | "" => true,
206 _ => panic!("need 1|0 or empty string for DEBUG_LOCKABLE_FILE"),
207 }
208 } else {
209 false
210 };
211}
212
213impl OwningLockableFile<File> {
214 pub fn open(path: Arc<Path>) -> std::io::Result<Self> {
215 File::open(path.as_ref()).and_then(|file| {
216 let path = if *DEBUGGING { Some(path) } else { None };
217
218 Ok(OwningLockableFile {
219 debug: path,
220 file: Arc::new(file),
221 })
222 })
223 }
224}