use libc::{LOCK_EX, LOCK_NB, LOCK_UN}; use std::fs::{File, OpenOptions}; use std::io; use std::os::unix::io::{AsRawFd, RawFd}; use std::path::Path; use crate::error::*; #[derive(Debug)] pub(crate) struct RawNamedLock { lock_file: File, } impl RawNamedLock { pub(crate) fn create(lock_path: &Path) -> Result { let lock_file = OpenOptions::new() .write(true) .create_new(true) .open(&lock_path) .or_else(|_| OpenOptions::new().write(true).open(&lock_path)) .map_err(Error::CreateFailed)?; Ok(RawNamedLock { lock_file, }) } pub(crate) fn try_lock(&self) -> Result<()> { unsafe { flock(self.lock_file.as_raw_fd(), LOCK_EX | LOCK_NB) } } pub(crate) fn lock(&self) -> Result<()> { unsafe { flock(self.lock_file.as_raw_fd(), LOCK_EX) } } pub(crate) fn unlock(&self) -> Result<()> { unsafe { flock(self.lock_file.as_raw_fd(), LOCK_UN) } } } unsafe fn flock(fd: RawFd, operation: i32) -> Result<()> { loop { let rc = libc::flock(fd, operation); if rc < 0 { let err = io::Error::last_os_error(); if err.kind() == io::ErrorKind::Interrupted { continue; } else if err.kind() == io::ErrorKind::WouldBlock { return Err(Error::WouldBlock); } else if (operation & LOCK_EX) == LOCK_EX { return Err(Error::LockFailed); } else if (operation & LOCK_UN) == LOCK_UN { return Err(Error::UnlockFailed); } } break; } Ok(()) }