|
1 | 1 | mod dto; |
2 | 2 |
|
3 | 3 | use std::collections::btree_map::Entry; |
4 | | -use std::io::{self, Read as _}; |
5 | | -use std::os::unix::net::UnixStream; |
| 4 | +use std::io::{self}; |
| 5 | +#[cfg(not(target_os = "macos"))] |
| 6 | +use std::os::unix::net::{UnixListener, UnixStream}; |
6 | 7 | use std::path::{Path, PathBuf}; |
7 | 8 | use std::time::Duration; |
8 | 9 | use std::{fs, thread}; |
@@ -175,18 +176,22 @@ impl<'a> LockedRoot<'a> { |
175 | 176 | } |
176 | 177 | Entry::Occupied(mut e) => { |
177 | 178 | if let Some(prev_sock_path) = e.get().socket_path.as_ref() { |
178 | | - if let Ok(mut s) = UnixStream::connect(prev_sock_path) { |
| 179 | + if let Ok(s) = try_lock(prev_sock_path) { |
179 | 180 | info!( |
180 | 181 | target: LOG_TARGET, |
181 | 182 | key, |
182 | 183 | lock_id, |
183 | 184 | sock_path = %prev_sock_path.display(), |
184 | | - "Previous lock holder still alive" |
| 185 | + "Previous lock holder still alive (potentially)" |
185 | 186 | ); |
186 | 187 | had_to_wait |= true; |
187 | 188 | self.r#yield_with(|| { |
188 | | - // we are just waiting to get disconnected here |
189 | | - let _ = s.read(&mut [0]); |
| 189 | + let _ = clear_lock(s, prev_sock_path).inspect_err(|err| { |
| 190 | + info!( |
| 191 | + %err, |
| 192 | + "Error during waiting for / clearing the old lock" |
| 193 | + ) |
| 194 | + }); |
190 | 195 | })?; |
191 | 196 | } else { |
192 | 197 | debug!( |
@@ -289,3 +294,66 @@ fn rm_prev_sock_path(prev_sock_path: &Path) { |
289 | 294 | } |
290 | 295 | } |
291 | 296 | } |
| 297 | + |
| 298 | +// On Darwin Unix Sockets are not automatically removed, and linger, |
| 299 | +// with processes that try to connect to them just hanging. This makes them |
| 300 | +// unsuitable for our needs. Just use a file that we lock exclusively. |
| 301 | +#[cfg(target_os = "macos")] |
| 302 | +pub fn mk_lock(path: &Path) -> Result<fs::File> { |
| 303 | + let lock_file = fs::File::create(path)?; |
| 304 | + lock_file.lock_exclusive()?; |
| 305 | + Ok(lock_file) |
| 306 | +} |
| 307 | + |
| 308 | +// On Linux we can use Unix Sockets as they disappear automatically, |
| 309 | +// which is nice. |
| 310 | +#[cfg(not(target_os = "macos"))] |
| 311 | +pub fn mk_lock(path: &Path) -> Result<UnixListener> { |
| 312 | + use std::os::unix::net::UnixStream; |
| 313 | + |
| 314 | + let socket = UnixListener::bind(path)?; |
| 315 | + |
| 316 | + assert!(UnixStream::connect(path).is_ok()); |
| 317 | + |
| 318 | + Ok(socket) |
| 319 | +} |
| 320 | + |
| 321 | +#[cfg(target_os = "macos")] |
| 322 | +pub fn try_lock(path: &Path) -> Result<fs::File> { |
| 323 | + let lock_file = fs::File::open(path)?; |
| 324 | + Ok(lock_file) |
| 325 | +} |
| 326 | + |
| 327 | +#[cfg(not(target_os = "macos"))] |
| 328 | +pub fn try_lock(path: &Path) -> Result<UnixStream> { |
| 329 | + let socket = UnixStream::connect(path)?; |
| 330 | + |
| 331 | + Ok(socket) |
| 332 | +} |
| 333 | + |
| 334 | +#[cfg(target_os = "macos")] |
| 335 | +pub fn clear_lock(mut file: fs::File, path: &Path) -> Result<()> { |
| 336 | + file.lock_exclusive()?; |
| 337 | + |
| 338 | + // We want to unlock the file, even if we failed to remove it |
| 339 | + let rm_res = fs::remove_file(path); |
| 340 | + |
| 341 | + file.unlock()?; |
| 342 | + |
| 343 | + // if removing failed, report it now |
| 344 | + rm_res?; |
| 345 | + |
| 346 | + Ok(()) |
| 347 | +} |
| 348 | + |
| 349 | +#[cfg(not(target_os = "macos"))] |
| 350 | +pub fn clear_lock(mut s: UnixStream, _path: &Path) -> Result<()> { |
| 351 | + // we are just waiting to get disconnected here |
| 352 | + |
| 353 | + use std::io::Read as _; |
| 354 | + |
| 355 | + // ignore, we *will* disconnect, nothing interesting about it |
| 356 | + let _ = s.read(&mut [0]); |
| 357 | + |
| 358 | + Ok(()) |
| 359 | +} |
0 commit comments