From 1628a4c731ae81a921d940905533625863190144 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Wed, 19 Dec 2018 11:08:57 +0100 Subject: [PATCH] use timers with a signal for file locking * rename lock_file -> open_file_locked, * add lock_file as a function working on already-opened files * change timeout types to std::time::Duration Signed-off-by: Wolfgang Bumiller --- src/backup/chunk_store.rs | 4 +- src/tools.rs | 90 ++++++++++++++++++++++----------------- 2 files changed, 53 insertions(+), 41 deletions(-) diff --git a/src/backup/chunk_store.rs b/src/backup/chunk_store.rs index 3398de99..d610c7e3 100644 --- a/src/backup/chunk_store.rs +++ b/src/backup/chunk_store.rs @@ -1,6 +1,7 @@ use failure::*; use std::path::{Path, PathBuf}; use std::io::Write; +use std::time::Duration; use crypto::digest::Digest; use crypto::sha2::Sha512Trunc256; @@ -100,7 +101,8 @@ impl ChunkStore { lockfile_path.push(".lock"); // make sure only one process/thread/task can use it - let lockfile = tools::lock_file(lockfile_path, 10)?; + let lockfile = tools::open_file_locked( + lockfile_path, Duration::from_secs(10))?; Ok(ChunkStore { base, diff --git a/src/tools.rs b/src/tools.rs index 51452fe1..09a6076a 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -1,15 +1,15 @@ use failure::*; use nix::unistd; use nix::sys::stat; -use nix::fcntl::{flock, FlockArg}; use std::fs::{File, OpenOptions}; use std::io::Write; use std::path::Path; use std::io::Read; use std::io::ErrorKind; +use std::time::Duration; -use std::os::unix::io::AsRawFd; +use std::os::unix::io::{RawFd, AsRawFd}; pub mod timer; @@ -58,49 +58,59 @@ pub fn file_set_contents>( Ok(()) } -pub fn lock_file>( - filename: P, - timeout: usize -) -> Result { +pub fn lock_file( + file: &mut F, + exclusive: bool, + timeout: Option, + ) -> Result<(), Error> +{ + let lockarg = + if exclusive { + nix::fcntl::FlockArg::LockExclusive + } else { + nix::fcntl::FlockArg::LockShared + }; - let path = filename.as_ref(); - let lockfile = match OpenOptions::new() - .create(true) - .append(true) - .open(path) { + let timeout = match timeout { + None => { + nix::fcntl::flock(file.as_raw_fd(), lockarg)?; + return Ok(()); + } + Some(t) => t, + }; + + // unblock the timeout signal temporarily + let _sigblock_guard = timer::unblock_timeout_signal(); + + // setup a timeout timer + let mut timer = timer::Timer::create( + timer::Clock::Realtime, + timer::TimerEvent::ThisThreadSignal(timer::SIGTIMEOUT))?; + + timer.arm(timer::TimerSpec::new() + .value(Some(timeout)) + .interval(Some(Duration::from_millis(10))))?; + + nix::fcntl::flock(file.as_raw_fd(), lockarg)?; + Ok(()) +} + +pub fn open_file_locked>(path: P, timeout: Duration) + -> Result +{ + let path = path.as_ref(); + let mut file = + match OpenOptions::new() + .create(true) + .append(true) + .open(path) + { Ok(file) => file, Err(err) => bail!("Unable to open lock {:?} - {}", path, err), }; - - let fd = lockfile.as_raw_fd(); - - let now = std::time::SystemTime::now(); - let mut print_msg = true; - loop { - match flock(fd, FlockArg::LockExclusiveNonblock) { - Ok(_) => break, - Err(_) => { - if print_msg { - print_msg = false; - eprintln!("trying to aquire lock..."); - } - } - } - - match now.elapsed() { - Ok(elapsed) => { - if elapsed.as_secs() >= (timeout as u64) { - bail!("unable to aquire lock {:?} - got timeout", path); - } - } - Err(err) => { - bail!("unable to aquire lock {:?} - clock problems - {}", path, err); - } - } - std::thread::sleep(std::time::Duration::from_millis(100)); - } - Ok(lockfile) + lock_file(&mut file, true, Some(timeout))?; + Ok(file) } // Note: We cannot implement an Iterator, because Iterators cannot