use crate::util::errors::CodeError;
use std::{fs::File, io};
pub struct FileLock {
file: File,
#[cfg(windows)]
overlapped: winapi::um::minwinbase::OVERLAPPED,
}
#[cfg(windows)]
unsafe impl Send for FileLock {}
pub enum Lock {
Acquired(FileLock),
AlreadyLocked(File),
}
#[cfg(windows)]
pub const PREFIX_LOCKED_BYTES: usize = 1;
#[cfg(unix)]
pub const PREFIX_LOCKED_BYTES: usize = 0;
impl FileLock {
#[cfg(windows)]
pub fn acquire(file: File) -> Result<Lock, CodeError> {
use std::os::windows::prelude::AsRawHandle;
use winapi::{
shared::winerror::{ERROR_IO_PENDING, ERROR_LOCK_VIOLATION},
um::{
fileapi::LockFileEx,
minwinbase::{LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY},
},
};
let handle = file.as_raw_handle();
let (overlapped, ok) = unsafe {
let mut overlapped = std::mem::zeroed();
let ok = LockFileEx(
handle,
LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY,
0,
PREFIX_LOCKED_BYTES as u32,
0,
&mut overlapped,
);
(overlapped, ok)
};
if ok != 0 {
return Ok(Lock::Acquired(Self { file, overlapped }));
}
let err = io::Error::last_os_error();
let raw = err.raw_os_error();
if raw == Some(ERROR_IO_PENDING as i32) || raw == Some(ERROR_LOCK_VIOLATION as i32) {
return Ok(Lock::AlreadyLocked(file));
}
Err(CodeError::SingletonLockfileOpenFailed(err))
}
#[cfg(unix)]
pub fn acquire(file: File) -> Result<Lock, CodeError> {
use std::os::unix::io::AsRawFd;
let fd = file.as_raw_fd();
let res = unsafe { libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB) };
if res == 0 {
return Ok(Lock::Acquired(Self { file }));
}
let err = io::Error::last_os_error();
if err.kind() == io::ErrorKind::WouldBlock {
return Ok(Lock::AlreadyLocked(file));
}
Err(CodeError::SingletonLockfileOpenFailed(err))
}
pub fn file(&self) -> &File {
&self.file
}
pub fn file_mut(&mut self) -> &mut File {
&mut self.file
}
}
impl Drop for FileLock {
#[cfg(windows)]
fn drop(&mut self) {
use std::os::windows::prelude::AsRawHandle;
use winapi::um::fileapi::UnlockFileEx;
unsafe {
UnlockFileEx(
self.file.as_raw_handle(),
0,
u32::MAX,
u32::MAX,
&mut self.overlapped,
)
};
}
#[cfg(unix)]
fn drop(&mut self) {
use std::os::unix::io::AsRawFd;
unsafe { libc::flock(self.file.as_raw_fd(), libc::LOCK_UN) };
}
}