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 <w.bumiller@proxmox.com>
This commit is contained in:
parent
8cf6e764ad
commit
1628a4c731
|
@ -1,6 +1,7 @@
|
||||||
use failure::*;
|
use failure::*;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use crypto::digest::Digest;
|
use crypto::digest::Digest;
|
||||||
use crypto::sha2::Sha512Trunc256;
|
use crypto::sha2::Sha512Trunc256;
|
||||||
|
@ -100,7 +101,8 @@ impl ChunkStore {
|
||||||
lockfile_path.push(".lock");
|
lockfile_path.push(".lock");
|
||||||
|
|
||||||
// make sure only one process/thread/task can use it
|
// 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 {
|
Ok(ChunkStore {
|
||||||
base,
|
base,
|
||||||
|
|
90
src/tools.rs
90
src/tools.rs
|
@ -1,15 +1,15 @@
|
||||||
use failure::*;
|
use failure::*;
|
||||||
use nix::unistd;
|
use nix::unistd;
|
||||||
use nix::sys::stat;
|
use nix::sys::stat;
|
||||||
use nix::fcntl::{flock, FlockArg};
|
|
||||||
|
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::io::ErrorKind;
|
use std::io::ErrorKind;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::{RawFd, AsRawFd};
|
||||||
|
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
|
|
||||||
|
@ -58,49 +58,59 @@ pub fn file_set_contents<P: AsRef<Path>>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lock_file<P: AsRef<Path>>(
|
pub fn lock_file<F: AsRawFd>(
|
||||||
filename: P,
|
file: &mut F,
|
||||||
timeout: usize
|
exclusive: bool,
|
||||||
) -> Result<File, Error> {
|
timeout: Option<Duration>,
|
||||||
|
) -> Result<(), Error>
|
||||||
|
{
|
||||||
|
let lockarg =
|
||||||
|
if exclusive {
|
||||||
|
nix::fcntl::FlockArg::LockExclusive
|
||||||
|
} else {
|
||||||
|
nix::fcntl::FlockArg::LockShared
|
||||||
|
};
|
||||||
|
|
||||||
let path = filename.as_ref();
|
let timeout = match timeout {
|
||||||
let lockfile = match OpenOptions::new()
|
None => {
|
||||||
.create(true)
|
nix::fcntl::flock(file.as_raw_fd(), lockarg)?;
|
||||||
.append(true)
|
return Ok(());
|
||||||
.open(path) {
|
}
|
||||||
|
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<P: AsRef<Path>>(path: P, timeout: Duration)
|
||||||
|
-> Result<File, Error>
|
||||||
|
{
|
||||||
|
let path = path.as_ref();
|
||||||
|
let mut file =
|
||||||
|
match OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(path)
|
||||||
|
{
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(err) => bail!("Unable to open lock {:?} - {}",
|
Err(err) => bail!("Unable to open lock {:?} - {}",
|
||||||
path, err),
|
path, err),
|
||||||
};
|
};
|
||||||
|
lock_file(&mut file, true, Some(timeout))?;
|
||||||
let fd = lockfile.as_raw_fd();
|
Ok(file)
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: We cannot implement an Iterator, because Iterators cannot
|
// Note: We cannot implement an Iterator, because Iterators cannot
|
||||||
|
|
Loading…
Reference in New Issue